From 5b2b9021e0b4b9b8a104a6f82b91ac07f730df77 Mon Sep 17 00:00:00 2001 From: okjack Date: Sat, 8 Jun 2024 09:37:32 +0800 Subject: [PATCH 1/8] Revert Parser.java --- .../android/tv/player/danmu/Parser.java | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/fongmi/android/tv/player/danmu/Parser.java b/app/src/main/java/com/fongmi/android/tv/player/danmu/Parser.java index 72318e239..cf6d8b5d5 100644 --- a/app/src/main/java/com/fongmi/android/tv/player/danmu/Parser.java +++ b/app/src/main/java/com/fongmi/android/tv/player/danmu/Parser.java @@ -24,8 +24,10 @@ import master.flame.danmaku.danmaku.util.DanmakuUtils; public class Parser extends BaseDanmakuParser { private final Danmu danmu; + private BaseDanmaku item; private float scaleX; private float scaleY; + private int index; public Parser(String path) { this.danmu = Danmu.fromXml(getContent(path)); @@ -43,9 +45,11 @@ public class Parser extends BaseDanmakuParser { for (Danmu.Data data : danmu.getData()) { String[] values = data.getParam().split(","); if (values.length < 4) continue; - BaseDanmaku item = createDanmaku(values); - setText(item, data.getText()); - result.addItem(item); + setParam(values); + setText(data.getText()); + synchronized (result.obtainSynchronizer()) { + result.addItem(item); + } } return result; } @@ -58,27 +62,27 @@ public class Parser extends BaseDanmakuParser { return this; } - private BaseDanmaku createDanmaku(String[] values) { + private void setParam(String[] values) { int type = Integer.parseInt(values[1]); long time = (long) (Float.parseFloat(values[0]) * 1000); float size = Float.parseFloat(values[2]) * (mDispDensity - 0.6f); int color = (int) ((0x00000000ff000000L | Long.parseLong(values[3])) & 0x00000000ffffffffL); - BaseDanmaku item = mContext.mDanmakuFactory.createDanmaku(type, mContext); + item = mContext.mDanmakuFactory.createDanmaku(type, mContext); item.setTime(time); item.setTimer(mTimer); item.textSize = size; item.textColor = color; item.textShadowColor = color <= Color.BLACK ? Color.WHITE : Color.BLACK; item.flags = mContext.mGlobalFlagValues; - return item; } - private void setText(BaseDanmaku item, String text) { + private void setText(String text) { + item.index = index++; DanmakuUtils.fillText(item, decodeXmlString(text)); - if (item.getType() == BaseDanmaku.TYPE_SPECIAL && text.startsWith("[") && text.endsWith("]")) setSpecial(item); + if (item.getType() == BaseDanmaku.TYPE_SPECIAL && text.startsWith("[") && text.endsWith("]")) setSpecial(); } - private void setSpecial(BaseDanmaku item) { + private void setSpecial() { String[] textArr = null; try { JSONArray jsonArray = new JSONArray(item.text); From 8da575b5e3beff40790db1c14fc7a11cf95ef6da Mon Sep 17 00:00:00 2001 From: okjack Date: Sat, 8 Jun 2024 09:41:17 +0800 Subject: [PATCH 2/8] Fix artwork --- .../com/fongmi/android/tv/ui/activity/CastActivity.java | 6 +++++- .../com/fongmi/android/tv/ui/activity/LiveActivity.java | 6 +++++- .../com/fongmi/android/tv/ui/activity/VideoActivity.java | 6 +++++- .../com/fongmi/android/tv/ui/activity/LiveActivity.java | 6 +++++- .../com/fongmi/android/tv/ui/activity/VideoActivity.java | 6 +++++- 5 files changed, 25 insertions(+), 5 deletions(-) diff --git a/app/src/leanback/java/com/fongmi/android/tv/ui/activity/CastActivity.java b/app/src/leanback/java/com/fongmi/android/tv/ui/activity/CastActivity.java index 8b6bb1110..228133685 100644 --- a/app/src/leanback/java/com/fongmi/android/tv/ui/activity/CastActivity.java +++ b/app/src/leanback/java/com/fongmi/android/tv/ui/activity/CastActivity.java @@ -353,7 +353,11 @@ public class CastActivity extends BaseActivity implements CustomKeyDownCast.List String title = mBinding.widget.title.getText().toString(); MediaMetadataCompat.Builder builder = new MediaMetadataCompat.Builder(); builder.putString(MediaMetadataCompat.METADATA_KEY_TITLE, title); - builder.putBitmap(MediaMetadataCompat.METADATA_KEY_ART, getIjk().getDefaultArtwork()); + try { + builder.putBitmap(MediaMetadataCompat.METADATA_KEY_ART, getIjk().getDefaultArtwork()); + } catch (Exception e) { + e.printStackTrace(); + } builder.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, mPlayers.getDuration()); mPlayers.setMetadata(builder.build()); } diff --git a/app/src/leanback/java/com/fongmi/android/tv/ui/activity/LiveActivity.java b/app/src/leanback/java/com/fongmi/android/tv/ui/activity/LiveActivity.java index 6cdd828b8..983132c54 100644 --- a/app/src/leanback/java/com/fongmi/android/tv/ui/activity/LiveActivity.java +++ b/app/src/leanback/java/com/fongmi/android/tv/ui/activity/LiveActivity.java @@ -778,7 +778,11 @@ public class LiveActivity extends BaseActivity implements Clock.Callback, GroupP MediaMetadataCompat.Builder builder = new MediaMetadataCompat.Builder(); builder.putString(MediaMetadataCompat.METADATA_KEY_TITLE, title); builder.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, artist); - builder.putBitmap(MediaMetadataCompat.METADATA_KEY_ART, getIjk().getDefaultArtwork()); + try { + builder.putBitmap(MediaMetadataCompat.METADATA_KEY_ART, getIjk().getDefaultArtwork()); + } catch (Exception e) { + e.printStackTrace(); + } builder.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, mPlayers.getDuration()); mPlayers.setMetadata(builder.build()); } diff --git a/app/src/leanback/java/com/fongmi/android/tv/ui/activity/VideoActivity.java b/app/src/leanback/java/com/fongmi/android/tv/ui/activity/VideoActivity.java index e217fbe0d..bce285bad 100644 --- a/app/src/leanback/java/com/fongmi/android/tv/ui/activity/VideoActivity.java +++ b/app/src/leanback/java/com/fongmi/android/tv/ui/activity/VideoActivity.java @@ -1377,7 +1377,11 @@ public class VideoActivity extends BaseActivity implements CustomKeyDownVod.List MediaMetadataCompat.Builder builder = new MediaMetadataCompat.Builder(); builder.putString(MediaMetadataCompat.METADATA_KEY_TITLE, title); builder.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, artist); - builder.putBitmap(MediaMetadataCompat.METADATA_KEY_ART, getIjk().getDefaultArtwork()); + try { + builder.putBitmap(MediaMetadataCompat.METADATA_KEY_ART, getIjk().getDefaultArtwork()); + } catch (Exception e) { + e.printStackTrace(); + } builder.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, mPlayers.getDuration()); mPlayers.setMetadata(builder.build()); } diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/activity/LiveActivity.java b/app/src/mobile/java/com/fongmi/android/tv/ui/activity/LiveActivity.java index 92a696a08..801c9eb75 100644 --- a/app/src/mobile/java/com/fongmi/android/tv/ui/activity/LiveActivity.java +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/activity/LiveActivity.java @@ -843,7 +843,11 @@ public class LiveActivity extends BaseActivity implements Clock.Callback, Custom MediaMetadataCompat.Builder builder = new MediaMetadataCompat.Builder(); builder.putString(MediaMetadataCompat.METADATA_KEY_TITLE, title); builder.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, artist); - builder.putBitmap(MediaMetadataCompat.METADATA_KEY_ART, getIjk().getDefaultArtwork()); + try { + builder.putBitmap(MediaMetadataCompat.METADATA_KEY_ART, getIjk().getDefaultArtwork()); + } catch (Exception e) { + e.printStackTrace(); + } builder.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, mPlayers.getDuration()); mPlayers.setMetadata(builder.build()); ActionEvent.update(); diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/activity/VideoActivity.java b/app/src/mobile/java/com/fongmi/android/tv/ui/activity/VideoActivity.java index 0e86fc6a5..e73c6f8ee 100644 --- a/app/src/mobile/java/com/fongmi/android/tv/ui/activity/VideoActivity.java +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/activity/VideoActivity.java @@ -1322,7 +1322,11 @@ public class VideoActivity extends BaseActivity implements Clock.Callback, Custo MediaMetadataCompat.Builder builder = new MediaMetadataCompat.Builder(); builder.putString(MediaMetadataCompat.METADATA_KEY_TITLE, title); builder.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, artist); - builder.putBitmap(MediaMetadataCompat.METADATA_KEY_ART, getIjk().getDefaultArtwork()); + try { + builder.putBitmap(MediaMetadataCompat.METADATA_KEY_ART, getIjk().getDefaultArtwork()); + } catch (Exception e) { + e.printStackTrace(); + } builder.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, mPlayers.getDuration()); mPlayers.setMetadata(builder.build()); ActionEvent.update(); From 55e69e1c3468581f332a2eddcf53572c8fcd2878 Mon Sep 17 00:00:00 2001 From: okjack Date: Sat, 8 Jun 2024 09:48:53 +0800 Subject: [PATCH 3/8] Support tvg-url parse --- .../android/tv/ui/activity/LiveActivity.java | 12 ++- .../tv/ui/presenter/ChannelPresenter.java | 2 + .../leanback/res/drawable/ic_action_arrow.xml | 10 +++ .../leanback/res/layout/adapter_channel.xml | 8 ++ .../com/fongmi/android/tv/api/EpgParser.java | 89 ++++++++++++++++++ .../com/fongmi/android/tv/api/LiveParser.java | 3 + .../com/fongmi/android/tv/bean/Channel.java | 4 +- .../java/com/fongmi/android/tv/bean/Epg.java | 14 ++- .../com/fongmi/android/tv/bean/EpgData.java | 8 ++ .../java/com/fongmi/android/tv/bean/Live.java | 4 + .../java/com/fongmi/android/tv/bean/Tv.java | 90 +++++++++++++++++++ .../android/tv/model/LiveViewModel.java | 4 +- .../android/tv/ui/activity/LiveActivity.java | 25 +++--- .../android/tv/ui/adapter/ChannelAdapter.java | 3 - 14 files changed, 254 insertions(+), 22 deletions(-) create mode 100644 app/src/leanback/res/drawable/ic_action_arrow.xml create mode 100644 app/src/main/java/com/fongmi/android/tv/api/EpgParser.java create mode 100644 app/src/main/java/com/fongmi/android/tv/bean/Tv.java diff --git a/app/src/leanback/java/com/fongmi/android/tv/ui/activity/LiveActivity.java b/app/src/leanback/java/com/fongmi/android/tv/ui/activity/LiveActivity.java index 983132c54..044e412ea 100644 --- a/app/src/leanback/java/com/fongmi/android/tv/ui/activity/LiveActivity.java +++ b/app/src/leanback/java/com/fongmi/android/tv/ui/activity/LiveActivity.java @@ -590,9 +590,13 @@ public class LiveActivity extends BaseActivity implements Clock.Callback, GroupP @Override public void onItemClick(Channel item) { - mGroup.setPosition(mBinding.channel.getSelectedPosition()); - setChannel(item.group(mGroup)); - hideUI(); + if (item.getData().getList().size() > 0 && item.isSelected() && mChannel != null) { + showEpg(item); + } else { + mGroup.setPosition(mBinding.channel.getSelectedPosition()); + setChannel(item.group(mGroup)); + hideUI(); + } } @Override @@ -664,7 +668,7 @@ public class LiveActivity extends BaseActivity implements Clock.Callback, GroupP } private void setEpg(Epg epg) { - if (mChannel != null && mChannel.getName().equals(epg.getKey())) setEpg(); + if (mChannel != null && mChannel.getTvgName().equals(epg.getKey())) setEpg(); } private void fetch() { diff --git a/app/src/leanback/java/com/fongmi/android/tv/ui/presenter/ChannelPresenter.java b/app/src/leanback/java/com/fongmi/android/tv/ui/presenter/ChannelPresenter.java index 63e25a245..0eb99fe13 100644 --- a/app/src/leanback/java/com/fongmi/android/tv/ui/presenter/ChannelPresenter.java +++ b/app/src/leanback/java/com/fongmi/android/tv/ui/presenter/ChannelPresenter.java @@ -1,6 +1,7 @@ package com.fongmi.android.tv.ui.presenter; import android.view.LayoutInflater; +import android.view.View; import android.view.ViewGroup; import androidx.annotation.NonNull; @@ -42,6 +43,7 @@ public class ChannelPresenter extends Presenter { setOnClickListener(holder, view -> mListener.onItemClick(item)); holder.view.setOnLongClickListener(view -> mListener.onLongClick(item)); holder.binding.getRoot().setRightListener(() -> mListener.showEpg(item)); + holder.binding.epg.setVisibility(item.getData().getList().isEmpty() || !item.isSelected() ? View.GONE : View.VISIBLE); } @Override diff --git a/app/src/leanback/res/drawable/ic_action_arrow.xml b/app/src/leanback/res/drawable/ic_action_arrow.xml new file mode 100644 index 000000000..3577518ab --- /dev/null +++ b/app/src/leanback/res/drawable/ic_action_arrow.xml @@ -0,0 +1,10 @@ + + + \ No newline at end of file diff --git a/app/src/leanback/res/layout/adapter_channel.xml b/app/src/leanback/res/layout/adapter_channel.xml index a21460d90..37401a3d4 100644 --- a/app/src/leanback/res/layout/adapter_channel.xml +++ b/app/src/leanback/res/layout/adapter_channel.xml @@ -42,4 +42,12 @@ android:textStyle="bold" tools:text="CNN" /> + + \ No newline at end of file diff --git a/app/src/main/java/com/fongmi/android/tv/api/EpgParser.java b/app/src/main/java/com/fongmi/android/tv/api/EpgParser.java new file mode 100644 index 000000000..e57513428 --- /dev/null +++ b/app/src/main/java/com/fongmi/android/tv/api/EpgParser.java @@ -0,0 +1,89 @@ +package com.fongmi.android.tv.api; + +import android.net.Uri; + +import com.fongmi.android.tv.bean.Channel; +import com.fongmi.android.tv.bean.Epg; +import com.fongmi.android.tv.bean.EpgData; +import com.fongmi.android.tv.bean.Group; +import com.fongmi.android.tv.bean.Live; +import com.fongmi.android.tv.bean.Tv; +import com.fongmi.android.tv.utils.Download; +import com.github.catvod.utils.Path; +import com.github.catvod.utils.Trans; + +import org.simpleframework.xml.core.Persister; + +import java.io.File; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class EpgParser { + + private static final SimpleDateFormat formatFull = new SimpleDateFormat("yyyyMMddHHmmss"); + private static final SimpleDateFormat formatDate = new SimpleDateFormat("yyyy-MM-dd"); + private static final SimpleDateFormat formatTime = new SimpleDateFormat("HH:mm"); + + public static void start(Live live) { + try { + if (!live.getEpg().contains(".xml") || live.getEpg().contains("{")) return; + File file = Path.cache(Uri.parse(live.getEpg()).getLastPathSegment()); + if (shouldDownload(file)) Download.create(live.getEpg(), file).start(); + readXml(live, Path.read(file)); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private static boolean shouldDownload(File file) { + return !file.exists() || !equalToday(file); + } + + private static boolean equalToday(File file) { + Calendar calendar = Calendar.getInstance(); + calendar.setTimeInMillis(file.lastModified()); + return calendar.get(Calendar.DAY_OF_MONTH) == Calendar.getInstance().get(Calendar.DAY_OF_MONTH); + } + + private static Date parseDateTime(String text) throws Exception { + return formatFull.parse(text.substring(0, 14)); + } + + private static void readXml(Live live, String xml) throws Exception { + Set exist = new HashSet<>(); + Map epgMap = new HashMap<>(); + Map mapping = new HashMap<>(); + String today = formatDate.format(new Date()); + Tv tv = new Persister().read(Tv.class, xml); + for (Group group : live.getGroups()) for (Channel channel : group.getChannel()) exist.add(channel.getTvgName()); + for (Tv.Channel channel : tv.getChannel()) mapping.put(channel.getId(), channel.getDisplayName()); + for (Tv.Programme programme : tv.getProgramme()) { + String key = mapping.get(programme.getChannel()); + if (!exist.contains(key)) continue; + if (!programme.equals(today)) continue; + if (!epgMap.containsKey(key)) epgMap.put(key, Epg.create(key, today)); + String title = programme.getTitle(); + String start = programme.getStart(); + String stop = programme.getStop(); + Date startDate = parseDateTime(start); + Date endDate = parseDateTime(stop); + EpgData epgData = new EpgData(); + epgData.setStart(formatTime.format(startDate)); + epgData.setEnd(formatTime.format(endDate)); + epgData.setStartTime(startDate.getTime()); + epgData.setEndTime(endDate.getTime()); + epgData.setTitle(Trans.s2t(title)); + epgMap.get(key).getList().add(epgData); + } + for (Group group : live.getGroups()) { + for (Channel channel : group.getChannel()) { + channel.setData(epgMap.get(channel.getTvgName())); + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/fongmi/android/tv/api/LiveParser.java b/app/src/main/java/com/fongmi/android/tv/api/LiveParser.java index 6495bcf7a..6512899f3 100644 --- a/app/src/main/java/com/fongmi/android/tv/api/LiveParser.java +++ b/app/src/main/java/com/fongmi/android/tv/api/LiveParser.java @@ -28,6 +28,8 @@ public class LiveParser { private static final Pattern CATCHUP = Pattern.compile(".*catchup=\"(.?|.+?)\".*"); private static final Pattern TVG_NAME = Pattern.compile(".*tvg-name=\"(.?|.+?)\".*"); private static final Pattern TVG_LOGO = Pattern.compile(".*tvg-logo=\"(.?|.+?)\".*"); + private static final Pattern TVG_URL = Pattern.compile(".*x-tvg-url=\"(.?|.+?)\".*"); + private static final Pattern GROUP = Pattern.compile(".*group-title=\"(.?|.+?)\".*"); private static final Pattern NAME = Pattern.compile(".*,(.+?)$"); @@ -78,6 +80,7 @@ public class LiveParser { } else if (line.startsWith("#EXTM3U")) { catchup.setType(extract(line, CATCHUP)); catchup.setSource(extract(line, CATCHUP_SOURCE)); + if (live.getEpg().isEmpty()) live.setEpg(extract(line, TVG_URL)); } else if (line.startsWith("#EXTINF:")) { Group group = live.find(Group.create(extract(line, GROUP), live.isPass())); channel = group.find(Channel.create(extract(line, NAME))); diff --git a/app/src/main/java/com/fongmi/android/tv/bean/Channel.java b/app/src/main/java/com/fongmi/android/tv/bean/Channel.java index 646b3ea0f..19866ec2b 100644 --- a/app/src/main/java/com/fongmi/android/tv/bean/Channel.java +++ b/app/src/main/java/com/fongmi/android/tv/bean/Channel.java @@ -336,8 +336,8 @@ public class Channel { if (!live.getCatchup().isEmpty() && getCatchup().isEmpty()) setCatchup(live.getCatchup()); if (live.getReferer().length() > 0 && getReferer().isEmpty()) setReferer(live.getReferer()); if (live.getPlayerType() != -1 && getPlayerType() == -1) setPlayerType(live.getPlayerType()); - if (!getEpg().startsWith("http")) setEpg(live.getEpg().replace("{name}", getTvgName()).replace("{epg}", getEpg())); - if (!getLogo().startsWith("http")) setLogo(live.getLogo().replace("{name}", getTvgName()).replace("{logo}", getLogo())); + if (!getEpg().startsWith("http") && live.getEpg().contains("{")) setEpg(live.getEpg().replace("{name}", getTvgName()).replace("{epg}", getEpg())); + if (!getLogo().startsWith("http") && live.getLogo().contains("{")) setLogo(live.getLogo().replace("{name}", getTvgName()).replace("{logo}", getLogo())); } public void setLine(String line) { diff --git a/app/src/main/java/com/fongmi/android/tv/bean/Epg.java b/app/src/main/java/com/fongmi/android/tv/bean/Epg.java index 4ccdadaaf..ec98d0323 100644 --- a/app/src/main/java/com/fongmi/android/tv/bean/Epg.java +++ b/app/src/main/java/com/fongmi/android/tv/bean/Epg.java @@ -35,8 +35,16 @@ public class Epg { } } + public static Epg create(String key, String date) { + Epg item = new Epg(); + item.setKey(key); + item.setDate(date); + item.setList(new ArrayList<>()); + return item; + } + public String getKey() { - return key; + return TextUtils.isEmpty(key) ? "" : key; } public void setKey(String key) { @@ -47,6 +55,10 @@ public class Epg { return TextUtils.isEmpty(date) ? "" : date; } + public void setDate(String date) { + this.date = date; + } + public List getList() { return list == null ? Collections.emptyList() : list; } diff --git a/app/src/main/java/com/fongmi/android/tv/bean/EpgData.java b/app/src/main/java/com/fongmi/android/tv/bean/EpgData.java index e3b29c602..5cbdd31b4 100644 --- a/app/src/main/java/com/fongmi/android/tv/bean/EpgData.java +++ b/app/src/main/java/com/fongmi/android/tv/bean/EpgData.java @@ -36,10 +36,18 @@ public class EpgData { return TextUtils.isEmpty(start) ? "" : start; } + public void setStart(String start) { + this.start = start; + } + public String getEnd() { return TextUtils.isEmpty(end) ? "" : end; } + public void setEnd(String end) { + this.end = end; + } + public boolean isSelected() { return selected; } diff --git a/app/src/main/java/com/fongmi/android/tv/bean/Live.java b/app/src/main/java/com/fongmi/android/tv/bean/Live.java index 838a54b58..914a48e24 100644 --- a/app/src/main/java/com/fongmi/android/tv/bean/Live.java +++ b/app/src/main/java/com/fongmi/android/tv/bean/Live.java @@ -184,6 +184,10 @@ public class Live { return TextUtils.isEmpty(epg) ? "" : epg; } + public void setEpg(String epg) { + this.epg = epg; + } + public String getUa() { return TextUtils.isEmpty(ua) ? "" : ua; } diff --git a/app/src/main/java/com/fongmi/android/tv/bean/Tv.java b/app/src/main/java/com/fongmi/android/tv/bean/Tv.java new file mode 100644 index 000000000..2335e0046 --- /dev/null +++ b/app/src/main/java/com/fongmi/android/tv/bean/Tv.java @@ -0,0 +1,90 @@ +package com.fongmi.android.tv.bean; + +import android.text.TextUtils; + +import org.simpleframework.xml.Attribute; +import org.simpleframework.xml.Element; +import org.simpleframework.xml.ElementList; +import org.simpleframework.xml.Root; + +import java.util.Collections; +import java.util.List; + +@Root(name = "tv", strict = false) +public class Tv { + + @ElementList(entry = "channel", inline = true) + private List channel; + + @ElementList(entry = "programme", inline = true) + private List programme; + + public List getChannel() { + return channel == null ? Collections.emptyList() : channel; + } + + public List getProgramme() { + return programme == null ? Collections.emptyList() : programme; + } + + @Root(name = "channel") + public static class Channel { + + @Attribute(name = "id") + private String id; + + @Element(name = "display-name") + private String displayName; + + public String getId() { + return TextUtils.isEmpty(id) ? "" : id; + } + + public String getDisplayName() { + return TextUtils.isEmpty(displayName) ? "" : displayName; + } + } + + @Root(name = "programme") + public static class Programme { + + @Attribute(name = "start") + private String start; + + @Attribute(name = "stop") + private String stop; + + @Attribute(name = "channel") + private String channel; + + @Element(name = "title") + private String title; + + @Element(name = "date", required = false) + private String date; + + public String getStart() { + return TextUtils.isEmpty(start) ? "" : start; + } + + public String getStop() { + return TextUtils.isEmpty(stop) ? "" : stop; + } + + public String getChannel() { + return TextUtils.isEmpty(channel) ? "" : channel; + } + + public String getTitle() { + return TextUtils.isEmpty(title) ? "" : title; + } + + public String getDate() { + return TextUtils.isEmpty(date) ? "" : date; + } + + public boolean equals(String date) { + return getDate().isEmpty() || getDate().equals(date); + } + } +} diff --git a/app/src/main/java/com/fongmi/android/tv/model/LiveViewModel.java b/app/src/main/java/com/fongmi/android/tv/model/LiveViewModel.java index 20f3398c4..8a0910d81 100644 --- a/app/src/main/java/com/fongmi/android/tv/model/LiveViewModel.java +++ b/app/src/main/java/com/fongmi/android/tv/model/LiveViewModel.java @@ -4,6 +4,7 @@ import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.ViewModel; import com.fongmi.android.tv.Constant; +import com.fongmi.android.tv.api.EpgParser; import com.fongmi.android.tv.api.LiveParser; import com.fongmi.android.tv.api.config.VodConfig; import com.fongmi.android.tv.bean.Channel; @@ -53,6 +54,7 @@ public class LiveViewModel extends ViewModel { execute(LIVE, () -> { VodConfig.get().setRecent(item.getJar()); LiveParser.start(item); + EpgParser.start(item); verify(item); return item; }); @@ -62,7 +64,7 @@ public class LiveViewModel extends ViewModel { String date = formatDate.format(new Date()); String url = item.getEpg().replace("{date}", date); execute(EPG, () -> { - if (!item.getData().equal(date)) item.setData(Epg.objectFrom(OkHttp.string(url), item.getName(), formatTime)); + if (!item.getData().equal(date)) item.setData(Epg.objectFrom(OkHttp.string(url), item.getTvgName(), formatTime)); return item.getData().selected(); }); } diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/activity/LiveActivity.java b/app/src/mobile/java/com/fongmi/android/tv/ui/activity/LiveActivity.java index 801c9eb75..591354947 100644 --- a/app/src/mobile/java/com/fongmi/android/tv/ui/activity/LiveActivity.java +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/activity/LiveActivity.java @@ -493,8 +493,7 @@ public class LiveActivity extends BaseActivity implements Clock.Callback, Custom hideEpg(); } - @Override - public void showEpg(Channel item) { + private void showEpg(Channel item) { if (mChannel == null || mChannel.getData().getList().isEmpty() || mEpgDataAdapter.getItemCount() == 0 || !mChannel.equals(item)) return; mBinding.widget.epgData.scrollToPosition(item.getData().getSelected()); mBinding.widget.epg.setVisibility(View.VISIBLE); @@ -638,14 +637,18 @@ public class LiveActivity extends BaseActivity implements Clock.Callback, Custom @Override public void onItemClick(Channel item) { - mGroup.setPosition(mChannelAdapter.setSelected(item.group(mGroup))); - mPlayers.setPlayer(getPlayerType(item.getPlayerType())); - setArtwork(item.getLogo()); - mChannel = item; - setPlayerView(); - showInfo(); - hideUI(); - fetch(); + if (item.getData().getList().size() > 0 && item.isSelected() && mChannel != null) { + showEpg(item); + } else { + mGroup.setPosition(mChannelAdapter.setSelected(item.group(mGroup))); + mPlayers.setPlayer(getPlayerType(item.getPlayerType())); + setArtwork(item.getLogo()); + mChannel = item; + setPlayerView(); + showInfo(); + hideUI(); + fetch(); + } } @Override @@ -711,7 +714,7 @@ public class LiveActivity extends BaseActivity implements Clock.Callback, Custom } private void setEpg(Epg epg) { - if (mChannel != null && mChannel.getName().equals(epg.getKey())) setEpg(); + if (mChannel != null && mChannel.getTvgName().equals(epg.getKey())) setEpg(); } private void fetch() { diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/adapter/ChannelAdapter.java b/app/src/mobile/java/com/fongmi/android/tv/ui/adapter/ChannelAdapter.java index f31ade09f..0c0b89cdf 100644 --- a/app/src/mobile/java/com/fongmi/android/tv/ui/adapter/ChannelAdapter.java +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/adapter/ChannelAdapter.java @@ -25,8 +25,6 @@ public class ChannelAdapter extends RecyclerView.Adapter mListener.showEpg(item)); holder.binding.getRoot().setOnClickListener(view -> mListener.onItemClick(item)); holder.binding.getRoot().setOnLongClickListener(view -> mListener.onLongClick(item)); holder.binding.epg.setVisibility(item.getData().getList().isEmpty() || !item.isSelected() ? View.GONE : View.VISIBLE); From 9fee7682ede6e4987a6b43b31ec3c0e039eb1f97 Mon Sep 17 00:00:00 2001 From: okjack Date: Sat, 8 Jun 2024 09:49:40 +0800 Subject: [PATCH 4/8] Update proguard-rules.pro --- app/proguard-rules.pro | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 13015298f..7f69b6d2a 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -1,3 +1,6 @@ +# TV +-keep class com.fongmi.android.tv.bean.** { *; } + # Gson -keepattributes Signature -keepattributes *Annotation* @@ -92,9 +95,6 @@ public static ** valueOf(java.lang.String); } -# bean --keep class com.fongmi.android.tv.bean.** { *; } - # x5 -dontwarn dalvik.** -dontwarn com.tencent.smtt.** From 0b4a4a93c7cfae959c538fc1d9a76e367901f639 Mon Sep 17 00:00:00 2001 From: okjack Date: Sat, 8 Jun 2024 09:50:55 +0800 Subject: [PATCH 5/8] Optimize load background --- .../android/tv/ui/base/BaseActivity.java | 36 +++------------- .../android/tv/api/config/WallConfig.java | 35 ++++++++++++++-- .../android/tv/ui/base/BaseActivity.java | 41 +++---------------- 3 files changed, 41 insertions(+), 71 deletions(-) diff --git a/app/src/leanback/java/com/fongmi/android/tv/ui/base/BaseActivity.java b/app/src/leanback/java/com/fongmi/android/tv/ui/base/BaseActivity.java index 4dda114d0..e6a245380 100644 --- a/app/src/leanback/java/com/fongmi/android/tv/ui/base/BaseActivity.java +++ b/app/src/leanback/java/com/fongmi/android/tv/ui/base/BaseActivity.java @@ -3,25 +3,19 @@ package com.fongmi.android.tv.ui.base; import android.app.Activity; import android.content.res.Configuration; import android.content.res.Resources; -import android.graphics.drawable.Drawable; import android.os.Bundle; import android.view.View; import androidx.activity.OnBackPressedCallback; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import androidx.leanback.widget.ArrayObjectAdapter; import androidx.recyclerview.widget.RecyclerView; import androidx.viewbinding.ViewBinding; -import com.bumptech.glide.Glide; -import com.bumptech.glide.request.target.CustomTarget; -import com.bumptech.glide.request.transition.Transition; -import com.bumptech.glide.signature.ObjectKey; -import com.fongmi.android.tv.App; import com.fongmi.android.tv.R; import com.fongmi.android.tv.Setting; +import com.fongmi.android.tv.api.config.WallConfig; import com.fongmi.android.tv.event.RefreshEvent; import com.fongmi.android.tv.utils.FileUtil; import com.fongmi.android.tv.utils.ResUtil; @@ -89,10 +83,6 @@ public abstract class BaseActivity extends AppCompatActivity { if (!view.isComputingLayout()) adapter.notifyArrayItemRangeChanged(0, adapter.size()); } - protected void notifyItemChanged(RecyclerView view, RecyclerView.Adapter adapter) { - if (!view.isComputingLayout()) adapter.notifyItemRangeChanged(0, adapter.getItemCount()); - } - private void setBackCallback() { getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(handleBack()) { @Override @@ -106,31 +96,13 @@ public abstract class BaseActivity extends AppCompatActivity { try { if (!customWall()) return; File file = FileUtil.getWall(Setting.getWall()); - if (file.exists() && file.length() > 0) loadWall(file); + if (file.exists() && file.length() > 0) getWindow().setBackgroundDrawable(WallConfig.drawable(file)); else getWindow().setBackgroundDrawableResource(ResUtil.getDrawable(file.getName())); } catch (Exception e) { getWindow().setBackgroundDrawableResource(R.drawable.wallpaper_1); } } - private void loadWall(File file) { - Glide.with(App.get()).load(file).centerCrop().override(ResUtil.getScreenWidth(), ResUtil.getScreenHeight()).signature(new ObjectKey(com.github.catvod.utils.Util.md5(file))).into(new CustomTarget() { - @Override - public void onResourceReady(@NonNull Drawable drawable, @Nullable Transition transition) { - getWindow().setBackgroundDrawable(drawable); - } - - @Override - public void onLoadFailed(@Nullable Drawable error) { - getWindow().setBackgroundDrawableResource(R.drawable.wallpaper_1); - } - - @Override - public void onLoadCleared(@Nullable Drawable drawable) { - } - }); - } - private Resources hackResources(Resources resources) { try { AutoSizeCompat.autoConvertDensityOfGlobal(resources); @@ -142,7 +114,9 @@ public abstract class BaseActivity extends AppCompatActivity { @Subscribe(threadMode = ThreadMode.MAIN) public void onRefreshEvent(RefreshEvent event) { - if (event.getType() == RefreshEvent.Type.WALL) refreshWall(); + if (event.getType() != RefreshEvent.Type.WALL) return; + WallConfig.get().setDrawable(null); + refreshWall(); } @Override diff --git a/app/src/main/java/com/fongmi/android/tv/api/config/WallConfig.java b/app/src/main/java/com/fongmi/android/tv/api/config/WallConfig.java index 3cc4e1626..0cb7d800c 100644 --- a/app/src/main/java/com/fongmi/android/tv/api/config/WallConfig.java +++ b/app/src/main/java/com/fongmi/android/tv/api/config/WallConfig.java @@ -1,7 +1,11 @@ package com.fongmi.android.tv.api.config; +import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; import android.text.TextUtils; +import com.bumptech.glide.Glide; +import com.bumptech.glide.signature.ObjectKey; import com.fongmi.android.tv.App; import com.fongmi.android.tv.R; import com.fongmi.android.tv.Setting; @@ -10,15 +14,18 @@ import com.fongmi.android.tv.event.RefreshEvent; import com.fongmi.android.tv.impl.Callback; import com.fongmi.android.tv.utils.FileUtil; import com.fongmi.android.tv.utils.Notify; +import com.fongmi.android.tv.utils.ResUtil; import com.github.catvod.net.OkHttp; import com.github.catvod.utils.Asset; import com.github.catvod.utils.Path; +import com.github.catvod.utils.Util; import java.io.File; -import java.io.IOException; +import java.io.FileOutputStream; public class WallConfig { + private Drawable drawable; private Config config; private boolean sync; @@ -38,6 +45,12 @@ public class WallConfig { return get().getConfig().getDesc(); } + public static Drawable drawable(File file) { + if (get().drawable != null) return get().drawable; + get().setDrawable(Drawable.createFromPath(file.getAbsolutePath())); + return get().drawable; + } + public static void load(Config config, Callback callback) { get().clear().config(config).load(callback); } @@ -62,6 +75,10 @@ public class WallConfig { return config == null ? Config.wall() : config; } + public void setDrawable(Drawable drawable) { + this.drawable = drawable; + } + public void load(Callback callback) { App.execute(() -> loadConfig(callback)); } @@ -80,12 +97,22 @@ public class WallConfig { } } - private File write(File file) throws IOException { + private File write(File file) throws Exception { if (getUrl().startsWith("file")) Path.copy(Path.local(getUrl()), file); else if (getUrl().startsWith("assets")) Path.copy(Asset.open(getUrl()), file); else if (getUrl().startsWith("http")) Path.write(file, OkHttp.newCall(getUrl()).execute().body().bytes()); - else file.delete(); - return file; + return resize(file); + } + + private File resize(File file) { + try { + Bitmap bitmap = Glide.with(App.get()).asBitmap().load(file).centerCrop().override(ResUtil.getScreenWidth(), ResUtil.getScreenHeight()).signature(new ObjectKey(Util.md5(file))).submit().get(); + bitmap.compress(Bitmap.CompressFormat.JPEG, 100, new FileOutputStream(file)); + bitmap.recycle(); + return file; + } catch (Exception e) { + return file; + } } public boolean needSync(String url) { diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/base/BaseActivity.java b/app/src/mobile/java/com/fongmi/android/tv/ui/base/BaseActivity.java index cae4612a7..b95267b5d 100644 --- a/app/src/mobile/java/com/fongmi/android/tv/ui/base/BaseActivity.java +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/base/BaseActivity.java @@ -1,9 +1,7 @@ package com.fongmi.android.tv.ui.base; import android.app.Activity; -import android.content.res.Configuration; import android.graphics.Color; -import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Bundle; import android.view.DisplayCutout; @@ -12,19 +10,12 @@ import android.view.ViewGroup; import android.view.WindowManager; import androidx.activity.OnBackPressedCallback; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import androidx.viewbinding.ViewBinding; -import com.bumptech.glide.Glide; -import com.bumptech.glide.request.RequestOptions; -import com.bumptech.glide.request.target.CustomTarget; -import com.bumptech.glide.request.transition.Transition; -import com.bumptech.glide.signature.ObjectKey; -import com.fongmi.android.tv.App; import com.fongmi.android.tv.R; import com.fongmi.android.tv.Setting; +import com.fongmi.android.tv.api.config.WallConfig; import com.fongmi.android.tv.event.RefreshEvent; import com.fongmi.android.tv.utils.FileUtil; import com.fongmi.android.tv.utils.ResUtil; @@ -129,40 +120,18 @@ public abstract class BaseActivity extends AppCompatActivity { try { if (!customWall()) return; File file = FileUtil.getWall(Setting.getWall()); - if (file.exists() && file.length() > 0) loadWall(file); + if (file.exists() && file.length() > 0) getWindow().setBackgroundDrawable(WallConfig.drawable(file)); else getWindow().setBackgroundDrawableResource(ResUtil.getDrawable(file.getName())); } catch (Exception e) { getWindow().setBackgroundDrawableResource(R.drawable.wallpaper_1); } } - private void loadWall(File file) { - Glide.with(App.get()).load(file).centerCrop().signature(new ObjectKey(file.lastModified())).apply(new RequestOptions().override(ResUtil.getScreenWidth(), ResUtil.getScreenHeight())).into(new CustomTarget() { - @Override - public void onResourceReady(@NonNull Drawable drawable, @Nullable Transition transition) { - getWindow().setBackgroundDrawable(drawable); - } - - @Override - public void onLoadFailed(@Nullable Drawable error) { - getWindow().setBackgroundDrawableResource(R.drawable.wallpaper_1); - } - - @Override - public void onLoadCleared(@Nullable Drawable drawable) { - } - }); - } - - @Override - public void onConfigurationChanged(@NonNull Configuration newConfig) { - if (getRequestedOrientation() != newConfig.orientation) refreshWall(); - super.onConfigurationChanged(newConfig); - } - @Subscribe(threadMode = ThreadMode.MAIN) public void onRefreshEvent(RefreshEvent event) { - if (event.getType() == RefreshEvent.Type.WALL) refreshWall(); + if (event.getType() != RefreshEvent.Type.WALL) return; + WallConfig.get().setDrawable(null); + refreshWall(); } @Override From 9ffa37b11f795fa73c1bdf6d541c1ec93330e56e Mon Sep 17 00:00:00 2001 From: okjack Date: Sat, 8 Jun 2024 09:51:24 +0800 Subject: [PATCH 6/8] Skip cache for wallpaper --- .../java/com/fongmi/android/tv/api/config/WallConfig.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/fongmi/android/tv/api/config/WallConfig.java b/app/src/main/java/com/fongmi/android/tv/api/config/WallConfig.java index 0cb7d800c..9f1c187a3 100644 --- a/app/src/main/java/com/fongmi/android/tv/api/config/WallConfig.java +++ b/app/src/main/java/com/fongmi/android/tv/api/config/WallConfig.java @@ -5,7 +5,7 @@ import android.graphics.drawable.Drawable; import android.text.TextUtils; import com.bumptech.glide.Glide; -import com.bumptech.glide.signature.ObjectKey; +import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.fongmi.android.tv.App; import com.fongmi.android.tv.R; import com.fongmi.android.tv.Setting; @@ -18,7 +18,6 @@ import com.fongmi.android.tv.utils.ResUtil; import com.github.catvod.net.OkHttp; import com.github.catvod.utils.Asset; import com.github.catvod.utils.Path; -import com.github.catvod.utils.Util; import java.io.File; import java.io.FileOutputStream; @@ -106,7 +105,7 @@ public class WallConfig { private File resize(File file) { try { - Bitmap bitmap = Glide.with(App.get()).asBitmap().load(file).centerCrop().override(ResUtil.getScreenWidth(), ResUtil.getScreenHeight()).signature(new ObjectKey(Util.md5(file))).submit().get(); + Bitmap bitmap = Glide.with(App.get()).asBitmap().load(file).centerCrop().override(ResUtil.getScreenWidth(), ResUtil.getScreenHeight()).skipMemoryCache(true).diskCacheStrategy(DiskCacheStrategy.NONE).submit().get(); bitmap.compress(Bitmap.CompressFormat.JPEG, 100, new FileOutputStream(file)); bitmap.recycle(); return file; From 820ecf4d2ce545a4985ca0f3ef8eedeadfd893b6 Mon Sep 17 00:00:00 2001 From: okjack Date: Sat, 8 Jun 2024 09:52:02 +0800 Subject: [PATCH 7/8] Fix parse bug --- .../main/java/com/fongmi/android/tv/player/ParseJob.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/fongmi/android/tv/player/ParseJob.java b/app/src/main/java/com/fongmi/android/tv/player/ParseJob.java index 40e334c80..19e1b63b3 100644 --- a/app/src/main/java/com/fongmi/android/tv/player/ParseJob.java +++ b/app/src/main/java/com/fongmi/android/tv/player/ParseJob.java @@ -111,9 +111,9 @@ public class ParseJob implements ParseCallback { private void jsonParse(Parse item, String webUrl, boolean error) throws Exception { String body = OkHttp.newCall(item.getUrl() + webUrl, Headers.of(item.getHeaders())).execute().body().string(); JsonObject object = Json.parse(body).getAsJsonObject(); - object = object.has("data") ? object.getAsJsonObject("data") : object; - boolean illegal = body.contains("不存在") || body.contains("已过期"); - String url = illegal ? "" : Json.safeString(object, "url"); + String url = Json.safeString(object, "url"); + object = object.getAsJsonObject("data"); + if (url.isEmpty()) url = Json.safeString(object, "url"); checkResult(getHeader(object), url, item.getName(), error); } From 32d9e51f9f2f17dac2ebf892becb79f8e31f2c84 Mon Sep 17 00:00:00 2001 From: okjack Date: Sat, 8 Jun 2024 09:52:53 +0800 Subject: [PATCH 8/8] Adjust layout --- .../tv/ui/activity/CollectActivity.java | 4 +- .../mobile/res/layout/activity_collect.xml | 106 ++++++++++-------- 2 files changed, 59 insertions(+), 51 deletions(-) diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/activity/CollectActivity.java b/app/src/mobile/java/com/fongmi/android/tv/ui/activity/CollectActivity.java index 792867df7..cb256ac60 100644 --- a/app/src/mobile/java/com/fongmi/android/tv/ui/activity/CollectActivity.java +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/activity/CollectActivity.java @@ -128,10 +128,10 @@ public class CollectActivity extends BaseActivity implements CustomScroller.Call mBinding.recycler.setHasFixedSize(true); mBinding.recycler.addOnScrollListener(mScroller); mBinding.recycler.setAdapter(mSearchAdapter = new SearchAdapter(this)); - mBinding.wordRecycler.setHasFixedSize(true); + mBinding.wordRecycler.setHasFixedSize(false); mBinding.wordRecycler.setAdapter(mWordAdapter = new WordAdapter(this)); mBinding.wordRecycler.setLayoutManager(new FlexboxLayoutManager(this, FlexDirection.ROW)); - mBinding.recordRecycler.setHasFixedSize(true); + mBinding.recordRecycler.setHasFixedSize(false); mBinding.recordRecycler.setAdapter(mRecordAdapter = new RecordAdapter(this)); mBinding.recordRecycler.setLayoutManager(new FlexboxLayoutManager(this, FlexDirection.ROW)); } diff --git a/app/src/mobile/res/layout/activity_collect.xml b/app/src/mobile/res/layout/activity_collect.xml index 1e8d575a3..8fa395f60 100644 --- a/app/src/mobile/res/layout/activity_collect.xml +++ b/app/src/mobile/res/layout/activity_collect.xml @@ -50,64 +50,72 @@ - - - - + android:layout_height="match_parent" + android:orientation="vertical"> - + - + - + + + + + +