mirror of https://github.com/FongMi/TV.git
parent
082033c055
commit
508a7af02b
@ -0,0 +1,57 @@ |
||||
package com.fongmi.android.tv.cast; |
||||
|
||||
import org.fourthline.cling.model.meta.Device; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
public class CastDevice { |
||||
|
||||
private final List<Device<?, ?, ?>> devices; |
||||
|
||||
private static class Loader { |
||||
static volatile CastDevice INSTANCE = new CastDevice(); |
||||
} |
||||
|
||||
public static CastDevice get() { |
||||
return Loader.INSTANCE; |
||||
} |
||||
|
||||
public CastDevice() { |
||||
this.devices = new ArrayList<>(); |
||||
} |
||||
|
||||
public boolean isEmpty() { |
||||
return devices.isEmpty(); |
||||
} |
||||
|
||||
private com.fongmi.android.tv.bean.Device create(Device<?, ?, ?> item) { |
||||
com.fongmi.android.tv.bean.Device device = new com.fongmi.android.tv.bean.Device(); |
||||
device.setUuid(item.getIdentity().getUdn().getIdentifierString()); |
||||
device.setName(item.getDetails().getFriendlyName()); |
||||
device.setType(2); |
||||
return device; |
||||
} |
||||
|
||||
public List<com.fongmi.android.tv.bean.Device> getAll() { |
||||
List<com.fongmi.android.tv.bean.Device> items = new ArrayList<>(); |
||||
for (Device<?, ?, ?> device : devices) items.add(create(device)); |
||||
return items; |
||||
} |
||||
|
||||
public List<com.fongmi.android.tv.bean.Device> add(Device<?, ?, ?> device) { |
||||
devices.remove(device); |
||||
devices.add(device); |
||||
return getAll(); |
||||
} |
||||
|
||||
public com.fongmi.android.tv.bean.Device remove(Device<?, ?, ?> device) { |
||||
devices.remove(device); |
||||
return create(device); |
||||
} |
||||
|
||||
public Device<?, ?, ?> find(com.fongmi.android.tv.bean.Device item) { |
||||
for (Device<?, ?, ?> device : devices) if (device.getIdentity().getUdn().getIdentifierString().equals(item.getUuid())) return device; |
||||
return null; |
||||
} |
||||
} |
||||
@ -0,0 +1,43 @@ |
||||
package com.fongmi.android.tv.cast; |
||||
|
||||
import androidx.annotation.NonNull; |
||||
|
||||
import com.android.cast.dlna.core.ICast; |
||||
import com.fongmi.android.tv.server.Server; |
||||
import com.fongmi.android.tv.utils.FileUtil; |
||||
|
||||
import java.util.UUID; |
||||
|
||||
public class CastVideo implements ICast { |
||||
|
||||
private final String name; |
||||
private final String url; |
||||
|
||||
public static CastVideo get(String name, String url) { |
||||
return new CastVideo(name, url); |
||||
} |
||||
|
||||
private CastVideo(String name, String url) { |
||||
if (url.startsWith("file")) url = Server.get().getAddress() + "/" + url.replace(FileUtil.getRootPath(), ""); |
||||
this.name = name; |
||||
this.url = url; |
||||
} |
||||
|
||||
@NonNull |
||||
@Override |
||||
public String getId() { |
||||
return UUID.randomUUID().toString(); |
||||
} |
||||
|
||||
@NonNull |
||||
@Override |
||||
public String getUri() { |
||||
return url; |
||||
} |
||||
|
||||
@NonNull |
||||
@Override |
||||
public String getName() { |
||||
return name; |
||||
} |
||||
} |
||||
@ -0,0 +1,20 @@ |
||||
package com.fongmi.android.tv.cast; |
||||
|
||||
import org.greenrobot.eventbus.EventBus; |
||||
|
||||
public class ScanEvent { |
||||
|
||||
private final String address; |
||||
|
||||
public static void post(String address) { |
||||
EventBus.getDefault().post(new ScanEvent(address)); |
||||
} |
||||
|
||||
public ScanEvent(String address) { |
||||
this.address = address; |
||||
} |
||||
|
||||
public String getAddress() { |
||||
return address; |
||||
} |
||||
} |
||||
@ -0,0 +1,80 @@ |
||||
package com.fongmi.android.tv.cast; |
||||
|
||||
import com.fongmi.android.tv.App; |
||||
import com.fongmi.android.tv.bean.Device; |
||||
import com.fongmi.android.tv.net.OkHttp; |
||||
import com.fongmi.android.tv.server.Server; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.LinkedHashSet; |
||||
import java.util.List; |
||||
import java.util.concurrent.CountDownLatch; |
||||
|
||||
import okhttp3.OkHttpClient; |
||||
|
||||
public class ScanTask { |
||||
|
||||
private final Listener listener; |
||||
private final OkHttpClient client; |
||||
private final List<Device> devices; |
||||
|
||||
public static ScanTask create(Listener listener) { |
||||
return new ScanTask(listener); |
||||
} |
||||
|
||||
public ScanTask(Listener listener) { |
||||
this.listener = listener; |
||||
this.client = OkHttp.client(1000); |
||||
this.devices = new ArrayList<>(); |
||||
} |
||||
|
||||
public void start(List<String> ips) { |
||||
App.execute(() -> run(getUrl(ips))); |
||||
} |
||||
|
||||
public void start(String url) { |
||||
App.execute(() -> run(List.of(url))); |
||||
} |
||||
|
||||
private void run(List<String> items) { |
||||
try { |
||||
getDevice(items); |
||||
} catch (Exception e) { |
||||
e.printStackTrace(); |
||||
} finally { |
||||
App.post(() -> listener.onFind(devices)); |
||||
} |
||||
} |
||||
|
||||
private void getDevice(List<String> urls) throws Exception { |
||||
CountDownLatch cd = new CountDownLatch(urls.size()); |
||||
for (String url : urls) new Thread(() -> findDevice(cd, url)).start(); |
||||
cd.await(); |
||||
} |
||||
|
||||
private List<String> getUrl(List<String> ips) { |
||||
LinkedHashSet<String> urls = new LinkedHashSet<>(ips); |
||||
String local = Server.get().getAddress(); |
||||
String base = local.substring(0, local.lastIndexOf(".") + 1); |
||||
for (int i = 1; i < 256; i++) urls.add(base + i + ":9978"); |
||||
return new ArrayList<>(urls); |
||||
} |
||||
|
||||
private void findDevice(CountDownLatch cd, String url) { |
||||
try { |
||||
if (url.contains(Server.get().getAddress())) return; |
||||
String result = OkHttp.newCall(client, url.concat("/device")).execute().body().string(); |
||||
Device device = Device.objectFrom(result); |
||||
if (device == null) return; |
||||
devices.add(device.save()); |
||||
} catch (Exception ignored) { |
||||
} finally { |
||||
cd.countDown(); |
||||
} |
||||
} |
||||
|
||||
public interface Listener { |
||||
|
||||
void onFind(List<Device> devices); |
||||
} |
||||
} |
||||
@ -0,0 +1,97 @@ |
||||
package com.fongmi.android.tv.ui.activity; |
||||
|
||||
import android.app.Activity; |
||||
import android.content.Intent; |
||||
import android.content.res.Configuration; |
||||
import android.os.Bundle; |
||||
|
||||
import androidx.annotation.NonNull; |
||||
import androidx.viewbinding.ViewBinding; |
||||
|
||||
import com.fongmi.android.tv.cast.ScanEvent; |
||||
import com.fongmi.android.tv.databinding.ActivityScanBinding; |
||||
import com.fongmi.android.tv.ui.base.BaseActivity; |
||||
import com.fongmi.android.tv.utils.Utils; |
||||
import com.google.zxing.BarcodeFormat; |
||||
import com.google.zxing.ResultPoint; |
||||
import com.journeyapps.barcodescanner.BarcodeCallback; |
||||
import com.journeyapps.barcodescanner.BarcodeResult; |
||||
import com.journeyapps.barcodescanner.CaptureManager; |
||||
import com.journeyapps.barcodescanner.DefaultDecoderFactory; |
||||
|
||||
import java.util.List; |
||||
|
||||
public class ScanActivity extends BaseActivity implements BarcodeCallback { |
||||
|
||||
private ActivityScanBinding mBinding; |
||||
private CaptureManager mCapture; |
||||
|
||||
public static void start(Activity activity) { |
||||
activity.startActivity(new Intent(activity, ScanActivity.class)); |
||||
} |
||||
|
||||
@Override |
||||
protected ViewBinding getBinding() { |
||||
return mBinding = ActivityScanBinding.inflate(getLayoutInflater()); |
||||
} |
||||
|
||||
@Override |
||||
protected void onCreate(Bundle savedInstanceState) { |
||||
super.onCreate(savedInstanceState); |
||||
Utils.hideSystemUI(this); |
||||
} |
||||
|
||||
@Override |
||||
protected void initView(Bundle savedInstanceState) { |
||||
mCapture = new CaptureManager(this, mBinding.scanner); |
||||
mBinding.scanner.getBarcodeView().setDecoderFactory(new DefaultDecoderFactory(List.of(BarcodeFormat.QR_CODE))); |
||||
} |
||||
|
||||
@Override |
||||
public void possibleResultPoints(List<ResultPoint> resultPoints) { |
||||
} |
||||
|
||||
@Override |
||||
public void barcodeResult(BarcodeResult result) { |
||||
if (!result.getText().startsWith("http")) return; |
||||
ScanEvent.post(result.getText()); |
||||
finish(); |
||||
} |
||||
|
||||
@Override |
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { |
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults); |
||||
mCapture.onRequestPermissionsResult(requestCode, permissions, grantResults); |
||||
} |
||||
|
||||
@Override |
||||
public void onConfigurationChanged(@NonNull Configuration newConfig) { |
||||
super.onConfigurationChanged(newConfig); |
||||
Utils.hideSystemUI(this); |
||||
} |
||||
|
||||
@Override |
||||
public void onWindowFocusChanged(boolean hasFocus) { |
||||
super.onWindowFocusChanged(hasFocus); |
||||
if (hasFocus) Utils.hideSystemUI(this); |
||||
} |
||||
|
||||
@Override |
||||
protected void onResume() { |
||||
super.onResume(); |
||||
mCapture.onResume(); |
||||
mBinding.scanner.decodeSingle(this); |
||||
} |
||||
|
||||
@Override |
||||
protected void onPause() { |
||||
super.onPause(); |
||||
mCapture.onPause(); |
||||
} |
||||
|
||||
@Override |
||||
protected void onDestroy() { |
||||
super.onDestroy(); |
||||
mCapture.onDestroy(); |
||||
} |
||||
} |
||||
@ -0,0 +1,85 @@ |
||||
package com.fongmi.android.tv.ui.adapter; |
||||
|
||||
import android.view.LayoutInflater; |
||||
import android.view.ViewGroup; |
||||
|
||||
import androidx.annotation.NonNull; |
||||
import androidx.recyclerview.widget.RecyclerView; |
||||
|
||||
import com.fongmi.android.tv.R; |
||||
import com.fongmi.android.tv.bean.Device; |
||||
import com.fongmi.android.tv.databinding.AdapterDeviceBinding; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
public class DeviceAdapter extends RecyclerView.Adapter<DeviceAdapter.ViewHolder> { |
||||
|
||||
private final OnClickListener mListener; |
||||
private final List<Device> mItems; |
||||
|
||||
public DeviceAdapter(OnClickListener listener) { |
||||
this.mItems = new ArrayList<>(); |
||||
this.mListener = listener; |
||||
} |
||||
|
||||
public interface OnClickListener { |
||||
|
||||
void onItemClick(Device item); |
||||
} |
||||
|
||||
public void addAll(List<Device> items) { |
||||
if (items == null) return; |
||||
mItems.removeAll(items); |
||||
mItems.addAll(items); |
||||
notifyDataSetChanged(); |
||||
} |
||||
|
||||
public void remove(Device item) { |
||||
if (item == null) return; |
||||
mItems.remove(item); |
||||
notifyDataSetChanged(); |
||||
} |
||||
|
||||
public void clear() { |
||||
mItems.clear(); |
||||
Device.delete(); |
||||
notifyDataSetChanged(); |
||||
} |
||||
|
||||
public List<String> getIps() { |
||||
List<String> ips = new ArrayList<>(); |
||||
for (Device item : mItems) if (!item.isDLNA()) ips.add(item.getIp()); |
||||
return ips; |
||||
} |
||||
|
||||
@Override |
||||
public int getItemCount() { |
||||
return mItems.size(); |
||||
} |
||||
|
||||
@NonNull |
||||
@Override |
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { |
||||
return new ViewHolder(AdapterDeviceBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)); |
||||
} |
||||
|
||||
@Override |
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) { |
||||
Device item = mItems.get(position); |
||||
holder.binding.name.setText(item.getName()); |
||||
holder.binding.host.setText(item.getHost()); |
||||
holder.binding.getRoot().setOnClickListener(v -> mListener.onItemClick(item)); |
||||
holder.binding.type.setImageResource(item.isMobile() ? R.drawable.ic_cast_mobile : R.drawable.ic_cast_tv); |
||||
} |
||||
|
||||
static class ViewHolder extends RecyclerView.ViewHolder { |
||||
|
||||
private final AdapterDeviceBinding binding; |
||||
|
||||
ViewHolder(@NonNull AdapterDeviceBinding binding) { |
||||
super(binding.getRoot()); |
||||
this.binding = binding; |
||||
} |
||||
} |
||||
} |
||||
Loading…
Reference in new issue