eliminar imagenes
This commit is contained in:
parent
e9304a7a4e
commit
71fef94f0d
5
app/src/main/java/com/isolaatti/common/Deletable.kt
Normal file
5
app/src/main/java/com/isolaatti/common/Deletable.kt
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
package com.isolaatti.common
|
||||||
|
|
||||||
|
open class Deletable {
|
||||||
|
var delete = false
|
||||||
|
}
|
||||||
@ -25,7 +25,7 @@ interface ImagesApi {
|
|||||||
@Part setAsProfile: MultipartBody.Part? = null,
|
@Part setAsProfile: MultipartBody.Part? = null,
|
||||||
@Part squadId: MultipartBody.Part? = null): Call<ImageDto>
|
@Part squadId: MultipartBody.Part? = null): Call<ImageDto>
|
||||||
|
|
||||||
@POST("images/delete_many")
|
@POST("images/delete/delete_many")
|
||||||
fun deleteImages(@Body deleteImagesDto: DeleteImagesDto): Call<Void>
|
fun deleteImages(@Body deleteImagesDto: DeleteImagesDto): Call<Void>
|
||||||
|
|
||||||
@GET("images/of_squad/{squadId}")
|
@GET("images/of_squad/{squadId}")
|
||||||
|
|||||||
@ -46,7 +46,7 @@ class ImagesRepositoryImpl @Inject constructor(private val imagesApi: ImagesApi,
|
|||||||
|
|
||||||
override fun deleteImages(images: List<Image>): Flow<Resource<Boolean>> = flow {
|
override fun deleteImages(images: List<Image>): Flow<Resource<Boolean>> = flow {
|
||||||
emit(Resource.Loading())
|
emit(Resource.Loading())
|
||||||
val dto = DeleteImagesDto(images.map { it.imageUrl })
|
val dto = DeleteImagesDto(images.map { it.id })
|
||||||
try {
|
try {
|
||||||
val response = imagesApi.deleteImages(dto).awaitResponse()
|
val response = imagesApi.deleteImages(dto).awaitResponse()
|
||||||
if(response.isSuccessful) {
|
if(response.isSuccessful) {
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package com.isolaatti.images.common.domain.entity
|
package com.isolaatti.images.common.domain.entity
|
||||||
|
|
||||||
|
import com.isolaatti.common.Deletable
|
||||||
import com.isolaatti.images.common.data.remote.ImageDto
|
import com.isolaatti.images.common.data.remote.ImageDto
|
||||||
import com.isolaatti.utils.UrlGen
|
import com.isolaatti.utils.UrlGen
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
@ -9,7 +10,7 @@ data class Image(
|
|||||||
val userId: Int,
|
val userId: Int,
|
||||||
val name: String,
|
val name: String,
|
||||||
val username: String
|
val username: String
|
||||||
): Serializable {
|
): Deletable(), Serializable {
|
||||||
val imageUrl: String get() = UrlGen.imageUrl(id)
|
val imageUrl: String get() = UrlGen.imageUrl(id)
|
||||||
val smallImageUrl : String get() = UrlGen.imageUrl(id, UrlGen.IMAGE_MODE_SMALL)
|
val smallImageUrl : String get() = UrlGen.imageUrl(id, UrlGen.IMAGE_MODE_SMALL)
|
||||||
val reducedImageUrl: String get() = UrlGen.imageUrl(id, UrlGen.IMAGE_MODE_REDUCED)
|
val reducedImageUrl: String get() = UrlGen.imageUrl(id, UrlGen.IMAGE_MODE_REDUCED)
|
||||||
|
|||||||
@ -18,10 +18,16 @@ import kotlin.properties.Delegates
|
|||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class ImageListViewModel @Inject constructor(private val imagesRepository: ImagesRepository) : ViewModel() {
|
class ImageListViewModel @Inject constructor(private val imagesRepository: ImagesRepository) : ViewModel() {
|
||||||
|
|
||||||
val liveList: MutableLiveData<List<Image>> = MutableLiveData()
|
enum class Event {
|
||||||
|
REMOVED_IMAGE, ADDED_IMAGE_BEGINNING
|
||||||
|
}
|
||||||
|
|
||||||
|
val liveList: MutableLiveData<List<Image>> = MutableLiveData(listOf())
|
||||||
val error: MutableLiveData<Resource.Error.ErrorType?> = MutableLiveData()
|
val error: MutableLiveData<Resource.Error.ErrorType?> = MutableLiveData()
|
||||||
val loading: MutableLiveData<Boolean> = MutableLiveData()
|
val loading: MutableLiveData<Boolean> = MutableLiveData()
|
||||||
|
val deleting: MutableLiveData<Boolean> = MutableLiveData()
|
||||||
var noMoreContent = false
|
var noMoreContent = false
|
||||||
|
var lastEvent: Event? = null
|
||||||
private var loadedFirstTime = false
|
private var loadedFirstTime = false
|
||||||
var userId by Delegates.notNull<Int>()
|
var userId by Delegates.notNull<Int>()
|
||||||
|
|
||||||
@ -74,10 +80,16 @@ class ImageListViewModel @Inject constructor(private val imagesRepository: Image
|
|||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
imagesRepository.deleteImages(images).onEach {
|
imagesRepository.deleteImages(images).onEach {
|
||||||
when(it) {
|
when(it) {
|
||||||
is Resource.Error -> {}
|
is Resource.Error -> {
|
||||||
is Resource.Loading -> {}
|
deleting.postValue(false)
|
||||||
|
}
|
||||||
|
is Resource.Loading -> {
|
||||||
|
deleting.postValue(true)
|
||||||
|
}
|
||||||
is Resource.Success -> {
|
is Resource.Success -> {
|
||||||
liveList.value = list.filterNot { image -> images.contains(image) }
|
deleting.postValue(false)
|
||||||
|
lastEvent = Event.REMOVED_IMAGE
|
||||||
|
liveList.postValue(list.filterNot { image -> images.contains(image) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.flowOn(Dispatchers.IO).launchIn(this)
|
}.flowOn(Dispatchers.IO).launchIn(this)
|
||||||
|
|||||||
@ -34,13 +34,11 @@ class ImagesAdapter(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var selectionState: Array<Boolean> = arrayOf()
|
|
||||||
|
|
||||||
var deleteMode: Boolean = false
|
var deleteMode: Boolean = false
|
||||||
set(value) {
|
set(value) {
|
||||||
field = value
|
field = value
|
||||||
if(!value) {
|
if(!value) {
|
||||||
selectionState.forEachIndexed { index, _ -> selectionState[index] = false }
|
currentList.forEach { it.delete = false }
|
||||||
}
|
}
|
||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
@ -48,7 +46,7 @@ class ImagesAdapter(
|
|||||||
inner class ImageViewHolder(val imageItemBinding: ImageItemBinding) : RecyclerView.ViewHolder(imageItemBinding.root)
|
inner class ImageViewHolder(val imageItemBinding: ImageItemBinding) : RecyclerView.ViewHolder(imageItemBinding.root)
|
||||||
|
|
||||||
fun getSelectedImages(): List<Image> {
|
fun getSelectedImages(): List<Image> {
|
||||||
return currentList.filterIndexed { index, _ -> selectionState[index] }
|
return currentList.filter { it.delete }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCurrentListChanged(
|
override fun onCurrentListChanged(
|
||||||
@ -57,7 +55,6 @@ class ImagesAdapter(
|
|||||||
) {
|
) {
|
||||||
super.onCurrentListChanged(previousList, currentList)
|
super.onCurrentListChanged(previousList, currentList)
|
||||||
noMoreContent = (currentList.size - previousList.size) == 0
|
noMoreContent = (currentList.size - previousList.size) == 0
|
||||||
selectionState = Array(currentList.size) { false }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ImageViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ImageViewHolder {
|
||||||
@ -89,11 +86,11 @@ class ImagesAdapter(
|
|||||||
holder.imageItemBinding.root.setOnClickListener {
|
holder.imageItemBinding.root.setOnClickListener {
|
||||||
holder.imageItemBinding.imageCheckbox.isChecked = !holder.imageItemBinding.imageCheckbox.isChecked
|
holder.imageItemBinding.imageCheckbox.isChecked = !holder.imageItemBinding.imageCheckbox.isChecked
|
||||||
}
|
}
|
||||||
holder.imageItemBinding.imageCheckbox.isChecked = selectionState[position]
|
holder.imageItemBinding.imageCheckbox.isChecked = image.delete
|
||||||
holder.imageItemBinding.imageCheckbox.setOnCheckedChangeListener { buttonView, isChecked ->
|
holder.imageItemBinding.imageCheckbox.setOnCheckedChangeListener { buttonView, isChecked ->
|
||||||
selectionState[position] = isChecked
|
image.delete = isChecked
|
||||||
|
|
||||||
onImageSelectedCountUpdate?.invoke(selectionState.count { it })
|
onImageSelectedCountUpdate?.invoke(currentList.count { it.delete })
|
||||||
}
|
}
|
||||||
holder.imageItemBinding.root.setOnLongClickListener(null)
|
holder.imageItemBinding.root.setOnLongClickListener(null)
|
||||||
} else {
|
} else {
|
||||||
@ -105,9 +102,9 @@ class ImagesAdapter(
|
|||||||
}
|
}
|
||||||
holder.imageItemBinding.imageCheckbox.setOnCheckedChangeListener(null)
|
holder.imageItemBinding.imageCheckbox.setOnCheckedChangeListener(null)
|
||||||
holder.imageItemBinding.root.setOnLongClickListener {
|
holder.imageItemBinding.root.setOnLongClickListener {
|
||||||
selectionState[position] = true
|
image.delete = true
|
||||||
onDeleteMode?.invoke(true)
|
onDeleteMode?.invoke(true)
|
||||||
onImageSelectedCountUpdate?.invoke(selectionState.count { it })
|
onImageSelectedCountUpdate?.invoke(currentList.count { it.delete })
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package com.isolaatti.images.image_list.ui
|
package com.isolaatti.images.image_list.ui
|
||||||
|
|
||||||
|
import android.app.Dialog
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
@ -51,6 +52,7 @@ class ImagesFragment : Fragment() {
|
|||||||
|
|
||||||
private val imageMakerLauncher = registerForActivityResult(ImageMakerContract()) { image ->
|
private val imageMakerLauncher = registerForActivityResult(ImageMakerContract()) { image ->
|
||||||
image?.also {
|
image?.also {
|
||||||
|
viewModel.lastEvent = ImageListViewModel.Event.ADDED_IMAGE_BEGINNING
|
||||||
viewModel.addImageAtTheBeginning(it)
|
viewModel.addImageAtTheBeginning(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -73,6 +75,8 @@ class ImagesFragment : Fragment() {
|
|||||||
return FileProvider.getUriForFile(requireContext(), "${MyApplication.myApp.packageName}.provider", cacheFile)
|
return FileProvider.getUriForFile(requireContext(), "${MyApplication.myApp.packageName}.provider", cacheFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var deletingImagesDialog: Dialog? = null
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
@ -93,7 +97,6 @@ class ImagesFragment : Fragment() {
|
|||||||
viewModel.loadNext()
|
viewModel.loadNext()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setupAdapter()
|
setupAdapter()
|
||||||
setupObservers()
|
setupObservers()
|
||||||
setupListeners()
|
setupListeners()
|
||||||
@ -201,6 +204,11 @@ class ImagesFragment : Fragment() {
|
|||||||
private fun setupObservers() {
|
private fun setupObservers() {
|
||||||
|
|
||||||
viewModel.liveList.observe(viewLifecycleOwner) { list ->
|
viewModel.liveList.observe(viewLifecycleOwner) { list ->
|
||||||
|
if(viewModel.lastEvent == ImageListViewModel.Event.REMOVED_IMAGE || viewModel.lastEvent == ImageListViewModel.Event.ADDED_IMAGE_BEGINNING) {
|
||||||
|
actionMode?.finish()
|
||||||
|
|
||||||
|
}
|
||||||
|
viewBinding.noImagesCard.visibility = if(list.isEmpty()) View.VISIBLE else View.GONE
|
||||||
adapter.submitList(list)
|
adapter.submitList(list)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,6 +226,18 @@ class ImagesFragment : Fragment() {
|
|||||||
viewModel.error.observe(viewLifecycleOwner) {
|
viewModel.error.observe(viewLifecycleOwner) {
|
||||||
errorViewModel.error.value = it
|
errorViewModel.error.value = it
|
||||||
}
|
}
|
||||||
|
|
||||||
|
viewModel.deleting.observe(viewLifecycleOwner) { deleting ->
|
||||||
|
if(deleting) {
|
||||||
|
deletingImagesDialog = MaterialAlertDialogBuilder(requireContext())
|
||||||
|
.setMessage(R.string.deleting_please_wait)
|
||||||
|
.setCancelable(false)
|
||||||
|
.show()
|
||||||
|
} else {
|
||||||
|
deletingImagesDialog?.dismiss()
|
||||||
|
deletingImagesDialog = null
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@ -65,14 +65,17 @@ class ImageMakerActivity : IsolaattiBaseActivity() {
|
|||||||
is Resource.Error -> {
|
is Resource.Error -> {
|
||||||
errorViewModel.error.value = it.errorType
|
errorViewModel.error.value = it.errorType
|
||||||
binding.progressBarLoading.visibility = View.GONE
|
binding.progressBarLoading.visibility = View.GONE
|
||||||
|
binding.uploadPhotoFab.visibility = View.VISIBLE
|
||||||
binding.textImageName.isEnabled = true
|
binding.textImageName.isEnabled = true
|
||||||
}
|
}
|
||||||
is Resource.Loading -> {
|
is Resource.Loading -> {
|
||||||
binding.progressBarLoading.visibility = View.VISIBLE
|
binding.progressBarLoading.visibility = View.VISIBLE
|
||||||
|
binding.uploadPhotoFab.visibility = View.INVISIBLE
|
||||||
binding.textImageName.isEnabled = false
|
binding.textImageName.isEnabled = false
|
||||||
}
|
}
|
||||||
is Resource.Success -> {
|
is Resource.Success -> {
|
||||||
binding.progressBarLoading.visibility = View.GONE
|
binding.progressBarLoading.visibility = View.GONE
|
||||||
|
binding.uploadPhotoFab.visibility = View.VISIBLE
|
||||||
setResult(Activity.RESULT_OK, Intent().putExtra(EXTRA_IMAGE, it.data))
|
setResult(Activity.RESULT_OK, Intent().putExtra(EXTRA_IMAGE, it.data))
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
|
|||||||
9
app/src/main/res/drawable/face_kiss_wink_heart_solid.xml
Normal file
9
app/src/main/res/drawable/face_kiss_wink_heart_solid.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="16dp"
|
||||||
|
android:height="16dp"
|
||||||
|
android:viewportWidth="512"
|
||||||
|
android:viewportHeight="512">
|
||||||
|
<path
|
||||||
|
android:pathData="M498,339.7c9.1,-26.2 14,-54.4 14,-83.7C512,114.6 397.4,0 256,0S0,114.6 0,256S114.6,512 256,512c35.4,0 69.1,-7.2 99.7,-20.2c-4.8,-5.5 -8.5,-12.2 -10.4,-19.7l-22.9,-89.3c-10,-39 11.8,-80.9 51.8,-92.1c37.2,-10.4 73.8,10.1 87.5,44c12.7,-1.6 25.1,0.4 36.2,5zM296,332c0,6.9 -3.1,13.2 -7.3,18.3c-4.3,5.2 -10.1,9.7 -16.7,13.4c-2.7,1.5 -5.7,3 -8.7,4.3c3.1,1.3 6,2.7 8.7,4.3c6.6,3.7 12.5,8.2 16.7,13.4c4.3,5.1 7.3,11.4 7.3,18.3s-3.1,13.2 -7.3,18.3c-4.3,5.2 -10.1,9.7 -16.7,13.4C258.7,443.1 241.4,448 224,448c-3.6,0 -6.8,-2.5 -7.7,-6s0.6,-7.2 3.8,-9l0,0 0,0 0,0 0,0 0.2,-0.1c0.2,-0.1 0.5,-0.3 0.9,-0.5c0.8,-0.5 2,-1.2 3.4,-2.1c2.8,-1.9 6.5,-4.5 10.2,-7.6c3.7,-3.1 7.2,-6.6 9.6,-10.1c2.5,-3.5 3.5,-6.4 3.5,-8.6s-1,-5 -3.5,-8.6c-2.5,-3.5 -5.9,-6.9 -9.6,-10.1c-3.7,-3.1 -7.4,-5.7 -10.2,-7.6c-1.4,-0.9 -2.6,-1.6 -3.4,-2.1l-0.6,-0.4 -0.3,-0.2 -0.2,-0.1 0,0 0,0 0,0c-2.5,-1.4 -4.1,-4.1 -4.1,-7s1.6,-5.6 4.1,-7l0,0 0,0 0,0 0,0 0,0 0.2,-0.1c0.2,-0.1 0.5,-0.3 0.9,-0.5c0.8,-0.5 2,-1.2 3.4,-2.1c2.8,-1.9 6.5,-4.5 10.2,-7.6c3.7,-3.1 7.2,-6.6 9.6,-10.1c2.5,-3.5 3.5,-6.4 3.5,-8.6s-1,-5 -3.5,-8.6c-2.5,-3.5 -5.9,-6.9 -9.6,-10.1c-3.7,-3.1 -7.4,-5.7 -10.2,-7.6c-1.4,-0.9 -2.6,-1.6 -3.4,-2.1c-0.4,-0.2 -0.7,-0.4 -0.9,-0.5l-0.2,-0.1 0,0 0,0 0,0c-3.2,-1.8 -4.7,-5.5 -3.8,-9s4.1,-6 7.7,-6c17.4,0 34.7,4.9 47.9,12.3c6.6,3.7 12.5,8.2 16.7,13.4c4.3,5.1 7.3,11.4 7.3,18.3zM176.4,176a32,32 0,1 1,0 64,32 32,0 1,1 0,-64zM371.2,233.6c-17.6,-23.5 -52.8,-23.5 -70.4,0c-5.3,7.1 -15.3,8.5 -22.4,3.2s-8.5,-15.3 -3.2,-22.4c30.4,-40.5 91.2,-40.5 121.6,0c5.3,7.1 3.9,17.1 -3.2,22.4s-17.1,3.9 -22.4,-3.2zM434,352.3c-6,-23.2 -28.8,-37 -51.1,-30.8s-35.4,30.1 -29.5,53.4l22.9,89.3c2.2,8.7 11.2,13.9 19.8,11.4l84.9,-23.8c22.2,-6.2 35.4,-30.1 29.5,-53.4s-28.8,-37 -51.1,-30.8l-20.2,5.6 -5.4,-21z"
|
||||||
|
android:fillColor="#1E3050"/>
|
||||||
|
</vector>
|
||||||
@ -43,4 +43,29 @@
|
|||||||
android:layout_margin="16dp"
|
android:layout_margin="16dp"
|
||||||
app:srcCompat="@drawable/baseline_add_24"
|
app:srcCompat="@drawable/baseline_add_24"
|
||||||
android:contentDescription="@string/upload_a_picture" />
|
android:contentDescription="@string/upload_a_picture" />
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/no_images_card"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:layout_marginHorizontal="24dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible">
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_margin="24dp">
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="30dp"
|
||||||
|
android:layout_height="30dp"
|
||||||
|
android:src="@drawable/face_kiss_wink_heart_solid"
|
||||||
|
android:layout_gravity="center_horizontal" />
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:text="No images to show, use the plus button to add a new Image"/>
|
||||||
|
</LinearLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
@ -124,4 +124,5 @@
|
|||||||
<string name="audio_options">Audio options</string>
|
<string name="audio_options">Audio options</string>
|
||||||
<string name="yes_discard_image">Yes, discard image</string>
|
<string name="yes_discard_image">Yes, discard image</string>
|
||||||
<string name="discard_image">Discard image?</string>
|
<string name="discard_image">Discard image?</string>
|
||||||
|
<string name="deleting_please_wait">Deleting, please wait…</string>
|
||||||
</resources>
|
</resources>
|
||||||
Loading…
x
Reference in New Issue
Block a user