Skip to content
Merged
Show file tree
Hide file tree
Changes from 98 commits
Commits
Show all changes
111 commits
Select commit Hold shift + click to select a range
8d2040b
YiR: Pre-design scaffolding
cooltey Sep 19, 2025
0916538
Merge branch 'main' into yir-re-design
cooltey Sep 19, 2025
350cf1b
Wire up API calls and put TODOs
cooltey Sep 19, 2025
f0557c0
Put async for api calls
cooltey Sep 19, 2025
6f2b7fb
Merge branch 'main' into yir-re-design
cooltey Sep 22, 2025
42f85eb
Format for the headline/body text
cooltey Sep 23, 2025
e385bb0
Revert "Format for the headline/body text"
cooltey Sep 23, 2025
84b73de
Separate initial screen out
cooltey Sep 23, 2025
26655a5
Onboarding
cooltey Sep 24, 2025
344f904
Simplify a bit
cooltey Sep 24, 2025
43700b5
Initial thought about indivial screens
cooltey Sep 24, 2025
9d93c41
Merge branch 'main' into yir-re-design
cooltey Sep 24, 2025
cdb2548
Add temporary functions for slides
cooltey Sep 24, 2025
9cbbf1f
Review comment
cooltey Sep 24, 2025
259209c
clean up async
cooltey Sep 24, 2025
d7237fc
Fix error
cooltey Sep 24, 2025
7595e56
Consolidate routes
cooltey Sep 25, 2025
65409d7
Update a bit
cooltey Sep 25, 2025
ce28f83
Merge branch 'main' into yir-re-design
cooltey Sep 25, 2025
d4c4592
Remove survey for now and refine the finish action for onboarding and
cooltey Sep 25, 2025
18256af
Fix preview
cooltey Sep 25, 2025
1aff666
YiR: update entry point in More menu. (#5964)
dbrant Sep 25, 2025
f8c7114
YiR: Login dialog in the onboarding screen. (#5965)
dbrant Sep 25, 2025
711a974
Model class
cooltey Sep 25, 2025
1379200
use data class
cooltey Sep 25, 2025
095c7b5
Merge branch 'main' into yir-re-design
cooltey Sep 25, 2025
785e946
Create dataModel for YiR
cooltey Sep 25, 2025
20f226a
Update model
cooltey Sep 25, 2025
3fec482
Update model class and add todos
cooltey Sep 25, 2025
b2a8abb
Update query for top visited articles
cooltey Sep 25, 2025
d8d3cd2
No longer use CenterAlignedTopAppBar with W icon.
dbrant Sep 26, 2025
4266944
Merge branch 'main' into yir-re-design
cooltey Sep 29, 2025
3529736
Wire up onboarding screen to PageActivity, with result.
dbrant Sep 29, 2025
c1fa000
Merge branch 'yir-re-design' of github.com:wikimedia/apps-android-wik…
dbrant Sep 29, 2025
246b99e
Update naming and fix lint
cooltey Sep 29, 2025
45e73cf
Add two parameters for YearInReviewSlides
cooltey Sep 30, 2025
a129b12
Clean up outdated strings
cooltey Sep 30, 2025
e898995
Add filterNotNull() for slide routes
cooltey Sep 30, 2025
1f121e1
Fix translation test
cooltey Oct 1, 2025
a3ee3a7
YiR: save YiR model data to preference (#5967)
cooltey Oct 1, 2025
1e43871
YiR: Create Most visited Categories slide (#5968)
cooltey Oct 1, 2025
b16c57d
YiR: Create Your Top Articles slide (#5969)
cooltey Oct 1, 2025
8be18fc
Merge branch 'main' into yir-re-design
cooltey Oct 2, 2025
cf67139
Fix translation
cooltey Oct 2, 2025
ef1576d
YiR: Create Viewed Edits slide (#5970)
cooltey Oct 3, 2025
a323891
YiR: Add English most popular articles slide (#5971)
cooltey Oct 3, 2025
44fcdc6
Merge branch 'main' into yir-re-design
cooltey Oct 3, 2025
ecaa1a8
YiR: Add Setting to enable/disable. (#5978)
dbrant Oct 3, 2025
7988f7e
Remove duplicated preference from developer settings
cooltey Oct 3, 2025
ba82c1e
Swap donate icon with share icon and adjust the UI a bit
cooltey Oct 3, 2025
fa906ef
YiR: Create Articles Saved slide (#5972)
cooltey Oct 6, 2025
884448e
YiR: Update # of articles edited slide (#5979)
cooltey Oct 6, 2025
e6d7e04
Merge branch 'main' into yir-re-design
cooltey Oct 7, 2025
d34b05e
Fix translation
cooltey Oct 7, 2025
3adcfd2
Use year constant instead of LocalDate.
dbrant Oct 7, 2025
bba160d
YiR: Custom app icon (#5974)
dbrant Oct 7, 2025
b25e2d4
Merge branch 'main' into yir-re-design
dbrant Oct 8, 2025
f74b97c
Tweak verbiage per design.
dbrant Oct 8, 2025
98a77af
YiR: Create Reading Patterns slide (#5982)
cooltey Oct 8, 2025
52f92b4
Use localtime for queries and minor code updates
cooltey Oct 8, 2025
88b444f
Update MW info links.
dbrant Oct 8, 2025
850b6b3
Rename string items a bit
cooltey Oct 8, 2025
2bbdd56
Merge branch 'main' into yir-re-design
cooltey Oct 9, 2025
df380a6
Merge branch 'main' into yir-re-design
cooltey Oct 9, 2025
322dab9
Merge branch 'main' into yir-re-design
cooltey Oct 10, 2025
350014d
Prioritize categories that have 2+ spaces.
dbrant Oct 10, 2025
1d635a6
Merge branch 'yir-re-design' of github.com:wikimedia/apps-android-wik…
dbrant Oct 10, 2025
12b37d3
Merge branch 'main' into yir-re-design
cooltey Oct 10, 2025
1bf48b6
YiR: Create contributor slides (#5991)
cooltey Oct 14, 2025
dc91e9a
YiR: Update personalized "articles read" slides (#5988)
cooltey Oct 14, 2025
2101cdd
Fully integrate with remote config.
dbrant Oct 14, 2025
5ca7b51
Correct color.
dbrant Oct 14, 2025
c02836b
Merge branch 'main' into yir-re-design
Williamrai Oct 14, 2025
7256c1f
Fetch remote config explicitly.
dbrant Oct 14, 2025
0013a71
Unnecessary intermediate vars.
dbrant Oct 14, 2025
5306a9f
Update onboarding illustration.
dbrant Oct 14, 2025
f12efc5
- adds logic to support year in review survey
Williamrai Oct 14, 2025
29078a9
Merge branch 'main' into yir-re-design
cooltey Oct 14, 2025
ee1b161
YiR: add a filter for the top visited articles to show if visited > 1…
cooltey Oct 15, 2025
db3f290
Merge branch 'main' into yir-re-design
Williamrai Oct 15, 2025
65cf748
- replaces navigation left button with close button in app bar (#6003)
Williamrai Oct 15, 2025
56275b2
YiR: update local queries to accept time range to meet the feature re…
cooltey Oct 15, 2025
33ad06c
Wire up data start/end times from remote config.
dbrant Oct 15, 2025
6eacd63
Fix calculation of time spent, and explitit units naming.
dbrant Oct 15, 2025
6985a68
Merge branch 'main' into yir-re-design
dbrant Oct 15, 2025
d844652
YiR: Wire up actual total enabling/disabling based on configuration. …
dbrant Oct 15, 2025
a9b0d8e
Update style of Donate button.
dbrant Oct 16, 2025
e373872
Correct divider color.
dbrant Oct 16, 2025
f6603c1
Don't actually need a BottomAppBar.
dbrant Oct 16, 2025
ef9221c
Merge branch 'yir-re-design' of github.com:wikimedia/apps-android-wik…
dbrant Oct 16, 2025
71f3890
Improve naming and thread allowDonate throughout.t
dbrant Oct 16, 2025
08b77c5
Reinstitute rounded corner clipping.
dbrant Oct 16, 2025
3aeaa75
YiR: update app articles viewed times screen
cooltey Oct 16, 2025
72deb14
Merge branch 'main' into yir-re-design
cooltey Oct 16, 2025
96c4f22
- ensures bottom bar is drawn above the navigation bar (#6013)
Williamrai Oct 16, 2025
38f685e
Merge remote-tracking branch 'origin/yir-re-design' into yir-survey
Williamrai Oct 16, 2025
5c47485
Merge remote-tracking branch 'origin/yir-re-design' into yir-survey
Williamrai Oct 16, 2025
f7205c4
- code merge fixes
Williamrai Oct 16, 2025
f1fedf6
Merge branch 'main' into yir-re-design
dbrant Oct 17, 2025
270f077
Complete integration with isAccessible from remote config.
dbrant Oct 17, 2025
a954530
Merge remote-tracking branch 'origin/yir-re-design' into yir-survey
Williamrai Oct 17, 2025
1e016fb
Merge remote-tracking branch 'origin/main' into yir-survey
Williamrai Oct 20, 2025
6759f1b
- adds an enum to manage survey state
Williamrai Oct 20, 2025
a1d01f2
Merge branch 'main' into yir-survey
Williamrai Oct 21, 2025
7f651e5
Merge remote-tracking branch 'origin/main' into yir-survey
Williamrai Oct 21, 2025
bf04ae8
- merge conflict fix
Williamrai Oct 21, 2025
27c6b1f
Merge branch 'main' into yir-survey
Williamrai Oct 22, 2025
af4d2d6
Merge branch 'main' into yir-survey
Williamrai Oct 22, 2025
0205cfb
Merge branch 'main' into yir-survey
cooltey Oct 22, 2025
d2c8a87
- code fixes
Williamrai Oct 22, 2025
1e7ef16
Merge branch 'main' into yir-survey
Williamrai Oct 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 28 additions & 2 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,13 @@

<activity
android:name=".main.MainActivity"
android:icon="@mipmap/launcher"
android:windowSoftInputMode="adjustResize"
android:theme="@style/AppTheme.Splash"
android:exported="true" />
<activity-alias
android:name=".DefaultIcon"
android:targetActivity=".main.MainActivity"
android:icon="@mipmap/launcher"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
Expand All @@ -103,7 +107,26 @@
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="wikipedia" />
</intent-filter>
</activity>
</activity-alias>
<activity-alias
android:name=".YIR25Icon"
android:targetActivity=".main.MainActivity"
android:icon="@mipmap/launcher_yir25"
android:exported="true"
android:enabled="false">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.MULTIWINDOW_LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="wikipedia" />
</intent-filter>
</activity-alias>

<activity
android:name=".page.PageActivity"
android:windowSoftInputMode="stateAlwaysHidden|adjustPan"
Expand Down Expand Up @@ -373,6 +396,9 @@
<activity
android:name=".settings.dev.playground.CategoryDeveloperPlayGround"/>

<activity
android:name=".yearinreview.YearInReviewOnboardingActivity"/>

<activity
android:name=".yearinreview.YearInReviewActivity"/>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,20 +136,20 @@ class ActivityTabViewModel() : ViewModel() {
val now = LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()
val weekInMillis = TimeUnit.DAYS.toMillis(7)
var weekAgo = now - weekInMillis
val totalTimeSpent = AppDatabase.instance.historyEntryWithImageDao().getTimeSpentSinceTimeStamp(weekAgo)
val totalTimeSpent = AppDatabase.instance.historyEntryWithImageDao().getTimeSpentBetween(weekAgo)

val thirtyDaysAgo = now - TimeUnit.DAYS.toMillis(30)
val articlesReadThisMonth = AppDatabase.instance.historyEntryDao().getDistinctEntriesSince(thirtyDaysAgo) ?: 0
val articlesReadThisMonth = AppDatabase.instance.historyEntryDao().getDistinctEntriesCountSince(thirtyDaysAgo) ?: 0
val articlesReadByWeek = mutableListOf<Int>()
articlesReadByWeek.add(AppDatabase.instance.historyEntryDao().getDistinctEntriesSince(weekAgo) ?: 0)
articlesReadByWeek.add(AppDatabase.instance.historyEntryDao().getDistinctEntriesCountSince(weekAgo) ?: 0)
for (i in 1..3) {
weekAgo -= weekInMillis
val articlesRead = AppDatabase.instance.historyEntryDao().getDistinctEntriesBetween(weekAgo, weekAgo + weekInMillis)
val articlesRead = AppDatabase.instance.historyEntryDao().getDistinctEntriesCountBetween(weekAgo, weekAgo + weekInMillis)
articlesReadByWeek.add(articlesRead)
}
val mostRecentReadTime = AppDatabase.instance.historyEntryDao().getMostRecentEntry()?.timestamp?.toInstant()?.atZone(ZoneId.systemDefault())?.toLocalDateTime()

val articlesSavedThisMonth = AppDatabase.instance.readingListPageDao().getTotalLocallySavedPagesSince(thirtyDaysAgo) ?: 0
val articlesSavedThisMonth = AppDatabase.instance.readingListPageDao().getTotalLocallySavedPagesBetween(thirtyDaysAgo) ?: 0
val articlesSaved = AppDatabase.instance.readingListPageDao().getLocallySavedPagesSince(thirtyDaysAgo, 4)
.map { ReadingListPage.toPageTitle(it) }
val mostRecentSaveTime = AppDatabase.instance.readingListPageDao().getMostRecentLocallySavedPage()?.atime?.let { Instant.ofEpochMilli(it) }?.atZone(ZoneId.systemDefault())?.toLocalDateTime()
Expand Down
4 changes: 4 additions & 0 deletions app/src/main/java/org/wikipedia/categories/db/CategoryDao.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ interface CategoryDao {
@Query("SELECT year, month, title, lang, SUM (count) AS count FROM Category WHERE year = :year AND month = :month GROUP BY title, lang ORDER BY count DESC")
suspend fun getTopCategoriesByMonth(year: Int, month: Int): List<Category>

// Get top categories by year
@Query("SELECT year, month, title, lang, SUM (count) AS count FROM Category WHERE year = :year GROUP BY title, lang ORDER BY count DESC LIMIT :limit")
suspend fun getTopCategoriesByYear(year: Int, limit: Int): List<Category>

@Query("SELECT * FROM Category")
suspend fun getAllCategories(): List<Category>

Expand Down
23 changes: 16 additions & 7 deletions app/src/main/java/org/wikipedia/history/db/HistoryEntryDao.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,14 @@ interface HistoryEntryDao {
@Query("SELECT * FROM HistoryEntry WHERE authority = :authority AND lang = :lang AND apiTitle = :apiTitle AND timestamp = :timestamp LIMIT 1")
suspend fun findEntryBy(authority: String, lang: String, apiTitle: String, timestamp: Long): HistoryEntry?

@Query("SELECT COUNT(*) FROM (SELECT DISTINCT HistoryEntry.lang, HistoryEntry.apiTitle FROM HistoryEntry WHERE timestamp BETWEEN :startDate AND :endDate)")
suspend fun getDistinctEntriesBetween(startDate: Long?, endDate: Long?): Int
@Query("SELECT COUNT(*) FROM (SELECT DISTINCT HistoryEntry.lang, HistoryEntry.apiTitle FROM HistoryEntry WHERE timestamp BETWEEN :startMillis AND :endMillis)")
suspend fun getDistinctEntriesCountBetween(startMillis: Long, endMillis: Long = System.currentTimeMillis()): Int

@Query("SELECT DISTINCT displayTitle FROM HistoryEntry LIMIT 3")
suspend fun getDisplayTitles(): List<String>
@Query("SELECT COUNT(*) FROM (SELECT DISTINCT HistoryEntry.lang, HistoryEntry.apiTitle FROM HistoryEntry WHERE timestamp > :timestamp)")
suspend fun getDistinctEntriesCountSince(timestamp: Long): Int?

@Query("SELECT displayTitle FROM HistoryEntry WHERE timestamp BETWEEN :startMillis AND :endMillis GROUP BY displayTitle HAVING COUNT(displayTitle) > 1 ORDER BY COUNT(displayTitle) DESC LIMIT :limit")
suspend fun getTopVisitedEntriesBetween(limit: Int, startMillis: Long, endMillis: Long = System.currentTimeMillis()): List<String>

@Query("SELECT COUNT(*) FROM HistoryEntry")
suspend fun getHistoryCount(): Int
Expand All @@ -39,12 +42,18 @@ interface HistoryEntryDao {
@Query("DELETE FROM HistoryEntry WHERE authority = :authority AND lang = :lang AND namespace = :namespace AND apiTitle = :apiTitle")
suspend fun deleteBy(authority: String, lang: String, namespace: String?, apiTitle: String)

@Query("SELECT COUNT(*) FROM (SELECT DISTINCT HistoryEntry.lang, HistoryEntry.apiTitle FROM HistoryEntry WHERE timestamp > :timestamp)")
suspend fun getDistinctEntriesSince(timestamp: Long): Int?

@Query("SELECT * FROM HistoryEntry ORDER BY timestamp DESC LIMIT 1")
suspend fun getMostRecentEntry(): HistoryEntry?

@Query("SELECT CAST(strftime('%H', timestamp / 1000, 'unixepoch', 'localtime') AS INTEGER) AS hour FROM HistoryEntry WHERE timestamp BETWEEN :startMillis AND :endMillis GROUP BY hour ORDER BY COUNT(id) DESC LIMIT 1")
suspend fun getFavoriteTimeToReadBetween(startMillis: Long, endMillis: Long = System.currentTimeMillis()): Int?

@Query("SELECT CAST(strftime('%w', timestamp / 1000, 'unixepoch', 'localtime') AS INTEGER) AS dayOfWeek FROM HistoryEntry WHERE timestamp BETWEEN :startMillis AND :endMillis GROUP BY dayOfWeek ORDER BY COUNT(id) DESC LIMIT 1")
suspend fun getFavoriteDayToReadBetween(startMillis: Long, endMillis: Long = System.currentTimeMillis()): Int?

@Query("SELECT CAST(strftime('%m', timestamp / 1000, 'unixepoch', 'localtime') AS INTEGER) AS month FROM HistoryEntry WHERE timestamp BETWEEN :startMillis AND :endMillis GROUP BY month ORDER BY COUNT(id) DESC LIMIT 1")
suspend fun getMostReadingMonthBetween(startMillis: Long, endMillis: Long = System.currentTimeMillis()): Int?

@Transaction
suspend fun insert(entries: List<HistoryEntry>) {
entries.forEach {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ interface HistoryEntryWithImageDao {
@Query("SELECT SUM(timeSpentSec) FROM (" +
" SELECT DISTINCT HistoryEntry.lang, HistoryEntry.apiTitle, PageImage.timeSpentSec FROM HistoryEntry" +
" LEFT OUTER JOIN PageImage ON (HistoryEntry.namespace = PageImage.namespace AND HistoryEntry.apiTitle = PageImage.apiTitle AND HistoryEntry.lang = PageImage.lang)" +
" WHERE timestamp > :timeStamp" +
" WHERE timestamp BETWEEN :startMillis AND :endMillis" +
")")
suspend fun getTimeSpentSinceTimeStamp(timeStamp: Long): Long
suspend fun getTimeSpentBetween(startMillis: Long, endMillis: Long = System.currentTimeMillis()): Long

suspend fun findHistoryItem(wikiSite: WikiSite, searchQuery: String): SearchResults {
var normalizedQuery = StringUtils.stripAccents(searchQuery)
Expand Down
1 change: 1 addition & 0 deletions app/src/main/java/org/wikipedia/login/LoginActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ class LoginActivity : BaseActivity() {
const val SOURCE_SUGGESTED_EDITS = "suggestededits"
const val SOURCE_TALK = "talk"
const val SOURCE_ACTIVITY_TAB = "activity_tab"
const val SOURCE_YEAR_IN_REVIEW = "yir"

fun newIntent(context: Context, source: String, createAccountFirst: Boolean = true): Intent {
return Intent(context, LoginActivity::class.java)
Expand Down
6 changes: 4 additions & 2 deletions app/src/main/java/org/wikipedia/main/MainFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ import org.wikipedia.views.NotificationButtonView
import org.wikipedia.views.TabCountsView
import org.wikipedia.views.imageservice.ImageService
import org.wikipedia.watchlist.WatchlistActivity
import org.wikipedia.yearinreview.YearInReviewEntryDialog
import org.wikipedia.yearinreview.YearInReviewOnboardingActivity
import org.wikipedia.yearinreview.maybeShowYearInReviewFeedbackDialog
import java.io.File
import java.util.concurrent.TimeUnit

Expand Down Expand Up @@ -213,6 +214,7 @@ class MainFragment : Fragment(), BackPressedHandler, MenuProvider, FeedFragment.
downloadReceiver.register(requireContext(), downloadReceiverCallback)
// reset the last-page-viewed timer
Prefs.pageLastShown = 0
maybeShowYearInReviewFeedbackDialog(requireActivity())
}

override fun onDestroyView() {
Expand Down Expand Up @@ -499,7 +501,7 @@ class MainFragment : Fragment(), BackPressedHandler, MenuProvider, FeedFragment.
}

override fun yearInReviewClick() {
ExclusiveBottomSheetPresenter.show(childFragmentManager, YearInReviewEntryDialog.newInstance())
startActivity(YearInReviewOnboardingActivity.newIntent(requireActivity()))
}

fun setBottomNavVisible(visible: Boolean) {
Expand Down
1 change: 1 addition & 0 deletions app/src/main/java/org/wikipedia/navtab/MenuNavTabDialog.kt
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ class MenuNavTabDialog : ExtendedBottomSheetDialogFragment() {
callback()?.yearInReviewClick()
dismiss()
}
binding.yearInReviewRedDot.isVisible = !Prefs.yearInReviewVisited

binding.mainDrawerEditContainer.setOnClickListener {
BreadCrumbLogEvent.logClick(requireActivity(), binding.mainDrawerEditContainer)
Expand Down
17 changes: 17 additions & 0 deletions app/src/main/java/org/wikipedia/page/PageActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ import org.wikipedia.views.FrameLayoutNavMenuTriggerer
import org.wikipedia.views.ObservableWebView
import org.wikipedia.views.ViewUtil
import org.wikipedia.watchlist.WatchlistExpiry
import org.wikipedia.yearinreview.YearInReviewOnboardingActivity
import org.wikipedia.yearinreview.maybeShowYearInReviewFeedbackDialog
import java.util.Locale

class PageActivity : BaseActivity(), PageFragment.Callback, LinkPreviewDialog.LoadPageCallback, FrameLayoutNavMenuTriggerer.Callback {
Expand Down Expand Up @@ -179,6 +181,12 @@ class PageActivity : BaseActivity(), PageFragment.Callback, LinkPreviewDialog.Lo
}
}

private val yearInReviewLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode == RESULT_CANCELED) {
FeedbackUtil.showMessage(this, getString(R.string.year_in_review_get_started_later))
}
}

public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (!DeviceUtil.assertAppContext(this)) {
Expand Down Expand Up @@ -283,6 +291,8 @@ class PageActivity : BaseActivity(), PageFragment.Callback, LinkPreviewDialog.Lo
// then we must have been launched with an Intent, so... handle it!
handleIntent(intent)
}

maybeShowYearInReview()
}

override fun onStart() {
Expand Down Expand Up @@ -328,6 +338,7 @@ class PageActivity : BaseActivity(), PageFragment.Callback, LinkPreviewDialog.Lo
app.resetWikiSite()
updateNotificationsButton(false)
Prefs.temporaryWikitext = null
maybeShowYearInReviewFeedbackDialog(this)
}

override fun onPause() {
Expand Down Expand Up @@ -706,6 +717,12 @@ class PageActivity : BaseActivity(), PageFragment.Callback, LinkPreviewDialog.Lo
.show()
}

private fun maybeShowYearInReview() {
if (Prefs.isYearInReviewEnabled && !Prefs.yearInReviewVisited) {
yearInReviewLauncher.launch((YearInReviewOnboardingActivity.newIntent(this)))
}
}

private fun maybeShowThemeTooltip() {
if (!Prefs.showOneTimeCustomizeToolbarTooltip) {
return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ interface ReadingListPageDao {
@Query("SELECT * FROM ReadingListPage WHERE lang = :lang ORDER BY RANDOM() LIMIT 1")
suspend fun getRandomPage(lang: String): ReadingListPage?

@Query("SELECT displayTitle FROM ReadingListPage WHERE atime > 0 AND atime BETWEEN :startMillis AND :endMillis ORDER BY RANDOM() LIMIT :limit")
suspend fun getRandomPageTitlesBetween(limit: Int, startMillis: Long, endMillis: Long = System.currentTimeMillis()): List<String?>

@Query("SELECT * FROM ReadingListPage WHERE UPPER(displayTitle) LIKE UPPER(:term) ESCAPE '\\'")
suspend fun findPageBySearchTerm(term: String): List<ReadingListPage>

Expand All @@ -86,8 +89,8 @@ interface ReadingListPageDao {
@Query("SELECT * FROM ReadingListPage WHERE remoteId < 1")
suspend fun getAllPagesToBeSynced(): List<ReadingListPage>

@Query("SELECT COUNT(*) FROM ReadingListPage WHERE atime > 0 AND atime > :timestamp")
suspend fun getTotalLocallySavedPagesSince(timestamp: Long): Int?
@Query("SELECT COUNT(*) FROM ReadingListPage WHERE atime > 0 AND atime BETWEEN :startMillis AND :endMillis")
suspend fun getTotalLocallySavedPagesBetween(startMillis: Long, endMillis: Long = System.currentTimeMillis()): Int?

@Query("SELECT * FROM ReadingListPage WHERE atime > 0 AND atime > :timestamp ORDER BY atime DESC LIMIT :limit")
suspend fun getLocallySavedPagesSince(timestamp: Long, limit: Int): List<ReadingListPage>
Expand Down
83 changes: 83 additions & 0 deletions app/src/main/java/org/wikipedia/settings/AppIconDialog.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package org.wikipedia.settings

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import com.google.android.flexbox.AlignItems
import com.google.android.flexbox.FlexDirection
import com.google.android.flexbox.FlexboxLayoutManager
import com.google.android.flexbox.JustifyContent
import org.wikipedia.R
import org.wikipedia.appshortcuts.AppShortcuts
import org.wikipedia.databinding.DialogAppIconBinding
import org.wikipedia.databinding.ItemAppIconBinding
import org.wikipedia.page.ExtendedBottomSheetDialogFragment
import org.wikipedia.util.DimenUtil
import org.wikipedia.util.ResourceUtil

class AppIconDialog : ExtendedBottomSheetDialogFragment() {
private var _binding: DialogAppIconBinding? = null
private val binding get() = _binding!!

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
_binding = DialogAppIconBinding.inflate(inflater, container, false)

binding.appIconRecyclerView.layoutManager = FlexboxLayoutManager(requireContext()).apply {
flexDirection = FlexDirection.ROW
justifyContent = JustifyContent.CENTER
alignItems = AlignItems.CENTER
}
binding.appIconRecyclerView.adapter = AppIconAdapter(LauncherIcon.entries).apply {
onItemClickListener = { icon ->
Prefs.selectedAppIcon = icon.key
LauncherController.setIcon(icon)
AppShortcuts.setShortcuts(requireContext())
dismiss()
Toast.makeText(requireActivity(), R.string.settings_app_icon_updated, Toast.LENGTH_SHORT).show()
}
}
return binding.root
}

override fun onDestroyView() {
super.onDestroyView()
_binding = null
}

private class AppIconAdapter(val list: List<LauncherIcon>) : RecyclerView.Adapter<AppIconAdapter.AppIconViewHolder>() {
var onItemClickListener: ((LauncherIcon) -> Unit)? = null

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AppIconViewHolder {
val view = ItemAppIconBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return AppIconViewHolder(view)
}

override fun getItemCount() = list.size

override fun onBindViewHolder(holder: AppIconViewHolder, position: Int) {
val item = list[position]
holder.bind(item)
}

private inner class AppIconViewHolder(val binding: ItemAppIconBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: LauncherIcon) {
binding.appIcon.apply {
foreground = ContextCompat.getDrawable(binding.root.context, item.foreground)
background = ContextCompat.getDrawable(binding.root.context, item.background)
setOnClickListener {
onItemClickListener?.invoke(item)
}
val isSelected = Prefs.selectedAppIcon == item.key
val strokeColor = if (isSelected) R.attr.progressive_color else R.attr.border_color
val newStrokeWidth = if (isSelected) 2f else 1f
this.strokeColor = ResourceUtil.getThemedColorStateList(context, strokeColor)
this.strokeWidth = DimenUtil.dpToPx(newStrokeWidth)
}
}
}
}
}
47 changes: 47 additions & 0 deletions app/src/main/java/org/wikipedia/settings/LauncherController.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package org.wikipedia.settings

import android.content.ComponentName
import android.content.Context
import android.content.pm.PackageManager
import org.wikipedia.R
import org.wikipedia.WikipediaApp

object LauncherController {

fun setIcon(icon: LauncherIcon) {
val context = WikipediaApp.instance.applicationContext
val packageManager = context.packageManager
LauncherIcon.entries.forEach { launcherIcon ->
packageManager.setComponentEnabledSetting(
launcherIcon.getComponentName(context),
if (launcherIcon == icon) PackageManager.COMPONENT_ENABLED_STATE_ENABLED else
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP
)
}
}
}

enum class LauncherIcon(
val key: String,
val background: Int,
val foreground: Int,
val label: Int
) {
DEFAULT(
key = "DefaultIcon",
background = R.drawable.launcher_background,
foreground = R.drawable.launcher_foreground,
label = R.string.app_name
),
YIR25(
key = "YIR25Icon",
background = R.drawable.launcher_background,
foreground = R.drawable.launcher_foreground_yir25,
label = R.string.app_name
);

fun getComponentName(context: Context): ComponentName {
return ComponentName(context.packageName, "org.wikipedia.$key")
}
}
Loading
Loading