From 88b687f57b6e564dca8364f663653184e516b0ab Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Sat, 13 Sep 2025 05:35:03 +0530 Subject: [PATCH 1/9] Refactor codebase to use Instant instead of OffsetDateTime --- .../newpipe/database/DatabaseMigrationTest.kt | 4 +- .../org/schabi/newpipe/database/Converters.kt | 18 +++--- .../newpipe/database/feed/dao/FeedDAO.kt | 24 ++++---- .../feed/model/FeedLastUpdatedEntity.kt | 4 +- .../history/model/SearchHistoryEntry.kt | 15 ++--- .../history/model/StreamHistoryEntity.java | 18 +++--- .../history/model/StreamHistoryEntry.kt | 14 +---- .../database/stream/StreamStatisticsEntry.kt | 4 +- .../newpipe/database/stream/dao/StreamDAO.kt | 10 ++-- .../database/stream/model/StreamEntity.kt | 16 +++--- .../fragments/detail/DescriptionFragment.java | 11 ++-- .../list/comments/CommentRepliesFragment.java | 2 +- .../holder/CommentInfoItemHolder.java | 13 ++--- .../holder/StreamInfoItemHolder.java | 2 +- .../newpipe/local/feed/FeedDatabaseManager.kt | 55 +++++++++---------- .../schabi/newpipe/local/feed/FeedFragment.kt | 15 ++--- .../schabi/newpipe/local/feed/FeedState.kt | 4 +- .../newpipe/local/feed/FeedViewModel.kt | 10 ++-- .../newpipe/local/feed/item/StreamItem.kt | 4 +- .../local/feed/service/FeedLoadManager.kt | 30 +++++----- .../local/history/HistoryRecordManager.java | 13 ++--- .../history/StatisticsPlaylistFragment.java | 2 +- .../LocalStatisticStreamItemHolder.java | 2 +- .../org/schabi/newpipe/util/Localization.java | 35 +++++------- .../schabi/newpipe/util/LocalizationTest.kt | 20 ++++--- 25 files changed, 160 insertions(+), 185 deletions(-) diff --git a/app/src/androidTest/java/org/schabi/newpipe/database/DatabaseMigrationTest.kt b/app/src/androidTest/java/org/schabi/newpipe/database/DatabaseMigrationTest.kt index a34cfece671..0cf0ce26e99 100644 --- a/app/src/androidTest/java/org/schabi/newpipe/database/DatabaseMigrationTest.kt +++ b/app/src/androidTest/java/org/schabi/newpipe/database/DatabaseMigrationTest.kt @@ -144,7 +144,7 @@ class DatabaseMigrationTest { assertEquals(DEFAULT_THUMBNAIL, streamFromMigratedDatabase.thumbnailUrl) assertNull(streamFromMigratedDatabase.viewCount) assertNull(streamFromMigratedDatabase.textualUploadDate) - assertNull(streamFromMigratedDatabase.uploadDate) + assertNull(streamFromMigratedDatabase.uploadInstant) assertNull(streamFromMigratedDatabase.isUploadDateApproximation) val secondStreamFromMigratedDatabase = listFromDB[1] @@ -158,7 +158,7 @@ class DatabaseMigrationTest { assertEquals("", secondStreamFromMigratedDatabase.thumbnailUrl) assertNull(secondStreamFromMigratedDatabase.viewCount) assertNull(secondStreamFromMigratedDatabase.textualUploadDate) - assertNull(secondStreamFromMigratedDatabase.uploadDate) + assertNull(secondStreamFromMigratedDatabase.uploadInstant) assertNull(secondStreamFromMigratedDatabase.isUploadDateApproximation) } diff --git a/app/src/main/java/org/schabi/newpipe/database/Converters.kt b/app/src/main/java/org/schabi/newpipe/database/Converters.kt index ec097cc1bf3..efd351fb131 100644 --- a/app/src/main/java/org/schabi/newpipe/database/Converters.kt +++ b/app/src/main/java/org/schabi/newpipe/database/Converters.kt @@ -4,31 +4,27 @@ import androidx.room.TypeConverter import org.schabi.newpipe.extractor.stream.StreamType import org.schabi.newpipe.local.subscription.FeedGroupIcon import java.time.Instant -import java.time.OffsetDateTime -import java.time.ZoneOffset class Converters { /** - * Convert a long value to a [OffsetDateTime]. + * Convert a long value to an [Instant]. * * @param value the long value - * @return the `OffsetDateTime` + * @return the `Instant` */ @TypeConverter - fun offsetDateTimeFromTimestamp(value: Long?): OffsetDateTime? { - return value?.let { OffsetDateTime.ofInstant(Instant.ofEpochMilli(it), ZoneOffset.UTC) } + fun timestampToInstant(value: Long?): Instant? { + return value?.let { Instant.ofEpochMilli(it) } } /** - * Convert a [OffsetDateTime] to a long value. + * Convert an [Instant] to a long value. * - * @param offsetDateTime the `OffsetDateTime` + * @param instant the `Instant` * @return the long value */ @TypeConverter - fun offsetDateTimeToTimestamp(offsetDateTime: OffsetDateTime?): Long? { - return offsetDateTime?.withOffsetSameInstant(ZoneOffset.UTC)?.toInstant()?.toEpochMilli() - } + fun instantToTimestamp(instant: Instant?) = instant?.toEpochMilli() @TypeConverter fun streamTypeOf(value: String): StreamType { diff --git a/app/src/main/java/org/schabi/newpipe/database/feed/dao/FeedDAO.kt b/app/src/main/java/org/schabi/newpipe/database/feed/dao/FeedDAO.kt index e7ed934977a..06055a76489 100644 --- a/app/src/main/java/org/schabi/newpipe/database/feed/dao/FeedDAO.kt +++ b/app/src/main/java/org/schabi/newpipe/database/feed/dao/FeedDAO.kt @@ -15,7 +15,7 @@ import org.schabi.newpipe.database.stream.StreamWithState import org.schabi.newpipe.database.stream.model.StreamStateEntity import org.schabi.newpipe.database.subscription.NotificationMode import org.schabi.newpipe.database.subscription.SubscriptionEntity -import java.time.OffsetDateTime +import java.time.Instant @Dao abstract class FeedDAO { @@ -90,7 +90,7 @@ abstract class FeedDAO { groupId: Long, includePlayed: Boolean, includePartiallyPlayed: Boolean, - uploadDateBefore: OffsetDateTime? + uploadDateBefore: Instant? ): Maybe> /** @@ -99,7 +99,7 @@ abstract class FeedDAO { * * One stream per uploader is kept because it is needed as reference * when fetching new streams to check if they are new or not. - * @param offsetDateTime the newest date to keep, older streams are removed + * @param instant the newest date to keep, older streams are removed */ @Query( """ @@ -115,11 +115,11 @@ abstract class FeedDAO { INNER JOIN feed f ON s.uid = f.stream_id - WHERE s.upload_date < :offsetDateTime + WHERE s.upload_date < :instant AND s.upload_date <> max_upload_date)) """ ) - abstract fun unlinkStreamsOlderThan(offsetDateTime: OffsetDateTime) + abstract fun unlinkStreamsOlderThan(instant: Instant) @Query( """ @@ -168,13 +168,13 @@ abstract class FeedDAO { ON fgs.subscription_id = lu.subscription_id AND fgs.group_id = :groupId """ ) - abstract fun oldestSubscriptionUpdate(groupId: Long): Flowable> + abstract fun getOldestSubscriptionUpdate(groupId: Long): Flowable> @Query("SELECT MIN(last_updated) FROM feed_last_updated") - abstract fun oldestSubscriptionUpdateFromAll(): Flowable> + abstract fun getOldestSubscriptionUpdateFromAll(): Flowable> @Query("SELECT COUNT(*) FROM feed_last_updated WHERE last_updated IS NULL") - abstract fun notLoadedCount(): Flowable + abstract fun getNotLoadedCount(): Flowable @Query( """ @@ -189,7 +189,7 @@ abstract class FeedDAO { WHERE lu.last_updated IS NULL """ ) - abstract fun notLoadedCountForGroup(groupId: Long): Flowable + abstract fun getNotLoadedCountForGroup(groupId: Long): Flowable @Query( """ @@ -201,7 +201,7 @@ abstract class FeedDAO { WHERE lu.last_updated IS NULL OR lu.last_updated < :outdatedThreshold """ ) - abstract fun getAllOutdated(outdatedThreshold: OffsetDateTime): Flowable> + abstract fun getAllOutdated(outdatedThreshold: Instant): Flowable> @Query( """ @@ -216,7 +216,7 @@ abstract class FeedDAO { WHERE lu.last_updated IS NULL OR lu.last_updated < :outdatedThreshold """ ) - abstract fun getAllOutdatedForGroup(groupId: Long, outdatedThreshold: OffsetDateTime): Flowable> + abstract fun getAllOutdatedForGroup(groupId: Long, outdatedThreshold: Instant): Flowable> @Query( """ @@ -231,7 +231,7 @@ abstract class FeedDAO { """ ) abstract fun getOutdatedWithNotificationMode( - outdatedThreshold: OffsetDateTime, + outdatedThreshold: Instant, @NotificationMode notificationMode: Int ): Flowable> } diff --git a/app/src/main/java/org/schabi/newpipe/database/feed/model/FeedLastUpdatedEntity.kt b/app/src/main/java/org/schabi/newpipe/database/feed/model/FeedLastUpdatedEntity.kt index a19af9c456e..217bfd860c6 100644 --- a/app/src/main/java/org/schabi/newpipe/database/feed/model/FeedLastUpdatedEntity.kt +++ b/app/src/main/java/org/schabi/newpipe/database/feed/model/FeedLastUpdatedEntity.kt @@ -7,7 +7,7 @@ import androidx.room.PrimaryKey import org.schabi.newpipe.database.feed.model.FeedLastUpdatedEntity.Companion.FEED_LAST_UPDATED_TABLE import org.schabi.newpipe.database.feed.model.FeedLastUpdatedEntity.Companion.SUBSCRIPTION_ID import org.schabi.newpipe.database.subscription.SubscriptionEntity -import java.time.OffsetDateTime +import java.time.Instant @Entity( tableName = FEED_LAST_UPDATED_TABLE, @@ -26,7 +26,7 @@ data class FeedLastUpdatedEntity( var subscriptionId: Long, @ColumnInfo(name = LAST_UPDATED) - var lastUpdated: OffsetDateTime? = null + var lastUpdated: Instant? = null ) { companion object { const val FEED_LAST_UPDATED_TABLE = "feed_last_updated" diff --git a/app/src/main/java/org/schabi/newpipe/database/history/model/SearchHistoryEntry.kt b/app/src/main/java/org/schabi/newpipe/database/history/model/SearchHistoryEntry.kt index 8cb9a25ca17..d8c8e97d46c 100644 --- a/app/src/main/java/org/schabi/newpipe/database/history/model/SearchHistoryEntry.kt +++ b/app/src/main/java/org/schabi/newpipe/database/history/model/SearchHistoryEntry.kt @@ -5,18 +5,16 @@ import androidx.room.Entity import androidx.room.Ignore import androidx.room.Index import androidx.room.PrimaryKey -import java.time.OffsetDateTime +import java.time.Instant @Entity( tableName = SearchHistoryEntry.TABLE_NAME, indices = [Index(value = [SearchHistoryEntry.SEARCH])] ) data class SearchHistoryEntry( - @field:ColumnInfo(name = CREATION_DATE) var creationDate: OffsetDateTime?, - @field:ColumnInfo( - name = SERVICE_ID - ) var serviceId: Int, - @field:ColumnInfo(name = SEARCH) var search: String? + @ColumnInfo(name = CREATION_DATE) var creationInstant: Instant?, + @ColumnInfo(name = SERVICE_ID) var serviceId: Int, + @ColumnInfo(name = SEARCH) var search: String? ) { @ColumnInfo(name = ID) @PrimaryKey(autoGenerate = true) @@ -24,10 +22,7 @@ data class SearchHistoryEntry( @Ignore fun hasEqualValues(otherEntry: SearchHistoryEntry): Boolean { - return ( - serviceId == otherEntry.serviceId && - search == otherEntry.search - ) + return serviceId == otherEntry.serviceId && search == otherEntry.search } companion object { diff --git a/app/src/main/java/org/schabi/newpipe/database/history/model/StreamHistoryEntity.java b/app/src/main/java/org/schabi/newpipe/database/history/model/StreamHistoryEntity.java index a9d69afe855..f2216064336 100644 --- a/app/src/main/java/org/schabi/newpipe/database/history/model/StreamHistoryEntity.java +++ b/app/src/main/java/org/schabi/newpipe/database/history/model/StreamHistoryEntity.java @@ -8,7 +8,7 @@ import org.schabi.newpipe.database.stream.model.StreamEntity; -import java.time.OffsetDateTime; +import java.time.Instant; import static androidx.room.ForeignKey.CASCADE; import static org.schabi.newpipe.database.history.model.StreamHistoryEntity.JOIN_STREAM_ID; @@ -36,21 +36,21 @@ public class StreamHistoryEntity { @NonNull @ColumnInfo(name = STREAM_ACCESS_DATE) - private OffsetDateTime accessDate; + private Instant accessInstant; @ColumnInfo(name = STREAM_REPEAT_COUNT) private long repeatCount; /** * @param streamUid the stream id this history item will refer to - * @param accessDate the last time the stream was accessed + * @param accessInstant the last time the stream was accessed * @param repeatCount the total number of views this stream received */ public StreamHistoryEntity(final long streamUid, - @NonNull final OffsetDateTime accessDate, + @NonNull final Instant accessInstant, final long repeatCount) { this.streamUid = streamUid; - this.accessDate = accessDate; + this.accessInstant = accessInstant; this.repeatCount = repeatCount; } @@ -63,12 +63,12 @@ public void setStreamUid(final long streamUid) { } @NonNull - public OffsetDateTime getAccessDate() { - return accessDate; + public Instant getAccessInstant() { + return accessInstant; } - public void setAccessDate(@NonNull final OffsetDateTime accessDate) { - this.accessDate = accessDate; + public void setAccessInstant(@NonNull final Instant accessInstant) { + this.accessInstant = accessInstant; } public long getRepeatCount() { diff --git a/app/src/main/java/org/schabi/newpipe/database/history/model/StreamHistoryEntry.kt b/app/src/main/java/org/schabi/newpipe/database/history/model/StreamHistoryEntry.kt index 27fc429f1b9..41bd5323f41 100644 --- a/app/src/main/java/org/schabi/newpipe/database/history/model/StreamHistoryEntry.kt +++ b/app/src/main/java/org/schabi/newpipe/database/history/model/StreamHistoryEntry.kt @@ -5,7 +5,7 @@ import androidx.room.Embedded import org.schabi.newpipe.database.stream.model.StreamEntity import org.schabi.newpipe.extractor.stream.StreamInfoItem import org.schabi.newpipe.util.image.ImageStrategy -import java.time.OffsetDateTime +import java.time.Instant data class StreamHistoryEntry( @Embedded @@ -15,21 +15,11 @@ data class StreamHistoryEntry( val streamId: Long, @ColumnInfo(name = StreamHistoryEntity.STREAM_ACCESS_DATE) - val accessDate: OffsetDateTime, + val accessInstant: Instant, @ColumnInfo(name = StreamHistoryEntity.STREAM_REPEAT_COUNT) val repeatCount: Long ) { - - fun toStreamHistoryEntity(): StreamHistoryEntity { - return StreamHistoryEntity(streamId, accessDate, repeatCount) - } - - fun hasEqualValues(other: StreamHistoryEntry): Boolean { - return this.streamEntity.uid == other.streamEntity.uid && streamId == other.streamId && - accessDate.isEqual(other.accessDate) - } - fun toStreamInfoItem(): StreamInfoItem = StreamInfoItem( streamEntity.serviceId, diff --git a/app/src/main/java/org/schabi/newpipe/database/stream/StreamStatisticsEntry.kt b/app/src/main/java/org/schabi/newpipe/database/stream/StreamStatisticsEntry.kt index 1f3654e7ae4..87444a535e4 100644 --- a/app/src/main/java/org/schabi/newpipe/database/stream/StreamStatisticsEntry.kt +++ b/app/src/main/java/org/schabi/newpipe/database/stream/StreamStatisticsEntry.kt @@ -8,7 +8,7 @@ import org.schabi.newpipe.database.stream.model.StreamEntity import org.schabi.newpipe.database.stream.model.StreamStateEntity.STREAM_PROGRESS_MILLIS import org.schabi.newpipe.extractor.stream.StreamInfoItem import org.schabi.newpipe.util.image.ImageStrategy -import java.time.OffsetDateTime +import java.time.Instant class StreamStatisticsEntry( @Embedded @@ -21,7 +21,7 @@ class StreamStatisticsEntry( val streamId: Long, @ColumnInfo(name = STREAM_LATEST_DATE) - val latestAccessDate: OffsetDateTime, + val latestAccessInstant: Instant, @ColumnInfo(name = STREAM_WATCH_COUNT) val watchCount: Long diff --git a/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamDAO.kt b/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamDAO.kt index d8c19c1e979..50adbfe2623 100644 --- a/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamDAO.kt +++ b/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamDAO.kt @@ -13,7 +13,7 @@ import org.schabi.newpipe.database.stream.model.StreamEntity import org.schabi.newpipe.database.stream.model.StreamEntity.Companion.STREAM_ID import org.schabi.newpipe.extractor.stream.StreamType import org.schabi.newpipe.util.StreamTypeUtil -import java.time.OffsetDateTime +import java.time.Instant @Dao abstract class StreamDAO : BasicDAO { @@ -95,9 +95,9 @@ abstract class StreamDAO : BasicDAO { // Use the existent upload date if the newer stream does not have a better precision // (i.e. is an approximation). This is done to prevent unnecessary changes. val hasBetterPrecision = - newerStream.uploadDate != null && newerStream.isUploadDateApproximation != true - if (existentMinimalStream.uploadDate != null && !hasBetterPrecision) { - newerStream.uploadDate = existentMinimalStream.uploadDate + newerStream.uploadInstant != null && newerStream.isUploadDateApproximation != true + if (existentMinimalStream.uploadInstant != null && !hasBetterPrecision) { + newerStream.uploadInstant = existentMinimalStream.uploadInstant newerStream.textualUploadDate = existentMinimalStream.textualUploadDate newerStream.isUploadDateApproximation = existentMinimalStream.isUploadDateApproximation } @@ -138,7 +138,7 @@ abstract class StreamDAO : BasicDAO { var textualUploadDate: String? = null, @ColumnInfo(name = StreamEntity.STREAM_UPLOAD_DATE) - var uploadDate: OffsetDateTime? = null, + var uploadInstant: Instant? = null, @ColumnInfo(name = StreamEntity.STREAM_IS_UPLOAD_DATE_APPROXIMATION) var isUploadDateApproximation: Boolean? = null, diff --git a/app/src/main/java/org/schabi/newpipe/database/stream/model/StreamEntity.kt b/app/src/main/java/org/schabi/newpipe/database/stream/model/StreamEntity.kt index d9c160b89bc..eeebc419954 100644 --- a/app/src/main/java/org/schabi/newpipe/database/stream/model/StreamEntity.kt +++ b/app/src/main/java/org/schabi/newpipe/database/stream/model/StreamEntity.kt @@ -15,7 +15,7 @@ import org.schabi.newpipe.extractor.stream.StreamType import org.schabi.newpipe.player.playqueue.PlayQueueItem import org.schabi.newpipe.util.image.ImageStrategy import java.io.Serializable -import java.time.OffsetDateTime +import java.time.Instant @Entity( tableName = STREAM_TABLE, @@ -59,7 +59,7 @@ data class StreamEntity( var textualUploadDate: String? = null, @ColumnInfo(name = STREAM_UPLOAD_DATE) - var uploadDate: OffsetDateTime? = null, + var uploadInstant: Instant? = null, @ColumnInfo(name = STREAM_IS_UPLOAD_DATE_APPROXIMATION) var isUploadDateApproximation: Boolean? = null @@ -69,8 +69,9 @@ data class StreamEntity( serviceId = item.serviceId, url = item.url, title = item.name, streamType = item.streamType, duration = item.duration, uploader = item.uploaderName, uploaderUrl = item.uploaderUrl, - thumbnailUrl = ImageStrategy.imageListToDbUrl(item.thumbnails), viewCount = item.viewCount, - textualUploadDate = item.textualUploadDate, uploadDate = item.uploadDate?.offsetDateTime(), + thumbnailUrl = ImageStrategy.imageListToDbUrl(item.thumbnails), + viewCount = item.viewCount, textualUploadDate = item.textualUploadDate, + uploadInstant = item.uploadDate?.instant, isUploadDateApproximation = item.uploadDate?.isApproximation ) @@ -79,8 +80,9 @@ data class StreamEntity( serviceId = info.serviceId, url = info.url, title = info.name, streamType = info.streamType, duration = info.duration, uploader = info.uploaderName, uploaderUrl = info.uploaderUrl, - thumbnailUrl = ImageStrategy.imageListToDbUrl(info.thumbnails), viewCount = info.viewCount, - textualUploadDate = info.textualUploadDate, uploadDate = info.uploadDate?.offsetDateTime(), + thumbnailUrl = ImageStrategy.imageListToDbUrl(info.thumbnails), + viewCount = info.viewCount, + textualUploadDate = info.textualUploadDate, uploadInstant = info.uploadDate?.instant, isUploadDateApproximation = info.uploadDate?.isApproximation ) @@ -101,7 +103,7 @@ data class StreamEntity( if (viewCount != null) item.viewCount = viewCount as Long item.textualUploadDate = textualUploadDate - item.uploadDate = uploadDate?.let { + item.uploadDate = uploadInstant?.let { DateWrapper(it, isUploadDateApproximation ?: false) } diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/DescriptionFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/DescriptionFragment.java index 2b0d22a32ed..e5e09ad834e 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/detail/DescriptionFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/DescriptionFragment.java @@ -65,11 +65,12 @@ public List getTags() { } @Override - protected void setupMetadata(final LayoutInflater inflater, - final LinearLayout layout) { - if (streamInfo != null && streamInfo.getUploadDate() != null) { - binding.detailUploadDateView.setText(Localization - .localizeUploadDate(activity, streamInfo.getUploadDate().offsetDateTime())); + protected void setupMetadata(final LayoutInflater inflater, final LinearLayout layout) { + final var uploadDate = streamInfo != null ? streamInfo.getUploadDate() : null; + if (uploadDate != null) { + final String formattedDate = activity.getString(R.string.upload_date_text, + Localization.formatDate(uploadDate)); + binding.detailUploadDateView.setText(formattedDate); } else { binding.detailUploadDateView.setVisibility(View.GONE); } diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.java index ce52c029dd2..816ee9bf49c 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.java @@ -90,7 +90,7 @@ protected Supplier getListHeaderSupplier() { // setup author name and comment date binding.authorName.setText(item.getUploaderName()); - binding.uploadDate.setText(Localization.relativeTimeOrTextual( + binding.uploadDate.setText(Localization.formatRelativeTimeOrTextual( getContext(), item.getUploadDate(), item.getTextualUploadDate())); binding.authorTouchArea.setOnClickListener( v -> NavigationHelper.openCommentAuthorIfPresent(requireActivity(), item)); diff --git a/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentInfoItemHolder.java index a19831cc7e6..7c9deb4b935 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentInfoItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentInfoItemHolder.java @@ -82,11 +82,9 @@ public CommentInfoItemHolder(final InfoItemBuilder infoItemBuilder, @Override public void updateFromItem(final InfoItem infoItem, final HistoryRecordManager historyRecordManager) { - if (!(infoItem instanceof CommentsInfoItem)) { + if (!(infoItem instanceof CommentsInfoItem item)) { return; } - final CommentsInfoItem item = (CommentsInfoItem) infoItem; - // load the author avatar PicassoHelper.loadAvatar(item.getUploaderAvatars()).into(itemThumbnailView); @@ -104,12 +102,9 @@ public void updateFromItem(final InfoItem infoItem, // setup the top row, with pinned icon, author name and comment date itemPinnedView.setVisibility(item.isPinned() ? View.VISIBLE : View.GONE); final String uploaderName = Localization.localizeUserName(item.getUploaderName()); - itemTitleView.setText(Localization.concatenateStrings( - uploaderName, - Localization.relativeTimeOrTextual( - itemBuilder.getContext(), - item.getUploadDate(), - item.getTextualUploadDate()))); + final String relativeTime = Localization.formatRelativeTimeOrTextual( + itemBuilder.getContext(), item.getUploadDate(), item.getTextualUploadDate()); + itemTitleView.setText(Localization.concatenateStrings(uploaderName, relativeTime)); // setup bottom row, with likes, heart and replies button itemLikesCountView.setText( 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 80f62eed3d1..dfd10e9807d 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 @@ -77,7 +77,7 @@ private String getStreamInfoDetailLine(final StreamInfoItem infoItem) { } } - final String uploadDate = Localization.relativeTimeOrTextual(itemBuilder.getContext(), + final String uploadDate = Localization.formatRelativeTimeOrTextual(itemBuilder.getContext(), infoItem.getUploadDate(), infoItem.getTextualUploadDate()); if (!TextUtils.isEmpty(uploadDate)) { diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/FeedDatabaseManager.kt b/app/src/main/java/org/schabi/newpipe/local/feed/FeedDatabaseManager.kt index ed65d4048e8..60fb5b09c32 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/FeedDatabaseManager.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/FeedDatabaseManager.kt @@ -18,9 +18,9 @@ import org.schabi.newpipe.database.subscription.NotificationMode import org.schabi.newpipe.extractor.stream.StreamInfoItem import org.schabi.newpipe.extractor.stream.StreamType import org.schabi.newpipe.local.subscription.FeedGroupIcon +import java.time.Instant import java.time.LocalDate -import java.time.OffsetDateTime -import java.time.ZoneOffset +import java.time.ZoneId class FeedDatabaseManager(context: Context) { private val database = NewPipeDatabase.getInstance(context) @@ -32,8 +32,7 @@ class FeedDatabaseManager(context: Context) { /** * Only items that are newer than this will be saved. */ - val FEED_OLDEST_ALLOWED_DATE: OffsetDateTime = LocalDate.now().minusWeeks(13) - .atStartOfDay().atOffset(ZoneOffset.UTC) + val FEED_OLDEST_ALLOWED_DATE: LocalDate = LocalDate.now().minusWeeks(13) } fun groups() = feedGroupTable.getAll() @@ -50,27 +49,27 @@ class FeedDatabaseManager(context: Context) { groupId, includePlayedStreams, includePartiallyPlayedStreams, - if (includeFutureStreams) null else OffsetDateTime.now() + if (includeFutureStreams) null else Instant.now() ) } - fun outdatedSubscriptions(outdatedThreshold: OffsetDateTime) = feedTable.getAllOutdated(outdatedThreshold) + fun getOutdatedSubscriptions(outdatedThreshold: Instant) = feedTable.getAllOutdated(outdatedThreshold) - fun outdatedSubscriptionsWithNotificationMode( - outdatedThreshold: OffsetDateTime, + fun getOutdatedSubscriptionsWithNotificationMode( + outdatedThreshold: Instant, @NotificationMode notificationMode: Int ) = feedTable.getOutdatedWithNotificationMode(outdatedThreshold, notificationMode) - fun notLoadedCount(groupId: Long = FeedGroupEntity.GROUP_ALL_ID): Flowable { + fun getNotLoadedCount(groupId: Long = FeedGroupEntity.GROUP_ALL_ID): Flowable { return when (groupId) { - FeedGroupEntity.GROUP_ALL_ID -> feedTable.notLoadedCount() - else -> feedTable.notLoadedCountForGroup(groupId) + FeedGroupEntity.GROUP_ALL_ID -> feedTable.getNotLoadedCount() + else -> feedTable.getNotLoadedCountForGroup(groupId) } } - fun outdatedSubscriptionsForGroup( + fun getOutdatedSubscriptionsForGroup( groupId: Long = FeedGroupEntity.GROUP_ALL_ID, - outdatedThreshold: OffsetDateTime + outdatedThreshold: Instant ) = feedTable.getAllOutdatedForGroup(groupId, outdatedThreshold) fun markAsOutdated(subscriptionId: Long) = feedTable @@ -83,17 +82,14 @@ class FeedDatabaseManager(context: Context) { fun upsertAll( subscriptionId: Long, items: List, - oldestAllowedDate: OffsetDateTime = FEED_OLDEST_ALLOWED_DATE + oldestAllowedDate: LocalDate = FEED_OLDEST_ALLOWED_DATE ) { - val itemsToInsert = ArrayList() - loop@ for (streamItem in items) { - val uploadDate = streamItem.uploadDate - - itemsToInsert += when { - uploadDate == null && streamItem.streamType == StreamType.LIVE_STREAM -> streamItem - uploadDate != null && uploadDate.offsetDateTime() >= oldestAllowedDate -> streamItem - else -> continue@loop - } + val zoneId = ZoneId.systemDefault() + val itemsToInsert = items.filter { + val uploadDate = it.uploadDate?.let { LocalDate.ofInstant(it.instant, zoneId) } + + (uploadDate == null && it.streamType == StreamType.LIVE_STREAM) || + (uploadDate != null && uploadDate >= oldestAllowedDate) } feedTable.unlinkOldLivestreams(subscriptionId) @@ -107,12 +103,13 @@ class FeedDatabaseManager(context: Context) { } feedTable.setLastUpdatedForSubscription( - FeedLastUpdatedEntity(subscriptionId, OffsetDateTime.now(ZoneOffset.UTC)) + FeedLastUpdatedEntity(subscriptionId, Instant.now()) ) } - fun removeOrphansOrOlderStreams(oldestAllowedDate: OffsetDateTime = FEED_OLDEST_ALLOWED_DATE) { - feedTable.unlinkStreamsOlderThan(oldestAllowedDate) + fun removeOrphansOrOlderStreams(oldestAllowedDate: LocalDate = FEED_OLDEST_ALLOWED_DATE) { + val instant = oldestAllowedDate.atStartOfDay(ZoneId.systemDefault()).toInstant() + feedTable.unlinkStreamsOlderThan(instant) streamTable.deleteOrphans() } @@ -177,10 +174,10 @@ class FeedDatabaseManager(context: Context) { .observeOn(AndroidSchedulers.mainThread()) } - fun oldestSubscriptionUpdate(groupId: Long): Flowable> { + fun oldestSubscriptionUpdate(groupId: Long): Flowable> { return when (groupId) { - FeedGroupEntity.GROUP_ALL_ID -> feedTable.oldestSubscriptionUpdateFromAll() - else -> feedTable.oldestSubscriptionUpdate(groupId) + FeedGroupEntity.GROUP_ALL_ID -> feedTable.getOldestSubscriptionUpdateFromAll() + else -> feedTable.getOldestSubscriptionUpdate(groupId) } } } diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt b/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt index 91f98f5d298..37b845e8d9c 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt @@ -81,7 +81,7 @@ import org.schabi.newpipe.util.ThemeHelper.getGridSpanCountStreams import org.schabi.newpipe.util.ThemeHelper.getItemViewMode import org.schabi.newpipe.util.ThemeHelper.resolveDrawable import org.schabi.newpipe.util.ThemeHelper.shouldUseGridLayout -import java.time.OffsetDateTime +import java.time.Instant import java.util.function.Consumer class FeedFragment : BaseStateFragment() { @@ -95,7 +95,7 @@ class FeedFragment : BaseStateFragment() { private var groupId = FeedGroupEntity.GROUP_ALL_ID private var groupName = "" - private var oldestSubscriptionUpdate: OffsetDateTime? = null + private var oldestSubscriptionUpdate: Instant? = null private lateinit var groupAdapter: GroupieAdapter @@ -415,8 +415,8 @@ class FeedFragment : BaseStateFragment() { val oldOldestSubscriptionUpdate = oldestSubscriptionUpdate groupAdapter.updateAsync(loadedState.items, false) { - oldOldestSubscriptionUpdate?.run { - highlightNewItemsAfter(oldOldestSubscriptionUpdate) + oldOldestSubscriptionUpdate?.let { + highlightNewItemsAfter(it) } } @@ -543,14 +543,14 @@ class FeedFragment : BaseStateFragment() { private fun updateRefreshViewState() { feedBinding.refreshText.text = getString( R.string.feed_oldest_subscription_update, - oldestSubscriptionUpdate?.let { Localization.relativeTime(it) } ?: "—" + oldestSubscriptionUpdate?.let { Localization.formatRelativeTime(it) } ?: "—" ) } /** * Highlights all items that are after the specified time */ - private fun highlightNewItemsAfter(updateTime: OffsetDateTime) { + private fun highlightNewItemsAfter(updateTime: Instant) { var highlightCount = 0 var doCheck = true @@ -563,8 +563,9 @@ class FeedFragment : BaseStateFragment() { resolveDrawable(ctx, android.R.attr.selectableItemBackground) } if (doCheck) { + val instant = item.streamWithState.stream.uploadInstant // If the uploadDate is null or true we should highlight the item - if (item.streamWithState.stream.uploadDate?.isAfter(updateTime) != false) { + if (instant != null && instant > updateTime) { highlightCount++ typeface = Typeface.DEFAULT_BOLD diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/FeedState.kt b/app/src/main/java/org/schabi/newpipe/local/feed/FeedState.kt index 665ebbe4396..da0fd3c1361 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/FeedState.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/FeedState.kt @@ -2,7 +2,7 @@ package org.schabi.newpipe.local.feed import androidx.annotation.StringRes import org.schabi.newpipe.local.feed.item.StreamItem -import java.time.OffsetDateTime +import java.time.Instant sealed class FeedState { data class ProgressState( @@ -13,7 +13,7 @@ sealed class FeedState { data class LoadedState( val items: List, - val oldestUpdate: OffsetDateTime?, + val oldestUpdate: Instant?, val notLoadedCount: Long, val itemsErrors: List ) : FeedState() diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/FeedViewModel.kt b/app/src/main/java/org/schabi/newpipe/local/feed/FeedViewModel.kt index 728570b17e0..c22a245aaf3 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/FeedViewModel.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/FeedViewModel.kt @@ -25,7 +25,7 @@ import org.schabi.newpipe.local.feed.service.FeedEventManager.Event.IdleEvent import org.schabi.newpipe.local.feed.service.FeedEventManager.Event.ProgressEvent import org.schabi.newpipe.local.feed.service.FeedEventManager.Event.SuccessResultEvent import org.schabi.newpipe.util.DEFAULT_THROTTLE_TIMEOUT -import java.time.OffsetDateTime +import java.time.Instant import java.util.concurrent.TimeUnit class FeedViewModel( @@ -61,11 +61,11 @@ class FeedViewModel( showPlayedItemsFlowable, showPartiallyPlayedItemsFlowable, showFutureItemsFlowable, - feedDatabaseManager.notLoadedCount(groupId), + feedDatabaseManager.getNotLoadedCount(groupId), feedDatabaseManager.oldestSubscriptionUpdate(groupId), Function6 { t1: FeedEventManager.Event, t2: Boolean, t3: Boolean, t4: Boolean, - t5: Long, t6: List -> + t5: Long, t6: List -> return@Function6 CombineResultEventHolder(t1, t2, t3, t4, t5, t6.firstOrNull()) } ) @@ -109,14 +109,14 @@ class FeedViewModel( val t3: Boolean, val t4: Boolean, val t5: Long, - val t6: OffsetDateTime? + val t6: Instant? ) private data class CombineResultDataHolder( val t1: FeedEventManager.Event, val t2: List, val t3: Long, - val t4: OffsetDateTime? + val t4: Instant? ) fun setSaveShowPlayedItems(showPlayedItems: Boolean) { 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 4a071d6df75..790a25f16da 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 @@ -137,9 +137,9 @@ data class StreamItem( } private fun getFormattedRelativeUploadDate(context: Context): String? { - val uploadDate = stream.uploadDate + val uploadDate = stream.uploadInstant return if (uploadDate != null) { - var formattedRelativeTime = Localization.relativeTime(uploadDate) + var formattedRelativeTime = Localization.formatRelativeTime(uploadDate) if (MainActivity.DEBUG) { val key = context.getString(R.string.show_original_time_ago_key) diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadManager.kt b/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadManager.kt index 9b0f177d568..3aff7d87d47 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadManager.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadManager.kt @@ -27,8 +27,9 @@ import org.schabi.newpipe.util.ChannelTabHelper import org.schabi.newpipe.util.ExtractorHelper.getChannelInfo import org.schabi.newpipe.util.ExtractorHelper.getChannelTab import org.schabi.newpipe.util.ExtractorHelper.getMoreChannelTabItems -import java.time.OffsetDateTime -import java.time.ZoneOffset +import java.time.Instant +import java.time.LocalDate +import java.time.ZoneId import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicInteger @@ -69,26 +70,26 @@ class FeedLoadManager(private val context: Context) { ) val outdatedThreshold = if (ignoreOutdatedThreshold) { - OffsetDateTime.now(ZoneOffset.UTC) + Instant.now() } else { val thresholdOutdatedSeconds = defaultSharedPreferences.getStringSafe( context.getString(R.string.feed_update_threshold_key), context.getString(R.string.feed_update_threshold_default_value) - ).toInt() - OffsetDateTime.now(ZoneOffset.UTC).minusSeconds(thresholdOutdatedSeconds.toLong()) + ).toLong() + Instant.now().minusSeconds(thresholdOutdatedSeconds) } /** * subscriptions which have not been updated within the feed updated threshold */ val outdatedSubscriptions = when (groupId) { - FeedGroupEntity.GROUP_ALL_ID -> feedDatabaseManager.outdatedSubscriptions( + FeedGroupEntity.GROUP_ALL_ID -> feedDatabaseManager.getOutdatedSubscriptions( outdatedThreshold ) - GROUP_NOTIFICATION_ENABLED -> feedDatabaseManager.outdatedSubscriptionsWithNotificationMode( + GROUP_NOTIFICATION_ENABLED -> feedDatabaseManager.getOutdatedSubscriptionsWithNotificationMode( outdatedThreshold, NotificationMode.ENABLED ) - else -> feedDatabaseManager.outdatedSubscriptionsForGroup(groupId, outdatedThreshold) + else -> feedDatabaseManager.getOutdatedSubscriptionsForGroup(groupId, outdatedThreshold) } // like `currentProgress`, but counts the number of YouTube extractions that have begun, so @@ -319,15 +320,14 @@ class FeedLoadManager(private val context: Context) { } private fun filterNewStreams(list: List): List { + val zoneId = ZoneId.systemDefault() return list.filter { + // Streams older than this date are automatically removed from the feed. + // Therefore, streams which are not in the database, + // but older than this date, are considered old. + val date = it.uploadDate?.let { LocalDate.ofInstant(it.instant, zoneId) } !feedDatabaseManager.doesStreamExist(it) && - it.uploadDate != null && - // Streams older than this date are automatically removed from the feed. - // Therefore, streams which are not in the database, - // but older than this date, are considered old. - it.uploadDate!!.offsetDateTime().isAfter( - FeedDatabaseManager.FEED_OLDEST_ALLOWED_DATE - ) + date != null && date > FeedDatabaseManager.FEED_OLDEST_ALLOWED_DATE } } } diff --git a/app/src/main/java/org/schabi/newpipe/local/history/HistoryRecordManager.java b/app/src/main/java/org/schabi/newpipe/local/history/HistoryRecordManager.java index ed3cf548f96..624ab4e6070 100644 --- a/app/src/main/java/org/schabi/newpipe/local/history/HistoryRecordManager.java +++ b/app/src/main/java/org/schabi/newpipe/local/history/HistoryRecordManager.java @@ -47,8 +47,7 @@ import org.schabi.newpipe.player.playqueue.PlayQueueItem; import org.schabi.newpipe.util.ExtractorHelper; -import java.time.OffsetDateTime; -import java.time.ZoneOffset; +import java.time.Instant; import java.util.ArrayList; import java.util.List; @@ -96,7 +95,7 @@ public Maybe markAsWatched(final StreamInfoItem info) { return Maybe.empty(); } - final OffsetDateTime currentTime = OffsetDateTime.now(ZoneOffset.UTC); + final var currentTime = Instant.now(); return Maybe.fromCallable(() -> database.runInTransaction(() -> { final long streamId; final long duration; @@ -139,14 +138,14 @@ public Maybe onViewed(final StreamInfo info) { return Maybe.empty(); } - final OffsetDateTime currentTime = OffsetDateTime.now(ZoneOffset.UTC); + final var currentTime = Instant.now(); return Maybe.fromCallable(() -> database.runInTransaction(() -> { final long streamId = streamTable.upsert(new StreamEntity(info)); final StreamHistoryEntity latestEntry = streamHistoryTable.getLatestEntry(streamId); if (latestEntry != null) { streamHistoryTable.delete(latestEntry); - latestEntry.setAccessDate(currentTime); + latestEntry.setAccessInstant(currentTime); latestEntry.setRepeatCount(latestEntry.getRepeatCount() + 1); return streamHistoryTable.insert(latestEntry); } else { @@ -194,13 +193,13 @@ public Maybe onSearched(final int serviceId, final String search) { return Maybe.empty(); } - final OffsetDateTime currentTime = OffsetDateTime.now(ZoneOffset.UTC); + final var currentTime = Instant.now(); final SearchHistoryEntry newEntry = new SearchHistoryEntry(currentTime, serviceId, search); return Maybe.fromCallable(() -> database.runInTransaction(() -> { final SearchHistoryEntry latestEntry = searchHistoryTable.getLatestEntry(); if (latestEntry != null && latestEntry.hasEqualValues(newEntry)) { - latestEntry.setCreationDate(currentTime); + latestEntry.setCreationInstant(currentTime); return (long) searchHistoryTable.update(latestEntry); } else { return searchHistoryTable.insert(newEntry); diff --git a/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java index 3302e387ec5..058aac29155 100644 --- a/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java @@ -69,7 +69,7 @@ private List processResult(final List comparator; switch (sortMode) { case LAST_PLAYED: - comparator = Comparator.comparing(StreamStatisticsEntry::getLatestAccessDate); + comparator = Comparator.comparing(StreamStatisticsEntry::getLatestAccessInstant); break; case MOST_PLAYED: comparator = Comparator.comparingLong(StreamStatisticsEntry::getWatchCount); 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 150a35eb59c..2e1f09a9df5 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 @@ -74,7 +74,7 @@ private String getStreamInfoDetailLine(final StreamStatisticsEntry entry, return Localization.concatenateStrings( // watchCount Localization.shortViewCount(itemBuilder.getContext(), entry.getWatchCount()), - dateTimeFormatter.format(entry.getLatestAccessDate()), + dateTimeFormatter.format(entry.latestAccessInstant()), // serviceName ServiceHelper.getNameOfServiceById(entry.getStreamEntity().getServiceId())); } 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 b8cc26b414a..b8780c53547 100644 --- a/app/src/main/java/org/schabi/newpipe/util/Localization.java +++ b/app/src/main/java/org/schabi/newpipe/util/Localization.java @@ -2,7 +2,6 @@ import static org.schabi.newpipe.MainActivity.DEBUG; -import android.annotation.SuppressLint; import android.content.Context; import android.content.SharedPreferences; import android.content.res.Resources; @@ -34,7 +33,8 @@ import java.math.BigDecimal; import java.math.RoundingMode; import java.text.NumberFormat; -import java.time.OffsetDateTime; +import java.time.Instant; +import java.time.LocalDate; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.time.format.FormatStyle; @@ -130,16 +130,11 @@ public static String localizeNumber(final double number) { return NumberFormat.getInstance(getAppLocale()).format(number); } - public static String formatDate(@NonNull final OffsetDateTime offsetDateTime) { - return DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM) - .withLocale(getAppLocale()) - .format(offsetDateTime.atZoneSameInstant(ZoneId.systemDefault())); - } - - @SuppressLint("StringFormatInvalid") - public static String localizeUploadDate(@NonNull final Context context, - @NonNull final OffsetDateTime offsetDateTime) { - return context.getString(R.string.upload_date_text, formatDate(offsetDateTime)); + @NonNull + public static String formatDate(@NonNull final DateWrapper dateWrapper) { + final var localDate = LocalDate.ofInstant(dateWrapper.getInstant(), ZoneId.systemDefault()); + return DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).withLocale(getAppLocale()) + .format(localDate); } public static String localizeViewCount(@NonNull final Context context, final long viewCount) { @@ -379,8 +374,8 @@ public static PrettyTime resolvePrettyTime() { return new PrettyTime(getAppLocale()); } - public static String relativeTime(@NonNull final OffsetDateTime offsetDateTime) { - return prettyTime.formatUnrounded(offsetDateTime); + public static String formatRelativeTime(@NonNull final Instant instant) { + return prettyTime.formatUnrounded(instant); } /** @@ -389,23 +384,23 @@ public static String relativeTime(@NonNull final OffsetDateTime offsetDateTime) * @param parsed the textual date or time ago parsed by NewPipeExtractor, or {@code null} if * the extractor could not parse it * @param textual the original textual date or time ago string as provided by services - * @return {@link #relativeTime(OffsetDateTime)} is used if {@code parsed != null}, otherwise + * @return {@link #formatRelativeTime(Instant)} is used if {@code parsed != null}, otherwise * {@code textual} is returned. If in debug mode, {@code context != null}, * {@code parsed != null} and the relevant setting is enabled, {@code textual} will * be appended to the returned string for debugging purposes. */ @Nullable - public static String relativeTimeOrTextual(@Nullable final Context context, - @Nullable final DateWrapper parsed, - @Nullable final String textual) { + public static String formatRelativeTimeOrTextual(@Nullable final Context context, + @Nullable final DateWrapper parsed, + @Nullable final String textual) { if (parsed == null) { return textual; } else if (DEBUG && context != null && PreferenceManager .getDefaultSharedPreferences(context) .getBoolean(context.getString(R.string.show_original_time_ago_key), false)) { - return relativeTime(parsed.offsetDateTime()) + " (" + textual + ")"; + return formatRelativeTime(parsed.getInstant()) + " (" + textual + ")"; } else { - return relativeTime(parsed.offsetDateTime()); + return formatRelativeTime(parsed.getInstant()); } } diff --git a/app/src/test/java/org/schabi/newpipe/util/LocalizationTest.kt b/app/src/test/java/org/schabi/newpipe/util/LocalizationTest.kt index ab639695160..f1f354f5491 100644 --- a/app/src/test/java/org/schabi/newpipe/util/LocalizationTest.kt +++ b/app/src/test/java/org/schabi/newpipe/util/LocalizationTest.kt @@ -3,25 +3,29 @@ package org.schabi.newpipe.util import org.junit.Assert.assertEquals import org.junit.Test import org.ocpsoft.prettytime.PrettyTime +import java.time.Instant import java.time.LocalDate -import java.time.OffsetDateTime -import java.time.ZoneOffset +import java.time.Month +import java.time.ZoneId import java.util.Locale class LocalizationTest { @Test(expected = NullPointerException::class) - fun `relativeTime() must fail without initializing pretty time`() { - Localization.relativeTime(OffsetDateTime.of(2021, 1, 6, 0, 0, 0, 0, ZoneOffset.UTC)) + fun `formatRelativeTime() must fail without initializing pretty time`() { + val instant = Instant.now() + Localization.formatRelativeTime(instant) } @Test - fun `relativeTime() with a OffsetDateTime must work`() { - val prettyTime = PrettyTime(LocalDate.of(2021, 1, 1), ZoneOffset.UTC) + fun `formatRelativeTime() with an Instant must work`() { + val zoneId = ZoneId.systemDefault() + val date = LocalDate.of(2021, Month.JANUARY, 1) + val prettyTime = PrettyTime(date, zoneId) prettyTime.locale = Locale.ENGLISH Localization.initPrettyTime(prettyTime) - val offset = OffsetDateTime.of(2021, 1, 6, 0, 0, 0, 0, ZoneOffset.UTC) - val actual = Localization.relativeTime(offset) + val instant = date.plusDays(5).atStartOfDay(zoneId).toInstant() + val actual = Localization.formatRelativeTime(instant) assertEquals("5 days from now", actual) } From 903922c2b02a33fa5437db295f3f2c25e6da0bc3 Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Thu, 11 Sep 2025 07:14:43 +0530 Subject: [PATCH 2/9] Create history stream detail message using Core-i18n --- app/build.gradle | 1 + .../newpipe/local/LocalItemListAdapter.java | 22 +++++------- .../LocalBookmarkPlaylistItemHolder.java | 10 ++---- .../newpipe/local/holder/LocalItemHolder.java | 5 +-- .../local/holder/LocalPlaylistItemHolder.java | 12 +++---- .../holder/LocalPlaylistStreamItemHolder.java | 9 ++--- .../LocalStatisticStreamItemHolder.java | 35 +++++++++++-------- .../local/holder/PlaylistItemHolder.java | 5 +-- .../RemoteBookmarkPlaylistItemHolder.java | 10 ++---- .../holder/RemotePlaylistItemHolder.java | 12 +++---- app/src/main/res/values/strings.xml | 3 ++ 11 files changed, 51 insertions(+), 73 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 38765476a23..77eec0dd573 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -229,6 +229,7 @@ dependencies { implementation 'androidx.cardview:cardview:1.0.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation 'androidx.core:core-ktx:1.12.0' + implementation 'androidx.core:core-i18n:1.0.0' implementation 'androidx.documentfile:documentfile:1.0.1' implementation 'androidx.fragment:fragment-ktx:1.6.2' implementation "androidx.lifecycle:lifecycle-livedata-ktx:${androidxLifecycleVersion}" diff --git a/app/src/main/java/org/schabi/newpipe/local/LocalItemListAdapter.java b/app/src/main/java/org/schabi/newpipe/local/LocalItemListAdapter.java index b33619dea7a..3bb8ab1e534 100644 --- a/app/src/main/java/org/schabi/newpipe/local/LocalItemListAdapter.java +++ b/app/src/main/java/org/schabi/newpipe/local/LocalItemListAdapter.java @@ -30,11 +30,8 @@ import org.schabi.newpipe.local.holder.RemotePlaylistGridItemHolder; import org.schabi.newpipe.local.holder.RemotePlaylistItemHolder; import org.schabi.newpipe.util.FallbackViewHolder; -import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.OnClickGesture; -import java.time.format.DateTimeFormatter; -import java.time.format.FormatStyle; import java.util.ArrayList; import java.util.List; @@ -85,7 +82,6 @@ public class LocalItemListAdapter extends RecyclerView.Adapter localItems; private final HistoryRecordManager recordManager; - private final DateTimeFormatter dateTimeFormatter; private boolean showFooter = false; private View header = null; @@ -97,8 +93,6 @@ public LocalItemListAdapter(final Context context) { recordManager = new HistoryRecordManager(context); localItemBuilder = new LocalItemBuilder(context); localItems = new ArrayList<>(); - dateTimeFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT) - .withLocale(Localization.getPreferredLocale(context)); } public void setSelectedListener(final OnClickGesture listener) { @@ -364,19 +358,19 @@ public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, int + "position = [" + position + "]"); } - if (holder instanceof LocalItemHolder) { + if (holder instanceof LocalItemHolder localItemHolder) { // If header isn't null, offset the items by -1 if (header != null) { position--; } - ((LocalItemHolder) holder) - .updateFromItem(localItems.get(position), recordManager, dateTimeFormatter); - } else if (holder instanceof HeaderFooterHolder && position == 0 && header != null) { - ((HeaderFooterHolder) holder).view = header; - } else if (holder instanceof HeaderFooterHolder && position == sizeConsideringHeader() - && footer != null && showFooter) { - ((HeaderFooterHolder) holder).view = footer; + localItemHolder.updateFromItem(localItems.get(position), recordManager); + } else if (holder instanceof HeaderFooterHolder headerFooterHolder) { + if (position == 0 && header != null) { + headerFooterHolder.view = header; + } else if (footer != null && showFooter) { + headerFooterHolder.view = footer; + } } } diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/LocalBookmarkPlaylistItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/LocalBookmarkPlaylistItemHolder.java index 16130009b6e..7b6e939b537 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/LocalBookmarkPlaylistItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/LocalBookmarkPlaylistItemHolder.java @@ -10,8 +10,6 @@ import org.schabi.newpipe.local.LocalItemBuilder; import org.schabi.newpipe.local.history.HistoryRecordManager; -import java.time.format.DateTimeFormatter; - public class LocalBookmarkPlaylistItemHolder extends LocalPlaylistItemHolder { private final View itemHandleView; @@ -28,16 +26,14 @@ public LocalBookmarkPlaylistItemHolder(final LocalItemBuilder infoItemBuilder, @Override public void updateFromItem(final LocalItem localItem, - final HistoryRecordManager historyRecordManager, - final DateTimeFormatter dateTimeFormatter) { - if (!(localItem instanceof PlaylistMetadataEntry)) { + final HistoryRecordManager historyRecordManager) { + if (!(localItem instanceof PlaylistMetadataEntry item)) { return; } - final PlaylistMetadataEntry item = (PlaylistMetadataEntry) localItem; itemHandleView.setOnTouchListener(getOnTouchListener(item)); - super.updateFromItem(localItem, historyRecordManager, dateTimeFormatter); + super.updateFromItem(localItem, historyRecordManager); } private View.OnTouchListener getOnTouchListener(final PlaylistMetadataEntry item) { diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/LocalItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/LocalItemHolder.java index a093d93e1a0..363081f0e12 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/LocalItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/LocalItemHolder.java @@ -9,8 +9,6 @@ import org.schabi.newpipe.local.LocalItemBuilder; import org.schabi.newpipe.local.history.HistoryRecordManager; -import java.time.format.DateTimeFormatter; - /* * Created by Christian Schabesberger on 12.02.17. * @@ -40,8 +38,7 @@ public LocalItemHolder(final LocalItemBuilder itemBuilder, final int layoutId, this.itemBuilder = itemBuilder; } - public abstract void updateFromItem(LocalItem item, HistoryRecordManager historyRecordManager, - DateTimeFormatter dateTimeFormatter); + public abstract void updateFromItem(LocalItem item, HistoryRecordManager historyRecordManager); public void updateState(final LocalItem localItem, final HistoryRecordManager historyRecordManager) { } diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistItemHolder.java index 336f5cfe30b..c43debc0a4e 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistItemHolder.java @@ -8,10 +8,8 @@ import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry; import org.schabi.newpipe.local.LocalItemBuilder; import org.schabi.newpipe.local.history.HistoryRecordManager; -import org.schabi.newpipe.util.image.PicassoHelper; import org.schabi.newpipe.util.Localization; - -import java.time.format.DateTimeFormatter; +import org.schabi.newpipe.util.image.PicassoHelper; public class LocalPlaylistItemHolder extends PlaylistItemHolder { @@ -28,12 +26,10 @@ public LocalPlaylistItemHolder(final LocalItemBuilder infoItemBuilder, final Vie @Override public void updateFromItem(final LocalItem localItem, - final HistoryRecordManager historyRecordManager, - final DateTimeFormatter dateTimeFormatter) { - if (!(localItem instanceof PlaylistMetadataEntry)) { + final HistoryRecordManager historyRecordManager) { + if (!(localItem instanceof PlaylistMetadataEntry item)) { return; } - final PlaylistMetadataEntry item = (PlaylistMetadataEntry) localItem; itemTitleView.setText(item.name); itemStreamCountView.setText(Localization.localizeStreamCountMini( @@ -49,6 +45,6 @@ public void updateFromItem(final LocalItem localItem, itemView.setAlpha(1.0f); } - super.updateFromItem(localItem, historyRecordManager, dateTimeFormatter); + super.updateFromItem(localItem, historyRecordManager); } } diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistStreamItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistStreamItemHolder.java index 89a714fd7f6..53f6cc9558a 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistStreamItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistStreamItemHolder.java @@ -16,11 +16,10 @@ import org.schabi.newpipe.local.history.HistoryRecordManager; import org.schabi.newpipe.util.DependentPreferenceHelper; import org.schabi.newpipe.util.Localization; -import org.schabi.newpipe.util.image.PicassoHelper; import org.schabi.newpipe.util.ServiceHelper; +import org.schabi.newpipe.util.image.PicassoHelper; import org.schabi.newpipe.views.AnimatedProgressBar; -import java.time.format.DateTimeFormatter; import java.util.concurrent.TimeUnit; public class LocalPlaylistStreamItemHolder extends LocalItemHolder { @@ -50,12 +49,10 @@ public LocalPlaylistStreamItemHolder(final LocalItemBuilder infoItemBuilder, @Override public void updateFromItem(final LocalItem localItem, - final HistoryRecordManager historyRecordManager, - final DateTimeFormatter dateTimeFormatter) { - if (!(localItem instanceof PlaylistStreamEntry)) { + final HistoryRecordManager historyRecordManager) { + if (!(localItem instanceof PlaylistStreamEntry item)) { return; } - final PlaylistStreamEntry item = (PlaylistStreamEntry) localItem; itemVideoTitleView.setText(item.getStreamEntity().getTitle()); itemAdditionalDetailsView.setText(Localization 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 2e1f09a9df5..ce126466064 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 @@ -1,12 +1,16 @@ package org.schabi.newpipe.local.holder; +import static org.schabi.newpipe.util.ServiceHelper.getNameOfServiceById; + import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; +import androidx.core.i18n.MessageFormat; import org.schabi.newpipe.R; import org.schabi.newpipe.database.LocalItem; @@ -17,10 +21,11 @@ import org.schabi.newpipe.util.DependentPreferenceHelper; import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.image.PicassoHelper; -import org.schabi.newpipe.util.ServiceHelper; import org.schabi.newpipe.views.AnimatedProgressBar; -import java.time.format.DateTimeFormatter; +import java.time.ZoneId; +import java.util.GregorianCalendar; +import java.util.Map; import java.util.concurrent.TimeUnit; /* @@ -69,24 +74,24 @@ public LocalStatisticStreamItemHolder(final LocalItemBuilder itemBuilder, itemProgressView = itemView.findViewById(R.id.itemProgressView); } - private String getStreamInfoDetailLine(final StreamStatisticsEntry entry, - final DateTimeFormatter dateTimeFormatter) { - return Localization.concatenateStrings( - // watchCount - Localization.shortViewCount(itemBuilder.getContext(), entry.getWatchCount()), - dateTimeFormatter.format(entry.latestAccessInstant()), - // serviceName - ServiceHelper.getNameOfServiceById(entry.getStreamEntity().getServiceId())); + @NonNull + private String getStreamInfoDetailLine(@NonNull final StreamStatisticsEntry entry) { + final var context = itemBuilder.getContext(); + final var zdt = entry.getLatestAccessInstant().atZone(ZoneId.systemDefault()); + final Map args = Map.of( + "formatted_views", Localization.shortViewCount(context, entry.getWatchCount()), + "last_viewed_date", GregorianCalendar.from(zdt), + "service_name", getNameOfServiceById(entry.getStreamEntity().getServiceId()) + ); + return MessageFormat.format(context, R.string.history_detail_line, args); } @Override public void updateFromItem(final LocalItem localItem, - final HistoryRecordManager historyRecordManager, - final DateTimeFormatter dateTimeFormatter) { - if (!(localItem instanceof StreamStatisticsEntry)) { + final HistoryRecordManager historyRecordManager) { + if (!(localItem instanceof StreamStatisticsEntry item)) { return; } - final StreamStatisticsEntry item = (StreamStatisticsEntry) localItem; itemVideoTitleView.setText(item.getStreamEntity().getTitle()); itemUploaderView.setText(item.getStreamEntity().getUploader()); @@ -113,7 +118,7 @@ public void updateFromItem(final LocalItem localItem, } if (itemAdditionalDetails != null) { - itemAdditionalDetails.setText(getStreamInfoDetailLine(item, dateTimeFormatter)); + itemAdditionalDetails.setText(getStreamInfoDetailLine(item)); } // Default thumbnail is shown on error, while loading and if the url is empty diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/PlaylistItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/PlaylistItemHolder.java index e8c53161e9b..7dbefec12d4 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/PlaylistItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/PlaylistItemHolder.java @@ -9,8 +9,6 @@ import org.schabi.newpipe.local.LocalItemBuilder; import org.schabi.newpipe.local.history.HistoryRecordManager; -import java.time.format.DateTimeFormatter; - public abstract class PlaylistItemHolder extends LocalItemHolder { public final ImageView itemThumbnailView; final TextView itemStreamCountView; @@ -33,8 +31,7 @@ public PlaylistItemHolder(final LocalItemBuilder infoItemBuilder, final ViewGrou @Override public void updateFromItem(final LocalItem localItem, - final HistoryRecordManager historyRecordManager, - final DateTimeFormatter dateTimeFormatter) { + final HistoryRecordManager historyRecordManager) { itemView.setOnClickListener(view -> { if (itemBuilder.getOnItemSelectedListener() != null) { itemBuilder.getOnItemSelectedListener().selected(localItem); diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/RemoteBookmarkPlaylistItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/RemoteBookmarkPlaylistItemHolder.java index 6d61d1e08bf..b1dfc8cc504 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/RemoteBookmarkPlaylistItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/RemoteBookmarkPlaylistItemHolder.java @@ -10,8 +10,6 @@ import org.schabi.newpipe.local.LocalItemBuilder; import org.schabi.newpipe.local.history.HistoryRecordManager; -import java.time.format.DateTimeFormatter; - public class RemoteBookmarkPlaylistItemHolder extends RemotePlaylistItemHolder { private final View itemHandleView; @@ -28,16 +26,14 @@ public RemoteBookmarkPlaylistItemHolder(final LocalItemBuilder infoItemBuilder, @Override public void updateFromItem(final LocalItem localItem, - final HistoryRecordManager historyRecordManager, - final DateTimeFormatter dateTimeFormatter) { - if (!(localItem instanceof PlaylistRemoteEntity)) { + final HistoryRecordManager historyRecordManager) { + if (!(localItem instanceof PlaylistRemoteEntity item)) { return; } - final PlaylistRemoteEntity item = (PlaylistRemoteEntity) localItem; itemHandleView.setOnTouchListener(getOnTouchListener(item)); - super.updateFromItem(localItem, historyRecordManager, dateTimeFormatter); + super.updateFromItem(localItem, historyRecordManager); } private View.OnTouchListener getOnTouchListener(final PlaylistRemoteEntity item) { diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/RemotePlaylistItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/RemotePlaylistItemHolder.java index 7657320634c..18b4a4bd7bc 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/RemotePlaylistItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/RemotePlaylistItemHolder.java @@ -8,10 +8,8 @@ import org.schabi.newpipe.local.LocalItemBuilder; import org.schabi.newpipe.local.history.HistoryRecordManager; import org.schabi.newpipe.util.Localization; -import org.schabi.newpipe.util.image.PicassoHelper; import org.schabi.newpipe.util.ServiceHelper; - -import java.time.format.DateTimeFormatter; +import org.schabi.newpipe.util.image.PicassoHelper; public class RemotePlaylistItemHolder extends PlaylistItemHolder { @@ -27,12 +25,10 @@ public RemotePlaylistItemHolder(final LocalItemBuilder infoItemBuilder, @Override public void updateFromItem(final LocalItem localItem, - final HistoryRecordManager historyRecordManager, - final DateTimeFormatter dateTimeFormatter) { - if (!(localItem instanceof PlaylistRemoteEntity)) { + final HistoryRecordManager historyRecordManager) { + if (!(localItem instanceof PlaylistRemoteEntity item)) { return; } - final PlaylistRemoteEntity item = (PlaylistRemoteEntity) localItem; itemTitleView.setText(item.getName()); itemStreamCountView.setText(Localization.localizeStreamCountMini( @@ -47,6 +43,6 @@ public void updateFromItem(final LocalItem localItem, PicassoHelper.loadPlaylistThumbnail(item.getThumbnailUrl()).into(itemThumbnailView); - super.updateFromItem(localItem, historyRecordManager, dateTimeFormatter); + super.updateFromItem(localItem, historyRecordManager); } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 147c88938a9..fe51179c673 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -882,4 +882,7 @@ HTTP error 403 received from server while playing, likely caused by an IP ban or streaming URL deobfuscation issues %1$s refused to provide data, asking for a login to confirm the requester is not a bot.\n\nYour IP might have been temporarily banned by %1$s, you can wait some time or switch to a different IP (for example by turning on/off a VPN, or by switching from WiFi to mobile data). This content is not available for the currently selected content country.\n\nChange your selection from \"Settings > Content > Default content country\". + + {formatted_views} • {last_viewed_date, date, short} • {service_name} + From 4b5bf11a04b2f2de59f0a1ae64a6161f44f1e20a Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Sat, 13 Sep 2025 06:59:18 +0530 Subject: [PATCH 3/9] Remove constant --- .../newpipe/local/feed/FeedDatabaseManager.kt | 17 ++++------------- .../local/feed/service/FeedLoadManager.kt | 6 +++--- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/FeedDatabaseManager.kt b/app/src/main/java/org/schabi/newpipe/local/feed/FeedDatabaseManager.kt index 60fb5b09c32..aae713f84a2 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/FeedDatabaseManager.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/FeedDatabaseManager.kt @@ -28,13 +28,6 @@ class FeedDatabaseManager(context: Context) { private val feedGroupTable = database.feedGroupDAO() private val streamTable = database.streamDAO() - companion object { - /** - * Only items that are newer than this will be saved. - */ - val FEED_OLDEST_ALLOWED_DATE: LocalDate = LocalDate.now().minusWeeks(13) - } - fun groups() = feedGroupTable.getAll() fun database() = database @@ -79,11 +72,8 @@ class FeedDatabaseManager(context: Context) { return streamTable.exists(stream.serviceId, stream.url) } - fun upsertAll( - subscriptionId: Long, - items: List, - oldestAllowedDate: LocalDate = FEED_OLDEST_ALLOWED_DATE - ) { + fun upsertAll(subscriptionId: Long, items: List) { + val oldestAllowedDate = LocalDate.now().minusWeeks(13) val zoneId = ZoneId.systemDefault() val itemsToInsert = items.filter { val uploadDate = it.uploadDate?.let { LocalDate.ofInstant(it.instant, zoneId) } @@ -107,7 +97,8 @@ class FeedDatabaseManager(context: Context) { ) } - fun removeOrphansOrOlderStreams(oldestAllowedDate: LocalDate = FEED_OLDEST_ALLOWED_DATE) { + fun removeOrphansOrOlderStreams() { + val oldestAllowedDate = LocalDate.now().minusWeeks(13) val instant = oldestAllowedDate.atStartOfDay(ZoneId.systemDefault()).toInstant() feedTable.unlinkStreamsOlderThan(instant) streamTable.deleteOrphans() diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadManager.kt b/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadManager.kt index 3aff7d87d47..63984acd4ac 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadManager.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadManager.kt @@ -254,7 +254,7 @@ class FeedLoadManager(private val context: Context) { * Keep the feed and the stream tables small * to reduce loading times when trying to display the feed. *
- * Remove streams from the feed which are older than [FeedDatabaseManager.FEED_OLDEST_ALLOWED_DATE]. + * Remove streams from the feed which are older than 13 weeks. * Remove streams from the database which are not linked / used by any table. */ private fun postProcessFeed() = Completable.fromRunnable { @@ -321,13 +321,13 @@ class FeedLoadManager(private val context: Context) { private fun filterNewStreams(list: List): List { val zoneId = ZoneId.systemDefault() + val oldestAllowedDate = LocalDate.now().minusWeeks(13) return list.filter { // Streams older than this date are automatically removed from the feed. // Therefore, streams which are not in the database, // but older than this date, are considered old. val date = it.uploadDate?.let { LocalDate.ofInstant(it.instant, zoneId) } - !feedDatabaseManager.doesStreamExist(it) && - date != null && date > FeedDatabaseManager.FEED_OLDEST_ALLOWED_DATE + !feedDatabaseManager.doesStreamExist(it) && date != null && date > oldestAllowedDate } } } From cf4781e742d774be1277d88b96b0196c7c168ac8 Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Sat, 13 Sep 2025 07:23:18 +0530 Subject: [PATCH 4/9] Update extractor --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 77eec0dd573..5c7c12c1f5e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -214,7 +214,7 @@ dependencies { // the corresponding commit hash, since JitPack sometimes deletes artifacts. // If there’s already a git hash, just add more of it to the end (or remove a letter) // to cause jitpack to regenerate the artifact. - implementation 'com.github.TeamNewPipe:NewPipeExtractor:0023b22095a2d62a60cdfc87f4b5cd85c8b266c3' + implementation 'com.github.TeamNewPipe:NewPipeExtractor:c79afc00764a5f2deb8f73e78244d93920cb3456' implementation 'com.github.TeamNewPipe:NoNonsense-FilePicker:5.0.0' /** Checkstyle **/ From e4c7e4b24190cd152b669e489c191b50538dab13 Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Sat, 20 Sep 2025 11:45:30 +0530 Subject: [PATCH 5/9] Use new DateWrapper method --- .../org/schabi/newpipe/local/feed/FeedDatabaseManager.kt | 3 +-- .../schabi/newpipe/local/feed/service/FeedLoadManager.kt | 4 +--- app/src/main/java/org/schabi/newpipe/util/Localization.java | 6 +----- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/FeedDatabaseManager.kt b/app/src/main/java/org/schabi/newpipe/local/feed/FeedDatabaseManager.kt index aae713f84a2..6c58201343a 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/FeedDatabaseManager.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/FeedDatabaseManager.kt @@ -74,9 +74,8 @@ class FeedDatabaseManager(context: Context) { fun upsertAll(subscriptionId: Long, items: List) { val oldestAllowedDate = LocalDate.now().minusWeeks(13) - val zoneId = ZoneId.systemDefault() val itemsToInsert = items.filter { - val uploadDate = it.uploadDate?.let { LocalDate.ofInstant(it.instant, zoneId) } + val uploadDate = it.uploadDate?.localDateTime?.toLocalDate() (uploadDate == null && it.streamType == StreamType.LIVE_STREAM) || (uploadDate != null && uploadDate >= oldestAllowedDate) diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadManager.kt b/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadManager.kt index 63984acd4ac..623ca6e4bbb 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadManager.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadManager.kt @@ -29,7 +29,6 @@ import org.schabi.newpipe.util.ExtractorHelper.getChannelTab import org.schabi.newpipe.util.ExtractorHelper.getMoreChannelTabItems import java.time.Instant import java.time.LocalDate -import java.time.ZoneId import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicInteger @@ -320,13 +319,12 @@ class FeedLoadManager(private val context: Context) { } private fun filterNewStreams(list: List): List { - val zoneId = ZoneId.systemDefault() val oldestAllowedDate = LocalDate.now().minusWeeks(13) return list.filter { // Streams older than this date are automatically removed from the feed. // Therefore, streams which are not in the database, // but older than this date, are considered old. - val date = it.uploadDate?.let { LocalDate.ofInstant(it.instant, zoneId) } + val date = it.uploadDate?.localDateTime?.toLocalDate() !feedDatabaseManager.doesStreamExist(it) && date != null && date > oldestAllowedDate } } 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 b8780c53547..14c0f1a6328 100644 --- a/app/src/main/java/org/schabi/newpipe/util/Localization.java +++ b/app/src/main/java/org/schabi/newpipe/util/Localization.java @@ -34,8 +34,6 @@ import java.math.RoundingMode; import java.text.NumberFormat; import java.time.Instant; -import java.time.LocalDate; -import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.time.format.FormatStyle; import java.util.Arrays; @@ -43,7 +41,6 @@ import java.util.Locale; import java.util.stream.Collectors; - /* * Created by chschtsch on 12/29/15. * @@ -132,9 +129,8 @@ public static String localizeNumber(final double number) { @NonNull public static String formatDate(@NonNull final DateWrapper dateWrapper) { - final var localDate = LocalDate.ofInstant(dateWrapper.getInstant(), ZoneId.systemDefault()); return DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).withLocale(getAppLocale()) - .format(localDate); + .format(dateWrapper.getLocalDateTime()); } public static String localizeViewCount(@NonNull final Context context, final long viewCount) { From 5e8eb71429eba029255489f39336f235862cb670 Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Sun, 12 Oct 2025 06:50:00 +0530 Subject: [PATCH 6/9] Update extractor version --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 5c7c12c1f5e..0918f7fd2b4 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -214,7 +214,7 @@ dependencies { // the corresponding commit hash, since JitPack sometimes deletes artifacts. // If there’s already a git hash, just add more of it to the end (or remove a letter) // to cause jitpack to regenerate the artifact. - implementation 'com.github.TeamNewPipe:NewPipeExtractor:c79afc00764a5f2deb8f73e78244d93920cb3456' + implementation 'com.github.TeamNewPipe:NewPipeExtractor:39b9482ea2be0e9ab95a31987c2f3347396e84ec' implementation 'com.github.TeamNewPipe:NoNonsense-FilePicker:5.0.0' /** Checkstyle **/ From 4a9dcb006f73ea83370e6648f2e7d337ac5e2597 Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Sun, 12 Oct 2025 07:03:36 +0530 Subject: [PATCH 7/9] Fix tests --- .../schabi/newpipe/database/FeedDAOTest.kt | 28 ++++++++++--------- .../local/history/HistoryRecordManagerTest.kt | 3 +- .../org/schabi/newpipe/util/Localization.java | 4 +++ .../schabi/newpipe/util/LocalizationTest.kt | 4 +-- 4 files changed, 22 insertions(+), 17 deletions(-) diff --git a/app/src/androidTest/java/org/schabi/newpipe/database/FeedDAOTest.kt b/app/src/androidTest/java/org/schabi/newpipe/database/FeedDAOTest.kt index 893ae82b7f9..4a0d7da9b70 100644 --- a/app/src/androidTest/java/org/schabi/newpipe/database/FeedDAOTest.kt +++ b/app/src/androidTest/java/org/schabi/newpipe/database/FeedDAOTest.kt @@ -21,8 +21,10 @@ import org.schabi.newpipe.extractor.ServiceList import org.schabi.newpipe.extractor.channel.ChannelInfo import org.schabi.newpipe.extractor.stream.StreamType import java.io.IOException -import java.time.OffsetDateTime -import kotlin.streams.toList +import java.time.Instant +import java.time.LocalDate +import java.time.Month +import java.time.ZoneOffset class FeedDAOTest { private lateinit var db: AppDatabase @@ -32,13 +34,13 @@ class FeedDAOTest { private val serviceId = ServiceList.YouTube.serviceId - private val stream1 = StreamEntity(1, serviceId, "https://youtube.com/watch?v=1", "stream 1", StreamType.VIDEO_STREAM, 1000, "channel-1", "https://youtube.com/channel/1", "https://i.ytimg.com/vi/1/hqdefault.jpg", 100, "2023-01-01", OffsetDateTime.parse("2023-01-01T00:00:00Z")) - private val stream2 = StreamEntity(2, serviceId, "https://youtube.com/watch?v=2", "stream 2", StreamType.VIDEO_STREAM, 1000, "channel-1", "https://youtube.com/channel/1", "https://i.ytimg.com/vi/1/hqdefault.jpg", 100, "2023-01-02", OffsetDateTime.parse("2023-01-02T00:00:00Z")) - private val stream3 = StreamEntity(3, serviceId, "https://youtube.com/watch?v=3", "stream 3", StreamType.LIVE_STREAM, 1000, "channel-1", "https://youtube.com/channel/1", "https://i.ytimg.com/vi/1/hqdefault.jpg", 100, "2023-01-03", OffsetDateTime.parse("2023-01-03T00:00:00Z")) - private val stream4 = StreamEntity(4, serviceId, "https://youtube.com/watch?v=4", "stream 4", StreamType.VIDEO_STREAM, 1000, "channel-2", "https://youtube.com/channel/2", "https://i.ytimg.com/vi/1/hqdefault.jpg", 100, "2023-08-10", OffsetDateTime.parse("2023-08-10T00:00:00Z")) - private val stream5 = StreamEntity(5, serviceId, "https://youtube.com/watch?v=5", "stream 5", StreamType.VIDEO_STREAM, 1000, "channel-2", "https://youtube.com/channel/2", "https://i.ytimg.com/vi/1/hqdefault.jpg", 100, "2023-08-20", OffsetDateTime.parse("2023-08-20T00:00:00Z")) - private val stream6 = StreamEntity(6, serviceId, "https://youtube.com/watch?v=6", "stream 6", StreamType.VIDEO_STREAM, 1000, "channel-3", "https://youtube.com/channel/3", "https://i.ytimg.com/vi/1/hqdefault.jpg", 100, "2023-09-01", OffsetDateTime.parse("2023-09-01T00:00:00Z")) - private val stream7 = StreamEntity(7, serviceId, "https://youtube.com/watch?v=7", "stream 7", StreamType.VIDEO_STREAM, 1000, "channel-4", "https://youtube.com/channel/4", "https://i.ytimg.com/vi/1/hqdefault.jpg", 100, "2023-08-10", OffsetDateTime.parse("2023-08-10T00:00:00Z")) + private val stream1 = StreamEntity(1, serviceId, "https://youtube.com/watch?v=1", "stream 1", StreamType.VIDEO_STREAM, 1000, "channel-1", "https://youtube.com/channel/1", "https://i.ytimg.com/vi/1/hqdefault.jpg", 100, "2023-01-01", Instant.parse("2023-01-01T00:00:00Z")) + private val stream2 = StreamEntity(2, serviceId, "https://youtube.com/watch?v=2", "stream 2", StreamType.VIDEO_STREAM, 1000, "channel-1", "https://youtube.com/channel/1", "https://i.ytimg.com/vi/1/hqdefault.jpg", 100, "2023-01-02", Instant.parse("2023-01-02T00:00:00Z")) + private val stream3 = StreamEntity(3, serviceId, "https://youtube.com/watch?v=3", "stream 3", StreamType.LIVE_STREAM, 1000, "channel-1", "https://youtube.com/channel/1", "https://i.ytimg.com/vi/1/hqdefault.jpg", 100, "2023-01-03", Instant.parse("2023-01-03T00:00:00Z")) + private val stream4 = StreamEntity(4, serviceId, "https://youtube.com/watch?v=4", "stream 4", StreamType.VIDEO_STREAM, 1000, "channel-2", "https://youtube.com/channel/2", "https://i.ytimg.com/vi/1/hqdefault.jpg", 100, "2023-08-10", Instant.parse("2023-08-10T00:00:00Z")) + private val stream5 = StreamEntity(5, serviceId, "https://youtube.com/watch?v=5", "stream 5", StreamType.VIDEO_STREAM, 1000, "channel-2", "https://youtube.com/channel/2", "https://i.ytimg.com/vi/1/hqdefault.jpg", 100, "2023-08-20", Instant.parse("2023-08-20T00:00:00Z")) + private val stream6 = StreamEntity(6, serviceId, "https://youtube.com/watch?v=6", "stream 6", StreamType.VIDEO_STREAM, 1000, "channel-3", "https://youtube.com/channel/3", "https://i.ytimg.com/vi/1/hqdefault.jpg", 100, "2023-09-01", Instant.parse("2023-09-01T00:00:00Z")) + private val stream7 = StreamEntity(7, serviceId, "https://youtube.com/watch?v=7", "stream 7", StreamType.VIDEO_STREAM, 1000, "channel-4", "https://youtube.com/channel/4", "https://i.ytimg.com/vi/1/hqdefault.jpg", 100, "2023-08-10", Instant.parse("2023-08-10T00:00:00Z")) private val allStreams = listOf( stream1, stream2, stream3, stream4, stream5, stream6, stream7 @@ -63,7 +65,7 @@ class FeedDAOTest { @Test fun testUnlinkStreamsOlderThan_KeepOne() { - setupUnlinkDelete("2023-08-15T00:00:00Z") + setupUnlinkDelete(LocalDate.of(2023, Month.AUGUST, 15)) val streams = feedDAO.getStreams( FeedGroupEntity.GROUP_ALL_ID, includePlayed = true, includePartiallyPlayed = true, null ) @@ -74,7 +76,7 @@ class FeedDAOTest { @Test fun testUnlinkStreamsOlderThan_KeepMultiple() { - setupUnlinkDelete("2023-08-01T00:00:00Z") + setupUnlinkDelete(LocalDate.of(2023, Month.AUGUST, 1)) val streams = feedDAO.getStreams( FeedGroupEntity.GROUP_ALL_ID, includePlayed = true, includePartiallyPlayed = true, null ) @@ -94,10 +96,10 @@ class FeedDAOTest { ) } - private fun setupUnlinkDelete(time: String) { + private fun setupUnlinkDelete(localDate: LocalDate) { clearAndFillTables() Single.fromCallable { - feedDAO.unlinkStreamsOlderThan(OffsetDateTime.parse(time)) + feedDAO.unlinkStreamsOlderThan(localDate.atStartOfDay(ZoneOffset.UTC).toInstant()) }.blockingSubscribe() Single.fromCallable { streamDAO.deleteOrphans() diff --git a/app/src/androidTest/java/org/schabi/newpipe/local/history/HistoryRecordManagerTest.kt b/app/src/androidTest/java/org/schabi/newpipe/local/history/HistoryRecordManagerTest.kt index 24be0f868d1..f01bf7a3907 100644 --- a/app/src/androidTest/java/org/schabi/newpipe/local/history/HistoryRecordManagerTest.kt +++ b/app/src/androidTest/java/org/schabi/newpipe/local/history/HistoryRecordManagerTest.kt @@ -12,7 +12,6 @@ import org.schabi.newpipe.database.history.model.SearchHistoryEntry import org.schabi.newpipe.testUtil.TestDatabase import org.schabi.newpipe.testUtil.TrampolineSchedulerRule import java.time.LocalDateTime -import java.time.OffsetDateTime import java.time.ZoneOffset class HistoryRecordManagerTest { @@ -163,7 +162,7 @@ class HistoryRecordManagerTest { } companion object { - private val time = OffsetDateTime.of(LocalDateTime.of(2000, 1, 1, 1, 1), ZoneOffset.UTC) + private val time = LocalDateTime.of(2000, 1, 1, 1, 1).toInstant(ZoneOffset.UTC) private val RELATED_SEARCHES_ENTRIES = listOf( SearchHistoryEntry(time.minusSeconds(7), 2, "AC"), 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 14c0f1a6328..67c3a41c0d1 100644 --- a/app/src/main/java/org/schabi/newpipe/util/Localization.java +++ b/app/src/main/java/org/schabi/newpipe/util/Localization.java @@ -366,6 +366,10 @@ public static void initPrettyTime(@NonNull final PrettyTime time) { prettyTime.removeUnit(Decade.class); } + static void resetPrettyTime() { + prettyTime = null; + } + public static PrettyTime resolvePrettyTime() { return new PrettyTime(getAppLocale()); } diff --git a/app/src/test/java/org/schabi/newpipe/util/LocalizationTest.kt b/app/src/test/java/org/schabi/newpipe/util/LocalizationTest.kt index f1f354f5491..53e475bde19 100644 --- a/app/src/test/java/org/schabi/newpipe/util/LocalizationTest.kt +++ b/app/src/test/java/org/schabi/newpipe/util/LocalizationTest.kt @@ -12,8 +12,8 @@ import java.util.Locale class LocalizationTest { @Test(expected = NullPointerException::class) fun `formatRelativeTime() must fail without initializing pretty time`() { - val instant = Instant.now() - Localization.formatRelativeTime(instant) + Localization.resetPrettyTime() + Localization.formatRelativeTime(Instant.now()) } @Test From 550fc2e4d8ae44f04feec00b7e2ed1c38a3f0c30 Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Sun, 26 Oct 2025 12:49:22 +0530 Subject: [PATCH 8/9] Clean up some test code --- .../schabi/newpipe/database/FeedDAOTest.kt | 54 +++++++++++++++---- 1 file changed, 44 insertions(+), 10 deletions(-) diff --git a/app/src/androidTest/java/org/schabi/newpipe/database/FeedDAOTest.kt b/app/src/androidTest/java/org/schabi/newpipe/database/FeedDAOTest.kt index 4a0d7da9b70..51991be62a6 100644 --- a/app/src/androidTest/java/org/schabi/newpipe/database/FeedDAOTest.kt +++ b/app/src/androidTest/java/org/schabi/newpipe/database/FeedDAOTest.kt @@ -21,7 +21,6 @@ import org.schabi.newpipe.extractor.ServiceList import org.schabi.newpipe.extractor.channel.ChannelInfo import org.schabi.newpipe.extractor.stream.StreamType import java.io.IOException -import java.time.Instant import java.time.LocalDate import java.time.Month import java.time.ZoneOffset @@ -34,16 +33,51 @@ class FeedDAOTest { private val serviceId = ServiceList.YouTube.serviceId - private val stream1 = StreamEntity(1, serviceId, "https://youtube.com/watch?v=1", "stream 1", StreamType.VIDEO_STREAM, 1000, "channel-1", "https://youtube.com/channel/1", "https://i.ytimg.com/vi/1/hqdefault.jpg", 100, "2023-01-01", Instant.parse("2023-01-01T00:00:00Z")) - private val stream2 = StreamEntity(2, serviceId, "https://youtube.com/watch?v=2", "stream 2", StreamType.VIDEO_STREAM, 1000, "channel-1", "https://youtube.com/channel/1", "https://i.ytimg.com/vi/1/hqdefault.jpg", 100, "2023-01-02", Instant.parse("2023-01-02T00:00:00Z")) - private val stream3 = StreamEntity(3, serviceId, "https://youtube.com/watch?v=3", "stream 3", StreamType.LIVE_STREAM, 1000, "channel-1", "https://youtube.com/channel/1", "https://i.ytimg.com/vi/1/hqdefault.jpg", 100, "2023-01-03", Instant.parse("2023-01-03T00:00:00Z")) - private val stream4 = StreamEntity(4, serviceId, "https://youtube.com/watch?v=4", "stream 4", StreamType.VIDEO_STREAM, 1000, "channel-2", "https://youtube.com/channel/2", "https://i.ytimg.com/vi/1/hqdefault.jpg", 100, "2023-08-10", Instant.parse("2023-08-10T00:00:00Z")) - private val stream5 = StreamEntity(5, serviceId, "https://youtube.com/watch?v=5", "stream 5", StreamType.VIDEO_STREAM, 1000, "channel-2", "https://youtube.com/channel/2", "https://i.ytimg.com/vi/1/hqdefault.jpg", 100, "2023-08-20", Instant.parse("2023-08-20T00:00:00Z")) - private val stream6 = StreamEntity(6, serviceId, "https://youtube.com/watch?v=6", "stream 6", StreamType.VIDEO_STREAM, 1000, "channel-3", "https://youtube.com/channel/3", "https://i.ytimg.com/vi/1/hqdefault.jpg", 100, "2023-09-01", Instant.parse("2023-09-01T00:00:00Z")) - private val stream7 = StreamEntity(7, serviceId, "https://youtube.com/watch?v=7", "stream 7", StreamType.VIDEO_STREAM, 1000, "channel-4", "https://youtube.com/channel/4", "https://i.ytimg.com/vi/1/hqdefault.jpg", 100, "2023-08-10", Instant.parse("2023-08-10T00:00:00Z")) + private val stream1 = + createStreamEntity( + 1, "https://youtube.com/watch?v=1", "stream 1", uploader = "channel-1", + uploaderUrl = "https://youtube.com/channel/1", date = LocalDate.of(2023, Month.JANUARY, 2), + ) + private val stream2 = + createStreamEntity( + 2, "https://youtube.com/watch?v=2", "stream 2", uploader = "channel-1", + uploaderUrl = "https://youtube.com/channel/1", date = LocalDate.of(2023, Month.JANUARY, 2), + ) + private val stream3 = + createStreamEntity( + 3, "https://youtube.com/watch?v=3", "stream 3", StreamType.LIVE_STREAM, + "channel-1", "https://youtube.com/channel/1", LocalDate.of(2023, Month.JANUARY, 3), + ) + private val stream4 = + createStreamEntity( + 4, "https://youtube.com/watch?v=4", "stream 4", uploader = "channel-2", + uploaderUrl = "https://youtube.com/channel/2", date = LocalDate.of(2023, Month.AUGUST, 10), + ) + private val stream5 = + createStreamEntity( + 5, "https://youtube.com/watch?v=5", "stream 5", uploader = "channel-2", + uploaderUrl = "https://youtube.com/channel/2", date = LocalDate.of(2023, Month.AUGUST, 20), + ) + private val stream6 = + createStreamEntity( + 6, "https://youtube.com/watch?v=6", "stream 6", uploader = "channel-3", + uploaderUrl = "https://youtube.com/channel/3", date = LocalDate.of(2023, Month.SEPTEMBER, 1), + ) + private val stream7 = + createStreamEntity( + 7, "https://youtube.com/watch?v=7", "stream 7", uploader = "channel-4", + uploaderUrl = "https://youtube.com/channel/4", date = LocalDate.of(2023, Month.AUGUST, 10), + ) + + private val allStreams = listOf(stream1, stream2, stream3, stream4, stream5, stream6, stream7) - private val allStreams = listOf( - stream1, stream2, stream3, stream4, stream5, stream6, stream7 + private fun createStreamEntity( + uid: Long, url: String, title: String, type: StreamType = StreamType.VIDEO_STREAM, + uploader: String, uploaderUrl: String, date: LocalDate, + ) = StreamEntity( + uid, serviceId, url, title, type, duration = 1000, uploader, uploaderUrl, + thumbnailUrl = "https://i.ytimg.com/vi/1/hqdefault.jpg", viewCount = 100, textualUploadDate = date.toString(), + uploadInstant = date.atStartOfDay(ZoneOffset.UTC).toInstant(), ) @Before From b0035a8699575606428869a3db8dc654a03bd74d Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Sun, 26 Oct 2025 13:00:12 +0530 Subject: [PATCH 9/9] Fix lint --- .../java/org/schabi/newpipe/database/FeedDAOTest.kt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/app/src/androidTest/java/org/schabi/newpipe/database/FeedDAOTest.kt b/app/src/androidTest/java/org/schabi/newpipe/database/FeedDAOTest.kt index 51991be62a6..33ed9991dfb 100644 --- a/app/src/androidTest/java/org/schabi/newpipe/database/FeedDAOTest.kt +++ b/app/src/androidTest/java/org/schabi/newpipe/database/FeedDAOTest.kt @@ -72,8 +72,13 @@ class FeedDAOTest { private val allStreams = listOf(stream1, stream2, stream3, stream4, stream5, stream6, stream7) private fun createStreamEntity( - uid: Long, url: String, title: String, type: StreamType = StreamType.VIDEO_STREAM, - uploader: String, uploaderUrl: String, date: LocalDate, + uid: Long, + url: String, + title: String, + type: StreamType = StreamType.VIDEO_STREAM, + uploader: String, + uploaderUrl: String, + date: LocalDate, ) = StreamEntity( uid, serviceId, url, title, type, duration = 1000, uploader, uploaderUrl, thumbnailUrl = "https://i.ytimg.com/vi/1/hqdefault.jpg", viewCount = 100, textualUploadDate = date.toString(),