diff --git a/app/src/main/java/org/wikipedia/history/db/HistoryEntryDao.kt b/app/src/main/java/org/wikipedia/history/db/HistoryEntryDao.kt index 58346a77c84..dc86dc7923e 100644 --- a/app/src/main/java/org/wikipedia/history/db/HistoryEntryDao.kt +++ b/app/src/main/java/org/wikipedia/history/db/HistoryEntryDao.kt @@ -45,6 +45,15 @@ interface HistoryEntryDao { @Query("SELECT * FROM HistoryEntry ORDER BY timestamp DESC LIMIT 1") suspend fun getMostRecentEntry(): HistoryEntry? + @Query("SELECT CAST(strftime('%H', timestamp / 1000, 'unixepoch') AS INTEGER) AS hour FROM HistoryEntry WHERE timestamp BETWEEN :startDate AND :endDate GROUP BY hour ORDER BY COUNT(id) DESC LIMIT 1") + suspend fun getFavoriteTimeToReadSince(startDate: Long, endDate: Long): Int? + + @Query("SELECT CAST(strftime('%w', timestamp / 1000, 'unixepoch') AS INTEGER) AS dayOfWeek FROM HistoryEntry WHERE timestamp BETWEEN :startDate AND :endDate GROUP BY dayOfWeek ORDER BY COUNT(id) DESC LIMIT 1") + suspend fun getFavoriteDayToReadSince(startDate: Long, endDate: Long): Int? + + @Query("SELECT CAST(strftime('%m', timestamp / 1000, 'unixepoch') AS INTEGER) AS month FROM HistoryEntry WHERE timestamp BETWEEN :startDate AND :endDate GROUP BY month ORDER BY COUNT(id) DESC LIMIT 1") + suspend fun getMostReadingMonthSince(startDate: Long, endDate: Long): Int? + @Transaction suspend fun insert(entries: List) { entries.forEach { diff --git a/app/src/main/java/org/wikipedia/yearinreview/YearInReviewModel.kt b/app/src/main/java/org/wikipedia/yearinreview/YearInReviewModel.kt index 1816befb56d..ac84c9c2040 100644 --- a/app/src/main/java/org/wikipedia/yearinreview/YearInReviewModel.kt +++ b/app/src/main/java/org/wikipedia/yearinreview/YearInReviewModel.kt @@ -23,9 +23,9 @@ data class YearInReviewModel( val localSavedArticles: List, val localTopVisitedArticles: List, val localTopCategories: List, - val favoriteTimeToRead: String, - val favoriteDayToRead: String, - val favoriteMonthDidMostReading: String, + val favoriteTimeToRead: Int, + val favoriteDayToRead: Int, + val favoriteMonthDidMostReading: Int, val closestLocation: Pair, val closestArticles: List, val userEditsCount: Int, diff --git a/app/src/main/java/org/wikipedia/yearinreview/YearInReviewScreenData.kt b/app/src/main/java/org/wikipedia/yearinreview/YearInReviewScreenData.kt index 3d88fd5aadb..91caf8d43f4 100644 --- a/app/src/main/java/org/wikipedia/yearinreview/YearInReviewScreenData.kt +++ b/app/src/main/java/org/wikipedia/yearinreview/YearInReviewScreenData.kt @@ -116,6 +116,21 @@ sealed class YearInReviewScreenData( val bodyText: String? = null ) : YearInReviewScreenData() + class ReadingPatterns( + animatedImageResource: Int = 0, + staticImageResource: Int = 0, + headlineText: Any? = null, + bodyText: Any? = null, + val favoriteTimeText: String, + val favoriteDayText: String, + val favoriteMonthText: String + ) : StandardScreen( + animatedImageResource = animatedImageResource, + staticImageResource = staticImageResource, + headlineText = headlineText, + bodyText = bodyText, + ) + class CustomIconScreen( headlineText: Any? = null, bodyText: Any? = null, diff --git a/app/src/main/java/org/wikipedia/yearinreview/YearInReviewScreenDeck.kt b/app/src/main/java/org/wikipedia/yearinreview/YearInReviewScreenDeck.kt index 2692a485d97..f8c2740c70b 100644 --- a/app/src/main/java/org/wikipedia/yearinreview/YearInReviewScreenDeck.kt +++ b/app/src/main/java/org/wikipedia/yearinreview/YearInReviewScreenDeck.kt @@ -400,7 +400,7 @@ fun YearInReviewScreenContent( ) { when (screenData) { is YearInReviewScreenData.StandardScreen -> { - StandardLayoutWithVariants( + StandardScreenContent( innerPadding = innerPadding, screenData = screenData, screenCaptureMode = screenCaptureMode, @@ -418,7 +418,7 @@ fun YearInReviewScreenContent( } @Composable -private fun StandardLayoutWithVariants( +private fun StandardScreenContent( modifier: Modifier = Modifier, innerPadding: PaddingValues, screenData: YearInReviewScreenData.StandardScreen, @@ -466,23 +466,63 @@ private fun StandardLayoutWithVariants( } } } - HtmlText( - modifier = Modifier - .padding(top = 10.dp, start = 16.dp, end = 16.dp, bottom = 16.dp) - .height(IntrinsicSize.Min), - text = processString(screenData.bodyText), - linkStyle = TextLinkStyles( - style = SpanStyle( - color = WikipediaTheme.colors.progressiveColor, - fontSize = 16.sp + if (screenData is YearInReviewScreenData.ReadingPatterns) { + val readingPattersMap = mapOf( + screenData.favoriteTimeText to R.string.year_in_review_slide_reading_patterns_body_favorite_time, + screenData.favoriteDayText to R.string.year_in_review_slide_reading_patterns_body_favorite_day, + screenData.favoriteMonthText to R.string.year_in_review_slide_reading_patterns_body_favorite_month, + ) + readingPattersMap.forEach { (title, description) -> + ReadingPatternsItem( + title = title, + description = description ) - ), - style = MaterialTheme.typography.bodyLarge - ) + } + } else { + HtmlText( + modifier = Modifier + .padding(top = 10.dp, start = 16.dp, end = 16.dp, bottom = 16.dp) + .height(IntrinsicSize.Min), + text = processString(screenData.bodyText), + linkStyle = TextLinkStyles( + style = SpanStyle( + color = WikipediaTheme.colors.progressiveColor, + fontSize = 16.sp + ) + ), + style = MaterialTheme.typography.bodyLarge + ) + } } } } +@Composable +fun ReadingPatternsItem( + title: String, + description: Int, +) { + Column( + modifier = Modifier + .padding(horizontal = 16.dp, vertical = 12.dp) + ) { + Text( + modifier = Modifier + .height(IntrinsicSize.Min), + text = processString(title), + color = WikipediaTheme.colors.primaryColor, + style = MaterialTheme.typography.bodyLarge + ) + Text( + modifier = Modifier + .height(IntrinsicSize.Min), + text = processString(description), + color = WikipediaTheme.colors.primaryColor, + style = MaterialTheme.typography.bodyMedium + ) + } +} + @Composable fun LoadingIndicator() { Column( @@ -534,7 +574,7 @@ fun PreviewScreenShot() { @Preview @Composable -fun PreviewContent() { +fun PreviewStandardContent() { BaseTheme(currentTheme = Theme.LIGHT) { YearInReviewScreenDeck( state = UiState.Success(listOf( @@ -551,3 +591,26 @@ fun PreviewContent() { ) } } + +@Preview +@Composable +fun PreviewReadingPatternsContent() { + BaseTheme(currentTheme = Theme.LIGHT) { + YearInReviewScreenDeck( + state = UiState.Success(listOf( + YearInReviewScreenData.ReadingPatterns( + animatedImageResource = R.drawable.year_in_review_puzzle_pieces, + staticImageResource = R.drawable.year_in_review_puzzle_pieces, + headlineText = "You have clear reading patterns", + bodyText = "", + favoriteTimeText = "Afternoon", + favoriteDayText = "Wednesday", + favoriteMonthText = "February" + ) + )), + onDonateClick = {}, + onBackButtonClick = {}, + onNextButtonClick = {} + ) + } +} diff --git a/app/src/main/java/org/wikipedia/yearinreview/YearInReviewSlides.kt b/app/src/main/java/org/wikipedia/yearinreview/YearInReviewSlides.kt index d5104140900..ef92bbe2b20 100644 --- a/app/src/main/java/org/wikipedia/yearinreview/YearInReviewSlides.kt +++ b/app/src/main/java/org/wikipedia/yearinreview/YearInReviewSlides.kt @@ -4,6 +4,9 @@ import android.content.Context import org.wikipedia.R import org.wikipedia.settings.Prefs import java.text.NumberFormat +import java.time.DayOfWeek +import java.time.Month +import java.time.format.TextStyle import java.util.Locale class YearInReviewSlides( @@ -81,13 +84,30 @@ class YearInReviewSlides( ) } - private fun readingPatternsScreen(vararg params: Int): YearInReviewScreenData.StandardScreen { - // TODO: yir106 + yir107 - return YearInReviewScreenData.StandardScreen( - animatedImageResource = R.drawable.year_in_review_puzzle_pieces, - staticImageResource = R.drawable.year_in_review_puzzle_pieces, - headlineText = "You have clear reading patterns", - bodyText = "TBD" + private fun readingPatternsScreen(): YearInReviewScreenData.StandardScreen? { + if (yearInReviewModel.localReadingArticlesCount < YearInReviewViewModel.MIN_READING_PATTERNS_ARTICLES) { + return null + } + // TODO: check if the time needs to follow the locale + val favoriteTimeText = when (yearInReviewModel.favoriteTimeToRead) { + in 0..5 -> context.getString(R.string.year_in_review_slide_reading_pattern_late_night) + in 5..12 -> context.getString(R.string.year_in_review_slide_reading_pattern_morning) + in 12..13 -> context.getString(R.string.year_in_review_slide_reading_pattern_midday) + in 13..17 -> context.getString(R.string.year_in_review_slide_reading_pattern_afternoon) + in 17..21 -> context.getString(R.string.year_in_review_slide_reading_pattern_evening) + else -> context.getString(R.string.year_in_review_slide_reading_pattern_night) + } + val favoriteDayText = DayOfWeek.of(yearInReviewModel.favoriteDayToRead) + .getDisplayName(TextStyle.FULL, Locale.getDefault()) + val favoriteMonthText = Month.of(yearInReviewModel.favoriteMonthDidMostReading) + .getDisplayName(TextStyle.FULL, Locale.getDefault()) + return YearInReviewScreenData.ReadingPatterns( + animatedImageResource = R.drawable.year_in_review_puzzle_pieces, // TODO: tbd + staticImageResource = R.drawable.year_in_review_puzzle_pieces, // TODO: tbd + headlineText = context.getString(R.string.year_in_review_slide_reading_patterns_headline), + favoriteTimeText = favoriteTimeText, + favoriteDayText = favoriteDayText, + favoriteMonthText = favoriteMonthText ) } diff --git a/app/src/main/java/org/wikipedia/yearinreview/YearInReviewViewModel.kt b/app/src/main/java/org/wikipedia/yearinreview/YearInReviewViewModel.kt index 5a388d68623..dbd3930b783 100644 --- a/app/src/main/java/org/wikipedia/yearinreview/YearInReviewViewModel.kt +++ b/app/src/main/java/org/wikipedia/yearinreview/YearInReviewViewModel.kt @@ -172,6 +172,29 @@ class YearInReviewViewModel() : ViewModel() { editCount += wikidataResponse.query?.userInfo!!.editCount editCount += commonsResponse.query?.userInfo!!.editCount + val favoriteTimeToRead = async { + AppDatabase.instance.historyEntryDao() + .getFavoriteTimeToReadSince(startTimeInMillis, endTimeInMillis) + } + + val favoriteDayToRead = async { + AppDatabase.instance.historyEntryDao() + .getFavoriteDayToReadSince(startTimeInMillis, endTimeInMillis) + } + + val mostReadingMonth = async { + AppDatabase.instance.historyEntryDao() + .getMostReadingMonthSince(startTimeInMillis, endTimeInMillis) + } + + val favoriteTimeToReadHour = favoriteTimeToRead.await() ?: 0 + + val favoriteDayToReadIndex = favoriteDayToRead.await()?.let { + if (it == 0) 7 else it + } ?: 1 + + val mostReadingMonthIndex = mostReadingMonth.await() ?: 1 + yearInReviewModelMap[YIR_YEAR] = YearInReviewModel( enReadingTimePerHour = 0L, // TODO: remote config enPopularArticles = listOf("Dog", "Cat", "Bear", "Bird", "Tiger"), // TODO: remote config @@ -192,9 +215,9 @@ class YearInReviewViewModel() : ViewModel() { localSavedArticles = randomSavedArticleTitles.await(), localTopVisitedArticles = topVisitedArticlesForTheYear.await(), localTopCategories = topVisitedCategoryForTheYear.await(), - favoriteTimeToRead = "Evening", - favoriteDayToRead = "Saturday", - favoriteMonthDidMostReading = "March", + favoriteTimeToRead = favoriteTimeToReadHour, + favoriteDayToRead = favoriteDayToReadIndex, + favoriteMonthDidMostReading = mostReadingMonthIndex, closestLocation = Pair(0.0, 0.0), closestArticles = emptyList(), userEditsCount = editCount, @@ -229,5 +252,7 @@ class YearInReviewViewModel() : ViewModel() { const val MAX_TOP_ARTICLES = 5 const val MIN_TOP_CATEGORY = 3 const val MAX_TOP_CATEGORY = 5 + + const val MIN_READING_PATTERNS_ARTICLES = 5 } } diff --git a/app/src/main/res/values-qq/strings.xml b/app/src/main/res/values-qq/strings.xml index cb38b536eaa..8abbf8c6c5a 100644 --- a/app/src/main/res/values-qq/strings.xml +++ b/app/src/main/res/values-qq/strings.xml @@ -1908,8 +1908,8 @@ Body text of the top categories slide for the Year in Review. The %1$d will be replaced by the current year and %2$s will be replaced by the list of top categories. Title of the most popular English Wikipedia articles for the Year in Review. - Title of the most popular English Wikipedia articles for the Year in Review. The %1$d will be replaced by the number of articles and %2$s will be replaced by the list of the most popular English articles. - Title of the most popular English Wikipedia articles for the Year in Review. The %1$d will be replaced by the number of articles and %2$s will be replaced by the list of the most popular English articles. + Body text of the most popular English Wikipedia articles for the Year in Review. The %1$d will be replaced by the number of articles and %2$s will be replaced by the list of the most popular English articles. + Body text of the most popular English Wikipedia articles for the Year in Review. The %1$d will be replaced by the number of articles and %2$s will be replaced by the list of the most popular English articles. Title of the edit viewed times slide for the Year in Review. %s will be replaced by the formatted number. @@ -1923,14 +1923,14 @@ Title of the edited time per minute slide for the Year in Review. %s will be replaced by the formatted number. Title of the edited times per minute slide for the Year in Review. %s will be replaced by the formatted number. - Title of the edited times per minute slide for the Year in Review. %s will be replaced by the wiki page URL. + Body text of the edited times per minute slide for the Year in Review. %s will be replaced by the wiki page URL. Title of the added byte slide for the Year in Review. %s will be replaced by the formatted number. Title of the added bytes slide for the Year in Review. %s will be replaced by the formatted number. - Title of the added byte slide for the Year in Review. %1$d will be replaced by the current year, %2$s will be replaced by the formatted number, and %3$s will be replaced by the wiki page URL. - Title of the added bytes slide for the Year in Review. %1$d will be replaced by the current year, %2$s will be replaced by the formatted number, and %3$s will be replaced by the wiki page URL. + Body text of thea added byte slide for the Year in Review. %1$d will be replaced by the current year, %2$s will be replaced by the formatted number, and %3$s will be replaced by the wiki page URL. + Body text of thea added bytes slide for the Year in Review. %1$d will be replaced by the current year, %2$s will be replaced by the formatted number, and %3$s will be replaced by the wiki page URL. Title of the user edited times slide for the Year in Review. %s will be replaced by the formatted number. @@ -1970,6 +1970,16 @@ Title of the app saved articles slide for the Year in Review. %s will be replaced by the total number of saved articles from the app. Body text of the app saved articles slide for the Year in Review. + Title of the reading patterns slide for the Year in Review + Text of the time bucket that indicates the favorite time to read. + Text of the time bucket that indicates the favorite day to read. + Text of the time bucket that indicates the month you did the most reading. + Text of the time bucket that indicates morning. + Text of the time bucket that indicates midday + Text of the time bucket that indicates afternoon + Text of the time bucket that indicates evening + Text of the time bucket that indicates night + Text of the time bucket that indicates late night Title for the recommended reading list feature. Title of the onboarding card for the recommended reading list. Message on the onboarding card for the recommended reading list. diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d94e9b097f6..529660acf19 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -2077,6 +2077,16 @@ App users had %s saved articles Adding articles to reading lists allows you to access articles even while offline. You can also log in to sync reading lists across devices. + You have clear reading patterns + Favorite time to read + Favorite day to read + Month you did the most reading + Morning + Midday + Afternoon + Evening + Night + Late night Discover