Skip to content

Commit

Permalink
Refactor BaseRowItem to use inheritance (#3598)
Browse files Browse the repository at this point in the history
* Only use SDK types in BaseRowItem

This also cleans the constructor hell a bit (although still a mess)

* Refactor BaseRowItem constructors into subclasses

* Move function implementations to BaseRowItem subclasses

* Move properties to BaseRowItem subclasses

* Add missing BaseItemKinds in BaseItemDtoBaseRowItem

* Remove refresh function from BaseRowItem

* Optimize MutableObjectAdapter.remove(element: T)

* Make BaseRowItem immutable

* Remove dependency injection from BaseRowItem

* Use properties when possible in BaseRowItem

* Remove index property from BaseRowItem

* Move even more BaseRowItem properties

* Remove index from AudioQueueBaseRowItem

* Fix ItemRowAdapter counts after index removal

* Remove unused ChangeTriggerTypes
  • Loading branch information
nielsvanvelzen authored May 25, 2024
1 parent 7926aaf commit feafdf2
Show file tree
Hide file tree
Showing 30 changed files with 550 additions and 573 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,5 @@ enum class ChangeTriggerType {
TvPlayback,
GuideNeedsLoad,
MusicPlayback,
Always,
VideoQueueChange,
FavoriteUpdate,
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ abstract class BrowseFolderFragment : BrowseSupportFragment(), RowLoader {
itemLauncher.launch(
item,
(row as ListRow).adapter as ItemRowAdapter,
item.index,
requireContext()
)
}
Expand All @@ -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)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -125,6 +126,7 @@ public class BrowseGridFragment extends Fragment implements View.OnKeyListener {
private final Lazy<NavigationRepository> navigationRepository = inject(NavigationRepository.class);
private final Lazy<ItemLauncher> itemLauncher = inject(ItemLauncher.class);
private final Lazy<KeyProcessor> keyProcessor = inject(KeyProcessor.class);
private final Lazy<ApiClient> api = inject(ApiClient.class);

private int mCardsScreenEst = 0;
private int mCardsScreenStride = 0;
Expand Down Expand Up @@ -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<BaseItemDto>(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(mAdapter.indexOf(mCurrentItem));
}
return null;
});
}

private final class ItemViewClickedListener implements OnItemViewClickedListener {
Expand All @@ -967,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());
}
}

Expand Down Expand Up @@ -998,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));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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<BaseItemDto>(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 {
Expand Down Expand Up @@ -462,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());
}
}

Expand Down Expand Up @@ -497,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());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -114,8 +115,8 @@ public void setOverlayText(String text) {
public void setOverlayInfo(BaseRowItem item) {
if (binding.overlayText == null) return;

if (getCardType() == BaseCardView.CARD_TYPE_MAIN_ONLY && item.showCardInfoOverlay()) {
switch (item.getBaseItemType()) {
if (getCardType() == BaseCardView.CARD_TYPE_MAIN_ONLY && item.getShowCardInfoOverlay()) {
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;
Expand All @@ -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);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,19 +39,17 @@ 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
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
Expand Down Expand Up @@ -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<BaseItemDto?>(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() {
Expand All @@ -263,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())
}
}

Expand All @@ -282,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)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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, requireContext());
return true;
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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, requireContext());
return true;
}
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.jellyfin.androidtv.ui.itemhandling

import org.jellyfin.sdk.model.api.BaseItemDto

class AudioQueueBaseRowItem(
item: BaseItemDto,
) : BaseItemDtoBaseRowItem(
item = item,
staticHeight = true,
) {
var playing: Boolean = false
}

This file was deleted.

Loading

0 comments on commit feafdf2

Please sign in to comment.