WIP eliminar fotos
This commit is contained in:
parent
dacba723b0
commit
1cca08df0b
@ -0,0 +1,5 @@
|
||||
package com.isolaatti.images.image_list.data.remote
|
||||
|
||||
data class DeleteImagesDto(
|
||||
val imageIds: List<String>
|
||||
)
|
||||
@ -0,0 +1,6 @@
|
||||
package com.isolaatti.images.image_list.data.remote
|
||||
|
||||
data class DeleteImagesResultDto(
|
||||
val success: Boolean,
|
||||
val unSuccessIds: List<String>
|
||||
)
|
||||
@ -27,6 +27,9 @@ interface ImagesApi {
|
||||
@DELETE("images/{imageId}")
|
||||
fun deleteImage(@Path("imageId") imageId: String): Call<Any>
|
||||
|
||||
@DELETE("images/delete_many")
|
||||
fun deleteImages(@Body deleteImagesDto: DeleteImagesDto): Call<DeleteImagesResultDto>
|
||||
|
||||
@GET("images/of_squad/{squadId}")
|
||||
fun getImagesOfSquad(@Path("squadId") squadId: String,
|
||||
@Query("lastId") lastId: String?): Call<List<ImageDto>>
|
||||
|
||||
@ -26,4 +26,8 @@ class ImagesRepositoryImpl @Inject constructor(private val imagesApi: ImagesApi)
|
||||
emit(Resource.Error(Resource.Error.ErrorType.NetworkError))
|
||||
}
|
||||
}
|
||||
|
||||
override fun deleteImages(images: List<Image>): Flow<Resource<Boolean>> = flow {
|
||||
|
||||
}
|
||||
}
|
||||
@ -6,4 +6,5 @@ import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface ImagesRepository {
|
||||
fun getImagesOfUser(userId: Int, lastId: String? = null): Flow<Resource<List<Image>>>
|
||||
fun deleteImages(images: List<Image>): Flow<Resource<Boolean>>
|
||||
}
|
||||
@ -26,4 +26,8 @@ class ImageListViewModel @Inject constructor(private val imagesRepository: Image
|
||||
}.flowOn(Dispatchers.IO).launchIn(this)
|
||||
}
|
||||
}
|
||||
|
||||
fun removeImages(images: List<Image>) {
|
||||
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
package com.isolaatti.images.image_list.presentation
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.LinearLayout
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
@ -10,17 +11,36 @@ import com.isolaatti.common.CoilImageLoader.imageLoader
|
||||
import com.isolaatti.databinding.ImageItemBinding
|
||||
import com.isolaatti.images.image_list.domain.entity.Image
|
||||
|
||||
class ImagesAdapter(private val imageOnClick: ((images: List<Image>, position: Int) -> Unit), private val itemWidth: Int) : Adapter<ImagesAdapter.ImageViewHolder>(){
|
||||
class ImagesAdapter(
|
||||
private val imageOnClick: ((images: List<Image>, position: Int) -> Unit),
|
||||
private val itemWidth: Int,
|
||||
private val onImageSelectedCountUpdate: ((count: Int) -> Unit)? = null,
|
||||
private val onDeleteMode: ((enabled: Boolean) -> Unit)? = null) : Adapter<ImagesAdapter.ImageViewHolder>(){
|
||||
|
||||
private var data: List<Image> = listOf()
|
||||
private var selectionState: Array<Boolean> = arrayOf()
|
||||
|
||||
var deleteMode: Boolean = false
|
||||
set(value) {
|
||||
field = value
|
||||
if(!value) {
|
||||
selectionState.forEachIndexed { index, _ -> selectionState[index] = false }
|
||||
}
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
inner class ImageViewHolder(val imageItemBinding: ImageItemBinding) : RecyclerView.ViewHolder(imageItemBinding.root)
|
||||
|
||||
fun setData(data: List<Image>) {
|
||||
this.data = data
|
||||
selectionState = Array(data.size) { false }
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
fun getSelectedImages(): List<Image> {
|
||||
return data.filterIndexed { index, _ -> selectionState[index] }
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ImageViewHolder {
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
val binding = ImageItemBinding.inflate(inflater)
|
||||
@ -36,9 +56,35 @@ class ImagesAdapter(private val imageOnClick: ((images: List<Image>, position: I
|
||||
override fun onBindViewHolder(holder: ImageViewHolder, position: Int) {
|
||||
val image = data[position]
|
||||
|
||||
holder.imageItemBinding.image.load(image.smallImageUrl, imageLoader)
|
||||
holder.imageItemBinding.root.setOnClickListener {
|
||||
imageOnClick(data, position)
|
||||
holder.imageItemBinding.image.load(image.reducedImageUrl, imageLoader)
|
||||
|
||||
if(deleteMode) {
|
||||
holder.imageItemBinding.imageCheckbox.visibility = View.VISIBLE
|
||||
holder.imageItemBinding.imageOverlay.visibility = View.VISIBLE
|
||||
holder.imageItemBinding.root.setOnClickListener {
|
||||
holder.imageItemBinding.imageCheckbox.isChecked = !holder.imageItemBinding.imageCheckbox.isChecked
|
||||
}
|
||||
holder.imageItemBinding.imageCheckbox.isChecked = selectionState[position]
|
||||
holder.imageItemBinding.imageCheckbox.setOnCheckedChangeListener { buttonView, isChecked ->
|
||||
selectionState[position] = isChecked
|
||||
|
||||
onImageSelectedCountUpdate?.invoke(selectionState.count { it })
|
||||
}
|
||||
holder.imageItemBinding.root.setOnLongClickListener(null)
|
||||
} else {
|
||||
holder.imageItemBinding.imageCheckbox.visibility = View.GONE
|
||||
holder.imageItemBinding.imageCheckbox.isChecked = false
|
||||
holder.imageItemBinding.imageOverlay.visibility = View.GONE
|
||||
holder.imageItemBinding.root.setOnClickListener {
|
||||
imageOnClick(data, position)
|
||||
}
|
||||
holder.imageItemBinding.imageCheckbox.setOnCheckedChangeListener(null)
|
||||
holder.imageItemBinding.root.setOnLongClickListener {
|
||||
selectionState[position] = true
|
||||
onDeleteMode?.invoke(true)
|
||||
onImageSelectedCountUpdate?.invoke(selectionState.count { it })
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3,7 +3,10 @@ package com.isolaatti.images.image_list.ui
|
||||
import android.content.res.Resources
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.ActionMode
|
||||
import android.view.LayoutInflater
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.activity.result.PickVisualMediaRequest
|
||||
@ -15,6 +18,7 @@ import androidx.fragment.app.viewModels
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.isolaatti.MyApplication
|
||||
import com.isolaatti.R
|
||||
import com.isolaatti.databinding.FragmentImagesBinding
|
||||
@ -88,10 +92,60 @@ class ImagesFragment : Fragment() {
|
||||
viewBinding.topAppBar.inflateMenu(R.menu.images_menu)
|
||||
}
|
||||
|
||||
private fun showDeleteDialog() {
|
||||
val imagesToDelete = adapter.getSelectedImages()
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.delete)
|
||||
.setMessage(getString(R.string.delete_images_dialog_message, imagesToDelete.size))
|
||||
.setPositiveButton(R.string.yes_continue) { _, _ ->
|
||||
viewModel.removeImages(imagesToDelete)
|
||||
}
|
||||
.setNegativeButton(R.string.no, null)
|
||||
.show()
|
||||
}
|
||||
|
||||
private var actionMode: ActionMode? = null
|
||||
|
||||
private val contextBarCallback: ActionMode.Callback = object: ActionMode.Callback {
|
||||
override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {
|
||||
requireActivity().menuInflater.inflate(R.menu.images_context_menu, menu)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onActionItemClicked(mode: ActionMode?, item: MenuItem?): Boolean {
|
||||
return when(item?.itemId) {
|
||||
R.id.delete_item -> {
|
||||
showDeleteDialog()
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyActionMode(mode: ActionMode?) {
|
||||
adapter.deleteMode = false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun setupListeners() {
|
||||
viewBinding.topAppBar.setNavigationOnClickListener {
|
||||
findNavController().popBackStack()
|
||||
}
|
||||
viewBinding.topAppBar.setOnMenuItemClickListener {
|
||||
when(it.itemId) {
|
||||
R.id.delete_mode_item -> {
|
||||
adapter.deleteMode = true
|
||||
actionMode = requireActivity().startActionMode(contextBarCallback)
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
viewBinding.newPictureButton.setOnClickListener {
|
||||
val popup = PopupMenu(requireContext(), it)
|
||||
popup.menuInflater.inflate(R.menu.add_picture_menu, popup.menu)
|
||||
@ -116,7 +170,17 @@ class ImagesFragment : Fragment() {
|
||||
}
|
||||
|
||||
private fun setupAdapter() {
|
||||
adapter = ImagesAdapter(imageOnClick, Resources.getSystem().displayMetrics.widthPixels/3)
|
||||
adapter = ImagesAdapter(
|
||||
imageOnClick = imageOnClick,
|
||||
itemWidth = Resources.getSystem().displayMetrics.widthPixels/3,
|
||||
onImageSelectedCountUpdate = {
|
||||
actionMode?.title = getString(R.string.selected_images_count, it)
|
||||
actionMode?.menu?.findItem(R.id.delete_item)?.isEnabled = it > 0
|
||||
},
|
||||
onDeleteMode = {
|
||||
adapter.deleteMode = it
|
||||
actionMode = requireActivity().startActionMode(contextBarCallback)
|
||||
})
|
||||
viewBinding.recyclerView.layoutManager =
|
||||
GridLayoutManager(requireContext(), 3, GridLayoutManager.VERTICAL, false)
|
||||
viewBinding.recyclerView.adapter = adapter
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
<vector android:height="24dp" android:tint="#000000"
|
||||
<vector android:height="24dp" android:tint="@color/on_surface"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
|
||||
|
||||
111
app/src/main/res/layout-sw600dp/activity_login.xml
Normal file
111
app/src/main/res/layout-sw600dp/activity_login.xml
Normal file
@ -0,0 +1,111 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
style="?android:attr/progressBarStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:translationZ="10dp"
|
||||
android:visibility="gone" />
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:layout_width="400dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center">
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="24dp"
|
||||
android:text="@string/app_name"
|
||||
android:textAlignment="center"
|
||||
style="@style/toolbar_text" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?attr/textAppearanceHeadlineSmall"
|
||||
android:layout_margin="24dp"
|
||||
android:text="@string/sign_in"/>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/textFieldEmail"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:layout_marginVertical="8dp"
|
||||
android:hint="@string/email">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textEmailAddress"
|
||||
/>
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/textFieldPassword"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:layout_marginVertical="8dp"
|
||||
app:endIconMode="password_toggle"
|
||||
android:hint="@string/password">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textPassword"
|
||||
/>
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/sign_in_btn"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/sign_in"
|
||||
android:layout_margin="8dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/forgot_password_btn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="32dp"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:text="@string/forgot_password"
|
||||
style="@style/Widget.Material3.Button.TextButton"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/or_you_can_sign_up"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginTop="24dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/sign_up_btn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginTop="24dp"
|
||||
android:text="@string/sign_up"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
</ScrollView>
|
||||
</FrameLayout>
|
||||
@ -4,6 +4,7 @@
|
||||
android:layout_height="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/appBarLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
|
||||
@ -1,10 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<ImageView
|
||||
android:id="@+id/image"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
android:layout_height="match_parent"
|
||||
android:src="@drawable/baseline_image_24"/>
|
||||
<CheckBox
|
||||
android:id="@+id/image_checkbox"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"/>
|
||||
<FrameLayout
|
||||
android:id="@+id/image_overlay"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="gone"
|
||||
android:background="@color/translucent_purple"
|
||||
tools:visibility="visible"/>
|
||||
|
||||
</LinearLayout>
|
||||
</RelativeLayout>
|
||||
12
app/src/main/res/menu/images_context_menu.xml
Normal file
12
app/src/main/res/menu/images_context_menu.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/delete_item"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:showAsAction="always"
|
||||
android:icon="@drawable/baseline_delete_24"
|
||||
android:title="@string/delete" />
|
||||
</menu>
|
||||
@ -3,7 +3,7 @@
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/delete_item"
|
||||
android:id="@+id/delete_mode_item"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:icon="@drawable/baseline_delete_24"
|
||||
|
||||
@ -5,6 +5,8 @@
|
||||
<item name="android:colorPrimary">@color/purple</item>
|
||||
<item name="android:statusBarColor">@color/purple</item>
|
||||
<item name="colorOnSurface">@color/on_surface</item>
|
||||
|
||||
<item name="windowActionModeOverlay">true</item>
|
||||
<item name="actionModeCloseDrawable">@drawable/baseline_close_24</item>
|
||||
<item name="actionBarTheme">@style/ThemeOverlay.Material3.Dark.ActionBar</item>
|
||||
</style>
|
||||
</resources>
|
||||
@ -7,4 +7,5 @@
|
||||
<color name="danger">#BA0606</color>
|
||||
<color name="black">#000000</color>
|
||||
<color name="translucent_black">#9A000000</color>
|
||||
<color name="translucent_purple">#704D3B68</color>
|
||||
</resources>
|
||||
@ -118,4 +118,6 @@
|
||||
<string name="upload_a_picture">Upload a picture</string>
|
||||
<string name="take_a_photo">Take a photo</string>
|
||||
<string name="upload_photo">Upload picture</string>
|
||||
<string name="delete_images_dialog_message">Remove %d images?</string>
|
||||
<string name="selected_images_count">Images selected: %d</string>
|
||||
</resources>
|
||||
@ -5,6 +5,9 @@
|
||||
<item name="colorSurface">@color/surface</item>
|
||||
<item name="colorOnSurface">@color/on_surface</item>
|
||||
<item name="android:statusBarColor">@color/purple</item>
|
||||
<item name="windowActionModeOverlay">true</item>
|
||||
<item name="actionModeCloseDrawable">@drawable/baseline_close_24</item>
|
||||
<item name="actionBarTheme">@style/ThemeOverlay.Material3.Dark.ActionBar</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.Isolaatti.Splash" parent="Theme.SplashScreen"/>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user