mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-26 04:13:53 +01:00
Merge branch 'main' into fix/upload-limit-#3101
This commit is contained in:
commit
e17a668acc
14 changed files with 208 additions and 53 deletions
|
|
@ -173,19 +173,13 @@ class BookmarkItemsDao @Inject constructor(
|
|||
categoryNameList: List<String>,
|
||||
categoryDescriptionList: List<String>,
|
||||
categoryThumbnailList: List<String>
|
||||
): List<CategoryItem> {
|
||||
return buildList {
|
||||
for (i in categoryNameList.indices) {
|
||||
add(
|
||||
CategoryItem(
|
||||
categoryNameList[i],
|
||||
categoryDescriptionList[i],
|
||||
categoryThumbnailList[i],
|
||||
false
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
): List<CategoryItem> = categoryNameList.mapIndexed { index, name ->
|
||||
CategoryItem(
|
||||
name = name,
|
||||
description = categoryDescriptionList.getOrNull(index),
|
||||
thumbnail = categoryThumbnailList.getOrNull(index),
|
||||
isSelected = false
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ import com.google.gson.annotations.SerializedName
|
|||
*/
|
||||
class CampaignConfig {
|
||||
@SerializedName("showOnlyLiveCampaigns")
|
||||
private val showOnlyLiveCampaigns = false
|
||||
var showOnlyLiveCampaigns = false
|
||||
|
||||
@SerializedName("sortBy")
|
||||
private val sortBy: String? = null
|
||||
}
|
||||
var sortBy: String? = null
|
||||
}
|
||||
|
|
@ -8,8 +8,8 @@ import fr.free.nrw.commons.campaigns.models.Campaign
|
|||
*/
|
||||
class CampaignResponseDTO {
|
||||
@SerializedName("config")
|
||||
val campaignConfig: CampaignConfig? = null
|
||||
var campaignConfig: CampaignConfig? = null
|
||||
|
||||
@SerializedName("campaigns")
|
||||
val campaigns: List<Campaign>? = null
|
||||
}
|
||||
var campaigns: List<Campaign>? = null
|
||||
}
|
||||
|
|
@ -324,7 +324,7 @@ after opening the app.
|
|||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.blockingGet()
|
||||
Timber.d("Resuming " + stuckUploads.size + " uploads...")
|
||||
Timber.d("Resuming %d uploads...", stuckUploads.size)
|
||||
if (!stuckUploads.isEmpty()) {
|
||||
for (contribution in stuckUploads) {
|
||||
contribution.state = Contribution.STATE_QUEUED
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import android.view.LayoutInflater
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.Switch
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.view.isVisible
|
||||
|
|
@ -20,6 +19,7 @@ import androidx.lifecycle.lifecycleScope
|
|||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.switchmaterial.SwitchMaterial
|
||||
import fr.free.nrw.commons.contributions.Contribution
|
||||
import fr.free.nrw.commons.contributions.ContributionDao
|
||||
import fr.free.nrw.commons.customselector.database.NotForUploadStatusDao
|
||||
|
|
@ -82,7 +82,7 @@ class ImageFragment :
|
|||
*/
|
||||
private var selectorRV: RecyclerView? = null
|
||||
private var loader: ProgressBar? = null
|
||||
private var switch: Switch? = null
|
||||
private var switch: SwitchMaterial? = null
|
||||
lateinit var filteredImages: ArrayList<Image>
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -112,8 +112,8 @@ class WikidataItemDetailsActivity : BaseActivity(), MediaDetailProvider, Categor
|
|||
|
||||
viewPagerAdapter!!.setTabs(
|
||||
R.string.title_for_media to depictionImagesListFragment!!,
|
||||
R.string.title_for_subcategories to childDepictionsFragment,
|
||||
R.string.title_for_parent_categories to parentDepictionsFragment
|
||||
R.string.title_for_child_classes to childDepictionsFragment,
|
||||
R.string.title_for_parent_classes to parentDepictionsFragment
|
||||
)
|
||||
binding!!.viewPager.offscreenPageLimit = 2
|
||||
viewPagerAdapter!!.notifyDataSetChanged()
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import java.io.File
|
|||
import java.io.FileOutputStream
|
||||
import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* This activity will set two tabs, achievements and
|
||||
|
|
@ -122,7 +123,7 @@ class ProfileActivity : BaseActivity() {
|
|||
val rootView = window.decorView.findViewById<View>(android.R.id.content)
|
||||
val screenShot = getScreenShot(rootView)
|
||||
if (screenShot == null) {
|
||||
Log.e("ERROR", "ScreenShot is null")
|
||||
Timber.e("ScreenShot is null")
|
||||
return false
|
||||
}
|
||||
showAlert(screenShot)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package fr.free.nrw.commons.settings
|
||||
|
||||
import android.Manifest.permission
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.app.Dialog
|
||||
import android.content.Context.MODE_PRIVATE
|
||||
|
|
@ -303,6 +304,11 @@ class SettingsFragment : PreferenceFragmentCompat() {
|
|||
)
|
||||
}
|
||||
|
||||
// Remove the space for icons in the settings menu.
|
||||
// This uses an internal API that shouldn't be used in app code,
|
||||
// but it appears to be the most robust way to do this at the moment,
|
||||
// disable the warning.
|
||||
@SuppressLint("RestrictedApi")
|
||||
override fun onCreateAdapter(preferenceScreen: PreferenceScreen): Adapter<PreferenceViewHolder>
|
||||
{
|
||||
return object : PreferenceGroupAdapter(preferenceScreen) {
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ object LocationUtils {
|
|||
latLng = LatLng(latLngArray[1].trim().toDouble(),
|
||||
latLngArray[0].trim().toDouble(), 1f)
|
||||
} catch (e: Exception) {
|
||||
Timber.e("Error while parsing user entered lat long: %s", e)
|
||||
Timber.e(e, "Error while parsing user entered lat long")
|
||||
}
|
||||
|
||||
return latLng
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:background="?attr/mainBackground">
|
||||
|
||||
<Switch
|
||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
||||
android:id="@+id/switchWidget"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<resources>
|
||||
<!--accessibility UI description strings-->
|
||||
<string name="commons_facebook">Commons Facebook Page</string>
|
||||
<string name="commons_github">Commons Github Source Code</string>
|
||||
<string name="commons_github">Commons GitHub Source Code</string>
|
||||
<string name="commons_logo">Commons Logo</string>
|
||||
<string name="commons_website">Commons Website</string>
|
||||
<string name="exit_location_picker">Exit location picker</string>
|
||||
|
|
@ -493,12 +493,12 @@ Upload your first media by tapping on the add button.</string>
|
|||
<string name="check_category_failure_message">Could not request category check for %1$s</string>
|
||||
<string name="check_category_toast">Requesting category check for %1$s</string>
|
||||
<string name="nominate_for_deletion_done">Done</string>
|
||||
<string name="send_thank_success_title">Sending Thanks: Success</string>
|
||||
<string name="send_thank_success_title">Sending thanks: Success</string>
|
||||
<string name="send_thank_success_message">Sent thanks to %1$s</string>
|
||||
<string name="send_thank_failure_message">Failed to send thanks to %1$s</string>
|
||||
<string name="send_thank_failure_title">Sending Thanks: Failure</string>
|
||||
<string name="send_thank_failure_title">Sending thanks: Failure</string>
|
||||
|
||||
<string name="send_thank_toast">Sending Thanks for %1$s</string>
|
||||
<string name="send_thank_toast">Sending thanks for %1$s</string>
|
||||
<string name="review_copyright">Does this follow the rules of copyright?</string>
|
||||
<string name="review_category">Is this correctly categorized?</string>
|
||||
<string name="review_spam">Is this in-scope?</string>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,10 @@ package fr.free.nrw.commons
|
|||
import com.google.gson.Gson
|
||||
import com.nhaarman.mockitokotlin2.any
|
||||
import com.nhaarman.mockitokotlin2.verify
|
||||
import com.nhaarman.mockitokotlin2.times
|
||||
import fr.free.nrw.commons.campaigns.CampaignResponseDTO
|
||||
import fr.free.nrw.commons.campaigns.CampaignConfig
|
||||
import fr.free.nrw.commons.campaigns.models.Campaign
|
||||
import fr.free.nrw.commons.explore.depictions.DepictsClient
|
||||
import fr.free.nrw.commons.location.LatLng
|
||||
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient
|
||||
|
|
@ -10,13 +14,22 @@ import fr.free.nrw.commons.nearby.model.NearbyQueryParams
|
|||
import okhttp3.Call
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import okhttp3.ResponseBody
|
||||
import okhttp3.mockwebserver.MockResponse
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito
|
||||
import org.mockito.Mockito.times
|
||||
import org.mockito.Mockito.eq
|
||||
import org.mockito.MockitoAnnotations
|
||||
import java.io.BufferedReader
|
||||
import java.io.InputStreamReader
|
||||
import java.lang.Exception
|
||||
|
||||
class OkHttpJsonApiClientTests {
|
||||
|
|
@ -45,34 +58,43 @@ class OkHttpJsonApiClientTests {
|
|||
@Mock
|
||||
lateinit var response: Response
|
||||
|
||||
@Mock
|
||||
lateinit var responseBody: ResponseBody
|
||||
|
||||
private lateinit var mockWebServer: TestWebServer
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.openMocks(this)
|
||||
okHttpJsonApiClient =
|
||||
OkHttpJsonApiClient(
|
||||
okhttpClient,
|
||||
depictsClient,
|
||||
wikiMediaToolforgeUrl,
|
||||
sparqlQueryUrl,
|
||||
campaignsUrl,
|
||||
gson,
|
||||
)
|
||||
mockWebServer = TestWebServer()
|
||||
mockWebServer.setUp()
|
||||
okHttpJsonApiClient = OkHttpJsonApiClient(
|
||||
okhttpClient,
|
||||
depictsClient,
|
||||
wikiMediaToolforgeUrl,
|
||||
sparqlQueryUrl,
|
||||
mockWebServer.getUrl(), //use the mock server for the campaignsUrl
|
||||
gson
|
||||
)
|
||||
Mockito.`when`(okhttpClient.newCall(any())).thenReturn(call)
|
||||
Mockito.`when`(call.execute()).thenReturn(response)
|
||||
Mockito.`when`(response.isSuccessful).thenReturn(false)
|
||||
Mockito.`when`(response.message).thenReturn("test")
|
||||
Mockito.`when`(response.body).thenReturn(responseBody)
|
||||
Mockito.`when`(responseBody.string()).thenReturn("{\"error\": \"test\"}")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetNearbyPlacesCustomQuery() {
|
||||
Mockito.`when`(response.message).thenReturn("test")
|
||||
try {
|
||||
okHttpJsonApiClient.getNearbyPlaces(latLng, "test", 10.0, "test")
|
||||
} catch (e: Exception) {
|
||||
assert(e.message.equals("test"))
|
||||
assertEquals("test", e.message)
|
||||
}
|
||||
try {
|
||||
okHttpJsonApiClient.getNearbyPlaces(NearbyQueryParams.Rectangular(latLng, latLng), "test", true, "test")
|
||||
} catch (e: Exception) {
|
||||
assert(e.message.equals("test"))
|
||||
assertEquals("test", e.message)
|
||||
}
|
||||
verify(okhttpClient, times(2)).newCall(any())
|
||||
verify(call, times(2)).execute()
|
||||
|
|
@ -80,11 +102,10 @@ class OkHttpJsonApiClientTests {
|
|||
|
||||
@Test
|
||||
fun testGetNearbyPlaces() {
|
||||
Mockito.`when`(response.message).thenReturn("test")
|
||||
try {
|
||||
okHttpJsonApiClient.getNearbyPlaces(latLng, "test", 10.0, null)
|
||||
} catch (e: Exception) {
|
||||
assert(e.message.equals("test"))
|
||||
assertEquals("test", e.message)
|
||||
}
|
||||
try {
|
||||
okHttpJsonApiClient.getNearbyPlaces(
|
||||
|
|
@ -93,9 +114,8 @@ class OkHttpJsonApiClientTests {
|
|||
true,
|
||||
null
|
||||
)
|
||||
|
||||
} catch (e: Exception) {
|
||||
assert(e.message.equals("test"))
|
||||
assertEquals("test", e.message)
|
||||
}
|
||||
try {
|
||||
okHttpJsonApiClient.getNearbyPlaces(
|
||||
|
|
@ -105,7 +125,7 @@ class OkHttpJsonApiClientTests {
|
|||
null
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
assert(e.message.equals("test"))
|
||||
assertEquals("test", e.message)
|
||||
}
|
||||
verify(okhttpClient, times(3)).newCall(any())
|
||||
verify(call, times(3)).execute()
|
||||
|
|
@ -113,18 +133,121 @@ class OkHttpJsonApiClientTests {
|
|||
|
||||
@Test
|
||||
fun testGetNearbyItemCount() {
|
||||
Mockito.`when`(response.message).thenReturn("test")
|
||||
try {
|
||||
okHttpJsonApiClient.getNearbyItemCount(NearbyQueryParams.Radial(latLng, 10f))
|
||||
} catch (e: Exception) {
|
||||
assert(e.message.equals("test"))
|
||||
assertEquals("test", e.message)
|
||||
}
|
||||
try {
|
||||
okHttpJsonApiClient.getNearbyItemCount(NearbyQueryParams.Rectangular(latLng, latLng))
|
||||
} catch (e: Exception) {
|
||||
assert(e.message.equals("test"))
|
||||
assertEquals("test", e.message)
|
||||
}
|
||||
verify(okhttpClient, times(2)).newCall(any())
|
||||
verify(call, times(2)).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetCampaignsWithData() {
|
||||
//loads the json response from resources
|
||||
val jsonResponse = loadJsonFromResource("campaigns_response_with_data.json")
|
||||
|
||||
//mocks the succesfull response chain
|
||||
Mockito.`when`(response.isSuccessful).thenReturn(true)
|
||||
Mockito.`when`(response.message).thenReturn("OK")
|
||||
Mockito.`when`(response.body).thenReturn(responseBody)
|
||||
Mockito.`when`(responseBody.string()).thenReturn(jsonResponse)
|
||||
|
||||
val campaignResponse = CampaignResponseDTO().apply {
|
||||
campaignConfig = CampaignConfig().apply {
|
||||
showOnlyLiveCampaigns = false
|
||||
sortBy = "startDate"
|
||||
}
|
||||
campaigns = listOf(
|
||||
Campaign().apply {
|
||||
title = "Wiki Loves Monuments"
|
||||
isWLMCampaign = true
|
||||
},
|
||||
Campaign().apply {
|
||||
title = "Wiki Loves Nature"
|
||||
isWLMCampaign = false
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
//any() for the string argument and eq() for the class argument.
|
||||
Mockito.`when`(
|
||||
gson.fromJson(
|
||||
any<String>(),
|
||||
eq(CampaignResponseDTO::class.java)
|
||||
)
|
||||
).thenReturn(campaignResponse)
|
||||
|
||||
//call the getCampaigns
|
||||
val result: CampaignResponseDTO? = okHttpJsonApiClient.getCampaigns().blockingGet()
|
||||
|
||||
//verify the results
|
||||
assertNotNull(result)
|
||||
assertNotNull(result?.campaigns)
|
||||
assertEquals(2, result?.campaigns!!.size)
|
||||
assertEquals("Wiki Loves Monuments", result.campaigns!![0].title)
|
||||
assertTrue(result.campaigns!![0].isWLMCampaign)
|
||||
assertEquals("Wiki Loves Nature", result.campaigns!![1].title)
|
||||
assertEquals(false, result.campaigns!![1].isWLMCampaign)
|
||||
assertNotNull(result.campaignConfig)
|
||||
assertFalse(result.campaignConfig!!.showOnlyLiveCampaigns)
|
||||
assertEquals("startDate", result.campaignConfig!!.sortBy)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetCampaignsEmpty() {
|
||||
//loads the empty json response
|
||||
val jsonResponse = loadJsonFromResource("campaigns_response_empty.json")
|
||||
|
||||
//mocks the successful response chain
|
||||
Mockito.`when`(response.isSuccessful).thenReturn(true)
|
||||
Mockito.`when`(response.message).thenReturn("OK")
|
||||
Mockito.`when`(response.body).thenReturn(responseBody)
|
||||
Mockito.`when`(responseBody.string()).thenReturn(jsonResponse)
|
||||
|
||||
val campaignResponse = CampaignResponseDTO().apply {
|
||||
campaignConfig = CampaignConfig().apply {
|
||||
showOnlyLiveCampaigns = false
|
||||
sortBy = "startDate"
|
||||
}
|
||||
campaigns = emptyList()
|
||||
}
|
||||
|
||||
//use any() for the string argument and eq() for the class argument.
|
||||
Mockito.`when`(
|
||||
gson.fromJson(
|
||||
any<String>(),
|
||||
eq(CampaignResponseDTO::class.java)
|
||||
)
|
||||
).thenReturn(campaignResponse)
|
||||
|
||||
//calls getCampaigns
|
||||
val result: CampaignResponseDTO? = okHttpJsonApiClient.getCampaigns().blockingGet()
|
||||
|
||||
//verify the results
|
||||
assertNotNull(result)
|
||||
assertNotNull(result?.campaigns)
|
||||
assertTrue(result?.campaigns!!.isEmpty())
|
||||
assertNotNull(result.campaignConfig)
|
||||
assertFalse(result.campaignConfig!!.showOnlyLiveCampaigns)
|
||||
assertEquals("startDate", result.campaignConfig!!.sortBy)
|
||||
}
|
||||
|
||||
fun loadJsonFromResource(fileName: String): String {
|
||||
val resourcePath = "raw/$fileName"
|
||||
//uses the classloader to find the resource in the test environment
|
||||
val inputStream = javaClass.classLoader?.getResourceAsStream(resourcePath)
|
||||
|
||||
if (inputStream != null) {
|
||||
//reads the entire stream content
|
||||
return BufferedReader(InputStreamReader(inputStream)).use { it.readText() }
|
||||
}
|
||||
//throws an exception with the correct expected path
|
||||
throw IllegalArgumentException("Resource $fileName not found. Please ensure the file is located in app/src/test/resources/raw/")
|
||||
}
|
||||
}
|
||||
7
app/src/test/resources/raw/campaigns_response_empty.json
Normal file
7
app/src/test/resources/raw/campaigns_response_empty.json
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"config": {
|
||||
"showOnlyLiveCampaigns": false,
|
||||
"sortBy": "startDate"
|
||||
},
|
||||
"campaigns": []
|
||||
}
|
||||
24
app/src/test/resources/raw/campaigns_response_with_data.json
Normal file
24
app/src/test/resources/raw/campaigns_response_with_data.json
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"config": {
|
||||
"showOnlyLiveCampaigns": false,
|
||||
"sortBy": "startDate"
|
||||
},
|
||||
"campaigns": [
|
||||
{
|
||||
"title": "Wiki Loves Monuments",
|
||||
"description": "A campaign to photograph monuments",
|
||||
"startDate": "2025-09-01",
|
||||
"endDate": "2025-09-30",
|
||||
"link": "https://commons.wikimedia.org/wiki/Campaign:Wiki_Loves_Monuments",
|
||||
"isWLMCampaign": true
|
||||
},
|
||||
{
|
||||
"title": "Wiki Loves Nature",
|
||||
"description": "A campaign to photograph nature",
|
||||
"startDate": "2025-06-01",
|
||||
"endDate": "2025-06-30",
|
||||
"link": "https://commons.wikimedia.org/wiki/Campaign:Wiki_Loves_Nature",
|
||||
"isWLMCampaign": false
|
||||
}
|
||||
]
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue