From 3ba0696fda79374041ec0012d1bd94c27848f075 Mon Sep 17 00:00:00 2001 From: Niels van Velzen Date: Tue, 21 May 2024 13:44:06 +0200 Subject: [PATCH 01/15] Only use SDK types in BaseRowItem This also cleans the constructor hell a bit (although still a mess) --- .../androidtv/ui/itemhandling/BaseRowItem.kt | 54 +++---------------- .../ui/itemhandling/ItemLauncher.java | 3 +- .../ui/itemhandling/ItemRowAdapter.java | 42 +++++++-------- .../ui/itemhandling/ItemRowAdapterHelper.kt | 2 +- 4 files changed, 31 insertions(+), 70 deletions(-) diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseRowItem.kt b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseRowItem.kt index 9960fd59c8..ef78ce2b77 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseRowItem.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseRowItem.kt @@ -18,17 +18,15 @@ import org.jellyfin.androidtv.util.ImageHelper import org.jellyfin.androidtv.util.TimeUtils import org.jellyfin.androidtv.util.apiclient.LifecycleAwareResponse import org.jellyfin.androidtv.util.apiclient.getSeriesOverview -import org.jellyfin.androidtv.util.sdk.compat.asSdk import org.jellyfin.androidtv.util.sdk.getFullName import org.jellyfin.androidtv.util.sdk.getSubName -import org.jellyfin.apiclient.model.dto.BaseItemDto -import org.jellyfin.apiclient.model.livetv.ChannelInfoDto -import org.jellyfin.apiclient.model.livetv.SeriesTimerInfoDto import org.jellyfin.sdk.api.client.ApiClient import org.jellyfin.sdk.api.client.exception.ApiClientException import org.jellyfin.sdk.api.client.extensions.userLibraryApi +import org.jellyfin.sdk.model.api.BaseItemDto import org.jellyfin.sdk.model.api.BaseItemKind import org.jellyfin.sdk.model.api.BaseItemPerson +import org.jellyfin.sdk.model.api.SeriesTimerInfoDto import org.jellyfin.sdk.model.extensions.ticks import org.jellyfin.sdk.model.serializer.toUUID import org.koin.core.component.KoinComponent @@ -44,7 +42,7 @@ open class BaseRowItem protected constructor( val preferParentThumb: Boolean = false, val selectAction: BaseRowItemSelectAction = BaseRowItemSelectAction.ShowDetails, var playing: Boolean = false, - var baseItem: org.jellyfin.sdk.model.api.BaseItemDto? = null, + var baseItem: BaseItemDto? = null, val basePerson: BaseItemPerson? = null, val chapterInfo: ChapterItemInfo? = null, val seriesTimerInfo: SeriesTimerInfoDto? = null, @@ -63,7 +61,8 @@ open class BaseRowItem protected constructor( selectAction: BaseRowItemSelectAction = BaseRowItemSelectAction.ShowDetails, preferSeriesPoster: Boolean = false, ) : this( - baseRowType = when (item.asSdk().type) { + baseRowType = when (item.type) { + BaseItemKind.TV_CHANNEL -> BaseRowType.LiveTvChannel BaseItemKind.PROGRAM -> BaseRowType.LiveTvProgram BaseItemKind.RECORDING -> BaseRowType.LiveTvRecording else -> BaseRowType.BaseItem @@ -72,45 +71,8 @@ open class BaseRowItem protected constructor( staticHeight = staticHeight, preferParentThumb = preferParentThumb, selectAction = selectAction, - baseItem = item.asSdk(), - preferSeriesPoster = preferSeriesPoster, - ) - - @JvmOverloads - constructor( - item: org.jellyfin.sdk.model.api.BaseItemDto, - index: Int = 0, - preferParentThumb: Boolean = false, - staticHeight: Boolean = false, - ) : this( - index = index, - preferParentThumb = preferParentThumb, - staticHeight = staticHeight, - baseRowType = when (item.type) { - BaseItemKind.PROGRAM -> BaseRowType.LiveTvProgram - BaseItemKind.RECORDING -> BaseRowType.LiveTvRecording - else -> BaseRowType.BaseItem - }, baseItem = item, - ) - - constructor( - index: Int, - item: ChannelInfoDto, - ) : this( - index = index, - baseRowType = BaseRowType.LiveTvChannel, - baseItem = item.asSdk(), - ) - - constructor( - item: BaseItemDto, - staticHeight: Boolean, - ) : this( - index = 0, - item = item, - preferParentThumb = false, - staticHeight = staticHeight, + preferSeriesPoster = preferSeriesPoster, ) constructor( @@ -279,7 +241,7 @@ open class BaseRowItem protected constructor( BaseRowType.LiveTvRecording, BaseRowType.LiveTvProgram -> baseItem?.overview - BaseRowType.SeriesTimer -> seriesTimerInfo?.asSdk()?.getSeriesOverview(context) + BaseRowType.SeriesTimer -> seriesTimerInfo?.getSeriesOverview(context) else -> null }.orEmpty() @@ -331,7 +293,7 @@ open class BaseRowItem protected constructor( @JvmOverloads fun refresh( - outerResponse: LifecycleAwareResponse, + outerResponse: LifecycleAwareResponse, scope: CoroutineScope = ProcessLifecycleOwner.get().lifecycleScope, ) { if (baseRowType == BaseRowType.BaseItem) { diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemLauncher.java b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemLauncher.java index 77cea3b3a3..9232d10c39 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemLauncher.java +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemLauncher.java @@ -18,7 +18,6 @@ import org.jellyfin.androidtv.util.PlaybackHelper; import org.jellyfin.androidtv.util.Utils; import org.jellyfin.androidtv.util.sdk.compat.JavaCompat; -import org.jellyfin.androidtv.util.sdk.compat.ModelCompat; import org.jellyfin.apiclient.interaction.Response; import org.jellyfin.sdk.model.api.BaseItemDto; import org.jellyfin.sdk.model.api.BaseItemKind; @@ -254,7 +253,7 @@ public void onResponse(BaseItemDto response) { break; case SeriesTimer: - navigationRepository.getValue().navigate(Destinations.INSTANCE.seriesTimerDetails(UUIDSerializerKt.toUUID(rowItem.getItemId()), ModelCompat.asSdk(rowItem.getSeriesTimerInfo()))); + navigationRepository.getValue().navigate(Destinations.INSTANCE.seriesTimerDetails(UUIDSerializerKt.toUUID(rowItem.getItemId()), rowItem.getSeriesTimerInfo())); break; diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemRowAdapter.java b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemRowAdapter.java index 02b3d43e28..41da38fd1a 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemRowAdapter.java +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemRowAdapter.java @@ -819,7 +819,7 @@ public void onResponse(ItemsResult response) { ItemRowAdapterHelperKt.setItems(ItemRowAdapter.this, response.getItems(), (item, i) -> { if (userViewsRepository.getValue().isSupported(ModelCompat.asSdk(item).getCollectionType())) { item.setDisplayPreferencesId(item.getId()); - return new BaseRowItem(i, item, preferParentThumb, staticHeight); + return new BaseRowItem(i, ModelCompat.asSdk(item), preferParentThumb, staticHeight); } else { return null; } @@ -853,7 +853,7 @@ public void onResponse(ItemsResult response) { if (response.getItems() != null && response.getItems().length > 0) { setTotalItems(query.getEnableTotalRecordCount() ? response.getTotalRecordCount() : response.getItems().length); - ItemRowAdapterHelperKt.setItems(ItemRowAdapter.this, response.getItems(), (item, i) -> new BaseRowItem(i, item, getPreferParentThumb(), isStaticHeight())); + ItemRowAdapterHelperKt.setItems(ItemRowAdapter.this, response.getItems(), (item, i) -> new BaseRowItem(i, ModelCompat.asSdk(item), getPreferParentThumb(), isStaticHeight())); } else if (getItemsLoaded() == 0) { removeRow(); } @@ -885,7 +885,7 @@ public void onResponse(ItemsResult response) { if (response.getItems() != null && response.getItems().length > 0) { setTotalItems(response.getTotalRecordCount()); - ItemRowAdapterHelperKt.setItems(ItemRowAdapter.this, response.getItems(), (item, i) -> new BaseRowItem(i, item, getPreferParentThumb(), isStaticHeight())); + ItemRowAdapterHelperKt.setItems(ItemRowAdapter.this, response.getItems(), (item, i) -> new BaseRowItem(i, ModelCompat.asSdk(item), getPreferParentThumb(), isStaticHeight())); } else if (getItemsLoaded() == 0) { removeRow(); } @@ -909,7 +909,7 @@ public void onResponse(ItemsResult response) { if (response.getItems() != null && response.getItems().length > 0) { setTotalItems(response.getTotalRecordCount()); - ItemRowAdapterHelperKt.setItems(ItemRowAdapter.this, response.getItems(), (item, i) -> new BaseRowItem(i, item, getPreferParentThumb(), isStaticHeight())); + ItemRowAdapterHelperKt.setItems(ItemRowAdapter.this, response.getItems(), (item, i) -> new BaseRowItem(i, ModelCompat.asSdk(item), getPreferParentThumb(), isStaticHeight())); } else if (getItemsLoaded() == 0) { removeRow(); } @@ -933,7 +933,7 @@ public void onResponse(BaseItemDto[] response) { if (response != null && response.length > 0) { setTotalItems(response.length); - ItemRowAdapterHelperKt.setItems(ItemRowAdapter.this, response, (item, i) -> new BaseRowItem(i, item, getPreferParentThumb(), isStaticHeight(), BaseRowItemSelectAction.ShowDetails, getPreferParentThumb())); + ItemRowAdapterHelperKt.setItems(ItemRowAdapter.this, response, (item, i) -> new BaseRowItem(i, ModelCompat.asSdk(item), getPreferParentThumb(), isStaticHeight(), BaseRowItemSelectAction.ShowDetails, getPreferParentThumb())); } else if (getItemsLoaded() == 0) { removeRow(); } @@ -997,12 +997,12 @@ public void onResponse(ItemsResult response) { } if (existing == null) { Timber.d("Adding new episode 1 to premieres %s", item.getSeriesName()); - adapter.add(new BaseRowItem(i++, item, preferParentThumb, true)); + adapter.add(new BaseRowItem(i++, ModelCompat.asSdk(item), preferParentThumb, true)); } else if (existing.getBaseItem().getParentIndexNumber() > item.getParentIndexNumber()) { //Replace the newer item with the earlier season Timber.d("Replacing newer episode 1 with an older season for %s", item.getSeriesName()); - adapter.set(existingPos, new BaseRowItem(i++, item, preferParentThumb, false)); + adapter.set(existingPos, new BaseRowItem(i++, ModelCompat.asSdk(item), preferParentThumb, false)); } // otherwise, just ignore this newer season premiere since we have the older one already } else { @@ -1034,7 +1034,7 @@ private void retrieve(final NextUpQuery query) { public void onResponse(final ItemsResult response) { if (response.getItems() != null && response.getItems().length > 0) { setTotalItems(response.getTotalRecordCount()); - ItemRowAdapterHelperKt.setItems(ItemRowAdapter.this, response.getItems(), (item, i) -> new BaseRowItem(i, item, preferParentThumb, staticHeight)); + ItemRowAdapterHelperKt.setItems(ItemRowAdapter.this, response.getItems(), (item, i) -> new BaseRowItem(i, ModelCompat.asSdk(item), preferParentThumb, staticHeight)); //If this was for a single series, get the rest of the episodes in the season if (query.getSeriesId() != null) { @@ -1050,7 +1050,7 @@ public void onResponse(ItemsResult innerResponse) { if (response.getItems() != null) { int n = response.getItems().length; for (BaseItemDto item : innerResponse.getItems()) { - adapter.add(new BaseRowItem(n++, item, preferParentThumb, false)); + adapter.add(new BaseRowItem(n++, ModelCompat.asSdk(item), preferParentThumb, false)); } totalItems += innerResponse.getTotalRecordCount(); setItemsLoaded(itemsLoaded + n); @@ -1096,7 +1096,7 @@ public void onResponse(ChannelInfoDtoResult response) { adapter.clear(); } for (ChannelInfoDto item : response.getItems()) { - adapter.add(new BaseRowItem(i, item)); + adapter.add(new BaseRowItem(i, ModelCompat.asSdk(item))); i++; } totalItems = response.getTotalRecordCount(); @@ -1132,7 +1132,7 @@ public void onResponse(ItemsResult response) { int i = 0; int prevItems = Math.max(adapter.size(), 0); for (BaseItemDto item : response.getItems()) { - adapter.add(new BaseRowItem(item, staticHeight)); + adapter.add(new BaseRowItem(0, ModelCompat.asSdk(item), false, staticHeight)); i++; } totalItems = response.getTotalRecordCount(); @@ -1171,7 +1171,7 @@ public void onResponse(SeriesTimerInfoDtoResult response) { int i = 0; int prevItems = Math.max(adapter.size(), 0); for (SeriesTimerInfoDto item : response.getItems()) { - adapter.add(new BaseRowItem(item)); + adapter.add(new BaseRowItem(ModelCompat.asSdk(item))); i++; } totalItems = response.getTotalRecordCount(); @@ -1223,7 +1223,7 @@ public void onResponse(ItemsResult response) { } for (BaseItemDto item : response.getItems()) { - adapter.add(new BaseRowItem(item, staticHeight)); + adapter.add(new BaseRowItem(0, ModelCompat.asSdk(item), false, staticHeight)); i++; } totalItems = response.getTotalRecordCount(); @@ -1264,7 +1264,7 @@ public void onResponse(BaseItemDto[] response) { adapter.clear(); } for (BaseItemDto item : response) { - adapter.add(new BaseRowItem(i++, item, preferParentThumb, false)); + adapter.add(new BaseRowItem(i++, ModelCompat.asSdk(item), preferParentThumb, false)); } totalItems = response.length; setItemsLoaded(itemsLoaded + i); @@ -1300,7 +1300,7 @@ public void onResponse(ItemsResult response) { adapter.clear(); } for (BaseItemDto item : response.getItems()) { - adapter.add(new BaseRowItem(i++, item)); + adapter.add(new BaseRowItem(i++, ModelCompat.asSdk(item))); } totalItems = response.getTotalRecordCount(); setItemsLoaded(itemsLoaded + i); @@ -1336,7 +1336,7 @@ public void onResponse(BaseItemDto[] response) { } for (BaseItemDto item : response) { item.setName(context.getString(R.string.lbl_trailer) + (i + 1)); - adapter.add(new BaseRowItem(i++, item, preferParentThumb, false, BaseRowItemSelectAction.Play)); + adapter.add(new BaseRowItem(i++, ModelCompat.asSdk(item), preferParentThumb, false, BaseRowItemSelectAction.Play)); } totalItems = response.length; setItemsLoaded(itemsLoaded + i); @@ -1372,7 +1372,7 @@ public void onResponse(ItemsResult response) { adapter.clear(); } for (BaseItemDto item : response.getItems()) { - adapter.add(new BaseRowItem(i++, item)); + adapter.add(new BaseRowItem(i++, ModelCompat.asSdk(item))); } totalItems = response.getTotalRecordCount(); setItemsLoaded(itemsLoaded + i); @@ -1408,7 +1408,7 @@ public void onResponse(ItemsResult response) { adapter.clear(); } for (BaseItemDto item : response.getItems()) { - adapter.add(new BaseRowItem(i++, item)); + adapter.add(new BaseRowItem(i++, ModelCompat.asSdk(item))); } totalItems = response.getTotalRecordCount(); setItemsLoaded(itemsLoaded + i); @@ -1445,7 +1445,7 @@ public void onResponse(ItemsResult response) { } for (BaseItemDto item : response.getItems()) { if (query.getParentId() == null || item.getSeriesId() == null || item.getSeriesId().equals(query.getParentId())) { - adapter.add(new BaseRowItem(i++, item)); + adapter.add(new BaseRowItem(i++, ModelCompat.asSdk(item))); } } totalItems = response.getTotalRecordCount(); @@ -1482,7 +1482,7 @@ public void onResponse(ItemsResult response) { adapter.clear(); } for (BaseItemDto item : response.getItems()) { - adapter.add(new BaseRowItem(i++, item)); + adapter.add(new BaseRowItem(i++, ModelCompat.asSdk(item))); } totalItems = response.getTotalRecordCount(); setItemsLoaded(i); @@ -1516,7 +1516,7 @@ public void onResponse(ItemsResult response) { int i = 0; int prevItems = Math.max(adapter.size(), 0); for (BaseItemDto item : response.getItems()) { - adapter.add(new BaseRowItem(i++, item)); + adapter.add(new BaseRowItem(i++, ModelCompat.asSdk(item))); } totalItems = response.getTotalRecordCount(); setItemsLoaded(itemsLoaded + i); diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemRowAdapterHelper.kt b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemRowAdapterHelper.kt index 31c7a0bf64..d80c187073 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemRowAdapterHelper.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemRowAdapterHelper.kt @@ -43,7 +43,7 @@ fun ItemRowAdapter.retrieveResumeItems(api: ApiClient, query: GetResumeItemsRequ setItems( items = response.items.orEmpty().toTypedArray(), - transform = { item, i -> BaseRowItem(item, i, preferParentThumb, isStaticHeight) } + transform = { item, i -> BaseRowItem(i, item, preferParentThumb, isStaticHeight) } ) if (response.items.isNullOrEmpty()) removeRow() From e5d396c00e2906bfb0382fbf56f3caca71a75d0f Mon Sep 17 00:00:00 2001 From: Niels van Velzen Date: Tue, 21 May 2024 14:04:14 +0200 Subject: [PATCH 02/15] Refactor BaseRowItem constructors into subclasses --- .../ui/itemdetail/ItemListFragment.java | 4 +- .../MusicFavoritesListFragment.java | 4 +- .../ui/itemhandling/AudioQueueBaseRowItem.kt | 12 ++++ .../ui/itemhandling/AudioQueueItem.kt | 18 ------ .../ui/itemhandling/BaseItemDtoBaseRowItem.kt | 26 +++++++++ .../itemhandling/BaseItemPersonBaseRowItem.kt | 11 ++++ .../androidtv/ui/itemhandling/BaseRowItem.kt | 58 +------------------ .../ChapterItemInfoBaseRowItem.kt | 11 ++++ .../ui/itemhandling/GridButtonBaseRowItem.kt | 11 ++++ .../ui/itemhandling/ItemLauncher.java | 4 +- .../ui/itemhandling/ItemRowAdapter.java | 56 +++++++++--------- .../ui/itemhandling/ItemRowAdapterHelper.kt | 2 +- .../SeriesTimerInfoDtoBaseRowItem.kt | 10 ++++ .../playback/rewrite/RewriteMediaManager.kt | 6 +- .../jellyfin/androidtv/util/KeyProcessor.java | 6 +- 15 files changed, 123 insertions(+), 116 deletions(-) create mode 100644 app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/AudioQueueBaseRowItem.kt delete mode 100644 app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/AudioQueueItem.kt create mode 100644 app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemDtoBaseRowItem.kt create mode 100644 app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemPersonBaseRowItem.kt create mode 100644 app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ChapterItemInfoBaseRowItem.kt create mode 100644 app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/GridButtonBaseRowItem.kt create mode 100644 app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/SeriesTimerInfoDtoBaseRowItem.kt diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemdetail/ItemListFragment.java b/app/src/main/java/org/jellyfin/androidtv/ui/itemdetail/ItemListFragment.java index 9745cdaf1e..86714c76d7 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemdetail/ItemListFragment.java +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemdetail/ItemListFragment.java @@ -34,7 +34,7 @@ import org.jellyfin.androidtv.ui.ItemListViewHelperKt; import org.jellyfin.androidtv.ui.ItemRowView; import org.jellyfin.androidtv.ui.TextUnderButton; -import org.jellyfin.androidtv.ui.itemhandling.BaseRowItem; +import org.jellyfin.androidtv.ui.itemhandling.BaseItemDtoBaseRowItem; import org.jellyfin.androidtv.ui.itemhandling.ItemLauncher; import org.jellyfin.androidtv.ui.navigation.Destination; import org.jellyfin.androidtv.ui.navigation.Destinations; @@ -266,7 +266,7 @@ private void showMenu(final ItemRowView row, boolean showOpen) { open.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { - itemLauncher.getValue().launch(new BaseRowItem(row.getItem()), null, 0, requireContext()); + itemLauncher.getValue().launch(new BaseItemDtoBaseRowItem(row.getItem()), null, 0, requireContext()); return true; } }); diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemdetail/MusicFavoritesListFragment.java b/app/src/main/java/org/jellyfin/androidtv/ui/itemdetail/MusicFavoritesListFragment.java index 71927c73e5..4bf01dadcc 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemdetail/MusicFavoritesListFragment.java +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemdetail/MusicFavoritesListFragment.java @@ -25,7 +25,7 @@ import org.jellyfin.androidtv.ui.ItemListView; import org.jellyfin.androidtv.ui.ItemRowView; import org.jellyfin.androidtv.ui.TextUnderButton; -import org.jellyfin.androidtv.ui.itemhandling.BaseRowItem; +import org.jellyfin.androidtv.ui.itemhandling.BaseItemDtoBaseRowItem; import org.jellyfin.androidtv.ui.itemhandling.ItemLauncher; import org.jellyfin.androidtv.ui.playback.AudioEventListener; import org.jellyfin.androidtv.ui.playback.MediaManager; @@ -226,7 +226,7 @@ private void showMenu(final ItemRowView row, boolean showOpen) { open.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { - itemLauncher.getValue().launch(new BaseRowItem(row.getItem()), null, 0, requireContext()); + itemLauncher.getValue().launch(new BaseItemDtoBaseRowItem(row.getItem()), null, 0, requireContext()); return true; } }); diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/AudioQueueBaseRowItem.kt b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/AudioQueueBaseRowItem.kt new file mode 100644 index 0000000000..aae770c871 --- /dev/null +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/AudioQueueBaseRowItem.kt @@ -0,0 +1,12 @@ +package org.jellyfin.androidtv.ui.itemhandling + +import org.jellyfin.sdk.model.api.BaseItemDto + +class AudioQueueBaseRowItem( + index: Int, + item: BaseItemDto, +) : BaseItemDtoBaseRowItem( + index = index, + item = item, + staticHeight = true, +) diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/AudioQueueItem.kt b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/AudioQueueItem.kt deleted file mode 100644 index 049183fefd..0000000000 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/AudioQueueItem.kt +++ /dev/null @@ -1,18 +0,0 @@ -package org.jellyfin.androidtv.ui.itemhandling - -import org.jellyfin.sdk.model.api.BaseItemDto -import org.jellyfin.sdk.model.api.BaseItemKind - -class AudioQueueItem( - index: Int, - item: BaseItemDto, -) : BaseRowItem( - baseRowType = when (item.type) { - BaseItemKind.PROGRAM -> BaseRowType.LiveTvProgram - BaseItemKind.RECORDING -> BaseRowType.LiveTvRecording - else -> BaseRowType.BaseItem - }, - index = index, - staticHeight = true, - baseItem = item, -) diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemDtoBaseRowItem.kt b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemDtoBaseRowItem.kt new file mode 100644 index 0000000000..735d3aa27d --- /dev/null +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemDtoBaseRowItem.kt @@ -0,0 +1,26 @@ +package org.jellyfin.androidtv.ui.itemhandling + +import org.jellyfin.sdk.model.api.BaseItemDto +import org.jellyfin.sdk.model.api.BaseItemKind + +open class BaseItemDtoBaseRowItem @JvmOverloads constructor( + index: Int = 0, + item: BaseItemDto, + preferParentThumb: Boolean = false, + staticHeight: Boolean = false, + selectAction: BaseRowItemSelectAction = BaseRowItemSelectAction.ShowDetails, + preferSeriesPoster: Boolean = false, +) : BaseRowItem( + baseRowType = when (item.type) { + BaseItemKind.TV_CHANNEL -> BaseRowType.LiveTvChannel + BaseItemKind.PROGRAM -> BaseRowType.LiveTvProgram + BaseItemKind.RECORDING -> BaseRowType.LiveTvRecording + else -> BaseRowType.BaseItem + }, + index = index, + staticHeight = staticHeight, + preferParentThumb = preferParentThumb, + selectAction = selectAction, + baseItem = item, + preferSeriesPoster = preferSeriesPoster, +) diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemPersonBaseRowItem.kt b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemPersonBaseRowItem.kt new file mode 100644 index 0000000000..311c0c5785 --- /dev/null +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemPersonBaseRowItem.kt @@ -0,0 +1,11 @@ +package org.jellyfin.androidtv.ui.itemhandling + +import org.jellyfin.sdk.model.api.BaseItemPerson + +class BaseItemPersonBaseRowItem( + item: BaseItemPerson, +) : BaseRowItem( + baseRowType = BaseRowType.Person, + staticHeight = true, + basePerson = item, +) diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseRowItem.kt b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseRowItem.kt index ef78ce2b77..f6427f6314 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseRowItem.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseRowItem.kt @@ -35,7 +35,7 @@ import org.koin.core.component.inject import timber.log.Timber import java.text.SimpleDateFormat -open class BaseRowItem protected constructor( +abstract class BaseRowItem protected constructor( val baseRowType: BaseRowType, var index: Int = 0, val staticHeight: Boolean = false, @@ -51,62 +51,6 @@ open class BaseRowItem protected constructor( ) : KoinComponent { val imageHelper by inject() - // Start of constructor hell - @JvmOverloads - constructor( - index: Int = 0, - item: BaseItemDto, - preferParentThumb: Boolean = false, - staticHeight: Boolean = false, - selectAction: BaseRowItemSelectAction = BaseRowItemSelectAction.ShowDetails, - preferSeriesPoster: Boolean = false, - ) : this( - baseRowType = when (item.type) { - BaseItemKind.TV_CHANNEL -> BaseRowType.LiveTvChannel - BaseItemKind.PROGRAM -> BaseRowType.LiveTvProgram - BaseItemKind.RECORDING -> BaseRowType.LiveTvRecording - else -> BaseRowType.BaseItem - }, - index = index, - staticHeight = staticHeight, - preferParentThumb = preferParentThumb, - selectAction = selectAction, - baseItem = item, - preferSeriesPoster = preferSeriesPoster, - ) - - constructor( - item: SeriesTimerInfoDto, - ) : this( - baseRowType = BaseRowType.SeriesTimer, - seriesTimerInfo = item, - ) - - constructor( - item: BaseItemPerson, - ) : this( - baseRowType = BaseRowType.Person, - staticHeight = true, - basePerson = item, - ) - - constructor( - item: ChapterItemInfo, - ) : this( - baseRowType = BaseRowType.Chapter, - staticHeight = true, - chapterInfo = item, - ) - - constructor( - item: GridButton, - ) : this( - baseRowType = BaseRowType.GridButton, - staticHeight = true, - gridButton = item, - ) - // End of constructor hell - fun showCardInfoOverlay() = baseRowType == BaseRowType.BaseItem && listOf( BaseItemKind.FOLDER, BaseItemKind.PHOTO_ALBUM, diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ChapterItemInfoBaseRowItem.kt b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ChapterItemInfoBaseRowItem.kt new file mode 100644 index 0000000000..d8ed408974 --- /dev/null +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ChapterItemInfoBaseRowItem.kt @@ -0,0 +1,11 @@ +package org.jellyfin.androidtv.ui.itemhandling + +import org.jellyfin.androidtv.data.model.ChapterItemInfo + +class ChapterItemInfoBaseRowItem( + item: ChapterItemInfo, +) : BaseRowItem( + baseRowType = BaseRowType.Chapter, + staticHeight = true, + chapterInfo = item, +) diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/GridButtonBaseRowItem.kt b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/GridButtonBaseRowItem.kt new file mode 100644 index 0000000000..a48c8c1639 --- /dev/null +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/GridButtonBaseRowItem.kt @@ -0,0 +1,11 @@ +package org.jellyfin.androidtv.ui.itemhandling + +import org.jellyfin.androidtv.ui.GridButton + +class GridButtonBaseRowItem( + item: GridButton, +) : BaseRowItem( + baseRowType = BaseRowType.GridButton, + staticHeight = true, + gridButton = item, +) diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemLauncher.java b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemLauncher.java index 9232d10c39..3d65414925 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemLauncher.java +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemLauncher.java @@ -99,9 +99,9 @@ public void launch(final BaseRowItem rowItem, ItemRowAdapter adapter, int pos, f return; // if the song currently playing is selected (and is the exact item - this only happens in the nowPlayingRow), open AudioNowPlayingActivity - if (mediaManager.getValue().hasAudioQueueItems() && rowItem instanceof AudioQueueItem && rowItem.getBaseItem().getId().equals(mediaManager.getValue().getCurrentAudioItem().getId())) { + if (mediaManager.getValue().hasAudioQueueItems() && rowItem instanceof AudioQueueBaseRowItem && rowItem.getBaseItem().getId().equals(mediaManager.getValue().getCurrentAudioItem().getId())) { navigationRepository.getValue().navigate(Destinations.INSTANCE.getNowPlaying()); - } else if (mediaManager.getValue().hasAudioQueueItems() && rowItem instanceof AudioQueueItem && pos < mediaManager.getValue().getCurrentAudioQueueSize()) { + } else if (mediaManager.getValue().hasAudioQueueItems() && rowItem instanceof AudioQueueBaseRowItem && pos < mediaManager.getValue().getCurrentAudioQueueSize()) { Timber.d("playing audio queue item"); mediaManager.getValue().playFrom(pos); } else if (adapter.getQueryType() == QueryType.Search) { diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemRowAdapter.java b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemRowAdapter.java index 41da38fd1a..1652471134 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemRowAdapter.java +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemRowAdapter.java @@ -757,7 +757,7 @@ public void Retrieve() { private void loadPeople() { if (mPersons != null) { for (BaseItemPerson person : mPersons) { - add(new BaseRowItem(person)); + add(new BaseItemPersonBaseRowItem(person)); } } else { @@ -770,7 +770,7 @@ private void loadPeople() { private void loadChapters() { if (mChapters != null) { for (ChapterItemInfo chapter : mChapters) { - add(new BaseRowItem(chapter)); + add(new ChapterItemInfoBaseRowItem(chapter)); } } else { @@ -783,7 +783,7 @@ private void loadChapters() { private void loadStaticItems() { if (mItems != null) { for (org.jellyfin.sdk.model.api.BaseItemDto item : mItems) { - add(new BaseRowItem(item)); + add(new BaseItemDtoBaseRowItem(item)); } itemsLoaded = mItems.size(); } else { @@ -797,7 +797,7 @@ private void loadStaticAudioItems() { if (mItems != null) { int i = 0; for (org.jellyfin.sdk.model.api.BaseItemDto item : mItems) { - add(new AudioQueueItem(i++, item)); + add(new AudioQueueBaseRowItem(i++, item)); } itemsLoaded = i; @@ -819,7 +819,7 @@ public void onResponse(ItemsResult response) { ItemRowAdapterHelperKt.setItems(ItemRowAdapter.this, response.getItems(), (item, i) -> { if (userViewsRepository.getValue().isSupported(ModelCompat.asSdk(item).getCollectionType())) { item.setDisplayPreferencesId(item.getId()); - return new BaseRowItem(i, ModelCompat.asSdk(item), preferParentThumb, staticHeight); + return new BaseItemDtoBaseRowItem(i, ModelCompat.asSdk(item), preferParentThumb, staticHeight); } else { return null; } @@ -853,7 +853,7 @@ public void onResponse(ItemsResult response) { if (response.getItems() != null && response.getItems().length > 0) { setTotalItems(query.getEnableTotalRecordCount() ? response.getTotalRecordCount() : response.getItems().length); - ItemRowAdapterHelperKt.setItems(ItemRowAdapter.this, response.getItems(), (item, i) -> new BaseRowItem(i, ModelCompat.asSdk(item), getPreferParentThumb(), isStaticHeight())); + ItemRowAdapterHelperKt.setItems(ItemRowAdapter.this, response.getItems(), (item, i) -> new BaseItemDtoBaseRowItem(i, ModelCompat.asSdk(item), getPreferParentThumb(), isStaticHeight())); } else if (getItemsLoaded() == 0) { removeRow(); } @@ -885,7 +885,7 @@ public void onResponse(ItemsResult response) { if (response.getItems() != null && response.getItems().length > 0) { setTotalItems(response.getTotalRecordCount()); - ItemRowAdapterHelperKt.setItems(ItemRowAdapter.this, response.getItems(), (item, i) -> new BaseRowItem(i, ModelCompat.asSdk(item), getPreferParentThumb(), isStaticHeight())); + ItemRowAdapterHelperKt.setItems(ItemRowAdapter.this, response.getItems(), (item, i) -> new BaseItemDtoBaseRowItem(i, ModelCompat.asSdk(item), getPreferParentThumb(), isStaticHeight())); } else if (getItemsLoaded() == 0) { removeRow(); } @@ -909,7 +909,7 @@ public void onResponse(ItemsResult response) { if (response.getItems() != null && response.getItems().length > 0) { setTotalItems(response.getTotalRecordCount()); - ItemRowAdapterHelperKt.setItems(ItemRowAdapter.this, response.getItems(), (item, i) -> new BaseRowItem(i, ModelCompat.asSdk(item), getPreferParentThumb(), isStaticHeight())); + ItemRowAdapterHelperKt.setItems(ItemRowAdapter.this, response.getItems(), (item, i) -> new BaseItemDtoBaseRowItem(i, ModelCompat.asSdk(item), getPreferParentThumb(), isStaticHeight())); } else if (getItemsLoaded() == 0) { removeRow(); } @@ -933,7 +933,7 @@ public void onResponse(BaseItemDto[] response) { if (response != null && response.length > 0) { setTotalItems(response.length); - ItemRowAdapterHelperKt.setItems(ItemRowAdapter.this, response, (item, i) -> new BaseRowItem(i, ModelCompat.asSdk(item), getPreferParentThumb(), isStaticHeight(), BaseRowItemSelectAction.ShowDetails, getPreferParentThumb())); + ItemRowAdapterHelperKt.setItems(ItemRowAdapter.this, response, (item, i) -> new BaseItemDtoBaseRowItem(i, ModelCompat.asSdk(item), getPreferParentThumb(), isStaticHeight(), BaseRowItemSelectAction.ShowDetails, getPreferParentThumb())); } else if (getItemsLoaded() == 0) { removeRow(); } @@ -997,12 +997,12 @@ public void onResponse(ItemsResult response) { } if (existing == null) { Timber.d("Adding new episode 1 to premieres %s", item.getSeriesName()); - adapter.add(new BaseRowItem(i++, ModelCompat.asSdk(item), preferParentThumb, true)); + adapter.add(new BaseItemDtoBaseRowItem(i++, ModelCompat.asSdk(item), preferParentThumb, true)); } else if (existing.getBaseItem().getParentIndexNumber() > item.getParentIndexNumber()) { //Replace the newer item with the earlier season Timber.d("Replacing newer episode 1 with an older season for %s", item.getSeriesName()); - adapter.set(existingPos, new BaseRowItem(i++, ModelCompat.asSdk(item), preferParentThumb, false)); + adapter.set(existingPos, new BaseItemDtoBaseRowItem(i++, ModelCompat.asSdk(item), preferParentThumb, false)); } // otherwise, just ignore this newer season premiere since we have the older one already } else { @@ -1034,7 +1034,7 @@ private void retrieve(final NextUpQuery query) { public void onResponse(final ItemsResult response) { if (response.getItems() != null && response.getItems().length > 0) { setTotalItems(response.getTotalRecordCount()); - ItemRowAdapterHelperKt.setItems(ItemRowAdapter.this, response.getItems(), (item, i) -> new BaseRowItem(i, ModelCompat.asSdk(item), preferParentThumb, staticHeight)); + ItemRowAdapterHelperKt.setItems(ItemRowAdapter.this, response.getItems(), (item, i) -> new BaseItemDtoBaseRowItem(i, ModelCompat.asSdk(item), preferParentThumb, staticHeight)); //If this was for a single series, get the rest of the episodes in the season if (query.getSeriesId() != null) { @@ -1050,7 +1050,7 @@ public void onResponse(ItemsResult innerResponse) { if (response.getItems() != null) { int n = response.getItems().length; for (BaseItemDto item : innerResponse.getItems()) { - adapter.add(new BaseRowItem(n++, ModelCompat.asSdk(item), preferParentThumb, false)); + adapter.add(new BaseItemDtoBaseRowItem(n++, ModelCompat.asSdk(item), preferParentThumb, false)); } totalItems += innerResponse.getTotalRecordCount(); setItemsLoaded(itemsLoaded + n); @@ -1096,7 +1096,7 @@ public void onResponse(ChannelInfoDtoResult response) { adapter.clear(); } for (ChannelInfoDto item : response.getItems()) { - adapter.add(new BaseRowItem(i, ModelCompat.asSdk(item))); + adapter.add(new BaseItemDtoBaseRowItem(i, ModelCompat.asSdk(item))); i++; } totalItems = response.getTotalRecordCount(); @@ -1132,7 +1132,7 @@ public void onResponse(ItemsResult response) { int i = 0; int prevItems = Math.max(adapter.size(), 0); for (BaseItemDto item : response.getItems()) { - adapter.add(new BaseRowItem(0, ModelCompat.asSdk(item), false, staticHeight)); + adapter.add(new BaseItemDtoBaseRowItem(0, ModelCompat.asSdk(item), false, staticHeight)); i++; } totalItems = response.getTotalRecordCount(); @@ -1171,7 +1171,7 @@ public void onResponse(SeriesTimerInfoDtoResult response) { int i = 0; int prevItems = Math.max(adapter.size(), 0); for (SeriesTimerInfoDto item : response.getItems()) { - adapter.add(new BaseRowItem(ModelCompat.asSdk(item))); + adapter.add(new SeriesTimerInfoDtoBaseRowItem(ModelCompat.asSdk(item))); i++; } totalItems = response.getTotalRecordCount(); @@ -1210,20 +1210,20 @@ public void onResponse(ItemsResult response) { int prevItems = Math.max(adapter.size(), 0); if (adapter.chunkSize == 0) { // and recordings as first item if showing all - adapter.add(new BaseRowItem(new GridButton(LiveTvOption.LIVE_TV_RECORDINGS_OPTION_ID, context.getString(R.string.lbl_recorded_tv)))); + adapter.add(new GridButtonBaseRowItem(new GridButton(LiveTvOption.LIVE_TV_RECORDINGS_OPTION_ID, context.getString(R.string.lbl_recorded_tv)))); i++; if (Utils.canManageRecordings(KoinJavaComponent.get(UserRepository.class).getCurrentUser().getValue())) { // and schedule - adapter.add(new BaseRowItem(new GridButton(LiveTvOption.LIVE_TV_SCHEDULE_OPTION_ID, context.getString(R.string.lbl_schedule)))); + adapter.add(new GridButtonBaseRowItem(new GridButton(LiveTvOption.LIVE_TV_SCHEDULE_OPTION_ID, context.getString(R.string.lbl_schedule)))); i++; // and series - adapter.add(new BaseRowItem(new GridButton(LiveTvOption.LIVE_TV_SERIES_OPTION_ID, context.getString(R.string.lbl_series)))); + adapter.add(new GridButtonBaseRowItem(new GridButton(LiveTvOption.LIVE_TV_SERIES_OPTION_ID, context.getString(R.string.lbl_series)))); i++; } } for (BaseItemDto item : response.getItems()) { - adapter.add(new BaseRowItem(0, ModelCompat.asSdk(item), false, staticHeight)); + adapter.add(new BaseItemDtoBaseRowItem(0, ModelCompat.asSdk(item), false, staticHeight)); i++; } totalItems = response.getTotalRecordCount(); @@ -1264,7 +1264,7 @@ public void onResponse(BaseItemDto[] response) { adapter.clear(); } for (BaseItemDto item : response) { - adapter.add(new BaseRowItem(i++, ModelCompat.asSdk(item), preferParentThumb, false)); + adapter.add(new BaseItemDtoBaseRowItem(i++, ModelCompat.asSdk(item), preferParentThumb, false)); } totalItems = response.length; setItemsLoaded(itemsLoaded + i); @@ -1300,7 +1300,7 @@ public void onResponse(ItemsResult response) { adapter.clear(); } for (BaseItemDto item : response.getItems()) { - adapter.add(new BaseRowItem(i++, ModelCompat.asSdk(item))); + adapter.add(new BaseItemDtoBaseRowItem(i++, ModelCompat.asSdk(item))); } totalItems = response.getTotalRecordCount(); setItemsLoaded(itemsLoaded + i); @@ -1336,7 +1336,7 @@ public void onResponse(BaseItemDto[] response) { } for (BaseItemDto item : response) { item.setName(context.getString(R.string.lbl_trailer) + (i + 1)); - adapter.add(new BaseRowItem(i++, ModelCompat.asSdk(item), preferParentThumb, false, BaseRowItemSelectAction.Play)); + adapter.add(new BaseItemDtoBaseRowItem(i++, ModelCompat.asSdk(item), preferParentThumb, false, BaseRowItemSelectAction.Play)); } totalItems = response.length; setItemsLoaded(itemsLoaded + i); @@ -1372,7 +1372,7 @@ public void onResponse(ItemsResult response) { adapter.clear(); } for (BaseItemDto item : response.getItems()) { - adapter.add(new BaseRowItem(i++, ModelCompat.asSdk(item))); + adapter.add(new BaseItemDtoBaseRowItem(i++, ModelCompat.asSdk(item))); } totalItems = response.getTotalRecordCount(); setItemsLoaded(itemsLoaded + i); @@ -1408,7 +1408,7 @@ public void onResponse(ItemsResult response) { adapter.clear(); } for (BaseItemDto item : response.getItems()) { - adapter.add(new BaseRowItem(i++, ModelCompat.asSdk(item))); + adapter.add(new BaseItemDtoBaseRowItem(i++, ModelCompat.asSdk(item))); } totalItems = response.getTotalRecordCount(); setItemsLoaded(itemsLoaded + i); @@ -1445,7 +1445,7 @@ public void onResponse(ItemsResult response) { } for (BaseItemDto item : response.getItems()) { if (query.getParentId() == null || item.getSeriesId() == null || item.getSeriesId().equals(query.getParentId())) { - adapter.add(new BaseRowItem(i++, ModelCompat.asSdk(item))); + adapter.add(new BaseItemDtoBaseRowItem(i++, ModelCompat.asSdk(item))); } } totalItems = response.getTotalRecordCount(); @@ -1482,7 +1482,7 @@ public void onResponse(ItemsResult response) { adapter.clear(); } for (BaseItemDto item : response.getItems()) { - adapter.add(new BaseRowItem(i++, ModelCompat.asSdk(item))); + adapter.add(new BaseItemDtoBaseRowItem(i++, ModelCompat.asSdk(item))); } totalItems = response.getTotalRecordCount(); setItemsLoaded(i); @@ -1516,7 +1516,7 @@ public void onResponse(ItemsResult response) { int i = 0; int prevItems = Math.max(adapter.size(), 0); for (BaseItemDto item : response.getItems()) { - adapter.add(new BaseRowItem(i++, ModelCompat.asSdk(item))); + adapter.add(new BaseItemDtoBaseRowItem(i++, ModelCompat.asSdk(item))); } totalItems = response.getTotalRecordCount(); setItemsLoaded(itemsLoaded + i); diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemRowAdapterHelper.kt b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemRowAdapterHelper.kt index d80c187073..a4dac63821 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemRowAdapterHelper.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemRowAdapterHelper.kt @@ -43,7 +43,7 @@ fun ItemRowAdapter.retrieveResumeItems(api: ApiClient, query: GetResumeItemsRequ setItems( items = response.items.orEmpty().toTypedArray(), - transform = { item, i -> BaseRowItem(i, item, preferParentThumb, isStaticHeight) } + transform = { item, i -> BaseItemDtoBaseRowItem(i, item, preferParentThumb, isStaticHeight) } ) if (response.items.isNullOrEmpty()) removeRow() diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/SeriesTimerInfoDtoBaseRowItem.kt b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/SeriesTimerInfoDtoBaseRowItem.kt new file mode 100644 index 0000000000..85dd6a6cd4 --- /dev/null +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/SeriesTimerInfoDtoBaseRowItem.kt @@ -0,0 +1,10 @@ +package org.jellyfin.androidtv.ui.itemhandling + +import org.jellyfin.sdk.model.api.SeriesTimerInfoDto + +class SeriesTimerInfoDtoBaseRowItem( + item: SeriesTimerInfoDto, +) : BaseRowItem( + baseRowType = BaseRowType.SeriesTimer, + seriesTimerInfo = item, +) diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/playback/rewrite/RewriteMediaManager.kt b/app/src/main/java/org/jellyfin/androidtv/ui/playback/rewrite/RewriteMediaManager.kt index 74c00cb47e..d28fc1e781 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/playback/rewrite/RewriteMediaManager.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/playback/rewrite/RewriteMediaManager.kt @@ -11,7 +11,7 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import org.jellyfin.androidtv.constant.QueryType -import org.jellyfin.androidtv.ui.itemhandling.AudioQueueItem +import org.jellyfin.androidtv.ui.itemhandling.AudioQueueBaseRowItem import org.jellyfin.androidtv.ui.itemhandling.BaseRowItem import org.jellyfin.androidtv.ui.itemhandling.ItemRowAdapter import org.jellyfin.androidtv.ui.navigation.Destinations @@ -142,7 +142,7 @@ class RewriteMediaManager( .items // Map to audio queue items .mapIndexed { index, item -> - AudioQueueItem(index, item).apply { + AudioQueueBaseRowItem(index, item).apply { playing = playbackManager.state.queue.entryIndex.value == index } } @@ -152,7 +152,7 @@ class RewriteMediaManager( // Update item row currentAudioQueue.replaceAll( items, - areItemsTheSame = { old, new -> (old as AudioQueueItem).baseItem == (new as AudioQueueItem).baseItem }, + areItemsTheSame = { old, new -> (old as AudioQueueBaseRowItem).baseItem == (new as AudioQueueBaseRowItem).baseItem }, // The equals functions for BaseRowItem only compare by id areContentsTheSame = { _, _ -> false }, ) diff --git a/app/src/main/java/org/jellyfin/androidtv/util/KeyProcessor.java b/app/src/main/java/org/jellyfin/androidtv/util/KeyProcessor.java index e17cf82f6d..cc1a070ee0 100644 --- a/app/src/main/java/org/jellyfin/androidtv/util/KeyProcessor.java +++ b/app/src/main/java/org/jellyfin/androidtv/util/KeyProcessor.java @@ -13,7 +13,7 @@ import org.jellyfin.androidtv.data.querying.StdItemQuery; import org.jellyfin.androidtv.data.repository.CustomMessageRepository; import org.jellyfin.androidtv.data.repository.ItemMutationRepository; -import org.jellyfin.androidtv.ui.itemhandling.AudioQueueItem; +import org.jellyfin.androidtv.ui.itemhandling.AudioQueueBaseRowItem; import org.jellyfin.androidtv.ui.itemhandling.BaseRowItem; import org.jellyfin.androidtv.ui.itemhandling.BaseRowType; import org.jellyfin.androidtv.ui.navigation.Destinations; @@ -80,7 +80,7 @@ public boolean handleKey(int key, BaseRowItem rowItem, FragmentActivity activity if (!BaseItemExtensionsKt.canPlay(item)) return false; switch (item.getType()) { case AUDIO: - if (rowItem instanceof AudioQueueItem) { + if (rowItem instanceof AudioQueueBaseRowItem) { createItemMenu(rowItem, item.getUserData(), activity); return true; } @@ -187,7 +187,7 @@ public PopupMenu createItemMenu(BaseRowItem rowItem, UserItemDataDto userData, F PopupMenu menu = new PopupMenu(activity, activity.getCurrentFocus(), Gravity.END); int order = 0; - if (rowItem instanceof AudioQueueItem) { + if (rowItem instanceof AudioQueueBaseRowItem) { if (rowItem.getBaseItem() != mediaManager.getValue().getCurrentAudioItem()) menu.getMenu().add(0, MENU_ADVANCE_QUEUE, order++, R.string.lbl_play_from_here); menu.getMenu().add(0, MENU_GOTO_NOW_PLAYING, order++, R.string.lbl_goto_now_playing); From 0de85b2a9fb40a60ad048e124495acc294390566 Mon Sep 17 00:00:00 2001 From: Niels van Velzen Date: Tue, 21 May 2024 14:19:57 +0200 Subject: [PATCH 03/15] Move function implementations to BaseRowItem subclasses --- .../ui/card/LegacyImageCardView.java | 2 +- .../ui/itemhandling/BaseItemDtoBaseRowItem.kt | 194 +++++++++++++- .../itemhandling/BaseItemPersonBaseRowItem.kt | 13 +- .../androidtv/ui/itemhandling/BaseRowItem.kt | 247 ++---------------- .../ChapterItemInfoBaseRowItem.kt | 17 +- .../ui/itemhandling/GridButtonBaseRowItem.kt | 13 +- .../ui/itemhandling/ItemLauncher.java | 5 +- .../SeriesTimerInfoDtoBaseRowItem.kt | 24 +- .../jellyfin/androidtv/util/KeyProcessor.java | 2 +- app/src/main/res/values/strings.xml | 1 + 10 files changed, 285 insertions(+), 233 deletions(-) diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/card/LegacyImageCardView.java b/app/src/main/java/org/jellyfin/androidtv/ui/card/LegacyImageCardView.java index 51e43f858d..288f6c4d78 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/card/LegacyImageCardView.java +++ b/app/src/main/java/org/jellyfin/androidtv/ui/card/LegacyImageCardView.java @@ -114,7 +114,7 @@ public void setOverlayText(String text) { public void setOverlayInfo(BaseRowItem item) { if (binding.overlayText == null) return; - if (getCardType() == BaseCardView.CARD_TYPE_MAIN_ONLY && item.showCardInfoOverlay()) { + if (getCardType() == BaseCardView.CARD_TYPE_MAIN_ONLY && item.getShowCardInfoOverlay()) { switch (item.getBaseItemType()) { case PHOTO: insertCardData(item.getBaseItem().getPremiereDate() != null ? android.text.format.DateFormat.getDateFormat(getContext()).format(TimeUtils.getDate(item.getBaseItem().getPremiereDate())) : item.getFullName(getContext()), R.drawable.ic_camera, true); diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemDtoBaseRowItem.kt b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemDtoBaseRowItem.kt index 735d3aa27d..35b5832533 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemDtoBaseRowItem.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemDtoBaseRowItem.kt @@ -1,7 +1,27 @@ package org.jellyfin.androidtv.ui.itemhandling +import android.content.Context +import android.text.format.DateFormat +import androidx.core.content.ContextCompat +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import org.jellyfin.androidtv.R +import org.jellyfin.androidtv.constant.ImageType +import org.jellyfin.androidtv.util.TimeUtils +import org.jellyfin.androidtv.util.apiclient.LifecycleAwareResponse +import org.jellyfin.androidtv.util.sdk.getFullName +import org.jellyfin.androidtv.util.sdk.getSubName +import org.jellyfin.sdk.api.client.ApiClient +import org.jellyfin.sdk.api.client.exception.ApiClientException +import org.jellyfin.sdk.api.client.extensions.userLibraryApi import org.jellyfin.sdk.model.api.BaseItemDto import org.jellyfin.sdk.model.api.BaseItemKind +import org.jellyfin.sdk.model.extensions.ticks +import org.koin.core.component.get +import timber.log.Timber +import java.text.SimpleDateFormat open class BaseItemDtoBaseRowItem @JvmOverloads constructor( index: Int = 0, @@ -23,4 +43,176 @@ open class BaseItemDtoBaseRowItem @JvmOverloads constructor( selectAction = selectAction, baseItem = item, preferSeriesPoster = preferSeriesPoster, -) +) { + override val showCardInfoOverlay + get() = when (baseItem?.type) { + BaseItemKind.FOLDER, + BaseItemKind.PHOTO_ALBUM, + BaseItemKind.USER_VIEW, + BaseItemKind.COLLECTION_FOLDER, + BaseItemKind.PHOTO, + BaseItemKind.VIDEO, + BaseItemKind.PERSON, + BaseItemKind.PLAYLIST, + BaseItemKind.MUSIC_ARTIST -> true + + else -> false + } + + override fun getItemId() = baseItem?.id + + override fun getBaseItemType() = baseItem?.type + override fun isFavorite() = baseItem?.userData?.isFavorite == true + override fun isPlayed() = baseItem?.userData?.played == true + + override fun getChildCountStr(): String? { + // Playlist + if (baseItem?.type == BaseItemKind.PLAYLIST) { + val childCount = baseItem?.cumulativeRunTimeTicks?.ticks?.let { duration -> + TimeUtils.formatMillis(duration.inWholeMilliseconds) + } + if (childCount != null) return childCount + } + + // Folder + if (baseItem?.isFolder == true && baseItem?.type != BaseItemKind.MUSIC_ARTIST) { + val childCount = baseItem?.childCount + if (childCount != null && childCount > 0) return childCount.toString() + } + + // Default + return null + } + + override fun getCardName(context: Context) = when { + baseItem?.type == BaseItemKind.AUDIO && baseItem?.albumArtist != null -> baseItem?.albumArtist + baseItem?.type == BaseItemKind.AUDIO && baseItem?.album != null -> baseItem?.album + else -> baseItem?.getFullName(context) + } + + override fun getFullName(context: Context) = baseItem?.getFullName(context) + override fun getName(context: Context) = when (baseItem?.type) { + BaseItemKind.AUDIO -> baseItem?.getFullName(context) + else -> baseItem?.name + } + + override fun getSummary(context: Context) = baseItem?.overview + + override fun getSubText(context: Context) = when (baseItem?.type) { + BaseItemKind.TV_CHANNEL -> baseItem?.number + BaseItemKind.TV_PROGRAM, + BaseItemKind.PROGRAM -> baseItem?.episodeTitle ?: baseItem?.channelName + BaseItemKind.RECORDING -> { + val title = listOfNotNull( + baseItem?.channelName, + baseItem?.episodeTitle + ).joinToString(" - ") + + val timestamp = buildString { + append(SimpleDateFormat("d MMM").format(TimeUtils.getDate(baseItem!!.startDate))) + append(" ") + append( + (DateFormat.getTimeFormat(context) + .format(TimeUtils.getDate(baseItem!!.startDate))) + ) + append(" - ") + append( + DateFormat.getTimeFormat(context).format(TimeUtils.getDate(baseItem!!.endDate)) + ) + } + + "$title $timestamp" + } + + else -> baseItem?.getSubName(context) + } + + override fun getImageUrl( + context: Context, + imageType: ImageType, + fillWidth: Int, + fillHeight: Int + ): String? { + val seriesId = baseItem?.seriesId + val seriesPrimaryImageTag = baseItem?.seriesPrimaryImageTag + + return when { + preferSeriesPoster && seriesId != null && seriesPrimaryImageTag != null -> { + imageHelper.getImageUrl( + seriesId, + org.jellyfin.sdk.model.api.ImageType.PRIMARY, + seriesPrimaryImageTag + ) + } + + imageType == ImageType.BANNER -> imageHelper.getBannerImageUrl( + requireNotNull( + baseItem + ), fillWidth, fillHeight + ) + + imageType == ImageType.THUMB -> imageHelper.getThumbImageUrl( + requireNotNull( + baseItem + ), fillWidth, fillHeight + ) + + else -> getPrimaryImageUrl(context, fillHeight) + } + } + + override fun getPrimaryImageUrl( + context: Context, + fillHeight: Int, + ) = imageHelper.getPrimaryImageUrl( + baseItem!!, + preferParentThumb, + null, + fillHeight + ) + + override fun getBadgeImage(context: Context) = when (baseItem?.type) { + BaseItemKind.LIVE_TV_PROGRAM, + BaseItemKind.PROGRAM -> when { + baseItem?.seriesTimerId != null -> R.drawable.ic_record_series_red + baseItem?.timerId != null -> R.drawable.ic_record_red + + else -> null + } + + else -> when { + baseItem?.criticRating != null -> when { + baseItem!!.criticRating!! > 59f -> R.drawable.ic_rt_fresh + else -> R.drawable.ic_rt_rotten + } + + else -> null + } + }?.let { ContextCompat.getDrawable(context, it) } + + override fun refresh( + outerResponse: LifecycleAwareResponse, + scope: CoroutineScope, + ) { + val itemId = baseItem?.id + val api = get() + + if (itemId == null) { + Timber.w("Skipping call to BaseRowItem.refresh()") + return + } + + scope.launch(Dispatchers.IO) { + baseItem = try { + api.userLibraryApi.getItem(itemId = itemId).content + } catch (err: ApiClientException) { + Timber.e(err, "Failed to refresh item") + null + } + + if (outerResponse.active) withContext(Dispatchers.Main) { + outerResponse.onResponse(baseItem) + } + } + } +} diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemPersonBaseRowItem.kt b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemPersonBaseRowItem.kt index 311c0c5785..37000a0321 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemPersonBaseRowItem.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemPersonBaseRowItem.kt @@ -1,5 +1,6 @@ package org.jellyfin.androidtv.ui.itemhandling +import android.content.Context import org.jellyfin.sdk.model.api.BaseItemPerson class BaseItemPersonBaseRowItem( @@ -8,4 +9,14 @@ class BaseItemPersonBaseRowItem( baseRowType = BaseRowType.Person, staticHeight = true, basePerson = item, -) +) { + override fun getPrimaryImageUrl( + context: Context, + fillHeight: Int, + ) = imageHelper.getPrimaryImageUrl(basePerson!!, fillHeight) + + override fun getItemId() = basePerson?.id + override fun getFullName(context: Context) = basePerson?.name + override fun getName(context: Context) = basePerson?.name + override fun getSubText(context: Context) = basePerson?.role +} diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseRowItem.kt b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseRowItem.kt index f6427f6314..fcbd61aed7 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseRowItem.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseRowItem.kt @@ -2,39 +2,24 @@ package org.jellyfin.androidtv.ui.itemhandling import android.content.Context import android.graphics.drawable.Drawable -import android.text.format.DateFormat -import androidx.core.content.ContextCompat import androidx.lifecycle.ProcessLifecycleOwner import androidx.lifecycle.lifecycleScope import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import org.jellyfin.androidtv.R import org.jellyfin.androidtv.constant.ImageType import org.jellyfin.androidtv.data.model.ChapterItemInfo import org.jellyfin.androidtv.ui.GridButton import org.jellyfin.androidtv.util.ImageHelper -import org.jellyfin.androidtv.util.TimeUtils import org.jellyfin.androidtv.util.apiclient.LifecycleAwareResponse -import org.jellyfin.androidtv.util.apiclient.getSeriesOverview -import org.jellyfin.androidtv.util.sdk.getFullName -import org.jellyfin.androidtv.util.sdk.getSubName -import org.jellyfin.sdk.api.client.ApiClient -import org.jellyfin.sdk.api.client.exception.ApiClientException -import org.jellyfin.sdk.api.client.extensions.userLibraryApi import org.jellyfin.sdk.model.api.BaseItemDto import org.jellyfin.sdk.model.api.BaseItemKind import org.jellyfin.sdk.model.api.BaseItemPerson import org.jellyfin.sdk.model.api.SeriesTimerInfoDto -import org.jellyfin.sdk.model.extensions.ticks -import org.jellyfin.sdk.model.serializer.toUUID import org.koin.core.component.KoinComponent -import org.koin.core.component.get import org.koin.core.component.inject -import timber.log.Timber -import java.text.SimpleDateFormat +import java.util.UUID +// TODO: Move properties to relevant classes only (e.g. baseItem should be in +// BaseItemDtoBaseRowItem) abstract class BaseRowItem protected constructor( val baseRowType: BaseRowType, var index: Int = 0, @@ -51,219 +36,35 @@ abstract class BaseRowItem protected constructor( ) : KoinComponent { val imageHelper by inject() - fun showCardInfoOverlay() = baseRowType == BaseRowType.BaseItem && listOf( - BaseItemKind.FOLDER, - BaseItemKind.PHOTO_ALBUM, - BaseItemKind.USER_VIEW, - BaseItemKind.COLLECTION_FOLDER, - BaseItemKind.PHOTO, - BaseItemKind.VIDEO, - BaseItemKind.PERSON, - BaseItemKind.PLAYLIST, - BaseItemKind.MUSIC_ARTIST - ).contains(baseItem?.type) + open val showCardInfoOverlay: Boolean = false + open fun getBaseItemType(): BaseItemKind? = null + open fun isFavorite(): Boolean = false + open fun isPlayed(): Boolean = false - fun getImageUrl(context: Context, imageType: ImageType, fillWidth: Int, fillHeight: Int) = when (baseRowType) { - BaseRowType.BaseItem, - BaseRowType.LiveTvProgram, - BaseRowType.LiveTvRecording -> { - val seriesId = baseItem?.seriesId - val seriesPrimaryImageTag = baseItem?.seriesPrimaryImageTag + open fun getCardName(context: Context): String? = getFullName(context) - when { - preferSeriesPoster && seriesId != null && seriesPrimaryImageTag != null -> { - imageHelper.getImageUrl(seriesId, org.jellyfin.sdk.model.api.ImageType.PRIMARY, seriesPrimaryImageTag) - } + open fun getChildCountStr(): String? = null - imageType == ImageType.BANNER -> imageHelper.getBannerImageUrl(requireNotNull(baseItem), fillWidth, fillHeight) - imageType == ImageType.THUMB -> imageHelper.getThumbImageUrl(requireNotNull(baseItem), fillWidth, fillHeight) - else -> getPrimaryImageUrl(context, fillHeight) - } - } + open fun getImageUrl( + context: Context, + imageType: ImageType, + fillWidth: Int, + fillHeight: Int, + ) = getPrimaryImageUrl(context, fillHeight) - else -> getPrimaryImageUrl(context, fillHeight) - } - - fun getPrimaryImageUrl(context: Context, fillHeight: Int) = when (baseRowType) { - BaseRowType.BaseItem, - BaseRowType.LiveTvProgram, - BaseRowType.LiveTvRecording -> imageHelper.getPrimaryImageUrl(baseItem!!, preferParentThumb, null, fillHeight) - - BaseRowType.Person -> imageHelper.getPrimaryImageUrl(basePerson!!, fillHeight) - BaseRowType.Chapter -> chapterInfo?.imagePath - BaseRowType.LiveTvChannel -> imageHelper.getPrimaryImageUrl(baseItem!!) - BaseRowType.GridButton -> gridButton?.imageRes?.let { imageHelper.getResourceUrl(context, it) } - BaseRowType.SeriesTimer -> imageHelper.getResourceUrl(context, R.drawable.tile_land_series_timer) - } - - fun getBaseItemType() = baseItem?.type - - fun isFavorite(): Boolean = baseItem?.userData?.isFavorite == true - fun isFolder(): Boolean = baseItem?.isFolder == true - fun isPlayed(): Boolean = baseItem?.userData?.played == true - - fun getCardName(context: Context): String? = when { - baseItem?.type == BaseItemKind.AUDIO && baseItem!!.albumArtist != null -> baseItem!!.albumArtist - baseItem?.type == BaseItemKind.AUDIO && baseItem!!.album != null -> baseItem!!.album - else -> getFullName(context) - } - - fun getFullName(context: Context) = when (baseRowType) { - BaseRowType.BaseItem, - BaseRowType.LiveTvProgram, - BaseRowType.LiveTvRecording -> baseItem?.getFullName(context) - - BaseRowType.Person -> basePerson?.name - BaseRowType.Chapter -> chapterInfo?.name - BaseRowType.LiveTvChannel -> baseItem?.name - BaseRowType.GridButton -> gridButton?.text - BaseRowType.SeriesTimer -> seriesTimerInfo?.name - } - - fun getName(context: Context) = when (baseRowType) { - BaseRowType.BaseItem, - BaseRowType.LiveTvRecording, - BaseRowType.LiveTvProgram -> when (baseItem?.type) { - BaseItemKind.AUDIO -> getFullName(context) - else -> baseItem?.name - } - - BaseRowType.Person -> basePerson?.name - BaseRowType.Chapter -> chapterInfo?.name - BaseRowType.LiveTvChannel -> baseItem?.name - BaseRowType.GridButton -> gridButton?.text - BaseRowType.SeriesTimer -> seriesTimerInfo?.name - } - - fun getItemId() = when (baseRowType) { - BaseRowType.BaseItem, - BaseRowType.LiveTvProgram, - BaseRowType.LiveTvChannel, - BaseRowType.LiveTvRecording -> baseItem?.id.toString() - - BaseRowType.Person -> basePerson?.id?.toString() - BaseRowType.Chapter -> chapterInfo?.itemId?.toString() - BaseRowType.GridButton -> null - BaseRowType.SeriesTimer -> seriesTimerInfo?.id - } - - fun getSubText(context: Context) = when (baseRowType) { - BaseRowType.BaseItem -> baseItem?.getSubName(context) - BaseRowType.Person -> basePerson?.role - BaseRowType.Chapter -> chapterInfo?.startPositionTicks?.ticks?.inWholeMilliseconds?.let(TimeUtils::formatMillis) - BaseRowType.LiveTvChannel -> baseItem?.number - BaseRowType.LiveTvProgram -> baseItem?.episodeTitle ?: baseItem?.channelName - BaseRowType.LiveTvRecording -> { - val title = listOfNotNull( - baseItem?.channelName, - baseItem?.episodeTitle - ).joinToString(" - ") - - val timestamp = buildString { - append(SimpleDateFormat("d MMM").format(TimeUtils.getDate(baseItem!!.startDate))) - append(" ") - append((DateFormat.getTimeFormat(context).format(TimeUtils.getDate(baseItem!!.startDate)))) - append(" - ") - append(DateFormat.getTimeFormat(context).format(TimeUtils.getDate(baseItem!!.endDate))) - } - - "$title $timestamp" - } - - BaseRowType.SeriesTimer -> { - val channelName = if (seriesTimerInfo?.recordAnyChannel == true) "All Channels" - else seriesTimerInfo?.channelName - - listOfNotNull(channelName, seriesTimerInfo?.dayPattern).joinToString(" ") - } - - BaseRowType.GridButton -> "" - } - - fun getSummary(context: Context) = when (baseRowType) { - BaseRowType.BaseItem, - BaseRowType.LiveTvRecording, - BaseRowType.LiveTvProgram -> baseItem?.overview - - BaseRowType.SeriesTimer -> seriesTimerInfo?.getSeriesOverview(context) - else -> null - }.orEmpty() - - fun getChildCountStr(): String? { - // Playlist - if (baseItem?.type == BaseItemKind.PLAYLIST) { - val childCount = baseItem?.cumulativeRunTimeTicks?.ticks?.inWholeMilliseconds?.let { - TimeUtils.formatMillis(it) - } - if (childCount != null) return childCount - } - - // Folder - if (isFolder() && baseItem?.type != BaseItemKind.MUSIC_ARTIST) { - val childCount = baseItem?.childCount - if (childCount != null && childCount > 0) return childCount.toString() - } - - // Default - return null - } - - fun getBadgeImage(context: Context): Drawable? { - return when (baseRowType) { - BaseRowType.BaseItem -> when { - baseItem?.type == BaseItemKind.MOVIE && baseItem!!.criticRating != null -> when { - baseItem!!.criticRating!! > 59f -> R.drawable.ic_rt_fresh - else -> R.drawable.ic_rt_rotten - } - - baseItem?.type == BaseItemKind.PROGRAM && baseItem!!.timerId != null -> when { - baseItem!!.seriesTimerId != null -> R.drawable.ic_record_series_red - else -> R.drawable.ic_record_red - } - - else -> R.drawable.blank10x10 - } - - BaseRowType.Person, - BaseRowType.LiveTvProgram -> when { - baseItem?.seriesTimerId != null -> R.drawable.ic_record_series_red - baseItem?.timerId != null -> R.drawable.ic_record_red - else -> R.drawable.blank10x10 - } - - else -> R.drawable.blank10x10 - }.let { ContextCompat.getDrawable(context, it) } - } + open fun getPrimaryImageUrl(context: Context, fillHeight: Int): String? = null + open fun getFullName(context: Context): String? = null + open fun getName(context: Context): String? = null + open fun getItemId(): UUID? = null + open fun getSubText(context: Context): String? = null + open fun getSummary(context: Context): String? = null + open fun getBadgeImage(context: Context): Drawable? = null @JvmOverloads - fun refresh( + open fun refresh( outerResponse: LifecycleAwareResponse, scope: CoroutineScope = ProcessLifecycleOwner.get().lifecycleScope, - ) { - if (baseRowType == BaseRowType.BaseItem) { - val id = getItemId() - val api = get() - - if (id.isNullOrBlank()) { - Timber.w("Skipping call to BaseRowItem.refresh()") - return - } - - scope.launch(Dispatchers.IO) { - baseItem = try { - api.userLibraryApi.getItem(itemId = id.toUUID()).content - } catch (err: ApiClientException) { - Timber.e(err, "Failed to refresh item") - // TODO Only set to null when returned status is 404 (requires Jellyfin 10.9>=) - null - } - - if (outerResponse.active) withContext(Dispatchers.Main) { - outerResponse.onResponse(baseItem) - } - } - } - } + ) = Unit override fun equals(other: Any?): Boolean { if (other is BaseRowItem) return other.getItemId() == getItemId() diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ChapterItemInfoBaseRowItem.kt b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ChapterItemInfoBaseRowItem.kt index d8ed408974..74c79d1e0a 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ChapterItemInfoBaseRowItem.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ChapterItemInfoBaseRowItem.kt @@ -1,6 +1,9 @@ package org.jellyfin.androidtv.ui.itemhandling +import android.content.Context import org.jellyfin.androidtv.data.model.ChapterItemInfo +import org.jellyfin.androidtv.util.TimeUtils +import org.jellyfin.sdk.model.extensions.ticks class ChapterItemInfoBaseRowItem( item: ChapterItemInfo, @@ -8,4 +11,16 @@ class ChapterItemInfoBaseRowItem( baseRowType = BaseRowType.Chapter, staticHeight = true, chapterInfo = item, -) +) { + override fun getPrimaryImageUrl( + context: Context, + fillHeight: Int, + ) = chapterInfo?.imagePath + + override fun getItemId() = chapterInfo?.itemId + override fun getFullName(context: Context) = chapterInfo?.name + override fun getName(context: Context) = chapterInfo?.name + + override fun getSubText(context: Context) = + chapterInfo?.startPositionTicks?.ticks?.inWholeMilliseconds?.let(TimeUtils::formatMillis) +} diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/GridButtonBaseRowItem.kt b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/GridButtonBaseRowItem.kt index a48c8c1639..fbff8c8e61 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/GridButtonBaseRowItem.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/GridButtonBaseRowItem.kt @@ -1,5 +1,6 @@ package org.jellyfin.androidtv.ui.itemhandling +import android.content.Context import org.jellyfin.androidtv.ui.GridButton class GridButtonBaseRowItem( @@ -8,4 +9,14 @@ class GridButtonBaseRowItem( baseRowType = BaseRowType.GridButton, staticHeight = true, gridButton = item, -) +) { + override fun getPrimaryImageUrl( + context: Context, + fillHeight: Int, + ) = gridButton?.imageRes?.let { + imageHelper.getResourceUrl(context, it) + } + + override fun getFullName(context: Context) = gridButton?.text + override fun getName(context: Context) = gridButton?.text +} diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemLauncher.java b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemLauncher.java index 3d65414925..b8e3e7cb04 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemLauncher.java +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemLauncher.java @@ -22,7 +22,6 @@ import org.jellyfin.sdk.model.api.BaseItemDto; import org.jellyfin.sdk.model.api.BaseItemKind; import org.jellyfin.sdk.model.api.CollectionType; -import org.jellyfin.sdk.model.serializer.UUIDSerializerKt; import org.koin.java.KoinJavaComponent; import java.util.ArrayList; @@ -175,7 +174,7 @@ public void onResponse(List response) { case Chapter: final ChapterItemInfo chapter = rowItem.getChapterInfo(); //Start playback of the item at the chapter point - ItemLauncherHelper.getItem(chapter.getItemId(), new Response() { + ItemLauncherHelper.getItem(rowItem.getItemId(), new Response() { @Override public void onResponse(BaseItemDto response) { List items = new ArrayList<>(); @@ -253,7 +252,7 @@ public void onResponse(BaseItemDto response) { break; case SeriesTimer: - navigationRepository.getValue().navigate(Destinations.INSTANCE.seriesTimerDetails(UUIDSerializerKt.toUUID(rowItem.getItemId()), rowItem.getSeriesTimerInfo())); + navigationRepository.getValue().navigate(Destinations.INSTANCE.seriesTimerDetails(rowItem.getItemId(), rowItem.getSeriesTimerInfo())); break; diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/SeriesTimerInfoDtoBaseRowItem.kt b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/SeriesTimerInfoDtoBaseRowItem.kt index 85dd6a6cd4..610cfda680 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/SeriesTimerInfoDtoBaseRowItem.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/SeriesTimerInfoDtoBaseRowItem.kt @@ -1,10 +1,32 @@ package org.jellyfin.androidtv.ui.itemhandling +import android.content.Context +import org.jellyfin.androidtv.R +import org.jellyfin.androidtv.util.apiclient.getSeriesOverview import org.jellyfin.sdk.model.api.SeriesTimerInfoDto +import org.jellyfin.sdk.model.serializer.toUUIDOrNull class SeriesTimerInfoDtoBaseRowItem( item: SeriesTimerInfoDto, ) : BaseRowItem( baseRowType = BaseRowType.SeriesTimer, seriesTimerInfo = item, -) +) { + override fun getPrimaryImageUrl( + context: Context, + fillHeight: Int, + ) = imageHelper.getResourceUrl( + context, + R.drawable.tile_land_series_timer + ) + + override fun getFullName(context: Context) = seriesTimerInfo?.name + override fun getName(context: Context) = seriesTimerInfo?.name + override fun getItemId() = seriesTimerInfo?.id?.toUUIDOrNull() + override fun getSubText(context: Context): String = listOfNotNull( + if (seriesTimerInfo?.recordAnyChannel == true) context.getString(R.string.all_channels) + else seriesTimerInfo?.channelName, + seriesTimerInfo?.dayPattern + ).joinToString(" ") + override fun getSummary(context: Context) = seriesTimerInfo?.getSeriesOverview(context) +} diff --git a/app/src/main/java/org/jellyfin/androidtv/util/KeyProcessor.java b/app/src/main/java/org/jellyfin/androidtv/util/KeyProcessor.java index cc1a070ee0..1ed9cf75d4 100644 --- a/app/src/main/java/org/jellyfin/androidtv/util/KeyProcessor.java +++ b/app/src/main/java/org/jellyfin/androidtv/util/KeyProcessor.java @@ -123,7 +123,7 @@ public boolean handleKey(int key, BaseRowItem rowItem, FragmentActivity activity case LiveTvChannel: case LiveTvRecording: // retrieve full item and play - playbackHelper.getValue().retrieveAndPlay(UUIDSerializerKt.toUUID(rowItem.getItemId()), false, activity); + playbackHelper.getValue().retrieveAndPlay(rowItem.getItemId(), false, activity); return true; case LiveTvProgram: // retrieve channel this program belongs to and play diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7fcb073f27..62347b3c75 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -515,6 +515,7 @@ Require age rating Only showing items with an age rating set Showing all items + All channels %1$s second %1$s seconds From 755f0f6fda1e8f10b5a426c49cb30370d6822158 Mon Sep 17 00:00:00 2001 From: Niels van Velzen Date: Tue, 21 May 2024 19:07:11 +0200 Subject: [PATCH 04/15] Move properties to BaseRowItem subclasses --- .../ui/itemhandling/AudioQueueBaseRowItem.kt | 4 +++- .../ui/itemhandling/BaseItemDtoBaseRowItem.kt | 3 +-- .../ui/itemhandling/BaseItemPersonBaseRowItem.kt | 13 ++++++------- .../androidtv/ui/itemhandling/BaseRowItem.kt | 4 ---- .../androidtv/ui/itemhandling/ItemLauncher.java | 2 +- .../ui/playback/rewrite/RewriteMediaManager.kt | 3 +-- .../androidtv/ui/presentation/CardPresenter.java | 8 +++++--- 7 files changed, 17 insertions(+), 20 deletions(-) diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/AudioQueueBaseRowItem.kt b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/AudioQueueBaseRowItem.kt index aae770c871..3b28f14221 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/AudioQueueBaseRowItem.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/AudioQueueBaseRowItem.kt @@ -9,4 +9,6 @@ class AudioQueueBaseRowItem( index = index, item = item, staticHeight = true, -) +) { + var playing: Boolean = false +} diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemDtoBaseRowItem.kt b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemDtoBaseRowItem.kt index 35b5832533..f5d8d3314f 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemDtoBaseRowItem.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemDtoBaseRowItem.kt @@ -29,7 +29,7 @@ open class BaseItemDtoBaseRowItem @JvmOverloads constructor( preferParentThumb: Boolean = false, staticHeight: Boolean = false, selectAction: BaseRowItemSelectAction = BaseRowItemSelectAction.ShowDetails, - preferSeriesPoster: Boolean = false, + val preferSeriesPoster: Boolean = false, ) : BaseRowItem( baseRowType = when (item.type) { BaseItemKind.TV_CHANNEL -> BaseRowType.LiveTvChannel @@ -42,7 +42,6 @@ open class BaseItemDtoBaseRowItem @JvmOverloads constructor( preferParentThumb = preferParentThumb, selectAction = selectAction, baseItem = item, - preferSeriesPoster = preferSeriesPoster, ) { override val showCardInfoOverlay get() = when (baseItem?.type) { diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemPersonBaseRowItem.kt b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemPersonBaseRowItem.kt index 37000a0321..ba8793e2b3 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemPersonBaseRowItem.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemPersonBaseRowItem.kt @@ -4,19 +4,18 @@ import android.content.Context import org.jellyfin.sdk.model.api.BaseItemPerson class BaseItemPersonBaseRowItem( - item: BaseItemPerson, + val person: BaseItemPerson, ) : BaseRowItem( baseRowType = BaseRowType.Person, staticHeight = true, - basePerson = item, ) { override fun getPrimaryImageUrl( context: Context, fillHeight: Int, - ) = imageHelper.getPrimaryImageUrl(basePerson!!, fillHeight) + ) = imageHelper.getPrimaryImageUrl(person, fillHeight) - override fun getItemId() = basePerson?.id - override fun getFullName(context: Context) = basePerson?.name - override fun getName(context: Context) = basePerson?.name - override fun getSubText(context: Context) = basePerson?.role + override fun getItemId() = person.id + override fun getFullName(context: Context) = person.name + override fun getName(context: Context) = person.name + override fun getSubText(context: Context) = person.role } diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseRowItem.kt b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseRowItem.kt index fcbd61aed7..7620d30598 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseRowItem.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseRowItem.kt @@ -12,7 +12,6 @@ import org.jellyfin.androidtv.util.ImageHelper import org.jellyfin.androidtv.util.apiclient.LifecycleAwareResponse import org.jellyfin.sdk.model.api.BaseItemDto import org.jellyfin.sdk.model.api.BaseItemKind -import org.jellyfin.sdk.model.api.BaseItemPerson import org.jellyfin.sdk.model.api.SeriesTimerInfoDto import org.koin.core.component.KoinComponent import org.koin.core.component.inject @@ -26,13 +25,10 @@ abstract class BaseRowItem protected constructor( val staticHeight: Boolean = false, val preferParentThumb: Boolean = false, val selectAction: BaseRowItemSelectAction = BaseRowItemSelectAction.ShowDetails, - var playing: Boolean = false, var baseItem: BaseItemDto? = null, - val basePerson: BaseItemPerson? = null, val chapterInfo: ChapterItemInfo? = null, val seriesTimerInfo: SeriesTimerInfoDto? = null, val gridButton: GridButton? = null, - var preferSeriesPoster: Boolean = false, ) : KoinComponent { val imageHelper by inject() diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemLauncher.java b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemLauncher.java index b8e3e7cb04..7c11c03aa1 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemLauncher.java +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemLauncher.java @@ -168,7 +168,7 @@ public void onResponse(List response) { } break; case Person: - navigationRepository.getValue().navigate(Destinations.INSTANCE.itemDetails(rowItem.getBasePerson().getId())); + navigationRepository.getValue().navigate(Destinations.INSTANCE.itemDetails(rowItem.getItemId())); break; case Chapter: diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/playback/rewrite/RewriteMediaManager.kt b/app/src/main/java/org/jellyfin/androidtv/ui/playback/rewrite/RewriteMediaManager.kt index d28fc1e781..f91c2d2a61 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/playback/rewrite/RewriteMediaManager.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/playback/rewrite/RewriteMediaManager.kt @@ -12,7 +12,6 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import org.jellyfin.androidtv.constant.QueryType import org.jellyfin.androidtv.ui.itemhandling.AudioQueueBaseRowItem -import org.jellyfin.androidtv.ui.itemhandling.BaseRowItem import org.jellyfin.androidtv.ui.itemhandling.ItemRowAdapter import org.jellyfin.androidtv.ui.navigation.Destinations import org.jellyfin.androidtv.ui.navigation.NavigationRepository @@ -106,7 +105,7 @@ class RewriteMediaManager( private suspend fun watchPlaybackStateChanges() = coroutineScope { playbackManager.state.playState.onEach { playState -> notifyListeners { - val firstItem = currentAudioQueue.get(0) as? BaseRowItem + val firstItem = currentAudioQueue.get(0) as? AudioQueueBaseRowItem firstItem?.playing = playState == PlayState.PLAYING onPlaybackStateChange(when (playState) { diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/presentation/CardPresenter.java b/app/src/main/java/org/jellyfin/androidtv/ui/presentation/CardPresenter.java index 0eb2704799..01c85e49cf 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/presentation/CardPresenter.java +++ b/app/src/main/java/org/jellyfin/androidtv/ui/presentation/CardPresenter.java @@ -19,6 +19,8 @@ import org.jellyfin.androidtv.preference.constant.RatingType; import org.jellyfin.androidtv.preference.constant.WatchedIndicatorBehavior; import org.jellyfin.androidtv.ui.card.LegacyImageCardView; +import org.jellyfin.androidtv.ui.itemhandling.AudioQueueBaseRowItem; +import org.jellyfin.androidtv.ui.itemhandling.BaseItemDtoBaseRowItem; import org.jellyfin.androidtv.ui.itemhandling.BaseRowItem; import org.jellyfin.androidtv.util.ImageHelper; import org.jellyfin.androidtv.util.Utils; @@ -135,7 +137,7 @@ public void setItem(BaseRowItem m, ImageType imageType, int lHeight, int pHeight aspect = ImageHelper.ASPECT_RATIO_2_3; break; case EPISODE: - if (m.getPreferSeriesPoster()) { + if (m instanceof BaseItemDtoBaseRowItem && ((BaseItemDtoBaseRowItem) m).getPreferSeriesPoster()) { mDefaultCardImage = ContextCompat.getDrawable(mCardView.getContext(), R.drawable.tile_port_tv); aspect = ImageHelper.ASPECT_RATIO_2_3; } else { @@ -360,7 +362,7 @@ public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) { holder.mCardView.setOverlayInfo(rowItem); } holder.mCardView.showFavIcon(rowItem.isFavorite()); - if (rowItem.getPlaying()) { + if (rowItem instanceof AudioQueueBaseRowItem && ((AudioQueueBaseRowItem) rowItem).getPlaying()) { holder.mCardView.setPlayingIndicator(true); } else { holder.mCardView.setPlayingIndicator(false); @@ -388,7 +390,7 @@ public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) { if (aspect == ImageHelper.ASPECT_RATIO_BANNER) { blurHashMap = rowItem.getBaseItem().getImageBlurHashes().get(org.jellyfin.sdk.model.api.ImageType.BANNER); imageTag = rowItem.getBaseItem().getImageTags().get(org.jellyfin.sdk.model.api.ImageType.BANNER); - } else if (aspect == ImageHelper.ASPECT_RATIO_2_3 && rowItem.getBaseItemType() == BaseItemKind.EPISODE && rowItem.getPreferSeriesPoster()) { + } else if (aspect == ImageHelper.ASPECT_RATIO_2_3 && rowItem.getBaseItemType() == BaseItemKind.EPISODE && rowItem instanceof BaseItemDtoBaseRowItem && ((BaseItemDtoBaseRowItem) rowItem).getPreferSeriesPoster()) { blurHashMap = rowItem.getBaseItem().getImageBlurHashes().get(org.jellyfin.sdk.model.api.ImageType.PRIMARY); imageTag = rowItem.getBaseItem().getSeriesPrimaryImageTag(); } else if (aspect == ImageHelper.ASPECT_RATIO_16_9 && !isUserView && (rowItem.getBaseItemType() != BaseItemKind.EPISODE || !rowItem.getBaseItem().getImageTags().containsKey(org.jellyfin.sdk.model.api.ImageType.PRIMARY) || (rowItem.getPreferParentThumb() && rowItem.getBaseItem().getParentThumbImageTag() != null))) { From 107475641c3d85c1c7423578fba5f00d3316b8f4 Mon Sep 17 00:00:00 2001 From: Niels van Velzen Date: Wed, 22 May 2024 20:44:54 +0200 Subject: [PATCH 05/15] Add missing BaseItemKinds in BaseItemDtoBaseRowItem --- .../ui/itemhandling/BaseItemDtoBaseRowItem.kt | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemDtoBaseRowItem.kt b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemDtoBaseRowItem.kt index f5d8d3314f..d3491a0255 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemDtoBaseRowItem.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemDtoBaseRowItem.kt @@ -32,8 +32,14 @@ open class BaseItemDtoBaseRowItem @JvmOverloads constructor( val preferSeriesPoster: Boolean = false, ) : BaseRowItem( baseRowType = when (item.type) { - BaseItemKind.TV_CHANNEL -> BaseRowType.LiveTvChannel - BaseItemKind.PROGRAM -> BaseRowType.LiveTvProgram + BaseItemKind.CHANNEL, + BaseItemKind.TV_CHANNEL, + BaseItemKind.LIVE_TV_CHANNEL -> BaseRowType.LiveTvChannel + + BaseItemKind.PROGRAM, + BaseItemKind.TV_PROGRAM, + BaseItemKind.LIVE_TV_PROGRAM -> BaseRowType.LiveTvProgram + BaseItemKind.RECORDING -> BaseRowType.LiveTvRecording else -> BaseRowType.BaseItem }, From ef07ebd06181808f13ab9444f892620a0d135f07 Mon Sep 17 00:00:00 2001 From: Niels van Velzen Date: Wed, 22 May 2024 21:51:42 +0200 Subject: [PATCH 06/15] Remove refresh function from BaseRowItem --- .../ui/browsing/BrowseGridFragment.java | 43 ++++++--------- .../ui/browsing/EnhancedBrowseFragment.java | 22 ++------ .../androidtv/ui/home/HomeRowsFragment.kt | 22 ++------ .../ui/itemhandling/BaseItemDtoBaseRowItem.kt | 36 ------------- .../androidtv/ui/itemhandling/BaseRowItem.kt | 10 ---- .../ui/itemhandling/ItemRowAdapterHelper.kt | 54 ++++++++++++++++++- 6 files changed, 78 insertions(+), 109 deletions(-) diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/browsing/BrowseGridFragment.java b/app/src/main/java/org/jellyfin/androidtv/ui/browsing/BrowseGridFragment.java index a615f66898..42e47385f4 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/browsing/BrowseGridFragment.java +++ b/app/src/main/java/org/jellyfin/androidtv/ui/browsing/BrowseGridFragment.java @@ -52,6 +52,7 @@ import org.jellyfin.androidtv.ui.itemhandling.BaseRowItem; import org.jellyfin.androidtv.ui.itemhandling.ItemLauncher; import org.jellyfin.androidtv.ui.itemhandling.ItemRowAdapter; +import org.jellyfin.androidtv.ui.itemhandling.ItemRowAdapterHelperKt; import org.jellyfin.androidtv.ui.navigation.Destinations; import org.jellyfin.androidtv.ui.navigation.NavigationRepository; import org.jellyfin.androidtv.ui.presentation.CardPresenter; @@ -62,9 +63,9 @@ import org.jellyfin.androidtv.util.KeyProcessor; import org.jellyfin.androidtv.util.Utils; import org.jellyfin.androidtv.util.apiclient.EmptyLifecycleAwareResponse; -import org.jellyfin.androidtv.util.apiclient.LifecycleAwareResponse; import org.jellyfin.apiclient.model.querying.ArtistsQuery; import org.jellyfin.apiclient.model.querying.ItemFields; +import org.jellyfin.sdk.api.client.ApiClient; import org.jellyfin.sdk.model.api.BaseItemDto; import org.jellyfin.sdk.model.api.BaseItemKind; import org.jellyfin.sdk.model.api.CollectionType; @@ -125,6 +126,7 @@ public class BrowseGridFragment extends Fragment implements View.OnKeyListener { private final Lazy navigationRepository = inject(NavigationRepository.class); private final Lazy itemLauncher = inject(ItemLauncher.class); private final Lazy keyProcessor = inject(KeyProcessor.class); + private final Lazy api = inject(ApiClient.class); private int mCardsScreenEst = 0; private int mCardsScreenStride = 0; @@ -934,31 +936,20 @@ else if (mGridPresenter instanceof VerticalGridPresenter) } private void refreshCurrentItem() { - if (mCurrentItem != null && mCurrentItem.getBaseItemType() != BaseItemKind.PHOTO && mCurrentItem.getBaseItemType() != BaseItemKind.PHOTO_ALBUM - && mCurrentItem.getBaseItemType() != BaseItemKind.MUSIC_ARTIST && mCurrentItem.getBaseItemType() != BaseItemKind.MUSIC_ALBUM) { - Timber.d("Refresh item \"%s\"", mCurrentItem.getFullName(requireContext())); - BaseRowItem item = mCurrentItem; - item.refresh(new LifecycleAwareResponse(getLifecycle()) { - @Override - public void onResponse(BaseItemDto response) { - if (!getActive()) return; - - if (response == null) mAdapter.removeAt(mAdapter.indexOf(item), 1); - else mAdapter.notifyItemRangeChanged(mAdapter.indexOf(item), 1); - - //Now - if filtered make sure we still pass - if (response != null && mAdapter.getFilters() != null) { - if ((mAdapter.getFilters().isFavoriteOnly() && !item.isFavorite()) || (mAdapter.getFilters().isUnwatchedOnly() && item.isPlayed())) { - // if we are about to remove the current item, throw focus to toolbar so framework doesn't crash - binding.toolBar.requestFocus(); - mAdapter.remove(item); - mAdapter.setTotalItems(mAdapter.getTotalItems() - 1); - updateCounter(item.getIndex()); - } - } - } - }); - } + if (mCurrentItem == null) return; + Timber.d("Refresh item \"%s\"", mCurrentItem.getFullName(requireContext())); + ItemRowAdapterHelperKt.refreshItem(mAdapter, api.getValue(), this, mCurrentItem, () -> { + //Now - if filtered make sure we still pass + if (mAdapter.getFilters() == null) return null; + if ((mAdapter.getFilters().isFavoriteOnly() && !mCurrentItem.isFavorite()) || (mAdapter.getFilters().isUnwatchedOnly() && mCurrentItem.isPlayed())) { + // if we are about to remove the current item, throw focus to toolbar so framework doesn't crash + binding.toolBar.requestFocus(); + mAdapter.remove(mCurrentItem); + mAdapter.setTotalItems(mAdapter.getTotalItems() - 1); + updateCounter(mCurrentItem.getIndex()); + } + return null; + }); } private final class ItemViewClickedListener implements OnItemViewClickedListener { diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/browsing/EnhancedBrowseFragment.java b/app/src/main/java/org/jellyfin/androidtv/ui/browsing/EnhancedBrowseFragment.java index 8ae106d3b1..f3bf9ee61f 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/browsing/EnhancedBrowseFragment.java +++ b/app/src/main/java/org/jellyfin/androidtv/ui/browsing/EnhancedBrowseFragment.java @@ -42,6 +42,7 @@ import org.jellyfin.androidtv.ui.itemhandling.BaseRowItem; import org.jellyfin.androidtv.ui.itemhandling.ItemLauncher; import org.jellyfin.androidtv.ui.itemhandling.ItemRowAdapter; +import org.jellyfin.androidtv.ui.itemhandling.ItemRowAdapterHelperKt; import org.jellyfin.androidtv.ui.navigation.Destinations; import org.jellyfin.androidtv.ui.navigation.NavigationRepository; import org.jellyfin.androidtv.ui.presentation.CardPresenter; @@ -52,7 +53,6 @@ import org.jellyfin.androidtv.util.InfoLayoutHelper; import org.jellyfin.androidtv.util.KeyProcessor; import org.jellyfin.androidtv.util.MarkdownRenderer; -import org.jellyfin.androidtv.util.apiclient.LifecycleAwareResponse; import org.jellyfin.androidtv.util.sdk.compat.JavaCompat; import org.jellyfin.sdk.api.client.ApiClient; import org.jellyfin.sdk.model.api.BaseItemDto; @@ -354,24 +354,8 @@ public boolean onKey(View v, int keyCode, KeyEvent event) { } private void refreshCurrentItem() { - if (mCurrentItem != null && - mCurrentItem.getBaseItemType() != BaseItemKind.PHOTO && - mCurrentItem.getBaseItemType() != BaseItemKind.MUSIC_ARTIST && - mCurrentItem.getBaseItemType() != BaseItemKind.MUSIC_ALBUM && - mCurrentItem.getBaseItemType() != BaseItemKind.PLAYLIST - ) { - BaseRowItem item = mCurrentItem; - item.refresh(new LifecycleAwareResponse(getLifecycle()) { - @Override - public void onResponse(BaseItemDto response) { - if (!getActive()) return; - - ItemRowAdapter adapter = (ItemRowAdapter) mCurrentRow.getAdapter(); - if (response == null) adapter.removeAt(adapter.indexOf(item), 1); - else adapter.notifyItemRangeChanged(adapter.indexOf(item), 1); - } - }); - } + if (mCurrentRow == null || mCurrentItem == null) return; + ItemRowAdapterHelperKt.refreshItem((ItemRowAdapter) mCurrentRow.getAdapter(), api.getValue(), this, mCurrentItem); } private final class SpecialViewClickedListener implements OnItemViewClickedListener { diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/home/HomeRowsFragment.kt b/app/src/main/java/org/jellyfin/androidtv/ui/home/HomeRowsFragment.kt index c9e23f76cd..034a7f85cc 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/home/HomeRowsFragment.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/home/HomeRowsFragment.kt @@ -39,6 +39,7 @@ import org.jellyfin.androidtv.ui.browsing.CompositeSelectedListener import org.jellyfin.androidtv.ui.itemhandling.BaseRowItem import org.jellyfin.androidtv.ui.itemhandling.ItemLauncher import org.jellyfin.androidtv.ui.itemhandling.ItemRowAdapter +import org.jellyfin.androidtv.ui.itemhandling.refreshItem import org.jellyfin.androidtv.ui.navigation.NavigationRepository import org.jellyfin.androidtv.ui.playback.AudioEventListener import org.jellyfin.androidtv.ui.playback.MediaManager @@ -46,12 +47,9 @@ import org.jellyfin.androidtv.ui.presentation.CardPresenter import org.jellyfin.androidtv.ui.presentation.MutableObjectAdapter import org.jellyfin.androidtv.ui.presentation.PositionableListRowPresenter import org.jellyfin.androidtv.util.KeyProcessor -import org.jellyfin.androidtv.util.apiclient.LifecycleAwareResponse import org.jellyfin.sdk.api.client.ApiClient import org.jellyfin.sdk.api.client.extensions.liveTvApi import org.jellyfin.sdk.api.sockets.subscribe -import org.jellyfin.sdk.model.api.BaseItemDto -import org.jellyfin.sdk.model.api.BaseItemKind import org.jellyfin.sdk.model.api.LibraryChangedMessage import org.jellyfin.sdk.model.api.UserDataChangedMessage import org.koin.android.ext.android.inject @@ -232,21 +230,11 @@ class HomeRowsFragment : RowsSupportFragment(), AudioEventListener, View.OnKeyLi } private fun refreshCurrentItem() { - currentItem?.let { item -> - if (item.getBaseItemType() == BaseItemKind.USER_VIEW || item.getBaseItemType() == BaseItemKind.COLLECTION_FOLDER) return + val adapter = currentRow?.adapter as? ItemRowAdapter ?: return + val item = currentItem ?: return - Timber.d("Refresh item ${item.getFullName(requireContext())}") - - item.refresh(object : LifecycleAwareResponse(lifecycle) { - override fun onResponse(response: BaseItemDto?) { - if (!active) return - - val adapter = currentRow?.adapter as? ItemRowAdapter - if (response == null) adapter?.removeAt(adapter.indexOf(item), 1) - else adapter?.notifyItemRangeChanged(adapter.indexOf(item), 1) - } - }) - } + Timber.d("Refresh item ${item.getFullName(requireContext())}") + adapter.refreshItem(api, this, item) } override fun onDestroy() { diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemDtoBaseRowItem.kt b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemDtoBaseRowItem.kt index d3491a0255..e514b16128 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemDtoBaseRowItem.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemDtoBaseRowItem.kt @@ -3,24 +3,14 @@ package org.jellyfin.androidtv.ui.itemhandling import android.content.Context import android.text.format.DateFormat import androidx.core.content.ContextCompat -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import org.jellyfin.androidtv.R import org.jellyfin.androidtv.constant.ImageType import org.jellyfin.androidtv.util.TimeUtils -import org.jellyfin.androidtv.util.apiclient.LifecycleAwareResponse import org.jellyfin.androidtv.util.sdk.getFullName import org.jellyfin.androidtv.util.sdk.getSubName -import org.jellyfin.sdk.api.client.ApiClient -import org.jellyfin.sdk.api.client.exception.ApiClientException -import org.jellyfin.sdk.api.client.extensions.userLibraryApi import org.jellyfin.sdk.model.api.BaseItemDto import org.jellyfin.sdk.model.api.BaseItemKind import org.jellyfin.sdk.model.extensions.ticks -import org.koin.core.component.get -import timber.log.Timber import java.text.SimpleDateFormat open class BaseItemDtoBaseRowItem @JvmOverloads constructor( @@ -194,30 +184,4 @@ open class BaseItemDtoBaseRowItem @JvmOverloads constructor( else -> null } }?.let { ContextCompat.getDrawable(context, it) } - - override fun refresh( - outerResponse: LifecycleAwareResponse, - scope: CoroutineScope, - ) { - val itemId = baseItem?.id - val api = get() - - if (itemId == null) { - Timber.w("Skipping call to BaseRowItem.refresh()") - return - } - - scope.launch(Dispatchers.IO) { - baseItem = try { - api.userLibraryApi.getItem(itemId = itemId).content - } catch (err: ApiClientException) { - Timber.e(err, "Failed to refresh item") - null - } - - if (outerResponse.active) withContext(Dispatchers.Main) { - outerResponse.onResponse(baseItem) - } - } - } } diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseRowItem.kt b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseRowItem.kt index 7620d30598..b8958bb1fe 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseRowItem.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseRowItem.kt @@ -2,14 +2,10 @@ package org.jellyfin.androidtv.ui.itemhandling import android.content.Context import android.graphics.drawable.Drawable -import androidx.lifecycle.ProcessLifecycleOwner -import androidx.lifecycle.lifecycleScope -import kotlinx.coroutines.CoroutineScope import org.jellyfin.androidtv.constant.ImageType import org.jellyfin.androidtv.data.model.ChapterItemInfo import org.jellyfin.androidtv.ui.GridButton import org.jellyfin.androidtv.util.ImageHelper -import org.jellyfin.androidtv.util.apiclient.LifecycleAwareResponse import org.jellyfin.sdk.model.api.BaseItemDto import org.jellyfin.sdk.model.api.BaseItemKind import org.jellyfin.sdk.model.api.SeriesTimerInfoDto @@ -56,12 +52,6 @@ abstract class BaseRowItem protected constructor( open fun getSummary(context: Context): String? = null open fun getBadgeImage(context: Context): Drawable? = null - @JvmOverloads - open fun refresh( - outerResponse: LifecycleAwareResponse, - scope: CoroutineScope = ProcessLifecycleOwner.get().lifecycleScope, - ) = Unit - override fun equals(other: Any?): Boolean { if (other is BaseRowItem) return other.getItemId() == getItemId() return super.equals(other) diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemRowAdapterHelper.kt b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemRowAdapterHelper.kt index a4dac63821..1c661f9d43 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemRowAdapterHelper.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemRowAdapterHelper.kt @@ -1,10 +1,15 @@ package org.jellyfin.androidtv.ui.itemhandling +import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.ProcessLifecycleOwner import androidx.lifecycle.lifecycleScope +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import org.jellyfin.sdk.api.client.ApiClient +import org.jellyfin.sdk.api.client.exception.InvalidStatusException import org.jellyfin.sdk.api.client.extensions.itemsApi +import org.jellyfin.sdk.api.client.extensions.userLibraryApi import org.jellyfin.sdk.model.api.request.GetResumeItemsRequest import timber.log.Timber import kotlin.math.min @@ -43,9 +48,56 @@ fun ItemRowAdapter.retrieveResumeItems(api: ApiClient, query: GetResumeItemsRequ setItems( items = response.items.orEmpty().toTypedArray(), - transform = { item, i -> BaseItemDtoBaseRowItem(i, item, preferParentThumb, isStaticHeight) } + transform = { item, i -> + BaseItemDtoBaseRowItem( + i, + item, + preferParentThumb, + isStaticHeight + ) + } ) if (response.items.isNullOrEmpty()) removeRow() } } + +@JvmOverloads +fun ItemRowAdapter.refreshItem( + api: ApiClient, + lifecycleOwner: LifecycleOwner, + currentBaseRowItem: BaseRowItem, + callback: () -> Unit = {} +) { + if (currentBaseRowItem !is BaseItemDtoBaseRowItem) return + val currentBaseItem = currentBaseRowItem.baseItem ?: return + + lifecycleOwner.lifecycleScope.launch(Dispatchers.IO) { + runCatching { + api.userLibraryApi.getItem(itemId = currentBaseItem.id).content + }.fold( + onSuccess = { refreshedBaseItem -> + withContext(Dispatchers.Main) { + set( + index = indexOf(currentBaseRowItem), + element = BaseItemDtoBaseRowItem( + index = currentBaseRowItem.index, + item = refreshedBaseItem, + preferParentThumb = currentBaseRowItem.preferParentThumb, + staticHeight = currentBaseRowItem.staticHeight, + selectAction = currentBaseRowItem.selectAction, + preferSeriesPoster = currentBaseRowItem.preferSeriesPoster + ) + ) + } + }, + onFailure = { err -> + if (err is InvalidStatusException && err.status == 404) withContext(Dispatchers.Main) { + remove(currentBaseRowItem) + } else Timber.e(err, "Failed to refresh item") + } + ) + + callback() + } +} From 1bad4c0fd81ad0e458cdce0137f546a9580e3b6a Mon Sep 17 00:00:00 2001 From: Niels van Velzen Date: Wed, 22 May 2024 21:52:03 +0200 Subject: [PATCH 07/15] Optimize MutableObjectAdapter.remove(element: T) --- .../androidtv/ui/presentation/MutableObjectAdapter.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/presentation/MutableObjectAdapter.kt b/app/src/main/java/org/jellyfin/androidtv/ui/presentation/MutableObjectAdapter.kt index 12e5b24239..67b8398538 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/presentation/MutableObjectAdapter.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/presentation/MutableObjectAdapter.kt @@ -77,9 +77,9 @@ open class MutableObjectAdapter : ObjectAdapter, Iterable { } fun remove(element: T): Boolean { - val removed = data.remove(element) - if (removed) notifyChanged() - return removed + val index = indexOf(element) + if (index == -1) return false + return removeAt(index, 1) } fun removeAt(index: Int, length: Int = 1): Boolean { From 38e826d2a2a89760bd084c5ad56cea2a99dd5311 Mon Sep 17 00:00:00 2001 From: Niels van Velzen Date: Wed, 22 May 2024 21:53:23 +0200 Subject: [PATCH 08/15] Make BaseRowItem immutable --- .../org/jellyfin/androidtv/ui/itemhandling/BaseRowItem.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseRowItem.kt b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseRowItem.kt index b8958bb1fe..a73cc88015 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseRowItem.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseRowItem.kt @@ -17,11 +17,11 @@ import java.util.UUID // BaseItemDtoBaseRowItem) abstract class BaseRowItem protected constructor( val baseRowType: BaseRowType, - var index: Int = 0, + val index: Int = 0, val staticHeight: Boolean = false, val preferParentThumb: Boolean = false, val selectAction: BaseRowItemSelectAction = BaseRowItemSelectAction.ShowDetails, - var baseItem: BaseItemDto? = null, + val baseItem: BaseItemDto? = null, val chapterInfo: ChapterItemInfo? = null, val seriesTimerInfo: SeriesTimerInfoDto? = null, val gridButton: GridButton? = null, From 8d04ff35cce6b24fe70172515791778afa20e76e Mon Sep 17 00:00:00 2001 From: Niels van Velzen Date: Wed, 22 May 2024 22:02:10 +0200 Subject: [PATCH 09/15] Remove dependency injection from BaseRowItem --- .../ui/itemhandling/BaseItemDtoBaseRowItem.kt | 55 ++++++++++--------- .../itemhandling/BaseItemPersonBaseRowItem.kt | 9 ++- .../androidtv/ui/itemhandling/BaseRowItem.kt | 14 ++--- .../ChapterItemInfoBaseRowItem.kt | 9 ++- .../ui/itemhandling/GridButtonBaseRowItem.kt | 9 ++- .../SeriesTimerInfoDtoBaseRowItem.kt | 10 +++- .../ui/presentation/CardPresenter.java | 13 ++--- 7 files changed, 67 insertions(+), 52 deletions(-) diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemDtoBaseRowItem.kt b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemDtoBaseRowItem.kt index e514b16128..a740d4a79a 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemDtoBaseRowItem.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemDtoBaseRowItem.kt @@ -5,6 +5,7 @@ import android.text.format.DateFormat import androidx.core.content.ContextCompat import org.jellyfin.androidtv.R import org.jellyfin.androidtv.constant.ImageType +import org.jellyfin.androidtv.util.ImageHelper import org.jellyfin.androidtv.util.TimeUtils import org.jellyfin.androidtv.util.sdk.getFullName import org.jellyfin.androidtv.util.sdk.getSubName @@ -63,15 +64,15 @@ open class BaseItemDtoBaseRowItem @JvmOverloads constructor( override fun getChildCountStr(): String? { // Playlist if (baseItem?.type == BaseItemKind.PLAYLIST) { - val childCount = baseItem?.cumulativeRunTimeTicks?.ticks?.let { duration -> + val childCount = baseItem.cumulativeRunTimeTicks?.ticks?.let { duration -> TimeUtils.formatMillis(duration.inWholeMilliseconds) } if (childCount != null) return childCount } // Folder - if (baseItem?.isFolder == true && baseItem?.type != BaseItemKind.MUSIC_ARTIST) { - val childCount = baseItem?.childCount + if (baseItem?.isFolder == true && baseItem.type != BaseItemKind.MUSIC_ARTIST) { + val childCount = baseItem.childCount if (childCount != null && childCount > 0) return childCount.toString() } @@ -80,39 +81,40 @@ open class BaseItemDtoBaseRowItem @JvmOverloads constructor( } override fun getCardName(context: Context) = when { - baseItem?.type == BaseItemKind.AUDIO && baseItem?.albumArtist != null -> baseItem?.albumArtist - baseItem?.type == BaseItemKind.AUDIO && baseItem?.album != null -> baseItem?.album + baseItem?.type == BaseItemKind.AUDIO && baseItem.albumArtist != null -> baseItem.albumArtist + baseItem?.type == BaseItemKind.AUDIO && baseItem.album != null -> baseItem.album else -> baseItem?.getFullName(context) } override fun getFullName(context: Context) = baseItem?.getFullName(context) override fun getName(context: Context) = when (baseItem?.type) { - BaseItemKind.AUDIO -> baseItem?.getFullName(context) + BaseItemKind.AUDIO -> baseItem.getFullName(context) else -> baseItem?.name } override fun getSummary(context: Context) = baseItem?.overview override fun getSubText(context: Context) = when (baseItem?.type) { - BaseItemKind.TV_CHANNEL -> baseItem?.number + BaseItemKind.TV_CHANNEL -> baseItem.number BaseItemKind.TV_PROGRAM, - BaseItemKind.PROGRAM -> baseItem?.episodeTitle ?: baseItem?.channelName + BaseItemKind.PROGRAM -> baseItem.episodeTitle ?: baseItem.channelName + BaseItemKind.RECORDING -> { val title = listOfNotNull( - baseItem?.channelName, - baseItem?.episodeTitle + baseItem.channelName, + baseItem.episodeTitle ).joinToString(" - ") val timestamp = buildString { - append(SimpleDateFormat("d MMM").format(TimeUtils.getDate(baseItem!!.startDate))) + append(SimpleDateFormat("d MMM").format(TimeUtils.getDate(baseItem.startDate))) append(" ") append( (DateFormat.getTimeFormat(context) - .format(TimeUtils.getDate(baseItem!!.startDate))) + .format(TimeUtils.getDate(baseItem.startDate))) ) append(" - ") append( - DateFormat.getTimeFormat(context).format(TimeUtils.getDate(baseItem!!.endDate)) + DateFormat.getTimeFormat(context).format(TimeUtils.getDate(baseItem.endDate)) ) } @@ -124,6 +126,7 @@ open class BaseItemDtoBaseRowItem @JvmOverloads constructor( override fun getImageUrl( context: Context, + imageHelper: ImageHelper, imageType: ImageType, fillWidth: Int, fillHeight: Int @@ -152,32 +155,30 @@ open class BaseItemDtoBaseRowItem @JvmOverloads constructor( ), fillWidth, fillHeight ) - else -> getPrimaryImageUrl(context, fillHeight) + else -> imageHelper.getPrimaryImageUrl( + baseItem!!, + preferParentThumb, + null, + fillHeight + ) } } - override fun getPrimaryImageUrl( + override fun getBadgeImage( context: Context, - fillHeight: Int, - ) = imageHelper.getPrimaryImageUrl( - baseItem!!, - preferParentThumb, - null, - fillHeight - ) - - override fun getBadgeImage(context: Context) = when (baseItem?.type) { + imageHelper: ImageHelper, + ) = when (baseItem?.type) { BaseItemKind.LIVE_TV_PROGRAM, BaseItemKind.PROGRAM -> when { - baseItem?.seriesTimerId != null -> R.drawable.ic_record_series_red - baseItem?.timerId != null -> R.drawable.ic_record_red + baseItem.seriesTimerId != null -> R.drawable.ic_record_series_red + baseItem.timerId != null -> R.drawable.ic_record_red else -> null } else -> when { baseItem?.criticRating != null -> when { - baseItem!!.criticRating!! > 59f -> R.drawable.ic_rt_fresh + baseItem.criticRating!! > 59f -> R.drawable.ic_rt_fresh else -> R.drawable.ic_rt_rotten } diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemPersonBaseRowItem.kt b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemPersonBaseRowItem.kt index ba8793e2b3..6c42d3839e 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemPersonBaseRowItem.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemPersonBaseRowItem.kt @@ -1,6 +1,8 @@ package org.jellyfin.androidtv.ui.itemhandling import android.content.Context +import org.jellyfin.androidtv.constant.ImageType +import org.jellyfin.androidtv.util.ImageHelper import org.jellyfin.sdk.model.api.BaseItemPerson class BaseItemPersonBaseRowItem( @@ -9,9 +11,12 @@ class BaseItemPersonBaseRowItem( baseRowType = BaseRowType.Person, staticHeight = true, ) { - override fun getPrimaryImageUrl( + override fun getImageUrl( context: Context, - fillHeight: Int, + imageHelper: ImageHelper, + imageType: ImageType, + fillWidth: Int, + fillHeight: Int ) = imageHelper.getPrimaryImageUrl(person, fillHeight) override fun getItemId() = person.id diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseRowItem.kt b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseRowItem.kt index a73cc88015..f2608a53d8 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseRowItem.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseRowItem.kt @@ -9,12 +9,8 @@ import org.jellyfin.androidtv.util.ImageHelper import org.jellyfin.sdk.model.api.BaseItemDto import org.jellyfin.sdk.model.api.BaseItemKind import org.jellyfin.sdk.model.api.SeriesTimerInfoDto -import org.koin.core.component.KoinComponent -import org.koin.core.component.inject import java.util.UUID -// TODO: Move properties to relevant classes only (e.g. baseItem should be in -// BaseItemDtoBaseRowItem) abstract class BaseRowItem protected constructor( val baseRowType: BaseRowType, val index: Int = 0, @@ -25,9 +21,7 @@ abstract class BaseRowItem protected constructor( val chapterInfo: ChapterItemInfo? = null, val seriesTimerInfo: SeriesTimerInfoDto? = null, val gridButton: GridButton? = null, -) : KoinComponent { - val imageHelper by inject() - +) { open val showCardInfoOverlay: Boolean = false open fun getBaseItemType(): BaseItemKind? = null open fun isFavorite(): Boolean = false @@ -39,18 +33,18 @@ abstract class BaseRowItem protected constructor( open fun getImageUrl( context: Context, + imageHelper: ImageHelper, imageType: ImageType, fillWidth: Int, fillHeight: Int, - ) = getPrimaryImageUrl(context, fillHeight) + ): String? = null - open fun getPrimaryImageUrl(context: Context, fillHeight: Int): String? = null open fun getFullName(context: Context): String? = null open fun getName(context: Context): String? = null open fun getItemId(): UUID? = null open fun getSubText(context: Context): String? = null open fun getSummary(context: Context): String? = null - open fun getBadgeImage(context: Context): Drawable? = null + open fun getBadgeImage(context: Context, imageHelper: ImageHelper): Drawable? = null override fun equals(other: Any?): Boolean { if (other is BaseRowItem) return other.getItemId() == getItemId() diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ChapterItemInfoBaseRowItem.kt b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ChapterItemInfoBaseRowItem.kt index 74c79d1e0a..fa017a0098 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ChapterItemInfoBaseRowItem.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ChapterItemInfoBaseRowItem.kt @@ -1,7 +1,9 @@ package org.jellyfin.androidtv.ui.itemhandling import android.content.Context +import org.jellyfin.androidtv.constant.ImageType import org.jellyfin.androidtv.data.model.ChapterItemInfo +import org.jellyfin.androidtv.util.ImageHelper import org.jellyfin.androidtv.util.TimeUtils import org.jellyfin.sdk.model.extensions.ticks @@ -12,9 +14,12 @@ class ChapterItemInfoBaseRowItem( staticHeight = true, chapterInfo = item, ) { - override fun getPrimaryImageUrl( + override fun getImageUrl( context: Context, - fillHeight: Int, + imageHelper: ImageHelper, + imageType: ImageType, + fillWidth: Int, + fillHeight: Int ) = chapterInfo?.imagePath override fun getItemId() = chapterInfo?.itemId diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/GridButtonBaseRowItem.kt b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/GridButtonBaseRowItem.kt index fbff8c8e61..f58a924aa1 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/GridButtonBaseRowItem.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/GridButtonBaseRowItem.kt @@ -1,7 +1,9 @@ package org.jellyfin.androidtv.ui.itemhandling import android.content.Context +import org.jellyfin.androidtv.constant.ImageType import org.jellyfin.androidtv.ui.GridButton +import org.jellyfin.androidtv.util.ImageHelper class GridButtonBaseRowItem( item: GridButton, @@ -10,9 +12,12 @@ class GridButtonBaseRowItem( staticHeight = true, gridButton = item, ) { - override fun getPrimaryImageUrl( + override fun getImageUrl( context: Context, - fillHeight: Int, + imageHelper: ImageHelper, + imageType: ImageType, + fillWidth: Int, + fillHeight: Int ) = gridButton?.imageRes?.let { imageHelper.getResourceUrl(context, it) } diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/SeriesTimerInfoDtoBaseRowItem.kt b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/SeriesTimerInfoDtoBaseRowItem.kt index 610cfda680..da5d33327a 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/SeriesTimerInfoDtoBaseRowItem.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/SeriesTimerInfoDtoBaseRowItem.kt @@ -2,6 +2,8 @@ package org.jellyfin.androidtv.ui.itemhandling import android.content.Context import org.jellyfin.androidtv.R +import org.jellyfin.androidtv.constant.ImageType +import org.jellyfin.androidtv.util.ImageHelper import org.jellyfin.androidtv.util.apiclient.getSeriesOverview import org.jellyfin.sdk.model.api.SeriesTimerInfoDto import org.jellyfin.sdk.model.serializer.toUUIDOrNull @@ -12,9 +14,12 @@ class SeriesTimerInfoDtoBaseRowItem( baseRowType = BaseRowType.SeriesTimer, seriesTimerInfo = item, ) { - override fun getPrimaryImageUrl( + override fun getImageUrl( context: Context, - fillHeight: Int, + imageHelper: ImageHelper, + imageType: ImageType, + fillWidth: Int, + fillHeight: Int ) = imageHelper.getResourceUrl( context, R.drawable.tile_land_series_timer @@ -28,5 +33,6 @@ class SeriesTimerInfoDtoBaseRowItem( else seriesTimerInfo?.channelName, seriesTimerInfo?.dayPattern ).joinToString(" ") + override fun getSummary(context: Context) = seriesTimerInfo?.getSeriesOverview(context) } diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/presentation/CardPresenter.java b/app/src/main/java/org/jellyfin/androidtv/ui/presentation/CardPresenter.java index 01c85e49cf..c6e15791fc 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/presentation/CardPresenter.java +++ b/app/src/main/java/org/jellyfin/androidtv/ui/presentation/CardPresenter.java @@ -33,16 +33,16 @@ import java.util.Locale; import java.util.Map; +import kotlin.Lazy; + public class CardPresenter extends Presenter { private int mStaticHeight = 150; private ImageType mImageType = ImageType.POSTER; private double aspect; - private boolean mShowInfo = true; - private boolean isUserView = false; - private boolean isUniformAspect = false; + private final Lazy imageHelper = KoinJavaComponent.inject(ImageHelper.class); public CardPresenter() { super(); @@ -104,8 +104,7 @@ public void setItem(BaseRowItem m, ImageType imageType, int lHeight, int pHeight } else if (imageType.equals(ImageType.THUMB)) { aspect = ImageHelper.ASPECT_RATIO_16_9; } else { - ImageHelper imageHelper = KoinJavaComponent.get(ImageHelper.class); - aspect = Utils.getSafeValue(imageHelper.getImageAspectRatio(itemDto, m.getPreferParentThumb()), ImageHelper.ASPECT_RATIO_7_9); + aspect = Utils.getSafeValue(imageHelper.getValue().getImageAspectRatio(itemDto, m.getPreferParentThumb()), ImageHelper.ASPECT_RATIO_7_9); } switch (itemDto.getType()) { case AUDIO: @@ -370,7 +369,7 @@ public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) { if (rowItem.getBaseItem() != null && rowItem.getBaseItemType() != BaseItemKind.USER_VIEW) { RatingType ratingType = KoinJavaComponent.get(UserPreferences.class).get(UserPreferences.Companion.getDefaultRatingType()); if (ratingType == RatingType.RATING_TOMATOES) { - Drawable badge = rowItem.getBadgeImage(holder.view.getContext()); + Drawable badge = rowItem.getBadgeImage(holder.view.getContext(), imageHelper.getValue()); holder.mCardView.setRating(null); if (badge != null) { holder.mCardView.setBadgeImage(badge); @@ -410,7 +409,7 @@ public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) { int fillHeight = Math.round(holder.getCardHeight() * holder.mCardView.getResources().getDisplayMetrics().density); holder.updateCardViewImage( - rowItem.getImageUrl(holder.mCardView.getContext(), mImageType, fillWidth, fillHeight), + rowItem.getImageUrl(holder.mCardView.getContext(), imageHelper.getValue(), mImageType, fillWidth, fillHeight), blurHash ); } From 1ea34ac891a0409eb017951a82e799a2e512c2d2 Mon Sep 17 00:00:00 2001 From: Niels van Velzen Date: Thu, 23 May 2024 21:20:43 +0200 Subject: [PATCH 10/15] Use properties when possible in BaseRowItem --- .../ui/itemhandling/BaseItemDtoBaseRowItem.kt | 41 ++++++++++--------- .../itemhandling/BaseItemPersonBaseRowItem.kt | 2 +- .../androidtv/ui/itemhandling/BaseRowItem.kt | 13 +++--- .../ChapterItemInfoBaseRowItem.kt | 2 +- .../SeriesTimerInfoDtoBaseRowItem.kt | 2 +- 5 files changed, 30 insertions(+), 30 deletions(-) diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemDtoBaseRowItem.kt b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemDtoBaseRowItem.kt index a740d4a79a..41b4c71dbb 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemDtoBaseRowItem.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemDtoBaseRowItem.kt @@ -55,31 +55,32 @@ open class BaseItemDtoBaseRowItem @JvmOverloads constructor( else -> false } - override fun getItemId() = baseItem?.id - - override fun getBaseItemType() = baseItem?.type - override fun isFavorite() = baseItem?.userData?.isFavorite == true - override fun isPlayed() = baseItem?.userData?.played == true + override val itemId get() = baseItem?.id + + override val baseItemType get() = baseItem?.type + override val isFavorite get() = baseItem?.userData?.isFavorite == true + override val isPlayed get() = baseItem?.userData?.played == true + + override val childCountStr: String? + get() { + // Playlist + if (baseItem?.type == BaseItemKind.PLAYLIST) { + val childCount = baseItem.cumulativeRunTimeTicks?.ticks?.let { duration -> + TimeUtils.formatMillis(duration.inWholeMilliseconds) + } + if (childCount != null) return childCount + } - override fun getChildCountStr(): String? { - // Playlist - if (baseItem?.type == BaseItemKind.PLAYLIST) { - val childCount = baseItem.cumulativeRunTimeTicks?.ticks?.let { duration -> - TimeUtils.formatMillis(duration.inWholeMilliseconds) + // Folder + if (baseItem?.isFolder == true && baseItem.type != BaseItemKind.MUSIC_ARTIST) { + val childCount = baseItem.childCount + if (childCount != null && childCount > 0) return childCount.toString() } - if (childCount != null) return childCount - } - // Folder - if (baseItem?.isFolder == true && baseItem.type != BaseItemKind.MUSIC_ARTIST) { - val childCount = baseItem.childCount - if (childCount != null && childCount > 0) return childCount.toString() + // Default + return null } - // Default - return null - } - override fun getCardName(context: Context) = when { baseItem?.type == BaseItemKind.AUDIO && baseItem.albumArtist != null -> baseItem.albumArtist baseItem?.type == BaseItemKind.AUDIO && baseItem.album != null -> baseItem.album diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemPersonBaseRowItem.kt b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemPersonBaseRowItem.kt index 6c42d3839e..a40810b378 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemPersonBaseRowItem.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemPersonBaseRowItem.kt @@ -19,7 +19,7 @@ class BaseItemPersonBaseRowItem( fillHeight: Int ) = imageHelper.getPrimaryImageUrl(person, fillHeight) - override fun getItemId() = person.id + override val itemId get() = person.id override fun getFullName(context: Context) = person.name override fun getName(context: Context) = person.name override fun getSubText(context: Context) = person.role diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseRowItem.kt b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseRowItem.kt index f2608a53d8..37303f848a 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseRowItem.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseRowItem.kt @@ -22,15 +22,15 @@ abstract class BaseRowItem protected constructor( val seriesTimerInfo: SeriesTimerInfoDto? = null, val gridButton: GridButton? = null, ) { + open val itemId: UUID? = null + open val baseItemType: BaseItemKind? = null open val showCardInfoOverlay: Boolean = false - open fun getBaseItemType(): BaseItemKind? = null - open fun isFavorite(): Boolean = false - open fun isPlayed(): Boolean = false + open val childCountStr: String? = null + open val isFavorite: Boolean = false + open val isPlayed: Boolean = false open fun getCardName(context: Context): String? = getFullName(context) - open fun getChildCountStr(): String? = null - open fun getImageUrl( context: Context, imageHelper: ImageHelper, @@ -41,13 +41,12 @@ abstract class BaseRowItem protected constructor( open fun getFullName(context: Context): String? = null open fun getName(context: Context): String? = null - open fun getItemId(): UUID? = null open fun getSubText(context: Context): String? = null open fun getSummary(context: Context): String? = null open fun getBadgeImage(context: Context, imageHelper: ImageHelper): Drawable? = null override fun equals(other: Any?): Boolean { - if (other is BaseRowItem) return other.getItemId() == getItemId() + if (other is BaseRowItem) return other.itemId == itemId return super.equals(other) } } diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ChapterItemInfoBaseRowItem.kt b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ChapterItemInfoBaseRowItem.kt index fa017a0098..078d1e78fd 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ChapterItemInfoBaseRowItem.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ChapterItemInfoBaseRowItem.kt @@ -22,7 +22,7 @@ class ChapterItemInfoBaseRowItem( fillHeight: Int ) = chapterInfo?.imagePath - override fun getItemId() = chapterInfo?.itemId + override val itemId get() = chapterInfo?.itemId override fun getFullName(context: Context) = chapterInfo?.name override fun getName(context: Context) = chapterInfo?.name diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/SeriesTimerInfoDtoBaseRowItem.kt b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/SeriesTimerInfoDtoBaseRowItem.kt index da5d33327a..bd2cdec97d 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/SeriesTimerInfoDtoBaseRowItem.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/SeriesTimerInfoDtoBaseRowItem.kt @@ -27,7 +27,7 @@ class SeriesTimerInfoDtoBaseRowItem( override fun getFullName(context: Context) = seriesTimerInfo?.name override fun getName(context: Context) = seriesTimerInfo?.name - override fun getItemId() = seriesTimerInfo?.id?.toUUIDOrNull() + override val itemId get() = seriesTimerInfo?.id?.toUUIDOrNull() override fun getSubText(context: Context): String = listOfNotNull( if (seriesTimerInfo?.recordAnyChannel == true) context.getString(R.string.all_channels) else seriesTimerInfo?.channelName, From 6d5b22c4164c7a390270858b2b2c7db720d71488 Mon Sep 17 00:00:00 2001 From: Niels van Velzen Date: Thu, 23 May 2024 21:38:38 +0200 Subject: [PATCH 11/15] Remove index property from BaseRowItem --- .../ui/browsing/BrowseFolderFragment.kt | 3 +- .../ui/browsing/BrowseGridFragment.java | 6 +-- .../ui/browsing/EnhancedBrowseFragment.java | 4 +- .../androidtv/ui/home/HomeRowsFragment.kt | 5 ++- .../ui/itemdetail/FullDetailsFragment.java | 2 +- .../ui/itemdetail/ItemListFragment.java | 2 +- .../MusicFavoritesListFragment.java | 2 +- .../ui/itemhandling/AudioQueueBaseRowItem.kt | 1 - .../ui/itemhandling/BaseItemDtoBaseRowItem.kt | 2 - .../androidtv/ui/itemhandling/BaseRowItem.kt | 1 - .../ui/itemhandling/ItemLauncher.java | 11 +++-- .../ui/itemhandling/ItemRowAdapter.java | 42 +++++++++---------- .../ui/itemhandling/ItemRowAdapterHelper.kt | 2 - .../ui/playback/AudioNowPlayingFragment.java | 6 ++- .../androidtv/ui/playback/MediaManager.kt | 4 +- .../playback/rewrite/RewriteMediaManager.kt | 17 +++++--- .../ui/search/SearchFragmentDelegate.kt | 2 +- .../jellyfin/androidtv/util/KeyProcessor.java | 12 +++--- 18 files changed, 62 insertions(+), 62 deletions(-) diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/browsing/BrowseFolderFragment.kt b/app/src/main/java/org/jellyfin/androidtv/ui/browsing/BrowseFolderFragment.kt index 0392ebdc23..3277decd3d 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/browsing/BrowseFolderFragment.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/browsing/BrowseFolderFragment.kt @@ -51,7 +51,6 @@ abstract class BrowseFolderFragment : BrowseSupportFragment(), RowLoader { itemLauncher.launch( item, (row as ListRow).adapter as ItemRowAdapter, - item.index, requireContext() ) } @@ -61,7 +60,7 @@ abstract class BrowseFolderFragment : BrowseSupportFragment(), RowLoader { backgroundService.clearBackgrounds() } else { val adapter = (row as? ListRow)?.adapter - if (adapter is ItemRowAdapter) adapter.loadMoreItemsIfNeeded(item.index.toLong()) + if (adapter is ItemRowAdapter) adapter.loadMoreItemsIfNeeded(adapter.indexOf(item)) backgroundService.setBackground(item.baseItem) } diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/browsing/BrowseGridFragment.java b/app/src/main/java/org/jellyfin/androidtv/ui/browsing/BrowseGridFragment.java index 42e47385f4..f1e7e94d1a 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/browsing/BrowseGridFragment.java +++ b/app/src/main/java/org/jellyfin/androidtv/ui/browsing/BrowseGridFragment.java @@ -946,7 +946,7 @@ private void refreshCurrentItem() { binding.toolBar.requestFocus(); mAdapter.remove(mCurrentItem); mAdapter.setTotalItems(mAdapter.getTotalItems() - 1); - updateCounter(mCurrentItem.getIndex()); + updateCounter(mAdapter.indexOf(mCurrentItem)); } return null; }); @@ -958,7 +958,7 @@ public void onItemClicked(final Presenter.ViewHolder itemViewHolder, Object item RowPresenter.ViewHolder rowViewHolder, Row row) { if (!(item instanceof BaseRowItem)) return; - itemLauncher.getValue().launch((BaseRowItem) item, mAdapter, ((BaseRowItem) item).getIndex(), requireContext()); + itemLauncher.getValue().launch((BaseRowItem) item, mAdapter, requireContext()); } } @@ -989,7 +989,7 @@ public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item, mHandler.postDelayed(mDelayedSetItem, VIEW_SELECT_UPDATE_DELAY); if (!determiningPosterSize) - mAdapter.loadMoreItemsIfNeeded(mCurrentItem.getIndex()); + mAdapter.loadMoreItemsIfNeeded(mAdapter.indexOf(mCurrentItem)); } } } diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/browsing/EnhancedBrowseFragment.java b/app/src/main/java/org/jellyfin/androidtv/ui/browsing/EnhancedBrowseFragment.java index f3bf9ee61f..384a01c463 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/browsing/EnhancedBrowseFragment.java +++ b/app/src/main/java/org/jellyfin/androidtv/ui/browsing/EnhancedBrowseFragment.java @@ -446,7 +446,7 @@ private final class ItemViewClickedListener implements OnItemViewClickedListener public void onItemClicked(final Presenter.ViewHolder itemViewHolder, Object item, RowPresenter.ViewHolder rowViewHolder, Row row) { if (!(item instanceof BaseRowItem)) return; - itemLauncher.getValue().launch((BaseRowItem) item, (ItemRowAdapter) ((ListRow) row).getAdapter(), ((BaseRowItem) item).getIndex(), requireContext()); + itemLauncher.getValue().launch((BaseRowItem) item, (ItemRowAdapter) ((ListRow) row).getAdapter(), requireContext()); } } @@ -481,7 +481,7 @@ public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item, InfoLayoutHelper.addInfoRow(requireContext(), rowItem.getBaseItem(), mInfoRow, true); ItemRowAdapter adapter = (ItemRowAdapter) ((ListRow) row).getAdapter(); - adapter.loadMoreItemsIfNeeded(rowItem.getIndex()); + adapter.loadMoreItemsIfNeeded(adapter.indexOf(rowItem)); backgroundService.getValue().setBackground(rowItem.getBaseItem()); } diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/home/HomeRowsFragment.kt b/app/src/main/java/org/jellyfin/androidtv/ui/home/HomeRowsFragment.kt index 034a7f85cc..8d0af66188 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/home/HomeRowsFragment.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/home/HomeRowsFragment.kt @@ -251,7 +251,7 @@ class HomeRowsFragment : RowsSupportFragment(), AudioEventListener, View.OnKeyLi row: Row?, ) { if (item !is BaseRowItem) return - itemLauncher.launch(item, (row as ListRow).adapter as ItemRowAdapter, item.index, requireContext()) + itemLauncher.launch(item, (row as ListRow).adapter as ItemRowAdapter, requireContext()) } } @@ -270,7 +270,8 @@ class HomeRowsFragment : RowsSupportFragment(), AudioEventListener, View.OnKeyLi currentItem = item currentRow = row as ListRow - (row.adapter as? ItemRowAdapter)?.loadMoreItemsIfNeeded(item.index.toLong()) + val itemRowAdapter = row.adapter as? ItemRowAdapter + itemRowAdapter?.loadMoreItemsIfNeeded(itemRowAdapter.indexOf(item)) backgroundService.setBackground(item.baseItem) } diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemdetail/FullDetailsFragment.java b/app/src/main/java/org/jellyfin/androidtv/ui/itemdetail/FullDetailsFragment.java index 5e45936eb3..ec733da99b 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemdetail/FullDetailsFragment.java +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemdetail/FullDetailsFragment.java @@ -1404,7 +1404,7 @@ public void onItemClicked(final Presenter.ViewHolder itemViewHolder, Object item RowPresenter.ViewHolder rowViewHolder, Row row) { if (!(item instanceof BaseRowItem)) return; - itemLauncher.getValue().launch((BaseRowItem) item, (ItemRowAdapter) ((ListRow) row).getAdapter(), ((BaseRowItem) item).getIndex(), requireContext()); + itemLauncher.getValue().launch((BaseRowItem) item, (ItemRowAdapter) ((ListRow) row).getAdapter(), requireContext()); } } diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemdetail/ItemListFragment.java b/app/src/main/java/org/jellyfin/androidtv/ui/itemdetail/ItemListFragment.java index 86714c76d7..d4b00b1196 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemdetail/ItemListFragment.java +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemdetail/ItemListFragment.java @@ -266,7 +266,7 @@ private void showMenu(final ItemRowView row, boolean showOpen) { open.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { - itemLauncher.getValue().launch(new BaseItemDtoBaseRowItem(row.getItem()), null, 0, requireContext()); + itemLauncher.getValue().launch(new BaseItemDtoBaseRowItem(row.getItem()), null, requireContext()); return true; } }); diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemdetail/MusicFavoritesListFragment.java b/app/src/main/java/org/jellyfin/androidtv/ui/itemdetail/MusicFavoritesListFragment.java index 4bf01dadcc..e15c571d11 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemdetail/MusicFavoritesListFragment.java +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemdetail/MusicFavoritesListFragment.java @@ -226,7 +226,7 @@ private void showMenu(final ItemRowView row, boolean showOpen) { open.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { - itemLauncher.getValue().launch(new BaseItemDtoBaseRowItem(row.getItem()), null, 0, requireContext()); + itemLauncher.getValue().launch(new BaseItemDtoBaseRowItem(row.getItem()), null, requireContext()); return true; } }); diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/AudioQueueBaseRowItem.kt b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/AudioQueueBaseRowItem.kt index 3b28f14221..fa3fa46417 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/AudioQueueBaseRowItem.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/AudioQueueBaseRowItem.kt @@ -6,7 +6,6 @@ class AudioQueueBaseRowItem( index: Int, item: BaseItemDto, ) : BaseItemDtoBaseRowItem( - index = index, item = item, staticHeight = true, ) { diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemDtoBaseRowItem.kt b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemDtoBaseRowItem.kt index 41b4c71dbb..51c046bf94 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemDtoBaseRowItem.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemDtoBaseRowItem.kt @@ -15,7 +15,6 @@ import org.jellyfin.sdk.model.extensions.ticks import java.text.SimpleDateFormat open class BaseItemDtoBaseRowItem @JvmOverloads constructor( - index: Int = 0, item: BaseItemDto, preferParentThumb: Boolean = false, staticHeight: Boolean = false, @@ -34,7 +33,6 @@ open class BaseItemDtoBaseRowItem @JvmOverloads constructor( BaseItemKind.RECORDING -> BaseRowType.LiveTvRecording else -> BaseRowType.BaseItem }, - index = index, staticHeight = staticHeight, preferParentThumb = preferParentThumb, selectAction = selectAction, diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseRowItem.kt b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseRowItem.kt index 37303f848a..2f9fb48601 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseRowItem.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseRowItem.kt @@ -13,7 +13,6 @@ import java.util.UUID abstract class BaseRowItem protected constructor( val baseRowType: BaseRowType, - val index: Int = 0, val staticHeight: Boolean = false, val preferParentThumb: Boolean = false, val selectAction: BaseRowItemSelectAction = BaseRowItemSelectAction.ShowDetails, diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemLauncher.java b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemLauncher.java index 7c11c03aa1..dd74e451c7 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemLauncher.java +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemLauncher.java @@ -66,12 +66,12 @@ public Destination.Fragment getUserViewDestination(@Nullable final BaseItemDto b } } - public void launch(final BaseRowItem rowItem, ItemRowAdapter adapter, int pos, final Context context) { + public void launch(final BaseRowItem rowItem, ItemRowAdapter adapter, final Context context) { switch (rowItem.getBaseRowType()) { case BaseItem: BaseItemDto baseItem = rowItem.getBaseItem(); try { - Timber.d("Item selected: %d - %s (%s)", rowItem.getIndex(), baseItem.getName(), baseItem.getType().toString()); + Timber.d("Item selected: %s (%s)", baseItem.getName(), baseItem.getType().toString()); } catch (Exception e) { //swallow it } @@ -93,16 +93,15 @@ public void launch(final BaseRowItem rowItem, ItemRowAdapter adapter, int pos, f return; case AUDIO: - Timber.d("got pos %s", pos); if (rowItem.getBaseItem() == null) return; // if the song currently playing is selected (and is the exact item - this only happens in the nowPlayingRow), open AudioNowPlayingActivity if (mediaManager.getValue().hasAudioQueueItems() && rowItem instanceof AudioQueueBaseRowItem && rowItem.getBaseItem().getId().equals(mediaManager.getValue().getCurrentAudioItem().getId())) { navigationRepository.getValue().navigate(Destinations.INSTANCE.getNowPlaying()); - } else if (mediaManager.getValue().hasAudioQueueItems() && rowItem instanceof AudioQueueBaseRowItem && pos < mediaManager.getValue().getCurrentAudioQueueSize()) { + } else if (mediaManager.getValue().hasAudioQueueItems() && rowItem instanceof AudioQueueBaseRowItem && adapter.indexOf(rowItem) < mediaManager.getValue().getCurrentAudioQueueSize()) { Timber.d("playing audio queue item"); - mediaManager.getValue().playFrom(pos); + mediaManager.getValue().playFrom(rowItem.getBaseItem()); } else if (adapter.getQueryType() == QueryType.Search) { mediaManager.getValue().playNow(context, Arrays.asList(rowItem.getBaseItem()), 0, false); } else { @@ -113,7 +112,7 @@ public void launch(final BaseRowItem rowItem, ItemRowAdapter adapter, int pos, f if (item instanceof BaseRowItem && ((BaseRowItem) item).getBaseItem() != null) audioItemsAsList.add(((BaseRowItem) item).getBaseItem()); } - mediaManager.getValue().playNow(context, audioItemsAsList, pos, false); + mediaManager.getValue().playNow(context, audioItemsAsList, adapter.indexOf(rowItem), false); } return; diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemRowAdapter.java b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemRowAdapter.java index 1652471134..c865079f24 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemRowAdapter.java +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemRowAdapter.java @@ -530,7 +530,7 @@ public void removeRow() { mParent.remove(mRow); } - public void loadMoreItemsIfNeeded(long pos) { + public void loadMoreItemsIfNeeded(int pos) { if (fullyLoaded) { //context.getLogger().Debug("Row is fully loaded"); return; @@ -819,7 +819,7 @@ public void onResponse(ItemsResult response) { ItemRowAdapterHelperKt.setItems(ItemRowAdapter.this, response.getItems(), (item, i) -> { if (userViewsRepository.getValue().isSupported(ModelCompat.asSdk(item).getCollectionType())) { item.setDisplayPreferencesId(item.getId()); - return new BaseItemDtoBaseRowItem(i, ModelCompat.asSdk(item), preferParentThumb, staticHeight); + return new BaseItemDtoBaseRowItem(ModelCompat.asSdk(item), preferParentThumb, staticHeight); } else { return null; } @@ -853,7 +853,7 @@ public void onResponse(ItemsResult response) { if (response.getItems() != null && response.getItems().length > 0) { setTotalItems(query.getEnableTotalRecordCount() ? response.getTotalRecordCount() : response.getItems().length); - ItemRowAdapterHelperKt.setItems(ItemRowAdapter.this, response.getItems(), (item, i) -> new BaseItemDtoBaseRowItem(i, ModelCompat.asSdk(item), getPreferParentThumb(), isStaticHeight())); + ItemRowAdapterHelperKt.setItems(ItemRowAdapter.this, response.getItems(), (item, i) -> new BaseItemDtoBaseRowItem(ModelCompat.asSdk(item), getPreferParentThumb(), isStaticHeight())); } else if (getItemsLoaded() == 0) { removeRow(); } @@ -885,7 +885,7 @@ public void onResponse(ItemsResult response) { if (response.getItems() != null && response.getItems().length > 0) { setTotalItems(response.getTotalRecordCount()); - ItemRowAdapterHelperKt.setItems(ItemRowAdapter.this, response.getItems(), (item, i) -> new BaseItemDtoBaseRowItem(i, ModelCompat.asSdk(item), getPreferParentThumb(), isStaticHeight())); + ItemRowAdapterHelperKt.setItems(ItemRowAdapter.this, response.getItems(), (item, i) -> new BaseItemDtoBaseRowItem(ModelCompat.asSdk(item), getPreferParentThumb(), isStaticHeight())); } else if (getItemsLoaded() == 0) { removeRow(); } @@ -909,7 +909,7 @@ public void onResponse(ItemsResult response) { if (response.getItems() != null && response.getItems().length > 0) { setTotalItems(response.getTotalRecordCount()); - ItemRowAdapterHelperKt.setItems(ItemRowAdapter.this, response.getItems(), (item, i) -> new BaseItemDtoBaseRowItem(i, ModelCompat.asSdk(item), getPreferParentThumb(), isStaticHeight())); + ItemRowAdapterHelperKt.setItems(ItemRowAdapter.this, response.getItems(), (item, i) -> new BaseItemDtoBaseRowItem(ModelCompat.asSdk(item), getPreferParentThumb(), isStaticHeight())); } else if (getItemsLoaded() == 0) { removeRow(); } @@ -933,7 +933,7 @@ public void onResponse(BaseItemDto[] response) { if (response != null && response.length > 0) { setTotalItems(response.length); - ItemRowAdapterHelperKt.setItems(ItemRowAdapter.this, response, (item, i) -> new BaseItemDtoBaseRowItem(i, ModelCompat.asSdk(item), getPreferParentThumb(), isStaticHeight(), BaseRowItemSelectAction.ShowDetails, getPreferParentThumb())); + ItemRowAdapterHelperKt.setItems(ItemRowAdapter.this, response, (item, i) -> new BaseItemDtoBaseRowItem(ModelCompat.asSdk(item), getPreferParentThumb(), isStaticHeight(), BaseRowItemSelectAction.ShowDetails, getPreferParentThumb())); } else if (getItemsLoaded() == 0) { removeRow(); } @@ -997,12 +997,12 @@ public void onResponse(ItemsResult response) { } if (existing == null) { Timber.d("Adding new episode 1 to premieres %s", item.getSeriesName()); - adapter.add(new BaseItemDtoBaseRowItem(i++, ModelCompat.asSdk(item), preferParentThumb, true)); + adapter.add(new BaseItemDtoBaseRowItem(ModelCompat.asSdk(item), preferParentThumb, true)); } else if (existing.getBaseItem().getParentIndexNumber() > item.getParentIndexNumber()) { //Replace the newer item with the earlier season Timber.d("Replacing newer episode 1 with an older season for %s", item.getSeriesName()); - adapter.set(existingPos, new BaseItemDtoBaseRowItem(i++, ModelCompat.asSdk(item), preferParentThumb, false)); + adapter.set(existingPos, new BaseItemDtoBaseRowItem(ModelCompat.asSdk(item), preferParentThumb, false)); } // otherwise, just ignore this newer season premiere since we have the older one already } else { @@ -1034,7 +1034,7 @@ private void retrieve(final NextUpQuery query) { public void onResponse(final ItemsResult response) { if (response.getItems() != null && response.getItems().length > 0) { setTotalItems(response.getTotalRecordCount()); - ItemRowAdapterHelperKt.setItems(ItemRowAdapter.this, response.getItems(), (item, i) -> new BaseItemDtoBaseRowItem(i, ModelCompat.asSdk(item), preferParentThumb, staticHeight)); + ItemRowAdapterHelperKt.setItems(ItemRowAdapter.this, response.getItems(), (item, i) -> new BaseItemDtoBaseRowItem(ModelCompat.asSdk(item), preferParentThumb, staticHeight)); //If this was for a single series, get the rest of the episodes in the season if (query.getSeriesId() != null) { @@ -1050,7 +1050,7 @@ public void onResponse(ItemsResult innerResponse) { if (response.getItems() != null) { int n = response.getItems().length; for (BaseItemDto item : innerResponse.getItems()) { - adapter.add(new BaseItemDtoBaseRowItem(n++, ModelCompat.asSdk(item), preferParentThumb, false)); + adapter.add(new BaseItemDtoBaseRowItem(ModelCompat.asSdk(item), preferParentThumb, false)); } totalItems += innerResponse.getTotalRecordCount(); setItemsLoaded(itemsLoaded + n); @@ -1096,7 +1096,7 @@ public void onResponse(ChannelInfoDtoResult response) { adapter.clear(); } for (ChannelInfoDto item : response.getItems()) { - adapter.add(new BaseItemDtoBaseRowItem(i, ModelCompat.asSdk(item))); + adapter.add(new BaseItemDtoBaseRowItem(ModelCompat.asSdk(item))); i++; } totalItems = response.getTotalRecordCount(); @@ -1132,7 +1132,7 @@ public void onResponse(ItemsResult response) { int i = 0; int prevItems = Math.max(adapter.size(), 0); for (BaseItemDto item : response.getItems()) { - adapter.add(new BaseItemDtoBaseRowItem(0, ModelCompat.asSdk(item), false, staticHeight)); + adapter.add(new BaseItemDtoBaseRowItem(ModelCompat.asSdk(item), false, staticHeight)); i++; } totalItems = response.getTotalRecordCount(); @@ -1223,7 +1223,7 @@ public void onResponse(ItemsResult response) { } for (BaseItemDto item : response.getItems()) { - adapter.add(new BaseItemDtoBaseRowItem(0, ModelCompat.asSdk(item), false, staticHeight)); + adapter.add(new BaseItemDtoBaseRowItem(ModelCompat.asSdk(item), false, staticHeight)); i++; } totalItems = response.getTotalRecordCount(); @@ -1264,7 +1264,7 @@ public void onResponse(BaseItemDto[] response) { adapter.clear(); } for (BaseItemDto item : response) { - adapter.add(new BaseItemDtoBaseRowItem(i++, ModelCompat.asSdk(item), preferParentThumb, false)); + adapter.add(new BaseItemDtoBaseRowItem(ModelCompat.asSdk(item), preferParentThumb, false)); } totalItems = response.length; setItemsLoaded(itemsLoaded + i); @@ -1300,7 +1300,7 @@ public void onResponse(ItemsResult response) { adapter.clear(); } for (BaseItemDto item : response.getItems()) { - adapter.add(new BaseItemDtoBaseRowItem(i++, ModelCompat.asSdk(item))); + adapter.add(new BaseItemDtoBaseRowItem(ModelCompat.asSdk(item))); } totalItems = response.getTotalRecordCount(); setItemsLoaded(itemsLoaded + i); @@ -1336,7 +1336,7 @@ public void onResponse(BaseItemDto[] response) { } for (BaseItemDto item : response) { item.setName(context.getString(R.string.lbl_trailer) + (i + 1)); - adapter.add(new BaseItemDtoBaseRowItem(i++, ModelCompat.asSdk(item), preferParentThumb, false, BaseRowItemSelectAction.Play)); + adapter.add(new BaseItemDtoBaseRowItem(ModelCompat.asSdk(item), preferParentThumb, false, BaseRowItemSelectAction.Play, false)); } totalItems = response.length; setItemsLoaded(itemsLoaded + i); @@ -1372,7 +1372,7 @@ public void onResponse(ItemsResult response) { adapter.clear(); } for (BaseItemDto item : response.getItems()) { - adapter.add(new BaseItemDtoBaseRowItem(i++, ModelCompat.asSdk(item))); + adapter.add(new BaseItemDtoBaseRowItem(ModelCompat.asSdk(item))); } totalItems = response.getTotalRecordCount(); setItemsLoaded(itemsLoaded + i); @@ -1408,7 +1408,7 @@ public void onResponse(ItemsResult response) { adapter.clear(); } for (BaseItemDto item : response.getItems()) { - adapter.add(new BaseItemDtoBaseRowItem(i++, ModelCompat.asSdk(item))); + adapter.add(new BaseItemDtoBaseRowItem(ModelCompat.asSdk(item))); } totalItems = response.getTotalRecordCount(); setItemsLoaded(itemsLoaded + i); @@ -1445,7 +1445,7 @@ public void onResponse(ItemsResult response) { } for (BaseItemDto item : response.getItems()) { if (query.getParentId() == null || item.getSeriesId() == null || item.getSeriesId().equals(query.getParentId())) { - adapter.add(new BaseItemDtoBaseRowItem(i++, ModelCompat.asSdk(item))); + adapter.add(new BaseItemDtoBaseRowItem(ModelCompat.asSdk(item))); } } totalItems = response.getTotalRecordCount(); @@ -1482,7 +1482,7 @@ public void onResponse(ItemsResult response) { adapter.clear(); } for (BaseItemDto item : response.getItems()) { - adapter.add(new BaseItemDtoBaseRowItem(i++, ModelCompat.asSdk(item))); + adapter.add(new BaseItemDtoBaseRowItem(ModelCompat.asSdk(item))); } totalItems = response.getTotalRecordCount(); setItemsLoaded(i); @@ -1516,7 +1516,7 @@ public void onResponse(ItemsResult response) { int i = 0; int prevItems = Math.max(adapter.size(), 0); for (BaseItemDto item : response.getItems()) { - adapter.add(new BaseItemDtoBaseRowItem(i++, ModelCompat.asSdk(item))); + adapter.add(new BaseItemDtoBaseRowItem(ModelCompat.asSdk(item))); } totalItems = response.getTotalRecordCount(); setItemsLoaded(itemsLoaded + i); diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemRowAdapterHelper.kt b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemRowAdapterHelper.kt index 1c661f9d43..9ac68fcd5d 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemRowAdapterHelper.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemRowAdapterHelper.kt @@ -50,7 +50,6 @@ fun ItemRowAdapter.retrieveResumeItems(api: ApiClient, query: GetResumeItemsRequ items = response.items.orEmpty().toTypedArray(), transform = { item, i -> BaseItemDtoBaseRowItem( - i, item, preferParentThumb, isStaticHeight @@ -81,7 +80,6 @@ fun ItemRowAdapter.refreshItem( set( index = indexOf(currentBaseRowItem), element = BaseItemDtoBaseRowItem( - index = currentBaseRowItem.index, item = refreshedBaseItem, preferParentThumb = currentBaseRowItem.preferParentThumb, staticHeight = currentBaseRowItem.staticHeight, diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/playback/AudioNowPlayingFragment.java b/app/src/main/java/org/jellyfin/androidtv/ui/playback/AudioNowPlayingFragment.java index ab838e4163..1491b7fb05 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/playback/AudioNowPlayingFragment.java +++ b/app/src/main/java/org/jellyfin/androidtv/ui/playback/AudioNowPlayingFragment.java @@ -34,6 +34,7 @@ import org.jellyfin.androidtv.databinding.FragmentAudioNowPlayingBinding; import org.jellyfin.androidtv.ui.AsyncImageView; import org.jellyfin.androidtv.ui.itemhandling.BaseRowItem; +import org.jellyfin.androidtv.ui.itemhandling.ItemRowAdapter; import org.jellyfin.androidtv.ui.navigation.Destinations; import org.jellyfin.androidtv.ui.navigation.NavigationRepository; import org.jellyfin.androidtv.ui.presentation.PositionableListRowPresenter; @@ -388,8 +389,9 @@ public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item, RowPresenter.ViewHolder rowViewHolder, Row row) { if (item instanceof BaseRowItem) { - //Keep counter - mCounter.setText(((BaseRowItem) item).getIndex() + 1 + " | " + mQueueRow.getAdapter().size()); + // Keep counter + ItemRowAdapter adapter = (ItemRowAdapter) mQueueRow.getAdapter(); + mCounter.setText((adapter.indexOf(item) + 1) + " | " + adapter.size()); } } } diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/playback/MediaManager.kt b/app/src/main/java/org/jellyfin/androidtv/ui/playback/MediaManager.kt index 231ba4b450..e23809af72 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/playback/MediaManager.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/playback/MediaManager.kt @@ -23,10 +23,10 @@ interface MediaManager { fun queueAudioItem(item: BaseItemDto) fun clearAudioQueue() fun addToAudioQueue(items: List) - fun removeFromAudioQueue(ndx: Int) + fun removeFromAudioQueue(item: BaseItemDto) val isPlayingAudio: Boolean fun playNow(context: Context, items: List, position: Int, shuffle: Boolean) - fun playFrom(ndx: Int): Boolean + fun playFrom(item: BaseItemDto): Boolean fun shuffleAudioQueue() fun hasNextAudioItem(): Boolean fun hasPrevAudioItem(): Boolean diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/playback/rewrite/RewriteMediaManager.kt b/app/src/main/java/org/jellyfin/androidtv/ui/playback/rewrite/RewriteMediaManager.kt index f91c2d2a61..0e9877da93 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/playback/rewrite/RewriteMediaManager.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/playback/rewrite/RewriteMediaManager.kt @@ -203,11 +203,14 @@ class RewriteMediaManager( updateAdapter() } - override fun removeFromAudioQueue(ndx: Int) { + override fun removeFromAudioQueue(item: BaseItemDto) { + val index = queue.items.indexOf(item) + if (index == -1) return + // Disallow removing currently playing item (legacy UI cannot keep up) - if (playbackManager.state.queue.entryIndex.value == ndx) return + if (playbackManager.state.queue.entryIndex.value == index) return - queue.items.removeAt(ndx) + queue.items.removeAt(index) updateAdapter() } @@ -224,8 +227,12 @@ class RewriteMediaManager( navigationRepository.navigate(Destinations.nowPlaying) } - override fun playFrom(ndx: Int): Boolean = runBlocking { - playbackManager.state.queue.setIndex(ndx) != null + override fun playFrom(item: BaseItemDto): Boolean { + val index = queue.items.indexOf(item) + if (index == -1) return false + return runBlocking { + playbackManager.state.queue.setIndex(index) != null + } } override fun shuffleAudioQueue() { diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/search/SearchFragmentDelegate.kt b/app/src/main/java/org/jellyfin/androidtv/ui/search/SearchFragmentDelegate.kt index 0cf6103aa4..78deee5dd2 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/search/SearchFragmentDelegate.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/search/SearchFragmentDelegate.kt @@ -44,7 +44,7 @@ class SearchFragmentDelegate( if (item !is BaseRowItem) return@OnItemViewClickedListener row as ListRow val adapter = row.adapter as ItemRowAdapter - itemLauncher.launch(item as BaseRowItem?, adapter, item.index, context) + itemLauncher.launch(item as BaseRowItem?, adapter, context) } val onItemViewSelectedListener = OnItemViewSelectedListener { _, item, _, _ -> diff --git a/app/src/main/java/org/jellyfin/androidtv/util/KeyProcessor.java b/app/src/main/java/org/jellyfin/androidtv/util/KeyProcessor.java index 1ed9cf75d4..cd2b66be12 100644 --- a/app/src/main/java/org/jellyfin/androidtv/util/KeyProcessor.java +++ b/app/src/main/java/org/jellyfin/androidtv/util/KeyProcessor.java @@ -260,7 +260,7 @@ public PopupMenu createItemMenu(BaseRowItem rowItem, UserItemDataDto userData, F } } - menu.setOnMenuItemClickListener(new KeyProcessorItemMenuClickListener(activity, rowItem.getBaseItem(), rowItem.getIndex())); + menu.setOnMenuItemClickListener(new KeyProcessorItemMenuClickListener(activity, rowItem.getBaseItem())); menu.show(); return menu; } @@ -277,19 +277,17 @@ private void createPlayMenu(BaseItemDto item, boolean isMusic, FragmentActivity menu.getMenu().add(0, MENU_ADD_QUEUE, order, R.string.lbl_add_to_queue); } - menu.setOnMenuItemClickListener(new KeyProcessorItemMenuClickListener(activity, item, -1)); + menu.setOnMenuItemClickListener(new KeyProcessorItemMenuClickListener(activity, item)); menu.show(); } private class KeyProcessorItemMenuClickListener implements PopupMenu.OnMenuItemClickListener { private BaseItemDto item; private FragmentActivity activity; - private int rowIndex; - private KeyProcessorItemMenuClickListener(FragmentActivity activity, BaseItemDto item, int rowIndex) { + private KeyProcessorItemMenuClickListener(FragmentActivity activity, BaseItemDto item) { this.item = item; this.activity = activity; - this.rowIndex = rowIndex; } @Override @@ -361,10 +359,10 @@ public void onError(Exception exception) { mediaManager.getValue().shuffleAudioQueue(); return true; case MENU_REMOVE_FROM_QUEUE: - mediaManager.getValue().removeFromAudioQueue(rowIndex); + mediaManager.getValue().removeFromAudioQueue(item); return true; case MENU_ADVANCE_QUEUE: - mediaManager.getValue().playFrom(rowIndex); + mediaManager.getValue().playFrom(item); return true; case MENU_CLEAR_QUEUE: mediaManager.getValue().clearAudioQueue(); From 74446e1ea666bcd32de82e25c731f1974230d19a Mon Sep 17 00:00:00 2001 From: Niels van Velzen Date: Thu, 23 May 2024 22:18:11 +0200 Subject: [PATCH 12/15] Move even more BaseRowItem properties --- .../androidtv/ui/card/LegacyImageCardView.java | 9 +++++++-- .../ui/itemhandling/BaseItemDtoBaseRowItem.kt | 3 +-- .../androidtv/ui/itemhandling/BaseRowItem.kt | 9 --------- .../itemhandling/ChapterItemInfoBaseRowItem.kt | 13 ++++++------- .../ui/itemhandling/GridButtonBaseRowItem.kt | 11 ++++------- .../androidtv/ui/itemhandling/ItemLauncher.java | 8 ++++---- .../SeriesTimerInfoDtoBaseRowItem.kt | 17 ++++++++--------- .../playback/CustomPlaybackOverlayFragment.java | 16 ++++++---------- .../ui/presentation/CardPresenter.java | 6 +++--- .../jellyfin/androidtv/util/KeyProcessor.java | 2 +- 10 files changed, 40 insertions(+), 54 deletions(-) diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/card/LegacyImageCardView.java b/app/src/main/java/org/jellyfin/androidtv/ui/card/LegacyImageCardView.java index 288f6c4d78..f01572ed31 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/card/LegacyImageCardView.java +++ b/app/src/main/java/org/jellyfin/androidtv/ui/card/LegacyImageCardView.java @@ -17,6 +17,7 @@ import org.jellyfin.androidtv.R; import org.jellyfin.androidtv.databinding.ViewCardLegacyImageBinding; import org.jellyfin.androidtv.ui.AsyncImageView; +import org.jellyfin.androidtv.ui.itemhandling.BaseItemDtoBaseRowItem; import org.jellyfin.androidtv.ui.itemhandling.BaseRowItem; import org.jellyfin.androidtv.util.ContextExtensionsKt; import org.jellyfin.androidtv.util.TimeUtils; @@ -115,7 +116,7 @@ public void setOverlayInfo(BaseRowItem item) { if (binding.overlayText == null) return; if (getCardType() == BaseCardView.CARD_TYPE_MAIN_ONLY && item.getShowCardInfoOverlay()) { - switch (item.getBaseItemType()) { + switch (item.getBaseItem().getType()) { case PHOTO: insertCardData(item.getBaseItem().getPremiereDate() != null ? android.text.format.DateFormat.getDateFormat(getContext()).format(TimeUtils.getDate(item.getBaseItem().getPremiereDate())) : item.getFullName(getContext()), R.drawable.ic_camera, true); break; @@ -135,7 +136,11 @@ public void setOverlayInfo(BaseRowItem item) { binding.overlayText.setText(item.getFullName(getContext())); break; } - binding.overlayCount.setText(item.getChildCountStr()); + if (item instanceof BaseItemDtoBaseRowItem) { + binding.overlayCount.setText(((BaseItemDtoBaseRowItem) item).getChildCountStr()); + } else { + binding.overlayCount.setText(null); + } binding.nameOverlay.setVisibility(VISIBLE); } } diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemDtoBaseRowItem.kt b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemDtoBaseRowItem.kt index 51c046bf94..6248f286fd 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemDtoBaseRowItem.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseItemDtoBaseRowItem.kt @@ -55,11 +55,10 @@ open class BaseItemDtoBaseRowItem @JvmOverloads constructor( override val itemId get() = baseItem?.id - override val baseItemType get() = baseItem?.type override val isFavorite get() = baseItem?.userData?.isFavorite == true override val isPlayed get() = baseItem?.userData?.played == true - override val childCountStr: String? + val childCountStr: String? get() { // Playlist if (baseItem?.type == BaseItemKind.PLAYLIST) { diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseRowItem.kt b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseRowItem.kt index 2f9fb48601..9e8c035490 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseRowItem.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/BaseRowItem.kt @@ -3,12 +3,8 @@ package org.jellyfin.androidtv.ui.itemhandling import android.content.Context import android.graphics.drawable.Drawable import org.jellyfin.androidtv.constant.ImageType -import org.jellyfin.androidtv.data.model.ChapterItemInfo -import org.jellyfin.androidtv.ui.GridButton import org.jellyfin.androidtv.util.ImageHelper import org.jellyfin.sdk.model.api.BaseItemDto -import org.jellyfin.sdk.model.api.BaseItemKind -import org.jellyfin.sdk.model.api.SeriesTimerInfoDto import java.util.UUID abstract class BaseRowItem protected constructor( @@ -17,14 +13,9 @@ abstract class BaseRowItem protected constructor( val preferParentThumb: Boolean = false, val selectAction: BaseRowItemSelectAction = BaseRowItemSelectAction.ShowDetails, val baseItem: BaseItemDto? = null, - val chapterInfo: ChapterItemInfo? = null, - val seriesTimerInfo: SeriesTimerInfoDto? = null, - val gridButton: GridButton? = null, ) { open val itemId: UUID? = null - open val baseItemType: BaseItemKind? = null open val showCardInfoOverlay: Boolean = false - open val childCountStr: String? = null open val isFavorite: Boolean = false open val isPlayed: Boolean = false diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ChapterItemInfoBaseRowItem.kt b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ChapterItemInfoBaseRowItem.kt index 078d1e78fd..dc0d36796d 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ChapterItemInfoBaseRowItem.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ChapterItemInfoBaseRowItem.kt @@ -8,11 +8,10 @@ import org.jellyfin.androidtv.util.TimeUtils import org.jellyfin.sdk.model.extensions.ticks class ChapterItemInfoBaseRowItem( - item: ChapterItemInfo, + val chapterInfo: ChapterItemInfo, ) : BaseRowItem( baseRowType = BaseRowType.Chapter, staticHeight = true, - chapterInfo = item, ) { override fun getImageUrl( context: Context, @@ -20,12 +19,12 @@ class ChapterItemInfoBaseRowItem( imageType: ImageType, fillWidth: Int, fillHeight: Int - ) = chapterInfo?.imagePath + ) = chapterInfo.imagePath - override val itemId get() = chapterInfo?.itemId - override fun getFullName(context: Context) = chapterInfo?.name - override fun getName(context: Context) = chapterInfo?.name + override val itemId get() = chapterInfo.itemId + override fun getFullName(context: Context) = chapterInfo.name + override fun getName(context: Context) = chapterInfo.name override fun getSubText(context: Context) = - chapterInfo?.startPositionTicks?.ticks?.inWholeMilliseconds?.let(TimeUtils::formatMillis) + chapterInfo.startPositionTicks.ticks.inWholeMilliseconds.let(TimeUtils::formatMillis) } diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/GridButtonBaseRowItem.kt b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/GridButtonBaseRowItem.kt index f58a924aa1..0f8b2244e5 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/GridButtonBaseRowItem.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/GridButtonBaseRowItem.kt @@ -6,11 +6,10 @@ import org.jellyfin.androidtv.ui.GridButton import org.jellyfin.androidtv.util.ImageHelper class GridButtonBaseRowItem( - item: GridButton, + val gridButton: GridButton, ) : BaseRowItem( baseRowType = BaseRowType.GridButton, staticHeight = true, - gridButton = item, ) { override fun getImageUrl( context: Context, @@ -18,10 +17,8 @@ class GridButtonBaseRowItem( imageType: ImageType, fillWidth: Int, fillHeight: Int - ) = gridButton?.imageRes?.let { - imageHelper.getResourceUrl(context, it) - } + ) = gridButton.imageRes?.let { imageHelper.getResourceUrl(context, it) } - override fun getFullName(context: Context) = gridButton?.text - override fun getName(context: Context) = gridButton?.text + override fun getFullName(context: Context) = gridButton.text + override fun getName(context: Context) = gridButton.text } diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemLauncher.java b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemLauncher.java index dd74e451c7..13f5a9c71f 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemLauncher.java +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemLauncher.java @@ -171,7 +171,7 @@ public void onResponse(List response) { break; case Chapter: - final ChapterItemInfo chapter = rowItem.getChapterInfo(); + final ChapterItemInfo chapter = ((ChapterItemInfoBaseRowItem) rowItem).getChapterInfo(); //Start playback of the item at the chapter point ItemLauncherHelper.getItem(rowItem.getItemId(), new Response() { @Override @@ -242,7 +242,7 @@ public void onResponse(BaseItemDto response) { List items = new ArrayList<>(); items.add(response); videoQueueManager.getValue().setCurrentVideoQueue(items); - Destination destination = playbackLauncher.getValue().getPlaybackDestination(rowItem.getBaseItemType(), 0); + Destination destination = playbackLauncher.getValue().getPlaybackDestination(rowItem.getBaseItem().getType(), 0); navigationRepository.getValue().navigate(destination); } }); @@ -251,12 +251,12 @@ public void onResponse(BaseItemDto response) { break; case SeriesTimer: - navigationRepository.getValue().navigate(Destinations.INSTANCE.seriesTimerDetails(rowItem.getItemId(), rowItem.getSeriesTimerInfo())); + navigationRepository.getValue().navigate(Destinations.INSTANCE.seriesTimerDetails(rowItem.getItemId(), ((SeriesTimerInfoDtoBaseRowItem) rowItem).getSeriesTimerInfo())); break; case GridButton: - switch (rowItem.getGridButton().getId()) { + switch (((GridButtonBaseRowItem) rowItem).getGridButton().getId()) { case LiveTvOption.LIVE_TV_GUIDE_OPTION_ID: navigationRepository.getValue().navigate(Destinations.INSTANCE.getLiveTvGuide()); break; diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/SeriesTimerInfoDtoBaseRowItem.kt b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/SeriesTimerInfoDtoBaseRowItem.kt index bd2cdec97d..b6fa803c69 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/SeriesTimerInfoDtoBaseRowItem.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/SeriesTimerInfoDtoBaseRowItem.kt @@ -9,10 +9,9 @@ import org.jellyfin.sdk.model.api.SeriesTimerInfoDto import org.jellyfin.sdk.model.serializer.toUUIDOrNull class SeriesTimerInfoDtoBaseRowItem( - item: SeriesTimerInfoDto, + val seriesTimerInfo: SeriesTimerInfoDto, ) : BaseRowItem( baseRowType = BaseRowType.SeriesTimer, - seriesTimerInfo = item, ) { override fun getImageUrl( context: Context, @@ -25,14 +24,14 @@ class SeriesTimerInfoDtoBaseRowItem( R.drawable.tile_land_series_timer ) - override fun getFullName(context: Context) = seriesTimerInfo?.name - override fun getName(context: Context) = seriesTimerInfo?.name - override val itemId get() = seriesTimerInfo?.id?.toUUIDOrNull() + override fun getFullName(context: Context) = seriesTimerInfo.name + override fun getName(context: Context) = seriesTimerInfo.name + override val itemId get() = seriesTimerInfo.id?.toUUIDOrNull() override fun getSubText(context: Context): String = listOfNotNull( - if (seriesTimerInfo?.recordAnyChannel == true) context.getString(R.string.all_channels) - else seriesTimerInfo?.channelName, - seriesTimerInfo?.dayPattern + if (seriesTimerInfo.recordAnyChannel) context.getString(R.string.all_channels) + else seriesTimerInfo.channelName, + seriesTimerInfo.dayPattern ).joinToString(" ") - override fun getSummary(context: Context) = seriesTimerInfo?.getSeriesOverview(context) + override fun getSummary(context: Context) = seriesTimerInfo.getSeriesOverview(context) } diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/playback/CustomPlaybackOverlayFragment.java b/app/src/main/java/org/jellyfin/androidtv/ui/playback/CustomPlaybackOverlayFragment.java index db0837aaa3..2c5853cb01 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/playback/CustomPlaybackOverlayFragment.java +++ b/app/src/main/java/org/jellyfin/androidtv/ui/playback/CustomPlaybackOverlayFragment.java @@ -56,7 +56,7 @@ import org.jellyfin.androidtv.ui.ObservableScrollView; import org.jellyfin.androidtv.ui.ProgramGridCell; import org.jellyfin.androidtv.ui.ScrollViewListener; -import org.jellyfin.androidtv.ui.itemhandling.BaseRowItem; +import org.jellyfin.androidtv.ui.itemhandling.ChapterItemInfoBaseRowItem; import org.jellyfin.androidtv.ui.itemhandling.ItemRowAdapter; import org.jellyfin.androidtv.ui.livetv.LiveTvGuide; import org.jellyfin.androidtv.ui.livetv.LiveTvGuideFragment; @@ -418,15 +418,11 @@ public void onAudioFocusChange(int focusChange) { @Override public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item, RowPresenter.ViewHolder rowViewHolder, Row row) { - if (item instanceof BaseRowItem) { - BaseRowItem rowItem = (BaseRowItem) item; - switch (rowItem.getBaseRowType()) { - case Chapter: - Long start = rowItem.getChapterInfo().getStartPositionTicks() / 10000; - playbackControllerContainer.getValue().getPlaybackController().seek(start); - hidePopupPanel(); - break; - } + if (item instanceof ChapterItemInfoBaseRowItem) { + ChapterItemInfoBaseRowItem rowItem = (ChapterItemInfoBaseRowItem) item; + Long start = rowItem.getChapterInfo().getStartPositionTicks() / 10000; + playbackControllerContainer.getValue().getPlaybackController().seek(start); + hidePopupPanel(); } else if (item instanceof ChannelInfoDto) { hidePopupPanel(); switchChannel(UUIDSerializerKt.toUUID(((ChannelInfoDto) item).getId())); diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/presentation/CardPresenter.java b/app/src/main/java/org/jellyfin/androidtv/ui/presentation/CardPresenter.java index c6e15791fc..b4b34e0fc3 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/presentation/CardPresenter.java +++ b/app/src/main/java/org/jellyfin/androidtv/ui/presentation/CardPresenter.java @@ -366,7 +366,7 @@ public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) { } else { holder.mCardView.setPlayingIndicator(false); - if (rowItem.getBaseItem() != null && rowItem.getBaseItemType() != BaseItemKind.USER_VIEW) { + if (rowItem.getBaseItem() != null && rowItem.getBaseItem().getType() != BaseItemKind.USER_VIEW) { RatingType ratingType = KoinJavaComponent.get(UserPreferences.class).get(UserPreferences.Companion.getDefaultRatingType()); if (ratingType == RatingType.RATING_TOMATOES) { Drawable badge = rowItem.getBadgeImage(holder.view.getContext(), imageHelper.getValue()); @@ -389,10 +389,10 @@ public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) { if (aspect == ImageHelper.ASPECT_RATIO_BANNER) { blurHashMap = rowItem.getBaseItem().getImageBlurHashes().get(org.jellyfin.sdk.model.api.ImageType.BANNER); imageTag = rowItem.getBaseItem().getImageTags().get(org.jellyfin.sdk.model.api.ImageType.BANNER); - } else if (aspect == ImageHelper.ASPECT_RATIO_2_3 && rowItem.getBaseItemType() == BaseItemKind.EPISODE && rowItem instanceof BaseItemDtoBaseRowItem && ((BaseItemDtoBaseRowItem) rowItem).getPreferSeriesPoster()) { + } else if (aspect == ImageHelper.ASPECT_RATIO_2_3 && rowItem.getBaseItem().getType() == BaseItemKind.EPISODE && rowItem instanceof BaseItemDtoBaseRowItem && ((BaseItemDtoBaseRowItem) rowItem).getPreferSeriesPoster()) { blurHashMap = rowItem.getBaseItem().getImageBlurHashes().get(org.jellyfin.sdk.model.api.ImageType.PRIMARY); imageTag = rowItem.getBaseItem().getSeriesPrimaryImageTag(); - } else if (aspect == ImageHelper.ASPECT_RATIO_16_9 && !isUserView && (rowItem.getBaseItemType() != BaseItemKind.EPISODE || !rowItem.getBaseItem().getImageTags().containsKey(org.jellyfin.sdk.model.api.ImageType.PRIMARY) || (rowItem.getPreferParentThumb() && rowItem.getBaseItem().getParentThumbImageTag() != null))) { + } else if (aspect == ImageHelper.ASPECT_RATIO_16_9 && !isUserView && (rowItem.getBaseItem().getType() != BaseItemKind.EPISODE || !rowItem.getBaseItem().getImageTags().containsKey(org.jellyfin.sdk.model.api.ImageType.PRIMARY) || (rowItem.getPreferParentThumb() && rowItem.getBaseItem().getParentThumbImageTag() != null))) { blurHashMap = rowItem.getBaseItem().getImageBlurHashes().get(org.jellyfin.sdk.model.api.ImageType.THUMB); imageTag = (rowItem.getPreferParentThumb() || !rowItem.getBaseItem().getImageTags().containsKey(org.jellyfin.sdk.model.api.ImageType.PRIMARY)) ? rowItem.getBaseItem().getParentThumbImageTag() : rowItem.getBaseItem().getImageTags().get(org.jellyfin.sdk.model.api.ImageType.THUMB); } else { diff --git a/app/src/main/java/org/jellyfin/androidtv/util/KeyProcessor.java b/app/src/main/java/org/jellyfin/androidtv/util/KeyProcessor.java index cd2b66be12..a8fd07bb7d 100644 --- a/app/src/main/java/org/jellyfin/androidtv/util/KeyProcessor.java +++ b/app/src/main/java/org/jellyfin/androidtv/util/KeyProcessor.java @@ -68,7 +68,7 @@ public boolean handleKey(int key, BaseRowItem rowItem, FragmentActivity activity switch (key) { case KeyEvent.KEYCODE_MEDIA_PLAY: case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: - if (mediaManager.getValue().isPlayingAudio() && (rowItem.getBaseRowType() != BaseRowType.BaseItem || rowItem.getBaseItemType() != BaseItemKind.PHOTO)) { + if (mediaManager.getValue().isPlayingAudio() && (rowItem.getBaseRowType() != BaseRowType.BaseItem || rowItem.getBaseItem().getType() != BaseItemKind.PHOTO)) { // Rewrite uses media sessions which the system automatically manipulates on key presses return false; } From a8a8d398f595352bbf45410fdabdfa97a073dd79 Mon Sep 17 00:00:00 2001 From: Niels van Velzen Date: Sat, 25 May 2024 13:38:58 +0200 Subject: [PATCH 13/15] Remove index from AudioQueueBaseRowItem --- .../jellyfin/androidtv/ui/itemhandling/AudioQueueBaseRowItem.kt | 1 - .../org/jellyfin/androidtv/ui/itemhandling/ItemRowAdapter.java | 2 +- .../androidtv/ui/playback/rewrite/RewriteMediaManager.kt | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/AudioQueueBaseRowItem.kt b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/AudioQueueBaseRowItem.kt index fa3fa46417..9acb858aa4 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/AudioQueueBaseRowItem.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/AudioQueueBaseRowItem.kt @@ -3,7 +3,6 @@ package org.jellyfin.androidtv.ui.itemhandling import org.jellyfin.sdk.model.api.BaseItemDto class AudioQueueBaseRowItem( - index: Int, item: BaseItemDto, ) : BaseItemDtoBaseRowItem( item = item, diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemRowAdapter.java b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemRowAdapter.java index c865079f24..f2fde9bc15 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemRowAdapter.java +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemRowAdapter.java @@ -797,7 +797,7 @@ private void loadStaticAudioItems() { if (mItems != null) { int i = 0; for (org.jellyfin.sdk.model.api.BaseItemDto item : mItems) { - add(new AudioQueueBaseRowItem(i++, item)); + add(new AudioQueueBaseRowItem(item)); } itemsLoaded = i; diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/playback/rewrite/RewriteMediaManager.kt b/app/src/main/java/org/jellyfin/androidtv/ui/playback/rewrite/RewriteMediaManager.kt index 0e9877da93..8ae4cf7f8f 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/playback/rewrite/RewriteMediaManager.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/playback/rewrite/RewriteMediaManager.kt @@ -141,7 +141,7 @@ class RewriteMediaManager( .items // Map to audio queue items .mapIndexed { index, item -> - AudioQueueBaseRowItem(index, item).apply { + AudioQueueBaseRowItem(item).apply { playing = playbackManager.state.queue.entryIndex.value == index } } From a5e538b50b83ff564bfb861506162700acdcaf4f Mon Sep 17 00:00:00 2001 From: Niels van Velzen Date: Sat, 25 May 2024 13:44:38 +0200 Subject: [PATCH 14/15] Fix ItemRowAdapter counts after index removal --- .../ui/itemhandling/ItemRowAdapter.java | 38 +++++++------------ 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemRowAdapter.java b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemRowAdapter.java index f2fde9bc15..309579bddd 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemRowAdapter.java +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemRowAdapter.java @@ -795,11 +795,10 @@ private void loadStaticItems() { private void loadStaticAudioItems() { if (mItems != null) { - int i = 0; for (org.jellyfin.sdk.model.api.BaseItemDto item : mItems) { add(new AudioQueueBaseRowItem(item)); } - itemsLoaded = i; + itemsLoaded = mItems.size(); } else { removeRow(); @@ -967,7 +966,6 @@ public void onResponse(ItemsResult response) { adapter.clear(); } if (response.getItems() != null && response.getItems().length > 0) { - int i = 0; Calendar compare = Calendar.getInstance(); compare.add(Calendar.MONTH, -2); BaseItemDto[] nextUpItems = nextUpResponse.getItems(); @@ -1010,7 +1008,7 @@ public void onResponse(ItemsResult response) { } } } - setItemsLoaded(itemsLoaded + i); + setItemsLoaded(itemsLoaded + response.getItems().length); } @@ -1259,7 +1257,6 @@ private void retrieve(final SpecialsQuery query) { @Override public void onResponse(BaseItemDto[] response) { if (response.length > 0) { - int i = 0; if (adapter.size() > 0) { adapter.clear(); } @@ -1267,10 +1264,7 @@ public void onResponse(BaseItemDto[] response) { adapter.add(new BaseItemDtoBaseRowItem(ModelCompat.asSdk(item), preferParentThumb, false)); } totalItems = response.length; - setItemsLoaded(itemsLoaded + i); - if (i == 0) { - removeRow(); - } + setItemsLoaded(itemsLoaded + response.length); } else { // no results - don't show us removeRow(); @@ -1295,7 +1289,6 @@ private void retrieve(final AdditionalPartsQuery query) { @Override public void onResponse(ItemsResult response) { if (response.getItems() != null && response.getItems().length > 0) { - int i = 0; if (adapter.size() > 0) { adapter.clear(); } @@ -1303,8 +1296,8 @@ public void onResponse(ItemsResult response) { adapter.add(new BaseItemDtoBaseRowItem(ModelCompat.asSdk(item))); } totalItems = response.getTotalRecordCount(); - setItemsLoaded(itemsLoaded + i); - if (i == 0) { + setItemsLoaded(itemsLoaded + response.getItems().length); + if (response.getItems().length == 0) { removeRow(); } } else { @@ -1335,7 +1328,8 @@ public void onResponse(BaseItemDto[] response) { adapter.clear(); } for (BaseItemDto item : response) { - item.setName(context.getString(R.string.lbl_trailer) + (i + 1)); + i++; + item.setName(context.getString(R.string.lbl_trailer) + i); adapter.add(new BaseItemDtoBaseRowItem(ModelCompat.asSdk(item), preferParentThumb, false, BaseRowItemSelectAction.Play, false)); } totalItems = response.length; @@ -1367,7 +1361,6 @@ private void retrieveSimilarSeries(final SimilarItemsQuery query) { @Override public void onResponse(ItemsResult response) { if (response.getItems() != null && response.getItems().length > 0) { - int i = 0; if (adapter.size() > 0) { adapter.clear(); } @@ -1375,8 +1368,8 @@ public void onResponse(ItemsResult response) { adapter.add(new BaseItemDtoBaseRowItem(ModelCompat.asSdk(item))); } totalItems = response.getTotalRecordCount(); - setItemsLoaded(itemsLoaded + i); - if (i == 0) { + setItemsLoaded(itemsLoaded + response.getItems().length); + if (response.getItems().length == 0) { removeRow(); } } else { @@ -1403,7 +1396,6 @@ private void retrieveSimilarMovies(final SimilarItemsQuery query) { @Override public void onResponse(ItemsResult response) { if (response.getItems() != null && response.getItems().length > 0) { - int i = 0; if (adapter.size() > 0) { adapter.clear(); } @@ -1411,8 +1403,8 @@ public void onResponse(ItemsResult response) { adapter.add(new BaseItemDtoBaseRowItem(ModelCompat.asSdk(item))); } totalItems = response.getTotalRecordCount(); - setItemsLoaded(itemsLoaded + i); - if (i == 0) { + setItemsLoaded(itemsLoaded + response.getItems().length); + if (response.getItems().length == 0) { removeRow(); } } else { @@ -1439,7 +1431,6 @@ private void retrieve(final UpcomingEpisodesQuery query) { @Override public void onResponse(ItemsResult response) { if (response.getItems() != null && response.getItems().length > 0) { - int i = 0; if (adapter.size() > 0) { adapter.clear(); } @@ -1449,8 +1440,8 @@ public void onResponse(ItemsResult response) { } } totalItems = response.getTotalRecordCount(); - setItemsLoaded(itemsLoaded + i); - if (i == 0) { + setItemsLoaded(itemsLoaded + response.getItems().length); + if (response.getItems().length == 0) { removeRow(); } } else { @@ -1513,13 +1504,12 @@ private void retrieve(SeasonQuery query) { @Override public void onResponse(ItemsResult response) { if (response.getItems() != null && response.getItems().length > 0) { - int i = 0; int prevItems = Math.max(adapter.size(), 0); for (BaseItemDto item : response.getItems()) { adapter.add(new BaseItemDtoBaseRowItem(ModelCompat.asSdk(item))); } totalItems = response.getTotalRecordCount(); - setItemsLoaded(itemsLoaded + i); + setItemsLoaded(itemsLoaded + response.getItems().length); if (prevItems > 0) { // remove previous items as we re-retrieved // this is done this way instead of clearing the adapter to avoid bugs in the framework elements From 6e4c27b67a46eedf1cb2538ccd192acbf7fef4af Mon Sep 17 00:00:00 2001 From: Niels van Velzen Date: Sat, 25 May 2024 14:04:18 +0200 Subject: [PATCH 15/15] Remove unused ChangeTriggerTypes --- .../java/org/jellyfin/androidtv/constant/ChangeTriggerType.kt | 2 -- .../java/org/jellyfin/androidtv/ui/home/HomeFragmentHelper.kt | 2 +- .../org/jellyfin/androidtv/ui/itemhandling/ItemRowAdapter.java | 3 --- 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/app/src/main/java/org/jellyfin/androidtv/constant/ChangeTriggerType.kt b/app/src/main/java/org/jellyfin/androidtv/constant/ChangeTriggerType.kt index f2fd6a7f9b..16d06a8240 100644 --- a/app/src/main/java/org/jellyfin/androidtv/constant/ChangeTriggerType.kt +++ b/app/src/main/java/org/jellyfin/androidtv/constant/ChangeTriggerType.kt @@ -6,7 +6,5 @@ enum class ChangeTriggerType { TvPlayback, GuideNeedsLoad, MusicPlayback, - Always, - VideoQueueChange, FavoriteUpdate, } diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/home/HomeFragmentHelper.kt b/app/src/main/java/org/jellyfin/androidtv/ui/home/HomeFragmentHelper.kt index f1d35035b6..c690be0ba7 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/home/HomeFragmentHelper.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/home/HomeFragmentHelper.kt @@ -44,7 +44,7 @@ class HomeFragmentHelper( mediaTypes = includeMediaTypes, ) - return HomeFragmentBrowseRowDefRow(BrowseRowDef(title, query, 0, false, true, arrayOf(ChangeTriggerType.VideoQueueChange, ChangeTriggerType.TvPlayback, ChangeTriggerType.MoviePlayback))) + return HomeFragmentBrowseRowDefRow(BrowseRowDef(title, query, 0, false, true, arrayOf(ChangeTriggerType.TvPlayback, ChangeTriggerType.MoviePlayback))) } fun loadResumeVideo(): HomeFragmentRow { diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemRowAdapter.java b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemRowAdapter.java index 309579bddd..a0983d24f7 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemRowAdapter.java +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemhandling/ItemRowAdapter.java @@ -653,9 +653,6 @@ public boolean ReRetrieveIfNeeded() { start.set(Calendar.SECOND, 0); retrieve |= TvManager.programsNeedLoad(start); break; - case Always: - retrieve = true; - break; } }