2017-04-09 19:34:00 +02:00
|
|
|
package org.schabi.newpipe.fragments.detail;
|
2015-09-04 02:15:03 +02:00
|
|
|
|
2019-12-29 22:15:01 +01:00
|
|
|
import android.animation.ValueAnimator;
|
2017-04-09 19:34:00 +02:00
|
|
|
import android.app.Activity;
|
2020-07-14 19:21:32 +02:00
|
|
|
import android.content.BroadcastReceiver;
|
|
|
|
import android.content.Context;
|
|
|
|
import android.content.Intent;
|
|
|
|
import android.content.IntentFilter;
|
|
|
|
import android.content.SharedPreferences;
|
2019-12-29 22:15:01 +01:00
|
|
|
import android.content.pm.ActivityInfo;
|
2020-01-16 12:20:22 +01:00
|
|
|
import android.database.ContentObserver;
|
2020-09-15 13:43:43 +02:00
|
|
|
import android.graphics.Color;
|
2021-01-14 13:18:34 +01:00
|
|
|
import android.graphics.Point;
|
|
|
|
import android.graphics.Rect;
|
2020-04-02 08:25:47 +02:00
|
|
|
import android.graphics.drawable.Drawable;
|
2020-10-16 20:01:03 +02:00
|
|
|
import android.net.Uri;
|
2017-03-31 20:15:26 +02:00
|
|
|
import android.os.Build;
|
2015-09-04 02:15:03 +02:00
|
|
|
import android.os.Bundle;
|
2019-12-29 22:15:01 +01:00
|
|
|
import android.os.Handler;
|
2020-09-17 22:42:35 +02:00
|
|
|
import android.os.Looper;
|
2019-12-29 22:15:01 +01:00
|
|
|
import android.provider.Settings;
|
2020-08-16 10:24:58 +02:00
|
|
|
import android.util.DisplayMetrics;
|
|
|
|
import android.util.Log;
|
2021-01-14 13:18:34 +01:00
|
|
|
import android.util.TypedValue;
|
2020-07-14 19:21:32 +02:00
|
|
|
import android.view.LayoutInflater;
|
|
|
|
import android.view.MotionEvent;
|
|
|
|
import android.view.View;
|
|
|
|
import android.view.ViewGroup;
|
2020-10-31 21:55:45 +01:00
|
|
|
import android.view.ViewTreeObserver;
|
2020-07-14 19:21:32 +02:00
|
|
|
import android.view.WindowManager;
|
2019-12-29 22:15:01 +01:00
|
|
|
import android.view.animation.DecelerateInterpolator;
|
2020-07-14 19:21:32 +02:00
|
|
|
import android.widget.FrameLayout;
|
|
|
|
import android.widget.RelativeLayout;
|
2020-08-16 10:24:58 +02:00
|
|
|
|
2020-12-08 21:47:02 +01:00
|
|
|
import androidx.annotation.AttrRes;
|
2019-10-04 14:59:08 +02:00
|
|
|
import androidx.annotation.NonNull;
|
|
|
|
import androidx.annotation.Nullable;
|
2021-01-14 11:06:37 +01:00
|
|
|
import androidx.annotation.StringRes;
|
2020-03-10 10:06:38 +01:00
|
|
|
import androidx.appcompat.app.AlertDialog;
|
2020-08-16 10:24:58 +02:00
|
|
|
import androidx.appcompat.content.res.AppCompatResources;
|
2020-07-25 03:14:29 +02:00
|
|
|
import androidx.appcompat.widget.Toolbar;
|
2019-12-29 22:15:01 +01:00
|
|
|
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
2020-08-16 10:24:58 +02:00
|
|
|
import androidx.core.content.ContextCompat;
|
2020-10-31 21:55:45 +01:00
|
|
|
import androidx.preference.PreferenceManager;
|
2020-08-16 10:24:58 +02:00
|
|
|
|
2019-12-29 22:15:01 +01:00
|
|
|
import com.google.android.exoplayer2.ExoPlaybackException;
|
|
|
|
import com.google.android.exoplayer2.PlaybackParameters;
|
2019-10-04 14:59:08 +02:00
|
|
|
import com.google.android.material.appbar.AppBarLayout;
|
2019-12-29 22:15:01 +01:00
|
|
|
import com.google.android.material.bottomsheet.BottomSheetBehavior;
|
2020-12-08 21:47:02 +01:00
|
|
|
import com.google.android.material.tabs.TabLayout;
|
2021-03-27 14:37:44 +01:00
|
|
|
import com.squareup.picasso.Callback;
|
2017-03-31 20:15:26 +02:00
|
|
|
|
2020-09-17 22:42:35 +02:00
|
|
|
import org.schabi.newpipe.App;
|
2016-08-02 21:17:54 +02:00
|
|
|
import org.schabi.newpipe.R;
|
2021-10-09 18:46:20 +02:00
|
|
|
import org.schabi.newpipe.database.stream.model.StreamEntity;
|
2021-01-17 05:21:11 +01:00
|
|
|
import org.schabi.newpipe.databinding.FragmentVideoDetailBinding;
|
2017-03-31 20:15:26 +02:00
|
|
|
import org.schabi.newpipe.download.DownloadDialog;
|
2020-12-09 12:42:01 +01:00
|
|
|
import org.schabi.newpipe.error.ErrorInfo;
|
2021-12-01 09:43:24 +01:00
|
|
|
import org.schabi.newpipe.error.ErrorUtil;
|
2020-12-09 12:42:01 +01:00
|
|
|
import org.schabi.newpipe.error.ReCaptchaActivity;
|
|
|
|
import org.schabi.newpipe.error.UserAction;
|
2017-06-17 13:43:09 +02:00
|
|
|
import org.schabi.newpipe.extractor.InfoItem;
|
2017-03-31 20:15:26 +02:00
|
|
|
import org.schabi.newpipe.extractor.NewPipe;
|
2021-03-14 17:51:47 +01:00
|
|
|
import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException;
|
2018-09-23 03:32:19 +02:00
|
|
|
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
2017-09-03 08:04:18 +02:00
|
|
|
import org.schabi.newpipe.extractor.stream.AudioStream;
|
2018-06-19 03:27:30 +02:00
|
|
|
import org.schabi.newpipe.extractor.stream.Stream;
|
2017-09-03 08:04:18 +02:00
|
|
|
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
2018-02-26 00:10:11 +01:00
|
|
|
import org.schabi.newpipe.extractor.stream.StreamType;
|
2017-09-03 08:04:18 +02:00
|
|
|
import org.schabi.newpipe.extractor.stream.VideoStream;
|
|
|
|
import org.schabi.newpipe.fragments.BackPressable;
|
|
|
|
import org.schabi.newpipe.fragments.BaseStateFragment;
|
2021-01-15 11:26:09 +01:00
|
|
|
import org.schabi.newpipe.fragments.EmptyFragment;
|
2018-10-02 17:09:16 +02:00
|
|
|
import org.schabi.newpipe.fragments.list.comments.CommentsFragment;
|
2021-03-31 20:16:08 +02:00
|
|
|
import org.schabi.newpipe.fragments.list.videos.RelatedItemsFragment;
|
2021-01-16 04:32:01 +01:00
|
|
|
import org.schabi.newpipe.ktx.AnimationType;
|
2021-10-09 18:46:20 +02:00
|
|
|
import org.schabi.newpipe.local.dialog.PlaylistDialog;
|
2018-09-03 01:22:59 +02:00
|
|
|
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
2020-07-14 19:21:32 +02:00
|
|
|
import org.schabi.newpipe.player.MainPlayer;
|
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;
|
2021-01-16 04:32:01 +01:00
|
|
|
import org.schabi.newpipe.player.Player;
|
2020-07-13 03:17:21 +02:00
|
|
|
import org.schabi.newpipe.player.event.OnKeyDownListener;
|
2020-09-17 22:42:35 +02:00
|
|
|
import org.schabi.newpipe.player.event.PlayerServiceExtendedEventListener;
|
2019-12-29 22:15:01 +01:00
|
|
|
import org.schabi.newpipe.player.helper.PlayerHelper;
|
2020-09-17 22:42:35 +02:00
|
|
|
import org.schabi.newpipe.player.helper.PlayerHolder;
|
2020-07-14 19:21:32 +02:00
|
|
|
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
|
|
|
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
|
|
|
|
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
|
2017-10-08 21:04:37 +02:00
|
|
|
import org.schabi.newpipe.util.Constants;
|
2020-08-16 10:24:58 +02:00
|
|
|
import org.schabi.newpipe.util.DeviceUtils;
|
2017-09-03 08:04:18 +02:00
|
|
|
import org.schabi.newpipe.util.ExtractorHelper;
|
|
|
|
import org.schabi.newpipe.util.ListHelper;
|
2017-05-08 15:33:26 +02:00
|
|
|
import org.schabi.newpipe.util.Localization;
|
2017-04-09 19:34:00 +02:00
|
|
|
import org.schabi.newpipe.util.NavigationHelper;
|
2017-03-31 20:15:26 +02:00
|
|
|
import org.schabi.newpipe.util.PermissionHelper;
|
2021-03-27 14:37:44 +01:00
|
|
|
import org.schabi.newpipe.util.PicassoHelper;
|
2020-03-25 16:23:47 +01:00
|
|
|
import org.schabi.newpipe.util.ThemeHelper;
|
2021-03-27 14:37:44 +01:00
|
|
|
import org.schabi.newpipe.util.external_communication.KoreUtils;
|
|
|
|
import org.schabi.newpipe.util.external_communication.ShareUtils;
|
2015-11-17 00:32:00 +01:00
|
|
|
|
2020-12-08 21:47:02 +01:00
|
|
|
import java.util.ArrayList;
|
2021-10-09 18:46:20 +02:00
|
|
|
import java.util.Collections;
|
2020-07-14 19:21:32 +02:00
|
|
|
import java.util.Iterator;
|
|
|
|
import java.util.LinkedList;
|
|
|
|
import java.util.List;
|
2020-10-18 20:19:50 +02:00
|
|
|
import java.util.Objects;
|
2019-04-13 09:31:32 +02:00
|
|
|
import java.util.concurrent.TimeUnit;
|
2017-09-03 08:04:18 +02:00
|
|
|
|
|
|
|
import icepick.State;
|
2020-10-31 21:55:45 +01:00
|
|
|
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
|
|
|
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
|
|
|
import io.reactivex.rxjava3.disposables.Disposable;
|
|
|
|
import io.reactivex.rxjava3.schedulers.Schedulers;
|
2017-04-09 19:34:00 +02:00
|
|
|
|
2020-12-15 17:41:21 +01:00
|
|
|
import static android.text.TextUtils.isEmpty;
|
2019-02-15 20:53:26 +01:00
|
|
|
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.COMMENTS;
|
2020-08-16 10:24:58 +02:00
|
|
|
import static org.schabi.newpipe.extractor.stream.StreamExtractor.NO_AGE_LIMIT;
|
2021-01-17 05:21:11 +01:00
|
|
|
import static org.schabi.newpipe.ktx.ViewUtils.animate;
|
2021-01-17 15:59:29 +01:00
|
|
|
import static org.schabi.newpipe.ktx.ViewUtils.animateRotation;
|
2020-09-27 03:11:38 +02:00
|
|
|
import static org.schabi.newpipe.player.helper.PlayerHelper.globalScreenOrientationLocked;
|
2020-03-10 10:06:38 +01:00
|
|
|
import static org.schabi.newpipe.player.helper.PlayerHelper.isClearingQueueConfirmationRequired;
|
2019-12-29 22:15:01 +01:00
|
|
|
import static org.schabi.newpipe.player.playqueue.PlayQueueItem.RECOVERY_UNSET;
|
2020-12-20 15:05:37 +01:00
|
|
|
import static org.schabi.newpipe.util.ExtractorHelper.showMetaInfoInTextView;
|
2017-05-08 15:33:26 +02:00
|
|
|
|
2020-10-18 20:19:50 +02:00
|
|
|
public final class VideoDetailFragment
|
2018-02-16 11:31:25 +01:00
|
|
|
extends BaseStateFragment<StreamInfo>
|
|
|
|
implements BackPressable,
|
|
|
|
SharedPreferences.OnSharedPreferenceChangeListener,
|
|
|
|
View.OnClickListener,
|
2019-12-29 22:15:01 +01:00
|
|
|
View.OnLongClickListener,
|
2020-09-17 22:42:35 +02:00
|
|
|
PlayerServiceExtendedEventListener,
|
2020-07-13 03:17:21 +02:00
|
|
|
OnKeyDownListener {
|
2020-10-18 20:19:50 +02:00
|
|
|
public static final String KEY_SWITCHING_PLAYERS = "switching_players";
|
2017-03-31 20:15:26 +02:00
|
|
|
|
2019-12-29 22:15:01 +01:00
|
|
|
private static final float MAX_OVERLAY_ALPHA = 0.9f;
|
2020-02-05 06:59:30 +01:00
|
|
|
private static final float MAX_PLAYER_HEIGHT = 0.7f;
|
2019-12-29 22:15:01 +01:00
|
|
|
|
2020-07-14 19:21:32 +02:00
|
|
|
public static final String ACTION_SHOW_MAIN_PLAYER =
|
2020-12-19 14:48:03 +01:00
|
|
|
App.PACKAGE_NAME + ".VideoDetailFragment.ACTION_SHOW_MAIN_PLAYER";
|
2020-07-14 19:21:32 +02:00
|
|
|
public static final String ACTION_HIDE_MAIN_PLAYER =
|
2020-12-19 14:48:03 +01:00
|
|
|
App.PACKAGE_NAME + ".VideoDetailFragment.ACTION_HIDE_MAIN_PLAYER";
|
2020-09-29 05:22:53 +02:00
|
|
|
public static final String ACTION_PLAYER_STARTED =
|
2020-12-19 14:48:03 +01:00
|
|
|
App.PACKAGE_NAME + ".VideoDetailFragment.ACTION_PLAYER_STARTED";
|
2020-07-14 19:21:32 +02:00
|
|
|
public static final String ACTION_VIDEO_FRAGMENT_RESUMED =
|
2020-12-19 14:48:03 +01:00
|
|
|
App.PACKAGE_NAME + ".VideoDetailFragment.ACTION_VIDEO_FRAGMENT_RESUMED";
|
2020-07-14 19:21:32 +02:00
|
|
|
public static final String ACTION_VIDEO_FRAGMENT_STOPPED =
|
2020-12-19 14:48:03 +01:00
|
|
|
App.PACKAGE_NAME + ".VideoDetailFragment.ACTION_VIDEO_FRAGMENT_STOPPED";
|
2017-03-31 20:15:26 +02:00
|
|
|
|
2020-06-27 05:25:50 +02:00
|
|
|
private static final String COMMENTS_TAB_TAG = "COMMENTS";
|
|
|
|
private static final String RELATED_TAB_TAG = "NEXT VIDEO";
|
2020-11-01 13:55:20 +01:00
|
|
|
private static final String DESCRIPTION_TAB_TAG = "DESCRIPTION TAB";
|
2021-01-15 14:55:05 +01:00
|
|
|
private static final String EMPTY_TAB_TAG = "EMPTY TAB";
|
2020-06-27 05:25:50 +02:00
|
|
|
|
2021-03-27 14:37:44 +01:00
|
|
|
private static final String PICASSO_VIDEO_DETAILS_TAG = "PICASSO_VIDEO_DETAILS_TAG";
|
|
|
|
|
2020-12-08 21:47:02 +01:00
|
|
|
// tabs
|
2018-09-03 01:22:59 +02:00
|
|
|
private boolean showComments;
|
2021-03-31 20:16:08 +02:00
|
|
|
private boolean showRelatedItems;
|
2020-12-29 22:20:33 +01:00
|
|
|
private boolean showDescription;
|
2019-03-24 02:01:28 +01:00
|
|
|
private String selectedTabTag;
|
2020-12-08 21:47:02 +01:00
|
|
|
@AttrRes @NonNull final List<Integer> tabIcons = new ArrayList<>();
|
2021-01-14 11:06:37 +01:00
|
|
|
@StringRes @NonNull final List<Integer> tabContentDescriptions = new ArrayList<>();
|
2020-12-29 22:20:33 +01:00
|
|
|
private boolean tabSettingsChanged = false;
|
2021-01-14 13:18:34 +01:00
|
|
|
private int lastAppBarVerticalOffset = Integer.MAX_VALUE; // prevents useless updates
|
2020-06-27 05:25:50 +02:00
|
|
|
|
2018-09-03 01:22:59 +02:00
|
|
|
@State
|
|
|
|
protected int serviceId = Constants.NO_SERVICE_ID;
|
|
|
|
@State
|
2020-10-18 20:19:50 +02:00
|
|
|
@NonNull
|
|
|
|
protected String title = "";
|
2018-09-03 01:22:59 +02:00
|
|
|
@State
|
2020-10-18 20:19:50 +02:00
|
|
|
@Nullable
|
|
|
|
protected String url = null;
|
|
|
|
@Nullable
|
|
|
|
protected PlayQueue playQueue = null;
|
2019-12-29 22:15:01 +01:00
|
|
|
@State
|
2020-01-06 11:39:01 +01:00
|
|
|
int bottomSheetState = BottomSheetBehavior.STATE_EXPANDED;
|
2020-01-13 17:24:28 +01:00
|
|
|
@State
|
|
|
|
protected boolean autoPlayEnabled = true;
|
2017-09-03 08:04:18 +02:00
|
|
|
|
2020-10-18 20:19:50 +02:00
|
|
|
@Nullable
|
|
|
|
private StreamInfo currentInfo = null;
|
2017-09-03 08:04:18 +02:00
|
|
|
private Disposable currentWorker;
|
2018-09-03 01:22:59 +02:00
|
|
|
@NonNull
|
2020-10-18 20:19:50 +02:00
|
|
|
private final CompositeDisposable disposables = new CompositeDisposable();
|
2019-04-13 09:31:32 +02:00
|
|
|
@Nullable
|
|
|
|
private Disposable positionSubscriber = null;
|
2017-06-28 03:39:33 +02:00
|
|
|
|
2018-04-08 13:08:19 +02:00
|
|
|
private List<VideoStream> sortedVideoStreams;
|
|
|
|
private int selectedVideoStreamIndex = -1;
|
2020-07-12 02:59:47 +02:00
|
|
|
private BottomSheetBehavior<FrameLayout> bottomSheetBehavior;
|
2019-12-29 22:15:01 +01:00
|
|
|
private BroadcastReceiver broadcastReceiver;
|
2018-02-16 12:18:15 +01:00
|
|
|
|
2017-03-31 20:15:26 +02:00
|
|
|
/*//////////////////////////////////////////////////////////////////////////
|
|
|
|
// Views
|
|
|
|
//////////////////////////////////////////////////////////////////////////*/
|
2018-03-10 17:16:51 +01:00
|
|
|
|
2021-01-17 05:21:11 +01:00
|
|
|
private FragmentVideoDetailBinding binding;
|
|
|
|
|
2020-09-06 14:01:59 +02:00
|
|
|
private TabAdapter pageAdapter;
|
2018-09-03 01:22:59 +02:00
|
|
|
|
2020-01-16 12:20:22 +01:00
|
|
|
private ContentObserver settingsContentObserver;
|
2020-10-16 20:01:03 +02:00
|
|
|
@Nullable
|
2019-12-29 22:15:01 +01:00
|
|
|
private MainPlayer playerService;
|
2021-01-08 18:35:33 +01:00
|
|
|
private Player player;
|
2021-08-31 11:10:52 +02:00
|
|
|
private final PlayerHolder playerHolder = PlayerHolder.getInstance();
|
2019-12-29 22:15:01 +01:00
|
|
|
|
|
|
|
/*//////////////////////////////////////////////////////////////////////////
|
|
|
|
// Service management
|
|
|
|
//////////////////////////////////////////////////////////////////////////*/
|
2020-09-17 22:42:35 +02:00
|
|
|
@Override
|
2021-01-08 18:35:33 +01:00
|
|
|
public void onServiceConnected(final Player connectedPlayer,
|
2020-09-17 22:42:35 +02:00
|
|
|
final MainPlayer connectedPlayerService,
|
|
|
|
final boolean playAfterConnect) {
|
|
|
|
player = connectedPlayer;
|
|
|
|
playerService = connectedPlayerService;
|
2019-12-29 22:15:01 +01:00
|
|
|
|
2020-09-17 22:42:35 +02:00
|
|
|
// It will do nothing if the player is not in fullscreen mode
|
|
|
|
hideSystemUiIfNeeded();
|
2019-12-29 22:15:01 +01:00
|
|
|
|
2020-09-17 22:42:35 +02:00
|
|
|
if (!player.videoPlayerSelected() && !playAfterConnect) {
|
|
|
|
return;
|
2020-07-14 19:21:32 +02:00
|
|
|
}
|
2019-12-29 22:15:01 +01:00
|
|
|
|
2021-03-25 22:54:55 +01:00
|
|
|
if (DeviceUtils.isLandscape(requireContext())) {
|
2020-09-17 22:42:35 +02:00
|
|
|
// If the video is playing but orientation changed
|
|
|
|
// let's make the video in fullscreen again
|
|
|
|
checkLandscape();
|
2020-11-20 21:43:05 +01:00
|
|
|
} else if (player.isFullscreen() && !player.isVerticalVideo()
|
|
|
|
// Tablet UI has orientation-independent fullscreen
|
|
|
|
&& !DeviceUtils.isTablet(activity)) {
|
2020-09-17 22:42:35 +02:00
|
|
|
// Device is in portrait orientation after rotation but UI is in fullscreen.
|
|
|
|
// Return back to non-fullscreen state
|
|
|
|
player.toggleFullscreen();
|
2019-12-29 22:15:01 +01:00
|
|
|
}
|
|
|
|
|
2020-09-17 22:42:35 +02:00
|
|
|
if (playerIsNotStopped() && player.videoPlayerSelected()) {
|
|
|
|
addVideoPlayerView();
|
2020-07-14 19:21:32 +02:00
|
|
|
}
|
2019-12-29 22:15:01 +01:00
|
|
|
|
2020-09-17 22:42:35 +02:00
|
|
|
if (playAfterConnect
|
|
|
|
|| (currentInfo != null
|
|
|
|
&& isAutoplayEnabled()
|
|
|
|
&& player.getParentActivity() == null)) {
|
2020-11-01 21:25:31 +01:00
|
|
|
autoPlayEnabled = true; // forcefully start playing
|
2021-07-27 11:02:32 +02:00
|
|
|
openVideoPlayerAutoFullscreen();
|
2020-07-14 19:21:32 +02:00
|
|
|
}
|
2019-12-29 22:15:01 +01:00
|
|
|
}
|
|
|
|
|
2020-09-17 22:42:35 +02:00
|
|
|
@Override
|
|
|
|
public void onServiceDisconnected() {
|
|
|
|
playerService = null;
|
|
|
|
player = null;
|
|
|
|
restoreDefaultBrightness();
|
2019-12-29 22:15:01 +01:00
|
|
|
}
|
|
|
|
|
2018-02-16 12:18:15 +01:00
|
|
|
|
2017-04-09 19:34:00 +02:00
|
|
|
/*////////////////////////////////////////////////////////////////////////*/
|
|
|
|
|
2020-10-18 20:19:50 +02:00
|
|
|
public static VideoDetailFragment getInstance(final int serviceId,
|
|
|
|
@Nullable final String videoUrl,
|
|
|
|
@NonNull final String name,
|
|
|
|
@Nullable final PlayQueue queue) {
|
2020-08-16 10:24:58 +02:00
|
|
|
final VideoDetailFragment instance = new VideoDetailFragment();
|
2020-09-17 22:42:35 +02:00
|
|
|
instance.setInitialData(serviceId, videoUrl, name, queue);
|
2017-04-09 19:34:00 +02:00
|
|
|
return instance;
|
|
|
|
}
|
|
|
|
|
2020-09-29 23:49:34 +02:00
|
|
|
public static VideoDetailFragment getInstanceInCollapsedState() {
|
2020-09-29 05:22:53 +02:00
|
|
|
final VideoDetailFragment instance = new VideoDetailFragment();
|
|
|
|
instance.bottomSheetState = BottomSheetBehavior.STATE_COLLAPSED;
|
|
|
|
return instance;
|
|
|
|
}
|
|
|
|
|
2020-04-02 13:51:10 +02:00
|
|
|
|
2017-03-31 20:15:26 +02:00
|
|
|
/*//////////////////////////////////////////////////////////////////////////
|
2017-04-09 19:34:00 +02:00
|
|
|
// Fragment's Lifecycle
|
2017-03-31 20:15:26 +02:00
|
|
|
//////////////////////////////////////////////////////////////////////////*/
|
|
|
|
|
|
|
|
@Override
|
2020-03-31 19:20:15 +02:00
|
|
|
public void onCreate(final Bundle savedInstanceState) {
|
2015-09-04 02:15:03 +02:00
|
|
|
super.onCreate(savedInstanceState);
|
2019-12-29 22:15:01 +01:00
|
|
|
|
2020-12-29 22:20:33 +01:00
|
|
|
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity);
|
|
|
|
showComments = prefs.getBoolean(getString(R.string.show_comments_key), true);
|
2021-03-31 20:16:08 +02:00
|
|
|
showRelatedItems = prefs.getBoolean(getString(R.string.show_next_video_key), true);
|
2020-12-29 22:20:33 +01:00
|
|
|
showDescription = prefs.getBoolean(getString(R.string.show_description_key), true);
|
|
|
|
selectedTabTag = prefs.getString(
|
|
|
|
getString(R.string.stream_info_selected_tab_key), COMMENTS_TAB_TAG);
|
|
|
|
prefs.registerOnSharedPreferenceChangeListener(this);
|
2019-12-29 22:15:01 +01:00
|
|
|
|
|
|
|
setupBroadcastReceiver();
|
2020-01-08 17:16:50 +01:00
|
|
|
|
2020-01-16 12:20:22 +01:00
|
|
|
settingsContentObserver = new ContentObserver(new Handler()) {
|
|
|
|
@Override
|
2020-07-12 02:59:47 +02:00
|
|
|
public void onChange(final boolean selfChange) {
|
2020-09-27 03:11:38 +02:00
|
|
|
if (activity != null && !globalScreenOrientationLocked(activity)) {
|
2020-01-16 12:20:22 +01:00
|
|
|
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
|
2020-07-14 19:21:32 +02:00
|
|
|
}
|
2020-01-16 12:20:22 +01:00
|
|
|
}
|
|
|
|
};
|
2020-01-08 17:16:50 +01:00
|
|
|
activity.getContentResolver().registerContentObserver(
|
2020-01-16 12:20:22 +01:00
|
|
|
Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION), false,
|
2020-01-08 17:16:50 +01:00
|
|
|
settingsContentObserver);
|
2017-04-09 19:34:00 +02:00
|
|
|
}
|
2015-09-04 02:15:03 +02:00
|
|
|
|
2017-04-09 19:34:00 +02:00
|
|
|
@Override
|
2020-03-31 19:20:15 +02:00
|
|
|
public View onCreateView(@NonNull final LayoutInflater inflater, final ViewGroup container,
|
|
|
|
final Bundle savedInstanceState) {
|
2021-06-23 16:53:01 +02:00
|
|
|
binding = FragmentVideoDetailBinding.inflate(inflater, container, false);
|
|
|
|
return binding.getRoot();
|
2017-04-09 19:34:00 +02:00
|
|
|
}
|
2017-03-31 20:15:26 +02:00
|
|
|
|
2017-04-09 19:34:00 +02:00
|
|
|
@Override
|
2017-09-03 08:04:18 +02:00
|
|
|
public void onPause() {
|
|
|
|
super.onPause();
|
2020-03-31 19:20:15 +02:00
|
|
|
if (currentWorker != null) {
|
|
|
|
currentWorker.dispose();
|
|
|
|
}
|
2020-09-11 19:52:38 +02:00
|
|
|
restoreDefaultBrightness();
|
2020-08-27 22:55:57 +02:00
|
|
|
PreferenceManager.getDefaultSharedPreferences(requireContext())
|
2019-03-24 02:01:28 +01:00
|
|
|
.edit()
|
2020-03-31 19:20:15 +02:00
|
|
|
.putString(getString(R.string.stream_info_selected_tab_key),
|
2021-01-17 05:21:11 +01:00
|
|
|
pageAdapter.getItemTitle(binding.viewPager.getCurrentItem()))
|
2019-03-24 02:01:28 +01:00
|
|
|
.apply();
|
2017-08-12 06:50:25 +02:00
|
|
|
}
|
|
|
|
|
2017-04-26 21:32:04 +02:00
|
|
|
@Override
|
|
|
|
public void onResume() {
|
|
|
|
super.onResume();
|
|
|
|
|
2020-02-29 00:57:54 +01:00
|
|
|
activity.sendBroadcast(new Intent(ACTION_VIDEO_FRAGMENT_RESUMED));
|
2019-12-29 22:15:01 +01:00
|
|
|
|
2020-09-04 04:39:55 +02:00
|
|
|
setupBrightness();
|
2019-12-29 22:15:01 +01:00
|
|
|
|
2020-12-29 22:20:33 +01:00
|
|
|
if (tabSettingsChanged) {
|
|
|
|
tabSettingsChanged = false;
|
|
|
|
initTabs();
|
|
|
|
if (currentInfo != null) {
|
|
|
|
updateTabs(currentInfo);
|
2017-04-26 21:32:04 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-15 19:32:29 +01:00
|
|
|
// Check if it was loading when the fragment was stopped/paused
|
2020-07-14 19:21:32 +02:00
|
|
|
if (wasLoading.getAndSet(false) && !wasCleared()) {
|
2020-03-10 10:06:38 +01:00
|
|
|
startLoading(false);
|
2020-07-14 19:21:32 +02:00
|
|
|
}
|
2019-12-29 22:15:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onStop() {
|
|
|
|
super.onStop();
|
|
|
|
|
2020-07-14 19:21:32 +02:00
|
|
|
if (!activity.isChangingConfigurations()) {
|
2020-02-29 00:57:54 +01:00
|
|
|
activity.sendBroadcast(new Intent(ACTION_VIDEO_FRAGMENT_STOPPED));
|
2020-07-14 19:21:32 +02:00
|
|
|
}
|
2017-04-26 21:32:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onDestroy() {
|
|
|
|
super.onDestroy();
|
2019-12-29 22:15:01 +01:00
|
|
|
|
2020-07-14 19:21:32 +02:00
|
|
|
// Stop the service when user leaves the app with double back press
|
|
|
|
// if video player is selected. Otherwise unbind
|
2021-06-23 07:11:41 +02:00
|
|
|
if (activity.isFinishing() && isPlayerAvailable() && player.videoPlayerSelected()) {
|
2021-06-24 10:00:56 +02:00
|
|
|
playerHolder.stopService();
|
2020-07-14 19:21:32 +02:00
|
|
|
} else {
|
2021-06-24 10:00:56 +02:00
|
|
|
playerHolder.setListener(null);
|
2020-07-14 19:21:32 +02:00
|
|
|
}
|
2019-12-29 22:15:01 +01:00
|
|
|
|
2018-02-16 11:31:25 +01:00
|
|
|
PreferenceManager.getDefaultSharedPreferences(activity)
|
|
|
|
.unregisterOnSharedPreferenceChangeListener(this);
|
2020-01-08 17:16:50 +01:00
|
|
|
activity.unregisterReceiver(broadcastReceiver);
|
|
|
|
activity.getContentResolver().unregisterContentObserver(settingsContentObserver);
|
2017-09-03 08:04:18 +02:00
|
|
|
|
2020-03-31 19:20:15 +02:00
|
|
|
if (positionSubscriber != null) {
|
|
|
|
positionSubscriber.dispose();
|
|
|
|
}
|
|
|
|
if (currentWorker != null) {
|
|
|
|
currentWorker.dispose();
|
|
|
|
}
|
2020-07-12 02:59:47 +02:00
|
|
|
disposables.clear();
|
2019-04-13 09:31:32 +02:00
|
|
|
positionSubscriber = null;
|
2017-09-03 08:04:18 +02:00
|
|
|
currentWorker = null;
|
2020-01-17 15:37:53 +01:00
|
|
|
bottomSheetBehavior.setBottomSheetCallback(null);
|
2020-09-17 22:42:35 +02:00
|
|
|
|
|
|
|
if (activity.isFinishing()) {
|
|
|
|
playQueue = null;
|
|
|
|
currentInfo = null;
|
|
|
|
stack = new LinkedList<>();
|
|
|
|
}
|
2017-03-31 20:15:26 +02:00
|
|
|
}
|
|
|
|
|
2021-06-23 16:53:01 +02:00
|
|
|
@Override
|
|
|
|
public void onDestroyView() {
|
|
|
|
super.onDestroyView();
|
|
|
|
binding = null;
|
|
|
|
}
|
|
|
|
|
2017-03-31 20:15:26 +02:00
|
|
|
@Override
|
2020-03-31 19:20:15 +02:00
|
|
|
public void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
|
2017-03-31 20:15:26 +02:00
|
|
|
super.onActivityResult(requestCode, resultCode, data);
|
|
|
|
switch (requestCode) {
|
|
|
|
case ReCaptchaActivity.RECAPTCHA_REQUEST:
|
2017-04-09 19:34:00 +02:00
|
|
|
if (resultCode == Activity.RESULT_OK) {
|
2020-10-18 20:19:50 +02:00
|
|
|
NavigationHelper.openVideoDetailFragment(requireContext(), getFM(),
|
|
|
|
serviceId, url, title, null, false);
|
2020-03-31 19:20:15 +02:00
|
|
|
} else {
|
|
|
|
Log.e(TAG, "ReCaptcha failed");
|
|
|
|
}
|
2017-03-31 20:15:26 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
Log.e(TAG, "Request code from activity not supported [" + requestCode + "]");
|
|
|
|
break;
|
|
|
|
}
|
2015-10-29 17:56:35 +01:00
|
|
|
}
|
|
|
|
|
2016-01-01 23:04:29 +01:00
|
|
|
@Override
|
2020-03-31 19:20:15 +02:00
|
|
|
public void onSharedPreferenceChanged(final SharedPreferences sharedPreferences,
|
|
|
|
final String key) {
|
2020-12-29 22:20:33 +01:00
|
|
|
if (key.equals(getString(R.string.show_comments_key))) {
|
|
|
|
showComments = sharedPreferences.getBoolean(key, true);
|
|
|
|
tabSettingsChanged = true;
|
|
|
|
} else if (key.equals(getString(R.string.show_next_video_key))) {
|
2021-03-31 20:16:08 +02:00
|
|
|
showRelatedItems = sharedPreferences.getBoolean(key, true);
|
2020-12-29 22:20:33 +01:00
|
|
|
tabSettingsChanged = true;
|
|
|
|
} else if (key.equals(getString(R.string.show_description_key))) {
|
2021-09-04 11:28:32 +02:00
|
|
|
showDescription = sharedPreferences.getBoolean(key, true);
|
2020-12-29 22:20:33 +01:00
|
|
|
tabSettingsChanged = true;
|
2017-03-31 20:15:26 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-26 21:32:04 +02:00
|
|
|
/*//////////////////////////////////////////////////////////////////////////
|
|
|
|
// OnClick
|
|
|
|
//////////////////////////////////////////////////////////////////////////*/
|
|
|
|
|
|
|
|
@Override
|
2020-03-31 19:20:15 +02:00
|
|
|
public void onClick(final View v) {
|
2017-04-26 21:32:04 +02:00
|
|
|
switch (v.getId()) {
|
|
|
|
case R.id.detail_controls_background:
|
2017-10-13 02:02:07 +02:00
|
|
|
openBackgroundPlayer(false);
|
2017-04-26 21:32:04 +02:00
|
|
|
break;
|
|
|
|
case R.id.detail_controls_popup:
|
2017-10-13 02:02:07 +02:00
|
|
|
openPopupPlayer(false);
|
2017-04-26 21:32:04 +02:00
|
|
|
break;
|
2018-01-17 23:32:09 +01:00
|
|
|
case R.id.detail_controls_playlist_append:
|
2020-08-03 14:47:02 +02:00
|
|
|
if (getFM() != null && currentInfo != null) {
|
2020-09-29 00:32:24 +02:00
|
|
|
disposables.add(
|
2021-10-09 18:46:20 +02:00
|
|
|
PlaylistDialog.createCorrespondingDialog(
|
|
|
|
getContext(),
|
|
|
|
Collections.singletonList(new StreamEntity(currentInfo)),
|
|
|
|
dialog -> dialog.show(getFM(), TAG)
|
2020-12-19 14:48:03 +01:00
|
|
|
)
|
2020-09-29 00:32:24 +02:00
|
|
|
);
|
2018-01-17 23:32:09 +01:00
|
|
|
}
|
|
|
|
break;
|
2018-02-11 21:34:32 +01:00
|
|
|
case R.id.detail_controls_download:
|
2019-08-15 04:00:11 +02:00
|
|
|
if (PermissionHelper.checkStoragePermissions(activity,
|
|
|
|
PermissionHelper.DOWNLOAD_DIALOG_REQUEST_CODE)) {
|
|
|
|
this.openDownloadDialog();
|
|
|
|
}
|
2018-02-11 21:34:32 +01:00
|
|
|
break;
|
2020-10-16 20:01:03 +02:00
|
|
|
case R.id.detail_controls_share:
|
|
|
|
if (currentInfo != null) {
|
2021-03-20 16:35:14 +01:00
|
|
|
ShareUtils.shareText(requireContext(), currentInfo.getName(),
|
|
|
|
currentInfo.getUrl(), currentInfo.getThumbnailUrl());
|
2020-10-16 20:01:03 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case R.id.detail_controls_open_in_browser:
|
|
|
|
if (currentInfo != null) {
|
|
|
|
ShareUtils.openUrlInBrowser(requireContext(), currentInfo.getUrl());
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case R.id.detail_controls_play_with_kodi:
|
|
|
|
if (currentInfo != null) {
|
|
|
|
try {
|
|
|
|
NavigationHelper.playWithKore(
|
|
|
|
requireContext(), Uri.parse(currentInfo.getUrl()));
|
|
|
|
} catch (final Exception e) {
|
|
|
|
if (DEBUG) {
|
|
|
|
Log.i(TAG, "Failed to start kore", e);
|
|
|
|
}
|
2021-03-26 13:28:11 +01:00
|
|
|
KoreUtils.showInstallKoreDialog(requireContext());
|
2020-10-16 20:01:03 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2017-06-15 16:26:48 +02:00
|
|
|
case R.id.detail_uploader_root_layout:
|
2020-12-15 17:41:21 +01:00
|
|
|
if (isEmpty(currentInfo.getSubChannelUrl())) {
|
|
|
|
if (!isEmpty(currentInfo.getUploaderUrl())) {
|
2020-04-13 22:40:44 +02:00
|
|
|
openChannel(currentInfo.getUploaderUrl(), currentInfo.getUploaderName());
|
2020-03-31 19:20:15 +02:00
|
|
|
}
|
2020-05-08 18:03:19 +02:00
|
|
|
|
|
|
|
if (DEBUG) {
|
|
|
|
Log.i(TAG, "Can't open sub-channel because we got no channel URL");
|
|
|
|
}
|
2020-04-13 22:40:44 +02:00
|
|
|
} else {
|
2020-04-16 17:35:42 +02:00
|
|
|
openChannel(currentInfo.getSubChannelUrl(),
|
|
|
|
currentInfo.getSubChannelName());
|
2017-06-15 16:26:48 +02:00
|
|
|
}
|
2017-04-26 21:32:04 +02:00
|
|
|
break;
|
2017-06-15 16:26:48 +02:00
|
|
|
case R.id.detail_thumbnail_root_layout:
|
2020-11-01 21:25:31 +01:00
|
|
|
autoPlayEnabled = true; // forcefully start playing
|
2021-07-27 11:02:32 +02:00
|
|
|
openVideoPlayerAutoFullscreen();
|
2017-04-26 21:32:04 +02:00
|
|
|
break;
|
|
|
|
case R.id.detail_title_root_layout:
|
2020-12-08 21:47:02 +01:00
|
|
|
toggleTitleAndSecondaryControls();
|
2017-04-26 21:32:04 +02:00
|
|
|
break;
|
2019-12-29 22:15:01 +01:00
|
|
|
case R.id.overlay_thumbnail:
|
|
|
|
case R.id.overlay_metadata_layout:
|
|
|
|
case R.id.overlay_buttons_layout:
|
|
|
|
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
|
|
|
|
break;
|
|
|
|
case R.id.overlay_play_pause_button:
|
2020-02-29 00:57:54 +01:00
|
|
|
if (playerIsNotStopped()) {
|
2021-01-08 18:35:33 +01:00
|
|
|
player.playPause();
|
2020-07-12 02:59:47 +02:00
|
|
|
player.hideControls(0, 0);
|
2020-01-03 17:19:14 +01:00
|
|
|
showSystemUi();
|
2020-07-14 19:21:32 +02:00
|
|
|
} else {
|
2020-11-01 21:25:31 +01:00
|
|
|
autoPlayEnabled = true; // forcefully start playing
|
2021-01-19 08:13:44 +01:00
|
|
|
openVideoPlayer(false);
|
2020-07-14 19:21:32 +02:00
|
|
|
}
|
2019-12-29 22:15:01 +01:00
|
|
|
|
2021-06-23 07:11:41 +02:00
|
|
|
setOverlayPlayPauseImage(isPlayerAvailable() && player.isPlaying());
|
2019-12-29 22:15:01 +01:00
|
|
|
break;
|
|
|
|
case R.id.overlay_close_button:
|
|
|
|
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
|
|
|
|
break;
|
2017-04-26 21:32:04 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-16 17:35:42 +02:00
|
|
|
private void openChannel(final String subChannelUrl, final String subChannelName) {
|
2020-04-13 22:40:44 +02:00
|
|
|
try {
|
2020-08-03 14:47:02 +02:00
|
|
|
NavigationHelper.openChannelFragment(getFM(), currentInfo.getServiceId(),
|
|
|
|
subChannelUrl, subChannelName);
|
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:44 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-13 02:02:07 +02:00
|
|
|
@Override
|
2020-03-31 19:20:15 +02:00
|
|
|
public boolean onLongClick(final View v) {
|
|
|
|
if (isLoading.get() || currentInfo == null) {
|
|
|
|
return false;
|
|
|
|
}
|
2017-10-13 02:02:07 +02:00
|
|
|
|
|
|
|
switch (v.getId()) {
|
|
|
|
case R.id.detail_controls_background:
|
|
|
|
openBackgroundPlayer(true);
|
|
|
|
break;
|
|
|
|
case R.id.detail_controls_popup:
|
|
|
|
openPopupPlayer(true);
|
|
|
|
break;
|
2018-04-08 21:53:15 +02:00
|
|
|
case R.id.detail_controls_download:
|
2020-01-08 17:16:50 +01:00
|
|
|
NavigationHelper.openDownloads(activity);
|
2018-04-08 21:53:15 +02:00
|
|
|
break;
|
2019-12-29 22:15:01 +01:00
|
|
|
case R.id.overlay_thumbnail:
|
|
|
|
case R.id.overlay_metadata_layout:
|
2021-01-17 05:21:11 +01:00
|
|
|
openChannel(currentInfo.getUploaderUrl(), currentInfo.getUploaderName());
|
2018-04-08 21:53:15 +02:00
|
|
|
break;
|
2020-04-19 23:01:02 +02:00
|
|
|
case R.id.detail_uploader_root_layout:
|
2020-12-15 17:41:21 +01:00
|
|
|
if (isEmpty(currentInfo.getSubChannelUrl())) {
|
2020-04-19 23:01:02 +02:00
|
|
|
Log.w(TAG,
|
|
|
|
"Can't open parent channel because we got no parent channel URL");
|
|
|
|
} else {
|
|
|
|
openChannel(currentInfo.getUploaderUrl(), currentInfo.getUploaderName());
|
|
|
|
}
|
|
|
|
break;
|
2020-06-10 23:11:06 +02:00
|
|
|
case R.id.detail_title_root_layout:
|
2020-08-27 22:56:58 +02:00
|
|
|
ShareUtils.copyToClipboard(requireContext(),
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.detailVideoTitleView.getText().toString());
|
2019-12-29 22:15:01 +01:00
|
|
|
break;
|
2017-10-13 02:02:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-12-08 21:47:02 +01:00
|
|
|
private void toggleTitleAndSecondaryControls() {
|
2020-11-01 13:55:20 +01:00
|
|
|
if (binding.detailSecondaryControlPanel.getVisibility() == View.GONE) {
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.detailVideoTitleView.setMaxLines(10);
|
2021-01-17 15:59:29 +01:00
|
|
|
animateRotation(binding.detailToggleSecondaryControlsView,
|
|
|
|
Player.DEFAULT_CONTROLS_DURATION, 180);
|
2020-10-16 20:01:03 +02:00
|
|
|
binding.detailSecondaryControlPanel.setVisibility(View.VISIBLE);
|
2020-11-01 13:55:20 +01:00
|
|
|
} else {
|
|
|
|
binding.detailVideoTitleView.setMaxLines(1);
|
2021-01-17 15:59:29 +01:00
|
|
|
animateRotation(binding.detailToggleSecondaryControlsView,
|
|
|
|
Player.DEFAULT_CONTROLS_DURATION, 0);
|
2020-11-01 13:55:20 +01:00
|
|
|
binding.detailSecondaryControlPanel.setVisibility(View.GONE);
|
2017-04-26 21:32:04 +02:00
|
|
|
}
|
2021-02-12 10:54:16 +01:00
|
|
|
// view pager height has changed, update the tab layout
|
2021-01-14 13:18:34 +01:00
|
|
|
updateTabLayoutVisibility();
|
2017-04-26 21:32:04 +02:00
|
|
|
}
|
|
|
|
|
2017-03-31 20:15:26 +02:00
|
|
|
/*//////////////////////////////////////////////////////////////////////////
|
|
|
|
// Init
|
|
|
|
//////////////////////////////////////////////////////////////////////////*/
|
|
|
|
|
2021-09-21 21:01:10 +02:00
|
|
|
@Override
|
|
|
|
public void onViewCreated(@NonNull final View rootView, final Bundle savedInstanceState) {
|
|
|
|
super.onViewCreated(rootView, savedInstanceState);
|
|
|
|
}
|
|
|
|
|
2021-06-23 16:53:01 +02:00
|
|
|
@Override // called from onViewCreated in {@link BaseFragment#onViewCreated}
|
2020-03-31 19:20:15 +02:00
|
|
|
protected void initViews(final View rootView, final Bundle savedInstanceState) {
|
2017-04-26 21:32:04 +02:00
|
|
|
super.initViews(rootView, savedInstanceState);
|
2018-09-23 03:32:19 +02:00
|
|
|
|
2021-01-17 05:21:11 +01:00
|
|
|
pageAdapter = new TabAdapter(getChildFragmentManager());
|
|
|
|
binding.viewPager.setAdapter(pageAdapter);
|
|
|
|
binding.tabLayout.setupWithViewPager(binding.viewPager);
|
2018-12-08 22:51:55 +01:00
|
|
|
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.detailThumbnailRootLayout.requestFocus();
|
2020-04-22 22:35:33 +02:00
|
|
|
|
2021-09-21 21:01:10 +02:00
|
|
|
binding.detailControlsPlayWithKodi.setVisibility(
|
|
|
|
KoreUtils.shouldShowPlayWithKodi(requireContext(), serviceId)
|
|
|
|
? View.VISIBLE
|
|
|
|
: View.GONE
|
|
|
|
);
|
|
|
|
binding.detailControlsCrashThePlayer.setVisibility(
|
|
|
|
DEBUG && PreferenceManager.getDefaultSharedPreferences(getContext())
|
|
|
|
.getBoolean(getString(R.string.show_crash_the_player_key), false)
|
|
|
|
? View.VISIBLE
|
|
|
|
: View.GONE
|
|
|
|
);
|
|
|
|
|
2020-07-21 00:43:49 +02:00
|
|
|
if (DeviceUtils.isTv(getContext())) {
|
2020-04-22 22:35:33 +02:00
|
|
|
// remove ripple effects from detail controls
|
2021-01-17 05:21:11 +01:00
|
|
|
final int transparent = ContextCompat.getColor(requireContext(),
|
|
|
|
R.color.transparent_background_color);
|
|
|
|
binding.detailControlsPlaylistAppend.setBackgroundColor(transparent);
|
|
|
|
binding.detailControlsBackground.setBackgroundColor(transparent);
|
|
|
|
binding.detailControlsPopup.setBackgroundColor(transparent);
|
|
|
|
binding.detailControlsDownload.setBackgroundColor(transparent);
|
2020-10-16 20:01:03 +02:00
|
|
|
binding.detailControlsShare.setBackgroundColor(transparent);
|
|
|
|
binding.detailControlsOpenInBrowser.setBackgroundColor(transparent);
|
|
|
|
binding.detailControlsPlayWithKodi.setBackgroundColor(transparent);
|
2020-04-22 22:35:33 +02:00
|
|
|
}
|
2018-09-23 03:32:19 +02:00
|
|
|
}
|
|
|
|
|
2017-09-03 08:04:18 +02:00
|
|
|
@Override
|
2017-04-26 21:32:04 +02:00
|
|
|
protected void initListeners() {
|
|
|
|
super.initListeners();
|
2017-03-31 20:15:26 +02:00
|
|
|
|
2020-10-16 20:01:03 +02:00
|
|
|
binding.detailTitleRootLayout.setOnClickListener(this);
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.detailTitleRootLayout.setOnLongClickListener(this);
|
|
|
|
binding.detailUploaderRootLayout.setOnClickListener(this);
|
|
|
|
binding.detailUploaderRootLayout.setOnLongClickListener(this);
|
|
|
|
binding.detailThumbnailRootLayout.setOnClickListener(this);
|
2021-01-14 22:34:55 +01:00
|
|
|
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.detailControlsBackground.setOnClickListener(this);
|
2020-10-16 20:01:03 +02:00
|
|
|
binding.detailControlsBackground.setOnLongClickListener(this);
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.detailControlsPopup.setOnClickListener(this);
|
2020-10-16 20:01:03 +02:00
|
|
|
binding.detailControlsPopup.setOnLongClickListener(this);
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.detailControlsPlaylistAppend.setOnClickListener(this);
|
|
|
|
binding.detailControlsDownload.setOnClickListener(this);
|
|
|
|
binding.detailControlsDownload.setOnLongClickListener(this);
|
2020-10-16 20:01:03 +02:00
|
|
|
binding.detailControlsShare.setOnClickListener(this);
|
|
|
|
binding.detailControlsOpenInBrowser.setOnClickListener(this);
|
|
|
|
binding.detailControlsPlayWithKodi.setOnClickListener(this);
|
2021-09-21 21:01:10 +02:00
|
|
|
if (DEBUG) {
|
|
|
|
binding.detailControlsCrashThePlayer.setOnClickListener(
|
2021-11-28 13:42:26 +01:00
|
|
|
v -> VideoDetailPlayerCrasher.onCrashThePlayer(
|
|
|
|
this.getContext(),
|
|
|
|
this.player,
|
|
|
|
getLayoutInflater())
|
2021-11-23 20:11:36 +01:00
|
|
|
);
|
2021-09-21 21:01:10 +02:00
|
|
|
}
|
2021-01-17 05:21:11 +01:00
|
|
|
|
|
|
|
binding.overlayThumbnail.setOnClickListener(this);
|
|
|
|
binding.overlayThumbnail.setOnLongClickListener(this);
|
|
|
|
binding.overlayMetadataLayout.setOnClickListener(this);
|
|
|
|
binding.overlayMetadataLayout.setOnLongClickListener(this);
|
|
|
|
binding.overlayButtonsLayout.setOnClickListener(this);
|
|
|
|
binding.overlayCloseButton.setOnClickListener(this);
|
|
|
|
binding.overlayPlayPauseButton.setOnClickListener(this);
|
|
|
|
|
|
|
|
binding.detailControlsBackground.setOnTouchListener(getOnControlsTouchListener());
|
|
|
|
binding.detailControlsPopup.setOnTouchListener(getOnControlsTouchListener());
|
2019-12-29 22:15:01 +01:00
|
|
|
|
2021-01-14 13:18:34 +01:00
|
|
|
binding.appBarLayout.addOnOffsetChangedListener((layout, verticalOffset) -> {
|
|
|
|
// prevent useless updates to tab layout visibility if nothing changed
|
|
|
|
if (verticalOffset != lastAppBarVerticalOffset) {
|
|
|
|
lastAppBarVerticalOffset = verticalOffset;
|
2021-02-12 10:54:16 +01:00
|
|
|
// the view was scrolled
|
2021-01-14 13:18:34 +01:00
|
|
|
updateTabLayoutVisibility();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2019-12-29 22:15:01 +01:00
|
|
|
setupBottomPlayer();
|
2021-12-21 00:18:58 +01:00
|
|
|
if (!playerHolder.isBound()) {
|
2020-09-17 22:42:35 +02:00
|
|
|
setHeightThumbnail();
|
|
|
|
} else {
|
2021-06-24 10:00:56 +02:00
|
|
|
playerHolder.startService(false, this);
|
2020-09-17 22:42:35 +02:00
|
|
|
}
|
2017-10-14 23:09:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private View.OnTouchListener getOnControlsTouchListener() {
|
2020-11-20 00:54:27 +01:00
|
|
|
return (view, motionEvent) -> {
|
2018-02-16 11:31:25 +01:00
|
|
|
if (!PreferenceManager.getDefaultSharedPreferences(activity)
|
|
|
|
.getBoolean(getString(R.string.show_hold_to_append_key), true)) {
|
|
|
|
return false;
|
|
|
|
}
|
2018-02-11 21:34:32 +01:00
|
|
|
|
|
|
|
if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
|
2021-01-17 05:21:11 +01:00
|
|
|
animate(binding.touchAppendDetail, true, 250, AnimationType.ALPHA,
|
2021-01-16 04:32:01 +01:00
|
|
|
0, () ->
|
2021-01-17 05:21:11 +01:00
|
|
|
animate(binding.touchAppendDetail, false, 1500,
|
2021-01-16 04:32:01 +01:00
|
|
|
AnimationType.ALPHA, 1000));
|
2017-10-14 23:09:49 +02:00
|
|
|
}
|
2018-02-11 21:34:32 +01:00
|
|
|
return false;
|
2017-10-14 23:09:49 +02:00
|
|
|
};
|
2017-03-31 20:15:26 +02:00
|
|
|
}
|
|
|
|
|
2020-03-31 19:20:15 +02:00
|
|
|
private void initThumbnailViews(@NonNull final StreamInfo info) {
|
2021-03-27 14:37:44 +01:00
|
|
|
PicassoHelper.loadThumbnail(info.getThumbnailUrl()).tag(PICASSO_VIDEO_DETAILS_TAG)
|
|
|
|
.into(binding.detailThumbnailImageView, new Callback() {
|
|
|
|
@Override
|
|
|
|
public void onSuccess() {
|
2021-06-07 11:24:13 +02:00
|
|
|
// nothing to do, the image was loaded correctly into the thumbnail
|
2021-03-27 14:37:44 +01:00
|
|
|
}
|
2018-02-16 11:31:25 +01:00
|
|
|
|
2021-03-27 14:37:44 +01:00
|
|
|
@Override
|
|
|
|
public void onError(final Exception e) {
|
|
|
|
showSnackBarError(new ErrorInfo(e, UserAction.LOAD_IMAGE,
|
|
|
|
info.getThumbnailUrl(), info));
|
|
|
|
}
|
|
|
|
});
|
2018-02-16 11:31:25 +01:00
|
|
|
|
2021-03-27 14:37:44 +01:00
|
|
|
PicassoHelper.loadAvatar(info.getSubChannelAvatarUrl()).tag(PICASSO_VIDEO_DETAILS_TAG)
|
|
|
|
.into(binding.detailSubChannelThumbnailView);
|
|
|
|
PicassoHelper.loadAvatar(info.getUploaderAvatarUrl()).tag(PICASSO_VIDEO_DETAILS_TAG)
|
|
|
|
.into(binding.detailUploaderThumbnailView);
|
2016-01-01 23:04:29 +01:00
|
|
|
}
|
|
|
|
|
2017-04-09 19:34:00 +02:00
|
|
|
/*//////////////////////////////////////////////////////////////////////////
|
|
|
|
// OwnStack
|
|
|
|
//////////////////////////////////////////////////////////////////////////*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Stack that contains the "navigation history".<br>
|
|
|
|
* The peek is the current video.
|
|
|
|
*/
|
2020-09-17 22:42:35 +02:00
|
|
|
private static LinkedList<StackItem> stack = new LinkedList<>();
|
2017-04-09 19:34:00 +02:00
|
|
|
|
2015-09-04 02:15:03 +02:00
|
|
|
@Override
|
2020-07-13 03:17:21 +02:00
|
|
|
public boolean onKeyDown(final int keyCode) {
|
2021-06-23 07:11:41 +02:00
|
|
|
return isPlayerAvailable() && player.onKeyDown(keyCode);
|
2017-03-31 20:15:26 +02:00
|
|
|
}
|
2017-04-26 21:32:04 +02:00
|
|
|
|
2017-09-03 08:04:18 +02:00
|
|
|
@Override
|
|
|
|
public boolean onBackPressed() {
|
|
|
|
if (DEBUG) {
|
2020-07-13 03:17:21 +02:00
|
|
|
Log.d(TAG, "onBackPressed() called");
|
2017-04-09 19:34:00 +02:00
|
|
|
}
|
2019-12-29 22:15:01 +01:00
|
|
|
|
2020-01-06 11:39:01 +01:00
|
|
|
// If we are in fullscreen mode just exit from it via first back press
|
2021-06-23 07:11:41 +02:00
|
|
|
if (isPlayerAvailable() && player.isFullscreen()) {
|
2020-07-21 00:43:49 +02:00
|
|
|
if (!DeviceUtils.isTablet(activity)) {
|
2021-01-08 18:35:33 +01:00
|
|
|
player.pause();
|
2020-07-14 19:21:32 +02:00
|
|
|
}
|
2019-12-29 22:15:01 +01:00
|
|
|
restoreDefaultOrientation();
|
2020-10-18 20:19:50 +02:00
|
|
|
setAutoPlay(false);
|
2019-12-29 22:15:01 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-01-06 11:39:01 +01:00
|
|
|
// If we have something in history of played items we replay it here
|
2021-06-23 07:11:41 +02:00
|
|
|
if (isPlayerAvailable()
|
2020-03-10 10:06:38 +01:00
|
|
|
&& player.getPlayQueue() != null
|
|
|
|
&& player.videoPlayerSelected()
|
2020-06-27 05:25:50 +02:00
|
|
|
&& player.getPlayQueue().previous()) {
|
2021-09-19 19:09:11 +02:00
|
|
|
return true; // no code here, as previous() was used in the if
|
2020-01-06 11:39:01 +01:00
|
|
|
}
|
2021-09-19 19:09:11 +02:00
|
|
|
|
2017-04-09 19:34:00 +02:00
|
|
|
// That means that we are on the start of the stack,
|
2019-12-29 22:15:01 +01:00
|
|
|
if (stack.size() <= 1) {
|
|
|
|
restoreDefaultOrientation();
|
2021-09-19 19:09:11 +02:00
|
|
|
return false; // let MainActivity handle the onBack (e.g. to minimize the mini player)
|
2019-12-29 22:15:01 +01:00
|
|
|
}
|
2021-09-19 19:09:11 +02:00
|
|
|
|
2017-04-09 19:34:00 +02:00
|
|
|
// Remove top
|
|
|
|
stack.pop();
|
2017-09-03 08:04:18 +02:00
|
|
|
// Get stack item from the new top
|
2021-09-19 19:09:11 +02:00
|
|
|
setupFromHistoryItem(Objects.requireNonNull(stack.peek()));
|
2017-04-26 21:32:04 +02:00
|
|
|
|
2020-01-09 16:28:06 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-07-12 02:59:47 +02:00
|
|
|
private void setupFromHistoryItem(final StackItem item) {
|
2020-10-18 20:19:50 +02:00
|
|
|
setAutoPlay(false);
|
2021-08-31 11:10:52 +02:00
|
|
|
hideMainPlayerOnLoadingNewStream();
|
2019-12-29 22:15:01 +01:00
|
|
|
|
2020-10-18 20:19:50 +02:00
|
|
|
setInitialData(item.getServiceId(), item.getUrl(),
|
|
|
|
item.getTitle() == null ? "" : item.getTitle(), item.getPlayQueue());
|
2020-03-10 10:06:38 +01:00
|
|
|
startLoading(false);
|
|
|
|
|
|
|
|
// Maybe an item was deleted in background activity
|
2020-07-14 19:21:32 +02:00
|
|
|
if (item.getPlayQueue().getItem() == null) {
|
|
|
|
return;
|
|
|
|
}
|
2020-02-05 06:59:30 +01:00
|
|
|
|
2020-07-12 02:59:47 +02:00
|
|
|
final PlayQueueItem playQueueItem = item.getPlayQueue().getItem();
|
2020-02-05 06:59:30 +01:00
|
|
|
// Update title, url, uploader from the last item in the stack (it's current now)
|
2021-06-23 07:11:41 +02:00
|
|
|
final boolean isPlayerStopped = !isPlayerAvailable() || player.isStopped();
|
2020-07-14 19:21:32 +02:00
|
|
|
if (playQueueItem != null && isPlayerStopped) {
|
|
|
|
updateOverlayData(playQueueItem.getTitle(),
|
|
|
|
playQueueItem.getUploader(), playQueueItem.getThumbnailUrl());
|
|
|
|
}
|
2017-04-09 19:34:00 +02:00
|
|
|
}
|
|
|
|
|
2017-03-31 20:15:26 +02:00
|
|
|
/*//////////////////////////////////////////////////////////////////////////
|
2017-09-03 08:04:18 +02:00
|
|
|
// Info loading and handling
|
2017-03-31 20:15:26 +02:00
|
|
|
//////////////////////////////////////////////////////////////////////////*/
|
|
|
|
|
2017-09-03 08:04:18 +02:00
|
|
|
@Override
|
|
|
|
protected void doInitialLoadLogic() {
|
2020-07-14 19:21:32 +02:00
|
|
|
if (wasCleared()) {
|
|
|
|
return;
|
|
|
|
}
|
2019-12-29 22:15:01 +01:00
|
|
|
|
2020-03-31 19:20:15 +02:00
|
|
|
if (currentInfo == null) {
|
|
|
|
prepareAndLoadInfo();
|
|
|
|
} else {
|
2020-09-17 22:42:35 +02:00
|
|
|
prepareAndHandleInfoIfNeededAfterDelay(currentInfo, false, 50);
|
2020-03-31 19:20:15 +02:00
|
|
|
}
|
2017-03-31 20:15:26 +02:00
|
|
|
}
|
|
|
|
|
2020-10-18 20:19:50 +02:00
|
|
|
public void selectAndLoadVideo(final int newServiceId,
|
|
|
|
@Nullable final String newUrl,
|
|
|
|
@NonNull final String newTitle,
|
|
|
|
@Nullable final PlayQueue newQueue) {
|
2021-06-23 07:11:41 +02:00
|
|
|
if (isPlayerAvailable() && newQueue != null && playQueue != null
|
2021-07-21 18:07:26 +02:00
|
|
|
&& playQueue.getItem() != null && !playQueue.getItem().getUrl().equals(newUrl)) {
|
2020-10-18 20:19:50 +02:00
|
|
|
// Preloading can be disabled since playback is surely being replaced.
|
2020-09-07 18:34:10 +02:00
|
|
|
player.disablePreloadingOfCurrentTrack();
|
|
|
|
}
|
2020-10-18 20:19:50 +02:00
|
|
|
|
|
|
|
setInitialData(newServiceId, newUrl, newTitle, newQueue);
|
2020-03-10 10:06:38 +01:00
|
|
|
startLoading(false, true);
|
2017-04-09 19:34:00 +02:00
|
|
|
}
|
|
|
|
|
2020-09-17 22:42:35 +02:00
|
|
|
private void prepareAndHandleInfoIfNeededAfterDelay(final StreamInfo info,
|
|
|
|
final boolean scrollToTop,
|
|
|
|
final long delay) {
|
|
|
|
new Handler(Looper.getMainLooper()).postDelayed(() -> {
|
|
|
|
if (activity == null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Data can already be drawn, don't spend time twice
|
2021-01-17 05:21:11 +01:00
|
|
|
if (info.getName().equals(binding.detailVideoTitleView.getText().toString())) {
|
2020-09-17 22:42:35 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
prepareAndHandleInfo(info, scrollToTop);
|
|
|
|
}, delay);
|
|
|
|
}
|
|
|
|
|
2020-04-02 13:51:10 +02:00
|
|
|
private void prepareAndHandleInfo(final StreamInfo info, final boolean scrollToTop) {
|
2020-03-31 19:20:15 +02:00
|
|
|
if (DEBUG) {
|
|
|
|
Log.d(TAG, "prepareAndHandleInfo() called with: "
|
|
|
|
+ "info = [" + info + "], scrollToTop = [" + scrollToTop + "]");
|
|
|
|
}
|
2017-04-28 05:58:50 +02:00
|
|
|
|
2017-09-03 08:04:18 +02:00
|
|
|
showLoading();
|
2018-10-02 17:09:16 +02:00
|
|
|
initTabs();
|
2017-04-26 21:32:04 +02:00
|
|
|
|
2020-07-14 19:21:32 +02:00
|
|
|
if (scrollToTop) {
|
|
|
|
scrollToTop();
|
|
|
|
}
|
2018-12-19 06:28:59 +01:00
|
|
|
handleResult(info);
|
|
|
|
showContent();
|
|
|
|
|
2017-04-09 19:34:00 +02:00
|
|
|
}
|
2017-03-31 20:15:26 +02:00
|
|
|
|
2017-09-03 08:04:18 +02:00
|
|
|
protected void prepareAndLoadInfo() {
|
2019-12-31 17:06:39 +01:00
|
|
|
scrollToTop();
|
2017-09-03 08:04:18 +02:00
|
|
|
startLoading(false);
|
|
|
|
}
|
2017-04-28 05:58:50 +02:00
|
|
|
|
2017-09-03 08:04:18 +02:00
|
|
|
@Override
|
2020-07-12 02:59:47 +02:00
|
|
|
public void startLoading(final boolean forceLoad) {
|
2017-09-03 08:04:18 +02:00
|
|
|
super.startLoading(forceLoad);
|
|
|
|
|
2018-10-02 17:09:16 +02:00
|
|
|
initTabs();
|
2017-09-03 08:04:18 +02:00
|
|
|
currentInfo = null;
|
2020-03-31 19:20:15 +02:00
|
|
|
if (currentWorker != null) {
|
|
|
|
currentWorker.dispose();
|
|
|
|
}
|
2017-09-03 08:04:18 +02:00
|
|
|
|
2020-03-10 10:06:38 +01:00
|
|
|
runWorker(forceLoad, stack.isEmpty());
|
|
|
|
}
|
|
|
|
|
2020-07-12 02:59:47 +02:00
|
|
|
private void startLoading(final boolean forceLoad, final boolean addToBackStack) {
|
2020-07-14 19:21:32 +02:00
|
|
|
super.startLoading(forceLoad);
|
2020-03-10 10:06:38 +01:00
|
|
|
|
|
|
|
initTabs();
|
|
|
|
currentInfo = null;
|
2020-07-14 19:21:32 +02:00
|
|
|
if (currentWorker != null) {
|
|
|
|
currentWorker.dispose();
|
|
|
|
}
|
2020-03-10 10:06:38 +01:00
|
|
|
|
|
|
|
runWorker(forceLoad, addToBackStack);
|
|
|
|
}
|
|
|
|
|
2020-07-12 02:59:47 +02:00
|
|
|
private void runWorker(final boolean forceLoad, final boolean addToBackStack) {
|
2020-07-13 03:17:21 +02:00
|
|
|
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity);
|
2017-09-03 08:04:18 +02:00
|
|
|
currentWorker = ExtractorHelper.getStreamInfo(serviceId, url, forceLoad)
|
|
|
|
.subscribeOn(Schedulers.io())
|
|
|
|
.observeOn(AndroidSchedulers.mainThread())
|
2020-11-20 00:54:27 +01:00
|
|
|
.subscribe(result -> {
|
2018-02-11 21:34:32 +01:00
|
|
|
isLoading.set(false);
|
2021-08-31 11:10:52 +02:00
|
|
|
hideMainPlayerOnLoadingNewStream();
|
2020-04-10 14:25:00 +02:00
|
|
|
if (result.getAgeLimit() != NO_AGE_LIMIT && !prefs.getBoolean(
|
|
|
|
getString(R.string.show_age_restricted_content), false)) {
|
2020-04-11 09:30:12 +02:00
|
|
|
hideAgeRestrictedContent();
|
2020-04-10 14:25:00 +02:00
|
|
|
} else {
|
|
|
|
handleResult(result);
|
|
|
|
showContent();
|
2020-07-13 03:17:21 +02:00
|
|
|
if (addToBackStack) {
|
2020-07-14 19:21:32 +02:00
|
|
|
if (playQueue == null) {
|
|
|
|
playQueue = new SinglePlayQueue(result);
|
|
|
|
}
|
|
|
|
if (stack.isEmpty() || !stack.peek().getPlayQueue().equals(playQueue)) {
|
2020-10-18 20:19:50 +02:00
|
|
|
stack.push(new StackItem(serviceId, url, title, playQueue));
|
2020-07-14 19:21:32 +02:00
|
|
|
}
|
|
|
|
}
|
2021-01-19 08:13:44 +01:00
|
|
|
|
2020-07-14 19:21:32 +02:00
|
|
|
if (isAutoplayEnabled()) {
|
2021-07-27 11:02:32 +02:00
|
|
|
openVideoPlayerAutoFullscreen();
|
2020-07-13 03:17:21 +02:00
|
|
|
}
|
2020-03-10 10:06:38 +01:00
|
|
|
}
|
2020-12-11 14:55:47 +01:00
|
|
|
}, throwable -> showError(new ErrorInfo(throwable, UserAction.REQUESTED_STREAM,
|
|
|
|
url == null ? "no url" : url, serviceId)));
|
2018-10-02 17:09:16 +02:00
|
|
|
}
|
2018-09-23 03:32:19 +02:00
|
|
|
|
2021-01-14 13:18:34 +01:00
|
|
|
/*//////////////////////////////////////////////////////////////////////////
|
|
|
|
// Tabs
|
|
|
|
//////////////////////////////////////////////////////////////////////////*/
|
|
|
|
|
2018-10-02 17:09:16 +02:00
|
|
|
private void initTabs() {
|
2019-03-24 02:01:28 +01:00
|
|
|
if (pageAdapter.getCount() != 0) {
|
2021-01-17 05:21:11 +01:00
|
|
|
selectedTabTag = pageAdapter.getItemTitle(binding.viewPager.getCurrentItem());
|
2019-03-24 02:01:28 +01:00
|
|
|
}
|
2018-10-02 17:09:16 +02:00
|
|
|
pageAdapter.clearAllItems();
|
2020-12-08 21:47:02 +01:00
|
|
|
tabIcons.clear();
|
2021-01-14 11:06:37 +01:00
|
|
|
tabContentDescriptions.clear();
|
2018-10-02 17:09:16 +02:00
|
|
|
|
2020-07-12 02:59:47 +02:00
|
|
|
if (shouldShowComments()) {
|
2020-07-14 19:21:32 +02:00
|
|
|
pageAdapter.addFragment(
|
2020-10-18 20:19:50 +02:00
|
|
|
CommentsFragment.getInstance(serviceId, url, title), COMMENTS_TAB_TAG);
|
2021-03-27 15:45:49 +01:00
|
|
|
tabIcons.add(R.drawable.ic_comment);
|
2021-01-14 11:06:37 +01:00
|
|
|
tabContentDescriptions.add(R.string.comments_tab_description);
|
2018-10-02 17:09:16 +02:00
|
|
|
}
|
|
|
|
|
2021-03-31 20:16:08 +02:00
|
|
|
if (showRelatedItems && binding.relatedItemsLayout == null) {
|
2021-01-15 11:26:09 +01:00
|
|
|
// temp empty fragment. will be updated in handleResult
|
2021-05-05 08:55:54 +02:00
|
|
|
pageAdapter.addFragment(EmptyFragment.newInstance(false), RELATED_TAB_TAG);
|
2021-03-27 15:45:49 +01:00
|
|
|
tabIcons.add(R.drawable.ic_art_track);
|
2021-03-31 20:16:08 +02:00
|
|
|
tabContentDescriptions.add(R.string.related_items_tab_description);
|
2018-10-02 17:09:16 +02:00
|
|
|
}
|
|
|
|
|
2020-12-29 22:20:33 +01:00
|
|
|
if (showDescription) {
|
|
|
|
// temp empty fragment. will be updated in handleResult
|
2021-05-05 08:55:54 +02:00
|
|
|
pageAdapter.addFragment(EmptyFragment.newInstance(false), DESCRIPTION_TAB_TAG);
|
2021-03-27 15:45:49 +01:00
|
|
|
tabIcons.add(R.drawable.ic_description);
|
2021-01-14 11:06:37 +01:00
|
|
|
tabContentDescriptions.add(R.string.description_tab_description);
|
2020-12-29 22:20:33 +01:00
|
|
|
}
|
2021-01-15 11:26:09 +01:00
|
|
|
|
|
|
|
if (pageAdapter.getCount() == 0) {
|
2021-05-05 08:55:54 +02:00
|
|
|
pageAdapter.addFragment(EmptyFragment.newInstance(true), EMPTY_TAB_TAG);
|
2021-01-15 11:26:09 +01:00
|
|
|
}
|
2018-10-02 18:00:11 +02:00
|
|
|
pageAdapter.notifyDataSetUpdate();
|
|
|
|
|
2021-01-14 13:18:34 +01:00
|
|
|
if (pageAdapter.getCount() >= 2) {
|
2020-08-16 10:24:58 +02:00
|
|
|
final int position = pageAdapter.getItemPositionByTitle(selectedTabTag);
|
2020-03-31 19:20:15 +02:00
|
|
|
if (position != -1) {
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.viewPager.setCurrentItem(position);
|
2020-03-31 19:20:15 +02:00
|
|
|
}
|
2021-01-14 11:06:37 +01:00
|
|
|
updateTabIconsAndContentDescriptions();
|
2018-10-02 17:09:16 +02:00
|
|
|
}
|
2021-02-12 10:54:16 +01:00
|
|
|
// the page adapter now contains tabs: show the tab layout
|
2021-01-14 13:18:34 +01:00
|
|
|
updateTabLayoutVisibility();
|
2018-09-23 03:32:19 +02:00
|
|
|
}
|
|
|
|
|
2020-12-08 21:47:02 +01:00
|
|
|
/**
|
|
|
|
* To be called whenever {@link #pageAdapter} is modified, since that triggers a refresh in
|
2021-01-14 11:06:37 +01:00
|
|
|
* {@link FragmentVideoDetailBinding#tabLayout} resetting all tab's icons and content
|
|
|
|
* descriptions. This reads icons from {@link #tabIcons} and content descriptions from
|
|
|
|
* {@link #tabContentDescriptions}, which are all set in {@link #initTabs()}.
|
2020-12-08 21:47:02 +01:00
|
|
|
*/
|
2021-01-14 11:06:37 +01:00
|
|
|
private void updateTabIconsAndContentDescriptions() {
|
2020-12-08 21:47:02 +01:00
|
|
|
for (int i = 0; i < tabIcons.size(); ++i) {
|
|
|
|
final TabLayout.Tab tab = binding.tabLayout.getTabAt(i);
|
|
|
|
if (tab != null) {
|
|
|
|
tab.setIcon(tabIcons.get(i));
|
2021-01-14 11:06:37 +01:00
|
|
|
tab.setContentDescription(tabContentDescriptions.get(i));
|
2020-12-08 21:47:02 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void updateTabs(@NonNull final StreamInfo info) {
|
2021-03-31 20:16:08 +02:00
|
|
|
if (showRelatedItems) {
|
|
|
|
if (binding.relatedItemsLayout == null) { // phone
|
|
|
|
pageAdapter.updateItem(RELATED_TAB_TAG, RelatedItemsFragment.getInstance(info));
|
2021-01-14 22:34:55 +01:00
|
|
|
} else { // tablet + TV
|
2020-12-08 21:47:02 +01:00
|
|
|
getChildFragmentManager().beginTransaction()
|
2021-03-31 20:16:08 +02:00
|
|
|
.replace(R.id.relatedItemsLayout, RelatedItemsFragment.getInstance(info))
|
2020-12-08 21:47:02 +01:00
|
|
|
.commitAllowingStateLoss();
|
2021-03-31 20:16:08 +02:00
|
|
|
binding.relatedItemsLayout.setVisibility(
|
2021-06-23 07:11:41 +02:00
|
|
|
isPlayerAvailable() && player.isFullscreen() ? View.GONE : View.VISIBLE);
|
2020-12-08 21:47:02 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-29 22:20:33 +01:00
|
|
|
if (showDescription) {
|
2021-02-12 10:54:16 +01:00
|
|
|
pageAdapter.updateItem(DESCRIPTION_TAB_TAG, new DescriptionFragment(info));
|
2020-12-29 22:20:33 +01:00
|
|
|
}
|
|
|
|
|
2021-02-12 10:54:16 +01:00
|
|
|
binding.viewPager.setVisibility(View.VISIBLE);
|
|
|
|
// make sure the tab layout is visible
|
|
|
|
updateTabLayoutVisibility();
|
2020-12-08 21:47:02 +01:00
|
|
|
pageAdapter.notifyDataSetUpdate();
|
2021-01-14 11:06:37 +01:00
|
|
|
updateTabIconsAndContentDescriptions();
|
2020-12-08 21:47:02 +01:00
|
|
|
}
|
|
|
|
|
2018-10-02 17:09:16 +02:00
|
|
|
private boolean shouldShowComments() {
|
|
|
|
try {
|
2019-02-15 20:53:26 +01:00
|
|
|
return showComments && NewPipe.getService(serviceId)
|
|
|
|
.getServiceInfo()
|
|
|
|
.getMediaCapabilities()
|
|
|
|
.contains(COMMENTS);
|
2020-08-16 10:24:58 +02:00
|
|
|
} catch (final ExtractionException e) {
|
2018-10-02 17:09:16 +02:00
|
|
|
return false;
|
2018-09-23 03:32:19 +02:00
|
|
|
}
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
2017-04-28 05:58:50 +02:00
|
|
|
|
2021-01-14 13:18:34 +01:00
|
|
|
public void updateTabLayoutVisibility() {
|
2021-04-01 15:07:21 +02:00
|
|
|
|
|
|
|
if (binding == null) {
|
|
|
|
//If binding is null we do not need to and should not do anything with its object(s)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-02-12 10:54:16 +01:00
|
|
|
if (pageAdapter.getCount() < 2 || binding.viewPager.getVisibility() != View.VISIBLE) {
|
|
|
|
// hide tab layout if there is only one tab or if the view pager is also hidden
|
2021-01-14 13:18:34 +01:00
|
|
|
binding.tabLayout.setVisibility(View.GONE);
|
|
|
|
} else {
|
2021-02-12 10:54:16 +01:00
|
|
|
// call `post()` to be sure `viewPager.getHitRect()`
|
|
|
|
// is up to date and not being currently recomputed
|
2021-01-14 13:18:34 +01:00
|
|
|
binding.tabLayout.post(() -> {
|
|
|
|
if (getContext() != null) {
|
|
|
|
final Rect pagerHitRect = new Rect();
|
|
|
|
binding.viewPager.getHitRect(pagerHitRect);
|
|
|
|
|
|
|
|
final Point displaySize = new Point();
|
|
|
|
Objects.requireNonNull(ContextCompat.getSystemService(getContext(),
|
|
|
|
WindowManager.class)).getDefaultDisplay().getSize(displaySize);
|
|
|
|
|
|
|
|
final int viewPagerVisibleHeight = displaySize.y - pagerHitRect.top;
|
2021-02-12 10:54:16 +01:00
|
|
|
// see TabLayout.DEFAULT_HEIGHT, which is equal to 48dp
|
2021-01-14 13:18:34 +01:00
|
|
|
final float tabLayoutHeight = TypedValue.applyDimension(
|
|
|
|
TypedValue.COMPLEX_UNIT_DIP, 48, getResources().getDisplayMetrics());
|
|
|
|
|
|
|
|
if (viewPagerVisibleHeight > tabLayoutHeight * 2) {
|
|
|
|
// no translation at all when viewPagerVisibleHeight > tabLayout.height * 3
|
|
|
|
binding.tabLayout.setTranslationY(
|
|
|
|
Math.max(0, tabLayoutHeight * 3 - viewPagerVisibleHeight));
|
|
|
|
binding.tabLayout.setVisibility(View.VISIBLE);
|
|
|
|
} else {
|
2021-02-12 10:54:16 +01:00
|
|
|
// view pager is not visible enough
|
2021-01-14 13:18:34 +01:00
|
|
|
binding.tabLayout.setVisibility(View.GONE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-31 17:06:39 +01:00
|
|
|
public void scrollToTop() {
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.appBarLayout.setExpanded(true, true);
|
2021-02-12 10:54:16 +01:00
|
|
|
// notify tab layout of scrolling
|
2021-01-14 13:18:34 +01:00
|
|
|
updateTabLayoutVisibility();
|
2019-12-31 17:06:39 +01:00
|
|
|
}
|
|
|
|
|
2017-09-03 08:04:18 +02:00
|
|
|
/*//////////////////////////////////////////////////////////////////////////
|
|
|
|
// Play Utils
|
|
|
|
//////////////////////////////////////////////////////////////////////////*/
|
2017-04-09 19:34:00 +02:00
|
|
|
|
2021-06-23 16:07:04 +02:00
|
|
|
private void toggleFullscreenIfInFullscreenMode() {
|
|
|
|
// If a user watched video inside fullscreen mode and than chose another player
|
|
|
|
// return to non-fullscreen mode
|
|
|
|
if (isPlayerAvailable() && player.isFullscreen()) {
|
|
|
|
player.toggleFullscreen();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-13 02:02:07 +02:00
|
|
|
private void openBackgroundPlayer(final boolean append) {
|
2020-07-12 02:59:47 +02:00
|
|
|
final AudioStream audioStream = currentInfo.getAudioStreams()
|
2018-02-16 11:31:25 +01:00
|
|
|
.get(ListHelper.getDefaultAudioFormat(activity, currentInfo.getAudioStreams()));
|
2017-03-31 20:15:26 +02:00
|
|
|
|
2020-07-14 19:21:32 +02:00
|
|
|
final boolean useExternalAudioPlayer = PreferenceManager
|
|
|
|
.getDefaultSharedPreferences(activity)
|
2017-09-03 08:04:18 +02:00
|
|
|
.getBoolean(activity.getString(R.string.use_external_audio_player_key), false);
|
2017-04-28 05:58:50 +02:00
|
|
|
|
2021-06-23 16:07:04 +02:00
|
|
|
toggleFullscreenIfInFullscreenMode();
|
2019-12-31 03:07:07 +01:00
|
|
|
|
2022-01-17 20:52:07 +01:00
|
|
|
if (isPlayerAvailable()) {
|
|
|
|
// FIXME Workaround #7427
|
|
|
|
player.setRecovery();
|
|
|
|
}
|
|
|
|
|
2020-08-27 22:55:57 +02:00
|
|
|
if (!useExternalAudioPlayer) {
|
2017-10-13 02:02:07 +02:00
|
|
|
openNormalBackgroundPlayer(append);
|
2017-04-26 21:32:04 +02:00
|
|
|
} else {
|
2018-06-19 03:27:30 +02:00
|
|
|
startOnExternalPlayer(activity, currentInfo, audioStream);
|
2017-04-26 21:32:04 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-13 02:02:07 +02:00
|
|
|
private void openPopupPlayer(final boolean append) {
|
2018-01-04 07:53:31 +01:00
|
|
|
if (!PermissionHelper.isPopupEnabled(activity)) {
|
|
|
|
PermissionHelper.showPopupEnablementToast(activity);
|
2017-09-03 08:04:18 +02:00
|
|
|
return;
|
|
|
|
}
|
2017-04-26 21:32:04 +02:00
|
|
|
|
2019-12-29 22:15:01 +01:00
|
|
|
// See UI changes while remote playQueue changes
|
2021-06-23 07:11:41 +02:00
|
|
|
if (!isPlayerAvailable()) {
|
2021-06-24 10:00:56 +02:00
|
|
|
playerHolder.startService(false, this);
|
2022-01-17 20:52:07 +01:00
|
|
|
} else {
|
|
|
|
// FIXME Workaround #7427
|
|
|
|
player.setRecovery();
|
2020-07-14 19:21:32 +02:00
|
|
|
}
|
2019-12-29 22:15:01 +01:00
|
|
|
|
2021-06-23 16:07:04 +02:00
|
|
|
toggleFullscreenIfInFullscreenMode();
|
2019-12-31 03:07:07 +01:00
|
|
|
|
2020-07-12 02:59:47 +02:00
|
|
|
final PlayQueue queue = setupPlayQueueForIntent(append);
|
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
|
|
|
if (append) { //resumePlayback: false
|
|
|
|
NavigationHelper.enqueueOnPlayer(activity, queue, PlayerType.POPUP);
|
2017-10-13 02:02:07 +02:00
|
|
|
} else {
|
2020-07-14 19:21:32 +02:00
|
|
|
replaceQueueIfUserConfirms(() -> NavigationHelper
|
|
|
|
.playOnPopupPlayer(activity, queue, true));
|
2017-10-13 02:02:07 +02:00
|
|
|
}
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
2017-04-26 21:32:04 +02:00
|
|
|
|
2021-08-31 11:28:34 +02:00
|
|
|
/**
|
|
|
|
* Opens the video player, in fullscreen if needed. In order to open fullscreen, the activity
|
|
|
|
* is toggled to landscape orientation (which will then cause fullscreen mode).
|
|
|
|
*
|
|
|
|
* @param directlyFullscreenIfApplicable whether to open fullscreen if we are not already
|
|
|
|
* in landscape and screen orientation is locked
|
|
|
|
*/
|
2021-01-19 08:13:44 +01:00
|
|
|
public void openVideoPlayer(final boolean directlyFullscreenIfApplicable) {
|
|
|
|
if (directlyFullscreenIfApplicable
|
2021-03-25 22:54:55 +01:00
|
|
|
&& !DeviceUtils.isLandscape(requireContext())
|
2021-01-19 08:13:44 +01:00
|
|
|
&& PlayerHelper.globalScreenOrientationLocked(requireContext())) {
|
2021-07-27 10:31:25 +02:00
|
|
|
// Make sure the bottom sheet turns out expanded. When this code kicks in the bottom
|
|
|
|
// sheet could not have fully expanded yet, and thus be in the STATE_SETTLING state.
|
|
|
|
// When the activity is rotated, and its state is saved and then restored, the bottom
|
|
|
|
// sheet would forget what it was doing, since even if STATE_SETTLING is restored, it
|
|
|
|
// doesn't tell which state it was settling to, and thus the bottom sheet settles to
|
|
|
|
// STATE_COLLAPSED. This can be solved by manually setting the state that will be
|
|
|
|
// restored (i.e. bottomSheetState) to STATE_EXPANDED.
|
|
|
|
bottomSheetState = BottomSheetBehavior.STATE_EXPANDED;
|
2021-03-28 12:03:33 +02:00
|
|
|
// toggle landscape in order to open directly in fullscreen
|
2021-01-19 08:13:44 +01:00
|
|
|
onScreenRotationButtonClicked();
|
|
|
|
}
|
|
|
|
|
2018-02-16 11:31:25 +01:00
|
|
|
if (PreferenceManager.getDefaultSharedPreferences(activity)
|
|
|
|
.getBoolean(this.getString(R.string.use_external_video_player_key), false)) {
|
2020-07-23 23:43:09 +02:00
|
|
|
showExternalPlaybackDialog();
|
2017-04-26 21:32:04 +02:00
|
|
|
} else {
|
2020-06-27 05:25:50 +02:00
|
|
|
replaceQueueIfUserConfirms(this::openMainPlayer);
|
2017-04-26 21:32:04 +02:00
|
|
|
}
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
2017-04-26 21:32:04 +02:00
|
|
|
|
2021-08-31 11:28:34 +02:00
|
|
|
/**
|
|
|
|
* If the option to start directly fullscreen is enabled, calls
|
|
|
|
* {@link #openVideoPlayer(boolean)} with {@code directlyFullscreenIfApplicable = true}, so that
|
|
|
|
* if the user is not already in landscape and he has screen orientation locked the activity
|
|
|
|
* rotates and fullscreen starts. Otherwise, if the option to start directly fullscreen is
|
|
|
|
* disabled, calls {@link #openVideoPlayer(boolean)} with {@code directlyFullscreenIfApplicable
|
|
|
|
* = false}, hence preventing it from going directly fullscreen.
|
|
|
|
*/
|
2021-07-27 11:02:32 +02:00
|
|
|
public void openVideoPlayerAutoFullscreen() {
|
|
|
|
openVideoPlayer(PlayerHelper.isStartMainPlayerFullscreenEnabled(requireContext()));
|
|
|
|
}
|
|
|
|
|
2017-10-13 02:02:07 +02:00
|
|
|
private void openNormalBackgroundPlayer(final boolean append) {
|
2019-12-29 22:15:01 +01:00
|
|
|
// See UI changes while remote playQueue changes
|
2021-06-23 07:11:41 +02:00
|
|
|
if (!isPlayerAvailable()) {
|
2021-06-24 10:00:56 +02:00
|
|
|
playerHolder.startService(false, this);
|
2020-07-14 19:21:32 +02:00
|
|
|
}
|
2019-12-29 22:15:01 +01:00
|
|
|
|
2020-07-12 02:59:47 +02:00
|
|
|
final PlayQueue queue = setupPlayQueueForIntent(append);
|
2017-10-14 06:07:19 +02:00
|
|
|
if (append) {
|
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, queue, PlayerType.AUDIO);
|
2017-10-14 06:07:19 +02:00
|
|
|
} else {
|
2020-07-14 19:21:32 +02:00
|
|
|
replaceQueueIfUserConfirms(() -> NavigationHelper
|
|
|
|
.playOnBackgroundPlayer(activity, queue, true));
|
2017-10-14 06:07:19 +02:00
|
|
|
}
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
2017-04-26 21:32:04 +02:00
|
|
|
|
2020-06-27 05:25:50 +02:00
|
|
|
private void openMainPlayer() {
|
2021-06-23 07:11:41 +02:00
|
|
|
if (!isPlayerServiceAvailable()) {
|
2021-06-24 10:00:56 +02:00
|
|
|
playerHolder.startService(autoPlayEnabled, this);
|
2020-07-14 19:21:32 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (currentInfo == null) {
|
2020-07-12 02:59:47 +02:00
|
|
|
return;
|
2020-01-17 15:37:53 +01:00
|
|
|
}
|
2019-12-29 22:15:01 +01:00
|
|
|
|
2020-07-12 02:59:47 +02:00
|
|
|
final PlayQueue queue = setupPlayQueueForIntent(false);
|
2019-12-29 22:15:01 +01:00
|
|
|
|
2020-07-14 19:21:32 +02:00
|
|
|
// Video view can have elements visible from popup,
|
|
|
|
// We hide it here but once it ready the view will be shown in handleIntent()
|
2020-11-02 18:06:14 +01:00
|
|
|
if (playerService.getView() != null) {
|
|
|
|
playerService.getView().setVisibility(View.GONE);
|
|
|
|
}
|
2020-01-17 15:37:53 +01:00
|
|
|
addVideoPlayerView();
|
2019-12-29 22:15:01 +01:00
|
|
|
|
2021-01-19 08:13:44 +01:00
|
|
|
final Intent playerIntent = NavigationHelper.getPlayerIntent(requireContext(),
|
2021-10-20 22:11:46 +02:00
|
|
|
MainPlayer.class, queue, true, autoPlayEnabled);
|
2021-06-24 10:00:56 +02:00
|
|
|
ContextCompat.startForegroundService(activity, playerIntent);
|
2019-12-29 22:15:01 +01:00
|
|
|
}
|
|
|
|
|
2021-08-31 11:10:52 +02:00
|
|
|
/**
|
|
|
|
* When the video detail fragment is already showing details for a video and the user opens a
|
|
|
|
* new one, the video detail fragment changes all of its old data to the new stream, so if there
|
|
|
|
* is a video player currently open it should be hidden. This method does exactly that. If
|
|
|
|
* autoplay is enabled, the underlying player is not stopped completely, since it is going to
|
|
|
|
* be reused in a few milliseconds and the flickering would be annoying.
|
|
|
|
*/
|
|
|
|
private void hideMainPlayerOnLoadingNewStream() {
|
2021-06-23 07:11:41 +02:00
|
|
|
if (!isPlayerServiceAvailable()
|
2020-07-14 19:21:32 +02:00
|
|
|
|| playerService.getView() == null
|
|
|
|
|| !player.videoPlayerSelected()) {
|
2019-12-29 22:15:01 +01:00
|
|
|
return;
|
2020-07-14 19:21:32 +02:00
|
|
|
}
|
2019-12-29 22:15:01 +01:00
|
|
|
|
|
|
|
removeVideoPlayerView();
|
2021-08-31 11:10:52 +02:00
|
|
|
if (isAutoplayEnabled()) {
|
|
|
|
playerService.stopForImmediateReusing();
|
|
|
|
playerService.getView().setVisibility(View.GONE);
|
|
|
|
} else {
|
|
|
|
playerHolder.stopService();
|
|
|
|
}
|
2019-12-29 22:15:01 +01:00
|
|
|
}
|
|
|
|
|
2020-07-12 02:59:47 +02:00
|
|
|
private PlayQueue setupPlayQueueForIntent(final boolean append) {
|
2020-07-14 19:21:32 +02:00
|
|
|
if (append) {
|
|
|
|
return new SinglePlayQueue(currentInfo);
|
|
|
|
}
|
2019-12-29 22:15:01 +01:00
|
|
|
|
|
|
|
PlayQueue queue = playQueue;
|
|
|
|
// Size can be 0 because queue removes bad stream automatically when error occurs
|
2020-11-18 23:54:16 +01:00
|
|
|
if (queue == null || queue.isEmpty()) {
|
2019-12-29 22:15:01 +01:00
|
|
|
queue = new SinglePlayQueue(currentInfo);
|
2020-07-14 19:21:32 +02:00
|
|
|
}
|
2019-12-29 22:15:01 +01:00
|
|
|
|
|
|
|
return queue;
|
|
|
|
}
|
|
|
|
|
2017-09-03 08:04:18 +02:00
|
|
|
/*//////////////////////////////////////////////////////////////////////////
|
|
|
|
// Utils
|
|
|
|
//////////////////////////////////////////////////////////////////////////*/
|
|
|
|
|
2020-10-18 20:19:50 +02:00
|
|
|
public void setAutoPlay(final boolean autoPlay) {
|
|
|
|
this.autoPlayEnabled = autoPlay;
|
2017-03-31 20:15:26 +02:00
|
|
|
}
|
|
|
|
|
2018-06-19 03:27:30 +02:00
|
|
|
private void startOnExternalPlayer(@NonNull final Context context,
|
|
|
|
@NonNull final StreamInfo info,
|
|
|
|
@NonNull final Stream selectedStream) {
|
|
|
|
NavigationHelper.playOnExternalPlayer(context, currentInfo.getName(),
|
2020-04-16 17:35:42 +02:00
|
|
|
currentInfo.getSubChannelName(), selectedStream);
|
2018-06-19 03:27:30 +02:00
|
|
|
|
|
|
|
final HistoryRecordManager recordManager = new HistoryRecordManager(requireContext());
|
|
|
|
disposables.add(recordManager.onViewed(info).onErrorComplete()
|
|
|
|
.subscribe(
|
2020-03-31 19:20:15 +02:00
|
|
|
ignored -> { /* successful */ },
|
2018-06-19 03:27:30 +02:00
|
|
|
error -> Log.e(TAG, "Register view failure: ", error)
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
2019-12-29 22:15:01 +01:00
|
|
|
private boolean isExternalPlayerEnabled() {
|
2020-09-09 20:45:42 +02:00
|
|
|
return PreferenceManager.getDefaultSharedPreferences(requireContext())
|
2019-12-29 22:15:01 +01:00
|
|
|
.getBoolean(getString(R.string.use_external_video_player_key), false);
|
|
|
|
}
|
|
|
|
|
2020-10-18 20:19:50 +02:00
|
|
|
// This method overrides default behaviour when setAutoPlay() is called.
|
2020-01-08 17:16:50 +01:00
|
|
|
// Don't auto play if the user selected an external player or disabled it in settings
|
2019-12-29 22:15:01 +01:00
|
|
|
private boolean isAutoplayEnabled() {
|
2020-01-13 17:24:28 +01:00
|
|
|
return autoPlayEnabled
|
2020-01-08 17:16:50 +01:00
|
|
|
&& !isExternalPlayerEnabled()
|
2021-06-23 07:11:41 +02:00
|
|
|
&& (!isPlayerAvailable() || player.videoPlayerSelected())
|
2020-01-26 05:33:52 +01:00
|
|
|
&& bottomSheetState != BottomSheetBehavior.STATE_HIDDEN
|
2020-09-09 20:45:42 +02:00
|
|
|
&& PlayerHelper.isAutoplayAllowedByUser(requireContext());
|
2019-12-29 22:15:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private void addVideoPlayerView() {
|
2021-06-23 07:11:41 +02:00
|
|
|
if (!isPlayerAvailable() || getView() == null) {
|
2020-07-14 19:21:32 +02:00
|
|
|
return;
|
|
|
|
}
|
2019-12-29 22:15:01 +01:00
|
|
|
|
|
|
|
// Check if viewHolder already contains a child
|
2021-01-17 05:21:11 +01:00
|
|
|
if (player.getRootView().getParent() != binding.playerPlaceholder) {
|
2020-09-17 22:42:35 +02:00
|
|
|
playerService.removeViewFromParent();
|
2020-07-14 19:21:32 +02:00
|
|
|
}
|
2019-12-31 03:07:07 +01:00
|
|
|
setHeightThumbnail();
|
2019-12-29 22:15:01 +01:00
|
|
|
|
|
|
|
// Prevent from re-adding a view multiple times
|
2020-07-14 19:21:32 +02:00
|
|
|
if (player.getRootView().getParent() == null) {
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.playerPlaceholder.addView(player.getRootView());
|
2020-07-14 19:21:32 +02:00
|
|
|
}
|
2019-12-29 22:15:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private void removeVideoPlayerView() {
|
|
|
|
makeDefaultHeightForVideoPlaceholder();
|
|
|
|
|
|
|
|
playerService.removeViewFromParent();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void makeDefaultHeightForVideoPlaceholder() {
|
2020-07-14 19:21:32 +02:00
|
|
|
if (getView() == null) {
|
|
|
|
return;
|
|
|
|
}
|
2020-01-17 15:37:53 +01:00
|
|
|
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.playerPlaceholder.getLayoutParams().height = FrameLayout.LayoutParams.MATCH_PARENT;
|
|
|
|
binding.playerPlaceholder.requestLayout();
|
2019-12-29 22:15:01 +01:00
|
|
|
}
|
|
|
|
|
2020-09-17 22:42:35 +02:00
|
|
|
private final ViewTreeObserver.OnPreDrawListener preDrawListener =
|
|
|
|
new ViewTreeObserver.OnPreDrawListener() {
|
|
|
|
@Override
|
|
|
|
public boolean onPreDraw() {
|
|
|
|
final DisplayMetrics metrics = getResources().getDisplayMetrics();
|
|
|
|
|
|
|
|
if (getView() != null) {
|
2021-03-25 22:54:55 +01:00
|
|
|
final int height = (DeviceUtils.isInMultiWindow(activity)
|
2020-10-31 20:57:26 +01:00
|
|
|
? requireView()
|
|
|
|
: activity.getWindow().getDecorView()).getHeight();
|
2020-09-17 22:42:35 +02:00
|
|
|
setHeightThumbnail(height, metrics);
|
|
|
|
getView().getViewTreeObserver().removeOnPreDrawListener(preDrawListener);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-02-05 06:59:30 +01:00
|
|
|
/**
|
2020-07-14 19:21:32 +02:00
|
|
|
* Method which controls the size of thumbnail and the size of main player inside
|
|
|
|
* a layout with thumbnail. It decides what height the player should have in both
|
|
|
|
* screen orientations. It knows about multiWindow feature
|
|
|
|
* and about videos with aspectRatio ZOOM (the height for them will be a bit higher,
|
|
|
|
* {@link #MAX_PLAYER_HEIGHT})
|
2020-02-05 06:59:30 +01:00
|
|
|
*/
|
2017-03-31 20:15:26 +02:00
|
|
|
private void setHeightThumbnail() {
|
2017-09-03 08:04:18 +02:00
|
|
|
final DisplayMetrics metrics = getResources().getDisplayMetrics();
|
2020-07-12 02:59:47 +02:00
|
|
|
final boolean isPortrait = metrics.heightPixels > metrics.widthPixels;
|
2020-09-17 22:42:35 +02:00
|
|
|
requireView().getViewTreeObserver().removeOnPreDrawListener(preDrawListener);
|
2019-12-31 03:07:07 +01:00
|
|
|
|
2021-06-23 07:11:41 +02:00
|
|
|
if (isPlayerAvailable() && player.isFullscreen()) {
|
2021-03-25 22:54:55 +01:00
|
|
|
final int height = (DeviceUtils.isInMultiWindow(activity)
|
2020-10-31 20:57:26 +01:00
|
|
|
? requireView()
|
|
|
|
: activity.getWindow().getDecorView()).getHeight();
|
2020-09-17 22:42:35 +02:00
|
|
|
// Height is zero when the view is not yet displayed like after orientation change
|
|
|
|
if (height != 0) {
|
|
|
|
setHeightThumbnail(height, metrics);
|
|
|
|
} else {
|
|
|
|
requireView().getViewTreeObserver().addOnPreDrawListener(preDrawListener);
|
|
|
|
}
|
2020-07-14 19:21:32 +02:00
|
|
|
} else {
|
2020-10-31 20:57:26 +01:00
|
|
|
final int height = (int) (isPortrait
|
|
|
|
? metrics.widthPixels / (16.0f / 9.0f)
|
|
|
|
: metrics.heightPixels / 2.0f);
|
2020-09-17 22:42:35 +02:00
|
|
|
setHeightThumbnail(height, metrics);
|
2020-07-14 19:21:32 +02:00
|
|
|
}
|
2020-09-17 22:42:35 +02:00
|
|
|
}
|
2019-12-31 03:07:07 +01:00
|
|
|
|
2020-09-17 22:42:35 +02:00
|
|
|
private void setHeightThumbnail(final int newHeight, final DisplayMetrics metrics) {
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.detailThumbnailImageView.setLayoutParams(
|
2020-09-17 22:42:35 +02:00
|
|
|
new FrameLayout.LayoutParams(
|
|
|
|
RelativeLayout.LayoutParams.MATCH_PARENT, newHeight));
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.detailThumbnailImageView.setMinimumHeight(newHeight);
|
2021-06-23 07:11:41 +02:00
|
|
|
if (isPlayerAvailable()) {
|
2020-07-12 02:59:47 +02:00
|
|
|
final int maxHeight = (int) (metrics.heightPixels * MAX_PLAYER_HEIGHT);
|
2020-09-17 22:42:35 +02:00
|
|
|
player.getSurfaceView()
|
|
|
|
.setHeights(newHeight, player.isFullscreen() ? newHeight : maxHeight);
|
2020-02-05 06:59:30 +01:00
|
|
|
}
|
2017-03-31 20:15:26 +02:00
|
|
|
}
|
|
|
|
|
2018-12-19 06:28:59 +01:00
|
|
|
private void showContent() {
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.detailContentRootHiding.setVisibility(View.VISIBLE);
|
2017-04-28 05:58:50 +02:00
|
|
|
}
|
2017-03-31 20:15:26 +02:00
|
|
|
|
2020-10-18 20:19:50 +02:00
|
|
|
protected void setInitialData(final int newServiceId,
|
|
|
|
@Nullable final String newUrl,
|
|
|
|
@NonNull final String newTitle,
|
|
|
|
@Nullable final PlayQueue newPlayQueue) {
|
|
|
|
this.serviceId = newServiceId;
|
|
|
|
this.url = newUrl;
|
|
|
|
this.title = newTitle;
|
|
|
|
this.playQueue = newPlayQueue;
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
|
|
|
|
2017-04-26 21:32:04 +02:00
|
|
|
private void setErrorImage(final int imageResource) {
|
2021-01-17 05:21:11 +01:00
|
|
|
if (binding == null || activity == null) {
|
2020-03-31 19:20:15 +02:00
|
|
|
return;
|
|
|
|
}
|
2017-09-03 08:04:18 +02:00
|
|
|
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.detailThumbnailImageView.setImageDrawable(
|
2020-04-06 21:07:16 +02:00
|
|
|
AppCompatResources.getDrawable(requireContext(), imageResource));
|
2021-01-17 05:21:11 +01:00
|
|
|
animate(binding.detailThumbnailImageView, false, 0, AnimationType.ALPHA,
|
|
|
|
0, () -> animate(binding.detailThumbnailImageView, true, 500));
|
2017-04-26 21:32:04 +02:00
|
|
|
}
|
2017-03-31 20:15:26 +02:00
|
|
|
|
2017-04-26 21:32:04 +02:00
|
|
|
@Override
|
2020-12-11 14:55:47 +01:00
|
|
|
public void handleError() {
|
|
|
|
super.handleError();
|
2021-02-14 13:40:17 +01:00
|
|
|
setErrorImage(R.drawable.not_available_monkey);
|
|
|
|
|
2021-03-31 20:16:08 +02:00
|
|
|
if (binding.relatedItemsLayout != null) { // hide related streams for tablets
|
|
|
|
binding.relatedItemsLayout.setVisibility(View.INVISIBLE);
|
2021-02-14 13:40:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// hide comments / related streams / description tabs
|
|
|
|
binding.viewPager.setVisibility(View.GONE);
|
|
|
|
binding.tabLayout.setVisibility(View.GONE);
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
2017-03-31 20:15:26 +02:00
|
|
|
|
2021-02-14 13:40:17 +01:00
|
|
|
private void hideAgeRestrictedContent() {
|
2020-12-11 14:55:47 +01:00
|
|
|
showTextError(getString(R.string.restricted_video,
|
|
|
|
getString(R.string.show_age_restricted_content_title)));
|
2017-04-26 21:32:04 +02:00
|
|
|
}
|
2017-03-31 20:15:26 +02:00
|
|
|
|
2019-12-29 22:15:01 +01:00
|
|
|
private void setupBroadcastReceiver() {
|
|
|
|
broadcastReceiver = new BroadcastReceiver() {
|
|
|
|
@Override
|
2020-07-14 19:21:32 +02:00
|
|
|
public void onReceive(final Context context, final Intent intent) {
|
2020-11-18 23:58:41 +01:00
|
|
|
switch (intent.getAction()) {
|
|
|
|
case ACTION_SHOW_MAIN_PLAYER:
|
|
|
|
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
|
|
|
|
break;
|
|
|
|
case ACTION_HIDE_MAIN_PLAYER:
|
|
|
|
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
|
|
|
|
break;
|
|
|
|
case ACTION_PLAYER_STARTED:
|
|
|
|
// If the state is not hidden we don't need to show the mini player
|
|
|
|
if (bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_HIDDEN) {
|
|
|
|
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
|
|
|
|
}
|
|
|
|
// Rebound to the service if it was closed via notification or mini player
|
2021-12-21 00:18:58 +01:00
|
|
|
if (!playerHolder.isBound()) {
|
2021-06-24 10:00:56 +02:00
|
|
|
playerHolder.startService(
|
|
|
|
false, VideoDetailFragment.this);
|
2020-11-18 23:58:41 +01:00
|
|
|
}
|
|
|
|
break;
|
2019-12-29 22:15:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2020-08-16 10:24:58 +02:00
|
|
|
final IntentFilter intentFilter = new IntentFilter();
|
2020-01-09 16:28:06 +01:00
|
|
|
intentFilter.addAction(ACTION_SHOW_MAIN_PLAYER);
|
|
|
|
intentFilter.addAction(ACTION_HIDE_MAIN_PLAYER);
|
2020-09-29 05:22:53 +02:00
|
|
|
intentFilter.addAction(ACTION_PLAYER_STARTED);
|
2020-01-08 17:16:50 +01:00
|
|
|
activity.registerReceiver(broadcastReceiver, intentFilter);
|
2019-12-29 22:15:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*//////////////////////////////////////////////////////////////////////////
|
|
|
|
// Orientation listener
|
|
|
|
//////////////////////////////////////////////////////////////////////////*/
|
|
|
|
|
|
|
|
private void restoreDefaultOrientation() {
|
2021-09-19 19:09:11 +02:00
|
|
|
if (isPlayerAvailable() && player.videoPlayerSelected()) {
|
|
|
|
toggleFullscreenIfInFullscreenMode();
|
2020-07-14 19:21:32 +02:00
|
|
|
}
|
2019-12-29 22:15:01 +01:00
|
|
|
|
|
|
|
// This will show systemUI and pause the player.
|
|
|
|
// User can tap on Play button and video will be in fullscreen mode again
|
2020-07-14 19:21:32 +02:00
|
|
|
// Note for tablet: trying to avoid orientation changes since it's not easy
|
|
|
|
// to physically rotate the tablet every time
|
2021-09-19 19:09:11 +02:00
|
|
|
if (activity != null && !DeviceUtils.isTablet(activity)) {
|
2020-01-17 15:37:53 +01:00
|
|
|
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
|
2020-07-14 19:21:32 +02:00
|
|
|
}
|
2019-12-29 22:15:01 +01:00
|
|
|
}
|
|
|
|
|
2017-06-28 03:39:33 +02:00
|
|
|
/*//////////////////////////////////////////////////////////////////////////
|
2017-09-03 08:04:18 +02:00
|
|
|
// Contract
|
2017-06-28 03:39:33 +02:00
|
|
|
//////////////////////////////////////////////////////////////////////////*/
|
|
|
|
|
2017-04-26 21:32:04 +02:00
|
|
|
@Override
|
2017-09-03 08:04:18 +02:00
|
|
|
public void showLoading() {
|
2019-08-07 12:00:47 +02:00
|
|
|
|
2017-09-03 08:04:18 +02:00
|
|
|
super.showLoading();
|
2017-04-09 19:34:00 +02:00
|
|
|
|
2019-08-07 12:00:47 +02:00
|
|
|
//if data is already cached, transition from VISIBLE -> INVISIBLE -> VISIBLE is not required
|
2020-07-12 02:59:47 +02:00
|
|
|
if (!ExtractorHelper.isCached(serviceId, url, InfoItem.InfoType.STREAM)) {
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.detailContentRootHiding.setVisibility(View.INVISIBLE);
|
2019-08-07 12:00:47 +02:00
|
|
|
}
|
|
|
|
|
2021-01-17 05:21:11 +01:00
|
|
|
animate(binding.detailThumbnailPlayButton, false, 50);
|
|
|
|
animate(binding.detailDurationView, false, 100);
|
|
|
|
animate(binding.detailPositionView, false, 100);
|
|
|
|
animate(binding.positionView, false, 50);
|
2017-03-31 20:15:26 +02:00
|
|
|
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.detailVideoTitleView.setText(title);
|
|
|
|
binding.detailVideoTitleView.setMaxLines(1);
|
|
|
|
animate(binding.detailVideoTitleView, true, 0);
|
2017-04-26 21:32:04 +02:00
|
|
|
|
2021-01-17 15:59:29 +01:00
|
|
|
binding.detailToggleSecondaryControlsView.setVisibility(View.GONE);
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.detailTitleRootLayout.setClickable(false);
|
2020-10-16 20:01:03 +02:00
|
|
|
binding.detailSecondaryControlPanel.setVisibility(View.GONE);
|
2017-03-31 20:15:26 +02:00
|
|
|
|
2021-03-31 20:16:08 +02:00
|
|
|
if (binding.relatedItemsLayout != null) {
|
|
|
|
if (showRelatedItems) {
|
|
|
|
binding.relatedItemsLayout.setVisibility(
|
2021-06-23 07:11:41 +02:00
|
|
|
isPlayerAvailable() && player.isFullscreen() ? View.GONE : View.INVISIBLE);
|
2020-07-12 02:59:47 +02:00
|
|
|
} else {
|
2021-03-31 20:16:08 +02:00
|
|
|
binding.relatedItemsLayout.setVisibility(View.GONE);
|
2018-12-08 22:51:55 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-27 14:37:44 +01:00
|
|
|
PicassoHelper.cancelTag(PICASSO_VIDEO_DETAILS_TAG);
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.detailThumbnailImageView.setImageBitmap(null);
|
|
|
|
binding.detailSubChannelThumbnailView.setImageBitmap(null);
|
2017-03-31 20:15:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-03-31 19:20:15 +02:00
|
|
|
public void handleResult(@NonNull final StreamInfo info) {
|
2017-09-03 08:04:18 +02:00
|
|
|
super.handleResult(info);
|
2017-04-26 21:32:04 +02:00
|
|
|
|
2019-12-29 22:15:01 +01:00
|
|
|
currentInfo = info;
|
2020-01-10 15:32:05 +01:00
|
|
|
setInitialData(info.getServiceId(), info.getOriginalUrl(), info.getName(), playQueue);
|
2019-12-29 22:15:01 +01:00
|
|
|
|
2020-12-08 21:47:02 +01:00
|
|
|
updateTabs(info);
|
2020-11-01 13:55:20 +01:00
|
|
|
|
2021-01-17 05:21:11 +01:00
|
|
|
animate(binding.detailThumbnailPlayButton, true, 200);
|
|
|
|
binding.detailVideoTitleView.setText(title);
|
2017-03-31 20:15:26 +02:00
|
|
|
|
2021-12-28 21:54:36 +01:00
|
|
|
binding.detailSubChannelThumbnailView.setVisibility(View.GONE);
|
|
|
|
|
2020-12-15 17:41:21 +01:00
|
|
|
if (!isEmpty(info.getSubChannelName())) {
|
2020-04-16 17:35:42 +02:00
|
|
|
displayBothUploaderAndSubChannel(info);
|
2020-12-15 17:41:21 +01:00
|
|
|
} else if (!isEmpty(info.getUploaderName())) {
|
2020-04-16 17:35:42 +02:00
|
|
|
displayUploaderAsSubChannel(info);
|
2017-12-08 15:05:08 +01:00
|
|
|
} else {
|
2020-11-01 13:55:20 +01:00
|
|
|
binding.detailUploaderTextView.setVisibility(View.GONE);
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.detailUploaderThumbnailView.setVisibility(View.GONE);
|
2017-12-08 15:05:08 +01:00
|
|
|
}
|
2020-04-13 22:40:44 +02:00
|
|
|
|
2020-08-16 10:24:58 +02:00
|
|
|
final Drawable buddyDrawable = AppCompatResources.getDrawable(activity, R.drawable.buddy);
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.detailSubChannelThumbnailView.setImageDrawable(buddyDrawable);
|
|
|
|
binding.detailUploaderThumbnailView.setImageDrawable(buddyDrawable);
|
2017-03-31 20:15:26 +02:00
|
|
|
|
2017-12-08 15:05:08 +01:00
|
|
|
if (info.getViewCount() >= 0) {
|
2019-10-28 03:37:36 +01:00
|
|
|
if (info.getStreamType().equals(StreamType.AUDIO_LIVE_STREAM)) {
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.detailViewCountView.setText(Localization.listeningCount(activity,
|
|
|
|
info.getViewCount()));
|
2019-10-28 03:37:36 +01:00
|
|
|
} else if (info.getStreamType().equals(StreamType.LIVE_STREAM)) {
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.detailViewCountView.setText(Localization
|
2020-03-31 19:20:15 +02:00
|
|
|
.localizeWatchingCount(activity, info.getViewCount()));
|
2019-10-28 03:37:36 +01:00
|
|
|
} else {
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.detailViewCountView.setText(Localization
|
2020-03-31 19:20:15 +02:00
|
|
|
.localizeViewCount(activity, info.getViewCount()));
|
2019-10-28 03:37:36 +01:00
|
|
|
}
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.detailViewCountView.setVisibility(View.VISIBLE);
|
2017-12-08 15:05:08 +01:00
|
|
|
} else {
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.detailViewCountView.setVisibility(View.GONE);
|
2017-12-08 15:05:08 +01:00
|
|
|
}
|
2017-03-31 20:15:26 +02:00
|
|
|
|
2017-12-08 15:05:08 +01:00
|
|
|
if (info.getDislikeCount() == -1 && info.getLikeCount() == -1) {
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.detailThumbsDownImgView.setVisibility(View.VISIBLE);
|
|
|
|
binding.detailThumbsUpImgView.setVisibility(View.VISIBLE);
|
|
|
|
binding.detailThumbsUpCountView.setVisibility(View.GONE);
|
|
|
|
binding.detailThumbsDownCountView.setVisibility(View.GONE);
|
2017-04-12 08:07:15 +02:00
|
|
|
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.detailThumbsDisabledView.setVisibility(View.VISIBLE);
|
2017-09-03 08:04:18 +02:00
|
|
|
} else {
|
2017-12-08 15:05:08 +01:00
|
|
|
if (info.getDislikeCount() >= 0) {
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.detailThumbsDownCountView.setText(Localization
|
2020-03-31 19:20:15 +02:00
|
|
|
.shortCount(activity, info.getDislikeCount()));
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.detailThumbsDownCountView.setVisibility(View.VISIBLE);
|
|
|
|
binding.detailThumbsDownImgView.setVisibility(View.VISIBLE);
|
2017-12-08 15:05:08 +01:00
|
|
|
} else {
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.detailThumbsDownCountView.setVisibility(View.GONE);
|
|
|
|
binding.detailThumbsDownImgView.setVisibility(View.GONE);
|
2017-12-08 15:05:08 +01:00
|
|
|
}
|
2017-06-28 03:39:33 +02:00
|
|
|
|
2017-12-08 15:05:08 +01:00
|
|
|
if (info.getLikeCount() >= 0) {
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.detailThumbsUpCountView.setText(Localization.shortCount(activity,
|
|
|
|
info.getLikeCount()));
|
|
|
|
binding.detailThumbsUpCountView.setVisibility(View.VISIBLE);
|
|
|
|
binding.detailThumbsUpImgView.setVisibility(View.VISIBLE);
|
2017-12-08 15:05:08 +01:00
|
|
|
} else {
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.detailThumbsUpCountView.setVisibility(View.GONE);
|
|
|
|
binding.detailThumbsUpImgView.setVisibility(View.GONE);
|
2017-12-08 15:05:08 +01:00
|
|
|
}
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.detailThumbsDisabledView.setVisibility(View.GONE);
|
2017-09-03 08:04:18 +02:00
|
|
|
}
|
2017-06-15 16:26:48 +02:00
|
|
|
|
2018-03-10 17:16:51 +01:00
|
|
|
if (info.getDuration() > 0) {
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.detailDurationView.setText(Localization.getDurationString(info.getDuration()));
|
|
|
|
binding.detailDurationView.setBackgroundColor(
|
2019-01-31 13:24:02 +01:00
|
|
|
ContextCompat.getColor(activity, R.color.duration_background_color));
|
2021-01-17 05:21:11 +01:00
|
|
|
animate(binding.detailDurationView, true, 100);
|
2018-03-10 17:16:51 +01:00
|
|
|
} else if (info.getStreamType() == StreamType.LIVE_STREAM) {
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.detailDurationView.setText(R.string.duration_live);
|
|
|
|
binding.detailDurationView.setBackgroundColor(
|
2019-01-31 13:24:02 +01:00
|
|
|
ContextCompat.getColor(activity, R.color.live_duration_background_color));
|
2021-01-17 05:21:11 +01:00
|
|
|
animate(binding.detailDurationView, true, 100);
|
2018-03-10 17:16:51 +01:00
|
|
|
} else {
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.detailDurationView.setVisibility(View.GONE);
|
2018-03-10 17:16:51 +01:00
|
|
|
}
|
|
|
|
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.detailTitleRootLayout.setClickable(true);
|
2021-01-17 15:59:29 +01:00
|
|
|
binding.detailToggleSecondaryControlsView.setRotation(0);
|
|
|
|
binding.detailToggleSecondaryControlsView.setVisibility(View.VISIBLE);
|
2020-10-16 20:01:03 +02:00
|
|
|
binding.detailSecondaryControlPanel.setVisibility(View.GONE);
|
2019-10-28 03:35:51 +01:00
|
|
|
|
2020-07-12 02:59:47 +02:00
|
|
|
sortedVideoStreams = ListHelper.getSortedStreamVideosList(
|
|
|
|
activity,
|
|
|
|
info.getVideoStreams(),
|
|
|
|
info.getVideoOnlyStreams(),
|
|
|
|
false);
|
2020-07-14 19:21:32 +02:00
|
|
|
selectedVideoStreamIndex = ListHelper
|
|
|
|
.getDefaultResolutionIndex(activity, sortedVideoStreams);
|
2019-04-13 09:31:32 +02:00
|
|
|
updateProgressInfo(info);
|
2017-09-03 08:04:18 +02:00
|
|
|
initThumbnailViews(info);
|
2021-06-05 15:35:48 +02:00
|
|
|
showMetaInfoInTextView(info.getMetaInfo(), binding.detailMetaInfoTextView,
|
|
|
|
binding.detailMetaInfoSeparator, disposables);
|
2018-04-06 09:35:44 +02:00
|
|
|
|
2021-06-23 07:11:41 +02:00
|
|
|
if (!isPlayerAvailable() || player.isStopped()) {
|
2020-02-05 06:59:30 +01:00
|
|
|
updateOverlayData(info.getName(), info.getUploaderName(), info.getThumbnailUrl());
|
2020-07-14 19:21:32 +02:00
|
|
|
}
|
2017-06-15 16:26:48 +02:00
|
|
|
|
2017-12-08 15:05:08 +01:00
|
|
|
if (!info.getErrors().isEmpty()) {
|
2021-03-14 17:51:47 +01:00
|
|
|
// Bandcamp fan pages are not yet supported and thus a ContentNotAvailableException is
|
|
|
|
// thrown. This is not an error and thus should not be shown to the user.
|
|
|
|
for (final Throwable throwable : info.getErrors()) {
|
|
|
|
if (throwable instanceof ContentNotSupportedException
|
|
|
|
&& "Fan pages are not supported".equals(throwable.getMessage())) {
|
|
|
|
info.getErrors().remove(throwable);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!info.getErrors().isEmpty()) {
|
|
|
|
showSnackBarError(new ErrorInfo(info.getErrors(),
|
|
|
|
UserAction.REQUESTED_STREAM, info.getUrl(), info));
|
|
|
|
}
|
2017-06-15 16:26:48 +02:00
|
|
|
}
|
|
|
|
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.detailControlsDownload.setVisibility(info.getStreamType() == StreamType.LIVE_STREAM
|
2020-10-02 16:03:43 +02:00
|
|
|
|| info.getStreamType() == StreamType.AUDIO_LIVE_STREAM ? View.GONE : View.VISIBLE);
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.detailControlsBackground.setVisibility(info.getAudioStreams().isEmpty()
|
2020-10-02 16:03:43 +02:00
|
|
|
? View.GONE : View.VISIBLE);
|
|
|
|
|
|
|
|
final boolean noVideoStreams =
|
|
|
|
info.getVideoStreams().isEmpty() && info.getVideoOnlyStreams().isEmpty();
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.detailControlsPopup.setVisibility(noVideoStreams ? View.GONE : View.VISIBLE);
|
|
|
|
binding.detailThumbnailPlayButton.setImageResource(
|
2020-10-02 16:03:43 +02:00
|
|
|
noVideoStreams ? R.drawable.ic_headset_shadow : R.drawable.ic_play_arrow_shadow);
|
2017-06-15 16:26:48 +02:00
|
|
|
}
|
|
|
|
|
2020-04-16 17:35:42 +02:00
|
|
|
private void displayUploaderAsSubChannel(final StreamInfo info) {
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.detailSubChannelTextView.setText(info.getUploaderName());
|
|
|
|
binding.detailSubChannelTextView.setVisibility(View.VISIBLE);
|
|
|
|
binding.detailSubChannelTextView.setSelected(true);
|
2020-11-01 13:55:20 +01:00
|
|
|
binding.detailUploaderTextView.setVisibility(View.GONE);
|
2020-04-13 22:40:44 +02:00
|
|
|
}
|
|
|
|
|
2020-04-16 17:35:42 +02:00
|
|
|
private void displayBothUploaderAndSubChannel(final StreamInfo info) {
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.detailSubChannelTextView.setText(info.getSubChannelName());
|
|
|
|
binding.detailSubChannelTextView.setVisibility(View.VISIBLE);
|
|
|
|
binding.detailSubChannelTextView.setSelected(true);
|
2020-04-13 22:40:44 +02:00
|
|
|
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.detailSubChannelThumbnailView.setVisibility(View.VISIBLE);
|
2020-05-07 20:51:54 +02:00
|
|
|
|
2020-12-15 17:41:21 +01:00
|
|
|
if (!isEmpty(info.getUploaderName())) {
|
2020-11-01 13:55:20 +01:00
|
|
|
binding.detailUploaderTextView.setText(
|
2020-04-16 17:35:42 +02:00
|
|
|
String.format(getString(R.string.video_detail_by), info.getUploaderName()));
|
2020-11-01 13:55:20 +01:00
|
|
|
binding.detailUploaderTextView.setVisibility(View.VISIBLE);
|
|
|
|
binding.detailUploaderTextView.setSelected(true);
|
2020-04-13 22:40:44 +02:00
|
|
|
} else {
|
2020-11-01 13:55:20 +01:00
|
|
|
binding.detailUploaderTextView.setVisibility(View.GONE);
|
2020-04-13 22:40:44 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-07 19:32:02 +02:00
|
|
|
public void openDownloadDialog() {
|
2020-12-11 14:55:47 +01:00
|
|
|
if (currentInfo == null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-07-12 02:59:47 +02:00
|
|
|
try {
|
|
|
|
final DownloadDialog downloadDialog = DownloadDialog.newInstance(currentInfo);
|
|
|
|
downloadDialog.setVideoStreams(sortedVideoStreams);
|
|
|
|
downloadDialog.setAudioStreams(currentInfo.getAudioStreams());
|
|
|
|
downloadDialog.setSelectedVideoStream(selectedVideoStreamIndex);
|
|
|
|
downloadDialog.setSubtitleStreams(currentInfo.getSubtitles());
|
|
|
|
|
|
|
|
downloadDialog.show(activity.getSupportFragmentManager(), "downloadDialog");
|
2020-08-16 10:24:58 +02:00
|
|
|
} catch (final Exception e) {
|
2021-12-01 09:43:24 +01:00
|
|
|
ErrorUtil.showSnackbar(activity, new ErrorInfo(e, UserAction.DOWNLOAD_OPEN_DIALOG,
|
|
|
|
"Showing download dialog", currentInfo));
|
2020-07-12 02:59:47 +02:00
|
|
|
}
|
2018-04-07 19:32:02 +02:00
|
|
|
}
|
|
|
|
|
2017-09-03 08:04:18 +02:00
|
|
|
/*//////////////////////////////////////////////////////////////////////////
|
|
|
|
// Stream Results
|
|
|
|
//////////////////////////////////////////////////////////////////////////*/
|
|
|
|
|
2019-04-13 09:31:32 +02:00
|
|
|
private void updateProgressInfo(@NonNull final StreamInfo info) {
|
|
|
|
if (positionSubscriber != null) {
|
|
|
|
positionSubscriber.dispose();
|
|
|
|
}
|
|
|
|
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity);
|
2020-03-31 19:20:15 +02:00
|
|
|
final boolean playbackResumeEnabled = prefs
|
|
|
|
.getBoolean(activity.getString(R.string.enable_watch_history_key), true)
|
|
|
|
&& prefs.getBoolean(activity.getString(R.string.enable_playback_resume_key), true);
|
2019-12-29 22:15:01 +01:00
|
|
|
final boolean showPlaybackPosition = prefs.getBoolean(
|
|
|
|
activity.getString(R.string.enable_playback_state_lists_key), true);
|
2020-03-10 10:06:38 +01:00
|
|
|
if (!playbackResumeEnabled) {
|
2019-12-29 22:15:01 +01:00
|
|
|
if (playQueue == null || playQueue.getStreams().isEmpty()
|
2020-07-14 19:21:32 +02:00
|
|
|
|| playQueue.getItem().getRecoveryPosition() == RECOVERY_UNSET
|
|
|
|
|| !showPlaybackPosition) {
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.positionView.setVisibility(View.INVISIBLE);
|
|
|
|
binding.detailPositionView.setVisibility(View.GONE);
|
2020-07-13 03:17:21 +02:00
|
|
|
// TODO: Remove this check when separation of concerns is done.
|
|
|
|
// (live streams weren't getting updated because they are mixed)
|
|
|
|
if (!info.getStreamType().equals(StreamType.LIVE_STREAM)
|
|
|
|
&& !info.getStreamType().equals(StreamType.AUDIO_LIVE_STREAM)) {
|
|
|
|
return;
|
|
|
|
}
|
2019-12-29 22:15:01 +01:00
|
|
|
} else {
|
|
|
|
// Show saved position from backStack if user allows it
|
|
|
|
showPlaybackProgress(playQueue.getItem().getRecoveryPosition(),
|
|
|
|
playQueue.getItem().getDuration() * 1000);
|
2021-01-17 05:21:11 +01:00
|
|
|
animate(binding.positionView, true, 500);
|
|
|
|
animate(binding.detailPositionView, true, 500);
|
2019-12-29 22:15:01 +01:00
|
|
|
}
|
2020-07-14 19:21:32 +02:00
|
|
|
return;
|
|
|
|
}
|
2019-04-13 09:31:32 +02:00
|
|
|
final HistoryRecordManager recordManager = new HistoryRecordManager(requireContext());
|
2019-12-16 08:36:04 +01:00
|
|
|
|
|
|
|
// TODO: Separate concerns when updating database data.
|
|
|
|
// (move the updating part to when the loading happens)
|
2019-04-13 09:31:32 +02:00
|
|
|
positionSubscriber = recordManager.loadStreamState(info)
|
|
|
|
.subscribeOn(Schedulers.io())
|
|
|
|
.onErrorComplete()
|
|
|
|
.observeOn(AndroidSchedulers.mainThread())
|
|
|
|
.subscribe(state -> {
|
2021-06-07 09:35:40 +02:00
|
|
|
showPlaybackProgress(state.getProgressMillis(), info.getDuration() * 1000);
|
2021-01-17 05:21:11 +01:00
|
|
|
animate(binding.positionView, true, 500);
|
|
|
|
animate(binding.detailPositionView, true, 500);
|
2019-04-13 09:31:32 +02:00
|
|
|
}, e -> {
|
2020-03-31 19:20:15 +02:00
|
|
|
if (DEBUG) {
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
2019-04-13 09:31:32 +02:00
|
|
|
}, () -> {
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.positionView.setVisibility(View.GONE);
|
|
|
|
binding.detailPositionView.setVisibility(View.GONE);
|
2019-04-13 09:31:32 +02:00
|
|
|
});
|
2017-08-12 06:50:25 +02:00
|
|
|
}
|
2019-12-29 22:15:01 +01:00
|
|
|
|
2020-07-12 02:59:47 +02:00
|
|
|
private void showPlaybackProgress(final long progress, final long duration) {
|
2019-12-29 22:15:01 +01:00
|
|
|
final int progressSeconds = (int) TimeUnit.MILLISECONDS.toSeconds(progress);
|
|
|
|
final int durationSeconds = (int) TimeUnit.MILLISECONDS.toSeconds(duration);
|
2020-08-16 21:44:27 +02:00
|
|
|
// If the old and the new progress values have a big difference then use
|
|
|
|
// animation. Otherwise don't because it affects CPU
|
2021-01-17 05:21:11 +01:00
|
|
|
final boolean shouldAnimate = Math.abs(binding.positionView.getProgress()
|
|
|
|
- progressSeconds) > 2;
|
|
|
|
binding.positionView.setMax(durationSeconds);
|
2020-08-16 21:44:27 +02:00
|
|
|
if (shouldAnimate) {
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.positionView.setProgressAnimated(progressSeconds);
|
2020-08-05 11:46:25 +02:00
|
|
|
} else {
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.positionView.setProgress(progressSeconds);
|
2020-08-05 11:46:25 +02:00
|
|
|
}
|
|
|
|
final String position = Localization.getDurationString(progressSeconds);
|
2021-01-17 05:21:11 +01:00
|
|
|
if (position != binding.detailPositionView.getText()) {
|
|
|
|
binding.detailPositionView.setText(position);
|
2020-08-05 11:46:25 +02:00
|
|
|
}
|
2021-01-17 05:21:11 +01:00
|
|
|
if (binding.positionView.getVisibility() != View.VISIBLE) {
|
|
|
|
animate(binding.positionView, true, 100);
|
|
|
|
animate(binding.detailPositionView, true, 100);
|
2020-03-10 10:06:38 +01:00
|
|
|
}
|
2019-12-29 22:15:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*//////////////////////////////////////////////////////////////////////////
|
|
|
|
// Player event listener
|
|
|
|
//////////////////////////////////////////////////////////////////////////*/
|
|
|
|
|
2020-01-06 11:39:01 +01:00
|
|
|
@Override
|
2020-07-12 02:59:47 +02:00
|
|
|
public void onQueueUpdate(final PlayQueue queue) {
|
2020-01-06 11:39:01 +01:00
|
|
|
playQueue = queue;
|
2020-10-18 09:50:42 +02:00
|
|
|
if (DEBUG) {
|
|
|
|
Log.d(TAG, "onQueueUpdate() called with: serviceId = ["
|
|
|
|
+ serviceId + "], videoUrl = [" + url + "], name = ["
|
2020-10-18 20:19:50 +02:00
|
|
|
+ title + "], playQueue = [" + playQueue + "]");
|
2020-10-18 09:50:42 +02:00
|
|
|
}
|
|
|
|
|
2020-07-14 19:21:32 +02:00
|
|
|
// This should be the only place where we push data to stack.
|
|
|
|
// It will allow to have live instance of PlayQueue with actual information about
|
|
|
|
// deleted/added items inside Channel/Playlist queue and makes possible to have
|
|
|
|
// a history of played items
|
2020-10-18 09:50:42 +02:00
|
|
|
@Nullable final StackItem stackPeek = stack.peek();
|
2020-11-13 21:39:27 +01:00
|
|
|
if (stackPeek != null && !stackPeek.getPlayQueue().equals(queue)) {
|
2020-10-18 09:50:42 +02:00
|
|
|
@Nullable final PlayQueueItem playQueueItem = queue.getItem();
|
|
|
|
if (playQueueItem != null) {
|
|
|
|
stack.push(new StackItem(playQueueItem.getServiceId(), playQueueItem.getUrl(),
|
|
|
|
playQueueItem.getTitle(), queue));
|
|
|
|
return;
|
|
|
|
} // else continue below
|
2020-02-29 00:57:54 +01:00
|
|
|
}
|
2020-01-06 11:39:01 +01:00
|
|
|
|
2020-10-18 09:50:42 +02:00
|
|
|
@Nullable final StackItem stackWithQueue = findQueueInStack(queue);
|
|
|
|
if (stackWithQueue != null) {
|
|
|
|
// On every MainPlayer service's destroy() playQueue gets disposed and
|
|
|
|
// no longer able to track progress. That's why we update our cached disposed
|
|
|
|
// queue with the new one that is active and have the same history.
|
|
|
|
// Without that the cached playQueue will have an old recovery position
|
|
|
|
stackWithQueue.setPlayQueue(queue);
|
2020-01-06 11:39:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-29 22:15:01 +01:00
|
|
|
@Override
|
2020-07-14 19:21:32 +02:00
|
|
|
public void onPlaybackUpdate(final int state,
|
|
|
|
final int repeatMode,
|
|
|
|
final boolean shuffled,
|
|
|
|
final PlaybackParameters parameters) {
|
2020-09-29 05:22:53 +02:00
|
|
|
setOverlayPlayPauseImage(player != null && player.isPlaying());
|
2019-12-29 22:15:01 +01:00
|
|
|
|
|
|
|
switch (state) {
|
2021-01-08 18:35:33 +01:00
|
|
|
case Player.STATE_PLAYING:
|
2021-01-17 05:21:11 +01:00
|
|
|
if (binding.positionView.getAlpha() != 1.0f
|
2020-01-10 15:32:05 +01:00
|
|
|
&& player.getPlayQueue() != null
|
|
|
|
&& player.getPlayQueue().getItem() != null
|
|
|
|
&& player.getPlayQueue().getItem().getUrl().equals(url)) {
|
2021-01-17 05:21:11 +01:00
|
|
|
animate(binding.positionView, true, 100);
|
|
|
|
animate(binding.detailPositionView, true, 100);
|
2020-01-10 15:32:05 +01:00
|
|
|
}
|
2019-12-29 22:15:01 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-07-14 19:21:32 +02:00
|
|
|
public void onProgressUpdate(final int currentProgress,
|
|
|
|
final int duration,
|
|
|
|
final int bufferPercent) {
|
2019-12-29 22:15:01 +01:00
|
|
|
// Progress updates every second even if media is paused. It's useless until playing
|
2021-01-08 18:35:33 +01:00
|
|
|
if (!player.isPlaying() || playQueue == null) {
|
2020-07-14 19:21:32 +02:00
|
|
|
return;
|
|
|
|
}
|
2020-01-09 16:28:06 +01:00
|
|
|
|
2020-07-14 19:21:32 +02:00
|
|
|
if (player.getPlayQueue().getItem().getUrl().equals(url)) {
|
2020-01-10 15:32:05 +01:00
|
|
|
showPlaybackProgress(currentProgress, duration);
|
2020-07-14 19:21:32 +02:00
|
|
|
}
|
2019-12-29 22:15:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-07-12 02:59:47 +02:00
|
|
|
public void onMetadataUpdate(final StreamInfo info, final PlayQueue queue) {
|
|
|
|
final StackItem item = findQueueInStack(queue);
|
2020-03-10 10:06:38 +01:00
|
|
|
if (item != null) {
|
2020-07-14 19:21:32 +02:00
|
|
|
// When PlayQueue can have multiple streams (PlaylistPlayQueue or ChannelPlayQueue)
|
|
|
|
// every new played stream gives new title and url.
|
|
|
|
// StackItem contains information about first played stream. Let's update it here
|
2020-03-10 10:06:38 +01:00
|
|
|
item.setTitle(info.getName());
|
|
|
|
item.setUrl(info.getUrl());
|
2020-01-06 11:39:01 +01:00
|
|
|
}
|
2020-07-14 19:21:32 +02:00
|
|
|
// They are not equal when user watches something in popup while browsing in fragment and
|
|
|
|
// then changes screen orientation. In that case the fragment will set itself as
|
|
|
|
// a service listener and will receive initial call to onMetadataUpdate()
|
|
|
|
if (!queue.equals(playQueue)) {
|
|
|
|
return;
|
|
|
|
}
|
2020-01-06 11:39:01 +01:00
|
|
|
|
2020-02-05 06:59:30 +01:00
|
|
|
updateOverlayData(info.getName(), info.getUploaderName(), info.getThumbnailUrl());
|
2020-07-14 19:21:32 +02:00
|
|
|
if (currentInfo != null && info.getUrl().equals(currentInfo.getUrl())) {
|
|
|
|
return;
|
|
|
|
}
|
2019-12-29 22:15:01 +01:00
|
|
|
|
|
|
|
currentInfo = info;
|
2020-07-12 02:59:47 +02:00
|
|
|
setInitialData(info.getServiceId(), info.getUrl(), info.getName(), queue);
|
2020-10-18 20:19:50 +02:00
|
|
|
setAutoPlay(false);
|
2020-09-17 22:42:35 +02:00
|
|
|
// Delay execution just because it freezes the main thread, and while playing
|
|
|
|
// next/previous video you see visual glitches
|
|
|
|
// (when non-vertical video goes after vertical video)
|
|
|
|
prepareAndHandleInfoIfNeededAfterDelay(info, true, 200);
|
2019-12-29 22:15:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-07-12 02:59:47 +02:00
|
|
|
public void onPlayerError(final ExoPlaybackException error) {
|
2020-07-14 19:21:32 +02:00
|
|
|
if (error.type == ExoPlaybackException.TYPE_SOURCE
|
|
|
|
|| error.type == ExoPlaybackException.TYPE_UNEXPECTED) {
|
2020-09-17 18:01:20 +02:00
|
|
|
// Properly exit from fullscreen
|
2021-06-23 16:07:04 +02:00
|
|
|
toggleFullscreenIfInFullscreenMode();
|
2021-08-31 11:10:52 +02:00
|
|
|
hideMainPlayerOnLoadingNewStream();
|
2019-12-29 22:15:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onServiceStopped() {
|
2020-09-29 05:22:53 +02:00
|
|
|
setOverlayPlayPauseImage(false);
|
2020-07-14 19:21:32 +02:00
|
|
|
if (currentInfo != null) {
|
|
|
|
updateOverlayData(currentInfo.getName(),
|
|
|
|
currentInfo.getUploaderName(),
|
|
|
|
currentInfo.getThumbnailUrl());
|
|
|
|
}
|
2019-12-29 22:15:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-07-12 02:59:47 +02:00
|
|
|
public void onFullscreenStateChanged(final boolean fullscreen) {
|
2020-09-04 04:39:55 +02:00
|
|
|
setupBrightness();
|
2021-06-23 07:11:41 +02:00
|
|
|
if (!isPlayerAndPlayerServiceAvailable()
|
|
|
|
|| playerService.getView() == null
|
|
|
|
|| player.getParentActivity() == null) {
|
2019-12-29 22:15:01 +01:00
|
|
|
return;
|
2020-07-14 19:21:32 +02:00
|
|
|
}
|
2019-12-29 22:15:01 +01:00
|
|
|
|
2020-07-12 02:59:47 +02:00
|
|
|
final View view = playerService.getView();
|
|
|
|
final ViewGroup parent = (ViewGroup) view.getParent();
|
2020-07-14 19:21:32 +02:00
|
|
|
if (parent == null) {
|
|
|
|
return;
|
|
|
|
}
|
2019-12-29 22:15:01 +01:00
|
|
|
|
|
|
|
if (fullscreen) {
|
2020-02-29 00:57:54 +01:00
|
|
|
hideSystemUiIfNeeded();
|
2021-02-12 09:58:15 +01:00
|
|
|
binding.overlayPlayPauseButton.requestFocus();
|
2019-12-29 22:15:01 +01:00
|
|
|
} else {
|
|
|
|
showSystemUi();
|
|
|
|
}
|
|
|
|
|
2021-03-31 20:16:08 +02:00
|
|
|
if (binding.relatedItemsLayout != null) {
|
|
|
|
binding.relatedItemsLayout.setVisibility(fullscreen ? View.GONE : View.VISIBLE);
|
2020-07-14 19:21:32 +02:00
|
|
|
}
|
2020-02-25 00:15:22 +01:00
|
|
|
scrollToTop();
|
2019-12-29 22:15:01 +01:00
|
|
|
|
2020-09-27 03:25:06 +02:00
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
|
|
|
addVideoPlayerView();
|
|
|
|
} else {
|
|
|
|
// KitKat needs a delay before addVideoPlayerView call or it reports wrong height in
|
|
|
|
// activity.getWindow().getDecorView().getHeight()
|
|
|
|
new Handler().post(this::addVideoPlayerView);
|
|
|
|
}
|
2019-12-29 22:15:01 +01:00
|
|
|
}
|
|
|
|
|
2020-01-16 12:20:22 +01:00
|
|
|
@Override
|
|
|
|
public void onScreenRotationButtonClicked() {
|
2020-07-14 19:21:32 +02:00
|
|
|
// In tablet user experience will be better if screen will not be rotated
|
|
|
|
// from landscape to portrait every time.
|
2020-01-17 15:37:53 +01:00
|
|
|
// Just turn on fullscreen mode in landscape orientation
|
2020-09-27 03:11:38 +02:00
|
|
|
// or portrait & unlocked global orientation
|
2021-03-25 22:54:55 +01:00
|
|
|
final boolean isLandscape = DeviceUtils.isLandscape(requireContext());
|
2020-09-27 03:11:38 +02:00
|
|
|
if (DeviceUtils.isTablet(activity)
|
2021-03-25 22:54:55 +01:00
|
|
|
&& (!globalScreenOrientationLocked(activity) || isLandscape)) {
|
2020-01-17 15:37:53 +01:00
|
|
|
player.toggleFullscreen();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-03-25 22:54:55 +01:00
|
|
|
final int newOrientation = isLandscape
|
2020-09-27 03:11:38 +02:00
|
|
|
? ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
|
2020-01-16 12:20:22 +01:00
|
|
|
: ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
|
|
|
|
|
|
|
|
activity.setRequestedOrientation(newOrientation);
|
|
|
|
}
|
|
|
|
|
2019-12-29 22:15:01 +01:00
|
|
|
/*
|
2020-07-12 02:59:47 +02:00
|
|
|
* Will scroll down to description view after long click on moreOptionsButton
|
|
|
|
* */
|
2019-12-29 22:15:01 +01:00
|
|
|
@Override
|
|
|
|
public void onMoreOptionsLongClicked() {
|
2020-07-14 19:21:32 +02:00
|
|
|
final CoordinatorLayout.LayoutParams params =
|
2021-01-17 05:21:11 +01:00
|
|
|
(CoordinatorLayout.LayoutParams) binding.appBarLayout.getLayoutParams();
|
2020-07-12 02:59:47 +02:00
|
|
|
final AppBarLayout.Behavior behavior = (AppBarLayout.Behavior) params.getBehavior();
|
2020-07-14 19:21:32 +02:00
|
|
|
final ValueAnimator valueAnimator = ValueAnimator
|
2021-01-17 05:21:11 +01:00
|
|
|
.ofInt(0, -binding.playerPlaceholder.getHeight());
|
2019-12-29 22:15:01 +01:00
|
|
|
valueAnimator.setInterpolator(new DecelerateInterpolator());
|
|
|
|
valueAnimator.addUpdateListener(animation -> {
|
|
|
|
behavior.setTopAndBottomOffset((int) animation.getAnimatedValue());
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.appBarLayout.requestLayout();
|
2019-12-29 22:15:01 +01:00
|
|
|
});
|
|
|
|
valueAnimator.setInterpolator(new DecelerateInterpolator());
|
|
|
|
valueAnimator.setDuration(500);
|
|
|
|
valueAnimator.start();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*//////////////////////////////////////////////////////////////////////////
|
|
|
|
// Player related utils
|
|
|
|
//////////////////////////////////////////////////////////////////////////*/
|
|
|
|
|
|
|
|
private void showSystemUi() {
|
2020-07-14 19:21:32 +02:00
|
|
|
if (DEBUG) {
|
|
|
|
Log.d(TAG, "showSystemUi() called");
|
|
|
|
}
|
2019-12-29 22:15:01 +01:00
|
|
|
|
2020-07-14 19:21:32 +02:00
|
|
|
if (activity == null) {
|
|
|
|
return;
|
|
|
|
}
|
2020-01-08 17:16:50 +01:00
|
|
|
|
2020-08-17 16:42:32 +02:00
|
|
|
// Prevent jumping of the player on devices with cutout
|
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
|
|
|
activity.getWindow().getAttributes().layoutInDisplayCutoutMode =
|
2021-11-27 15:58:48 +01:00
|
|
|
isMultiWindowOrFullscreen()
|
|
|
|
? WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER
|
|
|
|
: WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
|
2020-08-17 16:42:32 +02:00
|
|
|
}
|
2020-01-08 17:16:50 +01:00
|
|
|
activity.getWindow().getDecorView().setSystemUiVisibility(0);
|
2020-09-15 13:43:43 +02:00
|
|
|
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
2020-09-14 10:30:41 +02:00
|
|
|
activity.getWindow().setStatusBarColor(ThemeHelper.resolveColorFromAttr(
|
|
|
|
requireContext(), android.R.attr.colorPrimary));
|
|
|
|
}
|
2019-12-29 22:15:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private void hideSystemUi() {
|
2020-07-14 19:21:32 +02:00
|
|
|
if (DEBUG) {
|
|
|
|
Log.d(TAG, "hideSystemUi() called");
|
|
|
|
}
|
2019-12-29 22:15:01 +01:00
|
|
|
|
2020-07-14 19:21:32 +02:00
|
|
|
if (activity == null) {
|
|
|
|
return;
|
|
|
|
}
|
2020-01-08 17:16:50 +01:00
|
|
|
|
2020-08-17 16:42:32 +02:00
|
|
|
// Prevent jumping of the player on devices with cutout
|
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
|
|
|
activity.getWindow().getAttributes().layoutInDisplayCutoutMode =
|
2021-11-27 15:58:48 +01:00
|
|
|
isMultiWindowOrFullscreen()
|
|
|
|
? WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER
|
|
|
|
: WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
|
2020-08-17 16:42:32 +02:00
|
|
|
}
|
2020-09-15 13:43:43 +02:00
|
|
|
int visibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
2020-01-03 06:05:31 +01:00
|
|
|
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
|
|
|
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
|
|
|
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
|
|
|
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
|
2021-03-25 22:54:55 +01:00
|
|
|
|
2020-09-15 13:43:43 +02:00
|
|
|
// In multiWindow mode status bar is not transparent for devices with cutout
|
|
|
|
// if I include this flag. So without it is better in this case
|
2021-03-25 22:54:55 +01:00
|
|
|
final boolean isInMultiWindow = DeviceUtils.isInMultiWindow(activity);
|
|
|
|
if (!isInMultiWindow) {
|
2020-09-15 13:43:43 +02:00
|
|
|
visibility |= View.SYSTEM_UI_FLAG_FULLSCREEN;
|
|
|
|
}
|
2020-01-03 06:05:31 +01:00
|
|
|
activity.getWindow().getDecorView().setSystemUiVisibility(visibility);
|
2020-09-15 13:43:43 +02:00
|
|
|
|
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
|
2021-11-27 15:58:48 +01:00
|
|
|
&& isMultiWindowOrFullscreen()) {
|
2020-09-15 13:43:43 +02:00
|
|
|
activity.getWindow().setStatusBarColor(Color.TRANSPARENT);
|
2020-09-15 18:50:46 +02:00
|
|
|
activity.getWindow().setNavigationBarColor(Color.TRANSPARENT);
|
2020-09-15 13:43:43 +02:00
|
|
|
}
|
|
|
|
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
2019-12-29 22:15:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Listener implementation
|
2020-02-29 00:57:54 +01:00
|
|
|
public void hideSystemUiIfNeeded() {
|
2021-06-23 07:11:41 +02:00
|
|
|
if (isPlayerAvailable()
|
2020-07-14 19:21:32 +02:00
|
|
|
&& player.isFullscreen()
|
|
|
|
&& bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED) {
|
2019-12-29 22:15:01 +01:00
|
|
|
hideSystemUi();
|
2020-07-14 19:21:32 +02:00
|
|
|
}
|
2019-12-29 22:15:01 +01:00
|
|
|
}
|
|
|
|
|
2021-11-27 15:58:48 +01:00
|
|
|
private boolean isMultiWindowOrFullscreen() {
|
|
|
|
return DeviceUtils.isInMultiWindow(activity)
|
|
|
|
|| (isPlayerAvailable() && player.isFullscreen());
|
|
|
|
}
|
|
|
|
|
2020-02-29 00:57:54 +01:00
|
|
|
private boolean playerIsNotStopped() {
|
2021-06-23 07:11:41 +02:00
|
|
|
return isPlayerAvailable() && !player.isStopped();
|
2020-02-29 00:57:54 +01:00
|
|
|
}
|
|
|
|
|
2020-09-11 19:52:38 +02:00
|
|
|
private void restoreDefaultBrightness() {
|
2020-09-04 04:39:55 +02:00
|
|
|
final WindowManager.LayoutParams lp = activity.getWindow().getAttributes();
|
|
|
|
if (lp.screenBrightness == -1) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Restore the old brightness when fragment.onPause() called or
|
|
|
|
// when a player is in portrait
|
|
|
|
lp.screenBrightness = -1;
|
|
|
|
activity.getWindow().setAttributes(lp);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void setupBrightness() {
|
2020-07-14 19:21:32 +02:00
|
|
|
if (activity == null) {
|
|
|
|
return;
|
|
|
|
}
|
2020-01-08 17:16:50 +01:00
|
|
|
|
2020-07-12 02:59:47 +02:00
|
|
|
final WindowManager.LayoutParams lp = activity.getWindow().getAttributes();
|
2021-06-23 07:11:41 +02:00
|
|
|
if (!isPlayerAvailable()
|
2020-09-04 04:39:55 +02:00
|
|
|
|| !player.videoPlayerSelected()
|
|
|
|
|| !player.isFullscreen()
|
|
|
|
|| bottomSheetState != BottomSheetBehavior.STATE_EXPANDED) {
|
|
|
|
// Apply system brightness when the player is not in fullscreen
|
2020-09-11 19:52:38 +02:00
|
|
|
restoreDefaultBrightness();
|
2019-12-29 22:15:01 +01:00
|
|
|
} else {
|
2020-12-26 18:47:08 +01:00
|
|
|
// Do not restore if user has disabled brightness gesture
|
|
|
|
if (!PlayerHelper.isBrightnessGestureEnabled(activity)) {
|
|
|
|
return;
|
|
|
|
}
|
2019-12-29 22:15:01 +01:00
|
|
|
// Restore already saved brightness level
|
2020-07-12 02:59:47 +02:00
|
|
|
final float brightnessLevel = PlayerHelper.getScreenBrightness(activity);
|
2020-09-04 04:39:55 +02:00
|
|
|
if (brightnessLevel == lp.screenBrightness) {
|
2019-12-29 22:15:01 +01:00
|
|
|
return;
|
2020-07-14 19:21:32 +02:00
|
|
|
}
|
2019-12-29 22:15:01 +01:00
|
|
|
lp.screenBrightness = brightnessLevel;
|
2020-09-04 04:39:55 +02:00
|
|
|
activity.getWindow().setAttributes(lp);
|
2019-12-29 22:15:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void checkLandscape() {
|
2020-07-14 19:21:32 +02:00
|
|
|
if ((!player.isPlaying() && player.getPlayQueue() != playQueue)
|
|
|
|
|| player.getPlayQueue() == null) {
|
2020-10-18 20:19:50 +02:00
|
|
|
setAutoPlay(true);
|
2020-07-14 19:21:32 +02:00
|
|
|
}
|
2019-12-29 22:15:01 +01:00
|
|
|
|
2020-02-25 00:15:22 +01:00
|
|
|
player.checkLandscape();
|
2019-12-29 22:15:01 +01:00
|
|
|
// Let's give a user time to look at video information page if video is not playing
|
2020-09-27 03:11:38 +02:00
|
|
|
if (globalScreenOrientationLocked(activity) && !player.isPlaying()) {
|
2021-01-08 18:35:33 +01:00
|
|
|
player.play();
|
2020-01-16 12:20:22 +01:00
|
|
|
}
|
2019-12-29 22:15:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2020-07-14 19:21:32 +02:00
|
|
|
* Means that the player fragment was swiped away via BottomSheetLayout
|
|
|
|
* and is empty but ready for any new actions. See cleanUp()
|
2020-07-12 02:59:47 +02:00
|
|
|
* */
|
2019-12-29 22:15:01 +01:00
|
|
|
private boolean wasCleared() {
|
|
|
|
return url == null;
|
|
|
|
}
|
|
|
|
|
2020-10-18 09:50:42 +02:00
|
|
|
@Nullable
|
2020-07-12 02:59:47 +02:00
|
|
|
private StackItem findQueueInStack(final PlayQueue queue) {
|
2020-03-10 10:06:38 +01:00
|
|
|
StackItem item = null;
|
2020-07-12 02:59:47 +02:00
|
|
|
final Iterator<StackItem> iterator = stack.descendingIterator();
|
2020-03-10 10:06:38 +01:00
|
|
|
while (iterator.hasNext()) {
|
2020-07-12 02:59:47 +02:00
|
|
|
final StackItem next = iterator.next();
|
2020-03-10 10:06:38 +01:00
|
|
|
if (next.getPlayQueue().equals(queue)) {
|
|
|
|
item = next;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return item;
|
|
|
|
}
|
|
|
|
|
2020-06-27 05:25:50 +02:00
|
|
|
private void replaceQueueIfUserConfirms(final Runnable onAllow) {
|
2021-06-23 07:11:41 +02:00
|
|
|
@Nullable final PlayQueue activeQueue = isPlayerAvailable() ? player.getPlayQueue() : null;
|
2020-06-27 05:25:50 +02:00
|
|
|
|
2020-03-10 10:06:38 +01:00
|
|
|
// Player will have STATE_IDLE when a user pressed back button
|
2020-06-27 05:25:50 +02:00
|
|
|
if (isClearingQueueConfirmationRequired(activity)
|
|
|
|
&& playerIsNotStopped()
|
|
|
|
&& activeQueue != null
|
2020-10-09 16:46:42 +02:00
|
|
|
&& !activeQueue.equals(playQueue)) {
|
2020-06-27 05:25:50 +02:00
|
|
|
showClearingQueueConfirmation(onAllow);
|
|
|
|
} else {
|
|
|
|
onAllow.run();
|
|
|
|
}
|
2020-03-10 10:06:38 +01:00
|
|
|
}
|
|
|
|
|
2020-07-12 02:59:47 +02:00
|
|
|
private void showClearingQueueConfirmation(final Runnable onAllow) {
|
2020-03-10 10:06:38 +01:00
|
|
|
new AlertDialog.Builder(activity)
|
2020-07-23 23:43:09 +02:00
|
|
|
.setTitle(R.string.clear_queue_confirmation_description)
|
2021-08-04 18:55:32 +02:00
|
|
|
.setNegativeButton(R.string.cancel, null)
|
2021-08-30 16:18:40 +02:00
|
|
|
.setPositiveButton(R.string.ok, (dialog, which) -> {
|
2020-03-10 10:06:38 +01:00
|
|
|
onAllow.run();
|
|
|
|
dialog.dismiss();
|
|
|
|
}).show();
|
|
|
|
}
|
|
|
|
|
2020-07-23 23:43:09 +02:00
|
|
|
private void showExternalPlaybackDialog() {
|
|
|
|
if (sortedVideoStreams == null) {
|
|
|
|
return;
|
|
|
|
}
|
2020-08-16 10:24:58 +02:00
|
|
|
final CharSequence[] resolutions = new CharSequence[sortedVideoStreams.size()];
|
2020-07-23 23:43:09 +02:00
|
|
|
for (int i = 0; i < sortedVideoStreams.size(); i++) {
|
|
|
|
resolutions[i] = sortedVideoStreams.get(i).getResolution();
|
|
|
|
}
|
2020-08-16 10:24:58 +02:00
|
|
|
final AlertDialog.Builder builder = new AlertDialog.Builder(activity)
|
2021-08-04 18:55:32 +02:00
|
|
|
.setNegativeButton(R.string.cancel, null)
|
2020-07-23 23:43:09 +02:00
|
|
|
.setNeutralButton(R.string.open_in_browser, (dialog, i) ->
|
|
|
|
ShareUtils.openUrlInBrowser(requireActivity(), url)
|
|
|
|
);
|
|
|
|
// Maybe there are no video streams available, show just `open in browser` button
|
|
|
|
if (resolutions.length > 0) {
|
|
|
|
builder.setSingleChoiceItems(resolutions, selectedVideoStreamIndex, (dialog, i) -> {
|
|
|
|
dialog.dismiss();
|
|
|
|
startOnExternalPlayer(activity, currentInfo, sortedVideoStreams.get(i));
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
builder.show();
|
|
|
|
}
|
|
|
|
|
2019-12-29 22:15:01 +01:00
|
|
|
/*
|
2020-07-12 02:59:47 +02:00
|
|
|
* Remove unneeded information while waiting for a next task
|
|
|
|
* */
|
2019-12-29 22:15:01 +01:00
|
|
|
private void cleanUp() {
|
|
|
|
// New beginning
|
|
|
|
stack.clear();
|
2020-07-14 19:21:32 +02:00
|
|
|
if (currentWorker != null) {
|
|
|
|
currentWorker.dispose();
|
|
|
|
}
|
2021-06-24 10:00:56 +02:00
|
|
|
playerHolder.stopService();
|
2020-07-12 02:59:47 +02:00
|
|
|
setInitialData(0, null, "", null);
|
2019-12-29 22:15:01 +01:00
|
|
|
currentInfo = null;
|
2020-02-05 06:59:30 +01:00
|
|
|
updateOverlayData(null, null, null);
|
2019-12-29 22:15:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*//////////////////////////////////////////////////////////////////////////
|
|
|
|
// Bottom mini player
|
|
|
|
//////////////////////////////////////////////////////////////////////////*/
|
|
|
|
|
2020-07-25 03:14:29 +02:00
|
|
|
/**
|
|
|
|
* That's for Android TV support. Move focus from main fragment to the player or back
|
|
|
|
* based on what is currently selected
|
2020-07-25 06:00:53 +02:00
|
|
|
*
|
2020-07-25 03:14:29 +02:00
|
|
|
* @param toMain if true than the main fragment will be focused or the player otherwise
|
2020-07-25 06:00:53 +02:00
|
|
|
*/
|
2020-07-25 03:14:29 +02:00
|
|
|
private void moveFocusToMainFragment(final boolean toMain) {
|
2020-09-04 04:39:55 +02:00
|
|
|
setupBrightness();
|
2020-07-25 03:14:29 +02:00
|
|
|
final ViewGroup mainFragment = requireActivity().findViewById(R.id.fragment_holder);
|
|
|
|
// Hamburger button steels a focus even under bottomSheet
|
|
|
|
final Toolbar toolbar = requireActivity().findViewById(R.id.toolbar);
|
|
|
|
final int afterDescendants = ViewGroup.FOCUS_AFTER_DESCENDANTS;
|
|
|
|
final int blockDescendants = ViewGroup.FOCUS_BLOCK_DESCENDANTS;
|
|
|
|
if (toMain) {
|
|
|
|
mainFragment.setDescendantFocusability(afterDescendants);
|
|
|
|
toolbar.setDescendantFocusability(afterDescendants);
|
|
|
|
((ViewGroup) requireView()).setDescendantFocusability(blockDescendants);
|
2022-01-03 11:52:08 +01:00
|
|
|
// Only focus the mainFragment if the mainFragment (e.g. search-results)
|
|
|
|
// or the toolbar (e.g. Textfield for search) don't have focus.
|
|
|
|
// This was done to fix problems with the keyboard input, see also #7490
|
|
|
|
if (!mainFragment.hasFocus() && !toolbar.hasFocus()) {
|
|
|
|
mainFragment.requestFocus();
|
|
|
|
}
|
2020-07-25 03:14:29 +02:00
|
|
|
} else {
|
|
|
|
mainFragment.setDescendantFocusability(blockDescendants);
|
|
|
|
toolbar.setDescendantFocusability(blockDescendants);
|
|
|
|
((ViewGroup) requireView()).setDescendantFocusability(afterDescendants);
|
2022-01-03 11:52:08 +01:00
|
|
|
// Only focus the player if it not already has focus
|
|
|
|
if (!binding.getRoot().hasFocus()) {
|
|
|
|
binding.detailThumbnailRootLayout.requestFocus();
|
|
|
|
}
|
2020-07-25 03:14:29 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-15 19:51:52 +02:00
|
|
|
/**
|
|
|
|
* When the mini player exists the view underneath it is not touchable.
|
|
|
|
* Bottom padding should be equal to the mini player's height in this case
|
|
|
|
*
|
|
|
|
* @param showMore whether main fragment should be expanded or not
|
2020-09-04 04:39:55 +02:00
|
|
|
*/
|
2020-08-15 19:51:52 +02:00
|
|
|
private void manageSpaceAtTheBottom(final boolean showMore) {
|
|
|
|
final int peekHeight = getResources().getDimensionPixelSize(R.dimen.mini_player_height);
|
|
|
|
final ViewGroup holder = requireActivity().findViewById(R.id.fragment_holder);
|
|
|
|
final int newBottomPadding;
|
|
|
|
if (showMore) {
|
|
|
|
newBottomPadding = 0;
|
|
|
|
} else {
|
|
|
|
newBottomPadding = peekHeight;
|
|
|
|
}
|
|
|
|
if (holder.getPaddingBottom() == newBottomPadding) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
holder.setPadding(holder.getPaddingLeft(),
|
|
|
|
holder.getPaddingTop(),
|
|
|
|
holder.getPaddingRight(),
|
|
|
|
newBottomPadding);
|
|
|
|
}
|
|
|
|
|
2019-12-29 22:15:01 +01:00
|
|
|
private void setupBottomPlayer() {
|
2020-07-14 19:21:32 +02:00
|
|
|
final CoordinatorLayout.LayoutParams params =
|
2021-01-17 05:21:11 +01:00
|
|
|
(CoordinatorLayout.LayoutParams) binding.appBarLayout.getLayoutParams();
|
2020-07-12 02:59:47 +02:00
|
|
|
final AppBarLayout.Behavior behavior = (AppBarLayout.Behavior) params.getBehavior();
|
2019-12-29 22:15:01 +01:00
|
|
|
|
2020-07-12 02:59:47 +02:00
|
|
|
final FrameLayout bottomSheetLayout = activity.findViewById(R.id.fragment_player_holder);
|
2019-12-29 22:15:01 +01:00
|
|
|
bottomSheetBehavior = BottomSheetBehavior.from(bottomSheetLayout);
|
|
|
|
bottomSheetBehavior.setState(bottomSheetState);
|
|
|
|
final int peekHeight = getResources().getDimensionPixelSize(R.dimen.mini_player_height);
|
|
|
|
if (bottomSheetState != BottomSheetBehavior.STATE_HIDDEN) {
|
2020-08-15 19:51:52 +02:00
|
|
|
manageSpaceAtTheBottom(false);
|
2019-12-29 22:15:01 +01:00
|
|
|
bottomSheetBehavior.setPeekHeight(peekHeight);
|
2020-01-17 15:37:53 +01:00
|
|
|
if (bottomSheetState == BottomSheetBehavior.STATE_COLLAPSED) {
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.overlayLayout.setAlpha(MAX_OVERLAY_ALPHA);
|
2020-01-17 15:37:53 +01:00
|
|
|
} else if (bottomSheetState == BottomSheetBehavior.STATE_EXPANDED) {
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.overlayLayout.setAlpha(0);
|
2019-12-29 22:15:01 +01:00
|
|
|
setOverlayElementsClickable(false);
|
2020-01-17 15:37:53 +01:00
|
|
|
}
|
2019-12-29 22:15:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
|
2020-07-12 02:59:47 +02:00
|
|
|
@Override
|
|
|
|
public void onStateChanged(@NonNull final View bottomSheet, final int newState) {
|
2019-12-29 22:15:01 +01:00
|
|
|
bottomSheetState = newState;
|
2020-07-25 03:14:29 +02:00
|
|
|
|
2019-12-29 22:15:01 +01:00
|
|
|
switch (newState) {
|
|
|
|
case BottomSheetBehavior.STATE_HIDDEN:
|
2020-07-25 03:14:29 +02:00
|
|
|
moveFocusToMainFragment(true);
|
2020-08-15 19:51:52 +02:00
|
|
|
manageSpaceAtTheBottom(true);
|
2020-07-25 03:14:29 +02:00
|
|
|
|
2019-12-29 22:15:01 +01:00
|
|
|
bottomSheetBehavior.setPeekHeight(0);
|
|
|
|
cleanUp();
|
|
|
|
break;
|
|
|
|
case BottomSheetBehavior.STATE_EXPANDED:
|
2020-07-25 03:14:29 +02:00
|
|
|
moveFocusToMainFragment(false);
|
2020-08-15 19:51:52 +02:00
|
|
|
manageSpaceAtTheBottom(false);
|
2020-07-25 03:14:29 +02:00
|
|
|
|
2019-12-29 22:15:01 +01:00
|
|
|
bottomSheetBehavior.setPeekHeight(peekHeight);
|
2020-07-14 19:21:32 +02:00
|
|
|
// Disable click because overlay buttons located on top of buttons
|
|
|
|
// from the player
|
2019-12-29 22:15:01 +01:00
|
|
|
setOverlayElementsClickable(false);
|
2020-02-29 00:57:54 +01:00
|
|
|
hideSystemUiIfNeeded();
|
2020-06-27 05:25:50 +02:00
|
|
|
// Conditions when the player should be expanded to fullscreen
|
2021-03-25 22:54:55 +01:00
|
|
|
if (DeviceUtils.isLandscape(requireContext())
|
2021-06-23 07:11:41 +02:00
|
|
|
&& isPlayerAvailable()
|
2020-02-05 06:59:30 +01:00
|
|
|
&& player.isPlaying()
|
2020-06-27 05:25:50 +02:00
|
|
|
&& !player.isFullscreen()
|
2020-07-21 00:43:49 +02:00
|
|
|
&& !DeviceUtils.isTablet(activity)
|
2020-07-14 19:21:32 +02:00
|
|
|
&& player.videoPlayerSelected()) {
|
|
|
|
player.toggleFullscreen();
|
|
|
|
}
|
2021-01-17 05:21:11 +01:00
|
|
|
setOverlayLook(binding.appBarLayout, behavior, 1);
|
2019-12-29 22:15:01 +01:00
|
|
|
break;
|
|
|
|
case BottomSheetBehavior.STATE_COLLAPSED:
|
2020-07-25 03:14:29 +02:00
|
|
|
moveFocusToMainFragment(true);
|
2020-09-29 05:22:53 +02:00
|
|
|
manageSpaceAtTheBottom(false);
|
|
|
|
|
|
|
|
bottomSheetBehavior.setPeekHeight(peekHeight);
|
2020-07-25 03:14:29 +02:00
|
|
|
|
2019-12-29 22:15:01 +01:00
|
|
|
// Re-enable clicks
|
|
|
|
setOverlayElementsClickable(true);
|
2021-06-23 07:11:41 +02:00
|
|
|
if (isPlayerAvailable()) {
|
2021-01-14 21:58:19 +01:00
|
|
|
player.closeItemsList();
|
2020-07-14 19:21:32 +02:00
|
|
|
}
|
2021-01-17 05:21:11 +01:00
|
|
|
setOverlayLook(binding.appBarLayout, behavior, 0);
|
2019-12-29 22:15:01 +01:00
|
|
|
break;
|
|
|
|
case BottomSheetBehavior.STATE_DRAGGING:
|
|
|
|
case BottomSheetBehavior.STATE_SETTLING:
|
2021-06-23 07:11:41 +02:00
|
|
|
if (isPlayerAvailable() && player.isFullscreen()) {
|
2020-07-14 19:21:32 +02:00
|
|
|
showSystemUi();
|
|
|
|
}
|
2021-06-23 07:11:41 +02:00
|
|
|
if (isPlayerAvailable() && player.isControlsVisible()) {
|
2020-07-14 19:21:32 +02:00
|
|
|
player.hideControls(0, 0);
|
|
|
|
}
|
2019-12-29 22:15:01 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2020-07-12 02:59:47 +02:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onSlide(@NonNull final View bottomSheet, final float slideOffset) {
|
2021-01-17 05:21:11 +01:00
|
|
|
setOverlayLook(binding.appBarLayout, behavior, slideOffset);
|
2019-12-29 22:15:01 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// User opened a new page and the player will hide itself
|
|
|
|
activity.getSupportFragmentManager().addOnBackStackChangedListener(() -> {
|
2020-07-14 19:21:32 +02:00
|
|
|
if (bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED) {
|
2019-12-29 22:15:01 +01:00
|
|
|
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
|
2020-07-14 19:21:32 +02:00
|
|
|
}
|
2019-12-29 22:15:01 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-10-18 20:19:50 +02:00
|
|
|
private void updateOverlayData(@Nullable final String overlayTitle,
|
2020-07-14 19:21:32 +02:00
|
|
|
@Nullable final String uploader,
|
|
|
|
@Nullable final String thumbnailUrl) {
|
2021-03-13 20:12:23 +01:00
|
|
|
binding.overlayTitleTextView.setText(isEmpty(overlayTitle) ? "" : overlayTitle);
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.overlayChannelTextView.setText(isEmpty(uploader) ? "" : uploader);
|
|
|
|
binding.overlayThumbnail.setImageResource(R.drawable.dummy_thumbnail_dark);
|
2021-03-27 14:37:44 +01:00
|
|
|
PicassoHelper.loadThumbnail(thumbnailUrl).tag(PICASSO_VIDEO_DETAILS_TAG)
|
|
|
|
.into(binding.overlayThumbnail);
|
2020-02-05 06:59:30 +01:00
|
|
|
}
|
|
|
|
|
2020-09-29 05:22:53 +02:00
|
|
|
private void setOverlayPlayPauseImage(final boolean playerIsPlaying) {
|
2021-03-27 15:45:49 +01:00
|
|
|
final int drawable = playerIsPlaying
|
|
|
|
? R.drawable.ic_pause
|
|
|
|
: R.drawable.ic_play_arrow;
|
|
|
|
binding.overlayPlayPauseButton.setImageResource(drawable);
|
2019-12-29 22:15:01 +01:00
|
|
|
}
|
|
|
|
|
2020-07-14 19:21:32 +02:00
|
|
|
private void setOverlayLook(final AppBarLayout appBar,
|
|
|
|
final AppBarLayout.Behavior behavior,
|
|
|
|
final float slideOffset) {
|
2020-07-14 20:52:55 +02:00
|
|
|
// SlideOffset < 0 when mini player is about to close via swipe.
|
|
|
|
// Stop animation in this case
|
|
|
|
if (behavior == null || slideOffset < 0) {
|
|
|
|
return;
|
2019-12-29 22:15:01 +01:00
|
|
|
}
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.overlayLayout.setAlpha(Math.min(MAX_OVERLAY_ALPHA, 1 - slideOffset));
|
2020-07-14 20:52:55 +02:00
|
|
|
// These numbers are not special. They just do a cool transition
|
|
|
|
behavior.setTopAndBottomOffset(
|
2021-01-17 05:21:11 +01:00
|
|
|
(int) (-binding.detailThumbnailImageView.getHeight() * 2 * (1 - slideOffset) / 3));
|
2020-07-14 20:52:55 +02:00
|
|
|
appBar.requestLayout();
|
2019-12-29 22:15:01 +01:00
|
|
|
}
|
|
|
|
|
2020-07-12 02:59:47 +02:00
|
|
|
private void setOverlayElementsClickable(final boolean enable) {
|
2021-01-17 05:21:11 +01:00
|
|
|
binding.overlayThumbnail.setClickable(enable);
|
|
|
|
binding.overlayThumbnail.setLongClickable(enable);
|
|
|
|
binding.overlayMetadataLayout.setClickable(enable);
|
|
|
|
binding.overlayMetadataLayout.setLongClickable(enable);
|
|
|
|
binding.overlayButtonsLayout.setClickable(enable);
|
|
|
|
binding.overlayPlayPauseButton.setClickable(enable);
|
|
|
|
binding.overlayCloseButton.setClickable(enable);
|
2019-12-29 22:15:01 +01:00
|
|
|
}
|
2021-06-23 07:11:41 +02:00
|
|
|
|
|
|
|
// helpers to check the state of player and playerService
|
|
|
|
boolean isPlayerAvailable() {
|
|
|
|
return (player != null);
|
|
|
|
}
|
|
|
|
|
|
|
|
boolean isPlayerServiceAvailable() {
|
|
|
|
return (playerService != null);
|
|
|
|
}
|
|
|
|
|
|
|
|
boolean isPlayerAndPlayerServiceAvailable() {
|
|
|
|
return (player != null && playerService != null);
|
|
|
|
}
|
2018-11-20 23:10:50 +01:00
|
|
|
}
|