diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 037c3cb532b..00e20c9ff6e 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -208,6 +208,7 @@ dependencies { implementation(libs.androidx.cardview) implementation(libs.androidx.constraintlayout) implementation(libs.androidx.core) + implementation(libs.androidx.core.i18n) implementation(libs.androidx.documentfile) implementation(libs.androidx.fragment) implementation(libs.androidx.lifecycle.livedata) 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/androidTest/java/org/schabi/newpipe/database/FeedDAOTest.kt b/app/src/androidTest/java/org/schabi/newpipe/database/FeedDAOTest.kt index 893ae82b7f9..33ed9991dfb 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,9 @@ 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.LocalDate +import java.time.Month +import java.time.ZoneOffset class FeedDAOTest { private lateinit var db: AppDatabase @@ -32,16 +33,56 @@ 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 = + 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 @@ -63,7 +104,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 +115,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 +135,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/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/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/feed/FeedDatabaseManager.kt b/app/src/main/java/org/schabi/newpipe/local/feed/FeedDatabaseManager.kt index ed65d4048e8..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 @@ -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) @@ -28,14 +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: OffsetDateTime = LocalDate.now().minusWeeks(13) - .atStartOfDay().atOffset(ZoneOffset.UTC) - } - fun groups() = feedGroupTable.getAll() fun database() = database @@ -50,27 +42,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 @@ -80,20 +72,13 @@ class FeedDatabaseManager(context: Context) { return streamTable.exists(stream.serviceId, stream.url) } - fun upsertAll( - subscriptionId: Long, - items: List, - oldestAllowedDate: OffsetDateTime = 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 - } + fun upsertAll(subscriptionId: Long, items: List) { + val oldestAllowedDate = LocalDate.now().minusWeeks(13) + val itemsToInsert = items.filter { + val uploadDate = it.uploadDate?.localDateTime?.toLocalDate() + + (uploadDate == null && it.streamType == StreamType.LIVE_STREAM) || + (uploadDate != null && uploadDate >= oldestAllowedDate) } feedTable.unlinkOldLivestreams(subscriptionId) @@ -107,12 +92,14 @@ 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() { + val oldestAllowedDate = LocalDate.now().minusWeeks(13) + val instant = oldestAllowedDate.atStartOfDay(ZoneId.systemDefault()).toInstant() + feedTable.unlinkStreamsOlderThan(instant) streamTable.deleteOrphans() } @@ -177,10 +164,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..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 @@ -27,8 +27,8 @@ 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.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicInteger @@ -69,26 +69,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 @@ -253,7 +253,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 { @@ -319,15 +319,13 @@ class FeedLoadManager(private val context: Context) { } private fun filterNewStreams(list: List): List { + val oldestAllowedDate = LocalDate.now().minusWeeks(13) return list.filter { - !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 - ) + // 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?.localDateTime?.toLocalDate() + !feedDatabaseManager.doesStreamExist(it) && date != null && date > oldestAllowedDate } } } 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/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 150a35eb59c..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.getLatestAccessDate()), - // 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/java/org/schabi/newpipe/util/Localization.java b/app/src/main/java/org/schabi/newpipe/util/Localization.java index b8cc26b414a..67c3a41c0d1 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,8 +33,7 @@ import java.math.BigDecimal; import java.math.RoundingMode; import java.text.NumberFormat; -import java.time.OffsetDateTime; -import java.time.ZoneId; +import java.time.Instant; 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. * @@ -130,16 +127,10 @@ 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) { + return DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).withLocale(getAppLocale()) + .format(dateWrapper.getLocalDateTime()); } public static String localizeViewCount(@NonNull final Context context, final long viewCount) { @@ -375,12 +366,16 @@ 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()); } - 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/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} + 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..53e475bde19 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`() { + Localization.resetPrettyTime() + Localization.formatRelativeTime(Instant.now()) } @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) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f4867886784..aaacd9d684e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -14,6 +14,7 @@ cardview = "1.0.0" checkstyle = "10.26.1" constraintlayout = "2.1.4" core = "1.12.0" +coreI18n = "1.0.0" desugar = "2.0.4" documentfile = "1.0.1" exoplayer = "2.18.7" @@ -69,6 +70,7 @@ androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "a androidx-cardview = { module = "androidx.cardview:cardview", version.ref = "cardview" } androidx-constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "constraintlayout" } androidx-core = { module = "androidx.core:core-ktx", version.ref = "core" } +androidx-core-i18n = { module = "androidx.core:core-i18n", version.ref = "coreI18n" } androidx-documentfile = { module = "androidx.documentfile:documentfile", version.ref = "documentfile" } androidx-fragment = { module = "androidx.fragment:fragment-ktx", version.ref = "fragment" } androidx-junit = { module = "androidx.test.ext:junit", version.ref = "junit-ext" }