diff --git a/app/build.gradle b/app/build.gradle
index f5318e10a52..1883147dd2e 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -37,7 +37,7 @@ android {
applicationId 'org.wikipedia'
minSdkVersion 21
targetSdkVersion 33
- versionCode 50444
+ versionCode 50448
testApplicationId 'org.wikipedia.test'
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
testInstrumentationRunnerArguments clearPackageData: 'true'
@@ -56,6 +56,10 @@ android {
buildConfigField "String", "META_WIKI_BASE_URI", '"https://meta.wikimedia.org"'
buildConfigField "String", "EVENTGATE_ANALYTICS_EXTERNAL_BASE_URI", '"https://intake-analytics.wikimedia.org"'
buildConfigField "String", "EVENTGATE_LOGGING_EXTERNAL_BASE_URI", '"https://intake-logging.wikimedia.org"'
+ def TEST_LOGIN_USERNAME = System.getenv('TEST_LOGIN_USERNAME')
+ def TEST_LOGIN_PASSWORD = System.getenv('TEST_LOGIN_PASSWORD')
+ buildConfigField "String", "TEST_LOGIN_USERNAME", TEST_LOGIN_USERNAME != null ? "\"${TEST_LOGIN_USERNAME}\"" : '"Foo"'
+ buildConfigField "String", "TEST_LOGIN_PASSWORD", TEST_LOGIN_PASSWORD != null ? "\"${TEST_LOGIN_PASSWORD}\"" : '"Bar"'
}
testOptions {
@@ -110,11 +114,6 @@ android {
buildConfigField "String", "META_WIKI_BASE_URI", '"https://meta.wikimedia.beta.wmflabs.org"'
buildConfigField "String", "EVENTGATE_ANALYTICS_EXTERNAL_BASE_URI", '"https://intake-analytics.wikimedia.beta.wmflabs.org"'
buildConfigField "String", "EVENTGATE_LOGGING_EXTERNAL_BASE_URI", '"https://intake-logging.wikimedia.beta.wmflabs.org"'
-
- def TEST_LOGIN_USERNAME = System.getenv('TEST_LOGIN_USERNAME')
- def TEST_LOGIN_PASSWORD = System.getenv('TEST_LOGIN_PASSWORD')
- buildConfigField "String", "TEST_LOGIN_USERNAME", TEST_LOGIN_USERNAME != null ? "\"${TEST_LOGIN_USERNAME}\"" : '"Foo"'
- buildConfigField "String", "TEST_LOGIN_PASSWORD", TEST_LOGIN_PASSWORD != null ? "\"${TEST_LOGIN_PASSWORD}\"" : '"Bar"'
}
prod {
versionName computeVersionName(defaultConfig.versionCode, 'r')
@@ -172,15 +171,16 @@ dependencies {
String okHttpVersion = '4.10.0'
String retrofitVersion = '2.9.0'
- String glideVersion = '4.13.2'
+ String glideVersion = '4.15.1'
String mockitoVersion = '5.2.0'
- String leakCanaryVersion = '2.10'
- String kotlinCoroutinesVersion = '1.3.9'
+ String leakCanaryVersion = '2.11'
+ String kotlinCoroutinesVersion = '1.7.1'
String firebaseMessagingVersion = '23.1.2'
String mlKitVersion = '17.0.4'
String roomVersion = "2.5.1"
String espressoVersion = '3.5.1'
- String serialization_version = '1.4.0'
+ String serialization_version = '1.5.1'
+ String metricsVersion = '1.12'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.3'
@@ -204,6 +204,7 @@ dependencies {
implementation 'com.android.installreferrer:installreferrer:2.2'
implementation 'androidx.drawerlayout:drawerlayout:1.2.0'
implementation 'androidx.work:work-runtime-ktx:2.8.1'
+ implementation "org.wikimedia.metrics:metrics-platform:$metricsVersion"
implementation ('com.github.michael-rapp:chrome-like-tab-switcher:0.4.6') {
exclude group: 'org.jetbrains'
@@ -219,10 +220,10 @@ dependencies {
implementation "io.reactivex.rxjava3:rxjava:3.1.6"
implementation "io.reactivex.rxjava3:rxandroid:3.0.2"
implementation 'org.apache.commons:commons-lang3:3.12.0'
- implementation 'org.jsoup:jsoup:1.15.4'
+ implementation 'org.jsoup:jsoup:1.16.1'
implementation 'com.github.chrisbanes:PhotoView:2.3.0'
- implementation "com.github.skydoves:balloon:1.3.4"
- implementation "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:0.8.0"
+ implementation "com.github.skydoves:balloon:1.5.3"
+ implementation "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0"
implementation("androidx.room:room-runtime:$roomVersion")
annotationProcessor "androidx.room:room-compiler:$roomVersion"
@@ -249,7 +250,7 @@ dependencies {
testImplementation 'junit:junit:4.13.2'
testImplementation "org.mockito:mockito-inline:$mockitoVersion"
- testImplementation 'org.robolectric:robolectric:4.9.2'
+ testImplementation 'org.robolectric:robolectric:4.10.3'
testImplementation "com.squareup.okhttp3:okhttp:$okHttpVersion"
testImplementation "com.squareup.okhttp3:mockwebserver:$okHttpVersion"
testImplementation 'org.hamcrest:hamcrest:2.2'
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
index e97f446a604..782306469e2 100644
--- a/app/proguard-rules.pro
+++ b/app/proguard-rules.pro
@@ -41,4 +41,10 @@
}
-keepclasseswithmembers class org.wikipedia.** {
kotlinx.serialization.KSerializer serializer(...);
-}
\ No newline at end of file
+}
+
+# --- Metrics Platform ---
+-dontwarn edu.umd.cs.findbugs.annotations.SuppressFBWarnings
+-dontwarn java.beans.ConstructorProperties
+-dontwarn lombok.Generated
+# --- /Metrics Platform ---
diff --git a/app/src/androidTest/java/org/wikipedia/main/LoggedInTests.kt b/app/src/androidTest/java/org/wikipedia/main/LoggedInTests.kt
index 3bc250a2130..5aca5553da6 100644
--- a/app/src/androidTest/java/org/wikipedia/main/LoggedInTests.kt
+++ b/app/src/androidTest/java/org/wikipedia/main/LoggedInTests.kt
@@ -12,6 +12,7 @@ import org.hamcrest.Matchers.allOf
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.wikipedia.BuildConfig
import org.wikipedia.R
import org.wikipedia.TestUtil
@@ -52,10 +53,10 @@ class LoggedInTests {
// Type in an incorrect username and password
onView(allOf(TestUtil.withGrandparent(withId(R.id.login_username_text)), withClassName(`is`("org.wikipedia.views.PlainPasteEditText"))))
- .perform(replaceText("Foo"), closeSoftKeyboard())
+ .perform(replaceText(BuildConfig.TEST_LOGIN_USERNAME), closeSoftKeyboard())
onView(allOf(TestUtil.withGrandparent(withId(R.id.login_password_input)), withClassName(`is`("org.wikipedia.views.PlainPasteEditText"))))
- .perform(replaceText("Bar"), closeSoftKeyboard())
+ .perform(replaceText(BuildConfig.TEST_LOGIN_PASSWORD), closeSoftKeyboard())
// Click the login button
onView(withId(R.id.login_button))
diff --git a/app/src/androidTest/java/org/wikipedia/main/SmokeTests.kt b/app/src/androidTest/java/org/wikipedia/main/SmokeTests.kt
index c66c07497a8..89223cc3a04 100644
--- a/app/src/androidTest/java/org/wikipedia/main/SmokeTests.kt
+++ b/app/src/androidTest/java/org/wikipedia/main/SmokeTests.kt
@@ -477,7 +477,7 @@ class SmokeTests {
TestUtil.delay(1)
// Go to Saved tab
- onView(withId(NavTab.READING_LISTS.id())).perform(click())
+ onView(withId(NavTab.READING_LISTS.id)).perform(click())
TestUtil.delay(1)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 0b5e534d336..9ff95569266 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -305,6 +305,10 @@
+
+
, pageData: PageData? = null) {
+ if (ReleaseUtil.isPreProdRelease && Prefs.isEventLoggingEnabled) {
+ MetricsPlatform.client.submitMetricsEvent(
+ EVENT_NAME_BASE + eventName,
+ getClientData(pageData),
+ customData + applicationData)
+ }
+ }
+
+ private fun getClientData(pageData: PageData?): ClientData {
+ return ClientData(
+ MetricsPlatform.agentData,
+ pageData,
+ MetricsPlatform.mediawikiData,
+ getPerformerData(),
+ MetricsPlatform.domain
+ )
+ }
+
+ protected fun getPageData(fragment: PageFragment?): PageData? {
+ val pageProperties = fragment?.page?.pageProperties ?: return null
+ return PageData(
+ pageProperties.pageId,
+ fragment.model.title?.prefixedText.orEmpty(),
+ pageProperties.namespace.code(),
+ Namespace.of(pageProperties.namespace.code()).toString(),
+ pageProperties.revisionId,
+ pageProperties.wikiBaseItem.orEmpty(),
+ fragment.model.title?.wikiSite?.languageCode.orEmpty(),
+ null,
+ null,
+ null
+ )
+ }
+
+ protected fun getPageData(pageTitle: PageTitle?, pageId: Int = 0, revisionId: Long = 0): PageData? {
+ if (pageTitle == null) return null
+ return PageData(
+ pageId,
+ pageTitle.prefixedText,
+ pageTitle.namespace().code(),
+ Namespace.of(pageTitle.namespace().code()).toString(),
+ revisionId,
+ "",
+ pageTitle.wikiSite.languageCode,
+ null, null, null)
+ }
+
+ private fun getPerformerData(): PerformerData {
+ return PerformerData(
+ AccountUtil.userName,
+ AccountUtil.isLoggedIn,
+ AccountUtil.hashCode(),
+ EventPlatformClient.AssociationController.sessionId,
+ EventPlatformClient.AssociationController.pageViewId,
+ AccountUtil.groups,
+ null,
+ WikipediaApp.instance.languageState.appLanguageCode,
+ WikipediaApp.instance.languageState.appLanguageCodes.toString(),
+ null,
+ null,
+ null,
+ null
+ )
+ }
+
+ companion object {
+ private const val EVENT_NAME_BASE = "android.metrics_platform."
+ }
+}
diff --git a/app/src/main/java/org/wikipedia/analytics/metricsplatform/MetricsPlatform.kt b/app/src/main/java/org/wikipedia/analytics/metricsplatform/MetricsPlatform.kt
new file mode 100644
index 00000000000..dd0e1717d36
--- /dev/null
+++ b/app/src/main/java/org/wikipedia/analytics/metricsplatform/MetricsPlatform.kt
@@ -0,0 +1,45 @@
+package org.wikipedia.analytics.metricsplatform
+
+import org.wikimedia.metrics_platform.MetricsClient
+import org.wikimedia.metrics_platform.context.AgentData
+import org.wikimedia.metrics_platform.context.ClientData
+import org.wikimedia.metrics_platform.context.MediawikiData
+import org.wikipedia.WikipediaApp
+import org.wikipedia.settings.Prefs
+import org.wikipedia.util.ReleaseUtil
+import java.time.Duration
+
+object MetricsPlatform {
+ val agentData = AgentData(
+ WikipediaApp.instance.appInstallID,
+ "android",
+ "app"
+ )
+
+ val mediawikiData = MediawikiData(
+ WikipediaApp.instance.currentTheme.toString(),
+ WikipediaApp.instance.versionCode.toString(),
+ ReleaseUtil.isProdRelease,
+ ReleaseUtil.isDevRelease,
+ WikipediaApp.instance.wikiSite.dbName(),
+ WikipediaApp.instance.languageState.systemLanguageCode,
+ null
+ )
+
+ val domain = WikipediaApp.instance.wikiSite.authority()
+
+ private val clientData = ClientData(
+ agentData,
+ null,
+ mediawikiData,
+ null,
+ domain
+ )
+
+ val client: MetricsClient = MetricsClient.builder(clientData)
+ .eventQueueCapacity(Prefs.analyticsQueueSize)
+ .streamConfigFetchInterval(Duration.ofHours(12))
+ .sendEventsInterval(Duration.ofSeconds(30))
+ .isDebug(ReleaseUtil.isDevRelease)
+ .build()
+}
diff --git a/app/src/main/java/org/wikipedia/analytics/metricsplatform/TimedMetricsEvent.kt b/app/src/main/java/org/wikipedia/analytics/metricsplatform/TimedMetricsEvent.kt
new file mode 100644
index 00000000000..571daa80132
--- /dev/null
+++ b/app/src/main/java/org/wikipedia/analytics/metricsplatform/TimedMetricsEvent.kt
@@ -0,0 +1,7 @@
+package org.wikipedia.analytics.metricsplatform
+
+import org.wikipedia.util.ActiveTimer
+
+open class TimedMetricsEvent : MetricsEvent() {
+ protected val timer = ActiveTimer()
+}
diff --git a/app/src/main/java/org/wikipedia/bridge/JavaScriptActionHandler.kt b/app/src/main/java/org/wikipedia/bridge/JavaScriptActionHandler.kt
index e4f05bd0c3e..ccc052943b8 100644
--- a/app/src/main/java/org/wikipedia/bridge/JavaScriptActionHandler.kt
+++ b/app/src/main/java/org/wikipedia/bridge/JavaScriptActionHandler.kt
@@ -30,7 +30,11 @@ object JavaScriptActionHandler {
}
fun setTopMargin(top: Int): String {
- return String.format(Locale.ROOT, "pcs.c1.Page.setMargins({ top:'%dpx', right:'%dpx', bottom:'%dpx', left:'%dpx' })", top + 16, 16, 48, 16)
+ return setMargins(16, top + 16, 16, 48)
+ }
+
+ fun setMargins(left: Int, top: Int, right: Int, bottom: Int): String {
+ return "pcs.c1.Page.setMargins({ top:'${top}px', right:'${right}px', bottom:'${bottom}px', left:'${left}px' })"
}
fun getTextSelection(): String {
@@ -110,7 +114,7 @@ object JavaScriptActionHandler {
" \"dimImages\": ${(app.currentTheme.isDark && Prefs.dimDarkModeImages)}," +
" \"margins\": { \"top\": \"%dpx\", \"right\": \"%dpx\", \"bottom\": \"%dpx\", \"left\": \"%dpx\" }," +
" \"leadImageHeight\": \"%dpx\"," +
- " \"areTablesInitiallyExpanded\": ${!Prefs.isCollapseTablesEnabled}," +
+ " \"areTablesInitiallyExpanded\": ${isPreview || !Prefs.isCollapseTablesEnabled}," +
" \"textSizeAdjustmentPercentage\": \"100%%\"," +
" \"loadImages\": ${Prefs.isImageDownloadEnabled}," +
" \"userGroups\": \"${AccountUtil.groups}\"," +
@@ -141,6 +145,7 @@ object JavaScriptActionHandler {
"pcs.c1.Footer.MenuItemType.lastEdited, " +
(if (showTalkLink) "pcs.c1.Footer.MenuItemType.talkPage, " else "") +
(if (showMapLink) "pcs.c1.Footer.MenuItemType.coordinate, " else "") +
+ "pcs.c1.Footer.MenuItemType.pageIssues, " +
" pcs.c1.Footer.MenuItemType.referenceList " +
" ]," +
" fragment: \"pcs-menu\"," +
diff --git a/app/src/main/java/org/wikipedia/captcha/CaptchaHandler.kt b/app/src/main/java/org/wikipedia/captcha/CaptchaHandler.kt
index 500d9affabb..eaa43aa88b4 100644
--- a/app/src/main/java/org/wikipedia/captcha/CaptchaHandler.kt
+++ b/app/src/main/java/org/wikipedia/captcha/CaptchaHandler.kt
@@ -83,7 +83,7 @@ class CaptchaHandler(private val activity: Activity, private val wiki: WikiSite,
}
fun hideCaptcha() {
- (activity as AppCompatActivity).supportActionBar!!.title = prevTitle
+ (activity as AppCompatActivity).supportActionBar?.title = prevTitle
ViewAnimations.crossFade(binding.root, primaryView)
}
diff --git a/app/src/main/java/org/wikipedia/categories/CategoryActivity.kt b/app/src/main/java/org/wikipedia/categories/CategoryActivity.kt
index b1491b01dca..646add199ad 100644
--- a/app/src/main/java/org/wikipedia/categories/CategoryActivity.kt
+++ b/app/src/main/java/org/wikipedia/categories/CategoryActivity.kt
@@ -23,6 +23,7 @@ import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.tabs.TabLayout
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
+import org.wikipedia.Constants
import org.wikipedia.Constants.InvokeSource
import org.wikipedia.R
import org.wikipedia.activity.BaseActivity
@@ -255,11 +256,9 @@ class CategoryActivity : BaseActivity(), LinkPreviewDialog.Callback {
}
companion object {
- const val EXTRA_TITLE = "categoryTitle"
-
fun newIntent(context: Context, categoryTitle: PageTitle): Intent {
return Intent(context, CategoryActivity::class.java)
- .putExtra(EXTRA_TITLE, categoryTitle)
+ .putExtra(Constants.ARG_TITLE, categoryTitle)
}
}
}
diff --git a/app/src/main/java/org/wikipedia/categories/CategoryActivityViewModel.kt b/app/src/main/java/org/wikipedia/categories/CategoryActivityViewModel.kt
index 4456ede6c02..83169767984 100644
--- a/app/src/main/java/org/wikipedia/categories/CategoryActivityViewModel.kt
+++ b/app/src/main/java/org/wikipedia/categories/CategoryActivityViewModel.kt
@@ -5,13 +5,14 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import androidx.paging.*
+import org.wikipedia.Constants
import org.wikipedia.dataclient.ServiceFactory
import org.wikipedia.dataclient.WikiSite
import org.wikipedia.page.PageTitle
class CategoryActivityViewModel(bundle: Bundle) : ViewModel() {
- val pageTitle = bundle.getParcelable(CategoryActivity.EXTRA_TITLE)!!
+ val pageTitle = bundle.getParcelable(Constants.ARG_TITLE)!!
var showSubcategories = false
val categoryMembersFlow = Pager(PagingConfig(pageSize = 10)) {
diff --git a/app/src/main/java/org/wikipedia/categories/CategoryDialog.kt b/app/src/main/java/org/wikipedia/categories/CategoryDialog.kt
index 8dc2fc9b481..490c139c4c6 100644
--- a/app/src/main/java/org/wikipedia/categories/CategoryDialog.kt
+++ b/app/src/main/java/org/wikipedia/categories/CategoryDialog.kt
@@ -10,6 +10,7 @@ import androidx.core.view.isVisible
import androidx.fragment.app.viewModels
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
+import org.wikipedia.Constants
import org.wikipedia.R
import org.wikipedia.databinding.DialogCategoriesBinding
import org.wikipedia.page.ExtendedBottomSheetDialogFragment
@@ -120,10 +121,8 @@ class CategoryDialog : ExtendedBottomSheetDialogFragment() {
}
companion object {
- const val ARG_TITLE = "title"
-
fun newInstance(title: PageTitle): CategoryDialog {
- return CategoryDialog().apply { arguments = bundleOf(ARG_TITLE to title) }
+ return CategoryDialog().apply { arguments = bundleOf(Constants.ARG_TITLE to title) }
}
}
}
diff --git a/app/src/main/java/org/wikipedia/categories/CategoryDialogViewModel.kt b/app/src/main/java/org/wikipedia/categories/CategoryDialogViewModel.kt
index bee84981a30..c1c9bd31c22 100644
--- a/app/src/main/java/org/wikipedia/categories/CategoryDialogViewModel.kt
+++ b/app/src/main/java/org/wikipedia/categories/CategoryDialogViewModel.kt
@@ -7,13 +7,14 @@ import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.launch
+import org.wikipedia.Constants
import org.wikipedia.dataclient.ServiceFactory
import org.wikipedia.page.PageTitle
import org.wikipedia.util.Resource
class CategoryDialogViewModel(bundle: Bundle) : ViewModel() {
- val pageTitle = bundle.getParcelable(CategoryDialog.ARG_TITLE)!!
+ val pageTitle = bundle.getParcelable(Constants.ARG_TITLE)!!
val categoriesData = MutableLiveData>>()
init {
diff --git a/app/src/main/java/org/wikipedia/commons/FilePageActivity.kt b/app/src/main/java/org/wikipedia/commons/FilePageActivity.kt
index d79aff27373..1803d64a4fd 100644
--- a/app/src/main/java/org/wikipedia/commons/FilePageActivity.kt
+++ b/app/src/main/java/org/wikipedia/commons/FilePageActivity.kt
@@ -3,6 +3,7 @@ package org.wikipedia.commons
import android.content.Context
import android.content.Intent
import android.os.Bundle
+import org.wikipedia.Constants
import org.wikipedia.R
import org.wikipedia.activity.SingleFragmentActivity
import org.wikipedia.page.PageTitle
@@ -18,17 +19,16 @@ class FilePageActivity : SingleFragmentActivity() {
}
override fun createFragment(): FilePageFragment {
- return FilePageFragment.newInstance(intent.getParcelableExtra(INTENT_EXTRA_PAGE_TITLE)!!,
+ return FilePageFragment.newInstance(intent.getParcelableExtra(Constants.ARG_TITLE)!!,
intent.getBooleanExtra(INTENT_EXTRA_ALLOW_EDIT, true))
}
companion object {
- const val INTENT_EXTRA_PAGE_TITLE = "pageTitle"
const val INTENT_EXTRA_ALLOW_EDIT = "allowEdit"
fun newIntent(context: Context, pageTitle: PageTitle, allowEdit: Boolean = true): Intent {
return Intent(context, FilePageActivity::class.java)
- .putExtra(INTENT_EXTRA_PAGE_TITLE, pageTitle)
+ .putExtra(Constants.ARG_TITLE, pageTitle)
.putExtra(INTENT_EXTRA_ALLOW_EDIT, allowEdit)
}
}
diff --git a/app/src/main/java/org/wikipedia/commons/FilePageFragment.kt b/app/src/main/java/org/wikipedia/commons/FilePageFragment.kt
index ff66f4633a0..d04e8451a8d 100644
--- a/app/src/main/java/org/wikipedia/commons/FilePageFragment.kt
+++ b/app/src/main/java/org/wikipedia/commons/FilePageFragment.kt
@@ -51,7 +51,7 @@ class FilePageFragment : Fragment(), FilePageView.Callback {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- pageTitle = requireArguments().getParcelable(FilePageActivity.INTENT_EXTRA_PAGE_TITLE)!!
+ pageTitle = requireArguments().getParcelable(Constants.ARG_TITLE)!!
allowEdit = requireArguments().getBoolean(FilePageActivity.INTENT_EXTRA_ALLOW_EDIT)
retainInstance = true
}
@@ -180,7 +180,7 @@ class FilePageFragment : Fragment(), FilePageView.Callback {
companion object {
fun newInstance(pageTitle: PageTitle, allowEdit: Boolean): FilePageFragment {
return FilePageFragment().apply {
- arguments = bundleOf(FilePageActivity.INTENT_EXTRA_PAGE_TITLE to pageTitle,
+ arguments = bundleOf(Constants.ARG_TITLE to pageTitle,
FilePageActivity.INTENT_EXTRA_ALLOW_EDIT to allowEdit)
}
}
diff --git a/app/src/main/java/org/wikipedia/connectivity/ConnectionStateMonitor.kt b/app/src/main/java/org/wikipedia/connectivity/ConnectionStateMonitor.kt
index a94cf104584..dd436dcbaf7 100644
--- a/app/src/main/java/org/wikipedia/connectivity/ConnectionStateMonitor.kt
+++ b/app/src/main/java/org/wikipedia/connectivity/ConnectionStateMonitor.kt
@@ -22,18 +22,10 @@ class ConnectionStateMonitor : ConnectivityManager.NetworkCallback() {
private var prevOnline = true
private var lastCheckedMillis = 0L
private val callbacks = mutableListOf()
+ private var networkCallbackRegistered = false
- fun enable(context: Context) {
- val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- connectivityManager.registerDefaultNetworkCallback(this)
- } else {
- connectivityManager.registerNetworkCallback(NetworkRequest.Builder()
- .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
- .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
- .build(), this)
- }
+ fun enable() {
+ ensureNetworkCallbackRegistered()
updateOnlineState()
}
@@ -46,6 +38,7 @@ class ConnectionStateMonitor : ConnectivityManager.NetworkCallback() {
}
fun registerCallback(callback: Callback) {
+ ensureNetworkCallbackRegistered()
callbacks.add(callback)
}
@@ -63,6 +56,29 @@ class ConnectionStateMonitor : ConnectivityManager.NetworkCallback() {
updateOnlineState()
}
+ private fun ensureNetworkCallbackRegistered() {
+ if (networkCallbackRegistered) {
+ return
+ }
+ try {
+ val connectivityManager = WikipediaApp.instance.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ connectivityManager.registerDefaultNetworkCallback(this)
+ } else {
+ connectivityManager.registerNetworkCallback(
+ NetworkRequest.Builder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+ .build(), this
+ )
+ }
+ networkCallbackRegistered = true
+ } catch (e: Exception) {
+ // Framework bug, will only be fixed in Android S:
+ // https://issuetracker.google.com/issues/175055271
+ }
+ }
+
private fun updateOnlineState() {
val connectivityManager = WikipediaApp.instance.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
online = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
diff --git a/app/src/main/java/org/wikipedia/dataclient/CoreRestService.kt b/app/src/main/java/org/wikipedia/dataclient/CoreRestService.kt
index 65a1802986a..5e4f1f37a7d 100644
--- a/app/src/main/java/org/wikipedia/dataclient/CoreRestService.kt
+++ b/app/src/main/java/org/wikipedia/dataclient/CoreRestService.kt
@@ -1,31 +1,40 @@
package org.wikipedia.dataclient
+import org.wikipedia.dataclient.growthtasks.GrowthImageSuggestion
import org.wikipedia.dataclient.restbase.DiffResponse
import org.wikipedia.dataclient.restbase.EditCount
import org.wikipedia.dataclient.restbase.Revision
+import retrofit2.http.Body
import retrofit2.http.GET
+import retrofit2.http.PUT
import retrofit2.http.Path
interface CoreRestService {
- @GET("revision/{oldRev}/compare/{newRev}")
+ @GET("v1/revision/{oldRev}/compare/{newRev}")
suspend fun getDiff(
@Path("oldRev") oldRev: Long,
@Path("newRev") newRev: Long
): DiffResponse
- @GET("page/{title}/history/counts/{editType}")
+ @GET("v1/page/{title}/history/counts/{editType}")
suspend fun getEditCount(
@Path("title") title: String,
@Path("editType") editType: String
): EditCount
- @GET("revision/{rev}")
+ @GET("v1/revision/{rev}")
suspend fun getRevision(
@Path("rev") rev: Long
): Revision
+ @PUT("growthexperiments/v0/suggestions/addimage/feedback/{title}")
+ suspend fun addImageFeedback(
+ @Path("title") title: String,
+ @Body body: GrowthImageSuggestion.AddImageFeedbackBody
+ )
+
companion object {
- const val CORE_REST_API_PREFIX = "w/rest.php/v1/"
+ const val CORE_REST_API_PREFIX = "w/rest.php/"
}
}
diff --git a/app/src/main/java/org/wikipedia/dataclient/RestService.kt b/app/src/main/java/org/wikipedia/dataclient/RestService.kt
index 425611817db..2e1f36f6377 100644
--- a/app/src/main/java/org/wikipedia/dataclient/RestService.kt
+++ b/app/src/main/java/org/wikipedia/dataclient/RestService.kt
@@ -58,8 +58,8 @@ interface RestService {
@Headers("x-analytics: preview=1", "Accept: $ACCEPT_HEADER_SUMMARY")
@GET("page/summary/{title}")
suspend fun getPageSummary(
- @Header("Referer") referrerUrl: String?,
- @Path("title") title: String
+ @Header("Referer") referrerUrl: String?,
+ @Path("title") title: String
): PageSummary
// todo: this Content Service-only endpoint is under page/ but that implementation detail should
diff --git a/app/src/main/java/org/wikipedia/dataclient/Service.kt b/app/src/main/java/org/wikipedia/dataclient/Service.kt
index 522529455b1..35180b2bf3c 100644
--- a/app/src/main/java/org/wikipedia/dataclient/Service.kt
+++ b/app/src/main/java/org/wikipedia/dataclient/Service.kt
@@ -60,6 +60,17 @@ interface Service {
@Query("aulimit") maxResults: Int
): Observable
+ @GET(
+ MW_API_PREFIX + "action=query&generator=search&prop=imageinfo&iiprop=extmetadata|url" +
+ "&gsrnamespace=6&iiurlwidth=" + PREFERRED_THUMB_SIZE
+ )
+ suspend fun fullTextSearchCommons(
+ @Query("gsrsearch") searchTerm: String?,
+ @Query("gsroffset") gsrOffset: String?,
+ @Query("gsrlimit") gsrLimit: Int,
+ @Query("continue") cont: String?
+ ): MwQueryResponse
+
// ------- Miscellaneous -------
@get:GET(MW_API_PREFIX + "action=fancycaptchareload")
@@ -71,11 +82,8 @@ interface Service {
@GET(MW_API_PREFIX + "action=query&prop=description&redirects=1")
fun getDescription(@Query("titles") titles: String): Observable
- @GET(MW_API_PREFIX + "action=query&prop=info|description&inprop=varianttitles|displaytitle&redirects=1")
- fun getInfoByPageId(@Query("pageids") pageIds: String): Observable
-
- @GET(MW_API_PREFIX + "action=query&prop=info|description|pageimages&inprop=varianttitles|displaytitle&redirects=1")
- suspend fun getPageTitlesByPageIdsOrTitles(@Query("pageids") pageIds: String? = null, @Query("titles") titles: String? = null): MwQueryResponse
+ @GET(MW_API_PREFIX + "action=query&prop=info|description|pageimages&inprop=varianttitles|displaytitle&redirects=1&pithumbsize=" + PREFERRED_THUMB_SIZE)
+ suspend fun getInfoByPageIdsOrTitles(@Query("pageids") pageIds: String? = null, @Query("titles") titles: String? = null): MwQueryResponse
@GET(MW_API_PREFIX + "action=query")
suspend fun getPageIds(@Query("titles") titles: String): MwQueryResponse
@@ -125,6 +133,9 @@ interface Service {
@get:GET(MW_API_PREFIX + "action=query&meta=siteinfo&maxage=" + SITE_INFO_MAXAGE + "&smaxage=" + SITE_INFO_MAXAGE)
val siteInfo: Observable
+ @GET(MW_API_PREFIX + "action=query&meta=siteinfo&siprop=general|magicwords")
+ suspend fun getSiteInfoWithMagicWords(): MwQueryResponse
+
@GET(MW_API_PREFIX + "action=parse&prop=text&mobileformat=1")
fun parsePage(@Query("page") pageTitle: String): Observable
@@ -260,11 +271,11 @@ interface Service {
@FormUrlEncoded
@POST(MW_API_PREFIX + "action=echomarkread")
- fun markRead(
+ suspend fun markRead(
@Field("token") token: String,
@Field("list") readList: String?,
@Field("unreadlist") unreadList: String?
- ): Observable
+ ): MwQueryResponse
@Headers("Cache-Control: no-cache")
@GET(MW_API_PREFIX + "action=query&meta=notifications¬wikis=*¬prop=list¬filter=!read¬limit=1")
@@ -295,7 +306,7 @@ interface Service {
// ------- Editing -------
- @GET(MW_API_PREFIX + "action=query&prop=revisions|info&rvslots=main&rvprop=content|timestamp|ids&rvlimit=1&converttitles=&intestactions=edit&intestactionsdetail=full")
+ @GET(MW_API_PREFIX + "action=query&prop=revisions|info&rvslots=main&rvprop=content|timestamp|ids&rvlimit=1&converttitles=&intestactions=edit&intestactionsdetail=full&inprop=editintro")
fun getWikiTextForSectionWithInfo(
@Query("titles") title: String,
@Query("rvsection") section: Int?
@@ -330,6 +341,24 @@ interface Service {
@Field("watchlist") watchlist: String? = null,
): Observable
+ @FormUrlEncoded
+ @POST(MW_API_PREFIX + "action=visualeditoredit")
+ suspend fun postVisualEditorEdit(
+ @Field("paction") action: String,
+ @Field("page") title: String,
+ @Field("token") token: String,
+ @Field("section") section: Int,
+ @Field("sectiontitle") newSectionTitle: String?,
+ @Field("summary") summary: String,
+ @Field("assert") user: String?,
+ @Field("captchaid") captchaId: String?,
+ @Field("captchaword") captchaWord: String?,
+ @Field("minor") minor: Boolean? = null,
+ @Field("watchlist") watchlist: String? = null,
+ @Field("plugins") plugins: String? = null,
+ @Field("data-ge-task-image-recommendation") imageRecommendationJson: String? = null,
+ ): Edit
+
@GET(MW_API_PREFIX + "action=query&list=usercontribs&ucprop=ids|title|timestamp|comment|size|flags|sizediff|tags&meta=userinfo&uiprop=groups|blockinfo|editcount|latestcontrib")
suspend fun getUserContributions(
@Query("ucuser") username: String,
@@ -347,10 +376,10 @@ interface Service {
): MwQueryResponse
@GET(MW_API_PREFIX + "action=query&prop=pageviews")
- fun getPageViewsForTitles(@Query("titles") titles: String): Observable
+ suspend fun getPageViewsForTitles(@Query("titles") titles: String): MwQueryResponse
- @get:GET(MW_API_PREFIX + "action=query&meta=wikimediaeditortaskscounts|userinfo&uiprop=groups|blockinfo|editcount|latestcontrib")
- val editorTaskCounts: Observable
+ @GET(MW_API_PREFIX + "action=query&meta=wikimediaeditortaskscounts|userinfo&uiprop=groups|blockinfo|editcount|latestcontrib")
+ suspend fun getEditorTaskCounts(): MwQueryResponse
@FormUrlEncoded
@POST(MW_API_PREFIX + "action=rollback")
@@ -389,7 +418,7 @@ interface Service {
): Observable
@GET(MW_API_PREFIX + "action=wbgetentities&props=descriptions|labels|sitelinks")
- fun getWikidataLabelsAndDescriptions(@Query("ids") idList: String): Observable
+ suspend fun getWikidataLabelsAndDescriptions(@Query("ids") idList: String): Entities
@POST(MW_API_PREFIX + "action=wbsetclaim&errorlang=uselang")
@FormUrlEncoded
@@ -436,9 +465,6 @@ interface Service {
@Field("tags") tags: String?
): Observable
- @GET(MW_API_PREFIX + "action=visualeditor&paction=metadata")
- fun getVisualEditorMetadata(@Query("page") page: String): Observable
-
// ------- Watchlist -------
@Headers("Cache-Control: no-cache")
@@ -583,12 +609,20 @@ interface Service {
@Query("ggtlimit") count: Int
): MwQueryResponse
- @GET(MW_API_PREFIX + "action=query&prop=growthimagesuggestiondata")
- suspend fun getImageRecommendationForPage(
- @Query("titles") titles: String?,
- @Query("pageids") pageIds: String? = null
+ @GET(MW_API_PREFIX + "action=query&prop=growthimagesuggestiondata&generator=search&gsrsearch=hasrecommendation%3Aimage&gsrnamespace=0&gsrsort=random")
+ suspend fun getPagesWithImageRecommendations(
+ @Query("gsrlimit") count: Int
): MwQueryResponse
+ @POST(MW_API_PREFIX + "action=growthinvalidateimagerecommendation")
+ @FormUrlEncoded
+ suspend fun invalidateImageRecommendation(
+ @Field("tasktype") taskType: String,
+ @Field("title") title: String,
+ @Field("filename") fileName: String,
+ @Field("token") token: String
+ ): MwPostResponse
+
@GET(MW_API_PREFIX + "action=paraminfo")
suspend fun getParamInfo(
@Query("modules") modules: String
diff --git a/app/src/main/java/org/wikipedia/dataclient/WikiSite.kt b/app/src/main/java/org/wikipedia/dataclient/WikiSite.kt
index 9087e079f80..7c8bc198f95 100644
--- a/app/src/main/java/org/wikipedia/dataclient/WikiSite.kt
+++ b/app/src/main/java/org/wikipedia/dataclient/WikiSite.kt
@@ -5,6 +5,7 @@ import android.os.Parcelable
import kotlinx.parcelize.Parcelize
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
+import org.wikipedia.Constants
import org.wikipedia.WikipediaApp
import org.wikipedia.json.UriSerializer
import org.wikipedia.language.AppLanguageLookUpTable
@@ -45,11 +46,11 @@ data class WikiSite(
constructor(uri: Uri) : this(uri, "") {
val tempUri = ensureScheme(uri)
var authority = tempUri.authority.orEmpty()
- if ((BASE_DOMAIN == authority || ("www." + BASE_DOMAIN) == authority) &&
+ if ((BASE_DOMAIN == authority || ("www.$BASE_DOMAIN") == authority) &&
tempUri.path?.startsWith("/wiki") == true
) {
// Special case for Wikipedia only: assume English subdomain when none given.
- authority = "en." + BASE_DOMAIN
+ authority = "en.$BASE_DOMAIN"
}
// Unconditionally transform any mobile authority to canonical.
@@ -61,6 +62,12 @@ data class WikiSite(
languageCode = LanguageUtil.firstSelectedChineseVariant
}
+ if (languageCode == Constants.WIKI_CODE_COMMONS) {
+ // Special case for Commons: if the WikiSite was constructed from "commons.wikimedia.org",
+ // then the languageCode will be "commons" which is incorrect, so set it to the default language.
+ languageCode = WikipediaApp.instance.appOrSystemLanguageCode
+ }
+
// Use default subdomain in authority to prevent error when requesting endpoints. e.g. zh-tw.wikipedia.org
if (authority.contains(BASE_DOMAIN) && subdomain().isNotEmpty()) {
authority = subdomain() + "." + BASE_DOMAIN
diff --git a/app/src/main/java/org/wikipedia/dataclient/growthtasks/GrowthImageSuggestion.kt b/app/src/main/java/org/wikipedia/dataclient/growthtasks/GrowthImageSuggestion.kt
index 8c75ea452db..7636ae5d0de 100644
--- a/app/src/main/java/org/wikipedia/dataclient/growthtasks/GrowthImageSuggestion.kt
+++ b/app/src/main/java/org/wikipedia/dataclient/growthtasks/GrowthImageSuggestion.kt
@@ -8,7 +8,7 @@ class GrowthImageSuggestion(
val titleNamespace: Int = 0,
val titleText: String = "",
val datasetId: String = "",
- val images: List = emptyList(),
+ val images: List = emptyList()
) {
@Serializable
class ImageItem(
@@ -16,7 +16,7 @@ class GrowthImageSuggestion(
val displayFilename: String = "",
val source: String = "",
val projects: List = emptyList(),
- val metadata: ImageMetadata? = null,
+ val metadata: ImageMetadata? = null
)
@Serializable
@@ -34,6 +34,17 @@ class GrowthImageSuggestion(
val caption: String = "",
val categories: List = emptyList(),
val reason: String = "",
- val contentLanguageName: String = "",
+ val contentLanguageName: String = ""
+ )
+
+ @Serializable
+ class AddImageFeedbackBody(
+ val token: String = "",
+ val editRevId: Long = 0,
+ val filename: String = "",
+ // Boolean fields must be nullable for androidx.serialization to serialize properly.
+ val accepted: Boolean? = null,
+ val reasons: List = emptyList(),
+ val caption: String? = null
)
}
diff --git a/app/src/main/java/org/wikipedia/dataclient/mwapi/MwQueryPage.kt b/app/src/main/java/org/wikipedia/dataclient/mwapi/MwQueryPage.kt
index d966699231a..9c3967a193c 100644
--- a/app/src/main/java/org/wikipedia/dataclient/mwapi/MwQueryPage.kt
+++ b/app/src/main/java/org/wikipedia/dataclient/mwapi/MwQueryPage.kt
@@ -2,9 +2,13 @@ package org.wikipedia.dataclient.mwapi
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
+import kotlinx.serialization.json.JsonElement
+import kotlinx.serialization.json.JsonObject
+import kotlinx.serialization.json.decodeFromJsonElement
import org.wikipedia.dataclient.growthtasks.GrowthImageSuggestion
import org.wikipedia.dataclient.page.Protection
import org.wikipedia.gallery.ImageInfo
+import org.wikipedia.json.JsonUtil
import org.wikipedia.page.Namespace
import org.wikipedia.util.DateUtil
@@ -23,8 +27,9 @@ class MwQueryPage {
private val ns = 0
val coordinates: List? = null
private val thumbnail: Thumbnail? = null
- private val varianttitles: Map? = null
+ val varianttitles: Map? = null
private val actions: Map>? = null
+ private val editintro: JsonElement? = null
val index = 0
var title: String = ""
@@ -76,6 +81,10 @@ class MwQueryPage {
return actions?.get(actionName) ?: emptyList()
}
+ fun getEditNotices(): Map {
+ return if (editintro != null && editintro is JsonObject) JsonUtil.json.decodeFromJsonElement(editintro) else emptyMap()
+ }
+
@Serializable
class Revision {
private val slots: Map? = null
diff --git a/app/src/main/java/org/wikipedia/dataclient/mwapi/MwQueryResult.kt b/app/src/main/java/org/wikipedia/dataclient/mwapi/MwQueryResult.kt
index 036952b247f..1190a970b72 100644
--- a/app/src/main/java/org/wikipedia/dataclient/mwapi/MwQueryResult.kt
+++ b/app/src/main/java/org/wikipedia/dataclient/mwapi/MwQueryResult.kt
@@ -24,17 +24,18 @@ class MwQueryResult {
@SerialName("allusers") val allUsers: List? = null
@SerialName("globaluserinfo") val globalUserInfo: UserInfo? = null
- private val redirects: MutableList? = null
- private val converted: MutableList? = null
+ private val redirects: List? = null
+ private val converted: List? = null
private val tokens: Tokens? = null
private val echomarkread: MarkReadResponse? = null
val users: List? = null
- val pages: MutableList? = null
+ val pages: List? = null
val echomarkseen: MarkReadResponse? = null
val notifications: NotificationList? = null
val watchlist: List = emptyList()
val namespaces: Map? = null
val allmessages: List? = null
+ val magicwords: List? = null
init {
resolveConvertedTitles()
@@ -75,7 +76,7 @@ class MwQueryResult {
return users?.find { StringUtil.capitalize(userName) == it.name }
}
- fun langLinks(): MutableList {
+ fun langLinks(): List {
val result = mutableListOf()
if (pages.isNullOrEmpty()) {
return result
@@ -194,4 +195,10 @@ class MwQueryResult {
val name: String = ""
val content: String = ""
}
+
+ @Serializable
+ class MagicWord {
+ val name: String = ""
+ val aliases: List = emptyList()
+ }
}
diff --git a/app/src/main/java/org/wikipedia/dataclient/mwapi/MwVisualEditorResponse.kt b/app/src/main/java/org/wikipedia/dataclient/mwapi/MwVisualEditorResponse.kt
deleted file mode 100644
index 038e34535a7..00000000000
--- a/app/src/main/java/org/wikipedia/dataclient/mwapi/MwVisualEditorResponse.kt
+++ /dev/null
@@ -1,21 +0,0 @@
-package org.wikipedia.dataclient.mwapi
-
-import kotlinx.serialization.Serializable
-import kotlinx.serialization.json.JsonElement
-import kotlinx.serialization.json.JsonObject
-import kotlinx.serialization.json.decodeFromJsonElement
-import org.wikipedia.json.JsonUtil
-
-@Serializable
-class MwVisualEditorResponse : MwResponse() {
- var visualeditor: VisualEditorData? = null
-
- @Serializable
- class VisualEditorData {
- private val notices: JsonElement? = null
-
- fun getEditNotices(): Map? {
- return if (notices != null && notices is JsonObject) JsonUtil.json.decodeFromJsonElement(notices) else null
- }
- }
-}
diff --git a/app/src/main/java/org/wikipedia/dataclient/okhttp/OkHttpGlideModule.kt b/app/src/main/java/org/wikipedia/dataclient/okhttp/OkHttpGlideModule.kt
index b17c712fccb..6b13a5343cd 100644
--- a/app/src/main/java/org/wikipedia/dataclient/okhttp/OkHttpGlideModule.kt
+++ b/app/src/main/java/org/wikipedia/dataclient/okhttp/OkHttpGlideModule.kt
@@ -15,7 +15,7 @@ import java.io.InputStream
@GlideModule
class OkHttpGlideModule : AppGlideModule() {
override fun registerComponents(context: Context, glide: Glide, registry: Registry) {
- glide.registry.replace(GlideUrl::class.java, InputStream::class.java,
+ registry.replace(GlideUrl::class.java, InputStream::class.java,
OkHttpUrlLoader.Factory(OkHttpConnectionFactory.client))
}
diff --git a/app/src/main/java/org/wikipedia/dataclient/restbase/RbServiceError.kt b/app/src/main/java/org/wikipedia/dataclient/restbase/RbServiceError.kt
index 97fe747b63c..2f896dbff85 100644
--- a/app/src/main/java/org/wikipedia/dataclient/restbase/RbServiceError.kt
+++ b/app/src/main/java/org/wikipedia/dataclient/restbase/RbServiceError.kt
@@ -12,9 +12,18 @@ class RbServiceError : ServiceError {
private val method: String? = null
private val uri: String? = null
+ private val errorKey: String? = null
+ private val messageTranslations: Map? = null
+
override val title: String = ""
- override val details: String get() = detail.orEmpty()
+ override val details: String get() {
+ return if (messageTranslations != null) {
+ messageTranslations.values.firstOrNull() ?: ""
+ } else {
+ detail.orEmpty()
+ }
+ }
companion object {
fun create(rspBody: String): RbServiceError {
diff --git a/app/src/main/java/org/wikipedia/descriptions/DescriptionEditActivity.kt b/app/src/main/java/org/wikipedia/descriptions/DescriptionEditActivity.kt
index d268008647c..6fff194f490 100644
--- a/app/src/main/java/org/wikipedia/descriptions/DescriptionEditActivity.kt
+++ b/app/src/main/java/org/wikipedia/descriptions/DescriptionEditActivity.kt
@@ -35,7 +35,7 @@ class DescriptionEditActivity : SingleFragmentActivity(
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val action = intent.getSerializableExtra(Constants.INTENT_EXTRA_ACTION) as Action
- val pageTitle = intent.getParcelableExtra(EXTRA_TITLE)!!
+ val pageTitle = intent.getParcelableExtra(Constants.ARG_TITLE)!!
MachineGeneratedArticleDescriptionsAnalyticsHelper.isUserInExperiment = (ReleaseUtil.isPreBetaRelease && AccountUtil.isLoggedIn &&
action == Action.ADD_DESCRIPTION && pageTitle.description.isNullOrEmpty() &&
@@ -53,7 +53,7 @@ class DescriptionEditActivity : SingleFragmentActivity(
public override fun createFragment(): DescriptionEditFragment {
val invokeSource = intent.getSerializableExtra(Constants.INTENT_EXTRA_INVOKE_SOURCE) as InvokeSource
val action = intent.getSerializableExtra(Constants.INTENT_EXTRA_ACTION) as Action
- val title = intent.getParcelableExtra(EXTRA_TITLE)!!
+ val title = intent.getParcelableExtra(Constants.ARG_TITLE)!!
return DescriptionEditFragment.newInstance(title,
intent.getStringExtra(EXTRA_HIGHLIGHT_TEXT),
intent.getParcelableExtra(EXTRA_SOURCE_SUMMARY),
@@ -121,7 +121,6 @@ class DescriptionEditActivity : SingleFragmentActivity(
}
companion object {
- private const val EXTRA_TITLE = "title"
private const val EXTRA_HIGHLIGHT_TEXT = "highlightText"
private const val EXTRA_SOURCE_SUMMARY = "sourceSummary"
private const val EXTRA_TARGET_SUMMARY = "targetSummary"
@@ -134,7 +133,7 @@ class DescriptionEditActivity : SingleFragmentActivity(
action: Action,
invokeSource: InvokeSource): Intent {
return Intent(context, DescriptionEditActivity::class.java)
- .putExtra(EXTRA_TITLE, title)
+ .putExtra(Constants.ARG_TITLE, title)
.putExtra(EXTRA_HIGHLIGHT_TEXT, highlightText)
.putExtra(EXTRA_SOURCE_SUMMARY, sourceSummary)
.putExtra(EXTRA_TARGET_SUMMARY, targetSummary)
diff --git a/app/src/main/java/org/wikipedia/descriptions/DescriptionEditFragment.kt b/app/src/main/java/org/wikipedia/descriptions/DescriptionEditFragment.kt
index 753262a2282..444e6296e15 100644
--- a/app/src/main/java/org/wikipedia/descriptions/DescriptionEditFragment.kt
+++ b/app/src/main/java/org/wikipedia/descriptions/DescriptionEditFragment.kt
@@ -26,6 +26,8 @@ import org.wikipedia.analytics.eventplatform.ABTest.Companion.GROUP_3
import org.wikipedia.analytics.eventplatform.EditAttemptStepEvent
import org.wikipedia.analytics.eventplatform.MachineGeneratedArticleDescriptionsAnalyticsHelper
import org.wikipedia.auth.AccountUtil
+import org.wikipedia.captcha.CaptchaHandler
+import org.wikipedia.captcha.CaptchaResult
import org.wikipedia.csrf.CsrfTokenClient
import org.wikipedia.databinding.FragmentDescriptionEditBinding
import org.wikipedia.dataclient.ServiceFactory
@@ -64,6 +66,7 @@ class DescriptionEditFragment : Fragment() {
private var targetSummary: PageSummaryForEdit? = null
private var highlightText: String? = null
private var editingAllowed = true
+ private lateinit var captchaHandler: CaptchaHandler
private val analyticsHelper = MachineGeneratedArticleDescriptionsAnalyticsHelper()
@@ -117,7 +120,7 @@ class DescriptionEditFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- pageTitle = requireArguments().getParcelable(ARG_TITLE)!!
+ pageTitle = requireArguments().getParcelable(Constants.ARG_TITLE)!!
highlightText = requireArguments().getString(ARG_HIGHLIGHT_TEXT)
action = requireArguments().getSerializable(ARG_ACTION) as DescriptionEditActivity.Action
invokeSource = requireArguments().getSerializable(Constants.INTENT_EXTRA_INVOKE_SOURCE) as InvokeSource
@@ -139,6 +142,8 @@ class DescriptionEditFragment : Fragment() {
val loginIntent = LoginActivity.newIntent(requireActivity(), LoginActivity.SOURCE_EDIT)
loginLauncher.launch(loginIntent)
}
+ captchaHandler = CaptchaHandler(requireActivity(), pageTitle.wikiSite, binding.fragmentDescriptionEditView.getCaptchaContainer().root,
+ binding.fragmentDescriptionEditView.getDescriptionEditTextView(), "", null)
return binding.root
}
@@ -153,6 +158,7 @@ class DescriptionEditFragment : Fragment() {
}
override fun onDestroyView() {
+ captchaHandler.dispose()
binding.fragmentDescriptionEditView.callback = null
_binding = null
super.onDestroyView()
@@ -296,6 +302,9 @@ class DescriptionEditFragment : Fragment() {
}
private fun getEditTokenThenSave() {
+ if (captchaHandler.isActive) {
+ captchaHandler.hideCaptcha()
+ }
val csrfSite = if (action == DescriptionEditActivity.Action.ADD_CAPTION ||
action == DescriptionEditActivity.Action.TRANSLATE_CAPTION) {
Constants.commonsWikiSite
@@ -330,10 +339,13 @@ class DescriptionEditFragment : Fragment() {
text = updateDescriptionInArticle(text, binding.fragmentDescriptionEditView.description.orEmpty())
ServiceFactory.get(wikiSite).postEditSubmit(pageTitle.prefixedText, "0", null,
- getEditComment().orEmpty(),
- if (AccountUtil.isLoggedIn) "user"
- else null, text, null, baseRevId, editToken, null, null)
- .subscribeOn(Schedulers.io())
+ getEditComment().orEmpty(),
+ if (AccountUtil.isLoggedIn) "user"
+ else null, text, null, baseRevId, editToken,
+ if (captchaHandler.isActive) captchaHandler.captchaId() else null,
+ if (captchaHandler.isActive) captchaHandler.captchaWord() else null
+ )
+ .subscribeOn(Schedulers.io())
}
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ result ->
@@ -354,8 +366,9 @@ class DescriptionEditFragment : Fragment() {
editFailed(MwException(MwServiceError(code, spamblacklist)), false)
}
hasCaptchaResponse -> {
- // TODO: handle captcha
- // new CaptchaResult(result.edit().captchaId());
+ binding.fragmentDescriptionEditView.showProgressBar(false)
+ binding.fragmentDescriptionEditView.setSaveState(false)
+ captchaHandler.handleCaptcha(null, CaptchaResult(result.edit.captchaId))
}
hasSpamBlacklistResponse -> {
editFailed(MwException(MwServiceError(code, info)), false)
@@ -475,7 +488,9 @@ class DescriptionEditFragment : Fragment() {
}
override fun onCancelClick() {
- if (binding.fragmentDescriptionEditView.showingReviewContent()) {
+ if (captchaHandler.isActive) {
+ captchaHandler.cancelCaptcha()
+ } else if (binding.fragmentDescriptionEditView.showingReviewContent()) {
binding.fragmentDescriptionEditView.loadReviewContent(false)
analyticsHelper.timer.resume()
} else {
@@ -513,7 +528,6 @@ class DescriptionEditFragment : Fragment() {
}
companion object {
- private const val ARG_TITLE = "title"
private const val ARG_REVIEWING = "inReviewing"
private const val ARG_DESCRIPTION = "description"
private const val ARG_HIGHLIGHT_TEXT = "highlightText"
@@ -539,7 +553,7 @@ class DescriptionEditFragment : Fragment() {
action: DescriptionEditActivity.Action,
source: InvokeSource): DescriptionEditFragment {
return DescriptionEditFragment().apply {
- arguments = bundleOf(ARG_TITLE to title,
+ arguments = bundleOf(Constants.ARG_TITLE to title,
ARG_HIGHLIGHT_TEXT to highlightText,
ARG_SOURCE_SUMMARY to sourceSummary,
ARG_TARGET_SUMMARY to targetSummary,
diff --git a/app/src/main/java/org/wikipedia/descriptions/DescriptionEditLicenseView.kt b/app/src/main/java/org/wikipedia/descriptions/DescriptionEditLicenseView.kt
index fbe424aac06..1bcbe7a90c6 100644
--- a/app/src/main/java/org/wikipedia/descriptions/DescriptionEditLicenseView.kt
+++ b/app/src/main/java/org/wikipedia/descriptions/DescriptionEditLicenseView.kt
@@ -44,7 +44,7 @@ class DescriptionEditLicenseView constructor(context: Context, attrs: AttributeS
DescriptionEditUtil.wikiUsesLocalDescriptions(lang.orEmpty())) {
binding.licenseText.text = StringUtil.fromHtml(context.getString(R.string.edit_save_action_license_logged_in,
context.getString(R.string.terms_of_use_url),
- context.getString(R.string.cc_by_sa_3_url)))
+ context.getString(R.string.cc_by_sa_4_url)))
} else {
binding.licenseText.text = StringUtil.fromHtml(context.getString(when (arg) {
ARG_NOTICE_ARTICLE_DESCRIPTION -> R.string.suggested_edits_license_notice
diff --git a/app/src/main/java/org/wikipedia/descriptions/DescriptionEditView.kt b/app/src/main/java/org/wikipedia/descriptions/DescriptionEditView.kt
index 8a091772bd6..53ec90f45be 100644
--- a/app/src/main/java/org/wikipedia/descriptions/DescriptionEditView.kt
+++ b/app/src/main/java/org/wikipedia/descriptions/DescriptionEditView.kt
@@ -17,6 +17,7 @@ import de.mrapp.android.view.drawable.CircularProgressDrawable
import org.wikipedia.R
import org.wikipedia.WikipediaApp
import org.wikipedia.analytics.eventplatform.MachineGeneratedArticleDescriptionsAnalyticsHelper
+import org.wikipedia.databinding.GroupCaptchaBinding
import org.wikipedia.databinding.ViewDescriptionEditBinding
import org.wikipedia.language.LanguageUtil
import org.wikipedia.mlkit.MlKitLanguageDetector
@@ -402,6 +403,10 @@ class DescriptionEditView : LinearLayout, MlKitLanguageDetector.Callback {
}
}
+ fun getDescriptionEditTextView(): LinearLayout {
+ return binding.viewDescriptionEditTextLayout
+ }
+
fun updateInfoText() {
binding.learnMoreButton.text =
if (action == DescriptionEditActivity.Action.ADD_DESCRIPTION ||
@@ -455,6 +460,10 @@ class DescriptionEditView : LinearLayout, MlKitLanguageDetector.Callback {
}
}
+ fun getCaptchaContainer(): GroupCaptchaBinding {
+ return binding.captchaContainer
+ }
+
companion object {
private const val TEXT_VALIDATE_DELAY_MILLIS = 1000L
}
diff --git a/app/src/main/java/org/wikipedia/diff/ArticleEditDetailsFragment.kt b/app/src/main/java/org/wikipedia/diff/ArticleEditDetailsFragment.kt
index 0f3f66f4a66..699aa364094 100644
--- a/app/src/main/java/org/wikipedia/diff/ArticleEditDetailsFragment.kt
+++ b/app/src/main/java/org/wikipedia/diff/ArticleEditDetailsFragment.kt
@@ -24,6 +24,7 @@ import org.wikipedia.Constants.InvokeSource
import org.wikipedia.R
import org.wikipedia.analytics.eventplatform.EditHistoryInteractionEvent
import org.wikipedia.auth.AccountUtil
+import org.wikipedia.commons.FilePageActivity
import org.wikipedia.databinding.FragmentArticleEditDetailsBinding
import org.wikipedia.dataclient.mwapi.MwQueryPage.Revision
import org.wikipedia.dataclient.watch.Watch
@@ -206,6 +207,8 @@ class ArticleEditDetailsFragment : Fragment(), WatchlistExpiryDialog.Callback, L
binding.articleTitleView.setOnClickListener {
if (viewModel.pageTitle.namespace() == Namespace.USER_TALK || viewModel.pageTitle.namespace() == Namespace.TALK) {
startActivity(TalkTopicsActivity.newIntent(requireContext(), viewModel.pageTitle, InvokeSource.DIFF_ACTIVITY))
+ } else if (viewModel.pageTitle.namespace() == Namespace.FILE) {
+ startActivity(FilePageActivity.newIntent(requireContext(), viewModel.pageTitle))
} else {
ExclusiveBottomSheetPresenter.show(childFragmentManager, LinkPreviewDialog.newInstance(
HistoryEntry(viewModel.pageTitle, HistoryEntry.SOURCE_EDIT_DIFF_DETAILS), null))
@@ -253,6 +256,7 @@ class ArticleEditDetailsFragment : Fragment(), WatchlistExpiryDialog.Callback, L
override fun onPrepareMenu(menu: Menu) {
val watchlistItem = menu.findItem(R.id.menu_add_watchlist)
+ watchlistItem.isVisible = AccountUtil.isLoggedIn
watchlistItem.title = getString(if (isWatched) R.string.menu_page_unwatch else R.string.menu_page_watch)
watchlistItem.setIcon(getWatchlistIcon(isWatched, hasWatchlistExpiry))
}
@@ -359,7 +363,7 @@ class ArticleEditDetailsFragment : Fragment(), WatchlistExpiryDialog.Callback, L
private fun setEnableDisableTint(view: ImageView, isDisabled: Boolean) {
ImageViewCompat.setImageTintList(view, AppCompatResources.getColorStateList(requireContext(),
ResourceUtil.getThemedAttributeId(requireContext(), if (isDisabled)
- R.attr.placeholder_color else R.attr.secondary_color)))
+ R.attr.inactive_color else R.attr.secondary_color)))
}
private fun setButtonTextAndIconColor(view: MaterialButton, themedColor: Int) {
diff --git a/app/src/main/java/org/wikipedia/diff/ArticleEditDetailsViewModel.kt b/app/src/main/java/org/wikipedia/diff/ArticleEditDetailsViewModel.kt
index c8aa3323df8..52785d0e163 100644
--- a/app/src/main/java/org/wikipedia/diff/ArticleEditDetailsViewModel.kt
+++ b/app/src/main/java/org/wikipedia/diff/ArticleEditDetailsViewModel.kt
@@ -60,25 +60,29 @@ class ArticleEditDetailsViewModel(bundle: Bundle) : ViewModel() {
viewModelScope.launch(CoroutineExceptionHandler { _, throwable ->
revisionDetails.postValue(Resource.Error(throwable))
}) {
+ revisionToId = revisionIdTo
if (watchedStatus.value !is Resource.Success) {
val query = ServiceFactory.get(pageTitle.wikiSite).getWatchedStatusWithRights(pageTitle.prefixedText).query!!
val page = query.firstPage()!!
if (pageId < 0) {
pageId = page.pageId
}
+ if (revisionToId < 0) {
+ revisionToId = page.lastrevid
+ }
watchedStatus.postValue(Resource.Success(page))
hasRollbackRights = query.userInfo?.rights?.contains("rollback") == true
rollbackRights.postValue(Resource.Success(hasRollbackRights))
}
if (revisionIdFrom >= 0) {
val responseFrom = async { ServiceFactory.get(pageTitle.wikiSite).getRevisionDetailsWithInfo(pageId.toString(), 2, revisionIdFrom) }
- val responseTo = async { ServiceFactory.get(pageTitle.wikiSite).getRevisionDetailsWithInfo(pageId.toString(), 2, revisionIdTo) }
+ val responseTo = async { ServiceFactory.get(pageTitle.wikiSite).getRevisionDetailsWithInfo(pageId.toString(), 2, revisionToId) }
val pageTo = responseTo.await().query?.firstPage()!!
revisionFrom = responseFrom.await().query?.firstPage()!!.revisions[0]
revisionTo = pageTo.revisions[0]
canGoForward = revisionTo!!.revId < pageTo.lastrevid
} else {
- val response = ServiceFactory.get(pageTitle.wikiSite).getRevisionDetailsWithInfo(pageId.toString(), 2, revisionIdTo)
+ val response = ServiceFactory.get(pageTitle.wikiSite).getRevisionDetailsWithInfo(pageId.toString(), 2, revisionToId)
val page = response.query?.firstPage()!!
val revisions = page.revisions
revisionTo = revisions[0]
diff --git a/app/src/main/java/org/wikipedia/edit/EditSectionActivity.kt b/app/src/main/java/org/wikipedia/edit/EditSectionActivity.kt
index 154dd71f398..619b723561e 100644
--- a/app/src/main/java/org/wikipedia/edit/EditSectionActivity.kt
+++ b/app/src/main/java/org/wikipedia/edit/EditSectionActivity.kt
@@ -143,7 +143,7 @@ class EditSectionActivity : BaseActivity(), ThemeChooserDialog.Callback {
}
override fun onRequestInsertMedia() {
- requestInsertMedia.launch(InsertMediaActivity.newIntent(this@EditSectionActivity, pageTitle.displayText))
+ requestInsertMedia.launch(InsertMediaActivity.newIntent(this@EditSectionActivity, pageTitle.wikiSite, pageTitle.displayText))
}
override fun onRequestInsertLink() {
@@ -181,7 +181,7 @@ class EditSectionActivity : BaseActivity(), ThemeChooserDialog.Callback {
setContentView(binding.root)
setNavigationBarColor(ResourceUtil.getThemedColor(this, android.R.attr.colorBackground))
- pageTitle = intent.getParcelableExtra(EXTRA_TITLE)!!
+ pageTitle = intent.getParcelableExtra(Constants.ARG_TITLE)!!
sectionID = intent.getIntExtra(EXTRA_SECTION_ID, -1)
sectionAnchor = intent.getStringExtra(EXTRA_SECTION_ANCHOR)
textToHighlight = intent.getStringExtra(EXTRA_HIGHLIGHT_TEXT)
@@ -280,7 +280,7 @@ class EditSectionActivity : BaseActivity(), ThemeChooserDialog.Callback {
val editLicenseText = ActivityCompat.requireViewById(this, R.id.licenseText)
editLicenseText.text = StringUtil.fromHtml(getString(if (isLoggedIn) R.string.edit_save_action_license_logged_in else R.string.edit_save_action_license_anon,
getString(R.string.terms_of_use_url),
- getString(R.string.cc_by_sa_3_url)))
+ getString(R.string.cc_by_sa_4_url)))
editLicenseText.movementMethod = LinkMovementMethodExt { url: String ->
if (url == "https://#login") {
val loginIntent = LoginActivity.newIntent(this@EditSectionActivity, LoginActivity.SOURCE_EDIT)
@@ -611,27 +611,23 @@ class EditSectionActivity : BaseActivity(), ThemeChooserDialog.Callback {
}
displaySectionText()
maybeShowEditSourceDialog()
- }) {
- showError(it)
- L.e(it)
- })
- disposables.add(ServiceFactory.get(pageTitle.wikiSite).getVisualEditorMetadata(pageTitle.prefixedText)
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe({
+
editNotices.clear()
// Populate edit notices, but filter out anonymous edit warnings, since
// we show that type of warning ourselves when previewing.
- editNotices.addAll(it.visualeditor?.getEditNotices().orEmpty()
- .filterKeys { key -> key.startsWith("editnotice") }
- .values.filter { str -> StringUtil.fromHtml(str).trim().isNotEmpty() })
+ editNotices.addAll(firstPage.getEditNotices()
+ .filterKeys { key -> (key.startsWith("editnotice") && !key.endsWith("-notext")) }
+ .values.filter { str -> StringUtil.fromHtml(str).trim().isNotEmpty() })
invalidateOptionsMenu()
if (Prefs.autoShowEditNotices) {
showEditNotices()
} else {
maybeShowEditNoticesTooltip()
}
- }, { L.e(it) }))
+ }) {
+ showError(it)
+ L.e(it)
+ })
} else {
displaySectionText()
}
@@ -740,7 +736,6 @@ class EditSectionActivity : BaseActivity(), ThemeChooserDialog.Callback {
private const val EXTRA_KEY_SECTION_TEXT_MODIFIED = "sectionTextModified"
private const val EXTRA_KEY_TEMPORARY_WIKITEXT_STORED = "hasTemporaryWikitextStored"
private const val EXTRA_KEY_EDITING_ALLOWED = "editingAllowed"
- const val EXTRA_TITLE = "org.wikipedia.edit_section.title"
const val EXTRA_SECTION_ID = "org.wikipedia.edit_section.sectionid"
const val EXTRA_SECTION_ANCHOR = "org.wikipedia.edit_section.anchor"
const val EXTRA_HIGHLIGHT_TEXT = "org.wikipedia.edit_section.highlight"
@@ -749,7 +744,7 @@ class EditSectionActivity : BaseActivity(), ThemeChooserDialog.Callback {
return Intent(context, EditSectionActivity::class.java)
.putExtra(EXTRA_SECTION_ID, sectionId)
.putExtra(EXTRA_SECTION_ANCHOR, sectionAnchor)
- .putExtra(EXTRA_TITLE, title)
+ .putExtra(Constants.ARG_TITLE, title)
.putExtra(EXTRA_HIGHLIGHT_TEXT, highlightText)
}
}
diff --git a/app/src/main/java/org/wikipedia/edit/insertmedia/InsertMediaActivity.kt b/app/src/main/java/org/wikipedia/edit/insertmedia/InsertMediaActivity.kt
index 514215babf8..9e12fe1f51d 100644
--- a/app/src/main/java/org/wikipedia/edit/insertmedia/InsertMediaActivity.kt
+++ b/app/src/main/java/org/wikipedia/edit/insertmedia/InsertMediaActivity.kt
@@ -37,6 +37,7 @@ import org.wikipedia.databinding.ItemInsertMediaBinding
import org.wikipedia.dataclient.WikiSite
import org.wikipedia.history.SearchActionModeCallback
import org.wikipedia.page.PageTitle
+import org.wikipedia.staticdata.FileAliasData
import org.wikipedia.util.DimenUtil
import org.wikipedia.util.ImageUrlUtil
import org.wikipedia.util.ResourceUtil
@@ -171,13 +172,21 @@ class InsertMediaActivity : BaseActivity() {
}
private fun combineMediaWikitext(): String {
- viewModel.selectedImage?.prefixedText?.let {
- var wikiText = "[[$it|${viewModel.imageSize}px|${viewModel.imageType}|${viewModel.imagePosition}"
+ viewModel.selectedImage?.let {
+ var wikiText = "[[" + FileAliasData.valueFor(viewModel.wikiSite.languageCode) + ":" + it.text
+ if (viewModel.imageSize != InsertMediaViewModel.IMAGE_SIZE_DEFAULT) {
+ wikiText += "|${viewModel.imageSize}px"
+ }
+ InsertMediaViewModel.magicWords[viewModel.imageType]?.let { type ->
+ wikiText += "|$type"
+ }
+ InsertMediaViewModel.magicWords[viewModel.imagePosition]?.let { pos ->
+ wikiText += "|$pos"
+ }
if (insertMediaSettingsFragment.alternativeText.isNotEmpty()) {
- wikiText += "|alt=${insertMediaSettingsFragment.alternativeText}"
+ wikiText += "|" + InsertMediaViewModel.magicWords[InsertMediaViewModel.IMAGE_ALT_TEXT].orEmpty().replace("$1", insertMediaSettingsFragment.alternativeText)
}
-
if (insertMediaSettingsFragment.captionText.isNotEmpty()) {
wikiText += "|${insertMediaSettingsFragment.captionText}"
}
@@ -347,9 +356,10 @@ class InsertMediaActivity : BaseActivity() {
const val RESULT_WIKITEXT = "insertMediaWikitext"
const val RESULT_INSERT_MEDIA_SUCCESS = 100
- fun newIntent(context: Context, searchQuery: String): Intent {
+ fun newIntent(context: Context, wikiSite: WikiSite, searchQuery: String): Intent {
return Intent(context, InsertMediaActivity::class.java)
- .putExtra(EXTRA_SEARCH_QUERY, searchQuery)
+ .putExtra(Constants.ARG_WIKISITE, wikiSite)
+ .putExtra(EXTRA_SEARCH_QUERY, searchQuery)
}
}
}
diff --git a/app/src/main/java/org/wikipedia/edit/insertmedia/InsertMediaSettingsFragment.kt b/app/src/main/java/org/wikipedia/edit/insertmedia/InsertMediaSettingsFragment.kt
index 789441e1b42..390c4dc2653 100644
--- a/app/src/main/java/org/wikipedia/edit/insertmedia/InsertMediaSettingsFragment.kt
+++ b/app/src/main/java/org/wikipedia/edit/insertmedia/InsertMediaSettingsFragment.kt
@@ -99,7 +99,8 @@ class InsertMediaSettingsFragment : Fragment() {
activity.supportActionBar?.title = getString(R.string.insert_media_settings)
viewModel.selectedImage?.let {
ViewUtil.loadImageWithRoundedCorners(binding.imageView, it.thumbUrl, true)
- binding.mediaDescription.text = StringUtil.removeHTMLTags(it.description.orEmpty().ifEmpty { it.displayText })
+ binding.mediaTitle.text = it.text
+ binding.mediaDescription.text = StringUtil.removeHTMLTags(it.description.orEmpty().ifEmpty { it.displayText }).trim()
}
binding.mediaCaptionLayout.requestFocus()
DeviceUtil.showSoftKeyboard(binding.mediaCaptionText)
diff --git a/app/src/main/java/org/wikipedia/edit/insertmedia/InsertMediaViewModel.kt b/app/src/main/java/org/wikipedia/edit/insertmedia/InsertMediaViewModel.kt
index 150a8e13c9e..9b0145ed45a 100644
--- a/app/src/main/java/org/wikipedia/edit/insertmedia/InsertMediaViewModel.kt
+++ b/app/src/main/java/org/wikipedia/edit/insertmedia/InsertMediaViewModel.kt
@@ -5,18 +5,24 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import androidx.paging.*
+import kotlinx.coroutines.CoroutineExceptionHandler
+import kotlinx.coroutines.launch
+import org.wikipedia.Constants
import org.wikipedia.dataclient.Service
import org.wikipedia.dataclient.ServiceFactory
import org.wikipedia.dataclient.WikiSite
import org.wikipedia.dataclient.mwapi.MwQueryResponse
import org.wikipedia.page.PageTitle
import org.wikipedia.util.StringUtil
+import org.wikipedia.util.log.L
class InsertMediaViewModel(bundle: Bundle) : ViewModel() {
+ val wikiSite = bundle.getParcelable(Constants.ARG_WIKISITE)!!
var searchQuery = StringUtil.removeHTMLTags(StringUtil.removeUnderscores(bundle.getString(InsertMediaActivity.EXTRA_SEARCH_QUERY)!!))
val originalSearchQuery = searchQuery
var selectedImage: PageTitle? = null
+
var imagePosition = IMAGE_POSITION_RIGHT
var imageType = IMAGE_TYPE_THUMBNAIL
var imageSize = IMAGE_SIZE_DEFAULT
@@ -25,6 +31,10 @@ class InsertMediaViewModel(bundle: Bundle) : ViewModel() {
InsertMediaPagingSource(searchQuery)
}.flow.cachedIn(viewModelScope)
+ init {
+ loadMagicWords()
+ }
+
class InsertMediaPagingSource(
val searchQuery: String,
) : PagingSource() {
@@ -32,12 +42,17 @@ class InsertMediaViewModel(bundle: Bundle) : ViewModel() {
return try {
val wikiSite = WikiSite(Service.COMMONS_URL)
val response = ServiceFactory.get(WikiSite(Service.COMMONS_URL))
- .fullTextSearch("File: $searchQuery", params.key?.gsroffset?.toString(), params.loadSize, params.key?.continuation)
+ .fullTextSearchCommons(searchQuery, params.key?.gsroffset?.toString(), params.loadSize, params.key?.continuation)
return response.query?.pages?.let { list ->
val results = list.sortedBy { it.index }.map {
val pageTitle = PageTitle(it.title, wikiSite, it.thumbUrl())
- pageTitle.description = it.description
+ // since this is an imageinfo query, the thumb URL and description will
+ // come from image metadata.
+ it.imageInfo()?.let { imageInfo ->
+ pageTitle.thumbUrl = imageInfo.thumbUrl
+ pageTitle.description = imageInfo.metadata?.imageDescription()
+ }
pageTitle
}
LoadResult.Page(results, null, response.continuation)
@@ -54,6 +69,39 @@ class InsertMediaViewModel(bundle: Bundle) : ViewModel() {
}
}
+ private fun loadMagicWords() {
+ if (magicWordsLang == wikiSite.languageCode) {
+ return
+ }
+
+ magicWords[IMAGE_POSITION_NONE] = "none"
+ magicWords[IMAGE_POSITION_CENTER] = "center"
+ magicWords[IMAGE_POSITION_LEFT] = "left"
+ magicWords[IMAGE_POSITION_RIGHT] = "right"
+ magicWords[IMAGE_TYPE_THUMBNAIL] = "thumb"
+ magicWords[IMAGE_TYPE_FRAMELESS] = "frameless"
+ magicWords[IMAGE_TYPE_FRAME] = "frame"
+ magicWords[IMAGE_ALT_TEXT] = "alt=$1"
+
+ viewModelScope.launch(CoroutineExceptionHandler { _, throwable ->
+ L.e(throwable)
+ }) {
+ ServiceFactory.get(wikiSite).getSiteInfoWithMagicWords()
+ .query?.magicwords?.let { it ->
+ it.find { it.name == IMAGE_POSITION_NONE }?.aliases?.first()?.let { magicWords[IMAGE_POSITION_NONE] = it }
+ it.find { it.name == IMAGE_POSITION_CENTER }?.aliases?.first()?.let { magicWords[IMAGE_POSITION_CENTER] = it }
+ it.find { it.name == IMAGE_POSITION_LEFT }?.aliases?.first()?.let { magicWords[IMAGE_POSITION_LEFT] = it }
+ it.find { it.name == IMAGE_POSITION_RIGHT }?.aliases?.first()?.let { magicWords[IMAGE_POSITION_RIGHT] = it }
+ it.find { it.name == IMAGE_TYPE_THUMBNAIL }?.aliases?.first()?.let { magicWords[IMAGE_TYPE_THUMBNAIL] = it }
+ it.find { it.name == IMAGE_TYPE_FRAMELESS }?.aliases?.first()?.let { magicWords[IMAGE_TYPE_FRAMELESS] = it }
+ it.find { it.name == IMAGE_TYPE_FRAME }?.aliases?.first()?.let { magicWords[IMAGE_TYPE_FRAME] = it }
+ it.find { it.name == IMAGE_ALT_TEXT }?.aliases?.first()?.let { magicWords[IMAGE_ALT_TEXT] = it }
+ it.find { it.name == IMAGE_POSITION_NONE }?.aliases?.first()?.let { magicWords[IMAGE_POSITION_NONE] = it }
+ }
+ magicWordsLang = wikiSite.languageCode
+ }
+ }
+
class Factory(private val bundle: Bundle) : ViewModelProvider.Factory {
@Suppress("unchecked_cast")
override fun create(modelClass: Class): T {
@@ -62,14 +110,18 @@ class InsertMediaViewModel(bundle: Bundle) : ViewModel() {
}
companion object {
- const val IMAGE_POSITION_NONE = "none"
- const val IMAGE_POSITION_CENTER = "center"
- const val IMAGE_POSITION_LEFT = "left"
- const val IMAGE_POSITION_RIGHT = "right"
- const val IMAGE_TYPE_THUMBNAIL = "thumb"
- const val IMAGE_TYPE_FRAMELESS = "frameless"
- const val IMAGE_TYPE_FRAME = "frame"
+ const val IMAGE_POSITION_NONE = "img_none"
+ const val IMAGE_POSITION_CENTER = "img_center"
+ const val IMAGE_POSITION_LEFT = "img_left"
+ const val IMAGE_POSITION_RIGHT = "img_right"
+ const val IMAGE_TYPE_THUMBNAIL = "img_thumbnail"
+ const val IMAGE_TYPE_FRAMELESS = "img_frameless"
+ const val IMAGE_TYPE_FRAME = "img_framed"
const val IMAGE_TYPE_BASIC = "basic"
+ const val IMAGE_ALT_TEXT = "img_alt"
const val IMAGE_SIZE_DEFAULT = "220x124"
+
+ private var magicWordsLang = ""
+ val magicWords = mutableMapOf()
}
}
diff --git a/app/src/main/java/org/wikipedia/edit/preview/EditPreviewFragment.kt b/app/src/main/java/org/wikipedia/edit/preview/EditPreviewFragment.kt
index 6011827e30e..6fc5d37bf3d 100644
--- a/app/src/main/java/org/wikipedia/edit/preview/EditPreviewFragment.kt
+++ b/app/src/main/java/org/wikipedia/edit/preview/EditPreviewFragment.kt
@@ -8,6 +8,7 @@ import android.view.View
import android.view.ViewGroup
import android.webkit.WebView
import androidx.core.app.ActivityCompat
+import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.wikipedia.R
@@ -26,9 +27,9 @@ import org.wikipedia.page.*
import org.wikipedia.page.references.PageReferences
import org.wikipedia.page.references.ReferenceDialog
import org.wikipedia.util.DeviceUtil
+import org.wikipedia.util.DimenUtil
import org.wikipedia.util.ResourceUtil
import org.wikipedia.util.UriUtil
-import org.wikipedia.views.ViewAnimations
class EditPreviewFragment : Fragment(), CommunicationBridgeListener, ReferenceDialog.Callback {
@@ -74,8 +75,9 @@ class EditPreviewFragment : Fragment(), CommunicationBridgeListener, ReferenceDi
RestService.PAGE_HTML_PREVIEW_ENDPOINT + UriUtil.encodeURL(title.prefixedText)
val postData = "wikitext=" + UriUtil.encodeURL(wikiText)
binding.editPreviewWebview.postUrl(url, postData.toByteArray())
- ViewAnimations.fadeIn(binding.editPreviewContainer) { requireActivity().invalidateOptionsMenu() }
- ViewAnimations.fadeOut(ActivityCompat.requireViewById(requireActivity(), R.id.edit_section_container))
+ ActivityCompat.requireViewById(requireActivity(), R.id.edit_section_container).isVisible = false
+ binding.editPreviewContainer.isVisible = true
+ requireActivity().invalidateOptionsMenu()
}
private fun initWebView() {
@@ -91,9 +93,10 @@ class EditPreviewFragment : Fragment(), CommunicationBridgeListener, ReferenceDi
if (!isAdded) {
return
}
+ bridge.onMetadataReady()
+ bridge.execute(JavaScriptActionHandler.setMargins(16, 0, 16, 16 + DimenUtil.roundedPxToDp(binding.licenseText.height.toFloat())))
(requireActivity() as EditSectionActivity).showProgressBar(false)
requireActivity().invalidateOptionsMenu()
- bridge.execute(JavaScriptActionHandler.setTopMargin(0))
}
}
@@ -130,7 +133,9 @@ class EditPreviewFragment : Fragment(), CommunicationBridgeListener, ReferenceDi
* When fade-out completes, the state of the actionbar button(s) is updated.
*/
fun hide(toView: View) {
- ViewAnimations.crossFade(binding.editPreviewContainer, toView) { requireActivity().invalidateOptionsMenu() }
+ binding.editPreviewContainer.isVisible = false
+ toView.isVisible = true
+ requireActivity().invalidateOptionsMenu()
}
inner class EditLinkHandler constructor(context: Context) : LinkHandler(context) {
diff --git a/app/src/main/java/org/wikipedia/feed/news/NewsActivity.kt b/app/src/main/java/org/wikipedia/feed/news/NewsActivity.kt
index 6dfb609fcf9..e383b38bc6d 100644
--- a/app/src/main/java/org/wikipedia/feed/news/NewsActivity.kt
+++ b/app/src/main/java/org/wikipedia/feed/news/NewsActivity.kt
@@ -4,6 +4,7 @@ import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.os.Bundle
+import org.wikipedia.Constants
import org.wikipedia.R
import org.wikipedia.activity.SingleFragmentActivity
import org.wikipedia.dataclient.WikiSite
@@ -19,7 +20,7 @@ class NewsActivity : SingleFragmentActivity() {
public override fun createFragment(): NewsFragment {
return newInstance(intent.getParcelableExtra(EXTRA_NEWS_ITEM)!!,
- intent.getParcelableExtra(EXTRA_WIKI)!!)
+ intent.getParcelableExtra(Constants.ARG_WIKISITE)!!)
}
fun updateNavigationBarColor() {
@@ -28,11 +29,11 @@ class NewsActivity : SingleFragmentActivity() {
companion object {
const val EXTRA_NEWS_ITEM = "item"
- const val EXTRA_WIKI = "wiki"
+
fun newIntent(context: Context, item: NewsItem, wiki: WikiSite): Intent {
return Intent(context, NewsActivity::class.java)
.putExtra(EXTRA_NEWS_ITEM, item)
- .putExtra(EXTRA_WIKI, wiki)
+ .putExtra(Constants.ARG_WIKISITE, wiki)
}
}
}
diff --git a/app/src/main/java/org/wikipedia/feed/news/NewsFragment.kt b/app/src/main/java/org/wikipedia/feed/news/NewsFragment.kt
index a39db1e5675..6429fe4cd80 100644
--- a/app/src/main/java/org/wikipedia/feed/news/NewsFragment.kt
+++ b/app/src/main/java/org/wikipedia/feed/news/NewsFragment.kt
@@ -134,7 +134,7 @@ class NewsFragment : Fragment() {
fun newInstance(item: NewsItem, wiki: WikiSite): NewsFragment {
return NewsFragment().apply {
arguments = bundleOf(NewsActivity.EXTRA_NEWS_ITEM to item,
- NewsActivity.EXTRA_WIKI to wiki)
+ Constants.ARG_WIKISITE to wiki)
}
}
}
diff --git a/app/src/main/java/org/wikipedia/feed/news/NewsViewModel.kt b/app/src/main/java/org/wikipedia/feed/news/NewsViewModel.kt
index bb5387ede3b..26d4342bbac 100644
--- a/app/src/main/java/org/wikipedia/feed/news/NewsViewModel.kt
+++ b/app/src/main/java/org/wikipedia/feed/news/NewsViewModel.kt
@@ -3,12 +3,13 @@ package org.wikipedia.feed.news
import android.os.Bundle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
+import org.wikipedia.Constants
import org.wikipedia.dataclient.WikiSite
class NewsViewModel(bundle: Bundle) : ViewModel() {
val item = bundle.getParcelable(NewsActivity.EXTRA_NEWS_ITEM)!!
- val wiki = bundle.getParcelable(NewsActivity.EXTRA_WIKI)!!
+ val wiki = bundle.getParcelable(Constants.ARG_WIKISITE)!!
class Factory(private val bundle: Bundle) : ViewModelProvider.Factory {
@Suppress("unchecked_cast")
diff --git a/app/src/main/java/org/wikipedia/feed/onthisday/OnThisDayActivity.kt b/app/src/main/java/org/wikipedia/feed/onthisday/OnThisDayActivity.kt
index 3662a64ec54..f1124acdbee 100644
--- a/app/src/main/java/org/wikipedia/feed/onthisday/OnThisDayActivity.kt
+++ b/app/src/main/java/org/wikipedia/feed/onthisday/OnThisDayActivity.kt
@@ -11,7 +11,7 @@ class OnThisDayActivity : SingleFragmentActivity() {
override fun createFragment(): OnThisDayFragment {
return OnThisDayFragment.newInstance(intent.getIntExtra(EXTRA_AGE, 0),
- intent.getParcelableExtra(EXTRA_WIKISITE)!!,
+ intent.getParcelableExtra(Constants.ARG_WIKISITE)!!,
intent.getIntExtra(EXTRA_YEAR, -1),
intent.getSerializableExtra(Constants.INTENT_EXTRA_INVOKE_SOURCE) as InvokeSource)
}
@@ -19,13 +19,12 @@ class OnThisDayActivity : SingleFragmentActivity() {
companion object {
const val EXTRA_AGE = "age"
const val EXTRA_YEAR = "year"
- const val EXTRA_WIKISITE = "wikiSite"
fun newIntent(context: Context, age: Int, year: Int,
wikiSite: WikiSite, invokeSource: InvokeSource): Intent {
return Intent(context, OnThisDayActivity::class.java)
.putExtra(EXTRA_AGE, age)
- .putExtra(EXTRA_WIKISITE, wikiSite)
+ .putExtra(Constants.ARG_WIKISITE, wikiSite)
.putExtra(EXTRA_YEAR, year)
.putExtra(Constants.INTENT_EXTRA_INVOKE_SOURCE, invokeSource)
}
diff --git a/app/src/main/java/org/wikipedia/feed/onthisday/OnThisDayFragment.kt b/app/src/main/java/org/wikipedia/feed/onthisday/OnThisDayFragment.kt
index 54fea9caefa..43e5e8b36b3 100644
--- a/app/src/main/java/org/wikipedia/feed/onthisday/OnThisDayFragment.kt
+++ b/app/src/main/java/org/wikipedia/feed/onthisday/OnThisDayFragment.kt
@@ -53,7 +53,7 @@ class OnThisDayFragment : Fragment(), CustomDatePicker.Callback {
val topDecorationDp = 24
val age = requireArguments().getInt(OnThisDayActivity.EXTRA_AGE, 0)
- wiki = requireArguments().getParcelable(OnThisDayActivity.EXTRA_WIKISITE)!!
+ wiki = requireArguments().getParcelable(Constants.ARG_WIKISITE)!!
invokeSource = requireArguments().getSerializable(Constants.INTENT_EXTRA_INVOKE_SOURCE) as InvokeSource
date = DateUtil.getDefaultDateFor(age)
yearOnCardView = requireArguments().getInt(OnThisDayActivity.EXTRA_YEAR, -1)
@@ -294,7 +294,7 @@ class OnThisDayFragment : Fragment(), CustomDatePicker.Callback {
fun newInstance(age: Int, wikiSite: WikiSite, year: Int, invokeSource: InvokeSource): OnThisDayFragment {
return OnThisDayFragment().apply {
arguments = bundleOf(OnThisDayActivity.EXTRA_AGE to age,
- OnThisDayActivity.EXTRA_WIKISITE to wikiSite,
+ Constants.ARG_WIKISITE to wikiSite,
OnThisDayActivity.EXTRA_YEAR to year,
Constants.INTENT_EXTRA_INVOKE_SOURCE to invokeSource)
}
diff --git a/app/src/main/java/org/wikipedia/feed/suggestededits/SuggestedEditsFeedClient.kt b/app/src/main/java/org/wikipedia/feed/suggestededits/SuggestedEditsFeedClient.kt
index 1aba55b23d5..663f44e453e 100644
--- a/app/src/main/java/org/wikipedia/feed/suggestededits/SuggestedEditsFeedClient.kt
+++ b/app/src/main/java/org/wikipedia/feed/suggestededits/SuggestedEditsFeedClient.kt
@@ -5,6 +5,10 @@ import android.os.Parcelable
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.schedulers.Schedulers
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
import kotlinx.parcelize.Parcelize
import org.wikipedia.Constants
import org.wikipedia.WikipediaApp
@@ -47,7 +51,16 @@ class SuggestedEditsFeedClient : FeedClient {
if (age == 0) {
// In the background, fetch the user's latest contribution stats, so that we can update whether the
// Suggested Edits feature is paused or disabled, the next time the feed is refreshed.
- UserContribStats.getEditCountsObservable().subscribe()
+ CoroutineScope(Dispatchers.Main).launch {
+ withContext(Dispatchers.IO) {
+ try {
+ UserContribStats.verifyEditCountsAndPauseState()
+ } catch (e: Exception) {
+ // Log the exception; will retry next time the feed is refreshed.
+ L.e(e)
+ }
+ }
+ }
}
if (UserContribStats.isDisabled() || UserContribStats.maybePauseAndGetEndDate() != null) {
diff --git a/app/src/main/java/org/wikipedia/gallery/GalleryActivity.kt b/app/src/main/java/org/wikipedia/gallery/GalleryActivity.kt
index 64302ee60f5..ca4f423934c 100644
--- a/app/src/main/java/org/wikipedia/gallery/GalleryActivity.kt
+++ b/app/src/main/java/org/wikipedia/gallery/GalleryActivity.kt
@@ -126,12 +126,12 @@ class GalleryActivity : BaseActivity(), LinkPreviewDialog.Callback, GalleryItemF
binding.errorView.visibility = View.GONE
loadGalleryContent()
}
- if (intent.hasExtra(EXTRA_PAGETITLE)) {
- pageTitle = intent.getParcelableExtra(EXTRA_PAGETITLE)
+ if (intent.hasExtra(Constants.ARG_TITLE)) {
+ pageTitle = intent.getParcelableExtra(Constants.ARG_TITLE)
}
initialFilename = intent.getStringExtra(EXTRA_FILENAME)
revision = intent.getLongExtra(EXTRA_REVISION, 0)
- sourceWiki = intent.getParcelableExtra(EXTRA_WIKI)!!
+ sourceWiki = intent.getParcelableExtra(Constants.ARG_WIKISITE)!!
galleryAdapter = GalleryItemAdapter(this@GalleryActivity)
binding.pager.adapter = galleryAdapter
binding.pager.registerOnPageChangeCallback(pageChangeListener)
@@ -669,9 +669,7 @@ class GalleryActivity : BaseActivity(), LinkPreviewDialog.Callback, GalleryItemF
const val ACTIVITY_RESULT_PAGE_SELECTED = 1
const val ACTIVITY_RESULT_IMAGE_CAPTION_ADDED = 2
const val ACTIVITY_RESULT_IMAGE_TAGS_ADDED = 3
- const val EXTRA_PAGETITLE = "pageTitle"
const val EXTRA_FILENAME = "filename"
- const val EXTRA_WIKI = "wiki"
const val EXTRA_REVISION = "revision"
const val EXTRA_SOURCE = "source"
const val SOURCE_LEAD_IMAGE = 0
@@ -683,11 +681,11 @@ class GalleryActivity : BaseActivity(), LinkPreviewDialog.Callback, GalleryItemF
val intent = Intent()
.setClass(context, GalleryActivity::class.java)
.putExtra(EXTRA_FILENAME, filename)
- .putExtra(EXTRA_WIKI, wiki)
+ .putExtra(Constants.ARG_WIKISITE, wiki)
.putExtra(EXTRA_REVISION, revision)
.putExtra(EXTRA_SOURCE, source)
if (pageTitle != null) {
- intent.putExtra(EXTRA_PAGETITLE, pageTitle)
+ intent.putExtra(Constants.ARG_TITLE, pageTitle)
}
return intent
}
diff --git a/app/src/main/java/org/wikipedia/gallery/GalleryItemFragment.kt b/app/src/main/java/org/wikipedia/gallery/GalleryItemFragment.kt
index d4e22f13fcb..aa1bd42fb8a 100644
--- a/app/src/main/java/org/wikipedia/gallery/GalleryItemFragment.kt
+++ b/app/src/main/java/org/wikipedia/gallery/GalleryItemFragment.kt
@@ -66,7 +66,7 @@ class GalleryItemFragment : Fragment(), MenuProvider, RequestListener
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mediaListItem = requireArguments().getParcelable(ARG_GALLERY_ITEM)!!
- pageTitle = requireArguments().getParcelable(ARG_PAGETITLE)
+ pageTitle = requireArguments().getParcelable(Constants.ARG_TITLE)
if (pageTitle == null) {
pageTitle = PageTitle(mediaListItem.title, Constants.commonsWikiSite)
}
@@ -300,12 +300,11 @@ class GalleryItemFragment : Fragment(), MenuProvider, RequestListener
}
companion object {
- private const val ARG_PAGETITLE = "pageTitle"
private const val ARG_GALLERY_ITEM = "galleryItem"
fun newInstance(pageTitle: PageTitle?, item: MediaListItem): GalleryItemFragment {
return GalleryItemFragment().apply {
- arguments = bundleOf(ARG_PAGETITLE to pageTitle, ARG_GALLERY_ITEM to item)
+ arguments = bundleOf(Constants.ARG_TITLE to pageTitle, ARG_GALLERY_ITEM to item)
}
}
}
diff --git a/app/src/main/java/org/wikipedia/language/AcceptLanguageUtil.java b/app/src/main/java/org/wikipedia/language/AcceptLanguageUtil.java
deleted file mode 100644
index 0b0bdb01991..00000000000
--- a/app/src/main/java/org/wikipedia/language/AcceptLanguageUtil.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package org.wikipedia.language;
-
-import androidx.annotation.NonNull;
-
-import java.util.Locale;
-
-public final class AcceptLanguageUtil {
- private static final float APP_LANGUAGE_QUALITY = .9f;
- private static final float SYSTEM_LANGUAGE_QUALITY = .8f;
-
- /**
- * @return The value that should go in the Accept-Language header.
- */
- @NonNull
- public static String getAcceptLanguage(@NonNull String wikiLanguageCode,
- @NonNull String appLanguageCode,
- @NonNull String systemLanguageCode) {
- String acceptLanguage = wikiLanguageCode;
- acceptLanguage = appendToAcceptLanguage(acceptLanguage, appLanguageCode, APP_LANGUAGE_QUALITY);
- acceptLanguage = appendToAcceptLanguage(acceptLanguage, systemLanguageCode, SYSTEM_LANGUAGE_QUALITY);
- return acceptLanguage;
- }
-
- @NonNull
- private static String appendToAcceptLanguage(@NonNull String acceptLanguage,
- @NonNull String languageCode, float quality) {
- // If accept-language already contains the language, just return accept-language.
- if (acceptLanguage.contains(languageCode)) {
- return acceptLanguage;
- }
-
- // If accept-language is empty, don't append. Just return the language.
- if (acceptLanguage.isEmpty()) {
- return languageCode;
- }
-
- // Accept-language is nonempty, append the language.
- return String.format(Locale.ROOT, "%s,%s;q=%.1f", acceptLanguage, languageCode, quality);
- }
-
- private AcceptLanguageUtil() { }
-}
diff --git a/app/src/main/java/org/wikipedia/language/AcceptLanguageUtil.kt b/app/src/main/java/org/wikipedia/language/AcceptLanguageUtil.kt
new file mode 100644
index 00000000000..e9baa133a2a
--- /dev/null
+++ b/app/src/main/java/org/wikipedia/language/AcceptLanguageUtil.kt
@@ -0,0 +1,22 @@
+package org.wikipedia.language
+
+import java.util.Locale
+
+object AcceptLanguageUtil {
+ private const val APP_LANGUAGE_QUALITY = .9f
+ private const val SYSTEM_LANGUAGE_QUALITY = .8f
+
+ fun getAcceptLanguage(wikiLanguageCode: String, appLanguageCode: String, systemLanguageCode: String): String {
+ var acceptLanguage = wikiLanguageCode
+ acceptLanguage = appendToAcceptLanguage(acceptLanguage, appLanguageCode, APP_LANGUAGE_QUALITY)
+ acceptLanguage = appendToAcceptLanguage(acceptLanguage, systemLanguageCode, SYSTEM_LANGUAGE_QUALITY)
+ return acceptLanguage
+ }
+
+ private fun appendToAcceptLanguage(acceptLanguage: String, languageCode: String, quality: Float): String {
+ if (acceptLanguage.contains(languageCode)) {
+ return acceptLanguage
+ }
+ return if (acceptLanguage.isEmpty()) languageCode else String.format(Locale.ROOT, "%s,%s;q=%.1f", acceptLanguage, languageCode, quality)
+ }
+}
diff --git a/app/src/main/java/org/wikipedia/language/AppLanguageLookUpTable.kt b/app/src/main/java/org/wikipedia/language/AppLanguageLookUpTable.kt
index 4ac4e260551..c2bf7ff2c8c 100644
--- a/app/src/main/java/org/wikipedia/language/AppLanguageLookUpTable.kt
+++ b/app/src/main/java/org/wikipedia/language/AppLanguageLookUpTable.kt
@@ -2,58 +2,38 @@ package org.wikipedia.language
import android.content.Context
import org.wikipedia.R
-import java.lang.ref.SoftReference
import java.util.*
class AppLanguageLookUpTable(context: Context) {
private val resources = context.resources
- private var codesRef = SoftReference>(null)
- private var canonicalNamesRef = SoftReference>(null)
- private var localizedNamesRef = SoftReference>(null)
- private var languagesVariantsRef = SoftReference