@@ -71,9 +71,10 @@ dependencies {
|
||||
implementation("com.squareup.retrofit2:retrofit:2.9.0") //internet, api etc...
|
||||
implementation("com.squareup.retrofit2:converter-gson:2.9.0") // Si tu veux utiliser Gson pour la sérialisation
|
||||
implementation("com.squareup.okhttp3:logging-interceptor:4.9.0") // Pour le logging
|
||||
implementation("com.github.bumptech.glide:glide:4.15.1") // Pour curl des images d'internet en gros
|
||||
implementation("com.github.bumptech.glide:glide:4.15.1")
|
||||
kapt("com.github.bumptech.glide:compiler:4.15.1")
|
||||
implementation("com.github.PhilJay:MPAndroidChart:v3.1.0")
|
||||
implementation("com.google.android.exoplayer:exoplayer:2.18.1")
|
||||
testImplementation(libs.junit)
|
||||
androidTestImplementation(libs.androidx.junit)
|
||||
androidTestImplementation(libs.androidx.espresso.core)
|
||||
|
||||
@@ -21,10 +21,12 @@
|
||||
android:theme="@style/Theme.Timelapse">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name=".VideoPlayerActivity" android:exported="false" android:label="@string/app_name" android:theme="@style/Theme.Timelapse">
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".ProjectActivity"
|
||||
android:exported="true"
|
||||
|
||||
@@ -6,12 +6,14 @@ import android.util.Log
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.StaggeredGridLayoutManager
|
||||
import com.dreamteam.timelapse.data.ApiService
|
||||
import com.dreamteam.timelapse.data.Measurement
|
||||
import com.dreamteam.timelapse.data.Project
|
||||
import com.dreamteam.timelapse.data.ProjectRepository
|
||||
import com.dreamteam.timelapse.data.VideoRepository
|
||||
import com.dreamteam.timelapse.databinding.ProjectBinding
|
||||
import com.github.mikephil.charting.data.Entry
|
||||
import com.github.mikephil.charting.data.LineData
|
||||
@@ -29,6 +31,7 @@ class ProjectActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var binding: ProjectBinding
|
||||
private lateinit var projectRepository: ProjectRepository
|
||||
private lateinit var videoRepository: VideoRepository
|
||||
private lateinit var apiService: ApiService // Déclare l'apiService
|
||||
private var measures: List<Measurement> = emptyList() // Déclare une liste de projets vide
|
||||
private var project: Project? = null
|
||||
@@ -50,7 +53,8 @@ class ProjectActivity : AppCompatActivity() {
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.build()
|
||||
apiService = retrofit.create(ApiService::class.java) // Crée une instance d'ApiService
|
||||
projectRepository = ProjectRepository(apiService) // Tu initialises ton repository
|
||||
projectRepository = ProjectRepository(apiService)
|
||||
videoRepository = VideoRepository(apiService)
|
||||
|
||||
this.project = intent.getParcelableExtra<Project>("PROJECT") // -1 est la valeur par défaut si l'ID n'est pas trouvé
|
||||
Log.d("project", "La project activity "+this.project?.id+" est créée")
|
||||
@@ -67,6 +71,7 @@ class ProjectActivity : AppCompatActivity() {
|
||||
fetchMeasuresAndRebuildGraph()
|
||||
Log.d("ProjetsFrag", "Actualisation des projets")
|
||||
}
|
||||
fetchVideos()
|
||||
|
||||
|
||||
|
||||
@@ -80,7 +85,19 @@ class ProjectActivity : AppCompatActivity() {
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
fun fetchVideos(){
|
||||
this.project?.let{
|
||||
videoRepository.fetchVideosOfProject(it.id, onSuccess = { videos ->
|
||||
val adapter = VideoAdapter(false, videos, null) //ImageAdapter(imageUrls)
|
||||
binding.videosList.layoutManager = GridLayoutManager(this.baseContext, 2, GridLayoutManager.HORIZONTAL, false)
|
||||
binding.videosList.addItemDecoration(GridSpacingItemDecoration(16)) // 16px spacing
|
||||
binding.videosList.adapter = adapter
|
||||
}, onError = {errorMessage ->
|
||||
Log.e("ProjectActivity", errorMessage)
|
||||
fetchVideos()
|
||||
})
|
||||
}
|
||||
}
|
||||
fun fetchMeasuresAndRebuildGraph(){
|
||||
this.project?.let{
|
||||
projectRepository.fetchMeasurementsOfProject(it.id,
|
||||
@@ -91,7 +108,7 @@ class ProjectActivity : AppCompatActivity() {
|
||||
initGraph(this.measures)
|
||||
|
||||
val adapter = ImageAdapter(this.measures.map { m-> "https://timelapse.kerboul.me/api/images/${m.project_id}/${m.order_id}"}) //ImageAdapter(imageUrls)
|
||||
binding.imagesList.layoutManager = StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.HORIZONTAL)
|
||||
binding.imagesList.layoutManager = GridLayoutManager(this.baseContext, 2, GridLayoutManager.HORIZONTAL, false)
|
||||
binding.imagesList.addItemDecoration(GridSpacingItemDecoration(16)) // 16px spacing
|
||||
binding.imagesList.adapter = adapter
|
||||
|
||||
|
||||
86
app/src/main/java/com/dreamteam/timelapse/VideoAdapter.kt
Normal file
86
app/src/main/java/com/dreamteam/timelapse/VideoAdapter.kt
Normal file
@@ -0,0 +1,86 @@
|
||||
package com.dreamteam.timelapse
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.graphics.drawable.DrawableCompat
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.bumptech.glide.Glide
|
||||
import com.dreamteam.timelapse.data.ApiService
|
||||
import com.dreamteam.timelapse.data.ProjectRepository
|
||||
import com.dreamteam.timelapse.data.Video
|
||||
import com.dreamteam.timelapse.data.VideoRepository
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.gson.GsonConverterFactory
|
||||
|
||||
class VideoAdapter(
|
||||
public val withProjectNames : Boolean,
|
||||
private val videos: List<Video>,
|
||||
private val listener: OnEmptyStateListener?
|
||||
) : RecyclerView.Adapter<VideoAdapter.VideoViewHolder>() {
|
||||
private var projectRepository: ProjectRepository;
|
||||
init {
|
||||
val retrofit = Retrofit.Builder()
|
||||
.baseUrl("https://timelapse.kerboul.me/api/")
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.build()
|
||||
|
||||
projectRepository = ProjectRepository(retrofit.create(ApiService::class.java))
|
||||
listener?.onEmptyStateChanged(videos.isEmpty())
|
||||
}
|
||||
|
||||
interface OnEmptyStateListener {
|
||||
fun onEmptyStateChanged(isEmpty: Boolean)
|
||||
}
|
||||
|
||||
class VideoViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
val videoTitle: TextView = itemView.findViewById(R.id.videoTitle)
|
||||
val videoThumbnail: ImageView = itemView.findViewById(R.id.videoThumbnail)
|
||||
val playButton: ImageView = itemView.findViewById(R.id.playButton)
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VideoViewHolder {
|
||||
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_video, parent, false)
|
||||
return VideoViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: VideoViewHolder, position: Int) {
|
||||
val video = videos[position]
|
||||
val context = holder.itemView.context
|
||||
holder.videoTitle.text = video.name
|
||||
if(withProjectNames){
|
||||
projectRepository.fetchProject(video.project_id, { p->
|
||||
holder.videoTitle.text = video.name + " (" + p.name + ")"
|
||||
}, { s->
|
||||
Log.e("VideoAdapter", s)
|
||||
})
|
||||
}
|
||||
|
||||
// Load video thumbnail
|
||||
val url = "https://timelapse.kerboul.me/api/videos/file/${video.id}"
|
||||
Glide.with(context)
|
||||
.asBitmap()
|
||||
.load(url) // Load the video URL
|
||||
.frame(0)
|
||||
.placeholder(R.drawable.not_found) // Optional placeholder
|
||||
.into(holder.videoThumbnail)
|
||||
// Click listener to open video player
|
||||
holder.itemView.setOnClickListener {
|
||||
val intent = Intent(context, VideoPlayerActivity::class.java)
|
||||
intent.putExtra("VIDEO_URL", url) // Pass video URL
|
||||
intent.setFlags(FLAG_ACTIVITY_NEW_TASK)
|
||||
context.startActivity(intent)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return videos.size
|
||||
}
|
||||
}
|
||||
@@ -9,10 +9,11 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.dreamteam.timelapse.data.ProjectRepository
|
||||
import com.dreamteam.timelapse.data.VideoRepository
|
||||
import com.dreamteam.timelapse.databinding.FragmentProjetsBinding
|
||||
import com.dreamteam.timelapse.data.ApiService
|
||||
import com.dreamteam.timelapse.data.Project
|
||||
import com.dreamteam.timelapse.data.Video
|
||||
import com.dreamteam.timelapse.databinding.FragmentVideosBinding
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.gson.GsonConverterFactory
|
||||
|
||||
@@ -20,17 +21,17 @@ import retrofit2.converter.gson.GsonConverterFactory
|
||||
* A simple [Fragment] subclass as the default destination in the navigation.
|
||||
*/
|
||||
|
||||
class VideoFrag : Fragment(), ProjectAdapter.OnEmptyStateListener {
|
||||
class VideoFrag : Fragment(), VideoAdapter.OnEmptyStateListener {
|
||||
|
||||
private lateinit var projectRepository: ProjectRepository
|
||||
private lateinit var videoRepository: VideoRepository
|
||||
//private lateinit var listView: ListView
|
||||
private lateinit var recyclerView: RecyclerView
|
||||
private lateinit var projectAdapter: ProjectAdapter
|
||||
private var _binding: FragmentProjetsBinding? = null
|
||||
private lateinit var videoAdapter: VideoAdapter
|
||||
private var _binding: FragmentVideosBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
private lateinit var apiService: ApiService // Déclare l'apiService
|
||||
private var projects: List<Project> = emptyList() // Déclare une liste de projets vide
|
||||
private var videos: List<Video> = emptyList() // Déclare une liste de projets vide
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
@@ -47,11 +48,11 @@ class VideoFrag : Fragment(), ProjectAdapter.OnEmptyStateListener {
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
_binding = FragmentProjetsBinding.inflate(inflater, container, false)
|
||||
_binding = FragmentVideosBinding.inflate(inflater, container, false)
|
||||
Log.d("ProjetsFrag", "Fragment Projets créé")
|
||||
|
||||
projectRepository = ProjectRepository(apiService) // Tu initialises ton repository
|
||||
//fetchProjects()
|
||||
videoRepository = VideoRepository(apiService) // Tu initialises ton repository
|
||||
//fetchVideos()
|
||||
|
||||
return _binding?.root
|
||||
}
|
||||
@@ -62,10 +63,10 @@ class VideoFrag : Fragment(), ProjectAdapter.OnEmptyStateListener {
|
||||
val swipeRefreshLayout = binding.swipeRefreshLayout
|
||||
|
||||
swipeRefreshLayout.setOnRefreshListener {
|
||||
fetchProjects()
|
||||
fetchVideos()
|
||||
Log.d("ProjetsFrag", "Actualisation des projets")
|
||||
}
|
||||
fetchProjects()
|
||||
fetchVideos()
|
||||
|
||||
recyclerView = binding.recyclerView // view.findViewById(R.id.recyclerView)
|
||||
recyclerView.layoutManager = LinearLayoutManager(context)
|
||||
@@ -73,28 +74,28 @@ class VideoFrag : Fragment(), ProjectAdapter.OnEmptyStateListener {
|
||||
|
||||
}
|
||||
|
||||
private fun fetchProjects() {
|
||||
private fun fetchVideos() {
|
||||
// Simulez l'appel API
|
||||
binding.swipeRefreshLayout.setEnabled(true)
|
||||
binding.swipeRefreshLayout.setRefreshing(true)
|
||||
//binding.swipeRefreshLayout.
|
||||
|
||||
//binding.swipeRefreshLayout.dragDown()
|
||||
Log.d("ProjetsFrag", "User has refreshed the projects list")
|
||||
projectRepository.fetchProjects(
|
||||
onSuccess = { projects ->
|
||||
Log.d("ProjetsFrag", "Projets reçus : $projects")
|
||||
this.projects = projects
|
||||
this.projectAdapter = ProjectAdapter(this.projects, this) //TODO : Potentiel problème pour les refresh ici (update de l'adapter ou un truc du genre à voir)
|
||||
this.recyclerView.adapter = this.projectAdapter
|
||||
Log.d("ProjetsFrag", "User has refreshed the videos list")
|
||||
videoRepository.fetchVideos(
|
||||
onSuccess = { videos ->
|
||||
Log.d("ProjetsFrag", "Projets reçus : $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.recyclerView.adapter = this.videoAdapter
|
||||
// Mettre à jour le RecyclerView ou autre traitement
|
||||
},
|
||||
onError = { errorMessage ->
|
||||
Log.e("ProjetsFrag prout", errorMessage)
|
||||
//onEmptyStateChanged(true)
|
||||
this.projects = listOf<Project>()
|
||||
this.projectAdapter = ProjectAdapter(this.projects, this) //TODO : Potentiel problème pour les refresh ici (update de l'adapter ou un truc du genre à voir)
|
||||
this.recyclerView.adapter = this.projectAdapter
|
||||
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.recyclerView.adapter = this.videoAdapter
|
||||
|
||||
}
|
||||
)
|
||||
@@ -103,9 +104,9 @@ class VideoFrag : Fragment(), ProjectAdapter.OnEmptyStateListener {
|
||||
override fun onEmptyStateChanged(isEmpty: Boolean) {
|
||||
// Afficher ou masquer le message
|
||||
if (isEmpty) {
|
||||
binding.noProjectsText.visibility = View.VISIBLE
|
||||
binding.noVideosText.visibility = View.VISIBLE
|
||||
} else {
|
||||
binding.noProjectsText.visibility = View.GONE
|
||||
binding.noVideosText.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.dreamteam.timelapse
|
||||
|
||||
import android.media.MediaPlayer
|
||||
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.google.android.exoplayer2.ExoPlayer
|
||||
import com.google.android.exoplayer2.MediaItem
|
||||
import com.google.android.exoplayer2.ui.PlayerView
|
||||
class VideoPlayerActivity : AppCompatActivity() {
|
||||
private lateinit var player: ExoPlayer
|
||||
private lateinit var playerView: PlayerView
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_video_player)
|
||||
|
||||
// Get reference to the PlayerView
|
||||
playerView = findViewById(R.id.playerView)
|
||||
|
||||
// Initialize the player
|
||||
player = ExoPlayer.Builder(this).build()
|
||||
|
||||
// Get video URL from intent
|
||||
val videoUrl = intent.getStringExtra("VIDEO_URL")
|
||||
if (videoUrl.isNullOrEmpty()) {
|
||||
Toast.makeText(this, "Invalid video URL", Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
|
||||
// Create a MediaItem from the URL
|
||||
val mediaItem = MediaItem.fromUri(videoUrl)
|
||||
|
||||
// Set the media item to the player
|
||||
player.setMediaItem(mediaItem)
|
||||
|
||||
// Prepare and start playback
|
||||
player.prepare()
|
||||
player.play()
|
||||
|
||||
// Connect the player to the PlayerView
|
||||
playerView.player = player
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
player.pause() // Pause the player when the activity is paused
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
player.release() // Release the player when the activity is stopped
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,10 @@ interface ApiService {
|
||||
fun getProjet(@Path("id") projectId: Int): Call<Project>
|
||||
@GET("projects/{id}/measurements") // Remplace par l'endpoint de ton API
|
||||
fun getMeasurements(@Path("id") projectId: Int): Call<List<Measurement>>
|
||||
@GET("videos")
|
||||
fun getVideos(): Call<List<VideoDTO>>
|
||||
@POST("projects/")
|
||||
fun createProject(@Body project: Project): Call<Confirmation>
|
||||
@GET("projects/{id}/videos")
|
||||
fun getVideosOfProject(@Path("id") projectId:Int): Call<List<VideoDTO>>
|
||||
}
|
||||
@@ -26,6 +26,25 @@ class ProjectRepository(private val apiService: ApiService) {
|
||||
}
|
||||
})
|
||||
}
|
||||
fun fetchProject(id:Int, onSuccess: (Project) -> Unit, onError: (String) -> Unit) {
|
||||
val call = apiService.getProjet(id)
|
||||
call.enqueue(object : Callback<Project> {
|
||||
override fun onResponse(call: Call<Project>, response: Response<Project>) {
|
||||
if (response.isSuccessful) {
|
||||
val project = response.body()
|
||||
project?.let {
|
||||
onSuccess(it)
|
||||
} ?: onError("Le projet ${id} n'existe pas")
|
||||
} else {
|
||||
onError("Erreur : ${response.code()}")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<Project>, t: Throwable) {
|
||||
onError("Échec de l'appel API : ${t.message}")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun fetchMeasurementsOfProject(pid: Int, onSuccess: (List<Measurement>) -> Unit, onError: (String) -> Unit) {
|
||||
val call = apiService.getMeasurements(pid)
|
||||
|
||||
24
app/src/main/java/com/dreamteam/timelapse/data/VideoDTO.kt
Normal file
24
app/src/main/java/com/dreamteam/timelapse/data/VideoDTO.kt
Normal file
@@ -0,0 +1,24 @@
|
||||
package com.dreamteam.timelapse.data
|
||||
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
|
||||
data class VideoDTO (
|
||||
val id: Int,
|
||||
val project_id: Int,
|
||||
val measurement_ids: String,
|
||||
val video_file: String?,
|
||||
val resolution:String,
|
||||
val duration: Int,
|
||||
val status: Int, //0 pas fini, 1 fini
|
||||
val name: String
|
||||
){
|
||||
fun toVideo(): Video {
|
||||
fun parseIntArray(jsonString: String): List<Int> {
|
||||
val type = object : TypeToken<List<Int>>() {}.type
|
||||
return Gson().fromJson<List<Int>>(jsonString, type)
|
||||
}
|
||||
return Video(id, project_id, parseIntArray(measurement_ids), video_file, resolution, duration, status, name)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.dreamteam.timelapse.data
|
||||
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
import java.util.Date
|
||||
|
||||
class VideoRepository(private val apiService: ApiService) {
|
||||
fun fetchVideos(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)
|
||||
} else {
|
||||
onError("Erreur serveur : ${response.code()}")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<List<VideoDTO>>, t: Throwable) {
|
||||
onError("Erreur réseau : ${t.message}")
|
||||
}
|
||||
})
|
||||
}
|
||||
fun fetchVideosOfProject(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)
|
||||
} else {
|
||||
onError("Erreur serveur : ${response.code()}")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<List<VideoDTO>>, t: Throwable) {
|
||||
onError("Erreur réseau : ${t.message}")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
11
app/src/main/res/layout/activity_video_player.xml
Normal file
11
app/src/main/res/layout/activity_video_player.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.google.android.exoplayer2.ui.PlayerView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/playerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
<!--<SurfaceView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/surfaceView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />-->
|
||||
@@ -1,10 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<!--<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".VideoFrag">
|
||||
tools:context=".ProjetsFrag">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
@@ -12,17 +12,17 @@
|
||||
android:padding="16dp">
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_second"
|
||||
android:id="@+id/button_first"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/previous"
|
||||
app:layout_constraintBottom_toTopOf="@id/textview_second"
|
||||
android:text="@string/next"
|
||||
app:layout_constraintBottom_toTopOf="@id/textview_first"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textview_second"
|
||||
android:id="@+id/textview_first"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
@@ -30,6 +30,45 @@
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/button_second" />
|
||||
app:layout_constraintTop_toBottomOf="@id/button_first" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
-->
|
||||
<!--<androidx.swiperefreshlayout.widget.SwipeRefreshLayout-->
|
||||
<com.dreamteam.timelapse.CustomSwipeRefreshLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:context=".VideoFrag"
|
||||
android:id="@+id/swipeRefreshLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<!-- RecyclerView -->
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/noVideosText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Aucune video disponible"
|
||||
android:visibility="gone"
|
||||
android:gravity="center"
|
||||
android:textSize="18sp"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
</FrameLayout>
|
||||
<!--<ListView
|
||||
android:id="@+id/listView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />-->
|
||||
|
||||
</com.dreamteam.timelapse.CustomSwipeRefreshLayout>
|
||||
|
||||
<!--</androidx.core.widget.NestedScrollView> -->
|
||||
|
||||
47
app/src/main/res/layout/item_video.xml
Normal file
47
app/src/main/res/layout/item_video.xml
Normal file
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<!-- Video Thumbnail -->
|
||||
<ImageView
|
||||
android:id="@+id/videoThumbnail"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:scaleType="centerCrop"
|
||||
app:layout_constraintDimensionRatio="16:9"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/videoTitle"
|
||||
/>
|
||||
|
||||
<!-- Play Button (Centered on Thumbnail) -->
|
||||
<ImageView
|
||||
android:id="@+id/playButton"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:src="@android:drawable/ic_media_play"
|
||||
app:layout_constraintBottom_toBottomOf="@id/videoThumbnail"
|
||||
app:layout_constraintEnd_toEndOf="@id/videoThumbnail"
|
||||
app:layout_constraintStart_toStartOf="@id/videoThumbnail"
|
||||
app:layout_constraintTop_toTopOf="@id/videoThumbnail" />
|
||||
|
||||
<!-- Video Title -->
|
||||
<TextView
|
||||
android:id="@+id/videoTitle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="8dp"
|
||||
android:text="..."
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:maxLines="2"
|
||||
android:ellipsize="end"
|
||||
app:layout_constraintTop_toBottomOf="@id/videoThumbnail"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:elevation="4dp"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -15,7 +15,9 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fitsSystemWindows="true"
|
||||
tools:ignore="MissingConstraints">
|
||||
tools:ignore="MissingConstraints"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/project_name"
|
||||
@@ -35,13 +37,17 @@
|
||||
android:id="@+id/swipeRefreshLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
||||
app:layout_constraintStart_toEndOf="@+id/appBarLayout"
|
||||
app:layout_constraintTop_toBottomOf="@+id/appBarLayout"
|
||||
tools:context=".ProjectActivity">
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
tools:context=".ProjectActivity">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
@@ -82,6 +88,7 @@
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/project_start_date" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/graph_header"
|
||||
android:layout_width="wrap_content"
|
||||
@@ -93,6 +100,7 @@
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/project_status" />
|
||||
|
||||
<com.github.mikephil.charting.charts.LineChart
|
||||
android:id="@+id/temperature_humidity_chart"
|
||||
android:layout_width="0dp"
|
||||
@@ -147,8 +155,6 @@
|
||||
app:layout_constraintTop_toBottomOf="@id/video_header" />
|
||||
|
||||
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
</com.dreamteam.timelapse.CustomSwipeRefreshLayout>
|
||||
|
||||
@@ -13,6 +13,7 @@ appcompat = "1.6.1"
|
||||
constraintlayout = "2.1.4"
|
||||
navigationFragmentKtx = "2.6.0"
|
||||
navigationUiKtx = "2.6.0"
|
||||
media3Exoplayer = "1.5.1"
|
||||
|
||||
[libraries]
|
||||
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
||||
@@ -34,6 +35,7 @@ androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version
|
||||
androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
|
||||
androidx-navigation-fragment-ktx = { group = "androidx.navigation", name = "navigation-fragment-ktx", version.ref = "navigationFragmentKtx" }
|
||||
androidx-navigation-ui-ktx = { group = "androidx.navigation", name = "navigation-ui-ktx", version.ref = "navigationUiKtx" }
|
||||
androidx-media3-exoplayer = { group = "androidx.media3", name = "media3-exoplayer", version.ref = "media3Exoplayer" }
|
||||
|
||||
[plugins]
|
||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||
|
||||
Reference in New Issue
Block a user