Fix Live TV guide crash when channel loading fails (#5801)

* Fix Live TV guide crash when channel loading fails

* Handle unavailable channels in Live TV lookups
This commit is contained in:
Andrew Codispoti authored and GitHub committed 2026-09-15 16:38:22 +02:00
1 parent d9902de016
commit 04552d661f
5 files changed
+52 -3

No files matched your search

@@ -257,9 +257,11 @@ public class LiveProgramDetailPopup {
public android.widget.ImageButton createFavoriteButton() {
BaseItemDto channel = TvManager.getChannel(TvManager.getAllChannelsIndex(mProgram.getChannelId()));
boolean isFav = channel.getUserData() != null && channel.getUserData().isFavorite();
boolean isFav = channel != null && channel.getUserData() != null && channel.getUserData().isFavorite();
android.widget.ImageButton fave = addImgButton(mDButtonRow, isFav ? R.drawable.ic_heart_red : R.drawable.ic_heart);
fave.setEnabled(channel != null);
if (channel == null) return fave;
fave.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -532,6 +532,7 @@ public class LiveTvGuideFragment extends Fragment implements LiveTvGuide, View.O
for (int i = start; i <= end; i++) {
if (isCancelled()) return null;
final BaseItemDto channel = TvManager.getChannel(i);
if (channel == null) continue;
List<BaseItemDto> programs = TvManager.getProgramsForChannel(channel.getId(), mFilters);
final LinearLayout row = getProgramRow(programs, channel.getId());
if (row == null) continue; // no row to show
@@ -6,6 +6,7 @@ import android.text.format.DateUtils;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.leanback.widget.HeaderItem;
import androidx.leanback.widget.ListRow;
@@ -31,6 +32,7 @@ import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -63,7 +65,7 @@ public class TvManager {
}
public static List<BaseItemDto> getAllChannels() {
return allChannels;
return allChannels == null ? Collections.emptyList() : allChannels;
}
public static void forceReload() {
@@ -80,8 +82,10 @@ public class TvManager {
return -1;
}
@Nullable
public static BaseItemDto getChannel(int ndx) {
return allChannels.get(ndx);
List<BaseItemDto> channels = getAllChannels();
return ndx >= 0 && ndx < channels.size() ? channels.get(ndx) : null;
}
public static void updateLastPlayedDate(UUID channelId) {
@@ -866,6 +866,7 @@ public class CustomPlaybackOverlayFragment extends Fragment implements LiveTvGui
for (int i = start; i <= end; i++) {
if (isCancelled()) return null;
final BaseItemDto channel = TvManager.getChannel(i);
if (channel == null) continue;
List<BaseItemDto> programs = TvManager.getProgramsForChannel(channel.getId());
final LinearLayout row = getProgramRow(programs, channel.getId());
if (first) {
@@ -0,0 +1,41 @@
package org.jellyfin.androidtv.ui.livetv
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import org.jellyfin.sdk.model.api.BaseItemDto
import org.jellyfin.sdk.model.api.BaseItemKind
import java.util.UUID
class TvManagerTests : FunSpec({
val channelsField = TvManager::class.java.getDeclaredField("allChannels").apply { isAccessible = true }
var previousChannels: Any? = null
beforeTest {
previousChannels = channelsField.get(null)
channelsField.set(null, null)
}
afterTest {
channelsField.set(null, previousChannels)
}
test("missing channel cache returns an empty list and no channel") {
TvManager.getAllChannels() shouldBe emptyList()
TvManager.getChannel(0) shouldBe null
TvManager.getChannel(-1) shouldBe null
}
test("empty channel cache returns no channel") {
channelsField.set(null, emptyList<BaseItemDto>())
TvManager.getChannel(0) shouldBe null
}
test("cached channel lookup preserves valid channels and rejects invalid indices") {
val channel = BaseItemDto(id = UUID.randomUUID(), type = BaseItemKind.TV_CHANNEL)
channelsField.set(null, listOf(channel))
TvManager.getChannel(0) shouldBe channel
TvManager.getChannel(-1) shouldBe null
TvManager.getChannel(1) shouldBe null
TvManager.getChannel(Int.MAX_VALUE) shouldBe null
}
})