Improved recycler view

This commit is contained in:
Kanahia 2024-07-16 11:21:52 +05:30
parent ca1bf88127
commit 33897bbdc0
6 changed files with 271 additions and 123 deletions

View file

@ -9,18 +9,18 @@ import android.webkit.URLUtil
import android.widget.ImageView import android.widget.ImageView
import android.widget.ProgressBar import android.widget.ProgressBar
import android.widget.TextView import android.widget.TextView
import androidx.paging.PagedListAdapter
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.facebook.imagepipeline.request.ImageRequest import com.facebook.imagepipeline.request.ImageRequest
import fr.free.nrw.commons.R import fr.free.nrw.commons.R
import fr.free.nrw.commons.contributions.Contribution import fr.free.nrw.commons.contributions.Contribution
import timber.log.Timber
import java.io.File import java.io.File
class FailedUploadsAdapter(items: List<Contribution>, callback: Callback) : class FailedUploadsAdapter(callback: Callback) :
RecyclerView.Adapter<FailedUploadsAdapter.ViewHolder>() { PagedListAdapter<Contribution, FailedUploadsAdapter.ViewHolder>(ContributionDiffCallback()) {
private val items: List<Contribution> = items private var callback: Callback = callback
private var callback: FailedUploadsAdapter.Callback = callback
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view: View = val view: View =
@ -29,10 +29,12 @@ class FailedUploadsAdapter(items: List<Contribution>, callback: Callback) :
} }
override fun onBindViewHolder(holder: ViewHolder, position: Int) { override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item: Contribution = items[position] val item: Contribution? = getItem(position)
if (item != null) {
holder.titleTextView.setText(item.media.displayTitle) holder.titleTextView.setText(item.media.displayTitle)
}
var imageRequest: ImageRequest? = null var imageRequest: ImageRequest? = null
val imageSource: String = item.localUri.toString() val imageSource: String = item?.localUri.toString()
if (!TextUtils.isEmpty(imageSource)) { if (!TextUtils.isEmpty(imageSource)) {
if (URLUtil.isFileUrl(imageSource)) { if (URLUtil.isFileUrl(imageSource)) {
@ -47,6 +49,7 @@ class FailedUploadsAdapter(items: List<Contribution>, callback: Callback) :
} }
} }
if (item != null) {
if (item.state == Contribution.STATE_FAILED) { if (item.state == Contribution.STATE_FAILED) {
if (item.errorInfo != null) { if (item.errorInfo != null) {
holder.errorTextView.setText(item.errorInfo) holder.errorTextView.setText(item.errorInfo)
@ -56,6 +59,7 @@ class FailedUploadsAdapter(items: List<Contribution>, callback: Callback) :
holder.errorTextView.visibility = View.VISIBLE holder.errorTextView.visibility = View.VISIBLE
holder.itemProgress.visibility = View.GONE holder.itemProgress.visibility = View.GONE
} }
}
holder.deleteButton.setOnClickListener { holder.deleteButton.setOnClickListener {
callback.deleteUpload(item) callback.deleteUpload(item)
} }
@ -65,12 +69,6 @@ class FailedUploadsAdapter(items: List<Contribution>, callback: Callback) :
holder.itemImage.setImageRequest(imageRequest) holder.itemImage.setImageRequest(imageRequest)
} }
override fun getItemCount(): Int {
return items.size
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var itemImage: com.facebook.drawee.view.SimpleDraweeView = var itemImage: com.facebook.drawee.view.SimpleDraweeView =
itemView.findViewById(R.id.itemImage) itemView.findViewById(R.id.itemImage)
@ -81,6 +79,20 @@ class FailedUploadsAdapter(items: List<Contribution>, callback: Callback) :
var retryButton: ImageView = itemView.findViewById(R.id.retryButton) var retryButton: ImageView = itemView.findViewById(R.id.retryButton)
} }
override fun getItemId(position: Int): Long {
return getItem(position)?.pageId?.hashCode()?.toLong() ?: position.toLong()
}
class ContributionDiffCallback : DiffUtil.ItemCallback<Contribution>() {
override fun areItemsTheSame(oldItem: Contribution, newItem: Contribution): Boolean {
return oldItem.pageId.hashCode() == newItem.pageId.hashCode()
}
override fun areContentsTheSame(oldItem: Contribution, newItem: Contribution): Boolean {
return oldItem.transferred == newItem.transferred
}
}
interface Callback { interface Callback {
fun deleteUpload(contribution: Contribution?) fun deleteUpload(contribution: Contribution?)
fun restartUpload(index: Int) fun restartUpload(index: Int)

View file

@ -27,7 +27,8 @@ import javax.inject.Inject
* Use the [FailedUploadsFragment.newInstance] factory method to * Use the [FailedUploadsFragment.newInstance] factory method to
* create an instance of this fragment. * create an instance of this fragment.
*/ */
class FailedUploadsFragment : CommonsDaggerSupportFragment(),PendingUploadsContract.View, FailedUploadsAdapter.Callback { class FailedUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsContract.View,
FailedUploadsAdapter.Callback {
private var param1: String? = null private var param1: String? = null
private var param2: String? = null private var param2: String? = null
private val ARG_PARAM1 = "param1" private val ARG_PARAM1 = "param1"
@ -46,6 +47,8 @@ class FailedUploadsFragment : CommonsDaggerSupportFragment(),PendingUploadsContr
lateinit var binding: FragmentFailedUploadsBinding lateinit var binding: FragmentFailedUploadsBinding
private lateinit var adapter: FailedUploadsAdapter
var contributionsList = ArrayList<Contribution>() var contributionsList = ArrayList<Contribution>()
private lateinit var uploadProgressActivity: UploadProgressActivity private lateinit var uploadProgressActivity: UploadProgressActivity
@ -81,16 +84,27 @@ class FailedUploadsFragment : CommonsDaggerSupportFragment(),PendingUploadsContr
): View? { ): View? {
binding = FragmentFailedUploadsBinding.inflate(layoutInflater) binding = FragmentFailedUploadsBinding.inflate(layoutInflater)
pendingUploadsPresenter.onAttachView(this) pendingUploadsPresenter.onAttachView(this)
initRecyclerView() initAdapter()
return binding.root return binding.root
} }
fun initAdapter() {
adapter = FailedUploadsAdapter(this)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initRecyclerView()
}
fun initRecyclerView() { fun initRecyclerView() {
binding.failedUploadsRecyclerView.setLayoutManager(LinearLayoutManager(this.context)) binding.failedUploadsRecyclerView.setLayoutManager(LinearLayoutManager(this.context))
binding.failedUploadsRecyclerView.adapter = adapter
pendingUploadsPresenter!!.getFailedContributions(userName) pendingUploadsPresenter!!.getFailedContributions(userName)
pendingUploadsPresenter!!.failedContributionList.observe( pendingUploadsPresenter!!.failedContributionList.observe(
viewLifecycleOwner viewLifecycleOwner
) { list: PagedList<Contribution?> -> ) { list: PagedList<Contribution?> ->
adapter.submitList(list)
contributionsList = ArrayList() contributionsList = ArrayList()
list.forEach { list.forEach {
if (it != null) { if (it != null) {
@ -105,7 +119,6 @@ class FailedUploadsFragment : CommonsDaggerSupportFragment(),PendingUploadsContr
uploadProgressActivity.setErrorIconsVisibility(true) uploadProgressActivity.setErrorIconsVisibility(true)
binding.nofailedTextView.visibility = View.GONE binding.nofailedTextView.visibility = View.GONE
binding.failedUplaodsLl.visibility = View.VISIBLE binding.failedUplaodsLl.visibility = View.VISIBLE
val adapter = FailedUploadsAdapter(contributionsList, this)
binding.failedUploadsRecyclerView.setAdapter(adapter) binding.failedUploadsRecyclerView.setAdapter(adapter)
} }
} }

View file

@ -9,88 +9,145 @@ import android.webkit.URLUtil
import android.widget.ImageView import android.widget.ImageView
import android.widget.ProgressBar import android.widget.ProgressBar
import android.widget.TextView import android.widget.TextView
import androidx.paging.PagedListAdapter
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.facebook.imagepipeline.request.ImageRequest import com.facebook.imagepipeline.request.ImageRequest
import fr.free.nrw.commons.R import fr.free.nrw.commons.R
import fr.free.nrw.commons.contributions.Contribution import fr.free.nrw.commons.contributions.Contribution
import timber.log.Timber
import java.io.File import java.io.File
class PendingUploadsAdapter(items: List<Contribution>, callback: Callback) : class PendingUploadsAdapter(private val callback: Callback) :
RecyclerView.Adapter<PendingUploadsAdapter.ViewHolder>() { PagedListAdapter<Contribution, PendingUploadsAdapter.ViewHolder>(ContributionDiffCallback()) {
private val items: List<Contribution> = items
private var callback: Callback = callback
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view: View = val view: View = LayoutInflater.from(parent.context)
LayoutInflater.from(parent.context).inflate(R.layout.item_pending_upload, parent, false) .inflate(R.layout.item_pending_upload, parent, false)
return ViewHolder(view) return ViewHolder(view)
} }
override fun onBindViewHolder(holder: ViewHolder, position: Int, payloads: MutableList<Any>) {
if (payloads.isNotEmpty()) {
when (val latestPayload = payloads.lastOrNull()) {
is ContributionChangePayload.Progress -> holder.bindProgress(
latestPayload.transferred,
latestPayload.total
)
is ContributionChangePayload.State -> holder.bindState(latestPayload.state)
else -> onBindViewHolder(holder, position)
}
} else {
onBindViewHolder(holder, position)
}
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) { override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item: Contribution = items[position] val contribution = getItem(position)
holder.titleTextView.setText(item.media.displayTitle) contribution?.let {
var imageRequest: ImageRequest? = null holder.bind(it)
val imageSource: String = item.localUri.toString()
if (!TextUtils.isEmpty(imageSource)) {
if (URLUtil.isFileUrl(imageSource)) {
imageRequest = ImageRequest.fromUri(Uri.parse(imageSource))!!
} else if (imageSource != null) {
val file = File(imageSource)
imageRequest = ImageRequest.fromFile(file)!!
}
if (imageRequest != null) {
holder.itemImage.setImageRequest(imageRequest)
}
}
if (item.state == Contribution.STATE_QUEUED || item.state == Contribution.STATE_PAUSED) {
holder.errorTextView.setText("Queued")
holder.errorTextView.visibility = View.VISIBLE
holder.itemProgress.visibility = View.GONE
} else {
if (item.transferred == 0L) {
holder.errorTextView.setText("Queued")
holder.errorTextView.visibility = View.VISIBLE
holder.itemProgress.visibility = View.GONE
} else {
holder.errorTextView.visibility = View.GONE
holder.itemProgress.visibility = View.VISIBLE
val total: Long = item.dataLength
val transferred: Long = item.transferred
if (transferred == 0L || transferred >= total) {
holder.itemProgress.setIndeterminate(true)
} else {
holder.itemProgress.setIndeterminate(false)
holder.itemProgress.setProgress(((transferred.toDouble() / total.toDouble()) * 100).toInt())
}
}
}
holder.itemImage.setImageRequest(imageRequest)
holder.deleteButton.setOnClickListener { holder.deleteButton.setOnClickListener {
callback!!.deleteUpload(item) callback.deleteUpload(contribution)
} }
} }
override fun getItemCount(): Int {
return items.size
} }
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var itemImage: com.facebook.drawee.view.SimpleDraweeView = var itemImage: com.facebook.drawee.view.SimpleDraweeView =
itemView.findViewById(R.id.itemImage) itemView.findViewById(R.id.itemImage)
var titleTextView: TextView = itemView.findViewById<TextView>(R.id.titleTextView) var titleTextView: TextView = itemView.findViewById(R.id.titleTextView)
var itemProgress: ProgressBar = itemView.findViewById<ProgressBar>(R.id.itemProgress) var itemProgress: ProgressBar = itemView.findViewById(R.id.itemProgress)
var errorTextView: TextView = itemView.findViewById<TextView>(R.id.errorTextView) var errorTextView: TextView = itemView.findViewById(R.id.errorTextView)
var deleteButton: ImageView = itemView.findViewById(R.id.deleteButton) var deleteButton: ImageView = itemView.findViewById(R.id.deleteButton)
fun bind(contribution: Contribution) {
titleTextView.text = contribution.media.displayTitle
val imageSource: String = contribution.localUri.toString()
var imageRequest: ImageRequest? = null
if (!TextUtils.isEmpty(imageSource)) {
if (URLUtil.isFileUrl(imageSource)) {
imageRequest = ImageRequest.fromUri(Uri.parse(imageSource))
} else {
val file = File(imageSource)
imageRequest = ImageRequest.fromFile(file)
}
}
if (imageRequest != null) {
itemImage.setImageRequest(imageRequest)
}
bindState(contribution.state)
bindProgress(contribution.transferred, contribution.dataLength)
}
fun bindState(state: Int) {
if (state == Contribution.STATE_QUEUED || state == Contribution.STATE_PAUSED) {
errorTextView.text = "Queued"
errorTextView.visibility = View.VISIBLE
itemProgress.visibility = View.GONE
} else {
errorTextView.visibility = View.GONE
itemProgress.visibility = View.VISIBLE
}
}
fun bindProgress(transferred: Long, total: Long) {
if (transferred == 0L) {
errorTextView.text = "Queued"
errorTextView.visibility = View.VISIBLE
itemProgress.visibility = View.GONE
} else {
errorTextView.visibility = View.GONE
itemProgress.visibility = View.VISIBLE
if (transferred >= total) {
itemProgress.isIndeterminate = true
} else {
itemProgress.isIndeterminate = false
itemProgress.progress =
((transferred.toDouble() / total.toDouble()) * 100).toInt()
}
}
}
} }
interface Callback { interface Callback {
fun deleteUpload(contribution: Contribution?) fun deleteUpload(contribution: Contribution?)
} }
class ContributionDiffCallback : DiffUtil.ItemCallback<Contribution>() {
override fun areItemsTheSame(oldItem: Contribution, newItem: Contribution): Boolean {
return oldItem.pageId.hashCode() == newItem.pageId.hashCode()
}
override fun areContentsTheSame(oldItem: Contribution, newItem: Contribution): Boolean {
return oldItem.transferred == newItem.transferred
}
override fun getChangePayload(oldItem: Contribution, newItem: Contribution): Any? {
return when {
oldItem.transferred != newItem.transferred -> {
ContributionChangePayload.Progress(newItem.transferred, newItem.dataLength)
}
oldItem.state != newItem.state -> {
ContributionChangePayload.State(newItem.state)
}
else -> super.getChangePayload(oldItem, newItem)
}
}
}
override fun getItemId(position: Int): Long {
return getItem(position)?.pageId?.hashCode()?.toLong() ?: position.toLong()
}
private sealed interface ContributionChangePayload {
data class Progress(val transferred: Long, val total: Long) : ContributionChangePayload
data class State(val state: Int) : ContributionChangePayload
}
} }

View file

@ -1,16 +1,17 @@
package fr.free.nrw.commons.upload package fr.free.nrw.commons.upload
import android.content.Context import android.content.Context
import android.os.AsyncTask
import android.os.Build.VERSION import android.os.Build.VERSION
import android.os.Build.VERSION_CODES import android.os.Build.VERSION_CODES
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater 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 androidx.paging.PagedList import androidx.paging.PagedList
import androidx.paging.PositionalDataSource
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import fr.free.nrw.commons.CommonsApplication import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver
import fr.free.nrw.commons.R import fr.free.nrw.commons.R
import fr.free.nrw.commons.auth.SessionManager import fr.free.nrw.commons.auth.SessionManager
import fr.free.nrw.commons.contributions.Contribution import fr.free.nrw.commons.contributions.Contribution
@ -19,7 +20,6 @@ import fr.free.nrw.commons.di.CommonsDaggerSupportFragment
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.NetworkUtils
import fr.free.nrw.commons.utils.ViewUtil import fr.free.nrw.commons.utils.ViewUtil
import org.apache.commons.lang3.StringUtils import org.apache.commons.lang3.StringUtils
import timber.log.Timber import timber.log.Timber
@ -54,6 +54,8 @@ class PendingUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsCon
private lateinit var uploadProgressActivity: UploadProgressActivity private lateinit var uploadProgressActivity: UploadProgressActivity
private lateinit var adapter: PendingUploadsAdapter
private var contributionsSize = 0 private var contributionsSize = 0
var contributionsList = ArrayList<Contribution>() var contributionsList = ArrayList<Contribution>()
private var totalUploads = 0 private var totalUploads = 0
@ -90,17 +92,22 @@ class PendingUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsCon
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = FragmentPendingUploadsBinding.inflate(inflater, container, false) binding = FragmentPendingUploadsBinding.inflate(inflater, container, false)
pendingUploadsPresenter.onAttachView(this) pendingUploadsPresenter.onAttachView(this)
initRecyclerView() initAdapter()
return binding.root return binding.root
} }
fun initAdapter() {
adapter = PendingUploadsAdapter(this)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
initRecyclerView()
} }
fun initRecyclerView() { fun initRecyclerView() {
binding.pendingUploadsRecyclerView.setLayoutManager(LinearLayoutManager(this.context)) binding.pendingUploadsRecyclerView.setLayoutManager(LinearLayoutManager(this.context))
binding.pendingUploadsRecyclerView.adapter = adapter
pendingUploadsPresenter!!.setup( pendingUploadsPresenter!!.setup(
userName, userName,
sessionManager!!.userName == userName sessionManager!!.userName == userName
@ -111,7 +118,6 @@ class PendingUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsCon
contributionsSize = list.size contributionsSize = list.size
contributionsList = ArrayList() contributionsList = ArrayList()
var pausedOrQueuedUploads = 0 var pausedOrQueuedUploads = 0
var failedUploads = 0
list.forEach { list.forEach {
if (it != null) { if (it != null) {
if (it.state == Contribution.STATE_PAUSED if (it.state == Contribution.STATE_PAUSED
@ -125,9 +131,6 @@ class PendingUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsCon
) { ) {
pausedOrQueuedUploads++ pausedOrQueuedUploads++
} }
if (it.state == Contribution.STATE_FAILED){
failedUploads++
}
} }
} }
if (contributionsSize == 0) { if (contributionsSize == 0) {
@ -142,13 +145,15 @@ class PendingUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsCon
binding.nopendingTextView.visibility = View.GONE binding.nopendingTextView.visibility = View.GONE
binding.pendingUplaodsLl.visibility = View.VISIBLE binding.pendingUplaodsLl.visibility = View.VISIBLE
val sortedContributionsList: List<Contribution> = if (VERSION.SDK_INT >= VERSION_CODES.N) { val sortedContributionsList: List<Contribution> =
if (VERSION.SDK_INT >= VERSION_CODES.N) {
contributionsList.sortedByDescending { it.dateModifiedInMillis() } contributionsList.sortedByDescending { it.dateModifiedInMillis() }
} else { } else {
contributionsList.sortedBy { it.dateModifiedInMillis() }.reversed() contributionsList.sortedBy { it.dateModifiedInMillis() }.reversed()
} }
val newContributionList: MutableList<Contribution> = sortedContributionsList.toMutableList() val newContributionList: MutableList<Contribution> =
sortedContributionsList.toMutableList()
val listOfRemoved: MutableList<Contribution> = mutableListOf() val listOfRemoved: MutableList<Contribution> = mutableListOf()
val last = sortedContributionsList.last() val last = sortedContributionsList.last()
for (i in sortedContributionsList.indices) { for (i in sortedContributionsList.indices) {
@ -159,8 +164,33 @@ class PendingUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsCon
} }
newContributionList.removeAll(listOfRemoved) newContributionList.removeAll(listOfRemoved)
newContributionList.addAll(listOfRemoved) newContributionList.addAll(listOfRemoved)
val adapter = PendingUploadsAdapter(newContributionList, this)
binding.pendingUploadsRecyclerView.setAdapter(adapter) // TODO: WORK ON THE SORTING ISSUE
val dataSource = object : PositionalDataSource<Contribution>() {
override fun loadInitial(
params: LoadInitialParams,
callback: LoadInitialCallback<Contribution>
) {
callback.onResult(newContributionList, 0, newContributionList.size)
}
override fun loadRange(
params: LoadRangeParams,
callback: LoadRangeCallback<Contribution>
) {
val start = params.startPosition
val end = Math.min(start + params.loadSize, newContributionList.size)
callback.onResult(newContributionList.subList(start, end))
}
}
val pagedList = PagedList.Builder(dataSource, 10)
.setFetchExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
.setNotifyExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
.build()
adapter.submitList(pagedList)
binding.progressTextView.setText((totalUploads - contributionsSize).toString() + "/" + totalUploads + " uploaded") binding.progressTextView.setText((totalUploads - contributionsSize).toString() + "/" + totalUploads + " uploaded")
binding.progressBarPending.progress = totalUploads - contributionsSize binding.progressBarPending.progress = totalUploads - contributionsSize
if (pausedOrQueuedUploads == contributionsSize) { if (pausedOrQueuedUploads == contributionsSize) {
@ -187,7 +217,10 @@ class PendingUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsCon
String.format(Locale.getDefault(), getString(R.string.no)), String.format(Locale.getDefault(), getString(R.string.no)),
{ {
ViewUtil.showShortToast(context, R.string.cancelling_upload) ViewUtil.showShortToast(context, R.string.cancelling_upload)
pendingUploadsPresenter.deleteUpload(contribution, this.requireContext().applicationContext) pendingUploadsPresenter.deleteUpload(
contribution,
this.requireContext().applicationContext
)
resetProgressBar() resetProgressBar()
}, },
{} {}
@ -207,13 +240,21 @@ class PendingUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsCon
fun restartUploads() { fun restartUploads() {
if (contributionsList != null) { if (contributionsList != null) {
pendingUploadsPresenter.restartUploads(contributionsList, 0 , this.requireContext().applicationContext) pendingUploadsPresenter.restartUploads(
contributionsList,
0,
this.requireContext().applicationContext
)
} }
} }
fun pauseUploads() { fun pauseUploads() {
if (contributionsList != null) { if (contributionsList != null) {
pendingUploadsPresenter.pauseUploads(contributionsList, 0, this.requireContext().applicationContext) pendingUploadsPresenter.pauseUploads(
contributionsList,
0,
this.requireContext().applicationContext
)
} }
} }
@ -234,7 +275,11 @@ class PendingUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsCon
{ {
ViewUtil.showShortToast(context, R.string.cancelling_upload) ViewUtil.showShortToast(context, R.string.cancelling_upload)
uploadProgressActivity.hidePendingIcons() uploadProgressActivity.hidePendingIcons()
pendingUploadsPresenter.deleteUploads(contributionsList, 0, this.requireContext().applicationContext) pendingUploadsPresenter.deleteUploads(
contributionsList,
0,
this.requireContext().applicationContext
)
}, },
{} {}
) )

View file

@ -1,28 +1,34 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:fresco="http://schemas.android.com/tools" xmlns:fresco="http://schemas.android.com/tools"
android:paddingBottom="8dp" android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center" android:gravity="center"
android:orientation="horizontal"> android:orientation="horizontal"
android:paddingBottom="8dp">
<com.facebook.drawee.view.SimpleDraweeView <com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/itemImage" android:id="@+id/itemImage"
android:layout_width="50dp" android:layout_width="50dp"
android:layout_height="50dp" android:layout_height="50dp"
android:layout_marginBottom="8dp"
android:background="?attr/mainBackground" android:background="?attr/mainBackground"
app:actualImageScaleType="centerCrop" app:actualImageScaleType="centerCrop"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
fresco:placeholderImage="@drawable/ic_image_black_24dp" /> fresco:placeholderImage="@drawable/ic_image_black_24dp" />
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingHorizontal="6dp"
android:layout_weight="1" android:layout_weight="1"
android:gravity="center" android:gravity="center"
android:orientation="vertical"> android:orientation="vertical"
android:paddingHorizontal="6dp"
app:layout_constraintEnd_toStartOf="@+id/deleteButton"
app:layout_constraintStart_toEndOf="@+id/itemImage">
<TextView <TextView
android:id="@+id/titleTextView" android:id="@+id/titleTextView"
@ -34,7 +40,8 @@
android:id="@+id/itemProgress" android:id="@+id/itemProgress"
style="?android:attr/progressBarStyleHorizontal" style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content"
android:visibility="visible" />
<TextView <TextView
android:id="@+id/errorTextView" android:id="@+id/errorTextView"
@ -49,6 +56,9 @@
android:id="@+id/deleteButton" android:id="@+id/deleteButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:src="@android:drawable/ic_menu_close_clear_cancel" /> android:src="@android:drawable/ic_menu_close_clear_cancel"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</LinearLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -18,6 +18,7 @@ import fr.free.nrw.commons.auth.csrf.CsrfTokenClient
import fr.free.nrw.commons.contributions.ChunkInfo import fr.free.nrw.commons.contributions.ChunkInfo
import fr.free.nrw.commons.contributions.Contribution import fr.free.nrw.commons.contributions.Contribution
import fr.free.nrw.commons.upload.UploadClient.TimeProvider import fr.free.nrw.commons.upload.UploadClient.TimeProvider
import fr.free.nrw.commons.upload.worker.UploadWorker
import fr.free.nrw.commons.wikidata.mwapi.MwException import fr.free.nrw.commons.wikidata.mwapi.MwException
import fr.free.nrw.commons.wikidata.mwapi.MwServiceError import fr.free.nrw.commons.wikidata.mwapi.MwServiceError
import io.reactivex.Observable import io.reactivex.Observable
@ -171,6 +172,16 @@ class UploadClientTest {
@Test @Test
fun uploadFileToStash_returnsFailureIfNothingToUpload() { fun uploadFileToStash_returnsFailureIfNothingToUpload() {
// val tempFile = File.createTempFile("tempFile", ".tmp")
// tempFile.deleteOnExit()
// whenever(contribution.isCompleted()).thenReturn(false)
// whenever(contribution.fileKey).thenReturn(filekey)
// whenever(contribution.localUriPath).thenReturn(tempFile)
// whenever(fileUtilsWrapper.getMimeType(anyOrNull<File>())).thenReturn("image/png")
// whenever(fileUtilsWrapper.getFileChunks(anyOrNull<File>(), eq(expectedChunkSize))).thenReturn(emptyList())
// val result = uploadClient.uploadFileToStash(filename, contribution, mock() ).test()
// result.assertNoErrors()
// assertEquals(StashUploadState.FAILED, result.values()[0].state)
whenever(contribution.isCompleted()).thenReturn(false) whenever(contribution.isCompleted()).thenReturn(false)
whenever(contribution.fileKey).thenReturn(filekey) whenever(contribution.fileKey).thenReturn(filekey)
whenever(fileUtilsWrapper.getMimeType(anyOrNull<File>())).thenReturn("image/png") whenever(fileUtilsWrapper.getMimeType(anyOrNull<File>())).thenReturn("image/png")