Compare commits

..

12 Commits

Author SHA1 Message Date
jhengazuki 0a029fd2d8 Clean code 1 month ago
jhengazuki ba90484dbb Clean code 1 month ago
jhengazuki 8c9a9f03a6 Update build.gradle 1 month ago
jhengazuki a84c83e165 Fix crash 1 month ago
jhengazuki 1ce88f6ce7 Clean import 1 month ago
jhengazuki da8df3b31d Support new drm param 1 month ago
jhengazuki aaa5412f7e Optimize search thread 1 month ago
jhengazuki 0d87630444 Adjust collect width 1 month ago
jhengazuki 585b195110 Optimize search thread 1 month ago
jhengazuki 29600c500a Fix bug 1 month ago
jhengazuki 9874e24fe3 Fix logo crash 1 month ago
jhengazuki aab020f5c6 Revert permission 1 month ago
  1. 4
      app/build.gradle
  2. 2
      app/src/leanback/java/com/fongmi/android/tv/ui/activity/CastActivity.java
  3. 19
      app/src/leanback/java/com/fongmi/android/tv/ui/activity/CollectActivity.java
  4. 5
      app/src/leanback/java/com/fongmi/android/tv/ui/activity/HomeActivity.java
  5. 2
      app/src/leanback/java/com/fongmi/android/tv/ui/activity/LiveActivity.java
  6. 7
      app/src/leanback/java/com/fongmi/android/tv/ui/activity/VideoActivity.java
  7. 3
      app/src/main/AndroidManifest.xml
  8. 29
      app/src/main/java/com/fongmi/android/tv/api/LiveParser.java
  9. 120
      app/src/main/java/com/fongmi/android/tv/model/LiveViewModel.java
  10. 37
      app/src/main/java/com/fongmi/android/tv/model/SiteViewModel.java
  11. 15
      app/src/main/java/com/fongmi/android/tv/player/Source.java
  12. 9
      app/src/main/java/com/fongmi/android/tv/utils/ImgUtil.java
  13. 57
      app/src/main/java/com/fongmi/android/tv/utils/PauseExecutor.java
  14. 7
      app/src/main/java/com/fongmi/android/tv/utils/PermissionUtil.java
  15. 2
      app/src/mobile/java/com/fongmi/android/tv/ui/activity/HomeActivity.java
  16. 2
      app/src/mobile/java/com/fongmi/android/tv/ui/activity/LiveActivity.java
  17. 7
      app/src/mobile/java/com/fongmi/android/tv/ui/activity/VideoActivity.java
  18. 22
      app/src/mobile/java/com/fongmi/android/tv/ui/fragment/CollectFragment.java
  19. 6
      app/src/mobile/java/com/fongmi/android/tv/ui/fragment/VodFragment.java

@ -12,8 +12,8 @@ android {
minSdk 24
//noinspection ExpiredTargetSdkVersion
targetSdk 28
versionCode 465
versionName "4.6.5"
versionCode 466
versionName "4.6.6"
javaCompileOptions {
annotationProcessorOptions {
arguments = ["room.schemaLocation": "$projectDir/schemas".toString(), "eventBusIndex": "com.fongmi.android.tv.event.EventIndex"]

@ -549,12 +549,12 @@ public class CastActivity extends BaseActivity implements CustomKeyDownVod.Liste
@Override
protected void onDestroy() {
super.onDestroy();
mClock.release();
mPlayers.release();
unbindService(this);
PlaybackService.stop();
mService.bindRealPlayer(null);
App.removeCallbacks(mR1, mR2);
super.onDestroy();
}
}

@ -30,19 +30,20 @@ import com.fongmi.android.tv.model.SiteViewModel;
import com.fongmi.android.tv.ui.base.BaseActivity;
import com.fongmi.android.tv.ui.fragment.CollectFragment;
import com.fongmi.android.tv.ui.presenter.CollectPresenter;
import com.fongmi.android.tv.utils.PauseExecutor;
import com.fongmi.android.tv.utils.ResUtil;
import com.google.gson.reflect.TypeToken;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CollectActivity extends BaseActivity {
private ActivityCollectBinding mBinding;
private ArrayObjectAdapter mAdapter;
private ExecutorService mExecutor;
private SiteViewModel mViewModel;
private PauseExecutor mExecutor;
private View mOldView;
public static void start(Activity activity, String keyword) {
@ -128,8 +129,8 @@ public class CollectActivity extends BaseActivity {
if (sites.isEmpty()) return;
mAdapter.add(Collect.all());
if (mExecutor != null) stop();
mExecutor = Executors.newCachedThreadPool();
mBinding.pager.getAdapter().notifyDataSetChanged();
mExecutor = new PauseExecutor(10, sites.size());
mBinding.result.setText(getString(R.string.collect_result, getKeyword()));
for (Site site : sites) mExecutor.execute(() -> search(site));
}
@ -170,18 +171,6 @@ public class CollectActivity extends BaseActivity {
}
};
@Override
protected void onResume() {
super.onResume();
if (mExecutor != null) mExecutor.resume();
}
@Override
protected void onPause() {
super.onPause();
if (mExecutor != null) mExecutor.pause();
}
@Override
protected void onBackInvoked() {
super.onBackInvoked();

@ -19,8 +19,6 @@ import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewbinding.ViewBinding;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.target.Target;
import com.fongmi.android.tv.App;
import com.fongmi.android.tv.Product;
import com.fongmi.android.tv.R;
@ -57,6 +55,7 @@ import com.fongmi.android.tv.ui.presenter.ProgressPresenter;
import com.fongmi.android.tv.ui.presenter.VodPresenter;
import com.fongmi.android.tv.utils.Clock;
import com.fongmi.android.tv.utils.FileChooser;
import com.fongmi.android.tv.utils.ImgUtil;
import com.fongmi.android.tv.utils.KeyUtil;
import com.fongmi.android.tv.utils.Notify;
import com.fongmi.android.tv.utils.PermissionUtil;
@ -297,7 +296,7 @@ public class HomeActivity extends BaseActivity implements CustomTitleView.Listen
}
private void setLogo() {
Glide.with(mBinding.logo).load(UrlUtil.convert(VodConfig.get().getConfig().getLogo())).circleCrop().override(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL).error(R.drawable.ic_logo).into(mBinding.logo);
ImgUtil.logo(mBinding.logo);
}
@Subscribe(threadMode = ThreadMode.MAIN)

@ -1053,12 +1053,12 @@ public class LiveActivity extends BaseActivity implements GroupPresenter.OnClick
@Override
protected void onDestroy() {
super.onDestroy();
mPlayers.release();
Source.get().exit();
PlaybackService.stop();
mViewModel.url.removeObserver(mObserveUrl);
mViewModel.epg.removeObserver(mObserveEpg);
App.removeCallbacks(mR0, mR1, mR3, mR3, mR4);
super.onDestroy();
}
}

@ -76,7 +76,6 @@ import com.fongmi.android.tv.utils.FileChooser;
import com.fongmi.android.tv.utils.ImgUtil;
import com.fongmi.android.tv.utils.KeyUtil;
import com.fongmi.android.tv.utils.Notify;
import com.fongmi.android.tv.utils.PermissionUtil;
import com.fongmi.android.tv.utils.ResUtil;
import com.fongmi.android.tv.utils.Sniffer;
import com.fongmi.android.tv.utils.Traffic;
@ -145,7 +144,7 @@ public class VideoActivity extends BaseActivity implements CustomKeyDownVod.List
public static void file(FragmentActivity activity, String path) {
if (TextUtils.isEmpty(path)) return;
String name = new File(path).getName();
PermissionUtil.requestFile(activity, allGranted -> start(activity, "push_agent", "file://" + path, name));
start(activity, "push_agent", "file://" + path, name);
}
public static void cast(Activity activity, History history) {
@ -1184,7 +1183,7 @@ public class VideoActivity extends BaseActivity implements CustomKeyDownVod.List
private void startSearch(String keyword) {
mQuickAdapter.clear();
List<Site> sites = new ArrayList<>();
mExecutor = Executors.newFixedThreadPool(10);
mExecutor = Executors.newCachedThreadPool();
for (Site site : VodConfig.get().getSites()) if (isPass(site)) sites.add(site);
for (Site site : sites) mExecutor.execute(() -> search(site, keyword));
}
@ -1437,7 +1436,6 @@ public class VideoActivity extends BaseActivity implements CustomKeyDownVod.List
@Override
protected void onDestroy() {
super.onDestroy();
stopSearch();
saveHistory();
mClock.release();
@ -1449,5 +1447,6 @@ public class VideoActivity extends BaseActivity implements CustomKeyDownVod.List
mViewModel.result.removeObserver(mObserveDetail);
mViewModel.player.removeObserver(mObservePlayer);
mViewModel.search.removeObserver(mObserveSearch);
super.onDestroy();
}
}

@ -7,8 +7,7 @@
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

@ -112,9 +112,9 @@ public class LiveParser {
unknown.setReplace(extract(line, CATCHUP_REPLACE));
channel.setCatchup(Catchup.decide(unknown, catchup));
} else if (!line.startsWith("#") && line.contains("://")) {
String[] split = line.split("\\|");
if (split.length > 1) setting.headers(Arrays.copyOfRange(split, 1, split.length));
channel.getUrls().add(split[0]);
String[] parts = line.split("\\|");
if (parts.length > 1) setting.headers(Arrays.copyOfRange(parts, 1, parts.length));
channel.getUrls().add(parts[0]);
setting.copy(channel).clear();
}
}
@ -131,9 +131,12 @@ public class LiveParser {
if (line.contains("#genre#")) live.getGroups().add(Group.create(split[0], live.isPass()));
if (split.length > 1 && live.getGroups().isEmpty()) live.getGroups().add(Group.create());
if (split.length > 1 && split[1].contains("://")) {
String[] parts = split[1].split("\\|");
if (parts.length > 1) setting.headers(Arrays.copyOfRange(parts, 1, parts.length));
Group group = live.getGroups().get(live.getGroups().size() - 1);
Channel channel = group.find(Channel.create(split[0]));
channel.addUrls(split[1].split("#"));
if (parts.length > 1) channel.getUrls().add(parts[0]);
else channel.addUrls(split[1].split("#"));
setting.copy(channel);
}
}
@ -299,12 +302,23 @@ public class LiveParser {
private void headers(String[] params) {
if (header == null) header = new HashMap<>();
for (String param : params) {
param = drmCheck(param);
if (!param.contains("=")) continue;
String[] a = param.split("=", 2);
header.put(a[0].trim(), a[1].trim().replace("\"", ""));
String k = a[0].trim().replace("\"", "");
String v = a[1].trim().replace("\"", "");
header.put(k, v);
}
}
private String drmCheck(String text) {
type = find(text, "(^|[?&])drmScheme=([^&]*)");
key = find(text, "(^|[?&])drmLicense=([^&]*)");
if (type == null && key == null) return text;
if (key != null && !key.startsWith("http")) convert();
return text.replaceAll("(^|[?&])drmScheme=[^&]*", "").replaceAll("(^|[?&])drmLicense=[^&]*", "").replaceAll("^[?&]+|[?&]+$", "").replaceAll("&{2,}", "&").trim();
}
private void convert() {
try {
ClearKey.objectFrom(key);
@ -313,6 +327,11 @@ public class LiveParser {
}
}
private String find(String text, String regex) {
Matcher m = Pattern.compile(regex).matcher(text);
return m.find() ? m.group(2) : null;
}
private void clear() {
ua = null;
key = null;

@ -20,48 +20,58 @@ import com.github.catvod.net.OkHttp;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.EnumMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
public class LiveViewModel extends ViewModel {
private static final int LIVE = 0;
private static final int EPG = 1;
private static final int URL = 2;
private static final int XML = 3;
private enum TaskType {
LIVE(Constant.TIMEOUT_LIVE),
EPG(Constant.TIMEOUT_EPG),
XML(Constant.TIMEOUT_XML),
URL(Constant.TIMEOUT_PARSE_LIVE);
final long timeout;
TaskType(long timeout) {
this.timeout = timeout;
}
}
private final List<SimpleDateFormat> formatTime;
private final Map<TaskType, Future<?>> futures;
private final SimpleDateFormat formatDate;
private final ExecutorService executor;
public MutableLiveData<Channel> url;
public MutableLiveData<Boolean> xml;
public MutableLiveData<Live> live;
public MutableLiveData<Epg> epg;
private ExecutorService executor1;
private ExecutorService executor2;
private ExecutorService executor3;
private ExecutorService executor4;
public final MutableLiveData<Channel> url;
public final MutableLiveData<Boolean> xml;
public final MutableLiveData<Live> live;
public final MutableLiveData<Epg> epg;
public LiveViewModel() {
this.live = new MutableLiveData<>();
this.epg = new MutableLiveData<>();
this.url = new MutableLiveData<>();
this.xml = new MutableLiveData<>();
this.url = new MutableLiveData<>();
this.formatTime = new ArrayList<>();
this.futures = new EnumMap<>(TaskType.class);
this.executor = Executors.newFixedThreadPool(4);
this.formatDate = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
this.formatTime.add(new SimpleDateFormat("yyyy-MM-ddHH:mm", Locale.getDefault()));
this.formatTime.add(new SimpleDateFormat("yyyy-MM-ddHH:mm:ss", Locale.getDefault()));
}
public void getLive(Live item) {
execute(LIVE, () -> {
execute(TaskType.LIVE, () -> {
LiveParser.start(item.recent());
setTimeZone(item);
verify(item);
@ -70,7 +80,7 @@ public class LiveViewModel extends ViewModel {
}
public void getXml(Live item) {
execute(XML, () -> {
execute(TaskType.XML, () -> {
boolean result = false;
for (String url : item.getEpgXml()) if (parseXml(item, url)) result = true;
return result;
@ -88,14 +98,14 @@ public class LiveViewModel extends ViewModel {
public void getEpg(Channel item) {
String date = formatDate.format(new Date());
String url = item.getEpg().replace("{date}", date);
execute(EPG, () -> {
execute(TaskType.EPG, () -> {
if (url.startsWith("http") && !item.getData().equal(date)) item.setData(Epg.objectFrom(OkHttp.string(url), item.getTvgId(), formatTime));
return item.getData().selected();
});
}
public void getUrl(Channel item) {
execute(URL, () -> {
execute(TaskType.URL, () -> {
item.setMsg(null);
Source.get().stop();
item.setUrl(Source.get().fetch(item));
@ -104,7 +114,7 @@ public class LiveViewModel extends ViewModel {
}
public void getUrl(Channel item, EpgData data) {
execute(URL, () -> {
execute(TaskType.URL, () -> {
item.setMsg(null);
Source.get().stop();
item.setUrl(item.getCatchup().format(Source.get().fetch(item), data));
@ -122,63 +132,43 @@ public class LiveViewModel extends ViewModel {
}
private void verify(Live item) {
Iterator<Group> iterator = item.getGroups().iterator();
while (iterator.hasNext()) if (iterator.next().isEmpty()) iterator.remove();
item.getGroups().removeIf(Group::isEmpty);
if (item.getGroups().isEmpty() || item.getGroups().get(0).isKeep()) return;
item.getGroups().add(0, Group.create(R.string.keep));
LiveConfig.get().setKeep(item.getGroups());
}
private void execute(int type, Callable<?> callable) {
switch (type) {
case LIVE:
if (executor1 != null) executor1.shutdownNow();
executor1 = Executors.newFixedThreadPool(2);
executor1.execute(runnable(type, callable, executor1));
break;
case EPG:
if (executor2 != null) executor2.shutdownNow();
executor2 = Executors.newFixedThreadPool(2);
executor2.execute(runnable(type, callable, executor2));
break;
case URL:
if (executor3 != null) executor3.shutdownNow();
executor3 = Executors.newFixedThreadPool(2);
executor3.execute(runnable(type, callable, executor3));
break;
case XML:
if (executor4 != null) executor4.shutdownNow();
executor4 = Executors.newFixedThreadPool(2);
executor4.execute(runnable(type, callable, executor4));
break;
}
}
private Runnable runnable(int type, Callable<?> callable, ExecutorService executor) {
return () -> {
private <T> void execute(TaskType type, Callable<T> callable) {
Future<?> oldFuture = futures.get(type);
if (oldFuture != null && !oldFuture.isDone()) oldFuture.cancel(true);
final Future<T> newFuture = executor.submit(callable);
futures.put(type, newFuture);
executor.execute(() -> {
try {
if (Thread.interrupted()) return;
if (type == EPG) epg.postValue((Epg) executor.submit(callable).get(Constant.TIMEOUT_EPG, TimeUnit.MILLISECONDS));
if (type == LIVE) live.postValue((Live) executor.submit(callable).get(Constant.TIMEOUT_LIVE, TimeUnit.MILLISECONDS));
if (type == XML) xml.postValue((Boolean) executor.submit(callable).get(Constant.TIMEOUT_XML, TimeUnit.MILLISECONDS));
if (type == URL) url.postValue((Channel) executor.submit(callable).get(Constant.TIMEOUT_PARSE_LIVE, TimeUnit.MILLISECONDS));
T result = newFuture.get(type.timeout, TimeUnit.MILLISECONDS);
if (newFuture.isCancelled()) return;
if (type == TaskType.EPG) epg.postValue((Epg) result);
else if (type == TaskType.LIVE) live.postValue((Live) result);
else if (type == TaskType.XML) xml.postValue((Boolean) result);
else if (type == TaskType.URL) url.postValue((Channel) result);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (Throwable e) {
if (e instanceof InterruptedException || Thread.interrupted()) return;
if (newFuture.isCancelled()) return;
if (e.getCause() instanceof ExtractException) url.postValue(Channel.error(e.getCause().getMessage()));
else if (type == URL) url.postValue(new Channel());
if (type == LIVE) live.postValue(new Live());
if (type == EPG) epg.postValue(new Epg());
if (type == XML) xml.postValue(false);
else if (type == TaskType.URL) url.postValue(new Channel());
else if (type == TaskType.LIVE) live.postValue(new Live());
else if (type == TaskType.EPG) epg.postValue(new Epg());
else if (type == TaskType.XML) xml.postValue(false);
e.printStackTrace();
}
};
});
}
@Override
protected void onCleared() {
if (executor1 != null) executor1.shutdownNow();
if (executor2 != null) executor2.shutdownNow();
if (executor3 != null) executor3.shutdownNow();
if (executor4 != null) executor4.shutdownNow();
super.onCleared();
for (Future<?> future : futures.values()) if (future != null) future.cancel(true);
if (executor != null) executor.shutdownNow();
}
}
}

@ -34,6 +34,7 @@ import java.util.HashMap;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import okhttp3.Call;
@ -41,14 +42,16 @@ import okhttp3.Response;
public class SiteViewModel extends ViewModel {
private final ExecutorService executor;
public MutableLiveData<Episode> episode;
public MutableLiveData<Result> result;
public MutableLiveData<Result> player;
public MutableLiveData<Result> search;
public MutableLiveData<Result> action;
private ExecutorService executor;
private Future<Result> future;
public SiteViewModel() {
executor = Executors.newFixedThreadPool(2);
episode = new MutableLiveData<>();
result = new MutableLiveData<>();
player = new MutableLiveData<>();
@ -91,11 +94,11 @@ public class SiteViewModel extends ViewModel {
SpiderDebug.log(homeContent);
return Result.fromJson(homeContent);
} else {
Response response = OkHttp.newCall(site.getApi(), site.getHeaders()).execute();
String homeContent = response.body().string();
SpiderDebug.log(homeContent);
response.close();
return fetchPic(site, Result.fromType(site.getType(), homeContent));
try (Response response = OkHttp.newCall(site.getApi(), site.getHeaders()).execute()) {
String homeContent = response.body().string();
SpiderDebug.log(homeContent);
return fetchPic(site, Result.fromType(site.getType(), homeContent));
}
}
});
}
@ -256,10 +259,9 @@ public class SiteViewModel extends ViewModel {
if (!site.getExt().isEmpty()) params.put("extend", site.getExt());
Call get = OkHttp.newCall(site.getApi(), site.getHeaders(), params);
Call post = OkHttp.newCall(site.getApi(), site.getHeaders(), OkHttp.toBody(params));
Response response = (site.getExt().length() <= 1000 ? get : post).execute();
String result = response.body().string();
response.close();
return result;
try (Response response = (site.getExt().length() <= 1000 ? get : post).execute()) {
return response.body().string();
}
}
private Result fetchPic(Site site, Result result) throws Exception {
@ -284,14 +286,17 @@ public class SiteViewModel extends ViewModel {
}
private void execute(MutableLiveData<Result> result, Callable<Result> callable) {
if (executor != null) executor.shutdownNow();
executor = Executors.newFixedThreadPool(2);
if (future != null && !future.isDone()) future.cancel(true);
future = executor.submit(callable);
executor.execute(() -> {
try {
if (Thread.interrupted()) return;
result.postValue(executor.submit(callable).get(Constant.TIMEOUT_VOD, TimeUnit.MILLISECONDS));
Result taskResult = future.get(Constant.TIMEOUT_VOD, TimeUnit.MILLISECONDS);
if (future.isCancelled()) return;
result.postValue(taskResult);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (Throwable e) {
if (e instanceof InterruptedException || Thread.interrupted()) return;
if (future.isCancelled()) return;
if (e.getCause() instanceof ExtractException) result.postValue(Result.error(e.getCause().getMessage()));
else result.postValue(Result.empty());
e.printStackTrace();
@ -301,6 +306,8 @@ public class SiteViewModel extends ViewModel {
@Override
protected void onCleared() {
super.onCleared();
if (future != null) future.cancel(true);
if (executor != null) executor.shutdownNow();
}
}

@ -1,6 +1,5 @@
package com.fongmi.android.tv.player;
import com.fongmi.android.tv.Constant;
import com.fongmi.android.tv.bean.Channel;
import com.fongmi.android.tv.bean.Episode;
import com.fongmi.android.tv.bean.Flag;
@ -65,13 +64,13 @@ public class Source {
}
public void parse(List<Flag> flags) throws Exception {
for (Flag flag : flags) {
ExecutorService executor = Executors.newFixedThreadPool(Constant.THREAD_POOL);
List<Callable<List<Episode>>> items = new ArrayList<>();
Iterator<Episode> iterator = flag.getEpisodes().iterator();
while (iterator.hasNext()) addCallable(iterator, items);
for (Future<List<Episode>> future : executor.invokeAll(items, 30, TimeUnit.SECONDS)) flag.getEpisodes().addAll(future.get());
executor.shutdownNow();
try (ExecutorService executor = Executors.newCachedThreadPool()) {
for (Flag flag : flags) {
List<Callable<List<Episode>>> items = new ArrayList<>();
Iterator<Episode> iterator = flag.getEpisodes().iterator();
while (iterator.hasNext()) addCallable(iterator, items);
for (Future<List<Episode>> future : executor.invokeAll(items, 30, TimeUnit.SECONDS)) flag.getEpisodes().addAll(future.get());
}
}
}

@ -23,6 +23,7 @@ import com.bumptech.glide.request.RequestListener;
import com.bumptech.glide.request.target.Target;
import com.fongmi.android.tv.App;
import com.fongmi.android.tv.R;
import com.fongmi.android.tv.api.config.VodConfig;
import com.fongmi.android.tv.impl.CustomTarget;
import com.github.catvod.utils.Json;
import com.google.common.net.HttpHeaders;
@ -37,6 +38,14 @@ public class ImgUtil {
private static final Set<String> failed = new HashSet<>();
public static void logo(ImageView view) {
try {
Glide.with(view).load(UrlUtil.convert(VodConfig.get().getConfig().getLogo())).circleCrop().override(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL).error(R.drawable.ic_logo).into(view);
} catch (Throwable e) {
e.printStackTrace();
}
}
public static void load(String url, CustomTarget<Bitmap> target) {
try {
Glide.with(App.get()).asBitmap().load(getUrl(url)).override(ResUtil.dp2px(96), ResUtil.dp2px(96)).error(R.drawable.artwork).into(target);

@ -1,57 +0,0 @@
package com.fongmi.android.tv.utils;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class PauseExecutor extends ThreadPoolExecutor {
private final ReentrantLock pauseLock;
private final Condition condition;
private boolean isPaused;
public PauseExecutor(int corePoolSize, int queueCapacity) {
this(corePoolSize, queueCapacity, new ThreadPoolExecutor.AbortPolicy());
}
public PauseExecutor(int corePoolSize, int queueCapacity, RejectedExecutionHandler handler) {
super(corePoolSize, corePoolSize, 0, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(queueCapacity), handler);
pauseLock = new ReentrantLock();
condition = pauseLock.newCondition();
}
@Override
protected void beforeExecute(Thread t, Runnable r) {
super.beforeExecute(t, r);
pauseLock.lock();
try {
while (isPaused) condition.await();
} catch (InterruptedException ie) {
t.interrupt();
} finally {
pauseLock.unlock();
}
}
public void pause() {
pauseLock.lock();
try {
isPaused = true;
} finally {
pauseLock.unlock();
}
}
public void resume() {
pauseLock.lock();
try {
isPaused = false;
condition.signalAll();
} finally {
pauseLock.unlock();
}
}
}

@ -5,7 +5,6 @@ import android.Manifest;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import com.fongmi.android.tv.Setting;
import com.fongmi.android.tv.impl.PermissionCallback;
import com.permissionx.guolindev.PermissionX;
@ -18,13 +17,11 @@ public class PermissionUtil {
}
public static void requestFile(FragmentActivity activity, Consumer<Boolean> callback) {
if (Setting.hasFileManager()) PermissionX.init(activity).permissions().requestManageExternalStoragePermissionNow(new PermissionCallback(callback));
else PermissionX.init(activity).permissions(Manifest.permission.WRITE_EXTERNAL_STORAGE).request(new PermissionCallback(callback));
PermissionX.init(activity).permissions(Manifest.permission.WRITE_EXTERNAL_STORAGE).request(new PermissionCallback(callback));
}
public static void requestFile(Fragment fragment, Consumer<Boolean> callback) {
if (Setting.hasFileManager()) PermissionX.init(fragment).permissions().requestManageExternalStoragePermissionNow(new PermissionCallback(callback));
else PermissionX.init(fragment).permissions(Manifest.permission.WRITE_EXTERNAL_STORAGE).request(new PermissionCallback(callback));
PermissionX.init(fragment).permissions(Manifest.permission.WRITE_EXTERNAL_STORAGE).request(new PermissionCallback(callback));
}
public static void requestNotify(FragmentActivity activity) {

@ -223,8 +223,8 @@ public class HomeActivity extends BaseActivity implements NavigationBarView.OnIt
VodConfig.get().clear();
OkHttp.get().clear();
AppDatabase.backup();
Source.get().exit();
Server.get().stop();
Source.get().exit();
super.onDestroy();
}
}

@ -1158,12 +1158,12 @@ public class LiveActivity extends BaseActivity implements CustomKeyDown.Listener
@Override
protected void onDestroy() {
super.onDestroy();
mPlayers.release();
Source.get().exit();
PlaybackService.stop();
App.removeCallbacks(mR1, mR2, mR3);
mViewModel.url.removeObserver(mObserveUrl);
mViewModel.epg.removeObserver(mObserveEpg);
super.onDestroy();
}
}

@ -90,7 +90,6 @@ import com.fongmi.android.tv.utils.Clock;
import com.fongmi.android.tv.utils.FileChooser;
import com.fongmi.android.tv.utils.ImgUtil;
import com.fongmi.android.tv.utils.Notify;
import com.fongmi.android.tv.utils.PermissionUtil;
import com.fongmi.android.tv.utils.PiP;
import com.fongmi.android.tv.utils.ResUtil;
import com.fongmi.android.tv.utils.Sniffer;
@ -160,7 +159,7 @@ public class VideoActivity extends BaseActivity implements Clock.Callback, Custo
public static void file(FragmentActivity activity, String path) {
if (TextUtils.isEmpty(path)) return;
String name = new File(path).getName();
PermissionUtil.requestFile(activity, allGranted -> start(activity, "push_agent", "file://" + path, name));
start(activity, "push_agent", "file://" + path, name);
}
public static void cast(Activity activity, History history) {
@ -1324,7 +1323,7 @@ public class VideoActivity extends BaseActivity implements Clock.Callback, Custo
private void startSearch(String keyword) {
mQuickAdapter.clear();
List<Site> sites = new ArrayList<>();
mExecutor = Executors.newFixedThreadPool(20);
mExecutor = Executors.newCachedThreadPool();
for (Site item : VodConfig.get().getSites()) if (isPass(item)) sites.add(item);
for (Site site : sites) mExecutor.execute(() -> search(site, keyword));
}
@ -1682,7 +1681,6 @@ public class VideoActivity extends BaseActivity implements Clock.Callback, Custo
@Override
protected void onDestroy() {
super.onDestroy();
stopSearch();
saveHistory();
mClock.release();
@ -1695,5 +1693,6 @@ public class VideoActivity extends BaseActivity implements Clock.Callback, Custo
mViewModel.result.removeObserver(mObserveDetail);
mViewModel.player.removeObserver(mObservePlayer);
mViewModel.search.removeObserver(mObserveSearch);
super.onDestroy();
}
}

@ -29,20 +29,21 @@ import com.fongmi.android.tv.ui.adapter.CollectAdapter;
import com.fongmi.android.tv.ui.adapter.SearchAdapter;
import com.fongmi.android.tv.ui.base.BaseFragment;
import com.fongmi.android.tv.ui.custom.CustomScroller;
import com.fongmi.android.tv.utils.PauseExecutor;
import com.fongmi.android.tv.utils.ResUtil;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CollectFragment extends BaseFragment implements MenuProvider, CollectAdapter.OnClickListener, SearchAdapter.OnClickListener, CustomScroller.Callback {
private FragmentCollectBinding mBinding;
private CollectAdapter mCollectAdapter;
private SearchAdapter mSearchAdapter;
private ExecutorService mExecutor;
private CustomScroller mScroller;
private SiteViewModel mViewModel;
private PauseExecutor mExecutor;
private List<Site> sites;
public static CollectFragment newInstance(String keyword) {
@ -106,7 +107,7 @@ public class CollectFragment extends BaseFragment implements MenuProvider, Colle
private void setWidth() {
int width = 0;
int space = ResUtil.dp2px(48);
int maxWidth = ResUtil.getScreenWidth() / (getCount() + 1) - ResUtil.dp2px(32);
int maxWidth = ResUtil.getScreenWidth() / (getCount() + 1) - ResUtil.dp2px(40);
for (Site site : sites) width = Math.max(width, ResUtil.getTextWidth(site.getName(), 14));
int contentWidth = width + space;
int minWidth = ResUtil.dp2px(120);
@ -118,8 +119,7 @@ public class CollectFragment extends BaseFragment implements MenuProvider, Colle
private void search() {
if (sites.isEmpty()) return;
if (mExecutor != null) mExecutor.shutdownNow();
mExecutor = new PauseExecutor(20, sites.size());
mExecutor = Executors.newCachedThreadPool();
mCollectAdapter.setItems(List.of(Collect.all()), () -> {
for (Site site : sites) mExecutor.execute(() -> search(site, getKeyword()));
});
@ -185,18 +185,6 @@ public class CollectFragment extends BaseFragment implements MenuProvider, Colle
return true;
}
@Override
public void onResume() {
super.onResume();
if (mExecutor != null) mExecutor.resume();
}
@Override
public void onPause() {
super.onPause();
if (mExecutor != null) mExecutor.pause();
}
@Override
public void onHiddenChanged(boolean hidden) {
if (hidden) requireActivity().removeMenuProvider(this);

@ -18,8 +18,6 @@ import androidx.lifecycle.ViewModelProvider;
import androidx.viewbinding.ViewBinding;
import androidx.viewpager.widget.ViewPager;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.target.Target;
import com.fongmi.android.tv.R;
import com.fongmi.android.tv.api.config.VodConfig;
import com.fongmi.android.tv.bean.Class;
@ -48,9 +46,9 @@ import com.fongmi.android.tv.ui.dialog.LinkDialog;
import com.fongmi.android.tv.ui.dialog.ReceiveDialog;
import com.fongmi.android.tv.ui.dialog.SiteDialog;
import com.fongmi.android.tv.utils.FileChooser;
import com.fongmi.android.tv.utils.ImgUtil;
import com.fongmi.android.tv.utils.Notify;
import com.fongmi.android.tv.utils.ResUtil;
import com.fongmi.android.tv.utils.UrlUtil;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
@ -215,7 +213,7 @@ public class VodFragment extends BaseFragment implements ConfigCallback, SiteCal
}
private void setLogo() {
Glide.with(mBinding.logo).load(UrlUtil.convert(VodConfig.get().getConfig().getLogo())).circleCrop().override(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL).error(R.drawable.ic_logo).into(mBinding.logo);
ImgUtil.logo(mBinding.logo);
}
@Subscribe(threadMode = ThreadMode.MAIN)

Loading…
Cancel
Save