Skip to content

Commit ac984f9

Browse files
Merge branch 'main' into Recurring-tasks-WorkManager
2 parents 552d2b5 + f5177a9 commit ac984f9

32 files changed

+608
-23
lines changed

app/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ android {
3737
applicationId 'org.wikipedia'
3838
minSdk 21
3939
targetSdk 34
40-
versionCode 50496
40+
versionCode 50498
4141
testApplicationId 'org.wikipedia.test'
4242
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
4343
testInstrumentationRunnerArguments clearPackageData: 'true'

app/src/main/java/org/wikipedia/descriptions/DescriptionEditFragment.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,10 @@ import org.wikipedia.suggestededits.SuggestedEditsSurvey
5353
import org.wikipedia.suggestededits.SuggestionsActivity
5454
import org.wikipedia.util.DeviceUtil
5555
import org.wikipedia.util.FeedbackUtil
56+
import org.wikipedia.util.ReleaseUtil
5657
import org.wikipedia.util.StringUtil
5758
import org.wikipedia.util.log.L
59+
import org.wikipedia.views.SuggestedArticleDescriptionsDialog
5860
import java.io.IOException
5961
import java.util.Date
6062
import java.util.concurrent.TimeUnit
@@ -219,7 +221,9 @@ class DescriptionEditFragment : Fragment() {
219221
binding.fragmentDescriptionEditView.setEditAllowed(editingAllowed)
220222
binding.fragmentDescriptionEditView.updateInfoText()
221223

222-
binding.fragmentDescriptionEditView.isSuggestionButtonEnabled = true
224+
binding.fragmentDescriptionEditView.isSuggestionButtonEnabled = ReleaseUtil.isPreBetaRelease &&
225+
SuggestedArticleDescriptionsDialog.availableLanguages.contains(pageTitle.wikiSite.languageCode) &&
226+
binding.fragmentDescriptionEditView.description.isNullOrEmpty()
223227

224228
if (binding.fragmentDescriptionEditView.isSuggestionButtonEnabled) {
225229
binding.fragmentDescriptionEditView.showSuggestedDescriptionsLoadingProgress()

app/src/main/java/org/wikipedia/feed/FeedContentType.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import org.wikipedia.feed.aggregated.AggregatedFeedContentClient
1010
import org.wikipedia.feed.becauseyouread.BecauseYouReadClient
1111
import org.wikipedia.feed.dataclient.FeedClient
1212
import org.wikipedia.feed.mainpage.MainPageClient
13+
import org.wikipedia.feed.places.PlacesFeedClient
1314
import org.wikipedia.feed.random.RandomClient
1415
import org.wikipedia.feed.suggestededits.SuggestedEditsFeedClient
1516
import org.wikipedia.model.EnumCode
@@ -32,6 +33,11 @@ enum class FeedContentType(private val code: Int,
3233
return if (isEnabled) AggregatedFeedContentClient.TopReadArticles(coroutineScope, aggregatedClient) else null
3334
}
3435
},
36+
PLACES(11, R.string.places_title, R.string.feed_item_type_places, false) {
37+
override fun newClient(coroutineScope: CoroutineScope, aggregatedClient: AggregatedFeedContentClient, age: Int): FeedClient? {
38+
return if (isEnabled) PlacesFeedClient(coroutineScope) else null
39+
}
40+
},
3541
FEATURED_IMAGE(7, R.string.view_featured_image_card_title, R.string.feed_item_type_featured_image, false) {
3642
override fun newClient(coroutineScope: CoroutineScope, aggregatedClient: AggregatedFeedContentClient, age: Int): FeedClient? {
3743
return if (isEnabled) AggregatedFeedContentClient.FeaturedImage(coroutineScope, aggregatedClient) else null

app/src/main/java/org/wikipedia/feed/FeedCoordinatorBase.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import org.wikipedia.feed.model.CardType
1414
import org.wikipedia.feed.news.NewsCard
1515
import org.wikipedia.feed.offline.OfflineCard
1616
import org.wikipedia.feed.onthisday.OnThisDayCard
17+
import org.wikipedia.feed.places.PlacesFeedClient
1718
import org.wikipedia.feed.progress.ProgressCard
1819
import org.wikipedia.feed.suggestededits.SuggestedEditsFeedClient
1920
import org.wikipedia.feed.topread.TopReadListCard
@@ -101,6 +102,10 @@ abstract class FeedCoordinatorBase(private val context: Context) {
101102
FeedContentType.MAIN_PAGE.isEnabled = false
102103
FeedContentType.saveState()
103104
}
105+
card.type() == CardType.PLACES -> {
106+
FeedContentType.PLACES.isEnabled = false
107+
FeedContentType.saveState()
108+
}
104109
else -> {
105110
addHiddenCard(card)
106111
}
@@ -120,6 +125,10 @@ abstract class FeedCoordinatorBase(private val context: Context) {
120125
FeedContentType.MAIN_PAGE.isEnabled = true
121126
FeedContentType.saveState()
122127
}
128+
card.type() == CardType.PLACES -> {
129+
FeedContentType.PLACES.isEnabled = true
130+
FeedContentType.saveState()
131+
}
123132
else -> unHideCard(card)
124133
}
125134
insertCard(card, position)
@@ -266,6 +275,7 @@ abstract class FeedCoordinatorBase(private val context: Context) {
266275
return pendingClient is SuggestedEditsFeedClient ||
267276
pendingClient is AnnouncementClient ||
268277
pendingClient is BecauseYouReadClient ||
278+
pendingClient is PlacesFeedClient ||
269279
pendingClient == null
270280
}
271281

app/src/main/java/org/wikipedia/feed/model/CardType.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import org.wikipedia.feed.mainpage.MainPageCardView
1313
import org.wikipedia.feed.news.NewsCardView
1414
import org.wikipedia.feed.offline.OfflineCardView
1515
import org.wikipedia.feed.onthisday.OnThisDayCardView
16+
import org.wikipedia.feed.places.PlacesCardView
1617
import org.wikipedia.feed.progress.ProgressCardView
1718
import org.wikipedia.feed.random.RandomCardView
1819
import org.wikipedia.feed.searchbar.SearchCardView
@@ -103,6 +104,11 @@ enum class CardType constructor(private val code: Int,
103104
return AccessibilityCardView(ctx)
104105
}
105106
},
107+
PLACES(23, FeedContentType.PLACES) {
108+
override fun newView(ctx: Context): FeedCardView<*> {
109+
return PlacesCardView(ctx)
110+
}
111+
},
106112
// TODO: refactor this item when the new Modern Event Platform is finished.
107113
ARTICLE_ANNOUNCEMENT(96) {
108114
override fun newView(ctx: Context): FeedCardView<*> {
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package org.wikipedia.feed.places
2+
3+
import org.wikipedia.R
4+
import org.wikipedia.WikipediaApp
5+
import org.wikipedia.dataclient.WikiSite
6+
import org.wikipedia.dataclient.page.NearbyPage
7+
import org.wikipedia.feed.model.CardType
8+
import org.wikipedia.feed.model.WikiSiteCard
9+
import org.wikipedia.util.DateUtil
10+
11+
class PlacesCard(wiki: WikiSite,
12+
val age: Int,
13+
val nearbyPage: NearbyPage? = null) : WikiSiteCard(wiki) {
14+
15+
override fun type(): CardType {
16+
return CardType.PLACES
17+
}
18+
19+
override fun title(): String {
20+
return WikipediaApp.instance.getString(R.string.places_card_title)
21+
}
22+
23+
override fun subtitle(): String {
24+
return DateUtil.getFeedCardDateString(age)
25+
}
26+
27+
fun footerActionText(): String {
28+
return WikipediaApp.instance.getString(R.string.places_card_action)
29+
}
30+
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package org.wikipedia.feed.places
2+
3+
import android.content.Context
4+
import android.location.Location
5+
import android.view.LayoutInflater
6+
import androidx.core.view.isVisible
7+
import org.wikipedia.R
8+
import org.wikipedia.analytics.eventplatform.PlacesEvent
9+
import org.wikipedia.databinding.ViewPlacesCardBinding
10+
import org.wikipedia.feed.view.CardFooterView
11+
import org.wikipedia.feed.view.DefaultFeedCardView
12+
import org.wikipedia.feed.view.FeedAdapter
13+
import org.wikipedia.history.HistoryEntry
14+
import org.wikipedia.page.PageTitle
15+
import org.wikipedia.places.PlacesActivity
16+
import org.wikipedia.readinglist.LongPressMenu
17+
import org.wikipedia.readinglist.database.ReadingListPage
18+
import org.wikipedia.settings.Prefs
19+
import org.wikipedia.util.GeoUtil
20+
import org.wikipedia.util.StringUtil
21+
import org.wikipedia.views.ViewUtil
22+
import java.util.Locale
23+
24+
class PlacesCardView(context: Context) : DefaultFeedCardView<PlacesCard>(context) {
25+
26+
private val binding = ViewPlacesCardBinding.inflate(LayoutInflater.from(context), this, true)
27+
28+
override var card: PlacesCard? = null
29+
set(value) {
30+
field = value
31+
value?.let {
32+
header(it)
33+
footer(it)
34+
updateContents(it)
35+
}
36+
}
37+
38+
override var callback: FeedAdapter.Callback? = null
39+
set(value) {
40+
field = value
41+
binding.cardHeader.setCallback(value)
42+
}
43+
44+
private fun updateContents(card: PlacesCard) {
45+
card.nearbyPage?.let {
46+
binding.placesEnableLocationContainer.isVisible = false
47+
binding.placesArticleContainer.isVisible = true
48+
binding.placesCardTitle.text = StringUtil.fromHtml(it.pageTitle.displayText)
49+
binding.placesCardDescription.text = StringUtil.fromHtml(it.pageTitle.description)
50+
binding.placesCardDescription.isVisible = !it.pageTitle.description.isNullOrEmpty()
51+
it.pageTitle.thumbUrl?.let { url ->
52+
ViewUtil.loadImage(binding.placesCardThumbnail, url, circleShape = true)
53+
}
54+
Prefs.placesLastLocationAndZoomLevel?.first?.let { location ->
55+
if (GeoUtil.isSamePlace(location.latitude, it.location.latitude, location.longitude, it.location.longitude)) {
56+
binding.placesCardDistance.text = context.getString(R.string.places_card_distance_unknown)
57+
} else {
58+
val distanceText = GeoUtil.getDistanceWithUnit(location, it.location, Locale.getDefault())
59+
binding.placesCardDistance.text = context.getString(R.string.places_card_distance_suffix, distanceText)
60+
}
61+
}
62+
binding.placesCardContainer.setOnClickListener { _ ->
63+
goToPlaces(it.pageTitle, it.location)
64+
}
65+
binding.placesCardContainer.setOnLongClickListener { view ->
66+
PlacesEvent.logAction("places_click", "explore_feed_more_menu")
67+
LongPressMenu(view, openPageInPlaces = true, location = it.location, callback = object : LongPressMenu.Callback {
68+
override fun onOpenInPlaces(entry: HistoryEntry, location: Location) {
69+
goToPlaces(entry.title, location)
70+
}
71+
72+
override fun onOpenInNewTab(entry: HistoryEntry) {
73+
callback?.onSelectPage(card, entry, true)
74+
}
75+
76+
override fun onAddRequest(entry: HistoryEntry, addToDefault: Boolean) {
77+
callback?.onAddPageToList(entry, addToDefault)
78+
}
79+
80+
override fun onMoveRequest(page: ReadingListPage?, entry: HistoryEntry) {
81+
callback?.onMovePageToList(page!!.listId, entry)
82+
}
83+
}).show(HistoryEntry(it.pageTitle, HistoryEntry.SOURCE_FEED_PLACES))
84+
85+
false
86+
}
87+
} ?: run {
88+
binding.placesEnableLocationContainer.isVisible = true
89+
binding.placesArticleContainer.isVisible = false
90+
binding.placesCardContainer.setOnClickListener {
91+
goToPlaces()
92+
}
93+
binding.placesEnableLocationButton.setOnClickListener {
94+
goToPlaces()
95+
}
96+
}
97+
}
98+
99+
private fun header(card: PlacesCard) {
100+
binding.cardHeader.setTitle(card.title())
101+
.setCard(card)
102+
.setLangCode(null)
103+
.setCallback(callback)
104+
}
105+
106+
private fun footer(card: PlacesCard) {
107+
binding.cardFooter.callback = CardFooterView.Callback {
108+
PlacesEvent.logAction("places_click", "explore_feed")
109+
goToPlaces()
110+
}
111+
binding.cardFooter.setFooterActionText(card.footerActionText(), card.wikiSite().languageCode)
112+
}
113+
114+
private fun goToPlaces(pageTitle: PageTitle? = null, location: Location? = null) {
115+
context.startActivity(PlacesActivity.newIntent(context, pageTitle, location))
116+
}
117+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package org.wikipedia.feed.places
2+
3+
import android.content.Context
4+
import kotlinx.coroutines.CoroutineExceptionHandler
5+
import kotlinx.coroutines.CoroutineScope
6+
import kotlinx.coroutines.Job
7+
import kotlinx.coroutines.launch
8+
import org.wikipedia.dataclient.ServiceFactory
9+
import org.wikipedia.dataclient.WikiSite
10+
import org.wikipedia.dataclient.page.NearbyPage
11+
import org.wikipedia.feed.dataclient.FeedClient
12+
import org.wikipedia.page.PageTitle
13+
import org.wikipedia.places.PlacesFragment
14+
import org.wikipedia.settings.Prefs
15+
import org.wikipedia.util.GeoUtil
16+
import org.wikipedia.util.ImageUrlUtil
17+
18+
class PlacesFeedClient(
19+
private val coroutineScope: CoroutineScope
20+
) : FeedClient {
21+
22+
private lateinit var cb: FeedClient.Callback
23+
private var age: Int = 0
24+
private var clientJob: Job? = null
25+
26+
override fun request(context: Context, wiki: WikiSite, age: Int, cb: FeedClient.Callback) {
27+
this.age = age
28+
this.cb = cb
29+
30+
Prefs.placesLastLocationAndZoomLevel?.let {
31+
coroutineScope.launch(CoroutineExceptionHandler { _, throwable ->
32+
cb.error(throwable)
33+
}) {
34+
val location = it.first
35+
val response = ServiceFactory.get(wiki).getGeoSearch("${location.latitude}|${location.longitude}", 10000, 10, 10)
36+
val lastPage = response.query?.pages.orEmpty()
37+
.filter { it.coordinates != null && !GeoUtil.isSamePlace(location.latitude, it.coordinates[0].lat, location.longitude, it.coordinates[0].lon) }
38+
.map {
39+
NearbyPage(it.pageId, PageTitle(it.title, wiki,
40+
if (it.thumbUrl().isNullOrEmpty()) null else ImageUrlUtil.getUrlForPreferredSize(it.thumbUrl()!!, PlacesFragment.THUMB_SIZE),
41+
it.description, it.displayTitle(wiki.languageCode)), it.coordinates!![0].lat, it.coordinates[0].lon)
42+
}[age % 10]
43+
cb.success(listOf(PlacesCard(wiki, age, lastPage)))
44+
}
45+
} ?: run {
46+
cb.success(listOf(PlacesCard(wiki, age)))
47+
}
48+
}
49+
50+
override fun cancel() {
51+
clientJob?.cancel()
52+
}
53+
}

app/src/main/java/org/wikipedia/history/HistoryEntry.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,5 +99,6 @@ class HistoryEntry(
9999
const val SOURCE_FILE_PAGE = 39
100100
const val SOURCE_SINGLE_WEBVIEW = 40
101101
const val SOURCE_SUGGESTED_EDITS_RECENT_EDITS = 41
102+
const val SOURCE_FEED_PLACES = 42
102103
}
103104
}

app/src/main/java/org/wikipedia/places/PlacesFragment.kt

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ class PlacesFragment : Fragment(), LinkPreviewDialog.LoadPageCallback, LinkPrevi
138138
permissions.getOrDefault(Manifest.permission.ACCESS_COARSE_LOCATION, false) -> {
139139
PlacesEvent.logAction("location_permission_granted", "map_view")
140140
startLocationTracking()
141-
goToLocation(viewModel.location)
141+
goToLocation(viewModel.location ?: getDefaultLocation())
142142
}
143143
else -> {
144144
PlacesEvent.logAction("location_permission_denied", "map_view")
@@ -386,8 +386,12 @@ class PlacesFragment : Fragment(), LinkPreviewDialog.LoadPageCallback, LinkPrevi
386386
viewModel.location?.let {
387387
goToLocation(it)
388388
} ?: run {
389-
val lastLocationAndZoomLevel = Prefs.placesLastLocationAndZoomLevel
390-
goToLocation(lastLocationAndZoomLevel?.first, lastLocationAndZoomLevel?.second ?: lastZoom)
389+
if (Prefs.placesDefaultLocationLatLng != null) {
390+
goToLocation(getDefaultLocation())
391+
} else {
392+
val lastLocationAndZoomLevel = Prefs.placesLastLocationAndZoomLevel
393+
goToLocation(lastLocationAndZoomLevel?.first, lastLocationAndZoomLevel?.second ?: lastZoom)
394+
}
391395

392396
if (!haveLocationPermissions()) {
393397
locationPermissionRequest.launch(arrayOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION))
@@ -405,6 +409,17 @@ class PlacesFragment : Fragment(), LinkPreviewDialog.LoadPageCallback, LinkPrevi
405409
}
406410
}
407411

412+
private fun getDefaultLocation(): Location? {
413+
return Prefs.placesDefaultLocationLatLng?.let { defaultLocationString ->
414+
val defaultLocationStrings = defaultLocationString.split(",").map { it.toDouble() }
415+
val defaultLocation = Location("").apply {
416+
latitude = defaultLocationStrings[0]
417+
longitude = defaultLocationStrings[1]
418+
}
419+
return defaultLocation
420+
}
421+
}
422+
408423
private fun updateToggleViews(isMapVisible: Boolean) {
409424
if ((binding.listRecyclerView.isVisible || binding.listEmptyContainer.isVisible) && isMapVisible) {
410425
PlacesEvent.logAction("map_view_click", "map_view")
@@ -629,6 +644,18 @@ class PlacesFragment : Fragment(), LinkPreviewDialog.LoadPageCallback, LinkPrevi
629644
}
630645
}
631646
binding.listRecyclerView.adapter = RecyclerViewAdapter(pages)
647+
648+
if (pages.isEmpty() && Prefs.placesLastLocationAndZoomLevel == null) {
649+
FeedbackUtil.makeSnackbar(requireActivity(), getString(R.string.places_empty_message_snackbar)).run {
650+
setAction(R.string.dialog_close_description) {
651+
dismiss()
652+
}
653+
show()
654+
}
655+
lastLocation?.let {
656+
Prefs.placesLastLocationAndZoomLevel = Pair(it, lastZoom)
657+
}
658+
}
632659
}
633660

634661
private fun haveLocationPermissions(): Boolean {

0 commit comments

Comments
 (0)