Merge branch 'play-queue-enhancement' of https://github.com/karyogamy/NewPipe into pqe
This commit is contained in:
commit
6d60e6698a
|
@ -36,6 +36,7 @@ import android.widget.FrameLayout;
|
||||||
import android.widget.ImageButton;
|
import android.widget.ImageButton;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.PopupMenu;
|
||||||
import android.widget.RelativeLayout;
|
import android.widget.RelativeLayout;
|
||||||
import android.widget.Spinner;
|
import android.widget.Spinner;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
@ -62,9 +63,10 @@ import org.schabi.newpipe.fragments.BackPressable;
|
||||||
import org.schabi.newpipe.fragments.BaseStateFragment;
|
import org.schabi.newpipe.fragments.BaseStateFragment;
|
||||||
import org.schabi.newpipe.history.HistoryListener;
|
import org.schabi.newpipe.history.HistoryListener;
|
||||||
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
||||||
import org.schabi.newpipe.player.BackgroundPlayer;
|
import org.schabi.newpipe.info_list.InfoItemDialog;
|
||||||
import org.schabi.newpipe.player.MainVideoPlayer;
|
import org.schabi.newpipe.player.MainVideoPlayer;
|
||||||
import org.schabi.newpipe.player.PopupVideoPlayer;
|
import org.schabi.newpipe.player.PopupVideoPlayer;
|
||||||
|
import org.schabi.newpipe.player.helper.PlayerHelper;
|
||||||
import org.schabi.newpipe.player.old.PlayVideoActivity;
|
import org.schabi.newpipe.player.old.PlayVideoActivity;
|
||||||
import org.schabi.newpipe.playlist.PlayQueue;
|
import org.schabi.newpipe.playlist.PlayQueue;
|
||||||
import org.schabi.newpipe.playlist.SinglePlayQueue;
|
import org.schabi.newpipe.playlist.SinglePlayQueue;
|
||||||
|
@ -459,6 +461,11 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
|
||||||
public void selected(StreamInfoItem selectedItem) {
|
public void selected(StreamInfoItem selectedItem) {
|
||||||
selectAndLoadVideo(selectedItem.service_id, selectedItem.url, selectedItem.name);
|
selectAndLoadVideo(selectedItem.service_id, selectedItem.url, selectedItem.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void held(StreamInfoItem selectedItem) {
|
||||||
|
showStreamDialog(selectedItem);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
videoTitleRoot.setOnClickListener(this);
|
videoTitleRoot.setOnClickListener(this);
|
||||||
|
@ -476,6 +483,32 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
|
||||||
detailControlsPopup.setOnTouchListener(getOnControlsTouchListener());
|
detailControlsPopup.setOnTouchListener(getOnControlsTouchListener());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void showStreamDialog(final StreamInfoItem item) {
|
||||||
|
final Context context = getContext();
|
||||||
|
final String[] commands = new String[]{
|
||||||
|
context.getResources().getString(R.string.enqueue_on_background),
|
||||||
|
context.getResources().getString(R.string.enqueue_on_popup)
|
||||||
|
};
|
||||||
|
|
||||||
|
final DialogInterface.OnClickListener actions = new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialogInterface, int i) {
|
||||||
|
switch (i) {
|
||||||
|
case 0:
|
||||||
|
NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(item));
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
NavigationHelper.enqueueOnPopupPlayer(context, new SinglePlayQueue(item));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
new InfoItemDialog(getActivity(), item, commands, actions).show();
|
||||||
|
}
|
||||||
|
|
||||||
private View.OnTouchListener getOnControlsTouchListener() {
|
private View.OnTouchListener getOnControlsTouchListener() {
|
||||||
return new View.OnTouchListener() {
|
return new View.OnTouchListener() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -792,17 +825,17 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
|
||||||
((HistoryListener) activity).onVideoPlayed(currentInfo, getSelectedVideoStream());
|
((HistoryListener) activity).onVideoPlayed(currentInfo, getSelectedVideoStream());
|
||||||
}
|
}
|
||||||
|
|
||||||
final PlayQueue playQueue = new SinglePlayQueue(currentInfo);
|
final PlayQueue itemQueue = new SinglePlayQueue(currentInfo);
|
||||||
final Intent intent;
|
|
||||||
if (append) {
|
if (append) {
|
||||||
Toast.makeText(activity, R.string.popup_playing_append, Toast.LENGTH_SHORT).show();
|
NavigationHelper.enqueueOnPopupPlayer(activity, itemQueue);
|
||||||
intent = NavigationHelper.getPlayerEnqueueIntent(activity, PopupVideoPlayer.class, playQueue);
|
|
||||||
} else {
|
} else {
|
||||||
Toast.makeText(activity, R.string.popup_playing_toast, Toast.LENGTH_SHORT).show();
|
Toast.makeText(activity, R.string.popup_playing_toast, Toast.LENGTH_SHORT).show();
|
||||||
intent = NavigationHelper.getPlayerIntent(activity, PopupVideoPlayer.class, playQueue, getSelectedVideoStream().resolution);
|
final Intent intent = NavigationHelper.getPlayerIntent(
|
||||||
}
|
activity, PopupVideoPlayer.class, itemQueue, getSelectedVideoStream().resolution
|
||||||
|
);
|
||||||
activity.startService(intent);
|
activity.startService(intent);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void openVideoPlayer() {
|
private void openVideoPlayer() {
|
||||||
VideoStream selectedVideoStream = getSelectedVideoStream();
|
VideoStream selectedVideoStream = getSelectedVideoStream();
|
||||||
|
@ -820,13 +853,11 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
|
||||||
|
|
||||||
|
|
||||||
private void openNormalBackgroundPlayer(final boolean append) {
|
private void openNormalBackgroundPlayer(final boolean append) {
|
||||||
final PlayQueue playQueue = new SinglePlayQueue(currentInfo);
|
final PlayQueue itemQueue = new SinglePlayQueue(currentInfo);
|
||||||
if (append) {
|
if (append) {
|
||||||
activity.startService(NavigationHelper.getPlayerEnqueueIntent(activity, BackgroundPlayer.class, playQueue));
|
NavigationHelper.enqueueOnBackgroundPlayer(activity, itemQueue);
|
||||||
Toast.makeText(activity, R.string.background_player_append, Toast.LENGTH_SHORT).show();
|
|
||||||
} else {
|
} else {
|
||||||
activity.startService(NavigationHelper.getPlayerIntent(activity, BackgroundPlayer.class, playQueue));
|
NavigationHelper.playOnBackgroundPlayer(activity, itemQueue);
|
||||||
Toast.makeText(activity, R.string.background_player_playing_toast, Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -866,8 +897,7 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
|
||||||
|
|
||||||
private void openNormalPlayer(VideoStream selectedVideoStream) {
|
private void openNormalPlayer(VideoStream selectedVideoStream) {
|
||||||
Intent mIntent;
|
Intent mIntent;
|
||||||
boolean useOldPlayer = PreferenceManager.getDefaultSharedPreferences(activity).getBoolean(getString(R.string.use_old_player_key), false)
|
boolean useOldPlayer = PlayerHelper.isUsingOldPlayer(activity) || (Build.VERSION.SDK_INT < 16);
|
||||||
|| (Build.VERSION.SDK_INT < 16);
|
|
||||||
if (!useOldPlayer) {
|
if (!useOldPlayer) {
|
||||||
// ExoPlayer
|
// ExoPlayer
|
||||||
final PlayQueue playQueue = new SinglePlayQueue(currentInfo);
|
final PlayQueue playQueue = new SinglePlayQueue(currentInfo);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package org.schabi.newpipe.fragments.list;
|
package org.schabi.newpipe.fragments.list;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.v7.app.ActionBar;
|
import android.support.v7.app.ActionBar;
|
||||||
|
@ -19,7 +20,9 @@ import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||||
import org.schabi.newpipe.fragments.BaseStateFragment;
|
import org.schabi.newpipe.fragments.BaseStateFragment;
|
||||||
import org.schabi.newpipe.fragments.OnScrollBelowItemsListener;
|
import org.schabi.newpipe.fragments.OnScrollBelowItemsListener;
|
||||||
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
||||||
|
import org.schabi.newpipe.info_list.InfoItemDialog;
|
||||||
import org.schabi.newpipe.info_list.InfoListAdapter;
|
import org.schabi.newpipe.info_list.InfoListAdapter;
|
||||||
|
import org.schabi.newpipe.playlist.SinglePlayQueue;
|
||||||
import org.schabi.newpipe.util.NavigationHelper;
|
import org.schabi.newpipe.util.NavigationHelper;
|
||||||
import org.schabi.newpipe.util.StateSaver;
|
import org.schabi.newpipe.util.StateSaver;
|
||||||
|
|
||||||
|
@ -139,6 +142,11 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
|
||||||
useAsFrontPage?getParentFragment().getFragmentManager():getFragmentManager(),
|
useAsFrontPage?getParentFragment().getFragmentManager():getFragmentManager(),
|
||||||
selectedItem.service_id, selectedItem.url, selectedItem.name);
|
selectedItem.service_id, selectedItem.url, selectedItem.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void held(StreamInfoItem selectedItem) {
|
||||||
|
showStreamDialog(selectedItem);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
infoListAdapter.setOnChannelSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener<ChannelInfoItem>() {
|
infoListAdapter.setOnChannelSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener<ChannelInfoItem>() {
|
||||||
|
@ -149,6 +157,9 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
|
||||||
useAsFrontPage?getParentFragment().getFragmentManager():getFragmentManager(),
|
useAsFrontPage?getParentFragment().getFragmentManager():getFragmentManager(),
|
||||||
selectedItem.service_id, selectedItem.url, selectedItem.name);
|
selectedItem.service_id, selectedItem.url, selectedItem.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void held(ChannelInfoItem selectedItem) {}
|
||||||
});
|
});
|
||||||
|
|
||||||
infoListAdapter.setOnPlaylistSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener<PlaylistInfoItem>() {
|
infoListAdapter.setOnPlaylistSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener<PlaylistInfoItem>() {
|
||||||
|
@ -159,6 +170,9 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
|
||||||
useAsFrontPage?getParentFragment().getFragmentManager():getFragmentManager(),
|
useAsFrontPage?getParentFragment().getFragmentManager():getFragmentManager(),
|
||||||
selectedItem.service_id, selectedItem.url, selectedItem.name);
|
selectedItem.service_id, selectedItem.url, selectedItem.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void held(PlaylistInfoItem selectedItem) {}
|
||||||
});
|
});
|
||||||
|
|
||||||
itemsList.clearOnScrollListeners();
|
itemsList.clearOnScrollListeners();
|
||||||
|
@ -176,6 +190,31 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void showStreamDialog(final StreamInfoItem item) {
|
||||||
|
final Context context = getContext();
|
||||||
|
final String[] commands = new String[]{
|
||||||
|
context.getResources().getString(R.string.enqueue_on_background),
|
||||||
|
context.getResources().getString(R.string.enqueue_on_popup)
|
||||||
|
};
|
||||||
|
|
||||||
|
final DialogInterface.OnClickListener actions = new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialogInterface, int i) {
|
||||||
|
switch (i) {
|
||||||
|
case 0:
|
||||||
|
NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(item));
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
NavigationHelper.enqueueOnPopupPlayer(context, new SinglePlayQueue(item));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
new InfoItemDialog(getActivity(), item, commands, actions).show();
|
||||||
|
}
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Menu
|
// Menu
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
package org.schabi.newpipe.fragments.list.channel;
|
package org.schabi.newpipe.fragments.list.channel;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
@ -10,6 +12,7 @@ import android.support.v4.content.ContextCompat;
|
||||||
import android.support.v7.app.ActionBar;
|
import android.support.v7.app.ActionBar;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.view.Gravity;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
|
@ -18,7 +21,9 @@ import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.jakewharton.rxbinding2.view.RxView;
|
import com.jakewharton.rxbinding2.view.RxView;
|
||||||
|
|
||||||
|
@ -28,12 +33,19 @@ import org.schabi.newpipe.extractor.ListExtractor;
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
import org.schabi.newpipe.extractor.NewPipe;
|
||||||
import org.schabi.newpipe.extractor.channel.ChannelInfo;
|
import org.schabi.newpipe.extractor.channel.ChannelInfo;
|
||||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||||
|
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||||
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
|
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
|
||||||
import org.schabi.newpipe.fragments.subscription.SubscriptionService;
|
import org.schabi.newpipe.fragments.subscription.SubscriptionService;
|
||||||
|
import org.schabi.newpipe.info_list.InfoItemDialog;
|
||||||
|
import org.schabi.newpipe.playlist.ChannelPlayQueue;
|
||||||
|
import org.schabi.newpipe.playlist.PlayQueue;
|
||||||
|
import org.schabi.newpipe.playlist.SinglePlayQueue;
|
||||||
import org.schabi.newpipe.report.UserAction;
|
import org.schabi.newpipe.report.UserAction;
|
||||||
import org.schabi.newpipe.util.AnimationUtils;
|
import org.schabi.newpipe.util.AnimationUtils;
|
||||||
import org.schabi.newpipe.util.ExtractorHelper;
|
import org.schabi.newpipe.util.ExtractorHelper;
|
||||||
import org.schabi.newpipe.util.Localization;
|
import org.schabi.newpipe.util.Localization;
|
||||||
|
import org.schabi.newpipe.util.NavigationHelper;
|
||||||
|
import org.schabi.newpipe.util.PermissionHelper;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
@ -69,6 +81,10 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
||||||
private TextView headerSubscribersTextView;
|
private TextView headerSubscribersTextView;
|
||||||
private Button headerSubscribeButton;
|
private Button headerSubscribeButton;
|
||||||
|
|
||||||
|
private LinearLayout headerPlayAllButton;
|
||||||
|
private LinearLayout headerPopupButton;
|
||||||
|
private LinearLayout headerBackgroundButton;
|
||||||
|
|
||||||
private MenuItem menuRssButton;
|
private MenuItem menuRssButton;
|
||||||
|
|
||||||
public static ChannelFragment getInstance(int serviceId, String url, String name) {
|
public static ChannelFragment getInstance(int serviceId, String url, String name) {
|
||||||
|
@ -125,9 +141,52 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
||||||
headerSubscribersTextView = headerRootLayout.findViewById(R.id.channel_subscriber_view);
|
headerSubscribersTextView = headerRootLayout.findViewById(R.id.channel_subscriber_view);
|
||||||
headerSubscribeButton = headerRootLayout.findViewById(R.id.channel_subscribe_button);
|
headerSubscribeButton = headerRootLayout.findViewById(R.id.channel_subscribe_button);
|
||||||
|
|
||||||
|
headerPlayAllButton = headerRootLayout.findViewById(R.id.channel_play_all_button);
|
||||||
|
headerPopupButton = headerRootLayout.findViewById(R.id.channel_play_popup_button);
|
||||||
|
headerBackgroundButton = headerRootLayout.findViewById(R.id.channel_play_bg_button);
|
||||||
|
|
||||||
return headerRootLayout;
|
return headerRootLayout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void showStreamDialog(final StreamInfoItem item) {
|
||||||
|
final Context context = getContext();
|
||||||
|
final String[] commands = new String[]{
|
||||||
|
context.getResources().getString(R.string.enqueue_on_background),
|
||||||
|
context.getResources().getString(R.string.enqueue_on_popup),
|
||||||
|
context.getResources().getString(R.string.start_here_on_main),
|
||||||
|
context.getResources().getString(R.string.start_here_on_background),
|
||||||
|
context.getResources().getString(R.string.start_here_on_popup),
|
||||||
|
};
|
||||||
|
|
||||||
|
final DialogInterface.OnClickListener actions = new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialogInterface, int i) {
|
||||||
|
final int index = Math.max(infoListAdapter.getItemsList().indexOf(item), 0);
|
||||||
|
switch (i) {
|
||||||
|
case 0:
|
||||||
|
NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(item));
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
NavigationHelper.enqueueOnPopupPlayer(context, new SinglePlayQueue(item));
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
NavigationHelper.playOnMainPlayer(context, getPlayQueue(index));
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
NavigationHelper.playOnBackgroundPlayer(context, getPlayQueue(index));
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
NavigationHelper.playOnPopupPlayer(context, getPlayQueue(index));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
new InfoItemDialog(getActivity(), item, commands, actions).show();
|
||||||
|
}
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Menu
|
// Menu
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
@ -391,6 +450,46 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
||||||
if (subscribeButtonMonitor != null) subscribeButtonMonitor.dispose();
|
if (subscribeButtonMonitor != null) subscribeButtonMonitor.dispose();
|
||||||
updateSubscription(result);
|
updateSubscription(result);
|
||||||
monitorSubscription(result);
|
monitorSubscription(result);
|
||||||
|
|
||||||
|
headerPlayAllButton.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
NavigationHelper.playOnMainPlayer(activity, getPlayQueue());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
headerPopupButton.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !PermissionHelper.checkSystemAlertWindowPermission(activity)) {
|
||||||
|
Toast toast = Toast.makeText(activity, R.string.msg_popup_permission, Toast.LENGTH_LONG);
|
||||||
|
TextView messageView = toast.getView().findViewById(android.R.id.message);
|
||||||
|
if (messageView != null) messageView.setGravity(Gravity.CENTER);
|
||||||
|
toast.show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
headerBackgroundButton.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private PlayQueue getPlayQueue() {
|
||||||
|
return getPlayQueue(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PlayQueue getPlayQueue(final int index) {
|
||||||
|
return new ChannelPlayQueue(
|
||||||
|
currentInfo.service_id,
|
||||||
|
currentInfo.url,
|
||||||
|
currentInfo.next_streams_url,
|
||||||
|
infoListAdapter.getItemsList(),
|
||||||
|
index
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package org.schabi.newpipe.fragments.list.playlist;
|
package org.schabi.newpipe.fragments.list.playlist;
|
||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
|
@ -13,8 +14,8 @@ import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.Button;
|
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
@ -23,12 +24,12 @@ import org.schabi.newpipe.extractor.ListExtractor;
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
import org.schabi.newpipe.extractor.NewPipe;
|
||||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
|
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
|
||||||
|
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||||
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
|
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
|
||||||
import org.schabi.newpipe.player.BackgroundPlayer;
|
import org.schabi.newpipe.info_list.InfoItemDialog;
|
||||||
import org.schabi.newpipe.player.MainVideoPlayer;
|
|
||||||
import org.schabi.newpipe.player.PopupVideoPlayer;
|
|
||||||
import org.schabi.newpipe.playlist.ExternalPlayQueue;
|
|
||||||
import org.schabi.newpipe.playlist.PlayQueue;
|
import org.schabi.newpipe.playlist.PlayQueue;
|
||||||
|
import org.schabi.newpipe.playlist.PlaylistPlayQueue;
|
||||||
|
import org.schabi.newpipe.playlist.SinglePlayQueue;
|
||||||
import org.schabi.newpipe.report.UserAction;
|
import org.schabi.newpipe.report.UserAction;
|
||||||
import org.schabi.newpipe.util.ExtractorHelper;
|
import org.schabi.newpipe.util.ExtractorHelper;
|
||||||
import org.schabi.newpipe.util.NavigationHelper;
|
import org.schabi.newpipe.util.NavigationHelper;
|
||||||
|
@ -51,9 +52,9 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
||||||
private ImageView headerUploaderAvatar;
|
private ImageView headerUploaderAvatar;
|
||||||
private TextView headerStreamCount;
|
private TextView headerStreamCount;
|
||||||
|
|
||||||
private Button headerPlayAllButton;
|
private LinearLayout headerPlayAllButton;
|
||||||
private Button headerPopupButton;
|
private LinearLayout headerPopupButton;
|
||||||
private Button headerBackgroundButton;
|
private LinearLayout headerBackgroundButton;
|
||||||
|
|
||||||
public static PlaylistFragment getInstance(int serviceId, String url, String name) {
|
public static PlaylistFragment getInstance(int serviceId, String url, String name) {
|
||||||
PlaylistFragment instance = new PlaylistFragment();
|
PlaylistFragment instance = new PlaylistFragment();
|
||||||
|
@ -103,6 +104,45 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
||||||
inflater.inflate(R.menu.menu_playlist, menu);
|
inflater.inflate(R.menu.menu_playlist, menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void showStreamDialog(final StreamInfoItem item) {
|
||||||
|
final Context context = getContext();
|
||||||
|
final String[] commands = new String[]{
|
||||||
|
context.getResources().getString(R.string.enqueue_on_background),
|
||||||
|
context.getResources().getString(R.string.enqueue_on_popup),
|
||||||
|
context.getResources().getString(R.string.start_here_on_main),
|
||||||
|
context.getResources().getString(R.string.start_here_on_background),
|
||||||
|
context.getResources().getString(R.string.start_here_on_popup),
|
||||||
|
};
|
||||||
|
|
||||||
|
final DialogInterface.OnClickListener actions = new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialogInterface, int i) {
|
||||||
|
final int index = Math.max(infoListAdapter.getItemsList().indexOf(item), 0);
|
||||||
|
switch (i) {
|
||||||
|
case 0:
|
||||||
|
NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(item));
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
NavigationHelper.enqueueOnPopupPlayer(context, new SinglePlayQueue(item));
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
NavigationHelper.playOnMainPlayer(context, getPlayQueue(index));
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
NavigationHelper.playOnBackgroundPlayer(context, getPlayQueue(index));
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
NavigationHelper.playOnPopupPlayer(context, getPlayQueue(index));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
new InfoItemDialog(getActivity(), item, commands, actions).show();
|
||||||
|
}
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Load and handle
|
// Load and handle
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
@ -160,7 +200,7 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
||||||
headerPlayAllButton.setOnClickListener(new View.OnClickListener() {
|
headerPlayAllButton.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View view) {
|
public void onClick(View view) {
|
||||||
startActivity(buildPlaylistIntent(MainVideoPlayer.class));
|
NavigationHelper.playOnMainPlayer(activity, getPlayQueue());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
headerPopupButton.setOnClickListener(new View.OnClickListener() {
|
headerPopupButton.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@ -173,26 +213,29 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
||||||
toast.show();
|
toast.show();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
activity.startService(buildPlaylistIntent(PopupVideoPlayer.class));
|
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
headerBackgroundButton.setOnClickListener(new View.OnClickListener() {
|
headerBackgroundButton.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View view) {
|
public void onClick(View view) {
|
||||||
activity.startService(buildPlaylistIntent(BackgroundPlayer.class));
|
NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private Intent buildPlaylistIntent(final Class targetClazz) {
|
private PlayQueue getPlayQueue() {
|
||||||
final PlayQueue playQueue = new ExternalPlayQueue(
|
return getPlayQueue(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PlayQueue getPlayQueue(final int index) {
|
||||||
|
return new PlaylistPlayQueue(
|
||||||
currentInfo.service_id,
|
currentInfo.service_id,
|
||||||
currentInfo.url,
|
currentInfo.url,
|
||||||
currentInfo.next_streams_url,
|
currentInfo.next_streams_url,
|
||||||
infoListAdapter.getItemsList(),
|
infoListAdapter.getItemsList(),
|
||||||
0
|
index
|
||||||
);
|
);
|
||||||
return NavigationHelper.getPlayerIntent(activity, targetClazz, playQueue);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -19,8 +19,6 @@ import org.schabi.newpipe.fragments.BaseStateFragment;
|
||||||
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
||||||
import org.schabi.newpipe.info_list.InfoListAdapter;
|
import org.schabi.newpipe.info_list.InfoListAdapter;
|
||||||
import org.schabi.newpipe.report.UserAction;
|
import org.schabi.newpipe.report.UserAction;
|
||||||
import org.schabi.newpipe.util.KioskTranslator;
|
|
||||||
import org.schabi.newpipe.report.ErrorActivity;
|
|
||||||
import org.schabi.newpipe.util.NavigationHelper;
|
import org.schabi.newpipe.util.NavigationHelper;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -134,6 +132,9 @@ public class SubscriptionFragment extends BaseStateFragment<List<SubscriptionEnt
|
||||||
NavigationHelper.openChannelFragment(getParentFragment().getFragmentManager(), selectedItem.service_id, selectedItem.url, selectedItem.name);
|
NavigationHelper.openChannelFragment(getParentFragment().getFragmentManager(), selectedItem.service_id, selectedItem.url, selectedItem.name);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void held(ChannelInfoItem selectedItem) {}
|
||||||
});
|
});
|
||||||
|
|
||||||
headerRootLayout.setOnClickListener(new View.OnClickListener() {
|
headerRootLayout.setOnClickListener(new View.OnClickListener() {
|
||||||
|
|
|
@ -44,6 +44,7 @@ public class InfoItemBuilder {
|
||||||
|
|
||||||
public interface OnInfoItemSelectedListener<T extends InfoItem> {
|
public interface OnInfoItemSelectedListener<T extends InfoItem> {
|
||||||
void selected(T selectedItem);
|
void selected(T selectedItem);
|
||||||
|
void held(T selectedItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
package org.schabi.newpipe.info_list;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.R;
|
||||||
|
import org.schabi.newpipe.extractor.InfoItem;
|
||||||
|
|
||||||
|
public class InfoItemDialog {
|
||||||
|
private final AlertDialog dialog;
|
||||||
|
|
||||||
|
public InfoItemDialog(@NonNull final Activity activity,
|
||||||
|
@NonNull final InfoItem item,
|
||||||
|
@NonNull final String[] commands,
|
||||||
|
@NonNull final DialogInterface.OnClickListener actions) {
|
||||||
|
|
||||||
|
final LayoutInflater inflater = activity.getLayoutInflater();
|
||||||
|
final View bannerView = inflater.inflate(R.layout.dialog_title, null);
|
||||||
|
bannerView.setSelected(true);
|
||||||
|
TextView titleView = bannerView.findViewById(R.id.itemTitleView);
|
||||||
|
titleView.setText(item.name);
|
||||||
|
TextView typeView = bannerView.findViewById(R.id.itemTypeView);
|
||||||
|
typeView.setText(item.info_type.name());
|
||||||
|
|
||||||
|
dialog = new AlertDialog.Builder(activity)
|
||||||
|
.setCustomTitle(bannerView)
|
||||||
|
.setItems(commands, actions)
|
||||||
|
.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void show() {
|
||||||
|
dialog.show();
|
||||||
|
}
|
||||||
|
}
|
|
@ -67,6 +67,38 @@ public class StreamMiniInfoItemHolder extends InfoItemHolder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
switch (item.stream_type) {
|
||||||
|
case AUDIO_STREAM:
|
||||||
|
case VIDEO_STREAM:
|
||||||
|
case FILE:
|
||||||
|
enableLongClick(item);
|
||||||
|
break;
|
||||||
|
case LIVE_STREAM:
|
||||||
|
case AUDIO_LIVE_STREAM:
|
||||||
|
case NONE:
|
||||||
|
default:
|
||||||
|
disableLongClick();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void enableLongClick(final StreamInfoItem item) {
|
||||||
|
itemView.setLongClickable(true);
|
||||||
|
itemView.setOnLongClickListener(new View.OnLongClickListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onLongClick(View view) {
|
||||||
|
if (itemBuilder.getOnStreamSelectedListener() != null) {
|
||||||
|
itemBuilder.getOnStreamSelectedListener().held(item);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void disableLongClick() {
|
||||||
|
itemView.setLongClickable(false);
|
||||||
|
itemView.setOnLongClickListener(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -48,6 +48,7 @@ import org.schabi.newpipe.player.event.PlayerEventListener;
|
||||||
import org.schabi.newpipe.player.helper.LockManager;
|
import org.schabi.newpipe.player.helper.LockManager;
|
||||||
import org.schabi.newpipe.playlist.PlayQueueItem;
|
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||||
import org.schabi.newpipe.util.ListHelper;
|
import org.schabi.newpipe.util.ListHelper;
|
||||||
|
import org.schabi.newpipe.util.NavigationHelper;
|
||||||
import org.schabi.newpipe.util.ThemeHelper;
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
|
||||||
import static org.schabi.newpipe.player.helper.PlayerHelper.getTimeString;
|
import static org.schabi.newpipe.player.helper.PlayerHelper.getTimeString;
|
||||||
|
@ -68,6 +69,10 @@ public final class BackgroundPlayer extends Service {
|
||||||
public static final String ACTION_REPEAT = "org.schabi.newpipe.player.BackgroundPlayer.REPEAT";
|
public static final String ACTION_REPEAT = "org.schabi.newpipe.player.BackgroundPlayer.REPEAT";
|
||||||
public static final String ACTION_PLAY_NEXT = "org.schabi.newpipe.player.BackgroundPlayer.ACTION_PLAY_NEXT";
|
public static final String ACTION_PLAY_NEXT = "org.schabi.newpipe.player.BackgroundPlayer.ACTION_PLAY_NEXT";
|
||||||
public static final String ACTION_PLAY_PREVIOUS = "org.schabi.newpipe.player.BackgroundPlayer.ACTION_PLAY_PREVIOUS";
|
public static final String ACTION_PLAY_PREVIOUS = "org.schabi.newpipe.player.BackgroundPlayer.ACTION_PLAY_PREVIOUS";
|
||||||
|
public static final String ACTION_FAST_REWIND = "org.schabi.newpipe.player.BackgroundPlayer.ACTION_FAST_REWIND";
|
||||||
|
public static final String ACTION_FAST_FORWARD = "org.schabi.newpipe.player.BackgroundPlayer.ACTION_FAST_FORWARD";
|
||||||
|
|
||||||
|
public static final String SET_IMAGE_RESOURCE_METHOD = "setImageResource";
|
||||||
|
|
||||||
private BasePlayerImpl basePlayerImpl;
|
private BasePlayerImpl basePlayerImpl;
|
||||||
private LockManager lockManager;
|
private LockManager lockManager;
|
||||||
|
@ -130,16 +135,6 @@ public final class BackgroundPlayer extends Service {
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Actions
|
// Actions
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
public void openControl(final Context context) {
|
|
||||||
Intent intent = new Intent(context, BackgroundPlayerActivity.class);
|
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
|
|
||||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
||||||
}
|
|
||||||
context.startActivity(intent);
|
|
||||||
context.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onClose() {
|
private void onClose() {
|
||||||
if (DEBUG) Log.d(TAG, "onClose() called");
|
if (DEBUG) Log.d(TAG, "onClose() called");
|
||||||
|
|
||||||
|
@ -191,6 +186,8 @@ public final class BackgroundPlayer extends Service {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupNotification(RemoteViews remoteViews) {
|
private void setupNotification(RemoteViews remoteViews) {
|
||||||
|
if (basePlayerImpl == null) return;
|
||||||
|
|
||||||
remoteViews.setTextViewText(R.id.notificationSongName, basePlayerImpl.getVideoTitle());
|
remoteViews.setTextViewText(R.id.notificationSongName, basePlayerImpl.getVideoTitle());
|
||||||
remoteViews.setTextViewText(R.id.notificationArtist, basePlayerImpl.getUploaderName());
|
remoteViews.setTextViewText(R.id.notificationArtist, basePlayerImpl.getUploaderName());
|
||||||
|
|
||||||
|
@ -203,10 +200,21 @@ public final class BackgroundPlayer extends Service {
|
||||||
remoteViews.setOnClickPendingIntent(R.id.notificationRepeat,
|
remoteViews.setOnClickPendingIntent(R.id.notificationRepeat,
|
||||||
PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_REPEAT), PendingIntent.FLAG_UPDATE_CURRENT));
|
PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_REPEAT), PendingIntent.FLAG_UPDATE_CURRENT));
|
||||||
|
|
||||||
|
if (basePlayerImpl.playQueue != null && basePlayerImpl.playQueue.size() > 1) {
|
||||||
|
remoteViews.setInt(R.id.notificationFRewind, SET_IMAGE_RESOURCE_METHOD, R.drawable.exo_controls_previous);
|
||||||
|
remoteViews.setInt(R.id.notificationFForward, SET_IMAGE_RESOURCE_METHOD, R.drawable.exo_controls_next);
|
||||||
remoteViews.setOnClickPendingIntent(R.id.notificationFRewind,
|
remoteViews.setOnClickPendingIntent(R.id.notificationFRewind,
|
||||||
PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_PLAY_PREVIOUS), PendingIntent.FLAG_UPDATE_CURRENT));
|
PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_PLAY_PREVIOUS), PendingIntent.FLAG_UPDATE_CURRENT));
|
||||||
remoteViews.setOnClickPendingIntent(R.id.notificationFForward,
|
remoteViews.setOnClickPendingIntent(R.id.notificationFForward,
|
||||||
PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_PLAY_NEXT), PendingIntent.FLAG_UPDATE_CURRENT));
|
PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_PLAY_NEXT), PendingIntent.FLAG_UPDATE_CURRENT));
|
||||||
|
} else {
|
||||||
|
remoteViews.setInt(R.id.notificationFRewind, SET_IMAGE_RESOURCE_METHOD, R.drawable.exo_controls_rewind);
|
||||||
|
remoteViews.setInt(R.id.notificationFForward, SET_IMAGE_RESOURCE_METHOD, R.drawable.exo_controls_fastforward);
|
||||||
|
remoteViews.setOnClickPendingIntent(R.id.notificationFRewind,
|
||||||
|
PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_FAST_REWIND), PendingIntent.FLAG_UPDATE_CURRENT));
|
||||||
|
remoteViews.setOnClickPendingIntent(R.id.notificationFForward,
|
||||||
|
PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_FAST_FORWARD), PendingIntent.FLAG_UPDATE_CURRENT));
|
||||||
|
}
|
||||||
|
|
||||||
setRepeatModeIcon(remoteViews, basePlayerImpl.getRepeatMode());
|
setRepeatModeIcon(remoteViews, basePlayerImpl.getRepeatMode());
|
||||||
}
|
}
|
||||||
|
@ -241,17 +249,15 @@ public final class BackgroundPlayer extends Service {
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
private void setRepeatModeIcon(final RemoteViews remoteViews, final int repeatMode) {
|
private void setRepeatModeIcon(final RemoteViews remoteViews, final int repeatMode) {
|
||||||
final String methodName = "setImageResource";
|
|
||||||
|
|
||||||
switch (repeatMode) {
|
switch (repeatMode) {
|
||||||
case Player.REPEAT_MODE_OFF:
|
case Player.REPEAT_MODE_OFF:
|
||||||
remoteViews.setInt(R.id.notificationRepeat, methodName, R.drawable.exo_controls_repeat_off);
|
remoteViews.setInt(R.id.notificationRepeat, SET_IMAGE_RESOURCE_METHOD, R.drawable.exo_controls_repeat_off);
|
||||||
break;
|
break;
|
||||||
case Player.REPEAT_MODE_ONE:
|
case Player.REPEAT_MODE_ONE:
|
||||||
remoteViews.setInt(R.id.notificationRepeat, methodName, R.drawable.exo_controls_repeat_one);
|
remoteViews.setInt(R.id.notificationRepeat, SET_IMAGE_RESOURCE_METHOD, R.drawable.exo_controls_repeat_one);
|
||||||
break;
|
break;
|
||||||
case Player.REPEAT_MODE_ALL:
|
case Player.REPEAT_MODE_ALL:
|
||||||
remoteViews.setInt(R.id.notificationRepeat, methodName, R.drawable.exo_controls_repeat_all);
|
remoteViews.setInt(R.id.notificationRepeat, SET_IMAGE_RESOURCE_METHOD, R.drawable.exo_controls_repeat_all);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -372,6 +378,7 @@ public final class BackgroundPlayer extends Service {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sync(@NonNull final PlayQueueItem item, @Nullable final StreamInfo info) {
|
public void sync(@NonNull final PlayQueueItem item, @Nullable final StreamInfo info) {
|
||||||
|
if (currentItem == item && currentInfo == info) return;
|
||||||
super.sync(item, info);
|
super.sync(item, info);
|
||||||
|
|
||||||
resetNotification();
|
resetNotification();
|
||||||
|
@ -380,9 +387,10 @@ public final class BackgroundPlayer extends Service {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Nullable
|
||||||
public MediaSource sourceOf(final PlayQueueItem item, final StreamInfo info) {
|
public MediaSource sourceOf(final PlayQueueItem item, final StreamInfo info) {
|
||||||
final int index = ListHelper.getDefaultAudioFormat(context, info.audio_streams);
|
final int index = ListHelper.getDefaultAudioFormat(context, info.audio_streams);
|
||||||
if (index < 0) return null;
|
if (index < 0 || index >= info.audio_streams.size()) return null;
|
||||||
|
|
||||||
final AudioStream audio = info.audio_streams.get(index);
|
final AudioStream audio = info.audio_streams.get(index);
|
||||||
return buildMediaSource(audio.url, MediaFormat.getSuffixById(audio.format));
|
return buildMediaSource(audio.url, MediaFormat.getSuffixById(audio.format));
|
||||||
|
@ -449,6 +457,8 @@ public final class BackgroundPlayer extends Service {
|
||||||
intentFilter.addAction(ACTION_REPEAT);
|
intentFilter.addAction(ACTION_REPEAT);
|
||||||
intentFilter.addAction(ACTION_PLAY_PREVIOUS);
|
intentFilter.addAction(ACTION_PLAY_PREVIOUS);
|
||||||
intentFilter.addAction(ACTION_PLAY_NEXT);
|
intentFilter.addAction(ACTION_PLAY_NEXT);
|
||||||
|
intentFilter.addAction(ACTION_FAST_REWIND);
|
||||||
|
intentFilter.addAction(ACTION_FAST_FORWARD);
|
||||||
|
|
||||||
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
|
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
|
||||||
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
|
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
|
||||||
|
@ -469,7 +479,7 @@ public final class BackgroundPlayer extends Service {
|
||||||
onVideoPlayPause();
|
onVideoPlayPause();
|
||||||
break;
|
break;
|
||||||
case ACTION_OPEN_CONTROLS:
|
case ACTION_OPEN_CONTROLS:
|
||||||
openControl(getApplicationContext());
|
NavigationHelper.openBackgroundPlayerControl(getApplicationContext());
|
||||||
break;
|
break;
|
||||||
case ACTION_REPEAT:
|
case ACTION_REPEAT:
|
||||||
onRepeatClicked();
|
onRepeatClicked();
|
||||||
|
@ -480,6 +490,12 @@ public final class BackgroundPlayer extends Service {
|
||||||
case ACTION_PLAY_PREVIOUS:
|
case ACTION_PLAY_PREVIOUS:
|
||||||
onPlayPrevious();
|
onPlayPrevious();
|
||||||
break;
|
break;
|
||||||
|
case ACTION_FAST_FORWARD:
|
||||||
|
onFastForward();
|
||||||
|
break;
|
||||||
|
case ACTION_FAST_REWIND:
|
||||||
|
onFastRewind();
|
||||||
|
break;
|
||||||
case Intent.ACTION_SCREEN_ON:
|
case Intent.ACTION_SCREEN_ON:
|
||||||
onScreenOnOff(true);
|
onScreenOnOff(true);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -26,6 +26,7 @@ import android.content.IntentFilter;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.media.AudioManager;
|
import android.media.AudioManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
@ -76,7 +77,6 @@ import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import io.reactivex.Observable;
|
import io.reactivex.Observable;
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
import io.reactivex.annotations.NonNull;
|
|
||||||
import io.reactivex.disposables.Disposable;
|
import io.reactivex.disposables.Disposable;
|
||||||
import io.reactivex.functions.Consumer;
|
import io.reactivex.functions.Consumer;
|
||||||
import io.reactivex.functions.Predicate;
|
import io.reactivex.functions.Predicate;
|
||||||
|
@ -193,7 +193,7 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.filter(new Predicate<Long>() {
|
.filter(new Predicate<Long>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean test(@NonNull Long aLong) throws Exception {
|
public boolean test(Long aLong) throws Exception {
|
||||||
return isProgressLoopRunning();
|
return isProgressLoopRunning();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -235,7 +235,7 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
|
||||||
initPlayback(queue);
|
initPlayback(queue);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void initPlayback(@NonNull final PlayQueue queue) {
|
protected void initPlayback(final PlayQueue queue) {
|
||||||
playQueue = queue;
|
playQueue = queue;
|
||||||
playQueue.init();
|
playQueue.init();
|
||||||
playbackManager = new MediaSourceManager(this, playQueue);
|
playbackManager = new MediaSourceManager(this, playQueue);
|
||||||
|
@ -514,11 +514,10 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Player.STATE_READY: //3
|
case Player.STATE_READY: //3
|
||||||
recover();
|
|
||||||
|
|
||||||
if (!isPrepared) {
|
if (!isPrepared) {
|
||||||
isPrepared = true;
|
isPrepared = true;
|
||||||
onPrepared(playWhenReady);
|
onPrepared(playWhenReady);
|
||||||
|
recover();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (currentState == STATE_PAUSED_SEEK) break;
|
if (currentState == STATE_PAUSED_SEEK) break;
|
||||||
|
@ -591,12 +590,12 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
|
||||||
if (DEBUG) Log.d(TAG, "onPositionDiscontinuity() called with window index = [" + newWindowIndex + "]");
|
if (DEBUG) Log.d(TAG, "onPositionDiscontinuity() called with window index = [" + newWindowIndex + "]");
|
||||||
|
|
||||||
// If the user selects a new track, then the discontinuity occurs after the index is changed.
|
// If the user selects a new track, then the discontinuity occurs after the index is changed.
|
||||||
// Therefore, the only source that causes a discrepancy would be autoplay,
|
// Therefore, the only source that causes a discrepancy would be gapless transition,
|
||||||
// which can only offset the current track by +1.
|
// which can only offset the current track by +1.
|
||||||
if (newWindowIndex != playQueue.getIndex() && playbackManager != null) {
|
if (newWindowIndex == playQueue.getIndex() + 1) {
|
||||||
playQueue.offsetIndex(+1);
|
playQueue.offsetIndex(+1);
|
||||||
playbackManager.load();
|
|
||||||
}
|
}
|
||||||
|
playbackManager.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -613,6 +612,8 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
|
||||||
if (simpleExoPlayer == null) return;
|
if (simpleExoPlayer == null) return;
|
||||||
if (DEBUG) Log.d(TAG, "Blocking...");
|
if (DEBUG) Log.d(TAG, "Blocking...");
|
||||||
|
|
||||||
|
currentItem = null;
|
||||||
|
currentInfo = null;
|
||||||
simpleExoPlayer.stop();
|
simpleExoPlayer.stop();
|
||||||
isPrepared = false;
|
isPrepared = false;
|
||||||
|
|
||||||
|
@ -631,17 +632,21 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sync(@android.support.annotation.NonNull final PlayQueueItem item,
|
public void sync(@NonNull final PlayQueueItem item,
|
||||||
@Nullable final StreamInfo info) {
|
@Nullable final StreamInfo info) {
|
||||||
if (simpleExoPlayer == null) return;
|
if (currentItem == item && currentInfo == info) return;
|
||||||
if (DEBUG) Log.d(TAG, "Syncing...");
|
|
||||||
|
|
||||||
currentItem = item;
|
currentItem = item;
|
||||||
currentInfo = info;
|
currentInfo = info;
|
||||||
|
|
||||||
|
if (DEBUG) Log.d(TAG, "Syncing...");
|
||||||
|
if (simpleExoPlayer == null) return;
|
||||||
|
|
||||||
// Check if on wrong window
|
// Check if on wrong window
|
||||||
final int currentSourceIndex = playQueue.getIndex();
|
final int currentSourceIndex = playQueue.indexOf(item);
|
||||||
if (simpleExoPlayer.getCurrentWindowIndex() != currentSourceIndex) {
|
if (currentSourceIndex != playQueue.getIndex()) {
|
||||||
|
Log.e(TAG, "Play Queue may be desynchronized: item index=[" + currentSourceIndex +
|
||||||
|
"], queue index=[" + playQueue.getIndex() + "]");
|
||||||
|
} else if (simpleExoPlayer.getCurrentWindowIndex() != currentSourceIndex || !isPlaying()) {
|
||||||
final long startPos = info != null ? info.start_position : 0;
|
final long startPos = info != null ? info.start_position : 0;
|
||||||
if (DEBUG) Log.d(TAG, "Rewinding to correct window: " + currentSourceIndex + " at: " + getTimeString((int)startPos));
|
if (DEBUG) Log.d(TAG, "Rewinding to correct window: " + currentSourceIndex + " at: " + getTimeString((int)startPos));
|
||||||
simpleExoPlayer.seekTo(currentSourceIndex, startPos);
|
simpleExoPlayer.seekTo(currentSourceIndex, startPos);
|
||||||
|
@ -756,10 +761,6 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
|
||||||
} else {
|
} else {
|
||||||
playQueue.setIndex(index);
|
playQueue.setIndex(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isPlaying()) {
|
|
||||||
onVideoPlayPause();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void seekBy(int milliSeconds) {
|
public void seekBy(int milliSeconds) {
|
||||||
|
@ -826,15 +827,15 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getVideoUrl() {
|
public String getVideoUrl() {
|
||||||
return currentItem == null ? null : currentItem.getUrl();
|
return currentItem == null ? context.getString(R.string.unknown_content) : currentItem.getUrl();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getVideoTitle() {
|
public String getVideoTitle() {
|
||||||
return currentItem == null ? null : currentItem.getTitle();
|
return currentItem == null ? context.getString(R.string.unknown_content) : currentItem.getTitle();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getUploaderName() {
|
public String getUploaderName() {
|
||||||
return currentItem == null ? null : currentItem.getUploader();
|
return currentItem == null ? context.getString(R.string.unknown_content) : currentItem.getUploader();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isCompleted() {
|
public boolean isCompleted() {
|
||||||
|
@ -870,8 +871,10 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
|
||||||
}
|
}
|
||||||
|
|
||||||
public PlaybackParameters getPlaybackParameters() {
|
public PlaybackParameters getPlaybackParameters() {
|
||||||
|
final PlaybackParameters defaultParameters = new PlaybackParameters(1f, 1f);
|
||||||
|
if (simpleExoPlayer == null) return defaultParameters;
|
||||||
final PlaybackParameters parameters = simpleExoPlayer.getPlaybackParameters();
|
final PlaybackParameters parameters = simpleExoPlayer.getPlaybackParameters();
|
||||||
return parameters == null ? new PlaybackParameters(1f, 1f) : parameters;
|
return parameters == null ? defaultParameters : parameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPlaybackParameters(float speed, float pitch) {
|
public void setPlaybackParameters(float speed, float pitch) {
|
||||||
|
@ -900,8 +903,10 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
|
||||||
final int queuePos = playQueue.getIndex();
|
final int queuePos = playQueue.getIndex();
|
||||||
final long windowPos = simpleExoPlayer.getCurrentPosition();
|
final long windowPos = simpleExoPlayer.getCurrentPosition();
|
||||||
|
|
||||||
|
if (windowPos > 0 && windowPos <= simpleExoPlayer.getDuration()) {
|
||||||
setRecovery(queuePos, windowPos);
|
setRecovery(queuePos, windowPos);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void setRecovery(final int queuePos, final long windowPos) {
|
public void setRecovery(final int queuePos, final long windowPos) {
|
||||||
if (playQueue.size() <= queuePos) return;
|
if (playQueue.size() <= queuePos) return;
|
||||||
|
|
|
@ -48,6 +48,7 @@ import com.google.android.exoplayer2.Player;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||||
import org.schabi.newpipe.extractor.stream.VideoStream;
|
import org.schabi.newpipe.extractor.stream.VideoStream;
|
||||||
|
import org.schabi.newpipe.fragments.OnScrollBelowItemsListener;
|
||||||
import org.schabi.newpipe.player.helper.PlayerHelper;
|
import org.schabi.newpipe.player.helper.PlayerHelper;
|
||||||
import org.schabi.newpipe.playlist.PlayQueueItem;
|
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||||
import org.schabi.newpipe.playlist.PlayQueueItemBuilder;
|
import org.schabi.newpipe.playlist.PlayQueueItemBuilder;
|
||||||
|
@ -397,7 +398,7 @@ public final class MainVideoPlayer extends Activity {
|
||||||
getControlsRoot().setVisibility(View.INVISIBLE);
|
getControlsRoot().setVisibility(View.INVISIBLE);
|
||||||
queueLayout.setVisibility(View.VISIBLE);
|
queueLayout.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
itemsList.smoothScrollToPosition(playQueue.getIndex());
|
itemsList.scrollToPosition(playQueue.getIndex());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onQueueClosed() {
|
private void onQueueClosed() {
|
||||||
|
@ -565,6 +566,9 @@ public final class MainVideoPlayer extends Activity {
|
||||||
itemsList.setClickable(true);
|
itemsList.setClickable(true);
|
||||||
itemsList.setLongClickable(true);
|
itemsList.setLongClickable(true);
|
||||||
|
|
||||||
|
itemsList.clearOnScrollListeners();
|
||||||
|
itemsList.addOnScrollListener(getQueueScrollListener());
|
||||||
|
|
||||||
itemTouchHelper = new ItemTouchHelper(getItemTouchCallback());
|
itemTouchHelper = new ItemTouchHelper(getItemTouchCallback());
|
||||||
itemTouchHelper.attachToRecyclerView(itemsList);
|
itemTouchHelper.attachToRecyclerView(itemsList);
|
||||||
|
|
||||||
|
@ -578,6 +582,19 @@ public final class MainVideoPlayer extends Activity {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private OnScrollBelowItemsListener getQueueScrollListener() {
|
||||||
|
return new OnScrollBelowItemsListener() {
|
||||||
|
@Override
|
||||||
|
public void onScrolledDown(RecyclerView recyclerView) {
|
||||||
|
if (playQueue != null && !playQueue.isComplete()) {
|
||||||
|
playQueue.fetch();
|
||||||
|
} else if (itemsList != null) {
|
||||||
|
itemsList.clearOnScrollListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private ItemTouchHelper.SimpleCallback getItemTouchCallback() {
|
private ItemTouchHelper.SimpleCallback getItemTouchCallback() {
|
||||||
return new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN, 0) {
|
return new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN, 0) {
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -65,6 +65,7 @@ import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamExtractor;
|
||||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||||
import org.schabi.newpipe.extractor.stream.VideoStream;
|
import org.schabi.newpipe.extractor.stream.VideoStream;
|
||||||
import org.schabi.newpipe.player.event.PlayerEventListener;
|
import org.schabi.newpipe.player.event.PlayerEventListener;
|
||||||
|
import org.schabi.newpipe.player.helper.PlayerHelper;
|
||||||
import org.schabi.newpipe.player.old.PlayVideoActivity;
|
import org.schabi.newpipe.player.old.PlayVideoActivity;
|
||||||
import org.schabi.newpipe.player.helper.LockManager;
|
import org.schabi.newpipe.player.helper.LockManager;
|
||||||
import org.schabi.newpipe.playlist.PlayQueueItem;
|
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||||
|
@ -96,7 +97,6 @@ import static org.schabi.newpipe.util.AnimationUtils.animateView;
|
||||||
public final class PopupVideoPlayer extends Service {
|
public final class PopupVideoPlayer extends Service {
|
||||||
private static final String TAG = ".PopupVideoPlayer";
|
private static final String TAG = ".PopupVideoPlayer";
|
||||||
private static final boolean DEBUG = BasePlayer.DEBUG;
|
private static final boolean DEBUG = BasePlayer.DEBUG;
|
||||||
private static final int SHUTDOWN_FLING_VELOCITY = 10000;
|
|
||||||
|
|
||||||
private static final int NOTIFICATION_ID = 40028922;
|
private static final int NOTIFICATION_ID = 40028922;
|
||||||
public static final String ACTION_CLOSE = "org.schabi.newpipe.player.PopupVideoPlayer.CLOSE";
|
public static final String ACTION_CLOSE = "org.schabi.newpipe.player.PopupVideoPlayer.CLOSE";
|
||||||
|
@ -112,6 +112,9 @@ public final class PopupVideoPlayer extends Service {
|
||||||
private WindowManager.LayoutParams windowLayoutParams;
|
private WindowManager.LayoutParams windowLayoutParams;
|
||||||
private GestureDetector gestureDetector;
|
private GestureDetector gestureDetector;
|
||||||
|
|
||||||
|
private int shutdownFlingVelocity;
|
||||||
|
private int tossFlingVelocity;
|
||||||
|
|
||||||
private float screenWidth, screenHeight;
|
private float screenWidth, screenHeight;
|
||||||
private float popupWidth, popupHeight;
|
private float popupWidth, popupHeight;
|
||||||
|
|
||||||
|
@ -211,12 +214,14 @@ public final class PopupVideoPlayer extends Service {
|
||||||
View rootView = View.inflate(this, R.layout.player_popup, null);
|
View rootView = View.inflate(this, R.layout.player_popup, null);
|
||||||
playerImpl.setup(rootView);
|
playerImpl.setup(rootView);
|
||||||
|
|
||||||
|
shutdownFlingVelocity = PlayerHelper.getShutdownFlingVelocity(this);
|
||||||
|
tossFlingVelocity = PlayerHelper.getTossFlingVelocity(this);
|
||||||
|
|
||||||
updateScreenSize();
|
updateScreenSize();
|
||||||
|
|
||||||
|
final boolean popupRememberSizeAndPos = PlayerHelper.isRememberingPopupDimensions(this);
|
||||||
|
final float defaultSize = getResources().getDimension(R.dimen.popup_default_width);
|
||||||
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
|
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||||
boolean popupRememberSizeAndPos = sharedPreferences.getBoolean(getString(R.string.popup_remember_size_pos_key), true);
|
|
||||||
|
|
||||||
float defaultSize = getResources().getDimension(R.dimen.popup_default_width);
|
|
||||||
popupWidth = popupRememberSizeAndPos ? sharedPreferences.getFloat(POPUP_SAVED_WIDTH, defaultSize) : defaultSize;
|
popupWidth = popupRememberSizeAndPos ? sharedPreferences.getFloat(POPUP_SAVED_WIDTH, defaultSize) : defaultSize;
|
||||||
|
|
||||||
final int layoutParamType = Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.O ? WindowManager.LayoutParams.TYPE_PHONE : WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
|
final int layoutParamType = Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.O ? WindowManager.LayoutParams.TYPE_PHONE : WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
|
||||||
|
@ -313,15 +318,6 @@ public final class PopupVideoPlayer extends Service {
|
||||||
stopSelf();
|
stopSelf();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void openControl(final Context context) {
|
|
||||||
Intent intent = new Intent(context, PopupVideoPlayerActivity.class);
|
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
|
|
||||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
||||||
}
|
|
||||||
context.startActivity(intent);
|
|
||||||
context.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Utils
|
// Utils
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
@ -366,6 +362,7 @@ public final class PopupVideoPlayer extends Service {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updatePopupSize(int width, int height) {
|
private void updatePopupSize(int width, int height) {
|
||||||
|
if (playerImpl == null) return;
|
||||||
if (DEBUG) Log.d(TAG, "updatePopupSize() called with: width = [" + width + "], height = [" + height + "]");
|
if (DEBUG) Log.d(TAG, "updatePopupSize() called with: width = [" + width + "], height = [" + height + "]");
|
||||||
|
|
||||||
width = (int) (width > maximumWidth ? maximumWidth : width < minimumWidth ? minimumWidth : width);
|
width = (int) (width > maximumWidth ? maximumWidth : width < minimumWidth ? minimumWidth : width);
|
||||||
|
@ -577,6 +574,7 @@ public final class PopupVideoPlayer extends Service {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sync(@NonNull PlayQueueItem item, @Nullable StreamInfo info) {
|
public void sync(@NonNull PlayQueueItem item, @Nullable StreamInfo info) {
|
||||||
|
if (currentItem == item && currentInfo == info) return;
|
||||||
super.sync(item, info);
|
super.sync(item, info);
|
||||||
updateMetadata();
|
updateMetadata();
|
||||||
}
|
}
|
||||||
|
@ -617,7 +615,7 @@ public final class PopupVideoPlayer extends Service {
|
||||||
onVideoPlayPause();
|
onVideoPlayPause();
|
||||||
break;
|
break;
|
||||||
case ACTION_OPEN_CONTROLS:
|
case ACTION_OPEN_CONTROLS:
|
||||||
openControl(getApplicationContext());
|
NavigationHelper.openPopupPlayerControl(getApplicationContext());
|
||||||
break;
|
break;
|
||||||
case ACTION_REPEAT:
|
case ACTION_REPEAT:
|
||||||
onRepeatClicked();
|
onRepeatClicked();
|
||||||
|
@ -791,11 +789,20 @@ public final class PopupVideoPlayer extends Service {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
|
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
|
||||||
|
if (DEBUG) Log.d(TAG, "Fling velocity: dX=[" + velocityX + "], dY=[" + velocityY + "]");
|
||||||
if (playerImpl == null) return false;
|
if (playerImpl == null) return false;
|
||||||
if (Math.abs(velocityX) > SHUTDOWN_FLING_VELOCITY) {
|
|
||||||
if (DEBUG) Log.d(TAG, "Popup close fling velocity= " + velocityX);
|
final float absVelocityX = Math.abs(velocityX);
|
||||||
|
final float absVelocityY = Math.abs(velocityY);
|
||||||
|
if (absVelocityX > shutdownFlingVelocity) {
|
||||||
onClose();
|
onClose();
|
||||||
return true;
|
return true;
|
||||||
|
} else if (Math.max(absVelocityX, absVelocityY) > tossFlingVelocity) {
|
||||||
|
if (absVelocityX > tossFlingVelocity) windowLayoutParams.x = (int) velocityX;
|
||||||
|
if (absVelocityY > tossFlingVelocity) windowLayoutParams.y = (int) velocityY;
|
||||||
|
checkPositionBounds();
|
||||||
|
windowManager.updateViewLayout(playerImpl.getRootView(), windowLayoutParams);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ import com.google.android.exoplayer2.Player;
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||||
|
import org.schabi.newpipe.fragments.OnScrollBelowItemsListener;
|
||||||
import org.schabi.newpipe.player.event.PlayerEventListener;
|
import org.schabi.newpipe.player.event.PlayerEventListener;
|
||||||
import org.schabi.newpipe.playlist.PlayQueueItem;
|
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||||
import org.schabi.newpipe.playlist.PlayQueueItemBuilder;
|
import org.schabi.newpipe.playlist.PlayQueueItemBuilder;
|
||||||
|
@ -57,6 +58,8 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
||||||
private static final int PLAYBACK_SPEED_POPUP_MENU_GROUP_ID = 61;
|
private static final int PLAYBACK_SPEED_POPUP_MENU_GROUP_ID = 61;
|
||||||
private static final int PLAYBACK_PITCH_POPUP_MENU_GROUP_ID = 97;
|
private static final int PLAYBACK_PITCH_POPUP_MENU_GROUP_ID = 97;
|
||||||
|
|
||||||
|
private static final int SMOOTH_SCROLL_MAXIMUM_DISTANCE = 80;
|
||||||
|
|
||||||
private View rootView;
|
private View rootView;
|
||||||
|
|
||||||
private RecyclerView itemsList;
|
private RecyclerView itemsList;
|
||||||
|
@ -225,6 +228,8 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
||||||
itemsList.setAdapter(player.getPlayQueueAdapter());
|
itemsList.setAdapter(player.getPlayQueueAdapter());
|
||||||
itemsList.setClickable(true);
|
itemsList.setClickable(true);
|
||||||
itemsList.setLongClickable(true);
|
itemsList.setLongClickable(true);
|
||||||
|
itemsList.clearOnScrollListeners();
|
||||||
|
itemsList.addOnScrollListener(getQueueScrollListener());
|
||||||
|
|
||||||
itemTouchHelper = new ItemTouchHelper(getItemTouchCallback());
|
itemTouchHelper = new ItemTouchHelper(getItemTouchCallback());
|
||||||
itemTouchHelper.attachToRecyclerView(itemsList);
|
itemTouchHelper.attachToRecyclerView(itemsList);
|
||||||
|
@ -286,6 +291,8 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
||||||
item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
|
item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public boolean onMenuItemClick(MenuItem menuItem) {
|
public boolean onMenuItemClick(MenuItem menuItem) {
|
||||||
|
if (player == null) return false;
|
||||||
|
|
||||||
player.setPlaybackSpeed(playbackSpeed);
|
player.setPlaybackSpeed(playbackSpeed);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -304,6 +311,8 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
||||||
item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
|
item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public boolean onMenuItemClick(MenuItem menuItem) {
|
public boolean onMenuItemClick(MenuItem menuItem) {
|
||||||
|
if (player == null) return false;
|
||||||
|
|
||||||
player.setPlaybackPitch(playbackPitch);
|
player.setPlaybackPitch(playbackPitch);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -317,6 +326,8 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
||||||
remove.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
|
remove.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public boolean onMenuItemClick(MenuItem menuItem) {
|
public boolean onMenuItemClick(MenuItem menuItem) {
|
||||||
|
if (player == null) return false;
|
||||||
|
|
||||||
final int index = player.getPlayQueue().indexOf(item);
|
final int index = player.getPlayQueue().indexOf(item);
|
||||||
if (index != -1) player.getPlayQueue().remove(index);
|
if (index != -1) player.getPlayQueue().remove(index);
|
||||||
return true;
|
return true;
|
||||||
|
@ -339,6 +350,19 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
||||||
// Component Helpers
|
// Component Helpers
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
private OnScrollBelowItemsListener getQueueScrollListener() {
|
||||||
|
return new OnScrollBelowItemsListener() {
|
||||||
|
@Override
|
||||||
|
public void onScrolledDown(RecyclerView recyclerView) {
|
||||||
|
if (player != null && player.getPlayQueue() != null && !player.getPlayQueue().isComplete()) {
|
||||||
|
player.getPlayQueue().fetch();
|
||||||
|
} else if (itemsList != null) {
|
||||||
|
itemsList.clearOnScrollListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private ItemTouchHelper.SimpleCallback getItemTouchCallback() {
|
private ItemTouchHelper.SimpleCallback getItemTouchCallback() {
|
||||||
return new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN, 0) {
|
return new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN, 0) {
|
||||||
@Override
|
@Override
|
||||||
|
@ -349,7 +373,7 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
||||||
|
|
||||||
final int sourceIndex = source.getLayoutPosition();
|
final int sourceIndex = source.getLayoutPosition();
|
||||||
final int targetIndex = target.getLayoutPosition();
|
final int targetIndex = target.getLayoutPosition();
|
||||||
player.getPlayQueue().move(sourceIndex, targetIndex);
|
if (player != null) player.getPlayQueue().move(sourceIndex, targetIndex);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -372,11 +396,13 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
||||||
return new PlayQueueItemBuilder.OnSelectedListener() {
|
return new PlayQueueItemBuilder.OnSelectedListener() {
|
||||||
@Override
|
@Override
|
||||||
public void selected(PlayQueueItem item, View view) {
|
public void selected(PlayQueueItem item, View view) {
|
||||||
player.onSelected(item);
|
if (player != null) player.onSelected(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void held(PlayQueueItem item, View view) {
|
public void held(PlayQueueItem item, View view) {
|
||||||
|
if (player == null) return;
|
||||||
|
|
||||||
final int index = player.getPlayQueue().indexOf(item);
|
final int index = player.getPlayQueue().indexOf(item);
|
||||||
if (index != -1) buildItemPopupMenu(item, view);
|
if (index != -1) buildItemPopupMenu(item, view);
|
||||||
}
|
}
|
||||||
|
@ -393,7 +419,23 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
||||||
}
|
}
|
||||||
|
|
||||||
private void scrollToSelected() {
|
private void scrollToSelected() {
|
||||||
itemsList.smoothScrollToPosition(player.getPlayQueue().getIndex());
|
if (player == null) return;
|
||||||
|
|
||||||
|
final int currentPlayingIndex = player.getPlayQueue().getIndex();
|
||||||
|
final int currentVisibleIndex;
|
||||||
|
if (itemsList.getLayoutManager() instanceof LinearLayoutManager) {
|
||||||
|
final LinearLayoutManager layout = ((LinearLayoutManager) itemsList.getLayoutManager());
|
||||||
|
currentVisibleIndex = layout.findFirstVisibleItemPosition();
|
||||||
|
} else {
|
||||||
|
currentVisibleIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int distance = Math.abs(currentPlayingIndex - currentVisibleIndex);
|
||||||
|
if (distance < SMOOTH_SCROLL_MAXIMUM_DISTANCE) {
|
||||||
|
itemsList.smoothScrollToPosition(currentPlayingIndex);
|
||||||
|
} else {
|
||||||
|
itemsList.scrollToPosition(currentPlayingIndex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -402,6 +444,8 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View view) {
|
public void onClick(View view) {
|
||||||
|
if (player == null) return;
|
||||||
|
|
||||||
if (view.getId() == repeatButton.getId()) {
|
if (view.getId() == repeatButton.getId()) {
|
||||||
player.onRepeatClicked();
|
player.onRepeatClicked();
|
||||||
|
|
||||||
|
@ -450,7 +494,7 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||||
player.simpleExoPlayer.seekTo(seekBar.getProgress());
|
if (player != null) player.simpleExoPlayer.seekTo(seekBar.getProgress());
|
||||||
seekDisplay.setVisibility(View.GONE);
|
seekDisplay.setVisibility(View.GONE);
|
||||||
seeking = false;
|
seeking = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -272,17 +272,18 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Nullable
|
||||||
public MediaSource sourceOf(final PlayQueueItem item, final StreamInfo info) {
|
public MediaSource sourceOf(final PlayQueueItem item, final StreamInfo info) {
|
||||||
final List<VideoStream> videos = ListHelper.getSortedStreamVideosList(context, info.video_streams, info.video_only_streams, false);
|
final List<VideoStream> videos = ListHelper.getSortedStreamVideosList(context, info.video_streams, info.video_only_streams, false);
|
||||||
|
|
||||||
final VideoStream video;
|
final int index;
|
||||||
if (playbackQuality == null) {
|
if (playbackQuality == null) {
|
||||||
final int index = getDefaultResolutionIndex(videos);
|
index = getDefaultResolutionIndex(videos);
|
||||||
video = videos.get(index);
|
|
||||||
} else {
|
} else {
|
||||||
final int index = getOverrideResolutionIndex(videos, getPlaybackQuality());
|
index = getOverrideResolutionIndex(videos, getPlaybackQuality());
|
||||||
video = videos.get(index);
|
|
||||||
}
|
}
|
||||||
|
if (index < 0 || index >= videos.size()) return null;
|
||||||
|
final VideoStream video = videos.get(index);
|
||||||
|
|
||||||
final MediaSource streamSource = buildMediaSource(video.url, MediaFormat.getSuffixById(video.format));
|
final MediaSource streamSource = buildMediaSource(video.url, MediaFormat.getSuffixById(video.format));
|
||||||
final AudioStream audio = ListHelper.getHighestQualityAudio(info.audio_streams);
|
final AudioStream audio = ListHelper.getHighestQualityAudio(info.audio_streams);
|
||||||
|
|
|
@ -12,6 +12,8 @@ import java.text.NumberFormat;
|
||||||
import java.util.Formatter;
|
import java.util.Formatter;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
public class PlayerHelper {
|
public class PlayerHelper {
|
||||||
private PlayerHelper() {}
|
private PlayerHelper() {}
|
||||||
|
|
||||||
|
@ -56,6 +58,10 @@ public class PlayerHelper {
|
||||||
return isUsingOldPlayer(context, false);
|
return isUsingOldPlayer(context, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isRememberingPopupDimensions(@Nonnull final Context context) {
|
||||||
|
return isRememberingPopupDimensions(context, true);
|
||||||
|
}
|
||||||
|
|
||||||
public static long getPreferredCacheSize(@NonNull final Context context) {
|
public static long getPreferredCacheSize(@NonNull final Context context) {
|
||||||
return 64 * 1024 * 1024L;
|
return 64 * 1024 * 1024L;
|
||||||
}
|
}
|
||||||
|
@ -83,6 +89,15 @@ public class PlayerHelper {
|
||||||
public static boolean isUsingDSP(@NonNull final Context context) {
|
public static boolean isUsingDSP(@NonNull final Context context) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int getShutdownFlingVelocity(@Nonnull final Context context) {
|
||||||
|
return 10000;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getTossFlingVelocity(@Nonnull final Context context) {
|
||||||
|
return 2500;
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
// Private helpers
|
// Private helpers
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -103,4 +118,8 @@ public class PlayerHelper {
|
||||||
private static boolean isUsingOldPlayer(@NonNull final Context context, final boolean b) {
|
private static boolean isUsingOldPlayer(@NonNull final Context context, final boolean b) {
|
||||||
return getPreferences(context).getBoolean(context.getString(R.string.use_old_player_key), b);
|
return getPreferences(context).getBoolean(context.getString(R.string.use_old_player_key), b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean isRememberingPopupDimensions(@Nonnull final Context context, final boolean b) {
|
||||||
|
return getPreferences(context).getBoolean(context.getString(R.string.popup_remember_size_pos_key), b);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ import io.reactivex.subjects.PublishSubject;
|
||||||
public class MediaSourceManager {
|
public class MediaSourceManager {
|
||||||
private final String TAG = "MediaSourceManager@" + Integer.toHexString(hashCode());
|
private final String TAG = "MediaSourceManager@" + Integer.toHexString(hashCode());
|
||||||
// One-side rolling window size for default loading
|
// One-side rolling window size for default loading
|
||||||
// Effectively loads windowSize * 2 + 1 streams, must be greater than 0
|
// Effectively loads windowSize * 2 + 1 streams per call to load, must be greater than 0
|
||||||
private final int windowSize;
|
private final int windowSize;
|
||||||
private final PlaybackListener playbackListener;
|
private final PlaybackListener playbackListener;
|
||||||
private final PlayQueue playQueue;
|
private final PlayQueue playQueue;
|
||||||
|
@ -38,7 +38,7 @@ public class MediaSourceManager {
|
||||||
// The higher it is, the less loading occurs during rapid noncritical timeline changes
|
// The higher it is, the less loading occurs during rapid noncritical timeline changes
|
||||||
// Not recommended to go below 100ms
|
// Not recommended to go below 100ms
|
||||||
private final long loadDebounceMillis;
|
private final long loadDebounceMillis;
|
||||||
private final PublishSubject<Long> loadSignal;
|
private final PublishSubject<Long> debouncedLoadSignal;
|
||||||
private final Disposable debouncedLoader;
|
private final Disposable debouncedLoader;
|
||||||
|
|
||||||
private final DeferredMediaSource.Callback sourceBuilder;
|
private final DeferredMediaSource.Callback sourceBuilder;
|
||||||
|
@ -52,7 +52,7 @@ public class MediaSourceManager {
|
||||||
|
|
||||||
public MediaSourceManager(@NonNull final PlaybackListener listener,
|
public MediaSourceManager(@NonNull final PlaybackListener listener,
|
||||||
@NonNull final PlayQueue playQueue) {
|
@NonNull final PlayQueue playQueue) {
|
||||||
this(listener, playQueue, 1, 1000L);
|
this(listener, playQueue, 1, 400L);
|
||||||
}
|
}
|
||||||
|
|
||||||
private MediaSourceManager(@NonNull final PlaybackListener listener,
|
private MediaSourceManager(@NonNull final PlaybackListener listener,
|
||||||
|
@ -69,7 +69,7 @@ public class MediaSourceManager {
|
||||||
this.loadDebounceMillis = loadDebounceMillis;
|
this.loadDebounceMillis = loadDebounceMillis;
|
||||||
|
|
||||||
this.syncReactor = new SerialDisposable();
|
this.syncReactor = new SerialDisposable();
|
||||||
this.loadSignal = PublishSubject.create();
|
this.debouncedLoadSignal = PublishSubject.create();
|
||||||
this.debouncedLoader = getDebouncedLoader();
|
this.debouncedLoader = getDebouncedLoader();
|
||||||
|
|
||||||
this.sourceBuilder = getSourceBuilder();
|
this.sourceBuilder = getSourceBuilder();
|
||||||
|
@ -101,7 +101,7 @@ public class MediaSourceManager {
|
||||||
* Dispose the manager and releases all message buses and loaders.
|
* Dispose the manager and releases all message buses and loaders.
|
||||||
* */
|
* */
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
if (loadSignal != null) loadSignal.onComplete();
|
if (debouncedLoadSignal != null) debouncedLoadSignal.onComplete();
|
||||||
if (debouncedLoader != null) debouncedLoader.dispose();
|
if (debouncedLoader != null) debouncedLoader.dispose();
|
||||||
if (playQueueReactor != null) playQueueReactor.cancel();
|
if (playQueueReactor != null) playQueueReactor.cancel();
|
||||||
if (syncReactor != null) syncReactor.dispose();
|
if (syncReactor != null) syncReactor.dispose();
|
||||||
|
@ -118,7 +118,7 @@ public class MediaSourceManager {
|
||||||
* Unblocks the player once the item at the current index is loaded.
|
* Unblocks the player once the item at the current index is loaded.
|
||||||
* */
|
* */
|
||||||
public void load() {
|
public void load() {
|
||||||
loadSignal.onNext(System.currentTimeMillis());
|
loadDebounced();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -157,12 +157,12 @@ public class MediaSourceManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onPlayQueueChanged(final PlayQueueEvent event) {
|
private void onPlayQueueChanged(final PlayQueueEvent event) {
|
||||||
if (playQueue.isEmpty()) {
|
if (playQueue.isEmpty() && playQueue.isComplete()) {
|
||||||
playbackListener.shutdown();
|
playbackListener.shutdown();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// why no pattern matching in Java =(
|
// Event specific action
|
||||||
switch (event.type()) {
|
switch (event.type()) {
|
||||||
case INIT:
|
case INIT:
|
||||||
case REORDER:
|
case REORDER:
|
||||||
|
@ -172,37 +172,34 @@ public class MediaSourceManager {
|
||||||
case APPEND:
|
case APPEND:
|
||||||
populateSources();
|
populateSources();
|
||||||
break;
|
break;
|
||||||
case SELECT:
|
|
||||||
sync();
|
|
||||||
break;
|
|
||||||
case REMOVE:
|
case REMOVE:
|
||||||
final RemoveEvent removeEvent = (RemoveEvent) event;
|
final RemoveEvent removeEvent = (RemoveEvent) event;
|
||||||
remove(removeEvent.getRemoveIndex());
|
remove(removeEvent.getRemoveIndex());
|
||||||
// Sync only when the currently playing is removed
|
|
||||||
if (removeEvent.getQueueIndex() == removeEvent.getRemoveIndex()) sync();
|
|
||||||
break;
|
break;
|
||||||
case MOVE:
|
case MOVE:
|
||||||
final MoveEvent moveEvent = (MoveEvent) event;
|
final MoveEvent moveEvent = (MoveEvent) event;
|
||||||
move(moveEvent.getFromIndex(), moveEvent.getToIndex());
|
move(moveEvent.getFromIndex(), moveEvent.getToIndex());
|
||||||
break;
|
break;
|
||||||
|
case SELECT:
|
||||||
case RECOVERY:
|
case RECOVERY:
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Loading and Syncing
|
||||||
switch (event.type()) {
|
switch (event.type()) {
|
||||||
case INIT:
|
case INIT:
|
||||||
case REORDER:
|
case REORDER:
|
||||||
case ERROR:
|
case ERROR:
|
||||||
case APPEND:
|
loadImmediate(); // low frequency, critical events
|
||||||
loadInternal(); // low frequency, critical events
|
|
||||||
break;
|
break;
|
||||||
|
case APPEND:
|
||||||
case REMOVE:
|
case REMOVE:
|
||||||
case SELECT:
|
case SELECT:
|
||||||
case MOVE:
|
case MOVE:
|
||||||
case RECOVERY:
|
case RECOVERY:
|
||||||
default:
|
default:
|
||||||
load(); // high frequency or noncritical events
|
loadDebounced(); // high frequency or noncritical events
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,7 +259,11 @@ public class MediaSourceManager {
|
||||||
syncReactor.set(currentItem.getStream().subscribe(syncPlayback, onError));
|
syncReactor.set(currentItem.getStream().subscribe(syncPlayback, onError));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadInternal() {
|
private void loadDebounced() {
|
||||||
|
debouncedLoadSignal.onNext(System.currentTimeMillis());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadImmediate() {
|
||||||
// The current item has higher priority
|
// The current item has higher priority
|
||||||
final int currentIndex = playQueue.getIndex();
|
final int currentIndex = playQueue.getIndex();
|
||||||
final PlayQueueItem currentItem = playQueue.getItem(currentIndex);
|
final PlayQueueItem currentItem = playQueue.getItem(currentIndex);
|
||||||
|
@ -290,7 +291,9 @@ public class MediaSourceManager {
|
||||||
|
|
||||||
final DeferredMediaSource mediaSource = (DeferredMediaSource) sources.getMediaSource(playQueue.indexOf(item));
|
final DeferredMediaSource mediaSource = (DeferredMediaSource) sources.getMediaSource(playQueue.indexOf(item));
|
||||||
if (mediaSource.state() == DeferredMediaSource.STATE_PREPARED) mediaSource.load();
|
if (mediaSource.state() == DeferredMediaSource.STATE_PREPARED) mediaSource.load();
|
||||||
if (tryUnblock()) sync();
|
|
||||||
|
tryUnblock();
|
||||||
|
if (!isBlocked) sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void resetSources() {
|
private void resetSources() {
|
||||||
|
@ -307,13 +310,13 @@ public class MediaSourceManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Disposable getDebouncedLoader() {
|
private Disposable getDebouncedLoader() {
|
||||||
return loadSignal
|
return debouncedLoadSignal
|
||||||
.debounce(loadDebounceMillis, TimeUnit.MILLISECONDS)
|
.debounce(loadDebounceMillis, TimeUnit.MILLISECONDS)
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe(new Consumer<Long>() {
|
.subscribe(new Consumer<Long>() {
|
||||||
@Override
|
@Override
|
||||||
public void accept(Long timestamp) throws Exception {
|
public void accept(Long timestamp) throws Exception {
|
||||||
loadInternal();
|
loadImmediate();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,7 @@ public interface PlaybackListener {
|
||||||
*
|
*
|
||||||
* May be called at any time.
|
* May be called at any time.
|
||||||
* */
|
* */
|
||||||
|
@Nullable
|
||||||
MediaSource sourceOf(final PlayQueueItem item, final StreamInfo info);
|
MediaSource sourceOf(final PlayQueueItem item, final StreamInfo info);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,132 @@
|
||||||
|
package org.schabi.newpipe.playlist;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.extractor.InfoItem;
|
||||||
|
import org.schabi.newpipe.extractor.ListExtractor;
|
||||||
|
import org.schabi.newpipe.extractor.ListInfo;
|
||||||
|
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import io.reactivex.SingleObserver;
|
||||||
|
import io.reactivex.annotations.NonNull;
|
||||||
|
import io.reactivex.disposables.Disposable;
|
||||||
|
|
||||||
|
abstract class AbstractInfoPlayQueue<T extends ListInfo, U extends InfoItem> extends PlayQueue {
|
||||||
|
boolean isInitial;
|
||||||
|
boolean isComplete;
|
||||||
|
|
||||||
|
int serviceId;
|
||||||
|
String baseUrl;
|
||||||
|
String nextUrl;
|
||||||
|
|
||||||
|
transient Disposable fetchReactor;
|
||||||
|
|
||||||
|
AbstractInfoPlayQueue(final U item) {
|
||||||
|
this(item.service_id, item.url, null, Collections.<InfoItem>emptyList(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
AbstractInfoPlayQueue(final int serviceId,
|
||||||
|
final String url,
|
||||||
|
final String nextPageUrl,
|
||||||
|
final List<InfoItem> streams,
|
||||||
|
final int index) {
|
||||||
|
super(index, extractListItems(streams));
|
||||||
|
|
||||||
|
this.baseUrl = url;
|
||||||
|
this.nextUrl = nextPageUrl;
|
||||||
|
this.serviceId = serviceId;
|
||||||
|
|
||||||
|
this.isInitial = streams.isEmpty();
|
||||||
|
this.isComplete = !isInitial && (nextPageUrl == null || nextPageUrl.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract protected String getTag();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isComplete() {
|
||||||
|
return isComplete;
|
||||||
|
}
|
||||||
|
|
||||||
|
SingleObserver<T> getHeadListObserver() {
|
||||||
|
return new SingleObserver<T>() {
|
||||||
|
@Override
|
||||||
|
public void onSubscribe(@NonNull Disposable d) {
|
||||||
|
if (isComplete || !isInitial || (fetchReactor != null && !fetchReactor.isDisposed())) {
|
||||||
|
d.dispose();
|
||||||
|
} else {
|
||||||
|
fetchReactor = d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSuccess(@NonNull T result) {
|
||||||
|
isInitial = false;
|
||||||
|
if (!result.has_more_streams) isComplete = true;
|
||||||
|
nextUrl = result.next_streams_url;
|
||||||
|
|
||||||
|
append(extractListItems(result.related_streams));
|
||||||
|
|
||||||
|
fetchReactor.dispose();
|
||||||
|
fetchReactor = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(@NonNull Throwable e) {
|
||||||
|
Log.e(getTag(), "Error fetching more playlist, marking playlist as complete.", e);
|
||||||
|
isComplete = true;
|
||||||
|
append(); // Notify change
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
SingleObserver<ListExtractor.NextItemsResult> getNextItemsObserver() {
|
||||||
|
return new SingleObserver<ListExtractor.NextItemsResult>() {
|
||||||
|
@Override
|
||||||
|
public void onSubscribe(@NonNull Disposable d) {
|
||||||
|
if (isComplete || isInitial || (fetchReactor != null && !fetchReactor.isDisposed())) {
|
||||||
|
d.dispose();
|
||||||
|
} else {
|
||||||
|
fetchReactor = d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSuccess(@NonNull ListExtractor.NextItemsResult result) {
|
||||||
|
if (!result.hasMoreStreams()) isComplete = true;
|
||||||
|
nextUrl = result.nextItemsUrl;
|
||||||
|
|
||||||
|
append(extractListItems(result.nextItemsList));
|
||||||
|
|
||||||
|
fetchReactor.dispose();
|
||||||
|
fetchReactor = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(@NonNull Throwable e) {
|
||||||
|
Log.e(getTag(), "Error fetching more playlist, marking playlist as complete.", e);
|
||||||
|
isComplete = true;
|
||||||
|
append(); // Notify change
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
if (fetchReactor != null) fetchReactor.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<PlayQueueItem> extractListItems(final List<InfoItem> infos) {
|
||||||
|
List<PlayQueueItem> result = new ArrayList<>();
|
||||||
|
for (final InfoItem stream : infos) {
|
||||||
|
if (stream instanceof StreamInfoItem) {
|
||||||
|
result.add(new PlayQueueItem((StreamInfoItem) stream));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
package org.schabi.newpipe.playlist;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.extractor.InfoItem;
|
||||||
|
import org.schabi.newpipe.extractor.channel.ChannelInfo;
|
||||||
|
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
|
||||||
|
import org.schabi.newpipe.util.ExtractorHelper;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
|
import io.reactivex.schedulers.Schedulers;
|
||||||
|
|
||||||
|
public final class ChannelPlayQueue extends AbstractInfoPlayQueue<ChannelInfo, ChannelInfoItem> {
|
||||||
|
public ChannelPlayQueue(final ChannelInfoItem item) {
|
||||||
|
super(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChannelPlayQueue(final int serviceId,
|
||||||
|
final String url,
|
||||||
|
final String nextPageUrl,
|
||||||
|
final List<InfoItem> streams,
|
||||||
|
final int index) {
|
||||||
|
super(serviceId, url, nextPageUrl, streams, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getTag() {
|
||||||
|
return "ChannelPlayQueue@" + Integer.toHexString(hashCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fetch() {
|
||||||
|
if (this.isInitial) {
|
||||||
|
ExtractorHelper.getChannelInfo(this.serviceId, this.baseUrl, false)
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(getHeadListObserver());
|
||||||
|
} else {
|
||||||
|
ExtractorHelper.getMoreChannelItems(this.serviceId, this.baseUrl, this.nextUrl)
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(getNextItemsObserver());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,104 +0,0 @@
|
||||||
package org.schabi.newpipe.playlist;
|
|
||||||
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import org.schabi.newpipe.extractor.InfoItem;
|
|
||||||
import org.schabi.newpipe.extractor.ListExtractor;
|
|
||||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
|
||||||
import org.schabi.newpipe.util.ExtractorHelper;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import io.reactivex.SingleObserver;
|
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
|
||||||
import io.reactivex.annotations.NonNull;
|
|
||||||
import io.reactivex.disposables.Disposable;
|
|
||||||
import io.reactivex.schedulers.Schedulers;
|
|
||||||
|
|
||||||
public final class ExternalPlayQueue extends PlayQueue {
|
|
||||||
private final String TAG = "ExternalPlayQueue@" + Integer.toHexString(hashCode());
|
|
||||||
|
|
||||||
private boolean isComplete;
|
|
||||||
|
|
||||||
private int serviceId;
|
|
||||||
private String baseUrl;
|
|
||||||
private String nextUrl;
|
|
||||||
|
|
||||||
private transient Disposable fetchReactor;
|
|
||||||
|
|
||||||
public ExternalPlayQueue(final int serviceId,
|
|
||||||
final String url,
|
|
||||||
final String nextPageUrl,
|
|
||||||
final List<InfoItem> streams,
|
|
||||||
final int index) {
|
|
||||||
super(index, extractPlaylistItems(streams));
|
|
||||||
|
|
||||||
this.baseUrl = url;
|
|
||||||
this.nextUrl = nextPageUrl;
|
|
||||||
this.serviceId = serviceId;
|
|
||||||
|
|
||||||
this.isComplete = nextPageUrl == null || nextPageUrl.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isComplete() {
|
|
||||||
return isComplete;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void fetch() {
|
|
||||||
ExtractorHelper.getMorePlaylistItems(this.serviceId, this.baseUrl, this.nextUrl)
|
|
||||||
.subscribeOn(Schedulers.io())
|
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.subscribe(getPlaylistObserver());
|
|
||||||
}
|
|
||||||
|
|
||||||
private SingleObserver<ListExtractor.NextItemsResult> getPlaylistObserver() {
|
|
||||||
return new SingleObserver<ListExtractor.NextItemsResult>() {
|
|
||||||
@Override
|
|
||||||
public void onSubscribe(@NonNull Disposable d) {
|
|
||||||
if (isComplete || (fetchReactor != null && !fetchReactor.isDisposed())) {
|
|
||||||
d.dispose();
|
|
||||||
} else {
|
|
||||||
fetchReactor = d;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSuccess(@NonNull ListExtractor.NextItemsResult result) {
|
|
||||||
if (!result.hasMoreStreams()) isComplete = true;
|
|
||||||
nextUrl = result.nextItemsUrl;
|
|
||||||
|
|
||||||
append(extractPlaylistItems(result.nextItemsList));
|
|
||||||
|
|
||||||
fetchReactor.dispose();
|
|
||||||
fetchReactor = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(@NonNull Throwable e) {
|
|
||||||
Log.e(TAG, "Error fetching more playlist, marking playlist as complete.", e);
|
|
||||||
isComplete = true;
|
|
||||||
append(); // Notify change
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void dispose() {
|
|
||||||
super.dispose();
|
|
||||||
if (fetchReactor != null) fetchReactor.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static List<PlayQueueItem> extractPlaylistItems(final List<InfoItem> infos) {
|
|
||||||
List<PlayQueueItem> result = new ArrayList<>();
|
|
||||||
for (final InfoItem stream : infos) {
|
|
||||||
if (stream instanceof StreamInfoItem) {
|
|
||||||
result.add(new PlayQueueItem((StreamInfoItem) stream));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -123,7 +123,7 @@ public abstract class PlayQueue implements Serializable {
|
||||||
* May throw {@link IndexOutOfBoundsException}.
|
* May throw {@link IndexOutOfBoundsException}.
|
||||||
* */
|
* */
|
||||||
public PlayQueueItem getItem(int index) {
|
public PlayQueueItem getItem(int index) {
|
||||||
if (index >= streams.size() || streams.get(index) == null) return null;
|
if (index < 0 || index >= streams.size() || streams.get(index) == null) return null;
|
||||||
return streams.get(index);
|
return streams.get(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,7 +279,7 @@ public abstract class PlayQueue implements Serializable {
|
||||||
queueIndex.set(currentIndex % (size - 1));
|
queueIndex.set(currentIndex % (size - 1));
|
||||||
|
|
||||||
} else if (currentIndex == removeIndex && currentIndex == size - 1){
|
} else if (currentIndex == removeIndex && currentIndex == size - 1){
|
||||||
queueIndex.set(removeIndex - 1);
|
queueIndex.set(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (backup != null) {
|
if (backup != null) {
|
||||||
|
|
|
@ -107,6 +107,8 @@ public class PlayQueueItemBuilder {
|
||||||
.bitmapConfig(Bitmap.Config.RGB_565) // Users won't be able to see much anyways
|
.bitmapConfig(Bitmap.Config.RGB_565) // Users won't be able to see much anyways
|
||||||
.preProcessor(bitmapProcessor)
|
.preProcessor(bitmapProcessor)
|
||||||
.imageScaleType(ImageScaleType.EXACTLY)
|
.imageScaleType(ImageScaleType.EXACTLY)
|
||||||
|
.cacheInMemory(true)
|
||||||
|
.cacheOnDisk(true)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
package org.schabi.newpipe.playlist;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.extractor.InfoItem;
|
||||||
|
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
|
||||||
|
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem;
|
||||||
|
import org.schabi.newpipe.util.ExtractorHelper;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
|
import io.reactivex.schedulers.Schedulers;
|
||||||
|
|
||||||
|
public final class PlaylistPlayQueue extends AbstractInfoPlayQueue<PlaylistInfo, PlaylistInfoItem> {
|
||||||
|
public PlaylistPlayQueue(final PlaylistInfoItem item) {
|
||||||
|
super(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PlaylistPlayQueue(final int serviceId,
|
||||||
|
final String url,
|
||||||
|
final String nextPageUrl,
|
||||||
|
final List<InfoItem> streams,
|
||||||
|
final int index) {
|
||||||
|
super(serviceId, url, nextPageUrl, streams, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getTag() {
|
||||||
|
return "PlaylistPlayQueue@" + Integer.toHexString(hashCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fetch() {
|
||||||
|
if (this.isInitial) {
|
||||||
|
ExtractorHelper.getPlaylistInfo(this.serviceId, this.baseUrl, false)
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(getHeadListObserver());
|
||||||
|
} else {
|
||||||
|
ExtractorHelper.getMorePlaylistItems(this.serviceId, this.baseUrl, this.nextUrl)
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(getNextItemsObserver());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,12 +1,21 @@
|
||||||
package org.schabi.newpipe.playlist;
|
package org.schabi.newpipe.playlist;
|
||||||
|
|
||||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||||
|
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
public final class SinglePlayQueue extends PlayQueue {
|
public final class SinglePlayQueue extends PlayQueue {
|
||||||
|
public SinglePlayQueue(final StreamInfoItem item) {
|
||||||
|
this(new PlayQueueItem(item));
|
||||||
|
}
|
||||||
|
|
||||||
public SinglePlayQueue(final StreamInfo info) {
|
public SinglePlayQueue(final StreamInfo info) {
|
||||||
super(0, Collections.singletonList(new PlayQueueItem(info)));
|
this(new PlayQueueItem(info));
|
||||||
|
}
|
||||||
|
|
||||||
|
private SinglePlayQueue(final PlayQueueItem playQueueItem) {
|
||||||
|
super(0, Collections.singletonList(playQueueItem));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -5,10 +5,11 @@ import android.content.ActivityNotFoundException;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
import android.support.v4.app.FragmentManager;
|
import android.support.v4.app.FragmentManager;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||||
|
|
||||||
|
@ -28,7 +29,12 @@ import org.schabi.newpipe.fragments.list.kiosk.KioskFragment;
|
||||||
import org.schabi.newpipe.fragments.list.playlist.PlaylistFragment;
|
import org.schabi.newpipe.fragments.list.playlist.PlaylistFragment;
|
||||||
import org.schabi.newpipe.fragments.list.search.SearchFragment;
|
import org.schabi.newpipe.fragments.list.search.SearchFragment;
|
||||||
import org.schabi.newpipe.history.HistoryActivity;
|
import org.schabi.newpipe.history.HistoryActivity;
|
||||||
|
import org.schabi.newpipe.player.BackgroundPlayer;
|
||||||
|
import org.schabi.newpipe.player.BackgroundPlayerActivity;
|
||||||
import org.schabi.newpipe.player.BasePlayer;
|
import org.schabi.newpipe.player.BasePlayer;
|
||||||
|
import org.schabi.newpipe.player.MainVideoPlayer;
|
||||||
|
import org.schabi.newpipe.player.PopupVideoPlayer;
|
||||||
|
import org.schabi.newpipe.player.PopupVideoPlayerActivity;
|
||||||
import org.schabi.newpipe.player.VideoPlayer;
|
import org.schabi.newpipe.player.VideoPlayer;
|
||||||
import org.schabi.newpipe.playlist.PlayQueue;
|
import org.schabi.newpipe.playlist.PlayQueue;
|
||||||
import org.schabi.newpipe.settings.SettingsActivity;
|
import org.schabi.newpipe.settings.SettingsActivity;
|
||||||
|
@ -77,6 +83,29 @@ public class NavigationHelper {
|
||||||
.putExtra(BasePlayer.PLAYBACK_PITCH, playbackPitch);
|
.putExtra(BasePlayer.PLAYBACK_PITCH, playbackPitch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void playOnMainPlayer(final Context context, final PlayQueue queue) {
|
||||||
|
context.startActivity(getPlayerIntent(context, MainVideoPlayer.class, queue));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void playOnPopupPlayer(final Context context, final PlayQueue queue) {
|
||||||
|
Toast.makeText(context, R.string.popup_playing_toast, Toast.LENGTH_SHORT).show();
|
||||||
|
context.startService(getPlayerIntent(context, PopupVideoPlayer.class, queue));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void playOnBackgroundPlayer(final Context context, final PlayQueue queue) {
|
||||||
|
Toast.makeText(context, R.string.background_player_playing_toast, Toast.LENGTH_SHORT).show();
|
||||||
|
context.startService(getPlayerIntent(context, BackgroundPlayer.class, queue));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void enqueueOnPopupPlayer(final Context context, final PlayQueue queue) {
|
||||||
|
Toast.makeText(context, R.string.popup_playing_append, Toast.LENGTH_SHORT).show();
|
||||||
|
context.startService(getPlayerEnqueueIntent(context, PopupVideoPlayer.class, queue));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void enqueueOnBackgroundPlayer(final Context context, final PlayQueue queue) {
|
||||||
|
Toast.makeText(context, R.string.background_player_append, Toast.LENGTH_SHORT).show();
|
||||||
|
context.startService(getPlayerEnqueueIntent(context, BackgroundPlayer.class, queue));
|
||||||
|
}
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Through FragmentManager
|
// Through FragmentManager
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
@ -230,6 +259,23 @@ public class NavigationHelper {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void openBackgroundPlayerControl(final Context context) {
|
||||||
|
openServicePlayerControl(context, BackgroundPlayerActivity.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void openPopupPlayerControl(final Context context) {
|
||||||
|
openServicePlayerControl(context, PopupVideoPlayerActivity.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void openServicePlayerControl(final Context context, final Class clazz) {
|
||||||
|
final Intent intent = new Intent(context, clazz);
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
|
||||||
|
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
}
|
||||||
|
context.startActivity(intent);
|
||||||
|
context.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
|
||||||
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Link handling
|
// Link handling
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
|
@ -6,7 +6,12 @@
|
||||||
android:id="@+id/channel_header_layout"
|
android:id="@+id/channel_header_layout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginBottom="12dp">
|
android:background="?attr/contrast_background_color">
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:id="@+id/channel_metadata"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/channel_banner_image"
|
android:id="@+id/channel_banner_image"
|
||||||
|
@ -78,3 +83,89 @@
|
||||||
tools:ignore="RtlHardcoded"
|
tools:ignore="RtlHardcoded"
|
||||||
tools:visibility="visible"/>
|
tools:visibility="visible"/>
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="25dp"
|
||||||
|
android:id="@+id/play_control"
|
||||||
|
android:layout_margin="@dimen/software_component_item_padding"
|
||||||
|
android:layout_below="@+id/channel_metadata">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="center"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:background="?attr/selectableItemBackground"
|
||||||
|
android:id="@+id/channel_play_bg_button">
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:text="@string/controls_background_title"
|
||||||
|
android:textSize="@dimen/channel_rss_title_size"
|
||||||
|
android:textColor="?attr/colorAccent"
|
||||||
|
android:drawablePadding="4dp"
|
||||||
|
android:drawableLeft="?attr/audio"
|
||||||
|
android:drawableStart="?attr/audio"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<View android:id="@+id/anchorLeft"
|
||||||
|
android:layout_width="1dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:paddingTop="3dp"
|
||||||
|
android:paddingBottom="3dp"
|
||||||
|
android:clickable="false"
|
||||||
|
android:background="?attr/colorAccent"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="center"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:background="?attr/selectableItemBackground"
|
||||||
|
android:id="@+id/channel_play_all_button">
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:text="@string/play_all"
|
||||||
|
android:textSize="@dimen/channel_rss_title_size"
|
||||||
|
android:textColor="?attr/colorAccent"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<View android:id="@+id/anchorRight"
|
||||||
|
android:layout_width="1dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:paddingTop="3dp"
|
||||||
|
android:paddingBottom="3dp"
|
||||||
|
android:clickable="false"
|
||||||
|
android:background="?attr/colorAccent"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="center"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:background="?attr/selectableItemBackground"
|
||||||
|
android:id="@+id/channel_play_popup_button">
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:text="@string/controls_popup_title"
|
||||||
|
android:textSize="@dimen/channel_rss_title_size"
|
||||||
|
android:textColor="?attr/colorAccent"
|
||||||
|
android:drawablePadding="4dp"
|
||||||
|
android:drawableLeft="?attr/popup"
|
||||||
|
android:drawableStart="?attr/popup"/>
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
|
@ -0,0 +1,36 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/itemRoot"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:clickable="false"
|
||||||
|
android:padding="@dimen/video_item_search_padding">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/itemTitleView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentTop="true"
|
||||||
|
android:ellipsize="marquee"
|
||||||
|
android:fadingEdge="horizontal"
|
||||||
|
android:marqueeRepeatLimit="marquee_forever"
|
||||||
|
android:scrollHorizontally="true"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||||
|
android:textSize="@dimen/video_item_search_title_text_size"
|
||||||
|
tools:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. "/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/itemTypeView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@+id/itemTitleView"
|
||||||
|
android:layout_alignParentLeft="true"
|
||||||
|
android:layout_alignParentStart="true"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
|
android:textSize="@dimen/video_item_search_uploader_text_size"
|
||||||
|
tools:text="TYPE" />
|
||||||
|
</RelativeLayout>
|
|
@ -7,6 +7,7 @@
|
||||||
android:layout_height="@dimen/video_item_search_height"
|
android:layout_height="@dimen/video_item_search_height"
|
||||||
android:background="?attr/selectableItemBackground"
|
android:background="?attr/selectableItemBackground"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
android:padding="@dimen/video_item_search_padding">
|
android:padding="@dimen/video_item_search_padding">
|
||||||
|
|
||||||
<de.hdodenhof.circleimageview.CircleImageView
|
<de.hdodenhof.circleimageview.CircleImageView
|
||||||
|
@ -28,6 +29,7 @@
|
||||||
android:layout_alignParentTop="true"
|
android:layout_alignParentTop="true"
|
||||||
android:layout_marginBottom="@dimen/video_item_search_image_right_margin"
|
android:layout_marginBottom="@dimen/video_item_search_image_right_margin"
|
||||||
android:layout_toRightOf="@+id/itemThumbnailView"
|
android:layout_toRightOf="@+id/itemThumbnailView"
|
||||||
|
android:layout_toEndOf="@+id/itemThumbnailView"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:lines="1"
|
android:lines="1"
|
||||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||||
|
@ -41,6 +43,7 @@
|
||||||
android:layout_above="@+id/itemAdditionalDetails"
|
android:layout_above="@+id/itemAdditionalDetails"
|
||||||
android:layout_marginBottom="@dimen/channel_item_description_to_details_margin"
|
android:layout_marginBottom="@dimen/channel_item_description_to_details_margin"
|
||||||
android:layout_toRightOf="@+id/itemThumbnailView"
|
android:layout_toRightOf="@+id/itemThumbnailView"
|
||||||
|
android:layout_toEndOf="@+id/itemThumbnailView"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:lines="2"
|
android:lines="2"
|
||||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
|
@ -53,6 +56,7 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignParentBottom="true"
|
android:layout_alignParentBottom="true"
|
||||||
android:layout_toRightOf="@+id/itemThumbnailView"
|
android:layout_toRightOf="@+id/itemThumbnailView"
|
||||||
|
android:layout_toEndOf="@+id/itemThumbnailView"
|
||||||
android:lines="1"
|
android:lines="1"
|
||||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
android:textSize="@dimen/video_item_search_upload_date_text_size"
|
android:textSize="@dimen/video_item_search_upload_date_text_size"
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="?attr/selectableItemBackground"
|
android:background="?attr/selectableItemBackground"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
android:padding="@dimen/video_item_search_padding">
|
android:padding="@dimen/video_item_search_padding">
|
||||||
|
|
||||||
<de.hdodenhof.circleimageview.CircleImageView
|
<de.hdodenhof.circleimageview.CircleImageView
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
android:layout_height="@dimen/video_item_search_height"
|
android:layout_height="@dimen/video_item_search_height"
|
||||||
android:background="?attr/selectableItemBackground"
|
android:background="?attr/selectableItemBackground"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
android:padding="@dimen/video_item_search_padding">
|
android:padding="@dimen/video_item_search_padding">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
android:layout_height="@dimen/video_item_search_height"
|
android:layout_height="@dimen/video_item_search_height"
|
||||||
android:background="?attr/selectableItemBackground"
|
android:background="?attr/selectableItemBackground"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
android:padding="@dimen/video_item_search_padding">
|
android:padding="@dimen/video_item_search_padding">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
|
@ -48,6 +49,7 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignParentTop="true"
|
android:layout_alignParentTop="true"
|
||||||
android:layout_toRightOf="@+id/itemThumbnailView"
|
android:layout_toRightOf="@+id/itemThumbnailView"
|
||||||
|
android:layout_toEndOf="@+id/itemThumbnailView"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:maxLines="2"
|
android:maxLines="2"
|
||||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||||
|
@ -60,6 +62,7 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_below="@+id/itemVideoTitleView"
|
android:layout_below="@+id/itemVideoTitleView"
|
||||||
android:layout_toRightOf="@+id/itemThumbnailView"
|
android:layout_toRightOf="@+id/itemThumbnailView"
|
||||||
|
android:layout_toEndOf="@+id/itemThumbnailView"
|
||||||
android:lines="1"
|
android:lines="1"
|
||||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
android:textSize="@dimen/video_item_search_uploader_text_size"
|
android:textSize="@dimen/video_item_search_uploader_text_size"
|
||||||
|
@ -71,6 +74,7 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignParentBottom="true"
|
android:layout_alignParentBottom="true"
|
||||||
android:layout_toRightOf="@+id/itemThumbnailView"
|
android:layout_toRightOf="@+id/itemThumbnailView"
|
||||||
|
android:layout_toEndOf="@+id/itemThumbnailView"
|
||||||
android:lines="1"
|
android:lines="1"
|
||||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
android:textSize="@dimen/video_item_search_upload_date_text_size"
|
android:textSize="@dimen/video_item_search_upload_date_text_size"
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="?attr/selectableItemBackground"
|
android:background="?attr/selectableItemBackground"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
android:padding="@dimen/video_item_search_padding">
|
android:padding="@dimen/video_item_search_padding">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
|
@ -48,11 +49,13 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignParentTop="true"
|
android:layout_alignParentTop="true"
|
||||||
android:layout_toRightOf="@+id/itemThumbnailView"
|
android:layout_toRightOf="@+id/itemThumbnailView"
|
||||||
|
android:layout_toEndOf="@+id/itemThumbnailView"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:maxLines="2"
|
android:maxLines="2"
|
||||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||||
android:textSize="@dimen/video_item_search_title_text_size"
|
android:textSize="@dimen/video_item_search_title_text_size"
|
||||||
tools:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc tristique vitae sem vitae blanditLorem ipsumLorem ipsumLorem ipsumLorem ipsumLorem ipsumLorem ipsumLorem ipsum"/>
|
tools:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc tristique vitae sem vitae blanditLorem ipsumLorem ipsumLorem ipsumLorem ipsumLorem ipsumLorem ipsumLorem ipsum"
|
||||||
|
/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/itemUploaderView"
|
android:id="@+id/itemUploaderView"
|
||||||
|
@ -60,6 +63,7 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_below="@+id/itemVideoTitleView"
|
android:layout_below="@+id/itemVideoTitleView"
|
||||||
android:layout_toRightOf="@+id/itemThumbnailView"
|
android:layout_toRightOf="@+id/itemThumbnailView"
|
||||||
|
android:layout_toEndOf="@+id/itemThumbnailView"
|
||||||
android:lines="1"
|
android:lines="1"
|
||||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
android:textSize="@dimen/video_item_search_uploader_text_size"
|
android:textSize="@dimen/video_item_search_uploader_text_size"
|
||||||
|
|
|
@ -79,8 +79,7 @@
|
||||||
android:layout_toLeftOf="@id/itemHandle"
|
android:layout_toLeftOf="@id/itemHandle"
|
||||||
android:layout_toStartOf="@id/itemHandle"
|
android:layout_toStartOf="@id/itemHandle"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:lines="1"
|
android:singleLine="true"
|
||||||
android:maxLines="1"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||||
android:textSize="@dimen/video_item_search_title_text_size"
|
android:textSize="@dimen/video_item_search_title_text_size"
|
||||||
tools:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. "/>
|
tools:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. "/>
|
||||||
|
@ -94,7 +93,8 @@
|
||||||
android:layout_toEndOf="@id/itemThumbnailView"
|
android:layout_toEndOf="@id/itemThumbnailView"
|
||||||
android:layout_toLeftOf="@id/itemHandle"
|
android:layout_toLeftOf="@id/itemHandle"
|
||||||
android:layout_toStartOf="@id/itemHandle"
|
android:layout_toStartOf="@id/itemHandle"
|
||||||
android:lines="1"
|
android:ellipsize="end"
|
||||||
|
android:singleLine="true"
|
||||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
android:textSize="@dimen/video_item_search_upload_date_text_size"
|
android:textSize="@dimen/video_item_search_upload_date_text_size"
|
||||||
tools:text="Uploader"/>
|
tools:text="Uploader"/>
|
||||||
|
|
|
@ -6,8 +6,7 @@
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="?attr/contrast_background_color"
|
android:background="?attr/contrast_background_color">
|
||||||
android:paddingBottom="6dp">
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/playlist_title_view"
|
android:id="@+id/playlist_title_view"
|
||||||
|
@ -81,64 +80,90 @@
|
||||||
android:textSize="@dimen/playlist_detail_subtext_size"
|
android:textSize="@dimen/playlist_detail_subtext_size"
|
||||||
tools:ignore="RtlHardcoded"
|
tools:ignore="RtlHardcoded"
|
||||||
tools:text="234 videos"/>
|
tools:text="234 videos"/>
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
<RelativeLayout
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="25dp"
|
||||||
android:id="@+id/play_control"
|
android:id="@+id/play_control"
|
||||||
android:paddingLeft="5dp"
|
android:layout_margin="@dimen/software_component_item_padding"
|
||||||
android:paddingRight="5dp"
|
|
||||||
android:layout_below="@+id/playlist_meta">
|
android:layout_below="@+id/playlist_meta">
|
||||||
|
|
||||||
<Button
|
<LinearLayout
|
||||||
android:id="@+id/playlist_play_bg_button"
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="center"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:background="?attr/selectableItemBackground"
|
||||||
|
android:id="@+id/playlist_play_bg_button">
|
||||||
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center_vertical|right"
|
android:gravity="center_vertical"
|
||||||
android:layout_marginRight="2dp"
|
|
||||||
android:layout_toLeftOf="@+id/playlist_play_all_button"
|
|
||||||
android:layout_toStartOf="@+id/playlist_play_all_button"
|
|
||||||
android:text="@string/controls_background_title"
|
android:text="@string/controls_background_title"
|
||||||
android:textSize="@dimen/channel_rss_title_size"
|
android:textSize="@dimen/channel_rss_title_size"
|
||||||
android:textColor="?attr/colorAccent"
|
android:textColor="?attr/colorAccent"
|
||||||
android:theme="@style/RedButton"
|
|
||||||
android:drawableLeft="?attr/audio"
|
|
||||||
android:drawablePadding="4dp"
|
android:drawablePadding="4dp"
|
||||||
tools:ignore="RtlHardcoded"
|
android:drawableLeft="?attr/audio"
|
||||||
tools:visibility="visible" />
|
android:drawableStart="?attr/audio"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<Button
|
<View android:id="@+id/anchorLeft"
|
||||||
android:id="@+id/playlist_play_all_button"
|
android:layout_width="1dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:paddingTop="3dp"
|
||||||
|
android:paddingBottom="3dp"
|
||||||
|
android:clickable="false"
|
||||||
|
android:background="?attr/colorAccent"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="center"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:background="?attr/selectableItemBackground"
|
||||||
|
android:id="@+id/playlist_play_all_button">
|
||||||
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center_vertical|right"
|
android:gravity="center_vertical"
|
||||||
android:layout_marginRight="2dp"
|
|
||||||
android:layout_toLeftOf="@+id/playlist_play_popup_button"
|
|
||||||
android:layout_toStartOf="@+id/playlist_play_popup_button"
|
|
||||||
android:text="@string/play_all"
|
android:text="@string/play_all"
|
||||||
android:textSize="@dimen/channel_rss_title_size"
|
android:textSize="@dimen/channel_rss_title_size"
|
||||||
android:textColor="?attr/colorAccent"
|
android:textColor="?attr/colorAccent"/>
|
||||||
android:theme="@style/RedButton"
|
</LinearLayout>
|
||||||
tools:ignore="RtlHardcoded"
|
|
||||||
tools:visibility="visible"/>
|
|
||||||
|
|
||||||
<Button
|
<View android:id="@+id/anchorRight"
|
||||||
android:id="@+id/playlist_play_popup_button"
|
android:layout_width="1dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:paddingTop="3dp"
|
||||||
|
android:paddingBottom="3dp"
|
||||||
|
android:clickable="false"
|
||||||
|
android:background="?attr/colorAccent"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="center"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:background="?attr/selectableItemBackground"
|
||||||
|
android:id="@+id/playlist_play_popup_button">
|
||||||
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center_vertical|right"
|
android:gravity="center_vertical"
|
||||||
android:layout_alignParentRight="true"
|
|
||||||
android:text="@string/controls_popup_title"
|
android:text="@string/controls_popup_title"
|
||||||
android:textSize="@dimen/channel_rss_title_size"
|
android:textSize="@dimen/channel_rss_title_size"
|
||||||
android:textColor="?attr/colorAccent"
|
android:textColor="?attr/colorAccent"
|
||||||
android:theme="@style/RedButton"
|
|
||||||
android:drawableLeft="?attr/popup"
|
|
||||||
android:drawablePadding="4dp"
|
android:drawablePadding="4dp"
|
||||||
tools:ignore="RtlHardcoded"
|
android:drawableLeft="?attr/popup"
|
||||||
tools:visibility="visible" />
|
android:drawableStart="?attr/popup"/>
|
||||||
</RelativeLayout>
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
|
@ -122,6 +122,8 @@
|
||||||
<string name="notification_channel_name">NewPipe Notification</string>
|
<string name="notification_channel_name">NewPipe Notification</string>
|
||||||
<string name="notification_channel_description">Notifications for NewPipe Background and Popup Players</string>
|
<string name="notification_channel_description">Notifications for NewPipe Background and Popup Players</string>
|
||||||
|
|
||||||
|
<string name="unknown_content">[Unknown]</string>
|
||||||
|
|
||||||
<!-- error strings -->
|
<!-- error strings -->
|
||||||
<string name="general_error">Error</string>
|
<string name="general_error">Error</string>
|
||||||
<string name="network_error">Network error</string>
|
<string name="network_error">Network error</string>
|
||||||
|
@ -308,4 +310,9 @@
|
||||||
<string name="play_queue_stream_detail">Details</string>
|
<string name="play_queue_stream_detail">Details</string>
|
||||||
<string name="play_queue_audio_settings">Audio Settings</string>
|
<string name="play_queue_audio_settings">Audio Settings</string>
|
||||||
<string name="hold_to_append">Hold To Enqueue</string>
|
<string name="hold_to_append">Hold To Enqueue</string>
|
||||||
|
<string name="enqueue_on_background">Enqueue on Background</string>
|
||||||
|
<string name="enqueue_on_popup">Enqueue on Popup</string>
|
||||||
|
<string name="start_here_on_main">Start Playing Here</string>
|
||||||
|
<string name="start_here_on_background">Start Here on Background</string>
|
||||||
|
<string name="start_here_on_popup">Start Here on Popup</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Loading…
Reference in New Issue