From ce5daf55f5f52258236e9858591d23b88b3f86d7 Mon Sep 17 00:00:00 2001
From: FlorianGandon <165256783+FlorianGandon@users.noreply.github.com>
Date: Wed, 16 Apr 2025 13:55:42 +0200
Subject: [PATCH] add async in activities

---
 app/build.gradle.kts                          |  2 +
 .../tdsaeandroid/DisplayMediaActivity.kt      | 61 +++++++++++--------
 .../tdsaeandroid/MediasFiltersActivity.kt     | 37 +++++------
 .../example/tdsaeandroid/ResultsActivity.kt   | 55 +++++++++++------
 gradle/libs.versions.toml                     |  4 ++
 5 files changed, 94 insertions(+), 65 deletions(-)

diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index fff613d..d9759d3 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -58,4 +58,6 @@ dependencies {
     androidTestImplementation(libs.androidx.espresso.core)
     implementation (libs.picasso)
     implementation(libs.androidx.activity.ktx)
+    implementation(libs.androidx.lifecycle.runtime.ktx)
+    implementation(libs.androidx.activity.ktx.v172)
 }
\ No newline at end of file
diff --git a/app/src/main/java/com/example/tdsaeandroid/DisplayMediaActivity.kt b/app/src/main/java/com/example/tdsaeandroid/DisplayMediaActivity.kt
index 32b4631..acabc03 100644
--- a/app/src/main/java/com/example/tdsaeandroid/DisplayMediaActivity.kt
+++ b/app/src/main/java/com/example/tdsaeandroid/DisplayMediaActivity.kt
@@ -2,17 +2,22 @@ package com.example.tdsaeandroid
 
 import android.content.Intent
 import android.os.Bundle
+import android.view.LayoutInflater
 import android.widget.ImageButton
 import android.widget.ImageView
 import android.widget.TextView
 import androidx.activity.enableEdgeToEdge
 import androidx.appcompat.app.AppCompatActivity
+import androidx.lifecycle.lifecycleScope
 import com.example.tdsaeandroid.dao.Dao
 import com.example.tdsaeandroid.model.media.Media
 import com.example.tdsaeandroid.model.movie.Movie
 import com.google.android.material.chip.Chip
 import com.google.android.material.chip.ChipGroup
 import com.squareup.picasso.Picasso
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
 
 class DisplayMediaActivity : AppCompatActivity() {
 
@@ -43,7 +48,6 @@ class DisplayMediaActivity : AppCompatActivity() {
         genresChips = findViewById(R.id.media__genres)
 
         type = (intent?.getSerializableExtra("type") ?: throw Exception("No activity type found")) as ActivityType
-        val id = intent?.extras?.getInt("id")  ?: throw Exception("No id found")
 
         btClose.setOnClickListener {
             val intent = Intent()
@@ -51,34 +55,43 @@ class DisplayMediaActivity : AppCompatActivity() {
             setResult(RESULT_OK, intent)
             finish()
         }
+        lifecycleScope.launch {
+            val data = getMediaData()
+            if (data == null) {
+                val intent = Intent()
+                intent.putExtra("found", false)
+                setResult(RESULT_OK, intent)
+                return@launch finish()
+            }
+            title.text = data.title
+            overview.text = data.overview
+            averageText.text = getString(R.string.rating_according, data.vote_average.toString(), data.vote_count.toString())
 
-        // get media data
-        val data: Media? = when (type) {
-            ActivityType.Movie -> dao.getMovieById(id)
-            ActivityType.Tv -> dao.getTvById(id)
+            if (data is Movie) {
+                date.text = getString(R.string.released_in, data.release_date)
+                if (data.adult) {
+                    includeAdultText.text = getString(R.string.warning_not_suitable)
+                }
+            }
+            for (g in data.genres) {
+                val chip = Chip(this@DisplayMediaActivity)
+                chip.text = g.name
+                genresChips.addView(chip)
+            }
+            Picasso.get().load(IMG_URL + data.poster_path).into(image)
         }
 
-        if (data == null) {
-            val intent = Intent()
-            intent.putExtra("found", false)
-            setResult(RESULT_OK, intent)
-            return finish()
-        }
-        title.text = data.title
-        overview.text = data.overview
-        averageText.text = getString(R.string.rating_according, data.vote_average.toString(), data.vote_count.toString())
 
-        if (data is Movie) {
-            date.text = getString(R.string.released_in, data.release_date)
-            if (data.adult) {
-                includeAdultText.text = getString(R.string.warning_not_suitable)
+    }
+
+    private suspend fun getMediaData(): Media? {
+        return withContext(Dispatchers.IO) {
+            val id = intent?.extras?.getInt("id")  ?: throw Exception("No id found")
+
+            when (type) {
+                ActivityType.Movie -> dao.getMovieById(id)
+                ActivityType.Tv -> dao.getTvById(id)
             }
         }
-        for (g in data.genres) {
-            val chip = Chip(this)
-            chip.text = g.name
-            genresChips.addView(chip)
-        }
-        Picasso.get().load(IMG_URL + data.poster_path).into(image)
     }
 }
\ No newline at end of file
diff --git a/app/src/main/java/com/example/tdsaeandroid/MediasFiltersActivity.kt b/app/src/main/java/com/example/tdsaeandroid/MediasFiltersActivity.kt
index 05b3b02..44e9186 100644
--- a/app/src/main/java/com/example/tdsaeandroid/MediasFiltersActivity.kt
+++ b/app/src/main/java/com/example/tdsaeandroid/MediasFiltersActivity.kt
@@ -7,21 +7,19 @@ import android.content.DialogInterface
 import android.content.Intent
 import android.icu.text.DateFormat.getDateInstance
 import android.icu.util.Calendar
-import android.os.Build
 import android.os.Bundle
 import android.view.LayoutInflater
 import android.widget.Button
 import android.widget.DatePicker
-import android.widget.EditText
 import android.widget.RatingBar
 import androidx.activity.enableEdgeToEdge
 import androidx.appcompat.app.AppCompatActivity
 import androidx.fragment.app.DialogFragment
+import androidx.lifecycle.lifecycleScope
+import kotlinx.coroutines.launch
 import com.example.tdsaeandroid.component.yearPickerDialog
 import com.example.tdsaeandroid.dao.Dao
 import com.example.tdsaeandroid.dao.DateFilter
-import com.example.tdsaeandroid.dao.DateSort
-import com.example.tdsaeandroid.dao.Filter
 import com.example.tdsaeandroid.dao.FilterContainer
 import com.example.tdsaeandroid.dao.GenresFilter
 import com.example.tdsaeandroid.dao.IncludeAdultFilter
@@ -30,21 +28,14 @@ import com.example.tdsaeandroid.dao.VotesFilter
 import com.example.tdsaeandroid.dao.YearFilter
 import com.example.tdsaeandroid.dao.formatDate
 import com.example.tdsaeandroid.model.media.Genre
-import com.example.tdsaeandroid.model.movie.MovieInList
 import com.google.android.material.chip.Chip
 import com.google.android.material.chip.ChipGroup
 import com.google.android.material.switchmaterial.SwitchMaterial
-import kotlinx.serialization.PolymorphicSerializer
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
 import kotlinx.serialization.encodeToString
-import kotlinx.serialization.json.Json
-import kotlinx.serialization.modules.SerializersModule
-import kotlinx.serialization.modules.polymorphic
-import kotlinx.serialization.modules.subclass
-import kotlinx.serialization.serializer
-import java.text.SimpleDateFormat
 import java.time.Year
 import java.util.Date
-import java.util.Random
 
 class MediasFiltersActivity : AppCompatActivity(), DatePickerListener {
 
@@ -142,16 +133,20 @@ class MediasFiltersActivity : AppCompatActivity(), DatePickerListener {
             newFragment.show(supportFragmentManager, "datePicker")
         }
 
-       genres = dao.getGenres().toTypedArray()
-
-        for (g in genres) {
-            val chip: Chip = LayoutInflater.from(this).inflate(R.layout.chip_layout, null) as Chip
-            chip.text = g.name
-            chip.id = g.id
-            chipGroup.addView(chip)
+        lifecycleScope.launch {
+            loadGenres().forEach {
+                val chip: Chip = LayoutInflater.from(this@MediasFiltersActivity).inflate(R.layout.chip_layout, null) as Chip
+                chip.text = it.name
+                chip.id = it.id
+                chipGroup.addView(chip)
+            }
         }
+    }
 
-        // todo lifecyclescope
+    private suspend fun loadGenres(): Array<Genre> {
+        return withContext(Dispatchers.IO) {
+            dao.getGenres().toTypedArray()
+        }
     }
 
     override fun onDateSetBefore(date: Date) {
diff --git a/app/src/main/java/com/example/tdsaeandroid/ResultsActivity.kt b/app/src/main/java/com/example/tdsaeandroid/ResultsActivity.kt
index fdb9056..81505ce 100644
--- a/app/src/main/java/com/example/tdsaeandroid/ResultsActivity.kt
+++ b/app/src/main/java/com/example/tdsaeandroid/ResultsActivity.kt
@@ -1,6 +1,7 @@
 package com.example.tdsaeandroid
 
 import android.os.Bundle
+import android.view.LayoutInflater
 import android.widget.ArrayAdapter
 import android.widget.Button
 import android.widget.ImageButton
@@ -14,8 +15,13 @@ import com.example.tdsaeandroid.viewmodel.ResultsViewModel
 import com.google.android.material.chip.Chip
 import com.google.android.material.chip.ChipGroup
 import androidx.activity.viewModels
+import androidx.lifecycle.lifecycleScope
 import com.example.tdsaeandroid.contract.DisplayMediaActivityContract
 import com.example.tdsaeandroid.dao.FilterContainer
+import com.example.tdsaeandroid.model.media.Genre
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
 
 class ResultsActivity : AppCompatActivity() {
 
@@ -52,30 +58,26 @@ class ResultsActivity : AppCompatActivity() {
         type = (intent?.getSerializableExtra("type") ?: throw Exception("No activity type found")) as ActivityType
 
 
-        val nameValue = intent.getStringExtra("nameValue")
-        val filterContainerJson = intent.getStringExtra("filterContainer")
-        println("aa")
-        val filterContainer: FilterContainer? = if (filterContainerJson != null) {
-            json.decodeFromString(filterContainerJson)
-        } else {
-            null
-        }
-        println("bb")
-        resultsViewModel.init(nameValue, filterContainer, type)
-
-        adapter = MediaItemAdapter(this, resultsViewModel.getResults())
+        adapter = MediaItemAdapter(this@ResultsActivity, mutableListOf())
         listView.adapter = adapter
 
-        updateButton(btPreviousPage, !resultsViewModel.isFirstPage())
-        updateButton(btNextPage, !resultsViewModel.isLastPage())
+        updateButton(btPreviousPage, false)
+        updateButton(btNextPage, false)
 
+        lifecycleScope.launch {
+            loadResults()
+            adapter.addAll(resultsViewModel.getResults())
 
-        if (resultsViewModel.filterContainer != null) {
-            for (f in resultsViewModel.filterContainer?.getFilters()!!) {
-                val chip = Chip(this)
-                "${ f.getKey()}: ${ f.getValue() }".also { chip.text = it }
-                chip.isChecked = true
-                chipGroup.addView(chip)
+            updateButton(btPreviousPage, !resultsViewModel.isFirstPage())
+            updateButton(btNextPage, !resultsViewModel.isLastPage())
+
+            if (resultsViewModel.filterContainer != null) {
+                for (f in resultsViewModel.filterContainer?.getFilters()!!) {
+                    val chip = Chip(this@ResultsActivity)
+                    "${ f.getKey()}: ${ f.getValue() }".also { chip.text = it }
+                    chip.isChecked = true
+                    chipGroup.addView(chip)
+                }
             }
         }
 
@@ -125,4 +127,17 @@ class ResultsActivity : AppCompatActivity() {
         bt.isEnabled = isEnable
         bt.isClickable = isEnable
     }
+
+    private suspend fun loadResults() {
+        val nameValue = intent.getStringExtra("nameValue")
+        val filterContainerJson = intent.getStringExtra("filterContainer")
+        val filterContainer: FilterContainer? = if (filterContainerJson != null) {
+            json.decodeFromString(filterContainerJson)
+        } else {
+            null
+        }
+        return withContext(Dispatchers.IO) {
+            resultsViewModel.init(nameValue, filterContainer, type)
+        }
+    }
 }
\ No newline at end of file
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 1a77dc7..4dfcd00 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,5 +1,6 @@
 [versions]
 activityKtx = "1.10.1"
+activityKtxVersion = "1.7.2"
 agp = "8.5.2"
 kotlin = "1.9.0"
 coreKtx = "1.10.1"
@@ -9,6 +10,7 @@ espressoCore = "3.5.1"
 appcompat = "1.6.1"
 kotlinxSerializationJson = "1.6.3"
 ktorVersion = "2.3.6"
+lifecycleRuntimeKtx = "2.8.7"
 material = "1.10.0"
 activity = "1.8.0"
 constraintlayout = "2.1.4"
@@ -18,7 +20,9 @@ junitJupiter = "5.8.1"
 
 [libraries]
 androidx-activity-ktx = { module = "androidx.activity:activity-ktx", version.ref = "activityKtx" }
+androidx-activity-ktx-v172 = { module = "androidx.activity:activity-ktx", version.ref = "activityKtxVersion" }
 androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
+androidx-lifecycle-runtime-ktx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
 junit = { group = "junit", name = "junit", version.ref = "junit" }
 androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
 androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
-- 
GitLab