@@ -29,22 +29,30 @@ import androidx.lifecycle.repeatOnLifecycle
2929import androidx.preference.PreferenceManager
3030import com.google.android.material.dialog.MaterialAlertDialogBuilder
3131import com.google.android.material.snackbar.Snackbar
32+ import kotlinx.coroutines.CoroutineExceptionHandler
33+ import kotlinx.coroutines.delay
3234import kotlinx.coroutines.flow.collectLatest
3335import kotlinx.coroutines.launch
36+ import kotlinx.serialization.json.encodeToJsonElement
3437import org.wikipedia.Constants
3538import org.wikipedia.Constants.InvokeSource
3639import org.wikipedia.R
3740import org.wikipedia.WikipediaApp
3841import org.wikipedia.activity.BaseActivity
3942import org.wikipedia.activity.SingleWebViewActivity
43+ import org.wikipedia.analytics.ABTest
4044import org.wikipedia.analytics.eventplatform.ArticleLinkPreviewInteractionEvent
4145import org.wikipedia.analytics.eventplatform.BreadCrumbLogEvent
4246import org.wikipedia.analytics.eventplatform.DonorExperienceEvent
47+ import org.wikipedia.analytics.eventplatform.RabbitHolesEvent
4348import org.wikipedia.analytics.metricsplatform.ArticleLinkPreviewInteraction
49+ import org.wikipedia.analytics.metricsplatform.RabbitHolesAnalyticsHelper
4450import org.wikipedia.auth.AccountUtil
4551import org.wikipedia.commons.FilePageActivity
4652import org.wikipedia.concurrency.FlowEventBus
53+ import org.wikipedia.database.AppDatabase
4754import org.wikipedia.databinding.ActivityPageBinding
55+ import org.wikipedia.dataclient.ServiceFactory
4856import org.wikipedia.dataclient.WikiSite
4957import org.wikipedia.dataclient.donate.CampaignCollection
5058import org.wikipedia.dataclient.mwapi.MwQueryPage
@@ -58,13 +66,15 @@ import org.wikipedia.events.ChangeTextSizeEvent
5866import org.wikipedia.extensions.parcelableExtra
5967import org.wikipedia.gallery.GalleryActivity
6068import org.wikipedia.history.HistoryEntry
69+ import org.wikipedia.json.JsonUtil
6170import org.wikipedia.language.LangLinksActivity
6271import org.wikipedia.navtab.NavTab
6372import org.wikipedia.notifications.AnonymousNotificationHelper
6473import org.wikipedia.notifications.NotificationActivity
6574import org.wikipedia.page.linkpreview.LinkPreviewDialog
6675import org.wikipedia.page.tabs.TabActivity
6776import org.wikipedia.readinglist.ReadingListActivity
77+ import org.wikipedia.readinglist.ReadingListsShareHelper
6878import org.wikipedia.search.SearchActivity
6979import org.wikipedia.settings.Prefs
7080import org.wikipedia.staticdata.MainPageNameData
@@ -84,9 +94,11 @@ import org.wikipedia.util.UriUtil
8494import org.wikipedia.util.log.L
8595import org.wikipedia.views.FrameLayoutNavMenuTriggerer
8696import org.wikipedia.views.ObservableWebView
97+ import org.wikipedia.views.SurveyDialog
8798import org.wikipedia.views.ViewUtil
8899import org.wikipedia.watchlist.WatchlistExpiry
89100import java.util.Locale
101+ import java.util.concurrent.TimeUnit
90102
91103class PageActivity : BaseActivity (), PageFragment.Callback, LinkPreviewDialog.LoadPageCallback, FrameLayoutNavMenuTriggerer.Callback {
92104
@@ -104,6 +116,7 @@ class PageActivity : BaseActivity(), PageFragment.Callback, LinkPreviewDialog.Lo
104116 private val isCabOpen get() = currentActionModes.isNotEmpty()
105117 private var exclusiveTooltipRunnable: Runnable ? = null
106118 private var isTooltipShowing = false
119+ private var suggestedSearchTerm: String? = null
107120
108121 private val requestEditSectionLauncher = registerForActivityResult(ActivityResultContracts .StartActivityForResult ()) {
109122 if (it.resultCode == EditHandler .RESULT_REFRESH_PAGE ) {
@@ -179,6 +192,13 @@ class PageActivity : BaseActivity(), PageFragment.Callback, LinkPreviewDialog.Lo
179192 }
180193 }
181194
195+ private val requestSuggestedReadingListLauncher = registerForActivityResult(ActivityResultContracts .StartActivityForResult ()) {
196+ if (it.resultCode == RESULT_CANCELED ) {
197+ RabbitHolesEvent .submit(" impression" , " reading_list_warn" )
198+ FeedbackUtil .showMessage(this , R .string.suggested_reading_list_back_cancel)
199+ }
200+ }
201+
182202 public override fun onCreate (savedInstanceState : Bundle ? ) {
183203 super .onCreate(savedInstanceState)
184204 PreferenceManager .setDefaultValues(this , R .xml.preferences, false )
@@ -214,7 +234,8 @@ class PageActivity : BaseActivity(), PageFragment.Callback, LinkPreviewDialog.Lo
214234 binding.pageToolbarButtonSearch.setOnClickListener {
215235 pageFragment.articleInteractionEvent?.logSearchWikipediaClick()
216236 pageFragment.metricsPlatformArticleEventToolbarInteraction.logSearchWikipediaClick()
217- startActivity(SearchActivity .newIntent(this @PageActivity, InvokeSource .TOOLBAR , null ))
237+ startActivity(SearchActivity .newIntent(this @PageActivity, InvokeSource .TOOLBAR , null ,
238+ suggestedSearchQuery = suggestedSearchTerm))
218239 }
219240 binding.pageToolbarButtonTabs.updateTabCount(false )
220241 binding.pageToolbarButtonTabs.setOnClickListener {
@@ -397,6 +418,8 @@ class PageActivity : BaseActivity(), PageFragment.Callback, LinkPreviewDialog.Lo
397418 override fun onPageLoadComplete () {
398419 removeTransitionAnimState()
399420 maybeShowThemeTooltip()
421+
422+ maybeStartRabbitHole()
400423 }
401424
402425 override fun onPageDismissBottomSheet () {
@@ -576,6 +599,9 @@ class PageActivity : BaseActivity(), PageFragment.Callback, LinkPreviewDialog.Lo
576599 * foreground tab.
577600 */
578601 private fun loadPage (pageTitle : PageTitle ? , entry : HistoryEntry ? , position : TabPosition ) {
602+
603+ binding.pageToolbarButtonSearch.setText(R .string.search_hint)
604+
579605 if (isDestroyed || pageTitle == null || entry == null ) {
580606 return
581607 }
@@ -794,6 +820,107 @@ class PageActivity : BaseActivity(), PageFragment.Callback, LinkPreviewDialog.Lo
794820 }
795821 }
796822
823+ private fun maybeStartRabbitHole () {
824+ if (! RabbitHolesAnalyticsHelper .rabbitHolesEnabled || RabbitHolesAnalyticsHelper .abcTest.group == ABTest .GROUP_1 ) {
825+ return
826+ }
827+ lifecycleScope.launch(CoroutineExceptionHandler { _, t ->
828+ L .e(t)
829+ }) {
830+ pageFragment.title?.let { title ->
831+ if (RabbitHolesAnalyticsHelper .abcTest.group == ABTest .GROUP_2 ) {
832+ if (title.displayText != MainPageNameData .valueFor(title.wikiSite.languageCode)) {
833+ val response = ServiceFactory .get(title.wikiSite).searchMoreLike(" morelike:${title.prefixedText} " , 3 , 3 )
834+ response.query?.pages?.firstOrNull()?.let { page ->
835+ applySuggestedSearchTerm(page.displayTitle(title.wikiSite.languageCode))
836+ }
837+ }
838+ } else if (RabbitHolesAnalyticsHelper .abcTest.group == ABTest .GROUP_3 && ! Prefs .suggestedReadingListDialogShown) {
839+ val historyEntries = AppDatabase .instance.historyEntryDao().getLastHistoryEntries(title.wikiSite.languageCode, 2 )
840+ .filter { it.displayTitle != MainPageNameData .valueFor(title.wikiSite.languageCode) }
841+ if (historyEntries.size < 2 ) {
842+ return @launch
843+ }
844+
845+ val pages = mutableListOf<MwQueryPage >()
846+ historyEntries.forEach { entry ->
847+ val response = ServiceFactory .get(title.wikiSite).searchMoreLike(" morelike:${entry.apiTitle} " , 10 , 10 )
848+ response.query?.pages?.filter { it.title != historyEntries[0 ].apiTitle && it.title != historyEntries[1 ].apiTitle }?.take(5 )?.let { pages.addAll(it) }
849+ }
850+
851+ if (pages.isNotEmpty()) {
852+ applySuggestedReadingList(historyEntries[0 ], historyEntries[1 ], pages)
853+
854+ Prefs .suggestedReadingListDialogShown = true
855+ RabbitHolesEvent .submit(" impression" , " reading_list_prompt" )
856+
857+ MaterialAlertDialogBuilder (this @PageActivity)
858+ .setTitle(R .string.suggested_reading_list_dialog_title)
859+ .setMessage(R .string.suggested_reading_list_dialog_body)
860+ .setPositiveButton(R .string.suggested_reading_list_dialog_positive) { _, _ ->
861+ RabbitHolesEvent .submit(" enter_click" , " reading_list_prompt" )
862+ requestSuggestedReadingListLauncher.launch(ReadingListActivity .newIntent(this @PageActivity, true , suggestedList = true ))
863+ }
864+ .setNegativeButton(R .string.suggested_reading_list_dialog_negative) { _, _ ->
865+ RabbitHolesEvent .submit(" ignore_click" , " reading_list_prompt" )
866+ FeedbackUtil .showMessage(this @PageActivity, R .string.suggested_reading_list_later_snackbar)
867+ }
868+ .show()
869+ }
870+ }
871+ }
872+ }
873+ maybeShowRabbitHolesSurvey()
874+ }
875+
876+ private fun applySuggestedSearchTerm (term : String ) {
877+ suggestedSearchTerm = term
878+ binding.pageToolbarButtonSearch.text = term
879+ }
880+
881+ private fun applySuggestedReadingList (basedOnTitle1 : HistoryEntry , basedOnTitle2 : HistoryEntry , pages : List <MwQueryPage >) {
882+ val listItems = pages.map {
883+ JsonUtil .json.encodeToJsonElement(
884+ ReadingListsShareHelper .ExportedReadingListPage (
885+ basedOnTitle1.title.wikiSite.languageCode,
886+ it.displayTitle(basedOnTitle1.title.wikiSite.languageCode),
887+ it.ns,
888+ it.description,
889+ it.thumbUrl()
890+ )
891+ )
892+ }
893+ val readingList = ReadingListsShareHelper .ExportedReadingList (
894+ list = mapOf (basedOnTitle1.title.wikiSite.languageCode to listItems),
895+ name = getString(R .string.suggested_reading_list_title),
896+ description = getString(R .string.suggested_reading_list_description_multi,
897+ StringUtil .fromHtml(basedOnTitle1.title.displayText).toString(),
898+ StringUtil .fromHtml(basedOnTitle2.title.displayText).toString())
899+ )
900+ Prefs .importReadingListsDialogShown = false
901+ Prefs .suggestedReadingListsData = JsonUtil .encodeToString(readingList)
902+ }
903+
904+ private fun maybeShowRabbitHolesSurvey () {
905+ lifecycleScope.launch(CoroutineExceptionHandler { _, t ->
906+ L .e(t)
907+ }) {
908+ delay(TimeUnit .SECONDS .toMillis(if (ReleaseUtil .isDevRelease) 1L else 10L ))
909+ pageFragment.historyEntry?.let {
910+ if (! Prefs .suggestedContentSurveyShown && it.source == HistoryEntry .SOURCE_RABBIT_HOLE_SEARCH ) {
911+ Prefs .suggestedContentSurveyShown = true
912+ SurveyDialog .showFeedbackOptionsDialog(
913+ this @PageActivity,
914+ titleId = R .string.rabbit_holes_survey_dialog_title,
915+ messageId = R .string.rabbit_holes_survey_dialog_body,
916+ snackbarMessageId = R .string.survey_dialog_submitted_snackbar,
917+ invokeSource = InvokeSource .RABBIT_HOLE_SEARCH
918+ )
919+ }
920+ }
921+ }
922+ }
923+
797924 companion object {
798925 private const val LANGUAGE_CODE_BUNDLE_KEY = " language"
799926 private const val EXCEPTION_MESSAGE_WEBVIEW = " webview"
0 commit comments