Skip to content

Commit 80e3519

Browse files
Share common view model among video detail fragments
1 parent 261fd5b commit 80e3519

9 files changed

Lines changed: 422 additions & 428 deletions

File tree

app/src/main/java/org/schabi/newpipe/fragments/detail/StackItem.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import java.io.Serializable;
88

9-
class StackItem implements Serializable {
9+
public class StackItem implements Serializable {
1010
private final int serviceId;
1111
private String url;
1212
private String title;

app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.kt

Lines changed: 285 additions & 352 deletions
Large diffs are not rendered by default.

app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentsFragment.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import androidx.compose.material3.Surface
77
import androidx.core.os.bundleOf
88
import androidx.fragment.app.Fragment
99
import androidx.fragment.compose.content
10+
import androidx.lifecycle.viewmodel.compose.viewModel
1011
import org.schabi.newpipe.ui.components.video.comment.CommentSection
1112
import org.schabi.newpipe.ui.theme.AppTheme
1213
import org.schabi.newpipe.util.KEY_SERVICE_ID
@@ -20,7 +21,7 @@ class CommentsFragment : Fragment() {
2021
) = content {
2122
AppTheme {
2223
Surface {
23-
CommentSection()
24+
CommentSection(viewModel(requireParentFragment()))
2425
}
2526
}
2627
}

app/src/main/java/org/schabi/newpipe/fragments/list/videos/RelatedItemsFragment.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import androidx.compose.material3.Surface
77
import androidx.core.os.bundleOf
88
import androidx.fragment.app.Fragment
99
import androidx.fragment.compose.content
10+
import androidx.lifecycle.viewmodel.compose.viewModel
1011
import org.schabi.newpipe.extractor.stream.StreamInfo
11-
import org.schabi.newpipe.ktx.serializable
1212
import org.schabi.newpipe.ui.components.video.RelatedItems
1313
import org.schabi.newpipe.ui.theme.AppTheme
1414
import org.schabi.newpipe.util.KEY_INFO
@@ -21,7 +21,7 @@ class RelatedItemsFragment : Fragment() {
2121
) = content {
2222
AppTheme {
2323
Surface {
24-
RelatedItems(requireArguments().serializable<StreamInfo>(KEY_INFO)!!)
24+
RelatedItems(viewModel(requireParentFragment()))
2525
}
2626
}
2727
}

app/src/main/java/org/schabi/newpipe/ui/components/video/RelatedItems.kt

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,35 @@ import androidx.compose.ui.res.stringResource
2121
import androidx.compose.ui.tooling.preview.Preview
2222
import androidx.compose.ui.unit.dp
2323
import androidx.core.content.edit
24+
import androidx.lifecycle.compose.collectAsStateWithLifecycle
2425
import androidx.preference.PreferenceManager
2526
import org.schabi.newpipe.R
2627
import org.schabi.newpipe.extractor.stream.StreamInfo
2728
import org.schabi.newpipe.extractor.stream.StreamType
2829
import org.schabi.newpipe.info_list.ItemViewMode
30+
import org.schabi.newpipe.ui.components.common.LoadingIndicator
2931
import org.schabi.newpipe.ui.components.items.ItemList
3032
import org.schabi.newpipe.ui.components.items.stream.StreamInfoItem
3133
import org.schabi.newpipe.ui.emptystate.EmptyStateComposable
3234
import org.schabi.newpipe.ui.emptystate.EmptyStateSpec
3335
import org.schabi.newpipe.ui.theme.AppTheme
3436
import org.schabi.newpipe.util.NO_SERVICE_ID
37+
import org.schabi.newpipe.viewmodels.VideoDetailViewModel
38+
import org.schabi.newpipe.viewmodels.util.Resource
3539

3640
@Composable
37-
fun RelatedItems(info: StreamInfo) {
41+
fun RelatedItems(viewModel: VideoDetailViewModel) {
42+
val streamState by viewModel.streamState.collectAsStateWithLifecycle()
43+
44+
when (val state = streamState) {
45+
is Resource.Loading -> LoadingIndicator()
46+
is Resource.Success -> RelatedItems(state.data)
47+
is Resource.Error -> TODO()
48+
}
49+
}
50+
51+
@Composable
52+
private fun RelatedItems(info: StreamInfo) {
3853
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(LocalContext.current)
3954
val key = stringResource(R.string.auto_queue_key)
4055
// TODO: AndroidX DataStore might be a better option.

app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import androidx.compose.ui.res.stringResource
1919
import androidx.compose.ui.tooling.preview.Preview
2020
import androidx.compose.ui.unit.dp
2121
import androidx.lifecycle.compose.collectAsStateWithLifecycle
22-
import androidx.lifecycle.viewmodel.compose.viewModel
2322
import androidx.paging.LoadState
2423
import androidx.paging.PagingData
2524
import androidx.paging.compose.collectAsLazyPagingItems
@@ -35,18 +34,21 @@ import org.schabi.newpipe.ui.emptystate.EmptyStateComposable
3534
import org.schabi.newpipe.ui.emptystate.EmptyStateSpec
3635
import org.schabi.newpipe.ui.theme.AppTheme
3736
import org.schabi.newpipe.util.image.ImageStrategy
38-
import org.schabi.newpipe.viewmodels.CommentsViewModel
37+
import org.schabi.newpipe.viewmodels.VideoDetailViewModel
3938
import org.schabi.newpipe.viewmodels.util.Resource
4039

4140
@Composable
42-
fun CommentSection(commentsViewModel: CommentsViewModel = viewModel()) {
43-
val streamState by commentsViewModel.streamState.collectAsStateWithLifecycle()
44-
val commentState by commentsViewModel.commentState.collectAsStateWithLifecycle()
45-
41+
fun CommentSection(videoDetailViewModel: VideoDetailViewModel) {
42+
val streamState by videoDetailViewModel.streamState.collectAsStateWithLifecycle()
43+
val commentState by videoDetailViewModel.commentState.collectAsStateWithLifecycle()
4644
val avatars = (streamState as? Resource.Success)?.data?.uploaderAvatars.orEmpty()
4745
val uploaderAvatarUrl = ImageStrategy.choosePreferredImage(avatars)
4846

49-
CommentSection(commentState, uploaderAvatarUrl, commentsViewModel.comments)
47+
CommentSection(
48+
commentState,
49+
uploaderAvatarUrl,
50+
videoDetailViewModel.comments,
51+
)
5052
}
5153

5254
@Composable

app/src/main/java/org/schabi/newpipe/util/DeviceUtils.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import static android.content.Context.INPUT_SERVICE;
44

55
import android.annotation.SuppressLint;
6+
import android.app.Activity;
67
import android.app.UiModeManager;
78
import android.content.Context;
89
import android.content.pm.PackageManager;
@@ -21,7 +22,6 @@
2122

2223
import androidx.annotation.Dimension;
2324
import androidx.annotation.NonNull;
24-
import androidx.appcompat.app.AppCompatActivity;
2525
import androidx.core.content.ContextCompat;
2626
import androidx.preference.PreferenceManager;
2727

@@ -286,7 +286,7 @@ public static boolean isLandscape(final Context context) {
286286
.getDisplayMetrics().widthPixels;
287287
}
288288

289-
public static boolean isInMultiWindow(final AppCompatActivity activity) {
289+
public static boolean isInMultiWindow(final Activity activity) {
290290
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && activity.isInMultiWindowMode();
291291
}
292292

app/src/main/java/org/schabi/newpipe/viewmodels/CommentsViewModel.kt

Lines changed: 0 additions & 62 deletions
This file was deleted.
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package org.schabi.newpipe.viewmodels
2+
3+
import android.app.Application
4+
import androidx.lifecycle.AndroidViewModel
5+
import androidx.lifecycle.SavedStateHandle
6+
import androidx.lifecycle.viewModelScope
7+
import androidx.paging.Pager
8+
import androidx.paging.PagingConfig
9+
import androidx.paging.cachedIn
10+
import kotlinx.coroutines.Dispatchers
11+
import kotlinx.coroutines.ExperimentalCoroutinesApi
12+
import kotlinx.coroutines.async
13+
import kotlinx.coroutines.flow.MutableStateFlow
14+
import kotlinx.coroutines.flow.asStateFlow
15+
import kotlinx.coroutines.flow.filterIsInstance
16+
import kotlinx.coroutines.flow.flatMapLatest
17+
import kotlinx.coroutines.launch
18+
import kotlinx.coroutines.rx3.await
19+
import org.schabi.newpipe.extractor.comments.CommentsInfo
20+
import org.schabi.newpipe.extractor.stream.StreamInfo
21+
import org.schabi.newpipe.paging.CommentsSource
22+
import org.schabi.newpipe.player.playqueue.PlayQueue
23+
import org.schabi.newpipe.ui.components.video.comment.CommentInfo
24+
import org.schabi.newpipe.util.ExtractorHelper
25+
import org.schabi.newpipe.util.KEY_SERVICE_ID
26+
import org.schabi.newpipe.util.KEY_TITLE
27+
import org.schabi.newpipe.util.KEY_URL
28+
import org.schabi.newpipe.util.NO_SERVICE_ID
29+
import org.schabi.newpipe.viewmodels.util.Resource
30+
31+
class VideoDetailViewModel(
32+
application: Application,
33+
private val savedStateHandle: SavedStateHandle,
34+
) : AndroidViewModel(application) {
35+
val url get() = savedStateHandle[KEY_URL] ?: ""
36+
val title get() = savedStateHandle[KEY_TITLE] ?: ""
37+
val serviceId get() = savedStateHandle[KEY_SERVICE_ID] ?: NO_SERVICE_ID
38+
39+
var addToBackStack = false
40+
41+
var playQueue: PlayQueue?
42+
get() = savedStateHandle[KEY_PLAY_QUEUE]
43+
set(value) = savedStateHandle.set(KEY_PLAY_QUEUE, value)
44+
45+
private val _streamState = MutableStateFlow<Resource<StreamInfo>>(Resource.Loading)
46+
val streamState = _streamState.asStateFlow()
47+
48+
private val _commentState = MutableStateFlow<Resource<CommentInfo>>(Resource.Loading)
49+
val commentState = _commentState.asStateFlow()
50+
51+
@OptIn(ExperimentalCoroutinesApi::class)
52+
val comments = _commentState
53+
.filterIsInstance<Resource.Success<CommentInfo>>()
54+
.flatMapLatest {
55+
Pager(PagingConfig(pageSize = 20, enablePlaceholders = false)) {
56+
CommentsSource(it.data)
57+
}.flow
58+
}
59+
.cachedIn(viewModelScope)
60+
61+
init {
62+
startLoading()
63+
}
64+
65+
fun startLoading(forceLoad: Boolean = true) {
66+
_streamState.value = Resource.Loading
67+
_commentState.value = Resource.Loading
68+
69+
viewModelScope.launch(Dispatchers.IO) {
70+
val url = url
71+
val streamInfoTask = async {
72+
if (url.isNotEmpty()) {
73+
Resource.Success(ExtractorHelper.getStreamInfo(serviceId, url, forceLoad).await())
74+
} else {
75+
Resource.Loading
76+
}
77+
}
78+
val commentInfoTask = async {
79+
if (url.isNotEmpty()) {
80+
Resource.Success(CommentInfo(CommentsInfo.getInfo(url)))
81+
} else {
82+
Resource.Loading
83+
}
84+
}
85+
86+
_streamState.value = streamInfoTask.await()
87+
_commentState.value = commentInfoTask.await()
88+
}
89+
}
90+
91+
fun updateStreamState(info: StreamInfo) {
92+
_streamState.value = Resource.Success(info)
93+
}
94+
95+
fun updateData(serviceId: Int, url: String?, title: String, playQueue: PlayQueue?) {
96+
savedStateHandle[KEY_URL] = url
97+
savedStateHandle[KEY_SERVICE_ID] = serviceId
98+
savedStateHandle[KEY_TITLE] = title
99+
savedStateHandle[KEY_PLAY_QUEUE] = playQueue
100+
}
101+
102+
companion object {
103+
const val KEY_PLAY_QUEUE = "playQueue"
104+
}
105+
}

0 commit comments

Comments
 (0)