support live - part 3

pull/123/head
FongMi 4 years ago
parent 09d96e2177
commit 0fb8106ebf
  1. 108
      app/src/leanback/java/com/fongmi/android/tv/ui/activity/LiveActivity.java
  2. 80
      app/src/leanback/java/com/fongmi/android/tv/ui/adapter/ChannelAdapter.java
  3. 66
      app/src/leanback/java/com/fongmi/android/tv/ui/adapter/GroupAdapter.java
  4. 43
      app/src/leanback/java/com/fongmi/android/tv/ui/adapter/holder/ChannelHolder.java
  5. 35
      app/src/leanback/java/com/fongmi/android/tv/ui/adapter/holder/GroupHolder.java
  6. 3
      app/src/leanback/java/com/fongmi/android/tv/ui/custom/CustomHorizontalGridView.java
  7. 2
      app/src/leanback/res/drawable/selector_live_channel.xml
  8. 2
      app/src/leanback/res/drawable/selector_live_group.xml
  9. 5
      app/src/leanback/res/layout/adapter_live_channel.xml
  10. 7
      app/src/leanback/res/layout/adapter_live_group.xml
  11. 2
      app/src/leanback/res/values/styles.xml
  12. 4
      app/src/main/java/com/fongmi/android/tv/api/LiveConfig.java
  13. 21
      app/src/main/java/com/fongmi/android/tv/bean/Channel.java
  14. 2
      app/src/main/java/com/fongmi/android/tv/player/ExoUtil.java
  15. 7
      app/src/main/java/com/fongmi/android/tv/player/Players.java
  16. 2
      app/src/main/java/com/fongmi/android/tv/ui/custom/CustomWebView.java

@ -1,15 +1,121 @@
package com.fongmi.android.tv.ui.activity;
import android.app.Activity;
import android.content.Intent;
import android.os.Handler;
import android.os.Looper;
import android.view.View;
import androidx.viewbinding.ViewBinding;
import com.fongmi.android.tv.api.LiveConfig;
import com.fongmi.android.tv.bean.Channel;
import com.fongmi.android.tv.bean.Group;
import com.fongmi.android.tv.databinding.ActivityLiveBinding;
import com.fongmi.android.tv.event.PlayerEvent;
import com.fongmi.android.tv.player.Players;
import com.fongmi.android.tv.ui.adapter.ChannelAdapter;
import com.fongmi.android.tv.ui.adapter.GroupAdapter;
import com.fongmi.android.tv.utils.Prefers;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
import com.google.android.exoplayer2.ui.StyledPlayerView;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
public class LiveActivity extends BaseActivity {
public class LiveActivity extends BaseActivity implements GroupAdapter.OnItemClickListener, ChannelAdapter.OnItemClickListener {
private ActivityLiveBinding mBinding;
private ChannelAdapter mChannelAdapter;
private GroupAdapter mGroupAdapter;
private Handler mHandler;
private Players mPlayers;
public static void start(Activity activity) {
activity.startActivity(new Intent(activity, LiveActivity.class));
}
private StyledPlayerView getPlayerView() {
return Prefers.getRender() == 0 ? mBinding.surface : mBinding.texture;
}
@Override
protected ViewBinding getBinding() {
return mBinding = ActivityLiveBinding.inflate(getLayoutInflater());
}
@Override
protected void initView() {
mHandler = new Handler(Looper.getMainLooper());
mPlayers = new Players().init();
setRecyclerView();
setVideoView();
}
@Override
protected void initEvent() {
EventBus.getDefault().register(this);
}
private void setRecyclerView() {
mBinding.group.setAdapter(mGroupAdapter = new GroupAdapter(this));
mBinding.channel.setAdapter(mChannelAdapter = new ChannelAdapter(this));
mGroupAdapter.addAll(LiveConfig.get().getLives().get(0).getGroups());
}
private void setVideoView() {
getPlayerView().setPlayer(mPlayers.exo());
getPlayerView().setVisibility(View.VISIBLE);
getPlayerView().setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_FILL);
}
@Override
public void onItemClick(Group item) {
mChannelAdapter.addAll(item);
}
@Override
public void onItemClick(Channel item) {
mPlayers.start(item, 0);
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onPlayerEvent(PlayerEvent event) {
switch (event.getState()) {
case 0:
break;
case Player.STATE_IDLE:
break;
case Player.STATE_BUFFERING:
//mBinding.widget.progress.getRoot().setVisibility(View.VISIBLE);
break;
case Player.STATE_READY:
mPlayers.setRetry(0);
//mBinding.widget.progress.getRoot().setVisibility(View.GONE);
break;
case Player.STATE_ENDED:
break;
default:
if (!event.isRetry() || mPlayers.addRetry() > 3) onError(event.getMsg());
else onRetry();
break;
}
}
private void onError(String msg) {
}
private void onRetry() {
mPlayers.start(mChannelAdapter.getCurrent(), 0);
}
@Override
protected void onDestroy() {
super.onDestroy();
mPlayers.release();
EventBus.getDefault().unregister(this);
}
}

@ -9,17 +9,56 @@ import androidx.recyclerview.widget.RecyclerView;
import com.fongmi.android.tv.bean.Channel;
import com.fongmi.android.tv.bean.Group;
import com.fongmi.android.tv.databinding.AdapterLiveChannelBinding;
import com.fongmi.android.tv.ui.adapter.holder.ChannelHolder;
import java.util.ArrayList;
import java.util.List;
public class ChannelAdapter extends RecyclerView.Adapter<ChannelAdapter.ViewHolder> {
public class ChannelAdapter extends RecyclerView.Adapter<ChannelHolder> {
private OnItemClickListener mListener;
private final List<Channel> mItems;
private Channel current;
private boolean focus;
private int position;
private Group group;
public ChannelAdapter() {
public ChannelAdapter(OnItemClickListener listener) {
this.mItems = new ArrayList<>();
this.mListener = listener;
}
public interface OnItemClickListener {
void onItemClick(Channel item);
}
private Channel getItem() {
return mItems.get(position);
}
public Channel getCurrent() {
return current;
}
public void setCurrent(Channel current) {
this.current = current;
}
public boolean isFocus() {
return focus;
}
public void setFocus(boolean focus) {
this.focus = focus;
}
public int getPosition() {
return position;
}
public void setPosition(int position) {
this.position = position;
}
public Group getGroup() {
@ -37,6 +76,22 @@ public class ChannelAdapter extends RecyclerView.Adapter<ChannelAdapter.ViewHold
notifyDataSetChanged();
}
public void setSelected() {
for (int i = 0; i < mItems.size(); i++) mItems.get(i).setSelect(i == position);
notifyDataSetChanged();
setFocus(true);
}
public void setChannel() {
if (position < 0 || position > mItems.size() - 1) return;
//if (!getGroup().isHidden()) getItem().putKeep();
mListener.onItemClick(getItem());
getGroup().setPosition(position);
getItem().setGroup(getGroup());
setCurrent(getItem());
setSelected();
}
@Override
public int getItemCount() {
return mItems.size();
@ -44,25 +99,12 @@ public class ChannelAdapter extends RecyclerView.Adapter<ChannelAdapter.ViewHold
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new ViewHolder(AdapterLiveChannelBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false));
public ChannelHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new ChannelHolder(this, AdapterLiveChannelBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false));
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
Channel item = mItems.get(position);
holder.binding.name.setText(item.getName());
holder.binding.number.setText(item.getNumber());
holder.binding.icon.setVisibility(item.getVisible());
}
public static class ViewHolder extends RecyclerView.ViewHolder {
private final AdapterLiveChannelBinding binding;
public ViewHolder(@NonNull AdapterLiveChannelBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
public void onBindViewHolder(@NonNull ChannelHolder holder, int position) {
holder.setView(mItems.get(position));
}
}

@ -8,18 +8,48 @@ import androidx.recyclerview.widget.RecyclerView;
import com.fongmi.android.tv.bean.Group;
import com.fongmi.android.tv.databinding.AdapterLiveGroupBinding;
import com.fongmi.android.tv.ui.adapter.holder.GroupHolder;
import java.util.ArrayList;
import java.util.List;
public class GroupAdapter extends RecyclerView.Adapter<GroupAdapter.ViewHolder> {
public class GroupAdapter extends RecyclerView.Adapter<GroupHolder> {
private OnItemClickListener mListener;
private final List<Group> mItems;
private final List<Group> mHides;
private boolean focus;
private int position;
public GroupAdapter() {
public GroupAdapter(OnItemClickListener listener) {
this.mItems = new ArrayList<>();
this.mHides = new ArrayList<>();
this.mListener = listener;
}
public interface OnItemClickListener {
void onItemClick(Group item);
}
private Group getItem() {
return mItems.get(position);
}
public boolean isFocus() {
return focus;
}
public void setFocus(boolean focus) {
this.focus = focus;
}
public int getPosition() {
return position;
}
public void setPosition(int position) {
this.position = position;
}
public void addAll(List<Group> items) {
@ -29,7 +59,17 @@ public class GroupAdapter extends RecyclerView.Adapter<GroupAdapter.ViewHolder>
}
private void addGroup(List<Group> items) {
for (Group item : items) if (item.isHidden()) mHides.add(item);else mItems.add(item);
for (Group item : items) if (item.isHidden()) mHides.add(item); else mItems.add(item);
}
public void setSelected() {
for (int i = 0; i < mItems.size(); i++) mItems.get(i).setSelect(i == position);
notifyDataSetChanged();
setFocus(true);
}
public void setType() {
mListener.onItemClick(getItem());
}
@Override
@ -39,24 +79,12 @@ public class GroupAdapter extends RecyclerView.Adapter<GroupAdapter.ViewHolder>
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new ViewHolder(AdapterLiveGroupBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false));
public GroupHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new GroupHolder(this, AdapterLiveGroupBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false));
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
Group item = mItems.get(position);
holder.binding.name.setText(item.getName());
holder.binding.icon.setVisibility(item.getVisible());
}
public static class ViewHolder extends RecyclerView.ViewHolder {
private final AdapterLiveGroupBinding binding;
public ViewHolder(@NonNull AdapterLiveGroupBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
public void onBindViewHolder(@NonNull GroupHolder holder, int position) {
holder.setView(mItems.get(position));
}
}

@ -0,0 +1,43 @@
package com.fongmi.android.tv.ui.adapter.holder;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.fongmi.android.tv.bean.Channel;
import com.fongmi.android.tv.databinding.AdapterLiveChannelBinding;
import com.fongmi.android.tv.ui.adapter.ChannelAdapter;
public class ChannelHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {
private final AdapterLiveChannelBinding binding;
private final ChannelAdapter adapter;
public ChannelHolder(ChannelAdapter adapter, @NonNull AdapterLiveChannelBinding binding) {
super(binding.getRoot());
this.binding = binding;
this.adapter = adapter;
itemView.setOnClickListener(this);
itemView.setOnLongClickListener(this);
}
@Override
public void onClick(View view) {
adapter.setPosition(getLayoutPosition());
adapter.setChannel();
}
@Override
public boolean onLongClick(View view) {
adapter.setPosition(getLayoutPosition());
return false;
}
public void setView(Channel item) {
itemView.setSelected(item.isSelect());
binding.name.setText(item.getName());
binding.number.setText(item.getNumber());
binding.icon.setVisibility(item.getVisible());
}
}

@ -0,0 +1,35 @@
package com.fongmi.android.tv.ui.adapter.holder;
import android.view.View;
import androidx.recyclerview.widget.RecyclerView;
import com.fongmi.android.tv.bean.Group;
import com.fongmi.android.tv.databinding.AdapterLiveGroupBinding;
import com.fongmi.android.tv.ui.adapter.GroupAdapter;
public class GroupHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private final AdapterLiveGroupBinding binding;
private final GroupAdapter adapter;
public GroupHolder(GroupAdapter adapter, AdapterLiveGroupBinding binding) {
super(binding.getRoot());
this.binding = binding;
this.adapter = adapter;
itemView.setOnClickListener(this);
}
@Override
public void onClick(View view) {
adapter.setPosition(getLayoutPosition());
adapter.setSelected();
adapter.setType();
}
public void setView(Group item) {
itemView.setSelected(item.isSelect());
binding.name.setText(item.getName());
binding.icon.setVisibility(item.getVisible());
}
}

@ -41,8 +41,7 @@ public class CustomHorizontalGridView extends HorizontalGridView {
@Override
public View focusSearch(View focused, int direction) {
if (focused != null) {
FocusFinder finder = FocusFinder.getInstance();
View found = finder.findNextFocus(this, focused, direction);
View found = FocusFinder.getInstance().findNextFocus(this, focused, direction);
if (direction == View.FOCUS_LEFT || direction == View.FOCUS_RIGHT) {
if ((found == null || found.getId() != R.id.text) && getScrollState() == SCROLL_STATE_IDLE) {
focused.clearAnimation();

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/shape_live_channel" android:state_focused="true" />
<item android:drawable="@drawable/shape_live_channel" android:state_selected="true" />
</selector>

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/shape_live_group" android:state_focused="true" />
<item android:drawable="@drawable/shape_live_group" android:state_selected="true" />
</selector>

@ -5,7 +5,6 @@
android:layout_height="wrap_content"
android:background="@drawable/selector_live_channel"
android:focusable="true"
android:focusableInTouchMode="true"
android:gravity="center"
android:orientation="horizontal"
android:paddingStart="20dp"
@ -20,7 +19,7 @@
android:layout_marginEnd="12dp"
android:letterSpacing="0.05"
android:textColor="@color/white"
android:textSize="14sp"
android:textSize="16sp"
android:textStyle="bold"
tools:text="01" />
@ -41,7 +40,7 @@
android:scrollHorizontally="true"
android:singleLine="true"
android:textColor="@color/white"
android:textSize="14sp"
android:textSize="16sp"
android:textStyle="bold"
tools:text="CNN" />

@ -1,12 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/selector_live_group"
android:focusable="true"
android:focusableInTouchMode="true"
android:gravity="center"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingStart="20dp"
android:paddingTop="12dp"
@ -27,7 +26,7 @@
android:letterSpacing="0.05"
android:singleLine="true"
android:textColor="@color/white"
android:textSize="14sp"
android:textSize="16sp"
android:textStyle="bold"
tools:text="收藏" />

@ -33,8 +33,10 @@
<item name="auto_show">false</item>
<item name="resize_mode">fill</item>
<item name="use_artwork">true</item>
<item name="use_controller">false</item>
<item name="animation_enabled">false</item>
<item name="default_artwork">@drawable/radio</item>
<item name="keep_content_on_player_reset">true</item>
</style>
</resources>

@ -41,8 +41,8 @@ public class LiveConfig {
Live live = new Live(item.getName(), new ArrayList<>());
parse(OKHttp.newCall(url).execute().body().string(), live);
if (live.getGroups().size() > 0) getLives().add(live);
} catch (Exception ignored) {
ignored.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}

@ -11,8 +11,10 @@ import com.google.gson.annotations.SerializedName;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
public class Channel {
@ -24,8 +26,8 @@ public class Channel {
private String icon;
@SerializedName("name")
private String name;
@SerializedName("type")
private Group type;
@SerializedName("group")
private Group group;
@SerializedName("ua")
private String ua;
@ -85,12 +87,12 @@ public class Channel {
this.name = name;
}
public Group getType() {
return type;
public Group getGroup() {
return group;
}
public void setType(Group type) {
this.type = type;
public void setGroup(Group group) {
this.group = group;
}
public String getUa() {
@ -117,6 +119,13 @@ public class Channel {
if (!getIcon().isEmpty()) ImgUtil.load(getIcon(), view);
}
public Map<String, String> getHeaders() {
HashMap<String, String> map = new HashMap<>();
if (getUa().isEmpty()) return map;
map.put("User-Agent", getUa());
return map;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;

@ -6,6 +6,7 @@ import android.net.Uri;
import com.fongmi.android.tv.App;
import com.fongmi.android.tv.bean.Result;
import com.fongmi.android.tv.utils.FileUtil;
import com.github.catvod.crawler.SpiderDebug;
import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.PlaybackException;
import com.google.android.exoplayer2.database.DatabaseProvider;
@ -45,6 +46,7 @@ public class ExoUtil {
}
private static MediaSource getSource(Map<String, String> headers, String url, int errorCode, List<MediaItem.SubtitleConfiguration> config) {
SpiderDebug.log(url + "," + headers);
MediaItem.Builder builder = new MediaItem.Builder().setUri(Uri.parse(url));
if (errorCode == PlaybackException.ERROR_CODE_PARSING_MANIFEST_MALFORMED) builder.setMimeType(MimeTypes.APPLICATION_OCTET);
else if (errorCode == PlaybackException.ERROR_CODE_PARSING_CONTAINER_UNSUPPORTED) builder.setMimeType(MimeTypes.APPLICATION_M3U8);

@ -4,6 +4,7 @@ import androidx.annotation.NonNull;
import com.fongmi.android.tv.App;
import com.fongmi.android.tv.R;
import com.fongmi.android.tv.bean.Channel;
import com.fongmi.android.tv.bean.Result;
import com.fongmi.android.tv.event.PlayerEvent;
import com.fongmi.android.tv.utils.Notify;
@ -140,6 +141,10 @@ public class Players implements Player.Listener, AnalyticsListener, ParseTask.Ca
exoPlayer = null;
}
public void start(Channel channel, int index) {
setMediaSource(channel.getHeaders(), channel.getUrls().get(index));
}
public void start(Result result, boolean useParse) {
if (result.getUrl().isEmpty()) {
PlayerEvent.error(R.string.error_play_load);
@ -159,6 +164,7 @@ public class Players implements Player.Listener, AnalyticsListener, ParseTask.Ca
exoPlayer.setMediaSource(ExoUtil.getSource(result, errorCode));
PlayerEvent.state(0);
exoPlayer.prepare();
exoPlayer.play();
errorCode = 0;
}
@ -166,6 +172,7 @@ public class Players implements Player.Listener, AnalyticsListener, ParseTask.Ca
exoPlayer.setMediaSource(ExoUtil.getSource(headers, url, errorCode));
PlayerEvent.state(0);
exoPlayer.prepare();
exoPlayer.play();
errorCode = 0;
}

@ -20,7 +20,6 @@ import androidx.annotation.Nullable;
import com.fongmi.android.tv.api.ApiConfig;
import com.fongmi.android.tv.player.ParseTask;
import com.fongmi.android.tv.utils.Utils;
import com.github.catvod.crawler.SpiderDebug;
import java.io.ByteArrayInputStream;
import java.util.Arrays;
@ -118,7 +117,6 @@ public class CustomWebView extends WebView {
handler.removeCallbacks(mTimer);
handler.post(() -> {
if (callback != null) callback.onParseSuccess(news, url, "");
SpiderDebug.log(url + "," + headers);
stop(false);
});
}

Loading…
Cancel
Save