From 7a7cb069a79626d3b978dffa0c2050f42e6a0fe8 Mon Sep 17 00:00:00 2001
From: Natanel Shitrit <65548905+Natanel-Shitrit@users.noreply.github.com>
Date: Thu, 24 Aug 2023 20:49:46 +0300
Subject: [PATCH 1/3] Add resources, add button to layout

---
 .../src/main/res/layout/season_header.xml     | 11 +++++
 .../main/res/drawable/ic_arrow_down_0_1.xml   | 41 +++++++++++++++++++
 .../main/res/drawable/ic_arrow_down_1_0.xml   | 41 +++++++++++++++++++
 3 files changed, 93 insertions(+)
 create mode 100644 core/src/main/res/drawable/ic_arrow_down_0_1.xml
 create mode 100644 core/src/main/res/drawable/ic_arrow_down_1_0.xml

diff --git a/app/phone/src/main/res/layout/season_header.xml b/app/phone/src/main/res/layout/season_header.xml
index 12901a25f4..59b03c7349 100644
--- a/app/phone/src/main/res/layout/season_header.xml
+++ b/app/phone/src/main/res/layout/season_header.xml
@@ -66,4 +66,15 @@
         app:layout_constraintStart_toEndOf="@id/season_poster"
         tools:text="Season 1" />
 
+    <com.google.android.material.button.MaterialButton
+        android:id="@+id/episode_order_button"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginHorizontal="24dp"
+        style="?attr/materialIconButtonFilledTonalStyle"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:icon="@drawable/ic_arrow_down_0_1"
+        tools:visibility="visible" />
+
 </androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/core/src/main/res/drawable/ic_arrow_down_0_1.xml b/core/src/main/res/drawable/ic_arrow_down_0_1.xml
new file mode 100644
index 0000000000..7a1c34dad2
--- /dev/null
+++ b/core/src/main/res/drawable/ic_arrow_down_0_1.xml
@@ -0,0 +1,41 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="m3,16 l4,4 4,-4"
+      android:strokeLineJoin="round"
+      android:strokeWidth="2"
+      android:fillColor="#00000000"
+      android:strokeColor="@android:color/white"
+      android:strokeLineCap="round"/>
+  <path
+      android:pathData="M7,20V4"
+      android:strokeLineJoin="round"
+      android:strokeWidth="2"
+      android:fillColor="#00000000"
+      android:strokeColor="@android:color/white"
+      android:strokeLineCap="round"/>
+  <path
+      android:pathData="M17,4L17,4A2,2 0,0 1,19 6L19,8A2,2 0,0 1,17 10L17,10A2,2 0,0 1,15 8L15,6A2,2 0,0 1,17 4z"
+      android:strokeLineJoin="round"
+      android:strokeWidth="2"
+      android:fillColor="#00000000"
+      android:strokeColor="@android:color/white"
+      android:strokeLineCap="round"/>
+  <path
+      android:pathData="M17,20v-6h-2"
+      android:strokeLineJoin="round"
+      android:strokeWidth="2"
+      android:fillColor="#00000000"
+      android:strokeColor="@android:color/white"
+      android:strokeLineCap="round"/>
+  <path
+      android:pathData="M15,20h4"
+      android:strokeLineJoin="round"
+      android:strokeWidth="2"
+      android:fillColor="#00000000"
+      android:strokeColor="@android:color/white"
+      android:strokeLineCap="round"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_arrow_down_1_0.xml b/core/src/main/res/drawable/ic_arrow_down_1_0.xml
new file mode 100644
index 0000000000..379c7c7c2a
--- /dev/null
+++ b/core/src/main/res/drawable/ic_arrow_down_1_0.xml
@@ -0,0 +1,41 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="m3,16 l4,4 4,-4"
+      android:strokeLineJoin="round"
+      android:strokeWidth="2"
+      android:fillColor="#00000000"
+      android:strokeColor="@android:color/white"
+      android:strokeLineCap="round"/>
+  <path
+      android:pathData="M7,20V4"
+      android:strokeLineJoin="round"
+      android:strokeWidth="2"
+      android:fillColor="#00000000"
+      android:strokeColor="@android:color/white"
+      android:strokeLineCap="round"/>
+  <path
+      android:pathData="M17,10V4h-2"
+      android:strokeLineJoin="round"
+      android:strokeWidth="2"
+      android:fillColor="#00000000"
+      android:strokeColor="@android:color/white"
+      android:strokeLineCap="round"/>
+  <path
+      android:pathData="M15,10h4"
+      android:strokeLineJoin="round"
+      android:strokeWidth="2"
+      android:fillColor="#00000000"
+      android:strokeColor="@android:color/white"
+      android:strokeLineCap="round"/>
+  <path
+      android:pathData="M17,14L17,14A2,2 0,0 1,19 16L19,18A2,2 0,0 1,17 20L17,20A2,2 0,0 1,15 18L15,16A2,2 0,0 1,17 14z"
+      android:strokeLineJoin="round"
+      android:strokeWidth="2"
+      android:fillColor="#00000000"
+      android:strokeColor="@android:color/white"
+      android:strokeLineCap="round"/>
+</vector>

From eaa4e722d28d81f887d2c2693ba1a7e236863a1b Mon Sep 17 00:00:00 2001
From: Natanel Shitrit <65548905+Natanel-Shitrit@users.noreply.github.com>
Date: Fri, 25 Aug 2023 16:12:40 +0300
Subject: [PATCH 2/3] Move button to top app bar, implement reverse sort

---
 .../jellyfin/fragments/SeasonFragment.kt      | 42 ++++++++++++++++++-
 .../src/main/res/layout/season_header.xml     | 11 -----
 .../jellyfin/viewmodels/SeasonViewModel.kt    |  9 ++--
 core/src/main/res/menu/season_menu.xml        | 10 +++++
 core/src/main/res/values/strings.xml          |  1 +
 5 files changed, 56 insertions(+), 17 deletions(-)
 create mode 100644 core/src/main/res/menu/season_menu.xml

diff --git a/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/SeasonFragment.kt b/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/SeasonFragment.kt
index fcc7e973e2..f87f9c66c8 100644
--- a/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/SeasonFragment.kt
+++ b/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/SeasonFragment.kt
@@ -2,8 +2,14 @@ package dev.jdtech.jellyfin.fragments
 
 import android.os.Bundle
 import android.view.LayoutInflater
+import android.view.Menu
+import android.view.MenuInflater
+import android.view.MenuItem
 import android.view.View
 import android.view.ViewGroup
+import androidx.appcompat.widget.SearchView
+import androidx.core.view.MenuHost
+import androidx.core.view.MenuProvider
 import androidx.core.view.isVisible
 import androidx.fragment.app.Fragment
 import androidx.fragment.app.viewModels
@@ -23,6 +29,7 @@ import dev.jdtech.jellyfin.viewmodels.PlayerViewModel
 import dev.jdtech.jellyfin.viewmodels.SeasonViewModel
 import kotlinx.coroutines.launch
 import timber.log.Timber
+import dev.jdtech.jellyfin.core.R as CoreR
 
 @AndroidEntryPoint
 class SeasonFragment : Fragment() {
@@ -34,6 +41,8 @@ class SeasonFragment : Fragment() {
 
     private lateinit var errorDialog: ErrorDialogFragment
 
+    private var ascendingEpisodeOrder: Boolean = true
+
     override fun onCreateView(
         inflater: LayoutInflater,
         container: ViewGroup?,
@@ -46,6 +55,35 @@ class SeasonFragment : Fragment() {
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
 
+        val menuHost: MenuHost = requireActivity()
+        menuHost.addMenuProvider(
+            object : MenuProvider {
+                override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
+                    menuInflater.inflate(CoreR.menu.season_menu, menu)
+                }
+
+                override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
+                    return when (menuItem.itemId) {
+                        CoreR.id.action_sort_order -> {
+                            ascendingEpisodeOrder = !ascendingEpisodeOrder
+                            menuItem.setIcon(
+                                when (ascendingEpisodeOrder) {
+                                    true -> CoreR.drawable.ic_arrow_down_0_1
+                                    false -> CoreR.drawable.ic_arrow_down_1_0
+                                }
+                            )
+
+                            viewModel.loadEpisodes(args.seriesId, args.seasonId, args.offline, ascendingEpisodeOrder)
+                            true
+                        }
+                        else -> false
+                    }
+                }
+            },
+            viewLifecycleOwner,
+            Lifecycle.State.RESUMED,
+        )
+
         viewLifecycleOwner.lifecycleScope.launch {
             viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
                 launch {
@@ -68,7 +106,7 @@ class SeasonFragment : Fragment() {
         }
 
         binding.errorLayout.errorRetryButton.setOnClickListener {
-            viewModel.loadEpisodes(args.seriesId, args.seasonId, args.offline)
+            viewModel.loadEpisodes(args.seriesId, args.seasonId, args.offline, ascendingEpisodeOrder)
         }
 
         playerViewModel.onPlaybackRequested(lifecycleScope) { playerItems ->
@@ -95,7 +133,7 @@ class SeasonFragment : Fragment() {
     override fun onResume() {
         super.onResume()
 
-        viewModel.loadEpisodes(args.seriesId, args.seasonId, args.offline)
+        viewModel.loadEpisodes(args.seriesId, args.seasonId, args.offline, ascendingEpisodeOrder)
     }
 
     private fun bindUiStateNormal(uiState: SeasonViewModel.UiState.Normal) {
diff --git a/app/phone/src/main/res/layout/season_header.xml b/app/phone/src/main/res/layout/season_header.xml
index 59b03c7349..12901a25f4 100644
--- a/app/phone/src/main/res/layout/season_header.xml
+++ b/app/phone/src/main/res/layout/season_header.xml
@@ -66,15 +66,4 @@
         app:layout_constraintStart_toEndOf="@id/season_poster"
         tools:text="Season 1" />
 
-    <com.google.android.material.button.MaterialButton
-        android:id="@+id/episode_order_button"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:layout_marginHorizontal="24dp"
-        style="?attr/materialIconButtonFilledTonalStyle"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:icon="@drawable/ic_arrow_down_0_1"
-        tools:visibility="visible" />
-
 </androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/core/src/main/java/dev/jdtech/jellyfin/viewmodels/SeasonViewModel.kt b/core/src/main/java/dev/jdtech/jellyfin/viewmodels/SeasonViewModel.kt
index 62b127bb9e..76eeefeeba 100644
--- a/core/src/main/java/dev/jdtech/jellyfin/viewmodels/SeasonViewModel.kt
+++ b/core/src/main/java/dev/jdtech/jellyfin/viewmodels/SeasonViewModel.kt
@@ -35,12 +35,12 @@ constructor(
 
     lateinit var season: FindroidSeason
 
-    fun loadEpisodes(seriesId: UUID, seasonId: UUID, offline: Boolean) {
+    fun loadEpisodes(seriesId: UUID, seasonId: UUID, offline: Boolean, ascendingOrder: Boolean) {
         viewModelScope.launch {
             _uiState.emit(UiState.Loading)
             try {
                 season = getSeason(seasonId)
-                val episodes = getEpisodes(seriesId, seasonId, offline)
+                val episodes = getEpisodes(seriesId, seasonId, offline, ascendingOrder)
                 _uiState.emit(UiState.Normal(episodes))
             } catch (_: NullPointerException) {
                 // Navigate back because item does not exist (probably because it's been deleted)
@@ -55,11 +55,12 @@ constructor(
         return jellyfinRepository.getSeason(seasonId)
     }
 
-    private suspend fun getEpisodes(seriesId: UUID, seasonId: UUID, offline: Boolean): List<EpisodeItem> {
+    private suspend fun getEpisodes(seriesId: UUID, seasonId: UUID, offline: Boolean, ascendingOrder: Boolean): List<EpisodeItem> {
         val header = EpisodeItem.Header(seriesId = season.seriesId, seasonId = season.id, seriesName = season.seriesName, seasonName = season.name)
         val episodes =
             jellyfinRepository.getEpisodes(seriesId, seasonId, fields = listOf(ItemFields.OVERVIEW), offline = offline)
+                .map { EpisodeItem.Episode(it) }
 
-        return listOf(header) + episodes.map { EpisodeItem.Episode(it) }
+        return listOf(header) + (if (ascendingOrder) episodes else episodes.reversed())
     }
 }
diff --git a/core/src/main/res/menu/season_menu.xml b/core/src/main/res/menu/season_menu.xml
new file mode 100644
index 0000000000..09fab748e9
--- /dev/null
+++ b/core/src/main/res/menu/season_menu.xml
@@ -0,0 +1,10 @@
+<?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/action_sort_order"
+        android:icon="@drawable/ic_arrow_down_0_1"
+        android:title="@string/title_sort_order"
+        app:showAsAction="always" />
+</menu>
\ No newline at end of file
diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml
index c78f6962c1..1efbf6b2a9 100644
--- a/core/src/main/res/values/strings.xml
+++ b/core/src/main/res/values/strings.xml
@@ -31,6 +31,7 @@
     <string name="title_favorite">Favorites</string>
     <string name="title_settings">Settings</string>
     <string name="title_download">Downloads</string>
+    <string name="title_sort_order">Sort Order</string>
     <string name="view_all">View all</string>
     <string name="error_loading_data">Error loading data</string>
     <string name="retry">Retry</string>

From 05d635f50b36bcf1480ee1afa884bd6cc93b6b4a Mon Sep 17 00:00:00 2001
From: Natanel Shitrit <65548905+Natanel-Shitrit@users.noreply.github.com>
Date: Fri, 25 Aug 2023 16:58:20 +0300
Subject: [PATCH 3/3] Fix linting

---
 .../main/java/dev/jdtech/jellyfin/fragments/SeasonFragment.kt  | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/SeasonFragment.kt b/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/SeasonFragment.kt
index f87f9c66c8..fffa57353a 100644
--- a/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/SeasonFragment.kt
+++ b/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/SeasonFragment.kt
@@ -7,7 +7,6 @@ import android.view.MenuInflater
 import android.view.MenuItem
 import android.view.View
 import android.view.ViewGroup
-import androidx.appcompat.widget.SearchView
 import androidx.core.view.MenuHost
 import androidx.core.view.MenuProvider
 import androidx.core.view.isVisible
@@ -70,7 +69,7 @@ class SeasonFragment : Fragment() {
                                 when (ascendingEpisodeOrder) {
                                     true -> CoreR.drawable.ic_arrow_down_0_1
                                     false -> CoreR.drawable.ic_arrow_down_1_0
-                                }
+                                },
                             )
 
                             viewModel.loadEpisodes(args.seriesId, args.seasonId, args.offline, ascendingEpisodeOrder)