appli finalized
This commit is contained in:
2
.idea/gradle.xml
generated
2
.idea/gradle.xml
generated
@@ -4,7 +4,6 @@
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="testRunner" value="CHOOSE_PER_TEST" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
|
||||
<option name="modules">
|
||||
@@ -13,6 +12,7 @@
|
||||
<option value="$PROJECT_DIR$/app" />
|
||||
</set>
|
||||
</option>
|
||||
<option name="resolveExternalAnnotations" value="false" />
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
|
||||
1
.idea/misc.xml
generated
1
.idea/misc.xml
generated
@@ -1,4 +1,3 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
|
||||
|
||||
@@ -83,3 +83,7 @@ dependencies {
|
||||
debugImplementation(libs.androidx.ui.tooling)
|
||||
debugImplementation(libs.androidx.ui.test.manifest)
|
||||
}
|
||||
|
||||
kotlin {
|
||||
tasks.register("testClasses")
|
||||
}
|
||||
BIN
app/release/app-release.apk
Normal file
BIN
app/release/app-release.apk
Normal file
Binary file not shown.
BIN
app/release/baselineProfiles/0/app-release.dm
Normal file
BIN
app/release/baselineProfiles/0/app-release.dm
Normal file
Binary file not shown.
BIN
app/release/baselineProfiles/1/app-release.dm
Normal file
BIN
app/release/baselineProfiles/1/app-release.dm
Normal file
Binary file not shown.
37
app/release/output-metadata.json
Normal file
37
app/release/output-metadata.json
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"version": 3,
|
||||
"artifactType": {
|
||||
"type": "APK",
|
||||
"kind": "Directory"
|
||||
},
|
||||
"applicationId": "com.dreamteam.timelapse",
|
||||
"variantName": "release",
|
||||
"elements": [
|
||||
{
|
||||
"type": "SINGLE",
|
||||
"filters": [],
|
||||
"attributes": [],
|
||||
"versionCode": 1,
|
||||
"versionName": "1.0",
|
||||
"outputFile": "app-release.apk"
|
||||
}
|
||||
],
|
||||
"elementType": "File",
|
||||
"baselineProfiles": [
|
||||
{
|
||||
"minApi": 28,
|
||||
"maxApi": 30,
|
||||
"baselineProfiles": [
|
||||
"baselineProfiles/1/app-release.dm"
|
||||
]
|
||||
},
|
||||
{
|
||||
"minApi": 31,
|
||||
"maxApi": 2147483647,
|
||||
"baselineProfiles": [
|
||||
"baselineProfiles/0/app-release.dm"
|
||||
]
|
||||
}
|
||||
],
|
||||
"minSdkVersionForDexing": 28
|
||||
}
|
||||
@@ -93,13 +93,27 @@ class MainActivity : AppCompatActivity() {
|
||||
|
||||
val builder: AlertDialog.Builder = AlertDialog.Builder(this)
|
||||
builder.setView(layout)
|
||||
builder.setPositiveButton("Save",
|
||||
builder.setPositiveButton("Sauvegarder",
|
||||
DialogInterface.OnClickListener { dialog, which ->
|
||||
val onSuccess : () -> Unit = {dialog.dismiss() ; Snackbar.make(viewfortoast, "Nouveau projet créé", Snackbar.LENGTH_LONG).setAnchorView(Rtmp.id.fab).show() ; }
|
||||
val onSuccess : () -> Unit = {
|
||||
dialog.dismiss() ;
|
||||
Snackbar.make(viewfortoast, "Nouveau projet créé", Snackbar.LENGTH_LONG).setAnchorView(Rtmp.id.fab).show() ;
|
||||
}
|
||||
val onError : (s:String) -> Unit = { s-> Snackbar.make(viewfortoast, "Erreur lors de la création du projet. ${s}", Snackbar.LENGTH_LONG).setAnchorView(Rtmp.id.fab).show() }
|
||||
|
||||
projectRepository.createProject(project_name.text.toString(), project_desc.text.toString(), onSuccess, onError)
|
||||
|
||||
val name = project_name.text.toString()
|
||||
val desc = project_desc.text.toString()
|
||||
if (name.isBlank() || desc.isBlank() || name == "" || desc == "") {
|
||||
Snackbar.make(viewfortoast, "Les champs ne doivent pas être vides", Snackbar.LENGTH_LONG)
|
||||
.show()
|
||||
}else {
|
||||
projectRepository.createProject(
|
||||
project_name.text.toString(),
|
||||
project_desc.text.toString(),
|
||||
onSuccess,
|
||||
onError
|
||||
)
|
||||
}
|
||||
})
|
||||
builder.setNegativeButton("Cancel",
|
||||
DialogInterface.OnClickListener { dialog, which -> dialog.dismiss() })
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
package com.dreamteam.timelapse
|
||||
import android.content.Intent
|
||||
import android.graphics.Color
|
||||
import android.graphics.Rect
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.EditText
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.dreamteam.timelapse.data.ApiService
|
||||
import com.dreamteam.timelapse.data.CaptureQuery
|
||||
import com.dreamteam.timelapse.data.Confirmation
|
||||
import com.dreamteam.timelapse.data.Measurement
|
||||
import com.dreamteam.timelapse.data.Project
|
||||
import com.dreamteam.timelapse.data.ProjectRepository
|
||||
@@ -21,6 +27,11 @@ import com.github.mikephil.charting.data.LineData
|
||||
import com.github.mikephil.charting.data.LineDataSet
|
||||
import com.github.mikephil.charting.formatter.ValueFormatter
|
||||
import com.github.mikephil.charting.interfaces.datasets.ILineDataSet
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.gson.GsonConverterFactory
|
||||
import java.text.SimpleDateFormat
|
||||
@@ -30,6 +41,9 @@ import java.util.Locale
|
||||
|
||||
class ProjectActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var stopingProcedureDialog: AlertDialog
|
||||
private lateinit var videoRenderDialog: AlertDialog
|
||||
private lateinit var startingProcedureDialog: AlertDialog
|
||||
private lateinit var binding: ProjectBinding
|
||||
private lateinit var projectRepository: ProjectRepository
|
||||
private lateinit var videoRepository: VideoRepository
|
||||
@@ -37,6 +51,11 @@ class ProjectActivity : AppCompatActivity() {
|
||||
private var measures: List<Measurement> = emptyList() // Déclare une liste de projets vide
|
||||
private var project: Project? = null
|
||||
private var imageUrls = emptyList<String>()
|
||||
enum class ButtonState {
|
||||
START, LOADING, FINISHED, ENDING
|
||||
}
|
||||
|
||||
private var buttonState = ButtonState.START
|
||||
|
||||
class GridSpacingItemDecoration(private val spacing: Int) : RecyclerView.ItemDecoration() {
|
||||
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
|
||||
@@ -48,6 +67,16 @@ class ProjectActivity : AppCompatActivity() {
|
||||
outRect.top = spacing
|
||||
}
|
||||
}
|
||||
|
||||
fun refreshMultiuseButton(){
|
||||
val floatingButton = findViewById<FloatingActionButton>(R.id.multiusefloating)
|
||||
var icon = when (buttonState) {
|
||||
ButtonState.START -> android.R.drawable.ic_media_play
|
||||
ButtonState.LOADING -> R.drawable.baseline_stop_24
|
||||
ButtonState.FINISHED, ButtonState.ENDING -> android.R.drawable.ic_menu_save
|
||||
}
|
||||
floatingButton.setImageResource(icon)
|
||||
}
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
val retrofit = Retrofit.Builder()
|
||||
.baseUrl("https://timelapse.kerboul.me/api/")
|
||||
@@ -65,36 +94,77 @@ class ProjectActivity : AppCompatActivity() {
|
||||
setContentView(binding.root)
|
||||
|
||||
initProjectInfo()
|
||||
|
||||
binding.imagesList.layoutManager = GridLayoutManager(this.baseContext, 2, GridLayoutManager.HORIZONTAL, false)
|
||||
binding.imagesList.addItemDecoration(GridSpacingItemDecoration(16)) // 16px spacing
|
||||
binding.videosList.layoutManager = GridLayoutManager(this.baseContext, 2, GridLayoutManager.HORIZONTAL, false)
|
||||
binding.videosList.addItemDecoration(GridSpacingItemDecoration(16)) // 16px spacing
|
||||
fetchMeasuresAndRebuildGraph()
|
||||
fetchVideos()
|
||||
val swipeRefreshLayout = binding.swipeRefreshLayout
|
||||
swipeRefreshLayout.setOnRefreshListener {
|
||||
fetchMeasuresAndRebuildGraph()
|
||||
fetchVideos()
|
||||
Log.d("ProjetsFrag", "Actualisation des projets")
|
||||
}
|
||||
fetchVideos()
|
||||
|
||||
val floatingButton = findViewById<FloatingActionButton>(R.id.multiusefloating)
|
||||
this.buttonState = when(project?.getStatusText()) {
|
||||
"Brouillon" -> ButtonState.START
|
||||
"En Cours" -> ButtonState.LOADING
|
||||
"Terminé" -> ButtonState.FINISHED
|
||||
"En cours d'arret" -> ButtonState.ENDING
|
||||
else -> ButtonState.START
|
||||
}
|
||||
var icon = when (buttonState) {
|
||||
ButtonState.START -> android.R.drawable.ic_media_play
|
||||
ButtonState.LOADING -> R.drawable.baseline_stop_24
|
||||
ButtonState.FINISHED -> android.R.drawable.ic_menu_save
|
||||
ButtonState.ENDING -> android.R.drawable.ic_menu_save
|
||||
}
|
||||
floatingButton.setImageResource(icon)
|
||||
|
||||
floatingButton.setOnClickListener {
|
||||
// when (buttonState) {
|
||||
// ButtonState.LOADING -> {
|
||||
// buttonState = ButtonState.FINISHED
|
||||
// }
|
||||
// else -> {}
|
||||
// }
|
||||
// refreshMultiuseButton()
|
||||
when (buttonState) {
|
||||
ButtonState.START -> clickStartProcess()
|
||||
ButtonState.LOADING -> clickStopProcess()
|
||||
ButtonState.FINISHED, ButtonState.ENDING -> clickSave()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var but = findViewById<View>(R.id.multiusefloating)
|
||||
but.post {
|
||||
project?.let {
|
||||
createVideoRenderDialog(but, it.id, measures)
|
||||
createStopProcedureDialog(but, it.id)
|
||||
createStartingProcedureDialog(but, it.id)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Bouton flottant
|
||||
// binding.fab.setOnClickListener { view ->
|
||||
// Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
|
||||
// //.setAction("Action", null) //sert à rien
|
||||
// .setAnchorView(R.id.fab).show() //au dessus du bouton mail
|
||||
//
|
||||
// }
|
||||
}
|
||||
|
||||
fun fetchVideos(){
|
||||
this.project?.let{
|
||||
videoRepository.fetchVideosOfProject(it.id, onSuccess = { videos ->
|
||||
videoRepository.fetchRenderedVideosOfProject(it.id, onSuccess = { videos ->
|
||||
val adapter = VideoAdapter(false, videos.toMutableList(), null) //ImageAdapter(imageUrls)
|
||||
binding.videosList.layoutManager = GridLayoutManager(this.baseContext, 2, GridLayoutManager.HORIZONTAL, false)
|
||||
binding.videosList.addItemDecoration(GridSpacingItemDecoration(16)) // 16px spacing
|
||||
binding.videosList.adapter = adapter
|
||||
binding.swipeRefreshLayout.isRefreshing = false
|
||||
}, onError = {errorMessage ->
|
||||
Log.e("ProjectActivity", errorMessage)
|
||||
Log.e("ProjectActivity", "Fetch Video " + errorMessage)
|
||||
fetchVideos()
|
||||
})
|
||||
}
|
||||
@@ -109,8 +179,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 = GridLayoutManager(this.baseContext, 2, GridLayoutManager.HORIZONTAL, false)
|
||||
binding.imagesList.addItemDecoration(GridSpacingItemDecoration(16)) // 16px spacing
|
||||
Log.d("ProjectActivity", "Je vais mettre l'adapter")
|
||||
binding.imagesList.adapter = adapter
|
||||
|
||||
binding.swipeRefreshLayout.isRefreshing = false
|
||||
@@ -124,7 +193,6 @@ class ProjectActivity : AppCompatActivity() {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fun initGraph(lm: List<Measurement>) {
|
||||
val temperatureHumidityChart = this.binding.temperatureHumidityChart
|
||||
|
||||
@@ -160,8 +228,15 @@ class ProjectActivity : AppCompatActivity() {
|
||||
val xAxis = temperatureHumidityChart.xAxis
|
||||
xAxis.valueFormatter = object : ValueFormatter() {
|
||||
override fun getFormattedValue(value: Float): String {
|
||||
// Map the index back to the corresponding timestamp
|
||||
val timestamp = lm.sortedBy { it.order_id }[value.toInt()].timestamp
|
||||
val index = value.toInt()
|
||||
val sortedList = lm.sortedBy { it.order_id }
|
||||
|
||||
if (index < 0 || index >= sortedList.size) {
|
||||
// Index out of bounds, return empty string or something neutral
|
||||
return ""
|
||||
}
|
||||
|
||||
val timestamp = sortedList[index].timestamp
|
||||
val date = Date(timestamp.time)
|
||||
val sdf = SimpleDateFormat("dd/MM/yyyy:HH:mm:ss", Locale.getDefault())
|
||||
return sdf.format(date)
|
||||
@@ -222,9 +297,6 @@ class ProjectActivity : AppCompatActivity() {
|
||||
// Refresh the chart
|
||||
temperatureHumidityChart.invalidate()
|
||||
}
|
||||
|
||||
|
||||
|
||||
fun initProjectInfo(){
|
||||
val nameview = findViewById<TextView>(R.id.project_name)
|
||||
val descriptionview = findViewById<TextView>(R.id.project_description)
|
||||
@@ -236,4 +308,200 @@ class ProjectActivity : AppCompatActivity() {
|
||||
beginview.text = project!!.start_date.toString()
|
||||
statusview.text = project!!.getStatusText()
|
||||
}
|
||||
private fun clickStartProcess() {
|
||||
Log.i("ProjectActivity", "Start collect...")
|
||||
|
||||
// Simule une durée avant la finalisation du processus
|
||||
// Handler(Looper.getMainLooper()).postDelayed({
|
||||
// val floatingButton = findViewById<FloatingActionButton>(R.id.multiusefloating)
|
||||
// floatingButton.setImageResource(android.R.drawable.ic_menu_save) // Icône d'enregistrement
|
||||
// buttonState = ButtonState.FINISHED
|
||||
// }, 5000) // 5 secondes de chargement
|
||||
startingProcedureDialog.show()
|
||||
}
|
||||
|
||||
private fun clickStopProcess() {
|
||||
Log.i("ProjectActivity", "Stop collect...")
|
||||
stopingProcedureDialog.show()
|
||||
// Implémente l'arrêt du processus (annulation si nécessaire)
|
||||
}
|
||||
|
||||
private fun clickSave() {
|
||||
Log.i("ProjectActivity", "Create video...")
|
||||
videoRenderDialog.show()
|
||||
//videoRepository.createVideo(project.id, measures, )
|
||||
// Toast.makeText(context, "Downloading...", Toast.LENGTH_SHORT).show()
|
||||
//
|
||||
// Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
|
||||
// .setAction("Action", null) //sert à rien
|
||||
// .setAnchorView(R.id.fab).show() //au dessus du bouton mail
|
||||
// Implémente la création d'une vidéo
|
||||
}
|
||||
|
||||
fun createVideoRenderDialog(viewfortoast: View, projectId: Int, measurements: List<Measurement>) {
|
||||
val inflater = getSystemService(LAYOUT_INFLATER_SERVICE) as LayoutInflater
|
||||
val layout: View = inflater.inflate(R.layout.video_render_dialog_layout, findViewById(R.id.layout_root))
|
||||
|
||||
val videoName = layout.findViewById<EditText>(R.id.video_name_dialog)
|
||||
//val videoResolution = layout.findViewById<EditText>(R.id.video_resolution_dialog)
|
||||
val videoDuration = layout.findViewById<EditText>(R.id.video_duration_dialog)
|
||||
|
||||
val builder = AlertDialog.Builder(this)
|
||||
builder.setView(layout)
|
||||
.setPositiveButton("Render Video") { dialog, _ ->
|
||||
val name = videoName.text.toString()
|
||||
//val resolution = videoResolution.text.toString()
|
||||
val durationText = videoDuration.text.toString()
|
||||
|
||||
if (name.isBlank() || durationText.isBlank()) {
|
||||
Snackbar.make(viewfortoast, "All fields must be filled", Snackbar.LENGTH_LONG)
|
||||
.show()
|
||||
return@setPositiveButton
|
||||
}
|
||||
|
||||
val duration = durationText.toIntOrNull()
|
||||
if (duration == null || duration <= 0) {
|
||||
Snackbar.make(viewfortoast, "Invalid duration", Snackbar.LENGTH_LONG)
|
||||
.show()
|
||||
return@setPositiveButton
|
||||
}
|
||||
|
||||
val onSuccess: (Confirmation?) -> Unit = {
|
||||
dialog.dismiss()
|
||||
Snackbar.make(viewfortoast, "Video render started", Snackbar.LENGTH_LONG)
|
||||
.show()
|
||||
}
|
||||
|
||||
val onError: (String) -> Unit = { error ->
|
||||
Snackbar.make(viewfortoast, "Error: $error", Snackbar.LENGTH_LONG)
|
||||
.show()
|
||||
}
|
||||
|
||||
videoRepository.createVideo(projectId, measures, name, "1920x1080", duration, onSuccess, onError)
|
||||
}
|
||||
.setNegativeButton("Cancel") { dialog, _ -> dialog.dismiss() }
|
||||
|
||||
this.videoRenderDialog = builder.create()
|
||||
}
|
||||
fun createStartingProcedureDialog(viewfortoast: View, projectId: Int) {
|
||||
val inflater = getSystemService(LAYOUT_INFLATER_SERVICE) as LayoutInflater
|
||||
val layout: View = inflater.inflate(R.layout.capture_start_dialog_layout, findViewById(R.id.layout_root))
|
||||
|
||||
val intervalView = layout.findViewById<EditText>(R.id.interval)
|
||||
//val videoResolution = layout.findViewById<EditText>(R.id.video_resolution_dialog)
|
||||
val nbImView = layout.findViewById<EditText>(R.id.nb_images)
|
||||
|
||||
val builder = AlertDialog.Builder(this)
|
||||
builder.setView(layout)
|
||||
.setPositiveButton("Run capture procedure") { dialog, _ ->
|
||||
val intervalStr = intervalView.text.toString()
|
||||
//val resolution = videoResolution.text.toString()
|
||||
val nb_imagesStr = nbImView.text.toString()
|
||||
|
||||
if (intervalStr.isBlank() || nb_imagesStr.isBlank()) {
|
||||
Snackbar.make(viewfortoast, "Les champs doivent être remplis", Snackbar.LENGTH_LONG)
|
||||
.show()
|
||||
return@setPositiveButton
|
||||
}
|
||||
|
||||
val interval = intervalStr.toIntOrNull()
|
||||
val nb_images = nb_imagesStr.toIntOrNull()
|
||||
if (interval == null || interval <= 0 || nb_images == null || nb_images <= 0) {
|
||||
Snackbar.make(viewfortoast, "Les chiffres fournis doivent être superieurs à 1", Snackbar.LENGTH_LONG)
|
||||
.show()
|
||||
return@setPositiveButton
|
||||
}
|
||||
|
||||
val onSuccess: () -> Unit = {
|
||||
dialog.dismiss()
|
||||
Snackbar.make(viewfortoast, "Processus de capture lancé", Snackbar.LENGTH_LONG)
|
||||
.show()
|
||||
buttonState = ButtonState.LOADING
|
||||
refreshMultiuseButton()
|
||||
|
||||
}
|
||||
|
||||
val onError: (String) -> Unit = { error ->
|
||||
Snackbar.make(viewfortoast, "Error : $error", Snackbar.LENGTH_LONG)
|
||||
.show()
|
||||
}
|
||||
|
||||
apiService.procedureStart(CaptureQuery(interval, nb_images, projectId)).enqueue(object :
|
||||
Callback<Void> {
|
||||
override fun onResponse(call: Call<Void>, response: Response<Void>) {
|
||||
if (response.isSuccessful) {
|
||||
refreshProjectInfo(onSuccess, onError)
|
||||
} else {
|
||||
onError("Erreur serveur : ${response.code()}")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<Void>, t: Throwable) {
|
||||
onError("Erreur réseau : ${t.message}")
|
||||
}
|
||||
})
|
||||
}
|
||||
.setNegativeButton("Cancel") { dialog, _ -> dialog.dismiss() }
|
||||
|
||||
this.startingProcedureDialog = builder.create()
|
||||
}
|
||||
fun createStopProcedureDialog(viewfortoast: View, projectId: Int) {
|
||||
val inflater = getSystemService(LAYOUT_INFLATER_SERVICE) as LayoutInflater
|
||||
val layout: View = inflater.inflate(R.layout.capture_stop_dialog_layout, findViewById(R.id.layout_root))
|
||||
|
||||
val builder = AlertDialog.Builder(this)
|
||||
builder.setView(layout)
|
||||
.setPositiveButton("Stop") { dialog, _ ->
|
||||
val onSuccess: () -> Unit = {
|
||||
dialog.dismiss()
|
||||
Snackbar.make(viewfortoast, "Processus arreté", Snackbar.LENGTH_LONG)
|
||||
.show()
|
||||
buttonState = ButtonState.FINISHED
|
||||
refreshMultiuseButton()
|
||||
|
||||
}
|
||||
|
||||
val onError: (String) -> Unit = { error ->
|
||||
Snackbar.make(viewfortoast, "Error : $error", Snackbar.LENGTH_LONG)
|
||||
.show()
|
||||
}
|
||||
|
||||
apiService.procedureStop().enqueue(object :
|
||||
Callback<Void> {
|
||||
override fun onResponse(call: Call<Void>, response: Response<Void>) {
|
||||
if (response.isSuccessful) {
|
||||
refreshProjectInfo(onSuccess, onError)
|
||||
} else {
|
||||
onError("Erreur serveur : ${response.code()}")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<Void>, t: Throwable) {
|
||||
onError("Erreur réseau : ${t.message}")
|
||||
}
|
||||
})
|
||||
}
|
||||
.setNegativeButton("Cancel") { dialog, _ -> dialog.dismiss() }
|
||||
|
||||
this.stopingProcedureDialog = builder.create()
|
||||
}
|
||||
|
||||
fun refreshProjectInfo(cbGood: ()->Unit, cbBad: (String)->Unit){
|
||||
this.project?.let {
|
||||
projectRepository.fetchProject(it.id, onSuccess = {
|
||||
Log.i("ProjectActivity", "Pull projet ok")
|
||||
this.project = it;
|
||||
this.initProjectInfo()
|
||||
cbGood()
|
||||
}, onError = {
|
||||
Log.i("ProjectActivity", "Erreur de pull projet : $it")
|
||||
cbBad("Erreur de pull projet : $it")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
super.onBackPressed()
|
||||
startActivity(Intent(this, MainActivity::class.java))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,9 +74,10 @@ class ProjectAdapter(private val projects: MutableList<Project>, private val lis
|
||||
}
|
||||
fun removeItem(position: Int, context: Context, v : View) {
|
||||
val builder = AlertDialog.Builder(context)
|
||||
val dialog = builder.setTitle("Confirm Deletion")
|
||||
val dialog = builder.setTitle("Confirmer la supression")
|
||||
.setMessage("Voulez vous supprimer ce projet ?")
|
||||
.setPositiveButton("Supprimer") { dialog, _ ->
|
||||
if(projects[position].status != 3 && projects[position].status != 2){
|
||||
projectRepository.deleteProject(projects[position].id, onSuccess = {
|
||||
val name = projects[position].name
|
||||
projects.removeAt(position)
|
||||
@@ -87,11 +88,14 @@ class ProjectAdapter(private val projects: MutableList<Project>, private val lis
|
||||
notifyItemChanged(position)
|
||||
Snackbar.make(v.rootView, "Error while trying to delete ${projects[position].name} : $s", Snackbar.LENGTH_SHORT).show()
|
||||
})
|
||||
}else{
|
||||
notifyItemChanged(position)
|
||||
Snackbar.make(v.rootView, "On ne peut pas supprimer un projet dans un état intermédiaire", Snackbar.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
}
|
||||
.setNegativeButton("Annuler") { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
notifyItemChanged(position) // Reset swipe animation
|
||||
}
|
||||
.show()
|
||||
dialog.setOnDismissListener {
|
||||
|
||||
@@ -17,6 +17,8 @@ interface ApiService {
|
||||
fun getMeasurements(@Path("id") projectId: Int): Call<List<Measurement>>
|
||||
@GET("videos")
|
||||
fun getVideos(): Call<List<VideoDTO>>
|
||||
@POST("videos")
|
||||
fun createVideo(@Body video: VideoDTO): Call<Confirmation>
|
||||
@DELETE("videos/{id}")
|
||||
fun deleteVideo(@Path("id") videoId : Int): Call<Void>
|
||||
@POST("projects/")
|
||||
@@ -25,4 +27,8 @@ interface ApiService {
|
||||
fun getVideosOfProject(@Path("id") projectId:Int): Call<List<VideoDTO>>
|
||||
@DELETE("projects/{id}")
|
||||
fun deleteProject(@Path("id") projectId:Int) : Call<Void>
|
||||
@POST("procedure/start")
|
||||
fun procedureStart(@Body captureQuery: CaptureQuery): Call<Void>
|
||||
@POST("procedure/stop")
|
||||
fun procedureStop(): Call<Void>
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.dreamteam.timelapse.data
|
||||
|
||||
data class CaptureQuery (
|
||||
val interval: Int,
|
||||
val nb_images: Int,
|
||||
val project_id: Int
|
||||
)
|
||||
{}
|
||||
@@ -24,7 +24,7 @@ data class Project(
|
||||
|
||||
fun getStatusText(): String{
|
||||
Log.i("Project", "Status $status being trasnlated")
|
||||
val statusArr = arrayOf("Brouillon", "En Cours", "Terminé", "Annulé")
|
||||
val statusArr = arrayOf("Brouillon", "En Cours", "Terminé", "En cours d'arret")
|
||||
return statusArr[status]
|
||||
}
|
||||
fun getStatusColor(): Int{
|
||||
|
||||
@@ -56,7 +56,8 @@ class ProjectRepository(private val apiService: ApiService) {
|
||||
onSuccess(it)
|
||||
} ?: onError("Aucune mesure trouvé")
|
||||
} else {
|
||||
onError("Erreur : ${response.code()}")
|
||||
onSuccess(emptyList())
|
||||
//onError("Erreur reception measures: ${response.code()}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +73,7 @@ class ProjectRepository(private val apiService: ApiService) {
|
||||
if (response.isSuccessful) {
|
||||
onSuccess()
|
||||
} else {
|
||||
onError("Erreur : ${response.code()}")
|
||||
onError("Erreur creation projet : ${response.code()}, ${response.body()}")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.dreamteam.timelapse.data
|
||||
|
||||
import android.util.Log
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
|
||||
@@ -16,7 +17,15 @@ data class VideoDTO (
|
||||
fun toVideo(): Video {
|
||||
fun parseIntArray(jsonString: String): List<Int> {
|
||||
val type = object : TypeToken<List<Int>>() {}.type
|
||||
try {
|
||||
Log.d("VideoDTO", Gson().fromJson<List<Int>>(jsonString, type).toString())
|
||||
return Gson().fromJson<List<Int>>(jsonString, type)
|
||||
}catch (e: Exception){
|
||||
Log.d("VideoDTO", jsonString)
|
||||
return Gson().fromJson<List<Int>>("[]", type)
|
||||
|
||||
//throw Exception("Tentative de transformer une videoDTO en video alors que le format est pas bon")
|
||||
}
|
||||
}
|
||||
return Video(id, project_id, parseIntArray(measurement_ids), video_file, resolution, duration, status, name)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.dreamteam.timelapse.data
|
||||
|
||||
import android.util.Log
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
@@ -39,15 +40,20 @@ class VideoRepository(private val apiService: ApiService) {
|
||||
}
|
||||
})
|
||||
}
|
||||
fun fetchVideosOfProject(id:Int, onSuccess: (List<Video>) -> Unit, onError: (String) -> Unit){
|
||||
fun fetchRenderedVideosOfProject(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()
|
||||
videoDtos.forEach {
|
||||
Log.d("VideoRepository", it.toString())
|
||||
it.toVideo()
|
||||
}
|
||||
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()}")
|
||||
onSuccess(emptyList())
|
||||
//onError("Erreur serveur : ${response.code()} ; id : $id ; ${response.body()}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,4 +94,24 @@ class VideoRepository(private val apiService: ApiService) {
|
||||
onError("Erreur réseau : ${t.message}")
|
||||
}
|
||||
})
|
||||
}}
|
||||
}
|
||||
fun createVideo(project_id:Int, measurements: List<Measurement>,
|
||||
name:String, resolution: String, duration:Int,
|
||||
onSuccess: (Confirmation?) -> Unit, onError: (String) -> Unit){
|
||||
Log.d("VideoRepository", measurements.map {m -> m.order_id}.toString())
|
||||
var v = VideoDTO(0, project_id, measurements.map {m -> m.order_id}.toString(), null, resolution, duration, 0, name)
|
||||
apiService.createVideo(v).enqueue(object : Callback<Confirmation> {
|
||||
override fun onResponse(call: Call<Confirmation>, response: Response<Confirmation>) {
|
||||
if (response.isSuccessful) {
|
||||
onSuccess(response.body())
|
||||
} else {
|
||||
onError("Erreur : ${response.code()} ; ${response.body()}")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<Confirmation>, t: Throwable) {
|
||||
onError("Échec de l'appel API : ${t.message}")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
5
app/src/main/res/drawable/baseline_stop_24.xml
Normal file
5
app/src/main/res/drawable/baseline_stop_24.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M6,6h12v12H6z"/>
|
||||
|
||||
</vector>
|
||||
29
app/src/main/res/layout/capture_start_dialog_layout.xml
Normal file
29
app/src/main/res/layout/capture_start_dialog_layout.xml
Normal file
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/layout_root"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/interval"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="Frequence (min)"
|
||||
android:inputType="number"/>
|
||||
|
||||
<!--<EditText
|
||||
android:id="@+id/video_resolution_dialog"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="Resolution (e.g., 1080p)" />-->
|
||||
|
||||
<EditText
|
||||
android:id="@+id/nb_images"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="Nombre d'images"
|
||||
android:inputType="number" />
|
||||
|
||||
</LinearLayout>
|
||||
16
app/src/main/res/layout/capture_stop_dialog_layout.xml
Normal file
16
app/src/main/res/layout/capture_stop_dialog_layout.xml
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/layout_root"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
<TextView
|
||||
android:id="@+id/textView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="25sp"
|
||||
android:text="Voulez vous annuler la prise de vue ? (la demande sera prise en compte par la camera lors de son prochain redemarrage)" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -165,4 +165,15 @@
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
</com.dreamteam.timelapse.CustomSwipeRefreshLayout>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/multiusefloating"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_marginEnd="25dp"
|
||||
android:layout_marginBottom="25dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:srcCompat="@android:drawable/ic_media_play" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
28
app/src/main/res/layout/video_render_dialog_layout.xml
Normal file
28
app/src/main/res/layout/video_render_dialog_layout.xml
Normal file
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/layout_root"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/video_name_dialog"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="Video Name" />
|
||||
|
||||
<!--<EditText
|
||||
android:id="@+id/video_resolution_dialog"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="Resolution (e.g., 1080p)" />-->
|
||||
|
||||
<EditText
|
||||
android:id="@+id/video_duration_dialog"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="Duration (seconds)"
|
||||
android:inputType="number" />
|
||||
|
||||
</LinearLayout>
|
||||
Reference in New Issue
Block a user