From 4b90295aab6ec0f91e392c3c1052c30ff47ef9fa Mon Sep 17 00:00:00 2001 From: Stypox Date: Thu, 28 Aug 2025 18:26:50 +0200 Subject: [PATCH] Uniform localizing view counts --- .../fragments/detail/VideoDetailFragment.kt | 11 +-- .../holder/StreamInfoItemHolder.java | 13 +--- .../newpipe/local/feed/item/StreamItem.kt | 24 +++---- .../LocalStatisticStreamItemHolder.java | 2 +- .../ui/components/items/stream/StreamUtils.kt | 14 ++-- .../ui/components/menu/LongPressMenu.kt | 11 ++- .../ui/components/menu/LongPressable.kt | 7 ++ .../org/schabi/newpipe/util/Localization.java | 68 ++++++++++++------- 8 files changed, 79 insertions(+), 71 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.kt b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.kt index ae5800ab3..aa3fad60c 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.kt +++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.kt @@ -1424,14 +1424,9 @@ class VideoDetailFragment : } if (info.viewCount >= 0) { - binding.detailViewCountView.text = - if (info.streamType == StreamType.AUDIO_LIVE_STREAM) { - Localization.listeningCount(activity, info.viewCount) - } else if (info.streamType == StreamType.LIVE_STREAM) { - Localization.localizeWatchingCount(activity, info.viewCount) - } else { - Localization.localizeViewCount(activity, info.viewCount) - } + binding.detailViewCountView.text = Localization.localizeViewCount( + activity, false, info.streamType, info.viewCount + ) binding.detailViewCountView.visibility = View.VISIBLE } else { binding.detailViewCountView.visibility = View.GONE diff --git a/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamInfoItemHolder.java index 80f62eed3..84ee2742a 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamInfoItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamInfoItemHolder.java @@ -7,7 +7,6 @@ import android.widget.TextView; import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem; -import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.info_list.InfoItemBuilder; import org.schabi.newpipe.local.history.HistoryRecordManager; import org.schabi.newpipe.util.Localization; @@ -65,16 +64,8 @@ public class StreamInfoItemHolder extends StreamMiniInfoItemHolder { private String getStreamInfoDetailLine(final StreamInfoItem infoItem) { String viewsAndDate = ""; if (infoItem.getViewCount() >= 0) { - if (infoItem.getStreamType().equals(StreamType.AUDIO_LIVE_STREAM)) { - viewsAndDate = Localization - .listeningCount(itemBuilder.getContext(), infoItem.getViewCount()); - } else if (infoItem.getStreamType().equals(StreamType.LIVE_STREAM)) { - viewsAndDate = Localization - .shortWatchingCount(itemBuilder.getContext(), infoItem.getViewCount()); - } else { - viewsAndDate = Localization - .shortViewCount(itemBuilder.getContext(), infoItem.getViewCount()); - } + viewsAndDate = Localization.localizeViewCount(itemBuilder.getContext(), true, + infoItem.getStreamType(), infoItem.getViewCount()); } final String uploadDate = Localization.relativeTimeOrTextual(itemBuilder.getContext(), diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/item/StreamItem.kt b/app/src/main/java/org/schabi/newpipe/local/feed/item/StreamItem.kt index 258a67a4c..970e8f09c 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/item/StreamItem.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/item/StreamItem.kt @@ -1,7 +1,6 @@ package org.schabi.newpipe.local.feed.item import android.content.Context -import android.text.TextUtils import android.view.View import androidx.core.content.ContextCompat import androidx.preference.PreferenceManager @@ -117,23 +116,16 @@ data class StreamItem( } private fun getStreamInfoDetailLine(context: Context): String { - var viewsAndDate = "" - val viewCount = stream.viewCount - if (viewCount != null && viewCount >= 0) { - viewsAndDate = when (stream.streamType) { - AUDIO_LIVE_STREAM -> Localization.listeningCount(context, viewCount) - LIVE_STREAM -> Localization.shortWatchingCount(context, viewCount) - else -> Localization.shortViewCount(context, viewCount) - } - } + val views = stream.viewCount + ?.takeIf { it >= 0 } + ?.let { Localization.localizeViewCount(context, true, stream.streamType, it) } + ?: "" + val uploadDate = getFormattedRelativeUploadDate(context) return when { - !TextUtils.isEmpty(uploadDate) -> when { - viewsAndDate.isEmpty() -> uploadDate!! - else -> Localization.concatenateStrings(viewsAndDate, uploadDate) - } - - else -> viewsAndDate + uploadDate.isNullOrEmpty() -> views + views.isEmpty() -> uploadDate + else -> Localization.concatenateStrings(views, uploadDate) } } diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/LocalStatisticStreamItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/LocalStatisticStreamItemHolder.java index f26a76ad9..dd8edfa66 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/LocalStatisticStreamItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/LocalStatisticStreamItemHolder.java @@ -73,7 +73,7 @@ public class LocalStatisticStreamItemHolder extends LocalItemHolder { final DateTimeFormatter dateTimeFormatter) { return Localization.concatenateStrings( // watchCount - Localization.shortViewCount(itemBuilder.getContext(), entry.getWatchCount()), + Localization.localizeWatchCount(itemBuilder.getContext(), entry.getWatchCount()), dateTimeFormatter.format(entry.getLatestAccessDate()), // serviceName ServiceHelper.getNameOfServiceById(entry.getStreamEntity().getServiceId())); diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/items/stream/StreamUtils.kt b/app/src/main/java/org/schabi/newpipe/ui/components/items/stream/StreamUtils.kt index d744b700d..b200a4b45 100644 --- a/app/src/main/java/org/schabi/newpipe/ui/components/items/stream/StreamUtils.kt +++ b/app/src/main/java/org/schabi/newpipe/ui/components/items/stream/StreamUtils.kt @@ -37,16 +37,10 @@ internal fun getStreamInfoDetail(stream: StreamInfoItem): String { val context = LocalContext.current return rememberSaveable(stream) { - val count = stream.viewCount - val views = if (count >= 0) { - when (stream.streamType) { - StreamType.AUDIO_LIVE_STREAM -> Localization.listeningCount(context, count) - StreamType.LIVE_STREAM -> Localization.shortWatchingCount(context, count) - else -> Localization.shortViewCount(context, count) - } - } else { - "" - } + val views = stream.viewCount + .takeIf { it >= 0 } + ?.let { Localization.localizeViewCount(context, true, stream.streamType, it) } + ?: "" val date = Localization.relativeTimeOrTextual(context, stream.uploadDate, stream.textualUploadDate) diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/menu/LongPressMenu.kt b/app/src/main/java/org/schabi/newpipe/ui/components/menu/LongPressMenu.kt index 40e0daed6..141903143 100644 --- a/app/src/main/java/org/schabi/newpipe/ui/components/menu/LongPressMenu.kt +++ b/app/src/main/java/org/schabi/newpipe/ui/components/menu/LongPressMenu.kt @@ -72,6 +72,7 @@ import androidx.compose.ui.tooling.preview.datasource.LoremIpsum import androidx.compose.ui.unit.dp import coil3.compose.AsyncImage import org.schabi.newpipe.R +import org.schabi.newpipe.extractor.stream.StreamType import org.schabi.newpipe.ktx.popFirst import org.schabi.newpipe.ui.components.menu.LongPressAction.Type.EnqueueNext import org.schabi.newpipe.ui.components.menu.LongPressAction.Type.ShowChannelDetails @@ -441,7 +442,9 @@ fun getSubtitleAnnotatedString( append(uploadDate) } - val viewCount = item.viewCount?.let { Localization.localizeViewCount(ctx, it) } + val viewCount = item.viewCount?.let { + Localization.localizeViewCount(ctx, true, item.streamType, it) + } if (!viewCount.isNullOrBlank()) { if (shouldAddSeparator) { append(Localization.DOT_SEPARATOR) @@ -545,6 +548,7 @@ private class LongPressablePreviews : CollectionPreviewParameterProvider?, val decoration: Decoration?, ) { @@ -50,6 +51,7 @@ data class LongPressable( uploader = item.uploaderName?.takeIf { it.isNotBlank() }, uploaderUrl = item.uploaderUrl?.takeIf { it.isNotBlank() }, viewCount = item.viewCount.takeIf { it >= 0 }, + streamType = item.streamType, uploadDate = item.uploadDate?.let { Either.right(it.offsetDateTime()) } ?: item.textualUploadDate?.let { Either.left(it) }, decoration = Decoration.from(item.streamType, item.duration), @@ -63,6 +65,7 @@ data class LongPressable( uploader = item.uploader.takeIf { it.isNotBlank() }, uploaderUrl = item.uploaderUrl?.takeIf { it.isNotBlank() }, viewCount = item.viewCount?.takeIf { it >= 0 }, + streamType = item.streamType, uploadDate = item.uploadDate?.let { Either.right(it) } ?: item.textualUploadDate?.let { Either.left(it) }, decoration = Decoration.from(item.streamType, item.duration), @@ -77,6 +80,7 @@ data class LongPressable( uploader = null, uploaderUrl = null, viewCount = null, + streamType = null, uploadDate = null, decoration = Decoration.Playlist(item.streamCount), ) @@ -89,6 +93,7 @@ data class LongPressable( uploader = item.uploader, uploaderUrl = null, viewCount = null, + streamType = null, uploadDate = null, decoration = Decoration.Playlist( item.streamCount ?: ListExtractor.ITEM_COUNT_UNKNOWN @@ -103,6 +108,7 @@ data class LongPressable( uploader = null, uploaderUrl = item.url?.takeIf { it.isNotBlank() }, viewCount = null, + streamType = null, uploadDate = null, decoration = null, ) @@ -115,6 +121,7 @@ data class LongPressable( uploader = item.uploaderName.takeIf { it.isNotBlank() }, uploaderUrl = item.uploaderUrl?.takeIf { it.isNotBlank() }, viewCount = null, + streamType = null, uploadDate = null, decoration = Decoration.Playlist(item.streamCount), ) diff --git a/app/src/main/java/org/schabi/newpipe/util/Localization.java b/app/src/main/java/org/schabi/newpipe/util/Localization.java index c44423290..fc19e578b 100644 --- a/app/src/main/java/org/schabi/newpipe/util/Localization.java +++ b/app/src/main/java/org/schabi/newpipe/util/Localization.java @@ -31,6 +31,7 @@ import org.schabi.newpipe.extractor.localization.ContentCountry; import org.schabi.newpipe.extractor.localization.DateWrapper; import org.schabi.newpipe.extractor.stream.AudioStream; import org.schabi.newpipe.extractor.stream.AudioTrackType; +import org.schabi.newpipe.extractor.stream.StreamType; import java.math.BigDecimal; import java.math.RoundingMode; @@ -183,9 +184,50 @@ public final class Localization { return context.getString(R.string.upload_date_text, formatDate(offsetDateTime)); } - public static String localizeViewCount(@NonNull final Context context, final long viewCount) { + /** + * Localizes the number of views of a stream reported by the service, + * with different words based on the stream type. + * + * @param context the Android context + * @param shortForm whether the number of views should be formatted in a short approximated form + * @param streamType influences the accompanying text, i.e. views/watching/listening + * @param viewCount the number of views reported by the service to localize + * @return the formatted and localized view count + */ + public static String localizeViewCount(@NonNull final Context context, + final boolean shortForm, + @Nullable final StreamType streamType, + final long viewCount) { + final String localizedNumber; + if (shortForm) { + localizedNumber = shortCount(context, viewCount); + } else { + localizedNumber = localizeNumber(viewCount); + } + + if (streamType == StreamType.AUDIO_LIVE_STREAM) { + return getQuantity(context, R.plurals.listening, R.string.no_one_listening, viewCount, + localizedNumber); + } else if (streamType == StreamType.LIVE_STREAM) { + return getQuantity(context, R.plurals.watching, R.string.no_one_watching, viewCount, + localizedNumber); + } else { + return getQuantity(context, R.plurals.views, R.string.no_views, viewCount, + localizedNumber); + } + } + + /** + * Localizes the number of times the user watched a video that they have in the history. + * + * @param context the Android context + * @param viewCount the number of times (stored in the database) the user watched a video + * @return the formatted and localized watch count + */ + public static String localizeWatchCount(@NonNull final Context context, + final long viewCount) { return getQuantity(context, R.plurals.views, R.string.no_views, viewCount, - localizeNumber(viewCount)); + shortCount(context, viewCount)); } public static String localizeStreamCount(@NonNull final Context context, @@ -217,12 +259,6 @@ public final class Localization { } } - public static String localizeWatchingCount(@NonNull final Context context, - final long watchingCount) { - return getQuantity(context, R.plurals.watching, R.string.no_one_watching, watchingCount, - localizeNumber(watchingCount)); - } - public static String shortCount(@NonNull final Context context, final long count) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { return CompactDecimalFormat.getInstance(getAppLocale(), @@ -250,22 +286,6 @@ public final class Localization { } } - public static String listeningCount(@NonNull final Context context, final long listeningCount) { - return getQuantity(context, R.plurals.listening, R.string.no_one_listening, listeningCount, - shortCount(context, listeningCount)); - } - - public static String shortWatchingCount(@NonNull final Context context, - final long watchingCount) { - return getQuantity(context, R.plurals.watching, R.string.no_one_watching, watchingCount, - shortCount(context, watchingCount)); - } - - public static String shortViewCount(@NonNull final Context context, final long viewCount) { - return getQuantity(context, R.plurals.views, R.string.no_views, viewCount, - shortCount(context, viewCount)); - } - public static String shortSubscriberCount(@NonNull final Context context, final long subscriberCount) { return getQuantity(context, R.plurals.subscribers, R.string.no_subscribers, subscriberCount,