Fix formatting with new ktlint rules

This commit is contained in:
Stypox 2026-02-02 20:28:20 +01:00
parent 8f19f95fee
commit 70c502d31f
No known key found for this signature in database
GPG Key ID: 4BDF1B40A49FDD23
20 changed files with 165 additions and 151 deletions

View File

@ -40,5 +40,5 @@ enum class UserAction(val message: String) {
GETTING_MAIN_SCREEN_TAB("getting main screen tab"),
PLAY_ON_POPUP("play on popup"),
SUBSCRIPTIONS("loading subscriptions"),
LONG_PRESS_MENU_ACTION("long press menu action"),
LONG_PRESS_MENU_ACTION("long press menu action")
}

View File

@ -1425,7 +1425,10 @@ class VideoDetailFragment :
if (info.viewCount >= 0) {
binding.detailViewCountView.text = Localization.localizeViewCount(
activity, false, info.streamType, info.viewCount
activity,
false,
info.streamType,
info.viewCount
)
binding.detailViewCountView.visibility = View.VISIBLE
} else {

View File

@ -8,5 +8,4 @@ package org.schabi.newpipe.ktx
* .letIf(someCondition) { padding(right = 4.dp) }
* ```
*/
inline fun <T> T.letIf(condition: Boolean, block: T.() -> T): T =
if (condition) block(this) else this
inline fun <T> T.letIf(condition: Boolean, block: T.() -> T): T = if (condition) block(this) else this

View File

@ -425,8 +425,8 @@ class FeedFragment : BaseStateFragment<FeedState>() {
// just in case
Log.w(TAG, "Could not get full list of items on long press")
return@fromStreamEntity SinglePlayQueue(item.stream.toStreamInfoItem())
},
),
}
)
)
return true
}

View File

@ -79,7 +79,7 @@ fun Modifier.detectDragGestures(
}
handleDragGestureChange(
change.position.toIntOffset(),
change.positionChange(),
change.positionChange()
)
change.consume()
}

View File

@ -39,7 +39,7 @@ fun NavigationIcon() {
Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = stringResource(R.string.back),
modifier = Modifier.padding(horizontal = SizeTokens.SpacingExtraSmall),
modifier = Modifier.padding(horizontal = SizeTokens.SpacingExtraSmall)
)
}

View File

@ -42,7 +42,7 @@ fun ScaffoldWithToolbar(
IconButton(onClick = onBackClick) {
Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = stringResource(R.string.back),
contentDescription = stringResource(R.string.back)
)
}
},

View File

@ -36,7 +36,7 @@ import org.schabi.newpipe.ui.theme.AppTheme
fun StreamListItem(
stream: StreamInfoItem,
showProgress: Boolean,
onClick: (StreamInfoItem) -> Unit = {},
onClick: (StreamInfoItem) -> Unit = {}
) {
var showLongPressMenu by rememberSaveable { mutableStateOf(false) }
@ -81,7 +81,7 @@ fun StreamListItem(
longPressable = LongPressable.fromStreamInfoItem(stream),
// TODO queueFromHere: allow playing the whole list starting from one stream
longPressActions = LongPressAction.fromStreamInfoItem(stream, null),
onDismissRequest = { showLongPressMenu = false },
onDismissRequest = { showLongPressMenu = false }
)
}
}

View File

@ -60,7 +60,7 @@ data class LongPressAction(
val type: Type,
@MainThread
val action: suspend (context: Context) -> Unit,
val enabled: (isPlayerRunning: Boolean) -> Boolean = { true },
val enabled: (isPlayerRunning: Boolean) -> Boolean = { true }
) {
enum class Type(
/**
@ -69,7 +69,7 @@ data class LongPressAction(
*/
val id: Int,
@StringRes val label: Int,
val icon: ImageVector,
val icon: ImageVector
) {
Enqueue(0, R.string.enqueue, Icons.Default.AddToQueue),
EnqueueNext(1, R.string.enqueue_next_stream, Icons.Default.QueuePlayNext),
@ -92,7 +92,7 @@ data class LongPressAction(
UnsetPlaylistThumbnail(18, R.string.unset_playlist_thumbnail, Icons.Default.HideImage),
Unsubscribe(19, R.string.unsubscribe, Icons.Default.Delete),
ShowDetails(20, R.string.play_queue_stream_detail, Icons.Default.Info),
Remove(21, R.string.play_queue_remove, Icons.Default.Delete),
Remove(21, R.string.play_queue_remove, Icons.Default.Delete)
;
// TODO allow actions to return disposables
@ -100,7 +100,7 @@ data class LongPressAction(
fun buildAction(
enabled: (isPlayerRunning: Boolean) -> Boolean = { true },
action: suspend (context: Context) -> Unit,
action: suspend (context: Context) -> Unit
) = LongPressAction(this, action, enabled)
companion object {
@ -109,7 +109,7 @@ data class LongPressAction(
val DefaultEnabledActions: List<Type> = listOf(
ShowDetails, Enqueue, EnqueueNext, Background, Popup, BackgroundFromHere, Download,
AddToPlaylist, Share, OpenInBrowser, MarkAsWatched, Delete,
Rename, SetAsPlaylistThumbnail, UnsetPlaylistThumbnail, Unsubscribe, Remove,
Rename, SetAsPlaylistThumbnail, UnsetPlaylistThumbnail, Unsubscribe, Remove
)
}
}
@ -133,7 +133,7 @@ data class LongPressAction(
},
Type.Play.buildAction { context ->
NavigationHelper.playOnMainPlayer(context, queue(context), false)
},
}
)
}
@ -147,7 +147,7 @@ data class LongPressAction(
},
Type.PlayFromHere.buildAction { context ->
NavigationHelper.playOnMainPlayer(context, queueFromHere(), false)
},
}
)
}
@ -158,7 +158,7 @@ data class LongPressAction(
},
Type.OpenInBrowser.buildAction { context ->
ShareUtils.openUrlInBrowser(context, item.url)
},
}
)
}
@ -169,7 +169,7 @@ data class LongPressAction(
},
Type.OpenInBrowser.buildAction { context ->
ShareUtils.openUrlInBrowser(context, url)
},
}
)
}
@ -200,13 +200,16 @@ data class LongPressAction(
},
Type.ShowChannelDetails.buildAction { context ->
val uploaderUrl = fetchUploaderUrlIfSparse(
context, item.serviceId, item.url, item.uploaderUrl
context,
item.serviceId,
item.url,
item.uploaderUrl
)
NavigationHelper.openChannelFragment(
context.findFragmentActivity().supportFragmentManager,
item.serviceId,
uploaderUrl,
item.uploaderName,
item.uploaderName
)
},
Type.MarkAsWatched.buildAction { context ->
@ -216,7 +219,7 @@ data class LongPressAction(
},
Type.PlayWithKodi.buildAction { context ->
KoreUtils.playWithKore(context, item.url.toUri())
},
}
)
}
@ -227,7 +230,7 @@ data class LongPressAction(
@JvmStatic
fun fromStreamInfoItem(
item: StreamInfoItem,
queueFromHere: (() -> PlayQueue)?,
queueFromHere: (() -> PlayQueue)?
/* TODO isKodiEnabled: Boolean, */
): List<LongPressAction> {
return buildPlayerActionList { context -> fetchItemInfoIfSparse(context, item) } +
@ -239,7 +242,7 @@ data class LongPressAction(
@JvmStatic
fun fromStreamEntity(
item: StreamEntity,
queueFromHere: (() -> PlayQueue)?,
queueFromHere: (() -> PlayQueue)?
): List<LongPressAction> {
// TODO decide if it's fine to just convert to StreamInfoItem here (it poses an
// unnecessary dependency on the extractor, when we want to just look at data; maybe
@ -251,38 +254,43 @@ data class LongPressAction(
fun fromPlayQueueItem(
item: PlayQueueItem,
playQueueFromWhichToDelete: PlayQueue,
showDetails: Boolean,
showDetails: Boolean
): List<LongPressAction> {
// TODO decide if it's fine to just convert to StreamInfoItem here (it poses an
// unnecessary dependency on the extractor, when we want to just look at data; maybe
// using something like LongPressable would work)
val streamInfoItem = item.toStreamInfoItem()
return buildShareActionList(streamInfoItem) +
buildAdditionalStreamActionList(streamInfoItem) +
if (showDetails) {
listOf(
Type.ShowDetails.buildAction { context ->
// playQueue is null since we don't want any queue change
NavigationHelper.openVideoDetail(
context, item.serviceId, item.url, item.title, null, false
)
}
)
} else {
listOf()
} +
buildAdditionalStreamActionList(streamInfoItem) +
if (showDetails) {
listOf(
Type.Remove.buildAction {
val index = playQueueFromWhichToDelete.indexOf(item)
playQueueFromWhichToDelete.remove(index)
Type.ShowDetails.buildAction { context ->
// playQueue is null since we don't want any queue change
NavigationHelper.openVideoDetail(
context,
item.serviceId,
item.url,
item.title,
null,
false
)
}
)
} else {
listOf()
} +
listOf(
Type.Remove.buildAction {
val index = playQueueFromWhichToDelete.indexOf(item)
playQueueFromWhichToDelete.remove(index)
}
)
}
@JvmStatic
fun fromStreamStatisticsEntry(
item: StreamStatisticsEntry,
queueFromHere: (() -> PlayQueue)?,
queueFromHere: (() -> PlayQueue)?
): List<LongPressAction> {
return fromStreamEntity(item.streamEntity, queueFromHere) +
listOf(
@ -304,7 +312,7 @@ data class LongPressAction(
queueFromHere: (() -> PlayQueue)?,
// TODO possibly embed these two actions here
onDelete: Runnable,
onSetAsPlaylistThumbnail: Runnable,
onSetAsPlaylistThumbnail: Runnable
): List<LongPressAction> {
return fromStreamEntity(item.streamEntity, queueFromHere) +
listOf(
@ -318,7 +326,7 @@ data class LongPressAction(
item: PlaylistMetadataEntry,
onRename: Runnable,
onDelete: Runnable,
unsetPlaylistThumbnail: Runnable?,
unsetPlaylistThumbnail: Runnable?
): List<LongPressAction> {
return listOf(
Type.Rename.buildAction { onRename.run() },
@ -332,7 +340,7 @@ data class LongPressAction(
@JvmStatic
fun fromPlaylistRemoteEntity(
item: PlaylistRemoteEntity,
onDelete: Runnable,
onDelete: Runnable
): List<LongPressAction> {
return buildPlayerActionList { PlaylistPlayQueue(item.serviceId, item.url) } +
buildShareActionList(
@ -341,14 +349,14 @@ data class LongPressAction(
item.thumbnailUrl
) +
listOf(
Type.Delete.buildAction { onDelete.run() },
Type.Delete.buildAction { onDelete.run() }
)
}
@JvmStatic
fun fromChannelInfoItem(
item: ChannelInfoItem,
onUnsubscribe: Runnable?,
onUnsubscribe: Runnable?
): List<LongPressAction> {
return buildPlayerActionList { ChannelTabPlayQueue(item.serviceId, item.url) } +
buildShareActionList(item) +
@ -358,7 +366,7 @@ data class LongPressAction(
context.findFragmentActivity().supportFragmentManager,
item.serviceId,
item.url,
item.name,
item.name
)
},
onUnsubscribe?.let { r -> Type.Unsubscribe.buildAction { r.run() } }

View File

@ -79,6 +79,7 @@ import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import androidx.lifecycle.viewmodel.compose.viewModel
import coil3.compose.AsyncImage
import java.time.OffsetDateTime
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@ -97,12 +98,11 @@ import org.schabi.newpipe.util.Either
import org.schabi.newpipe.util.Localization
import org.schabi.newpipe.util.text.FixedHeightCenteredText
import org.schabi.newpipe.util.text.fadedMarquee
import java.time.OffsetDateTime
fun openLongPressMenuInActivity(
activity: Activity,
longPressable: LongPressable,
longPressActions: List<LongPressAction>,
longPressActions: List<LongPressAction>
) {
activity.addContentView(
getLongPressMenuView(activity, longPressable, longPressActions),
@ -113,7 +113,7 @@ fun openLongPressMenuInActivity(
fun getLongPressMenuView(
context: Context,
longPressable: LongPressable,
longPressActions: List<LongPressAction>,
longPressActions: List<LongPressAction>
): ComposeView {
return ComposeView(context).apply {
setContent {
@ -121,7 +121,7 @@ fun getLongPressMenuView(
LongPressMenu(
longPressable = longPressable,
longPressActions = longPressActions,
onDismissRequest = { (this.parent as ViewGroup).removeView(this) },
onDismissRequest = { (this.parent as ViewGroup).removeView(this) }
)
}
}
@ -135,7 +135,7 @@ internal val ThumbnailHeight = 60.dp
fun LongPressMenu(
longPressable: LongPressable,
longPressActions: List<LongPressAction>,
onDismissRequest: () -> Unit,
onDismissRequest: () -> Unit
) {
val viewModel: LongPressMenuViewModel = viewModel()
val isHeaderEnabled by viewModel.isHeaderEnabled.collectAsState()
@ -152,7 +152,7 @@ fun LongPressMenu(
) {
ScaffoldWithToolbar(
title = stringResource(R.string.long_press_menu_actions_editor),
onBackClick = { showEditor = false },
onBackClick = { showEditor = false }
) { paddingValues ->
LongPressMenuEditor(modifier = Modifier.padding(paddingValues))
}
@ -179,7 +179,8 @@ fun LongPressMenu(
action.action(ctx)
} catch (t: Throwable) {
ErrorUtil.showSnackbar(
ctx, ErrorInfo(t, LONG_PRESS_MENU_ACTION, "Running action ${action.type}")
ctx,
ErrorInfo(t, LONG_PRESS_MENU_ACTION, "Running action ${action.type}")
)
}
onDismissRequest()
@ -199,14 +200,14 @@ fun LongPressMenu(
ModalBottomSheet(
sheetState = sheetState,
onDismissRequest = onDismissRequest,
dragHandle = { LongPressMenuDragHandle(onEditActions = { showEditor = true }) },
dragHandle = { LongPressMenuDragHandle(onEditActions = { showEditor = true }) }
) {
Box(modifier = Modifier.discardAllTouchesIf(isLoading)) {
LongPressMenuContent(
header = longPressable.takeIf { isHeaderEnabled },
onUploaderClick = onUploaderClick,
actions = enabledLongPressActions,
runActionAndDismiss = ::runActionAndDismiss,
runActionAndDismiss = ::runActionAndDismiss
)
// importing makes the ColumnScope overload be resolved, so we use qualified path...
androidx.compose.animation.AnimatedVisibility(
@ -215,7 +216,7 @@ fun LongPressMenu(
exit = fadeOut(),
modifier = Modifier
.matchParentSize()
.background(MaterialTheme.colorScheme.surfaceContainerLow),
.background(MaterialTheme.colorScheme.surfaceContainerLow)
) {
Box(contentAlignment = Alignment.Center) {
CircularProgressIndicator()
@ -231,7 +232,7 @@ private fun LongPressMenuContent(
header: LongPressable?,
onUploaderClick: (() -> Unit)?,
actions: List<LongPressAction>,
runActionAndDismiss: (LongPressAction) -> Unit,
runActionAndDismiss: (LongPressAction) -> Unit
) {
BoxWithConstraints(
modifier = Modifier
@ -251,7 +252,7 @@ private fun LongPressMenuContent(
while (actionIndex < actions.size) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth(),
modifier = Modifier.fillMaxWidth()
) {
var rowIndex = 0
while (rowIndex < buttonsPerRow) {
@ -263,7 +264,7 @@ private fun LongPressMenuContent(
modifier = Modifier
.height(buttonHeight)
.fillMaxWidth()
.weight((buttonsPerRow - rowIndex).toFloat()),
.weight((buttonsPerRow - rowIndex).toFloat())
)
break
} else if (actionIndex >= 0) {
@ -276,7 +277,7 @@ private fun LongPressMenuContent(
modifier = Modifier
.height(buttonHeight)
.fillMaxWidth()
.weight(1F),
.weight(1F)
)
rowIndex += 1
} else if (maxHeaderWidthInButtonsFullSpan >= buttonsPerRow) {
@ -290,7 +291,7 @@ private fun LongPressMenuContent(
// leave the height as small as possible, since it's the
// only item on the row anyway
.fillMaxWidth()
.weight(maxHeaderWidthInButtonsFullSpan.toFloat()),
.weight(maxHeaderWidthInButtonsFullSpan.toFloat())
)
rowIndex += maxHeaderWidthInButtonsFullSpan
} else {
@ -306,7 +307,7 @@ private fun LongPressMenuContent(
.padding(start = 8.dp, top = 11.dp, bottom = 11.dp)
.heightIn(min = ThumbnailHeight)
.fillMaxWidth()
.weight(headerWidthInButtonsReducedSpan.toFloat()),
.weight(headerWidthInButtonsReducedSpan.toFloat())
)
rowIndex += headerWidthInButtonsReducedSpan
}
@ -357,7 +358,7 @@ fun LongPressMenuDragHandle(onEditActions: () -> Unit) {
tint = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier
.padding(2.dp)
.size(16.dp),
.size(16.dp)
)
}
}
@ -378,7 +379,7 @@ private fun LongPressMenuDragHandlePreview() {
fun LongPressMenuHeader(
item: LongPressable,
onUploaderClick: (() -> Unit)?,
modifier: Modifier = Modifier,
modifier: Modifier = Modifier
) {
val ctx = LocalContext.current
@ -386,7 +387,7 @@ fun LongPressMenuHeader(
color = MaterialTheme.colorScheme.surfaceContainer,
contentColor = MaterialTheme.colorScheme.onSurfaceVariant,
shape = MaterialTheme.shapes.large,
modifier = modifier,
modifier = modifier
) {
Row(verticalAlignment = Alignment.CenterVertically) {
Box {
@ -413,12 +414,12 @@ fun LongPressMenuHeader(
modifier = Modifier
.align(Alignment.BottomEnd)
.padding(4.dp)
.clip(MaterialTheme.shapes.medium),
.clip(MaterialTheme.shapes.medium)
) {
Text(
text = Localization.getDurationString(decoration.duration),
style = MaterialTheme.typography.bodySmall,
modifier = Modifier.padding(vertical = 2.dp, horizontal = 4.dp),
modifier = Modifier.padding(vertical = 2.dp, horizontal = 4.dp)
)
}
}
@ -433,7 +434,7 @@ fun LongPressMenuHeader(
modifier = Modifier
.align(Alignment.BottomEnd)
.padding(4.dp)
.clip(MaterialTheme.shapes.medium),
.clip(MaterialTheme.shapes.medium)
) {
Text(
text = stringResource(R.string.duration_live).uppercase(),
@ -451,16 +452,16 @@ fun LongPressMenuHeader(
modifier = Modifier
.align(Alignment.TopEnd)
.size(width = 40.dp, height = ThumbnailHeight)
.clip(MaterialTheme.shapes.large),
.clip(MaterialTheme.shapes.large)
) {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxWidth(),
modifier = Modifier.fillMaxWidth()
) {
Icon(
Icons.AutoMirrored.Default.PlaylistPlay,
contentDescription = null,
contentDescription = null
)
Text(
text = Localization.localizeStreamCountMini(
@ -468,7 +469,7 @@ fun LongPressMenuHeader(
decoration.itemCount
),
style = MaterialTheme.typography.labelMedium,
maxLines = 1,
maxLines = 1
)
}
}
@ -479,7 +480,7 @@ fun LongPressMenuHeader(
}
Column(
modifier = Modifier.padding(vertical = 8.dp),
modifier = Modifier.padding(vertical = 8.dp)
) {
Text(
text = item.title,
@ -487,14 +488,14 @@ fun LongPressMenuHeader(
maxLines = 1,
modifier = Modifier
.fillMaxWidth()
.fadedMarquee(edgeWidth = 12.dp),
.fadedMarquee(edgeWidth = 12.dp)
)
val subtitle = getSubtitleAnnotatedString(
item = item,
showLink = onUploaderClick != null,
linkColor = MaterialTheme.customColors.onSurfaceVariantLink,
ctx = ctx,
ctx = ctx
)
if (subtitle.isNotBlank()) {
Spacer(Modifier.height(1.dp))
@ -509,7 +510,7 @@ fun LongPressMenuHeader(
Modifier.clickable(onClick = onUploaderClick)
}
.fillMaxWidth()
.fadedMarquee(edgeWidth = 12.dp),
.fadedMarquee(edgeWidth = 12.dp)
)
}
}
@ -521,7 +522,7 @@ fun getSubtitleAnnotatedString(
item: LongPressable,
showLink: Boolean,
linkColor: Color,
ctx: Context,
ctx: Context
) = buildAnnotatedString {
var shouldAddSeparator = false
if (showLink) {
@ -574,13 +575,13 @@ fun getSubtitleInlineContent() = mapOf(
placeholder = Placeholder(
width = MaterialTheme.typography.bodyMedium.fontSize,
height = MaterialTheme.typography.bodyMedium.fontSize,
placeholderVerticalAlign = PlaceholderVerticalAlign.Center,
placeholderVerticalAlign = PlaceholderVerticalAlign.Center
)
) {
Icon(
imageVector = Icons.AutoMirrored.Filled.OpenInNew,
contentDescription = null,
tint = MaterialTheme.customColors.onSurfaceVariantLink,
tint = MaterialTheme.customColors.onSurfaceVariantLink
)
}
)
@ -591,7 +592,7 @@ fun LongPressMenuButton(
text: String,
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
enabled: Boolean = true
) {
// TODO possibly make it so that when you long-press on the button, the label appears on-screen
// as a small popup, so in case the label text is cut off the users can still read it in full
@ -601,18 +602,18 @@ fun LongPressMenuButton(
shape = MaterialTheme.shapes.large,
contentPadding = PaddingValues(start = 3.dp, top = 8.dp, end = 3.dp, bottom = 2.dp),
border = null,
modifier = modifier,
modifier = modifier
) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Icon(
imageVector = icon,
contentDescription = null,
modifier = Modifier.size(32.dp),
modifier = Modifier.size(32.dp)
)
FixedHeightCenteredText(
text = text,
lines = 2,
style = MaterialTheme.typography.bodySmall,
style = MaterialTheme.typography.bodySmall
)
}
}
@ -650,7 +651,7 @@ private class LongPressablePreviews : CollectionPreviewParameterProvider<LongPre
viewCount = 8765432,
streamType = null,
uploadDate = Either.left("16 years ago"),
decoration = LongPressable.Decoration.Playlist(12),
decoration = LongPressable.Decoration.Playlist(12)
),
LongPressable(
title = LoremIpsum().values.first(),
@ -661,7 +662,7 @@ private class LongPressablePreviews : CollectionPreviewParameterProvider<LongPre
viewCount = 8765432,
streamType = StreamType.VIDEO_STREAM,
uploadDate = Either.left("16 years ago"),
decoration = LongPressable.Decoration.Duration(500),
decoration = LongPressable.Decoration.Duration(500)
),
LongPressable(
title = LoremIpsum().values.first(),
@ -672,7 +673,7 @@ private class LongPressablePreviews : CollectionPreviewParameterProvider<LongPre
viewCount = null,
streamType = null,
uploadDate = null,
decoration = null,
decoration = null
),
LongPressable(
title = LoremIpsum().values.first(),
@ -683,7 +684,7 @@ private class LongPressablePreviews : CollectionPreviewParameterProvider<LongPre
viewCount = null,
streamType = StreamType.AUDIO_STREAM,
uploadDate = null,
decoration = LongPressable.Decoration.Duration(500),
decoration = LongPressable.Decoration.Duration(500)
),
LongPressable(
title = LoremIpsum().values.first(),
@ -694,7 +695,7 @@ private class LongPressablePreviews : CollectionPreviewParameterProvider<LongPre
viewCount = null,
streamType = StreamType.LIVE_STREAM,
uploadDate = null,
decoration = LongPressable.Decoration.Live,
decoration = LongPressable.Decoration.Live
),
LongPressable(
title = LoremIpsum().values.first(),
@ -705,8 +706,8 @@ private class LongPressablePreviews : CollectionPreviewParameterProvider<LongPre
viewCount = null,
streamType = StreamType.AUDIO_LIVE_STREAM,
uploadDate = Either.right(OffsetDateTime.now().minusSeconds(12)),
decoration = LongPressable.Decoration.Playlist(1500),
),
decoration = LongPressable.Decoration.Playlist(1500)
)
)
)
@ -736,7 +737,7 @@ private fun LongPressMenuPreview(
actions = LongPressAction.Type.entries
// disable Enqueue actions just to show it off
.map { t -> t.buildAction({ t != EnqueueNext }) { } },
runActionAndDismiss = {},
runActionAndDismiss = {}
)
}
}

View File

@ -64,12 +64,12 @@ import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameter
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.toSize
import kotlin.math.floor
import org.schabi.newpipe.R
import org.schabi.newpipe.ktx.letIf
import org.schabi.newpipe.ui.detectDragGestures
import org.schabi.newpipe.ui.theme.AppTheme
import org.schabi.newpipe.util.text.FixedHeightCenteredText
import kotlin.math.floor
/**
* When making changes to this composable and to [LongPressMenuEditorState], make sure to test the
@ -112,7 +112,7 @@ fun LongPressMenuEditor(modifier: Modifier = Modifier) {
.detectDragGestures(
beginDragGesture = state::beginDragGesture,
handleDragGestureChange = state::handleDragGestureChange,
endDragGesture = state::completeDragGestureAndCleanUp,
endDragGesture = state::completeDragGestureAndCleanUp
)
// `.focusTarget().onKeyEvent()` handles DPAD on Android TVs
.focusTarget()
@ -120,12 +120,12 @@ fun LongPressMenuEditor(modifier: Modifier = Modifier) {
// same width as the LongPressMenu
columns = GridCells.Adaptive(MinButtonWidth),
userScrollEnabled = false,
state = gridState,
state = gridState
) {
itemsIndexed(
state.items,
key = { _, item -> item.stableUniqueKey() },
span = { _, item -> GridItemSpan(item.columnSpan ?: maxLineSpan) },
span = { _, item -> GridItemSpan(item.columnSpan ?: maxLineSpan) }
) { i, item ->
ItemInListUi(
item = item,
@ -151,7 +151,7 @@ fun LongPressMenuEditor(modifier: Modifier = Modifier) {
.size(size)
.offset { state.activeDragPosition }
.offset(-size.width / 2, -size.height / 2)
.offset((-24).dp, (-24).dp),
.offset((-24).dp, (-24).dp)
)
}
}
@ -162,7 +162,7 @@ private fun Subheader(
selected: Boolean,
@StringRes title: Int,
@StringRes description: Int,
modifier: Modifier = Modifier,
modifier: Modifier = Modifier
) {
Column(
modifier = modifier
@ -177,7 +177,7 @@ private fun Subheader(
Text(
text = stringResource(description),
fontStyle = FontStyle.Italic,
style = MaterialTheme.typography.bodyMedium,
style = MaterialTheme.typography.bodyMedium
)
}
}
@ -190,7 +190,7 @@ private fun ActionOrHeaderBox(
contentColor: Color,
modifier: Modifier = Modifier,
backgroundColor: Color = Color.Transparent,
horizontalPadding: Dp = 3.dp,
horizontalPadding: Dp = 3.dp
) {
Surface(
color = backgroundColor,
@ -199,19 +199,19 @@ private fun ActionOrHeaderBox(
border = BorderStroke(2.dp, contentColor.copy(alpha = 1f)).takeIf { selected },
modifier = modifier.padding(
horizontal = horizontalPadding,
vertical = 5.dp,
),
vertical = 5.dp
)
) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Icon(
imageVector = icon,
contentDescription = null,
modifier = Modifier.size(32.dp),
modifier = Modifier.size(32.dp)
)
FixedHeightCenteredText(
text = stringResource(text),
lines = 2,
style = MaterialTheme.typography.bodySmall,
style = MaterialTheme.typography.bodySmall
)
}
}
@ -221,7 +221,7 @@ private fun ActionOrHeaderBox(
private fun ItemInListUi(
item: ItemInList,
selected: Boolean,
modifier: Modifier = Modifier,
modifier: Modifier = Modifier
) {
when (item) {
ItemInList.EnabledCaption -> {
@ -229,26 +229,29 @@ private fun ItemInListUi(
modifier = modifier,
selected = selected,
title = R.string.long_press_menu_enabled_actions,
description = R.string.long_press_menu_enabled_actions_description,
description = R.string.long_press_menu_enabled_actions_description
)
}
ItemInList.HiddenCaption -> {
Subheader(
modifier = modifier,
selected = selected,
title = R.string.long_press_menu_hidden_actions,
description = R.string.long_press_menu_hidden_actions_description,
description = R.string.long_press_menu_hidden_actions_description
)
}
is ItemInList.Action -> {
ActionOrHeaderBox(
modifier = modifier,
selected = selected,
icon = item.type.icon,
text = item.type.label,
contentColor = MaterialTheme.colorScheme.onSurface,
contentColor = MaterialTheme.colorScheme.onSurface
)
}
ItemInList.HeaderBox -> {
ActionOrHeaderBox(
modifier = modifier,
@ -257,9 +260,10 @@ private fun ItemInListUi(
text = R.string.long_press_menu_header,
contentColor = MaterialTheme.colorScheme.onSurfaceVariant,
backgroundColor = MaterialTheme.colorScheme.surfaceContainer,
horizontalPadding = 12.dp,
horizontalPadding = 12.dp
)
}
ItemInList.NoneMarker -> {
ActionOrHeaderBox(
modifier = modifier,
@ -267,16 +271,17 @@ private fun ItemInListUi(
icon = Icons.Default.Close,
text = R.string.none,
// 0.38f is the same alpha that the Material3 library applies for disabled buttons
contentColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f),
contentColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f)
)
}
is ItemInList.DragMarker -> {
ActionOrHeaderBox(
modifier = modifier,
selected = selected,
icon = Icons.Default.DragHandle,
text = R.string.detail_drag_description,
contentColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.1f),
contentColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.1f)
)
}
}

View File

@ -20,14 +20,14 @@ import androidx.compose.ui.input.key.key
import androidx.compose.ui.input.key.type
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
private const val TAG = "LongPressMenuEditorStat"
@ -43,7 +43,7 @@ private const val TAG = "LongPressMenuEditorStat"
class LongPressMenuEditorState(
context: Context,
val gridState: LazyGridState,
val coroutineScope: CoroutineScope,
val coroutineScope: CoroutineScope
) {
val items = run {
// We get the current arrangement once and do not observe on purpose.
@ -99,10 +99,10 @@ class LongPressMenuEditorState(
private fun autoScrollSpeedFromTouchPos(
touchPos: IntOffset,
maxSpeed: Float = 20f,
scrollIfCloseToBorderPercent: Float = 0.2f,
scrollIfCloseToBorderPercent: Float = 0.2f
): Float {
val heightPosRatio = touchPos.y.toFloat() /
(gridState.layoutInfo.viewportEndOffset - gridState.layoutInfo.viewportStartOffset)
(gridState.layoutInfo.viewportEndOffset - gridState.layoutInfo.viewportStartOffset)
// just a linear piecewise function, sets higher speeds the closer the finger is to the border
return maxSpeed * max(
// proportionally positive speed when close to the bottom border
@ -390,11 +390,12 @@ class LongPressMenuEditorState(
sealed class ItemInList(
val isDraggable: Boolean = false,
val isCaption: Boolean = false,
open val columnSpan: Int? = 1,
// if null, then the item will occupy all of the line
open val columnSpan: Int? = 1
) {
// decoration items (i.e. text subheaders)
object EnabledCaption : ItemInList(isCaption = true, columnSpan = null /* i.e. all line */)
object HiddenCaption : ItemInList(isCaption = true, columnSpan = null /* i.e. all line */)
object EnabledCaption : ItemInList(isCaption = true, columnSpan = null) // i.e. span all line
object HiddenCaption : ItemInList(isCaption = true, columnSpan = null) // i.e. span all line
// actual draggable actions (+ a header)
object HeaderBox : ItemInList(isDraggable = true, columnSpan = 2)

View File

@ -1,6 +1,7 @@
package org.schabi.newpipe.ui.components.menu
import androidx.compose.runtime.Stable
import java.time.OffsetDateTime
import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry
import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity
import org.schabi.newpipe.database.stream.model.StreamEntity
@ -14,7 +15,6 @@ import org.schabi.newpipe.extractor.stream.StreamType.LIVE_STREAM
import org.schabi.newpipe.player.playqueue.PlayQueueItem
import org.schabi.newpipe.util.Either
import org.schabi.newpipe.util.image.ImageStrategy
import java.time.OffsetDateTime
@Stable
data class LongPressable(
@ -26,7 +26,7 @@ data class LongPressable(
val viewCount: Long?,
val streamType: StreamType?, // only used to format the view count properly
val uploadDate: Either<String, OffsetDateTime>?,
val decoration: Decoration?,
val decoration: Decoration?
) {
sealed interface Decoration {
data class Duration(val duration: Long) : Decoration
@ -34,12 +34,11 @@ data class LongPressable(
data class Playlist(val itemCount: Long) : Decoration
companion object {
internal fun from(streamType: StreamType, duration: Long) =
if (streamType == LIVE_STREAM || streamType == AUDIO_LIVE_STREAM) {
Live
} else {
duration.takeIf { it > 0 }?.let { Duration(it) }
}
internal fun from(streamType: StreamType, duration: Long) = if (streamType == LIVE_STREAM || streamType == AUDIO_LIVE_STREAM) {
Live
} else {
duration.takeIf { it > 0 }?.let { Duration(it) }
}
}
}
@ -55,7 +54,7 @@ data class LongPressable(
streamType = item.streamType,
uploadDate = item.uploadDate?.let { Either.right(it.offsetDateTime()) }
?: item.textualUploadDate?.let { Either.left(it) },
decoration = Decoration.from(item.streamType, item.duration),
decoration = Decoration.from(item.streamType, item.duration)
)
@JvmStatic
@ -69,7 +68,7 @@ data class LongPressable(
streamType = item.streamType,
uploadDate = item.uploadDate?.let { Either.right(it) }
?: item.textualUploadDate?.let { Either.left(it) },
decoration = Decoration.from(item.streamType, item.duration),
decoration = Decoration.from(item.streamType, item.duration)
)
@JvmStatic
@ -82,7 +81,7 @@ data class LongPressable(
viewCount = null,
streamType = item.streamType,
uploadDate = null,
decoration = Decoration.from(item.streamType, item.duration),
decoration = Decoration.from(item.streamType, item.duration)
)
@JvmStatic
@ -96,7 +95,7 @@ data class LongPressable(
viewCount = null,
streamType = null,
uploadDate = null,
decoration = Decoration.Playlist(item.streamCount),
decoration = Decoration.Playlist(item.streamCount)
)
@JvmStatic
@ -111,7 +110,7 @@ data class LongPressable(
uploadDate = null,
decoration = Decoration.Playlist(
item.streamCount ?: ListExtractor.ITEM_COUNT_UNKNOWN
),
)
)
@JvmStatic
@ -124,7 +123,7 @@ data class LongPressable(
viewCount = null,
streamType = null,
uploadDate = null,
decoration = null,
decoration = null
)
@JvmStatic
@ -137,7 +136,7 @@ data class LongPressable(
viewCount = null,
streamType = null,
uploadDate = null,
decoration = Decoration.Playlist(item.streamCount),
decoration = Decoration.Playlist(item.streamCount)
)
}
}

View File

@ -33,7 +33,7 @@ import org.schabi.newpipe.util.StreamTypeUtil
@MainThread
suspend fun fetchItemInfoIfSparse(
context: Context,
item: StreamInfoItem,
item: StreamInfoItem
): SinglePlayQueue {
if ((StreamTypeUtil.isLiveStream(item.streamType) || item.duration >= 0) &&
!Utils.isNullOrEmpty(item.uploaderUrl)
@ -66,7 +66,7 @@ suspend fun fetchUploaderUrlIfSparse(
context: Context,
serviceId: Int,
url: String,
uploaderUrl: String?,
uploaderUrl: String?
): String? {
if (!uploaderUrl.isNullOrEmpty()) {
return uploaderUrl
@ -89,7 +89,7 @@ suspend fun fetchUploaderUrlIfSparse(
suspend fun fetchStreamInfoAndSaveToDatabase(
context: Context,
serviceId: Int,
url: String,
url: String
): StreamInfo {
Toast.makeText(context, R.string.loading_stream_details, Toast.LENGTH_SHORT).show()

View File

@ -146,6 +146,6 @@ private fun BackgroundFromHerePreview() {
Icon(
imageVector = Icons.Filled.BackgroundFromHere,
contentDescription = null,
modifier = Modifier.size(240.dp),
modifier = Modifier.size(240.dp)
)
}

View File

@ -122,6 +122,6 @@ private fun PlayFromHerePreview() {
Icon(
imageVector = Icons.Filled.PlayFromHere,
contentDescription = null,
modifier = Modifier.size(240.dp),
modifier = Modifier.size(240.dp)
)
}

View File

@ -158,6 +158,6 @@ private fun PopupFromHerePreview() {
Icon(
imageVector = Icons.Filled.PopupFromHere,
contentDescription = null,
modifier = Modifier.size(240.dp),
modifier = Modifier.size(240.dp)
)
}

View File

@ -9,7 +9,7 @@ import androidx.compose.ui.graphics.Color
@Immutable
data class CustomColors(
val onSurfaceVariantLink: Color = Color.Unspecified,
val onSurfaceVariantLink: Color = Color.Unspecified
)
val onSurfaceVariantLinkLight = Color(0xFF5060B0)

View File

@ -9,7 +9,7 @@ import kotlin.reflect.safeCast
data class Either<A : Any, B : Any>(
val value: Any,
val classA: KClass<A>,
val classB: KClass<B>,
val classB: KClass<B>
) {
inline fun <R> match(ifLeft: (A) -> R, ifRight: (B) -> R): R {
return classA.safeCast(value)?.let { ifLeft(it) }
@ -17,9 +17,7 @@ data class Either<A : Any, B : Any>(
}
companion object {
inline fun <reified A : Any, reified B : Any> left(a: A): Either<A, B> =
Either(a, A::class, B::class)
inline fun <reified A : Any, reified B : Any> right(b: B): Either<A, B> =
Either(b, A::class, B::class)
inline fun <reified A : Any, reified B : Any> left(a: A): Either<A, B> = Either(a, A::class, B::class)
inline fun <reified A : Any, reified B : Any> right(b: B): Either<A, B> = Either(b, A::class, B::class)
}
}

View File

@ -18,7 +18,7 @@ fun FixedHeightCenteredText(
text: String,
lines: Int,
modifier: Modifier = Modifier,
style: TextStyle = LocalTextStyle.current,
style: TextStyle = LocalTextStyle.current
) {
Box(modifier = modifier) {
// this allows making the box always the same height (i.e. the height of [lines] text
@ -26,7 +26,7 @@ fun FixedHeightCenteredText(
Text(
text = "",
style = style,
minLines = lines,
minLines = lines
)
Text(
text = text,