Merge branch 'dev' into multiple-services
20
README.md
@ -18,16 +18,16 @@ WARNING: PUTTING NEWPIPE OR ANY FORK OF IT INTO GOOGLE PLAYSTORE VIOLATES THEIR
|
||||
|
||||
## Screenshots
|
||||
|
||||
[<img src="screenshots/shot_1.png" width=160>](screenshots/shot_1.png)
|
||||
[<img src="screenshots/shot_2.png" width=160>](screenshots/shot_2.png)
|
||||
[<img src="screenshots/shot_3.png" width=160>](screenshots/shot_3.png)
|
||||
[<img src="screenshots/shot_4.png" width=160>](screenshots/shot_4.png)
|
||||
[<img src="screenshots/shot_5.png" width=160>](screenshots/shot_5.png)
|
||||
[<img src="screenshots/shot_6.png" width=160>](screenshots/shot_6.png)
|
||||
[<img src="screenshots/shot_7.png" width=160>](screenshots/shot_7.png)
|
||||
[<img src="screenshots/shot_8.png" width=160>](screenshots/shot_8.png)
|
||||
[<img src="screenshots/shot_9.png" width=160>](screenshots/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_1.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_1.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="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_3.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_3.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="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_5.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_5.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="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_7.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_7.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="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_9.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_9.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
|
||||
|
||||
|
@ -185,7 +185,7 @@
|
||||
android:name=".RouterPopupActivity"
|
||||
android:label="@string/popup_mode_share_menu_title"
|
||||
android:taskAffinity=""
|
||||
android:theme="@android:style/Theme.NoDisplay">
|
||||
android:theme="@style/PopupPermissionsTheme">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH"/>
|
||||
|
@ -21,6 +21,7 @@ public class RouterPopupActivity extends RouterActivity {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|
||||
&& !PermissionHelper.checkSystemAlertWindowPermission(this)) {
|
||||
Toast.makeText(this, R.string.msg_popup_permission, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
StreamingService service;
|
||||
|
@ -50,6 +50,7 @@ public abstract class BaseStateFragment<I> extends BaseFragment implements ViewC
|
||||
protected Button errorButtonRetry;
|
||||
protected TextView errorTextView;
|
||||
|
||||
@State
|
||||
protected boolean useAsFrontPage = false;
|
||||
|
||||
@Override
|
||||
|
@ -36,6 +36,7 @@ import android.widget.FrameLayout;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.PopupMenu;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
@ -62,9 +63,10 @@ import org.schabi.newpipe.fragments.BackPressable;
|
||||
import org.schabi.newpipe.fragments.BaseStateFragment;
|
||||
import org.schabi.newpipe.history.HistoryListener;
|
||||
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.PopupVideoPlayer;
|
||||
import org.schabi.newpipe.player.helper.PlayerHelper;
|
||||
import org.schabi.newpipe.player.old.PlayVideoActivity;
|
||||
import org.schabi.newpipe.playlist.PlayQueue;
|
||||
import org.schabi.newpipe.playlist.SinglePlayQueue;
|
||||
@ -463,6 +465,11 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
|
||||
public void selected(StreamInfoItem selectedItem) {
|
||||
selectAndLoadVideo(selectedItem.service_id, selectedItem.url, selectedItem.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void held(StreamInfoItem selectedItem) {
|
||||
showStreamDialog(selectedItem);
|
||||
}
|
||||
});
|
||||
|
||||
videoTitleRoot.setOnClickListener(this);
|
||||
@ -480,6 +487,34 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
|
||||
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() {
|
||||
return new View.OnTouchListener() {
|
||||
@Override
|
||||
@ -796,16 +831,16 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
|
||||
((HistoryListener) activity).onVideoPlayed(currentInfo, getSelectedVideoStream());
|
||||
}
|
||||
|
||||
final PlayQueue playQueue = new SinglePlayQueue(currentInfo);
|
||||
final Intent intent;
|
||||
final PlayQueue itemQueue = new SinglePlayQueue(currentInfo);
|
||||
if (append) {
|
||||
Toast.makeText(activity, R.string.popup_playing_append, Toast.LENGTH_SHORT).show();
|
||||
intent = NavigationHelper.getPlayerEnqueueIntent(activity, PopupVideoPlayer.class, playQueue);
|
||||
NavigationHelper.enqueueOnPopupPlayer(activity, itemQueue);
|
||||
} else {
|
||||
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() {
|
||||
@ -824,13 +859,11 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
|
||||
|
||||
|
||||
private void openNormalBackgroundPlayer(final boolean append) {
|
||||
final PlayQueue playQueue = new SinglePlayQueue(currentInfo);
|
||||
final PlayQueue itemQueue = new SinglePlayQueue(currentInfo);
|
||||
if (append) {
|
||||
activity.startService(NavigationHelper.getPlayerEnqueueIntent(activity, BackgroundPlayer.class, playQueue));
|
||||
Toast.makeText(activity, R.string.background_player_append, Toast.LENGTH_SHORT).show();
|
||||
NavigationHelper.enqueueOnBackgroundPlayer(activity, itemQueue);
|
||||
} else {
|
||||
activity.startService(NavigationHelper.getPlayerIntent(activity, BackgroundPlayer.class, playQueue));
|
||||
Toast.makeText(activity, R.string.background_player_playing_toast, Toast.LENGTH_SHORT).show();
|
||||
NavigationHelper.playOnBackgroundPlayer(activity, itemQueue);
|
||||
}
|
||||
}
|
||||
|
||||
@ -870,8 +903,7 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
|
||||
|
||||
private void openNormalPlayer(VideoStream selectedVideoStream) {
|
||||
Intent mIntent;
|
||||
boolean useOldPlayer = PreferenceManager.getDefaultSharedPreferences(activity).getBoolean(getString(R.string.use_old_player_key), false)
|
||||
|| (Build.VERSION.SDK_INT < 16);
|
||||
boolean useOldPlayer = PlayerHelper.isUsingOldPlayer(activity) || (Build.VERSION.SDK_INT < 16);
|
||||
if (!useOldPlayer) {
|
||||
// ExoPlayer
|
||||
final PlayQueue playQueue = new SinglePlayQueue(currentInfo);
|
||||
|
@ -1,6 +1,7 @@
|
||||
package org.schabi.newpipe.fragments.list;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
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.OnScrollBelowItemsListener;
|
||||
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.playlist.SinglePlayQueue;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
import org.schabi.newpipe.util.StateSaver;
|
||||
|
||||
@ -139,6 +142,11 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
|
||||
useAsFrontPage?getParentFragment().getFragmentManager():getFragmentManager(),
|
||||
selectedItem.service_id, selectedItem.url, selectedItem.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void held(StreamInfoItem selectedItem) {
|
||||
showStreamDialog(selectedItem);
|
||||
}
|
||||
});
|
||||
|
||||
infoListAdapter.setOnChannelSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener<ChannelInfoItem>() {
|
||||
@ -149,6 +157,9 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
|
||||
useAsFrontPage?getParentFragment().getFragmentManager():getFragmentManager(),
|
||||
selectedItem.service_id, selectedItem.url, selectedItem.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void held(ChannelInfoItem selectedItem) {}
|
||||
});
|
||||
|
||||
infoListAdapter.setOnPlaylistSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener<PlaylistInfoItem>() {
|
||||
@ -159,6 +170,9 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
|
||||
useAsFrontPage?getParentFragment().getFragmentManager():getFragmentManager(),
|
||||
selectedItem.service_id, selectedItem.url, selectedItem.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void held(PlaylistInfoItem selectedItem) {}
|
||||
});
|
||||
|
||||
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
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
@ -1,8 +1,10 @@
|
||||
package org.schabi.newpipe.fragments.list.channel;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
@ -10,6 +12,7 @@ import android.support.v4.content.ContextCompat;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
@ -18,7 +21,9 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
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.channel.ChannelInfo;
|
||||
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.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.util.AnimationUtils;
|
||||
import org.schabi.newpipe.util.ExtractorHelper;
|
||||
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.concurrent.TimeUnit;
|
||||
@ -68,6 +80,11 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
||||
private TextView headerTitleView;
|
||||
private TextView headerSubscribersTextView;
|
||||
private Button headerSubscribeButton;
|
||||
private View playlistCtrl;
|
||||
|
||||
private LinearLayout headerPlayAllButton;
|
||||
private LinearLayout headerPopupButton;
|
||||
private LinearLayout headerBackgroundButton;
|
||||
|
||||
private MenuItem menuRssButton;
|
||||
|
||||
@ -124,10 +141,57 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
||||
headerTitleView = headerRootLayout.findViewById(R.id.channel_title_view);
|
||||
headerSubscribersTextView = headerRootLayout.findViewById(R.id.channel_subscriber_view);
|
||||
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;
|
||||
}
|
||||
|
||||
@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
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
@ -382,6 +446,7 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
||||
} else headerSubscribersTextView.setVisibility(View.GONE);
|
||||
|
||||
if (menuRssButton != null) menuRssButton.setVisible(!TextUtils.isEmpty(result.feed_url));
|
||||
playlistCtrl.setVisibility(View.VISIBLE);
|
||||
|
||||
if (!result.errors.isEmpty()) {
|
||||
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();
|
||||
updateSubscription(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
|
||||
|
@ -27,6 +27,7 @@ import org.schabi.newpipe.util.ExtractorHelper;
|
||||
import org.schabi.newpipe.util.KioskTranslator;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
|
||||
import icepick.State;
|
||||
import io.reactivex.Single;
|
||||
|
||||
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> {
|
||||
|
||||
private String kioskId = "";
|
||||
@State
|
||||
protected String kioskId = "";
|
||||
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
|
@ -1,6 +1,7 @@
|
||||
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.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
@ -13,7 +14,6 @@ import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
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.exceptions.ExtractionException;
|
||||
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.player.BackgroundPlayer;
|
||||
import org.schabi.newpipe.player.MainVideoPlayer;
|
||||
import org.schabi.newpipe.player.PopupVideoPlayer;
|
||||
import org.schabi.newpipe.playlist.ExternalPlayQueue;
|
||||
import org.schabi.newpipe.info_list.InfoItemDialog;
|
||||
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.util.ExtractorHelper;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
@ -50,10 +50,11 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
||||
private TextView headerUploaderName;
|
||||
private ImageView headerUploaderAvatar;
|
||||
private TextView headerStreamCount;
|
||||
private View playlistCtrl;
|
||||
|
||||
private Button headerPlayAllButton;
|
||||
private Button headerPopupButton;
|
||||
private Button headerBackgroundButton;
|
||||
private View headerPlayAllButton;
|
||||
private View headerPopupButton;
|
||||
private View headerBackgroundButton;
|
||||
|
||||
public static PlaylistFragment getInstance(int serviceId, String url, String name) {
|
||||
PlaylistFragment instance = new PlaylistFragment();
|
||||
@ -81,10 +82,11 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
||||
headerUploaderName = headerRootLayout.findViewById(R.id.uploader_name);
|
||||
headerUploaderAvatar = headerRootLayout.findViewById(R.id.uploader_avatar_view);
|
||||
headerStreamCount = headerRootLayout.findViewById(R.id.playlist_stream_count);
|
||||
playlistCtrl = headerRootLayout.findViewById(R.id.playlist_control);
|
||||
|
||||
headerPlayAllButton = headerRootLayout.findViewById(R.id.playlist_play_all_button);
|
||||
headerPopupButton = headerRootLayout.findViewById(R.id.playlist_play_popup_button);
|
||||
headerBackgroundButton = headerRootLayout.findViewById(R.id.playlist_play_bg_button);
|
||||
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;
|
||||
}
|
||||
@ -103,6 +105,47 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
||||
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
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
@ -150,6 +193,8 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
||||
}
|
||||
}
|
||||
|
||||
playlistCtrl.setVisibility(View.VISIBLE);
|
||||
|
||||
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));
|
||||
|
||||
@ -160,7 +205,7 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
||||
headerPlayAllButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
startActivity(buildPlaylistIntent(MainVideoPlayer.class));
|
||||
NavigationHelper.playOnMainPlayer(activity, getPlayQueue());
|
||||
}
|
||||
});
|
||||
headerPopupButton.setOnClickListener(new View.OnClickListener() {
|
||||
@ -173,26 +218,29 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
||||
toast.show();
|
||||
return;
|
||||
}
|
||||
activity.startService(buildPlaylistIntent(PopupVideoPlayer.class));
|
||||
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue());
|
||||
}
|
||||
});
|
||||
headerBackgroundButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
activity.startService(buildPlaylistIntent(BackgroundPlayer.class));
|
||||
NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Intent buildPlaylistIntent(final Class targetClazz) {
|
||||
final PlayQueue playQueue = new ExternalPlayQueue(
|
||||
private PlayQueue getPlayQueue() {
|
||||
return getPlayQueue(0);
|
||||
}
|
||||
|
||||
private PlayQueue getPlayQueue(final int index) {
|
||||
return new PlaylistPlayQueue(
|
||||
currentInfo.service_id,
|
||||
currentInfo.url,
|
||||
currentInfo.next_streams_url,
|
||||
infoListAdapter.getItemsList(),
|
||||
0
|
||||
index
|
||||
);
|
||||
return NavigationHelper.getPlayerIntent(activity, targetClazz, playQueue);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -165,7 +165,7 @@ public class SearchFragment extends BaseListFragment<SearchResult, ListExtractor
|
||||
suggestionListAdapter = new SuggestionListAdapter(activity);
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity);
|
||||
isSearchHistoryEnabled = preferences.getBoolean(getString(R.string.enable_search_history_key), true);
|
||||
suggestionListAdapter.setShowSugestinHistory(isSearchHistoryEnabled);
|
||||
suggestionListAdapter.setShowSuggestionHistory(isSearchHistoryEnabled);
|
||||
|
||||
searchHistoryDAO = NewPipeDatabase.getInstance().searchHistoryDAO();
|
||||
}
|
||||
@ -446,6 +446,12 @@ public class SearchFragment extends BaseListFragment<SearchResult, ListExtractor
|
||||
searchEditText.setText(item.query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuggestionItemInserted(SuggestionItem item) {
|
||||
searchEditText.setText(item.query);
|
||||
searchEditText.setSelection(searchEditText.getText().length());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuggestionItemLongClick(SuggestionItem item) {
|
||||
if (item.fromHistory) showDeleteSuggestionDialog(item);
|
||||
|
@ -19,10 +19,11 @@ public class SuggestionListAdapter extends RecyclerView.Adapter<SuggestionListAd
|
||||
private final ArrayList<SuggestionItem> items = new ArrayList<>();
|
||||
private final Context context;
|
||||
private OnSuggestionItemSelected listener;
|
||||
private boolean showSugestinHistory = true;
|
||||
private boolean showSuggestionHistory = true;
|
||||
|
||||
public interface OnSuggestionItemSelected {
|
||||
void onSuggestionItemSelected(SuggestionItem item);
|
||||
void onSuggestionItemInserted(SuggestionItem item);
|
||||
void onSuggestionItemLongClick(SuggestionItem item);
|
||||
}
|
||||
|
||||
@ -32,7 +33,7 @@ public class SuggestionListAdapter extends RecyclerView.Adapter<SuggestionListAd
|
||||
|
||||
public void setItems(List<SuggestionItem> items) {
|
||||
this.items.clear();
|
||||
if (showSugestinHistory) {
|
||||
if (showSuggestionHistory) {
|
||||
this.items.addAll(items);
|
||||
} else {
|
||||
// remove history items if history is disabled
|
||||
@ -49,8 +50,8 @@ public class SuggestionListAdapter extends RecyclerView.Adapter<SuggestionListAd
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public void setShowSugestinHistory(boolean v) {
|
||||
showSugestinHistory = v;
|
||||
public void setShowSuggestionHistory(boolean v) {
|
||||
showSuggestionHistory = v;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -62,19 +63,25 @@ public class SuggestionListAdapter extends RecyclerView.Adapter<SuggestionListAd
|
||||
public void onBindViewHolder(SuggestionItemHolder holder, int position) {
|
||||
final SuggestionItem currentItem = getItem(position);
|
||||
holder.updateFrom(currentItem);
|
||||
holder.itemView.setOnClickListener(new View.OnClickListener() {
|
||||
holder.queryView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (listener != null) listener.onSuggestionItemSelected(currentItem);
|
||||
}
|
||||
});
|
||||
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
|
||||
holder.queryView.setOnLongClickListener(new View.OnLongClickListener() {
|
||||
@Override
|
||||
public boolean onLongClick(View v) {
|
||||
if (listener != null) listener.onSuggestionItemLongClick(currentItem);
|
||||
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) {
|
||||
@ -93,6 +100,8 @@ public class SuggestionListAdapter extends RecyclerView.Adapter<SuggestionListAd
|
||||
public static class SuggestionItemHolder extends RecyclerView.ViewHolder {
|
||||
private final TextView itemSuggestionQuery;
|
||||
private final ImageView suggestionIcon;
|
||||
private final View queryView;
|
||||
private final View insertView;
|
||||
|
||||
// Cache some ids, as they can potentially be constantly updated/recycled
|
||||
private final int historyResId;
|
||||
@ -103,6 +112,9 @@ public class SuggestionListAdapter extends RecyclerView.Adapter<SuggestionListAd
|
||||
suggestionIcon = rootView.findViewById(R.id.item_suggestion_icon);
|
||||
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);
|
||||
searchResId = resolveResourceIdFromAttr(rootView.getContext(), R.attr.search);
|
||||
}
|
||||
|
@ -19,8 +19,6 @@ import org.schabi.newpipe.fragments.BaseStateFragment;
|
||||
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
||||
import org.schabi.newpipe.info_list.InfoListAdapter;
|
||||
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 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);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void held(ChannelInfoItem selectedItem) {}
|
||||
});
|
||||
|
||||
headerRootLayout.setOnClickListener(new View.OnClickListener() {
|
||||
|
@ -44,6 +44,7 @@ public class InfoItemBuilder {
|
||||
|
||||
public interface OnInfoItemSelectedListener<T extends InfoItem> {
|
||||
void selected(T selectedItem);
|
||||
void held(T selectedItem);
|
||||
}
|
||||
|
||||
private final Context context;
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -67,6 +67,38 @@ public class StreamMiniInfoItemHolder extends InfoItemHolder {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
switch (item.stream_type) {
|
||||
case AUDIO_STREAM:
|
||||
case VIDEO_STREAM:
|
||||
case FILE:
|
||||
enableLongClick(item);
|
||||
break;
|
||||
case LIVE_STREAM:
|
||||
case AUDIO_LIVE_STREAM:
|
||||
case NONE:
|
||||
default:
|
||||
disableLongClick();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void enableLongClick(final StreamInfoItem item) {
|
||||
itemView.setLongClickable(true);
|
||||
itemView.setOnLongClickListener(new View.OnLongClickListener() {
|
||||
@Override
|
||||
public boolean onLongClick(View view) {
|
||||
if (itemBuilder.getOnStreamSelectedListener() != null) {
|
||||
itemBuilder.getOnStreamSelectedListener().held(item);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void disableLongClick() {
|
||||
itemView.setLongClickable(false);
|
||||
itemView.setOnLongClickListener(null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -48,6 +48,7 @@ import org.schabi.newpipe.player.event.PlayerEventListener;
|
||||
import org.schabi.newpipe.player.helper.LockManager;
|
||||
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||
import org.schabi.newpipe.util.ListHelper;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
import org.schabi.newpipe.util.ThemeHelper;
|
||||
|
||||
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_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_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 LockManager lockManager;
|
||||
@ -130,16 +135,6 @@ public final class BackgroundPlayer extends Service {
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// 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() {
|
||||
if (DEBUG) Log.d(TAG, "onClose() called");
|
||||
|
||||
@ -191,6 +186,8 @@ public final class BackgroundPlayer extends Service {
|
||||
}
|
||||
|
||||
private void setupNotification(RemoteViews remoteViews) {
|
||||
if (basePlayerImpl == null) return;
|
||||
|
||||
remoteViews.setTextViewText(R.id.notificationSongName, basePlayerImpl.getVideoTitle());
|
||||
remoteViews.setTextViewText(R.id.notificationArtist, basePlayerImpl.getUploaderName());
|
||||
|
||||
@ -203,10 +200,21 @@ public final class BackgroundPlayer extends Service {
|
||||
remoteViews.setOnClickPendingIntent(R.id.notificationRepeat,
|
||||
PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_REPEAT), 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));
|
||||
if (basePlayerImpl.playQueue != null && basePlayerImpl.playQueue.size() > 1) {
|
||||
remoteViews.setInt(R.id.notificationFRewind, SET_IMAGE_RESOURCE_METHOD, R.drawable.exo_controls_previous);
|
||||
remoteViews.setInt(R.id.notificationFForward, SET_IMAGE_RESOURCE_METHOD, R.drawable.exo_controls_next);
|
||||
remoteViews.setOnClickPendingIntent(R.id.notificationFRewind,
|
||||
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());
|
||||
}
|
||||
@ -241,17 +249,15 @@ public final class BackgroundPlayer extends Service {
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private void setRepeatModeIcon(final RemoteViews remoteViews, final int repeatMode) {
|
||||
final String methodName = "setImageResource";
|
||||
|
||||
switch (repeatMode) {
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -372,6 +378,7 @@ public final class BackgroundPlayer extends Service {
|
||||
|
||||
@Override
|
||||
public void sync(@NonNull final PlayQueueItem item, @Nullable final StreamInfo info) {
|
||||
if (currentItem == item && currentInfo == info) return;
|
||||
super.sync(item, info);
|
||||
|
||||
resetNotification();
|
||||
@ -380,9 +387,10 @@ public final class BackgroundPlayer extends Service {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public MediaSource sourceOf(final PlayQueueItem item, final StreamInfo info) {
|
||||
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);
|
||||
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_PLAY_PREVIOUS);
|
||||
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_OFF);
|
||||
@ -469,7 +479,7 @@ public final class BackgroundPlayer extends Service {
|
||||
onVideoPlayPause();
|
||||
break;
|
||||
case ACTION_OPEN_CONTROLS:
|
||||
openControl(getApplicationContext());
|
||||
NavigationHelper.openBackgroundPlayerControl(getApplicationContext());
|
||||
break;
|
||||
case ACTION_REPEAT:
|
||||
onRepeatClicked();
|
||||
@ -480,6 +490,12 @@ public final class BackgroundPlayer extends Service {
|
||||
case ACTION_PLAY_PREVIOUS:
|
||||
onPlayPrevious();
|
||||
break;
|
||||
case ACTION_FAST_FORWARD:
|
||||
onFastForward();
|
||||
break;
|
||||
case ACTION_FAST_REWIND:
|
||||
onFastRewind();
|
||||
break;
|
||||
case Intent.ACTION_SCREEN_ON:
|
||||
onScreenOnOff(true);
|
||||
break;
|
||||
|
@ -26,6 +26,7 @@ import android.content.IntentFilter;
|
||||
import android.graphics.Bitmap;
|
||||
import android.media.AudioManager;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
@ -76,7 +77,6 @@ import java.util.concurrent.TimeUnit;
|
||||
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.annotations.NonNull;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.functions.Consumer;
|
||||
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 PLAY_PREV_ACTIVATION_LIMIT = 5000; // 5 seconds
|
||||
protected final static int PROGRESS_LOOP_INTERVAL = 500;
|
||||
protected final static int RECOVERY_SKIP_THRESHOLD = 3000; // 3 seconds
|
||||
|
||||
protected SimpleExoPlayer simpleExoPlayer;
|
||||
protected AudioReactor audioReactor;
|
||||
@ -193,7 +194,7 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.filter(new Predicate<Long>() {
|
||||
@Override
|
||||
public boolean test(@NonNull Long aLong) throws Exception {
|
||||
public boolean test(Long aLong) throws Exception {
|
||||
return isProgressLoopRunning();
|
||||
}
|
||||
})
|
||||
@ -235,7 +236,7 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
|
||||
initPlayback(queue);
|
||||
}
|
||||
|
||||
protected void initPlayback(@NonNull final PlayQueue queue) {
|
||||
protected void initPlayback(final PlayQueue queue) {
|
||||
playQueue = queue;
|
||||
playQueue.init();
|
||||
playbackManager = new MediaSourceManager(this, playQueue);
|
||||
@ -453,16 +454,20 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
|
||||
final PlayQueueItem currentSourceItem = playQueue.getItem();
|
||||
|
||||
// Check if already playing correct window
|
||||
final boolean isCurrentWindowCorrect = simpleExoPlayer.getCurrentWindowIndex() == currentSourceIndex;
|
||||
final boolean isCurrentWindowCorrect =
|
||||
simpleExoPlayer.getCurrentWindowIndex() == currentSourceIndex;
|
||||
|
||||
// Check if recovering
|
||||
if (isCurrentWindowCorrect && currentSourceItem != null &&
|
||||
currentSourceItem.getRecoveryPosition() != PlayQueueItem.RECOVERY_UNSET) {
|
||||
if (isCurrentWindowCorrect && currentSourceItem != null) {
|
||||
/* Recovering with sub-second position may cause a long buffer delay in ExoPlayer,
|
||||
* rounding this position to the nearest second will help alleviate this.*/
|
||||
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());
|
||||
playQueue.unsetRecovery(currentSourceIndex);
|
||||
}
|
||||
@ -515,7 +520,6 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
|
||||
break;
|
||||
case Player.STATE_READY: //3
|
||||
recover();
|
||||
|
||||
if (!isPrepared) {
|
||||
isPrepared = true;
|
||||
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.
|
||||
*
|
||||
* 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},
|
||||
* we reset and prepare the media source again to resume playback.<br><br>
|
||||
* In the event that this error is produced during a valid stream playback, we save the
|
||||
* 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>
|
||||
* 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>
|
||||
* If the renderer failed, treat the error as unrecoverable.
|
||||
@ -569,6 +577,10 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
|
||||
|
||||
switch (error.type) {
|
||||
case ExoPlaybackException.TYPE_SOURCE:
|
||||
if (simpleExoPlayer.getCurrentPosition() <
|
||||
simpleExoPlayer.getDuration() - RECOVERY_SKIP_THRESHOLD) {
|
||||
setRecovery();
|
||||
}
|
||||
playQueue.error(isCurrentWindowValid());
|
||||
showStreamError(error);
|
||||
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 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.
|
||||
if (newWindowIndex != playQueue.getIndex() && playbackManager != null) {
|
||||
if (newWindowIndex == playQueue.getIndex() + 1) {
|
||||
playQueue.offsetIndex(+1);
|
||||
playbackManager.load();
|
||||
}
|
||||
playbackManager.load();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -613,6 +625,8 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
|
||||
if (simpleExoPlayer == null) return;
|
||||
if (DEBUG) Log.d(TAG, "Blocking...");
|
||||
|
||||
currentItem = null;
|
||||
currentInfo = null;
|
||||
simpleExoPlayer.stop();
|
||||
isPrepared = false;
|
||||
|
||||
@ -631,17 +645,21 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sync(@android.support.annotation.NonNull final PlayQueueItem item,
|
||||
public void sync(@NonNull final PlayQueueItem item,
|
||||
@Nullable final StreamInfo info) {
|
||||
if (simpleExoPlayer == null) return;
|
||||
if (DEBUG) Log.d(TAG, "Syncing...");
|
||||
|
||||
if (currentItem == item && currentInfo == info) return;
|
||||
currentItem = item;
|
||||
currentInfo = info;
|
||||
|
||||
if (DEBUG) Log.d(TAG, "Syncing...");
|
||||
if (simpleExoPlayer == null) return;
|
||||
|
||||
// Check if on wrong window
|
||||
final int currentSourceIndex = playQueue.getIndex();
|
||||
if (simpleExoPlayer.getCurrentWindowIndex() != currentSourceIndex) {
|
||||
final int currentSourceIndex = playQueue.indexOf(item);
|
||||
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;
|
||||
if (DEBUG) Log.d(TAG, "Rewinding to correct window: " + currentSourceIndex + " at: " + getTimeString((int)startPos));
|
||||
simpleExoPlayer.seekTo(currentSourceIndex, startPos);
|
||||
@ -756,10 +774,6 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
|
||||
} else {
|
||||
playQueue.setIndex(index);
|
||||
}
|
||||
|
||||
if (!isPlaying()) {
|
||||
onVideoPlayPause();
|
||||
}
|
||||
}
|
||||
|
||||
public void seekBy(int milliSeconds) {
|
||||
@ -826,15 +840,15 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
|
||||
}
|
||||
|
||||
public String getVideoUrl() {
|
||||
return currentItem == null ? null : currentItem.getUrl();
|
||||
return currentItem == null ? context.getString(R.string.unknown_content) : currentItem.getUrl();
|
||||
}
|
||||
|
||||
public String getVideoTitle() {
|
||||
return currentItem == null ? null : currentItem.getTitle();
|
||||
return currentItem == null ? context.getString(R.string.unknown_content) : currentItem.getTitle();
|
||||
}
|
||||
|
||||
public String getUploaderName() {
|
||||
return currentItem == null ? null : currentItem.getUploader();
|
||||
return currentItem == null ? context.getString(R.string.unknown_content) : currentItem.getUploader();
|
||||
}
|
||||
|
||||
public boolean isCompleted() {
|
||||
@ -870,8 +884,10 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
|
||||
}
|
||||
|
||||
public PlaybackParameters getPlaybackParameters() {
|
||||
final PlaybackParameters defaultParameters = new PlaybackParameters(1f, 1f);
|
||||
if (simpleExoPlayer == null) return defaultParameters;
|
||||
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) {
|
||||
@ -900,7 +916,9 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
|
||||
final int queuePos = playQueue.getIndex();
|
||||
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) {
|
||||
|
@ -23,6 +23,7 @@ import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Color;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Build;
|
||||
@ -33,6 +34,7 @@ import android.support.v7.widget.RecyclerView;
|
||||
import android.support.v7.widget.helper.ItemTouchHelper;
|
||||
import android.util.Log;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.MenuItem;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
@ -48,6 +50,7 @@ import com.google.android.exoplayer2.Player;
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||
import org.schabi.newpipe.extractor.stream.VideoStream;
|
||||
import org.schabi.newpipe.fragments.OnScrollBelowItemsListener;
|
||||
import org.schabi.newpipe.player.helper.PlayerHelper;
|
||||
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||
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.ThemeHelper;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
|
||||
import static org.schabi.newpipe.util.AnimationUtils.animateView;
|
||||
@ -150,6 +155,17 @@ public final class MainVideoPlayer extends Activity {
|
||||
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
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
@ -222,7 +238,6 @@ public final class MainVideoPlayer extends Activity {
|
||||
private ImageButton repeatButton;
|
||||
private ImageButton shuffleButton;
|
||||
|
||||
private ImageButton screenRotationButton;
|
||||
private ImageButton playPauseButton;
|
||||
private ImageButton playPreviousButton;
|
||||
private ImageButton playNextButton;
|
||||
@ -234,6 +249,10 @@ public final class MainVideoPlayer extends Activity {
|
||||
|
||||
private boolean queueVisible;
|
||||
|
||||
private ImageButton moreOptionsButton;
|
||||
public int moreOptionsPopupMenuGroupId = 89;
|
||||
public PopupMenu moreOptionsPopupMenu;
|
||||
|
||||
VideoPlayerImpl(final Context context) {
|
||||
super("VideoPlayerImpl" + MainVideoPlayer.TAG, context);
|
||||
}
|
||||
@ -249,10 +268,12 @@ public final class MainVideoPlayer extends Activity {
|
||||
this.repeatButton = rootView.findViewById(R.id.repeatButton);
|
||||
this.shuffleButton = rootView.findViewById(R.id.shuffleButton);
|
||||
|
||||
this.screenRotationButton = rootView.findViewById(R.id.screenRotationButton);
|
||||
this.playPauseButton = rootView.findViewById(R.id.playPauseButton);
|
||||
this.playPreviousButton = rootView.findViewById(R.id.playPreviousButton);
|
||||
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);
|
||||
channelTextView.setSelected(true);
|
||||
@ -276,7 +297,7 @@ public final class MainVideoPlayer extends Activity {
|
||||
playPauseButton.setOnClickListener(this);
|
||||
playPreviousButton.setOnClickListener(this);
|
||||
playNextButton.setOnClickListener(this);
|
||||
screenRotationButton.setOnClickListener(this);
|
||||
moreOptionsButton.setOnClickListener(this);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
@ -348,6 +369,28 @@ public final class MainVideoPlayer extends Activity {
|
||||
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
|
||||
public void onClick(View v) {
|
||||
super.onClick(v);
|
||||
@ -360,9 +403,6 @@ public final class MainVideoPlayer extends Activity {
|
||||
} else if (v.getId() == playNextButton.getId()) {
|
||||
onPlayNext();
|
||||
|
||||
} else if (v.getId() == screenRotationButton.getId()) {
|
||||
onScreenRotationClicked();
|
||||
|
||||
} else if (v.getId() == queueButton.getId()) {
|
||||
onQueueClicked();
|
||||
return;
|
||||
@ -372,6 +412,8 @@ public final class MainVideoPlayer extends Activity {
|
||||
} else if (v.getId() == shuffleButton.getId()) {
|
||||
onShuffleClicked();
|
||||
return;
|
||||
} else if (v.getId() == moreOptionsButton.getId()) {
|
||||
onMoreOptionsClicked();
|
||||
}
|
||||
|
||||
if (getCurrentState() != STATE_COMPLETED) {
|
||||
@ -397,7 +439,7 @@ public final class MainVideoPlayer extends Activity {
|
||||
getControlsRoot().setVisibility(View.INVISIBLE);
|
||||
queueLayout.setVisibility(View.VISIBLE);
|
||||
|
||||
itemsList.smoothScrollToPosition(playQueue.getIndex());
|
||||
itemsList.scrollToPosition(playQueue.getIndex());
|
||||
}
|
||||
|
||||
private void onQueueClosed() {
|
||||
@ -405,6 +447,32 @@ public final class MainVideoPlayer extends Activity {
|
||||
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() {
|
||||
if (DEBUG) Log.d(TAG, "onScreenRotationClicked() called");
|
||||
toggleOrientation();
|
||||
@ -555,6 +623,27 @@ public final class MainVideoPlayer extends Activity {
|
||||
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() {
|
||||
queueLayout = findViewById(R.id.playQueuePanel);
|
||||
|
||||
@ -565,6 +654,9 @@ public final class MainVideoPlayer extends Activity {
|
||||
itemsList.setClickable(true);
|
||||
itemsList.setLongClickable(true);
|
||||
|
||||
itemsList.clearOnScrollListeners();
|
||||
itemsList.addOnScrollListener(getQueueScrollListener());
|
||||
|
||||
itemTouchHelper = new ItemTouchHelper(getItemTouchCallback());
|
||||
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() {
|
||||
return new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN, 0) {
|
||||
@Override
|
||||
@ -785,4 +890,4 @@ public final class MainVideoPlayer extends Activity {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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.VideoStream;
|
||||
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.helper.LockManager;
|
||||
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||
@ -96,7 +97,6 @@ import static org.schabi.newpipe.util.AnimationUtils.animateView;
|
||||
public final class PopupVideoPlayer extends Service {
|
||||
private static final String TAG = ".PopupVideoPlayer";
|
||||
private static final boolean DEBUG = BasePlayer.DEBUG;
|
||||
private static final int SHUTDOWN_FLING_VELOCITY = 10000;
|
||||
|
||||
private static final int NOTIFICATION_ID = 40028922;
|
||||
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 GestureDetector gestureDetector;
|
||||
|
||||
private int shutdownFlingVelocity;
|
||||
private int tossFlingVelocity;
|
||||
|
||||
private float screenWidth, screenHeight;
|
||||
private float popupWidth, popupHeight;
|
||||
|
||||
@ -211,12 +214,14 @@ public final class PopupVideoPlayer extends Service {
|
||||
View rootView = View.inflate(this, R.layout.player_popup, null);
|
||||
playerImpl.setup(rootView);
|
||||
|
||||
shutdownFlingVelocity = PlayerHelper.getShutdownFlingVelocity(this);
|
||||
tossFlingVelocity = PlayerHelper.getTossFlingVelocity(this);
|
||||
|
||||
updateScreenSize();
|
||||
|
||||
final boolean popupRememberSizeAndPos = PlayerHelper.isRememberingPopupDimensions(this);
|
||||
final float defaultSize = getResources().getDimension(R.dimen.popup_default_width);
|
||||
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;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
@ -366,6 +362,7 @@ public final class PopupVideoPlayer extends Service {
|
||||
}
|
||||
|
||||
private void updatePopupSize(int width, int height) {
|
||||
if (playerImpl == null) return;
|
||||
if (DEBUG) Log.d(TAG, "updatePopupSize() called with: width = [" + width + "], height = [" + height + "]");
|
||||
|
||||
width = (int) (width > maximumWidth ? maximumWidth : width < minimumWidth ? minimumWidth : width);
|
||||
@ -577,6 +574,7 @@ public final class PopupVideoPlayer extends Service {
|
||||
|
||||
@Override
|
||||
public void sync(@NonNull PlayQueueItem item, @Nullable StreamInfo info) {
|
||||
if (currentItem == item && currentInfo == info) return;
|
||||
super.sync(item, info);
|
||||
updateMetadata();
|
||||
}
|
||||
@ -617,7 +615,7 @@ public final class PopupVideoPlayer extends Service {
|
||||
onVideoPlayPause();
|
||||
break;
|
||||
case ACTION_OPEN_CONTROLS:
|
||||
openControl(getApplicationContext());
|
||||
NavigationHelper.openPopupPlayerControl(getApplicationContext());
|
||||
break;
|
||||
case ACTION_REPEAT:
|
||||
onRepeatClicked();
|
||||
@ -791,11 +789,20 @@ public final class PopupVideoPlayer extends Service {
|
||||
|
||||
@Override
|
||||
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 (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();
|
||||
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;
|
||||
}
|
||||
@ -835,6 +842,8 @@ public final class PopupVideoPlayer extends Service {
|
||||
}
|
||||
savePositionAndSize();
|
||||
}
|
||||
|
||||
v.performClick();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -873,23 +882,25 @@ public final class PopupVideoPlayer extends Service {
|
||||
private final Context context;
|
||||
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.context = context;
|
||||
this.url = url;
|
||||
this.serviceId = serviceId;
|
||||
}
|
||||
|
||||
/*package-private*/ void onReceive(final StreamInfo info) {
|
||||
private void onReceive(final StreamInfo info) {
|
||||
mainHandler.post(new Runnable() {
|
||||
@Override
|
||||
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 + "]");
|
||||
exception.printStackTrace();
|
||||
mainHandler.post(new Runnable() {
|
||||
@ -915,7 +926,7 @@ public final class PopupVideoPlayer extends Service {
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
/*package-private*/ void onReCaptchaException() {
|
||||
private void onReCaptchaException() {
|
||||
Toast.makeText(context, R.string.recaptcha_request_toast, Toast.LENGTH_LONG).show();
|
||||
// Starting ReCaptcha Challenge Activity
|
||||
Intent intent = new Intent(context, ReCaptchaActivity.class);
|
||||
|
@ -28,6 +28,7 @@ import com.google.android.exoplayer2.Player;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||
import org.schabi.newpipe.fragments.OnScrollBelowItemsListener;
|
||||
import org.schabi.newpipe.player.event.PlayerEventListener;
|
||||
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||
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_PITCH_POPUP_MENU_GROUP_ID = 97;
|
||||
|
||||
private static final int SMOOTH_SCROLL_MAXIMUM_DISTANCE = 80;
|
||||
|
||||
private View rootView;
|
||||
|
||||
private RecyclerView itemsList;
|
||||
@ -225,6 +228,8 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
||||
itemsList.setAdapter(player.getPlayQueueAdapter());
|
||||
itemsList.setClickable(true);
|
||||
itemsList.setLongClickable(true);
|
||||
itemsList.clearOnScrollListeners();
|
||||
itemsList.addOnScrollListener(getQueueScrollListener());
|
||||
|
||||
itemTouchHelper = new ItemTouchHelper(getItemTouchCallback());
|
||||
itemTouchHelper.attachToRecyclerView(itemsList);
|
||||
@ -286,6 +291,8 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
||||
item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem menuItem) {
|
||||
if (player == null) return false;
|
||||
|
||||
player.setPlaybackSpeed(playbackSpeed);
|
||||
return true;
|
||||
}
|
||||
@ -304,6 +311,8 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
||||
item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem menuItem) {
|
||||
if (player == null) return false;
|
||||
|
||||
player.setPlaybackPitch(playbackPitch);
|
||||
return true;
|
||||
}
|
||||
@ -317,6 +326,8 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
||||
remove.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem menuItem) {
|
||||
if (player == null) return false;
|
||||
|
||||
final int index = player.getPlayQueue().indexOf(item);
|
||||
if (index != -1) player.getPlayQueue().remove(index);
|
||||
return true;
|
||||
@ -339,6 +350,19 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
||||
// 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() {
|
||||
return new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN, 0) {
|
||||
@Override
|
||||
@ -349,7 +373,7 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
||||
|
||||
final int sourceIndex = source.getLayoutPosition();
|
||||
final int targetIndex = target.getLayoutPosition();
|
||||
player.getPlayQueue().move(sourceIndex, targetIndex);
|
||||
if (player != null) player.getPlayQueue().move(sourceIndex, targetIndex);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -372,11 +396,13 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
||||
return new PlayQueueItemBuilder.OnSelectedListener() {
|
||||
@Override
|
||||
public void selected(PlayQueueItem item, View view) {
|
||||
player.onSelected(item);
|
||||
if (player != null) player.onSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void held(PlayQueueItem item, View view) {
|
||||
if (player == null) return;
|
||||
|
||||
final int index = player.getPlayQueue().indexOf(item);
|
||||
if (index != -1) buildItemPopupMenu(item, view);
|
||||
}
|
||||
@ -393,7 +419,23 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
||||
}
|
||||
|
||||
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
|
||||
public void onClick(View view) {
|
||||
if (player == null) return;
|
||||
|
||||
if (view.getId() == repeatButton.getId()) {
|
||||
player.onRepeatClicked();
|
||||
|
||||
@ -450,7 +494,7 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
player.simpleExoPlayer.seekTo(seekBar.getProgress());
|
||||
if (player != null) player.simpleExoPlayer.seekTo(seekBar.getProgress());
|
||||
seekDisplay.setVisibility(View.GONE);
|
||||
seeking = false;
|
||||
}
|
||||
|
@ -124,12 +124,11 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
|
||||
|
||||
private View topControlsRoot;
|
||||
private TextView qualityTextView;
|
||||
private ImageButton fullScreenButton;
|
||||
|
||||
private ValueAnimator controlViewAnimator;
|
||||
private Handler controlsVisibilityHandler = new Handler();
|
||||
|
||||
private boolean isSomePopupMenuVisible = false;
|
||||
boolean isSomePopupMenuVisible = false;
|
||||
private int qualityPopupMenuGroupId = 69;
|
||||
private PopupMenu qualityPopupMenu;
|
||||
|
||||
@ -166,7 +165,6 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
|
||||
this.bottomControlsRoot = rootView.findViewById(R.id.bottomControls);
|
||||
this.topControlsRoot = rootView.findViewById(R.id.topControls);
|
||||
this.qualityTextView = rootView.findViewById(R.id.qualityTextView);
|
||||
this.fullScreenButton = rootView.findViewById(R.id.fullScreenButton);
|
||||
|
||||
//this.aspectRatioFrameLayout.setAspectRatio(16.0f / 9.0f);
|
||||
|
||||
@ -186,7 +184,6 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
|
||||
super.initListeners();
|
||||
playbackSeekBar.setOnSeekBarChangeListener(this);
|
||||
playbackSpeedTextView.setOnClickListener(this);
|
||||
fullScreenButton.setOnClickListener(this);
|
||||
qualityTextView.setOnClickListener(this);
|
||||
}
|
||||
|
||||
@ -272,17 +269,18 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
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 VideoStream video;
|
||||
final int index;
|
||||
if (playbackQuality == null) {
|
||||
final int index = getDefaultResolutionIndex(videos);
|
||||
video = videos.get(index);
|
||||
index = getDefaultResolutionIndex(videos);
|
||||
} else {
|
||||
final int index = getOverrideResolutionIndex(videos, getPlaybackQuality());
|
||||
video = videos.get(index);
|
||||
index = getOverrideResolutionIndex(videos, getPlaybackQuality());
|
||||
}
|
||||
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 AudioStream audio = ListHelper.getHighestQualityAudio(info.audio_streams);
|
||||
@ -453,9 +451,7 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (DEBUG) Log.d(TAG, "onClick() called with: v = [" + v + "]");
|
||||
if (v.getId() == fullScreenButton.getId()) {
|
||||
onFullScreenButtonClicked();
|
||||
} else if (v.getId() == qualityTextView.getId()) {
|
||||
if (v.getId() == qualityTextView.getId()) {
|
||||
onQualitySelectorClicked();
|
||||
} else if (v.getId() == playbackSpeedTextView.getId()) {
|
||||
onPlaybackSpeedClicked();
|
||||
@ -753,14 +749,14 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
|
||||
return qualityTextView;
|
||||
}
|
||||
|
||||
public ImageButton getFullScreenButton() {
|
||||
return fullScreenButton;
|
||||
}
|
||||
|
||||
public PopupMenu getQualityPopupMenu() {
|
||||
return qualityPopupMenu;
|
||||
}
|
||||
|
||||
public PopupMenu getPlaybackSpeedPopupMenu() {
|
||||
return playbackSpeedPopupMenu;
|
||||
}
|
||||
|
||||
public View getSurfaceForeground() {
|
||||
return surfaceForeground;
|
||||
}
|
||||
|
@ -12,6 +12,8 @@ import java.text.NumberFormat;
|
||||
import java.util.Formatter;
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class PlayerHelper {
|
||||
private PlayerHelper() {}
|
||||
|
||||
@ -56,6 +58,10 @@ public class PlayerHelper {
|
||||
return isUsingOldPlayer(context, false);
|
||||
}
|
||||
|
||||
public static boolean isRememberingPopupDimensions(@Nonnull final Context context) {
|
||||
return isRememberingPopupDimensions(context, true);
|
||||
}
|
||||
|
||||
public static long getPreferredCacheSize(@NonNull final Context context) {
|
||||
return 64 * 1024 * 1024L;
|
||||
}
|
||||
@ -83,6 +89,15 @@ public class PlayerHelper {
|
||||
public static boolean isUsingDSP(@NonNull final Context context) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static int getShutdownFlingVelocity(@Nonnull final Context context) {
|
||||
return 10000;
|
||||
}
|
||||
|
||||
public static int getTossFlingVelocity(@Nonnull final Context context) {
|
||||
return 2500;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Private helpers
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
@ -103,4 +118,8 @@ public class PlayerHelper {
|
||||
private static boolean isUsingOldPlayer(@NonNull final Context context, final boolean b) {
|
||||
return getPreferences(context).getBoolean(context.getString(R.string.use_old_player_key), b);
|
||||
}
|
||||
|
||||
private static boolean isRememberingPopupDimensions(@Nonnull final Context context, final boolean b) {
|
||||
return getPreferences(context).getBoolean(context.getString(R.string.popup_remember_size_pos_key), b);
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ import io.reactivex.subjects.PublishSubject;
|
||||
public class MediaSourceManager {
|
||||
private final String TAG = "MediaSourceManager@" + Integer.toHexString(hashCode());
|
||||
// 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 PlaybackListener playbackListener;
|
||||
private final PlayQueue playQueue;
|
||||
@ -38,7 +38,7 @@ public class MediaSourceManager {
|
||||
// The higher it is, the less loading occurs during rapid noncritical timeline changes
|
||||
// Not recommended to go below 100ms
|
||||
private final long loadDebounceMillis;
|
||||
private final PublishSubject<Long> loadSignal;
|
||||
private final PublishSubject<Long> debouncedLoadSignal;
|
||||
private final Disposable debouncedLoader;
|
||||
|
||||
private final DeferredMediaSource.Callback sourceBuilder;
|
||||
@ -52,7 +52,7 @@ public class MediaSourceManager {
|
||||
|
||||
public MediaSourceManager(@NonNull final PlaybackListener listener,
|
||||
@NonNull final PlayQueue playQueue) {
|
||||
this(listener, playQueue, 1, 1000L);
|
||||
this(listener, playQueue, 1, 400L);
|
||||
}
|
||||
|
||||
private MediaSourceManager(@NonNull final PlaybackListener listener,
|
||||
@ -69,7 +69,7 @@ public class MediaSourceManager {
|
||||
this.loadDebounceMillis = loadDebounceMillis;
|
||||
|
||||
this.syncReactor = new SerialDisposable();
|
||||
this.loadSignal = PublishSubject.create();
|
||||
this.debouncedLoadSignal = PublishSubject.create();
|
||||
this.debouncedLoader = getDebouncedLoader();
|
||||
|
||||
this.sourceBuilder = getSourceBuilder();
|
||||
@ -101,7 +101,7 @@ public class MediaSourceManager {
|
||||
* Dispose the manager and releases all message buses and loaders.
|
||||
* */
|
||||
public void dispose() {
|
||||
if (loadSignal != null) loadSignal.onComplete();
|
||||
if (debouncedLoadSignal != null) debouncedLoadSignal.onComplete();
|
||||
if (debouncedLoader != null) debouncedLoader.dispose();
|
||||
if (playQueueReactor != null) playQueueReactor.cancel();
|
||||
if (syncReactor != null) syncReactor.dispose();
|
||||
@ -118,7 +118,7 @@ public class MediaSourceManager {
|
||||
* Unblocks the player once the item at the current index is loaded.
|
||||
* */
|
||||
public void load() {
|
||||
loadSignal.onNext(System.currentTimeMillis());
|
||||
loadDebounced();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -157,12 +157,12 @@ public class MediaSourceManager {
|
||||
}
|
||||
|
||||
private void onPlayQueueChanged(final PlayQueueEvent event) {
|
||||
if (playQueue.isEmpty()) {
|
||||
if (playQueue.isEmpty() && playQueue.isComplete()) {
|
||||
playbackListener.shutdown();
|
||||
return;
|
||||
}
|
||||
|
||||
// why no pattern matching in Java =(
|
||||
// Event specific action
|
||||
switch (event.type()) {
|
||||
case INIT:
|
||||
case REORDER:
|
||||
@ -172,37 +172,34 @@ public class MediaSourceManager {
|
||||
case APPEND:
|
||||
populateSources();
|
||||
break;
|
||||
case SELECT:
|
||||
sync();
|
||||
break;
|
||||
case REMOVE:
|
||||
final RemoveEvent removeEvent = (RemoveEvent) event;
|
||||
remove(removeEvent.getRemoveIndex());
|
||||
// Sync only when the currently playing is removed
|
||||
if (removeEvent.getQueueIndex() == removeEvent.getRemoveIndex()) sync();
|
||||
break;
|
||||
case MOVE:
|
||||
final MoveEvent moveEvent = (MoveEvent) event;
|
||||
move(moveEvent.getFromIndex(), moveEvent.getToIndex());
|
||||
break;
|
||||
case SELECT:
|
||||
case RECOVERY:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Loading and Syncing
|
||||
switch (event.type()) {
|
||||
case INIT:
|
||||
case REORDER:
|
||||
case ERROR:
|
||||
case APPEND:
|
||||
loadInternal(); // low frequency, critical events
|
||||
loadImmediate(); // low frequency, critical events
|
||||
break;
|
||||
case APPEND:
|
||||
case REMOVE:
|
||||
case SELECT:
|
||||
case MOVE:
|
||||
case RECOVERY:
|
||||
default:
|
||||
load(); // high frequency or noncritical events
|
||||
loadDebounced(); // high frequency or noncritical events
|
||||
break;
|
||||
}
|
||||
|
||||
@ -262,7 +259,11 @@ public class MediaSourceManager {
|
||||
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
|
||||
final int currentIndex = playQueue.getIndex();
|
||||
final PlayQueueItem currentItem = playQueue.getItem(currentIndex);
|
||||
@ -290,7 +291,9 @@ public class MediaSourceManager {
|
||||
|
||||
final DeferredMediaSource mediaSource = (DeferredMediaSource) sources.getMediaSource(playQueue.indexOf(item));
|
||||
if (mediaSource.state() == DeferredMediaSource.STATE_PREPARED) mediaSource.load();
|
||||
if (tryUnblock()) sync();
|
||||
|
||||
tryUnblock();
|
||||
if (!isBlocked) sync();
|
||||
}
|
||||
|
||||
private void resetSources() {
|
||||
@ -307,13 +310,13 @@ public class MediaSourceManager {
|
||||
}
|
||||
|
||||
private Disposable getDebouncedLoader() {
|
||||
return loadSignal
|
||||
return debouncedLoadSignal
|
||||
.debounce(loadDebounceMillis, TimeUnit.MILLISECONDS)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new Consumer<Long>() {
|
||||
@Override
|
||||
public void accept(Long timestamp) throws Exception {
|
||||
loadInternal();
|
||||
loadImmediate();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -43,6 +43,7 @@ public interface PlaybackListener {
|
||||
*
|
||||
* May be called at any time.
|
||||
* */
|
||||
@Nullable
|
||||
MediaSource sourceOf(final PlayQueueItem item, final StreamInfo info);
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,132 @@
|
||||
package org.schabi.newpipe.playlist;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.ListExtractor;
|
||||
import org.schabi.newpipe.extractor.ListInfo;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.SingleObserver;
|
||||
import io.reactivex.annotations.NonNull;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
|
||||
abstract class AbstractInfoPlayQueue<T extends ListInfo, U extends InfoItem> extends PlayQueue {
|
||||
boolean isInitial;
|
||||
boolean isComplete;
|
||||
|
||||
int serviceId;
|
||||
String baseUrl;
|
||||
String nextUrl;
|
||||
|
||||
transient Disposable fetchReactor;
|
||||
|
||||
AbstractInfoPlayQueue(final U item) {
|
||||
this(item.service_id, item.url, null, Collections.<InfoItem>emptyList(), 0);
|
||||
}
|
||||
|
||||
AbstractInfoPlayQueue(final int serviceId,
|
||||
final String url,
|
||||
final String nextPageUrl,
|
||||
final List<InfoItem> streams,
|
||||
final int index) {
|
||||
super(index, extractListItems(streams));
|
||||
|
||||
this.baseUrl = url;
|
||||
this.nextUrl = nextPageUrl;
|
||||
this.serviceId = serviceId;
|
||||
|
||||
this.isInitial = streams.isEmpty();
|
||||
this.isComplete = !isInitial && (nextPageUrl == null || nextPageUrl.isEmpty());
|
||||
}
|
||||
|
||||
abstract protected String getTag();
|
||||
|
||||
@Override
|
||||
public boolean isComplete() {
|
||||
return isComplete;
|
||||
}
|
||||
|
||||
SingleObserver<T> getHeadListObserver() {
|
||||
return new SingleObserver<T>() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull Disposable d) {
|
||||
if (isComplete || !isInitial || (fetchReactor != null && !fetchReactor.isDisposed())) {
|
||||
d.dispose();
|
||||
} else {
|
||||
fetchReactor = d;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(@NonNull T result) {
|
||||
isInitial = false;
|
||||
if (!result.has_more_streams) isComplete = true;
|
||||
nextUrl = result.next_streams_url;
|
||||
|
||||
append(extractListItems(result.related_streams));
|
||||
|
||||
fetchReactor.dispose();
|
||||
fetchReactor = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@NonNull Throwable e) {
|
||||
Log.e(getTag(), "Error fetching more playlist, marking playlist as complete.", e);
|
||||
isComplete = true;
|
||||
append(); // Notify change
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
SingleObserver<ListExtractor.NextItemsResult> getNextItemsObserver() {
|
||||
return new SingleObserver<ListExtractor.NextItemsResult>() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull Disposable d) {
|
||||
if (isComplete || isInitial || (fetchReactor != null && !fetchReactor.isDisposed())) {
|
||||
d.dispose();
|
||||
} else {
|
||||
fetchReactor = d;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(@NonNull ListExtractor.NextItemsResult result) {
|
||||
if (!result.hasMoreStreams()) isComplete = true;
|
||||
nextUrl = result.nextItemsUrl;
|
||||
|
||||
append(extractListItems(result.nextItemsList));
|
||||
|
||||
fetchReactor.dispose();
|
||||
fetchReactor = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@NonNull Throwable e) {
|
||||
Log.e(getTag(), "Error fetching more playlist, marking playlist as complete.", e);
|
||||
isComplete = true;
|
||||
append(); // Notify change
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
if (fetchReactor != null) fetchReactor.dispose();
|
||||
}
|
||||
|
||||
private static List<PlayQueueItem> extractListItems(final List<InfoItem> infos) {
|
||||
List<PlayQueueItem> result = new ArrayList<>();
|
||||
for (final InfoItem stream : infos) {
|
||||
if (stream instanceof StreamInfoItem) {
|
||||
result.add(new PlayQueueItem((StreamInfoItem) stream));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package org.schabi.newpipe.playlist;
|
||||
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.channel.ChannelInfo;
|
||||
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
|
||||
import org.schabi.newpipe.util.ExtractorHelper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
public final class ChannelPlayQueue extends AbstractInfoPlayQueue<ChannelInfo, ChannelInfoItem> {
|
||||
public ChannelPlayQueue(final ChannelInfoItem item) {
|
||||
super(item);
|
||||
}
|
||||
|
||||
public ChannelPlayQueue(final int serviceId,
|
||||
final String url,
|
||||
final String nextPageUrl,
|
||||
final List<InfoItem> streams,
|
||||
final int index) {
|
||||
super(serviceId, url, nextPageUrl, streams, index);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTag() {
|
||||
return "ChannelPlayQueue@" + Integer.toHexString(hashCode());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fetch() {
|
||||
if (this.isInitial) {
|
||||
ExtractorHelper.getChannelInfo(this.serviceId, this.baseUrl, false)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(getHeadListObserver());
|
||||
} else {
|
||||
ExtractorHelper.getMoreChannelItems(this.serviceId, this.baseUrl, this.nextUrl)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(getNextItemsObserver());
|
||||
}
|
||||
}
|
||||
}
|
@ -1,104 +0,0 @@
|
||||
package org.schabi.newpipe.playlist;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.ListExtractor;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
import org.schabi.newpipe.util.ExtractorHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.SingleObserver;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.annotations.NonNull;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
public final class ExternalPlayQueue extends PlayQueue {
|
||||
private final String TAG = "ExternalPlayQueue@" + Integer.toHexString(hashCode());
|
||||
|
||||
private boolean isComplete;
|
||||
|
||||
private int serviceId;
|
||||
private String baseUrl;
|
||||
private String nextUrl;
|
||||
|
||||
private transient Disposable fetchReactor;
|
||||
|
||||
public ExternalPlayQueue(final int serviceId,
|
||||
final String url,
|
||||
final String nextPageUrl,
|
||||
final List<InfoItem> streams,
|
||||
final int index) {
|
||||
super(index, extractPlaylistItems(streams));
|
||||
|
||||
this.baseUrl = url;
|
||||
this.nextUrl = nextPageUrl;
|
||||
this.serviceId = serviceId;
|
||||
|
||||
this.isComplete = nextPageUrl == null || nextPageUrl.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isComplete() {
|
||||
return isComplete;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fetch() {
|
||||
ExtractorHelper.getMorePlaylistItems(this.serviceId, this.baseUrl, this.nextUrl)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(getPlaylistObserver());
|
||||
}
|
||||
|
||||
private SingleObserver<ListExtractor.NextItemsResult> getPlaylistObserver() {
|
||||
return new SingleObserver<ListExtractor.NextItemsResult>() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull Disposable d) {
|
||||
if (isComplete || (fetchReactor != null && !fetchReactor.isDisposed())) {
|
||||
d.dispose();
|
||||
} else {
|
||||
fetchReactor = d;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(@NonNull ListExtractor.NextItemsResult result) {
|
||||
if (!result.hasMoreStreams()) isComplete = true;
|
||||
nextUrl = result.nextItemsUrl;
|
||||
|
||||
append(extractPlaylistItems(result.nextItemsList));
|
||||
|
||||
fetchReactor.dispose();
|
||||
fetchReactor = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@NonNull Throwable e) {
|
||||
Log.e(TAG, "Error fetching more playlist, marking playlist as complete.", e);
|
||||
isComplete = true;
|
||||
append(); // Notify change
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
if (fetchReactor != null) fetchReactor.dispose();
|
||||
}
|
||||
|
||||
private static List<PlayQueueItem> extractPlaylistItems(final List<InfoItem> infos) {
|
||||
List<PlayQueueItem> result = new ArrayList<>();
|
||||
for (final InfoItem stream : infos) {
|
||||
if (stream instanceof StreamInfoItem) {
|
||||
result.add(new PlayQueueItem((StreamInfoItem) stream));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
@ -123,7 +123,7 @@ public abstract class PlayQueue implements Serializable {
|
||||
* May throw {@link IndexOutOfBoundsException}.
|
||||
* */
|
||||
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);
|
||||
}
|
||||
|
||||
@ -279,7 +279,7 @@ public abstract class PlayQueue implements Serializable {
|
||||
queueIndex.set(currentIndex % (size - 1));
|
||||
|
||||
} else if (currentIndex == removeIndex && currentIndex == size - 1){
|
||||
queueIndex.set(removeIndex - 1);
|
||||
queueIndex.set(0);
|
||||
}
|
||||
|
||||
if (backup != null) {
|
||||
|
@ -107,6 +107,8 @@ public class PlayQueueItemBuilder {
|
||||
.bitmapConfig(Bitmap.Config.RGB_565) // Users won't be able to see much anyways
|
||||
.preProcessor(bitmapProcessor)
|
||||
.imageScaleType(ImageScaleType.EXACTLY)
|
||||
.cacheInMemory(true)
|
||||
.cacheOnDisk(true)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,45 @@
|
||||
package org.schabi.newpipe.playlist;
|
||||
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
|
||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem;
|
||||
import org.schabi.newpipe.util.ExtractorHelper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
public final class PlaylistPlayQueue extends AbstractInfoPlayQueue<PlaylistInfo, PlaylistInfoItem> {
|
||||
public PlaylistPlayQueue(final PlaylistInfoItem item) {
|
||||
super(item);
|
||||
}
|
||||
|
||||
public PlaylistPlayQueue(final int serviceId,
|
||||
final String url,
|
||||
final String nextPageUrl,
|
||||
final List<InfoItem> streams,
|
||||
final int index) {
|
||||
super(serviceId, url, nextPageUrl, streams, index);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTag() {
|
||||
return "PlaylistPlayQueue@" + Integer.toHexString(hashCode());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fetch() {
|
||||
if (this.isInitial) {
|
||||
ExtractorHelper.getPlaylistInfo(this.serviceId, this.baseUrl, false)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(getHeadListObserver());
|
||||
} else {
|
||||
ExtractorHelper.getMorePlaylistItems(this.serviceId, this.baseUrl, this.nextUrl)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(getNextItemsObserver());
|
||||
}
|
||||
}
|
||||
}
|
@ -1,12 +1,21 @@
|
||||
package org.schabi.newpipe.playlist;
|
||||
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
public final class SinglePlayQueue extends PlayQueue {
|
||||
public SinglePlayQueue(final StreamInfoItem item) {
|
||||
this(new PlayQueueItem(item));
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -26,6 +26,9 @@ import android.util.Log;
|
||||
import org.schabi.newpipe.MainActivity;
|
||||
import org.schabi.newpipe.extractor.Info;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
||||
public final class InfoCache {
|
||||
private static final boolean DEBUG = MainActivity.DEBUG;
|
||||
@ -37,9 +40,9 @@ public final class InfoCache {
|
||||
* Trim the cache to this size
|
||||
*/
|
||||
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, Info> lruCache = new LruCache<>(MAX_ITEMS_ON_CACHE);
|
||||
private static final LruCache<String, CacheData> lruCache = new LruCache<>(MAX_ITEMS_ON_CACHE);
|
||||
|
||||
private InfoCache() {
|
||||
//no instance
|
||||
@ -52,28 +55,29 @@ public final class InfoCache {
|
||||
public Info getFromKey(int serviceId, @NonNull String url) {
|
||||
if (DEBUG) Log.d(TAG, "getFromKey() called with: serviceId = [" + serviceId + "], url = [" + url + "]");
|
||||
synchronized (lruCache) {
|
||||
return lruCache.get(serviceId + url);
|
||||
return getInfo(lruCache, keyOf(serviceId, url));
|
||||
}
|
||||
}
|
||||
|
||||
public void putInfo(@NonNull Info info) {
|
||||
if (DEBUG) Log.d(TAG, "putInfo() called with: info = [" + info + "]");
|
||||
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) {
|
||||
if (DEBUG) Log.d(TAG, "removeInfo() called with: info = [" + info + "]");
|
||||
synchronized (lruCache) {
|
||||
lruCache.remove(info.service_id + info.url);
|
||||
lruCache.remove(keyOf(info));
|
||||
}
|
||||
}
|
||||
|
||||
public void removeInfo(int serviceId, @NonNull String url) {
|
||||
if (DEBUG) Log.d(TAG, "removeInfo() called with: serviceId = [" + serviceId + "], url = [" + url + "]");
|
||||
synchronized (lruCache) {
|
||||
lruCache.remove(serviceId + url);
|
||||
lruCache.remove(keyOf(serviceId, url));
|
||||
}
|
||||
}
|
||||
|
||||
@ -87,6 +91,7 @@ public final class InfoCache {
|
||||
public void trimCache() {
|
||||
if (DEBUG) Log.d(TAG, "trimCache() called");
|
||||
synchronized (lruCache) {
|
||||
removeStaleCache(lruCache);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,10 +5,11 @@ import android.content.ActivityNotFoundException;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.widget.Toast;
|
||||
|
||||
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.search.SearchFragment;
|
||||
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.MainVideoPlayer;
|
||||
import org.schabi.newpipe.player.PopupVideoPlayer;
|
||||
import org.schabi.newpipe.player.PopupVideoPlayerActivity;
|
||||
import org.schabi.newpipe.player.VideoPlayer;
|
||||
import org.schabi.newpipe.playlist.PlayQueue;
|
||||
import org.schabi.newpipe.settings.SettingsActivity;
|
||||
@ -77,6 +83,29 @@ public class NavigationHelper {
|
||||
.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
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
@ -230,6 +259,23 @@ public class NavigationHelper {
|
||||
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
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
BIN
app/src/main/res/drawable-hdpi/ic_arrow_top_left_black_24dp.png
Normal file
After Width: | Height: | Size: 554 B |
BIN
app/src/main/res/drawable-hdpi/ic_arrow_top_left_white_24dp.png
Normal file
After Width: | Height: | Size: 546 B |
BIN
app/src/main/res/drawable-hdpi/ic_more_vert_black_24dp.png
Normal file
After Width: | Height: | Size: 132 B |
BIN
app/src/main/res/drawable-hdpi/ic_more_vert_white_24dp.png
Normal file
After Width: | Height: | Size: 134 B |
BIN
app/src/main/res/drawable-mdpi/ic_arrow_top_left_black_24dp.png
Normal file
After Width: | Height: | Size: 418 B |
BIN
app/src/main/res/drawable-mdpi/ic_arrow_top_left_white_24dp.png
Normal file
After Width: | Height: | Size: 427 B |
BIN
app/src/main/res/drawable-mdpi/ic_more_vert_black_24dp.png
Normal file
After Width: | Height: | Size: 108 B |
BIN
app/src/main/res/drawable-mdpi/ic_more_vert_white_24dp.png
Normal file
After Width: | Height: | Size: 112 B |
BIN
app/src/main/res/drawable-xhdpi/ic_arrow_top_left_black_24dp.png
Normal file
After Width: | Height: | Size: 492 B |
BIN
app/src/main/res/drawable-xhdpi/ic_arrow_top_left_white_24dp.png
Normal file
After Width: | Height: | Size: 500 B |
BIN
app/src/main/res/drawable-xhdpi/ic_more_vert_black_24dp.png
Normal file
After Width: | Height: | Size: 155 B |
BIN
app/src/main/res/drawable-xhdpi/ic_more_vert_white_24dp.png
Normal file
After Width: | Height: | Size: 158 B |
After Width: | Height: | Size: 570 B |
After Width: | Height: | Size: 573 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_more_vert_black_24dp.png
Normal file
After Width: | Height: | Size: 205 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_more_vert_white_24dp.png
Normal file
After Width: | Height: | Size: 216 B |
After Width: | Height: | Size: 675 B |
After Width: | Height: | Size: 730 B |
BIN
app/src/main/res/drawable-xxxhdpi/ic_more_vert_black_24dp.png
Normal file
After Width: | Height: | Size: 272 B |
BIN
app/src/main/res/drawable-xxxhdpi/ic_more_vert_white_24dp.png
Normal file
After Width: | Height: | Size: 305 B |
@ -209,7 +209,7 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="2dp"
|
||||
android:layout_toLeftOf="@+id/screenRotationButton"
|
||||
android:layout_toLeftOf="@+id/queueButton"
|
||||
android:gravity="center"
|
||||
android:minHeight="35dp"
|
||||
android:minWidth="40dp"
|
||||
@ -218,28 +218,13 @@
|
||||
tools:ignore="RtlHardcoded,RtlSymmetry"
|
||||
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
|
||||
android:id="@+id/queueButton"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="35dp"
|
||||
android:layout_marginLeft="2dp"
|
||||
android:layout_marginRight="2dp"
|
||||
android:layout_toLeftOf="@+id/fullScreenButton"
|
||||
android:layout_toLeftOf="@+id/moreOptionsButton"
|
||||
android:background="#00ffffff"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
@ -249,16 +234,17 @@
|
||||
tools:ignore="ContentDescription,RtlHardcoded"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/fullScreenButton"
|
||||
android:layout_width="35dp"
|
||||
android:layout_height="35dp"
|
||||
android:id="@+id/moreOptionsButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_marginLeft="4dp"
|
||||
android:layout_marginLeft="2dp"
|
||||
android:padding="5dp"
|
||||
android:background="#00ffffff"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:scaleType="fitXY"
|
||||
android:src="@drawable/ic_fullscreen_exit_white"
|
||||
android:src="?attr/options"
|
||||
tools:ignore="ContentDescription,RtlHardcoded"/>
|
||||
|
||||
</RelativeLayout>
|
||||
@ -454,4 +440,4 @@
|
||||
tools:visibility="visible"/>
|
||||
</RelativeLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
</RelativeLayout>
|
||||
|
@ -6,75 +6,90 @@
|
||||
android:id="@+id/channel_header_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="12dp">
|
||||
android:background="?attr/contrast_background_color">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/channel_banner_image"
|
||||
<RelativeLayout
|
||||
android:id="@+id/channel_metadata"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="70dp"
|
||||
android:background="@android:color/black"
|
||||
android:fitsSystemWindows="true"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/channel_banner"
|
||||
tools:ignore="ContentDescription"/>
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<de.hdodenhof.circleimageview.CircleImageView
|
||||
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"/>
|
||||
<ImageView
|
||||
android:id="@+id/channel_banner_image"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="70dp"
|
||||
android:background="@android:color/black"
|
||||
android:fitsSystemWindows="true"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/channel_banner"
|
||||
tools:ignore="ContentDescription"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/channel_title_view"
|
||||
<de.hdodenhof.circleimageview.CircleImageView
|
||||
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_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"/>
|
||||
android:layout_below="@id/channel_metadata">
|
||||
|
||||
<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"/>
|
||||
<include layout="@layout/playlist_control" />
|
||||
</LinearLayout>
|
||||
|
||||
<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>
|
40
app/src/main/res/layout/dialog_title.xml
Normal 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>
|
@ -1,36 +1,71 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:orientation="horizontal"
|
||||
android:paddingBottom="8dp"
|
||||
android:paddingTop="8dp">
|
||||
android:orientation="horizontal">
|
||||
|
||||
<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"
|
||||
<LinearLayout
|
||||
android:id="@+id/suggestion_search"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
|
||||
android:textSize="14sp"
|
||||
tools:ignore="RtlHardcoded"
|
||||
tools:text="Search query"/>
|
||||
</LinearLayout>
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_toLeftOf="@id/suggestion_insert"
|
||||
android:layout_toStartOf="@id/suggestion_insert"
|
||||
android:layout_centerVertical="true"
|
||||
android:paddingBottom="8dp"
|
||||
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>
|
||||
|
@ -7,6 +7,7 @@
|
||||
android:layout_height="@dimen/video_item_search_height"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:padding="@dimen/video_item_search_padding">
|
||||
|
||||
<de.hdodenhof.circleimageview.CircleImageView
|
||||
@ -28,11 +29,12 @@
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginBottom="@dimen/video_item_search_image_right_margin"
|
||||
android:layout_toRightOf="@+id/itemThumbnailView"
|
||||
android:layout_toEndOf="@+id/itemThumbnailView"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
android:textSize="@dimen/video_item_search_title_text_size"
|
||||
tools:text="Channel Title, Lorem ipsum"/>
|
||||
tools:text="Channel Title, Lorem ipsum" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/itemChannelDescriptionView"
|
||||
@ -41,11 +43,12 @@
|
||||
android:layout_above="@+id/itemAdditionalDetails"
|
||||
android:layout_marginBottom="@dimen/channel_item_description_to_details_margin"
|
||||
android:layout_toRightOf="@+id/itemThumbnailView"
|
||||
android:layout_toEndOf="@+id/itemThumbnailView"
|
||||
android:ellipsize="end"
|
||||
android:lines="2"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
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
|
||||
android:id="@+id/itemAdditionalDetails"
|
||||
@ -53,6 +56,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_toRightOf="@+id/itemThumbnailView"
|
||||
android:layout_toEndOf="@+id/itemThumbnailView"
|
||||
android:lines="1"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textSize="@dimen/video_item_search_upload_date_text_size"
|
||||
|
@ -7,6 +7,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:padding="@dimen/video_item_search_padding">
|
||||
|
||||
<de.hdodenhof.circleimageview.CircleImageView
|
||||
|
@ -7,6 +7,7 @@
|
||||
android:layout_height="@dimen/video_item_search_height"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:padding="@dimen/video_item_search_padding">
|
||||
|
||||
<ImageView
|
||||
|
@ -7,6 +7,7 @@
|
||||
android:layout_height="@dimen/video_item_search_height"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:padding="@dimen/video_item_search_padding">
|
||||
|
||||
<ImageView
|
||||
@ -48,6 +49,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_toRightOf="@+id/itemThumbnailView"
|
||||
android:layout_toEndOf="@+id/itemThumbnailView"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
@ -60,6 +62,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/itemVideoTitleView"
|
||||
android:layout_toRightOf="@+id/itemThumbnailView"
|
||||
android:layout_toEndOf="@+id/itemThumbnailView"
|
||||
android:lines="1"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textSize="@dimen/video_item_search_uploader_text_size"
|
||||
@ -71,6 +74,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_toRightOf="@+id/itemThumbnailView"
|
||||
android:layout_toEndOf="@+id/itemThumbnailView"
|
||||
android:lines="1"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textSize="@dimen/video_item_search_upload_date_text_size"
|
||||
|
@ -7,6 +7,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:padding="@dimen/video_item_search_padding">
|
||||
|
||||
<ImageView
|
||||
@ -48,11 +49,13 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_toRightOf="@+id/itemThumbnailView"
|
||||
android:layout_toEndOf="@+id/itemThumbnailView"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
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
|
||||
android:id="@+id/itemUploaderView"
|
||||
@ -60,8 +63,9 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/itemVideoTitleView"
|
||||
android:layout_toRightOf="@+id/itemThumbnailView"
|
||||
android:layout_toEndOf="@+id/itemThumbnailView"
|
||||
android:lines="1"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textSize="@dimen/video_item_search_uploader_text_size"
|
||||
tools:text="Uploader"/>
|
||||
tools:text="Uploader" />
|
||||
</RelativeLayout>
|
@ -79,8 +79,7 @@
|
||||
android:layout_toLeftOf="@id/itemHandle"
|
||||
android:layout_toStartOf="@id/itemHandle"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
android:textSize="@dimen/video_item_search_title_text_size"
|
||||
tools:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. "/>
|
||||
@ -94,7 +93,8 @@
|
||||
android:layout_toEndOf="@id/itemThumbnailView"
|
||||
android:layout_toLeftOf="@id/itemHandle"
|
||||
android:layout_toStartOf="@id/itemHandle"
|
||||
android:lines="1"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textSize="@dimen/video_item_search_upload_date_text_size"
|
||||
tools:text="Uploader"/>
|
||||
|
@ -34,22 +34,22 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/notificationSongName"
|
||||
style="@android:style/TextAppearance.StatusBar.EventContent.Title"
|
||||
android:layout_width="match_parent"
|
||||
android:ellipsize="end"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="1"
|
||||
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."/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/notificationArtist"
|
||||
android:layout_width="match_parent"
|
||||
style="@android:style/TextAppearance.StatusBar.EventContent"
|
||||
android:ellipsize="end"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="1"
|
||||
android:textSize="12sp"
|
||||
android:textColor="@color/background_subtext_color"
|
||||
tools:text="Duis posuere arcu condimentum lobortis mattis."/>
|
||||
</LinearLayout>
|
||||
|
||||
|
@ -46,22 +46,22 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/notificationSongName"
|
||||
style="@android:style/TextAppearance.StatusBar.EventContent.Title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
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."/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/notificationArtist"
|
||||
style="@android:style/TextAppearance.StatusBar.EventContent"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textSize="12sp"
|
||||
android:textColor="@color/background_subtext_color"
|
||||
tools:text="Duis posuere arcu condimentum lobortis mattis."/>
|
||||
</LinearLayout>
|
||||
|
||||
@ -80,7 +80,6 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/notificationTime"
|
||||
style="@android:style/TextAppearance.StatusBar.EventContent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="8dp"
|
||||
@ -92,6 +91,7 @@
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textSize="12sp"
|
||||
android:textColor="@color/background_subtext_color"
|
||||
tools:text="Duis posuere"/>
|
||||
|
||||
<RelativeLayout
|
||||
|
@ -29,22 +29,22 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/notificationSongName"
|
||||
style="@android:style/TextAppearance.StatusBar.EventContent.Title"
|
||||
android:layout_width="match_parent"
|
||||
android:ellipsize="end"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="1"
|
||||
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."/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/notificationArtist"
|
||||
android:layout_width="match_parent"
|
||||
style="@android:style/TextAppearance.StatusBar.EventContent"
|
||||
android:ellipsize="end"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="1"
|
||||
android:textSize="12sp"
|
||||
android:textColor="@color/background_subtext_color"
|
||||
tools:text="Duis posuere arcu condimentum lobortis mattis."/>
|
||||
|
||||
</LinearLayout>
|
||||
|
83
app/src/main/res/layout/playlist_control.xml
Normal 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>
|
@ -6,8 +6,7 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/contrast_background_color"
|
||||
android:paddingBottom="6dp">
|
||||
android:background="?attr/contrast_background_color">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/playlist_title_view"
|
||||
@ -81,64 +80,14 @@
|
||||
android:textSize="@dimen/playlist_detail_subtext_size"
|
||||
tools:ignore="RtlHardcoded"
|
||||
tools:text="234 videos"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
|
||||
<RelativeLayout
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/play_control"
|
||||
android:paddingLeft="5dp"
|
||||
android:paddingRight="5dp"
|
||||
android:layout_below="@+id/playlist_meta">
|
||||
android:layout_below="@id/playlist_meta">
|
||||
|
||||
<Button
|
||||
android:id="@+id/playlist_play_bg_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_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>
|
||||
<include layout="@layout/playlist_control"/>
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
21
app/src/main/res/menu/menu_videooptions.xml
Normal 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>
|
||||
|
@ -275,4 +275,6 @@
|
||||
<string name="title_activity_background_player">Reproductor de fondu</string>
|
||||
<string name="play_queue_remove">Desaniciar</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>
|
||||
|
@ -136,7 +136,7 @@
|
||||
<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="short_million">mil.</string>
|
||||
|
@ -42,15 +42,15 @@
|
||||
<string name="background_player_playing_toast">Spiele im Hintergrund ab</string>
|
||||
<string name="play_btn_text">Abspielen</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_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="network_error">Netzwerkfehler</string>
|
||||
|
||||
<string name="download_path_audio_title">Downloadverzeichnis für Musik</string>
|
||||
<string name="download_path_audio_summary">Verzeichnis zum Speichern heruntergeladener Audiodateien</string>
|
||||
<string name="download_path_audio_dialog_title">Pfad für heruntergeladene Audiodateien eingeben</string>
|
||||
<string name="download_path_audio_title">Downloadverzeichnis für Audiodateien</string>
|
||||
<string name="download_path_audio_summary">Verzeichnis für heruntergeladene Audiodateien</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="light_theme_title">Hell</string>
|
||||
|
||||
@ -61,7 +61,7 @@
|
||||
<string name="general_error">Fehler</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="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="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="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_snackbar_action">MELDEN</string>
|
||||
<string name="what_device_headline">Info:</string>
|
||||
@ -94,7 +94,7 @@
|
||||
<string name="report_error">Einen Fehler melden</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="downloads">Downloads</string>
|
||||
@ -163,13 +163,13 @@
|
||||
|
||||
<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="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_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_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="refresh">Aktualisieren</string>
|
||||
<string name="clear">Leeren</string>
|
||||
@ -224,7 +224,7 @@
|
||||
|
||||
|
||||
<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="settings_category_player_behavior_title">Verhalten</string>
|
||||
@ -258,12 +258,12 @@
|
||||
<string name="settings_category_player_title">Player</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="select_a_channel">Wähle einen Kanal aus</string>
|
||||
<string name="no_channel_subscribed_yet">Noch kein Kanal abonniert</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_queue_remove">Entfernen</string>
|
||||
@ -284,8 +284,16 @@
|
||||
<string name="select_a_kiosk">Kiosk auswählen</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="background_player_append">In der Warteschlange bei der Hintergrundwiedergabe</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 der Hintergrundwiedergabe</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>
|
||||
|
@ -96,7 +96,7 @@
|
||||
<string name="report_error">Reportar un error</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="retry">Reintentar</string>
|
||||
@ -287,4 +287,14 @@ abrir en modo popup</string>
|
||||
<string name="play_queue_remove">Remover</string>
|
||||
<string name="play_queue_stream_detail">Detalles</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>
|
||||
|
@ -185,7 +185,7 @@
|
||||
<string name="finish">OK</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_server_unsupported">Serveriä ei tueta</string>
|
||||
<string name="msg_exists">Tiedosto on jo olemassa</string>
|
||||
@ -268,4 +268,11 @@
|
||||
<string name="play_queue_audio_settings">Ääniasetukset</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>
|
||||
</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>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<resources>
|
||||
<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="did_you_mean">Vouliez-vous dire : %1$s ?</string>
|
||||
<string name="download">Télécharger</string>
|
||||
@ -18,7 +18,7 @@
|
||||
<string name="settings">Paramètres</string>
|
||||
<string name="share">Partager</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 l’option « Lire avec Kodi »</string>
|
||||
<string name="upload_date_text">Ajoutée le %1$s</string>
|
||||
<string name="view_count_text">%1$s vues</string>
|
||||
@ -37,7 +37,7 @@
|
||||
<string name="detail_thumbnail_view_description">Miniature d’aperçu vidéo</string>
|
||||
<string name="detail_dislikes_img_view_description">Je n’aime pas</string>
|
||||
<string name="detail_likes_img_view_description">J’aime</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 l’avatar de l’utilisateur</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>
|
||||
@ -153,7 +153,7 @@
|
||||
<string name="controls_popup_title">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="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>
|
||||
@ -233,7 +233,7 @@
|
||||
<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="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>
|
||||
<plurals name="subscribers">
|
||||
@ -257,9 +257,9 @@
|
||||
|
||||
<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="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="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="channel_page_summary">Page de la 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_stream_detail">Détails</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>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<resources>
|
||||
<string name="view_count_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="open_in_browser">ब्राउज़र में खोलें</string>
|
||||
<string name="open_in_popup_mode">पॉपअप मोड में खोलें</string>
|
||||
@ -11,15 +11,15 @@
|
||||
<string name="search">खोज</string>
|
||||
<string name="settings">सेटिंग</string>
|
||||
<string name="choose_browser">ब्राउज़र चुनें</string>
|
||||
<string name="screen_rotation">रोटेशन</string>
|
||||
<string name="popup_mode_share_menu_title">[NEWPIPE] पॉपअप मोड</string>
|
||||
<string name="screen_rotation">स्क्रीन रोटेशन</string>
|
||||
<string name="popup_mode_share_menu_title">NewPipe पॉपअप मोड</string>
|
||||
<string name="subscribe_button_title">सदस्य बनें</string>
|
||||
<string name="subscribed_button_title">सदस्यता ली</string>
|
||||
<string name="channel_unsubscribed">सदस्यता निकाली गई</string>
|
||||
<string name="tab_subscriptions">सदस्यता</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="play_audio">ऑडियो</string>
|
||||
@ -30,32 +30,250 @@
|
||||
<string name="next_video_title">अगला वीडियो</string>
|
||||
<string name="settings_category_video_audio_title">वीडियो और ऑडियो</string>
|
||||
<string name="settings_category_history_title">इतिहास</string>
|
||||
<string name="background_player_playing_toast">पीछे चल रहा है</string>
|
||||
<string name="no_views">कोई दृश्य नहीं</string>
|
||||
<string name="background_player_playing_toast">बैकग्राउंड में चल रहा है</string>
|
||||
<string name="no_views">कोई दर्शक नहीं</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_about">इसके बारे में</string>
|
||||
<string name="title_licenses">तृतीय पक्ष लाइसेंस</string>
|
||||
<string name="title_licenses">तृतीय-पक्ष लाइसेंस</string>
|
||||
<string name="action_open_website">वेबसाइट खोलें</string>
|
||||
<string name="tab_about">इसके बारे में</string>
|
||||
<string name="tab_licenses">लाइसेंस</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="contribution_title">योगदान</string>
|
||||
|
||||
<string name="title_activity_history">इतिहास</string>
|
||||
<string name="title_history_search">कोज चुके</string>
|
||||
<string name="title_history_view">देखा वीडियो</string>
|
||||
<string name="history_disabled">इतिहास अक्षम है</string>
|
||||
<string name="title_history_search">खोजा जा चूका है</string>
|
||||
<string name="title_history_view">विडियो जो देख लिए गए है</string>
|
||||
<string name="history_disabled">इतिहास disable है</string>
|
||||
<string name="action_history">इतिहास</string>
|
||||
<string name="history_empty">इतिहास खाली है</string>
|
||||
<string name="history_cleared">इतिहास स्पष्ट हो चुकी है</string>
|
||||
<string name="item_deleted">मद हटाया गया</string>
|
||||
<string name="trending">चर्चित</string>
|
||||
<string name="history_cleared">इतिहास साफ़ हो चुकी है</string>
|
||||
<string name="item_deleted">Item हटा दिया गया है</string>
|
||||
<string name="trending">फ़िलहाल चर्चा में है</string>
|
||||
<string name="play_queue_audio_settings">ऑडियो सेटिंग</string>
|
||||
<string name="main_bg_subtitle">खोज चिह्न दबाके आरंम्भ करे</string>
|
||||
<string name="cancel">रद्द करें</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>
|
||||
|
@ -41,7 +41,7 @@
|
||||
<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="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="default_resolution_title">Zadana razlučivost</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="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="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="play_audio">Zvuk</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_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>
|
||||
|
@ -281,7 +281,7 @@
|
||||
<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_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="play_all">Riproduci tutto</string>
|
||||
|
||||
@ -295,4 +295,11 @@
|
||||
<string name="play_queue_stream_detail">Dettagli</string>
|
||||
<string name="play_queue_audio_settings">Impostazioni audio</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>
|
||||
|
@ -274,4 +274,21 @@
|
||||
<string name="play_queue_stream_detail">Detaljer</string>
|
||||
<string name="play_queue_audio_settings">Lydinnstillinger</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>
|
||||
|
@ -291,4 +291,11 @@ te openen in pop-upmodus</string>
|
||||
<string name="play_queue_stream_detail">Details</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="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>
|
||||
|
@ -27,15 +27,15 @@
|
||||
<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_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="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="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="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="theme_title">Motyw</string>
|
||||
<string name="dark_theme_title">Ciemny</string>
|
||||
@ -126,7 +126,7 @@
|
||||
<string name="reCaptcha_title">Rozwiąż 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="default_popup_resolution_title">Domyślna rozdzielczość dla trybu okienkowego</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_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_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_summary">Pokazuj sugestie podczas wyszukiwania</string>
|
||||
|
||||
@ -202,9 +202,9 @@
|
||||
<string name="fragment_whats_new">Co nowego</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_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_summary">Kontynuuj odtwarzanie po przerwaniu (np. po rozmowie telefonicznej)</string>
|
||||
|
||||
|
@ -256,4 +256,31 @@ o modo “popup“</string>
|
||||
<string name="history_cleared">Histórico eliminado</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="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>
|
||||
|
@ -109,7 +109,7 @@
|
||||
<string name="report_error">Сообщить об ошибке</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="audio">Аудио</string>
|
||||
@ -126,7 +126,7 @@
|
||||
<string name="controls_popup_title">В окне</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="popup_remember_size_pos_title">Запоминать размер и положение всплывающего окна</string>
|
||||
<string name="player_gesture_controls_summary">Использовать жесты для изменения яркости и громкости</string>
|
||||
@ -144,8 +144,8 @@
|
||||
|
||||
<string name="add">Новая миссия</string>
|
||||
<string name="info_labels">Что:\\nЗапрос:\\nЯзык контента:\\nСервис:\\nВремя по Гринвичу:\\nПакет:\\nВерсия:\\nВерсия ОС:\\nГлобальный диапазон IP:</string>
|
||||
<string name="msg_popup_permission">Это разрешение нужно,
|
||||
\nчтобы показывать всплывающее окно</string>
|
||||
<string name="msg_popup_permission">Это разрешение нужно для
|
||||
\nвоспроизведения видео в отдельном окне</string>
|
||||
|
||||
<string name="reCaptchaActivity">reCAPTCHA</string>
|
||||
<string name="open_in_popup_mode">Открыть в отдельном окне</string>
|
||||
@ -182,7 +182,7 @@
|
||||
<string name="tab_about">О приложении</string>
|
||||
<string name="tab_contributors">Участники</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="contribution_encouragement">Приветствуется всё — идеи, перевод, изменения дизайна, чистка кода или огромные изменения в коде. Чем больше сделано, тем лучше!</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="play_queue_stream_detail">Подробности</string>
|
||||
<string name="play_queue_audio_settings">Настройки аудио</string>
|
||||
<string name="no_channel_subscribed_yet">Пока нет подписок</string>
|
||||
<string name="play_queue_remove">Удалить</string>
|
||||
</resources>
|
||||
|
@ -273,4 +273,22 @@ odpiranje v pojavnem načinu</string>
|
||||
|
||||
<string name="item_deleted">Predmet je izbrisan</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>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?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="no_player_found">Ingen strömspelare hittades. Vill du installera VLC?</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_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_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_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="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_summary">Visa ett alternativ för att spela upp en video med mediacentret Kodi</string>
|
||||
<string name="play_audio">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="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="light_theme_title">Ljus</string>
|
||||
<string name="dark_theme_title">Mörk</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_summary">Kom ihåg den senast inställda storleken och positionen av popup-rutan</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 popup-rutans senaste storlek och position</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="show_search_suggestions_title">Sökförslag</string>
|
||||
@ -59,7 +59,7 @@
|
||||
<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="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 & Ljud</string>
|
||||
<string name="settings_category_popup_title">Popup-ruta</string>
|
||||
<string name="settings_category_appearance_title">Utseende</string>
|
||||
@ -69,7 +69,7 @@
|
||||
<string name="play_btn_text">Spela upp</string>
|
||||
<string name="content">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="downloads">Nerladdningar</string>
|
||||
<string name="downloads_title">Nerladdningar</string>
|
||||
@ -87,12 +87,12 @@
|
||||
<string name="general_error">Fel</string>
|
||||
<string name="network_error">Nätverksfel</string>
|
||||
<string name="could_not_load_thumbnails">Kunde inte ladda alla miniatyrbilder</string>
|
||||
<string name="parsing_error">Kunde inte tolka hemsidan.</string>
|
||||
<string name="light_parsing_error">Kunde inte tolka hemsidan fullt ut.</string>
|
||||
<string name="content_not_available">Innehållet är inte tillgängligt.</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="live_streams_not_supported">Det här är en DIREKTSÄNDNING. Det finns ännu inget stöd för dessa.</string>
|
||||
<string name="parsing_error">Det gick inte att analysera webbplatsen</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="blocked_by_gema">Blockerat av GEMA</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 vilket ännu inte stöds.</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="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="use_tor_title">Använd Tor</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>
|
||||
|
@ -248,12 +248,49 @@
|
||||
</plurals>
|
||||
|
||||
<string name="title_activity_history">Geçmiş</string>
|
||||
<string name="title_history_search">Arandı</string>
|
||||
<string name="title_history_view">İzlendi</string>
|
||||
<string name="title_history_search">Aranan</string>
|
||||
<string name="title_history_view">İzlenen</string>
|
||||
<string name="history_disabled">Geçmiş devre dışı</string>
|
||||
<string name="action_history">Geçmiş</string>
|
||||
<string name="history_empty">Geçmiş boş</string>
|
||||
<string name="history_cleared">Geçmiş temizlendi</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="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">Açı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">Açı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">Açı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">Açılır Pencerede Burayı Başlat</string>
|
||||
</resources>
|
||||
|
@ -21,13 +21,13 @@
|
||||
<string name="download_path_dialog_title">输入视频存储路径</string>
|
||||
<string name="default_resolution_title">默认分辨率</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_summary">显示以 Kodi 媒体中心播放视频的选项</string>
|
||||
<string name="play_audio">音频</string>
|
||||
<string name="default_audio_format_title">默认音频格式</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="dark_theme_title">暗色系</string>
|
||||
<string name="light_theme_title">明亮色系</string>
|
||||
@ -36,7 +36,7 @@
|
||||
<string name="next_video_title">即将播放</string>
|
||||
<string name="show_next_and_similar_title">显示下一部和相近的视频</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_appearance_title">外观</string>
|
||||
<string name="settings_category_other_title">其他</string>
|
||||
@ -62,14 +62,14 @@
|
||||
<string name="video_is_age_restricted">视频有年龄限制。请先在设置中启用\"显示年龄限制内容\"。</string>
|
||||
<string name="general_error">错误</string>
|
||||
<string name="could_not_load_thumbnails">无法加载所有缩略图</string>
|
||||
<string name="youtube_signature_decryption_error">无法解密视频网址签名。</string>
|
||||
<string name="parsing_error">无法解析网站。</string>
|
||||
<string name="light_parsing_error">无法完全解析网站。</string>
|
||||
<string name="content_not_available">内容不可用。</string>
|
||||
<string name="blocked_by_gema">已被 GEMA 屏蔽。</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="youtube_signature_decryption_error">无法解密视频网址签名</string>
|
||||
<string name="parsing_error">无法解析网站</string>
|
||||
<string name="light_parsing_error">无法完全解析网站</string>
|
||||
<string name="content_not_available">内容不可用</string>
|
||||
<string name="blocked_by_gema">已被 GEMA 屏蔽</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="sorry_string">抱歉,这本不应该发生。</string>
|
||||
<string name="error_report_button_text">使用邮件报告错误</string>
|
||||
<string name="error_snackbar_message">抱歉,发生了一些错误。</string>
|
||||
@ -86,15 +86,15 @@
|
||||
<string name="video">视频</string>
|
||||
<string name="audio">音频</string>
|
||||
<string name="retry">重试</string>
|
||||
<string name="storage_permission_denied">访问存储的权限被拒绝</string>
|
||||
<string name="autoplay_by_calling_app_title">另一应用调用时自动播放</string>
|
||||
<string name="storage_permission_denied">无权访问存储空间</string>
|
||||
<string name="autoplay_by_calling_app_title">自动播放</string>
|
||||
<string name="autoplay_by_calling_app_summary">当 NewPipe 被其他应用调用时自动播放视频</string>
|
||||
<string name="duration_live">直播</string>
|
||||
|
||||
<string name="main_bg_subtitle">点按搜索开始使用</string>
|
||||
<string name="start">开始</string>
|
||||
<string name="pause">暂停</string>
|
||||
<string name="view">查看</string>
|
||||
<string name="view">播放</string>
|
||||
<string name="delete">删除</string>
|
||||
<string name="checksum">校验和</string>
|
||||
|
||||
@ -109,8 +109,8 @@
|
||||
<string name="msg_running">NewPipe 正在下载</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_copied">已复制到剪贴板</string>
|
||||
<string name="no_available_dir">请选择一个可用的下载目录</string>
|
||||
|
||||
<string name="add">新任务</string>
|
||||
<string name="downloads">下载</string>
|
||||
@ -155,8 +155,8 @@
|
||||
<string name="show_search_suggestions_summary">在搜索时显示搜索建议</string>
|
||||
<string name="enable_search_history_title">搜索记录</string>
|
||||
<string name="enable_search_history_summary">在本地存储搜索请求</string>
|
||||
<string name="enable_watch_history_title">观看历史</string>
|
||||
<string name="enable_watch_history_summary">存储观看历史</string>
|
||||
<string name="enable_watch_history_title">历史</string>
|
||||
<string name="enable_watch_history_summary">记住观看的视频</string>
|
||||
<string name="resume_on_audio_focus_gain_title">焦点恢复</string>
|
||||
<string name="resume_on_audio_focus_gain_summary">在打扰(例如来电)过后恢复视频播放</string>
|
||||
|
||||
@ -178,7 +178,7 @@
|
||||
<string name="notification_channel_description">NewPipe 后台播放和窗口播放器的通知</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_million">M</string>
|
||||
<string name="short_billion">B</string>
|
||||
@ -216,7 +216,39 @@
|
||||
<string name="title_history_view">观看记录</string>
|
||||
<string name="history_disabled">历史记录已禁用</string>
|
||||
<string name="action_history">历史记录</string>
|
||||
<string name="history_empty">没有历史记录。</string>
|
||||
<string name="history_empty">没有历史记录</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>
|
||||
|
@ -20,6 +20,8 @@
|
||||
<attr name="history" format="reference"/>
|
||||
<attr name="drag_handle" 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-->
|
||||
<attr name="toolbar_shadow_drawable" format="reference"/>
|
||||
|
@ -39,7 +39,10 @@
|
||||
<color name="duration_text_color">#EEFFFFFF</color>
|
||||
<color name="playlist_stream_count_text_color">#ffffff</color>
|
||||
<color name="video_overlay_color">#66000000</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_text_color">#fff</color>
|
||||
|
@ -59,6 +59,8 @@
|
||||
|
||||
<!-- Playlist View Dimensions-->
|
||||
<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 -->
|
||||
<dimen name="playlist_item_title_text_size">14sp</dimen>
|
||||
<dimen name="playlist_detail_title_text_size">16sp</dimen>
|
||||
|
@ -123,6 +123,8 @@
|
||||
<string name="notification_channel_name">NewPipe Notification</string>
|
||||
<string name="notification_channel_description">Notifications for NewPipe Background and Popup Players</string>
|
||||
|
||||
<string name="unknown_content">[Unknown]</string>
|
||||
|
||||
<!-- error strings -->
|
||||
<string name="general_error">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_audio_settings">Audio Settings</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 -->
|
||||
<string name="drawer_open">Open Drawer</string>
|
||||
|
@ -27,6 +27,8 @@
|
||||
<item name="history">@drawable/ic_history_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="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="contrast_background_color">@color/light_contrast_background_color</item>
|
||||
@ -65,6 +67,8 @@
|
||||
<item name="history">@drawable/ic_history_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="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="contrast_background_color">@color/dark_contrast_background_color</item>
|
||||
@ -160,4 +164,12 @@
|
||||
<item name="android:background">@color/dark_youtube_primary_color</item>
|
||||
</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>
|
||||
|
1
fastlane/metadata/android/en-US/full_description.txt
Normal 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.
|
BIN
fastlane/metadata/android/en-US/images/featureGraphic.png
Normal file
After Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 112 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 110 KiB After Width: | Height: | Size: 110 KiB |
Before Width: | Height: | Size: 263 KiB After Width: | Height: | Size: 263 KiB |
Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 101 KiB |
Before Width: | Height: | Size: 122 KiB After Width: | Height: | Size: 122 KiB |