2017-09-03 08:04:18 +02:00
|
|
|
package org.schabi.newpipe.fragments.list.channel;
|
|
|
|
|
|
|
|
import android.content.Context;
|
|
|
|
import android.os.Bundle;
|
|
|
|
import android.text.TextUtils;
|
|
|
|
import android.util.Log;
|
2020-04-07 15:39:06 +02:00
|
|
|
import android.util.TypedValue;
|
2017-09-03 08:04:18 +02:00
|
|
|
import android.view.LayoutInflater;
|
|
|
|
import android.view.Menu;
|
|
|
|
import android.view.MenuInflater;
|
|
|
|
import android.view.MenuItem;
|
|
|
|
import android.view.View;
|
|
|
|
import android.view.ViewGroup;
|
|
|
|
import android.widget.Button;
|
|
|
|
|
2020-03-31 19:20:15 +02:00
|
|
|
import androidx.annotation.NonNull;
|
|
|
|
import androidx.annotation.Nullable;
|
|
|
|
import androidx.appcompat.app.ActionBar;
|
|
|
|
import androidx.core.content.ContextCompat;
|
2020-11-03 06:19:19 +01:00
|
|
|
import androidx.viewbinding.ViewBinding;
|
2020-03-31 19:20:15 +02:00
|
|
|
|
2020-10-31 21:55:45 +01:00
|
|
|
import com.jakewharton.rxbinding4.view.RxView;
|
2017-09-03 08:04:18 +02:00
|
|
|
|
|
|
|
import org.schabi.newpipe.R;
|
|
|
|
import org.schabi.newpipe.database.subscription.SubscriptionEntity;
|
2020-11-03 06:19:19 +01:00
|
|
|
import org.schabi.newpipe.databinding.ChannelHeaderBinding;
|
2020-11-03 06:26:29 +01:00
|
|
|
import org.schabi.newpipe.databinding.FragmentChannelBinding;
|
2020-11-03 06:19:19 +01:00
|
|
|
import org.schabi.newpipe.databinding.PlaylistControlBinding;
|
2020-12-11 14:55:47 +01:00
|
|
|
import org.schabi.newpipe.error.ErrorInfo;
|
2021-12-01 09:43:24 +01:00
|
|
|
import org.schabi.newpipe.error.ErrorUtil;
|
2020-12-11 14:55:47 +01:00
|
|
|
import org.schabi.newpipe.error.UserAction;
|
2018-03-18 16:37:49 +01:00
|
|
|
import org.schabi.newpipe.extractor.InfoItem;
|
2017-09-03 08:04:18 +02:00
|
|
|
import org.schabi.newpipe.extractor.ListExtractor;
|
|
|
|
import org.schabi.newpipe.extractor.channel.ChannelInfo;
|
2020-04-07 15:39:06 +02:00
|
|
|
import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException;
|
2017-11-11 23:47:34 +01:00
|
|
|
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
2017-09-03 08:04:18 +02:00
|
|
|
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
|
2021-01-16 04:32:01 +01:00
|
|
|
import org.schabi.newpipe.ktx.AnimationType;
|
2019-04-28 22:43:54 +02:00
|
|
|
import org.schabi.newpipe.local.subscription.SubscriptionManager;
|
Add play next to long press menu & refactor enqueue methods (#6872)
* added mvp play next button in long press menu; new intent handling, new long press dialog entry, new dialog functions, new strings
* changed line length for checkstyle pass
* cleaned comments, moved strings
* Update app/src/main/res/values/strings.xml
to make long press entry more descriptive
Co-authored-by: opusforlife2 <53176348+opusforlife2@users.noreply.github.com>
* Update app/src/main/res/values/strings.xml
Co-authored-by: Stypox <stypox@pm.me>
* replace redundant nextOnVideoPlayer methods
Co-authored-by: Stypox <stypox@pm.me>
* add enqueueNextOnPlayer and enqueueOnPlayer without selectOnAppend and RESUME_PLAYBACK/ deprecate enqueueNextOn*Player and enqueueOn*Player methods
add getPlayerIntent, getPlayerEnqueueIntent and getPlayerEnqueueNextIntent without selectOnAppend and RESUME_PLAYBACK/ deprecate those with
add section comments
* removed deprecated methods
removed redundant methods
* removed deprecated methods
removed redundant methods
* replaced APPEND_ONLY, removed SELECT_ON_APPEND / replaced remaining enqueueOn*Player methods
* now works with playlists
* renamed dialog entry
* checking for >1 items in the queue using the PlayerHolder
* making enqueue*OnPlayer safe to call when no video is playing (defaulting to audio)
* corrected strings
* improve getQueueSize in PlayerHolder
* long press to enqueue only if queue isnt empty
* add Whitespace
Co-authored-by: Stypox <stypox@pm.me>
* clarify comments / add spaces
* PlayerType as parameter of the enqueueOnPlayer method
add Helper method
* using the helper function everywhere (except for the background and popup long-press actions (also on playlists, history, ...)), so basically nowhere
/ passing checkstyle
* assimilated the enqueue*OnPlayer methods
* removed redundant comment, variable
* simplify code line
Co-authored-by: Stypox <stypox@pm.me>
* move if
* replace workaround for isPlayerOpen()
Co-authored-by: Stypox <stypox@pm.me>
* replaced workarounds (getType), corrected static access with getInstance
* remove unused imports
* changed method call to original, new method doesnt exist yet.
* Use getter method instead of property access syntax.
* improve conditional for play next entry
Co-authored-by: Stypox <stypox@pm.me>
* show play next btn in feed fragment
Co-authored-by: Stypox <stypox@pm.me>
* add play next to local playlist and statistics fragment
Co-authored-by: Stypox <stypox@pm.me>
* formating
Co-authored-by: Stypox <stypox@pm.me>
* correcting logic
Co-authored-by: Stypox <stypox@pm.me>
* remove 2 year old unused string, formating
Co-authored-by: Stypox <stypox@pm.me>
* correct enqueue (next) conditionals, default to background if no player is open. Dont generally default to background play.
* remove player open checks from button long press enqueue actions
* improve log msg
* Rename next to enqueue_next
* Refactor kotlin
Co-authored-by: opusforlife2 <53176348+opusforlife2@users.noreply.github.com>
Co-authored-by: Stypox <stypox@pm.me>
2021-09-18 11:22:49 +02:00
|
|
|
import org.schabi.newpipe.player.MainPlayer.PlayerType;
|
2018-04-21 23:10:01 +02:00
|
|
|
import org.schabi.newpipe.player.playqueue.ChannelPlayQueue;
|
|
|
|
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
2017-09-03 08:04:18 +02:00
|
|
|
import org.schabi.newpipe.util.ExtractorHelper;
|
|
|
|
import org.schabi.newpipe.util.Localization;
|
2017-11-04 19:30:01 +01:00
|
|
|
import org.schabi.newpipe.util.NavigationHelper;
|
2021-03-26 13:28:11 +01:00
|
|
|
import org.schabi.newpipe.util.external_communication.ShareUtils;
|
2021-03-27 14:37:44 +01:00
|
|
|
import org.schabi.newpipe.util.PicassoHelper;
|
2020-05-11 19:29:34 +02:00
|
|
|
import org.schabi.newpipe.util.ThemeHelper;
|
2017-09-03 08:04:18 +02:00
|
|
|
|
2018-03-18 16:37:49 +01:00
|
|
|
import java.util.ArrayList;
|
2017-09-03 08:04:18 +02:00
|
|
|
import java.util.List;
|
|
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
|
2020-10-31 21:55:45 +01:00
|
|
|
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
|
|
|
import io.reactivex.rxjava3.core.Observable;
|
|
|
|
import io.reactivex.rxjava3.core.Single;
|
|
|
|
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
|
|
|
import io.reactivex.rxjava3.disposables.Disposable;
|
|
|
|
import io.reactivex.rxjava3.functions.Action;
|
|
|
|
import io.reactivex.rxjava3.functions.Consumer;
|
|
|
|
import io.reactivex.rxjava3.functions.Function;
|
|
|
|
import io.reactivex.rxjava3.schedulers.Schedulers;
|
2017-09-03 08:04:18 +02:00
|
|
|
|
2021-01-16 04:32:01 +01:00
|
|
|
import static org.schabi.newpipe.ktx.TextViewUtils.animateTextColor;
|
|
|
|
import static org.schabi.newpipe.ktx.ViewUtils.animate;
|
|
|
|
import static org.schabi.newpipe.ktx.ViewUtils.animateBackgroundColor;
|
2017-09-03 08:04:18 +02:00
|
|
|
|
2020-04-13 22:40:58 +02:00
|
|
|
public class ChannelFragment extends BaseListInfoFragment<ChannelInfo>
|
|
|
|
implements View.OnClickListener {
|
2021-03-27 14:37:44 +01:00
|
|
|
|
2020-03-31 19:20:15 +02:00
|
|
|
private static final int BUTTON_DEBOUNCE_INTERVAL = 100;
|
2021-03-27 14:37:44 +01:00
|
|
|
private static final String PICASSO_CHANNEL_TAG = "PICASSO_CHANNEL_TAG";
|
|
|
|
|
2018-08-28 20:02:25 +02:00
|
|
|
private final CompositeDisposable disposables = new CompositeDisposable();
|
2017-09-03 08:04:18 +02:00
|
|
|
private Disposable subscribeButtonMonitor;
|
|
|
|
|
|
|
|
/*//////////////////////////////////////////////////////////////////////////
|
|
|
|
// Views
|
|
|
|
//////////////////////////////////////////////////////////////////////////*/
|
|
|
|
|
2020-03-31 19:20:15 +02:00
|
|
|
private SubscriptionManager subscriptionManager;
|
2020-11-03 06:19:19 +01:00
|
|
|
|
2020-11-03 06:26:29 +01:00
|
|
|
private FragmentChannelBinding channelBinding;
|
2020-11-03 06:19:19 +01:00
|
|
|
private ChannelHeaderBinding headerBinding;
|
|
|
|
private PlaylistControlBinding playlistControlBinding;
|
|
|
|
|
2017-09-03 08:04:18 +02:00
|
|
|
private MenuItem menuRssButton;
|
|
|
|
|
2020-03-31 19:20:15 +02:00
|
|
|
public static ChannelFragment getInstance(final int serviceId, final String url,
|
|
|
|
final String name) {
|
2020-08-16 10:24:58 +02:00
|
|
|
final ChannelFragment instance = new ChannelFragment();
|
2018-07-10 16:26:42 +02:00
|
|
|
instance.setInitialData(serviceId, url, name);
|
2017-09-03 08:04:18 +02:00
|
|
|
return instance;
|
|
|
|
}
|
|
|
|
|
2020-12-11 14:55:47 +01:00
|
|
|
public ChannelFragment() {
|
|
|
|
super(UserAction.REQUESTED_CHANNEL);
|
|
|
|
}
|
|
|
|
|
2017-10-26 20:45:16 +02:00
|
|
|
@Override
|
2021-10-16 21:33:45 +02:00
|
|
|
public void onResume() {
|
|
|
|
super.onResume();
|
|
|
|
if (activity != null && useAsFrontPage) {
|
2018-01-30 08:01:57 +01:00
|
|
|
setTitle(currentInfo != null ? currentInfo.getName() : name);
|
2017-10-26 20:45:16 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-02 13:51:10 +02:00
|
|
|
/*//////////////////////////////////////////////////////////////////////////
|
|
|
|
// LifeCycle
|
|
|
|
//////////////////////////////////////////////////////////////////////////*/
|
|
|
|
|
2017-09-03 08:04:18 +02:00
|
|
|
@Override
|
2020-11-01 13:55:20 +01:00
|
|
|
public void onAttach(@NonNull final Context context) {
|
2017-09-03 08:04:18 +02:00
|
|
|
super.onAttach(context);
|
2019-04-28 22:43:54 +02:00
|
|
|
subscriptionManager = new SubscriptionManager(activity);
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-03-31 19:20:15 +02:00
|
|
|
public View onCreateView(@NonNull final LayoutInflater inflater,
|
|
|
|
@Nullable final ViewGroup container,
|
|
|
|
@Nullable final Bundle savedInstanceState) {
|
2017-11-01 16:26:44 +01:00
|
|
|
return inflater.inflate(R.layout.fragment_channel, container, false);
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
|
|
|
|
2020-04-07 15:39:06 +02:00
|
|
|
@Override
|
2020-11-01 13:55:20 +01:00
|
|
|
public void onViewCreated(@NonNull final View rootView, final Bundle savedInstanceState) {
|
2020-04-07 15:39:06 +02:00
|
|
|
super.onViewCreated(rootView, savedInstanceState);
|
2020-11-03 06:26:29 +01:00
|
|
|
channelBinding = FragmentChannelBinding.bind(rootView);
|
2020-04-07 15:39:06 +02:00
|
|
|
}
|
|
|
|
|
2017-09-03 08:04:18 +02:00
|
|
|
@Override
|
|
|
|
public void onDestroy() {
|
|
|
|
super.onDestroy();
|
2020-11-03 06:19:19 +01:00
|
|
|
disposables.clear();
|
2020-03-31 19:20:15 +02:00
|
|
|
if (subscribeButtonMonitor != null) {
|
|
|
|
subscribeButtonMonitor.dispose();
|
|
|
|
}
|
2020-11-03 06:26:29 +01:00
|
|
|
channelBinding = null;
|
2020-11-03 06:19:19 +01:00
|
|
|
headerBinding = null;
|
|
|
|
playlistControlBinding = null;
|
|
|
|
}
|
|
|
|
|
2017-09-03 08:04:18 +02:00
|
|
|
/*//////////////////////////////////////////////////////////////////////////
|
|
|
|
// Init
|
|
|
|
//////////////////////////////////////////////////////////////////////////*/
|
|
|
|
|
2020-11-03 06:19:19 +01:00
|
|
|
@Override
|
|
|
|
protected ViewBinding getListHeader() {
|
|
|
|
headerBinding = ChannelHeaderBinding
|
|
|
|
.inflate(activity.getLayoutInflater(), itemsList, false);
|
|
|
|
playlistControlBinding = headerBinding.playlistControl;
|
|
|
|
|
|
|
|
return headerBinding;
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
|
|
|
|
2020-04-13 22:40:58 +02:00
|
|
|
@Override
|
|
|
|
protected void initListeners() {
|
2020-04-16 17:35:42 +02:00
|
|
|
super.initListeners();
|
|
|
|
|
2020-11-03 06:19:19 +01:00
|
|
|
headerBinding.subChannelTitleView.setOnClickListener(this);
|
|
|
|
headerBinding.subChannelAvatarView.setOnClickListener(this);
|
2020-04-13 22:40:58 +02:00
|
|
|
}
|
|
|
|
|
2017-09-03 08:04:18 +02:00
|
|
|
/*//////////////////////////////////////////////////////////////////////////
|
|
|
|
// Menu
|
|
|
|
//////////////////////////////////////////////////////////////////////////*/
|
|
|
|
|
|
|
|
@Override
|
2021-06-20 00:29:18 +02:00
|
|
|
public void onCreateOptionsMenu(@NonNull final Menu menu,
|
|
|
|
@NonNull final MenuInflater inflater) {
|
2017-09-03 08:04:18 +02:00
|
|
|
super.onCreateOptionsMenu(menu, inflater);
|
2020-08-16 10:24:58 +02:00
|
|
|
final ActionBar supportActionBar = activity.getSupportActionBar();
|
2019-11-23 20:04:40 +01:00
|
|
|
if (useAsFrontPage && supportActionBar != null) {
|
2017-09-26 18:16:39 +02:00
|
|
|
supportActionBar.setDisplayHomeAsUpEnabled(false);
|
|
|
|
} else {
|
|
|
|
inflater.inflate(R.menu.menu_channel, menu);
|
|
|
|
|
2020-03-31 19:20:15 +02:00
|
|
|
if (DEBUG) {
|
|
|
|
Log.d(TAG, "onCreateOptionsMenu() called with: "
|
|
|
|
+ "menu = [" + menu + "], inflater = [" + inflater + "]");
|
|
|
|
}
|
2017-09-26 18:16:39 +02:00
|
|
|
menuRssButton = menu.findItem(R.id.menu_item_rss);
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-03-31 19:20:15 +02:00
|
|
|
public boolean onOptionsItemSelected(final MenuItem item) {
|
2017-09-03 08:04:18 +02:00
|
|
|
switch (item.getItemId()) {
|
2020-02-22 10:36:10 +01:00
|
|
|
case R.id.action_settings:
|
|
|
|
NavigationHelper.openSettings(requireContext());
|
|
|
|
break;
|
2017-11-01 16:26:44 +01:00
|
|
|
case R.id.menu_item_rss:
|
2022-01-15 23:55:19 +01:00
|
|
|
if (currentInfo != null) {
|
|
|
|
ShareUtils.openUrlInBrowser(
|
|
|
|
requireContext(), currentInfo.getFeedUrl(), false);
|
|
|
|
}
|
2017-11-01 16:26:44 +01:00
|
|
|
break;
|
|
|
|
case R.id.menu_item_openInBrowser:
|
2019-12-02 21:34:52 +01:00
|
|
|
if (currentInfo != null) {
|
2020-02-22 10:36:10 +01:00
|
|
|
ShareUtils.openUrlInBrowser(requireContext(), currentInfo.getOriginalUrl());
|
2019-12-02 21:34:52 +01:00
|
|
|
}
|
2017-11-01 16:26:44 +01:00
|
|
|
break;
|
2018-01-29 08:01:06 +01:00
|
|
|
case R.id.menu_item_share:
|
2019-12-02 21:34:52 +01:00
|
|
|
if (currentInfo != null) {
|
2021-03-20 16:35:14 +01:00
|
|
|
ShareUtils.shareText(requireContext(), name, currentInfo.getOriginalUrl(),
|
|
|
|
currentInfo.getAvatarUrl());
|
2019-12-02 21:34:52 +01:00
|
|
|
}
|
2017-11-01 16:26:44 +01:00
|
|
|
break;
|
2017-09-03 08:04:18 +02:00
|
|
|
default:
|
|
|
|
return super.onOptionsItemSelected(item);
|
|
|
|
}
|
2017-11-01 16:26:44 +01:00
|
|
|
return true;
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*//////////////////////////////////////////////////////////////////////////
|
|
|
|
// Channel Subscription
|
|
|
|
//////////////////////////////////////////////////////////////////////////*/
|
|
|
|
|
|
|
|
private void monitorSubscription(final ChannelInfo info) {
|
2018-08-22 16:04:32 +02:00
|
|
|
final Consumer<Throwable> onError = (Throwable throwable) -> {
|
2021-01-16 04:32:01 +01:00
|
|
|
animate(headerBinding.channelSubscribeButton, false, 100);
|
2020-12-11 14:55:47 +01:00
|
|
|
showSnackBarError(new ErrorInfo(throwable, UserAction.SUBSCRIPTION_GET,
|
|
|
|
"Get subscription status", currentInfo));
|
2017-09-03 08:04:18 +02:00
|
|
|
};
|
|
|
|
|
2020-03-31 19:20:15 +02:00
|
|
|
final Observable<List<SubscriptionEntity>> observable = subscriptionManager
|
|
|
|
.subscriptionTable()
|
2019-04-28 22:43:54 +02:00
|
|
|
.getSubscriptionFlowable(info.getServiceId(), info.getUrl())
|
2017-09-03 08:04:18 +02:00
|
|
|
.toObservable();
|
|
|
|
|
|
|
|
disposables.add(observable
|
|
|
|
.observeOn(AndroidSchedulers.mainThread())
|
|
|
|
.subscribe(getSubscribeUpdateMonitor(info), onError));
|
|
|
|
|
|
|
|
disposables.add(observable
|
2020-03-31 19:20:15 +02:00
|
|
|
// Some updates are very rapid
|
|
|
|
// (for example when calling the updateSubscription(info))
|
|
|
|
// so only update the UI for the latest emission
|
|
|
|
// ("sync" the subscribe button's state)
|
2017-09-03 08:04:18 +02:00
|
|
|
.debounce(100, TimeUnit.MILLISECONDS)
|
|
|
|
.observeOn(AndroidSchedulers.mainThread())
|
2018-08-22 16:04:32 +02:00
|
|
|
.subscribe((List<SubscriptionEntity> subscriptionEntities) ->
|
2020-04-07 15:39:06 +02:00
|
|
|
updateSubscribeButton(!subscriptionEntities.isEmpty()), onError));
|
2017-09-03 08:04:18 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-03-31 19:20:15 +02:00
|
|
|
private Function<Object, Object> mapOnSubscribe(final SubscriptionEntity subscription,
|
|
|
|
final ChannelInfo info) {
|
2018-08-22 16:04:32 +02:00
|
|
|
return (@NonNull Object o) -> {
|
2019-04-28 22:43:54 +02:00
|
|
|
subscriptionManager.insertSubscription(subscription, info);
|
2018-08-22 16:04:32 +02:00
|
|
|
return o;
|
2017-09-03 08:04:18 +02:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
private Function<Object, Object> mapOnUnsubscribe(final SubscriptionEntity subscription) {
|
2018-08-22 16:04:32 +02:00
|
|
|
return (@NonNull Object o) -> {
|
2019-04-28 22:43:54 +02:00
|
|
|
subscriptionManager.deleteSubscription(subscription);
|
2018-08-22 16:04:32 +02:00
|
|
|
return o;
|
2017-09-03 08:04:18 +02:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
private void updateSubscription(final ChannelInfo info) {
|
2020-03-31 19:20:15 +02:00
|
|
|
if (DEBUG) {
|
|
|
|
Log.d(TAG, "updateSubscription() called with: info = [" + info + "]");
|
|
|
|
}
|
2018-08-22 16:04:32 +02:00
|
|
|
final Action onComplete = () -> {
|
2020-03-31 19:20:15 +02:00
|
|
|
if (DEBUG) {
|
|
|
|
Log.d(TAG, "Updated subscription: " + info.getUrl());
|
|
|
|
}
|
2017-09-03 08:04:18 +02:00
|
|
|
};
|
|
|
|
|
2018-08-22 16:04:32 +02:00
|
|
|
final Consumer<Throwable> onError = (@NonNull Throwable throwable) ->
|
2020-12-11 14:55:47 +01:00
|
|
|
showSnackBarError(new ErrorInfo(throwable, UserAction.SUBSCRIPTION_UPDATE,
|
|
|
|
"Updating subscription for " + info.getUrl(), info));
|
2017-09-03 08:04:18 +02:00
|
|
|
|
2019-04-28 22:43:54 +02:00
|
|
|
disposables.add(subscriptionManager.updateChannelInfo(info)
|
2017-09-03 08:04:18 +02:00
|
|
|
.subscribeOn(Schedulers.io())
|
|
|
|
.observeOn(AndroidSchedulers.mainThread())
|
|
|
|
.subscribe(onComplete, onError));
|
|
|
|
}
|
|
|
|
|
2020-03-31 19:20:15 +02:00
|
|
|
private Disposable monitorSubscribeButton(final Button subscribeButton,
|
|
|
|
final Function<Object, Object> action) {
|
2018-08-22 16:04:32 +02:00
|
|
|
final Consumer<Object> onNext = (@NonNull Object o) -> {
|
2020-03-31 19:20:15 +02:00
|
|
|
if (DEBUG) {
|
|
|
|
Log.d(TAG, "Changed subscription status to this channel!");
|
|
|
|
}
|
2017-09-03 08:04:18 +02:00
|
|
|
};
|
|
|
|
|
2018-08-22 16:04:32 +02:00
|
|
|
final Consumer<Throwable> onError = (@NonNull Throwable throwable) ->
|
2020-12-11 14:55:47 +01:00
|
|
|
showSnackBarError(new ErrorInfo(throwable, UserAction.SUBSCRIPTION_CHANGE,
|
|
|
|
"Changing subscription for " + currentInfo.getUrl(), currentInfo));
|
2017-09-03 08:04:18 +02:00
|
|
|
|
|
|
|
/* Emit clicks from main thread unto io thread */
|
|
|
|
return RxView.clicks(subscribeButton)
|
|
|
|
.subscribeOn(AndroidSchedulers.mainThread())
|
|
|
|
.observeOn(Schedulers.io())
|
|
|
|
.debounce(BUTTON_DEBOUNCE_INTERVAL, TimeUnit.MILLISECONDS) // Ignore rapid clicks
|
|
|
|
.map(action)
|
|
|
|
.subscribe(onNext, onError);
|
|
|
|
}
|
|
|
|
|
|
|
|
private Consumer<List<SubscriptionEntity>> getSubscribeUpdateMonitor(final ChannelInfo info) {
|
2018-08-22 16:04:32 +02:00
|
|
|
return (List<SubscriptionEntity> subscriptionEntities) -> {
|
2020-03-31 19:20:15 +02:00
|
|
|
if (DEBUG) {
|
|
|
|
Log.d(TAG, "subscriptionManager.subscriptionTable.doOnNext() called with: "
|
|
|
|
+ "subscriptionEntities = [" + subscriptionEntities + "]");
|
|
|
|
}
|
|
|
|
if (subscribeButtonMonitor != null) {
|
|
|
|
subscribeButtonMonitor.dispose();
|
|
|
|
}
|
2018-08-22 16:04:32 +02:00
|
|
|
|
|
|
|
if (subscriptionEntities.isEmpty()) {
|
2020-03-31 19:20:15 +02:00
|
|
|
if (DEBUG) {
|
|
|
|
Log.d(TAG, "No subscription to this channel!");
|
|
|
|
}
|
2020-08-16 10:24:58 +02:00
|
|
|
final SubscriptionEntity channel = new SubscriptionEntity();
|
2018-08-22 16:04:32 +02:00
|
|
|
channel.setServiceId(info.getServiceId());
|
|
|
|
channel.setUrl(info.getUrl());
|
|
|
|
channel.setData(info.getName(),
|
|
|
|
info.getAvatarUrl(),
|
|
|
|
info.getDescription(),
|
|
|
|
info.getSubscriberCount());
|
2020-11-03 06:19:19 +01:00
|
|
|
subscribeButtonMonitor = monitorSubscribeButton(
|
|
|
|
headerBinding.channelSubscribeButton, mapOnSubscribe(channel, info));
|
2018-08-22 16:04:32 +02:00
|
|
|
} else {
|
2020-03-31 19:20:15 +02:00
|
|
|
if (DEBUG) {
|
|
|
|
Log.d(TAG, "Found subscription to this channel!");
|
|
|
|
}
|
2018-08-22 16:04:32 +02:00
|
|
|
final SubscriptionEntity subscription = subscriptionEntities.get(0);
|
2020-11-03 06:19:19 +01:00
|
|
|
subscribeButtonMonitor = monitorSubscribeButton(
|
|
|
|
headerBinding.channelSubscribeButton, mapOnUnsubscribe(subscription));
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2020-03-31 19:20:15 +02:00
|
|
|
private void updateSubscribeButton(final boolean isSubscribed) {
|
|
|
|
if (DEBUG) {
|
|
|
|
Log.d(TAG, "updateSubscribeButton() called with: "
|
|
|
|
+ "isSubscribed = [" + isSubscribed + "]");
|
|
|
|
}
|
2017-09-03 08:04:18 +02:00
|
|
|
|
2020-11-03 06:19:19 +01:00
|
|
|
final boolean isButtonVisible = headerBinding.channelSubscribeButton.getVisibility()
|
|
|
|
== View.VISIBLE;
|
2020-08-16 10:24:58 +02:00
|
|
|
final int backgroundDuration = isButtonVisible ? 300 : 0;
|
|
|
|
final int textDuration = isButtonVisible ? 200 : 0;
|
2017-09-03 08:04:18 +02:00
|
|
|
|
2020-08-16 10:24:58 +02:00
|
|
|
final int subscribeBackground = ThemeHelper
|
2020-05-11 19:29:34 +02:00
|
|
|
.resolveColorFromAttr(activity, R.attr.colorPrimary);
|
2020-08-16 10:24:58 +02:00
|
|
|
final int subscribeText = ContextCompat.getColor(activity, R.color.subscribe_text_color);
|
|
|
|
final int subscribedBackground = ContextCompat
|
2020-03-31 19:20:15 +02:00
|
|
|
.getColor(activity, R.color.subscribed_background_color);
|
2020-08-16 10:24:58 +02:00
|
|
|
final int subscribedText = ContextCompat.getColor(activity, R.color.subscribed_text_color);
|
2017-09-03 08:04:18 +02:00
|
|
|
|
|
|
|
if (!isSubscribed) {
|
2020-11-03 06:19:19 +01:00
|
|
|
headerBinding.channelSubscribeButton.setText(R.string.subscribe_button_title);
|
|
|
|
animateBackgroundColor(headerBinding.channelSubscribeButton, backgroundDuration,
|
|
|
|
subscribedBackground, subscribeBackground);
|
|
|
|
animateTextColor(headerBinding.channelSubscribeButton, textDuration, subscribedText,
|
|
|
|
subscribeText);
|
2017-09-03 08:04:18 +02:00
|
|
|
} else {
|
2020-11-03 06:19:19 +01:00
|
|
|
headerBinding.channelSubscribeButton.setText(R.string.subscribed_button_title);
|
|
|
|
animateBackgroundColor(headerBinding.channelSubscribeButton, backgroundDuration,
|
|
|
|
subscribeBackground, subscribedBackground);
|
|
|
|
animateTextColor(headerBinding.channelSubscribeButton, textDuration, subscribeText,
|
|
|
|
subscribedText);
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
|
|
|
|
2021-01-16 04:32:01 +01:00
|
|
|
animate(headerBinding.channelSubscribeButton, true, 100,
|
|
|
|
AnimationType.LIGHT_SCALE_AND_ALPHA);
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*//////////////////////////////////////////////////////////////////////////
|
|
|
|
// Load and handle
|
|
|
|
//////////////////////////////////////////////////////////////////////////*/
|
|
|
|
|
|
|
|
@Override
|
2018-03-18 16:37:49 +01:00
|
|
|
protected Single<ListExtractor.InfoItemsPage> loadMoreItemsLogic() {
|
2020-04-15 15:31:53 +02:00
|
|
|
return ExtractorHelper.getMoreChannelItems(serviceId, url, currentNextPage);
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-03-31 19:20:15 +02:00
|
|
|
protected Single<ChannelInfo> loadResult(final boolean forceLoad) {
|
2018-07-10 16:26:42 +02:00
|
|
|
return ExtractorHelper.getChannelInfo(serviceId, url, forceLoad);
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
|
|
|
|
2020-04-16 17:35:42 +02:00
|
|
|
/*//////////////////////////////////////////////////////////////////////////
|
2020-04-13 22:40:58 +02:00
|
|
|
// OnClick
|
|
|
|
//////////////////////////////////////////////////////////////////////////*/
|
|
|
|
|
2020-04-16 17:35:42 +02:00
|
|
|
@Override
|
2020-04-13 22:40:58 +02:00
|
|
|
public void onClick(final View v) {
|
|
|
|
if (isLoading.get() || currentInfo == null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (v.getId()) {
|
2020-04-16 17:35:42 +02:00
|
|
|
case R.id.sub_channel_avatar_view:
|
|
|
|
case R.id.sub_channel_title_view:
|
2020-05-08 18:03:19 +02:00
|
|
|
if (!TextUtils.isEmpty(currentInfo.getParentChannelUrl())) {
|
2020-04-13 22:40:58 +02:00
|
|
|
try {
|
2020-08-03 14:47:02 +02:00
|
|
|
NavigationHelper.openChannelFragment(getFM(), currentInfo.getServiceId(),
|
|
|
|
currentInfo.getParentChannelUrl(),
|
2020-05-08 18:03:19 +02:00
|
|
|
currentInfo.getParentChannelName());
|
2020-08-16 10:24:58 +02:00
|
|
|
} catch (final Exception e) {
|
2021-12-01 09:43:24 +01:00
|
|
|
ErrorUtil.showUiErrorSnackbar(this, "Opening channel fragment", e);
|
2020-04-13 22:40:58 +02:00
|
|
|
}
|
2020-05-08 18:03:19 +02:00
|
|
|
} else if (DEBUG) {
|
|
|
|
Log.i(TAG, "Can't open parent channel because we got no channel URL");
|
2020-04-13 22:40:58 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-03 08:04:18 +02:00
|
|
|
/*//////////////////////////////////////////////////////////////////////////
|
|
|
|
// Contract
|
|
|
|
//////////////////////////////////////////////////////////////////////////*/
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void showLoading() {
|
|
|
|
super.showLoading();
|
2021-03-27 14:37:44 +01:00
|
|
|
PicassoHelper.cancelTag(PICASSO_CHANNEL_TAG);
|
2021-01-16 04:32:01 +01:00
|
|
|
animate(headerBinding.channelSubscribeButton, false, 100);
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-03-31 19:20:15 +02:00
|
|
|
public void handleResult(@NonNull final ChannelInfo result) {
|
2017-09-03 08:04:18 +02:00
|
|
|
super.handleResult(result);
|
|
|
|
|
2020-11-03 06:19:19 +01:00
|
|
|
headerBinding.getRoot().setVisibility(View.VISIBLE);
|
2021-03-27 14:37:44 +01:00
|
|
|
PicassoHelper.loadBanner(result.getBannerUrl()).tag(PICASSO_CHANNEL_TAG)
|
|
|
|
.into(headerBinding.channelBannerImage);
|
|
|
|
PicassoHelper.loadAvatar(result.getAvatarUrl()).tag(PICASSO_CHANNEL_TAG)
|
|
|
|
.into(headerBinding.channelAvatarView);
|
|
|
|
PicassoHelper.loadAvatar(result.getParentChannelAvatarUrl()).tag(PICASSO_CHANNEL_TAG)
|
|
|
|
.into(headerBinding.subChannelAvatarView);
|
2017-09-03 08:04:18 +02:00
|
|
|
|
2020-11-03 06:19:19 +01:00
|
|
|
headerBinding.channelSubscriberView.setVisibility(View.VISIBLE);
|
2018-09-05 04:54:17 +02:00
|
|
|
if (result.getSubscriberCount() >= 0) {
|
2020-11-03 06:19:19 +01:00
|
|
|
headerBinding.channelSubscriberView.setText(Localization
|
2020-03-31 19:20:15 +02:00
|
|
|
.shortSubscriberCount(activity, result.getSubscriberCount()));
|
2018-09-05 04:54:17 +02:00
|
|
|
} else {
|
2020-11-03 06:19:19 +01:00
|
|
|
headerBinding.channelSubscriberView.setText(R.string.subscribers_count_not_available);
|
2018-09-05 04:54:17 +02:00
|
|
|
}
|
2017-09-03 08:04:18 +02:00
|
|
|
|
2020-05-08 18:03:19 +02:00
|
|
|
if (!TextUtils.isEmpty(currentInfo.getParentChannelName())) {
|
2020-11-03 06:19:19 +01:00
|
|
|
headerBinding.subChannelTitleView.setText(String.format(
|
2021-04-09 08:53:54 +02:00
|
|
|
getString(R.string.channel_created_by),
|
|
|
|
currentInfo.getParentChannelName())
|
2020-04-16 17:35:42 +02:00
|
|
|
);
|
2020-11-03 06:19:19 +01:00
|
|
|
headerBinding.subChannelTitleView.setVisibility(View.VISIBLE);
|
|
|
|
headerBinding.subChannelAvatarView.setVisibility(View.VISIBLE);
|
2020-04-13 22:40:58 +02:00
|
|
|
} else {
|
2020-11-03 06:19:19 +01:00
|
|
|
headerBinding.subChannelTitleView.setVisibility(View.GONE);
|
2020-04-13 22:40:58 +02:00
|
|
|
}
|
|
|
|
|
2020-03-31 19:20:15 +02:00
|
|
|
if (menuRssButton != null) {
|
|
|
|
menuRssButton.setVisible(!TextUtils.isEmpty(result.getFeedUrl()));
|
|
|
|
}
|
2018-01-29 08:01:06 +01:00
|
|
|
|
2021-04-09 08:53:54 +02:00
|
|
|
// PlaylistControls should be visible only if there is some item in
|
|
|
|
// infoListAdapter other than header
|
|
|
|
if (infoListAdapter.getItemCount() != 1) {
|
|
|
|
playlistControlBinding.getRoot().setVisibility(View.VISIBLE);
|
|
|
|
} else {
|
|
|
|
playlistControlBinding.getRoot().setVisibility(View.GONE);
|
|
|
|
}
|
2017-09-03 08:04:18 +02:00
|
|
|
|
2020-12-11 14:55:47 +01:00
|
|
|
for (final Throwable throwable : result.getErrors()) {
|
|
|
|
if (throwable instanceof ContentNotSupportedException) {
|
|
|
|
showContentNotSupported();
|
2020-04-07 15:39:06 +02:00
|
|
|
}
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
|
|
|
|
2020-12-09 12:42:01 +01:00
|
|
|
disposables.clear();
|
2020-03-31 19:20:15 +02:00
|
|
|
if (subscribeButtonMonitor != null) {
|
|
|
|
subscribeButtonMonitor.dispose();
|
|
|
|
}
|
2017-09-03 08:04:18 +02:00
|
|
|
updateSubscription(result);
|
|
|
|
monitorSubscription(result);
|
2017-11-04 19:30:01 +01:00
|
|
|
|
2020-11-03 06:19:19 +01:00
|
|
|
playlistControlBinding.playlistCtrlPlayAllButton
|
|
|
|
.setOnClickListener(view -> NavigationHelper
|
|
|
|
.playOnMainPlayer(activity, getPlayQueue()));
|
|
|
|
playlistControlBinding.playlistCtrlPlayPopupButton
|
|
|
|
.setOnClickListener(view -> NavigationHelper
|
|
|
|
.playOnPopupPlayer(activity, getPlayQueue(), false));
|
|
|
|
playlistControlBinding.playlistCtrlPlayBgButton
|
|
|
|
.setOnClickListener(view -> NavigationHelper
|
|
|
|
.playOnBackgroundPlayer(activity, getPlayQueue(), false));
|
|
|
|
|
|
|
|
playlistControlBinding.playlistCtrlPlayPopupButton.setOnLongClickListener(view -> {
|
Add play next to long press menu & refactor enqueue methods (#6872)
* added mvp play next button in long press menu; new intent handling, new long press dialog entry, new dialog functions, new strings
* changed line length for checkstyle pass
* cleaned comments, moved strings
* Update app/src/main/res/values/strings.xml
to make long press entry more descriptive
Co-authored-by: opusforlife2 <53176348+opusforlife2@users.noreply.github.com>
* Update app/src/main/res/values/strings.xml
Co-authored-by: Stypox <stypox@pm.me>
* replace redundant nextOnVideoPlayer methods
Co-authored-by: Stypox <stypox@pm.me>
* add enqueueNextOnPlayer and enqueueOnPlayer without selectOnAppend and RESUME_PLAYBACK/ deprecate enqueueNextOn*Player and enqueueOn*Player methods
add getPlayerIntent, getPlayerEnqueueIntent and getPlayerEnqueueNextIntent without selectOnAppend and RESUME_PLAYBACK/ deprecate those with
add section comments
* removed deprecated methods
removed redundant methods
* removed deprecated methods
removed redundant methods
* replaced APPEND_ONLY, removed SELECT_ON_APPEND / replaced remaining enqueueOn*Player methods
* now works with playlists
* renamed dialog entry
* checking for >1 items in the queue using the PlayerHolder
* making enqueue*OnPlayer safe to call when no video is playing (defaulting to audio)
* corrected strings
* improve getQueueSize in PlayerHolder
* long press to enqueue only if queue isnt empty
* add Whitespace
Co-authored-by: Stypox <stypox@pm.me>
* clarify comments / add spaces
* PlayerType as parameter of the enqueueOnPlayer method
add Helper method
* using the helper function everywhere (except for the background and popup long-press actions (also on playlists, history, ...)), so basically nowhere
/ passing checkstyle
* assimilated the enqueue*OnPlayer methods
* removed redundant comment, variable
* simplify code line
Co-authored-by: Stypox <stypox@pm.me>
* move if
* replace workaround for isPlayerOpen()
Co-authored-by: Stypox <stypox@pm.me>
* replaced workarounds (getType), corrected static access with getInstance
* remove unused imports
* changed method call to original, new method doesnt exist yet.
* Use getter method instead of property access syntax.
* improve conditional for play next entry
Co-authored-by: Stypox <stypox@pm.me>
* show play next btn in feed fragment
Co-authored-by: Stypox <stypox@pm.me>
* add play next to local playlist and statistics fragment
Co-authored-by: Stypox <stypox@pm.me>
* formating
Co-authored-by: Stypox <stypox@pm.me>
* correcting logic
Co-authored-by: Stypox <stypox@pm.me>
* remove 2 year old unused string, formating
Co-authored-by: Stypox <stypox@pm.me>
* correct enqueue (next) conditionals, default to background if no player is open. Dont generally default to background play.
* remove player open checks from button long press enqueue actions
* improve log msg
* Rename next to enqueue_next
* Refactor kotlin
Co-authored-by: opusforlife2 <53176348+opusforlife2@users.noreply.github.com>
Co-authored-by: Stypox <stypox@pm.me>
2021-09-18 11:22:49 +02:00
|
|
|
NavigationHelper.enqueueOnPlayer(activity, getPlayQueue(), PlayerType.POPUP);
|
2020-04-19 10:34:07 +02:00
|
|
|
return true;
|
|
|
|
});
|
|
|
|
|
2020-11-03 06:19:19 +01:00
|
|
|
playlistControlBinding.playlistCtrlPlayBgButton.setOnLongClickListener(view -> {
|
Add play next to long press menu & refactor enqueue methods (#6872)
* added mvp play next button in long press menu; new intent handling, new long press dialog entry, new dialog functions, new strings
* changed line length for checkstyle pass
* cleaned comments, moved strings
* Update app/src/main/res/values/strings.xml
to make long press entry more descriptive
Co-authored-by: opusforlife2 <53176348+opusforlife2@users.noreply.github.com>
* Update app/src/main/res/values/strings.xml
Co-authored-by: Stypox <stypox@pm.me>
* replace redundant nextOnVideoPlayer methods
Co-authored-by: Stypox <stypox@pm.me>
* add enqueueNextOnPlayer and enqueueOnPlayer without selectOnAppend and RESUME_PLAYBACK/ deprecate enqueueNextOn*Player and enqueueOn*Player methods
add getPlayerIntent, getPlayerEnqueueIntent and getPlayerEnqueueNextIntent without selectOnAppend and RESUME_PLAYBACK/ deprecate those with
add section comments
* removed deprecated methods
removed redundant methods
* removed deprecated methods
removed redundant methods
* replaced APPEND_ONLY, removed SELECT_ON_APPEND / replaced remaining enqueueOn*Player methods
* now works with playlists
* renamed dialog entry
* checking for >1 items in the queue using the PlayerHolder
* making enqueue*OnPlayer safe to call when no video is playing (defaulting to audio)
* corrected strings
* improve getQueueSize in PlayerHolder
* long press to enqueue only if queue isnt empty
* add Whitespace
Co-authored-by: Stypox <stypox@pm.me>
* clarify comments / add spaces
* PlayerType as parameter of the enqueueOnPlayer method
add Helper method
* using the helper function everywhere (except for the background and popup long-press actions (also on playlists, history, ...)), so basically nowhere
/ passing checkstyle
* assimilated the enqueue*OnPlayer methods
* removed redundant comment, variable
* simplify code line
Co-authored-by: Stypox <stypox@pm.me>
* move if
* replace workaround for isPlayerOpen()
Co-authored-by: Stypox <stypox@pm.me>
* replaced workarounds (getType), corrected static access with getInstance
* remove unused imports
* changed method call to original, new method doesnt exist yet.
* Use getter method instead of property access syntax.
* improve conditional for play next entry
Co-authored-by: Stypox <stypox@pm.me>
* show play next btn in feed fragment
Co-authored-by: Stypox <stypox@pm.me>
* add play next to local playlist and statistics fragment
Co-authored-by: Stypox <stypox@pm.me>
* formating
Co-authored-by: Stypox <stypox@pm.me>
* correcting logic
Co-authored-by: Stypox <stypox@pm.me>
* remove 2 year old unused string, formating
Co-authored-by: Stypox <stypox@pm.me>
* correct enqueue (next) conditionals, default to background if no player is open. Dont generally default to background play.
* remove player open checks from button long press enqueue actions
* improve log msg
* Rename next to enqueue_next
* Refactor kotlin
Co-authored-by: opusforlife2 <53176348+opusforlife2@users.noreply.github.com>
Co-authored-by: Stypox <stypox@pm.me>
2021-09-18 11:22:49 +02:00
|
|
|
NavigationHelper.enqueueOnPlayer(activity, getPlayQueue(), PlayerType.AUDIO);
|
2020-04-19 10:34:07 +02:00
|
|
|
return true;
|
|
|
|
});
|
2017-11-04 19:30:01 +01:00
|
|
|
}
|
|
|
|
|
2020-04-07 15:39:06 +02:00
|
|
|
private void showContentNotSupported() {
|
2020-11-03 06:26:29 +01:00
|
|
|
channelBinding.errorContentNotSupported.setVisibility(View.VISIBLE);
|
|
|
|
channelBinding.channelKaomoji.setText("(︶︹︺)");
|
|
|
|
channelBinding.channelKaomoji.setTextSize(TypedValue.COMPLEX_UNIT_SP, 45f);
|
|
|
|
channelBinding.channelNoVideos.setVisibility(View.GONE);
|
2017-11-04 19:30:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private PlayQueue getPlayQueue() {
|
2017-11-11 23:47:34 +01:00
|
|
|
return getPlayQueue(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
private PlayQueue getPlayQueue(final int index) {
|
2022-01-15 23:55:19 +01:00
|
|
|
final List<StreamInfoItem> streamItems = infoListAdapter.getItemsList().stream()
|
|
|
|
.filter(StreamInfoItem.class::isInstance)
|
|
|
|
.map(StreamInfoItem.class::cast)
|
|
|
|
.collect(Collectors.toList());
|
|
|
|
|
2020-03-31 19:20:15 +02:00
|
|
|
return new ChannelPlayQueue(currentInfo.getServiceId(), currentInfo.getUrl(),
|
2020-04-15 15:31:53 +02:00
|
|
|
currentInfo.getNextPage(), streamItems, index);
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*//////////////////////////////////////////////////////////////////////////
|
|
|
|
// Utils
|
|
|
|
//////////////////////////////////////////////////////////////////////////*/
|
|
|
|
|
|
|
|
@Override
|
2020-03-31 19:20:15 +02:00
|
|
|
public void setTitle(final String title) {
|
2018-08-24 11:54:41 +02:00
|
|
|
super.setTitle(title);
|
2020-03-31 19:20:15 +02:00
|
|
|
if (!useAsFrontPage) {
|
2020-11-03 06:19:19 +01:00
|
|
|
headerBinding.channelTitleView.setText(title);
|
2020-03-31 19:20:15 +02:00
|
|
|
}
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
|
|
|
}
|