Skip to content

Commit

Permalink
FavoriteDetailPage
Browse files Browse the repository at this point in the history
  • Loading branch information
storytellerF committed Apr 26, 2023
1 parent d1f6e3f commit 1c5a995
Show file tree
Hide file tree
Showing 10 changed files with 242 additions and 78 deletions.
2 changes: 1 addition & 1 deletion .idea/deploymentTargetDropDown.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ dependencies {

implementation 'androidx.core:core-ktx:1.10.0'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.1'
implementation 'androidx.activity:activity-compose:1.7.0'
implementation 'androidx.activity:activity-compose:1.7.1'
implementation platform('androidx.compose:compose-bom:2022.10.00')
implementation 'androidx.compose.ui:ui'
implementation 'androidx.compose.ui:ui-graphics'
Expand All @@ -64,7 +64,7 @@ dependencies {
debugImplementation 'androidx.compose.ui:ui-tooling'
debugImplementation 'androidx.compose.ui:ui-test-manifest'

implementation("androidx.compose.runtime:runtime-livedata:1.4.1")
implementation("androidx.compose.runtime:runtime-livedata:1.4.2")
implementation 'com.google.accompanist:accompanist-systemuicontroller:0.30.0'
def nav_version = "2.5.3"
implementation "androidx.navigation:navigation-compose:$nav_version"
Expand All @@ -84,10 +84,11 @@ dependencies {

implementation(project(":bili-api"))

implementation 'com.google.android.exoplayer:exoplayer:2.18.5'
implementation 'com.google.android.exoplayer:exoplayer:2.18.6'

def paging_version = "3.1.1"

implementation "androidx.paging:paging-runtime:$paging_version"
implementation "androidx.paging:paging-compose:1.0.0-alpha18"
implementation("com.github.storytellerF.Bao:startup:2.1")
}
106 changes: 45 additions & 61 deletions app/src/main/java/com/storyteller_f/bi/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,8 @@ import androidx.compose.foundation.layout.width
import androidx.compose.material3.Button
import androidx.compose.material3.DrawerValue
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalNavigationDrawer
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
Expand All @@ -29,28 +26,29 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.unit.dp
import androidx.core.view.WindowCompat
import androidx.navigation.NavDestination.Companion.hierarchy
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import com.google.zxing.BarcodeFormat
import com.google.zxing.EncodeHintType
import com.google.zxing.qrcode.QRCodeWriter
import com.storyteller_f.bi.components.FavoriteDetailPage
import com.storyteller_f.bi.components.FavoritePage
import com.storyteller_f.bi.components.HistoryPage
import com.storyteller_f.bi.components.HomeNavigation
import com.storyteller_f.bi.components.HomeTopBar
import com.storyteller_f.bi.components.MomentsPage
import com.storyteller_f.bi.components.Screen
import com.storyteller_f.bi.components.PlaylistPage
import com.storyteller_f.bi.components.Screen
import com.storyteller_f.bi.components.UserCenterDrawer
import com.storyteller_f.bi.ui.theme.BiTheme
import kotlinx.coroutines.launch
Expand All @@ -70,6 +68,21 @@ class MainActivity : ComponentActivity() {
val drawerState = rememberDrawerState(DrawerValue.Closed)
val coroutineScope = rememberCoroutineScope()
val navController = rememberNavController()
val selectRoute = { screen: Screen ->
navController.navigate(screen.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
}
}

val open = {
coroutineScope.launch {
Expand All @@ -78,12 +91,7 @@ class MainActivity : ComponentActivity() {
}
val user by userInfo.observeAsState()
val u = user
val items = listOf(
Screen.History,
Screen.Moments,
Screen.Playlist,
Screen.Favorite
)

BiTheme {
ModalNavigationDrawer(
drawerContent = {
Expand All @@ -97,51 +105,13 @@ class MainActivity : ComponentActivity() {
}
}, bottomBar = {
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentDestination = navBackStackEntry?.destination
NavigationBar {
items.forEach { screen ->
val selected =
currentDestination?.hierarchy?.any { it.route == screen.route } == true
NavigationBarItem(selected = selected, onClick = {
navController.navigate(screen.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
}
}, {
when {
screen.icon != null -> {
Icon(
ImageVector.vectorResource(id = screen.icon),
contentDescription = screen.route
)
}

screen.vector != null -> Icon(
screen.vector,
contentDescription = screen.route
)
}
}, label = {
Text(text = stringResource(id = screen.resourceId))
})
}
NavigationBarItem(selected = false, onClick = {

}, {
Icon(ImageVector.vectorResource(id = R.drawable.baseline_history_24), contentDescription = null)
}, label = {
Text(text = "special")
})
val items = Screen.allRoute.map {
it.route
}
val any = navBackStackEntry?.destination?.hierarchy?.firstOrNull {
items.contains(it.route)
}?.route
HomeNavigation(any, selectRoute)
}) {
Surface(
modifier = Modifier
Expand Down Expand Up @@ -170,7 +140,19 @@ class MainActivity : ComponentActivity() {
}
composable(Screen.Favorite.route) {
UserAware {
FavoritePage()
FavoritePage {
navController.navigate(Screen.FavoriteList.route.replace("{id}", it.id))
}
}
}
composable(
Screen.FavoriteList.route,
arguments = listOf(navArgument("id") {
type = NavType.StringType
})
) {
UserAware {
FavoriteDetailPage(id = it.arguments?.getString("id").orEmpty())
}
}
}
Expand Down Expand Up @@ -204,9 +186,11 @@ class MainActivity : ComponentActivity() {

@Composable
fun StandBy(width: Int, height: Int, me: @Composable () -> Unit) {
StandBy(modifier = Modifier
.width(width.dp)
.height(height.dp), me)
StandBy(
modifier = Modifier
.width(width.dp)
.height(height.dp), me
)
}

@Composable
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package com.storyteller_f.bi.components

import android.content.Intent
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.CreationExtras
import androidx.lifecycle.viewmodel.MutableCreationExtras
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingSource
import androidx.paging.PagingState
import androidx.paging.cachedIn
import androidx.paging.compose.collectAsLazyPagingItems
import androidx.paging.compose.items
import com.a10miaomiao.bilimiao.comm.entity.ResultInfo
import com.a10miaomiao.bilimiao.comm.entity.media.MediaDetailInfo
import com.a10miaomiao.bilimiao.comm.entity.media.MediasInfo
import com.a10miaomiao.bilimiao.comm.network.BiliApiService
import com.a10miaomiao.bilimiao.comm.network.MiaoHttp.Companion.gson
import com.storyteller_f.bi.StateView
import com.storyteller_f.bi.VideoActivity

object FavoriteIdKey : CreationExtras.Key<String>

val defaultFactory = object : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T {
return FavoriteDetailViewModel(extras.get(FavoriteIdKey)!!) as T
}
}

@Composable
fun FavoriteDetailPage(id: String) {
val current = LocalContext.current
val detailViewModel = viewModel<FavoriteDetailViewModel>(factory = defaultFactory, extras = MutableCreationExtras().apply {
set(FavoriteIdKey, id)
})
val lazyPagingItems = detailViewModel.flow.collectAsLazyPagingItems()
StateView(state = lazyPagingItems.loadState.refresh) {
LazyColumn {
items(lazyPagingItems) {
VideoItem(it?.cover.orEmpty(), it?.title.orEmpty(), it?.upper?.name.orEmpty()) {
current.startActivity(Intent(current, VideoActivity::class.java).apply {
putExtra("videoId", it?.id)
})
}
}
}
}

}

class FavoriteDetailViewModel(id: String) : ViewModel() {
val flow = Pager(
// Configure how data is loaded by passing additional properties to
// PagingConfig, such as prefetchDistance.
PagingConfig(pageSize = 20)
) {
FavoriteDetailSource(id)
}.flow
.cachedIn(viewModelScope)
}

class FavoriteDetailSource(val id: String) : PagingSource<Int, MediasInfo>() {
override fun getRefreshKey(state: PagingState<Int, MediasInfo>): Int? {
return null
}

override suspend fun load(params: LoadParams<Int>): LoadResult<Int, MediasInfo> {
if (id.isEmpty()) {
return LoadResult.Error(Exception("id is empty"))
}
val lastPage = params.key ?: 0
val currentPage = lastPage + 1
val pageSize = 20
val res = BiliApiService.userApi
.mediaDetail(
media_id = id,
pageNum = currentPage,
pageSize = pageSize,
)
.awaitCall()
.gson<ResultInfo<MediaDetailInfo>>()
return if (res.code == 0) {
val data = res.data
val medias = data.medias.orEmpty()
LoadResult.Page(
medias,
null,
if (medias.size < pageSize) null else currentPage + 1
)
} else {
LoadResult.Error(Exception(res.message))
}
}

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.storyteller_f.bi.components

import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.padding
Expand Down Expand Up @@ -38,15 +39,15 @@ import com.storyteller_f.bi.userInfo
import kotlinx.coroutines.launch

@Composable
fun FavoritePage() {
fun FavoritePage(openMediaList: (MediaListInfo) -> Unit = {}) {
val favoriteViewModel = viewModel<FavoriteViewModel>()
val state by favoriteViewModel.state.observeAsState()
val data by favoriteViewModel.data.observeAsState()
StateView(state = state) {
LazyVerticalGrid(GridCells.Adaptive(150.dp)) {
data?.default_folder?.folder_detail?.let {
item {
MediaListContainer(it)
MediaListContainer(it, openMediaList)
}
}
data?.space_infos?.forEach {
Expand All @@ -57,7 +58,7 @@ fun FavoritePage() {
}
it.mediaListResponse.list?.let { list ->
items(list) { info ->
MediaListContainer(mediaListInfo = info)
MediaListContainer(mediaListInfo = info, openMediaList)
}
}
}
Expand All @@ -76,8 +77,10 @@ class MediaListContainerPreviewProvider : PreviewParameterProvider<MediaListInfo
@Preview
@OptIn(ExperimentalGlideComposeApi::class)
@Composable
fun MediaListContainer(@PreviewParameter(MediaListContainerPreviewProvider::class) mediaListInfo: MediaListInfo) {
Box(contentAlignment = Alignment.Center) {
fun MediaListContainer(@PreviewParameter(MediaListContainerPreviewProvider::class) mediaListInfo: MediaListInfo, openMediaList: (MediaListInfo) -> Unit = {}) {
Box(contentAlignment = Alignment.Center, modifier = Modifier.clickable {
openMediaList(mediaListInfo)
}) {
StandBy(width = 200, height = 100) {
val u = UrlUtil.autoHttps(mediaListInfo.cover)
GlideImage(
Expand Down
20 changes: 17 additions & 3 deletions app/src/main/java/com/storyteller_f/bi/components/HomeContent.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,23 @@ import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.ui.graphics.vector.ImageVector
import com.storyteller_f.bi.R

sealed class Screen(val route: String, @StringRes val resourceId: Int, val vector: ImageVector? = null, @DrawableRes val icon: Int? = null) {
sealed class Screen(
val route: String,
@StringRes val resourceId: Int,
val vector: ImageVector? = null,
@DrawableRes val icon: Int? = null
) {
object History : Screen("histories", R.string.histories, icon = R.drawable.baseline_history_24)
object Moments : Screen("moments", R.string.moments, vector = Icons.Filled.Menu)
object Playlist: Screen("playlist", R.string.playlist, vector = Icons.Filled.PlayArrow)
object Favorite: Screen("favorites", R.string.favorite, vector = Icons.Filled.Favorite)
object Playlist : Screen("playlist", R.string.playlist, vector = Icons.Filled.PlayArrow)
object Favorite : Screen("favorites", R.string.favorite, vector = Icons.Filled.Favorite)

object Search : Screen("search", R.string.search, icon = R.drawable.ic_launcher_foreground)

object FavoriteList : Screen("favorite-detail/{id}", R.string.favorite, vector = Icons.Filled.Favorite)
companion object {
val allRoute =
listOf(Screen.History, Screen.Moments, Screen.Playlist, Screen.Favorite, Screen.Search)
}
}

Loading

0 comments on commit 1c5a995

Please sign in to comment.