delete video notament

This commit is contained in:
Raphael
2025-03-13 12:25:56 +01:00
parent a373516f9c
commit 5da4c40bd8
7 changed files with 101 additions and 25 deletions

View File

@@ -89,7 +89,7 @@ class ProjectActivity : AppCompatActivity() {
fun fetchVideos(){ fun fetchVideos(){
this.project?.let{ this.project?.let{
videoRepository.fetchVideosOfProject(it.id, onSuccess = { videos -> videoRepository.fetchVideosOfProject(it.id, onSuccess = { videos ->
val adapter = VideoAdapter(false, videos, null) //ImageAdapter(imageUrls) val adapter = VideoAdapter(false, videos.toMutableList(), null) //ImageAdapter(imageUrls)
binding.videosList.layoutManager = GridLayoutManager(this.baseContext, 2, GridLayoutManager.HORIZONTAL, false) binding.videosList.layoutManager = GridLayoutManager(this.baseContext, 2, GridLayoutManager.HORIZONTAL, false)
binding.videosList.addItemDecoration(GridSpacingItemDecoration(16)) // 16px spacing binding.videosList.addItemDecoration(GridSpacingItemDecoration(16)) // 16px spacing
binding.videosList.adapter = adapter binding.videosList.adapter = adapter

View File

@@ -46,10 +46,11 @@ import java.io.FileOutputStream
class VideoAdapter( class VideoAdapter(
public val withProjectNames : Boolean, public val withProjectNames : Boolean,
private val videos: List<Video>, private val videos: MutableList<Video>,
private val listener: OnEmptyStateListener? private val listener: OnEmptyStateListener?
) : RecyclerView.Adapter<VideoAdapter.VideoViewHolder>() { ) : RecyclerView.Adapter<VideoAdapter.VideoViewHolder>() {
private var projectRepository: ProjectRepository; private var projectRepository: ProjectRepository;
private var videoRepository : VideoRepository;
init { init {
val retrofit = Retrofit.Builder() val retrofit = Retrofit.Builder()
.baseUrl("https://timelapse.kerboul.me/api/") .baseUrl("https://timelapse.kerboul.me/api/")
@@ -57,6 +58,7 @@ class VideoAdapter(
.build() .build()
projectRepository = ProjectRepository(retrofit.create(ApiService::class.java)) projectRepository = ProjectRepository(retrofit.create(ApiService::class.java))
videoRepository = VideoRepository(retrofit.create(ApiService::class.java))
listener?.onEmptyStateChanged(videos.isEmpty()) listener?.onEmptyStateChanged(videos.isEmpty())
} }
@@ -105,12 +107,12 @@ class VideoAdapter(
context.startActivity(intent) context.startActivity(intent)
} }
holder.itemView.setOnLongClickListener { holder.itemView.setOnLongClickListener {
showPopupMenu(it, url) showPopupMenu(it, url, video.id)
true true
} }
} }
private fun showPopupMenu(view: View, videoUrl: String) { private fun showPopupMenu(view: View, videoUrl: String, id:Int) {
val popupMenu = PopupMenu(view.context, view) val popupMenu = PopupMenu(view.context, view)
popupMenu.menuInflater.inflate(R.menu.video_popup_menu, popupMenu.menu) popupMenu.menuInflater.inflate(R.menu.video_popup_menu, popupMenu.menu)
@@ -120,6 +122,22 @@ class VideoAdapter(
downloadVideo(view.context, videoUrl) downloadVideo(view.context, videoUrl)
true true
} }
R.id.menu_delete -> {
videoRepository.deleteVideo(id, onSuccess = {
val v = videos.find {v -> v.id == id}
val i = videos.indexOf(v)
this.videos.removeAt(i)
Handler(Looper.getMainLooper()).post {
Toast.makeText(view.context, "Video supprimée", Toast.LENGTH_SHORT).show()
}
notifyItemRemoved(i)
}, onError = {s ->
Handler(Looper.getMainLooper()).post {
Toast.makeText(view.context, "Erreur lors de la suppression : $s", Toast.LENGTH_SHORT).show()
}
});
true
}
else -> false else -> false
} }
} }

View File

@@ -2,16 +2,13 @@ package com.dreamteam.timelapse
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import android.util.TypedValue
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
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 androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.dreamteam.timelapse.data.VideoRepository import com.dreamteam.timelapse.data.VideoRepository
import com.dreamteam.timelapse.databinding.FragmentProjetsBinding
import com.dreamteam.timelapse.data.ApiService import com.dreamteam.timelapse.data.ApiService
import com.dreamteam.timelapse.data.Video import com.dreamteam.timelapse.data.Video
import com.dreamteam.timelapse.databinding.FragmentVideosBinding import com.dreamteam.timelapse.databinding.FragmentVideosBinding
@@ -84,11 +81,11 @@ class VideoFrag : Fragment(), VideoAdapter.OnEmptyStateListener {
//binding.swipeRefreshLayout.dragDown() //binding.swipeRefreshLayout.dragDown()
Log.d("ProjetsFrag", "User has refreshed the videos list") Log.d("ProjetsFrag", "User has refreshed the videos list")
videoRepository.fetchVideos( videoRepository.fetchRenderedVideos(
onSuccess = { videos -> onSuccess = { videos ->
Log.d("ProjetsFrag", "Projets reçus : $videos") Log.d("ProjetsFrag", "Projets reçus : $videos")
this.videos = videos this.videos = videos
this.videoAdapter = VideoAdapter(true, this.videos, this) //TODO : Potentiel problème pour les refresh ici (update de l'adapter ou un truc du genre à voir) this.videoAdapter = VideoAdapter(true, this.videos.toMutableList(), this) //TODO : Potentiel problème pour les refresh ici (update de l'adapter ou un truc du genre à voir)
this.recyclerView.adapter = this.videoAdapter this.recyclerView.adapter = this.videoAdapter
// Mettre à jour le RecyclerView ou autre traitement // Mettre à jour le RecyclerView ou autre traitement
}, },
@@ -96,7 +93,7 @@ class VideoFrag : Fragment(), VideoAdapter.OnEmptyStateListener {
Log.e("ProjetsFrag prout", errorMessage) Log.e("ProjetsFrag prout", errorMessage)
//onEmptyStateChanged(true) //onEmptyStateChanged(true)
this.videos = listOf<Video>() this.videos = listOf<Video>()
this.videoAdapter = VideoAdapter(true, this.videos, this) //TODO : Potentiel problème pour les refresh ici (update de l'adapter ou un truc du genre à voir) this.videoAdapter = VideoAdapter(true, this.videos.toMutableList(), this) //TODO : Potentiel problème pour les refresh ici (update de l'adapter ou un truc du genre à voir)
this.recyclerView.adapter = this.videoAdapter this.recyclerView.adapter = this.videoAdapter
} }

View File

@@ -17,6 +17,8 @@ interface ApiService {
fun getMeasurements(@Path("id") projectId: Int): Call<List<Measurement>> fun getMeasurements(@Path("id") projectId: Int): Call<List<Measurement>>
@GET("videos") @GET("videos")
fun getVideos(): Call<List<VideoDTO>> fun getVideos(): Call<List<VideoDTO>>
@DELETE("videos/{id}")
fun deleteVideo(@Path("id") videoId : Int): Call<Void>
@POST("projects/") @POST("projects/")
fun createProject(@Body project: Project): Call<Confirmation> fun createProject(@Body project: Project): Call<Confirmation>
@GET("projects/{id}/videos") @GET("projects/{id}/videos")

View File

@@ -3,16 +3,32 @@ package com.dreamteam.timelapse.data
import retrofit2.Call import retrofit2.Call
import retrofit2.Callback import retrofit2.Callback
import retrofit2.Response import retrofit2.Response
import java.util.Date
class VideoRepository(private val apiService: ApiService) { class VideoRepository(private val apiService: ApiService) {
fun fetchVideos(onSuccess: (List<Video>) -> Unit, onError: (String) -> Unit) { fun fetchRenderedVideos(onSuccess: (List<Video>) -> Unit, onError: (String) -> Unit) {
apiService.getVideos().enqueue(object : Callback<List<VideoDTO>> { apiService.getVideos().enqueue(object : Callback<List<VideoDTO>> {
override fun onResponse(call: Call<List<VideoDTO>>, response: Response<List<VideoDTO>>) { override fun onResponse(call: Call<List<VideoDTO>>, response: Response<List<VideoDTO>>) {
if (response.isSuccessful) { if (response.isSuccessful) {
val videoDtos = response.body() ?: emptyList() val videoDtos = response.body() ?: emptyList()
val videos = videoDtos.map { it.toVideo() } // Convert ugly data to clean Video objects val videos = videoDtos.map { it.toVideo() } // Convert ugly data to clean Video objects
onSuccess(videos) onSuccess(videos.filter { v -> v.video_file != null })
} else {
onError("Erreur serveur : ${response.code()}")
}
}
override fun onFailure(call: Call<List<VideoDTO>>, t: Throwable) {
onError("Erreur réseau : ${t.message}")
}
})
}
fun fetchUnrenderedVideos(onSuccess: (List<Video>) -> Unit, onError: (String) -> Unit) {
apiService.getVideos().enqueue(object : Callback<List<VideoDTO>> {
override fun onResponse(call: Call<List<VideoDTO>>, response: Response<List<VideoDTO>>) {
if (response.isSuccessful) {
val videoDtos = response.body() ?: emptyList()
val videos = videoDtos.map { it.toVideo() } // Convert ugly data to clean Video objects
onSuccess(videos.filter { v -> v.video_file == null })
} else { } else {
onError("Erreur serveur : ${response.code()}") onError("Erreur serveur : ${response.code()}")
} }
@@ -40,4 +56,36 @@ class VideoRepository(private val apiService: ApiService) {
} }
}) })
} }
} fun fetchUnrenderedVideosOfProject(id:Int, onSuccess: (List<Video>) -> Unit, onError: (String) -> Unit){
apiService.getVideosOfProject(id).enqueue(object : Callback<List<VideoDTO>> {
override fun onResponse(call: Call<List<VideoDTO>>, response: Response<List<VideoDTO>>) {
if (response.isSuccessful) {
val videoDtos = response.body() ?: emptyList()
val videos = videoDtos.map { it.toVideo() } // Convert ugly data to clean Video objects
onSuccess(videos.filter { v -> v.video_file == null })
} else {
onError("Erreur serveur : ${response.code()}")
}
}
override fun onFailure(call: Call<List<VideoDTO>>, t: Throwable) {
onError("Erreur réseau : ${t.message}")
}
})
}
fun deleteVideo(id:Int, onSuccess: () -> Unit, onError: (String) -> Unit){
apiService.deleteVideo(id).enqueue(object : Callback<Void> {
override fun onResponse(call: Call<Void>, response: Response<Void>) {
if (response.isSuccessful) {
onSuccess()
} else {
onError("Erreur serveur : ${response.code()}")
}
}
override fun onFailure(call: Call<Void>, t: Throwable) {
onError("Erreur réseau : ${t.message}")
}
})
}}

View File

@@ -18,18 +18,25 @@
tools:ignore="MissingConstraints" tools:ignore="MissingConstraints"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
> >
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@+id/collapsingToolbar"
android:layout_width="match_parent"
android:layout_height="100sp"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
app:expandedTitleTextAppearance="@style/TextAppearance.AppCompat.Large"
app:collapsedTitleTextAppearance="@style/TextAppearance.AppCompat.Small">
<TextView <TextView
android:id="@+id/project_name" android:id="@+id/project_name"
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="match_parent"
android:layout_marginTop="16dp" android:gravity="center"
android:text="Nom du projet" android:padding="10dp"
android:textSize="20sp" android:text="Nom du projet"
android:textStyle="bold" android:textSize="20sp"
app:layout_constraintEnd_toEndOf="parent" android:textStyle="bold" />
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> </com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>

View File

@@ -6,4 +6,8 @@
android:id="@+id/menu_download" android:id="@+id/menu_download"
android:title="Télécharger" android:title="Télécharger"
android:icon="@android:drawable/ic_menu_save" /> android:icon="@android:drawable/ic_menu_save" />
<item
android:id="@+id/menu_delete"
android:title="Supprimer"
android:icon="@android:drawable/ic_delete" />
</menu> </menu>