mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-27 12:53:55 +01:00
Improved recycler view
This commit is contained in:
parent
ca1bf88127
commit
33897bbdc0
6 changed files with 271 additions and 123 deletions
|
|
@ -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,15 +49,17 @@ 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)
|
||||||
}else{
|
} else {
|
||||||
holder.errorTextView.setText("Failed")
|
holder.errorTextView.setText("Failed")
|
||||||
}
|
}
|
||||||
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,8 +79,22 @@ 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -133,7 +146,7 @@ class FailedUploadsFragment : CommonsDaggerSupportFragment(),PendingUploadsContr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun restartUpload(index : Int) {
|
override fun restartUpload(index: Int) {
|
||||||
if (contributionsList != null) {
|
if (contributionsList != null) {
|
||||||
uploadProgressActivity.resetProgressBar()
|
uploadProgressActivity.resetProgressBar()
|
||||||
pendingUploadsPresenter.restartUpload(
|
pendingUploadsPresenter.restartUpload(
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -33,7 +33,7 @@ import javax.inject.Inject
|
||||||
* create an instance of this fragment.
|
* create an instance of this fragment.
|
||||||
*/
|
*/
|
||||||
class PendingUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsContract.View,
|
class PendingUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsContract.View,
|
||||||
PendingUploadsAdapter.Callback{
|
PendingUploadsAdapter.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"
|
||||||
|
|
@ -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,9 +118,8 @@ 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
|
||||||
|| it.state == Contribution.STATE_QUEUED
|
|| it.state == Contribution.STATE_QUEUED
|
||||||
|| it.state == Contribution.STATE_IN_PROGRESS
|
|| it.state == Contribution.STATE_IN_PROGRESS
|
||||||
|
|
@ -125,9 +131,6 @@ class PendingUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsCon
|
||||||
) {
|
) {
|
||||||
pausedOrQueuedUploads++
|
pausedOrQueuedUploads++
|
||||||
}
|
}
|
||||||
if (it.state == Contribution.STATE_FAILED){
|
|
||||||
failedUploads++
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (contributionsSize == 0) {
|
if (contributionsSize == 0) {
|
||||||
|
|
@ -135,37 +138,64 @@ class PendingUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsCon
|
||||||
binding.pendingUplaodsLl.visibility = View.GONE
|
binding.pendingUplaodsLl.visibility = View.GONE
|
||||||
uploadProgressActivity.hidePendingIcons()
|
uploadProgressActivity.hidePendingIcons()
|
||||||
} else {
|
} else {
|
||||||
if (totalUploads == 0){
|
if (totalUploads == 0) {
|
||||||
totalUploads = contributionsSize
|
totalUploads = contributionsSize
|
||||||
binding.progressBarPending.max = totalUploads
|
binding.progressBarPending.max = totalUploads
|
||||||
}
|
}
|
||||||
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) {
|
||||||
val current = sortedContributionsList[i]
|
val current = sortedContributionsList[i]
|
||||||
if (current.transferred == 0L && (current.dateModifiedInMillis() / 100) > (last.dateModifiedInMillis() / 100)){
|
if (current.transferred == 0L && (current.dateModifiedInMillis() / 100) > (last.dateModifiedInMillis() / 100)) {
|
||||||
listOfRemoved.add(current)
|
listOfRemoved.add(current)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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
|
||||||
binding.progressTextView.setText((totalUploads-contributionsSize).toString() + "/" + totalUploads + " uploaded")
|
val dataSource = object : PositionalDataSource<Contribution>() {
|
||||||
binding.progressBarPending.progress = totalUploads-contributionsSize
|
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.progressBarPending.progress = totalUploads - contributionsSize
|
||||||
if (pausedOrQueuedUploads == contributionsSize) {
|
if (pausedOrQueuedUploads == contributionsSize) {
|
||||||
uploadProgressActivity.setPausedIcon(true)
|
uploadProgressActivity.setPausedIcon(true)
|
||||||
}else{
|
} else {
|
||||||
uploadProgressActivity.setPausedIcon(false)
|
uploadProgressActivity.setPausedIcon(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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()
|
||||||
},
|
},
|
||||||
{}
|
{}
|
||||||
|
|
@ -206,19 +239,27 @@ 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
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteUploads(){
|
fun deleteUploads() {
|
||||||
if (contributionsList != null){
|
if (contributionsList != null) {
|
||||||
showAlertDialog(
|
showAlertDialog(
|
||||||
requireActivity(),
|
requireActivity(),
|
||||||
String.format(
|
String.format(
|
||||||
|
|
@ -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
|
||||||
|
)
|
||||||
},
|
},
|
||||||
{}
|
{}
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue