Merge branch 'main' into fix-6495-bottom-buttons

This commit is contained in:
Ritika Pahwa 2025-10-26 12:24:59 +05:30 committed by GitHub
commit 7d6ad40362
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 137 additions and 57 deletions

View file

@ -1,7 +1,11 @@
package fr.free.nrw.commons package fr.free.nrw.commons
import androidx.annotation.VisibleForTesting import androidx.annotation.VisibleForTesting
import fr.free.nrw.commons.wikidata.GsonUtil
import fr.free.nrw.commons.wikidata.cookies.CommonsCookieJar import fr.free.nrw.commons.wikidata.cookies.CommonsCookieJar
import fr.free.nrw.commons.wikidata.mwapi.MwErrorResponse
import fr.free.nrw.commons.wikidata.mwapi.MwIOException
import fr.free.nrw.commons.wikidata.mwapi.MwLegacyServiceError
import okhttp3.Cache import okhttp3.Cache
import okhttp3.Interceptor import okhttp3.Interceptor
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
@ -86,16 +90,25 @@ private class UnsuccessfulResponseInterceptor : Interceptor {
rsp.peekBody(ERRORS_PREFIX.length.toLong()).use { responseBody -> rsp.peekBody(ERRORS_PREFIX.length.toLong()).use { responseBody ->
if (ERRORS_PREFIX == responseBody.string()) { if (ERRORS_PREFIX == responseBody.string()) {
rsp.body.use { body -> rsp.body.use { body ->
throw IOException(body!!.string()) val bodyString = body!!.string()
throw MwIOException(
"MediaWiki API returned error: $bodyString",
GsonUtil.defaultGson.fromJson(
bodyString,
MwErrorResponse::class.java
).error!!,
)
} }
} }
} }
} catch (e: IOException) { } catch (e: MwIOException) {
// Log the error as debug (and therefore, "expected") or at error level // Log the error as debug (and therefore, "expected") or at error level
if (suppressErrors) { if (suppressErrors) {
Timber.d(e, "Suppressed (known / expected) error") Timber.d(e, "Suppressed (known / expected) error")
} else { } else {
Timber.e(e) Timber.e(e)
throw e
} }
} }
return rsp return rsp

View file

@ -5,7 +5,6 @@ import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.res.Configuration import android.content.res.Configuration
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.os.Parcelable import android.os.Parcelable
import android.view.LayoutInflater import android.view.LayoutInflater
@ -20,6 +19,8 @@ import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
import androidx.annotation.VisibleForTesting import androidx.annotation.VisibleForTesting
import androidx.core.net.toUri
import androidx.core.os.BundleCompat
import androidx.paging.PagedList import androidx.paging.PagedList
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
@ -38,12 +39,10 @@ import fr.free.nrw.commons.filepicker.FilePicker
import fr.free.nrw.commons.media.MediaClient import fr.free.nrw.commons.media.MediaClient
import fr.free.nrw.commons.profile.ProfileActivity import fr.free.nrw.commons.profile.ProfileActivity
import fr.free.nrw.commons.utils.DialogUtil.showAlertDialog import fr.free.nrw.commons.utils.DialogUtil.showAlertDialog
import fr.free.nrw.commons.utils.SystemThemeUtils
import fr.free.nrw.commons.utils.ViewUtil.showShortToast import fr.free.nrw.commons.utils.ViewUtil.showShortToast
import fr.free.nrw.commons.utils.copyToClipboard import fr.free.nrw.commons.utils.copyToClipboard
import fr.free.nrw.commons.utils.handleWebUrl import fr.free.nrw.commons.utils.handleWebUrl
import fr.free.nrw.commons.wikidata.model.WikiSite import fr.free.nrw.commons.wikidata.model.WikiSite
import org.apache.commons.lang3.StringUtils
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Named import javax.inject.Named
@ -53,10 +52,6 @@ import javax.inject.Named
*/ */
class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsListContract.View, class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsListContract.View,
ContributionsListAdapter.Callback, WikipediaInstructionsDialogFragment.Callback { ContributionsListAdapter.Callback, WikipediaInstructionsDialogFragment.Callback {
@JvmField
@Inject
var systemThemeUtils: SystemThemeUtils? = null
@JvmField @JvmField
@Inject @Inject
var controller: ContributionController? = null var controller: ContributionController? = null
@ -83,13 +78,14 @@ class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsL
var sessionManager: SessionManager? = null var sessionManager: SessionManager? = null
private var binding: FragmentContributionsListBinding? = null private var binding: FragmentContributionsListBinding? = null
private var fab_close: Animation? = null private var fabClose: Animation? = null
private var fab_open: Animation? = null private var fabOpen: Animation? = null
private var rotate_forward: Animation? = null private var rotateForward: Animation? = null
private var rotate_backward: Animation? = null private var rotateBackward: Animation? = null
private var isFabOpen = false private var isFabOpen = false
private lateinit var inAppCameraLocationPermissionLauncher: ActivityResultLauncher<Array<String>> private lateinit var inAppCameraLocationPermissionLauncher:
ActivityResultLauncher<Array<String>>
@VisibleForTesting @VisibleForTesting
var rvContributionsList: RecyclerView? = null var rvContributionsList: RecyclerView? = null
@ -100,8 +96,8 @@ class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsL
@VisibleForTesting @VisibleForTesting
var callback: Callback? = null var callback: Callback? = null
private val SPAN_COUNT_LANDSCAPE = 3 private val spanCountLandscape = 3
private val SPAN_COUNT_PORTRAIT = 1 private val spanCountPortrait = 1
private var contributionsSize = 0 private var contributionsSize = 0
private var userName: String? = null private var userName: String? = null
@ -150,7 +146,7 @@ class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsL
userName = requireArguments().getString(ProfileActivity.KEY_USERNAME) userName = requireArguments().getString(ProfileActivity.KEY_USERNAME)
} }
if (StringUtils.isEmpty(userName)) { if (userName.isNullOrEmpty()) {
userName = sessionManager!!.userName userName = sessionManager!!.userName
} }
inAppCameraLocationPermissionLauncher = inAppCameraLocationPermissionLauncher =
@ -161,7 +157,8 @@ class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsL
controller?.locationPermissionCallback?.onLocationPermissionGranted() controller?.locationPermissionCallback?.onLocationPermissionGranted()
} else { } else {
activity?.let { currentActivity -> activity?.let { currentActivity ->
if (currentActivity.shouldShowRequestPermissionRationale(permission.ACCESS_FINE_LOCATION)) { if (currentActivity.shouldShowRequestPermissionRationale(
permission.ACCESS_FINE_LOCATION)) {
controller?.handleShowRationaleFlowCameraLocation( controller?.handleShowRationaleFlowCameraLocation(
currentActivity, currentActivity,
inAppCameraLocationPermissionLauncher, // Pass launcher inAppCameraLocationPermissionLauncher, // Pass launcher
@ -169,7 +166,8 @@ class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsL
) )
} else { } else {
controller?.locationPermissionCallback?.onLocationPermissionDenied( controller?.locationPermissionCallback?.onLocationPermissionDenied(
currentActivity.getString(R.string.in_app_camera_location_permission_denied) currentActivity.getString(
R.string.in_app_camera_location_permission_denied)
) )
} }
} }
@ -189,7 +187,7 @@ class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsL
contributionsListPresenter!!.onAttachView(this) contributionsListPresenter!!.onAttachView(this)
binding!!.fabCustomGallery.setOnClickListener { v: View? -> launchCustomSelector() } binding!!.fabCustomGallery.setOnClickListener { v: View? -> launchCustomSelector() }
binding!!.fabCustomGallery.setOnLongClickListener { view: View? -> binding!!.fabCustomGallery.setOnLongClickListener { view: View? ->
showShortToast(context, fr.free.nrw.commons.R.string.custom_selector_title) showShortToast(context, R.string.custom_selector_title)
true true
} }
@ -199,7 +197,7 @@ class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsL
} else { } else {
binding!!.tvContributionsOfUser.visibility = View.VISIBLE binding!!.tvContributionsOfUser.visibility = View.VISIBLE
binding!!.tvContributionsOfUser.text = binding!!.tvContributionsOfUser.text =
getString(fr.free.nrw.commons.R.string.contributions_of_user, userName) getString(R.string.contributions_of_user, userName)
binding!!.fabLayout.visibility = View.GONE binding!!.fabLayout.visibility = View.GONE
} }
@ -237,7 +235,10 @@ class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsL
} }
private fun initAdapter() { private fun initAdapter() {
adapter = ContributionsListAdapter(this, mediaClient!!, mediaDataExtractor!!, compositeDisposable) adapter = ContributionsListAdapter(this,
mediaClient!!,
mediaDataExtractor!!,
compositeDisposable)
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@ -312,7 +313,7 @@ class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsL
override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean { override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean {
if (e.action == MotionEvent.ACTION_DOWN) { if (e.action == MotionEvent.ACTION_DOWN) {
if (isFabOpen) { if (isFabOpen) {
animateFAB(isFabOpen) animateFAB(true)
} }
} }
return false return false
@ -344,14 +345,20 @@ class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsL
} }
private fun getSpanCount(orientation: Int): Int { private fun getSpanCount(orientation: Int): Int {
return if (orientation == Configuration.ORIENTATION_LANDSCAPE) SPAN_COUNT_LANDSCAPE else SPAN_COUNT_PORTRAIT return if (orientation == Configuration.ORIENTATION_LANDSCAPE)
spanCountLandscape
else
spanCountPortrait
} }
override fun onConfigurationChanged(newConfig: Configuration) { override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig) super.onConfigurationChanged(newConfig)
// check orientation // check orientation
binding!!.fabLayout.orientation = binding!!.fabLayout.orientation =
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) LinearLayout.HORIZONTAL else LinearLayout.VERTICAL if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE)
LinearLayout.HORIZONTAL
else
LinearLayout.VERTICAL
rvContributionsList rvContributionsList
?.setLayoutManager( ?.setLayoutManager(
GridLayoutManager(context, getSpanCount(newConfig.orientation)) GridLayoutManager(context, getSpanCount(newConfig.orientation))
@ -359,10 +366,10 @@ class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsL
} }
private fun initializeAnimations() { private fun initializeAnimations() {
fab_open = AnimationUtils.loadAnimation(activity, fr.free.nrw.commons.R.anim.fab_open) fabOpen = AnimationUtils.loadAnimation(activity, R.anim.fab_open)
fab_close = AnimationUtils.loadAnimation(activity, fr.free.nrw.commons.R.anim.fab_close) fabClose = AnimationUtils.loadAnimation(activity, R.anim.fab_close)
rotate_forward = AnimationUtils.loadAnimation(activity, fr.free.nrw.commons.R.anim.rotate_forward) rotateForward = AnimationUtils.loadAnimation(activity, R.anim.rotate_forward)
rotate_backward = AnimationUtils.loadAnimation(activity, fr.free.nrw.commons.R.anim.rotate_backward) rotateBackward = AnimationUtils.loadAnimation(activity, R.anim.rotate_backward)
} }
private fun setListeners() { private fun setListeners() {
@ -378,7 +385,7 @@ class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsL
binding!!.fabCamera.setOnLongClickListener { view: View? -> binding!!.fabCamera.setOnLongClickListener { view: View? ->
showShortToast( showShortToast(
context, context,
fr.free.nrw.commons.R.string.add_contribution_from_camera R.string.add_contribution_from_camera
) )
true true
} }
@ -387,7 +394,7 @@ class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsL
animateFAB(isFabOpen) animateFAB(isFabOpen)
} }
binding!!.fabGallery.setOnLongClickListener { view: View? -> binding!!.fabGallery.setOnLongClickListener { view: View? ->
showShortToast(context, fr.free.nrw.commons.R.string.menu_from_gallery) showShortToast(context, R.string.menu_from_gallery)
true true
} }
} }
@ -395,7 +402,7 @@ class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsL
/** /**
* Launch Custom Selector. * Launch Custom Selector.
*/ */
protected fun launchCustomSelector() { private fun launchCustomSelector() {
controller!!.initiateCustomGalleryPickWithPermission( controller!!.initiateCustomGalleryPickWithPermission(
requireActivity(), requireActivity(),
customSelectorLauncherForResult customSelectorLauncherForResult
@ -411,18 +418,18 @@ class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsL
this.isFabOpen = !isFabOpen this.isFabOpen = !isFabOpen
if (binding!!.fabPlus.isShown) { if (binding!!.fabPlus.isShown) {
if (isFabOpen) { if (isFabOpen) {
binding!!.fabPlus.startAnimation(rotate_backward) binding!!.fabPlus.startAnimation(rotateBackward)
binding!!.fabCamera.startAnimation(fab_close) binding!!.fabCamera.startAnimation(fabClose)
binding!!.fabGallery.startAnimation(fab_close) binding!!.fabGallery.startAnimation(fabClose)
binding!!.fabCustomGallery.startAnimation(fab_close) binding!!.fabCustomGallery.startAnimation(fabClose)
binding!!.fabCamera.hide() binding!!.fabCamera.hide()
binding!!.fabGallery.hide() binding!!.fabGallery.hide()
binding!!.fabCustomGallery.hide() binding!!.fabCustomGallery.hide()
} else { } else {
binding!!.fabPlus.startAnimation(rotate_forward) binding!!.fabPlus.startAnimation(rotateForward)
binding!!.fabCamera.startAnimation(fab_open) binding!!.fabCamera.startAnimation(fabOpen)
binding!!.fabGallery.startAnimation(fab_open) binding!!.fabGallery.startAnimation(fabOpen)
binding!!.fabCustomGallery.startAnimation(fab_open) binding!!.fabCustomGallery.startAnimation(fabOpen)
binding!!.fabCamera.show() binding!!.fabCamera.show()
binding!!.fabGallery.show() binding!!.fabGallery.show()
binding!!.fabCustomGallery.show() binding!!.fabCustomGallery.show()
@ -434,9 +441,9 @@ class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsL
/** /**
* Shows welcome message if user has no contributions yet i.e. new user. * Shows welcome message if user has no contributions yet i.e. new user.
*/ */
override fun showWelcomeTip(shouldShow: Boolean) { override fun showWelcomeTip(numberOfUploads: Boolean) {
binding!!.noContributionsYet.visibility = binding!!.noContributionsYet.visibility =
if (shouldShow) View.VISIBLE else View.GONE if (numberOfUploads) View.VISIBLE else View.GONE
} }
/** /**
@ -456,22 +463,22 @@ class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsL
override fun onSaveInstanceState(outState: Bundle) { override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState) super.onSaveInstanceState(outState)
val layoutManager = rvContributionsList val layoutManager = rvContributionsList?.layoutManager as GridLayoutManager?
?.getLayoutManager() as GridLayoutManager?
outState.putParcelable(RV_STATE, layoutManager!!.onSaveInstanceState()) outState.putParcelable(RV_STATE, layoutManager!!.onSaveInstanceState())
} }
override fun onViewStateRestored(savedInstanceState: Bundle?) { override fun onViewStateRestored(savedInstanceState: Bundle?) {
super.onViewStateRestored(savedInstanceState) super.onViewStateRestored(savedInstanceState)
if (null != savedInstanceState) { if (null != savedInstanceState) {
val savedRecyclerLayoutState = savedInstanceState.getParcelable<Parcelable>(RV_STATE) val savedRecyclerLayoutState =
BundleCompat.getParcelable(savedInstanceState, RV_STATE, Parcelable::class.java)
rvContributionsList!!.layoutManager!!.onRestoreInstanceState(savedRecyclerLayoutState) rvContributionsList!!.layoutManager!!.onRestoreInstanceState(savedRecyclerLayoutState)
} }
} }
override fun openMediaDetail(position: Int, isWikipediaButtonDisplayed: Boolean) { override fun openMediaDetail(contribution: Int, isWikipediaPageExists: Boolean) {
if (null != callback) { //Just being safe, ideally they won't be called when detached if (null != callback) { //Just being safe, ideally they won't be called when detached
callback!!.showDetail(position, isWikipediaButtonDisplayed) callback!!.showDetail(contribution, isWikipediaPageExists)
} }
} }
@ -483,8 +490,8 @@ class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsL
override fun addImageToWikipedia(contribution: Contribution?) { override fun addImageToWikipedia(contribution: Contribution?) {
showAlertDialog( showAlertDialog(
requireActivity(), requireActivity(),
getString(fr.free.nrw.commons.R.string.add_picture_to_wikipedia_article_title), getString(R.string.add_picture_to_wikipedia_article_title),
getString(fr.free.nrw.commons.R.string.add_picture_to_wikipedia_article_desc), getString(R.string.add_picture_to_wikipedia_article_desc),
{ {
if (contribution != null) { if (contribution != null) {
showAddImageToWikipediaInstructions(contribution) showAddImageToWikipediaInstructions(contribution)
@ -498,16 +505,18 @@ class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsL
* @param contribution * @param contribution
*/ */
private fun showAddImageToWikipediaInstructions(contribution: Contribution) { private fun showAddImageToWikipediaInstructions(contribution: Contribution) {
val fragmentManager = fragmentManager val fragmentManager = this.parentFragmentManager
val fragment = newInstance(contribution) val fragment = newInstance(contribution)
fragment.callback = fragment.callback =
WikipediaInstructionsDialogFragment.Callback { contribution: Contribution?, copyWikicode: Boolean -> WikipediaInstructionsDialogFragment.Callback {
this.onConfirmClicked( contribution: Contribution?,
copyWikicode: Boolean ->
onConfirmClicked(
contribution, contribution,
copyWikicode copyWikicode
) )
} }
fragment.show(fragmentManager!!, "WikimediaFragment") fragment.show(fragmentManager, "WikimediaFragment")
} }
@ -534,7 +543,7 @@ class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsL
val url = val url =
languageWikipediaSite!!.mobileUrl() + "/wiki/" + (contribution!!.wikidataPlace languageWikipediaSite!!.mobileUrl() + "/wiki/" + (contribution!!.wikidataPlace
?.getWikipediaPageTitle()) ?.getWikipediaPageTitle())
handleWebUrl(requireContext(), Uri.parse(url)) handleWebUrl(requireContext(), url.toUri())
} }
fun getContributionStateAt(position: Int): Int { fun getContributionStateAt(position: Int): Int {

View file

@ -17,6 +17,11 @@ interface CategoriesContract {
fun showError(stringResourceId: Int) fun showError(stringResourceId: Int)
/**
* Show a cancelable AlertDialog with a given message.
*/
fun showErrorDialog(message: String)
fun setCategories(categories: List<CategoryItem>?) fun setCategories(categories: List<CategoryItem>?)
fun goToNextScreen() fun goToNextScreen()

View file

@ -12,6 +12,7 @@ import fr.free.nrw.commons.di.CommonsApplicationModule.Companion.IO_THREAD
import fr.free.nrw.commons.di.CommonsApplicationModule.Companion.MAIN_THREAD import fr.free.nrw.commons.di.CommonsApplicationModule.Companion.MAIN_THREAD
import fr.free.nrw.commons.repository.UploadRepository import fr.free.nrw.commons.repository.UploadRepository
import fr.free.nrw.commons.upload.depicts.proxy import fr.free.nrw.commons.upload.depicts.proxy
import fr.free.nrw.commons.wikidata.mwapi.MwIOException
import io.reactivex.Observable import io.reactivex.Observable
import io.reactivex.Scheduler import io.reactivex.Scheduler
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
@ -75,7 +76,12 @@ class CategoriesPresenter
}, },
{ t: Throwable? -> { t: Throwable? ->
view.showProgress(false) view.showProgress(false)
view.showError(R.string.no_categories_found) view.showError(R.string.error_loading_categories)
val mwException = t as? MwIOException
view.showErrorDialog(
if (mwException == null) ""
else "\n${mwException.error.title} / ${mwException.error.details}"
)
Timber.e(t) Timber.e(t)
}, },
), ),
@ -194,7 +200,12 @@ class CategoriesPresenter
}, },
{ t: Throwable? -> { t: Throwable? ->
view.showProgress(false) view.showProgress(false)
view.showError(R.string.no_categories_found) view.showError(R.string.error_loading_categories)
val mwException = t as? MwIOException
view.showErrorDialog(
if (mwException == null) ""
else "\n${mwException.error.title} / ${mwException.error.details}"
)
Timber.e(t) Timber.e(t)
}, },
), ),

View file

@ -10,6 +10,7 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.jakewharton.rxbinding2.view.RxView import com.jakewharton.rxbinding2.view.RxView
@ -32,7 +33,6 @@ import io.reactivex.Notification
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable import io.reactivex.disposables.Disposable
import timber.log.Timber import timber.log.Timber
import java.util.Objects
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import javax.inject.Inject import javax.inject.Inject
@ -199,6 +199,15 @@ class UploadCategoriesFragment : UploadBaseFragment(), CategoriesContract.View {
binding?.tilContainerSearch?.error = getString(stringResourceId) binding?.tilContainerSearch?.error = getString(stringResourceId)
} }
override fun showErrorDialog(message: String) {
AlertDialog
.Builder(requireContext())
.setMessage(getString(R.string.error_loading_categories) + "\n" + message)
.setCancelable(false)
.setNegativeButton(R.string.ok){_,_ -> }
.show()
}
override fun setCategories(categories: List<CategoryItem>?) { override fun setCategories(categories: List<CategoryItem>?) {
if (adapter == null) { if (adapter == null) {
Timber.e("Adapter is null in setCategories") Timber.e("Adapter is null in setCategories")

View file

@ -0,0 +1,7 @@
package fr.free.nrw.commons.wikidata.mwapi
import fr.free.nrw.commons.wikidata.model.BaseModel
class MwErrorResponse : BaseModel() {
val error: MwLegacyServiceError? = null
}

View file

@ -0,0 +1,5 @@
package fr.free.nrw.commons.wikidata.mwapi
import java.io.IOException
class MwIOException(string: String, val error: MwLegacyServiceError) : IOException(string)

View file

@ -0,0 +1,14 @@
package fr.free.nrw.commons.wikidata.mwapi
import fr.free.nrw.commons.wikidata.model.BaseModel
class MwLegacyServiceError : BaseModel() {
val code: String? = null
private val info: String? = null
val title: String
get() = code ?: ""
val details: String
get() = info ?: ""
}

View file

@ -153,6 +153,13 @@ class UploadCategoriesFragmentUnitTests {
fragment.showError(R.string.no_categories_found) fragment.showError(R.string.no_categories_found)
} }
@Test
@Throws(Exception::class)
fun testShowErrorDialog() {
Shadows.shadowOf(Looper.getMainLooper()).idle()
fragment.showErrorDialog("")
}
@Test @Test
@Throws(Exception::class) @Throws(Exception::class)
fun testSetCategoriesCaseNull() { fun testSetCategoriesCaseNull() {