1
0
mirror of https://github.com/TeamNewPipe/NewPipe synced 2024-12-25 17:40:33 +00:00

Merge branch 'dev' into multiple-services

This commit is contained in:
wb9688 2017-11-19 17:41:57 +01:00
commit 011e151c91
105 changed files with 2124 additions and 572 deletions

View File

@ -18,16 +18,16 @@ WARNING: PUTTING NEWPIPE OR ANY FORK OF IT INTO GOOGLE PLAYSTORE VIOLATES THEIR
## Screenshots ## Screenshots
[<img src="screenshots/shot_1.png" width=160>](screenshots/shot_1.png) [<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_1.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_1.png)
[<img src="screenshots/shot_2.png" width=160>](screenshots/shot_2.png) [<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_2.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_2.png)
[<img src="screenshots/shot_3.png" width=160>](screenshots/shot_3.png) [<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_3.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_3.png)
[<img src="screenshots/shot_4.png" width=160>](screenshots/shot_4.png) [<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_4.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_4.png)
[<img src="screenshots/shot_5.png" width=160>](screenshots/shot_5.png) [<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_5.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_5.png)
[<img src="screenshots/shot_6.png" width=160>](screenshots/shot_6.png) [<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_6.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_6.png)
[<img src="screenshots/shot_7.png" width=160>](screenshots/shot_7.png) [<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_7.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_7.png)
[<img src="screenshots/shot_8.png" width=160>](screenshots/shot_8.png) [<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_8.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_8.png)
[<img src="screenshots/shot_9.png" width=160>](screenshots/shot_9.png) [<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_9.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_9.png)
[<img src="screenshots/shot_10.png" width=160>](screenshots/shot_10.png) [<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_10.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_10.png)
## Description ## Description

View File

@ -185,7 +185,7 @@
android:name=".RouterPopupActivity" android:name=".RouterPopupActivity"
android:label="@string/popup_mode_share_menu_title" android:label="@string/popup_mode_share_menu_title"
android:taskAffinity="" android:taskAffinity=""
android:theme="@android:style/Theme.NoDisplay"> android:theme="@style/PopupPermissionsTheme">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.VIEW"/> <action android:name="android.intent.action.VIEW"/>
<action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH"/> <action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH"/>

View File

@ -21,6 +21,7 @@ public class RouterPopupActivity extends RouterActivity {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
&& !PermissionHelper.checkSystemAlertWindowPermission(this)) { && !PermissionHelper.checkSystemAlertWindowPermission(this)) {
Toast.makeText(this, R.string.msg_popup_permission, Toast.LENGTH_LONG).show(); Toast.makeText(this, R.string.msg_popup_permission, Toast.LENGTH_LONG).show();
finish();
return; return;
} }
StreamingService service; StreamingService service;

View File

@ -50,6 +50,7 @@ public abstract class BaseStateFragment<I> extends BaseFragment implements ViewC
protected Button errorButtonRetry; protected Button errorButtonRetry;
protected TextView errorTextView; protected TextView errorTextView;
@State
protected boolean useAsFrontPage = false; protected boolean useAsFrontPage = false;
@Override @Override

View File

@ -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;
@ -463,6 +465,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);
@ -480,6 +487,34 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
detailControlsPopup.setOnTouchListener(getOnControlsTouchListener()); detailControlsPopup.setOnTouchListener(getOnControlsTouchListener());
} }
private void showStreamDialog(final StreamInfoItem item) {
final Context context = getContext();
if (context == null || context.getResources() == null || getActivity() == null) return;
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
@ -796,16 +831,16 @@ 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() {
@ -824,13 +859,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();
} }
} }
@ -870,8 +903,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);

View File

@ -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,33 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
} }
} }
protected void showStreamDialog(final StreamInfoItem item) {
final Context context = getContext();
if (context == null || context.getResources() == null || getActivity() == null) return;
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
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/

View File

@ -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;
@ -68,6 +80,11 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
private TextView headerTitleView; private TextView headerTitleView;
private TextView headerSubscribersTextView; private TextView headerSubscribersTextView;
private Button headerSubscribeButton; private Button headerSubscribeButton;
private View playlistCtrl;
private LinearLayout headerPlayAllButton;
private LinearLayout headerPopupButton;
private LinearLayout headerBackgroundButton;
private MenuItem menuRssButton; private MenuItem menuRssButton;
@ -124,10 +141,57 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
headerTitleView = headerRootLayout.findViewById(R.id.channel_title_view); headerTitleView = headerRootLayout.findViewById(R.id.channel_title_view);
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);
playlistCtrl = headerRootLayout.findViewById(R.id.playlist_control);
headerPlayAllButton = headerRootLayout.findViewById(R.id.playlist_ctrl_play_all_button);
headerPopupButton = headerRootLayout.findViewById(R.id.playlist_ctrl_play_popup_button);
headerBackgroundButton = headerRootLayout.findViewById(R.id.playlist_ctrl_play_bg_button);
return headerRootLayout; return headerRootLayout;
} }
@Override
protected void showStreamDialog(final StreamInfoItem item) {
final Context context = getContext();
if (context == null || context.getResources() == null || getActivity() == null) return;
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
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
@ -382,6 +446,7 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
} else headerSubscribersTextView.setVisibility(View.GONE); } else headerSubscribersTextView.setVisibility(View.GONE);
if (menuRssButton != null) menuRssButton.setVisible(!TextUtils.isEmpty(result.feed_url)); if (menuRssButton != null) menuRssButton.setVisible(!TextUtils.isEmpty(result.feed_url));
playlistCtrl.setVisibility(View.VISIBLE);
if (!result.errors.isEmpty()) { if (!result.errors.isEmpty()) {
showSnackBarError(result.errors, UserAction.REQUESTED_CHANNEL, NewPipe.getNameOfService(result.service_id), result.url, 0); showSnackBarError(result.errors, UserAction.REQUESTED_CHANNEL, NewPipe.getNameOfService(result.service_id), result.url, 0);
@ -391,6 +456,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

View File

@ -27,6 +27,7 @@ import org.schabi.newpipe.util.ExtractorHelper;
import org.schabi.newpipe.util.KioskTranslator; import org.schabi.newpipe.util.KioskTranslator;
import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.NavigationHelper;
import icepick.State;
import io.reactivex.Single; import io.reactivex.Single;
import static org.schabi.newpipe.util.AnimationUtils.animateView; import static org.schabi.newpipe.util.AnimationUtils.animateView;
@ -53,7 +54,8 @@ import static org.schabi.newpipe.util.AnimationUtils.animateView;
public class KioskFragment extends BaseListInfoFragment<KioskInfo> { public class KioskFragment extends BaseListInfoFragment<KioskInfo> {
private String kioskId = ""; @State
protected String kioskId = "";
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////

View File

@ -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,7 +14,6 @@ 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.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
@ -23,12 +23,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;
@ -50,10 +50,11 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
private TextView headerUploaderName; private TextView headerUploaderName;
private ImageView headerUploaderAvatar; private ImageView headerUploaderAvatar;
private TextView headerStreamCount; private TextView headerStreamCount;
private View playlistCtrl;
private Button headerPlayAllButton; private View headerPlayAllButton;
private Button headerPopupButton; private View headerPopupButton;
private Button headerBackgroundButton; private View 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();
@ -81,10 +82,11 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
headerUploaderName = headerRootLayout.findViewById(R.id.uploader_name); headerUploaderName = headerRootLayout.findViewById(R.id.uploader_name);
headerUploaderAvatar = headerRootLayout.findViewById(R.id.uploader_avatar_view); headerUploaderAvatar = headerRootLayout.findViewById(R.id.uploader_avatar_view);
headerStreamCount = headerRootLayout.findViewById(R.id.playlist_stream_count); headerStreamCount = headerRootLayout.findViewById(R.id.playlist_stream_count);
playlistCtrl = headerRootLayout.findViewById(R.id.playlist_control);
headerPlayAllButton = headerRootLayout.findViewById(R.id.playlist_play_all_button); headerPlayAllButton = headerRootLayout.findViewById(R.id.playlist_ctrl_play_all_button);
headerPopupButton = headerRootLayout.findViewById(R.id.playlist_play_popup_button); headerPopupButton = headerRootLayout.findViewById(R.id.playlist_ctrl_play_popup_button);
headerBackgroundButton = headerRootLayout.findViewById(R.id.playlist_play_bg_button); headerBackgroundButton = headerRootLayout.findViewById(R.id.playlist_ctrl_play_bg_button);
return headerRootLayout; return headerRootLayout;
} }
@ -103,6 +105,47 @@ 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();
if (context == null || context.getResources() == null || getActivity() == null) return;
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
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
@ -150,6 +193,8 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
} }
} }
playlistCtrl.setVisibility(View.VISIBLE);
imageLoader.displayImage(result.uploader_avatar_url, headerUploaderAvatar, DISPLAY_AVATAR_OPTIONS); imageLoader.displayImage(result.uploader_avatar_url, headerUploaderAvatar, DISPLAY_AVATAR_OPTIONS);
headerStreamCount.setText(getResources().getQuantityString(R.plurals.videos, (int) result.stream_count, (int) result.stream_count)); headerStreamCount.setText(getResources().getQuantityString(R.plurals.videos, (int) result.stream_count, (int) result.stream_count));
@ -160,7 +205,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 +218,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

View File

@ -165,7 +165,7 @@ public class SearchFragment extends BaseListFragment<SearchResult, ListExtractor
suggestionListAdapter = new SuggestionListAdapter(activity); suggestionListAdapter = new SuggestionListAdapter(activity);
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity); SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity);
isSearchHistoryEnabled = preferences.getBoolean(getString(R.string.enable_search_history_key), true); isSearchHistoryEnabled = preferences.getBoolean(getString(R.string.enable_search_history_key), true);
suggestionListAdapter.setShowSugestinHistory(isSearchHistoryEnabled); suggestionListAdapter.setShowSuggestionHistory(isSearchHistoryEnabled);
searchHistoryDAO = NewPipeDatabase.getInstance().searchHistoryDAO(); searchHistoryDAO = NewPipeDatabase.getInstance().searchHistoryDAO();
} }
@ -446,6 +446,12 @@ public class SearchFragment extends BaseListFragment<SearchResult, ListExtractor
searchEditText.setText(item.query); searchEditText.setText(item.query);
} }
@Override
public void onSuggestionItemInserted(SuggestionItem item) {
searchEditText.setText(item.query);
searchEditText.setSelection(searchEditText.getText().length());
}
@Override @Override
public void onSuggestionItemLongClick(SuggestionItem item) { public void onSuggestionItemLongClick(SuggestionItem item) {
if (item.fromHistory) showDeleteSuggestionDialog(item); if (item.fromHistory) showDeleteSuggestionDialog(item);

View File

@ -19,10 +19,11 @@ public class SuggestionListAdapter extends RecyclerView.Adapter<SuggestionListAd
private final ArrayList<SuggestionItem> items = new ArrayList<>(); private final ArrayList<SuggestionItem> items = new ArrayList<>();
private final Context context; private final Context context;
private OnSuggestionItemSelected listener; private OnSuggestionItemSelected listener;
private boolean showSugestinHistory = true; private boolean showSuggestionHistory = true;
public interface OnSuggestionItemSelected { public interface OnSuggestionItemSelected {
void onSuggestionItemSelected(SuggestionItem item); void onSuggestionItemSelected(SuggestionItem item);
void onSuggestionItemInserted(SuggestionItem item);
void onSuggestionItemLongClick(SuggestionItem item); void onSuggestionItemLongClick(SuggestionItem item);
} }
@ -32,7 +33,7 @@ public class SuggestionListAdapter extends RecyclerView.Adapter<SuggestionListAd
public void setItems(List<SuggestionItem> items) { public void setItems(List<SuggestionItem> items) {
this.items.clear(); this.items.clear();
if (showSugestinHistory) { if (showSuggestionHistory) {
this.items.addAll(items); this.items.addAll(items);
} else { } else {
// remove history items if history is disabled // remove history items if history is disabled
@ -49,8 +50,8 @@ public class SuggestionListAdapter extends RecyclerView.Adapter<SuggestionListAd
this.listener = listener; this.listener = listener;
} }
public void setShowSugestinHistory(boolean v) { public void setShowSuggestionHistory(boolean v) {
showSugestinHistory = v; showSuggestionHistory = v;
} }
@Override @Override
@ -62,19 +63,25 @@ public class SuggestionListAdapter extends RecyclerView.Adapter<SuggestionListAd
public void onBindViewHolder(SuggestionItemHolder holder, int position) { public void onBindViewHolder(SuggestionItemHolder holder, int position) {
final SuggestionItem currentItem = getItem(position); final SuggestionItem currentItem = getItem(position);
holder.updateFrom(currentItem); holder.updateFrom(currentItem);
holder.itemView.setOnClickListener(new View.OnClickListener() { holder.queryView.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
if (listener != null) listener.onSuggestionItemSelected(currentItem); if (listener != null) listener.onSuggestionItemSelected(currentItem);
} }
}); });
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() { holder.queryView.setOnLongClickListener(new View.OnLongClickListener() {
@Override @Override
public boolean onLongClick(View v) { public boolean onLongClick(View v) {
if (listener != null) listener.onSuggestionItemLongClick(currentItem); if (listener != null) listener.onSuggestionItemLongClick(currentItem);
return true; return true;
} }
}); });
holder.insertView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (listener != null) listener.onSuggestionItemInserted(currentItem);
}
});
} }
private SuggestionItem getItem(int position) { private SuggestionItem getItem(int position) {
@ -93,6 +100,8 @@ public class SuggestionListAdapter extends RecyclerView.Adapter<SuggestionListAd
public static class SuggestionItemHolder extends RecyclerView.ViewHolder { public static class SuggestionItemHolder extends RecyclerView.ViewHolder {
private final TextView itemSuggestionQuery; private final TextView itemSuggestionQuery;
private final ImageView suggestionIcon; private final ImageView suggestionIcon;
private final View queryView;
private final View insertView;
// Cache some ids, as they can potentially be constantly updated/recycled // Cache some ids, as they can potentially be constantly updated/recycled
private final int historyResId; private final int historyResId;
@ -103,6 +112,9 @@ public class SuggestionListAdapter extends RecyclerView.Adapter<SuggestionListAd
suggestionIcon = rootView.findViewById(R.id.item_suggestion_icon); suggestionIcon = rootView.findViewById(R.id.item_suggestion_icon);
itemSuggestionQuery = rootView.findViewById(R.id.item_suggestion_query); itemSuggestionQuery = rootView.findViewById(R.id.item_suggestion_query);
queryView = rootView.findViewById(R.id.suggestion_search);
insertView = rootView.findViewById(R.id.suggestion_insert);
historyResId = resolveResourceIdFromAttr(rootView.getContext(), R.attr.history); historyResId = resolveResourceIdFromAttr(rootView.getContext(), R.attr.history);
searchResId = resolveResourceIdFromAttr(rootView.getContext(), R.attr.search); searchResId = resolveResourceIdFromAttr(rootView.getContext(), R.attr.search);
} }

View File

@ -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() {

View File

@ -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;

View File

@ -0,0 +1,55 @@
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.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
public class InfoItemDialog {
private final AlertDialog dialog;
public InfoItemDialog(@NonNull final Activity activity,
@NonNull final StreamInfoItem info,
@NonNull final String[] commands,
@NonNull final DialogInterface.OnClickListener actions) {
this(activity, commands, actions, info.name, info.uploader_name);
}
public InfoItemDialog(@NonNull final Activity activity,
@NonNull final String[] commands,
@NonNull final DialogInterface.OnClickListener actions,
@NonNull final String title,
@Nullable final String additionalDetail) {
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(title);
TextView detailsView = bannerView.findViewById(R.id.itemAdditionalDetails);
if (additionalDetail != null) {
detailsView.setText(additionalDetail);
detailsView.setVisibility(View.VISIBLE);
} else {
detailsView.setVisibility(View.GONE);
}
dialog = new AlertDialog.Builder(activity)
.setCustomTitle(bannerView)
.setItems(commands, actions)
.create();
}
public void show() {
dialog.show();
}
}

View File

@ -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);
} }
/** /**

View File

@ -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));
remoteViews.setOnClickPendingIntent(R.id.notificationFRewind, if (basePlayerImpl.playQueue != null && basePlayerImpl.playQueue.size() > 1) {
PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_PLAY_PREVIOUS), PendingIntent.FLAG_UPDATE_CURRENT)); remoteViews.setInt(R.id.notificationFRewind, SET_IMAGE_RESOURCE_METHOD, R.drawable.exo_controls_previous);
remoteViews.setOnClickPendingIntent(R.id.notificationFForward, remoteViews.setInt(R.id.notificationFForward, SET_IMAGE_RESOURCE_METHOD, R.drawable.exo_controls_next);
PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_PLAY_NEXT), PendingIntent.FLAG_UPDATE_CURRENT)); remoteViews.setOnClickPendingIntent(R.id.notificationFRewind,
PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_PLAY_PREVIOUS), PendingIntent.FLAG_UPDATE_CURRENT));
remoteViews.setOnClickPendingIntent(R.id.notificationFForward,
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;

View File

@ -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;
@ -134,6 +134,7 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
protected final static int FAST_FORWARD_REWIND_AMOUNT = 10000; // 10 Seconds protected final static int FAST_FORWARD_REWIND_AMOUNT = 10000; // 10 Seconds
protected final static int PLAY_PREV_ACTIVATION_LIMIT = 5000; // 5 seconds protected final static int PLAY_PREV_ACTIVATION_LIMIT = 5000; // 5 seconds
protected final static int PROGRESS_LOOP_INTERVAL = 500; protected final static int PROGRESS_LOOP_INTERVAL = 500;
protected final static int RECOVERY_SKIP_THRESHOLD = 3000; // 3 seconds
protected SimpleExoPlayer simpleExoPlayer; protected SimpleExoPlayer simpleExoPlayer;
protected AudioReactor audioReactor; protected AudioReactor audioReactor;
@ -193,7 +194,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 +236,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);
@ -453,16 +454,20 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
final PlayQueueItem currentSourceItem = playQueue.getItem(); final PlayQueueItem currentSourceItem = playQueue.getItem();
// Check if already playing correct window // Check if already playing correct window
final boolean isCurrentWindowCorrect = simpleExoPlayer.getCurrentWindowIndex() == currentSourceIndex; final boolean isCurrentWindowCorrect =
simpleExoPlayer.getCurrentWindowIndex() == currentSourceIndex;
// Check if recovering // Check if recovering
if (isCurrentWindowCorrect && currentSourceItem != null && if (isCurrentWindowCorrect && currentSourceItem != null) {
currentSourceItem.getRecoveryPosition() != PlayQueueItem.RECOVERY_UNSET) {
/* Recovering with sub-second position may cause a long buffer delay in ExoPlayer, /* Recovering with sub-second position may cause a long buffer delay in ExoPlayer,
* rounding this position to the nearest second will help alleviate this.*/ * rounding this position to the nearest second will help alleviate this.*/
final long position = currentSourceItem.getRecoveryPosition(); final long position = currentSourceItem.getRecoveryPosition();
if (DEBUG) Log.d(TAG, "Rewinding to recovery window: " + currentSourceIndex + " at: " + getTimeString((int)position)); /* Skip recovering if the recovery position is not set.*/
if (position == PlayQueueItem.RECOVERY_UNSET) return;
if (DEBUG) Log.d(TAG, "Rewinding to recovery window: " + currentSourceIndex +
" at: " + getTimeString((int)position));
simpleExoPlayer.seekTo(currentSourceItem.getRecoveryPosition()); simpleExoPlayer.seekTo(currentSourceItem.getRecoveryPosition());
playQueue.unsetRecovery(currentSourceIndex); playQueue.unsetRecovery(currentSourceIndex);
} }
@ -515,7 +520,6 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
break; break;
case Player.STATE_READY: //3 case Player.STATE_READY: //3
recover(); recover();
if (!isPrepared) { if (!isPrepared) {
isPrepared = true; isPrepared = true;
onPrepared(playWhenReady); onPrepared(playWhenReady);
@ -545,14 +549,18 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
* an error to the play queue based on if the current error can be skipped. * an error to the play queue based on if the current error can be skipped.
* *
* This is done because ExoPlayer reports the source exceptions before window is * This is done because ExoPlayer reports the source exceptions before window is
* transitioned on seamless playback. * transitioned on seamless playback. Because player error causes ExoPlayer to go
* back to {@link Player#STATE_IDLE STATE_IDLE}, we reset and prepare the media source
* again to resume playback.
* *
* Because player error causes ExoPlayer to go back to {@link Player#STATE_IDLE STATE_IDLE}, * In the event that this error is produced during a valid stream playback, we save the
* we reset and prepare the media source again to resume playback.<br><br> * current position so the playback may be recovered and resumed manually by the user. This
* happens only if the playback is {@link #RECOVERY_SKIP_THRESHOLD} milliseconds until complete.
* <br><br>
* *
* {@link ExoPlaybackException#TYPE_UNEXPECTED TYPE_UNEXPECTED}: <br><br> * {@link ExoPlaybackException#TYPE_UNEXPECTED TYPE_UNEXPECTED}: <br><br>
* If a runtime error occurred, then we can try to recover it by restarting the playback * If a runtime error occurred, then we can try to recover it by restarting the playback
* after setting the timestamp recovery. * after setting the timestamp recovery. <br><br>
* *
* {@link ExoPlaybackException#TYPE_RENDERER TYPE_RENDERER}: <br><br> * {@link ExoPlaybackException#TYPE_RENDERER TYPE_RENDERER}: <br><br>
* If the renderer failed, treat the error as unrecoverable. * If the renderer failed, treat the error as unrecoverable.
@ -569,6 +577,10 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
switch (error.type) { switch (error.type) {
case ExoPlaybackException.TYPE_SOURCE: case ExoPlaybackException.TYPE_SOURCE:
if (simpleExoPlayer.getCurrentPosition() <
simpleExoPlayer.getDuration() - RECOVERY_SKIP_THRESHOLD) {
setRecovery();
}
playQueue.error(isCurrentWindowValid()); playQueue.error(isCurrentWindowValid());
showStreamError(error); showStreamError(error);
break; break;
@ -591,12 +603,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 +625,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 +645,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 +774,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 +840,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 +884,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,7 +916,9 @@ 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();
setRecovery(queuePos, windowPos); if (windowPos > 0 && windowPos <= simpleExoPlayer.getDuration()) {
setRecovery(queuePos, windowPos);
}
} }
public void setRecovery(final int queuePos, final long windowPos) { public void setRecovery(final int queuePos, final long windowPos) {

View File

@ -23,6 +23,7 @@ import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Color; import android.graphics.Color;
import android.media.AudioManager; import android.media.AudioManager;
import android.os.Build; import android.os.Build;
@ -33,6 +34,7 @@ import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper; import android.support.v7.widget.helper.ItemTouchHelper;
import android.util.Log; import android.util.Log;
import android.view.GestureDetector; import android.view.GestureDetector;
import android.view.MenuItem;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.view.WindowManager; import android.view.WindowManager;
@ -48,6 +50,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;
@ -58,6 +61,8 @@ import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.PermissionHelper; import org.schabi.newpipe.util.PermissionHelper;
import org.schabi.newpipe.util.ThemeHelper; import org.schabi.newpipe.util.ThemeHelper;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List; import java.util.List;
import static org.schabi.newpipe.util.AnimationUtils.animateView; import static org.schabi.newpipe.util.AnimationUtils.animateView;
@ -150,6 +155,17 @@ public final class MainVideoPlayer extends Activity {
if (playerImpl != null) playerImpl.destroy(); if (playerImpl != null) playerImpl.destroy();
} }
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (playerImpl.isSomePopupMenuVisible()) {
playerImpl.moreOptionsPopupMenu.dismiss();
playerImpl.getQualityPopupMenu().dismiss();
playerImpl.getPlaybackSpeedPopupMenu().dismiss();
}
}
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Utils // Utils
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
@ -222,7 +238,6 @@ public final class MainVideoPlayer extends Activity {
private ImageButton repeatButton; private ImageButton repeatButton;
private ImageButton shuffleButton; private ImageButton shuffleButton;
private ImageButton screenRotationButton;
private ImageButton playPauseButton; private ImageButton playPauseButton;
private ImageButton playPreviousButton; private ImageButton playPreviousButton;
private ImageButton playNextButton; private ImageButton playNextButton;
@ -234,6 +249,10 @@ public final class MainVideoPlayer extends Activity {
private boolean queueVisible; private boolean queueVisible;
private ImageButton moreOptionsButton;
public int moreOptionsPopupMenuGroupId = 89;
public PopupMenu moreOptionsPopupMenu;
VideoPlayerImpl(final Context context) { VideoPlayerImpl(final Context context) {
super("VideoPlayerImpl" + MainVideoPlayer.TAG, context); super("VideoPlayerImpl" + MainVideoPlayer.TAG, context);
} }
@ -249,10 +268,12 @@ public final class MainVideoPlayer extends Activity {
this.repeatButton = rootView.findViewById(R.id.repeatButton); this.repeatButton = rootView.findViewById(R.id.repeatButton);
this.shuffleButton = rootView.findViewById(R.id.shuffleButton); this.shuffleButton = rootView.findViewById(R.id.shuffleButton);
this.screenRotationButton = rootView.findViewById(R.id.screenRotationButton);
this.playPauseButton = rootView.findViewById(R.id.playPauseButton); this.playPauseButton = rootView.findViewById(R.id.playPauseButton);
this.playPreviousButton = rootView.findViewById(R.id.playPreviousButton); this.playPreviousButton = rootView.findViewById(R.id.playPreviousButton);
this.playNextButton = rootView.findViewById(R.id.playNextButton); this.playNextButton = rootView.findViewById(R.id.playNextButton);
this.moreOptionsButton = rootView.findViewById(R.id.moreOptionsButton);
this.moreOptionsPopupMenu = new PopupMenu(context, moreOptionsButton);
this.moreOptionsPopupMenu.getMenuInflater().inflate(R.menu.menu_videooptions, moreOptionsPopupMenu.getMenu());
titleTextView.setSelected(true); titleTextView.setSelected(true);
channelTextView.setSelected(true); channelTextView.setSelected(true);
@ -276,7 +297,7 @@ public final class MainVideoPlayer extends Activity {
playPauseButton.setOnClickListener(this); playPauseButton.setOnClickListener(this);
playPreviousButton.setOnClickListener(this); playPreviousButton.setOnClickListener(this);
playNextButton.setOnClickListener(this); playNextButton.setOnClickListener(this);
screenRotationButton.setOnClickListener(this); moreOptionsButton.setOnClickListener(this);
} }
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
@ -348,6 +369,28 @@ public final class MainVideoPlayer extends Activity {
finish(); finish();
} }
public void onPlayBackgroundButtonClicked() {
if (DEBUG) Log.d(TAG, "onPlayBackgroundButtonClicked() called");
if (playerImpl.getPlayer() == null) return;
setRecovery();
final Intent intent = NavigationHelper.getPlayerIntent(
context,
BackgroundPlayer.class,
this.getPlayQueue(),
this.getRepeatMode(),
this.getPlaybackSpeed(),
this.getPlaybackPitch(),
this.getPlaybackQuality()
);
context.startService(intent);
((View) getControlAnimationView().getParent()).setVisibility(View.GONE);
destroy();
finish();
}
@Override @Override
public void onClick(View v) { public void onClick(View v) {
super.onClick(v); super.onClick(v);
@ -360,9 +403,6 @@ public final class MainVideoPlayer extends Activity {
} else if (v.getId() == playNextButton.getId()) { } else if (v.getId() == playNextButton.getId()) {
onPlayNext(); onPlayNext();
} else if (v.getId() == screenRotationButton.getId()) {
onScreenRotationClicked();
} else if (v.getId() == queueButton.getId()) { } else if (v.getId() == queueButton.getId()) {
onQueueClicked(); onQueueClicked();
return; return;
@ -372,6 +412,8 @@ public final class MainVideoPlayer extends Activity {
} else if (v.getId() == shuffleButton.getId()) { } else if (v.getId() == shuffleButton.getId()) {
onShuffleClicked(); onShuffleClicked();
return; return;
} else if (v.getId() == moreOptionsButton.getId()) {
onMoreOptionsClicked();
} }
if (getCurrentState() != STATE_COMPLETED) { if (getCurrentState() != STATE_COMPLETED) {
@ -397,7 +439,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() {
@ -405,6 +447,32 @@ public final class MainVideoPlayer extends Activity {
queueVisible = false; queueVisible = false;
} }
private void onMoreOptionsClicked() {
if (DEBUG) Log.d(TAG, "onMoreOptionsClicked() called");
buildMoreOptionsMenu();
try {
Field[] fields = moreOptionsPopupMenu.getClass().getDeclaredFields();
for (Field field : fields) {
if ("mPopup".equals(field.getName())) {
field.setAccessible(true);
Object menuPopupHelper = field.get(moreOptionsPopupMenu);
Class<?> classPopupHelper = Class.forName(menuPopupHelper
.getClass().getName());
Method setForceIcons = classPopupHelper.getMethod(
"setForceShowIcon", boolean.class);
setForceIcons.invoke(menuPopupHelper, true);
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
moreOptionsPopupMenu.show();
isSomePopupMenuVisible = true;
showControls(300);
}
private void onScreenRotationClicked() { private void onScreenRotationClicked() {
if (DEBUG) Log.d(TAG, "onScreenRotationClicked() called"); if (DEBUG) Log.d(TAG, "onScreenRotationClicked() called");
toggleOrientation(); toggleOrientation();
@ -555,6 +623,27 @@ public final class MainVideoPlayer extends Activity {
setShuffleButton(shuffleButton, playQueue.isShuffled()); setShuffleButton(shuffleButton, playQueue.isShuffled());
} }
private void buildMoreOptionsMenu() {
if (moreOptionsPopupMenu == null) return;
moreOptionsPopupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem menuItem) {
switch (menuItem.getItemId()) {
case R.id.toggleOrientation:
onScreenRotationClicked();
break;
case R.id.switchPopup:
onFullScreenButtonClicked();
break;
case R.id.switchBackground:
onPlayBackgroundButtonClicked();
break;
}
return false;
}
});
}
private void buildQueue() { private void buildQueue() {
queueLayout = findViewById(R.id.playQueuePanel); queueLayout = findViewById(R.id.playQueuePanel);
@ -565,6 +654,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 +670,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

View File

@ -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;
} }
@ -835,6 +842,8 @@ public final class PopupVideoPlayer extends Service {
} }
savePositionAndSize(); savePositionAndSize();
} }
v.performClick();
return true; return true;
} }
@ -873,23 +882,25 @@ public final class PopupVideoPlayer extends Service {
private final Context context; private final Context context;
private final Handler mainHandler; private final Handler mainHandler;
FetcherHandler(Context context, int serviceId, String url) { private FetcherHandler(Context context, int serviceId, String url) {
this.mainHandler = new Handler(PopupVideoPlayer.this.getMainLooper()); this.mainHandler = new Handler(PopupVideoPlayer.this.getMainLooper());
this.context = context; this.context = context;
this.url = url; this.url = url;
this.serviceId = serviceId; this.serviceId = serviceId;
} }
/*package-private*/ void onReceive(final StreamInfo info) { private void onReceive(final StreamInfo info) {
mainHandler.post(new Runnable() { mainHandler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
playerImpl.initPlayback(new SinglePlayQueue(info)); final Intent intent = NavigationHelper.getPlayerIntent(getApplicationContext(),
PopupVideoPlayer.class, new SinglePlayQueue(info));
playerImpl.handleIntent(intent);
} }
}); });
} }
protected void onError(final Throwable exception) { private void onError(final Throwable exception) {
if (DEBUG) Log.d(TAG, "onError() called with: exception = [" + exception + "]"); if (DEBUG) Log.d(TAG, "onError() called with: exception = [" + exception + "]");
exception.printStackTrace(); exception.printStackTrace();
mainHandler.post(new Runnable() { mainHandler.post(new Runnable() {
@ -915,7 +926,7 @@ public final class PopupVideoPlayer extends Service {
stopSelf(); stopSelf();
} }
/*package-private*/ void onReCaptchaException() { private void onReCaptchaException() {
Toast.makeText(context, R.string.recaptcha_request_toast, Toast.LENGTH_LONG).show(); Toast.makeText(context, R.string.recaptcha_request_toast, Toast.LENGTH_LONG).show();
// Starting ReCaptcha Challenge Activity // Starting ReCaptcha Challenge Activity
Intent intent = new Intent(context, ReCaptchaActivity.class); Intent intent = new Intent(context, ReCaptchaActivity.class);

View File

@ -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;
} }

View File

@ -124,12 +124,11 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
private View topControlsRoot; private View topControlsRoot;
private TextView qualityTextView; private TextView qualityTextView;
private ImageButton fullScreenButton;
private ValueAnimator controlViewAnimator; private ValueAnimator controlViewAnimator;
private Handler controlsVisibilityHandler = new Handler(); private Handler controlsVisibilityHandler = new Handler();
private boolean isSomePopupMenuVisible = false; boolean isSomePopupMenuVisible = false;
private int qualityPopupMenuGroupId = 69; private int qualityPopupMenuGroupId = 69;
private PopupMenu qualityPopupMenu; private PopupMenu qualityPopupMenu;
@ -166,7 +165,6 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
this.bottomControlsRoot = rootView.findViewById(R.id.bottomControls); this.bottomControlsRoot = rootView.findViewById(R.id.bottomControls);
this.topControlsRoot = rootView.findViewById(R.id.topControls); this.topControlsRoot = rootView.findViewById(R.id.topControls);
this.qualityTextView = rootView.findViewById(R.id.qualityTextView); this.qualityTextView = rootView.findViewById(R.id.qualityTextView);
this.fullScreenButton = rootView.findViewById(R.id.fullScreenButton);
//this.aspectRatioFrameLayout.setAspectRatio(16.0f / 9.0f); //this.aspectRatioFrameLayout.setAspectRatio(16.0f / 9.0f);
@ -186,7 +184,6 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
super.initListeners(); super.initListeners();
playbackSeekBar.setOnSeekBarChangeListener(this); playbackSeekBar.setOnSeekBarChangeListener(this);
playbackSpeedTextView.setOnClickListener(this); playbackSpeedTextView.setOnClickListener(this);
fullScreenButton.setOnClickListener(this);
qualityTextView.setOnClickListener(this); qualityTextView.setOnClickListener(this);
} }
@ -272,17 +269,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);
@ -453,9 +451,7 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
@Override @Override
public void onClick(View v) { public void onClick(View v) {
if (DEBUG) Log.d(TAG, "onClick() called with: v = [" + v + "]"); if (DEBUG) Log.d(TAG, "onClick() called with: v = [" + v + "]");
if (v.getId() == fullScreenButton.getId()) { if (v.getId() == qualityTextView.getId()) {
onFullScreenButtonClicked();
} else if (v.getId() == qualityTextView.getId()) {
onQualitySelectorClicked(); onQualitySelectorClicked();
} else if (v.getId() == playbackSpeedTextView.getId()) { } else if (v.getId() == playbackSpeedTextView.getId()) {
onPlaybackSpeedClicked(); onPlaybackSpeedClicked();
@ -753,14 +749,14 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
return qualityTextView; return qualityTextView;
} }
public ImageButton getFullScreenButton() {
return fullScreenButton;
}
public PopupMenu getQualityPopupMenu() { public PopupMenu getQualityPopupMenu() {
return qualityPopupMenu; return qualityPopupMenu;
} }
public PopupMenu getPlaybackSpeedPopupMenu() {
return playbackSpeedPopupMenu;
}
public View getSurfaceForeground() { public View getSurfaceForeground() {
return surfaceForeground; return surfaceForeground;
} }

View File

@ -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);
}
} }

View File

@ -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();
} }
}); });
} }

View File

@ -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);
/** /**

View File

@ -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;
}
}

View File

@ -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());
}
}
}

View File

@ -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;
}
}

View File

@ -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) {

View File

@ -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();
} }
} }

View File

@ -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());
}
}
}

View File

@ -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

View File

@ -26,6 +26,9 @@ import android.util.Log;
import org.schabi.newpipe.MainActivity; import org.schabi.newpipe.MainActivity;
import org.schabi.newpipe.extractor.Info; import org.schabi.newpipe.extractor.Info;
import java.util.Map;
import java.util.concurrent.TimeUnit;
public final class InfoCache { public final class InfoCache {
private static final boolean DEBUG = MainActivity.DEBUG; private static final boolean DEBUG = MainActivity.DEBUG;
@ -37,9 +40,9 @@ public final class InfoCache {
* Trim the cache to this size * Trim the cache to this size
*/ */
private static final int TRIM_CACHE_TO = 30; private static final int TRIM_CACHE_TO = 30;
private static final int DEFAULT_TIMEOUT_HOURS = 4;
// TODO: Replace to one with timeout (like the one from guava) private static final LruCache<String, CacheData> lruCache = new LruCache<>(MAX_ITEMS_ON_CACHE);
private static final LruCache<String, Info> lruCache = new LruCache<>(MAX_ITEMS_ON_CACHE);
private InfoCache() { private InfoCache() {
//no instance //no instance
@ -52,28 +55,29 @@ public final class InfoCache {
public Info getFromKey(int serviceId, @NonNull String url) { public Info getFromKey(int serviceId, @NonNull String url) {
if (DEBUG) Log.d(TAG, "getFromKey() called with: serviceId = [" + serviceId + "], url = [" + url + "]"); if (DEBUG) Log.d(TAG, "getFromKey() called with: serviceId = [" + serviceId + "], url = [" + url + "]");
synchronized (lruCache) { synchronized (lruCache) {
return lruCache.get(serviceId + url); return getInfo(lruCache, keyOf(serviceId, url));
} }
} }
public void putInfo(@NonNull Info info) { public void putInfo(@NonNull Info info) {
if (DEBUG) Log.d(TAG, "putInfo() called with: info = [" + info + "]"); if (DEBUG) Log.d(TAG, "putInfo() called with: info = [" + info + "]");
synchronized (lruCache) { synchronized (lruCache) {
lruCache.put(info.service_id + info.url, info); final CacheData data = new CacheData(info, DEFAULT_TIMEOUT_HOURS, TimeUnit.HOURS);
lruCache.put(keyOf(info), data);
} }
} }
public void removeInfo(@NonNull Info info) { public void removeInfo(@NonNull Info info) {
if (DEBUG) Log.d(TAG, "removeInfo() called with: info = [" + info + "]"); if (DEBUG) Log.d(TAG, "removeInfo() called with: info = [" + info + "]");
synchronized (lruCache) { synchronized (lruCache) {
lruCache.remove(info.service_id + info.url); lruCache.remove(keyOf(info));
} }
} }
public void removeInfo(int serviceId, @NonNull String url) { public void removeInfo(int serviceId, @NonNull String url) {
if (DEBUG) Log.d(TAG, "removeInfo() called with: serviceId = [" + serviceId + "], url = [" + url + "]"); if (DEBUG) Log.d(TAG, "removeInfo() called with: serviceId = [" + serviceId + "], url = [" + url + "]");
synchronized (lruCache) { synchronized (lruCache) {
lruCache.remove(serviceId + url); lruCache.remove(keyOf(serviceId, url));
} }
} }
@ -87,6 +91,7 @@ public final class InfoCache {
public void trimCache() { public void trimCache() {
if (DEBUG) Log.d(TAG, "trimCache() called"); if (DEBUG) Log.d(TAG, "trimCache() called");
synchronized (lruCache) { synchronized (lruCache) {
removeStaleCache(lruCache);
lruCache.trimToSize(TRIM_CACHE_TO); lruCache.trimToSize(TRIM_CACHE_TO);
} }
} }
@ -97,4 +102,51 @@ public final class InfoCache {
} }
} }
private static String keyOf(@NonNull final Info info) {
return keyOf(info.service_id, info.url);
}
private static String keyOf(final int serviceId, @NonNull final String url) {
return serviceId + url;
}
private static void removeStaleCache(@NonNull final LruCache<String, CacheData> cache) {
for (Map.Entry<String, CacheData> entry : cache.snapshot().entrySet()) {
final CacheData data = entry.getValue();
if (data != null && data.isExpired()) {
cache.remove(entry.getKey());
}
}
}
private static Info getInfo(@NonNull final LruCache<String, CacheData> cache,
@NonNull final String key) {
final CacheData data = cache.get(key);
if (data == null) return null;
if (data.isExpired()) {
cache.remove(key);
return null;
}
return data.info;
}
final private static class CacheData {
final private long expireTimestamp;
final private Info info;
private CacheData(@NonNull final Info info,
final long timeout,
@NonNull final TimeUnit timeUnit) {
this.expireTimestamp = System.currentTimeMillis() +
TimeUnit.MILLISECONDS.convert(timeout, timeUnit);
this.info = info;
}
private boolean isExpired() {
return System.currentTimeMillis() > expireTimestamp;
}
}
} }

View File

@ -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
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/

Binary file not shown.

After

Width:  |  Height:  |  Size: 554 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 546 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 418 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 427 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 492 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 500 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 570 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 573 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 675 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 730 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 305 B

View File

@ -209,7 +209,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginRight="2dp" android:layout_marginRight="2dp"
android:layout_toLeftOf="@+id/screenRotationButton" android:layout_toLeftOf="@+id/queueButton"
android:gravity="center" android:gravity="center"
android:minHeight="35dp" android:minHeight="35dp"
android:minWidth="40dp" android:minWidth="40dp"
@ -218,28 +218,13 @@
tools:ignore="RtlHardcoded,RtlSymmetry" tools:ignore="RtlHardcoded,RtlSymmetry"
tools:text="1x" /> tools:text="1x" />
<ImageButton
android:id="@+id/screenRotationButton"
android:layout_width="35dp"
android:layout_height="35dp"
android:layout_marginLeft="2dp"
android:layout_marginRight="2dp"
android:layout_toLeftOf="@+id/queueButton"
android:background="#00ffffff"
android:clickable="true"
android:focusable="true"
android:padding="8dp"
android:scaleType="fitXY"
android:src="@drawable/ic_screen_rotation_white"
tools:ignore="ContentDescription,RtlHardcoded"/>
<ImageButton <ImageButton
android:id="@+id/queueButton" android:id="@+id/queueButton"
android:layout_width="30dp" android:layout_width="30dp"
android:layout_height="35dp" android:layout_height="35dp"
android:layout_marginLeft="2dp" android:layout_marginLeft="2dp"
android:layout_marginRight="2dp" android:layout_marginRight="2dp"
android:layout_toLeftOf="@+id/fullScreenButton" android:layout_toLeftOf="@+id/moreOptionsButton"
android:background="#00ffffff" android:background="#00ffffff"
android:clickable="true" android:clickable="true"
android:focusable="true" android:focusable="true"
@ -249,16 +234,17 @@
tools:ignore="ContentDescription,RtlHardcoded"/> tools:ignore="ContentDescription,RtlHardcoded"/>
<ImageButton <ImageButton
android:id="@+id/fullScreenButton" android:id="@+id/moreOptionsButton"
android:layout_width="35dp" android:layout_width="wrap_content"
android:layout_height="35dp" android:layout_height="wrap_content"
android:layout_alignParentRight="true" android:layout_alignParentRight="true"
android:layout_marginLeft="4dp" android:layout_marginLeft="2dp"
android:padding="5dp"
android:background="#00ffffff" android:background="#00ffffff"
android:clickable="true" android:clickable="true"
android:focusable="true" android:focusable="true"
android:scaleType="fitXY" android:scaleType="fitXY"
android:src="@drawable/ic_fullscreen_exit_white" android:src="?attr/options"
tools:ignore="ContentDescription,RtlHardcoded"/> tools:ignore="ContentDescription,RtlHardcoded"/>
</RelativeLayout> </RelativeLayout>

View File

@ -6,75 +6,90 @@
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">
<ImageView <RelativeLayout
android:id="@+id/channel_banner_image" android:id="@+id/channel_metadata"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="70dp" android:layout_height="wrap_content">
android:background="@android:color/black"
android:fitsSystemWindows="true"
android:scaleType="centerCrop"
android:src="@drawable/channel_banner"
tools:ignore="ContentDescription"/>
<de.hdodenhof.circleimageview.CircleImageView <ImageView
android:id="@+id/channel_avatar_view" android:id="@+id/channel_banner_image"
android:layout_width="@dimen/channel_avatar_size" android:layout_width="match_parent"
android:layout_height="@dimen/channel_avatar_size" android:layout_height="70dp"
android:layout_alignTop="@id/channel_banner_image" android:background="@android:color/black"
android:layout_marginLeft="8dp" android:fitsSystemWindows="true"
android:layout_marginTop="50dp" android:scaleType="centerCrop"
android:src="@drawable/buddy" android:src="@drawable/channel_banner"
app:civ_border_color="#ffffff" tools:ignore="ContentDescription"/>
app:civ_border_width="2dp"
tools:ignore="RtlHardcoded"/>
<TextView <de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/channel_title_view" android:id="@+id/channel_avatar_view"
android:layout_width="@dimen/channel_avatar_size"
android:layout_height="@dimen/channel_avatar_size"
android:layout_alignTop="@id/channel_banner_image"
android:layout_marginLeft="8dp"
android:layout_marginTop="50dp"
android:src="@drawable/buddy"
app:civ_border_color="#ffffff"
app:civ_border_width="2dp"
tools:ignore="RtlHardcoded"/>
<TextView
android:id="@+id/channel_title_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/channel_banner_image"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="6dp"
android:layout_toLeftOf="@+id/channel_subscribe_button"
android:layout_toRightOf="@+id/channel_avatar_view"
android:ellipsize="end"
android:lines="1"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textSize="@dimen/video_item_detail_title_text_size"
tools:ignore="RtlHardcoded"
tools:text="Lorem ipsum dolor"/>
<TextView
android:id="@+id/channel_subscriber_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/channel_title_view"
android:layout_alignRight="@+id/channel_title_view"
android:layout_below="@+id/channel_title_view"
android:ellipsize="end"
android:gravity="left|center"
android:lines="1"
android:textSize="@dimen/channel_subscribers_text_size"
android:visibility="gone"
tools:ignore="RtlHardcoded"
tools:text="123,141,411 subscribers"
tools:visibility="visible"/>
<android.support.v7.widget.AppCompatButton
android:id="@+id/channel_subscribe_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_below="@+id/channel_banner_image"
android:layout_gravity="center_vertical|right"
android:layout_marginRight="2dp"
android:text="@string/subscribe_button_title"
android:textSize="@dimen/channel_rss_title_size"
android:theme="@style/RedButton"
android:visibility="gone"
tools:ignore="RtlHardcoded"
tools:visibility="visible"/>
</RelativeLayout>
<LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@id/channel_banner_image" android:layout_below="@id/channel_metadata">
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="6dp"
android:layout_toLeftOf="@+id/channel_subscribe_button"
android:layout_toRightOf="@+id/channel_avatar_view"
android:ellipsize="end"
android:lines="1"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textSize="@dimen/video_item_detail_title_text_size"
tools:ignore="RtlHardcoded"
tools:text="Lorem ipsum dolor"/>
<TextView <include layout="@layout/playlist_control" />
android:id="@+id/channel_subscriber_view" </LinearLayout>
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/channel_title_view"
android:layout_alignRight="@+id/channel_title_view"
android:layout_below="@+id/channel_title_view"
android:ellipsize="end"
android:gravity="left|center"
android:lines="1"
android:textSize="@dimen/channel_subscribers_text_size"
android:visibility="gone"
tools:ignore="RtlHardcoded"
tools:text="123,141,411 subscribers"
tools:visibility="visible"/>
<android.support.v7.widget.AppCompatButton
android:id="@+id/channel_subscribe_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_below="@+id/channel_banner_image"
android:layout_gravity="center_vertical|right"
android:layout_marginRight="2dp"
android:text="@string/subscribe_button_title"
android:textSize="@dimen/channel_rss_title_size"
android:theme="@style/RedButton"
android:visibility="gone"
tools:ignore="RtlHardcoded"
tools:visibility="visible"/>
</RelativeLayout> </RelativeLayout>

View File

@ -0,0 +1,40 @@
<?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:paddingLeft="@dimen/video_item_search_padding"
android:paddingRight="@dimen/video_item_search_padding"
android:paddingTop="@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/channel_item_detail_title_text_size"
tools:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. "/>
<TextView
android:id="@+id/itemAdditionalDetails"
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"
android:visibility="gone"
tools:visibility="visible"
tools:text="TYPE" />
</RelativeLayout>

View File

@ -1,36 +1,71 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout <RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
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/selectableItemBackground" android:orientation="horizontal">
android:clickable="true"
android:orientation="horizontal"
android:paddingBottom="8dp"
android:paddingTop="8dp">
<ImageView <LinearLayout
android:id="@+id/item_suggestion_icon" android:id="@+id/suggestion_search"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center_vertical"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
tools:ignore="ContentDescription,RtlHardcoded"
tools:src="?attr/history"/>
<TextView
android:id="@+id/item_suggestion_query"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_vertical" android:background="?attr/selectableItemBackground"
android:layout_marginLeft="8dp" android:clickable="true"
android:layout_marginRight="16dp" android:focusable="true"
android:ellipsize="end" android:layout_alignParentLeft="true"
android:maxLines="2" android:layout_alignParentStart="true"
android:textAppearance="@style/TextAppearance.AppCompat.Body1" android:layout_toLeftOf="@id/suggestion_insert"
android:textSize="14sp" android:layout_toStartOf="@id/suggestion_insert"
tools:ignore="RtlHardcoded" android:layout_centerVertical="true"
tools:text="Search query"/> android:paddingBottom="8dp"
</LinearLayout> android:paddingTop="8dp">
<ImageView
android:id="@+id/item_suggestion_icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center_vertical"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
tools:ignore="ContentDescription,RtlHardcoded"
tools:src="?attr/history"/>
<TextView
android:id="@+id/item_suggestion_query"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:ellipsize="end"
android:maxLines="2"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
android:textSize="14sp"
tools:ignore="RtlHardcoded"
tools:text="Search query"/>
</LinearLayout>
<LinearLayout
android:id="@+id/suggestion_insert"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackgroundBorderless"
android:clickable="true"
android:focusable="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingBottom="8dp"
android:paddingTop="10dp">
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_gravity="center"
android:src="?attr/search_add"
tools:ignore="ContentDescription,RtlHardcoded"/>
</LinearLayout>
</RelativeLayout>

View File

@ -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,11 +29,12 @@
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"
android:textSize="@dimen/video_item_search_title_text_size" android:textSize="@dimen/video_item_search_title_text_size"
tools:text="Channel Title, Lorem ipsum"/> tools:text="Channel Title, Lorem ipsum" />
<TextView <TextView
android:id="@+id/itemChannelDescriptionView" android:id="@+id/itemChannelDescriptionView"
@ -41,11 +43,12 @@
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"
android:textSize="@dimen/video_item_search_uploader_text_size" android:textSize="@dimen/video_item_search_uploader_text_size"
tools:text="Channel description, Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc tristique vitae sem vitae blanditLorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc tristique vitae sem vitae blanditLorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc tristique vitae sem vitae blandit"/> tools:text="Channel description, Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc tristique vitae sem vitae blanditLorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc tristique vitae sem vitae blanditLorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc tristique vitae sem vitae blandit" />
<TextView <TextView
android:id="@+id/itemAdditionalDetails" android:id="@+id/itemAdditionalDetails"
@ -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"

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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,8 +63,9 @@
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"
tools:text="Uploader"/> tools:text="Uploader" />
</RelativeLayout> </RelativeLayout>

View File

@ -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"/>

View File

@ -34,22 +34,22 @@
<TextView <TextView
android:id="@+id/notificationSongName" android:id="@+id/notificationSongName"
style="@android:style/TextAppearance.StatusBar.EventContent.Title"
android:layout_width="match_parent" android:layout_width="match_parent"
android:ellipsize="end" android:ellipsize="end"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:maxLines="1" android:maxLines="1"
android:textSize="14sp" android:textSize="14sp"
android:textColor="@color/background_title_color"
tools:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis nec aliquam augue, eget cursus est. Ut id tristique enim, ut scelerisque tellus. Sed ultricies ipsum non mauris ultricies, commodo malesuada velit porta."/> tools:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis nec aliquam augue, eget cursus est. Ut id tristique enim, ut scelerisque tellus. Sed ultricies ipsum non mauris ultricies, commodo malesuada velit porta."/>
<TextView <TextView
android:id="@+id/notificationArtist" android:id="@+id/notificationArtist"
android:layout_width="match_parent" android:layout_width="match_parent"
style="@android:style/TextAppearance.StatusBar.EventContent"
android:ellipsize="end" android:ellipsize="end"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:maxLines="1" android:maxLines="1"
android:textSize="12sp" android:textSize="12sp"
android:textColor="@color/background_subtext_color"
tools:text="Duis posuere arcu condimentum lobortis mattis."/> tools:text="Duis posuere arcu condimentum lobortis mattis."/>
</LinearLayout> </LinearLayout>

View File

@ -46,22 +46,22 @@
<TextView <TextView
android:id="@+id/notificationSongName" android:id="@+id/notificationSongName"
style="@android:style/TextAppearance.StatusBar.EventContent.Title"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:ellipsize="end" android:ellipsize="end"
android:maxLines="2" android:maxLines="2"
android:textSize="14sp" android:textSize="14sp"
android:textColor="@color/background_title_color"
tools:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis nec aliquam augue, eget cursus est. Ut id tristique enim, ut scelerisque tellus. Sed ultricies ipsum non mauris ultricies, commodo malesuada velit porta."/> tools:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis nec aliquam augue, eget cursus est. Ut id tristique enim, ut scelerisque tellus. Sed ultricies ipsum non mauris ultricies, commodo malesuada velit porta."/>
<TextView <TextView
android:id="@+id/notificationArtist" android:id="@+id/notificationArtist"
style="@android:style/TextAppearance.StatusBar.EventContent"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:ellipsize="end" android:ellipsize="end"
android:maxLines="1" android:maxLines="1"
android:textSize="12sp" android:textSize="12sp"
android:textColor="@color/background_subtext_color"
tools:text="Duis posuere arcu condimentum lobortis mattis."/> tools:text="Duis posuere arcu condimentum lobortis mattis."/>
</LinearLayout> </LinearLayout>
@ -80,7 +80,6 @@
<TextView <TextView
android:id="@+id/notificationTime" android:id="@+id/notificationTime"
style="@android:style/TextAppearance.StatusBar.EventContent"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="8dp" android:layout_marginLeft="8dp"
@ -92,6 +91,7 @@
android:ellipsize="end" android:ellipsize="end"
android:maxLines="1" android:maxLines="1"
android:textSize="12sp" android:textSize="12sp"
android:textColor="@color/background_subtext_color"
tools:text="Duis posuere"/> tools:text="Duis posuere"/>
<RelativeLayout <RelativeLayout

View File

@ -29,22 +29,22 @@
<TextView <TextView
android:id="@+id/notificationSongName" android:id="@+id/notificationSongName"
style="@android:style/TextAppearance.StatusBar.EventContent.Title"
android:layout_width="match_parent" android:layout_width="match_parent"
android:ellipsize="end" android:ellipsize="end"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:maxLines="1" android:maxLines="1"
android:textSize="14sp" android:textSize="14sp"
android:textColor="@color/background_title_color"
tools:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis nec aliquam augue, eget cursus est. Ut id tristique enim, ut scelerisque tellus. Sed ultricies ipsum non mauris ultricies, commodo malesuada velit porta."/> tools:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis nec aliquam augue, eget cursus est. Ut id tristique enim, ut scelerisque tellus. Sed ultricies ipsum non mauris ultricies, commodo malesuada velit porta."/>
<TextView <TextView
android:id="@+id/notificationArtist" android:id="@+id/notificationArtist"
android:layout_width="match_parent" android:layout_width="match_parent"
style="@android:style/TextAppearance.StatusBar.EventContent"
android:ellipsize="end" android:ellipsize="end"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:maxLines="1" android:maxLines="1"
android:textSize="12sp" android:textSize="12sp"
android:textColor="@color/background_subtext_color"
tools:text="Duis posuere arcu condimentum lobortis mattis."/> tools:text="Duis posuere arcu condimentum lobortis mattis."/>
</LinearLayout> </LinearLayout>

View File

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:layout_width="match_parent"
android:layout_height="@dimen/playlist_ctrl_height"
android:id="@+id/playlist_control"
xmlns:android="http://schemas.android.com/apk/res/android"
android:visibility="invisible">
<LinearLayout
android:id="@+id/playlist_ctrl_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">
<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:clickable="false"
android:layout_marginBottom="@dimen/playlist_ctrl_seperator_margin"
android:layout_marginTop="@dimen/playlist_ctrl_seperator_margin"
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_ctrl_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:clickable="false"
android:layout_marginBottom="@dimen/playlist_ctrl_seperator_margin"
android:layout_marginTop="@dimen/playlist_ctrl_seperator_margin"
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_ctrl_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>

View File

@ -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,14 @@
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="wrap_content"
android:id="@+id/play_control" android:layout_below="@id/playlist_meta">
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:layout_below="@+id/playlist_meta">
<Button <include layout="@layout/playlist_control"/>
android:id="@+id/playlist_play_bg_button" </LinearLayout>
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|right"
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:textSize="@dimen/channel_rss_title_size"
android:textColor="?attr/colorAccent"
android:theme="@style/RedButton"
android:drawableLeft="?attr/audio"
android:drawablePadding="4dp"
tools:ignore="RtlHardcoded"
tools:visibility="visible" />
<Button
android:id="@+id/playlist_play_all_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|right"
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:textSize="@dimen/channel_rss_title_size"
android:textColor="?attr/colorAccent"
android:theme="@style/RedButton"
tools:ignore="RtlHardcoded"
tools:visibility="visible"/>
<Button
android:id="@+id/playlist_play_popup_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|right"
android:layout_alignParentRight="true"
android:text="@string/controls_popup_title"
android:textSize="@dimen/channel_rss_title_size"
android:textColor="?attr/colorAccent"
android:theme="@style/RedButton"
android:drawableLeft="?attr/popup"
android:drawablePadding="4dp"
tools:ignore="RtlHardcoded"
tools:visibility="visible" />
</RelativeLayout>
</RelativeLayout> </RelativeLayout>

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<menu
android:id="@+id/menu_video_options"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:icon="@drawable/ic_screen_rotation_white"
android:id="@+id/toggleOrientation"
android:title="Toggle orientation"
app:showAsAction="always|withText" />
<item
android:icon="@drawable/ic_fullscreen_exit_white"
android:id="@+id/switchPopup"
android:title="Switch to popup"
app:showAsAction="always|withText" />
<item android:icon="?audio"
android:id="@+id/switchBackground"
android:title="Switch to background"
app:showAsAction="always|withText" />
</menu>

View File

@ -275,4 +275,6 @@
<string name="title_activity_background_player">Reproductor de fondu</string> <string name="title_activity_background_player">Reproductor de fondu</string>
<string name="play_queue_remove">Desaniciar</string> <string name="play_queue_remove">Desaniciar</string>
<string name="play_queue_stream_detail">Detalles</string> <string name="play_queue_stream_detail">Detalles</string>
<string name="new_and_hot">Nuevo y popular</string>
<string name="play_queue_audio_settings">Axustes d\'audiu</string>
</resources> </resources>

View File

@ -136,7 +136,7 @@
<string name="later">Později</string> <string name="later">Později</string>
<string name="short_thousand">tis.</string> <string name="short_thousand">K</string>
<string name="open_in_popup_mode">Otevřít ve vyskakovacím okně</string> <string name="open_in_popup_mode">Otevřít ve vyskakovacím okně</string>
<string name="short_million">mil.</string> <string name="short_million">mil.</string>

View File

@ -42,15 +42,15 @@
<string name="background_player_playing_toast">Spiele im Hintergrund ab</string> <string name="background_player_playing_toast">Spiele im Hintergrund ab</string>
<string name="play_btn_text">Abspielen</string> <string name="play_btn_text">Abspielen</string>
<string name="use_tor_title">Benutze TOR</string> <string name="use_tor_title">Benutze Tor</string>
<string name="use_tor_summary">(Experimentell) Erzwinge das Herunterladen durch TOR für verbesserte Privatsphäre (Videostream noch nicht unterstützt).</string> <string name="use_tor_summary">(Experimentell) Erzwinge das Herunterladen durch Tor für verbesserte Privatsphäre (Videostream noch nicht unterstützt).</string>
<string name="network_error">Netzwerkfehler</string> <string name="network_error">Netzwerkfehler</string>
<string name="download_path_audio_title">Downloadverzeichnis für Musik</string> <string name="download_path_audio_title">Downloadverzeichnis für Audiodateien</string>
<string name="download_path_audio_summary">Verzeichnis zum Speichern heruntergeladener Audiodateien</string> <string name="download_path_audio_summary">Verzeichnis für heruntergeladene Audiodateien</string>
<string name="download_path_audio_dialog_title">Pfad für heruntergeladene Audiodateien eingeben</string> <string name="download_path_audio_dialog_title">Downloadverzeichnis für Audiodateien eingeben</string>
<string name="theme_title">Aussehen</string> <string name="theme_title">Design</string>
<string name="dark_theme_title">Dunkel</string> <string name="dark_theme_title">Dunkel</string>
<string name="light_theme_title">Hell</string> <string name="light_theme_title">Hell</string>
@ -61,7 +61,7 @@
<string name="general_error">Fehler</string> <string name="general_error">Fehler</string>
<string name="could_not_load_thumbnails">Konnte nicht alle Vorschaubilder laden</string> <string name="could_not_load_thumbnails">Konnte nicht alle Vorschaubilder laden</string>
<string name="youtube_signature_decryption_error">Konnte Video-URL-Signatur nicht entschlüsseln</string> <string name="youtube_signature_decryption_error">Konnte Video-URL-Signatur nicht entschlüsseln</string>
<string name="parsing_error">Konnte Webseite nicht parsen</string> <string name="parsing_error">Konnte Webseite nicht analysieren</string>
<string name="content_not_available">Inhalt nicht verfügbar</string> <string name="content_not_available">Inhalt nicht verfügbar</string>
<string name="blocked_by_gema">Durch die GEMA gesperrt</string> <string name="blocked_by_gema">Durch die GEMA gesperrt</string>
@ -73,7 +73,7 @@
<string name="live_streams_not_supported">Dies ist ein LIVESTREAM. Diese werden noch nicht unterstützt.</string> <string name="live_streams_not_supported">Dies ist ein LIVESTREAM. Diese werden noch nicht unterstützt.</string>
<string name="light_parsing_error">Konnte Webseite nicht vollständig parsen</string> <string name="light_parsing_error">Konnte Webseite nicht vollständig analysieren</string>
<string name="error_report_button_text">Fehler via E-Mail melden</string> <string name="error_report_button_text">Fehler via E-Mail melden</string>
<string name="error_snackbar_action">MELDEN</string> <string name="error_snackbar_action">MELDEN</string>
<string name="what_device_headline">Info:</string> <string name="what_device_headline">Info:</string>
@ -94,7 +94,7 @@
<string name="report_error">Einen Fehler melden</string> <string name="report_error">Einen Fehler melden</string>
<string name="user_report">Anwenderbericht</string> <string name="user_report">Anwenderbericht</string>
<string name="duration_live">live</string> <string name="duration_live">Live</string>
<string name="main_bg_subtitle">„Suchen“ antippen und loslegen</string> <string name="main_bg_subtitle">„Suchen“ antippen und loslegen</string>
<string name="downloads">Downloads</string> <string name="downloads">Downloads</string>
@ -163,13 +163,13 @@
<string name="popup_remember_size_pos_title">Größe und Position des Popups merken</string> <string name="popup_remember_size_pos_title">Größe und Position des Popups merken</string>
<string name="use_external_video_player_summary">Manche Auflösungen werden KEINE Tonspur haben, wenn diese Option eingeschaltet ist</string> <string name="use_external_video_player_summary">Manche Auflösungen werden KEINE Tonspur haben, wenn diese Option eingeschaltet ist</string>
<string name="popup_remember_size_pos_summary">Merke Position und Größe des Popups</string> <string name="popup_remember_size_pos_summary">Letzte Größe und Position des Popups merken</string>
<string name="player_gesture_controls_title">Gestensteuerung</string> <string name="player_gesture_controls_title">Gestensteuerung</string>
<string name="player_gesture_controls_summary">Helligkeit und Lautstärke des Players mit Gesten steuern</string> <string name="player_gesture_controls_summary">Helligkeit und Lautstärke des Players mit Gesten steuern</string>
<string name="show_search_suggestions_title">Durchsuche Vorschläge</string> <string name="show_search_suggestions_title">Durchsuche Vorschläge</string>
<string name="show_search_suggestions_summary">Zeige Vorschläge beim Suchen</string> <string name="show_search_suggestions_summary">Zeige Vorschläge beim Suchen</string>
<string name="settings_category_popup_title">Pop-up</string> <string name="settings_category_popup_title">Popup</string>
<string name="filter">Filter</string> <string name="filter">Filter</string>
<string name="refresh">Aktualisieren</string> <string name="refresh">Aktualisieren</string>
<string name="clear">Leeren</string> <string name="clear">Leeren</string>
@ -224,7 +224,7 @@
<string name="notification_channel_name">NewPipe Benachrichtigung</string> <string name="notification_channel_name">NewPipe Benachrichtigung</string>
<string name="notification_channel_description">Benachrichtigungen für NewPipe Hintergrund- und Popup-Spieler</string> <string name="notification_channel_description">Benachrichtigungen für NewPipe Hintergrund- und Popup-Player</string>
<string name="tab_main">Übersicht</string> <string name="tab_main">Übersicht</string>
<string name="settings_category_player_behavior_title">Verhalten</string> <string name="settings_category_player_behavior_title">Verhalten</string>
@ -258,12 +258,12 @@
<string name="settings_category_player_title">Player</string> <string name="settings_category_player_title">Player</string>
<string name="empty_subscription_feed_subtitle">Nichts hier außer Grillen</string> <string name="empty_subscription_feed_subtitle">Nichts hier außer Grillen</string>
<string name="delete_item_search_history">Möchten Sie dieses Element aus dem Suchverlauf löschen?</string> <string name="delete_item_search_history">Möchtest du dieses Element aus dem Suchverlauf löschen?</string>
<string name="blank_page_summary">Leere Seite</string> <string name="blank_page_summary">Leere Seite</string>
<string name="select_a_channel">Wähle einen Kanal aus</string> <string name="select_a_channel">Wähle einen Kanal aus</string>
<string name="no_channel_subscribed_yet">Noch kein Kanal abonniert</string> <string name="no_channel_subscribed_yet">Noch kein Kanal abonniert</string>
<string name="trending">Trends</string> <string name="trending">Trends</string>
<string name="popup_playing_append">Angereiht an Popup Player</string> <string name="popup_playing_append">In der Warteschlange des Popup-Players</string>
<string name="play_all">Alles abspielen</string> <string name="play_all">Alles abspielen</string>
<string name="play_queue_remove">Entfernen</string> <string name="play_queue_remove">Entfernen</string>
@ -284,8 +284,16 @@
<string name="select_a_kiosk">Kiosk auswählen</string> <string name="select_a_kiosk">Kiosk auswählen</string>
<string name="kiosk">Kiosk</string> <string name="kiosk">Kiosk</string>
<string name="show_hold_to_append_summary">Tipp anzeigen, wenn der Hintergrundwiedergabe-Button oder der Popup-Button auf der Videodetailseite gedrückt und gehalten wird</string> <string name="show_hold_to_append_summary">Tipp anzeigen, wenn der Hintergrundwiedergabe- oder Pop-up-Button auf der Videodetailseite gedrücktgehalten wird</string>
<string name="background_player_append">In der Warteschlange bei der Hintergrundwiedergabe</string> <string name="background_player_append">In der Warteschlange der Hintergrundwiedergabe</string>
<string name="new_and_hot">Neu und brandheiß</string> <string name="new_and_hot">Neu und brandheiß</string>
<string name="hold_to_append">Halten, um in die Warteschlange einzureihen</string> <string name="hold_to_append">Halten zum Hinzufügen zur Warteschleife</string>
<string name="show_hold_to_append_title">Gedrückt halten, um Tipp hinzuzufügen anzeigen</string>
<string name="unknown_content">[Unbekannt]</string>
<string name="enqueue_on_background">In Warteschlange für Hintergrundwiedergabe</string>
<string name="enqueue_on_popup">In Warteschlange für Pop-up</string>
<string name="start_here_on_main">Ab hier wiedergeben</string>
<string name="start_here_on_background">Ab hier im Hintergrundmodus</string>
<string name="start_here_on_popup">Ab hier im Pop-up</string>
</resources> </resources>

View File

@ -96,7 +96,7 @@
<string name="report_error">Reportar un error</string> <string name="report_error">Reportar un error</string>
<string name="user_report">Reporte de usuario</string> <string name="user_report">Reporte de usuario</string>
<string name="video">Vídeos</string> <string name="video">Vídeo</string>
<string name="audio">Audio</string> <string name="audio">Audio</string>
<string name="retry">Reintentar</string> <string name="retry">Reintentar</string>
@ -287,4 +287,14 @@ abrir en modo popup</string>
<string name="play_queue_remove">Remover</string> <string name="play_queue_remove">Remover</string>
<string name="play_queue_stream_detail">Detalles</string> <string name="play_queue_stream_detail">Detalles</string>
<string name="play_queue_audio_settings">Ajustes de audio</string> <string name="play_queue_audio_settings">Ajustes de audio</string>
<string name="unknown_content">[Desconocido]</string>
<string name="enqueue_on_background">Poner en cola de segundo plano</string>
<string name="enqueue_on_popup">Poner en cola de popup</string>
<string name="start_here_on_main">Comenzar a reproducir aquí</string>
<string name="start_here_on_background">Comenzar aquí en segundo plano</string>
<string name="start_here_on_popup">Comenzar aquí en popup</string>
<string name="show_hold_to_append_title">Desplegar consejo \"Mantener para poner en la fila\"</string>
<string name="new_and_hot">Nuevo y popular</string>
<string name="hold_to_append">Mantener para poner en la fila</string>
</resources> </resources>

View File

@ -185,7 +185,7 @@
<string name="finish">OK</string> <string name="finish">OK</string>
<string name="msg_name">Tiedostonimi</string> <string name="msg_name">Tiedostonimi</string>
<string name="msg_threads">Viestiketjut</string> <string name="msg_threads">Säikeet</string>
<string name="msg_error">Virhe</string> <string name="msg_error">Virhe</string>
<string name="msg_server_unsupported">Serveriä ei tueta</string> <string name="msg_server_unsupported">Serveriä ei tueta</string>
<string name="msg_exists">Tiedosto on jo olemassa</string> <string name="msg_exists">Tiedosto on jo olemassa</string>
@ -268,4 +268,11 @@
<string name="play_queue_audio_settings">Ääniasetukset</string> <string name="play_queue_audio_settings">Ääniasetukset</string>
<string name="hold_to_append">Pidä pohjassa lisätäksesi jonoon</string> <string name="hold_to_append">Pidä pohjassa lisätäksesi jonoon</string>
<string name="show_hold_to_append_title">Näytä vihje soittolistalle</string> <string name="show_hold_to_append_title">Näytä vihje soittolistalle</string>
</resources> <string name="unknown_content">[Tuntematon]</string>
<string name="enqueue_on_background">Lisää taustatoistojonoon</string>
<string name="enqueue_on_popup">Lisää ikkunajonoon</string>
<string name="start_here_on_main">Aloita toistaminen</string>
<string name="start_here_on_background">Aloita toisto taustalla</string>
<string name="start_here_on_popup">Aloita toisto ikkunassa</string>
</resources>

View File

@ -1,7 +1,7 @@
<?xml version='1.0' encoding='UTF-8'?> <?xml version='1.0' encoding='UTF-8'?>
<resources> <resources>
<string name="cancel">Annuler</string> <string name="cancel">Annuler</string>
<string name="choose_browser">Choisir un navigateur </string> <string name="choose_browser">Choisir un navigateur</string>
<string name="default_resolution_title">Définition par défaut</string> <string name="default_resolution_title">Définition par défaut</string>
<string name="did_you_mean">Vouliez-vous dire : %1$s ?</string> <string name="did_you_mean">Vouliez-vous dire : %1$s ?</string>
<string name="download">Télécharger</string> <string name="download">Télécharger</string>
@ -18,7 +18,7 @@
<string name="settings">Paramètres</string> <string name="settings">Paramètres</string>
<string name="share">Partager</string> <string name="share">Partager</string>
<string name="share_dialog_title">Partager avec</string> <string name="share_dialog_title">Partager avec</string>
<string name="show_play_with_kodi_summary">Afficher une option pour lire la vidéo via Kodi</string> <string name="show_play_with_kodi_summary">Afficher une option pour lire la vidéo avec Kodi</string>
<string name="show_play_with_kodi_title">Afficher loption «Lire avec Kodi»</string> <string name="show_play_with_kodi_title">Afficher loption «Lire avec Kodi»</string>
<string name="upload_date_text">Ajoutée le %1$s</string> <string name="upload_date_text">Ajoutée le %1$s</string>
<string name="view_count_text">%1$s vues</string> <string name="view_count_text">%1$s vues</string>
@ -37,7 +37,7 @@
<string name="detail_thumbnail_view_description">Miniature daperçu vidéo</string> <string name="detail_thumbnail_view_description">Miniature daperçu vidéo</string>
<string name="detail_dislikes_img_view_description">Je naime pas</string> <string name="detail_dislikes_img_view_description">Je naime pas</string>
<string name="detail_likes_img_view_description">Jaime</string> <string name="detail_likes_img_view_description">Jaime</string>
<string name="search_language_title">Langue préférée du contenu</string> <string name="search_language_title">Langue par défaut du contenu</string>
<string name="detail_uploader_thumbnail_view_description">Miniature de lavatar de lutilisateur</string> <string name="detail_uploader_thumbnail_view_description">Miniature de lavatar de lutilisateur</string>
<string name="use_external_video_player_title">Utiliser un lecteur vidéo externe</string> <string name="use_external_video_player_title">Utiliser un lecteur vidéo externe</string>
<string name="use_external_audio_player_title">Utiliser un lecteur audio externe</string> <string name="use_external_audio_player_title">Utiliser un lecteur audio externe</string>
@ -153,7 +153,7 @@
<string name="controls_popup_title">Lecture flottante</string> <string name="controls_popup_title">Lecture flottante</string>
<string name="default_popup_resolution_title">Résolution de la lecture flottante</string> <string name="default_popup_resolution_title">Résolution de la lecture flottante</string>
<string name="show_higher_resolutions_title">Afficher des résolutions plus grandes</string> <string name="show_higher_resolutions_title">Afficher des résolutions plus élevées</string>
<string name="show_higher_resolutions_summary">Seulement certains périphériques supportent la lecture 2K/4K</string> <string name="show_higher_resolutions_summary">Seulement certains périphériques supportent la lecture 2K/4K</string>
<string name="default_video_format_title">Format vidéo par défaut</string> <string name="default_video_format_title">Format vidéo par défaut</string>
<string name="popup_remember_size_pos_title">Mémoriser la taille et la position du lecteur flottant</string> <string name="popup_remember_size_pos_title">Mémoriser la taille et la position du lecteur flottant</string>
@ -233,7 +233,7 @@
<string name="notification_channel_description">Notifications pour les lecteurs arrière-plan et flottant de NewPipe</string> <string name="notification_channel_description">Notifications pour les lecteurs arrière-plan et flottant de NewPipe</string>
<string name="search_no_results">Aucun résultat</string> <string name="search_no_results">Aucun résultat</string>
<string name="empty_subscription_feed_subtitle">Rien ici à part des grillons</string> <string name="empty_subscription_feed_subtitle">Rien à voir ici</string>
<string name="no_subscribers">Aucun abonné</string> <string name="no_subscribers">Aucun abonné</string>
<plurals name="subscribers"> <plurals name="subscribers">
@ -257,9 +257,9 @@
<string name="item_deleted">Objet effacé</string> <string name="item_deleted">Objet effacé</string>
<string name="delete_item_search_history">Voulez-vous supprimer cet élément de l\'historique de recherche ?</string> <string name="delete_item_search_history">Voulez-vous supprimer cet élément de l\'historique de recherche ?</string>
<string name="main_page_content">Contentu</string> <string name="main_page_content">Contenu de la page principale</string>
<string name="blank_page_summary">Page vide</string> <string name="blank_page_summary">Page vide</string>
<string name="subscription_page_summary">Page de souscription</string> <string name="subscription_page_summary">Abonnements</string>
<string name="feed_page_summary">Page de Flux</string> <string name="feed_page_summary">Page de Flux</string>
<string name="channel_page_summary">Page de la chaîne</string> <string name="channel_page_summary">Page de la chaîne</string>
<string name="select_a_channel">Sélectionnez une chaîne</string> <string name="select_a_channel">Sélectionnez une chaîne</string>
@ -278,4 +278,20 @@
<string name="play_queue_remove">Retirer</string> <string name="play_queue_remove">Retirer</string>
<string name="play_queue_stream_detail">Détails</string> <string name="play_queue_stream_detail">Détails</string>
<string name="play_queue_audio_settings">Réglages audio</string> <string name="play_queue_audio_settings">Réglages audio</string>
</resources> <string name="show_hold_to_append_title">Afficher l\'aide \"Appui long pour mettre en file d\'attente\"</string>
<string name="show_hold_to_append_summary">Afficher l\'aide lors de l\'appui du bouton de fond ou de lecteur flottant sur la page de détails d\'une vidéo</string>
<string name="unknown_content">[Inconnu]</string>
<string name="player_recoverable_failure">Récupération de l\'erreur du lecteur</string>
<string name="kiosk_page_summary">Page Kiosque</string>
<string name="select_a_kiosk">Sélectionner un kiosque</string>
<string name="kiosk">Kiosque</string>
<string name="hold_to_append">Appui long pour mettre en file d\'attente</string>
<string name="enqueue_on_background">Mettre en file d\'attente en fond</string>
<string name="enqueue_on_popup">Mettre en file d\'attente en lecture flottante</string>
<string name="start_here_on_main">Démarrer ici</string>
<string name="start_here_on_background">Démarrer ici en fond</string>
<string name="start_here_on_popup">Démarrer ici en lecteur flottant</string>
</resources>

View File

@ -2,7 +2,7 @@
<resources> <resources>
<string name="view_count_text">%1$s दृश्य</string> <string name="view_count_text">%1$s दृश्य</string>
<string name="upload_date_text">%1$s को प्रकाशित</string> <string name="upload_date_text">%1$s को प्रकाशित</string>
<string name="no_player_found">वीडियो दिखान के लिए एप्लिकेशन नहीं मिला. क्या आप VLC इंस्टॉल करना चाहते हैं?</string> <string name="no_player_found">वीडियो दिखान के लिए एप्लिकेशन नहीं मिला. क्या आप VLC इंस्टॉल करना चाहते हैं ?</string>
<string name="install">इंस्टॉल करें</string> <string name="install">इंस्टॉल करें</string>
<string name="open_in_browser">ब्राउज़र में खोलें</string> <string name="open_in_browser">ब्राउज़र में खोलें</string>
<string name="open_in_popup_mode">पॉपअप मोड में खोलें</string> <string name="open_in_popup_mode">पॉपअप मोड में खोलें</string>
@ -11,15 +11,15 @@
<string name="search">खोज</string> <string name="search">खोज</string>
<string name="settings">सेटिंग</string> <string name="settings">सेटिंग</string>
<string name="choose_browser">ब्राउज़र चुनें</string> <string name="choose_browser">ब्राउज़र चुनें</string>
<string name="screen_rotation">रोटेशन</string> <string name="screen_rotation">स्क्रीन रोटेशन</string>
<string name="popup_mode_share_menu_title">[NEWPIPE] पॉपअप मोड</string> <string name="popup_mode_share_menu_title">NewPipe पॉपअप मोड</string>
<string name="subscribe_button_title">सदस्य बनें</string> <string name="subscribe_button_title">सदस्य बनें</string>
<string name="subscribed_button_title">सदस्यता ली</string> <string name="subscribed_button_title">सदस्यता ली</string>
<string name="channel_unsubscribed">सदस्यता निकाली गई</string> <string name="channel_unsubscribed">सदस्यता निकाली गई</string>
<string name="tab_subscriptions">सदस्यता</string> <string name="tab_subscriptions">सदस्यता</string>
<string name="controls_background_title">पीछे</string> <string name="controls_background_title">पीछे</string>
<string name="controls_popup_title">पॉप अप</string> <string name="controls_popup_title">पॉपअप</string>
<string name="autoplay_by_calling_app_title">स्वतः चलाएं</string> <string name="autoplay_by_calling_app_title">स्वतः चलाएं</string>
<string name="play_audio">ऑडियो</string> <string name="play_audio">ऑडियो</string>
@ -30,32 +30,250 @@
<string name="next_video_title">अगला वीडियो</string> <string name="next_video_title">अगला वीडियो</string>
<string name="settings_category_video_audio_title">वीडियो और ऑडियो</string> <string name="settings_category_video_audio_title">वीडियो और ऑडियो</string>
<string name="settings_category_history_title">इतिहास</string> <string name="settings_category_history_title">इतिहास</string>
<string name="background_player_playing_toast">पीछे चल रहा है</string> <string name="background_player_playing_toast">बैकग्राउंड में चल रहा है</string>
<string name="no_views">कोई दृश्य नहीं</string> <string name="no_views">कोई दर्शक नहीं</string>
<string name="no_videos">कोई वीडियो नहीं है</string> <string name="no_videos">कोई वीडियो नहीं है</string>
<string name="title_activity_about">[NewPipe] बारे में</string> <string name="title_activity_about">न्यूपाइप के बारे में जाने</string>
<string name="action_settings">सेटिंग</string> <string name="action_settings">सेटिंग</string>
<string name="action_about">इसके बारे में</string> <string name="action_about">इसके बारे में</string>
<string name="title_licenses">तृतीय पक्ष लाइसेंस</string> <string name="title_licenses">तृतीय-पक्ष लाइसेंस</string>
<string name="action_open_website">वेबसाइट खोलें</string> <string name="action_open_website">वेबसाइट खोलें</string>
<string name="tab_about">इसके बारे में</string> <string name="tab_about">इसके बारे में</string>
<string name="tab_licenses">लाइसेंस</string> <string name="tab_licenses">लाइसेंस</string>
<string name="view_on_github">GitHub में देखें</string> <string name="view_on_github">GitHub में देखें</string>
<string name="app_license_title">[NewPipe] का लाइसेंस</string> <string name="app_license_title">न्यूपाइप का लाइसेंस</string>
<string name="read_full_license">लाइसेंस पढ़ें</string> <string name="read_full_license">लाइसेंस पढ़ें</string>
<string name="contribution_title">योगदान</string> <string name="contribution_title">योगदान</string>
<string name="title_activity_history">इतिहास</string> <string name="title_activity_history">इतिहास</string>
<string name="title_history_search">कोज चुके</string> <string name="title_history_search">खोजा जा चूका है</string>
<string name="title_history_view">देखा वीडियो</string> <string name="title_history_view">विडियो जो देख लिए गए है</string>
<string name="history_disabled">इतिहास अक्षम है</string> <string name="history_disabled">इतिहास disable है</string>
<string name="action_history">इतिहास</string> <string name="action_history">इतिहास</string>
<string name="history_empty">इतिहास खाली है</string> <string name="history_empty">इतिहास खाली है</string>
<string name="history_cleared">इतिहास स्पष्ट हो चुकी है</string> <string name="history_cleared">इतिहास साफ़ हो चुकी है</string>
<string name="item_deleted">मद हटाया गया</string> <string name="item_deleted">Item हटा दिया गया है</string>
<string name="trending">चर्चित</string> <string name="trending">फ़िलहाल चर्चा में है</string>
<string name="play_queue_audio_settings">ऑडियो सेटिंग</string> <string name="play_queue_audio_settings">ऑडियो सेटिंग</string>
<string name="main_bg_subtitle">खोज चिह्न दबाके आरंम्भ करे</string> <string name="main_bg_subtitle">खोज चिह्न दबाके आरंम्भ करे</string>
<string name="cancel">रद्द करें</string> <string name="cancel">रद्द करें</string>
<string name="did_you_mean">क्या आप का मतलब %1$s है?</string> <string name="did_you_mean">क्या आप का मतलब %1$s है?</string>
</resources> <string name="share_dialog_title">शेयर करें</string>
<string name="use_external_video_player_title">अन्य वीडियो प्लेयर इस्तेमाल करे</string>
<string name="use_external_video_player_summary">यह विकल्प चुनने से कुछ वीडियो से ध्वनि नहीं आएगी</string>
<string name="use_external_audio_player_title">अन्य ऑडियो प्लेयर इस्तमाल करे</string>
<string name="tab_main">मुख्य</string>
<string name="subscription_change_failed">सदस्यता को बदलने में असमर्थ है</string>
<string name="subscription_update_failed">सदस्यता को अपडेट करने में असमर्थ है</string>
<string name="fragment_whats_new">देखे की नया क्या है</string>
<string name="download_path_title">विडियो को डाउनलोड करने के लिए फाइल की जगह</string>
<string name="download_path_summary">डाउनलोड किये गए विडियो को फाइल में रखने की जगह</string>
<string name="download_path_dialog_title">विडियो के लिए डाउनलोड करने की फाइल की जगह डाले</string>
<string name="download_path_audio_title">ऑडियो डाउनलोड का फाइल की जगह</string>
<string name="download_path_audio_summary">डाउनलोड किये गए ऑडियो की फाइल की जगह</string>
<string name="download_path_audio_dialog_title">ऑडियो डाउनलोड करने के लिए फाइल की जगह डाले</string>
<string name="autoplay_by_calling_app_summary">जब न्यू पाइप को दुसरे एप्प से बुलावा आये तो विडियो अपनी स्वेछा से चले</string>
<string name="default_resolution_title">डिफ़ॉल्ट वीडियो की क्वालिटी</string>
<string name="default_popup_resolution_title">डिफ़ॉल्ट विडियो पॉपअप की क्वालिटी</string>
<string name="show_higher_resolutions_title">विडियो की उच्त्तर क्वालिटी देखे</string>
<string name="show_higher_resolutions_summary">केवल कुछ ही फ़ोन है जो 2K/4K विडियो प्ले होते है</string>
<string name="play_with_kodi_title">kodi से चलाये</string>
<string name="kore_not_found">kore एप्प नहीं मिला,क्या आपके फ़ोन में इनस्टॉल है ?</string>
<string name="show_play_with_kodi_title">\"Kodi से चलाये\" वाला विकल्प दिखाए</string>
<string name="show_play_with_kodi_summary">उस विकल्प को दिखाए जिसमे Kodi Media Center के जरिये विडियो प्ले किया जाता है</string>
<string name="default_audio_format_title">डिफ़ॉल्ट ऑडियो का फॉर्मेट</string>
<string name="default_video_format_title">डिफ़ॉल्ट विडियो का फॉर्मेट</string>
<string name="webm_description">webm - फ्री फॉर्मेट</string>
<string name="m4a_description">M4A - बेहतर क्वालिटी</string>
<string name="theme_title">एप्प का नया रूप</string>
<string name="dark_theme_title">काला</string>
<string name="popup_remember_size_pos_title">विडियो पॉपअप की आकर और उसकी स्थति को याद रखे</string>
<string name="popup_remember_size_pos_summary">विडियो पॉपअप के पहले वाली आकर और उसकी स्थिति को याद रखे</string>
<string name="player_gesture_controls_title">विडियो प्लेयर के gesture कण्ट्रोल</string>
<string name="player_gesture_controls_summary">विडियो प्लेयर की ब्राइटनेस और ध्वनी को नियंत्रण के लिए फ़ोन में gesture से इशारो का प्रयोग करे</string>
<string name="show_search_suggestions_title">खोज के सुझाव देखे</string>
<string name="show_search_suggestions_summary">जब कुछ ढूंड रहे हो तो सुझाव दिखाये</string>
<string name="enable_search_history_title">खोज के इतिहास को देखे</string>
<string name="enable_search_history_summary">खोज के query को फ़ोन की मेमोरी में ही रखे</string>
<string name="enable_watch_history_summary">जो विडियो देखे है उस पर नजर रखे</string>
<string name="resume_on_audio_focus_gain_title">जब फोकस मिले तो विडियो resume हो</string>
<string name="resume_on_audio_focus_gain_summary">रूकावट आने पर भी विडियो को जारी रखे (जैसे - फ़ोन कॉल आये)</string>
<string name="show_next_and_similar_title">आगे ऐसा ही विडियो दिखाए जैसा पहले देखा था</string>
<string name="show_hold_to_append_title">tip को विस्तार करने के hold को दिखाए</string>
<string name="show_hold_to_append_summary">जब बैकग्राउंड और पॉपअप बटन विडियो के विवरण पन्ने में दबाई जाए तो tip को दिखाए</string>
<string name="url_not_supported_toast">ये वाला URL इसमें नहीं चलेगा</string>
<string name="search_language_title">डिफ़ॉल्ट विषय की भाषा</string>
<string name="settings_category_player_title">प्लेयर</string>
<string name="settings_category_player_behavior_title">चाल चलन</string>
<string name="settings_category_popup_title">पॉपअप</string>
<string name="settings_category_appearance_title">दिखावट</string>
<string name="settings_category_other_title">और दुसरे</string>
<string name="popup_playing_toast">विडियो पॉपअप के अंदाज में चल रहा</string>
<string name="background_player_append">बैकग्राउंड प्लेयर की कतार पर</string>
<string name="popup_playing_append">पॉपअप प्लेयर की कतार पर</string>
<string name="play_btn_text">चलाये</string>
<string name="content">विषयवस्तु</string>
<string name="show_age_restricted_content_title">उम्र प्रतिबंदित से सम्बंधित विषयवस्तु दिखाये (जैसे - हिंसात्मक,कामोतोजनक)</string>
<string name="video_is_age_restricted">उम्र प्रतिबंदित विडियो है .इस प्रकार की विषयवस्तु को अनुमति देने के लिए Setting से संभव है |</string>
<string name="duration_live">सीधा प्रसारण</string>
<string name="downloads">डाउनलोड</string>
<string name="downloads_title">डाउनलोड</string>
<string name="error_report_title">त्रुटी की रिपोर्ट</string>
<string name="all">सारे</string>
<string name="channel">चैनल</string>
<string name="playlist">प्लेलिस्ट</string>
<string name="yes">सहमत हूँ</string>
<string name="later">बाद में</string>
<string name="disabled">बंद करे</string>
<string name="filter">छलनी</string>
<string name="refresh">तरोताजा</string>
<string name="clear">साफ़</string>
<string name="popup_resizing_indicator_title">आकार को छोटा-बड़ा करे</string>
<string name="best_resolution">बेहतर विडियो की क्वालिटी</string>
<string name="undo">वापस जाए</string>
<string name="play_all">सारे प्ले करे</string>
<string name="notification_channel_name">NewPipe की अधिसूचना</string>
<string name="notification_channel_description">NewPipe के बैकग्राउंड में चल रहे विडियो और पॉपअप विडियो के लिए अधिसूचना</string>
<string name="unknown_content">[नहीं जानते]</string>
<string name="general_error">त्रुटी</string>
<string name="network_error">नेटवर्क में त्रुटी</string>
<string name="could_not_load_thumbnails">सारे thumbnail(फोटो जो फ़ोन की मेमोरी में है ) भरे नहीं जा सकते</string>
<string name="youtube_signature_decryption_error">विडियो के URL signature को decrypt नहीं कर सकते</string>
<string name="parsing_error">इस website का निरंक्षण नहीं कर सकते</string>
<string name="light_parsing_error">website का पूरी तरह से निरंक्षण नहीं हो सकता</string>
<string name="content_not_available">विषयवस्तु उपलब्ध नहीं है</string>
<string name="blocked_by_gema">GEMA ने block किया है</string>
<string name="could_not_setup_download_menu">डाउनलोड मेनू को स्थापित नहीं कर सकते</string>
<string name="live_streams_not_supported">यह सीधा प्रसारण है, जो की अभी पूरी तरह से काम नहीं कर रहा.</string>
<string name="could_not_get_stream">कोई भी विडियो नहीं मिल रहा</string>
<string name="could_not_load_image">फोटो को load नहीं कर सकते</string>
<string name="app_ui_crash">APP/UI टूट गया</string>
<string name="player_stream_failure">इस विडियो को चलाने में असफलता हो रही है</string>
<string name="player_unrecoverable_failure">कभी ठीक न होने वाले विडियो प्लेयर की त्रुटी आ रही है</string>
<string name="player_recoverable_failure">विडियो प्लेयर त्रुटी से ठीक हो रहा है</string>
<string name="sorry_string">खेद है की, ऐसा होना नहीं चाहिए था.</string>
<string name="error_report_button_text">त्रुटी की रिपोर्ट को ईमेल से भेजे</string>
<string name="error_snackbar_message">माफ़ करे , कुछ त्रुटियाँ हो रही है</string>
<string name="error_snackbar_action">रिपोर्ट</string>
<string name="what_device_headline">जानकारी:</string>
<string name="what_happened_headline">क्या हुआ:</string>
<string name="info_labels">क्या:\\nमांग:\\nविषयवस्तु की भाषा:\\nसेवा:\\nजीएमटी समय:\\nपैकेज:\\nसंस्करण:\\nOS संस्करण:\\nGLOB. IP रेंज:</string>
<string name="your_comment">आपकी टिप्पणी:</string>
<string name="error_details_headline">विवरण:</string>
<string name="list_thumbnail_view_description">विडियो के thumbnail के पूर्व दर्शन</string>
<string name="detail_thumbnail_view_description">विडियो के thumbnail के पूर्व दर्शन</string>
<string name="detail_uploader_thumbnail_view_description">अपलोडर के thumbnail वाले फोटो</string>
<string name="detail_likes_img_view_description">पसंद</string>
<string name="detail_dislikes_img_view_description">नापसंद</string>
<string name="use_tor_title">Tor का प्रयोग करे</string>
<string name="use_tor_summary">( प्रयोगात्मक ) व्यक्तिगत सुरक्षा को बढाने के लिए Tor के जरिये ट्रैफिक को डाउनलोड करने के लिए जोर दे ( विडियो को स्ट्रीम अभी नहीं कर सकता ).</string>
<string name="report_error">त्रुटी के रिपोर्ट करे</string>
<string name="user_report">उपयोगकर्ता की रिपोर्ट</string>
<string name="search_no_results">कोई परिणाम नहीं मिला</string>
<string name="empty_subscription_feed_subtitle">यंहा कुछ नहीं है</string>
<string name="err_dir_create">"डाउनलोड वाली डायरेक्टरी को बना नहीं सकते \'%1$s\'"</string>
<string name="info_dir_created">डाउनलोड की डायरेक्टरी बन चुकी है \'%1$s\'</string>
<string name="video">विडियो</string>
<string name="audio">ऑडियो</string>
<string name="retry">फिर से कोशिश करे</string>
<string name="storage_permission_denied">स्टोरेज में प्रवेश के लिए अनुमति नहीं मिली</string>
<string name="use_old_player_title">पुराना विडियो प्लेयर प्रयोग करे</string>
<string name="use_old_player_summary">पुराना Mediaframework Player जो फ़ोन में बना हुआ है</string>
<string name="short_thousand">हज़ार</string>
<string name="short_million">करोड़</string>
<string name="short_billion">अरब</string>
<string name="no_subscribers">कोई भी सदस्य नहीं है</string>
<plurals name="subscribers">
<item quantity="one">%s सदस्य</item>
<item quantity="other">%s सदस्यो</item>
</plurals>
<plurals name="views">
<item quantity="one">%s दर्शक</item>
<item quantity="other">%s दर्शके</item>
</plurals>
<plurals name="videos">
<item quantity="one">%s विडियो</item>
<item quantity="other">%s वीडियो</item>
</plurals>
<string name="start">शुरू</string>
<string name="pause">रोके</string>
<string name="view">चलाये</string>
<string name="delete">मिटाए</string>
<string name="checksum">checksum</string>
<string name="add">नया मिशन</string>
<string name="finish">ठीक है</string>
<string name="msg_name">फाइल का नाम</string>
<string name="msg_threads">मेसेज के thread</string>
<string name="msg_error">त्रुटी</string>
<string name="msg_server_unsupported">server ठीक से चल नहीं रहा</string>
<string name="msg_exists">फाइल पहले से ही बनी हुई है</string>
<string name="msg_url_malform">URL सही नहीं है या फिर हो सकता है इन्टरनेट उपलब्ध नहीं है</string>
<string name="msg_running">न्यूपाइप डाउनलोड हो रहा है</string>
<string name="msg_running_detail">विवरण देखने के लिए दबाये</string>
<string name="msg_wait">कृपया इंतज़ार करे…</string>
<string name="msg_copied">क्लिपबोर्ड पर कॉपी हो गया है</string>
<string name="no_available_dir">कृपया उपलब्ध डाउनलोड फोल्डर को चुने</string>
<string name="msg_popup_permission">पॉपअप के तरीके में खोलने के लिए अनुमति की जरुरत है</string>
<string name="reCaptchaActivity">reCAPTCHA</string>
<string name="reCaptcha_title">reCAPTCHA चुनोती</string>
<string name="recaptcha_request_toast">reCAPTCHA चुनोती के लिए निवेदन</string>
<string name="settings_category_downloads_title">डाउनलोड</string>
<string name="settings_file_charset_title">फाइल के नाम के लिए आवश्यक characters(जैसे - १२३, abc) की अनुमति है</string>
<string name="settings_file_replacement_character_summary">अमान्य characters इस value से बदल जायेंगे</string>
<string name="settings_file_replacement_character_title">रिप्लेसमेंट करैक्टर</string>
<string name="charset_letters_and_digits">वर्ण और अंक</string>
<string name="charset_most_special_characters">विशेष वर्ण</string>
<string name="copyright" formatted="true">%2$s के द्वारा © %1$s जो %3$s के अधीन आते है</string>
<string name="error_unable_to_load_license">लाइसेंस load नहीं हो रहा</string>
<string name="tab_contributors">सहयोगकर्ता</string>
<string name="app_description">एंड्राइड के लिए कम मेमोरी वाला और मुफ्त फ्रंट एंड Youtube.</string>
<string name="contribution_encouragement">अगर आपके पास कोई सुझाव हो जैसे -अनुवाद , डिजाईन में बदलाव ,code को साफ़ रखना , या फिर code में जायदा बदलाव लाना हो तो - साहयता के लिए आपका स्वागत है . जितना ज्यादा होगा उतना बेहतर होगा !</string>
<string name="delete_item_search_history">क्या आप इसको खोज इतिहास के मिटाना चाहते है ?</string>
<string name="main_page_content">मुख्य पेज की विषयवस्तु</string>
<string name="blank_page_summary">खाली पन्ना</string>
<string name="kiosk_page_summary">kiosk पन्ना</string>
<string name="subscription_page_summary">सदस्यता वाला पन्ना</string>
<string name="feed_page_summary">फीड वाला पेज</string>
<string name="channel_page_summary">चैनल वाला पन्ना</string>
<string name="select_a_channel">चैनल को चुने</string>
<string name="no_channel_subscribed_yet">अभी तक किसी भी चैनल के सदस्य नहीं है</string>
<string name="select_a_kiosk">kiosk को चुने</string>
<string name="kiosk">kiosk</string>
<string name="top_50">टॉप 50</string>
<string name="new_and_hot">नया और गरम</string>
<string name="title_activity_background_player">बैकग्राउंड प्लेयर</string>
<string name="title_activity_popup_player">पॉपअप प्लेयर</string>
<string name="play_queue_remove">निकाले</string>
<string name="play_queue_stream_detail">विवरण</string>
<string name="hold_to_append">कतार में खड़ा करे</string>
<string name="enqueue_on_background">कतार को बैकग्राउंड में लगाये</string>
<string name="enqueue_on_popup">कतार को पॉपअप में लगाये</string>
<string name="start_here_on_main">यंहा से चलाना शुरू करे</string>
<string name="start_here_on_background">बैकग्राउंड में चलाना शुरू करे</string>
<string name="start_here_on_popup">पॉपअप में चलाना शुरू करे</string>
</resources>

View File

@ -41,7 +41,7 @@
<string name="download_path_audio_summary">Put za spremanje preuzetog zvuka u</string> <string name="download_path_audio_summary">Put za spremanje preuzetog zvuka u</string>
<string name="download_path_audio_dialog_title">Unesi put za preuzimanje zvučne datoteke</string> <string name="download_path_audio_dialog_title">Unesi put za preuzimanje zvučne datoteke</string>
<string name="autoplay_by_calling_app_title">Automatski reprod. kada je NewPipe pozvan iz druge aplikacije</string> <string name="autoplay_by_calling_app_title">Auto. reprod. kada je NewPipe pozvan iz druge apl.</string>
<string name="autoplay_by_calling_app_summary">Automatski reproduciraj videozapis kad je NewPipe pozvan iz druge aplikacije</string> <string name="autoplay_by_calling_app_summary">Automatski reproduciraj videozapis kad je NewPipe pozvan iz druge aplikacije</string>
<string name="default_resolution_title">Zadana razlučivost</string> <string name="default_resolution_title">Zadana razlučivost</string>
<string name="default_popup_resolution_title">Zadana razlučivost skočnog prozora</string> <string name="default_popup_resolution_title">Zadana razlučivost skočnog prozora</string>
@ -49,7 +49,7 @@
<string name="show_higher_resolutions_summary">Samo neki uređaji podržavaju reprodukciju 2K/4K videozapisa</string> <string name="show_higher_resolutions_summary">Samo neki uređaji podržavaju reprodukciju 2K/4K videozapisa</string>
<string name="play_with_kodi_title">Reproduciraj sa Kodijem</string> <string name="play_with_kodi_title">Reproduciraj sa Kodijem</string>
<string name="kore_not_found">Kore aplikacija nije pronađena. Želite li ju instalirati?</string> <string name="kore_not_found">Kore aplikacija nije pronađena. Želite li ju instalirati?</string>
<string name="show_play_with_kodi_title">Prikaži \"Reproduciraj putem Kodija\" opciju</string> <string name="show_play_with_kodi_title">Prikaži opciju \"Reproduciraj putem Kodija\"</string>
<string name="show_play_with_kodi_summary">Prikaži opciju za reproduciranje videozapisa putem Kodija</string> <string name="show_play_with_kodi_summary">Prikaži opciju za reproduciranje videozapisa putem Kodija</string>
<string name="play_audio">Zvuk</string> <string name="play_audio">Zvuk</string>
<string name="default_audio_format_title">Zadani format zvuka</string> <string name="default_audio_format_title">Zadani format zvuka</string>
@ -255,4 +255,29 @@
<string name="player_unrecoverable_failure">Dogodila se neoporavljiva pogreška reproduktora</string> <string name="player_unrecoverable_failure">Dogodila se neoporavljiva pogreška reproduktora</string>
<string name="player_recoverable_failure">Oporavljanje od pogreške reproduktora</string> <string name="player_recoverable_failure">Oporavljanje od pogreške reproduktora</string>
</resources> <string name="show_hold_to_append_title">Prikaži savjet za držanje</string>
<string name="show_hold_to_append_summary">Prikažite savjet kada je pritisnut gumb za pozadinsku ili skočnu reprodukciju na stranici detalja videozapisa</string>
<string name="popup_playing_append">U redu čekanja za skočnu reprodukciju</string>
<string name="delete_item_search_history">Želite li izbrisati ovu stavku iz povijesti pretraživanja?</string>
<string name="main_page_content">Sadržaj</string>
<string name="blank_page_summary">Prazna stranica</string>
<string name="kiosk_page_summary">Kiosk stranica</string>
<string name="subscription_page_summary">Pretplate</string>
<string name="feed_page_summary">Novosti</string>
<string name="channel_page_summary">Kanal</string>
<string name="select_a_channel">Odaberite kanal</string>
<string name="no_channel_subscribed_yet">Niste pretplaćeni na nijedan kanal</string>
<string name="select_a_kiosk">Odaberite kiosk</string>
<string name="kiosk">Nazivi kioska</string>
<string name="trending">U trendu</string>
<string name="top_50">Top 50</string>
<string name="new_and_hot">Novo i popularno</string>
<string name="title_activity_background_player">Red čekanja</string>
<string name="title_activity_popup_player">Skočni reproduktor</string>
<string name="play_queue_remove">Ukloni</string>
<string name="play_queue_stream_detail">Detalji</string>
<string name="play_queue_audio_settings">Postavke zvuka</string>
<string name="hold_to_append">Zadržite za dodavanje u red čekanja</string>
</resources>

View File

@ -281,7 +281,7 @@
<string name="new_and_hot">Nuovi e caldi</string> <string name="new_and_hot">Nuovi e caldi</string>
<string name="show_hold_to_append_title">Mostra il suggerimento di tenere premuto per appendere</string> <string name="show_hold_to_append_title">Mostra il suggerimento di tenere premuto per appendere</string>
<string name="show_hold_to_append_summary">Mostra un suggerimento quando il pulsante in sottofondo o a comparsa viene premuto nella pagina dei dettagli di un video</string> <string name="show_hold_to_append_summary">Mostra un suggerimento quando il pulsante in sottofondo o a comparsa viene premuto nella pagina dei dettagli di un video</string>
<string name="background_player_append">In coda al riproduttore in sottofondo</string> <string name="background_player_append">In coda sul lettore di sfondo</string>
<string name="popup_playing_append">In coda al riproduttore a comparsa</string> <string name="popup_playing_append">In coda al riproduttore a comparsa</string>
<string name="play_all">Riproduci tutto</string> <string name="play_all">Riproduci tutto</string>
@ -295,4 +295,11 @@
<string name="play_queue_stream_detail">Dettagli</string> <string name="play_queue_stream_detail">Dettagli</string>
<string name="play_queue_audio_settings">Impostazioni audio</string> <string name="play_queue_audio_settings">Impostazioni audio</string>
<string name="hold_to_append">Tieni premuto per accodare</string> <string name="hold_to_append">Tieni premuto per accodare</string>
<string name="unknown_content">[Sconosciuto]</string>
<string name="enqueue_on_background">In coda sullo sfondo</string>
<string name="enqueue_on_popup">In coda nel Popup</string>
<string name="start_here_on_main">Inizia la riproduzione qui</string>
<string name="start_here_on_background">Inizia qui sullo sfondo</string>
<string name="start_here_on_popup">Inizia qui nel Popup</string>
</resources> </resources>

View File

@ -274,4 +274,21 @@
<string name="play_queue_stream_detail">Detaljer</string> <string name="play_queue_stream_detail">Detaljer</string>
<string name="play_queue_audio_settings">Lydinnstillinger</string> <string name="play_queue_audio_settings">Lydinnstillinger</string>
<string name="hold_to_append">Hold for å legge i kø</string> <string name="hold_to_append">Hold for å legge i kø</string>
<string name="show_hold_to_append_title">Vis \"Hold for å legge til\" -tips</string>
<string name="show_hold_to_append_summary">Vis tips når bakgrunns- eller oppsårettsknapp trykkes på siden for videodetaljer</string>
<string name="background_player_append">Lagt i kø for bakgrunnsavspiller</string>
<string name="popup_playing_append">Lagt i kø for oppsprettsspiller</string>
<string name="unknown_content">[Ukjent]</string>
<string name="player_recoverable_failure">Gjenoppretter fra spillerfeil</string>
<string name="main_page_content">Hovedsidens innhold</string>
<string name="feed_page_summary">Strømside</string>
<string name="no_channel_subscribed_yet">Abonnerer ikke på noen kanaler enda</string>
<string name="trending">På vei opp</string>
<string name="enqueue_on_background">Legg i bakgrunnskø</string>
<string name="enqueue_on_popup">Legg i oppsprettskø</string>
<string name="start_here_on_main">Start avspilling her</string>
<string name="start_here_on_background">Start her i bakgrunnen</string>
<string name="start_here_on_popup">Start her i oppsprettsvindu</string>
</resources> </resources>

View File

@ -291,4 +291,11 @@ te openen in pop-upmodus</string>
<string name="play_queue_stream_detail">Details</string> <string name="play_queue_stream_detail">Details</string>
<string name="play_queue_audio_settings">Audio-instellingen</string> <string name="play_queue_audio_settings">Audio-instellingen</string>
<string name="hold_to_append">Houd ingedrukt om toe te voegen aan wachtrij</string> <string name="hold_to_append">Houd ingedrukt om toe te voegen aan wachtrij</string>
<string name="unknown_content">[Onbekend]</string>
<string name="enqueue_on_background">Toevoegen aan wachtrij in achtergrond</string>
<string name="enqueue_on_popup">Toevoegen aan wachtrij in pop-up</string>
<string name="start_here_on_main">Hier beginnen spelen</string>
<string name="start_here_on_background">Hier beginnen in achtergrond</string>
<string name="start_here_on_popup">Hier beginnen in pop-up</string>
</resources> </resources>

View File

@ -27,15 +27,15 @@
<string name="download_path_audio_dialog_title">Podaj ścieżkę zapisu audio</string> <string name="download_path_audio_dialog_title">Podaj ścieżkę zapisu audio</string>
<string name="autoplay_by_calling_app_title">Autoodtwarzanie</string> <string name="autoplay_by_calling_app_title">Autoodtwarzanie</string>
<string name="autoplay_by_calling_app_summary">Automatycznie odtwórz video kiedy NewPipe zostanie wywołany z innej aplikacji</string> <string name="autoplay_by_calling_app_summary">Automatycznie odtwórz video, kiedy NewPipe zostanie wywołany z innej aplikacji</string>
<string name="default_resolution_title">Domyślna rozdzielczość</string> <string name="default_resolution_title">Domyślna rozdzielczość</string>
<string name="play_with_kodi_title">Odtwórz w Kodi</string> <string name="play_with_kodi_title">Odtwórz w Kodi</string>
<string name="kore_not_found">Aplikacja Kore nie została znaleziona. Zainstalować Kore?</string> <string name="kore_not_found">Aplikacja Kore nie została znaleziona. Zainstalować Kore?</string>
<string name="show_play_with_kodi_title">Pokaż opcję \"Odtwórz z Kodi\"</string> <string name="show_play_with_kodi_title">Pokaż opcję \"Odtwórz z Kodi\"</string>
<string name="show_play_with_kodi_summary">Wyświetl opcję aby odtworzyć video przez Kodi</string> <string name="show_play_with_kodi_summary">Wyświetl opcję, aby odtworzyć video przez Kodi</string>
<string name="play_audio">Audio</string> <string name="play_audio">Audio</string>
<string name="default_audio_format_title">Domyślny format audio</string> <string name="default_audio_format_title">Domyślny format audio</string>
<string name="webm_description">WebM — darmowy format</string> <string name="webm_description">WebM — otwarty i darmowy format</string>
<string name="m4a_description">m4a — lepsza jakość</string> <string name="m4a_description">m4a — lepsza jakość</string>
<string name="theme_title">Motyw</string> <string name="theme_title">Motyw</string>
<string name="dark_theme_title">Ciemny</string> <string name="dark_theme_title">Ciemny</string>
@ -126,7 +126,7 @@
<string name="reCaptcha_title">Rozwiąż reCAPTCHA</string> <string name="reCaptcha_title">Rozwiąż reCAPTCHA</string>
<string name="recaptcha_request_toast">Prośba o ReCAPTCHA</string> <string name="recaptcha_request_toast">Prośba o ReCAPTCHA</string>
<string name="use_external_video_player_summary">Niektóre rozdzielczości nie będą miały dźwięku jeśli ta opcja jest włączona</string> <string name="use_external_video_player_summary">Niektóre rozdzielczości nie będą miały dźwięku, jeśli ta opcja jest włączona</string>
<string name="controls_background_title">W tle</string> <string name="controls_background_title">W tle</string>
<string name="default_popup_resolution_title">Domyślna rozdzielczość dla trybu okienkowego</string> <string name="default_popup_resolution_title">Domyślna rozdzielczość dla trybu okienkowego</string>
<string name="show_higher_resolutions_title">Pokaż wyższe rozdzielczości</string> <string name="show_higher_resolutions_title">Pokaż wyższe rozdzielczości</string>
@ -136,7 +136,7 @@
<string name="popup_remember_size_pos_title">Zapamiętaj rozmiar i pozycję trybu okienkowego</string> <string name="popup_remember_size_pos_title">Zapamiętaj rozmiar i pozycję trybu okienkowego</string>
<string name="popup_remember_size_pos_summary">Zapamiętaj ostatni rozmiar i pozycję ustawioną dla trybu okienkowego</string> <string name="popup_remember_size_pos_summary">Zapamiętaj ostatni rozmiar i pozycję ustawioną dla trybu okienkowego</string>
<string name="player_gesture_controls_title">Sterowanie odtwarzaczem za pomocą gestów</string> <string name="player_gesture_controls_title">Sterowanie odtwarzaczem za pomocą gestów</string>
<string name="player_gesture_controls_summary">Użyj gestów aby sterować jasnością i głośnością odtwarzacza</string> <string name="player_gesture_controls_summary">Użyj gestów, aby sterować jasnością i głośnością odtwarzacza</string>
<string name="show_search_suggestions_title">Szukaj sugestii</string> <string name="show_search_suggestions_title">Szukaj sugestii</string>
<string name="show_search_suggestions_summary">Pokazuj sugestie podczas wyszukiwania</string> <string name="show_search_suggestions_summary">Pokazuj sugestie podczas wyszukiwania</string>
@ -202,9 +202,9 @@
<string name="fragment_whats_new">Co nowego</string> <string name="fragment_whats_new">Co nowego</string>
<string name="enable_search_history_title">Historia wyszukiwania</string> <string name="enable_search_history_title">Historia wyszukiwania</string>
<string name="enable_search_history_summary">Zapisuj historie wyszukiwania lokalnie</string> <string name="enable_search_history_summary">Zapisuj lokalnie historię wyszukiwania</string>
<string name="enable_watch_history_title">Historia oglądania</string> <string name="enable_watch_history_title">Historia oglądania</string>
<string name="enable_watch_history_summary">Zapisuj historie oglądania</string> <string name="enable_watch_history_summary">Zapisuj historię oglądania</string>
<string name="resume_on_audio_focus_gain_title"/> <string name="resume_on_audio_focus_gain_title"/>
<string name="resume_on_audio_focus_gain_summary">Kontynuuj odtwarzanie po przerwaniu (np. po rozmowie telefonicznej)</string> <string name="resume_on_audio_focus_gain_summary">Kontynuuj odtwarzanie po przerwaniu (np. po rozmowie telefonicznej)</string>

View File

@ -256,4 +256,31 @@ o modo “popup“</string>
<string name="history_cleared">Histórico eliminado</string> <string name="history_cleared">Histórico eliminado</string>
<string name="item_deleted">Item apagado</string> <string name="item_deleted">Item apagado</string>
<string name="delete_item_search_history">Deseja apagar este item do histórico de pesquisa?</string> <string name="delete_item_search_history">Deseja apagar este item do histórico de pesquisa?</string>
<string name="play_all">Reproduzir todos</string>
<string name="unknown_content">[Desconhecido]</string>
<string name="player_unrecoverable_failure">Ocorreu um erro irrecuperável do reprodutor</string>
<string name="player_recoverable_failure">Em recuperação de um erro do reprodutor</string>
<string name="main_page_content">Conteúdo da página principal</string>
<string name="blank_page_summary">Página em branco</string>
<string name="subscription_page_summary">Página de Subscrições</string>
<string name="channel_page_summary">Página do canal</string>
<string name="select_a_channel">Selecione um canal</string>
<string name="no_channel_subscribed_yet">Nenhum canal subscreveu ainda</string>
<string name="select_a_kiosk">Selecione um quiosque</string>
<string name="kiosk">Quiosque</string>
<string name="trending">Tendências</string>
<string name="top_50">Top 50</string>
<string name="new_and_hot">Novo e popular</string>
<string name="title_activity_background_player">Reprodutor em segundo plano</string>
<string name="title_activity_popup_player">Reprodutor Popup</string>
<string name="play_queue_remove">Remover</string>
<string name="play_queue_stream_detail">Detalhes</string>
<string name="play_queue_audio_settings">Definições de Áudio</string>
<string name="start_here_on_main">Começar a reproduzir aqui</string>
<string name="start_here_on_background">Começar aqui em Segundo Plano</string>
<string name="start_here_on_popup">Começar aqui em Popup</string>
</resources> </resources>

View File

@ -109,7 +109,7 @@
<string name="report_error">Сообщить об ошибке</string> <string name="report_error">Сообщить об ошибке</string>
<string name="user_report">Сообщить о нарушении</string> <string name="user_report">Сообщить о нарушении</string>
<string name="info_dir_created">Создать папку для загруженного \'%1$s\'</string> <string name="info_dir_created">Создана папка для загрузок \'%1$s\'</string>
<string name="video">Видео</string> <string name="video">Видео</string>
<string name="audio">Аудио</string> <string name="audio">Аудио</string>
@ -126,7 +126,7 @@
<string name="controls_popup_title">В окне</string> <string name="controls_popup_title">В окне</string>
<string name="show_higher_resolutions_summary">Только некоторые устройства могут воспроизводить видео в 2K/4K</string> <string name="show_higher_resolutions_summary">Только некоторые устройства могут воспроизводить видео в 2K/4K</string>
<string name="default_video_format_title">Предпочитаемый формат видео</string> <string name="default_video_format_title">Формат видео по умолчанию</string>
<string name="black_theme_title">Чёрная</string> <string name="black_theme_title">Чёрная</string>
<string name="popup_remember_size_pos_title">Запоминать размер и положение всплывающего окна</string> <string name="popup_remember_size_pos_title">Запоминать размер и положение всплывающего окна</string>
<string name="player_gesture_controls_summary">Использовать жесты для изменения яркости и громкости</string> <string name="player_gesture_controls_summary">Использовать жесты для изменения яркости и громкости</string>
@ -144,8 +144,8 @@
<string name="add">Новая миссия</string> <string name="add">Новая миссия</string>
<string name="info_labels">Что:\\nЗапрос:\\nЯзык контента:\\nСервис:\\nВремя по Гринвичу:\\nПакет:\\nВерсия:\\nВерсия ОС:\\nГлобальный диапазон IP:</string> <string name="info_labels">Что:\\nЗапрос:\\nЯзык контента:\\nСервис:\\nВремя по Гринвичу:\\nПакет:\\nВерсия:\\nВерсия ОС:\\nГлобальный диапазон IP:</string>
<string name="msg_popup_permission">Это разрешение нужно, <string name="msg_popup_permission">Это разрешение нужно для
\nчтобы показывать всплывающее окно</string> \nвоспроизведения видео в отдельном окне</string>
<string name="reCaptchaActivity">reCAPTCHA</string> <string name="reCaptchaActivity">reCAPTCHA</string>
<string name="open_in_popup_mode">Открыть в отдельном окне</string> <string name="open_in_popup_mode">Открыть в отдельном окне</string>
@ -182,7 +182,7 @@
<string name="tab_about">О приложении</string> <string name="tab_about">О приложении</string>
<string name="tab_contributors">Участники</string> <string name="tab_contributors">Участники</string>
<string name="read_full_license">Прочитать лицензию</string> <string name="read_full_license">Прочитать лицензию</string>
<string name="app_description">Бесплатный лёгкий интерфейс YouTube для Android.</string> <string name="app_description">Бесплатный и лёгкий интерфейс YouTube для Android.</string>
<string name="view_on_github">Открыть на GitHub</string> <string name="view_on_github">Открыть на GitHub</string>
<string name="contribution_encouragement">Приветствуется всё — идеи, перевод, изменения дизайна, чистка кода или огромные изменения в коде. Чем больше сделано, тем лучше!</string> <string name="contribution_encouragement">Приветствуется всё — идеи, перевод, изменения дизайна, чистка кода или огромные изменения в коде. Чем больше сделано, тем лучше!</string>
<string name="copyright" formatted="true">© %1$s %2$s под лицензией %3$s</string> <string name="copyright" formatted="true">© %1$s %2$s под лицензией %3$s</string>
@ -278,4 +278,6 @@
<string name="player_stream_failure">Не удалось воспроизвести этот поток</string> <string name="player_stream_failure">Не удалось воспроизвести этот поток</string>
<string name="play_queue_stream_detail">Подробности</string> <string name="play_queue_stream_detail">Подробности</string>
<string name="play_queue_audio_settings">Настройки аудио</string> <string name="play_queue_audio_settings">Настройки аудио</string>
<string name="no_channel_subscribed_yet">Пока нет подписок</string>
<string name="play_queue_remove">Удалить</string>
</resources> </resources>

View File

@ -273,4 +273,22 @@ odpiranje v pojavnem načinu</string>
<string name="item_deleted">Predmet je izbrisan</string> <string name="item_deleted">Predmet je izbrisan</string>
<string name="delete_item_search_history">Ali želite izbrisati predmet iz zgodovine iskanja?</string> <string name="delete_item_search_history">Ali želite izbrisati predmet iz zgodovine iskanja?</string>
</resources> <string name="play_all">Predvajaj vse</string>
<string name="unknown_content">[ Neznano ]</string>
<string name="player_stream_failure">Predvajanje pretoka je spodletelo</string>
<string name="main_page_content">Vsebina glavne strani</string>
<string name="blank_page_summary">Prazna stran</string>
<string name="subscription_page_summary">Stran naročanja</string>
<string name="feed_page_summary">Stran virov</string>
<string name="channel_page_summary">Stran kanalov</string>
<string name="select_a_channel">Izbor kanala</string>
<string name="top_50">Najboljših 50</string>
<string name="new_and_hot">Novo in vroče</string>
<string name="title_activity_background_player">Ozadnji predvajalnik</string>
<string name="title_activity_popup_player">Pojavni predvajalnik</string>
<string name="play_queue_remove">Odstrani</string>
<string name="play_queue_stream_detail">Podrobnosti</string>
<string name="play_queue_audio_settings">Nastavitve zvoka</string>
</resources>

View File

@ -1,5 +1,5 @@
<?xml version='1.0' encoding='UTF-8'?> <?xml version='1.0' encoding='UTF-8'?>
<resources><string name="main_bg_subtitle">Tryck på sök för att börja</string> <resources><string name="main_bg_subtitle">Tryck på sök för att komma igång</string>
<string name="upload_date_text">Publicerad den %1$s</string> <string name="upload_date_text">Publicerad den %1$s</string>
<string name="no_player_found">Ingen strömspelare hittades. Vill du installera VLC?</string> <string name="no_player_found">Ingen strömspelare hittades. Vill du installera VLC?</string>
<string name="install">Installera</string> <string name="install">Installera</string>
@ -29,26 +29,26 @@
<string name="download_path_audio_summary">Plats för att lagra nerladdat ljud i</string> <string name="download_path_audio_summary">Plats för att lagra nerladdat ljud i</string>
<string name="download_path_audio_dialog_title">Ange nerladdningsplats för ljudfiler</string> <string name="download_path_audio_dialog_title">Ange nerladdningsplats för ljudfiler</string>
<string name="autoplay_by_calling_app_summary">Spela automatiskt upp en video när NewPipe är kallad från en annan app</string> <string name="autoplay_by_calling_app_summary">Spelar automatiskt upp en video när NewPipe öppnas av en annan app</string>
<string name="default_resolution_title">Standardupplösning</string> <string name="default_resolution_title">Standardupplösning</string>
<string name="default_popup_resolution_title">Standardupplösning för popup</string> <string name="default_popup_resolution_title">Standardupplösning för popup</string>
<string name="show_higher_resolutions_title">Visa högre upplösningar</string> <string name="show_higher_resolutions_title">Visa högre upplösningar</string>
<string name="show_higher_resolutions_summary">Det finns endast ett fåtal enheter som har stöd för uppspelning av 2K/4K-videor</string> <string name="show_higher_resolutions_summary">Det finns endast ett fåtal enheter som har stöd för uppspelning av 2K/4K-videor</string>
<string name="play_with_kodi_title">Spela upp med Kodi</string> <string name="play_with_kodi_title">Spela upp med Kodi</string>
<string name="kore_not_found">Kore-appen hittades inte. Installera Kore?</string> <string name="kore_not_found">Kore-appen hittades inte. Installera den?</string>
<string name="show_play_with_kodi_title">Visa alternativet \"Spela upp med Kodi\"</string> <string name="show_play_with_kodi_title">Visa alternativet \"Spela upp med Kodi\"</string>
<string name="show_play_with_kodi_summary">Visa ett alternativ för att spela upp en video med mediacentret Kodi</string> <string name="show_play_with_kodi_summary">Visa ett alternativ för att spela upp en video med mediacentret Kodi</string>
<string name="play_audio">Ljud</string> <string name="play_audio">Ljud</string>
<string name="default_audio_format_title">Standardformat för ljud</string> <string name="default_audio_format_title">Standardformat för ljud</string>
<string name="default_video_format_title">Videoformat som föredras</string> <string name="default_video_format_title">Videoformat som föredras</string>
<string name="webm_description">WebM — fritt format</string> <string name="webm_description">WebM — fritt format</string>
<string name="m4a_description">m4a — bättre kvalité</string> <string name="m4a_description">M4A — bättre kvalité</string>
<string name="theme_title">Tema</string> <string name="theme_title">Tema</string>
<string name="light_theme_title">Ljus</string> <string name="light_theme_title">Ljus</string>
<string name="dark_theme_title">Mörk</string> <string name="dark_theme_title">Mörk</string>
<string name="black_theme_title">Svart</string> <string name="black_theme_title">Svart</string>
<string name="popup_remember_size_pos_title">Kom ihåg storleken och positionen för popup</string> <string name="popup_remember_size_pos_title">Kom ihåg popupstorlek och position</string>
<string name="popup_remember_size_pos_summary">Kom ihåg den senast inställda storleken och positionen av popup-rutan</string> <string name="popup_remember_size_pos_summary">Kom ihåg popup-rutans senaste storlek och position</string>
<string name="player_gesture_controls_title">Gestkontroller för spelare</string> <string name="player_gesture_controls_title">Gestkontroller för spelare</string>
<string name="player_gesture_controls_summary">Använd gester för att kontrollera spelarens ljusstyrka och volym</string> <string name="player_gesture_controls_summary">Använd gester för att kontrollera spelarens ljusstyrka och volym</string>
<string name="show_search_suggestions_title">Sökförslag</string> <string name="show_search_suggestions_title">Sökförslag</string>
@ -59,7 +59,7 @@
<string name="next_video_title">Nästa video</string> <string name="next_video_title">Nästa video</string>
<string name="show_next_and_similar_title">Visa nästkommande och liknande videor</string> <string name="show_next_and_similar_title">Visa nästkommande och liknande videor</string>
<string name="url_not_supported_toast">Webbadressen stöds inte</string> <string name="url_not_supported_toast">Webbadressen stöds inte</string>
<string name="search_language_title">Språk som föredras för innehållet</string> <string name="search_language_title">Standard innehållsspråk</string>
<string name="settings_category_video_audio_title">Video &amp; Ljud</string> <string name="settings_category_video_audio_title">Video &amp; Ljud</string>
<string name="settings_category_popup_title">Popup-ruta</string> <string name="settings_category_popup_title">Popup-ruta</string>
<string name="settings_category_appearance_title">Utseende</string> <string name="settings_category_appearance_title">Utseende</string>
@ -69,7 +69,7 @@
<string name="play_btn_text">Spela upp</string> <string name="play_btn_text">Spela upp</string>
<string name="content">Innehåll</string> <string name="content">Innehåll</string>
<string name="show_age_restricted_content_title">Visa åldersbegränsat innehåll</string> <string name="show_age_restricted_content_title">Visa åldersbegränsat innehåll</string>
<string name="video_is_age_restricted">Videon är åldersbegränsad. Aktivera åldersbegränsade videor i inställningarna först.</string> <string name="video_is_age_restricted">Videon är åldersbegränsad. Du kan aktivera åldersbegränsade videor i inställningarna.</string>
<string name="duration_live">direkt</string> <string name="duration_live">direkt</string>
<string name="downloads">Nerladdningar</string> <string name="downloads">Nerladdningar</string>
<string name="downloads_title">Nerladdningar</string> <string name="downloads_title">Nerladdningar</string>
@ -87,12 +87,12 @@
<string name="general_error">Fel</string> <string name="general_error">Fel</string>
<string name="network_error">Nätverksfel</string> <string name="network_error">Nätverksfel</string>
<string name="could_not_load_thumbnails">Kunde inte ladda alla miniatyrbilder</string> <string name="could_not_load_thumbnails">Kunde inte ladda alla miniatyrbilder</string>
<string name="parsing_error">Kunde inte tolka hemsidan.</string> <string name="parsing_error">Det gick inte att analysera webbplatsen</string>
<string name="light_parsing_error">Kunde inte tolka hemsidan fullt ut.</string> <string name="light_parsing_error">Det gick inte att analysera webbplatsen helt</string>
<string name="content_not_available">Innehållet är inte tillgängligt.</string> <string name="content_not_available">Innehållet är inte tillgängligt</string>
<string name="blocked_by_gema">Blockerat av GEMA.</string> <string name="blocked_by_gema">Blockerat av GEMA</string>
<string name="could_not_setup_download_menu">Kunde inte ställa in nerladdningsmenyn.</string> <string name="could_not_setup_download_menu">Kunde inte ställa in nerladdningsmenyn</string>
<string name="live_streams_not_supported">Det här är en DIREKTSÄNDNING. Det finns ännu inget stöd för dessa.</string> <string name="live_streams_not_supported">Det här är en DIREKTSÄNDNING vilket ännu inte stöds.</string>
<string name="could_not_load_image">Kunde inte ladda Bild</string> <string name="could_not_load_image">Kunde inte ladda Bild</string>
<string name="app_ui_crash">App/AnvändarGränssnitt kraschade</string> <string name="app_ui_crash">App/AnvändarGränssnitt kraschade</string>
<string name="sorry_string">Oj, det där skulle inte ha hänt.</string> <string name="sorry_string">Oj, det där skulle inte ha hänt.</string>
@ -108,4 +108,175 @@
<string name="detail_dislikes_img_view_description">Ogillanden</string> <string name="detail_dislikes_img_view_description">Ogillanden</string>
<string name="use_tor_title">Använd Tor</string> <string name="use_tor_title">Använd Tor</string>
<string name="report_error">Rapportera ett fel</string> <string name="report_error">Rapportera ett fel</string>
</resources> <string name="view_count_text">%1$s visningar</string>
<string name="subscribe_button_title">Prenumerera</string>
<string name="subscribed_button_title">Prenumererad</string>
<string name="channel_unsubscribed">Prenumerationen togs bort</string>
<string name="subscription_change_failed">Kunde inte ändra prenumeration</string>
<string name="subscription_update_failed">Kunde inte uppdatera prenumeration</string>
<string name="tab_main">Hem</string>
<string name="tab_subscriptions">Prenumerationer</string>
<string name="fragment_whats_new">Vad är nytt</string>
<string name="autoplay_by_calling_app_title">Autospela</string>
<string name="enable_search_history_title">Sök historik</string>
<string name="enable_search_history_summary">Spara sökfrågor lokalt</string>
<string name="enable_watch_history_title">Historik</string>
<string name="enable_watch_history_summary">Håll koll på videor som du tittat på</string>
<string name="resume_on_audio_focus_gain_title">Fortsätt då fokus fås</string>
<string name="resume_on_audio_focus_gain_summary">Fortsätta spela efter avbrott (t.ex. telefonsamtal)</string>
<string name="show_hold_to_append_title">Visa \"Håll för att lägga till\" tips</string>
<string name="show_hold_to_append_summary">Visa tips när bakgrunds- eller popup-knappen trycks på sidan för videodetaljer</string>
<string name="settings_category_player_title">Spelare</string>
<string name="settings_category_player_behavior_title">Beteende</string>
<string name="settings_category_history_title">Historik</string>
<string name="background_player_append">Tillagd till bakgrunds-spelar kön</string>
<string name="popup_playing_append">Tillagd till popup-spelar kön</string>
<string name="playlist">Spellista</string>
<string name="popup_resizing_indicator_title">Storleksändring</string>
<string name="undo">Ångra</string>
<string name="play_all">Spela Alla</string>
<string name="notification_channel_name">NewPipe Avisering</string>
<string name="notification_channel_description">Aviseringar för NewPipe bakgrunds och popup-spelare</string>
<string name="unknown_content">[Okänd]</string>
<string name="youtube_signature_decryption_error">Kunde inte dekryptera video URL signatur</string>
<string name="could_not_get_stream">Kunde inte hitta någon ström</string>
<string name="player_stream_failure">Misslyckades med att spela denna ström</string>
<string name="player_unrecoverable_failure">Allvarligt spelarfel inträffade</string>
<string name="player_recoverable_failure">Återhämtar sig från spelarfel</string>
<string name="error_report_button_text">Rapportera fel via e-post</string>
<string name="info_labels">Vad:\\nBegäran:\\nInnehålls Språk:\\nTjänst:\\nGMT Tid:\\nPaket:\\nVersion:\\nOS-version:\\nGlob. IP-intervall:</string>
<string name="list_thumbnail_view_description">Videons miniatyrbild</string>
<string name="detail_thumbnail_view_description">Videons miniatyrbild</string>
<string name="detail_uploader_thumbnail_view_description">Uppladdarens avatar miniatyrbild</string>
<string name="use_tor_summary">(Experimentellt) Tvinga nedladdningstrafik via Tor för ökad integritet (strömmande videon stöds inte ännu).</string>
<string name="user_report">Användarrapport</string>
<string name="search_no_results">Inga resultat</string>
<string name="empty_subscription_feed_subtitle">Här va\' det tomt</string>
<string name="err_dir_create">Kunde inte skapa nedladdnings katalog \'%1$s\'</string>
<string name="info_dir_created">Skapa nedladdnings katalog \'%1$s\'</string>
<string name="video">Video</string>
<string name="audio">Ljud</string>
<string name="retry">Försök igen</string>
<string name="storage_permission_denied">Tillgång till lagringsområde nekades</string>
<string name="use_old_player_title">Använd gamla spelaren</string>
<string name="use_old_player_summary">Gamla inbyggda Mediaframework-spelaren</string>
<string name="short_thousand">k</string>
<string name="short_million">mn</string>
<string name="short_billion">md</string>
<string name="no_subscribers">Inga prenumeranter</string>
<plurals name="subscribers">
<item quantity="one">%s prenumerant</item>
<item quantity="other">%s prenumeranter</item>
</plurals>
<string name="no_views">Inga visningar</string>
<plurals name="views">
<item quantity="one">%s visning</item>
<item quantity="other">%s visningar</item>
</plurals>
<string name="no_videos">Inga videor</string>
<plurals name="videos">
<item quantity="one">%s video</item>
<item quantity="other">%s videon</item>
</plurals>
<string name="start">Start</string>
<string name="pause">Pausa</string>
<string name="view">Spela</string>
<string name="delete">Ta bort</string>
<string name="checksum">Kontrollsumma</string>
<string name="add">Nytt uppdrag</string>
<string name="finish">Ok</string>
<string name="msg_name">Filnamn</string>
<string name="msg_threads">Trådar</string>
<string name="msg_error">Fel</string>
<string name="msg_server_unsupported">Serveren stöds inte</string>
<string name="msg_exists">Filen finns redan</string>
<string name="msg_url_malform">Felaktig webbadress eller Internet inte tillgängligt</string>
<string name="msg_running">NewPipe nedladdning</string>
<string name="msg_running_detail">Tryck för detaljer</string>
<string name="msg_wait">Vänta…</string>
<string name="msg_copied">Kopierat till urklipp</string>
<string name="no_available_dir">Välj en tillgänglig nedladdningsmapp</string>
<string name="msg_popup_permission">Denna tillåtelse behövs för att
\nöppna i popup-läge</string>
<string name="reCaptchaActivity">reCAPTCHA</string>
<string name="reCaptcha_title">reCAPTCHA utmaning</string>
<string name="recaptcha_request_toast">reCAPTCHA utmaning begärd</string>
<string name="settings_category_downloads_title">Nedladdning</string>
<string name="settings_file_charset_title">Tillåtna tecken i filnamn</string>
<string name="settings_file_replacement_character_summary">Ogiltiga tecken ersätts med detta värde</string>
<string name="settings_file_replacement_character_title">Ersättningstecknet</string>
<string name="charset_letters_and_digits">Bokstäver och siffror</string>
<string name="charset_most_special_characters">De flesta specialtecken</string>
<string name="title_activity_about">Om NewPipe</string>
<string name="action_settings">Inställningar</string>
<string name="action_about">Om</string>
<string name="title_licenses">Tredjepartslicenser</string>
<string name="copyright" formatted="true">© %1$s av %2$s under %3$s</string>
<string name="error_unable_to_load_license">Kunde inte ladda licens</string>
<string name="action_open_website">Öppna webbplats</string>
<string name="tab_about">Om</string>
<string name="tab_contributors">Medverkande</string>
<string name="tab_licenses">Licenser</string>
<string name="app_description">Gratis och enkel YouTube-app för Android.</string>
<string name="view_on_github">Visa på GitHub</string>
<string name="app_license_title">NewPipes licens</string>
<string name="contribution_encouragement">Vad du än har för idéer gällande översättning, designändringar, kod rengöring eller riktigt tunga så är hjälp alltid välkommet. Ju mer som görs desto bättre blir det!</string>
<string name="read_full_license">Läs hela licensen</string>
<string name="contribution_title">Bidrag</string>
<string name="title_activity_history">Historik</string>
<string name="title_history_search">Sökt</string>
<string name="title_history_view">Tittade</string>
<string name="history_disabled">Historik är inaktiverat</string>
<string name="action_history">Historik</string>
<string name="history_empty">Historiken är tom</string>
<string name="history_cleared">Historiken har rensats</string>
<string name="item_deleted">Objektet bortaget</string>
<string name="delete_item_search_history">Vill du ta bort det här objektet från sökhistoriken?</string>
<string name="main_page_content">Huvudsidans innehåll</string>
<string name="blank_page_summary">Tom sida</string>
<string name="kiosk_page_summary">Kiosk sida</string>
<string name="subscription_page_summary">Prenumerationssidan</string>
<string name="feed_page_summary">Flödessida</string>
<string name="channel_page_summary">Kanal-sida</string>
<string name="select_a_channel">Välj en kanal</string>
<string name="no_channel_subscribed_yet">Ingen kanal prenumererad än</string>
<string name="select_a_kiosk">Välj en kiosk</string>
<string name="kiosk">Kiosk</string>
<string name="trending">Trend</string>
<string name="top_50">Top 50</string>
<string name="new_and_hot">Aktuellt</string>
<string name="title_activity_background_player">Bakgrunds-spelare</string>
<string name="title_activity_popup_player">Popup-spelare</string>
<string name="play_queue_remove">Ta bort</string>
<string name="play_queue_stream_detail">Detaljer</string>
<string name="play_queue_audio_settings">Ljud inställningar</string>
<string name="hold_to_append">Håll för att placera i kön</string>
<string name="enqueue_on_background">Placera i bakgrunds-kön</string>
<string name="enqueue_on_popup">Placera i popup-kön</string>
<string name="start_here_on_main">Börja spela här</string>
<string name="start_here_on_background">Börja här i bakgrunden</string>
<string name="start_here_on_popup">Börja här i popup</string>
</resources>

View File

@ -248,12 +248,49 @@
</plurals> </plurals>
<string name="title_activity_history">Geçmiş</string> <string name="title_activity_history">Geçmiş</string>
<string name="title_history_search">Arandı</string> <string name="title_history_search">Aranan</string>
<string name="title_history_view">İzlendi</string> <string name="title_history_view">İzlenen</string>
<string name="history_disabled">Geçmiş devre dışı</string> <string name="history_disabled">Geçmiş devre dışı</string>
<string name="action_history">Geçmiş</string> <string name="action_history">Geçmiş</string>
<string name="history_empty">Geçmiş boş</string> <string name="history_empty">Geçmiş boş</string>
<string name="history_cleared">Geçmiş temizlendi</string> <string name="history_cleared">Geçmiş temizlendi</string>
<string name="item_deleted">Öge silindi</string> <string name="item_deleted">Öge silindi</string>
<string name="delete_item_search_history">Bu içeriği arama geçmişinden silmek istiyor musunuz?</string> <string name="delete_item_search_history">Bu içeriği arama geçmişinden silmek istiyor musunuz?</string>
<string name="show_hold_to_append_title">Eklemek İçin Bas İpucunu Göster</string>
<string name="show_hold_to_append_summary">Video ayrıntıları sayfasında arka plan veya açılır pencere düğmesine basıldığında ipucu göster</string>
<string name="background_player_append">Arka plan oynatıcıda kuyruğa eklendi</string>
<string name="popup_playing_append">ılır pencere oynatıcıda kuyruğa eklendi</string>
<string name="play_all">Tümünü Oynat</string>
<string name="unknown_content">[Bilinmeyen]</string>
<string name="player_stream_failure">Bu akış oynatılamıyor</string>
<string name="player_unrecoverable_failure">Kurtarılamaz oynatıcı hatası oluştu</string>
<string name="player_recoverable_failure">Oynatıcı hatasından kurtarılıyor</string>
<string name="main_page_content">Ana sayfanın içeriği</string>
<string name="blank_page_summary">Boş Sayfa</string>
<string name="kiosk_page_summary">Büfe Sayfası</string>
<string name="subscription_page_summary">Abonelik Sayfası</string>
<string name="feed_page_summary">Besleme Sayfası</string>
<string name="channel_page_summary">Kanal Sayfası</string>
<string name="select_a_channel">Kanal seç</string>
<string name="no_channel_subscribed_yet">Henüz abone olunan kanal yok</string>
<string name="select_a_kiosk">Büfe seç</string>
<string name="kiosk">Büfe</string>
<string name="trending">Eğilimler</string>
<string name="top_50">En Üst 50</string>
<string name="new_and_hot">Yeni ve sıcak</string>
<string name="title_activity_background_player">Arka Plan Oynatıcı</string>
<string name="title_activity_popup_player">ılır Pencere Oynatıcı</string>
<string name="play_queue_remove">Kaldır</string>
<string name="play_queue_stream_detail">Ayrıntılar</string>
<string name="play_queue_audio_settings">Ses Ayarları</string>
<string name="hold_to_append">Kuyruğa Almak İçin Bas</string>
<string name="enqueue_on_background">Arka Planda Kuyruğa Al</string>
<string name="enqueue_on_popup">ılır Pencerede Kuyruğa Al</string>
<string name="start_here_on_main">Burayı Oynatmaya Başla</string>
<string name="start_here_on_background">Arka Planda Burayı Başlat</string>
<string name="start_here_on_popup">ılır Pencerede Burayı Başlat</string>
</resources> </resources>

View File

@ -21,13 +21,13 @@
<string name="download_path_dialog_title">输入视频存储路径</string> <string name="download_path_dialog_title">输入视频存储路径</string>
<string name="default_resolution_title">默认分辨率</string> <string name="default_resolution_title">默认分辨率</string>
<string name="play_with_kodi_title">用 Kodi 播放</string> <string name="play_with_kodi_title">用 Kodi 播放</string>
<string name="kore_not_found">找不到 Kore 应用程式,您要安装 Kore 吗?</string> <string name="kore_not_found">找不到 Kore 应用程式,安装它吗?</string>
<string name="show_play_with_kodi_title">显示 “用 Kodi 播放” 的选项</string> <string name="show_play_with_kodi_title">显示 “用 Kodi 播放” 的选项</string>
<string name="show_play_with_kodi_summary">显示以 Kodi 媒体中心播放视频的选项</string> <string name="show_play_with_kodi_summary">显示以 Kodi 媒体中心播放视频的选项</string>
<string name="play_audio">音频</string> <string name="play_audio">音频</string>
<string name="default_audio_format_title">默认音频格式</string> <string name="default_audio_format_title">默认音频格式</string>
<string name="webm_description">WebM — 开放格式</string> <string name="webm_description">WebM — 开放格式</string>
<string name="m4a_description">m4a — 更佳品质</string> <string name="m4a_description">M4A — 更佳品质</string>
<string name="theme_title">主题</string> <string name="theme_title">主题</string>
<string name="dark_theme_title">暗色系</string> <string name="dark_theme_title">暗色系</string>
<string name="light_theme_title">明亮色系</string> <string name="light_theme_title">明亮色系</string>
@ -36,7 +36,7 @@
<string name="next_video_title">即将播放</string> <string name="next_video_title">即将播放</string>
<string name="show_next_and_similar_title">显示下一部和相近的视频</string> <string name="show_next_and_similar_title">显示下一部和相近的视频</string>
<string name="url_not_supported_toast">不支援此网址</string> <string name="url_not_supported_toast">不支援此网址</string>
<string name="search_language_title">首选内容语言</string> <string name="search_language_title">默认内容语言</string>
<string name="settings_category_video_audio_title">视频和音频</string> <string name="settings_category_video_audio_title">视频和音频</string>
<string name="settings_category_appearance_title">外观</string> <string name="settings_category_appearance_title">外观</string>
<string name="settings_category_other_title">其他</string> <string name="settings_category_other_title">其他</string>
@ -62,14 +62,14 @@
<string name="video_is_age_restricted">视频有年龄限制。请先在设置中启用\"显示年龄限制内容\"。</string> <string name="video_is_age_restricted">视频有年龄限制。请先在设置中启用\"显示年龄限制内容\"。</string>
<string name="general_error">错误</string> <string name="general_error">错误</string>
<string name="could_not_load_thumbnails">无法加载所有缩略图</string> <string name="could_not_load_thumbnails">无法加载所有缩略图</string>
<string name="youtube_signature_decryption_error">无法解密视频网址签名</string> <string name="youtube_signature_decryption_error">无法解密视频网址签名</string>
<string name="parsing_error">无法解析网站</string> <string name="parsing_error">无法解析网站</string>
<string name="light_parsing_error">无法完全解析网站</string> <string name="light_parsing_error">无法完全解析网站</string>
<string name="content_not_available">内容不可用</string> <string name="content_not_available">内容不可用</string>
<string name="blocked_by_gema">已被 GEMA 屏蔽</string> <string name="blocked_by_gema">已被 GEMA 屏蔽</string>
<string name="could_not_setup_download_menu">无法设置下载菜单</string> <string name="could_not_setup_download_menu">无法设置下载菜单</string>
<string name="live_streams_not_supported">这是一个在线流媒体。这尚不支持。</string> <string name="live_streams_not_supported">这是一个在线流媒体尚不支持。</string>
<string name="could_not_get_stream">无法获取任何流媒体</string> <string name="could_not_get_stream">无法获取任何流媒体</string>
<string name="sorry_string">抱歉,这本不应该发生。</string> <string name="sorry_string">抱歉,这本不应该发生。</string>
<string name="error_report_button_text">使用邮件报告错误</string> <string name="error_report_button_text">使用邮件报告错误</string>
<string name="error_snackbar_message">抱歉,发生了一些错误。</string> <string name="error_snackbar_message">抱歉,发生了一些错误。</string>
@ -86,15 +86,15 @@
<string name="video">视频</string> <string name="video">视频</string>
<string name="audio">音频</string> <string name="audio">音频</string>
<string name="retry">重试</string> <string name="retry">重试</string>
<string name="storage_permission_denied">访问存储的权限被拒绝</string> <string name="storage_permission_denied">无权访问存储空间</string>
<string name="autoplay_by_calling_app_title">另一应用调用时自动播放</string> <string name="autoplay_by_calling_app_title">自动播放</string>
<string name="autoplay_by_calling_app_summary">当 NewPipe 被其他应用调用时自动播放视频</string> <string name="autoplay_by_calling_app_summary">当 NewPipe 被其他应用调用时自动播放视频</string>
<string name="duration_live">直播</string> <string name="duration_live">直播</string>
<string name="main_bg_subtitle">点按搜索开始使用</string> <string name="main_bg_subtitle">点按搜索开始使用</string>
<string name="start">开始</string> <string name="start">开始</string>
<string name="pause">暂停</string> <string name="pause">暂停</string>
<string name="view">查看</string> <string name="view">播放</string>
<string name="delete">删除</string> <string name="delete">删除</string>
<string name="checksum">校验和</string> <string name="checksum">校验和</string>
@ -109,8 +109,8 @@
<string name="msg_running">NewPipe 正在下载</string> <string name="msg_running">NewPipe 正在下载</string>
<string name="msg_running_detail">点击了解细节</string> <string name="msg_running_detail">点击了解细节</string>
<string name="msg_wait">请稍候…</string> <string name="msg_wait">请稍候…</string>
<string name="msg_copied">已复制到剪贴板</string> <string name="msg_copied">已复制到剪贴板</string>
<string name="no_available_dir">请选择一个可用的下载目录</string> <string name="no_available_dir">请选择一个可用的下载目录</string>
<string name="add">新任务</string> <string name="add">新任务</string>
<string name="downloads">下载</string> <string name="downloads">下载</string>
@ -155,8 +155,8 @@
<string name="show_search_suggestions_summary">在搜索时显示搜索建议</string> <string name="show_search_suggestions_summary">在搜索时显示搜索建议</string>
<string name="enable_search_history_title">搜索记录</string> <string name="enable_search_history_title">搜索记录</string>
<string name="enable_search_history_summary">在本地存储搜索请求</string> <string name="enable_search_history_summary">在本地存储搜索请求</string>
<string name="enable_watch_history_title">观看历史</string> <string name="enable_watch_history_title">历史</string>
<string name="enable_watch_history_summary">存储观看历史</string> <string name="enable_watch_history_summary">记住观看的视频</string>
<string name="resume_on_audio_focus_gain_title">焦点恢复</string> <string name="resume_on_audio_focus_gain_title">焦点恢复</string>
<string name="resume_on_audio_focus_gain_summary">在打扰(例如来电)过后恢复视频播放</string> <string name="resume_on_audio_focus_gain_summary">在打扰(例如来电)过后恢复视频播放</string>
@ -178,7 +178,7 @@
<string name="notification_channel_description">NewPipe 后台播放和窗口播放器的通知</string> <string name="notification_channel_description">NewPipe 后台播放和窗口播放器的通知</string>
<string name="use_old_player_title">使用旧版播放器</string> <string name="use_old_player_title">使用旧版播放器</string>
<string name="use_old_player_summary">旧版本的内置媒体播放器</string> <string name="use_old_player_summary">旧版本的内置媒体播放器</string>
<string name="short_thousand">K</string> <string name="short_thousand">K</string>
<string name="short_million">M</string> <string name="short_million">M</string>
<string name="short_billion">B</string> <string name="short_billion">B</string>
@ -216,7 +216,39 @@
<string name="title_history_view">观看记录</string> <string name="title_history_view">观看记录</string>
<string name="history_disabled">历史记录已禁用</string> <string name="history_disabled">历史记录已禁用</string>
<string name="action_history">历史记录</string> <string name="action_history">历史记录</string>
<string name="history_empty">没有历史记录</string> <string name="history_empty">没有历史记录</string>
<string name="history_cleared">历史记录已清除</string> <string name="history_cleared">历史记录已清除</string>
</resources> <string name="settings_category_player_title">播放器</string>
<string name="settings_category_player_behavior_title">行为</string>
<string name="settings_category_history_title">历史记录</string>
<string name="background_player_append">在后台播放器上排队</string>
<string name="popup_playing_append">在弹出播放器上排队</string>
<string name="playlist">播放列表</string>
<string name="undo">撤销</string>
<string name="play_all">全部播放</string>
<string name="unknown_content">[未知]</string>
<string name="player_stream_failure">播放此流媒体失败</string>
<string name="player_unrecoverable_failure">发生无法解决的播放器错误</string>
<string name="search_no_results">没有结果</string>
<string name="empty_subscription_feed_subtitle">空空如也</string>
<string name="no_subscribers">无订阅者</string>
<plurals name="subscribers">
<item quantity="other">%s 位订阅者</item>
</plurals>
<string name="no_views">无观看次数</string>
<plurals name="views">
<item quantity="other">%s 次观看</item>
</plurals>
<string name="no_videos">没有视频</string>
<plurals name="videos">
<item quantity="other">%s 部视频</item>
</plurals>
<string name="item_deleted">项目已删除</string>
</resources>

View File

@ -20,6 +20,8 @@
<attr name="history" format="reference"/> <attr name="history" format="reference"/>
<attr name="drag_handle" format="reference"/> <attr name="drag_handle" format="reference"/>
<attr name="selected" format="reference"/> <attr name="selected" format="reference"/>
<attr name="search_add" format="reference"/>
<attr name="options" format="reference"/>
<!-- Can't refer to colors directly into drawable's xml--> <!-- Can't refer to colors directly into drawable's xml-->
<attr name="toolbar_shadow_drawable" format="reference"/> <attr name="toolbar_shadow_drawable" format="reference"/>

View File

@ -39,7 +39,10 @@
<color name="duration_text_color">#EEFFFFFF</color> <color name="duration_text_color">#EEFFFFFF</color>
<color name="playlist_stream_count_text_color">#ffffff</color> <color name="playlist_stream_count_text_color">#ffffff</color>
<color name="video_overlay_color">#66000000</color> <color name="video_overlay_color">#66000000</color>
<color name="background_notification_color">#323232</color> <color name="background_notification_color">#323232</color>
<color name="background_title_color">#ffffff</color>
<color name="background_subtext_color">#999999</color>
<color name="subscribe_background_color">#e53935</color> <color name="subscribe_background_color">#e53935</color>
<color name="subscribe_text_color">#fff</color> <color name="subscribe_text_color">#fff</color>

View File

@ -59,6 +59,8 @@
<!-- Playlist View Dimensions--> <!-- Playlist View Dimensions-->
<dimen name="playlist_item_thumbnail_stream_count_width">60dp</dimen> <dimen name="playlist_item_thumbnail_stream_count_width">60dp</dimen>
<dimen name="playlist_ctrl_height">50dp</dimen>
<dimen name="playlist_ctrl_seperator_margin">10dp</dimen>
<!-- Text Size --> <!-- Text Size -->
<dimen name="playlist_item_title_text_size">14sp</dimen> <dimen name="playlist_item_title_text_size">14sp</dimen>
<dimen name="playlist_detail_title_text_size">16sp</dimen> <dimen name="playlist_detail_title_text_size">16sp</dimen>

View File

@ -123,6 +123,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>
@ -309,6 +311,11 @@
<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>
<!-- Drawer --> <!-- Drawer -->
<string name="drawer_open">Open Drawer</string> <string name="drawer_open">Open Drawer</string>

View File

@ -27,6 +27,8 @@
<item name="history">@drawable/ic_history_black_24dp</item> <item name="history">@drawable/ic_history_black_24dp</item>
<item name="drag_handle">@drawable/ic_drag_handle_black_24dp</item> <item name="drag_handle">@drawable/ic_drag_handle_black_24dp</item>
<item name="selected">@drawable/ic_fiber_manual_record_black_24dp</item> <item name="selected">@drawable/ic_fiber_manual_record_black_24dp</item>
<item name="search_add">@drawable/ic_arrow_top_left_black_24dp</item>
<item name="options">@drawable/ic_more_vert_black_24dp</item>
<item name="separator_color">@color/light_separator_color</item> <item name="separator_color">@color/light_separator_color</item>
<item name="contrast_background_color">@color/light_contrast_background_color</item> <item name="contrast_background_color">@color/light_contrast_background_color</item>
@ -65,6 +67,8 @@
<item name="history">@drawable/ic_history_white_24dp</item> <item name="history">@drawable/ic_history_white_24dp</item>
<item name="drag_handle">@drawable/ic_drag_handle_white_24dp</item> <item name="drag_handle">@drawable/ic_drag_handle_white_24dp</item>
<item name="selected">@drawable/ic_fiber_manual_record_white_24dp</item> <item name="selected">@drawable/ic_fiber_manual_record_white_24dp</item>
<item name="search_add">@drawable/ic_arrow_top_left_white_24dp</item>
<item name="options">@drawable/ic_more_vert_white_24dp</item>
<item name="separator_color">@color/dark_separator_color</item> <item name="separator_color">@color/dark_separator_color</item>
<item name="contrast_background_color">@color/dark_contrast_background_color</item> <item name="contrast_background_color">@color/dark_contrast_background_color</item>
@ -160,4 +164,12 @@
<item name="android:background">@color/dark_youtube_primary_color</item> <item name="android:background">@color/dark_youtube_primary_color</item>
</style> </style>
<style name="PopupPermissionsTheme" parent="Theme.AppCompat.NoActionBar">
<item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowAnimationStyle">@android:style/Animation</item>
<item name="android:windowNoDisplay">true</item>
</style>
</resources> </resources>

View File

@ -0,0 +1 @@
NewPipe does not use any Google framework libraries, or the YouTube API. It only parses the website in order to gain the information it needs. Therefore this app can be used on devices without Google Services installed. Also, you don't need a YouTube account to use NewPipe, and it's FLOSS.

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View File

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 112 KiB

View File

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 40 KiB

View File

Before

Width:  |  Height:  |  Size: 110 KiB

After

Width:  |  Height:  |  Size: 110 KiB

View File

Before

Width:  |  Height:  |  Size: 263 KiB

After

Width:  |  Height:  |  Size: 263 KiB

View File

Before

Width:  |  Height:  |  Size: 101 KiB

After

Width:  |  Height:  |  Size: 101 KiB

View File

Before

Width:  |  Height:  |  Size: 122 KiB

After

Width:  |  Height:  |  Size: 122 KiB

Some files were not shown because too many files have changed in this diff Show More