[mobile] support google cast - part 1

pull/123/head
FongMi 3 years ago
parent 10e2402933
commit fd8f3a0b34
  1. 2
      app/build.gradle
  2. 3
      app/proguard-rules.pro
  3. 14
      app/src/main/java/com/fongmi/android/tv/bean/Device.java
  4. 2
      app/src/main/res/values/strings.xml
  5. 4
      app/src/mobile/AndroidManifest.xml
  6. 58
      app/src/mobile/java/com/fongmi/android/tv/cast/CastDevice.java
  7. 62
      app/src/mobile/java/com/fongmi/android/tv/cast/CastOptionsProvider.java
  8. 8
      app/src/mobile/java/com/fongmi/android/tv/ui/activity/DetailActivity.java
  9. 10
      app/src/mobile/java/com/fongmi/android/tv/ui/adapter/DeviceAdapter.java

@ -114,8 +114,10 @@ dependencies {
implementation('org.simpleframework:simple-xml:2.7.1') { exclude group: 'stax', module: 'stax-api' exclude group: 'xpp3', module: 'xpp3' }
leanbackImplementation 'androidx.leanback:leanback:1.2.0-alpha02'
leanbackImplementation 'me.jessyan:autosize:1.2.1'
mobileImplementation 'androidx.mediarouter:mediarouter:1.3.0'
mobileImplementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
mobileImplementation 'com.github.devin1014.DLNA-Cast:dlna-dmc:V1.0.0'
mobileImplementation 'com.google.android.gms:play-services-cast-framework:21.3.0'
mobileImplementation('com.journeyapps:zxing-android-embedded:4.3.0') { transitive = false }
annotationProcessor 'androidx.room:room-compiler:2.5.1'
annotationProcessor 'com.github.bumptech.glide:compiler:4.14.2'

@ -35,6 +35,9 @@
-keep class com.github.catvod.crawler.** { *; }
-keep class * extends com.github.catvod.crawler.Spider
# Cast
-keep class * implements com.google.android.gms.cast.framework.OptionsProvider
# Cling
-keep class org.fourthline.cling.** { *; }

@ -88,6 +88,10 @@ public class Device {
this.type = type;
}
public boolean isLeanback() {
return getType() == 0;
}
public boolean isMobile() {
return getType() == 1;
}
@ -96,8 +100,16 @@ public class Device {
return getType() == 2;
}
public boolean isCast() {
return getType() == 3;
}
public boolean isApp() {
return isLeanback() || isMobile();
}
public String getHost() {
return isDLNA() ? getUuid() : Uri.parse(getIp()).getHost();
return isDLNA() || isCast() ? getUuid() : Uri.parse(getIp()).getHost();
}
public Device save() {

@ -2,7 +2,7 @@
<!-- App -->
<string name="app_name">TV</string>
<string name="app_id">C0868879</string>
<string name="app_id">08955620</string>
<string name="app_exit">Press back again to exit</string>
<!-- Vod -->

@ -6,6 +6,10 @@
<application>
<meta-data
android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
android:value="com.fongmi.android.tv.cast.CastOptionsProvider" />
<activity
android:name=".ui.activity.MainActivity"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"

@ -1,5 +1,10 @@
package com.fongmi.android.tv.cast;
import android.annotation.SuppressLint;
import androidx.annotation.NonNull;
import androidx.mediarouter.media.MediaRouter;
import org.fourthline.cling.model.meta.Device;
import java.util.ArrayList;
@ -8,6 +13,7 @@ import java.util.List;
public class CastDevice {
private final List<Device<?, ?, ?>> devices;
private final List<MediaRouter.RouteInfo> routers;
private static class Loader {
static volatile CastDevice INSTANCE = new CastDevice();
@ -19,6 +25,7 @@ public class CastDevice {
public CastDevice() {
this.devices = new ArrayList<>();
this.routers = new ArrayList<>();
}
public boolean isEmpty() {
@ -33,16 +40,39 @@ public class CastDevice {
return device;
}
public List<com.fongmi.android.tv.bean.Device> getAll() {
private com.fongmi.android.tv.bean.Device create(MediaRouter.RouteInfo item) {
com.fongmi.android.tv.bean.Device device = new com.fongmi.android.tv.bean.Device();
device.setName(item.getName());
device.setUuid(item.getId());
device.setType(3);
return device;
}
public List<com.fongmi.android.tv.bean.Device> getDLNA() {
List<com.fongmi.android.tv.bean.Device> items = new ArrayList<>();
for (Device<?, ?, ?> device : devices) items.add(create(device));
for (Device<?, ?, ?> item : devices) items.add(create(item));
return items;
}
public List<com.fongmi.android.tv.bean.Device> add(Device<?, ?, ?> device) {
devices.remove(device);
devices.add(device);
return getAll();
public List<com.fongmi.android.tv.bean.Device> getCast() {
List<com.fongmi.android.tv.bean.Device> items = new ArrayList<>();
for (MediaRouter.RouteInfo item : routers) items.add(create(item));
return items;
}
public List<com.fongmi.android.tv.bean.Device> add(Device<?, ?, ?> item) {
devices.remove(item);
devices.add(item);
return getDLNA();
}
@SuppressLint("RestrictedApi")
public List<com.fongmi.android.tv.bean.Device> add(List<MediaRouter.RouteInfo> items) {
ArrayList<MediaRouter.RouteInfo> routes = new ArrayList<>(items);
onFilterRoutes(routes);
routers.clear();
routers.addAll(routes);
return getCast();
}
public com.fongmi.android.tv.bean.Device remove(Device<?, ?, ?> device) {
@ -50,8 +80,22 @@ public class CastDevice {
return create(device);
}
public Device<?, ?, ?> find(com.fongmi.android.tv.bean.Device item) {
public Device<?, ?, ?> findDLNA(com.fongmi.android.tv.bean.Device item) {
for (Device<?, ?, ?> device : devices) if (device.getIdentity().getUdn().getIdentifierString().equals(item.getUuid())) return device;
return null;
}
public MediaRouter.RouteInfo findCast(com.fongmi.android.tv.bean.Device item) {
for (MediaRouter.RouteInfo device : routers) if (device.getId().equals(item.getUuid())) return device;
return null;
}
private void onFilterRoutes(@NonNull List<MediaRouter.RouteInfo> routes) {
for (int i = routes.size(); i-- > 0; ) if (!onFilterRoute(routes.get(i))) routes.remove(i);
}
@SuppressLint("RestrictedApi")
private boolean onFilterRoute(@NonNull MediaRouter.RouteInfo route) {
return !route.isDefaultOrBluetooth() && route.isEnabled();
}
}

@ -0,0 +1,62 @@
package com.fongmi.android.tv.cast;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.fongmi.android.tv.R;
import com.fongmi.android.tv.ui.activity.DetailActivity;
import com.google.android.gms.cast.LaunchOptions;
import com.google.android.gms.cast.MediaMetadata;
import com.google.android.gms.cast.framework.CastOptions;
import com.google.android.gms.cast.framework.OptionsProvider;
import com.google.android.gms.cast.framework.SessionProvider;
import com.google.android.gms.cast.framework.media.CastMediaOptions;
import com.google.android.gms.cast.framework.media.ImageHints;
import com.google.android.gms.cast.framework.media.ImagePicker;
import com.google.android.gms.cast.framework.media.MediaIntentReceiver;
import com.google.android.gms.cast.framework.media.NotificationOptions;
import com.google.android.gms.common.images.WebImage;
import java.util.Arrays;
import java.util.List;
public class CastOptionsProvider implements OptionsProvider {
@NonNull
@Override
public CastOptions getCastOptions(Context context) {
NotificationOptions notificationOptions = new NotificationOptions.Builder().setActions(Arrays.asList(MediaIntentReceiver.ACTION_SKIP_NEXT, MediaIntentReceiver.ACTION_TOGGLE_PLAYBACK, MediaIntentReceiver.ACTION_STOP_CASTING), new int[]{1, 2}).setTargetActivityClassName(DetailActivity.class.getName()).build();
CastMediaOptions mediaOptions = new CastMediaOptions.Builder().setImagePicker(new ImagePickerImpl()).setNotificationOptions(notificationOptions).setExpandedControllerActivityClassName(DetailActivity.class.getName()).build();
LaunchOptions launchOptions = new LaunchOptions.Builder().setAndroidReceiverCompatible(true).build();
return new CastOptions.Builder().setLaunchOptions(launchOptions).setReceiverApplicationId(context.getString(R.string.app_id)).setCastMediaOptions(mediaOptions).build();
}
@Nullable
@Override
public List<SessionProvider> getAdditionalSessionProviders(@NonNull Context context) {
return null;
}
private static class ImagePickerImpl extends ImagePicker {
@Override
public WebImage onPickImage(MediaMetadata mediaMetadata, ImageHints hints) {
int type = hints.getType();
if ((mediaMetadata == null) || !mediaMetadata.hasImages()) {
return null;
}
List<WebImage> images = mediaMetadata.getImages();
if (images.size() == 1) {
return images.get(0);
} else {
if (type == ImagePicker.IMAGE_TYPE_MEDIA_ROUTE_CONTROLLER_DIALOG_BACKGROUND) {
return images.get(0);
} else {
return images.get(1);
}
}
}
}
}

@ -44,18 +44,17 @@ import com.fongmi.android.tv.event.ErrorEvent;
import com.fongmi.android.tv.event.PlayerEvent;
import com.fongmi.android.tv.event.RefreshEvent;
import com.fongmi.android.tv.model.SiteViewModel;
import com.fongmi.android.tv.utils.PiP;
import com.fongmi.android.tv.receiver.PiPReceiver;
import com.fongmi.android.tv.player.ExoUtil;
import com.fongmi.android.tv.player.Players;
import com.fongmi.android.tv.receiver.PiPReceiver;
import com.fongmi.android.tv.ui.adapter.EpisodeAdapter;
import com.fongmi.android.tv.ui.adapter.FlagAdapter;
import com.fongmi.android.tv.ui.adapter.ParseAdapter;
import com.fongmi.android.tv.ui.adapter.SearchAdapter;
import com.fongmi.android.tv.ui.base.BaseActivity;
import com.fongmi.android.tv.ui.base.ViewType;
import com.fongmi.android.tv.ui.custom.CustomKeyDownVod;
import com.fongmi.android.tv.ui.custom.SpaceItemDecoration;
import com.fongmi.android.tv.ui.base.ViewType;
import com.fongmi.android.tv.ui.custom.dialog.CastDialog;
import com.fongmi.android.tv.ui.custom.dialog.ControlDialog;
import com.fongmi.android.tv.ui.custom.dialog.EpisodeGridDialog;
@ -63,10 +62,12 @@ import com.fongmi.android.tv.ui.custom.dialog.EpisodeListDialog;
import com.fongmi.android.tv.ui.custom.dialog.TrackDialog;
import com.fongmi.android.tv.utils.Clock;
import com.fongmi.android.tv.utils.Notify;
import com.fongmi.android.tv.utils.PiP;
import com.fongmi.android.tv.utils.Prefers;
import com.fongmi.android.tv.utils.ResUtil;
import com.fongmi.android.tv.utils.Traffic;
import com.fongmi.android.tv.utils.Utils;
import com.google.android.gms.cast.framework.CastContext;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import com.permissionx.guolindev.PermissionX;
@ -94,6 +95,7 @@ public class DetailActivity extends BaseActivity implements Clock.Callback, Cust
private ExecutorService mExecutor;
private SiteViewModel mViewModel;
private FlagAdapter mFlagAdapter;
private CastContext mCastContext;
private List<Dialog> mDialogs;
private PiPReceiver mReceiver;
private History mHistory;

@ -49,7 +49,7 @@ public class DeviceAdapter extends RecyclerView.Adapter<DeviceAdapter.ViewHolder
public List<String> getIps() {
List<String> ips = new ArrayList<>();
for (Device item : mItems) if (!item.isDLNA()) ips.add(item.getIp());
for (Device item : mItems) if (item.isApp()) ips.add(item.getIp());
return ips;
}
@ -69,8 +69,14 @@ public class DeviceAdapter extends RecyclerView.Adapter<DeviceAdapter.ViewHolder
Device item = mItems.get(position);
holder.binding.name.setText(item.getName());
holder.binding.host.setText(item.getHost());
holder.binding.type.setImageResource(getIcon(item));
holder.binding.getRoot().setOnClickListener(v -> mListener.onItemClick(item));
holder.binding.type.setImageResource(item.isMobile() ? R.drawable.ic_cast_mobile : R.drawable.ic_cast_tv);
}
private int getIcon(Device item) {
if (item.isCast()) return R.drawable.ic_control_cast;
else if (item.isMobile()) return R.drawable.ic_cast_mobile;
return R.drawable.ic_cast_tv;
}
static class ViewHolder extends RecyclerView.ViewHolder {

Loading…
Cancel
Save