Skip to content

Commit 7cdbc43

Browse files
committed
Add FileName, FilePath + Camera to MetaData options
Add sorting and filtering (by content type, image/video) in all views.
1 parent 590d29f commit 7cdbc43

19 files changed

+340
-113
lines changed

app/src/main/java/nl/giejay/android/tv/immich/album/AlbumDetailsFragment.kt

Lines changed: 20 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package nl.giejay.android.tv.immich.album
22

3-
import android.os.Bundle
4-
import android.view.View
53
import androidx.navigation.fragment.findNavController
64
import arrow.core.Either
75
import arrow.core.flatMap
@@ -10,48 +8,39 @@ import nl.giejay.android.tv.immich.api.model.Asset
108
import nl.giejay.android.tv.immich.assets.GenericAssetFragment
119
import nl.giejay.android.tv.immich.card.Card
1210
import nl.giejay.android.tv.immich.home.HomeFragmentDirections
13-
import nl.giejay.android.tv.immich.shared.prefs.LivePreference
14-
import nl.giejay.android.tv.immich.shared.prefs.LiveSharedPreferences
15-
import nl.giejay.android.tv.immich.shared.prefs.PHOTOS_SORTING
11+
import nl.giejay.android.tv.immich.shared.prefs.ContentType
12+
import nl.giejay.android.tv.immich.shared.prefs.EnumByTitlePref
13+
import nl.giejay.android.tv.immich.shared.prefs.FILTER_CONTENT_TYPE_FOR_SPECIFIC_ALBUM
1614
import nl.giejay.android.tv.immich.shared.prefs.PHOTOS_SORTING_FOR_SPECIFIC_ALBUM
1715
import nl.giejay.android.tv.immich.shared.prefs.PhotosOrder
18-
import nl.giejay.android.tv.immich.shared.prefs.Pref
19-
import nl.giejay.android.tv.immich.shared.prefs.PreferenceManager
20-
2116

2217

2318
class AlbumDetailsFragment : GenericAssetFragment() {
2419
private lateinit var albumId: String
2520
private lateinit var albumName: String
26-
private var currentSort: PhotosOrder? = null
27-
private lateinit var prefKey: PHOTOS_SORTING_FOR_SPECIFIC_ALBUM
2821
private var pageToBucket: Map<Int, String>? = null
2922

30-
override fun onCreate(savedInstanceState: Bundle?) {
23+
override fun getFilterKey(): EnumByTitlePref<ContentType> {
3124
albumId = AlbumDetailsFragmentArgs.fromBundle(requireArguments()).albumId
3225
albumName = AlbumDetailsFragmentArgs.fromBundle(requireArguments()).albumName
33-
prefKey = PHOTOS_SORTING_FOR_SPECIFIC_ALBUM(albumId)
34-
currentSort = PhotosOrder.valueOf(PreferenceManager.getString(prefKey.key(), prefKey.defaultValue.toString()))
35-
PreferenceManager.subscribe(prefKey) { state ->
36-
if(state != currentSort){
37-
// need to refetch everything because of the timeline buckets approach
38-
clearState()
39-
pageToBucket = null
40-
currentSort = state
41-
fetchInitialItems()
42-
}
43-
}
44-
super.onCreate(savedInstanceState)
26+
return FILTER_CONTENT_TYPE_FOR_SPECIFIC_ALBUM(albumId, albumName)
27+
}
28+
29+
override fun getSortingKey(): EnumByTitlePref<PhotosOrder> {
30+
albumId = AlbumDetailsFragmentArgs.fromBundle(requireArguments()).albumId
31+
albumName = AlbumDetailsFragmentArgs.fromBundle(requireArguments()).albumName
32+
return PHOTOS_SORTING_FOR_SPECIFIC_ALBUM(albumId, albumName)
4533
}
4634

47-
override fun sortItems(items: List<Asset>): List<Asset> {
48-
return items.sortedWith(currentSort!!.sort)
35+
override fun clearState() {
36+
super.clearState()
37+
pageToBucket = null
4938
}
5039

5140
override suspend fun loadData(): Either<String, List<Asset>> {
5241
if (pageToBucket == null) {
5342
// initial call, fetch the buckets
54-
val listBuckets = apiClient.listBuckets(albumId, currentSort!!)
43+
val listBuckets = apiClient.listBuckets(albumId, currentSort)
5544
return listBuckets.map { list ->
5645
pageToBucket = list.associateBy({ list.indexOf(it) + 1 }, { it.timeBucket })
5746
return internalLoadData(emptyList())
@@ -63,8 +52,10 @@ class AlbumDetailsFragment : GenericAssetFragment() {
6352

6453
private suspend fun internalLoadData(prevAssets: List<Asset>): Either<String, List<Asset>> {
6554
return loadItems(apiClient, currentPage, FETCH_PAGE_COUNT).flatMap {
66-
val combined = it + prevAssets
67-
if (combined.size <= FETCH_COUNT && !allPagesLoaded(it)) {
55+
val filteredItems = it.filter { asset -> currentFilter == ContentType.ALL || asset.type.lowercase() == currentFilter.toString().lowercase() }
56+
val combined = filteredItems + prevAssets
57+
allPagesLoaded = allPagesLoaded(it)
58+
if (combined.size <= FETCH_COUNT && !allPagesLoaded) {
6859
// immediately load next bucket
6960
currentPage += 1
7061
internalLoadData(combined)
@@ -77,7 +68,7 @@ class AlbumDetailsFragment : GenericAssetFragment() {
7768
override suspend fun loadItems(apiClient: ApiClient, page: Int, pageCount: Int): Either<String, List<Asset>> {
7869
val bucketForPage = pageToBucket!![page]
7970
return if (bucketForPage != null) {
80-
apiClient.getAssetsForBucket(albumId, bucketForPage, currentSort!!).map { it.map { a -> a.copy(albumName = albumName) } }
71+
apiClient.getAssetsForBucket(albumId, bucketForPage, currentSort).map { it.map { a -> a.copy(albumName = albumName) } }
8172
} else {
8273
Either.Right(emptyList())
8374
}

app/src/main/java/nl/giejay/android/tv/immich/api/ApiClient.kt

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,8 @@ class ApiClient(private val config: ApiClientConfig) {
7070
return executeAPICall(200) {
7171
val response = service.listAssetsFromAlbum(albumId)
7272
val album = response.body()
73-
val assets = album!!.assets.filter(excludeByTag())
74-
.map { Asset(it.id, it.type, it.deviceAssetId, it.exifInfo, it.fileModifiedAt, album.albumName, it.people, it.tags) }
75-
val updatedAlbum = AlbumDetails(album.albumName, album.description, album.id, album.albumThumbnailAssetId, assets)
76-
Response.success(updatedAlbum)
73+
val assets = album!!.assets.filter(excludeByTag()).map { it.copy(albumName = album.albumName) }
74+
Response.success(album.copy(assets = assets))
7775
}
7876
}
7977

@@ -122,8 +120,9 @@ class ApiClient(private val config: ApiClientConfig) {
122120
executeAPICall(200) { service.listAssets(searchRequest) }.map { res -> res.assets.items }
123121
}).map { it.filter(excludeByTag()) }.map {
124122
val excludedAlbums = PreferenceManager.get(EXCLUDE_ASSETS_IN_ALBUM)
125-
if(excludedAlbums.isNotEmpty()){
126-
val excludedAssets = excludedAlbums.toList().flatMap { albumId -> listAssetsFromAlbum(albumId).getOrNull()?.assets ?: emptyList() }.map { it.id }
123+
if (excludedAlbums.isNotEmpty()) {
124+
val excludedAssets =
125+
excludedAlbums.toList().flatMap { albumId -> listAssetsFromAlbum(albumId).getOrNull()?.assets ?: emptyList() }.map { it.id }
127126
it.filterNot { asset -> excludedAssets.contains(asset.id) }
128127
} else {
129128
it
@@ -147,7 +146,7 @@ class ApiClient(private val config: ApiClientConfig) {
147146
}.map {
148147
it.id.pmap { t -> service.getAsset(t).body()!! }.toList()
149148
}
150-
if(response.isLeft()){
149+
if (response.isLeft()) {
151150
return executeAPICall(200) {
152151
service.getBucket(albumId = albumId, timeBucket = bucket, order = if (order == PhotosOrder.OLDEST_NEWEST) "asc" else "desc")
153152
}

app/src/main/java/nl/giejay/android/tv/immich/api/model/Asset.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ data class AssetExifInfo(
99
val exifImageHeight: Int?,
1010
val city: String?,
1111
val country: String?,
12-
val dateTimeOriginal: Date?
12+
val dateTimeOriginal: Date?,
13+
val make: String?,
14+
val model: String?
1315
)
1416

1517
data class Tag(
@@ -27,5 +29,7 @@ data class Asset(
2729
val fileModifiedAt: Date?,
2830
val albumName: String?,
2931
val people: List<Person>?,
30-
val tags: List<Tag>?
32+
val tags: List<Tag>?,
33+
val originalPath: String?,
34+
val originalFileName: String?
3135
)

app/src/main/java/nl/giejay/android/tv/immich/assets/AllAssetFragment.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package nl.giejay.android.tv.immich.assets
33
import arrow.core.Either
44
import nl.giejay.android.tv.immich.api.ApiClient
55
import nl.giejay.android.tv.immich.api.model.Asset
6-
import nl.giejay.android.tv.immich.shared.prefs.ALL_ASSETS_SORTING
76
import nl.giejay.android.tv.immich.shared.prefs.PhotosOrder
87
import nl.giejay.android.tv.immich.shared.prefs.PreferenceManager
98
import nl.giejay.android.tv.immich.shared.prefs.SLIDER_SHOW_MEDIA_COUNT
@@ -18,7 +17,7 @@ class AllAssetFragment : GenericAssetFragment() {
1817
return apiClient.listAssets(page,
1918
pageCount,
2019
false,
21-
if (PreferenceManager.get(ALL_ASSETS_SORTING) == PhotosOrder.NEWEST_OLDEST) "desc" else "asc")
20+
if (currentSort == PhotosOrder.NEWEST_OLDEST) "desc" else "asc")
2221
}
2322

2423
override fun showMediaCount(): Boolean {

app/src/main/java/nl/giejay/android/tv/immich/assets/GenericAssetFragment.kt

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
11
package nl.giejay.android.tv.immich.assets
22

3+
import android.os.Bundle
34
import androidx.navigation.fragment.findNavController
4-
import nl.giejay.mediaslider.model.MetaDataType
55
import nl.giejay.android.tv.immich.album.AlbumDetailsFragmentDirections
66
import nl.giejay.android.tv.immich.api.model.Asset
77
import nl.giejay.android.tv.immich.api.util.ApiUtil
88
import nl.giejay.android.tv.immich.card.Card
9+
import nl.giejay.android.tv.immich.home.HomeFragmentDirections
910
import nl.giejay.android.tv.immich.shared.fragment.VerticalCardGridFragment
11+
import nl.giejay.android.tv.immich.shared.prefs.ALL_ASSETS_SORTING
12+
import nl.giejay.android.tv.immich.shared.prefs.ContentType
1013
import nl.giejay.android.tv.immich.shared.prefs.DEBUG_MODE
14+
import nl.giejay.android.tv.immich.shared.prefs.EnumByTitlePref
15+
import nl.giejay.android.tv.immich.shared.prefs.FILTER_CONTENT_TYPE
1116
import nl.giejay.android.tv.immich.shared.prefs.MetaDataScreen
17+
import nl.giejay.android.tv.immich.shared.prefs.PhotosOrder
1218
import nl.giejay.android.tv.immich.shared.prefs.PreferenceManager
1319
import nl.giejay.android.tv.immich.shared.prefs.SCREENSAVER_ANIMATE_ASSET_SLIDE
1420
import nl.giejay.android.tv.immich.shared.prefs.SLIDER_ANIMATION_SPEED
@@ -18,19 +24,45 @@ import nl.giejay.android.tv.immich.shared.prefs.SLIDER_MAX_CUT_OFF_HEIGHT
1824
import nl.giejay.android.tv.immich.shared.prefs.SLIDER_MAX_CUT_OFF_WIDTH
1925
import nl.giejay.android.tv.immich.shared.prefs.SLIDER_MERGE_PORTRAIT_PHOTOS
2026
import nl.giejay.android.tv.immich.shared.prefs.SLIDER_ONLY_USE_THUMBNAILS
21-
import nl.giejay.android.tv.immich.shared.prefs.SLIDER_SHOW_CITY
22-
import nl.giejay.android.tv.immich.shared.prefs.SLIDER_SHOW_DATE
23-
import nl.giejay.android.tv.immich.shared.prefs.SLIDER_SHOW_DESCRIPTION
2427
import nl.giejay.android.tv.immich.shared.util.toCard
2528
import nl.giejay.android.tv.immich.shared.util.toSliderItems
2629
import nl.giejay.mediaslider.util.LoadMore
2730
import nl.giejay.mediaslider.config.MediaSliderConfiguration
28-
import java.util.EnumSet
2931

3032
abstract class GenericAssetFragment : VerticalCardGridFragment<Asset>() {
33+
protected lateinit var currentFilter: ContentType
34+
protected lateinit var currentSort: PhotosOrder
35+
36+
override fun onCreate(savedInstanceState: Bundle?) {
37+
val sortingKey = getSortingKey()
38+
val filterKey = getFilterKey()
39+
currentSort = PreferenceManager.get(sortingKey)
40+
currentFilter = PreferenceManager.get(filterKey)
41+
super.onCreate(savedInstanceState)
42+
PreferenceManager.subscribeMultiple(listOf(sortingKey, filterKey)) { state ->
43+
if(state[sortingKey.key()] != currentSort || state[filterKey.key()] != currentFilter){
44+
clearState()
45+
currentSort = state[sortingKey.key()] as PhotosOrder
46+
currentFilter = state[filterKey.key()] as ContentType
47+
fetchInitialItems()
48+
}
49+
}
50+
}
51+
52+
override fun filterItems(items: List<Asset>): List<Asset> {
53+
return items.filter { currentFilter == ContentType.ALL || it.type.lowercase() == currentFilter.toString().lowercase() }
54+
}
55+
56+
open fun getSortingKey(): EnumByTitlePref<PhotosOrder>{
57+
return ALL_ASSETS_SORTING
58+
}
59+
60+
open fun getFilterKey(): EnumByTitlePref<ContentType>{
61+
return FILTER_CONTENT_TYPE
62+
}
3163

3264
override fun sortItems(items: List<Asset>): List<Asset> {
33-
return items
65+
return items.sortedWith(currentSort.sort)
3466
}
3567

3668
override fun onItemSelected(card: Card, indexOf: Int) {
@@ -41,10 +73,16 @@ abstract class GenericAssetFragment : VerticalCardGridFragment<Asset>() {
4173
return false
4274
}
4375

76+
override fun openPopUpMenu() {
77+
findNavController().navigate(
78+
HomeFragmentDirections.actionGlobalToSettingsDialog("generic_asset_settings")
79+
)
80+
}
81+
4482
override fun onItemClicked(card: Card) {
4583
val toSliderItems = assets.toSliderItems(keepOrder = true, mergePortrait = PreferenceManager.get(SLIDER_MERGE_PORTRAIT_PHOTOS))
4684
val loadMore: LoadMore = suspend {
47-
loadAssets().toSliderItems(true, PreferenceManager.get(SLIDER_MERGE_PORTRAIT_PHOTOS))
85+
loadMoreAssets().toSliderItems(true, PreferenceManager.get(SLIDER_MERGE_PORTRAIT_PHOTOS))
4886
}
4987

5088
findNavController().navigate(

app/src/main/java/nl/giejay/android/tv/immich/settings/AlbumDetailsSettingsFragment.kt

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ import android.os.Bundle
44
import android.view.View
55
import androidx.preference.ListPreference
66
import androidx.preference.Preference
7-
import androidx.preference.PreferenceScreen
87
import arrow.core.Either
9-
import arrow.core.left
10-
import nl.giejay.android.tv.immich.R
11-
import nl.giejay.android.tv.immich.shared.prefs.PHOTOS_SORTING_FOR_SPECIFIC_ALBUM
12-
import nl.giejay.android.tv.immich.shared.prefs.PhotosOrder
8+
import nl.giejay.android.tv.immich.shared.prefs.ALL_ASSETS_SORTING
9+
import nl.giejay.android.tv.immich.shared.prefs.AlbumDetailsSettingsScreen
10+
import nl.giejay.android.tv.immich.shared.prefs.EnumByTitlePref
11+
import nl.giejay.android.tv.immich.shared.prefs.EnumWithTitle
12+
import nl.giejay.android.tv.immich.shared.prefs.FILTER_CONTENT_TYPE
1313
import nl.giejay.android.tv.immich.shared.prefs.PrefScreen
1414
import nl.giejay.android.tv.immich.shared.prefs.PreferenceManager
1515

@@ -19,25 +19,12 @@ class AlbumDetailsSettingsFragment : SettingsScreenFragment(){
1919
}
2020
}
2121
class AlbumDetailsInnerSettingsFragment : SettingsScreenFragment.SettingsInnerFragment() {
22-
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
23-
super.onViewCreated(view, savedInstanceState)
24-
val bundle = requireArguments()
25-
val albumId = bundle.getString("albumId")!!
26-
val albumName = bundle.getString("albumName")
27-
setTitle("$albumName settings")
28-
val sortPref = findPreference<ListPreference>("photos_sorting_specific_album")!!
29-
val photosSortingForSpecificAlbum = PHOTOS_SORTING_FOR_SPECIFIC_ALBUM(albumId)
30-
val sortingForAlbum = PreferenceManager.getString(photosSortingForSpecificAlbum.key(), photosSortingForSpecificAlbum.defaultValue.toString())
31-
sortPref.value = sortingForAlbum
32-
sortPref.title = "Sort photos in $albumName by"
33-
sortPref.onPreferenceChangeListener = Preference.OnPreferenceChangeListener{_, newValue ->
34-
PreferenceManager.save(photosSortingForSpecificAlbum, PhotosOrder.valueOf(newValue as String))
35-
true
36-
}
37-
}
3822

3923
override fun getLayout(): Either<Int, PrefScreen> {
40-
return Either.Left(R.xml.preferences_album_details)
24+
val bundle = requireArguments()
25+
val albumId = bundle.getString("albumId")!!
26+
val albumName = bundle.getString("albumName")!!
27+
return Either.Right(AlbumDetailsSettingsScreen(albumId, albumName))
4128
}
4229

4330
override fun handlePreferenceClick(preference: Preference?): Boolean {
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package nl.giejay.android.tv.immich.settings
2+
3+
import android.os.Bundle
4+
import android.view.LayoutInflater
5+
import android.view.View
6+
import android.view.ViewGroup
7+
import androidx.navigation.NavController
8+
import androidx.navigation.fragment.FragmentNavigator
9+
import androidx.navigation.fragment.findNavController
10+
import androidx.preference.ListPreference
11+
import androidx.preference.Preference
12+
import arrow.core.Either
13+
import nl.giejay.android.tv.immich.shared.prefs.ALL_ASSETS_SORTING
14+
import nl.giejay.android.tv.immich.shared.prefs.AlbumDetailsSettingsScreen
15+
import nl.giejay.android.tv.immich.shared.prefs.FILTER_CONTENT_TYPE
16+
import nl.giejay.android.tv.immich.shared.prefs.GenericAssetsSettingsScreen
17+
import nl.giejay.android.tv.immich.shared.prefs.PrefScreen
18+
import nl.giejay.android.tv.immich.shared.prefs.PreferenceManager
19+
20+
class GenericAssetsSettingsFragment : SettingsScreenFragment(){
21+
override fun getFragment(): SettingsInnerFragment {
22+
return GenericAssetsInnerSettingsFragment()
23+
}
24+
}
25+
class GenericAssetsInnerSettingsFragment : SettingsScreenFragment.SettingsInnerFragment() {
26+
27+
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
28+
super.onViewCreated(view, savedInstanceState)
29+
findPreference<ListPreference>(ALL_ASSETS_SORTING.key())!!.summary = ALL_ASSETS_SORTING.getValue(PreferenceManager.sharedPreference).getTitle()
30+
findPreference<ListPreference>(FILTER_CONTENT_TYPE.key())!!.summary = FILTER_CONTENT_TYPE.getValue(PreferenceManager.sharedPreference).getTitle()
31+
}
32+
33+
override fun getLayout(): Either<Int, PrefScreen> {
34+
return Either.Right(GenericAssetsSettingsScreen)
35+
}
36+
37+
override fun handlePreferenceClick(preference: Preference?): Boolean {
38+
return false
39+
}
40+
41+
}

app/src/main/java/nl/giejay/android/tv/immich/settings/SettingsDialogFragment.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ class SettingsDialogFragment : DialogFragment() {
2727
frag.arguments = requireArguments()
2828
frag
2929
}
30+
"generic_asset_settings" -> {
31+
GenericAssetsSettingsFragment()
32+
}
3033
"meta_data_item" -> {
3134
// todo refactor this, make MetaDataItemCustomizerFragment a child of SettingsDialogFragment and include it in the navGraph
3235
val frag = MetaDataItemCustomizerFragment()

app/src/main/java/nl/giejay/android/tv/immich/settings/SettingsScreenFragment.kt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package nl.giejay.android.tv.immich.settings
22

33
import android.os.Bundle
4+
import android.view.View
45
import androidx.fragment.app.Fragment
56
import androidx.leanback.preference.LeanbackPreferenceFragmentCompat
67
import androidx.leanback.preference.LeanbackSettingsFragmentCompat
@@ -11,7 +12,10 @@ import androidx.preference.PreferenceFragmentCompat
1112
import androidx.preference.PreferenceScreen
1213
import arrow.core.Either
1314
import nl.giejay.android.tv.immich.R
15+
import nl.giejay.android.tv.immich.shared.prefs.EnumByTitlePref
16+
import nl.giejay.android.tv.immich.shared.prefs.EnumWithTitle
1417
import nl.giejay.android.tv.immich.shared.prefs.PrefScreen
18+
import nl.giejay.android.tv.immich.shared.prefs.PreferenceManager
1519

1620

1721
abstract class SettingsScreenFragment : LeanbackSettingsFragmentCompat() {
@@ -59,6 +63,19 @@ abstract class SettingsScreenFragment : LeanbackSettingsFragmentCompat() {
5963
abstract class SettingsInnerFragment : LeanbackPreferenceFragmentCompat() {
6064
abstract fun getLayout(): Either<Int, PrefScreen>
6165

66+
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
67+
super.onViewCreated(view, savedInstanceState)
68+
69+
// after changing a pref, set the summary again for all listprefs with enumWithTitle
70+
getLayout().map { prefScreen ->
71+
val prefs = prefScreen.children.flatMap { it.children }
72+
prefs.filterIsInstance(EnumByTitlePref::class.java).forEach{
73+
val value: EnumWithTitle = it.getValue(PreferenceManager.sharedPreference) as EnumWithTitle
74+
findPreference<Preference>(it.key())!!.summary = value.getTitle()
75+
}
76+
}
77+
}
78+
6279
abstract fun handlePreferenceClick(preference: Preference?): Boolean
6380
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
6481
// Load the preferences from an XML resource

0 commit comments

Comments
 (0)