SponsorBlock: Merge branch 'dev' into sponsorblock
# Conflicts: # app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java # app/src/main/res/layout/player.xml # app/src/main/res/values/strings.xml
|
@ -33,7 +33,7 @@ android {
|
||||||
|
|
||||||
// suffix the app id and the app name with git branch name
|
// suffix the app id and the app name with git branch name
|
||||||
def workingBranch = getGitWorkingBranch()
|
def workingBranch = getGitWorkingBranch()
|
||||||
def normalizedWorkingBranch = workingBranch.replaceAll("[^A-Za-z]+", "").toLowerCase()
|
def normalizedWorkingBranch = workingBranch.replaceFirst("^[^A-Za-z]+", "").replaceAll("[^0-9A-Za-z]+", "")
|
||||||
if (normalizedWorkingBranch.isEmpty() || workingBranch == "master" || workingBranch == "dev") {
|
if (normalizedWorkingBranch.isEmpty() || workingBranch == "master" || workingBranch == "dev") {
|
||||||
// default values when branch name could not be determined or is master or dev
|
// default values when branch name could not be determined or is master or dev
|
||||||
applicationIdSuffix ".debug"
|
applicationIdSuffix ".debug"
|
||||||
|
|
|
@ -45,7 +45,8 @@
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name=".player.MainPlayer"
|
android:name=".player.MainPlayer"
|
||||||
android:exported="false">
|
android:exported="false"
|
||||||
|
android:foregroundServiceType="mediaPlayback">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MEDIA_BUTTON" />
|
<action android:name="android.intent.action.MEDIA_BUTTON" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
|
@ -39,6 +39,7 @@ import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
|
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
|
||||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||||
import org.schabi.newpipe.extractor.stream.VideoStream;
|
import org.schabi.newpipe.extractor.stream.VideoStream;
|
||||||
|
import org.schabi.newpipe.player.helper.PlayerHelper;
|
||||||
import org.schabi.newpipe.player.playqueue.ChannelPlayQueue;
|
import org.schabi.newpipe.player.playqueue.ChannelPlayQueue;
|
||||||
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
||||||
import org.schabi.newpipe.player.playqueue.PlaylistPlayQueue;
|
import org.schabi.newpipe.player.playqueue.PlaylistPlayQueue;
|
||||||
|
@ -268,7 +269,7 @@ public class RouterActivity extends AppCompatActivity {
|
||||||
|
|
||||||
final LayoutInflater inflater = LayoutInflater.from(themeWrapperContext);
|
final LayoutInflater inflater = LayoutInflater.from(themeWrapperContext);
|
||||||
final LinearLayout rootLayout = (LinearLayout) inflater.inflate(
|
final LinearLayout rootLayout = (LinearLayout) inflater.inflate(
|
||||||
R.layout.preferred_player_dialog_view, null, false);
|
R.layout.single_choice_dialog_view, null, false);
|
||||||
final RadioGroup radioGroup = rootLayout.findViewById(android.R.id.list);
|
final RadioGroup radioGroup = rootLayout.findViewById(android.R.id.list);
|
||||||
|
|
||||||
final DialogInterface.OnClickListener dialogButtonsClickListener = (dialog, which) -> {
|
final DialogInterface.OnClickListener dialogButtonsClickListener = (dialog, which) -> {
|
||||||
|
@ -278,6 +279,7 @@ public class RouterActivity extends AppCompatActivity {
|
||||||
|
|
||||||
handleChoice(choice.key);
|
handleChoice(choice.key);
|
||||||
|
|
||||||
|
// open future streams always like this one, because "always" button was used by user
|
||||||
if (which == DialogInterface.BUTTON_POSITIVE) {
|
if (which == DialogInterface.BUTTON_POSITIVE) {
|
||||||
preferences.edit()
|
preferences.edit()
|
||||||
.putString(getString(R.string.preferred_open_action_key), choice.key)
|
.putString(getString(R.string.preferred_open_action_key), choice.key)
|
||||||
|
@ -377,23 +379,50 @@ public class RouterActivity extends AppCompatActivity {
|
||||||
final boolean isExtAudioEnabled = preferences.getBoolean(
|
final boolean isExtAudioEnabled = preferences.getBoolean(
|
||||||
getString(R.string.use_external_audio_player_key), false);
|
getString(R.string.use_external_audio_player_key), false);
|
||||||
|
|
||||||
returnList.add(new AdapterChoiceItem(getString(R.string.show_info_key),
|
final AdapterChoiceItem videoPlayer = new AdapterChoiceItem(
|
||||||
getString(R.string.show_info),
|
getString(R.string.video_player_key), getString(R.string.video_player),
|
||||||
resolveResourceIdFromAttr(context, R.attr.ic_info_outline)));
|
resolveResourceIdFromAttr(context, R.attr.ic_play_arrow));
|
||||||
|
final AdapterChoiceItem showInfo = new AdapterChoiceItem(
|
||||||
|
getString(R.string.show_info_key), getString(R.string.show_info),
|
||||||
|
resolveResourceIdFromAttr(context, R.attr.ic_info_outline));
|
||||||
|
final AdapterChoiceItem popupPlayer = new AdapterChoiceItem(
|
||||||
|
getString(R.string.popup_player_key), getString(R.string.popup_player),
|
||||||
|
resolveResourceIdFromAttr(context, R.attr.ic_popup));
|
||||||
|
final AdapterChoiceItem backgroundPlayer = new AdapterChoiceItem(
|
||||||
|
getString(R.string.background_player_key), getString(R.string.background_player),
|
||||||
|
resolveResourceIdFromAttr(context, R.attr.ic_headset));
|
||||||
|
|
||||||
if (capabilities.contains(VIDEO) && !(isExtVideoEnabled && linkType != LinkType.STREAM)) {
|
if (linkType == LinkType.STREAM) {
|
||||||
returnList.add(new AdapterChoiceItem(getString(R.string.video_player_key),
|
if (isExtVideoEnabled) {
|
||||||
getString(R.string.video_player),
|
// show both "show info" and "video player", they are two different activities
|
||||||
resolveResourceIdFromAttr(context, R.attr.ic_play_arrow)));
|
returnList.add(showInfo);
|
||||||
returnList.add(new AdapterChoiceItem(getString(R.string.popup_player_key),
|
returnList.add(videoPlayer);
|
||||||
getString(R.string.popup_player),
|
} else if (capabilities.contains(VIDEO)
|
||||||
resolveResourceIdFromAttr(context, R.attr.ic_popup)));
|
&& PlayerHelper.isAutoplayAllowedByUser(context)) {
|
||||||
|
// show only "video player" since the details activity will be opened and the video
|
||||||
|
// will be autoplayed there and "show info" would do the exact same thing
|
||||||
|
returnList.add(videoPlayer);
|
||||||
|
} else {
|
||||||
|
// show only "show info" if video player is not applicable or autoplay is disabled
|
||||||
|
returnList.add(showInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (capabilities.contains(AUDIO) && !(isExtAudioEnabled && linkType != LinkType.STREAM)) {
|
if (capabilities.contains(VIDEO)) {
|
||||||
returnList.add(new AdapterChoiceItem(getString(R.string.background_player_key),
|
returnList.add(popupPlayer);
|
||||||
getString(R.string.background_player),
|
}
|
||||||
resolveResourceIdFromAttr(context, R.attr.ic_headset)));
|
if (capabilities.contains(AUDIO)) {
|
||||||
|
returnList.add(backgroundPlayer);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
returnList.add(showInfo);
|
||||||
|
if (capabilities.contains(VIDEO) && !isExtVideoEnabled) {
|
||||||
|
returnList.add(videoPlayer);
|
||||||
|
returnList.add(popupPlayer);
|
||||||
|
}
|
||||||
|
if (capabilities.contains(AUDIO) && !isExtAudioEnabled) {
|
||||||
|
returnList.add(backgroundPlayer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
returnList.add(new AdapterChoiceItem(getString(R.string.download_key),
|
returnList.add(new AdapterChoiceItem(getString(R.string.download_key),
|
||||||
|
|
|
@ -11,6 +11,7 @@ import android.content.ServiceConnection;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.pm.ActivityInfo;
|
import android.content.pm.ActivityInfo;
|
||||||
import android.database.ContentObserver;
|
import android.database.ContentObserver;
|
||||||
|
import android.graphics.Color;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
@ -102,13 +103,12 @@ import org.schabi.newpipe.util.ListHelper;
|
||||||
import org.schabi.newpipe.util.Localization;
|
import org.schabi.newpipe.util.Localization;
|
||||||
import org.schabi.newpipe.util.NavigationHelper;
|
import org.schabi.newpipe.util.NavigationHelper;
|
||||||
import org.schabi.newpipe.util.PermissionHelper;
|
import org.schabi.newpipe.util.PermissionHelper;
|
||||||
|
import org.schabi.newpipe.util.SerializedCache;
|
||||||
import org.schabi.newpipe.util.ShareUtils;
|
import org.schabi.newpipe.util.ShareUtils;
|
||||||
import org.schabi.newpipe.util.ThemeHelper;
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
import org.schabi.newpipe.views.AnimatedProgressBar;
|
import org.schabi.newpipe.views.AnimatedProgressBar;
|
||||||
import org.schabi.newpipe.views.LargeTextMovementMethod;
|
import org.schabi.newpipe.views.LargeTextMovementMethod;
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -125,6 +125,7 @@ import io.reactivex.schedulers.Schedulers;
|
||||||
|
|
||||||
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.COMMENTS;
|
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.COMMENTS;
|
||||||
import static org.schabi.newpipe.extractor.stream.StreamExtractor.NO_AGE_LIMIT;
|
import static org.schabi.newpipe.extractor.stream.StreamExtractor.NO_AGE_LIMIT;
|
||||||
|
import static org.schabi.newpipe.player.helper.PlayerHelper.globalScreenOrientationLocked;
|
||||||
import static org.schabi.newpipe.player.helper.PlayerHelper.isClearingQueueConfirmationRequired;
|
import static org.schabi.newpipe.player.helper.PlayerHelper.isClearingQueueConfirmationRequired;
|
||||||
import static org.schabi.newpipe.player.playqueue.PlayQueueItem.RECOVERY_UNSET;
|
import static org.schabi.newpipe.player.playqueue.PlayQueueItem.RECOVERY_UNSET;
|
||||||
import static org.schabi.newpipe.util.AnimationUtils.animateView;
|
import static org.schabi.newpipe.util.AnimationUtils.animateView;
|
||||||
|
@ -337,7 +338,7 @@ public class VideoDetailFragment
|
||||||
stopPlayerListener();
|
stopPlayerListener();
|
||||||
playerService = null;
|
playerService = null;
|
||||||
player = null;
|
player = null;
|
||||||
saveCurrentAndRestoreDefaultBrightness();
|
restoreDefaultBrightness();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -404,7 +405,7 @@ public class VideoDetailFragment
|
||||||
settingsContentObserver = new ContentObserver(new Handler()) {
|
settingsContentObserver = new ContentObserver(new Handler()) {
|
||||||
@Override
|
@Override
|
||||||
public void onChange(final boolean selfChange) {
|
public void onChange(final boolean selfChange) {
|
||||||
if (activity != null && !PlayerHelper.globalScreenOrientationLocked(activity)) {
|
if (activity != null && !globalScreenOrientationLocked(activity)) {
|
||||||
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
|
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -426,7 +427,7 @@ public class VideoDetailFragment
|
||||||
if (currentWorker != null) {
|
if (currentWorker != null) {
|
||||||
currentWorker.dispose();
|
currentWorker.dispose();
|
||||||
}
|
}
|
||||||
saveCurrentAndRestoreDefaultBrightness();
|
restoreDefaultBrightness();
|
||||||
PreferenceManager.getDefaultSharedPreferences(requireContext())
|
PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||||
.edit()
|
.edit()
|
||||||
.putString(getString(R.string.stream_info_selected_tab_key),
|
.putString(getString(R.string.stream_info_selected_tab_key),
|
||||||
|
@ -538,31 +539,51 @@ public class VideoDetailFragment
|
||||||
super.onSaveInstanceState(outState);
|
super.onSaveInstanceState(outState);
|
||||||
|
|
||||||
if (!isLoading.get() && currentInfo != null && isVisible()) {
|
if (!isLoading.get() && currentInfo != null && isVisible()) {
|
||||||
outState.putSerializable(INFO_KEY, currentInfo);
|
final String infoCacheKey = SerializedCache.getInstance()
|
||||||
|
.put(currentInfo, StreamInfo.class);
|
||||||
|
if (infoCacheKey != null) {
|
||||||
|
outState.putString(INFO_KEY, infoCacheKey);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (playQueue != null) {
|
if (playQueue != null) {
|
||||||
outState.putSerializable(VideoPlayer.PLAY_QUEUE_KEY, playQueue);
|
final String queueCacheKey = SerializedCache.getInstance()
|
||||||
|
.put(playQueue, PlayQueue.class);
|
||||||
|
if (queueCacheKey != null) {
|
||||||
|
outState.putString(VideoPlayer.PLAY_QUEUE_KEY, queueCacheKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final String stackCacheKey = SerializedCache.getInstance().put(stack, LinkedList.class);
|
||||||
|
if (stackCacheKey != null) {
|
||||||
|
outState.putString(STACK_KEY, stackCacheKey);
|
||||||
}
|
}
|
||||||
outState.putSerializable(STACK_KEY, stack);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onRestoreInstanceState(@NonNull final Bundle savedState) {
|
protected void onRestoreInstanceState(@NonNull final Bundle savedState) {
|
||||||
super.onRestoreInstanceState(savedState);
|
super.onRestoreInstanceState(savedState);
|
||||||
|
|
||||||
Serializable serializable = savedState.getSerializable(INFO_KEY);
|
final String infoCacheKey = savedState.getString(INFO_KEY);
|
||||||
if (serializable instanceof StreamInfo) {
|
if (infoCacheKey != null) {
|
||||||
currentInfo = (StreamInfo) serializable;
|
currentInfo = SerializedCache.getInstance().take(infoCacheKey, StreamInfo.class);
|
||||||
InfoCache.getInstance().putInfo(serviceId, url, currentInfo, InfoItem.InfoType.STREAM);
|
if (currentInfo != null) {
|
||||||
|
InfoCache.getInstance()
|
||||||
|
.putInfo(serviceId, url, currentInfo, InfoItem.InfoType.STREAM);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
serializable = savedState.getSerializable(STACK_KEY);
|
final String stackCacheKey = savedState.getString(STACK_KEY);
|
||||||
if (serializable instanceof Collection) {
|
if (stackCacheKey != null) {
|
||||||
//noinspection unchecked
|
final LinkedList<StackItem> cachedStack =
|
||||||
stack.addAll((Collection<? extends StackItem>) serializable);
|
SerializedCache.getInstance().take(stackCacheKey, LinkedList.class);
|
||||||
|
if (cachedStack != null) {
|
||||||
|
stack.addAll(cachedStack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final String queueCacheKey = savedState.getString(VideoPlayer.PLAY_QUEUE_KEY);
|
||||||
|
if (queueCacheKey != null) {
|
||||||
|
playQueue = SerializedCache.getInstance().take(queueCacheKey, PlayQueue.class);
|
||||||
}
|
}
|
||||||
playQueue = (PlayQueue) savedState.getSerializable(VideoPlayer.PLAY_QUEUE_KEY);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -958,6 +979,9 @@ public class VideoDetailFragment
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setInitialData(sid, videoUrl, title, queue);
|
setInitialData(sid, videoUrl, title, queue);
|
||||||
|
if (player != null) {
|
||||||
|
player.disablePreloadingOfCurrentTrack();
|
||||||
|
}
|
||||||
startLoading(false, true);
|
startLoading(false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1233,7 +1257,7 @@ public class VideoDetailFragment
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isExternalPlayerEnabled() {
|
private boolean isExternalPlayerEnabled() {
|
||||||
return PreferenceManager.getDefaultSharedPreferences(getContext())
|
return PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||||
.getBoolean(getString(R.string.use_external_video_player_key), false);
|
.getBoolean(getString(R.string.use_external_video_player_key), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1244,23 +1268,7 @@ public class VideoDetailFragment
|
||||||
&& !isExternalPlayerEnabled()
|
&& !isExternalPlayerEnabled()
|
||||||
&& (player == null || player.videoPlayerSelected())
|
&& (player == null || player.videoPlayerSelected())
|
||||||
&& bottomSheetState != BottomSheetBehavior.STATE_HIDDEN
|
&& bottomSheetState != BottomSheetBehavior.STATE_HIDDEN
|
||||||
&& isAutoplayAllowedByUser();
|
&& PlayerHelper.isAutoplayAllowedByUser(requireContext());
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isAutoplayAllowedByUser() {
|
|
||||||
if (activity == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (PlayerHelper.getAutoplayType(activity)) {
|
|
||||||
case PlayerHelper.AutoplayType.AUTOPLAY_TYPE_NEVER:
|
|
||||||
return false;
|
|
||||||
case PlayerHelper.AutoplayType.AUTOPLAY_TYPE_WIFI:
|
|
||||||
return !ListHelper.isMeteredNetwork(activity);
|
|
||||||
case PlayerHelper.AutoplayType.AUTOPLAY_TYPE_ALWAYS:
|
|
||||||
default:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addVideoPlayerView() {
|
private void addVideoPlayerView() {
|
||||||
|
@ -1821,9 +1829,6 @@ public class VideoDetailFragment
|
||||||
setOverlayPlayPauseImage();
|
setOverlayPlayPauseImage();
|
||||||
|
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case BasePlayer.STATE_COMPLETED:
|
|
||||||
restoreDefaultOrientation();
|
|
||||||
break;
|
|
||||||
case BasePlayer.STATE_PLAYING:
|
case BasePlayer.STATE_PLAYING:
|
||||||
if (positionView.getAlpha() != 1.0f
|
if (positionView.getAlpha() != 1.0f
|
||||||
&& player.getPlayQueue() != null
|
&& player.getPlayQueue() != null
|
||||||
|
@ -1882,10 +1887,11 @@ public class VideoDetailFragment
|
||||||
public void onPlayerError(final ExoPlaybackException error) {
|
public void onPlayerError(final ExoPlaybackException error) {
|
||||||
if (error.type == ExoPlaybackException.TYPE_SOURCE
|
if (error.type == ExoPlaybackException.TYPE_SOURCE
|
||||||
|| error.type == ExoPlaybackException.TYPE_UNEXPECTED) {
|
|| error.type == ExoPlaybackException.TYPE_UNEXPECTED) {
|
||||||
hideMainPlayer();
|
// Properly exit from fullscreen
|
||||||
if (playerService != null && player.isFullscreen()) {
|
if (playerService != null && player.isFullscreen()) {
|
||||||
player.toggleFullscreen();
|
player.toggleFullscreen();
|
||||||
}
|
}
|
||||||
|
hideMainPlayer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1924,7 +1930,13 @@ public class VideoDetailFragment
|
||||||
}
|
}
|
||||||
scrollToTop();
|
scrollToTop();
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
addVideoPlayerView();
|
addVideoPlayerView();
|
||||||
|
} else {
|
||||||
|
// KitKat needs a delay before addVideoPlayerView call or it reports wrong height in
|
||||||
|
// activity.getWindow().getDecorView().getHeight()
|
||||||
|
new Handler().post(this::addVideoPlayerView);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1932,13 +1944,15 @@ public class VideoDetailFragment
|
||||||
// In tablet user experience will be better if screen will not be rotated
|
// In tablet user experience will be better if screen will not be rotated
|
||||||
// from landscape to portrait every time.
|
// from landscape to portrait every time.
|
||||||
// Just turn on fullscreen mode in landscape orientation
|
// Just turn on fullscreen mode in landscape orientation
|
||||||
if (isLandscape() && DeviceUtils.isTablet(activity)) {
|
// or portrait & unlocked global orientation
|
||||||
|
if (DeviceUtils.isTablet(activity)
|
||||||
|
&& (!globalScreenOrientationLocked(activity) || isLandscape())) {
|
||||||
player.toggleFullscreen();
|
player.toggleFullscreen();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final int newOrientation = isLandscape()
|
final int newOrientation = isLandscape()
|
||||||
? ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
|
? ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
|
||||||
: ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
|
: ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
|
||||||
|
|
||||||
activity.setRequestedOrientation(newOrientation);
|
activity.setRequestedOrientation(newOrientation);
|
||||||
|
@ -1983,7 +1997,11 @@ public class VideoDetailFragment
|
||||||
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
|
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
|
||||||
}
|
}
|
||||||
activity.getWindow().getDecorView().setSystemUiVisibility(0);
|
activity.getWindow().getDecorView().setSystemUiVisibility(0);
|
||||||
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
|
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
activity.getWindow().setStatusBarColor(ThemeHelper.resolveColorFromAttr(
|
||||||
|
requireContext(), android.R.attr.colorPrimary));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void hideSystemUi() {
|
private void hideSystemUi() {
|
||||||
|
@ -1998,18 +2016,26 @@ public class VideoDetailFragment
|
||||||
// Prevent jumping of the player on devices with cutout
|
// Prevent jumping of the player on devices with cutout
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||||
activity.getWindow().getAttributes().layoutInDisplayCutoutMode =
|
activity.getWindow().getAttributes().layoutInDisplayCutoutMode =
|
||||||
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
|
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
|
||||||
}
|
}
|
||||||
final int visibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
int visibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||||
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||||
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||||
| View.SYSTEM_UI_FLAG_FULLSCREEN
|
|
||||||
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||||
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
|
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
|
||||||
|
// In multiWindow mode status bar is not transparent for devices with cutout
|
||||||
|
// if I include this flag. So without it is better in this case
|
||||||
|
if (!isInMultiWindow()) {
|
||||||
|
visibility |= View.SYSTEM_UI_FLAG_FULLSCREEN;
|
||||||
|
}
|
||||||
activity.getWindow().getDecorView().setSystemUiVisibility(visibility);
|
activity.getWindow().getDecorView().setSystemUiVisibility(visibility);
|
||||||
activity.getWindow().setFlags(
|
|
||||||
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
|
||||||
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
|
&& (isInMultiWindow() || (player != null && player.isFullscreen()))) {
|
||||||
|
activity.getWindow().setStatusBarColor(Color.TRANSPARENT);
|
||||||
|
activity.getWindow().setNavigationBarColor(Color.TRANSPARENT);
|
||||||
|
}
|
||||||
|
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Listener implementation
|
// Listener implementation
|
||||||
|
@ -2027,13 +2053,11 @@ public class VideoDetailFragment
|
||||||
&& player.getPlayer().getPlaybackState() != Player.STATE_IDLE;
|
&& player.getPlayer().getPlaybackState() != Player.STATE_IDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveCurrentAndRestoreDefaultBrightness() {
|
private void restoreDefaultBrightness() {
|
||||||
final WindowManager.LayoutParams lp = activity.getWindow().getAttributes();
|
final WindowManager.LayoutParams lp = activity.getWindow().getAttributes();
|
||||||
if (lp.screenBrightness == -1) {
|
if (lp.screenBrightness == -1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Save current brightness level
|
|
||||||
PlayerHelper.setScreenBrightness(activity, lp.screenBrightness);
|
|
||||||
|
|
||||||
// Restore the old brightness when fragment.onPause() called or
|
// Restore the old brightness when fragment.onPause() called or
|
||||||
// when a player is in portrait
|
// when a player is in portrait
|
||||||
|
@ -2052,7 +2076,7 @@ public class VideoDetailFragment
|
||||||
|| !player.isFullscreen()
|
|| !player.isFullscreen()
|
||||||
|| bottomSheetState != BottomSheetBehavior.STATE_EXPANDED) {
|
|| bottomSheetState != BottomSheetBehavior.STATE_EXPANDED) {
|
||||||
// Apply system brightness when the player is not in fullscreen
|
// Apply system brightness when the player is not in fullscreen
|
||||||
saveCurrentAndRestoreDefaultBrightness();
|
restoreDefaultBrightness();
|
||||||
} else {
|
} else {
|
||||||
// Restore already saved brightness level
|
// Restore already saved brightness level
|
||||||
final float brightnessLevel = PlayerHelper.getScreenBrightness(activity);
|
final float brightnessLevel = PlayerHelper.getScreenBrightness(activity);
|
||||||
|
@ -2071,11 +2095,9 @@ public class VideoDetailFragment
|
||||||
}
|
}
|
||||||
|
|
||||||
player.checkLandscape();
|
player.checkLandscape();
|
||||||
final boolean orientationLocked = PlayerHelper.globalScreenOrientationLocked(activity);
|
|
||||||
// Let's give a user time to look at video information page if video is not playing
|
// Let's give a user time to look at video information page if video is not playing
|
||||||
if (orientationLocked && !player.isPlaying()) {
|
if (globalScreenOrientationLocked(activity) && !player.isPlaying()) {
|
||||||
player.onPlay();
|
player.onPlay();
|
||||||
player.showControlsThenHide();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2278,6 +2300,7 @@ public class VideoDetailFragment
|
||||||
&& player.videoPlayerSelected()) {
|
&& player.videoPlayerSelected()) {
|
||||||
player.toggleFullscreen();
|
player.toggleFullscreen();
|
||||||
}
|
}
|
||||||
|
setOverlayLook(appBarLayout, behavior, 1);
|
||||||
break;
|
break;
|
||||||
case BottomSheetBehavior.STATE_COLLAPSED:
|
case BottomSheetBehavior.STATE_COLLAPSED:
|
||||||
moveFocusToMainFragment(true);
|
moveFocusToMainFragment(true);
|
||||||
|
@ -2287,6 +2310,7 @@ public class VideoDetailFragment
|
||||||
if (player != null) {
|
if (player != null) {
|
||||||
player.onQueueClosed();
|
player.onQueueClosed();
|
||||||
}
|
}
|
||||||
|
setOverlayLook(appBarLayout, behavior, 0);
|
||||||
break;
|
break;
|
||||||
case BottomSheetBehavior.STATE_DRAGGING:
|
case BottomSheetBehavior.STATE_DRAGGING:
|
||||||
case BottomSheetBehavior.STATE_SETTLING:
|
case BottomSheetBehavior.STATE_SETTLING:
|
||||||
|
|
|
@ -52,6 +52,7 @@ import org.schabi.newpipe.report.UserAction;
|
||||||
import org.schabi.newpipe.util.DeviceUtils;
|
import org.schabi.newpipe.util.DeviceUtils;
|
||||||
import org.schabi.newpipe.util.AnimationUtils;
|
import org.schabi.newpipe.util.AnimationUtils;
|
||||||
import org.schabi.newpipe.util.Constants;
|
import org.schabi.newpipe.util.Constants;
|
||||||
|
import org.schabi.newpipe.util.ExceptionUtils;
|
||||||
import org.schabi.newpipe.util.ExtractorHelper;
|
import org.schabi.newpipe.util.ExtractorHelper;
|
||||||
import org.schabi.newpipe.util.NavigationHelper;
|
import org.schabi.newpipe.util.NavigationHelper;
|
||||||
import org.schabi.newpipe.util.ServiceHelper;
|
import org.schabi.newpipe.util.ServiceHelper;
|
||||||
|
@ -78,7 +79,7 @@ import static androidx.recyclerview.widget.ItemTouchHelper.Callback.makeMovement
|
||||||
import static java.util.Arrays.asList;
|
import static java.util.Arrays.asList;
|
||||||
import static org.schabi.newpipe.util.AnimationUtils.animateView;
|
import static org.schabi.newpipe.util.AnimationUtils.animateView;
|
||||||
|
|
||||||
public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.InfoItemsPage>
|
public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.InfoItemsPage<?>>
|
||||||
implements BackPressable {
|
implements BackPressable {
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Search
|
// Search
|
||||||
|
@ -133,7 +134,6 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
||||||
private Map<Integer, String> menuItemToFilterName;
|
private Map<Integer, String> menuItemToFilterName;
|
||||||
private StreamingService service;
|
private StreamingService service;
|
||||||
private Page nextPage;
|
private Page nextPage;
|
||||||
private String contentCountry;
|
|
||||||
private boolean isSuggestionsEnabled = true;
|
private boolean isSuggestionsEnabled = true;
|
||||||
|
|
||||||
private Disposable searchDisposable;
|
private Disposable searchDisposable;
|
||||||
|
@ -154,6 +154,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
||||||
private TextView correctSuggestion;
|
private TextView correctSuggestion;
|
||||||
|
|
||||||
private View suggestionsPanel;
|
private View suggestionsPanel;
|
||||||
|
private boolean suggestionsPanelVisible = false;
|
||||||
private RecyclerView suggestionsRecyclerView;
|
private RecyclerView suggestionsRecyclerView;
|
||||||
|
|
||||||
/*////////////////////////////////////////////////////////////////////////*/
|
/*////////////////////////////////////////////////////////////////////////*/
|
||||||
|
@ -204,8 +205,6 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
||||||
= PreferenceManager.getDefaultSharedPreferences(activity);
|
= PreferenceManager.getDefaultSharedPreferences(activity);
|
||||||
isSuggestionsEnabled = preferences
|
isSuggestionsEnabled = preferences
|
||||||
.getBoolean(getString(R.string.show_search_suggestions_key), true);
|
.getBoolean(getString(R.string.show_search_suggestions_key), true);
|
||||||
contentCountry = preferences.getString(getString(R.string.content_country_key),
|
|
||||||
getString(R.string.default_localization_key));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -233,9 +232,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
||||||
if (suggestionDisposable != null) {
|
if (suggestionDisposable != null) {
|
||||||
suggestionDisposable.dispose();
|
suggestionDisposable.dispose();
|
||||||
}
|
}
|
||||||
if (disposables != null) {
|
|
||||||
disposables.clear();
|
disposables.clear();
|
||||||
}
|
|
||||||
hideKeyboardSearch();
|
hideKeyboardSearch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,8 +246,8 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
||||||
try {
|
try {
|
||||||
service = NewPipe.getService(serviceId);
|
service = NewPipe.getService(serviceId);
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
ErrorActivity.reportError(getActivity(), e, getActivity().getClass(),
|
ErrorActivity.reportError(getActivity(), e, requireActivity().getClass(),
|
||||||
getActivity().findViewById(android.R.id.content),
|
requireActivity().findViewById(android.R.id.content),
|
||||||
ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR,
|
ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR,
|
||||||
"",
|
"",
|
||||||
"", R.string.general_error));
|
"", R.string.general_error));
|
||||||
|
@ -303,26 +300,20 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
||||||
if (suggestionDisposable != null) {
|
if (suggestionDisposable != null) {
|
||||||
suggestionDisposable.dispose();
|
suggestionDisposable.dispose();
|
||||||
}
|
}
|
||||||
if (disposables != null) {
|
|
||||||
disposables.clear();
|
disposables.clear();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
|
public void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
|
||||||
switch (requestCode) {
|
if (requestCode == ReCaptchaActivity.RECAPTCHA_REQUEST) {
|
||||||
case ReCaptchaActivity.RECAPTCHA_REQUEST:
|
|
||||||
if (resultCode == Activity.RESULT_OK
|
if (resultCode == Activity.RESULT_OK
|
||||||
&& !TextUtils.isEmpty(searchString)) {
|
&& !TextUtils.isEmpty(searchString)) {
|
||||||
search(searchString, contentFilter, sortFilter);
|
search(searchString, contentFilter, sortFilter);
|
||||||
} else {
|
} else {
|
||||||
Log.e(TAG, "ReCaptcha failed");
|
Log.e(TAG, "ReCaptcha failed");
|
||||||
}
|
}
|
||||||
break;
|
} else {
|
||||||
|
|
||||||
default:
|
|
||||||
Log.e(TAG, "Request code from activity not supported [" + requestCode + "]");
|
Log.e(TAG, "Request code from activity not supported [" + requestCode + "]");
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -340,7 +331,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
||||||
@Override
|
@Override
|
||||||
public int getMovementFlags(@NonNull final RecyclerView recyclerView,
|
public int getMovementFlags(@NonNull final RecyclerView recyclerView,
|
||||||
@NonNull final RecyclerView.ViewHolder viewHolder) {
|
@NonNull final RecyclerView.ViewHolder viewHolder) {
|
||||||
return getSuggestionMovementFlags(recyclerView, viewHolder);
|
return getSuggestionMovementFlags(viewHolder);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -352,7 +343,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSwiped(@NonNull final RecyclerView.ViewHolder viewHolder, final int i) {
|
public void onSwiped(@NonNull final RecyclerView.ViewHolder viewHolder, final int i) {
|
||||||
onSuggestionItemSwiped(viewHolder, i);
|
onSuggestionItemSwiped(viewHolder);
|
||||||
}
|
}
|
||||||
}).attachToRecyclerView(suggestionsRecyclerView);
|
}).attachToRecyclerView(suggestionsRecyclerView);
|
||||||
|
|
||||||
|
@ -627,6 +618,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "showSuggestionsPanel() called");
|
Log.d(TAG, "showSuggestionsPanel() called");
|
||||||
}
|
}
|
||||||
|
suggestionsPanelVisible = true;
|
||||||
animateView(suggestionsPanel, AnimationUtils.Type.LIGHT_SLIDE_AND_ALPHA, true, 200);
|
animateView(suggestionsPanel, AnimationUtils.Type.LIGHT_SLIDE_AND_ALPHA, true, 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -634,6 +626,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "hideSuggestionsPanel() called");
|
Log.d(TAG, "hideSuggestionsPanel() called");
|
||||||
}
|
}
|
||||||
|
suggestionsPanelVisible = false;
|
||||||
animateView(suggestionsPanel, AnimationUtils.Type.LIGHT_SLIDE_AND_ALPHA, false, 200);
|
animateView(suggestionsPanel, AnimationUtils.Type.LIGHT_SLIDE_AND_ALPHA, false, 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -669,8 +662,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showDeleteSuggestionDialog(final SuggestionItem item) {
|
private void showDeleteSuggestionDialog(final SuggestionItem item) {
|
||||||
if (activity == null || historyRecordManager == null || suggestionPublisher == null
|
if (activity == null || historyRecordManager == null || searchEditText == null) {
|
||||||
|| searchEditText == null || disposables == null) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final String query = item.query;
|
final String query = item.query;
|
||||||
|
@ -695,7 +687,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onBackPressed() {
|
public boolean onBackPressed() {
|
||||||
if (suggestionsPanel.getVisibility() == View.VISIBLE
|
if (suggestionsPanelVisible
|
||||||
&& infoListAdapter.getItemsList().size() > 0
|
&& infoListAdapter.getItemsList().size() > 0
|
||||||
&& !isLoading.get()) {
|
&& !isLoading.get()) {
|
||||||
hideSuggestionsPanel();
|
hideSuggestionsPanel();
|
||||||
|
@ -742,6 +734,13 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
||||||
|
|
||||||
final Observable<List<SuggestionItem>> network = ExtractorHelper
|
final Observable<List<SuggestionItem>> network = ExtractorHelper
|
||||||
.suggestionsFor(serviceId, query)
|
.suggestionsFor(serviceId, query)
|
||||||
|
.onErrorReturn(throwable -> {
|
||||||
|
if (!ExceptionUtils.isNetworkRelated(throwable)) {
|
||||||
|
showSnackBarError(throwable, UserAction.GET_SUGGESTIONS,
|
||||||
|
NewPipe.getNameOfService(serviceId), searchString, 0);
|
||||||
|
}
|
||||||
|
return new ArrayList<>();
|
||||||
|
})
|
||||||
.toObservable()
|
.toObservable()
|
||||||
.map(strings -> {
|
.map(strings -> {
|
||||||
final List<SuggestionItem> result = new ArrayList<>();
|
final List<SuggestionItem> result = new ArrayList<>();
|
||||||
|
@ -791,21 +790,23 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
||||||
// no-op
|
// no-op
|
||||||
}
|
}
|
||||||
|
|
||||||
private void search(final String ss, final String[] cf, final String sf) {
|
private void search(final String theSearchString,
|
||||||
|
final String[] theContentFilter,
|
||||||
|
final String theSortFilter) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "search() called with: query = [" + ss + "]");
|
Log.d(TAG, "search() called with: query = [" + theSearchString + "]");
|
||||||
}
|
}
|
||||||
if (ss.isEmpty()) {
|
if (theSearchString.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final StreamingService streamingService = NewPipe.getServiceByUrl(ss);
|
final StreamingService streamingService = NewPipe.getServiceByUrl(theSearchString);
|
||||||
if (streamingService != null) {
|
if (streamingService != null) {
|
||||||
showLoading();
|
showLoading();
|
||||||
disposables.add(Observable
|
disposables.add(Observable
|
||||||
.fromCallable(() ->
|
.fromCallable(() -> NavigationHelper.getIntentByLink(activity,
|
||||||
NavigationHelper.getIntentByLink(activity, streamingService, ss))
|
streamingService, theSearchString))
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe(intent -> {
|
.subscribe(intent -> {
|
||||||
|
@ -820,29 +821,27 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
||||||
}
|
}
|
||||||
|
|
||||||
lastSearchedString = this.searchString;
|
lastSearchedString = this.searchString;
|
||||||
this.searchString = ss;
|
this.searchString = theSearchString;
|
||||||
infoListAdapter.clearStreamItemList();
|
infoListAdapter.clearStreamItemList();
|
||||||
hideSuggestionsPanel();
|
hideSuggestionsPanel();
|
||||||
hideKeyboardSearch();
|
hideKeyboardSearch();
|
||||||
|
|
||||||
historyRecordManager.onSearched(serviceId, ss)
|
disposables.add(historyRecordManager.onSearched(serviceId, theSearchString)
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe(
|
.subscribe(
|
||||||
ignored -> {
|
ignored -> {
|
||||||
},
|
},
|
||||||
error -> showSnackBarError(error, UserAction.SEARCHED,
|
error -> showSnackBarError(error, UserAction.SEARCHED,
|
||||||
NewPipe.getNameOfService(serviceId), ss, 0)
|
NewPipe.getNameOfService(serviceId), theSearchString, 0)
|
||||||
);
|
));
|
||||||
suggestionPublisher.onNext(ss);
|
suggestionPublisher.onNext(theSearchString);
|
||||||
startLoading(false);
|
startLoading(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void startLoading(final boolean forceLoad) {
|
public void startLoading(final boolean forceLoad) {
|
||||||
super.startLoading(forceLoad);
|
super.startLoading(forceLoad);
|
||||||
if (disposables != null) {
|
|
||||||
disposables.clear();
|
disposables.clear();
|
||||||
}
|
|
||||||
if (searchDisposable != null) {
|
if (searchDisposable != null) {
|
||||||
searchDisposable.dispose();
|
searchDisposable.dispose();
|
||||||
}
|
}
|
||||||
|
@ -881,8 +880,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean hasMoreItems() {
|
protected boolean hasMoreItems() {
|
||||||
// TODO: No way to tell if search has more items in the moment
|
return Page.isValid(nextPage);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -895,22 +893,25 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
||||||
// Utils
|
// Utils
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
private void changeContentFilter(final MenuItem item, final List<String> cf) {
|
private void changeContentFilter(final MenuItem item, final List<String> theContentFilter) {
|
||||||
this.filterItemCheckedId = item.getItemId();
|
filterItemCheckedId = item.getItemId();
|
||||||
item.setChecked(true);
|
item.setChecked(true);
|
||||||
|
|
||||||
this.contentFilter = new String[]{cf.get(0)};
|
contentFilter = new String[]{theContentFilter.get(0)};
|
||||||
|
|
||||||
if (!TextUtils.isEmpty(searchString)) {
|
if (!TextUtils.isEmpty(searchString)) {
|
||||||
search(searchString, this.contentFilter, sortFilter);
|
search(searchString, contentFilter, sortFilter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setQuery(final int sid, final String ss, final String[] cf, final String sf) {
|
private void setQuery(final int theServiceId,
|
||||||
this.serviceId = sid;
|
final String theSearchString,
|
||||||
this.searchString = searchString;
|
final String[] theContentFilter,
|
||||||
this.contentFilter = cf;
|
final String theSortFilter) {
|
||||||
this.sortFilter = sf;
|
serviceId = theServiceId;
|
||||||
|
searchString = theSearchString;
|
||||||
|
contentFilter = theContentFilter;
|
||||||
|
sortFilter = theSortFilter;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -924,7 +925,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
||||||
suggestionsRecyclerView.smoothScrollToPosition(0);
|
suggestionsRecyclerView.smoothScrollToPosition(0);
|
||||||
suggestionsRecyclerView.post(() -> suggestionListAdapter.setItems(suggestions));
|
suggestionsRecyclerView.post(() -> suggestionListAdapter.setItems(suggestions));
|
||||||
|
|
||||||
if (errorPanelRoot.getVisibility() == View.VISIBLE) {
|
if (suggestionsPanelVisible && errorPanelRoot.getVisibility() == View.VISIBLE) {
|
||||||
hideLoading();
|
hideLoading();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1027,7 +1028,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleNextItems(final ListExtractor.InfoItemsPage result) {
|
public void handleNextItems(final ListExtractor.InfoItemsPage<?> result) {
|
||||||
showListFooter(false);
|
showListFooter(false);
|
||||||
infoListAdapter.addInfoItemList(result.getItems());
|
infoListAdapter.addInfoItemList(result.getItems());
|
||||||
nextPage = result.getNextPage();
|
nextPage = result.getNextPage();
|
||||||
|
@ -1066,8 +1067,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
||||||
// Suggestion item touch helper
|
// Suggestion item touch helper
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
public int getSuggestionMovementFlags(@NonNull final RecyclerView recyclerView,
|
public int getSuggestionMovementFlags(@NonNull final RecyclerView.ViewHolder viewHolder) {
|
||||||
@NonNull final RecyclerView.ViewHolder viewHolder) {
|
|
||||||
final int position = viewHolder.getAdapterPosition();
|
final int position = viewHolder.getAdapterPosition();
|
||||||
if (position == RecyclerView.NO_POSITION) {
|
if (position == RecyclerView.NO_POSITION) {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1078,8 +1078,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
||||||
ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) : 0;
|
ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onSuggestionItemSwiped(@NonNull final RecyclerView.ViewHolder viewHolder,
|
public void onSuggestionItemSwiped(@NonNull final RecyclerView.ViewHolder viewHolder) {
|
||||||
final int i) {
|
|
||||||
final int position = viewHolder.getAdapterPosition();
|
final int position = viewHolder.getAdapterPosition();
|
||||||
final String query = suggestionListAdapter.getItem(position).query;
|
final String query = suggestionListAdapter.getItem(position).query;
|
||||||
final Disposable onDelete = historyRecordManager.deleteSearchHistory(query)
|
final Disposable onDelete = historyRecordManager.deleteSearchHistory(query)
|
||||||
|
|
|
@ -191,6 +191,8 @@ public abstract class BasePlayer implements
|
||||||
@NonNull
|
@NonNull
|
||||||
protected final HistoryRecordManager recordManager;
|
protected final HistoryRecordManager recordManager;
|
||||||
@NonNull
|
@NonNull
|
||||||
|
protected final SharedPreferences sharedPreferences;
|
||||||
|
@NonNull
|
||||||
protected final CustomTrackSelector trackSelector;
|
protected final CustomTrackSelector trackSelector;
|
||||||
@NonNull
|
@NonNull
|
||||||
protected final PlayerDataSource dataSource;
|
protected final PlayerDataSource dataSource;
|
||||||
|
@ -223,6 +225,7 @@ public abstract class BasePlayer implements
|
||||||
setupBroadcastReceiver(intentFilter);
|
setupBroadcastReceiver(intentFilter);
|
||||||
|
|
||||||
this.recordManager = new HistoryRecordManager(context);
|
this.recordManager = new HistoryRecordManager(context);
|
||||||
|
this.sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
|
|
||||||
this.progressUpdateReactor = new SerialDisposable();
|
this.progressUpdateReactor = new SerialDisposable();
|
||||||
this.databaseUpdateReactor = new CompositeDisposable();
|
this.databaseUpdateReactor = new CompositeDisposable();
|
||||||
|
@ -1422,7 +1425,15 @@ public abstract class BasePlayer implements
|
||||||
Log.d(TAG, "seekBy() called with: position = [" + positionMillis + "]");
|
Log.d(TAG, "seekBy() called with: position = [" + positionMillis + "]");
|
||||||
}
|
}
|
||||||
if (simpleExoPlayer != null) {
|
if (simpleExoPlayer != null) {
|
||||||
simpleExoPlayer.seekTo(positionMillis);
|
// prevent invalid positions when fast-forwarding/-rewinding
|
||||||
|
long normalizedPositionMillis = positionMillis;
|
||||||
|
if (normalizedPositionMillis < 0) {
|
||||||
|
normalizedPositionMillis = 0;
|
||||||
|
} else if (normalizedPositionMillis > simpleExoPlayer.getDuration()) {
|
||||||
|
normalizedPositionMillis = simpleExoPlayer.getDuration();
|
||||||
|
}
|
||||||
|
|
||||||
|
simpleExoPlayer.seekTo(normalizedPositionMillis);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1593,6 +1604,11 @@ public abstract class BasePlayer implements
|
||||||
return currentMetadata;
|
return currentMetadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public LoadController getLoadController() {
|
||||||
|
return (LoadController) loadControl;
|
||||||
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public String getVideoUrl() {
|
public String getVideoUrl() {
|
||||||
return currentMetadata == null
|
return currentMetadata == null
|
||||||
|
|
|
@ -19,35 +19,18 @@
|
||||||
|
|
||||||
package org.schabi.newpipe.player;
|
package org.schabi.newpipe.player;
|
||||||
|
|
||||||
import android.app.NotificationManager;
|
|
||||||
import android.app.PendingIntent;
|
|
||||||
import android.app.Service;
|
import android.app.Service;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
import android.os.Build;
|
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import androidx.preference.PreferenceManager;
|
|
||||||
import android.util.DisplayMetrics;
|
import android.util.DisplayMetrics;
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.view.WindowManager;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
import androidx.core.app.NotificationCompat;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.RemoteViews;
|
import android.view.ViewGroup;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
|
||||||
import com.google.android.exoplayer2.Player;
|
|
||||||
|
|
||||||
import org.schabi.newpipe.BuildConfig;
|
|
||||||
import org.schabi.newpipe.MainActivity;
|
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.util.BitmapUtils;
|
|
||||||
import org.schabi.newpipe.util.NavigationHelper;
|
|
||||||
import org.schabi.newpipe.util.ThemeHelper;
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
|
||||||
import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
|
import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
|
||||||
|
@ -64,7 +47,6 @@ public final class MainPlayer extends Service {
|
||||||
|
|
||||||
private VideoPlayerImpl playerImpl;
|
private VideoPlayerImpl playerImpl;
|
||||||
private WindowManager windowManager;
|
private WindowManager windowManager;
|
||||||
private SharedPreferences sharedPreferences;
|
|
||||||
|
|
||||||
private final IBinder mBinder = new MainPlayer.LocalBinder();
|
private final IBinder mBinder = new MainPlayer.LocalBinder();
|
||||||
|
|
||||||
|
@ -78,30 +60,26 @@ public final class MainPlayer extends Service {
|
||||||
// Notification
|
// Notification
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
static final int NOTIFICATION_ID = 123789;
|
static final String ACTION_CLOSE
|
||||||
private NotificationManager notificationManager;
|
= "org.schabi.newpipe.player.MainPlayer.CLOSE";
|
||||||
private NotificationCompat.Builder notBuilder;
|
static final String ACTION_PLAY_PAUSE
|
||||||
private RemoteViews notRemoteView;
|
= "org.schabi.newpipe.player.MainPlayer.PLAY_PAUSE";
|
||||||
private RemoteViews bigNotRemoteView;
|
static final String ACTION_OPEN_CONTROLS
|
||||||
|
= "org.schabi.newpipe.player.MainPlayer.OPEN_CONTROLS";
|
||||||
static final String ACTION_CLOSE =
|
static final String ACTION_REPEAT
|
||||||
"org.schabi.newpipe.player.MainPlayer.CLOSE";
|
= "org.schabi.newpipe.player.MainPlayer.REPEAT";
|
||||||
static final String ACTION_PLAY_PAUSE =
|
static final String ACTION_PLAY_NEXT
|
||||||
"org.schabi.newpipe.player.MainPlayer.PLAY_PAUSE";
|
= "org.schabi.newpipe.player.MainPlayer.ACTION_PLAY_NEXT";
|
||||||
static final String ACTION_OPEN_CONTROLS =
|
static final String ACTION_PLAY_PREVIOUS
|
||||||
"org.schabi.newpipe.player.MainPlayer.OPEN_CONTROLS";
|
= "org.schabi.newpipe.player.MainPlayer.ACTION_PLAY_PREVIOUS";
|
||||||
static final String ACTION_REPEAT =
|
static final String ACTION_FAST_REWIND
|
||||||
"org.schabi.newpipe.player.MainPlayer.REPEAT";
|
= "org.schabi.newpipe.player.MainPlayer.ACTION_FAST_REWIND";
|
||||||
static final String ACTION_PLAY_NEXT =
|
static final String ACTION_FAST_FORWARD
|
||||||
"org.schabi.newpipe.player.MainPlayer.ACTION_PLAY_NEXT";
|
= "org.schabi.newpipe.player.MainPlayer.ACTION_FAST_FORWARD";
|
||||||
static final String ACTION_PLAY_PREVIOUS =
|
static final String ACTION_SHUFFLE
|
||||||
"org.schabi.newpipe.player.MainPlayer.ACTION_PLAY_PREVIOUS";
|
= "org.schabi.newpipe.player.MainPlayer.ACTION_SHUFFLE";
|
||||||
static final String ACTION_FAST_REWIND =
|
public static final String ACTION_RECREATE_NOTIFICATION
|
||||||
"org.schabi.newpipe.player.MainPlayer.ACTION_FAST_REWIND";
|
= "org.schabi.newpipe.player.MainPlayer.ACTION_RECREATE_NOTIFICATION";
|
||||||
static final String ACTION_FAST_FORWARD =
|
|
||||||
"org.schabi.newpipe.player.MainPlayer.ACTION_FAST_FORWARD";
|
|
||||||
|
|
||||||
private static final String SET_IMAGE_RESOURCE_METHOD = "setImageResource";
|
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Service's LifeCycle
|
// Service's LifeCycle
|
||||||
|
@ -113,9 +91,7 @@ public final class MainPlayer extends Service {
|
||||||
Log.d(TAG, "onCreate() called");
|
Log.d(TAG, "onCreate() called");
|
||||||
}
|
}
|
||||||
assureCorrectAppLanguage(this);
|
assureCorrectAppLanguage(this);
|
||||||
notificationManager = ((NotificationManager) getSystemService(NOTIFICATION_SERVICE));
|
|
||||||
windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
|
windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
|
||||||
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
|
|
||||||
|
|
||||||
ThemeHelper.setTheme(this);
|
ThemeHelper.setTheme(this);
|
||||||
createView();
|
createView();
|
||||||
|
@ -143,7 +119,7 @@ public final class MainPlayer extends Service {
|
||||||
|
|
||||||
if (Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())
|
if (Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())
|
||||||
|| intent.getStringExtra(VideoPlayer.PLAY_QUEUE_KEY) != null) {
|
|| intent.getStringExtra(VideoPlayer.PLAY_QUEUE_KEY) != null) {
|
||||||
showNotificationAndStartForeground();
|
NotificationUtil.getInstance().createNotificationAndStartForeground(playerImpl, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
playerImpl.handleIntent(intent);
|
playerImpl.handleIntent(intent);
|
||||||
|
@ -171,12 +147,13 @@ public final class MainPlayer extends Service {
|
||||||
// Android TV will handle back button in case controls will be visible
|
// Android TV will handle back button in case controls will be visible
|
||||||
// (one more additional unneeded click while the player is hidden)
|
// (one more additional unneeded click while the player is hidden)
|
||||||
playerImpl.hideControls(0, 0);
|
playerImpl.hideControls(0, 0);
|
||||||
|
playerImpl.onQueueClosed();
|
||||||
// Notification shows information about old stream but if a user selects
|
// Notification shows information about old stream but if a user selects
|
||||||
// a stream from backStack it's not actual anymore
|
// a stream from backStack it's not actual anymore
|
||||||
// So we should hide the notification at all.
|
// So we should hide the notification at all.
|
||||||
// When autoplay enabled such notification flashing is annoying so skip this case
|
// When autoplay enabled such notification flashing is annoying so skip this case
|
||||||
if (!autoplayEnabled) {
|
if (!autoplayEnabled) {
|
||||||
stopForeground(true);
|
NotificationUtil.getInstance().cancelNotificationAndStopForeground(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -219,6 +196,10 @@ public final class MainPlayer extends Service {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (playerImpl != null) {
|
if (playerImpl != null) {
|
||||||
|
// Exit from fullscreen when user closes the player via notification
|
||||||
|
if (playerImpl.isFullscreen()) {
|
||||||
|
playerImpl.toggleFullscreen();
|
||||||
|
}
|
||||||
removeViewFromParent();
|
removeViewFromParent();
|
||||||
|
|
||||||
playerImpl.setRecovery();
|
playerImpl.setRecovery();
|
||||||
|
@ -227,11 +208,8 @@ public final class MainPlayer extends Service {
|
||||||
playerImpl.removePopupFromView();
|
playerImpl.removePopupFromView();
|
||||||
playerImpl.destroy();
|
playerImpl.destroy();
|
||||||
}
|
}
|
||||||
if (notificationManager != null) {
|
|
||||||
notificationManager.cancel(NOTIFICATION_ID);
|
|
||||||
}
|
|
||||||
|
|
||||||
stopForeground(true);
|
NotificationUtil.getInstance().cancelNotificationAndStopForeground(this);
|
||||||
stopSelf();
|
stopSelf();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -270,206 +248,6 @@ public final class MainPlayer extends Service {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showNotificationAndStartForeground() {
|
|
||||||
resetNotification();
|
|
||||||
if (getBigNotRemoteView() != null) {
|
|
||||||
getBigNotRemoteView().setProgressBar(R.id.notificationProgressBar, 100, 0, false);
|
|
||||||
}
|
|
||||||
if (getNotRemoteView() != null) {
|
|
||||||
getNotRemoteView().setProgressBar(R.id.notificationProgressBar, 100, 0, false);
|
|
||||||
}
|
|
||||||
startForeground(NOTIFICATION_ID, getNotBuilder().build());
|
|
||||||
}
|
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
|
||||||
// Notification
|
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
|
||||||
|
|
||||||
void resetNotification() {
|
|
||||||
notBuilder = createNotification();
|
|
||||||
playerImpl.timesNotificationUpdated = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private NotificationCompat.Builder createNotification() {
|
|
||||||
notRemoteView = new RemoteViews(BuildConfig.APPLICATION_ID,
|
|
||||||
R.layout.player_notification);
|
|
||||||
bigNotRemoteView = new RemoteViews(BuildConfig.APPLICATION_ID,
|
|
||||||
R.layout.player_notification_expanded);
|
|
||||||
|
|
||||||
setupNotification(notRemoteView);
|
|
||||||
setupNotification(bigNotRemoteView);
|
|
||||||
|
|
||||||
final NotificationCompat.Builder builder = new NotificationCompat
|
|
||||||
.Builder(this, getString(R.string.notification_channel_id))
|
|
||||||
.setOngoing(true)
|
|
||||||
.setSmallIcon(R.drawable.ic_newpipe_triangle_white)
|
|
||||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
|
||||||
.setCustomContentView(notRemoteView)
|
|
||||||
.setCustomBigContentView(bigNotRemoteView);
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
|
||||||
setLockScreenThumbnail(builder);
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.setPriority(NotificationCompat.PRIORITY_MAX);
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
|
||||||
private void setLockScreenThumbnail(final NotificationCompat.Builder builder) {
|
|
||||||
final boolean isLockScreenThumbnailEnabled = sharedPreferences.getBoolean(
|
|
||||||
getString(R.string.enable_lock_screen_video_thumbnail_key), true);
|
|
||||||
|
|
||||||
if (isLockScreenThumbnailEnabled) {
|
|
||||||
playerImpl.mediaSessionManager.setLockScreenArt(
|
|
||||||
builder,
|
|
||||||
getCenteredThumbnailBitmap()
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
playerImpl.mediaSessionManager.clearLockScreenArt(builder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private Bitmap getCenteredThumbnailBitmap() {
|
|
||||||
final int screenWidth = Resources.getSystem().getDisplayMetrics().widthPixels;
|
|
||||||
final int screenHeight = Resources.getSystem().getDisplayMetrics().heightPixels;
|
|
||||||
|
|
||||||
return BitmapUtils.centerCrop(playerImpl.getThumbnail(), screenWidth, screenHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupNotification(final RemoteViews remoteViews) {
|
|
||||||
// Don't show anything until player is playing
|
|
||||||
if (playerImpl == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
remoteViews.setTextViewText(R.id.notificationSongName, playerImpl.getVideoTitle());
|
|
||||||
remoteViews.setTextViewText(R.id.notificationArtist, playerImpl.getUploaderName());
|
|
||||||
remoteViews.setImageViewBitmap(R.id.notificationCover, playerImpl.getThumbnail());
|
|
||||||
|
|
||||||
remoteViews.setOnClickPendingIntent(R.id.notificationPlayPause,
|
|
||||||
PendingIntent.getBroadcast(this, NOTIFICATION_ID,
|
|
||||||
new Intent(ACTION_PLAY_PAUSE), PendingIntent.FLAG_UPDATE_CURRENT));
|
|
||||||
remoteViews.setOnClickPendingIntent(R.id.notificationStop,
|
|
||||||
PendingIntent.getBroadcast(this, NOTIFICATION_ID,
|
|
||||||
new Intent(ACTION_CLOSE), PendingIntent.FLAG_UPDATE_CURRENT));
|
|
||||||
// Starts VideoDetailFragment or opens BackgroundPlayerActivity.
|
|
||||||
remoteViews.setOnClickPendingIntent(R.id.notificationContent,
|
|
||||||
PendingIntent.getActivity(this, NOTIFICATION_ID,
|
|
||||||
getIntentForNotification(), PendingIntent.FLAG_UPDATE_CURRENT));
|
|
||||||
remoteViews.setOnClickPendingIntent(R.id.notificationRepeat,
|
|
||||||
PendingIntent.getBroadcast(this, NOTIFICATION_ID,
|
|
||||||
new Intent(ACTION_REPEAT), PendingIntent.FLAG_UPDATE_CURRENT));
|
|
||||||
|
|
||||||
|
|
||||||
if (playerImpl.playQueue != null && playerImpl.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, playerImpl.getRepeatMode());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the notification, and the play/pause button in it.
|
|
||||||
* Used for changes on the remoteView
|
|
||||||
*
|
|
||||||
* @param drawableId if != -1, sets the drawable with that id on the play/pause button
|
|
||||||
*/
|
|
||||||
synchronized void updateNotification(final int drawableId) {
|
|
||||||
/*if (DEBUG) {
|
|
||||||
Log.d(TAG, "updateNotification() called with: drawableId = [" + drawableId + "]");
|
|
||||||
}*/
|
|
||||||
if (notBuilder == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (drawableId != -1) {
|
|
||||||
if (notRemoteView != null) {
|
|
||||||
notRemoteView.setImageViewResource(R.id.notificationPlayPause, drawableId);
|
|
||||||
}
|
|
||||||
if (bigNotRemoteView != null) {
|
|
||||||
bigNotRemoteView.setImageViewResource(R.id.notificationPlayPause, drawableId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
notificationManager.notify(NOTIFICATION_ID, notBuilder.build());
|
|
||||||
playerImpl.timesNotificationUpdated++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
|
||||||
// Utils
|
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
|
||||||
|
|
||||||
private void setRepeatModeIcon(final RemoteViews remoteViews, final int repeatMode) {
|
|
||||||
if (remoteViews == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (repeatMode) {
|
|
||||||
case Player.REPEAT_MODE_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,
|
|
||||||
SET_IMAGE_RESOURCE_METHOD, R.drawable.exo_controls_repeat_one);
|
|
||||||
break;
|
|
||||||
case Player.REPEAT_MODE_ALL:
|
|
||||||
remoteViews.setInt(R.id.notificationRepeat,
|
|
||||||
SET_IMAGE_RESOURCE_METHOD, R.drawable.exo_controls_repeat_all);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Intent getIntentForNotification() {
|
|
||||||
final Intent intent;
|
|
||||||
if (playerImpl.audioPlayerSelected() || playerImpl.popupPlayerSelected()) {
|
|
||||||
// Means we play in popup or audio only. Let's show BackgroundPlayerActivity
|
|
||||||
intent = NavigationHelper.getBackgroundPlayerActivityIntent(getApplicationContext());
|
|
||||||
} else {
|
|
||||||
// We are playing in fragment. Don't open another activity just show fragment. That's it
|
|
||||||
intent = NavigationHelper.getPlayerIntent(this, MainActivity.class, null, true);
|
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
||||||
intent.setAction(Intent.ACTION_MAIN);
|
|
||||||
intent.addCategory(Intent.CATEGORY_LAUNCHER);
|
|
||||||
}
|
|
||||||
return intent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
|
||||||
// Getters
|
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
|
||||||
|
|
||||||
NotificationCompat.Builder getNotBuilder() {
|
|
||||||
return notBuilder;
|
|
||||||
}
|
|
||||||
|
|
||||||
RemoteViews getBigNotRemoteView() {
|
|
||||||
return bigNotRemoteView;
|
|
||||||
}
|
|
||||||
|
|
||||||
RemoteViews getNotRemoteView() {
|
|
||||||
return notRemoteView;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public class LocalBinder extends Binder {
|
public class LocalBinder extends Binder {
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,165 @@
|
||||||
|
package org.schabi.newpipe.player;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
|
||||||
|
import androidx.annotation.DrawableRes;
|
||||||
|
import androidx.annotation.IntDef;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.R;
|
||||||
|
import org.schabi.newpipe.util.Localization;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.SortedSet;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
|
||||||
|
public final class NotificationConstants {
|
||||||
|
|
||||||
|
private NotificationConstants() { }
|
||||||
|
|
||||||
|
|
||||||
|
public static final int NOTHING = 0;
|
||||||
|
public static final int PREVIOUS = 1;
|
||||||
|
public static final int NEXT = 2;
|
||||||
|
public static final int REWIND = 3;
|
||||||
|
public static final int FORWARD = 4;
|
||||||
|
public static final int SMART_REWIND_PREVIOUS = 5;
|
||||||
|
public static final int SMART_FORWARD_NEXT = 6;
|
||||||
|
public static final int PLAY_PAUSE = 7;
|
||||||
|
public static final int PLAY_PAUSE_BUFFERING = 8;
|
||||||
|
public static final int REPEAT = 9;
|
||||||
|
public static final int SHUFFLE = 10;
|
||||||
|
public static final int CLOSE = 11;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
@IntDef({NOTHING, PREVIOUS, NEXT, REWIND, FORWARD, SMART_REWIND_PREVIOUS, SMART_FORWARD_NEXT,
|
||||||
|
PLAY_PAUSE, PLAY_PAUSE_BUFFERING, REPEAT, SHUFFLE, CLOSE})
|
||||||
|
public @interface Action { }
|
||||||
|
|
||||||
|
@DrawableRes
|
||||||
|
public static final int[] ACTION_ICONS = {
|
||||||
|
0,
|
||||||
|
R.drawable.exo_icon_previous,
|
||||||
|
R.drawable.exo_icon_next,
|
||||||
|
R.drawable.exo_icon_rewind,
|
||||||
|
R.drawable.exo_icon_fastforward,
|
||||||
|
R.drawable.exo_icon_previous,
|
||||||
|
R.drawable.exo_icon_next,
|
||||||
|
R.drawable.ic_pause_white_24dp,
|
||||||
|
R.drawable.ic_hourglass_top_white_24dp,
|
||||||
|
R.drawable.exo_icon_repeat_all,
|
||||||
|
R.drawable.exo_icon_shuffle_on,
|
||||||
|
R.drawable.ic_close_white_24dp,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@Action
|
||||||
|
public static final int[] SLOT_DEFAULTS = {
|
||||||
|
SMART_REWIND_PREVIOUS,
|
||||||
|
PLAY_PAUSE_BUFFERING,
|
||||||
|
SMART_FORWARD_NEXT,
|
||||||
|
REPEAT,
|
||||||
|
CLOSE,
|
||||||
|
};
|
||||||
|
|
||||||
|
@Action
|
||||||
|
public static final int[][] SLOT_ALLOWED_ACTIONS = {
|
||||||
|
new int[] {PREVIOUS, REWIND, SMART_REWIND_PREVIOUS},
|
||||||
|
new int[] {REWIND, PLAY_PAUSE, PLAY_PAUSE_BUFFERING},
|
||||||
|
new int[] {NEXT, FORWARD, SMART_FORWARD_NEXT, PLAY_PAUSE, PLAY_PAUSE_BUFFERING},
|
||||||
|
new int[] {NOTHING, PREVIOUS, NEXT, REWIND, FORWARD, SMART_REWIND_PREVIOUS,
|
||||||
|
SMART_FORWARD_NEXT, REPEAT, SHUFFLE, CLOSE},
|
||||||
|
new int[] {NOTHING, NEXT, FORWARD, SMART_FORWARD_NEXT, REPEAT, SHUFFLE, CLOSE},
|
||||||
|
};
|
||||||
|
|
||||||
|
public static final int[] SLOT_PREF_KEYS = {
|
||||||
|
R.string.notification_slot_0_key,
|
||||||
|
R.string.notification_slot_1_key,
|
||||||
|
R.string.notification_slot_2_key,
|
||||||
|
R.string.notification_slot_3_key,
|
||||||
|
R.string.notification_slot_4_key,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
public static final Integer[] SLOT_COMPACT_DEFAULTS = {0, 1, 2};
|
||||||
|
|
||||||
|
public static final int[] SLOT_COMPACT_PREF_KEYS = {
|
||||||
|
R.string.notification_slot_compact_0_key,
|
||||||
|
R.string.notification_slot_compact_1_key,
|
||||||
|
R.string.notification_slot_compact_2_key,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
public static String getActionName(@NonNull final Context context, @Action final int action) {
|
||||||
|
switch (action) {
|
||||||
|
case PREVIOUS:
|
||||||
|
return context.getString(R.string.exo_controls_previous_description);
|
||||||
|
case NEXT:
|
||||||
|
return context.getString(R.string.exo_controls_next_description);
|
||||||
|
case REWIND:
|
||||||
|
return context.getString(R.string.exo_controls_rewind_description);
|
||||||
|
case FORWARD:
|
||||||
|
return context.getString(R.string.exo_controls_fastforward_description);
|
||||||
|
case SMART_REWIND_PREVIOUS:
|
||||||
|
return Localization.concatenateStrings(
|
||||||
|
context.getString(R.string.exo_controls_rewind_description),
|
||||||
|
context.getString(R.string.exo_controls_previous_description));
|
||||||
|
case SMART_FORWARD_NEXT:
|
||||||
|
return Localization.concatenateStrings(
|
||||||
|
context.getString(R.string.exo_controls_fastforward_description),
|
||||||
|
context.getString(R.string.exo_controls_next_description));
|
||||||
|
case PLAY_PAUSE:
|
||||||
|
return Localization.concatenateStrings(
|
||||||
|
context.getString(R.string.exo_controls_play_description),
|
||||||
|
context.getString(R.string.exo_controls_pause_description));
|
||||||
|
case PLAY_PAUSE_BUFFERING:
|
||||||
|
return Localization.concatenateStrings(
|
||||||
|
context.getString(R.string.exo_controls_play_description),
|
||||||
|
context.getString(R.string.exo_controls_pause_description),
|
||||||
|
context.getString(R.string.notification_action_buffering));
|
||||||
|
case REPEAT:
|
||||||
|
return context.getString(R.string.notification_action_repeat);
|
||||||
|
case SHUFFLE:
|
||||||
|
return context.getString(R.string.notification_action_shuffle);
|
||||||
|
case CLOSE:
|
||||||
|
return context.getString(R.string.close);
|
||||||
|
case NOTHING: default:
|
||||||
|
return context.getString(R.string.notification_action_nothing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param context the context to use
|
||||||
|
* @param sharedPreferences the shared preferences to query values from
|
||||||
|
* @param slotCount remove indices >= than this value (set to {@code 5} to do nothing, or make
|
||||||
|
* it lower if there are slots with empty actions)
|
||||||
|
* @return a sorted list of the indices of the slots to use as compact slots
|
||||||
|
*/
|
||||||
|
public static List<Integer> getCompactSlotsFromPreferences(
|
||||||
|
@NonNull final Context context,
|
||||||
|
final SharedPreferences sharedPreferences,
|
||||||
|
final int slotCount) {
|
||||||
|
final SortedSet<Integer> compactSlots = new TreeSet<>();
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
final int compactSlot = sharedPreferences.getInt(
|
||||||
|
context.getString(SLOT_COMPACT_PREF_KEYS[i]), Integer.MAX_VALUE);
|
||||||
|
|
||||||
|
if (compactSlot == Integer.MAX_VALUE) {
|
||||||
|
// settings not yet populated, return default values
|
||||||
|
return new ArrayList<>(Arrays.asList(SLOT_COMPACT_DEFAULTS));
|
||||||
|
}
|
||||||
|
|
||||||
|
// a negative value (-1) is set when the user does not want a particular compact slot
|
||||||
|
if (compactSlot >= 0 && compactSlot < slotCount) {
|
||||||
|
compactSlots.add(compactSlot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new ArrayList<>(compactSlots);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,371 @@
|
||||||
|
package org.schabi.newpipe.player;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.app.Service;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.ServiceInfo;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.Matrix;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.DrawableRes;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.StringRes;
|
||||||
|
import androidx.core.app.NotificationCompat;
|
||||||
|
import androidx.core.app.NotificationManagerCompat;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.MainActivity;
|
||||||
|
import org.schabi.newpipe.R;
|
||||||
|
import org.schabi.newpipe.util.NavigationHelper;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
|
||||||
|
import static com.google.android.exoplayer2.Player.REPEAT_MODE_ALL;
|
||||||
|
import static com.google.android.exoplayer2.Player.REPEAT_MODE_ONE;
|
||||||
|
import static org.schabi.newpipe.player.MainPlayer.ACTION_CLOSE;
|
||||||
|
import static org.schabi.newpipe.player.MainPlayer.ACTION_FAST_FORWARD;
|
||||||
|
import static org.schabi.newpipe.player.MainPlayer.ACTION_FAST_REWIND;
|
||||||
|
import static org.schabi.newpipe.player.MainPlayer.ACTION_PLAY_NEXT;
|
||||||
|
import static org.schabi.newpipe.player.MainPlayer.ACTION_PLAY_PAUSE;
|
||||||
|
import static org.schabi.newpipe.player.MainPlayer.ACTION_PLAY_PREVIOUS;
|
||||||
|
import static org.schabi.newpipe.player.MainPlayer.ACTION_REPEAT;
|
||||||
|
import static org.schabi.newpipe.player.MainPlayer.ACTION_SHUFFLE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a utility class for player notifications.
|
||||||
|
*
|
||||||
|
* @author cool-student
|
||||||
|
*/
|
||||||
|
public final class NotificationUtil {
|
||||||
|
private static final String TAG = NotificationUtil.class.getSimpleName();
|
||||||
|
private static final boolean DEBUG = BasePlayer.DEBUG;
|
||||||
|
private static final int NOTIFICATION_ID = 123789;
|
||||||
|
|
||||||
|
@Nullable private static NotificationUtil instance = null;
|
||||||
|
|
||||||
|
@NotificationConstants.Action
|
||||||
|
private int[] notificationSlots = NotificationConstants.SLOT_DEFAULTS.clone();
|
||||||
|
|
||||||
|
private NotificationManagerCompat notificationManager;
|
||||||
|
private NotificationCompat.Builder notificationBuilder;
|
||||||
|
|
||||||
|
private NotificationUtil() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static NotificationUtil getInstance() {
|
||||||
|
if (instance == null) {
|
||||||
|
instance = new NotificationUtil();
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
// NOTIFICATION
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the notification if it does not exist already and recreates it if forceRecreate is
|
||||||
|
* true. Updates the notification with the data in the player.
|
||||||
|
* @param player the player currently open, to take data from
|
||||||
|
* @param forceRecreate whether to force the recreation of the notification even if it already
|
||||||
|
* exists
|
||||||
|
*/
|
||||||
|
synchronized void createNotificationIfNeededAndUpdate(final VideoPlayerImpl player,
|
||||||
|
final boolean forceRecreate) {
|
||||||
|
if (forceRecreate || notificationBuilder == null) {
|
||||||
|
notificationBuilder = createNotification(player);
|
||||||
|
}
|
||||||
|
updateNotification(player);
|
||||||
|
notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized NotificationCompat.Builder createNotification(
|
||||||
|
final VideoPlayerImpl player) {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d(TAG, "createNotification()");
|
||||||
|
}
|
||||||
|
notificationManager = NotificationManagerCompat.from(player.context);
|
||||||
|
final NotificationCompat.Builder builder = new NotificationCompat.Builder(player.context,
|
||||||
|
player.context.getString(R.string.notification_channel_id));
|
||||||
|
|
||||||
|
initializeNotificationSlots(player);
|
||||||
|
|
||||||
|
// count the number of real slots, to make sure compact slots indices are not out of bound
|
||||||
|
int nonNothingSlotCount = 5;
|
||||||
|
if (notificationSlots[3] == NotificationConstants.NOTHING) {
|
||||||
|
--nonNothingSlotCount;
|
||||||
|
}
|
||||||
|
if (notificationSlots[4] == NotificationConstants.NOTHING) {
|
||||||
|
--nonNothingSlotCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// build the compact slot indices array (need code to convert from Integer... because Java)
|
||||||
|
final List<Integer> compactSlotList = NotificationConstants.getCompactSlotsFromPreferences(
|
||||||
|
player.context, player.sharedPreferences, nonNothingSlotCount);
|
||||||
|
final int[] compactSlots = new int[compactSlotList.size()];
|
||||||
|
for (int i = 0; i < compactSlotList.size(); i++) {
|
||||||
|
compactSlots[i] = compactSlotList.get(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.setStyle(new androidx.media.app.NotificationCompat.MediaStyle()
|
||||||
|
.setMediaSession(player.mediaSessionManager.getSessionToken())
|
||||||
|
.setShowActionsInCompactView(compactSlots))
|
||||||
|
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||||
|
.setSmallIcon(R.drawable.ic_newpipe_triangle_white)
|
||||||
|
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||||
|
.setColor(ContextCompat.getColor(player.context, R.color.gray))
|
||||||
|
.setCategory(NotificationCompat.CATEGORY_TRANSPORT)
|
||||||
|
.setDeleteIntent(PendingIntent.getBroadcast(player.context, NOTIFICATION_ID,
|
||||||
|
new Intent(ACTION_CLOSE), FLAG_UPDATE_CURRENT));
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the notification builder and the button icons depending on the playback state.
|
||||||
|
* @param player the player currently open, to take data from
|
||||||
|
*/
|
||||||
|
private synchronized void updateNotification(final VideoPlayerImpl player) {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d(TAG, "updateNotification()");
|
||||||
|
}
|
||||||
|
|
||||||
|
// also update content intent, in case the user switched players
|
||||||
|
notificationBuilder.setContentIntent(PendingIntent.getActivity(player.context,
|
||||||
|
NOTIFICATION_ID, getIntentForNotification(player), FLAG_UPDATE_CURRENT));
|
||||||
|
notificationBuilder.setContentTitle(player.getVideoTitle());
|
||||||
|
notificationBuilder.setContentText(player.getUploaderName());
|
||||||
|
notificationBuilder.setTicker(player.getVideoTitle());
|
||||||
|
updateActions(notificationBuilder, player);
|
||||||
|
setLargeIcon(notificationBuilder, player);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressLint("RestrictedApi")
|
||||||
|
boolean shouldUpdateBufferingSlot() {
|
||||||
|
if (notificationBuilder.mActions.size() < 3) {
|
||||||
|
// this should never happen, but let's make sure notification actions are populated
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// only second and third slot could contain PLAY_PAUSE_BUFFERING, update them only if they
|
||||||
|
// are not already in the buffering state (the only one with a null action intent)
|
||||||
|
return (notificationSlots[1] == NotificationConstants.PLAY_PAUSE_BUFFERING
|
||||||
|
&& notificationBuilder.mActions.get(1).actionIntent != null)
|
||||||
|
|| (notificationSlots[2] == NotificationConstants.PLAY_PAUSE_BUFFERING
|
||||||
|
&& notificationBuilder.mActions.get(2).actionIntent != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void createNotificationAndStartForeground(final VideoPlayerImpl player, final Service service) {
|
||||||
|
if (notificationBuilder == null) {
|
||||||
|
notificationBuilder = createNotification(player);
|
||||||
|
}
|
||||||
|
updateNotification(player);
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
|
service.startForeground(NOTIFICATION_ID, notificationBuilder.build(),
|
||||||
|
ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK);
|
||||||
|
} else {
|
||||||
|
service.startForeground(NOTIFICATION_ID, notificationBuilder.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cancelNotificationAndStopForeground(final Service service) {
|
||||||
|
service.stopForeground(true);
|
||||||
|
|
||||||
|
if (notificationManager != null) {
|
||||||
|
notificationManager.cancel(NOTIFICATION_ID);
|
||||||
|
}
|
||||||
|
notificationManager = null;
|
||||||
|
notificationBuilder = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
// ACTIONS
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
private void initializeNotificationSlots(final VideoPlayerImpl player) {
|
||||||
|
for (int i = 0; i < 5; ++i) {
|
||||||
|
notificationSlots[i] = player.sharedPreferences.getInt(
|
||||||
|
player.context.getString(NotificationConstants.SLOT_PREF_KEYS[i]),
|
||||||
|
NotificationConstants.SLOT_DEFAULTS[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("RestrictedApi")
|
||||||
|
private void updateActions(final NotificationCompat.Builder builder,
|
||||||
|
final VideoPlayerImpl player) {
|
||||||
|
builder.mActions.clear();
|
||||||
|
for (int i = 0; i < 5; ++i) {
|
||||||
|
addAction(builder, player, notificationSlots[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addAction(final NotificationCompat.Builder builder,
|
||||||
|
final VideoPlayerImpl player,
|
||||||
|
@NotificationConstants.Action final int slot) {
|
||||||
|
final NotificationCompat.Action action = getAction(player, slot);
|
||||||
|
if (action != null) {
|
||||||
|
builder.addAction(action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private NotificationCompat.Action getAction(
|
||||||
|
final VideoPlayerImpl player,
|
||||||
|
@NotificationConstants.Action final int selectedAction) {
|
||||||
|
final int baseActionIcon = NotificationConstants.ACTION_ICONS[selectedAction];
|
||||||
|
switch (selectedAction) {
|
||||||
|
case NotificationConstants.PREVIOUS:
|
||||||
|
return getAction(player, baseActionIcon,
|
||||||
|
R.string.exo_controls_previous_description, ACTION_PLAY_PREVIOUS);
|
||||||
|
|
||||||
|
case NotificationConstants.NEXT:
|
||||||
|
return getAction(player, baseActionIcon,
|
||||||
|
R.string.exo_controls_next_description, ACTION_PLAY_NEXT);
|
||||||
|
|
||||||
|
case NotificationConstants.REWIND:
|
||||||
|
return getAction(player, baseActionIcon,
|
||||||
|
R.string.exo_controls_rewind_description, ACTION_FAST_REWIND);
|
||||||
|
|
||||||
|
case NotificationConstants.FORWARD:
|
||||||
|
return getAction(player, baseActionIcon,
|
||||||
|
R.string.exo_controls_fastforward_description, ACTION_FAST_FORWARD);
|
||||||
|
|
||||||
|
case NotificationConstants.SMART_REWIND_PREVIOUS:
|
||||||
|
if (player.playQueue != null && player.playQueue.size() > 1) {
|
||||||
|
return getAction(player, R.drawable.exo_notification_previous,
|
||||||
|
R.string.exo_controls_previous_description, ACTION_PLAY_PREVIOUS);
|
||||||
|
} else {
|
||||||
|
return getAction(player, R.drawable.exo_controls_rewind,
|
||||||
|
R.string.exo_controls_rewind_description, ACTION_FAST_REWIND);
|
||||||
|
}
|
||||||
|
|
||||||
|
case NotificationConstants.SMART_FORWARD_NEXT:
|
||||||
|
if (player.playQueue != null && player.playQueue.size() > 1) {
|
||||||
|
return getAction(player, R.drawable.exo_notification_next,
|
||||||
|
R.string.exo_controls_next_description, ACTION_PLAY_NEXT);
|
||||||
|
} else {
|
||||||
|
return getAction(player, R.drawable.exo_controls_fastforward,
|
||||||
|
R.string.exo_controls_fastforward_description, ACTION_FAST_FORWARD);
|
||||||
|
}
|
||||||
|
|
||||||
|
case NotificationConstants.PLAY_PAUSE_BUFFERING:
|
||||||
|
if (player.getCurrentState() == BasePlayer.STATE_PREFLIGHT
|
||||||
|
|| player.getCurrentState() == BasePlayer.STATE_BLOCKED
|
||||||
|
|| player.getCurrentState() == BasePlayer.STATE_BUFFERING) {
|
||||||
|
// null intent -> show hourglass icon that does nothing when clicked
|
||||||
|
return new NotificationCompat.Action(R.drawable.ic_hourglass_top_white_24dp_png,
|
||||||
|
player.context.getString(R.string.notification_action_buffering),
|
||||||
|
null);
|
||||||
|
}
|
||||||
|
|
||||||
|
case NotificationConstants.PLAY_PAUSE:
|
||||||
|
if (player.getCurrentState() == BasePlayer.STATE_COMPLETED) {
|
||||||
|
return getAction(player, R.drawable.ic_replay_white_24dp_png,
|
||||||
|
R.string.exo_controls_pause_description, ACTION_PLAY_PAUSE);
|
||||||
|
} else if (player.isPlaying()
|
||||||
|
|| player.getCurrentState() == BasePlayer.STATE_PREFLIGHT
|
||||||
|
|| player.getCurrentState() == BasePlayer.STATE_BLOCKED
|
||||||
|
|| player.getCurrentState() == BasePlayer.STATE_BUFFERING) {
|
||||||
|
return getAction(player, R.drawable.exo_notification_pause,
|
||||||
|
R.string.exo_controls_pause_description, ACTION_PLAY_PAUSE);
|
||||||
|
} else {
|
||||||
|
return getAction(player, R.drawable.exo_notification_play,
|
||||||
|
R.string.exo_controls_play_description, ACTION_PLAY_PAUSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
case NotificationConstants.REPEAT:
|
||||||
|
if (player.getRepeatMode() == REPEAT_MODE_ALL) {
|
||||||
|
return getAction(player, R.drawable.exo_media_action_repeat_all,
|
||||||
|
R.string.exo_controls_repeat_all_description, ACTION_REPEAT);
|
||||||
|
} else if (player.getRepeatMode() == REPEAT_MODE_ONE) {
|
||||||
|
return getAction(player, R.drawable.exo_media_action_repeat_one,
|
||||||
|
R.string.exo_controls_repeat_one_description, ACTION_REPEAT);
|
||||||
|
} else /* player.getRepeatMode() == REPEAT_MODE_OFF */ {
|
||||||
|
return getAction(player, R.drawable.exo_media_action_repeat_off,
|
||||||
|
R.string.exo_controls_repeat_off_description, ACTION_REPEAT);
|
||||||
|
}
|
||||||
|
|
||||||
|
case NotificationConstants.SHUFFLE:
|
||||||
|
if (player.playQueue != null && player.playQueue.isShuffled()) {
|
||||||
|
return getAction(player, R.drawable.exo_controls_shuffle_on,
|
||||||
|
R.string.exo_controls_shuffle_on_description, ACTION_SHUFFLE);
|
||||||
|
} else {
|
||||||
|
return getAction(player, R.drawable.exo_controls_shuffle_off,
|
||||||
|
R.string.exo_controls_shuffle_off_description, ACTION_SHUFFLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
case NotificationConstants.CLOSE:
|
||||||
|
return getAction(player, R.drawable.ic_close_white_24dp_png,
|
||||||
|
R.string.close, ACTION_CLOSE);
|
||||||
|
|
||||||
|
case NotificationConstants.NOTHING:
|
||||||
|
default:
|
||||||
|
// do nothing
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private NotificationCompat.Action getAction(final VideoPlayerImpl player,
|
||||||
|
@DrawableRes final int drawable,
|
||||||
|
@StringRes final int title,
|
||||||
|
final String intentAction) {
|
||||||
|
return new NotificationCompat.Action(drawable, player.context.getString(title),
|
||||||
|
PendingIntent.getBroadcast(player.context, NOTIFICATION_ID,
|
||||||
|
new Intent(intentAction), FLAG_UPDATE_CURRENT));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Intent getIntentForNotification(final VideoPlayerImpl player) {
|
||||||
|
if (player.audioPlayerSelected() || player.popupPlayerSelected()) {
|
||||||
|
// Means we play in popup or audio only. Let's show the play queue
|
||||||
|
return NavigationHelper.getPlayQueueActivityIntent(player.context);
|
||||||
|
} else {
|
||||||
|
// We are playing in fragment. Don't open another activity just show fragment. That's it
|
||||||
|
final Intent intent = NavigationHelper.getPlayerIntent(
|
||||||
|
player.context, MainActivity.class, null, true);
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
intent.setAction(Intent.ACTION_MAIN);
|
||||||
|
intent.addCategory(Intent.CATEGORY_LAUNCHER);
|
||||||
|
return intent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
// BITMAP
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
private void setLargeIcon(final NotificationCompat.Builder builder,
|
||||||
|
final VideoPlayerImpl player) {
|
||||||
|
final boolean scaleImageToSquareAspectRatio = player.sharedPreferences.getBoolean(
|
||||||
|
player.context.getString(R.string.scale_to_square_image_in_notifications_key),
|
||||||
|
false);
|
||||||
|
if (scaleImageToSquareAspectRatio) {
|
||||||
|
builder.setLargeIcon(getBitmapWithSquareAspectRatio(player.getThumbnail()));
|
||||||
|
} else {
|
||||||
|
builder.setLargeIcon(player.getThumbnail());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Bitmap getBitmapWithSquareAspectRatio(final Bitmap bitmap) {
|
||||||
|
return getResizedBitmap(bitmap, bitmap.getWidth(), bitmap.getWidth());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Bitmap getResizedBitmap(final Bitmap bitmap, final int newWidth, final int newHeight) {
|
||||||
|
final int width = bitmap.getWidth();
|
||||||
|
final int height = bitmap.getHeight();
|
||||||
|
final float scaleWidth = ((float) newWidth) / width;
|
||||||
|
final float scaleHeight = ((float) newHeight) / height;
|
||||||
|
final Matrix matrix = new Matrix();
|
||||||
|
matrix.postScale(scaleWidth, scaleHeight);
|
||||||
|
return Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, false);
|
||||||
|
}
|
||||||
|
}
|
|
@ -131,6 +131,8 @@ public abstract class VideoPlayer extends BasePlayer
|
||||||
|
|
||||||
private View controlsRoot;
|
private View controlsRoot;
|
||||||
private TextView currentDisplaySeek;
|
private TextView currentDisplaySeek;
|
||||||
|
private View playerTopShadow;
|
||||||
|
private View playerBottomShadow;
|
||||||
|
|
||||||
private View bottomControlsRoot;
|
private View bottomControlsRoot;
|
||||||
private SeekBar playbackSeekBar;
|
private SeekBar playbackSeekBar;
|
||||||
|
@ -193,6 +195,8 @@ public abstract class VideoPlayer extends BasePlayer
|
||||||
this.controlAnimationView = view.findViewById(R.id.controlAnimationView);
|
this.controlAnimationView = view.findViewById(R.id.controlAnimationView);
|
||||||
this.controlsRoot = view.findViewById(R.id.playbackControlRoot);
|
this.controlsRoot = view.findViewById(R.id.playbackControlRoot);
|
||||||
this.currentDisplaySeek = view.findViewById(R.id.currentDisplaySeek);
|
this.currentDisplaySeek = view.findViewById(R.id.currentDisplaySeek);
|
||||||
|
this.playerTopShadow = view.findViewById(R.id.playerTopShadow);
|
||||||
|
this.playerBottomShadow = view.findViewById(R.id.playerBottomShadow);
|
||||||
this.playbackSeekBar = view.findViewById(R.id.playbackSeekBar);
|
this.playbackSeekBar = view.findViewById(R.id.playbackSeekBar);
|
||||||
this.playbackCurrentTime = view.findViewById(R.id.playbackCurrentTime);
|
this.playbackCurrentTime = view.findViewById(R.id.playbackCurrentTime);
|
||||||
this.playbackEndTime = view.findViewById(R.id.playbackEndTime);
|
this.playbackEndTime = view.findViewById(R.id.playbackEndTime);
|
||||||
|
@ -359,11 +363,11 @@ public abstract class VideoPlayer extends BasePlayer
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
// apply caption language from previous user preference
|
// apply caption language from previous user preference
|
||||||
if (userPreferredLanguage != null && (captionLanguage.equals(userPreferredLanguage)
|
if (userPreferredLanguage != null
|
||||||
|| searchForAutogenerated && captionLanguage.startsWith(userPreferredLanguage)
|
&& (captionLanguage.equals(userPreferredLanguage)
|
||||||
|| userPreferredLanguage.contains("(") && captionLanguage.startsWith(
|
|| (searchForAutogenerated && captionLanguage.startsWith(userPreferredLanguage))
|
||||||
userPreferredLanguage
|
|| (userPreferredLanguage.contains("(") && captionLanguage.startsWith(
|
||||||
.substring(0, userPreferredLanguage.indexOf('('))))) {
|
userPreferredLanguage.substring(0, userPreferredLanguage.indexOf('(')))))) {
|
||||||
final int textRendererIndex = getRendererIndex(C.TRACK_TYPE_TEXT);
|
final int textRendererIndex = getRendererIndex(C.TRACK_TYPE_TEXT);
|
||||||
if (textRendererIndex != RENDERER_UNAVAILABLE) {
|
if (textRendererIndex != RENDERER_UNAVAILABLE) {
|
||||||
trackSelector.setPreferredTextLanguage(captionLanguage);
|
trackSelector.setPreferredTextLanguage(captionLanguage);
|
||||||
|
@ -857,7 +861,6 @@ public abstract class VideoPlayer extends BasePlayer
|
||||||
}
|
}
|
||||||
qualityPopupMenu.show();
|
qualityPopupMenu.show();
|
||||||
isSomePopupMenuVisible = true;
|
isSomePopupMenuVisible = true;
|
||||||
showControls(DEFAULT_CONTROLS_DURATION);
|
|
||||||
|
|
||||||
final VideoStream videoStream = getSelectedVideoStream();
|
final VideoStream videoStream = getSelectedVideoStream();
|
||||||
if (videoStream != null) {
|
if (videoStream != null) {
|
||||||
|
@ -875,7 +878,6 @@ public abstract class VideoPlayer extends BasePlayer
|
||||||
}
|
}
|
||||||
playbackSpeedPopupMenu.show();
|
playbackSpeedPopupMenu.show();
|
||||||
isSomePopupMenuVisible = true;
|
isSomePopupMenuVisible = true;
|
||||||
showControls(DEFAULT_CONTROLS_DURATION);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onCaptionClicked() {
|
private void onCaptionClicked() {
|
||||||
|
@ -884,7 +886,6 @@ public abstract class VideoPlayer extends BasePlayer
|
||||||
}
|
}
|
||||||
captionPopupMenu.show();
|
captionPopupMenu.show();
|
||||||
isSomePopupMenuVisible = true;
|
isSomePopupMenuVisible = true;
|
||||||
showControls(DEFAULT_CONTROLS_DURATION);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void onResizeClicked() {
|
void onResizeClicked() {
|
||||||
|
@ -1061,6 +1062,7 @@ public abstract class VideoPlayer extends BasePlayer
|
||||||
? DEFAULT_CONTROLS_HIDE_TIME
|
? DEFAULT_CONTROLS_HIDE_TIME
|
||||||
: DPAD_CONTROLS_HIDE_TIME;
|
: DPAD_CONTROLS_HIDE_TIME;
|
||||||
|
|
||||||
|
showHideShadow(true, DEFAULT_CONTROLS_DURATION, 0);
|
||||||
animateView(controlsRoot, true, DEFAULT_CONTROLS_DURATION, 0,
|
animateView(controlsRoot, true, DEFAULT_CONTROLS_DURATION, 0,
|
||||||
() -> hideControls(DEFAULT_CONTROLS_DURATION, hideTime));
|
() -> hideControls(DEFAULT_CONTROLS_DURATION, hideTime));
|
||||||
}
|
}
|
||||||
|
@ -1070,6 +1072,7 @@ public abstract class VideoPlayer extends BasePlayer
|
||||||
Log.d(TAG, "showControls() called");
|
Log.d(TAG, "showControls() called");
|
||||||
}
|
}
|
||||||
controlsVisibilityHandler.removeCallbacksAndMessages(null);
|
controlsVisibilityHandler.removeCallbacksAndMessages(null);
|
||||||
|
showHideShadow(true, duration, 0);
|
||||||
animateView(controlsRoot, true, duration);
|
animateView(controlsRoot, true, duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1089,8 +1092,10 @@ public abstract class VideoPlayer extends BasePlayer
|
||||||
Log.d(TAG, "hideControls() called with: delay = [" + delay + "]");
|
Log.d(TAG, "hideControls() called with: delay = [" + delay + "]");
|
||||||
}
|
}
|
||||||
controlsVisibilityHandler.removeCallbacksAndMessages(null);
|
controlsVisibilityHandler.removeCallbacksAndMessages(null);
|
||||||
controlsVisibilityHandler.postDelayed(() ->
|
controlsVisibilityHandler.postDelayed(() -> {
|
||||||
animateView(controlsRoot, false, duration), delay);
|
showHideShadow(false, duration, 0);
|
||||||
|
animateView(controlsRoot, false, duration);
|
||||||
|
}, delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void hideControlsAndButton(final long duration, final long delay, final View button) {
|
public void hideControlsAndButton(final long duration, final long delay, final View button) {
|
||||||
|
@ -1109,6 +1114,11 @@ public abstract class VideoPlayer extends BasePlayer
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void showHideShadow(final boolean show, final long duration, final long delay) {
|
||||||
|
animateView(playerTopShadow, show, duration, delay, null);
|
||||||
|
animateView(playerBottomShadow, show, duration, delay, null);
|
||||||
|
}
|
||||||
|
|
||||||
public abstract void hideSystemUIIfNeeded();
|
public abstract void hideSystemUIIfNeeded();
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -27,22 +27,21 @@ import android.content.IntentFilter;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.database.ContentObserver;
|
import android.database.ContentObserver;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.Color;
|
||||||
import android.graphics.PixelFormat;
|
import android.graphics.PixelFormat;
|
||||||
import android.graphics.Point;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
|
import android.view.DisplayCutout;
|
||||||
import androidx.preference.PreferenceManager;
|
import androidx.preference.PreferenceManager;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.util.DisplayMetrics;
|
import android.util.DisplayMetrics;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.view.Display;
|
|
||||||
import android.view.GestureDetector;
|
import android.view.GestureDetector;
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.Surface;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
|
@ -57,7 +56,6 @@ import android.widget.RelativeLayout;
|
||||||
import android.widget.SeekBar;
|
import android.widget.SeekBar;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
@ -67,6 +65,7 @@ import androidx.recyclerview.widget.RecyclerView;
|
||||||
import com.google.android.exoplayer2.ExoPlaybackException;
|
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||||
import com.google.android.exoplayer2.Player;
|
import com.google.android.exoplayer2.Player;
|
||||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||||
|
import com.google.android.exoplayer2.Timeline;
|
||||||
import com.google.android.exoplayer2.source.MediaSource;
|
import com.google.android.exoplayer2.source.MediaSource;
|
||||||
import com.google.android.exoplayer2.text.CaptionStyleCompat;
|
import com.google.android.exoplayer2.text.CaptionStyleCompat;
|
||||||
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
|
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
|
||||||
|
@ -107,7 +106,6 @@ import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import static android.content.Context.WINDOW_SERVICE;
|
import static android.content.Context.WINDOW_SERVICE;
|
||||||
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
|
|
||||||
import static org.schabi.newpipe.player.MainPlayer.ACTION_CLOSE;
|
import static org.schabi.newpipe.player.MainPlayer.ACTION_CLOSE;
|
||||||
import static org.schabi.newpipe.player.MainPlayer.ACTION_FAST_FORWARD;
|
import static org.schabi.newpipe.player.MainPlayer.ACTION_FAST_FORWARD;
|
||||||
import static org.schabi.newpipe.player.MainPlayer.ACTION_FAST_REWIND;
|
import static org.schabi.newpipe.player.MainPlayer.ACTION_FAST_REWIND;
|
||||||
|
@ -115,10 +113,11 @@ import static org.schabi.newpipe.player.MainPlayer.ACTION_OPEN_CONTROLS;
|
||||||
import static org.schabi.newpipe.player.MainPlayer.ACTION_PLAY_NEXT;
|
import static org.schabi.newpipe.player.MainPlayer.ACTION_PLAY_NEXT;
|
||||||
import static org.schabi.newpipe.player.MainPlayer.ACTION_PLAY_PAUSE;
|
import static org.schabi.newpipe.player.MainPlayer.ACTION_PLAY_PAUSE;
|
||||||
import static org.schabi.newpipe.player.MainPlayer.ACTION_PLAY_PREVIOUS;
|
import static org.schabi.newpipe.player.MainPlayer.ACTION_PLAY_PREVIOUS;
|
||||||
|
import static org.schabi.newpipe.player.MainPlayer.ACTION_RECREATE_NOTIFICATION;
|
||||||
import static org.schabi.newpipe.player.MainPlayer.ACTION_REPEAT;
|
import static org.schabi.newpipe.player.MainPlayer.ACTION_REPEAT;
|
||||||
import static org.schabi.newpipe.player.MainPlayer.NOTIFICATION_ID;
|
import static org.schabi.newpipe.player.MainPlayer.ACTION_SHUFFLE;
|
||||||
import static org.schabi.newpipe.player.helper.PlayerHelper.MinimizeMode.MINIMIZE_ON_EXIT_MODE_BACKGROUND;
|
import static org.schabi.newpipe.player.helper.PlayerHelper.MinimizeMode.MINIMIZE_ON_EXIT_MODE_BACKGROUND;
|
||||||
import static org.schabi.newpipe.player.helper.PlayerHelper.getTimeString;
|
import static org.schabi.newpipe.player.helper.PlayerHelper.globalScreenOrientationLocked;
|
||||||
import static org.schabi.newpipe.util.AnimationUtils.Type.SLIDE_AND_ALPHA;
|
import static org.schabi.newpipe.util.AnimationUtils.Type.SLIDE_AND_ALPHA;
|
||||||
import static org.schabi.newpipe.util.AnimationUtils.animateRotation;
|
import static org.schabi.newpipe.util.AnimationUtils.animateRotation;
|
||||||
import static org.schabi.newpipe.util.AnimationUtils.animateView;
|
import static org.schabi.newpipe.util.AnimationUtils.animateView;
|
||||||
|
@ -141,14 +140,12 @@ public class VideoPlayerImpl extends VideoPlayer
|
||||||
static final String POPUP_SAVED_WIDTH = "popup_saved_width";
|
static final String POPUP_SAVED_WIDTH = "popup_saved_width";
|
||||||
static final String POPUP_SAVED_X = "popup_saved_x";
|
static final String POPUP_SAVED_X = "popup_saved_x";
|
||||||
static final String POPUP_SAVED_Y = "popup_saved_y";
|
static final String POPUP_SAVED_Y = "popup_saved_y";
|
||||||
private static final int MINIMUM_SHOW_EXTRA_WIDTH_DP = 300;
|
|
||||||
private static final int IDLE_WINDOW_FLAGS = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
private static final int IDLE_WINDOW_FLAGS = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
||||||
| WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
|
| WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
|
||||||
private static final int ONGOING_PLAYBACK_WINDOW_FLAGS = IDLE_WINDOW_FLAGS
|
private static final int ONGOING_PLAYBACK_WINDOW_FLAGS = IDLE_WINDOW_FLAGS
|
||||||
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
|
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
|
||||||
|
|
||||||
private static final float MAX_GESTURE_LENGTH = 0.75f;
|
private static final float MAX_GESTURE_LENGTH = 0.75f;
|
||||||
private static final int NOTIFICATION_UPDATES_BEFORE_RESET = 60;
|
|
||||||
|
|
||||||
private TextView titleTextView;
|
private TextView titleTextView;
|
||||||
private TextView channelTextView;
|
private TextView channelTextView;
|
||||||
|
@ -195,7 +192,6 @@ public class VideoPlayerImpl extends VideoPlayer
|
||||||
private boolean isVerticalVideo = false;
|
private boolean isVerticalVideo = false;
|
||||||
private boolean fragmentIsVisible = false;
|
private boolean fragmentIsVisible = false;
|
||||||
boolean shouldUpdateOnProgress;
|
boolean shouldUpdateOnProgress;
|
||||||
int timesNotificationUpdated;
|
|
||||||
|
|
||||||
private final MainPlayer service;
|
private final MainPlayer service;
|
||||||
private PlayerServiceEventListener fragmentListener;
|
private PlayerServiceEventListener fragmentListener;
|
||||||
|
@ -206,9 +202,6 @@ public class VideoPlayerImpl extends VideoPlayer
|
||||||
@NonNull
|
@NonNull
|
||||||
private final AudioPlaybackResolver resolver;
|
private final AudioPlaybackResolver resolver;
|
||||||
|
|
||||||
private int cachedDuration;
|
|
||||||
private String cachedDurationString;
|
|
||||||
|
|
||||||
// Popup
|
// Popup
|
||||||
private WindowManager.LayoutParams popupLayoutParams;
|
private WindowManager.LayoutParams popupLayoutParams;
|
||||||
public WindowManager windowManager;
|
public WindowManager windowManager;
|
||||||
|
@ -263,6 +256,7 @@ public class VideoPlayerImpl extends VideoPlayer
|
||||||
} else {
|
} else {
|
||||||
getRootView().setVisibility(View.VISIBLE);
|
getRootView().setVisibility(View.VISIBLE);
|
||||||
initVideoPlayer();
|
initVideoPlayer();
|
||||||
|
onQueueClosed();
|
||||||
// Android TV: without it focus will frame the whole player
|
// Android TV: without it focus will frame the whole player
|
||||||
playPauseButton.requestFocus();
|
playPauseButton.requestFocus();
|
||||||
}
|
}
|
||||||
|
@ -320,6 +314,9 @@ public class VideoPlayerImpl extends VideoPlayer
|
||||||
|
|
||||||
titleTextView.setSelected(true);
|
titleTextView.setSelected(true);
|
||||||
channelTextView.setSelected(true);
|
channelTextView.setSelected(true);
|
||||||
|
|
||||||
|
// Prevent hiding of bottom sheet via swipe inside queue
|
||||||
|
this.itemsList.setNestedScrollingEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -344,7 +341,6 @@ public class VideoPlayerImpl extends VideoPlayer
|
||||||
* This method ensures that popup and main players have different look.
|
* This method ensures that popup and main players have different look.
|
||||||
* We use one layout for both players and need to decide what to show and what to hide.
|
* We use one layout for both players and need to decide what to show and what to hide.
|
||||||
* Additional measuring should be done inside {@link #setupElementsSize}.
|
* Additional measuring should be done inside {@link #setupElementsSize}.
|
||||||
* {@link #setControlsSize} is used to adapt the UI to fullscreen mode, multiWindow, navBar, etc
|
|
||||||
*/
|
*/
|
||||||
private void setupElementsVisibility() {
|
private void setupElementsVisibility() {
|
||||||
if (popupPlayerSelected()) {
|
if (popupPlayerSelected()) {
|
||||||
|
@ -497,6 +493,17 @@ public class VideoPlayerImpl extends VideoPlayer
|
||||||
Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION), false,
|
Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION), false,
|
||||||
settingsContentObserver);
|
settingsContentObserver);
|
||||||
getRootView().addOnLayoutChangeListener(this);
|
getRootView().addOnLayoutChangeListener(this);
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||||
|
queueLayout.setOnApplyWindowInsetsListener((view, windowInsets) -> {
|
||||||
|
final DisplayCutout cutout = windowInsets.getDisplayCutout();
|
||||||
|
if (cutout != null) {
|
||||||
|
view.setPadding(cutout.getSafeInsetLeft(), cutout.getSafeInsetTop(),
|
||||||
|
cutout.getSafeInsetRight(), cutout.getSafeInsetBottom());
|
||||||
|
}
|
||||||
|
return windowInsets;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean onKeyDown(final int keyCode) {
|
public boolean onKeyDown(final int keyCode) {
|
||||||
|
@ -598,20 +605,23 @@ public class VideoPlayerImpl extends VideoPlayer
|
||||||
// ExoPlayer Video Listener
|
// ExoPlayer Video Listener
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
void onShuffleOrRepeatModeChanged() {
|
||||||
|
updatePlaybackButtons();
|
||||||
|
updatePlayback();
|
||||||
|
NotificationUtil.getInstance().createNotificationIfNeededAndUpdate(this, false);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRepeatModeChanged(final int i) {
|
public void onRepeatModeChanged(final int i) {
|
||||||
super.onRepeatModeChanged(i);
|
super.onRepeatModeChanged(i);
|
||||||
updatePlaybackButtons();
|
onShuffleOrRepeatModeChanged();
|
||||||
updatePlayback();
|
|
||||||
service.resetNotification();
|
|
||||||
service.updateNotification(-1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onShuffleClicked() {
|
public void onShuffleClicked() {
|
||||||
super.onShuffleClicked();
|
super.onShuffleClicked();
|
||||||
updatePlaybackButtons();
|
onShuffleOrRepeatModeChanged();
|
||||||
updatePlayback();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -627,6 +637,13 @@ public class VideoPlayerImpl extends VideoPlayer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTimelineChanged(final Timeline timeline, final int reason) {
|
||||||
|
super.onTimelineChanged(timeline, reason);
|
||||||
|
// force recreate notification to ensure seek bar is shown when preparation finishes
|
||||||
|
NotificationUtil.getInstance().createNotificationIfNeededAndUpdate(this, true);
|
||||||
|
}
|
||||||
|
|
||||||
protected void onMetadataChanged(@NonNull final MediaSourceTag tag) {
|
protected void onMetadataChanged(@NonNull final MediaSourceTag tag) {
|
||||||
super.onMetadataChanged(tag);
|
super.onMetadataChanged(tag);
|
||||||
|
|
||||||
|
@ -637,8 +654,7 @@ public class VideoPlayerImpl extends VideoPlayer
|
||||||
titleTextView.setText(tag.getMetadata().getName());
|
titleTextView.setText(tag.getMetadata().getName());
|
||||||
channelTextView.setText(tag.getMetadata().getUploaderName());
|
channelTextView.setText(tag.getMetadata().getUploaderName());
|
||||||
|
|
||||||
service.resetNotification();
|
NotificationUtil.getInstance().createNotificationIfNeededAndUpdate(this, false);
|
||||||
service.updateNotification(-1);
|
|
||||||
updateMetadata();
|
updateMetadata();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -680,35 +696,17 @@ public class VideoPlayerImpl extends VideoPlayer
|
||||||
public void onUpdateProgress(final int currentProgress,
|
public void onUpdateProgress(final int currentProgress,
|
||||||
final int duration, final int bufferPercent) {
|
final int duration, final int bufferPercent) {
|
||||||
super.onUpdateProgress(currentProgress, duration, bufferPercent);
|
super.onUpdateProgress(currentProgress, duration, bufferPercent);
|
||||||
|
|
||||||
updateProgress(currentProgress, duration, bufferPercent);
|
updateProgress(currentProgress, duration, bufferPercent);
|
||||||
|
|
||||||
if (!shouldUpdateOnProgress || getCurrentState() == BasePlayer.STATE_COMPLETED
|
// setMetadata only updates the metadata when any of the metadata keys are null
|
||||||
|| getCurrentState() == BasePlayer.STATE_PAUSED || getPlayQueue() == null) {
|
mediaSessionManager.setMetadata(getVideoTitle(), getUploaderName(), getThumbnail(),
|
||||||
return;
|
duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (timesNotificationUpdated > NOTIFICATION_UPDATES_BEFORE_RESET) {
|
@Override
|
||||||
service.resetNotification();
|
public void onPlayQueueEdited() {
|
||||||
}
|
updatePlayback();
|
||||||
|
NotificationUtil.getInstance().createNotificationIfNeededAndUpdate(this, false);
|
||||||
if (service.getBigNotRemoteView() != null) {
|
|
||||||
if (cachedDuration != duration) {
|
|
||||||
cachedDuration = duration;
|
|
||||||
cachedDurationString = getTimeString(duration);
|
|
||||||
}
|
|
||||||
service.getBigNotRemoteView()
|
|
||||||
.setProgressBar(R.id.notificationProgressBar,
|
|
||||||
duration, currentProgress, false);
|
|
||||||
service.getBigNotRemoteView()
|
|
||||||
.setTextViewText(R.id.notificationTime,
|
|
||||||
getTimeString(currentProgress) + " / " + cachedDurationString);
|
|
||||||
}
|
|
||||||
if (service.getNotRemoteView() != null) {
|
|
||||||
service.getNotRemoteView()
|
|
||||||
.setProgressBar(R.id.notificationProgressBar, duration, currentProgress, false);
|
|
||||||
}
|
|
||||||
service.updateNotification(-1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -754,11 +752,46 @@ public class VideoPlayerImpl extends VideoPlayer
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "toggleFullscreen() called");
|
Log.d(TAG, "toggleFullscreen() called");
|
||||||
}
|
}
|
||||||
if (simpleExoPlayer == null || getCurrentMetadata() == null) {
|
if (popupPlayerSelected()
|
||||||
|
|| simpleExoPlayer == null
|
||||||
|
|| getCurrentMetadata() == null
|
||||||
|
|| fragmentListener == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isFullscreen = !isFullscreen;
|
||||||
|
if (!isFullscreen) {
|
||||||
|
// Apply window insets because Android will not do it when orientation changes
|
||||||
|
// from landscape to portrait (open vertical video to reproduce)
|
||||||
|
getControlsRoot().setPadding(0, 0, 0, 0);
|
||||||
|
} else {
|
||||||
|
// Android needs tens milliseconds to send new insets but a user is able to see
|
||||||
|
// how controls changes it's position from `0` to `nav bar height` padding.
|
||||||
|
// So just hide the controls to hide this visual inconsistency
|
||||||
|
hideControls(0, 0);
|
||||||
|
}
|
||||||
|
fragmentListener.onFullscreenStateChanged(isFullscreen());
|
||||||
|
|
||||||
|
if (!isFullscreen()) {
|
||||||
|
titleTextView.setVisibility(View.GONE);
|
||||||
|
channelTextView.setVisibility(View.GONE);
|
||||||
|
playerCloseButton.setVisibility(videoPlayerSelected() ? View.VISIBLE : View.GONE);
|
||||||
|
} else {
|
||||||
|
titleTextView.setVisibility(View.VISIBLE);
|
||||||
|
channelTextView.setVisibility(View.VISIBLE);
|
||||||
|
playerCloseButton.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
setupScreenRotationButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void switchFromPopupToMain() {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d(TAG, "switchFromPopupToMain() called");
|
||||||
|
}
|
||||||
|
if (!popupPlayerSelected() || simpleExoPlayer == null || getCurrentMetadata() == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (popupPlayerSelected()) {
|
|
||||||
setRecovery();
|
setRecovery();
|
||||||
service.removeViewFromParent();
|
service.removeViewFromParent();
|
||||||
final Intent intent = NavigationHelper.getPlayerIntent(
|
final Intent intent = NavigationHelper.getPlayerIntent(
|
||||||
|
@ -783,27 +816,6 @@ public class VideoPlayerImpl extends VideoPlayer
|
||||||
intent.putExtra(VideoDetailFragment.AUTO_PLAY, true);
|
intent.putExtra(VideoDetailFragment.AUTO_PLAY, true);
|
||||||
service.onDestroy();
|
service.onDestroy();
|
||||||
context.startActivity(intent);
|
context.startActivity(intent);
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
if (fragmentListener == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
isFullscreen = !isFullscreen;
|
|
||||||
setControlsSize();
|
|
||||||
fragmentListener.onFullscreenStateChanged(isFullscreen());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isFullscreen()) {
|
|
||||||
titleTextView.setVisibility(View.GONE);
|
|
||||||
channelTextView.setVisibility(View.GONE);
|
|
||||||
playerCloseButton.setVisibility(videoPlayerSelected() ? View.VISIBLE : View.GONE);
|
|
||||||
} else {
|
|
||||||
titleTextView.setVisibility(View.VISIBLE);
|
|
||||||
channelTextView.setVisibility(View.VISIBLE);
|
|
||||||
playerCloseButton.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
setupScreenRotationButton();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -833,9 +845,12 @@ public class VideoPlayerImpl extends VideoPlayer
|
||||||
} else if (v.getId() == openInBrowser.getId()) {
|
} else if (v.getId() == openInBrowser.getId()) {
|
||||||
onOpenInBrowserClicked();
|
onOpenInBrowserClicked();
|
||||||
} else if (v.getId() == fullscreenButton.getId()) {
|
} else if (v.getId() == fullscreenButton.getId()) {
|
||||||
toggleFullscreen();
|
switchFromPopupToMain();
|
||||||
} else if (v.getId() == screenRotationButton.getId()) {
|
} else if (v.getId() == screenRotationButton.getId()) {
|
||||||
if (!isVerticalVideo) {
|
// Only if it's not a vertical video or vertical video but in landscape with locked
|
||||||
|
// orientation a screen orientation can be changed automatically
|
||||||
|
if (!isVerticalVideo
|
||||||
|
|| (service.isLandscape() && globalScreenOrientationLocked(service))) {
|
||||||
fragmentListener.onScreenRotationButtonClicked();
|
fragmentListener.onScreenRotationButtonClicked();
|
||||||
} else {
|
} else {
|
||||||
toggleFullscreen();
|
toggleFullscreen();
|
||||||
|
@ -850,9 +865,12 @@ public class VideoPlayerImpl extends VideoPlayer
|
||||||
|
|
||||||
if (getCurrentState() != STATE_COMPLETED) {
|
if (getCurrentState() != STATE_COMPLETED) {
|
||||||
getControlsVisibilityHandler().removeCallbacksAndMessages(null);
|
getControlsVisibilityHandler().removeCallbacksAndMessages(null);
|
||||||
|
showHideShadow(true, DEFAULT_CONTROLS_DURATION, 0);
|
||||||
animateView(getControlsRoot(), true, DEFAULT_CONTROLS_DURATION, 0, () -> {
|
animateView(getControlsRoot(), true, DEFAULT_CONTROLS_DURATION, 0, () -> {
|
||||||
if (getCurrentState() == STATE_PLAYING && !isSomePopupMenuVisible()) {
|
if (getCurrentState() == STATE_PLAYING && !isSomePopupMenuVisible()) {
|
||||||
if (v.getId() == playPauseButton.getId()) {
|
if (v.getId() == playPauseButton.getId()
|
||||||
|
// Hide controls in fullscreen immediately
|
||||||
|
|| (v.getId() == screenRotationButton.getId() && isFullscreen)) {
|
||||||
hideControls(0, 0);
|
hideControls(0, 0);
|
||||||
} else {
|
} else {
|
||||||
hideControls(DEFAULT_CONTROLS_DURATION, DEFAULT_CONTROLS_HIDE_TIME);
|
hideControls(DEFAULT_CONTROLS_DURATION, DEFAULT_CONTROLS_HIDE_TIME);
|
||||||
|
@ -918,7 +936,7 @@ public class VideoPlayerImpl extends VideoPlayer
|
||||||
buildQueue();
|
buildQueue();
|
||||||
updatePlaybackButtons();
|
updatePlaybackButtons();
|
||||||
|
|
||||||
getControlsRoot().setVisibility(View.INVISIBLE);
|
hideControls(0, 0);
|
||||||
queueLayout.requestFocus();
|
queueLayout.requestFocus();
|
||||||
animateView(queueLayout, SLIDE_AND_ALPHA, true,
|
animateView(queueLayout, SLIDE_AND_ALPHA, true,
|
||||||
DEFAULT_CONTROLS_DURATION);
|
DEFAULT_CONTROLS_DURATION);
|
||||||
|
@ -1010,9 +1028,8 @@ public class VideoPlayerImpl extends VideoPlayer
|
||||||
|
|
||||||
private void setupScreenRotationButton() {
|
private void setupScreenRotationButton() {
|
||||||
final boolean orientationLocked = PlayerHelper.globalScreenOrientationLocked(service);
|
final boolean orientationLocked = PlayerHelper.globalScreenOrientationLocked(service);
|
||||||
final boolean tabletInLandscape = DeviceUtils.isTablet(service) && service.isLandscape();
|
|
||||||
final boolean showButton = videoPlayerSelected()
|
final boolean showButton = videoPlayerSelected()
|
||||||
&& (orientationLocked || isVerticalVideo || tabletInLandscape);
|
&& (orientationLocked || isVerticalVideo || DeviceUtils.isTablet(service));
|
||||||
screenRotationButton.setVisibility(showButton ? View.VISIBLE : View.GONE);
|
screenRotationButton.setVisibility(showButton ? View.VISIBLE : View.GONE);
|
||||||
screenRotationButton.setImageDrawable(AppCompatResources.getDrawable(service, isFullscreen()
|
screenRotationButton.setImageDrawable(AppCompatResources.getDrawable(service, isFullscreen()
|
||||||
? R.drawable.ic_fullscreen_exit_white_24dp
|
? R.drawable.ic_fullscreen_exit_white_24dp
|
||||||
|
@ -1024,6 +1041,8 @@ public class VideoPlayerImpl extends VideoPlayer
|
||||||
if (orientationLocked
|
if (orientationLocked
|
||||||
&& isFullscreen()
|
&& isFullscreen()
|
||||||
&& service.isLandscape() == isVerticalVideo
|
&& service.isLandscape() == isVerticalVideo
|
||||||
|
&& !DeviceUtils.isTv(service)
|
||||||
|
&& !DeviceUtils.isTablet(service)
|
||||||
&& fragmentListener != null) {
|
&& fragmentListener != null) {
|
||||||
fragmentListener.onScreenRotationButtonClicked();
|
fragmentListener.onScreenRotationButtonClicked();
|
||||||
}
|
}
|
||||||
|
@ -1054,6 +1073,7 @@ public class VideoPlayerImpl extends VideoPlayer
|
||||||
super.onDismiss(menu);
|
super.onDismiss(menu);
|
||||||
if (isPlaying()) {
|
if (isPlaying()) {
|
||||||
hideControls(DEFAULT_CONTROLS_DURATION, 0);
|
hideControls(DEFAULT_CONTROLS_DURATION, 0);
|
||||||
|
hideSystemUIIfNeeded();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1078,15 +1098,6 @@ public class VideoPlayerImpl extends VideoPlayer
|
||||||
|
|
||||||
setInitialGestureValues();
|
setInitialGestureValues();
|
||||||
queueLayout.getLayoutParams().height = height - queueLayout.getTop();
|
queueLayout.getLayoutParams().height = height - queueLayout.getTop();
|
||||||
|
|
||||||
if (popupPlayerSelected()) {
|
|
||||||
final float widthDp = Math.abs(r - l) / service.getResources()
|
|
||||||
.getDisplayMetrics().density;
|
|
||||||
final int visibility = widthDp > MINIMUM_SHOW_EXTRA_WIDTH_DP
|
|
||||||
? View.VISIBLE
|
|
||||||
: View.GONE;
|
|
||||||
secondaryControls.setVisibility(visibility);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1169,8 +1180,7 @@ public class VideoPlayerImpl extends VideoPlayer
|
||||||
animatePlayButtons(false, 100);
|
animatePlayButtons(false, 100);
|
||||||
getRootView().setKeepScreenOn(false);
|
getRootView().setKeepScreenOn(false);
|
||||||
|
|
||||||
service.resetNotification();
|
NotificationUtil.getInstance().createNotificationIfNeededAndUpdate(this, false);
|
||||||
service.updateNotification(R.drawable.exo_controls_play);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1178,8 +1188,9 @@ public class VideoPlayerImpl extends VideoPlayer
|
||||||
super.onBuffering();
|
super.onBuffering();
|
||||||
getRootView().setKeepScreenOn(true);
|
getRootView().setKeepScreenOn(true);
|
||||||
|
|
||||||
service.resetNotification();
|
if (NotificationUtil.getInstance().shouldUpdateBufferingSlot()) {
|
||||||
service.updateNotification(R.drawable.exo_controls_play);
|
NotificationUtil.getInstance().createNotificationIfNeededAndUpdate(this, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1197,10 +1208,7 @@ public class VideoPlayerImpl extends VideoPlayer
|
||||||
checkLandscape();
|
checkLandscape();
|
||||||
getRootView().setKeepScreenOn(true);
|
getRootView().setKeepScreenOn(true);
|
||||||
|
|
||||||
service.resetNotification();
|
NotificationUtil.getInstance().createNotificationIfNeededAndUpdate(this, false);
|
||||||
service.updateNotification(R.drawable.exo_controls_pause);
|
|
||||||
|
|
||||||
service.startForeground(NOTIFICATION_ID, service.getNotBuilder().build());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1216,13 +1224,12 @@ public class VideoPlayerImpl extends VideoPlayer
|
||||||
|
|
||||||
updateWindowFlags(IDLE_WINDOW_FLAGS);
|
updateWindowFlags(IDLE_WINDOW_FLAGS);
|
||||||
|
|
||||||
service.resetNotification();
|
|
||||||
service.updateNotification(R.drawable.exo_controls_play);
|
|
||||||
|
|
||||||
// Remove running notification when user don't want music (or video in popup)
|
// Remove running notification when user don't want music (or video in popup)
|
||||||
// to be played in background
|
// to be played in background
|
||||||
if (!minimizeOnPopupEnabled() && !backgroundPlaybackEnabled() && videoPlayerSelected()) {
|
if (!minimizeOnPopupEnabled() && !backgroundPlaybackEnabled() && videoPlayerSelected()) {
|
||||||
service.stopForeground(true);
|
NotificationUtil.getInstance().cancelNotificationAndStopForeground(service);
|
||||||
|
} else {
|
||||||
|
NotificationUtil.getInstance().createNotificationIfNeededAndUpdate(this, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
getRootView().setKeepScreenOn(false);
|
getRootView().setKeepScreenOn(false);
|
||||||
|
@ -1234,8 +1241,7 @@ public class VideoPlayerImpl extends VideoPlayer
|
||||||
animatePlayButtons(false, 100);
|
animatePlayButtons(false, 100);
|
||||||
getRootView().setKeepScreenOn(true);
|
getRootView().setKeepScreenOn(true);
|
||||||
|
|
||||||
service.resetNotification();
|
NotificationUtil.getInstance().createNotificationIfNeededAndUpdate(this, false);
|
||||||
service.updateNotification(R.drawable.exo_controls_play);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1245,20 +1251,20 @@ public class VideoPlayerImpl extends VideoPlayer
|
||||||
playPauseButton.setImageResource(R.drawable.ic_replay_white_24dp);
|
playPauseButton.setImageResource(R.drawable.ic_replay_white_24dp);
|
||||||
animatePlayButtons(true, DEFAULT_CONTROLS_DURATION);
|
animatePlayButtons(true, DEFAULT_CONTROLS_DURATION);
|
||||||
});
|
});
|
||||||
getRootView().setKeepScreenOn(false);
|
|
||||||
|
|
||||||
|
getRootView().setKeepScreenOn(false);
|
||||||
updateWindowFlags(IDLE_WINDOW_FLAGS);
|
updateWindowFlags(IDLE_WINDOW_FLAGS);
|
||||||
|
|
||||||
service.resetNotification();
|
NotificationUtil.getInstance().createNotificationIfNeededAndUpdate(this, false);
|
||||||
service.updateNotification(R.drawable.ic_replay_white_24dp);
|
if (isFullscreen) {
|
||||||
|
toggleFullscreen();
|
||||||
|
}
|
||||||
super.onCompleted();
|
super.onCompleted();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void destroy() {
|
public void destroy() {
|
||||||
super.destroy();
|
super.destroy();
|
||||||
|
|
||||||
service.getContentResolver().unregisterContentObserver(settingsContentObserver);
|
service.getContentResolver().unregisterContentObserver(settingsContentObserver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1282,6 +1288,8 @@ public class VideoPlayerImpl extends VideoPlayer
|
||||||
intentFilter.addAction(ACTION_PLAY_NEXT);
|
intentFilter.addAction(ACTION_PLAY_NEXT);
|
||||||
intentFilter.addAction(ACTION_FAST_REWIND);
|
intentFilter.addAction(ACTION_FAST_REWIND);
|
||||||
intentFilter.addAction(ACTION_FAST_FORWARD);
|
intentFilter.addAction(ACTION_FAST_FORWARD);
|
||||||
|
intentFilter.addAction(ACTION_SHUFFLE);
|
||||||
|
intentFilter.addAction(ACTION_RECREATE_NOTIFICATION);
|
||||||
|
|
||||||
intentFilter.addAction(VideoDetailFragment.ACTION_VIDEO_FRAGMENT_RESUMED);
|
intentFilter.addAction(VideoDetailFragment.ACTION_VIDEO_FRAGMENT_RESUMED);
|
||||||
intentFilter.addAction(VideoDetailFragment.ACTION_VIDEO_FRAGMENT_STOPPED);
|
intentFilter.addAction(VideoDetailFragment.ACTION_VIDEO_FRAGMENT_STOPPED);
|
||||||
|
@ -1331,6 +1339,17 @@ public class VideoPlayerImpl extends VideoPlayer
|
||||||
case ACTION_REPEAT:
|
case ACTION_REPEAT:
|
||||||
onRepeatClicked();
|
onRepeatClicked();
|
||||||
break;
|
break;
|
||||||
|
case ACTION_SHUFFLE:
|
||||||
|
onShuffleClicked();
|
||||||
|
break;
|
||||||
|
case ACTION_RECREATE_NOTIFICATION:
|
||||||
|
NotificationUtil.getInstance().createNotificationIfNeededAndUpdate(this, true);
|
||||||
|
break;
|
||||||
|
case Intent.ACTION_HEADSET_PLUG: //FIXME
|
||||||
|
/*notificationManager.cancel(NOTIFICATION_ID);
|
||||||
|
mediaSessionManager.dispose();
|
||||||
|
mediaSessionManager.enable(getBaseContext(), basePlayerImpl.simpleExoPlayer);*/
|
||||||
|
break;
|
||||||
case VideoDetailFragment.ACTION_VIDEO_FRAGMENT_RESUMED:
|
case VideoDetailFragment.ACTION_VIDEO_FRAGMENT_RESUMED:
|
||||||
fragmentIsVisible = true;
|
fragmentIsVisible = true;
|
||||||
useVideoSource(true);
|
useVideoSource(true);
|
||||||
|
@ -1349,20 +1368,6 @@ public class VideoPlayerImpl extends VideoPlayer
|
||||||
updatePopupSize(getPopupLayoutParams().width, -1);
|
updatePopupSize(getPopupLayoutParams().width, -1);
|
||||||
checkPopupPositionBounds();
|
checkPopupPositionBounds();
|
||||||
}
|
}
|
||||||
|
|
||||||
// The only situation I need to re-calculate elements sizes is
|
|
||||||
// when a user rotates a device from landscape to landscape
|
|
||||||
// because in that case the controls should be aligned to another side of a screen.
|
|
||||||
// The problem is when user leaves the app and returns back
|
|
||||||
// (while the app in landscape) Android reports via DisplayMetrics that orientation
|
|
||||||
// is portrait and it gives wrong sizes calculations.
|
|
||||||
// Let's skip re-calculation in every case but landscape
|
|
||||||
final boolean reportedOrientationIsLandscape = service.isLandscape();
|
|
||||||
final boolean actualOrientationIsLandscape = context.getResources()
|
|
||||||
.getConfiguration().orientation == ORIENTATION_LANDSCAPE;
|
|
||||||
if (reportedOrientationIsLandscape && actualOrientationIsLandscape) {
|
|
||||||
setControlsSize();
|
|
||||||
}
|
|
||||||
// Close it because when changing orientation from portrait
|
// Close it because when changing orientation from portrait
|
||||||
// (in fullscreen mode) the size of queue layout can be larger than the screen size
|
// (in fullscreen mode) the size of queue layout can be larger than the screen size
|
||||||
onQueueClosed();
|
onQueueClosed();
|
||||||
|
@ -1372,23 +1377,18 @@ public class VideoPlayerImpl extends VideoPlayer
|
||||||
// Interrupt playback only when screen turns on
|
// Interrupt playback only when screen turns on
|
||||||
// and user is watching video in popup player.
|
// and user is watching video in popup player.
|
||||||
// Same actions for video player will be handled in ACTION_VIDEO_FRAGMENT_RESUMED
|
// Same actions for video player will be handled in ACTION_VIDEO_FRAGMENT_RESUMED
|
||||||
if (backgroundPlaybackEnabled()
|
if (popupPlayerSelected() && (isPlaying() || isLoading())) {
|
||||||
&& popupPlayerSelected()
|
|
||||||
&& (isPlaying() || isLoading())) {
|
|
||||||
useVideoSource(true);
|
useVideoSource(true);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Intent.ACTION_SCREEN_OFF:
|
case Intent.ACTION_SCREEN_OFF:
|
||||||
shouldUpdateOnProgress = false;
|
shouldUpdateOnProgress = false;
|
||||||
// Interrupt playback only when screen turns off with popup player working
|
// Interrupt playback only when screen turns off with popup player working
|
||||||
if (backgroundPlaybackEnabled()
|
if (popupPlayerSelected() && (isPlaying() || isLoading())) {
|
||||||
&& popupPlayerSelected()
|
|
||||||
&& (isPlaying() || isLoading())) {
|
|
||||||
useVideoSource(false);
|
useVideoSource(false);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
service.resetNotification();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -1400,10 +1400,7 @@ public class VideoPlayerImpl extends VideoPlayer
|
||||||
final View view,
|
final View view,
|
||||||
final Bitmap loadedImage) {
|
final Bitmap loadedImage) {
|
||||||
super.onLoadingComplete(imageUri, view, loadedImage);
|
super.onLoadingComplete(imageUri, view, loadedImage);
|
||||||
// rebuild notification here since remote view does not release bitmaps,
|
NotificationUtil.getInstance().createNotificationIfNeededAndUpdate(this, false);
|
||||||
// causing memory leaks
|
|
||||||
service.resetNotification();
|
|
||||||
service.updateNotification(-1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1411,15 +1408,13 @@ public class VideoPlayerImpl extends VideoPlayer
|
||||||
final View view,
|
final View view,
|
||||||
final FailReason failReason) {
|
final FailReason failReason) {
|
||||||
super.onLoadingFailed(imageUri, view, failReason);
|
super.onLoadingFailed(imageUri, view, failReason);
|
||||||
service.resetNotification();
|
NotificationUtil.getInstance().createNotificationIfNeededAndUpdate(this, false);
|
||||||
service.updateNotification(-1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLoadingCancelled(final String imageUri, final View view) {
|
public void onLoadingCancelled(final String imageUri, final View view) {
|
||||||
super.onLoadingCancelled(imageUri, view);
|
super.onLoadingCancelled(imageUri, view);
|
||||||
service.resetNotification();
|
NotificationUtil.getInstance().createNotificationIfNeededAndUpdate(this, false);
|
||||||
service.updateNotification(-1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -1526,9 +1521,10 @@ public class VideoPlayerImpl extends VideoPlayer
|
||||||
showOrHideButtons();
|
showOrHideButtons();
|
||||||
|
|
||||||
getControlsVisibilityHandler().removeCallbacksAndMessages(null);
|
getControlsVisibilityHandler().removeCallbacksAndMessages(null);
|
||||||
getControlsVisibilityHandler().postDelayed(() ->
|
getControlsVisibilityHandler().postDelayed(() -> {
|
||||||
animateView(getControlsRoot(), false, duration, 0,
|
showHideShadow(false, duration, 0);
|
||||||
this::hideSystemUIIfNeeded), delay
|
animateView(getControlsRoot(), false, duration, 0, this::hideSystemUIIfNeeded);
|
||||||
|
}, delay
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1556,11 +1552,17 @@ public class VideoPlayerImpl extends VideoPlayer
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showSystemUIPartially() {
|
private void showSystemUIPartially() {
|
||||||
if (isFullscreen() && getParentActivity() != null) {
|
final AppCompatActivity activity = getParentActivity();
|
||||||
|
if (isFullscreen() && activity != null) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
activity.getWindow().setStatusBarColor(Color.TRANSPARENT);
|
||||||
|
activity.getWindow().setNavigationBarColor(Color.TRANSPARENT);
|
||||||
|
}
|
||||||
final int visibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
final int visibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||||
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||||
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
|
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
|
||||||
getParentActivity().getWindow().getDecorView().setSystemUiVisibility(visibility);
|
activity.getWindow().getDecorView().setSystemUiVisibility(visibility);
|
||||||
|
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1571,90 +1573,8 @@ public class VideoPlayerImpl extends VideoPlayer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public void disablePreloadingOfCurrentTrack() {
|
||||||
* Measures width and height of controls visible on screen.
|
getLoadController().disablePreloadingOfCurrentTrack();
|
||||||
* It ensures that controls will be side-by-side with NavigationBar and notches
|
|
||||||
* but not under them. Tablets have only bottom NavigationBar
|
|
||||||
*/
|
|
||||||
public void setControlsSize() {
|
|
||||||
final Point size = new Point();
|
|
||||||
final Display display = getRootView().getDisplay();
|
|
||||||
if (display == null || !videoPlayerSelected()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// This method will give a correct size of a usable area of a window.
|
|
||||||
// It doesn't include NavigationBar, notches, etc.
|
|
||||||
display.getSize(size);
|
|
||||||
|
|
||||||
final boolean isLandscape = service.isLandscape();
|
|
||||||
final int width = isFullscreen
|
|
||||||
? (isLandscape ? size.x : size.y)
|
|
||||||
: ViewGroup.LayoutParams.MATCH_PARENT;
|
|
||||||
final int gravity = isFullscreen
|
|
||||||
? (display.getRotation() == Surface.ROTATION_90
|
|
||||||
? Gravity.START : Gravity.END)
|
|
||||||
: Gravity.TOP;
|
|
||||||
|
|
||||||
getTopControlsRoot().getLayoutParams().width = width;
|
|
||||||
final RelativeLayout.LayoutParams topParams =
|
|
||||||
((RelativeLayout.LayoutParams) getTopControlsRoot().getLayoutParams());
|
|
||||||
topParams.removeRule(RelativeLayout.ALIGN_PARENT_START);
|
|
||||||
topParams.removeRule(RelativeLayout.ALIGN_PARENT_END);
|
|
||||||
topParams.addRule(gravity == Gravity.END
|
|
||||||
? RelativeLayout.ALIGN_PARENT_END
|
|
||||||
: RelativeLayout.ALIGN_PARENT_START);
|
|
||||||
getTopControlsRoot().requestLayout();
|
|
||||||
|
|
||||||
getBottomControlsRoot().getLayoutParams().width = width;
|
|
||||||
final RelativeLayout.LayoutParams bottomParams =
|
|
||||||
((RelativeLayout.LayoutParams) getBottomControlsRoot().getLayoutParams());
|
|
||||||
bottomParams.removeRule(RelativeLayout.ALIGN_PARENT_START);
|
|
||||||
bottomParams.removeRule(RelativeLayout.ALIGN_PARENT_END);
|
|
||||||
bottomParams.addRule(gravity == Gravity.END
|
|
||||||
? RelativeLayout.ALIGN_PARENT_END
|
|
||||||
: RelativeLayout.ALIGN_PARENT_START);
|
|
||||||
getBottomControlsRoot().requestLayout();
|
|
||||||
|
|
||||||
final ViewGroup controlsRoot = getRootView().findViewById(R.id.playbackWindowRoot);
|
|
||||||
// In tablet navigationBar located at the bottom of the screen.
|
|
||||||
// And the situations when we need to set custom height is
|
|
||||||
// in fullscreen mode in tablet in non-multiWindow mode or with vertical video.
|
|
||||||
// Other than that MATCH_PARENT is good
|
|
||||||
final boolean navBarAtTheBottom = DeviceUtils.isTablet(service) || !isLandscape;
|
|
||||||
controlsRoot.getLayoutParams().height = isFullscreen && !isInMultiWindow()
|
|
||||||
&& navBarAtTheBottom ? size.y : ViewGroup.LayoutParams.MATCH_PARENT;
|
|
||||||
controlsRoot.requestLayout();
|
|
||||||
|
|
||||||
final DisplayMetrics metrics = getRootView().getResources().getDisplayMetrics();
|
|
||||||
int topPadding = isFullscreen && !isInMultiWindow() ? getStatusBarHeight() : 0;
|
|
||||||
topPadding = !isLandscape && DeviceUtils.hasCutout(topPadding, metrics) ? 0 : topPadding;
|
|
||||||
getRootView().findViewById(R.id.playbackWindowRoot).setTranslationY(topPadding);
|
|
||||||
getBottomControlsRoot().setTranslationY(-topPadding);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return statusBar height that was found inside system resources
|
|
||||||
* or default value if no value was provided inside resources
|
|
||||||
*/
|
|
||||||
private int getStatusBarHeight() {
|
|
||||||
int statusBarHeight = 0;
|
|
||||||
final int resourceId = service.isLandscape()
|
|
||||||
? service.getResources().getIdentifier(
|
|
||||||
"status_bar_height_landscape", "dimen", "android")
|
|
||||||
: service.getResources().getIdentifier(
|
|
||||||
"status_bar_height", "dimen", "android");
|
|
||||||
|
|
||||||
if (resourceId > 0) {
|
|
||||||
statusBarHeight = service.getResources().getDimensionPixelSize(resourceId);
|
|
||||||
}
|
|
||||||
if (statusBarHeight == 0) {
|
|
||||||
// Some devices provide wrong value for status bar height in landscape mode,
|
|
||||||
// this is workaround
|
|
||||||
final DisplayMetrics metrics = getRootView().getResources().getDisplayMetrics();
|
|
||||||
statusBarHeight = (int) TypedValue.applyDimension(
|
|
||||||
TypedValue.COMPLEX_UNIT_DIP, 24, metrics);
|
|
||||||
}
|
|
||||||
return statusBarHeight;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setMuteButton(final ImageButton button, final boolean isMuted) {
|
protected void setMuteButton(final ImageButton button, final boolean isMuted) {
|
||||||
|
@ -1724,8 +1644,6 @@ public class VideoPlayerImpl extends VideoPlayer
|
||||||
&& !DeviceUtils.isTablet(service)) {
|
&& !DeviceUtils.isTablet(service)) {
|
||||||
toggleFullscreen();
|
toggleFullscreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
setControlsSize();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void buildQueue() {
|
private void buildQueue() {
|
||||||
|
@ -2122,6 +2040,12 @@ public class VideoPlayerImpl extends VideoPlayer
|
||||||
public void setFragmentListener(final PlayerServiceEventListener listener) {
|
public void setFragmentListener(final PlayerServiceEventListener listener) {
|
||||||
fragmentListener = listener;
|
fragmentListener = listener;
|
||||||
fragmentIsVisible = true;
|
fragmentIsVisible = true;
|
||||||
|
// Apply window insets because Android will not do it when orientation changes
|
||||||
|
// from landscape to portrait
|
||||||
|
if (!isFullscreen) {
|
||||||
|
getControlsRoot().setPadding(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
queueLayout.setPadding(0, 0, 0, 0);
|
||||||
updateMetadata();
|
updateMetadata();
|
||||||
updatePlayback();
|
updatePlayback();
|
||||||
triggerProgressUpdate();
|
triggerProgressUpdate();
|
||||||
|
|
|
@ -39,12 +39,13 @@ public class CustomBottomSheetBehavior extends BottomSheetBehavior<FrameLayout>
|
||||||
}
|
}
|
||||||
|
|
||||||
// Found that user still swiping, continue following
|
// Found that user still swiping, continue following
|
||||||
if (skippingInterception) {
|
if (skippingInterception || getState() == BottomSheetBehavior.STATE_SETTLING) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't need to do anything if bottomSheet isn't expanded
|
// Don't need to do anything if bottomSheet isn't expanded
|
||||||
if (getState() == BottomSheetBehavior.STATE_EXPANDED) {
|
if (getState() == BottomSheetBehavior.STATE_EXPANDED
|
||||||
|
&& event.getAction() == MotionEvent.ACTION_DOWN) {
|
||||||
// Without overriding scrolling will not work when user touches these elements
|
// Without overriding scrolling will not work when user touches these elements
|
||||||
for (final Integer element : skipInterceptionOfElements) {
|
for (final Integer element : skipInterceptionOfElements) {
|
||||||
final ViewGroup viewGroup = child.findViewById(element);
|
final ViewGroup viewGroup = child.findViewById(element);
|
||||||
|
|
|
@ -9,6 +9,7 @@ import android.view.View;
|
||||||
import android.view.ViewConfiguration;
|
import android.view.ViewConfiguration;
|
||||||
import android.view.Window;
|
import android.view.Window;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
import androidx.appcompat.content.res.AppCompatResources;
|
import androidx.appcompat.content.res.AppCompatResources;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.player.BasePlayer;
|
import org.schabi.newpipe.player.BasePlayer;
|
||||||
|
@ -264,14 +265,19 @@ public class PlayerGestureListener
|
||||||
}
|
}
|
||||||
|
|
||||||
final Window window = parent.getWindow();
|
final Window window = parent.getWindow();
|
||||||
|
|
||||||
playerImpl.getBrightnessProgressBar().incrementProgressBy((int) distanceY);
|
|
||||||
final float currentProgressPercent = (float) playerImpl.getBrightnessProgressBar()
|
|
||||||
.getProgress() / playerImpl.getMaxGestureLength();
|
|
||||||
final WindowManager.LayoutParams layoutParams = window.getAttributes();
|
final WindowManager.LayoutParams layoutParams = window.getAttributes();
|
||||||
|
final ProgressBar bar = playerImpl.getBrightnessProgressBar();
|
||||||
|
final float oldBrightness = layoutParams.screenBrightness;
|
||||||
|
bar.setProgress((int) (bar.getMax() * Math.max(0, Math.min(1, oldBrightness))));
|
||||||
|
bar.incrementProgressBy((int) distanceY);
|
||||||
|
|
||||||
|
final float currentProgressPercent = (float) bar.getProgress() / bar.getMax();
|
||||||
layoutParams.screenBrightness = currentProgressPercent;
|
layoutParams.screenBrightness = currentProgressPercent;
|
||||||
window.setAttributes(layoutParams);
|
window.setAttributes(layoutParams);
|
||||||
|
|
||||||
|
// Save current brightness level
|
||||||
|
PlayerHelper.setScreenBrightness(parent, currentProgressPercent);
|
||||||
|
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "onScroll().brightnessControl, "
|
Log.d(TAG, "onScroll().brightnessControl, "
|
||||||
+ "currentBrightness = " + currentProgressPercent);
|
+ "currentBrightness = " + currentProgressPercent);
|
||||||
|
|
|
@ -13,6 +13,7 @@ public class LoadController implements LoadControl {
|
||||||
|
|
||||||
private final long initialPlaybackBufferUs;
|
private final long initialPlaybackBufferUs;
|
||||||
private final LoadControl internalLoadControl;
|
private final LoadControl internalLoadControl;
|
||||||
|
private boolean preloadingEnabled = true;
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Default Load Control
|
// Default Load Control
|
||||||
|
@ -41,6 +42,7 @@ public class LoadController implements LoadControl {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPrepared() {
|
public void onPrepared() {
|
||||||
|
preloadingEnabled = true;
|
||||||
internalLoadControl.onPrepared();
|
internalLoadControl.onPrepared();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,11 +54,13 @@ public class LoadController implements LoadControl {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStopped() {
|
public void onStopped() {
|
||||||
|
preloadingEnabled = true;
|
||||||
internalLoadControl.onStopped();
|
internalLoadControl.onStopped();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReleased() {
|
public void onReleased() {
|
||||||
|
preloadingEnabled = true;
|
||||||
internalLoadControl.onReleased();
|
internalLoadControl.onReleased();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,6 +82,9 @@ public class LoadController implements LoadControl {
|
||||||
@Override
|
@Override
|
||||||
public boolean shouldContinueLoading(final long bufferedDurationUs,
|
public boolean shouldContinueLoading(final long bufferedDurationUs,
|
||||||
final float playbackSpeed) {
|
final float playbackSpeed) {
|
||||||
|
if (!preloadingEnabled) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return internalLoadControl.shouldContinueLoading(bufferedDurationUs, playbackSpeed);
|
return internalLoadControl.shouldContinueLoading(bufferedDurationUs, playbackSpeed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,4 +97,8 @@ public class LoadController implements LoadControl {
|
||||||
.shouldStartPlayback(bufferedDurationUs, playbackSpeed, rebuffering);
|
.shouldStartPlayback(bufferedDurationUs, playbackSpeed, rebuffering);
|
||||||
return isInitialPlaybackBufferFilled || isInternalStartingPlayback;
|
return isInitialPlaybackBufferFilled || isInternalStartingPlayback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void disablePreloadingOfCurrentTrack() {
|
||||||
|
preloadingEnabled = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,44 +3,58 @@ package org.schabi.newpipe.player.helper;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.media.MediaMetadata;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.support.v4.media.MediaMetadataCompat;
|
import android.support.v4.media.MediaMetadataCompat;
|
||||||
import android.support.v4.media.session.MediaSessionCompat;
|
import android.support.v4.media.session.MediaSessionCompat;
|
||||||
|
import android.support.v4.media.session.PlaybackStateCompat;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
import androidx.core.app.NotificationCompat;
|
|
||||||
import androidx.media.app.NotificationCompat.MediaStyle;
|
|
||||||
import androidx.media.session.MediaButtonReceiver;
|
import androidx.media.session.MediaButtonReceiver;
|
||||||
|
|
||||||
import com.google.android.exoplayer2.Player;
|
import com.google.android.exoplayer2.Player;
|
||||||
import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector;
|
import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.MainActivity;
|
||||||
import org.schabi.newpipe.player.mediasession.MediaSessionCallback;
|
import org.schabi.newpipe.player.mediasession.MediaSessionCallback;
|
||||||
import org.schabi.newpipe.player.mediasession.PlayQueueNavigator;
|
import org.schabi.newpipe.player.mediasession.PlayQueueNavigator;
|
||||||
import org.schabi.newpipe.player.mediasession.PlayQueuePlaybackController;
|
import org.schabi.newpipe.player.mediasession.PlayQueuePlaybackController;
|
||||||
|
|
||||||
public class MediaSessionManager {
|
public class MediaSessionManager {
|
||||||
private static final String TAG = "MediaSessionManager";
|
private static final String TAG = MediaSessionManager.class.getSimpleName();
|
||||||
|
public static final boolean DEBUG = MainActivity.DEBUG;
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private final MediaSessionCompat mediaSession;
|
private final MediaSessionCompat mediaSession;
|
||||||
@NonNull
|
@NonNull
|
||||||
private final MediaSessionConnector sessionConnector;
|
private final MediaSessionConnector sessionConnector;
|
||||||
|
|
||||||
|
private int lastAlbumArtHashCode;
|
||||||
|
|
||||||
public MediaSessionManager(@NonNull final Context context,
|
public MediaSessionManager(@NonNull final Context context,
|
||||||
@NonNull final Player player,
|
@NonNull final Player player,
|
||||||
@NonNull final MediaSessionCallback callback) {
|
@NonNull final MediaSessionCallback callback) {
|
||||||
this.mediaSession = new MediaSessionCompat(context, TAG);
|
mediaSession = new MediaSessionCompat(context, TAG);
|
||||||
this.mediaSession.setActive(true);
|
mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS
|
||||||
|
| MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
|
||||||
|
mediaSession.setActive(true);
|
||||||
|
|
||||||
this.sessionConnector = new MediaSessionConnector(mediaSession);
|
mediaSession.setPlaybackState(new PlaybackStateCompat.Builder()
|
||||||
this.sessionConnector.setControlDispatcher(new PlayQueuePlaybackController(callback));
|
.setState(PlaybackStateCompat.STATE_NONE, -1, 1)
|
||||||
this.sessionConnector.setQueueNavigator(new PlayQueueNavigator(mediaSession, callback));
|
.setActions(PlaybackStateCompat.ACTION_SEEK_TO
|
||||||
this.sessionConnector.setPlayer(player);
|
| PlaybackStateCompat.ACTION_PLAY
|
||||||
|
| PlaybackStateCompat.ACTION_PAUSE // was play and pause now play/pause
|
||||||
|
| PlaybackStateCompat.ACTION_SKIP_TO_NEXT
|
||||||
|
| PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS
|
||||||
|
| PlaybackStateCompat.ACTION_SET_REPEAT_MODE
|
||||||
|
| PlaybackStateCompat.ACTION_STOP)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
sessionConnector = new MediaSessionConnector(mediaSession);
|
||||||
|
sessionConnector.setControlDispatcher(new PlayQueuePlaybackController(callback));
|
||||||
|
sessionConnector.setQueueNavigator(new PlayQueueNavigator(mediaSession, callback));
|
||||||
|
sessionConnector.setPlayer(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@ -49,46 +63,78 @@ public class MediaSessionManager {
|
||||||
return MediaButtonReceiver.handleIntent(mediaSession, intent);
|
return MediaButtonReceiver.handleIntent(mediaSession, intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
public MediaSessionCompat.Token getSessionToken() {
|
||||||
public void setLockScreenArt(final NotificationCompat.Builder builder,
|
return mediaSession.getSessionToken();
|
||||||
@Nullable final Bitmap thumbnailBitmap) {
|
}
|
||||||
if (thumbnailBitmap == null || !mediaSession.isActive()) {
|
|
||||||
|
public void setMetadata(final String title,
|
||||||
|
final String artist,
|
||||||
|
final Bitmap albumArt,
|
||||||
|
final long duration) {
|
||||||
|
if (albumArt == null || !mediaSession.isActive()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mediaSession.setMetadata(
|
if (DEBUG) {
|
||||||
new MediaMetadataCompat.Builder()
|
if (getMetadataAlbumArt() == null) {
|
||||||
.putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, thumbnailBitmap)
|
Log.d(TAG, "N_getMetadataAlbumArt: thumb == null");
|
||||||
.build()
|
}
|
||||||
);
|
if (getMetadataTitle() == null) {
|
||||||
|
Log.d(TAG, "N_getMetadataTitle: title == null");
|
||||||
final MediaStyle mediaStyle = new MediaStyle()
|
}
|
||||||
.setMediaSession(mediaSession.getSessionToken());
|
if (getMetadataArtist() == null) {
|
||||||
|
Log.d(TAG, "N_getMetadataArtist: artist == null");
|
||||||
builder.setStyle(mediaStyle);
|
}
|
||||||
|
if (getMetadataDuration() <= 1) {
|
||||||
|
Log.d(TAG, "N_getMetadataDuration: duration <= 1; " + getMetadataDuration());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
if (getMetadataAlbumArt() == null || getMetadataTitle() == null
|
||||||
public void clearLockScreenArt(final NotificationCompat.Builder builder) {
|
|| getMetadataArtist() == null || getMetadataDuration() <= 1
|
||||||
mediaSession.setMetadata(
|
|| albumArt.hashCode() != lastAlbumArtHashCode) {
|
||||||
new MediaMetadataCompat.Builder()
|
if (DEBUG) {
|
||||||
.putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, null)
|
Log.d(TAG, "setMetadata: N_Metadata update: t: " + title + " a: " + artist
|
||||||
.build()
|
+ " thumb: " + albumArt.hashCode() + " d: " + duration);
|
||||||
);
|
}
|
||||||
|
|
||||||
final MediaStyle mediaStyle = new MediaStyle()
|
mediaSession.setMetadata(new MediaMetadataCompat.Builder()
|
||||||
.setMediaSession(mediaSession.getSessionToken());
|
.putString(MediaMetadataCompat.METADATA_KEY_TITLE, title)
|
||||||
|
.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, artist)
|
||||||
|
.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, albumArt)
|
||||||
|
.putBitmap(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON, albumArt)
|
||||||
|
.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, duration).build());
|
||||||
|
lastAlbumArtHashCode = albumArt.hashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
builder.setStyle(mediaStyle);
|
private Bitmap getMetadataAlbumArt() {
|
||||||
|
return mediaSession.getController().getMetadata()
|
||||||
|
.getBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getMetadataTitle() {
|
||||||
|
return mediaSession.getController().getMetadata()
|
||||||
|
.getString(MediaMetadataCompat.METADATA_KEY_TITLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getMetadataArtist() {
|
||||||
|
return mediaSession.getController().getMetadata()
|
||||||
|
.getString(MediaMetadataCompat.METADATA_KEY_ARTIST);
|
||||||
|
}
|
||||||
|
|
||||||
|
private long getMetadataDuration() {
|
||||||
|
return mediaSession.getController().getMetadata()
|
||||||
|
.getLong(MediaMetadataCompat.METADATA_KEY_DURATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Should be called on player destruction to prevent leakage.
|
* Should be called on player destruction to prevent leakage.
|
||||||
*/
|
*/
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
this.sessionConnector.setPlayer(null);
|
sessionConnector.setPlayer(null);
|
||||||
this.sessionConnector.setQueueNavigator(null);
|
sessionConnector.setQueueNavigator(null);
|
||||||
this.mediaSession.setActive(false);
|
mediaSession.setActive(false);
|
||||||
this.mediaSession.release();
|
mediaSession.release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ import org.schabi.newpipe.extractor.stream.VideoStream;
|
||||||
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
||||||
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
|
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
|
||||||
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
|
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
|
||||||
|
import org.schabi.newpipe.util.ListHelper;
|
||||||
|
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
|
@ -248,6 +249,18 @@ public final class PlayerHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isAutoplayAllowedByUser(@NonNull final Context context) {
|
||||||
|
switch (PlayerHelper.getAutoplayType(context)) {
|
||||||
|
case PlayerHelper.AutoplayType.AUTOPLAY_TYPE_NEVER:
|
||||||
|
return false;
|
||||||
|
case PlayerHelper.AutoplayType.AUTOPLAY_TYPE_WIFI:
|
||||||
|
return !ListHelper.isMeteredNetwork(context);
|
||||||
|
case PlayerHelper.AutoplayType.AUTOPLAY_TYPE_ALWAYS:
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public static SeekParameters getSeekParameters(@NonNull final Context context) {
|
public static SeekParameters getSeekParameters(@NonNull final Context context) {
|
||||||
return isUsingInexactSeek(context) ? SeekParameters.CLOSEST_SYNC : SeekParameters.EXACT;
|
return isUsingInexactSeek(context) ? SeekParameters.CLOSEST_SYNC : SeekParameters.EXACT;
|
||||||
|
|
|
@ -255,21 +255,21 @@ public class MediaSourceManager {
|
||||||
|
|
||||||
// Loading and Syncing
|
// Loading and Syncing
|
||||||
switch (event.type()) {
|
switch (event.type()) {
|
||||||
case INIT:
|
case INIT: case REORDER: case ERROR: case SELECT:
|
||||||
case REORDER:
|
|
||||||
case ERROR:
|
|
||||||
case SELECT:
|
|
||||||
loadImmediate(); // low frequency, critical events
|
loadImmediate(); // low frequency, critical events
|
||||||
break;
|
break;
|
||||||
case APPEND:
|
case APPEND: case REMOVE: case MOVE: case RECOVERY:
|
||||||
case REMOVE:
|
|
||||||
case MOVE:
|
|
||||||
case RECOVERY:
|
|
||||||
default:
|
default:
|
||||||
loadDebounced(); // high frequency or noncritical events
|
loadDebounced(); // high frequency or noncritical events
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// update ui and notification
|
||||||
|
switch (event.type()) {
|
||||||
|
case APPEND: case REMOVE: case MOVE: case REORDER:
|
||||||
|
playbackListener.onPlayQueueEdited();
|
||||||
|
}
|
||||||
|
|
||||||
if (!isPlayQueueReady()) {
|
if (!isPlayQueueReady()) {
|
||||||
maybeBlock();
|
maybeBlock();
|
||||||
playQueue.fetch();
|
playQueue.fetch();
|
||||||
|
|
|
@ -69,7 +69,7 @@ public interface PlaybackListener {
|
||||||
MediaSource sourceOf(PlayQueueItem item, StreamInfo info);
|
MediaSource sourceOf(PlayQueueItem item, StreamInfo info);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the play queue can no longer to played or used.
|
* Called when the play queue can no longer be played or used.
|
||||||
* Currently, this means the play queue is empty and complete.
|
* Currently, this means the play queue is empty and complete.
|
||||||
* Signals to the listener that it should shutdown.
|
* Signals to the listener that it should shutdown.
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -77,4 +77,13 @@ public interface PlaybackListener {
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
void onPlaybackShutdown();
|
void onPlaybackShutdown();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called whenever the play queue was edited (items were added, deleted or moved),
|
||||||
|
* use this to e.g. update notification buttons or fragment ui.
|
||||||
|
* <p>
|
||||||
|
* May be called at any time.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
void onPlayQueueEdited();
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,8 @@ public enum UserAction {
|
||||||
DELETE_FROM_HISTORY("delete from history"),
|
DELETE_FROM_HISTORY("delete from history"),
|
||||||
PLAY_STREAM("Play stream"),
|
PLAY_STREAM("Play stream"),
|
||||||
DOWNLOAD_POSTPROCESSING("download post-processing"),
|
DOWNLOAD_POSTPROCESSING("download post-processing"),
|
||||||
DOWNLOAD_FAILED("download failed");
|
DOWNLOAD_FAILED("download failed"),
|
||||||
|
PREFERENCES_MIGRATION("migration of preferences");
|
||||||
|
|
||||||
|
|
||||||
private final String message;
|
private final String message;
|
||||||
|
|
|
@ -5,12 +5,12 @@ import android.os.Bundle;
|
||||||
import androidx.preference.PreferenceManager;
|
import androidx.preference.PreferenceManager;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.ActionBar;
|
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
|
||||||
import androidx.preference.PreferenceFragmentCompat;
|
import androidx.preference.PreferenceFragmentCompat;
|
||||||
|
|
||||||
import org.schabi.newpipe.MainActivity;
|
import org.schabi.newpipe.MainActivity;
|
||||||
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
|
||||||
public abstract class BasePreferenceFragment extends PreferenceFragmentCompat {
|
public abstract class BasePreferenceFragment extends PreferenceFragmentCompat {
|
||||||
protected final String TAG = getClass().getSimpleName() + "@" + Integer.toHexString(hashCode());
|
protected final String TAG = getClass().getSimpleName() + "@" + Integer.toHexString(hashCode());
|
||||||
|
@ -25,24 +25,16 @@ public abstract class BasePreferenceFragment extends PreferenceFragmentCompat {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onViewCreated(final View view, @Nullable final Bundle savedInstanceState) {
|
public void onViewCreated(@NonNull final View rootView,
|
||||||
super.onViewCreated(view, savedInstanceState);
|
@Nullable final Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(rootView, savedInstanceState);
|
||||||
setDivider(null);
|
setDivider(null);
|
||||||
updateTitle();
|
ThemeHelper.setTitleToAppCompatActivity(getActivity(), getPreferenceScreen().getTitle());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
updateTitle();
|
ThemeHelper.setTitleToAppCompatActivity(getActivity(), getPreferenceScreen().getTitle());
|
||||||
}
|
|
||||||
|
|
||||||
private void updateTitle() {
|
|
||||||
if (getActivity() instanceof AppCompatActivity) {
|
|
||||||
final ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
|
|
||||||
if (actionBar != null) {
|
|
||||||
actionBar.setTitle(getPreferenceScreen().getTitle());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import androidx.preference.PreferenceManager;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Created by k3b on 07.01.2016.
|
* Created by k3b on 07.01.2016.
|
||||||
|
@ -38,6 +39,22 @@ public final class NewPipeSettings {
|
||||||
private NewPipeSettings() { }
|
private NewPipeSettings() { }
|
||||||
|
|
||||||
public static void initSettings(final Context context) {
|
public static void initSettings(final Context context) {
|
||||||
|
// check if there are entries in the prefs to determine whether this is the first app run
|
||||||
|
Boolean isFirstRun = null;
|
||||||
|
final Set<String> prefsKeys = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
.getAll().keySet();
|
||||||
|
for (final String key: prefsKeys) {
|
||||||
|
// ACRA stores some info in the prefs during app initialization
|
||||||
|
// which happens before this method is called. Therefore ignore ACRA-related keys.
|
||||||
|
if (!key.toLowerCase().startsWith("acra")) {
|
||||||
|
isFirstRun = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isFirstRun == null) {
|
||||||
|
isFirstRun = true;
|
||||||
|
}
|
||||||
|
|
||||||
PreferenceManager.setDefaultValues(context, R.xml.appearance_settings, true);
|
PreferenceManager.setDefaultValues(context, R.xml.appearance_settings, true);
|
||||||
PreferenceManager.setDefaultValues(context, R.xml.content_settings, true);
|
PreferenceManager.setDefaultValues(context, R.xml.content_settings, true);
|
||||||
PreferenceManager.setDefaultValues(context, R.xml.download_settings, true);
|
PreferenceManager.setDefaultValues(context, R.xml.download_settings, true);
|
||||||
|
@ -49,6 +66,8 @@ public final class NewPipeSettings {
|
||||||
|
|
||||||
getVideoDownloadFolder(context);
|
getVideoDownloadFolder(context);
|
||||||
getAudioDownloadFolder(context);
|
getAudioDownloadFolder(context);
|
||||||
|
|
||||||
|
SettingMigrations.initMigrations(context, isFirstRun);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void getVideoDownloadFolder(final Context context) {
|
private static void getVideoDownloadFolder(final Context context) {
|
||||||
|
|
|
@ -0,0 +1,270 @@
|
||||||
|
package org.schabi.newpipe.settings;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.graphics.PorterDuff;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.CheckBox;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.RadioButton;
|
||||||
|
import android.widget.RadioGroup;
|
||||||
|
import android.widget.Switch;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.appcompat.content.res.AppCompatResources;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.R;
|
||||||
|
import org.schabi.newpipe.player.MainPlayer;
|
||||||
|
import org.schabi.newpipe.player.NotificationConstants;
|
||||||
|
import org.schabi.newpipe.util.DeviceUtils;
|
||||||
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
import org.schabi.newpipe.views.FocusOverlayView;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class NotificationSettingsFragment extends Fragment {
|
||||||
|
|
||||||
|
private Switch scaleSwitch;
|
||||||
|
private NotificationSlot[] notificationSlots;
|
||||||
|
|
||||||
|
private SharedPreferences pref;
|
||||||
|
private List<Integer> compactSlots;
|
||||||
|
private String scaleKey;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Lifecycle
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
pref = PreferenceManager.getDefaultSharedPreferences(requireContext());
|
||||||
|
scaleKey = getString(R.string.scale_to_square_image_in_notifications_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull final LayoutInflater inflater,
|
||||||
|
final ViewGroup container,
|
||||||
|
@Nullable final Bundle savedInstanceState) {
|
||||||
|
return inflater.inflate(R.layout.settings_notification, container, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(@NonNull final View rootView,
|
||||||
|
@Nullable final Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(rootView, savedInstanceState);
|
||||||
|
|
||||||
|
setupScaleSwitch(rootView);
|
||||||
|
setupActions(rootView);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
ThemeHelper.setTitleToAppCompatActivity(getActivity(),
|
||||||
|
getString(R.string.settings_category_notification_title));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
saveChanges();
|
||||||
|
requireContext().sendBroadcast(new Intent(MainPlayer.ACTION_RECREATE_NOTIFICATION));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Setup
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
private void setupScaleSwitch(@NonNull final View view) {
|
||||||
|
scaleSwitch = view.findViewById(R.id.notificationScaleSwitch);
|
||||||
|
scaleSwitch.setChecked(pref.getBoolean(scaleKey, false));
|
||||||
|
|
||||||
|
view.findViewById(R.id.notificationScaleSwitchClickableArea)
|
||||||
|
.setOnClickListener(v -> scaleSwitch.toggle());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupActions(@NonNull final View view) {
|
||||||
|
compactSlots =
|
||||||
|
NotificationConstants.getCompactSlotsFromPreferences(requireContext(), pref, 5);
|
||||||
|
notificationSlots = new NotificationSlot[5];
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
notificationSlots[i] = new NotificationSlot(i, view);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Saving
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
private void saveChanges() {
|
||||||
|
final SharedPreferences.Editor editor = pref.edit();
|
||||||
|
editor.putBoolean(scaleKey, scaleSwitch.isChecked());
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
editor.putInt(getString(NotificationConstants.SLOT_COMPACT_PREF_KEYS[i]),
|
||||||
|
(i < compactSlots.size() ? compactSlots.get(i) : -1));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
editor.putInt(getString(NotificationConstants.SLOT_PREF_KEYS[i]),
|
||||||
|
notificationSlots[i].selectedAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Notification action
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
private static final int[] SLOT_ITEMS = {
|
||||||
|
R.id.notificationAction0,
|
||||||
|
R.id.notificationAction1,
|
||||||
|
R.id.notificationAction2,
|
||||||
|
R.id.notificationAction3,
|
||||||
|
R.id.notificationAction4,
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final int[] SLOT_TITLES = {
|
||||||
|
R.string.notification_action_0_title,
|
||||||
|
R.string.notification_action_1_title,
|
||||||
|
R.string.notification_action_2_title,
|
||||||
|
R.string.notification_action_3_title,
|
||||||
|
R.string.notification_action_4_title,
|
||||||
|
};
|
||||||
|
|
||||||
|
private class NotificationSlot {
|
||||||
|
|
||||||
|
final int i;
|
||||||
|
@NotificationConstants.Action int selectedAction;
|
||||||
|
|
||||||
|
ImageView icon;
|
||||||
|
TextView summary;
|
||||||
|
|
||||||
|
NotificationSlot(final int actionIndex, final View parentView) {
|
||||||
|
this.i = actionIndex;
|
||||||
|
|
||||||
|
final View view = parentView.findViewById(SLOT_ITEMS[i]);
|
||||||
|
setupSelectedAction(view);
|
||||||
|
setupTitle(view);
|
||||||
|
setupCheckbox(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setupTitle(final View view) {
|
||||||
|
((TextView) view.findViewById(R.id.notificationActionTitle))
|
||||||
|
.setText(SLOT_TITLES[i]);
|
||||||
|
view.findViewById(R.id.notificationActionClickableArea).setOnClickListener(
|
||||||
|
v -> openActionChooserDialog());
|
||||||
|
}
|
||||||
|
|
||||||
|
void setupCheckbox(final View view) {
|
||||||
|
final CheckBox compactSlotCheckBox = view.findViewById(R.id.notificationActionCheckBox);
|
||||||
|
compactSlotCheckBox.setChecked(compactSlots.contains(i));
|
||||||
|
view.findViewById(R.id.notificationActionCheckBoxClickableArea).setOnClickListener(
|
||||||
|
v -> {
|
||||||
|
if (compactSlotCheckBox.isChecked()) {
|
||||||
|
compactSlots.remove((Integer) i);
|
||||||
|
} else if (compactSlots.size() < 3) {
|
||||||
|
compactSlots.add(i);
|
||||||
|
} else {
|
||||||
|
Toast.makeText(requireContext(),
|
||||||
|
R.string.notification_actions_at_most_three,
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
compactSlotCheckBox.toggle();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void setupSelectedAction(final View view) {
|
||||||
|
icon = view.findViewById(R.id.notificationActionIcon);
|
||||||
|
summary = view.findViewById(R.id.notificationActionSummary);
|
||||||
|
selectedAction = pref.getInt(getString(NotificationConstants.SLOT_PREF_KEYS[i]),
|
||||||
|
NotificationConstants.SLOT_DEFAULTS[i]);
|
||||||
|
updateInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateInfo() {
|
||||||
|
if (NotificationConstants.ACTION_ICONS[selectedAction] == 0) {
|
||||||
|
icon.setImageDrawable(null);
|
||||||
|
} else {
|
||||||
|
icon.setImageDrawable(AppCompatResources.getDrawable(requireContext(),
|
||||||
|
NotificationConstants.ACTION_ICONS[selectedAction]));
|
||||||
|
}
|
||||||
|
|
||||||
|
summary.setText(NotificationConstants.getActionName(requireContext(), selectedAction));
|
||||||
|
}
|
||||||
|
|
||||||
|
void openActionChooserDialog() {
|
||||||
|
final LayoutInflater inflater = LayoutInflater.from(requireContext());
|
||||||
|
final LinearLayout rootLayout = (LinearLayout) inflater.inflate(
|
||||||
|
R.layout.single_choice_dialog_view, null, false);
|
||||||
|
final RadioGroup radioGroup = rootLayout.findViewById(android.R.id.list);
|
||||||
|
|
||||||
|
final AlertDialog alertDialog = new AlertDialog.Builder(requireContext())
|
||||||
|
.setTitle(SLOT_TITLES[i])
|
||||||
|
.setView(radioGroup)
|
||||||
|
.setCancelable(true)
|
||||||
|
.create();
|
||||||
|
|
||||||
|
final View.OnClickListener radioButtonsClickListener = v -> {
|
||||||
|
selectedAction = NotificationConstants.SLOT_ALLOWED_ACTIONS[i][v.getId()];
|
||||||
|
updateInfo();
|
||||||
|
alertDialog.dismiss();
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int id = 0; id < NotificationConstants.SLOT_ALLOWED_ACTIONS[i].length; ++id) {
|
||||||
|
final int action = NotificationConstants.SLOT_ALLOWED_ACTIONS[i][id];
|
||||||
|
final RadioButton radioButton
|
||||||
|
= (RadioButton) inflater.inflate(R.layout.list_radio_icon_item, null);
|
||||||
|
|
||||||
|
// if present set action icon with correct color
|
||||||
|
if (NotificationConstants.ACTION_ICONS[action] != 0) {
|
||||||
|
final Drawable drawable = AppCompatResources.getDrawable(requireContext(),
|
||||||
|
NotificationConstants.ACTION_ICONS[action]);
|
||||||
|
if (drawable != null) {
|
||||||
|
final int color = ThemeHelper.resolveColorFromAttr(requireContext(),
|
||||||
|
android.R.attr.textColorPrimary);
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
drawable.setTint(color);
|
||||||
|
} else {
|
||||||
|
drawable.mutate().setColorFilter(color, PorterDuff.Mode.SRC_IN);
|
||||||
|
}
|
||||||
|
radioButton.setCompoundDrawablesWithIntrinsicBounds(
|
||||||
|
null, null, drawable, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
radioButton.setText(NotificationConstants.getActionName(requireContext(), action));
|
||||||
|
radioButton.setChecked(action == selectedAction);
|
||||||
|
radioButton.setId(id);
|
||||||
|
radioButton.setLayoutParams(new RadioGroup.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
|
radioButton.setOnClickListener(radioButtonsClickListener);
|
||||||
|
radioGroup.addView(radioButton);
|
||||||
|
}
|
||||||
|
alertDialog.show();
|
||||||
|
|
||||||
|
if (DeviceUtils.isTv(requireContext())) {
|
||||||
|
FocusOverlayView.setupFocusObserver(alertDialog);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,9 +22,7 @@ import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.ActionBar;
|
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
|
||||||
import androidx.appcompat.content.res.AppCompatResources;
|
import androidx.appcompat.content.res.AppCompatResources;
|
||||||
import androidx.appcompat.widget.AppCompatImageView;
|
import androidx.appcompat.widget.AppCompatImageView;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
|
@ -117,7 +115,8 @@ public class PeertubeInstanceListFragment extends Fragment {
|
||||||
@Override
|
@Override
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
updateTitle();
|
ThemeHelper.setTitleToAppCompatActivity(getActivity(),
|
||||||
|
getString(R.string.peertube_instance_url_title));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -176,15 +175,6 @@ public class PeertubeInstanceListFragment extends Fragment {
|
||||||
sharedPreferences.edit().putBoolean(Constants.KEY_MAIN_PAGE_CHANGE, true).apply();
|
sharedPreferences.edit().putBoolean(Constants.KEY_MAIN_PAGE_CHANGE, true).apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateTitle() {
|
|
||||||
if (getActivity() instanceof AppCompatActivity) {
|
|
||||||
final ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
|
|
||||||
if (actionBar != null) {
|
|
||||||
actionBar.setTitle(R.string.peertube_instance_url_title);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void saveChanges() {
|
private void saveChanges() {
|
||||||
final JsonStringWriter jsonWriter = JsonWriter.string().object().array("instances");
|
final JsonStringWriter jsonWriter = JsonWriter.string().object().array("instances");
|
||||||
for (final PeertubeInstance instance : instanceList) {
|
for (final PeertubeInstance instance : instanceList) {
|
||||||
|
|
|
@ -0,0 +1,140 @@
|
||||||
|
package org.schabi.newpipe.settings;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.preference.PreferenceManager;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.R;
|
||||||
|
import org.schabi.newpipe.report.ErrorActivity;
|
||||||
|
import org.schabi.newpipe.report.ErrorActivity.ErrorInfo;
|
||||||
|
import org.schabi.newpipe.report.UserAction;
|
||||||
|
|
||||||
|
import static org.schabi.newpipe.MainActivity.DEBUG;
|
||||||
|
|
||||||
|
public final class SettingMigrations {
|
||||||
|
private static final String TAG = SettingMigrations.class.toString();
|
||||||
|
/**
|
||||||
|
* Version number for preferences. Must be incremented every time a migration is necessary.
|
||||||
|
*/
|
||||||
|
public static final int VERSION = 2;
|
||||||
|
private static SharedPreferences sp;
|
||||||
|
|
||||||
|
public static final Migration MIGRATION_0_1 = new Migration(0, 1) {
|
||||||
|
@Override
|
||||||
|
public void migrate(final Context context) {
|
||||||
|
// We changed the content of the dialog which opens when sharing a link to NewPipe
|
||||||
|
// by removing the "open detail page" option.
|
||||||
|
// Therefore, show the dialog once again to ensure users need to choose again and are
|
||||||
|
// aware of the changed dialog.
|
||||||
|
final SharedPreferences.Editor editor = sp.edit();
|
||||||
|
editor.putString(context.getString(R.string.preferred_open_action_key),
|
||||||
|
context.getString(R.string.always_ask_open_action_key));
|
||||||
|
editor.apply();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static final Migration MIGRATION_1_2 = new Migration(1, 2) {
|
||||||
|
@Override
|
||||||
|
protected void migrate(final Context context) {
|
||||||
|
// The new application workflow introduced in #2907 allows minimizing videos
|
||||||
|
// while playing to do other stuff within the app.
|
||||||
|
// For an even better workflow, we minimize a stream when switching the app to play in
|
||||||
|
// background.
|
||||||
|
// Therefore, set default value to background, if it has not been changed yet.
|
||||||
|
final String minimizeOnExitKey = context.getString(R.string.minimize_on_exit_key);
|
||||||
|
if (sp.getString(minimizeOnExitKey, "")
|
||||||
|
.equals(context.getString(R.string.minimize_on_exit_none_key))) {
|
||||||
|
final SharedPreferences.Editor editor = sp.edit();
|
||||||
|
editor.putString(minimizeOnExitKey,
|
||||||
|
context.getString(R.string.minimize_on_exit_background_key));
|
||||||
|
editor.apply();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of all implemented migrations.
|
||||||
|
* <p>
|
||||||
|
* <b>Append new migrations to the end of the list</b> to keep it sorted ascending.
|
||||||
|
* If not sorted correctly, migrations which depend on each other, may fail.
|
||||||
|
*/
|
||||||
|
private static final Migration[] SETTING_MIGRATIONS = {
|
||||||
|
MIGRATION_0_1,
|
||||||
|
MIGRATION_1_2
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
public static void initMigrations(final Context context, final boolean isFirstRun) {
|
||||||
|
// setup migrations and check if there is something to do
|
||||||
|
sp = PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
|
final String lastPrefVersionKey = context.getString(R.string.last_used_preferences_version);
|
||||||
|
final int lastPrefVersion = sp.getInt(lastPrefVersionKey, 0);
|
||||||
|
|
||||||
|
// no migration to run, already up to date
|
||||||
|
if (isFirstRun) {
|
||||||
|
sp.edit().putInt(lastPrefVersionKey, VERSION).apply();
|
||||||
|
return;
|
||||||
|
} else if (lastPrefVersion == VERSION) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// run migrations
|
||||||
|
int currentVersion = lastPrefVersion;
|
||||||
|
for (final Migration currentMigration : SETTING_MIGRATIONS) {
|
||||||
|
try {
|
||||||
|
if (currentMigration.shouldMigrate(currentVersion)) {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d(TAG, "Migrating preferences from version "
|
||||||
|
+ currentVersion + " to " + currentMigration.newVersion);
|
||||||
|
}
|
||||||
|
currentMigration.migrate(context);
|
||||||
|
currentVersion = currentMigration.newVersion;
|
||||||
|
}
|
||||||
|
} catch (final Exception e) {
|
||||||
|
// save the version with the last successful migration and report the error
|
||||||
|
sp.edit().putInt(lastPrefVersionKey, currentVersion).apply();
|
||||||
|
final ErrorInfo errorInfo = ErrorInfo.make(
|
||||||
|
UserAction.PREFERENCES_MIGRATION,
|
||||||
|
"none",
|
||||||
|
"Migrating preferences from version " + lastPrefVersion + " to "
|
||||||
|
+ VERSION + ". "
|
||||||
|
+ "Error at " + currentVersion + " => " + ++currentVersion,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
ErrorActivity.reportError(context, e, SettingMigrations.class, null, errorInfo);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// store the current preferences version
|
||||||
|
sp.edit().putInt(lastPrefVersionKey, currentVersion).apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
private SettingMigrations() { }
|
||||||
|
|
||||||
|
abstract static class Migration {
|
||||||
|
public final int oldVersion;
|
||||||
|
public final int newVersion;
|
||||||
|
|
||||||
|
protected Migration(final int oldVersion, final int newVersion) {
|
||||||
|
this.oldVersion = oldVersion;
|
||||||
|
this.newVersion = newVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param currentVersion current settings version
|
||||||
|
* @return Returns whether this migration should be run.
|
||||||
|
* A migration is necessary if the old version of this migration is lower than or equal to
|
||||||
|
* the current settings version.
|
||||||
|
*/
|
||||||
|
private boolean shouldMigrate(final int currentVersion) {
|
||||||
|
return oldVersion >= currentVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void migrate(Context context);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -16,9 +16,7 @@ import android.widget.TextView;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.ActionBar;
|
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
|
||||||
import androidx.appcompat.content.res.AppCompatResources;
|
import androidx.appcompat.content.res.AppCompatResources;
|
||||||
import androidx.appcompat.widget.AppCompatImageView;
|
import androidx.appcompat.widget.AppCompatImageView;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
|
@ -92,7 +90,8 @@ public class ChooseTabsFragment extends Fragment {
|
||||||
@Override
|
@Override
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
updateTitle();
|
ThemeHelper.setTitleToAppCompatActivity(getActivity(),
|
||||||
|
getString(R.string.main_page_content));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -137,15 +136,6 @@ public class ChooseTabsFragment extends Fragment {
|
||||||
tabList.addAll(tabsManager.getTabs());
|
tabList.addAll(tabsManager.getTabs());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateTitle() {
|
|
||||||
if (getActivity() instanceof AppCompatActivity) {
|
|
||||||
final ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
|
|
||||||
if (actionBar != null) {
|
|
||||||
actionBar.setTitle(R.string.main_page_content);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void saveChanges() {
|
private void saveChanges() {
|
||||||
tabsManager.saveTabs(tabList);
|
tabsManager.saveTabs(tabList);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,6 @@ import android.content.pm.PackageManager;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.os.BatteryManager;
|
import android.os.BatteryManager;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.util.DisplayMetrics;
|
|
||||||
import android.util.TypedValue;
|
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
@ -74,17 +72,4 @@ public final class DeviceUtils {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Compares current status bar height with default status bar height in Android and decides,
|
|
||||||
* does the device has cutout or not
|
|
||||||
* */
|
|
||||||
public static boolean hasCutout(final float statusBarHeight, final DisplayMetrics metrics) {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
|
||||||
final float defaultStatusBarHeight = TypedValue.applyDimension(
|
|
||||||
TypedValue.COMPLEX_UNIT_DIP, 25, metrics);
|
|
||||||
return statusBarHeight > defaultStatusBarHeight;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ import org.schabi.newpipe.settings.SettingsActivity;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
@SuppressWarnings({"unused", "WeakerAccess"})
|
@SuppressWarnings({"unused"})
|
||||||
public final class NavigationHelper {
|
public final class NavigationHelper {
|
||||||
public static final String MAIN_FRAGMENT_TAG = "main_fragment_tag";
|
public static final String MAIN_FRAGMENT_TAG = "main_fragment_tag";
|
||||||
public static final String SEARCH_FRAGMENT_TAG = "search_fragment_tag";
|
public static final String SEARCH_FRAGMENT_TAG = "search_fragment_tag";
|
||||||
|
@ -69,17 +69,19 @@ public final class NavigationHelper {
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public static Intent getPlayerIntent(@NonNull final Context context,
|
public static <T> Intent getPlayerIntent(@NonNull final Context context,
|
||||||
@NonNull final Class targetClazz,
|
@NonNull final Class<T> targetClazz,
|
||||||
@NonNull final PlayQueue playQueue,
|
@Nullable final PlayQueue playQueue,
|
||||||
@Nullable final String quality,
|
@Nullable final String quality,
|
||||||
final boolean resumePlayback) {
|
final boolean resumePlayback) {
|
||||||
final Intent intent = new Intent(context, targetClazz);
|
final Intent intent = new Intent(context, targetClazz);
|
||||||
|
|
||||||
|
if (playQueue != null) {
|
||||||
final String cacheKey = SerializedCache.getInstance().put(playQueue, PlayQueue.class);
|
final String cacheKey = SerializedCache.getInstance().put(playQueue, PlayQueue.class);
|
||||||
if (cacheKey != null) {
|
if (cacheKey != null) {
|
||||||
intent.putExtra(VideoPlayer.PLAY_QUEUE_KEY, cacheKey);
|
intent.putExtra(VideoPlayer.PLAY_QUEUE_KEY, cacheKey);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (quality != null) {
|
if (quality != null) {
|
||||||
intent.putExtra(VideoPlayer.PLAYBACK_QUALITY, quality);
|
intent.putExtra(VideoPlayer.PLAYBACK_QUALITY, quality);
|
||||||
}
|
}
|
||||||
|
@ -90,17 +92,17 @@ public final class NavigationHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public static Intent getPlayerIntent(@NonNull final Context context,
|
public static <T> Intent getPlayerIntent(@NonNull final Context context,
|
||||||
@NonNull final Class targetClazz,
|
@NonNull final Class<T> targetClazz,
|
||||||
@NonNull final PlayQueue playQueue,
|
@Nullable final PlayQueue playQueue,
|
||||||
final boolean resumePlayback) {
|
final boolean resumePlayback) {
|
||||||
return getPlayerIntent(context, targetClazz, playQueue, null, resumePlayback);
|
return getPlayerIntent(context, targetClazz, playQueue, null, resumePlayback);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public static Intent getPlayerEnqueueIntent(@NonNull final Context context,
|
public static <T> Intent getPlayerEnqueueIntent(@NonNull final Context context,
|
||||||
@NonNull final Class targetClazz,
|
@NonNull final Class<T> targetClazz,
|
||||||
@NonNull final PlayQueue playQueue,
|
@Nullable final PlayQueue playQueue,
|
||||||
final boolean selectOnAppend,
|
final boolean selectOnAppend,
|
||||||
final boolean resumePlayback) {
|
final boolean resumePlayback) {
|
||||||
return getPlayerIntent(context, targetClazz, playQueue, resumePlayback)
|
return getPlayerIntent(context, targetClazz, playQueue, resumePlayback)
|
||||||
|
@ -109,9 +111,9 @@ public final class NavigationHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public static Intent getPlayerIntent(@NonNull final Context context,
|
public static <T> Intent getPlayerIntent(@NonNull final Context context,
|
||||||
@NonNull final Class targetClazz,
|
@NonNull final Class<T> targetClazz,
|
||||||
@NonNull final PlayQueue playQueue,
|
@Nullable final PlayQueue playQueue,
|
||||||
final int repeatMode,
|
final int repeatMode,
|
||||||
final float playbackSpeed,
|
final float playbackSpeed,
|
||||||
final float playbackPitch,
|
final float playbackPitch,
|
||||||
|
@ -126,15 +128,13 @@ public final class NavigationHelper {
|
||||||
.putExtra(BasePlayer.IS_MUTED, isMuted);
|
.putExtra(BasePlayer.IS_MUTED, isMuted);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void playOnMainPlayer(
|
public static void playOnMainPlayer(final AppCompatActivity activity,
|
||||||
final AppCompatActivity activity,
|
|
||||||
final PlayQueue queue,
|
final PlayQueue queue,
|
||||||
final boolean autoPlay) {
|
final boolean autoPlay) {
|
||||||
playOnMainPlayer(activity.getSupportFragmentManager(), queue, autoPlay);
|
playOnMainPlayer(activity.getSupportFragmentManager(), queue, autoPlay);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void playOnMainPlayer(
|
public static void playOnMainPlayer(final FragmentManager fragmentManager,
|
||||||
final FragmentManager fragmentManager,
|
|
||||||
final PlayQueue queue,
|
final PlayQueue queue,
|
||||||
final boolean autoPlay) {
|
final boolean autoPlay) {
|
||||||
final PlayQueueItem currentStream = queue.getItem();
|
final PlayQueueItem currentStream = queue.getItem();
|
||||||
|
@ -148,7 +148,7 @@ public final class NavigationHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void playOnMainPlayer(@NonNull final Context context,
|
public static void playOnMainPlayer(@NonNull final Context context,
|
||||||
@NonNull final PlayQueue queue,
|
@Nullable final PlayQueue queue,
|
||||||
@NonNull final StreamingService.LinkType linkType,
|
@NonNull final StreamingService.LinkType linkType,
|
||||||
@NonNull final String url,
|
@NonNull final String url,
|
||||||
@NonNull final String title,
|
@NonNull final String title,
|
||||||
|
@ -553,18 +553,14 @@ public final class NavigationHelper {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Intent getBackgroundPlayerActivityIntent(final Context context) {
|
public static Intent getPlayQueueActivityIntent(final Context context) {
|
||||||
return getServicePlayerActivityIntent(context, BackgroundPlayerActivity.class);
|
final Intent intent = new Intent(context, BackgroundPlayerActivity.class);
|
||||||
}
|
|
||||||
|
|
||||||
private static Intent getServicePlayerActivityIntent(final Context context,
|
|
||||||
final Class activityClass) {
|
|
||||||
final Intent intent = new Intent(context, activityClass);
|
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
|
||||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
}
|
}
|
||||||
return intent;
|
return intent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Link handling
|
// Link handling
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
package org.schabi.newpipe.util;
|
package org.schabi.newpipe.util;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.TypedArray;
|
import android.content.res.TypedArray;
|
||||||
import androidx.preference.PreferenceManager;
|
import androidx.preference.PreferenceManager;
|
||||||
|
@ -26,7 +27,10 @@ import android.util.TypedValue;
|
||||||
import android.view.ContextThemeWrapper;
|
import android.view.ContextThemeWrapper;
|
||||||
|
|
||||||
import androidx.annotation.AttrRes;
|
import androidx.annotation.AttrRes;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.StyleRes;
|
import androidx.annotation.StyleRes;
|
||||||
|
import androidx.appcompat.app.ActionBar;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
|
@ -231,4 +235,20 @@ public final class ThemeHelper {
|
||||||
return PreferenceManager.getDefaultSharedPreferences(context)
|
return PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
.getString(themeKey, defaultTheme);
|
.getString(themeKey, defaultTheme);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the title to the activity, if the activity is an {@link AppCompatActivity} and has an
|
||||||
|
* action bar.
|
||||||
|
* @param activity the activity to set the title of
|
||||||
|
* @param title the title to set to the activity
|
||||||
|
*/
|
||||||
|
public static void setTitleToAppCompatActivity(@Nullable final Activity activity,
|
||||||
|
final CharSequence title) {
|
||||||
|
if (activity instanceof AppCompatActivity) {
|
||||||
|
final ActionBar actionBar = ((AppCompatActivity) activity).getSupportActionBar();
|
||||||
|
if (actionBar != null) {
|
||||||
|
actionBar.setTitle(title);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
package org.schabi.newpipe.views;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.core.view.ViewCompat;
|
||||||
|
import androidx.core.view.WindowInsetsCompat;
|
||||||
|
import com.google.android.material.appbar.CollapsingToolbarLayout;
|
||||||
|
|
||||||
|
public class CustomCollapsingToolbarLayout extends CollapsingToolbarLayout {
|
||||||
|
public CustomCollapsingToolbarLayout(@NonNull final Context context) {
|
||||||
|
super(context);
|
||||||
|
overrideListener();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CustomCollapsingToolbarLayout(@NonNull final Context context,
|
||||||
|
@Nullable final AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
overrideListener();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CustomCollapsingToolbarLayout(@NonNull final Context context,
|
||||||
|
@Nullable final AttributeSet attrs,
|
||||||
|
final int defStyleAttr) {
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
overrideListener();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CollapsingToolbarLayout sets it's own setOnApplyInsetsListener which consumes
|
||||||
|
* system insets {@link CollapsingToolbarLayout#onWindowInsetChanged(WindowInsetsCompat)}
|
||||||
|
* so we will not receive them in subviews with fitsSystemWindows = true.
|
||||||
|
* Override Google's behavior
|
||||||
|
* */
|
||||||
|
public void overrideListener() {
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(this, (v, insets) -> insets);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,14 +1,17 @@
|
||||||
package org.schabi.newpipe.views;
|
package org.schabi.newpipe.views;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.os.Build.VERSION;
|
||||||
|
import android.os.Build.VERSION_CODES;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.view.SurfaceView;
|
import android.view.SurfaceView;
|
||||||
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
|
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
|
||||||
|
|
||||||
|
import static com.google.android.exoplayer2.ui.AspectRatioFrameLayout.RESIZE_MODE_FIT;
|
||||||
import static com.google.android.exoplayer2.ui.AspectRatioFrameLayout.RESIZE_MODE_ZOOM;
|
import static com.google.android.exoplayer2.ui.AspectRatioFrameLayout.RESIZE_MODE_ZOOM;
|
||||||
|
|
||||||
public class ExpandableSurfaceView extends SurfaceView {
|
public class ExpandableSurfaceView extends SurfaceView {
|
||||||
private int resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FIT;
|
private int resizeMode = RESIZE_MODE_FIT;
|
||||||
private int baseHeight = 0;
|
private int baseHeight = 0;
|
||||||
private int maxHeight = 0;
|
private int maxHeight = 0;
|
||||||
private float videoAspectRatio = 0.0f;
|
private float videoAspectRatio = 0.0f;
|
||||||
|
@ -30,7 +33,7 @@ public class ExpandableSurfaceView extends SurfaceView {
|
||||||
final boolean verticalVideo = videoAspectRatio < 1;
|
final boolean verticalVideo = videoAspectRatio < 1;
|
||||||
// Use maxHeight only on non-fit resize mode and in vertical videos
|
// Use maxHeight only on non-fit resize mode and in vertical videos
|
||||||
int height = maxHeight != 0
|
int height = maxHeight != 0
|
||||||
&& resizeMode != AspectRatioFrameLayout.RESIZE_MODE_FIT
|
&& resizeMode != RESIZE_MODE_FIT
|
||||||
&& verticalVideo ? maxHeight : baseHeight;
|
&& verticalVideo ? maxHeight : baseHeight;
|
||||||
|
|
||||||
if (height == 0) {
|
if (height == 0) {
|
||||||
|
@ -42,26 +45,22 @@ public class ExpandableSurfaceView extends SurfaceView {
|
||||||
scaleX = 1.0f;
|
scaleX = 1.0f;
|
||||||
scaleY = 1.0f;
|
scaleY = 1.0f;
|
||||||
|
|
||||||
switch (resizeMode) {
|
if (resizeMode == RESIZE_MODE_FIT
|
||||||
case AspectRatioFrameLayout.RESIZE_MODE_FIT:
|
// KitKat doesn't work well when a view has a scale like needed for ZOOM
|
||||||
|
|| (resizeMode == RESIZE_MODE_ZOOM && VERSION.SDK_INT < VERSION_CODES.LOLLIPOP)) {
|
||||||
if (aspectDeformation > 0) {
|
if (aspectDeformation > 0) {
|
||||||
height = (int) (width / videoAspectRatio);
|
height = (int) (width / videoAspectRatio);
|
||||||
} else {
|
} else {
|
||||||
width = (int) (height * videoAspectRatio);
|
width = (int) (height * videoAspectRatio);
|
||||||
}
|
}
|
||||||
|
} else if (resizeMode == RESIZE_MODE_ZOOM) {
|
||||||
break;
|
|
||||||
case RESIZE_MODE_ZOOM:
|
|
||||||
if (aspectDeformation < 0) {
|
if (aspectDeformation < 0) {
|
||||||
scaleY = viewAspectRatio / videoAspectRatio;
|
scaleY = viewAspectRatio / videoAspectRatio;
|
||||||
} else {
|
} else {
|
||||||
scaleX = videoAspectRatio / viewAspectRatio;
|
scaleX = videoAspectRatio / viewAspectRatio;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
|
super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
|
||||||
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
|
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,15 +17,19 @@
|
||||||
*/
|
*/
|
||||||
package org.schabi.newpipe.views;
|
package org.schabi.newpipe.views;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
|
import android.os.Build;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import android.view.WindowInsets;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
||||||
|
import org.schabi.newpipe.R;
|
||||||
|
|
||||||
public final class FocusAwareCoordinator extends CoordinatorLayout {
|
public final class FocusAwareCoordinator extends CoordinatorLayout {
|
||||||
private final Rect childFocus = new Rect();
|
private final Rect childFocus = new Rect();
|
||||||
|
@ -63,4 +67,41 @@ public final class FocusAwareCoordinator extends CoordinatorLayout {
|
||||||
requestChildRectangleOnScreen(child, childFocus, false);
|
requestChildRectangleOnScreen(child, childFocus, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies window insets to all children, not just for the first who consume the insets.
|
||||||
|
* Makes possible for multiple fragments to co-exist. Without this code
|
||||||
|
* the first ViewGroup who consumes will be the last who receive the insets
|
||||||
|
*/
|
||||||
|
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||||
|
@Override
|
||||||
|
public WindowInsets dispatchApplyWindowInsets(final WindowInsets insets) {
|
||||||
|
boolean consumed = false;
|
||||||
|
for (int i = 0; i < getChildCount(); i++) {
|
||||||
|
final View child = getChildAt(i);
|
||||||
|
final WindowInsets res = child.dispatchApplyWindowInsets(insets);
|
||||||
|
if (res.isConsumed()) {
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (consumed) {
|
||||||
|
insets.consumeSystemWindowInsets();
|
||||||
|
}
|
||||||
|
return insets;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adjusts player's controls manually because fitsSystemWindows doesn't work when multiple
|
||||||
|
* receivers adjust its bounds. So when two listeners are present (like in profile page)
|
||||||
|
* the player's controls will not receive insets. This method fixes it
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected boolean fitSystemWindows(final Rect insets) {
|
||||||
|
final ViewGroup controls = findViewById(R.id.playbackControlRoot);
|
||||||
|
if (controls != null) {
|
||||||
|
controls.setPadding(insets.left, insets.top, insets.right, insets.bottom);
|
||||||
|
}
|
||||||
|
return super.fitSystemWindows(insets);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Before Width: | Height: | Size: 221 B |
BIN
app/src/main/res/drawable-hdpi/ic_close_white_24dp_png.png
Normal file
After Width: | Height: | Size: 415 B |
After Width: | Height: | Size: 302 B |
Before Width: | Height: | Size: 675 B |
BIN
app/src/main/res/drawable-hdpi/ic_replay_white_24dp_png.png
Normal file
After Width: | Height: | Size: 621 B |
Before Width: | Height: | Size: 175 B |
BIN
app/src/main/res/drawable-mdpi/ic_close_white_24dp_png.png
Normal file
After Width: | Height: | Size: 285 B |
After Width: | Height: | Size: 246 B |
Before Width: | Height: | Size: 457 B |
BIN
app/src/main/res/drawable-mdpi/ic_replay_white_24dp_png.png
Normal file
After Width: | Height: | Size: 411 B |
Before Width: | Height: | Size: 257 B |
BIN
app/src/main/res/drawable-xhdpi/ic_close_white_24dp_png.png
Normal file
After Width: | Height: | Size: 602 B |
After Width: | Height: | Size: 413 B |
Before Width: | Height: | Size: 908 B |
BIN
app/src/main/res/drawable-xhdpi/ic_replay_white_24dp_png.png
Normal file
After Width: | Height: | Size: 837 B |
Before Width: | Height: | Size: 347 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_close_white_24dp_png.png
Normal file
After Width: | Height: | Size: 1 KiB |
After Width: | Height: | Size: 614 B |
Before Width: | Height: | Size: 1.4 KiB |
BIN
app/src/main/res/drawable-xxhdpi/ic_replay_white_24dp_png.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 436 B |
BIN
app/src/main/res/drawable-xxxhdpi/ic_close_white_24dp_png.png
Normal file
After Width: | Height: | Size: 2 KiB |
After Width: | Height: | Size: 777 B |
Before Width: | Height: | Size: 1.8 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/ic_replay_white_24dp_png.png
Normal file
After Width: | Height: | Size: 2 KiB |
9
app/src/main/res/drawable/ic_close_white_24dp.xml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFFFF"
|
||||||
|
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
|
||||||
|
</vector>
|
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M6,2l0.01,6L10,12l-3.99,4.01L6,22h12v-6l-4,-4l4,-3.99V2H6zM16,16.5V20H8v-3.5l4,-4L16,16.5z"
|
||||||
|
android:fillColor="#ffffff"/>
|
||||||
|
</vector>
|
9
app/src/main/res/drawable/ic_replay_white_24dp.xml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFFFF"
|
||||||
|
android:pathData="M12,5V1L7,6l5,5V7c3.31,0 6,2.69 6,6s-2.69,6 -6,6 -6,-2.69 -6,-6H4c0,4.42 3.58,8 8,8s8,-3.58 8,-8 -3.58,-8 -8,-8z"/>
|
||||||
|
</vector>
|
|
@ -20,7 +20,6 @@
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_weight="5"
|
android:layout_weight="5"
|
||||||
android:fitsSystemWindows="true"
|
|
||||||
android:isScrollContainer="true">
|
android:isScrollContainer="true">
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
@ -28,12 +27,11 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="@android:color/transparent"
|
android:background="@android:color/transparent"
|
||||||
android:fitsSystemWindows="true"
|
|
||||||
android:touchscreenBlocksFocus="false"
|
android:touchscreenBlocksFocus="false"
|
||||||
app:elevation="0dp"
|
app:elevation="0dp"
|
||||||
app:layout_behavior="com.google.android.material.appbar.FlingBehavior">
|
app:layout_behavior="com.google.android.material.appbar.FlingBehavior">
|
||||||
|
|
||||||
<com.google.android.material.appbar.CollapsingToolbarLayout
|
<org.schabi.newpipe.views.CustomCollapsingToolbarLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:layout_scrollFlags="scroll">
|
app:layout_scrollFlags="scroll">
|
||||||
|
@ -162,7 +160,7 @@
|
||||||
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
</org.schabi.newpipe.views.CustomCollapsingToolbarLayout>
|
||||||
|
|
||||||
<!-- CONTENT -->
|
<!-- CONTENT -->
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
|
|
|
@ -28,6 +28,22 @@
|
||||||
android:layout_centerInParent="true"
|
android:layout_centerInParent="true"
|
||||||
android:layout_gravity="center"/>
|
android:layout_gravity="center"/>
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/playerTopShadow"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="30dp"
|
||||||
|
android:background="@drawable/player_controls_top_background"
|
||||||
|
android:layout_alignParentTop="true"
|
||||||
|
android:visibility="gone"/>
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/playerBottomShadow"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="30dp"
|
||||||
|
android:background="@drawable/player_controls_background"
|
||||||
|
android:layout_alignParentBottom="true"
|
||||||
|
android:visibility="gone"/>
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/endScreen"
|
android:id="@+id/endScreen"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -42,28 +58,16 @@
|
||||||
android:id="@+id/playbackControlRoot"
|
android:id="@+id/playbackControlRoot"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
android:background="@color/video_overlay_color"
|
android:background="@color/video_overlay_color"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
tools:visibility="visible">
|
tools:visibility="visible">
|
||||||
|
|
||||||
<View
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="30dp"
|
|
||||||
android:background="@drawable/player_controls_top_background"
|
|
||||||
android:layout_alignParentTop="true" />
|
|
||||||
|
|
||||||
<View
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="30dp"
|
|
||||||
android:background="@drawable/player_controls_background"
|
|
||||||
android:layout_alignParentBottom="true" />
|
|
||||||
|
|
||||||
<!-- All top controls in this layout -->
|
<!-- All top controls in this layout -->
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:id="@+id/playbackWindowRoot"
|
android:id="@+id/playbackWindowRoot"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent">
|
||||||
android:fitsSystemWindows="true">
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/topControls"
|
android:id="@+id/topControls"
|
||||||
|
@ -228,7 +232,12 @@
|
||||||
tools:ignore="HardcodedText,RtlHardcoded"
|
tools:ignore="HardcodedText,RtlHardcoded"
|
||||||
tools:text="FIT"/>
|
tools:text="FIT"/>
|
||||||
|
|
||||||
<Button
|
<FrameLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="3">
|
||||||
|
|
||||||
|
<TextView
|
||||||
android:id="@+id/captionTextView"
|
android:id="@+id/captionTextView"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -241,16 +250,11 @@
|
||||||
android:minWidth="50dp"
|
android:minWidth="50dp"
|
||||||
android:textColor="@android:color/white"
|
android:textColor="@android:color/white"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
android:textAllCaps="false"
|
|
||||||
android:background="?attr/selectableItemBackground"
|
android:background="?attr/selectableItemBackground"
|
||||||
tools:ignore="RelativeOverlap,RtlHardcoded"
|
tools:ignore="RelativeOverlap,RtlHardcoded"
|
||||||
tools:text="English"/>
|
tools:text="English"/>
|
||||||
|
|
||||||
<Space
|
</FrameLayout>
|
||||||
android:id="@+id/spaceBeforeButton"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:layout_weight="3"/>
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageButton
|
<androidx.appcompat.widget.AppCompatImageButton
|
||||||
android:id="@+id/playWithKodi"
|
android:id="@+id/playWithKodi"
|
||||||
|
@ -307,16 +311,11 @@
|
||||||
android:contentDescription="@string/mute"
|
android:contentDescription="@string/mute"
|
||||||
tools:ignore="RtlHardcoded" />
|
tools:ignore="RtlHardcoded" />
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageButton
|
<androidx.appcompat.widget.AppCompatImageButton
|
||||||
android:id="@+id/fullScreenButton"
|
android:id="@+id/fullScreenButton"
|
||||||
android:layout_width="40dp"
|
android:layout_width="40dp"
|
||||||
android:layout_height="40dp"
|
android:layout_height="40dp"
|
||||||
android:padding="@dimen/player_main_buttons_padding"
|
android:padding="@dimen/player_main_buttons_padding"
|
||||||
android:layout_alignParentRight="true"
|
|
||||||
android:background="?attr/selectableItemBackground"
|
android:background="?attr/selectableItemBackground"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
|
@ -326,6 +325,10 @@
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
tools:visibility="visible"/>
|
tools:visibility="visible"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/bottomControls"
|
android:id="@+id/bottomControls"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -353,10 +356,10 @@
|
||||||
android:id="@+id/playbackSeekBar"
|
android:id="@+id/playbackSeekBar"
|
||||||
style="@style/Widget.AppCompat.SeekBar"
|
style="@style/Widget.AppCompat.SeekBar"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:paddingBottom="4dp"
|
android:layout_marginTop="2dp"
|
||||||
android:paddingTop="8dp"
|
|
||||||
tools:progress="25"
|
tools:progress="25"
|
||||||
android:nextFocusDown="@id/screenRotationButton"
|
android:nextFocusDown="@id/screenRotationButton"
|
||||||
tools:secondaryProgress="50"/>
|
tools:secondaryProgress="50"/>
|
||||||
|
@ -465,21 +468,20 @@
|
||||||
android:layout_height="60dp"
|
android:layout_height="60dp"
|
||||||
android:id="@+id/playQueueControl">
|
android:id="@+id/playQueueControl">
|
||||||
|
|
||||||
<ImageButton
|
<androidx.appcompat.widget.AppCompatImageButton
|
||||||
android:id="@+id/playQueueClose"
|
android:id="@+id/playQueueClose"
|
||||||
android:layout_width="50dp"
|
android:layout_width="50dp"
|
||||||
android:layout_height="50dp"
|
android:layout_height="50dp"
|
||||||
android:layout_centerVertical="true"
|
|
||||||
android:layout_alignParentEnd="true"
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
android:layout_marginEnd="40dp"
|
android:layout_marginEnd="40dp"
|
||||||
android:padding="10dp"
|
|
||||||
android:clickable="true"
|
|
||||||
android:focusable="true"
|
|
||||||
android:scaleType="fitXY"
|
|
||||||
android:tint="?attr/colorAccent"
|
|
||||||
android:src="@drawable/ic_close_white_24dp"
|
|
||||||
android:background="?android:selectableItemBackground"
|
android:background="?android:selectableItemBackground"
|
||||||
tools:ignore="ContentDescription"/>
|
android:clickable="true"
|
||||||
|
android:contentDescription="@string/close"
|
||||||
|
android:focusable="true"
|
||||||
|
android:padding="10dp"
|
||||||
|
android:scaleType="fitXY"
|
||||||
|
app:srcCompat="@drawable/ic_close_white_24dp" />
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/repeatButton"
|
android:id="@+id/repeatButton"
|
||||||
|
|
|
@ -4,8 +4,7 @@
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:id="@+id/drawer_layout"
|
android:id="@+id/drawer_layout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent">
|
||||||
android:fitsSystemWindows="true">
|
|
||||||
|
|
||||||
<org.schabi.newpipe.views.FocusAwareCoordinator
|
<org.schabi.newpipe.views.FocusAwareCoordinator
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|
|
@ -12,6 +12,6 @@
|
||||||
android:inputType="text"
|
android:inputType="text"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:layout_margin="10dp"
|
android:layout_margin="10dp"
|
||||||
android:hint="@string/playlist_name_input"/>
|
android:hint="@string/name"/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
|
@ -38,7 +38,7 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="8dp"
|
android:layout_marginStart="8dp"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:hint="@string/feed_group_dialog_name_input"
|
android:hint="@string/name"
|
||||||
android:paddingTop="6dp"
|
android:paddingTop="6dp"
|
||||||
android:paddingBottom="6dp"
|
android:paddingBottom="6dp"
|
||||||
app:errorEnabled="true"
|
app:errorEnabled="true"
|
||||||
|
|
|
@ -16,6 +16,6 @@
|
||||||
android:layout_marginRight="10dp"
|
android:layout_marginRight="10dp"
|
||||||
android:saveEnabled="true"
|
android:saveEnabled="true"
|
||||||
android:inputType="text"
|
android:inputType="text"
|
||||||
android:hint="@string/playlist_name_input"
|
android:hint="@string/name"
|
||||||
android:maxLines="1"/>
|
android:maxLines="1"/>
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
|
@ -18,11 +18,10 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="@android:color/transparent"
|
android:background="@android:color/transparent"
|
||||||
android:fitsSystemWindows="true"
|
|
||||||
app:elevation="0dp"
|
app:elevation="0dp"
|
||||||
app:layout_behavior="com.google.android.material.appbar.FlingBehavior">
|
app:layout_behavior="com.google.android.material.appbar.FlingBehavior">
|
||||||
|
|
||||||
<com.google.android.material.appbar.CollapsingToolbarLayout
|
<org.schabi.newpipe.views.CustomCollapsingToolbarLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:layout_scrollFlags="scroll">
|
app:layout_scrollFlags="scroll">
|
||||||
|
@ -148,8 +147,7 @@
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
</org.schabi.newpipe.views.CustomCollapsingToolbarLayout>
|
||||||
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
|
||||||
|
|
||||||
<!-- CONTENT -->
|
<!-- CONTENT -->
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
|
|
|
@ -10,10 +10,8 @@
|
||||||
android:maxLines="2"
|
android:maxLines="2"
|
||||||
android:minHeight="?attr/listPreferredItemHeightSmall"
|
android:minHeight="?attr/listPreferredItemHeightSmall"
|
||||||
android:paddingEnd="?attr/listPreferredItemPaddingRight"
|
android:paddingEnd="?attr/listPreferredItemPaddingRight"
|
||||||
android:paddingLeft="?attr/listPreferredItemPaddingLeft"
|
|
||||||
android:paddingRight="?attr/listPreferredItemPaddingRight"
|
|
||||||
android:paddingStart="?attr/listPreferredItemPaddingLeft"
|
android:paddingStart="?attr/listPreferredItemPaddingLeft"
|
||||||
android:background="?attr/checked_selector"
|
android:background="?attr/checked_selector"
|
||||||
android:textColor="?attr/textColorAlertDialogListItem"
|
android:textColor="?attr/textColorAlertDialogListItem"
|
||||||
tools:drawableLeft="?attr/ic_play"
|
tools:drawableLeft="?attr/ic_play_arrow"
|
||||||
tools:text="Lorem ipsum dolor sit amet" />
|
tools:text="Lorem ipsum dolor sit amet" />
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="@color/black"
|
android:background="@color/black"
|
||||||
|
@ -12,21 +12,37 @@
|
||||||
android:id="@+id/surfaceView"
|
android:id="@+id/surfaceView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_centerInParent="true"/>
|
android:layout_centerInParent="true" />
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:id="@+id/surfaceForeground"
|
android:id="@+id/surfaceForeground"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="@android:color/black"
|
android:layout_alignBottom="@+id/surfaceView"
|
||||||
android:layout_alignBottom="@+id/surfaceView"/>
|
android:background="@android:color/black" />
|
||||||
|
|
||||||
<com.google.android.exoplayer2.ui.SubtitleView
|
<com.google.android.exoplayer2.ui.SubtitleView
|
||||||
android:id="@+id/subtitleView"
|
android:id="@+id/subtitleView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_centerInParent="true"
|
android:layout_centerInParent="true"
|
||||||
android:layout_gravity="center"/>
|
android:layout_gravity="center" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/playerTopShadow"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="30dp"
|
||||||
|
android:layout_alignParentTop="true"
|
||||||
|
android:background="@drawable/player_controls_top_background"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/playerBottomShadow"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="30dp"
|
||||||
|
android:layout_alignParentBottom="true"
|
||||||
|
android:background="@drawable/player_controls_background"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/endScreen"
|
android:id="@+id/endScreen"
|
||||||
|
@ -36,171 +52,159 @@
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
tools:background="@android:color/white"
|
tools:background="@android:color/white"
|
||||||
tools:ignore="ContentDescription"
|
tools:ignore="ContentDescription"
|
||||||
tools:visibility="visible"/>
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:id="@+id/playbackControlRoot"
|
android:id="@+id/playbackControlRoot"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="@color/video_overlay_color"
|
android:background="@color/video_overlay_color"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
tools:visibility="visible">
|
tools:visibility="visible">
|
||||||
|
|
||||||
<View
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="30dp"
|
|
||||||
android:background="@drawable/player_controls_top_background"
|
|
||||||
android:layout_alignParentTop="true" />
|
|
||||||
|
|
||||||
<View
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="30dp"
|
|
||||||
android:background="@drawable/player_controls_background"
|
|
||||||
android:layout_alignParentBottom="true" />
|
|
||||||
|
|
||||||
<!-- All top controls in this layout -->
|
<!-- All top controls in this layout -->
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:id="@+id/playbackWindowRoot"
|
android:id="@+id/playbackWindowRoot"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent">
|
||||||
android:fitsSystemWindows="true">
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/topControls"
|
android:id="@+id/topControls"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignParentTop="true"
|
android:layout_alignParentTop="true"
|
||||||
android:orientation="vertical"
|
android:baselineAligned="false"
|
||||||
android:gravity="top"
|
android:gravity="top"
|
||||||
android:paddingTop="@dimen/player_main_top_padding"
|
android:orientation="vertical"
|
||||||
android:paddingStart="@dimen/player_main_controls_padding"
|
android:paddingStart="@dimen/player_main_controls_padding"
|
||||||
android:paddingEnd="@dimen/player_main_controls_padding"
|
android:paddingTop="@dimen/player_main_top_padding"
|
||||||
android:baselineAligned="false">
|
android:paddingEnd="@dimen/player_main_controls_padding">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/primaryControls"
|
android:id="@+id/primaryControls"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:minHeight="45dp"
|
|
||||||
android:baselineAligned="false"
|
android:baselineAligned="false"
|
||||||
android:gravity="top"
|
android:gravity="top"
|
||||||
|
android:minHeight="45dp"
|
||||||
tools:ignore="RtlHardcoded">
|
tools:ignore="RtlHardcoded">
|
||||||
|
|
||||||
<ImageButton
|
<androidx.appcompat.widget.AppCompatImageButton
|
||||||
android:id="@+id/playerCloseButton"
|
android:id="@+id/playerCloseButton"
|
||||||
android:layout_width="36dp"
|
android:layout_width="36dp"
|
||||||
android:layout_height="36dp"
|
android:layout_height="36dp"
|
||||||
android:padding="@dimen/player_main_buttons_padding"
|
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="8dp"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
|
android:padding="@dimen/player_main_buttons_padding"
|
||||||
android:scaleType="fitXY"
|
android:scaleType="fitXY"
|
||||||
android:src="?attr/ic_close"
|
android:visibility="gone"
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
app:srcCompat="?attr/ic_close"
|
||||||
tools:ignore="ContentDescription,RtlHardcoded"
|
tools:ignore="ContentDescription,RtlHardcoded" />
|
||||||
android:visibility="gone" />
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/metadataView"
|
android:id="@+id/metadataView"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="top"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_marginTop="6dp"
|
android:layout_marginTop="6dp"
|
||||||
android:layout_marginRight="8dp"
|
android:layout_marginRight="8dp"
|
||||||
tools:ignore="RtlHardcoded"
|
android:layout_weight="1"
|
||||||
android:layout_weight="1">
|
android:gravity="top"
|
||||||
|
android:orientation="vertical"
|
||||||
|
tools:ignore="RtlHardcoded">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/titleTextView"
|
android:id="@+id/titleTextView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:clickable="true"
|
||||||
android:ellipsize="marquee"
|
android:ellipsize="marquee"
|
||||||
android:fadingEdge="horizontal"
|
android:fadingEdge="horizontal"
|
||||||
|
android:focusable="true"
|
||||||
android:marqueeRepeatLimit="marquee_forever"
|
android:marqueeRepeatLimit="marquee_forever"
|
||||||
android:scrollHorizontally="true"
|
android:scrollHorizontally="true"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:textColor="@android:color/white"
|
android:textColor="@android:color/white"
|
||||||
android:textSize="15sp"
|
android:textSize="15sp"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
android:clickable="true"
|
|
||||||
android:focusable="true"
|
|
||||||
tools:ignore="RtlHardcoded"
|
tools:ignore="RtlHardcoded"
|
||||||
tools:text="The Video Title LONG very LONG"/>
|
tools:text="The Video Title LONG very LONG" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/channelTextView"
|
android:id="@+id/channelTextView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:clickable="true"
|
||||||
android:ellipsize="marquee"
|
android:ellipsize="marquee"
|
||||||
android:fadingEdge="horizontal"
|
android:fadingEdge="horizontal"
|
||||||
|
android:focusable="true"
|
||||||
android:marqueeRepeatLimit="marquee_forever"
|
android:marqueeRepeatLimit="marquee_forever"
|
||||||
android:scrollHorizontally="true"
|
android:scrollHorizontally="true"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:textColor="@android:color/white"
|
android:textColor="@android:color/white"
|
||||||
android:textSize="12sp"
|
android:textSize="12sp"
|
||||||
android:clickable="true"
|
tools:text="The Video Artist LONG very LONG very Long" />
|
||||||
android:focusable="true"
|
|
||||||
tools:text="The Video Artist LONG very LONG very Long"/>
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/qualityTextView"
|
android:id="@+id/qualityTextView"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="35dp"
|
android:layout_height="35dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:background="?attr/selectableItemBackground"
|
||||||
|
android:gravity="center"
|
||||||
android:minWidth="0dp"
|
android:minWidth="0dp"
|
||||||
android:padding="@dimen/player_main_buttons_padding"
|
android:padding="@dimen/player_main_buttons_padding"
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:gravity="center"
|
|
||||||
android:text="720p"
|
android:text="720p"
|
||||||
android:textColor="@android:color/white"
|
android:textColor="@android:color/white"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
android:background="?attr/selectableItemBackground"
|
|
||||||
android:visibility="visible"
|
android:visibility="visible"
|
||||||
tools:ignore="HardcodedText,RtlHardcoded"/>
|
tools:ignore="HardcodedText,RtlHardcoded" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/playbackSpeed"
|
android:id="@+id/playbackSpeed"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="35dp"
|
android:layout_height="35dp"
|
||||||
android:padding="@dimen/player_main_buttons_padding"
|
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="8dp"
|
||||||
|
android:background="?attr/selectableItemBackground"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:minWidth="0dp"
|
android:minWidth="0dp"
|
||||||
|
android:padding="@dimen/player_main_buttons_padding"
|
||||||
android:textColor="@android:color/white"
|
android:textColor="@android:color/white"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
android:background="?attr/selectableItemBackground"
|
|
||||||
tools:ignore="RtlHardcoded,RtlSymmetry"
|
tools:ignore="RtlHardcoded,RtlSymmetry"
|
||||||
tools:text="1x"/>
|
tools:text="1x" />
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageButton
|
<androidx.appcompat.widget.AppCompatImageButton
|
||||||
android:id="@+id/queueButton"
|
android:id="@+id/queueButton"
|
||||||
android:layout_width="35dp"
|
android:layout_width="35dp"
|
||||||
android:layout_height="35dp"
|
android:layout_height="35dp"
|
||||||
android:paddingTop="5dp"
|
|
||||||
android:paddingStart="3dp"
|
|
||||||
android:paddingEnd="3dp"
|
|
||||||
android:paddingBottom="3dp"
|
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="8dp"
|
||||||
|
android:background="?attr/selectableItemBackground"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
|
android:paddingStart="3dp"
|
||||||
|
android:paddingTop="5dp"
|
||||||
|
android:paddingEnd="3dp"
|
||||||
|
android:paddingBottom="3dp"
|
||||||
android:scaleType="fitCenter"
|
android:scaleType="fitCenter"
|
||||||
|
android:visibility="gone"
|
||||||
app:srcCompat="@drawable/ic_list_white_24dp"
|
app:srcCompat="@drawable/ic_list_white_24dp"
|
||||||
android:background="?attr/selectableItemBackground"
|
tools:ignore="ContentDescription,RtlHardcoded" />
|
||||||
tools:ignore="ContentDescription,RtlHardcoded"
|
|
||||||
android:visibility="gone"/>
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageButton
|
<androidx.appcompat.widget.AppCompatImageButton
|
||||||
android:id="@+id/moreOptionsButton"
|
android:id="@+id/moreOptionsButton"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:padding="@dimen/player_main_buttons_padding"
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
|
android:padding="@dimen/player_main_buttons_padding"
|
||||||
android:scaleType="fitXY"
|
android:scaleType="fitXY"
|
||||||
app:srcCompat="@drawable/ic_expand_more_white_24dp"
|
app:srcCompat="@drawable/ic_expand_more_white_24dp"
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
tools:ignore="ContentDescription,RtlHardcoded" />
|
||||||
tools:ignore="ContentDescription,RtlHardcoded"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
@ -223,32 +227,39 @@
|
||||||
android:id="@+id/resizeTextView"
|
android:id="@+id/resizeTextView"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="35dp"
|
android:layout_height="35dp"
|
||||||
android:padding="@dimen/player_main_buttons_padding"
|
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="8dp"
|
||||||
|
android:background="?attr/selectableItemBackground"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:minWidth="50dp"
|
android:minWidth="50dp"
|
||||||
|
android:padding="@dimen/player_main_buttons_padding"
|
||||||
android:textColor="@android:color/white"
|
android:textColor="@android:color/white"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
android:background="?attr/selectableItemBackground"
|
|
||||||
tools:ignore="HardcodedText,RtlHardcoded"
|
tools:ignore="HardcodedText,RtlHardcoded"
|
||||||
tools:text="FIT"/>
|
tools:text="FIT" />
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="3">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/captionTextView"
|
android:id="@+id/captionTextView"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:padding="@dimen/player_main_buttons_padding"
|
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="8dp"
|
||||||
android:gravity="center|left"
|
android:background="?attr/selectableItemBackground"
|
||||||
android:minHeight="35dp"
|
|
||||||
android:lines="1"
|
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
|
android:gravity="center|left"
|
||||||
|
android:lines="1"
|
||||||
android:minWidth="50dp"
|
android:minWidth="50dp"
|
||||||
|
android:minHeight="35dp"
|
||||||
|
android:padding="@dimen/player_main_buttons_padding"
|
||||||
android:textColor="@android:color/white"
|
android:textColor="@android:color/white"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
android:background="?attr/selectableItemBackground"
|
|
||||||
tools:ignore="RelativeOverlap,RtlHardcoded"
|
tools:ignore="RelativeOverlap,RtlHardcoded"
|
||||||
tools:text="English"/>
|
tools:text="English" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
@ -261,100 +272,99 @@
|
||||||
android:id="@+id/playWithKodi"
|
android:id="@+id/playWithKodi"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="35dp"
|
android:layout_height="35dp"
|
||||||
android:padding="@dimen/player_main_buttons_padding"
|
android:layout_weight="1"
|
||||||
|
android:background="?attr/selectableItemBackground"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
|
android:contentDescription="@string/play_with_kodi_title"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
|
android:padding="@dimen/player_main_buttons_padding"
|
||||||
android:scaleType="center"
|
android:scaleType="center"
|
||||||
app:srcCompat="@drawable/ic_cast_white_24dp"
|
app:srcCompat="@drawable/ic_cast_white_24dp"
|
||||||
android:background="?attr/selectableItemBackground"
|
tools:ignore="RtlHardcoded" />
|
||||||
android:contentDescription="@string/play_with_kodi_title"
|
|
||||||
tools:ignore="RtlHardcoded"
|
|
||||||
android:layout_weight="1"/>
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageButton
|
<androidx.appcompat.widget.AppCompatImageButton
|
||||||
android:id="@+id/openInBrowser"
|
android:id="@+id/openInBrowser"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="35dp"
|
android:layout_height="35dp"
|
||||||
android:padding="@dimen/player_main_buttons_padding"
|
android:layout_weight="1"
|
||||||
|
android:background="?attr/selectableItemBackground"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
|
android:contentDescription="@string/open_in_browser"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
|
android:padding="@dimen/player_main_buttons_padding"
|
||||||
android:scaleType="center"
|
android:scaleType="center"
|
||||||
app:srcCompat="@drawable/ic_language_white_24dp"
|
app:srcCompat="@drawable/ic_language_white_24dp"
|
||||||
android:background="?attr/selectableItemBackground"
|
tools:ignore="RtlHardcoded" />
|
||||||
android:contentDescription="@string/open_in_browser"
|
|
||||||
tools:ignore="RtlHardcoded"
|
|
||||||
android:layout_weight="1"/>
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageButton
|
<androidx.appcompat.widget.AppCompatImageButton
|
||||||
android:id="@+id/share"
|
android:id="@+id/share"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="35dp"
|
android:layout_height="35dp"
|
||||||
android:padding="@dimen/player_main_buttons_padding"
|
android:layout_weight="1"
|
||||||
|
android:background="?attr/selectableItemBackground"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
|
android:contentDescription="@string/share"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
|
android:padding="@dimen/player_main_buttons_padding"
|
||||||
android:scaleType="center"
|
android:scaleType="center"
|
||||||
app:srcCompat="@drawable/ic_share_white_24dp"
|
app:srcCompat="@drawable/ic_share_white_24dp"
|
||||||
android:background="?attr/selectableItemBackground"
|
tools:ignore="RtlHardcoded" />
|
||||||
android:contentDescription="@string/share"
|
|
||||||
tools:ignore="RtlHardcoded"
|
|
||||||
android:layout_weight="1"/>
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageButton
|
<androidx.appcompat.widget.AppCompatImageButton
|
||||||
android:id="@+id/switchMute"
|
android:id="@+id/switchMute"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="35dp"
|
android:layout_height="35dp"
|
||||||
android:padding="@dimen/player_main_buttons_padding"
|
android:layout_weight="1"
|
||||||
|
android:background="?attr/selectableItemBackground"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
|
android:contentDescription="@string/mute"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
|
android:padding="@dimen/player_main_buttons_padding"
|
||||||
android:scaleType="center"
|
android:scaleType="center"
|
||||||
app:srcCompat="@drawable/ic_volume_off_white_24dp"
|
app:srcCompat="@drawable/ic_volume_off_white_24dp"
|
||||||
android:background="?attr/selectableItemBackground"
|
tools:ignore="RtlHardcoded" />
|
||||||
android:contentDescription="@string/mute"
|
|
||||||
tools:ignore="RtlHardcoded"
|
|
||||||
android:layout_weight="1"/>
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageButton
|
<androidx.appcompat.widget.AppCompatImageButton
|
||||||
android:id="@+id/switchSponsorBlocking"
|
android:id="@+id/switchSponsorBlocking"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="35dp"
|
android:layout_height="35dp"
|
||||||
android:padding="@dimen/player_main_buttons_padding"
|
android:layout_weight="1"
|
||||||
|
android:background="?attr/selectableItemBackground"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
|
android:contentDescription="@string/sponsor_block_toggle_skipping"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
|
android:padding="@dimen/player_main_buttons_padding"
|
||||||
android:scaleType="center"
|
android:scaleType="center"
|
||||||
app:srcCompat="@drawable/ic_sponsor_block_disable_white_24dp"
|
app:srcCompat="@drawable/ic_sponsor_block_disable_white_24dp"
|
||||||
android:background="?attr/selectableItemBackground"
|
tools:ignore="RtlHardcoded" />
|
||||||
android:contentDescription="@string/sponsor_block_toggle_skipping"
|
|
||||||
tools:ignore="RtlHardcoded"
|
|
||||||
android:layout_weight="1"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageButton
|
<androidx.appcompat.widget.AppCompatImageButton
|
||||||
android:id="@+id/fullScreenButton"
|
android:id="@+id/fullScreenButton"
|
||||||
android:layout_width="40dp"
|
android:layout_width="40dp"
|
||||||
android:layout_height="40dp"
|
android:layout_height="40dp"
|
||||||
android:padding="@dimen/player_main_buttons_padding"
|
|
||||||
android:layout_alignParentRight="true"
|
|
||||||
android:background="?attr/selectableItemBackground"
|
android:background="?attr/selectableItemBackground"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
|
android:padding="@dimen/player_main_buttons_padding"
|
||||||
android:scaleType="fitCenter"
|
android:scaleType="fitCenter"
|
||||||
|
android:visibility="gone"
|
||||||
app:srcCompat="@drawable/ic_fullscreen_white_24dp"
|
app:srcCompat="@drawable/ic_fullscreen_white_24dp"
|
||||||
tools:ignore="ContentDescription,RtlHardcoded"
|
tools:ignore="ContentDescription,RtlHardcoded"
|
||||||
android:visibility="gone"
|
tools:visibility="visible" />
|
||||||
tools:visibility="visible"/>
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/bottomControls"
|
android:id="@+id/bottomControls"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:minHeight="40dp"
|
|
||||||
android:layout_alignParentBottom="true"
|
android:layout_alignParentBottom="true"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
|
android:minHeight="40dp"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:paddingLeft="@dimen/player_main_controls_padding"
|
android:paddingLeft="@dimen/player_main_controls_padding"
|
||||||
android:paddingRight="@dimen/player_main_controls_padding">
|
android:paddingRight="@dimen/player_main_controls_padding">
|
||||||
|
@ -368,19 +378,18 @@
|
||||||
android:text="-:--:--"
|
android:text="-:--:--"
|
||||||
android:textColor="@android:color/white"
|
android:textColor="@android:color/white"
|
||||||
tools:ignore="HardcodedText"
|
tools:ignore="HardcodedText"
|
||||||
tools:text="1:06:29"/>
|
tools:text="1:06:29" />
|
||||||
|
|
||||||
|
|
||||||
<org.schabi.newpipe.views.MarkableSeekBar
|
<org.schabi.newpipe.views.MarkableSeekBar
|
||||||
android:id="@+id/playbackSeekBar"
|
android:id="@+id/playbackSeekBar"
|
||||||
style="@style/Widget.AppCompat.SeekBar"
|
style="@style/Widget.AppCompat.SeekBar"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_marginTop="2dp"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:paddingBottom="4dp"
|
|
||||||
android:paddingTop="8dp"
|
|
||||||
tools:progress="25"
|
tools:progress="25"
|
||||||
tools:secondaryProgress="50"/>
|
tools:secondaryProgress="50" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/playbackEndTime"
|
android:id="@+id/playbackEndTime"
|
||||||
|
@ -390,20 +399,20 @@
|
||||||
android:text="-:--:--"
|
android:text="-:--:--"
|
||||||
android:textColor="@android:color/white"
|
android:textColor="@android:color/white"
|
||||||
tools:ignore="HardcodedText"
|
tools:ignore="HardcodedText"
|
||||||
tools:text="1:23:49"/>
|
tools:text="1:23:49" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/playbackLiveSync"
|
android:id="@+id/playbackLiveSync"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
android:background="?attr/selectableItemBackground"
|
||||||
|
android:gravity="center"
|
||||||
android:paddingLeft="4dp"
|
android:paddingLeft="4dp"
|
||||||
android:paddingRight="4dp"
|
android:paddingRight="4dp"
|
||||||
android:gravity="center"
|
|
||||||
android:text="@string/duration_live"
|
android:text="@string/duration_live"
|
||||||
android:textAllCaps="true"
|
android:textAllCaps="true"
|
||||||
android:textColor="@android:color/white"
|
android:textColor="@android:color/white"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
android:background="?attr/selectableItemBackground"
|
|
||||||
tools:ignore="HardcodedText,RtlHardcoded,RtlSymmetry" />
|
tools:ignore="HardcodedText,RtlHardcoded,RtlSymmetry" />
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageButton
|
<androidx.appcompat.widget.AppCompatImageButton
|
||||||
|
@ -411,15 +420,15 @@
|
||||||
android:layout_width="40dp"
|
android:layout_width="40dp"
|
||||||
android:layout_height="40dp"
|
android:layout_height="40dp"
|
||||||
android:layout_marginStart="4dp"
|
android:layout_marginStart="4dp"
|
||||||
android:padding="@dimen/player_main_buttons_padding"
|
|
||||||
android:background="?attr/selectableItemBackground"
|
android:background="?attr/selectableItemBackground"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
|
android:padding="@dimen/player_main_buttons_padding"
|
||||||
android:scaleType="fitCenter"
|
android:scaleType="fitCenter"
|
||||||
|
android:visibility="gone"
|
||||||
app:srcCompat="@drawable/ic_fullscreen_white_24dp"
|
app:srcCompat="@drawable/ic_fullscreen_white_24dp"
|
||||||
tools:ignore="ContentDescription,RtlHardcoded"
|
tools:ignore="ContentDescription,RtlHardcoded"
|
||||||
android:visibility="gone"
|
tools:visibility="visible" />
|
||||||
tools:visibility="visible"/>
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
|
@ -434,14 +443,14 @@
|
||||||
android:id="@+id/playPreviousButton"
|
android:id="@+id/playPreviousButton"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="40dp"
|
android:layout_height="40dp"
|
||||||
android:layout_weight="1"
|
|
||||||
android:layout_marginEnd="10dp"
|
android:layout_marginEnd="10dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
|
||||||
android:scaleType="fitCenter"
|
android:scaleType="fitCenter"
|
||||||
app:srcCompat="@drawable/ic_previous_white_24dp"
|
app:srcCompat="@drawable/ic_previous_white_24dp"
|
||||||
tools:ignore="ContentDescription"/>
|
tools:ignore="ContentDescription" />
|
||||||
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageButton
|
<androidx.appcompat.widget.AppCompatImageButton
|
||||||
|
@ -452,20 +461,20 @@
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
android:scaleType="fitCenter"
|
android:scaleType="fitCenter"
|
||||||
app:srcCompat="@drawable/ic_pause_white_24dp"
|
app:srcCompat="@drawable/ic_pause_white_24dp"
|
||||||
tools:ignore="ContentDescription"/>
|
tools:ignore="ContentDescription" />
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageButton
|
<androidx.appcompat.widget.AppCompatImageButton
|
||||||
android:id="@+id/playNextButton"
|
android:id="@+id/playNextButton"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="40dp"
|
android:layout_height="40dp"
|
||||||
android:layout_weight="1"
|
|
||||||
android:layout_marginStart="10dp"
|
android:layout_marginStart="10dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
|
||||||
android:scaleType="fitCenter"
|
android:scaleType="fitCenter"
|
||||||
app:srcCompat="@drawable/ic_next_white_24dp"
|
app:srcCompat="@drawable/ic_next_white_24dp"
|
||||||
tools:ignore="ContentDescription"/>
|
tools:ignore="ContentDescription" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
@ -475,48 +484,47 @@
|
||||||
android:id="@+id/playQueuePanel"
|
android:id="@+id/playQueuePanel"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:visibility="gone"
|
|
||||||
android:background="?attr/queue_background_color"
|
android:background="?attr/queue_background_color"
|
||||||
|
android:visibility="gone"
|
||||||
tools:visibility="visible">
|
tools:visibility="visible">
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
|
android:id="@+id/playQueueControl"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="60dp"
|
android:layout_height="60dp">
|
||||||
android:id="@+id/playQueueControl">
|
|
||||||
|
|
||||||
<ImageButton
|
<androidx.appcompat.widget.AppCompatImageButton
|
||||||
android:id="@+id/playQueueClose"
|
android:id="@+id/playQueueClose"
|
||||||
android:layout_width="50dp"
|
android:layout_width="50dp"
|
||||||
android:layout_height="50dp"
|
android:layout_height="50dp"
|
||||||
android:layout_centerVertical="true"
|
|
||||||
android:layout_alignParentEnd="true"
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
android:layout_marginEnd="40dp"
|
android:layout_marginEnd="40dp"
|
||||||
android:padding="10dp"
|
|
||||||
android:clickable="true"
|
|
||||||
android:focusable="true"
|
|
||||||
android:scaleType="fitXY"
|
|
||||||
android:tint="?attr/colorAccent"
|
|
||||||
android:src="@drawable/ic_close_white_24dp"
|
|
||||||
android:background="?android:selectableItemBackground"
|
android:background="?android:selectableItemBackground"
|
||||||
tools:ignore="ContentDescription"/>
|
android:clickable="true"
|
||||||
|
android:contentDescription="@string/close"
|
||||||
|
android:focusable="true"
|
||||||
|
android:padding="10dp"
|
||||||
|
android:scaleType="fitXY"
|
||||||
|
app:srcCompat="@drawable/ic_close_white_24dp" />
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/repeatButton"
|
android:id="@+id/repeatButton"
|
||||||
android:layout_width="50dp"
|
android:layout_width="50dp"
|
||||||
android:layout_height="50dp"
|
android:layout_height="50dp"
|
||||||
android:layout_centerVertical="true"
|
|
||||||
android:layout_alignParentLeft="true"
|
|
||||||
android:layout_alignParentStart="true"
|
android:layout_alignParentStart="true"
|
||||||
android:layout_marginLeft="40dp"
|
android:layout_alignParentLeft="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
android:layout_marginStart="40dp"
|
android:layout_marginStart="40dp"
|
||||||
android:padding="10dp"
|
android:layout_marginLeft="40dp"
|
||||||
|
android:background="?android:selectableItemBackground"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
|
android:padding="10dp"
|
||||||
android:scaleType="fitXY"
|
android:scaleType="fitXY"
|
||||||
android:tint="?attr/colorAccent"
|
|
||||||
android:src="@drawable/exo_controls_repeat_off"
|
android:src="@drawable/exo_controls_repeat_off"
|
||||||
android:background="?android:selectableItemBackground"
|
android:tint="?attr/colorAccent"
|
||||||
tools:ignore="ContentDescription,RtlHardcoded"/>
|
tools:ignore="ContentDescription,RtlHardcoded" />
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageButton
|
<androidx.appcompat.widget.AppCompatImageButton
|
||||||
android:id="@+id/shuffleButton"
|
android:id="@+id/shuffleButton"
|
||||||
|
@ -524,14 +532,14 @@
|
||||||
android:layout_height="50dp"
|
android:layout_height="50dp"
|
||||||
android:layout_centerVertical="true"
|
android:layout_centerVertical="true"
|
||||||
android:layout_toRightOf="@id/repeatButton"
|
android:layout_toRightOf="@id/repeatButton"
|
||||||
android:padding="10dp"
|
android:background="?android:selectableItemBackground"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
|
android:padding="10dp"
|
||||||
android:scaleType="fitXY"
|
android:scaleType="fitXY"
|
||||||
android:tint="?attr/colorAccent"
|
android:tint="?attr/colorAccent"
|
||||||
app:srcCompat="@drawable/ic_shuffle_white_24dp"
|
app:srcCompat="@drawable/ic_shuffle_white_24dp"
|
||||||
android:background="?android:selectableItemBackground"
|
tools:ignore="ContentDescription,RtlHardcoded" />
|
||||||
tools:ignore="ContentDescription,RtlHardcoded"/>
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
@ -541,7 +549,7 @@
|
||||||
android:layout_below="@id/playQueueControl"
|
android:layout_below="@id/playQueueControl"
|
||||||
android:scrollbars="vertical"
|
android:scrollbars="vertical"
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
tools:listitem="@layout/play_queue_item"/>
|
tools:listitem="@layout/play_queue_item" />
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
|
@ -555,8 +563,8 @@
|
||||||
android:id="@+id/controlAnimationView"
|
android:id="@+id/controlAnimationView"
|
||||||
android:layout_width="100dp"
|
android:layout_width="100dp"
|
||||||
android:layout_height="100dp"
|
android:layout_height="100dp"
|
||||||
android:padding="15dp"
|
|
||||||
android:background="@drawable/background_oval_black_transparent"
|
android:background="@drawable/background_oval_black_transparent"
|
||||||
|
android:padding="15dp"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
tools:ignore="ContentDescription"
|
tools:ignore="ContentDescription"
|
||||||
tools:src="@drawable/ic_fast_rewind_white_24dp"
|
tools:src="@drawable/ic_fast_rewind_white_24dp"
|
||||||
|
@ -577,7 +585,7 @@
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_centerInParent="true"
|
android:layout_centerInParent="true"
|
||||||
android:indeterminate="true"/>
|
android:indeterminate="true" />
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
|
@ -645,10 +653,10 @@
|
||||||
android:layout_centerInParent="true"
|
android:layout_centerInParent="true"
|
||||||
android:layout_marginBottom="58dp"
|
android:layout_marginBottom="58dp"
|
||||||
android:background="#64000000"
|
android:background="#64000000"
|
||||||
android:paddingBottom="10dp"
|
|
||||||
android:paddingLeft="30dp"
|
android:paddingLeft="30dp"
|
||||||
android:paddingRight="30dp"
|
|
||||||
android:paddingTop="10dp"
|
android:paddingTop="10dp"
|
||||||
|
android:paddingRight="30dp"
|
||||||
|
android:paddingBottom="10dp"
|
||||||
android:textColor="@android:color/white"
|
android:textColor="@android:color/white"
|
||||||
android:textSize="26sp"
|
android:textSize="26sp"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="64dp"
|
android:layout_height="64dp"
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/notificationContent"
|
android:id="@+id/notificationContent"
|
||||||
|
@ -107,8 +107,8 @@
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:padding="5dp"
|
android:padding="5dp"
|
||||||
android:scaleType="fitCenter"
|
android:scaleType="fitCenter"
|
||||||
android:src="@drawable/ic_close_white_24dp"
|
app:srcCompat="@drawable/ic_close_white_24dp"
|
||||||
tools:ignore="ContentDescription,RtlHardcoded"/>
|
tools:ignore="ContentDescription,RtlHardcoded" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
|
|
|
@ -29,8 +29,8 @@
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:padding="8dp"
|
android:padding="8dp"
|
||||||
android:scaleType="fitCenter"
|
android:scaleType="fitCenter"
|
||||||
android:src="@drawable/ic_close_white_24dp"
|
app:srcCompat="@drawable/ic_close_white_24dp"
|
||||||
tools:ignore="ContentDescription,RtlHardcoded"/>
|
tools:ignore="ContentDescription,RtlHardcoded" />
|
||||||
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<FrameLayout
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
@ -11,8 +10,8 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="bottom|center_horizontal"
|
android:layout_gravity="bottom|center_horizontal"
|
||||||
android:layout_marginBottom="24dp"
|
android:layout_marginBottom="24dp"
|
||||||
app:srcCompat="@drawable/ic_close_white_24dp"
|
|
||||||
app:backgroundTint="@color/light_youtube_primary_color"
|
app:backgroundTint="@color/light_youtube_primary_color"
|
||||||
app:borderWidth="0dp"
|
app:borderWidth="0dp"
|
||||||
app:fabSize="normal"/>
|
app:fabSize="normal"
|
||||||
|
app:srcCompat="@drawable/ic_close_white_24dp" />
|
||||||
</FrameLayout>
|
</FrameLayout>
|
|
@ -12,7 +12,7 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginLeft="12dp"
|
android:layout_marginLeft="12dp"
|
||||||
android:layout_alignBaseline="@+id/autoplay_switch"
|
android:layout_alignBaseline="@+id/autoplay_switch"
|
||||||
android:text="@string/next_video_title"
|
android:text="@string/exo_controls_next_description"
|
||||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||||
android:textSize="12sp"
|
android:textSize="12sp"
|
||||||
tools:ignore="RtlHardcoded" />
|
tools:ignore="RtlHardcoded" />
|
||||||
|
|
135
app/src/main/res/layout/settings_notification.xml
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="match_parent">
|
||||||
|
|
||||||
|
<Switch
|
||||||
|
android:id="@+id/notificationScaleSwitch"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:clickable="false"
|
||||||
|
android:focusable="false"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/textView2"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/textView" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:text="@string/notification_scale_to_square_image_title"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||||
|
android:textSize="14sp"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/notificationScaleSwitch"
|
||||||
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textView2"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:text="@string/notification_scale_to_square_image_summary"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/notificationScaleSwitch"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/textView" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/notificationScaleSwitchClickableArea"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/divider"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
android:background="?android:selectableItemBackground" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/divider"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:layout_marginStart="32dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginEnd="32dp"
|
||||||
|
android:background="?android:attr/listDivider"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/textView2" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textView4"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:clickable="false"
|
||||||
|
android:focusable="false"
|
||||||
|
android:gravity="center"
|
||||||
|
android:lines="4"
|
||||||
|
android:text="@string/notification_actions_summary"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/divider" />
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/notificationAction0"
|
||||||
|
layout="@layout/settings_notification_action"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/textView4" />
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/notificationAction1"
|
||||||
|
layout="@layout/settings_notification_action"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/notificationAction0" />
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/notificationAction2"
|
||||||
|
layout="@layout/settings_notification_action"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/notificationAction1" />
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/notificationAction3"
|
||||||
|
layout="@layout/settings_notification_action"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/notificationAction2" />
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/notificationAction4"
|
||||||
|
layout="@layout/settings_notification_action"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/notificationAction3" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</ScrollView>
|
88
app/src/main/res/layout/settings_notification_action.xml
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?android:selectableItemBackground">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
|
android:id="@+id/notificationActionIcon"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_marginStart="12dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
app:tint="?android:textColorPrimary"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintDimensionRatio="H,1:1"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:ignore="ContentDescription"
|
||||||
|
tools:src="@drawable/ic_previous_white_24dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/notificationActionTitle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||||
|
android:textSize="14sp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/notificationActionSummary"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/notificationActionClickableArea"
|
||||||
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/notificationActionIcon"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/notificationActionIcon"
|
||||||
|
app:layout_constraintVertical_chainStyle="packed"
|
||||||
|
tools:text="@string/notification_action_1_title" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/notificationActionSummary"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/notificationActionIcon"
|
||||||
|
app:layout_constraintEnd_toEndOf="@+id/notificationActionClickableArea"
|
||||||
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
|
app:layout_constraintStart_toStartOf="@+id/notificationActionTitle"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/notificationActionTitle"
|
||||||
|
tools:text="@string/notification_action_play_pause_buffering_value" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/notificationActionClickableArea"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:background="?android:selectableItemBackground"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/notificationActionCheckBoxClickableArea"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
android:id="@+id/notificationActionCheckBox"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:clickable="false"
|
||||||
|
android:focusable="false"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/notificationActionCheckBoxClickableArea"
|
||||||
|
app:layout_constraintEnd_toEndOf="@+id/notificationActionCheckBoxClickableArea"
|
||||||
|
app:layout_constraintStart_toStartOf="@+id/notificationActionCheckBoxClickableArea"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/notificationActionCheckBoxClickableArea"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintDimensionRatio="H,1:1"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -47,7 +47,7 @@
|
||||||
android:contentDescription="@string/search"
|
android:contentDescription="@string/search"
|
||||||
android:scaleType="fitCenter"
|
android:scaleType="fitCenter"
|
||||||
app:srcCompat="?attr/ic_close"
|
app:srcCompat="?attr/ic_close"
|
||||||
tools:ignore="RtlHardcoded"/>
|
tools:ignore="RtlHardcoded" />
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
|
@ -1,2 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources></resources>
|
<resources>
|
||||||
|
<string name="no_player_found">Geen stream-speler gevind nie. Installeer VLC\?</string>
|
||||||
|
<string name="main_bg_subtitle">Tik op \"Soek\" om aan die gang te kom</string>
|
||||||
|
</resources>
|
|
@ -143,7 +143,7 @@
|
||||||
<string name="error_snackbar_action">أبلِغ</string>
|
<string name="error_snackbar_action">أبلِغ</string>
|
||||||
<string name="what_device_headline">معلومات:</string>
|
<string name="what_device_headline">معلومات:</string>
|
||||||
<string name="what_happened_headline">ماذا حدث:</string>
|
<string name="what_happened_headline">ماذا حدث:</string>
|
||||||
<string name="info_labels">ماذا:\\nطلب:\\nيحتوى اللغة:\\nSالخدمات:\\nتوقيت غرينتش:\\nحزمة:\\nالإصدار:\\nOS إصدار نظام التشغيل:</string>
|
<string name="info_labels">ماذا:\\nطلب:\\nلغة المحتوى:\\nبلد المحتوى:\\nلغة التطبيق:\\nالخدمات:\\nتوقيت غرينتش:\\nالحزمة:\\nالإصدار:\\nOS الإصدار:</string>
|
||||||
<string name="your_comment">تعليقك (باللغة الإنجليزية):</string>
|
<string name="your_comment">تعليقك (باللغة الإنجليزية):</string>
|
||||||
<string name="error_details_headline">التفاصيل:</string>
|
<string name="error_details_headline">التفاصيل:</string>
|
||||||
<string name="report_error">الإبلاغ عن خطأ</string>
|
<string name="report_error">الإبلاغ عن خطأ</string>
|
||||||
|
@ -155,15 +155,15 @@
|
||||||
<string name="storage_permission_denied">ترخيص الوصول إلى التخزين أوّلا</string>
|
<string name="storage_permission_denied">ترخيص الوصول إلى التخزين أوّلا</string>
|
||||||
<string name="short_thousand">ألف</string>
|
<string name="short_thousand">ألف</string>
|
||||||
<string name="short_million">مليون</string>
|
<string name="short_million">مليون</string>
|
||||||
<string name="short_billion">B</string>
|
<string name="short_billion">بليون</string>
|
||||||
<string name="no_subscribers">ليس هناك مشترِكون</string>
|
<string name="no_subscribers">ليس هناك مشترِكون</string>
|
||||||
<plurals name="subscribers">
|
<plurals name="subscribers">
|
||||||
<item quantity="zero">%s لا يوجد مشترك</item>
|
<item quantity="zero">%s لا مشتركين</item>
|
||||||
<item quantity="one">%s مشترك</item>
|
<item quantity="one">%s مشترِك</item>
|
||||||
<item quantity="two">%s المشتركين</item>
|
<item quantity="two">مشتركين</item>
|
||||||
<item quantity="few">%s المشتركين</item>
|
<item quantity="few">%s مشتركين</item>
|
||||||
<item quantity="many">%s المشتركين</item>
|
<item quantity="many">%s مشتركين</item>
|
||||||
<item quantity="other">%s المشتركين</item>
|
<item quantity="other">%s الاشتراكات</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
<string name="no_views">دون مشاهدات</string>
|
<string name="no_views">دون مشاهدات</string>
|
||||||
<string name="no_videos">لاتوجد فيديوهات</string>
|
<string name="no_videos">لاتوجد فيديوهات</string>
|
||||||
|
@ -235,12 +235,12 @@
|
||||||
<string name="title_activity_recaptcha">تحدي الكابتشا</string>
|
<string name="title_activity_recaptcha">تحدي الكابتشا</string>
|
||||||
<string name="hold_to_append">ضغط مطول للإدراج الى قائمة الانتظار</string>
|
<string name="hold_to_append">ضغط مطول للإدراج الى قائمة الانتظار</string>
|
||||||
<plurals name="views">
|
<plurals name="views">
|
||||||
<item quantity="zero">%s بدون مشهادة</item>
|
<item quantity="zero">%s بدون مشاهد</item>
|
||||||
<item quantity="one">%s مشاهدة</item>
|
<item quantity="one">%s مشاهدة</item>
|
||||||
<item quantity="two">%s مشاهدة</item>
|
<item quantity="two">%s مشاهدات</item>
|
||||||
<item quantity="few">%s مشاهدة</item>
|
<item quantity="few">%s مشاهدات</item>
|
||||||
<item quantity="many">%s مشاهدة</item>
|
<item quantity="many">%s مشاهدات</item>
|
||||||
<item quantity="other">%s مشاهدة</item>
|
<item quantity="other">%s مشاهدات</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
<plurals name="videos">
|
<plurals name="videos">
|
||||||
<item quantity="zero">%s فيديو</item>
|
<item quantity="zero">%s فيديو</item>
|
||||||
|
@ -272,7 +272,7 @@
|
||||||
<string name="no_player_found_toast">لم يتم العثور على مشغل دفق (يمكنك تثبيت VLC لتشغيله).</string>
|
<string name="no_player_found_toast">لم يتم العثور على مشغل دفق (يمكنك تثبيت VLC لتشغيله).</string>
|
||||||
<string name="import_data_title">استيراد قاعدة البيانات</string>
|
<string name="import_data_title">استيراد قاعدة البيانات</string>
|
||||||
<string name="export_data_title">تصدير قاعدة البيانات</string>
|
<string name="export_data_title">تصدير قاعدة البيانات</string>
|
||||||
<string name="import_data_summary">"الكتابة فوق سجل التاريخ والاشتراكات الحالية "</string>
|
<string name="import_data_summary">يتجاوز السجل والاشتراكات الحالية</string>
|
||||||
<string name="export_data_summary">تصدير السجل، الإشتراكات وقوائم التشغيل</string>
|
<string name="export_data_summary">تصدير السجل، الإشتراكات وقوائم التشغيل</string>
|
||||||
<string name="show_info">عرض المعلومات</string>
|
<string name="show_info">عرض المعلومات</string>
|
||||||
<string name="controls_add_to_playlist_title">إضافة إلى</string>
|
<string name="controls_add_to_playlist_title">إضافة إلى</string>
|
||||||
|
@ -295,8 +295,8 @@
|
||||||
<string name="preferred_player_fetcher_notification_message">تحميل المحتوى المطلوب</string>
|
<string name="preferred_player_fetcher_notification_message">تحميل المحتوى المطلوب</string>
|
||||||
<string name="create_playlist">إنشاء قائمة تشغيل جديدة</string>
|
<string name="create_playlist">إنشاء قائمة تشغيل جديدة</string>
|
||||||
<string name="delete_playlist">حذف قائمة التشغيل</string>
|
<string name="delete_playlist">حذف قائمة التشغيل</string>
|
||||||
<string name="rename_playlist">"إعادة تسمية قائمة التشغيل "</string>
|
<string name="rename_playlist">إعادة تسمية</string>
|
||||||
<string name="playlist_name_input">التسمية</string>
|
<string name="name">التسمية</string>
|
||||||
<string name="append_playlist">إضافة إلى قائمة تشغيل</string>
|
<string name="append_playlist">إضافة إلى قائمة تشغيل</string>
|
||||||
<string name="delete_playlist_prompt">هل تريد حذف قائمة التشغيل هذه ؟</string>
|
<string name="delete_playlist_prompt">هل تريد حذف قائمة التشغيل هذه ؟</string>
|
||||||
<string name="playlist_creation_success">تم إنشاء قائمة التشغيل</string>
|
<string name="playlist_creation_success">تم إنشاء قائمة التشغيل</string>
|
||||||
|
@ -325,7 +325,7 @@
|
||||||
<string name="import_ongoing">عملية الإستعادة جارية …</string>
|
<string name="import_ongoing">عملية الإستعادة جارية …</string>
|
||||||
<string name="export_ongoing">عملية التصدير جارية …</string>
|
<string name="export_ongoing">عملية التصدير جارية …</string>
|
||||||
<string name="import_file_title">إستيراد ملف</string>
|
<string name="import_file_title">إستيراد ملف</string>
|
||||||
<string name="import_soundcloud_instructions_hint">"معرفك , soundcloud.com/ الخاص بك "</string>
|
<string name="import_soundcloud_instructions_hint">معرفك, soundcloud.com/هويتك</string>
|
||||||
<string name="download_thumbnail_summary">عند إيقاف تحميل أي صور مصغرة ، وتوفير البيانات واستخدام الذاكرة. تعمل التغييرات على محو ذاكرة التخزين المؤقت للصورة الموجودة على الذاكرة أو على القرص.</string>
|
<string name="download_thumbnail_summary">عند إيقاف تحميل أي صور مصغرة ، وتوفير البيانات واستخدام الذاكرة. تعمل التغييرات على محو ذاكرة التخزين المؤقت للصورة الموجودة على الذاكرة أو على القرص.</string>
|
||||||
<string name="metadata_cache_wipe_title">امسح البيانات الوصفية المخزنة مؤقتًا</string>
|
<string name="metadata_cache_wipe_title">امسح البيانات الوصفية المخزنة مؤقتًا</string>
|
||||||
<string name="metadata_cache_wipe_summary">إزالة جميع بيانات صفحات الويب المخزنة مؤقتًا</string>
|
<string name="metadata_cache_wipe_summary">إزالة جميع بيانات صفحات الويب المخزنة مؤقتًا</string>
|
||||||
|
@ -338,7 +338,6 @@
|
||||||
<string name="caption_none">بدون تسميات توضيحية</string>
|
<string name="caption_none">بدون تسميات توضيحية</string>
|
||||||
<string name="caption_setting_title">تسميات توضيحية</string>
|
<string name="caption_setting_title">تسميات توضيحية</string>
|
||||||
<string name="caption_setting_description">تعديل مشغل نص التسمية التوضيحية وأنماط الخلفية. يتطلب إعادة تشغيل التطبيق لتصبح التغييرات سارية المفعول.</string>
|
<string name="caption_setting_description">تعديل مشغل نص التسمية التوضيحية وأنماط الخلفية. يتطلب إعادة تشغيل التطبيق لتصبح التغييرات سارية المفعول.</string>
|
||||||
<string name="enable_leak_canary_title">تمكين تسرب الكناري</string>
|
|
||||||
<string name="enable_leak_canary_summary">قد تتسبب مراقبة تسرب الذاكرة في عدم استجابة التطبيق عند تفريغ السجلات</string>
|
<string name="enable_leak_canary_summary">قد تتسبب مراقبة تسرب الذاكرة في عدم استجابة التطبيق عند تفريغ السجلات</string>
|
||||||
<string name="enable_disposed_exceptions_title">تقرير الأخطاء خارج دورة الحياة</string>
|
<string name="enable_disposed_exceptions_title">تقرير الأخطاء خارج دورة الحياة</string>
|
||||||
<string name="enable_disposed_exceptions_summary">فرض الإبلاغ عن استثناءات Rx غير القابلة للتسليم خارج دورة حياة الجزء أو النشاط بعد التخلص منها</string>
|
<string name="enable_disposed_exceptions_summary">فرض الإبلاغ عن استثناءات Rx غير القابلة للتسليم خارج دورة حياة الجزء أو النشاط بعد التخلص منها</string>
|
||||||
|
@ -363,7 +362,7 @@
|
||||||
<string name="title_most_played">الأكثر تشغيلا</string>
|
<string name="title_most_played">الأكثر تشغيلا</string>
|
||||||
<string name="override_current_data">هذا سوف يُزيل إعداداتك الحالية.</string>
|
<string name="override_current_data">هذا سوف يُزيل إعداداتك الحالية.</string>
|
||||||
<string name="preferred_open_action_settings_title">طريقة \'التشغيل\' المفضلة</string>
|
<string name="preferred_open_action_settings_title">طريقة \'التشغيل\' المفضلة</string>
|
||||||
<string name="preferred_open_action_settings_summary">"الإجراء الافتراضي عند فتح المحتوى — %s"</string>
|
<string name="preferred_open_action_settings_summary">الإجراء الافتراضي عند فتح المحتوى — %s</string>
|
||||||
<string name="background_player">مشغل الخلفية</string>
|
<string name="background_player">مشغل الخلفية</string>
|
||||||
<string name="popup_player">المشغل المنبثق</string>
|
<string name="popup_player">المشغل المنبثق</string>
|
||||||
<string name="previous_export">نسخة احتياطية</string>
|
<string name="previous_export">نسخة احتياطية</string>
|
||||||
|
@ -388,7 +387,7 @@
|
||||||
<string name="playback_pitch">تردد الصوت</string>
|
<string name="playback_pitch">تردد الصوت</string>
|
||||||
<string name="unhook_checkbox">حل (قد يسبب تشويه)</string>
|
<string name="unhook_checkbox">حل (قد يسبب تشويه)</string>
|
||||||
<string name="import_settings">هل تريد أيضا استيراد الإعدادات؟</string>
|
<string name="import_settings">هل تريد أيضا استيراد الإعدادات؟</string>
|
||||||
<string name="privacy_policy_title">"سياسة الخصوصية في NewPipe "</string>
|
<string name="privacy_policy_title">سياسة خصوصية NewPipe</string>
|
||||||
<string name="privacy_policy_encouragement">يأخذ مشروع NewPipe خصوصيتك على محمل الجد. لذلك ، لا يجمع التطبيق أي بيانات دون موافقتك.
|
<string name="privacy_policy_encouragement">يأخذ مشروع NewPipe خصوصيتك على محمل الجد. لذلك ، لا يجمع التطبيق أي بيانات دون موافقتك.
|
||||||
\nتوضح سياسة خصوصية NewPipe بالتفصيل البيانات التي يتم إرسالها وتخزينها عند إرسال تقرير الأعطال.</string>
|
\nتوضح سياسة خصوصية NewPipe بالتفصيل البيانات التي يتم إرسالها وتخزينها عند إرسال تقرير الأعطال.</string>
|
||||||
<string name="read_privacy_policy">الإطلاع على سياسة الخصوصية</string>
|
<string name="read_privacy_policy">الإطلاع على سياسة الخصوصية</string>
|
||||||
|
@ -423,7 +422,7 @@
|
||||||
<string name="events">الأحداث</string>
|
<string name="events">الأحداث</string>
|
||||||
<string name="app_update_notification_channel_description">الإخطارات لإصدار NewPipe الجديد</string>
|
<string name="app_update_notification_channel_description">الإخطارات لإصدار NewPipe الجديد</string>
|
||||||
<string name="download_to_sdcard_error_title">وحدة التخزين الخارجية غير متوفرة</string>
|
<string name="download_to_sdcard_error_title">وحدة التخزين الخارجية غير متوفرة</string>
|
||||||
<string name="download_to_sdcard_error_message">"التنزيل على بطاقة SD الخارجية غير ممكن. إعادة تعيين موقع مجلد التحميل؟"</string>
|
<string name="download_to_sdcard_error_message">لا يمكن التنزيل على بطاقة SD الخارجية. هل تريد إعادة تعيين موقع مجلد التنزيل؟</string>
|
||||||
<string name="saved_tabs_invalid_json">تعذرت قراءة علامات التبويب المحفوظة, لذا استخدم علامات التبويب الافتراضية</string>
|
<string name="saved_tabs_invalid_json">تعذرت قراءة علامات التبويب المحفوظة, لذا استخدم علامات التبويب الافتراضية</string>
|
||||||
<string name="restore_defaults">استعادة الضبط الافتراضي</string>
|
<string name="restore_defaults">استعادة الضبط الافتراضي</string>
|
||||||
<string name="restore_defaults_confirmation">هل تريد استعادة الإعدادات الافتراضية؟</string>
|
<string name="restore_defaults_confirmation">هل تريد استعادة الإعدادات الافتراضية؟</string>
|
||||||
|
@ -469,7 +468,7 @@
|
||||||
<string name="stop">توقف</string>
|
<string name="stop">توقف</string>
|
||||||
<string name="max_retry_msg">أقصى عدد للمحاولات</string>
|
<string name="max_retry_msg">أقصى عدد للمحاولات</string>
|
||||||
<string name="max_retry_desc">الحد الأقصى لعدد محاولات قبل إلغاء التحميل</string>
|
<string name="max_retry_desc">الحد الأقصى لعدد محاولات قبل إلغاء التحميل</string>
|
||||||
<string name="pause_downloads_on_mobile">"إنقطع الإتصال بالشبكة عند التحويل إلى البيانات المتنقلة"</string>
|
<string name="pause_downloads_on_mobile">المقاطعة على الشبكات المقيسة</string>
|
||||||
<string name="pause_downloads_on_mobile_desc">مفيد عند التبديل إلى بيانات الجوال ، على الرغم من أنه لا يمكن تعليق بعض التنزيلات</string>
|
<string name="pause_downloads_on_mobile_desc">مفيد عند التبديل إلى بيانات الجوال ، على الرغم من أنه لا يمكن تعليق بعض التنزيلات</string>
|
||||||
<string name="show_comments_title">إظهار التعليقات</string>
|
<string name="show_comments_title">إظهار التعليقات</string>
|
||||||
<string name="show_comments_summary">عطّله لإخفاء التعليقات</string>
|
<string name="show_comments_summary">عطّله لإخفاء التعليقات</string>
|
||||||
|
@ -498,7 +497,8 @@
|
||||||
<string name="pause_downloads">إيقاف التحميل مؤقتا</string>
|
<string name="pause_downloads">إيقاف التحميل مؤقتا</string>
|
||||||
<string name="downloads_storage_ask_title">اسأل عن مكان التنزيل</string>
|
<string name="downloads_storage_ask_title">اسأل عن مكان التنزيل</string>
|
||||||
<string name="downloads_storage_ask_summary">سيُطلب منك مكان حفظ كل تنزيل</string>
|
<string name="downloads_storage_ask_summary">سيُطلب منك مكان حفظ كل تنزيل</string>
|
||||||
<string name="downloads_storage_ask_summary_kitkat">سيُطلب منك مكان حفظ كل تنزيل. اختر SAF إذا كنت تريد التنزيل على بطاقة SD خارجية</string>
|
<string name="downloads_storage_ask_summary_kitkat">سيطلب منك مكان حفظ كل تنزيل.
|
||||||
|
\nاختر SAF إذا كنت تريد التنزيل على بطاقة SD خارجية</string>
|
||||||
<string name="downloads_storage_use_saf_title">استخدام آمن</string>
|
<string name="downloads_storage_use_saf_title">استخدام آمن</string>
|
||||||
<string name="downloads_storage_use_saf_summary">يسمح \"إطار الوصول إلى التخزين\" بالتنزيل على بطاقة SD خارجية.
|
<string name="downloads_storage_use_saf_summary">يسمح \"إطار الوصول إلى التخزين\" بالتنزيل على بطاقة SD خارجية.
|
||||||
\nبعض الأجهزة غير متوافقة</string>
|
\nبعض الأجهزة غير متوافقة</string>
|
||||||
|
@ -583,7 +583,6 @@
|
||||||
<string name="settings_category_feed_title">تغذية</string>
|
<string name="settings_category_feed_title">تغذية</string>
|
||||||
<string name="feed_create_new_group_button_title">جديد</string>
|
<string name="feed_create_new_group_button_title">جديد</string>
|
||||||
<string name="feed_group_dialog_delete_message">هل تريد حذف هذه المجموعة\?</string>
|
<string name="feed_group_dialog_delete_message">هل تريد حذف هذه المجموعة\?</string>
|
||||||
<string name="feed_group_dialog_name_input">الاسم</string>
|
|
||||||
<string name="feed_group_dialog_empty_name">اسم المجموعة فارغ</string>
|
<string name="feed_group_dialog_empty_name">اسم المجموعة فارغ</string>
|
||||||
<plurals name="feed_group_dialog_selection_count">
|
<plurals name="feed_group_dialog_selection_count">
|
||||||
<item quantity="zero">%d تحديد</item>
|
<item quantity="zero">%d تحديد</item>
|
||||||
|
@ -650,4 +649,12 @@
|
||||||
<string name="video_detail_by">لـ %s</string>
|
<string name="video_detail_by">لـ %s</string>
|
||||||
<string name="channel_created_by">أنشأها %s</string>
|
<string name="channel_created_by">أنشأها %s</string>
|
||||||
<string name="detail_sub_channel_thumbnail_view_description">الصورة الرمزية للقناة</string>
|
<string name="detail_sub_channel_thumbnail_view_description">الصورة الرمزية للقناة</string>
|
||||||
|
<string name="playlist_page_summary">صفحة قائمة التشغيل</string>
|
||||||
|
<string name="feed_group_show_only_ungrouped_subscriptions">إظهار الاشتراكات غير المجمعة فقط</string>
|
||||||
|
<string name="no_playlist_bookmarked_yet">لا توجد إشارات مرجعية لقائمة التشغيل حتى الآن</string>
|
||||||
|
<string name="select_a_playlist">حدد قائمة تشغيل</string>
|
||||||
|
<string name="error_report_open_github_notice">يرجى التحقق مما إذا كانت هناك مشكلة في مناقشة تعطلك بالفعل. عند إنشاء تذاكر مكررة ، ستأخذ وقتًا منا يمكن أن نقضيه في إصلاح الخطأ الفعلي.</string>
|
||||||
|
<string name="error_report_open_issue_button_text">أبلغ عن خطأ على GitHub</string>
|
||||||
|
<string name="copy_for_github">نسخ تقرير منسق</string>
|
||||||
|
<string name="search_showing_result_for">عرض النتائج ل : %s</string>
|
||||||
</resources>
|
</resources>
|
|
@ -1,5 +1,6 @@
|
||||||
<?xml version='1.0' encoding='UTF-8'?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources><string name="main_bg_subtitle">Başlamaq üçün axtarışa bas</string>
|
<resources>
|
||||||
|
<string name="main_bg_subtitle">Başlamaq üçün axtarışa bas</string>
|
||||||
<string name="view_count_text">%1$s baxış</string>
|
<string name="view_count_text">%1$s baxış</string>
|
||||||
<string name="upload_date_text">Dərc edilib %1$s</string>
|
<string name="upload_date_text">Dərc edilib %1$s</string>
|
||||||
<string name="no_player_found">Axın oynadıcısı tapılmadı. VLC ni yükləmək istərdinizmi?</string>
|
<string name="no_player_found">Axın oynadıcısı tapılmadı. VLC ni yükləmək istərdinizmi?</string>
|
||||||
|
@ -22,22 +23,17 @@
|
||||||
<string name="subscribed_button_title">Abunə olunub</string>
|
<string name="subscribed_button_title">Abunə olunub</string>
|
||||||
<string name="channel_unsubscribed">Kanal</string>
|
<string name="channel_unsubscribed">Kanal</string>
|
||||||
<string name="show_info">Məlumat göstər</string>
|
<string name="show_info">Məlumat göstər</string>
|
||||||
|
|
||||||
<string name="tab_main">Əsas</string>
|
<string name="tab_main">Əsas</string>
|
||||||
<string name="tab_subscriptions">Abunəliklər</string>
|
<string name="tab_subscriptions">Abunəliklər</string>
|
||||||
<string name="tab_bookmarks">Əlfəcinlər</string>
|
<string name="tab_bookmarks">Əlfəcinlər</string>
|
||||||
|
|
||||||
<string name="fragment_feed_title">Yeni nə var</string>
|
<string name="fragment_feed_title">Yeni nə var</string>
|
||||||
|
|
||||||
<string name="controls_background_title">Arxa fon</string>
|
<string name="controls_background_title">Arxa fon</string>
|
||||||
<string name="download_path_title">Video yükləmə ünvanı</string>
|
<string name="download_path_title">Video yükləmə ünvanı</string>
|
||||||
<string name="download_path_summary">Yüklənən videoları saxlamaq üçün yer</string>
|
<string name="download_path_summary">Yüklənən videoları saxlamaq üçün yer</string>
|
||||||
<string name="download_path_dialog_title">Videolar üçün yükləmə yerini daxil et</string>
|
<string name="download_path_dialog_title">Videolar üçün yükləmə yerini daxil et</string>
|
||||||
|
|
||||||
<string name="download_path_audio_title">Audio yükləmə yeri</string>
|
<string name="download_path_audio_title">Audio yükləmə yeri</string>
|
||||||
<string name="download_path_audio_summary">Yüklənən audioları saxlamaq üçün yer</string>
|
<string name="download_path_audio_summary">Yüklənən audioları saxlamaq üçün yer</string>
|
||||||
<string name="download_path_audio_dialog_title">Audiolar üçün yükləmə yerini daxil et</string>
|
<string name="download_path_audio_dialog_title">Audiolar üçün yükləmə yerini daxil et</string>
|
||||||
|
|
||||||
<string name="autoplay_by_calling_app_summary">NewPipe başqa bir uyğulamadan çağrıldığı zaman avtomatik olaraq videonu oynadır</string>
|
<string name="autoplay_by_calling_app_summary">NewPipe başqa bir uyğulamadan çağrıldığı zaman avtomatik olaraq videonu oynadır</string>
|
||||||
<string name="default_resolution_title">Defolt ölçü</string>
|
<string name="default_resolution_title">Defolt ölçü</string>
|
||||||
<string name="show_higher_resolutions_title">Daha böyük ölçüləri göstər</string>
|
<string name="show_higher_resolutions_title">Daha böyük ölçüləri göstər</string>
|
||||||
|
@ -52,4 +48,8 @@
|
||||||
<string name="light_theme_title">Açıq</string>
|
<string name="light_theme_title">Açıq</string>
|
||||||
<string name="dark_theme_title">Qaranlıq</string>
|
<string name="dark_theme_title">Qaranlıq</string>
|
||||||
<string name="black_theme_title">Qara</string>
|
<string name="black_theme_title">Qara</string>
|
||||||
</resources>
|
<string name="unsubscribe">Abunəlik ləğvi</string>
|
||||||
|
<string name="popup_mode_share_menu_title">Açılan pəncərə</string>
|
||||||
|
<string name="screen_rotation">Fırlanma</string>
|
||||||
|
<string name="open_in_popup_mode">Açılan pəncərə modunda aç</string>
|
||||||
|
</resources>
|
|
@ -150,7 +150,7 @@
|
||||||
<string name="delete_all_history_prompt">¿De xuru que quies desaniciar tolos elementos del historial\?</string>
|
<string name="delete_all_history_prompt">¿De xuru que quies desaniciar tolos elementos del historial\?</string>
|
||||||
<string name="drawer_header_action_paceholder_text">Equí va apaecer dalgo ceo ;D</string>
|
<string name="drawer_header_action_paceholder_text">Equí va apaecer dalgo ceo ;D</string>
|
||||||
<string name="create_playlist">Llista nueva de repoducción</string>
|
<string name="create_playlist">Llista nueva de repoducción</string>
|
||||||
<string name="playlist_name_input">Nome</string>
|
<string name="name">Nome</string>
|
||||||
<string name="append_playlist">Amestar a una llista de repoducción</string>
|
<string name="append_playlist">Amestar a una llista de repoducción</string>
|
||||||
<string name="delete_playlist_prompt">¿Desaniciar esta llista de reproducción\?</string>
|
<string name="delete_playlist_prompt">¿Desaniciar esta llista de reproducción\?</string>
|
||||||
<string name="playlist_delete_failure">Nun pudo desaniciase la llista de reproducción.</string>
|
<string name="playlist_delete_failure">Nun pudo desaniciase la llista de reproducción.</string>
|
||||||
|
|
|
@ -71,7 +71,7 @@
|
||||||
<string name="subscribers_count_not_available">无法得知订阅人数</string>
|
<string name="subscribers_count_not_available">无法得知订阅人数</string>
|
||||||
<string name="updates_setting_description">发布新版本时,通知我升级应用</string>
|
<string name="updates_setting_description">发布新版本时,通知我升级应用</string>
|
||||||
<string name="grid">网格</string>
|
<string name="grid">网格</string>
|
||||||
<string name="app_update_notification_content_title">NewPipe有更新!</string>
|
<string name="app_update_notification_content_title">NewPipe可更新!</string>
|
||||||
<string name="error_http_unsupported_range">服务器不接受 接收 multi-threaded 下载, 以 @string/msg_threads = 1 重试</string>
|
<string name="error_http_unsupported_range">服务器不接受 接收 multi-threaded 下载, 以 @string/msg_threads = 1 重试</string>
|
||||||
<string name="autoplay_title">自动播放</string>
|
<string name="autoplay_title">自动播放</string>
|
||||||
<string name="settings_category_clear_data_title">清除数据</string>
|
<string name="settings_category_clear_data_title">清除数据</string>
|
||||||
|
@ -191,7 +191,7 @@
|
||||||
<string name="error_snackbar_action">报告</string>
|
<string name="error_snackbar_action">报告</string>
|
||||||
<string name="what_device_headline">信息:</string>
|
<string name="what_device_headline">信息:</string>
|
||||||
<string name="what_happened_headline">发生了什么:</string>
|
<string name="what_happened_headline">发生了什么:</string>
|
||||||
<string name="info_labels">详情:\\n请求:\\n内容语言:\\n服务:\\nGMT时间:\\n包:\\n版本:\\n操作系统版本:</string>
|
<string name="info_labels">详情:\\n请求:\\n内容语言:\\n内容国家:\\n客户端语言:\\n服务:\\nGMT时间:\\n包名:\\n版本:\\n操作系统版本:</string>
|
||||||
<string name="your_comment">您的附近说明(请用英文):</string>
|
<string name="your_comment">您的附近说明(请用英文):</string>
|
||||||
<string name="error_details_headline">详细信息:</string>
|
<string name="error_details_headline">详细信息:</string>
|
||||||
<string name="list_thumbnail_view_description">视频预览缩略图</string>
|
<string name="list_thumbnail_view_description">视频预览缩略图</string>
|
||||||
|
@ -324,7 +324,7 @@
|
||||||
<string name="create_playlist">新建播放列表</string>
|
<string name="create_playlist">新建播放列表</string>
|
||||||
<string name="delete_playlist">删除</string>
|
<string name="delete_playlist">删除</string>
|
||||||
<string name="rename_playlist">重命名</string>
|
<string name="rename_playlist">重命名</string>
|
||||||
<string name="playlist_name_input">名称</string>
|
<string name="name">名称</string>
|
||||||
<string name="append_playlist">添加到播放列表</string>
|
<string name="append_playlist">添加到播放列表</string>
|
||||||
<string name="set_as_playlist_thumbnail">设为播放列表缩略图</string>
|
<string name="set_as_playlist_thumbnail">设为播放列表缩略图</string>
|
||||||
<string name="bookmark_playlist">收藏播放列表</string>
|
<string name="bookmark_playlist">收藏播放列表</string>
|
||||||
|
@ -341,7 +341,6 @@
|
||||||
<string name="drawer_header_action_paceholder_text">敬请期待</string>
|
<string name="drawer_header_action_paceholder_text">敬请期待</string>
|
||||||
<string name="settings_category_debug_title">调试</string>
|
<string name="settings_category_debug_title">调试</string>
|
||||||
<string name="caption_auto_generated">自动生成</string>
|
<string name="caption_auto_generated">自动生成</string>
|
||||||
<string name="enable_leak_canary_title">启用LeakCanary</string>
|
|
||||||
<string name="enable_leak_canary_summary">『内存泄漏监视』可能导致应用在『核心转储』时无响应</string>
|
<string name="enable_leak_canary_summary">『内存泄漏监视』可能导致应用在『核心转储』时无响应</string>
|
||||||
<string name="enable_disposed_exceptions_title">报告『提前结束Android生命周期』错误</string>
|
<string name="enable_disposed_exceptions_title">报告『提前结束Android生命周期』错误</string>
|
||||||
<string name="enable_disposed_exceptions_summary">强制报告处理后的未送达的Activity或Fragment生命周期之外的Rx异常</string>
|
<string name="enable_disposed_exceptions_summary">强制报告处理后的未送达的Activity或Fragment生命周期之外的Rx异常</string>
|
||||||
|
@ -556,7 +555,6 @@
|
||||||
<item quantity="other">已选中%d</item>
|
<item quantity="other">已选中%d</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
<string name="feed_group_dialog_empty_name">组名为空</string>
|
<string name="feed_group_dialog_empty_name">组名为空</string>
|
||||||
<string name="feed_group_dialog_name_input">名称</string>
|
|
||||||
<string name="feed_group_dialog_delete_message">您要删除该组吗?</string>
|
<string name="feed_group_dialog_delete_message">您要删除该组吗?</string>
|
||||||
<string name="feed_create_new_group_button_title">新建</string>
|
<string name="feed_create_new_group_button_title">新建</string>
|
||||||
<string name="settings_category_feed_title">订阅</string>
|
<string name="settings_category_feed_title">订阅</string>
|
||||||
|
@ -602,4 +600,11 @@
|
||||||
<string name="show_original_time_ago_title">在项目上显示原始时间</string>
|
<string name="show_original_time_ago_title">在项目上显示原始时间</string>
|
||||||
<string name="youtube_restricted_mode_enabled_title">YouTube受限模式</string>
|
<string name="youtube_restricted_mode_enabled_title">YouTube受限模式</string>
|
||||||
<string name="feed_group_show_only_ungrouped_subscriptions">仅显示未分组订阅</string>
|
<string name="feed_group_show_only_ungrouped_subscriptions">仅显示未分组订阅</string>
|
||||||
|
<string name="playlist_page_summary">播放列表页</string>
|
||||||
|
<string name="no_playlist_bookmarked_yet">尚无播放列表书签</string>
|
||||||
|
<string name="select_a_playlist">选择播放列表</string>
|
||||||
|
<string name="error_report_open_github_notice">请检查您的问题是否已经存在。创建重复票证时,您需要从我们那里花些时间来让我们修复真正的bug。</string>
|
||||||
|
<string name="error_report_open_issue_button_text">去GitHub上报告错误</string>
|
||||||
|
<string name="copy_for_github">复制格式报告</string>
|
||||||
|
<string name="search_showing_result_for">显示结果为:%s</string>
|
||||||
</resources>
|
</resources>
|
|
@ -337,7 +337,7 @@
|
||||||
<string name="create_playlist">Стварыць плэйліст</string>
|
<string name="create_playlist">Стварыць плэйліст</string>
|
||||||
<string name="delete_playlist">Выдаліць</string>
|
<string name="delete_playlist">Выдаліць</string>
|
||||||
<string name="rename_playlist">Перайменаваць</string>
|
<string name="rename_playlist">Перайменаваць</string>
|
||||||
<string name="playlist_name_input">Імя</string>
|
<string name="name">Імя</string>
|
||||||
<string name="append_playlist">Дадаць у плэйліст</string>
|
<string name="append_playlist">Дадаць у плэйліст</string>
|
||||||
<string name="set_as_playlist_thumbnail">На мініяцюру плэйліста</string>
|
<string name="set_as_playlist_thumbnail">На мініяцюру плэйліста</string>
|
||||||
<string name="bookmark_playlist">Дадаць плэйліст у закладкі</string>
|
<string name="bookmark_playlist">Дадаць плэйліст у закладкі</string>
|
||||||
|
@ -354,7 +354,6 @@
|
||||||
<string name="caption_auto_generated">Створаны аўтаматычна</string>
|
<string name="caption_auto_generated">Створаны аўтаматычна</string>
|
||||||
<string name="caption_setting_title">Тытры</string>
|
<string name="caption_setting_title">Тытры</string>
|
||||||
<string name="caption_setting_description">Змяніць памер тэкста і фон тытраў. Патрэбен перазапуск</string>
|
<string name="caption_setting_description">Змяніць памер тэкста і фон тытраў. Патрэбен перазапуск</string>
|
||||||
<string name="enable_leak_canary_title">Уключыць LeakCanary</string>
|
|
||||||
<string name="enable_leak_canary_summary">Маніторынг уцечкі памяці можа прывесці да завісання прыкладання</string>
|
<string name="enable_leak_canary_summary">Маніторынг уцечкі памяці можа прывесці да завісання прыкладання</string>
|
||||||
<string name="enable_disposed_exceptions_title">Паведамляць пра памылкі жыццёвага цыклу</string>
|
<string name="enable_disposed_exceptions_title">Паведамляць пра памылкі жыццёвага цыклу</string>
|
||||||
<string name="enable_disposed_exceptions_summary">Прымусова паведамляць пра недастаўляемыя Rx-выключэнні па-за фрагментам або жыццёвым цыкле пасля выдалення</string>
|
<string name="enable_disposed_exceptions_summary">Прымусова паведамляць пра недастаўляемыя Rx-выключэнні па-за фрагментам або жыццёвым цыкле пасля выдалення</string>
|
||||||
|
@ -494,4 +493,11 @@
|
||||||
<string name="downloads_storage_use_saf_title">Выкарыстоўваць SAF</string>
|
<string name="downloads_storage_use_saf_title">Выкарыстоўваць SAF</string>
|
||||||
<string name="downloads_storage_use_saf_summary">Storage Framework Access дазваляе захоўваць файлы на вонкавым назапашвальніку.
|
<string name="downloads_storage_use_saf_summary">Storage Framework Access дазваляе захоўваць файлы на вонкавым назапашвальніку.
|
||||||
\nПадтрымліваецца не ўсімі прыладамі</string>
|
\nПадтрымліваецца не ўсімі прыладамі</string>
|
||||||
|
<string name="drawer_header_description">Пераключыць службу, выбраную ў дадзены момант:</string>
|
||||||
|
<string name="clear_playback_states_summary">Выдаліць ўсе пазіцыі прайгравання</string>
|
||||||
|
<string name="youtube_restricted_mode_enabled_title">Абмежаваны рэжым YouTube</string>
|
||||||
|
<string name="peertube_instance_add_https_only">Падтрымліваюцца толькі адрасы URL HTTPS</string>
|
||||||
|
<string name="peertube_instance_add_title">Дадаць інстанцыю</string>
|
||||||
|
<string name="peertube_instance_url_title">Інстанцыі PeerTube</string>
|
||||||
|
<string name="download_choose_new_path">Змяніце папкі загрузкі, каб змены ўступілі ў сілу</string>
|
||||||
</resources>
|
</resources>
|
131
app/src/main/res/values-ber/strings.xml
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="feed_create_new_group_button_title">ⴰⵎⴰⵢⵏⵓ</string>
|
||||||
|
<string name="name">ⵉⵙⵎ</string>
|
||||||
|
<plurals name="days">
|
||||||
|
<item quantity="one">%d ⵡⴰⵙⵙ</item>
|
||||||
|
<item quantity="other">%d ⵓⵙⵙⴰⵏ</item>
|
||||||
|
</plurals>
|
||||||
|
<plurals name="hours">
|
||||||
|
<item quantity="one">%d ⵜⵙⵔⵉⴳⵜ</item>
|
||||||
|
<item quantity="other">%d ⵜⵙⵔⵉⴳⵉⵏ</item>
|
||||||
|
</plurals>
|
||||||
|
<plurals name="minutes">
|
||||||
|
<item quantity="one">%d ⵜⵙⴷⵉⴷⵉⵜ</item>
|
||||||
|
<item quantity="other">%d ⵜⵙⴷⵉⴷⵉⵏ</item>
|
||||||
|
</plurals>
|
||||||
|
<string name="app_language_title">ⵜⵓⵜⵍⴰⵢⵜ ⵏ ⵓⵙⵏⵙⵉ</string>
|
||||||
|
<string name="close">ⵇⵇⵏ</string>
|
||||||
|
<string name="minimize_on_exit_none_description">ⵡⴰⵍⵓ</string>
|
||||||
|
<string name="playback_step">ⴰⵙⵓⵔⵉⴼ</string>
|
||||||
|
<string name="resize_zoom">ⵙⵎⵖⵕ</string>
|
||||||
|
<string name="mute">ⵙⵓⵙⵎ</string>
|
||||||
|
<string name="rename_playlist">ⵙⵏⴼⵍ ⵉⵙⵎ</string>
|
||||||
|
<string name="delete_playlist">ⴽⴽⵙ</string>
|
||||||
|
<string name="play_queue_audio_settings">ⵜⵉⵙⵖⴰⵍ ⵓⵎⵙⵍⴰⵢ</string>
|
||||||
|
<string name="play_queue_remove">ⴽⴽⵙ</string>
|
||||||
|
<string name="action_history">ⴰⵎⵣⵔⵓⵢ</string>
|
||||||
|
<string name="title_history_search">ⵜⴻⵜⵜⵡⴰⵔⵣⵓⵏ</string>
|
||||||
|
<string name="title_history_view">ⵜⴻⵜⵜⵓⵥⵕ</string>
|
||||||
|
<string name="title_activity_history">ⴰⵎⵣⵔⵓⵢ</string>
|
||||||
|
<string name="website_title">ⴰⵙⵉⵜ</string>
|
||||||
|
<string name="view_on_github">ⵥⵕ ⴳ GitHub</string>
|
||||||
|
<string name="tab_licenses">ⵜⵓⵔⴰⴳⵜ</string>
|
||||||
|
<string name="tab_about">ⵅⴼ</string>
|
||||||
|
<string name="action_open_website">ⵕⵥⵎ ⴰⵙⵉⵜ</string>
|
||||||
|
<string name="copyright">© %1$s ⵙⴳ %2$s ⴷⴷⴰⵡ %3$s</string>
|
||||||
|
<string name="action_about">ⵅⴼ</string>
|
||||||
|
<string name="action_settings">ⵜⵉⵙⵖⴰⵍ</string>
|
||||||
|
<string name="title_activity_about">ⵅⴼ NewPipe</string>
|
||||||
|
<string name="msg_wait">ⵕⴰⵊⴰ…</string>
|
||||||
|
<string name="msg_error">ⵜⴰⵣⴳⵍⵜ</string>
|
||||||
|
<string name="msg_name">ⵉⵙⵎ ⵓⴼⴰⵢⵍⵓ</string>
|
||||||
|
<string name="finish">ⵡⴰⵅⵅⴰ</string>
|
||||||
|
<string name="rename">ⵙⵙⵏⴼⵍ ⵉⵙⵎ</string>
|
||||||
|
<string name="dismiss">ⵙⵙⵔ</string>
|
||||||
|
<string name="delete_all">ⴽⴽⵙ ⵎⴰⵕⵕⴰ</string>
|
||||||
|
<string name="delete_one">ⴽⵙⵙ ⵢⴰⵏ</string>
|
||||||
|
<string name="delete">ⴽⴽⵙ</string>
|
||||||
|
<string name="view">ⵖⵔ</string>
|
||||||
|
<string name="no_comments">ⵓⵔ ⵍⵍⵉⵏ ⵉⵡⴼⴰⵡⴰⵍⵏ</string>
|
||||||
|
<plurals name="videos">
|
||||||
|
<item quantity="one">%s ⵓⴼⵉⴷⵢⵓ</item>
|
||||||
|
<item quantity="other">%s ⵉⴼⵉⴷⵢⵓⵜⵏ</item>
|
||||||
|
</plurals>
|
||||||
|
<string name="infinite_videos">∞ ⵉⴼⵉⴷⵢⵓⵜⵏ</string>
|
||||||
|
<string name="more_than_100_videos">100+ ⵉⴼⵉⴷⵢⵓⵜⵏ</string>
|
||||||
|
<string name="short_billion">ⴱ</string>
|
||||||
|
<string name="short_million">ⵎ</string>
|
||||||
|
<string name="short_thousand">ⴽ</string>
|
||||||
|
<string name="audio">ⴰⵎⵙⵍⴰⵢ</string>
|
||||||
|
<string name="video">ⴰⴼⵉⴷⵢⵓ</string>
|
||||||
|
<string name="detail_likes_img_view_description">ⵉⵔⵉⵜⵏ</string>
|
||||||
|
<string name="general_error">ⵜⴰⵣⴳⵍⵜ</string>
|
||||||
|
<string name="help">ⵜⵉⵡⵉⵙⵉ</string>
|
||||||
|
<string name="file">ⴰⴼⴰⵢⵍⵓ</string>
|
||||||
|
<string name="play_all">ⵖⵔ ⵎⴰⵕⵕⴰ</string>
|
||||||
|
<string name="file_deleted">ⵉⵜⵜⵡⴰⴽⴽⵙ ⵓⴼⴰⵢⵍⵓ</string>
|
||||||
|
<string name="filter">ⴰⵙⵜⵜⴰⵢ</string>
|
||||||
|
<string name="yes">ⵢⴰⵀ</string>
|
||||||
|
<string name="videos_string">ⵉⴼⵉⴷⵢⵓⵜⵏ</string>
|
||||||
|
<string name="all">ⵎⴰⵕⵕⴰ</string>
|
||||||
|
<string name="downloads_title">ⵓⴳⴳⴰⵎⵏ</string>
|
||||||
|
<string name="downloads">ⵓⴳⴳⴰⵎⵏ</string>
|
||||||
|
<string name="duration_live">ⵓⵙⵔⵉⴷ</string>
|
||||||
|
<string name="play_btn_text">ⵖⵔ</string>
|
||||||
|
<string name="settings_category_other_title">ⵢⴰⴹⵏ</string>
|
||||||
|
<string name="settings_category_video_audio_title">ⴰⴼⵉⴷⵢⵓ ⴷ ⵓⵎⵙⵍⴰⵢ</string>
|
||||||
|
<string name="download_dialog_title">ⴰⴳⵎ</string>
|
||||||
|
<string name="enable_watch_history_title">ⵥⵕ ⴰⵎⵣⵔⵓⵢ</string>
|
||||||
|
<string name="enable_search_history_title">ⴰⵎⵣⵔⵓⵢ ⵏ ⵓⵔⵣⵣⵓ</string>
|
||||||
|
<string name="black_theme_title">ⴰⴱⵔⴽⴰⵏ</string>
|
||||||
|
<string name="play_audio">ⴰⵎⵙⵍⴰⵢ</string>
|
||||||
|
<string name="play_with_kodi_title">ⵖⵔ ⵙ ⴽⵓⴷⵉ</string>
|
||||||
|
<string name="controls_add_to_playlist_title">ⵔⵏⵓ ⵖⵔ</string>
|
||||||
|
<string name="tab_choose">ⵙⵜⵉ ⴰⵙⴽⵙⵍ</string>
|
||||||
|
<string name="tab_new">ⴰⵙⴽⵙⵍ ⴰⵎⴰⵢⵏⵓ</string>
|
||||||
|
<string name="share_dialog_title">ⴱⴹⵓ ⵙ</string>
|
||||||
|
<string name="settings">ⵜⵉⵙⵖⴰⵍ</string>
|
||||||
|
<string name="search">ⵔⵣⵓ</string>
|
||||||
|
<string name="download">ⴰⴳⵎ</string>
|
||||||
|
<string name="share">ⴱⴹⵓ</string>
|
||||||
|
<string name="install">ⵙⵔⵙ</string>
|
||||||
|
<string name="cancel">ⵙⵔ</string>
|
||||||
|
<string name="video_detail_by">ⵙ %s</string>
|
||||||
|
<string name="channel_created_by">ⵔⵏⵏⵓ ⵜ %s</string>
|
||||||
|
<string name="feed_group_dialog_delete_message">ⵉⵙ ⵜⵅⵙⴷ ⴰⴷ ⵜⴽⴽⵙⴷ ⵜⵔⴰⴱⴱⵓⵜ ⴰ\?</string>
|
||||||
|
<string name="stop">ⴱⴷⴷ</string>
|
||||||
|
<string name="label_code">ⵉⵏⵉⴳⵍ</string>
|
||||||
|
<string name="grid">ⴰⵥⵟⵟⴰ</string>
|
||||||
|
<string name="list">ⵜⴰⵍⴳⴰⵎⵜ</string>
|
||||||
|
<string name="updates_setting_title">ⵜⵓⵙⴷⵖⵉⵏ</string>
|
||||||
|
<string name="decline">ⴰⴳⵉ</string>
|
||||||
|
<string name="accept">ⵜⴰⵢⵢⵉⵀⵜ</string>
|
||||||
|
<string name="resize_fill">ⴽⵜⵔ</string>
|
||||||
|
<string name="resize_fit">ⵙⴰⵙⵜⵡⴰ</string>
|
||||||
|
<string name="unmute">ⴽⴽⵙ ⴰⵙⵓⵔⵎ</string>
|
||||||
|
<string name="background_player">ⴰⵎⵖⵔⵉ ⴷⴼⴼⵉⵔ</string>
|
||||||
|
<string name="video_player">ⴰⵎⵖⵔⵉ ⵏ ⵓⴼⵉⴷⵢⵓ</string>
|
||||||
|
<string name="start_here_on_main">ⵖⵔ ⵙⴰ</string>
|
||||||
|
<string name="use_tor_title">ⵙⵙⵎⵔⵙ ⵟⵓⵕ</string>
|
||||||
|
<string name="detail_thumbnail_view_description">ⵖⵔ ⴰⴼⵉⴷⵢⵓ,ⴰⵣⵎⵣ:</string>
|
||||||
|
<string name="your_comment">ⵉⵅⴼⴰⵡⴰⵍ ⵏⵏⴽ (ⵙ ⵜⵏⴳⵍⵉⵣⵜ):</string>
|
||||||
|
<string name="what_device_headline">ⴰⵏⵖⵎⵉⵙ:</string>
|
||||||
|
<string name="error_snackbar_action">ⵎⵍ</string>
|
||||||
|
<string name="download_path_title">ⴰⵙⴷⴰⵡ ⵏ ⵓⴳⴰⵎ ⵓⴼⵉⴷⵢⵓ</string>
|
||||||
|
<string name="tab_subscriptions">ⵜⵉⵙⵓⵔⴰ</string>
|
||||||
|
<string name="tab_main">ⴰⵙⵏⵓⴱⴳ</string>
|
||||||
|
<string name="show_info">ⵙⵎⴰⵍ ⴰⵏⵖⵎⵉⵙ</string>
|
||||||
|
<string name="channel_unsubscribed">ⴽⴽⵙ ⵜⴰⵙⵓⵔⵉ ⵉ ⵓⵙⴰⵔⵓ</string>
|
||||||
|
<string name="unsubscribe">ⴽⴽⵙ ⵜⴰⵙⵓⵔⵉ</string>
|
||||||
|
<string name="subscribed_button_title">ⵉⵙⵙⵓⵔ</string>
|
||||||
|
<string name="subscribe_button_title">ⵙⵙⵓⵔ</string>
|
||||||
|
<string name="use_external_audio_player_title">ⵙⵎⵔⵙ ⵉⵎⵖⵔⵉ ⵏ ⵉⵎⵙⵍⵉ ⴰⴱⵕⵕⴰⵏⵉ</string>
|
||||||
|
<string name="use_external_video_player_title">ⵙⵎⵔⵙ ⵉⵎⵖⵔⵉ ⵏ ⵓⴼⵉⴷⵢⵓ ⴰⴱⵕⵕⴰⵏⵉ</string>
|
||||||
|
<string name="screen_rotation">ⵓⵜⵓⵢ</string>
|
||||||
|
<string name="choose_browser">ⵙⵜⵉ ⵉⵎⵉⵏⵉⴳ</string>
|
||||||
|
<string name="search_showing_result_for">ⴰⵙⵎⴰⵍ ⵏ ⵜⴰⵢⴰⴼⵓⵜ ⵉ: %s</string>
|
||||||
|
<string name="did_you_mean">ⵎⵉⵏ ⵜⵅⵙⴷ ⴰⴷ ⵜⵜ ⵜⵉⵏⵉⴷ: %1$s\?</string>
|
||||||
|
<string name="view_count_text">%1$s ⵏ ⵜⴰⵏⵏⴰⵢⵉⵏ</string>
|
||||||
|
<string name="main_bg_subtitle">ⴰⴷⵔ ⵅ \"ⵔⵣⵓ\"ⴰⴼⴰⴷ ⴰⴷ ⵜⵜⴰⵡⵍⴷ</string>
|
||||||
|
</resources>
|
|
@ -330,7 +330,7 @@
|
||||||
<string name="create_playlist">Нов Плейлист</string>
|
<string name="create_playlist">Нов Плейлист</string>
|
||||||
<string name="delete_playlist">Изтрий</string>
|
<string name="delete_playlist">Изтрий</string>
|
||||||
<string name="rename_playlist">"Преименувай "</string>
|
<string name="rename_playlist">"Преименувай "</string>
|
||||||
<string name="playlist_name_input">Име</string>
|
<string name="name">Име</string>
|
||||||
<string name="append_playlist">Добави Към Плейлист</string>
|
<string name="append_playlist">Добави Към Плейлист</string>
|
||||||
<string name="set_as_playlist_thumbnail">Задай като миниатюра на плейлиста</string>
|
<string name="set_as_playlist_thumbnail">Задай като миниатюра на плейлиста</string>
|
||||||
<string name="bookmark_playlist">Миниатюрата на плейлиста е сменена</string>
|
<string name="bookmark_playlist">Миниатюрата на плейлиста е сменена</string>
|
||||||
|
@ -347,7 +347,6 @@
|
||||||
<string name="caption_auto_generated">Авто-генерирани</string>
|
<string name="caption_auto_generated">Авто-генерирани</string>
|
||||||
<string name="caption_setting_title">Надписи</string>
|
<string name="caption_setting_title">Надписи</string>
|
||||||
<string name="caption_setting_description">Модифицирай мащаба на текста и фона на надписите. Изисква рестарт на приложението, за да се приложат промените.</string>
|
<string name="caption_setting_description">Модифицирай мащаба на текста и фона на надписите. Изисква рестарт на приложението, за да се приложат промените.</string>
|
||||||
<string name="enable_leak_canary_title">Включи LeakCanary</string>
|
|
||||||
<string name="enable_leak_canary_summary">Следенето за пропускане на памет може да направи приложението нестабилно</string>
|
<string name="enable_leak_canary_summary">Следенето за пропускане на памет може да направи приложението нестабилно</string>
|
||||||
<string name="enable_disposed_exceptions_title">Докладвай за извънредни грешки</string>
|
<string name="enable_disposed_exceptions_title">Докладвай за извънредни грешки</string>
|
||||||
<string name="import_export_title">Импортиране/Експортиране</string>
|
<string name="import_export_title">Импортиране/Експортиране</string>
|
||||||
|
@ -413,4 +412,5 @@
|
||||||
<string name="settings_category_updates_title">Промени</string>
|
<string name="settings_category_updates_title">Промени</string>
|
||||||
<string name="enable_playback_resume_title">Продължи възпроизвеждане</string>
|
<string name="enable_playback_resume_title">Продължи възпроизвеждане</string>
|
||||||
<string name="settings_category_clear_data_title">Изтрии данни</string>
|
<string name="settings_category_clear_data_title">Изтрии данни</string>
|
||||||
|
<string name="search_showing_result_for">Показване на резултати за: %s</string>
|
||||||
</resources>
|
</resources>
|
|
@ -196,4 +196,16 @@
|
||||||
<string name="volume_gesture_control_title">ভলিউম সংকেত নিয়ন্ত্রণ</string>
|
<string name="volume_gesture_control_title">ভলিউম সংকেত নিয়ন্ত্রণ</string>
|
||||||
<string name="missions_header_finished">সম্পূর্ণ</string>
|
<string name="missions_header_finished">সম্পূর্ণ</string>
|
||||||
<string name="list">তালিকা</string>
|
<string name="list">তালিকা</string>
|
||||||
|
<string name="enable_playback_state_lists_title">তালিকাতে পজিশন</string>
|
||||||
|
<string name="enable_playback_resume_summary">শেষ প্লেব্যাক পজিশন এ যাও</string>
|
||||||
|
<string name="enable_playback_resume_title">পুনরায় প্লে ব্যাক চালু করো</string>
|
||||||
|
<string name="enable_search_history_summary">সার্চগুলো স্থানীয়ভাবে জমা করো</string>
|
||||||
|
<string name="show_search_suggestions_summary">সার্চের সময় পরামর্শ দেখাও</string>
|
||||||
|
<string name="show_search_suggestions_title">সার্চ পরামর্শ</string>
|
||||||
|
<string name="player_gesture_controls_summary">প্লেয়ারের উজ্জ্বলতা এবং ভলিউম নিয়ন্ত্রণ করতে সংকেত ব্যবহার করো</string>
|
||||||
|
<string name="search_showing_result_for">রেজাল্ট দেখানো হচ্ছেঃ %s</string>
|
||||||
|
<string name="tab_about">সম্পর্কিত</string>
|
||||||
|
<string name="action_about">সম্পর্কিত</string>
|
||||||
|
<string name="title_activity_about">নিউপাইপ এর সম্বন্ধে</string>
|
||||||
|
<string name="trending">ট্রেন্ডিং</string>
|
||||||
</resources>
|
</resources>
|
|
@ -98,7 +98,7 @@
|
||||||
<string name="settings_category_player_title">প্লেয়ার</string>
|
<string name="settings_category_player_title">প্লেয়ার</string>
|
||||||
<string name="content_language_title">কন্টেন্ট এর জন্য পছন্দসই ভাষা</string>
|
<string name="content_language_title">কন্টেন্ট এর জন্য পছন্দসই ভাষা</string>
|
||||||
<string name="service_title">সেবা</string>
|
<string name="service_title">সেবা</string>
|
||||||
<string name="url_not_supported_toast">URL সমর্থিত নয়</string>
|
<string name="unsupported_url">URL সমর্থিত নয়</string>
|
||||||
<string name="show_next_and_similar_title">পরবর্তী এবং অনুরূপ ভিডিওগুলি দেখাও</string>
|
<string name="show_next_and_similar_title">পরবর্তী এবং অনুরূপ ভিডিওগুলি দেখাও</string>
|
||||||
<string name="next_video_title">পরবর্তী ভিডিও</string>
|
<string name="next_video_title">পরবর্তী ভিডিও</string>
|
||||||
<string name="download_dialog_title">ডাউনলোড</string>
|
<string name="download_dialog_title">ডাউনলোড</string>
|
||||||
|
@ -186,7 +186,6 @@
|
||||||
<string name="view_count_text">%1$s জন দেখেছে</string>
|
<string name="view_count_text">%1$s জন দেখেছে</string>
|
||||||
<string name="main_bg_subtitle">অনুসন্ধান এ চাপ দিয়ে শুরু করুন</string>
|
<string name="main_bg_subtitle">অনুসন্ধান এ চাপ দিয়ে শুরু করুন</string>
|
||||||
<string name="feed_create_new_group_button_title">নতুন</string>
|
<string name="feed_create_new_group_button_title">নতুন</string>
|
||||||
<string name="feed_group_dialog_name_input">নাম</string>
|
|
||||||
<string name="fragment_feed_title">নতুন কি</string>
|
<string name="fragment_feed_title">নতুন কি</string>
|
||||||
<string name="app_language_title">অ্যাপ এর ভাষা</string>
|
<string name="app_language_title">অ্যাপ এর ভাষা</string>
|
||||||
<string name="stop">বন্ধ করুন</string>
|
<string name="stop">বন্ধ করুন</string>
|
||||||
|
@ -239,7 +238,7 @@
|
||||||
<string name="invalid_url_toast">অবৈধ ইউ আর এল</string>
|
<string name="invalid_url_toast">অবৈধ ইউ আর এল</string>
|
||||||
<string name="download_to_sdcard_error_title">বাহ্যিক স্টোরেজ নেই</string>
|
<string name="download_to_sdcard_error_title">বাহ্যিক স্টোরেজ নেই</string>
|
||||||
<string name="search_history_deleted">সার্চ ইতিহাস ডিলিট হয়েছে।</string>
|
<string name="search_history_deleted">সার্চ ইতিহাস ডিলিট হয়েছে।</string>
|
||||||
<string name="playlist_name_input">নাম</string>
|
<string name="name">নাম</string>
|
||||||
<string name="rename_playlist">নাম পরিবর্তন</string>
|
<string name="rename_playlist">নাম পরিবর্তন</string>
|
||||||
<string name="action_history">ইতিহাস</string>
|
<string name="action_history">ইতিহাস</string>
|
||||||
<string name="read_full_license">লাইসেন্স পড়ুন</string>
|
<string name="read_full_license">লাইসেন্স পড়ুন</string>
|
||||||
|
@ -329,4 +328,13 @@
|
||||||
<string name="enable_search_history_summary">সার্চ গুলি স্থানীয় ভাবে জমা করুন</string>
|
<string name="enable_search_history_summary">সার্চ গুলি স্থানীয় ভাবে জমা করুন</string>
|
||||||
<string name="show_search_suggestions_summary">সার্চ এর সময় সাজেশন দেখান</string>
|
<string name="show_search_suggestions_summary">সার্চ এর সময় সাজেশন দেখান</string>
|
||||||
<string name="show_search_suggestions_title">সার্চ সাজেশন</string>
|
<string name="show_search_suggestions_title">সার্চ সাজেশন</string>
|
||||||
|
<string name="feed_notification_loading">ফিড লোড হচ্ছে…</string>
|
||||||
|
<string name="show_error">এরর দেখান</string>
|
||||||
|
<string name="select_a_playlist">একটি প্লে লিস্ট পছন্দ করুন</string>
|
||||||
|
<string name="tab_about">সম্পর্কিত</string>
|
||||||
|
<string name="title_licenses">থার্ড-পার্টি লাইসেন্স সমূহ</string>
|
||||||
|
<string name="action_about">সম্পর্কিত</string>
|
||||||
|
<string name="error_report_open_issue_button_text">গিটহাব এ এরর রিপোর্ট করুন</string>
|
||||||
|
<string name="restore_defaults">ডিফল্ট এ ফিরে যান</string>
|
||||||
|
<string name="search_showing_result_for">রেজাল্ট দেখান হচ্ছেঃ %s</string>
|
||||||
</resources>
|
</resources>
|
|
@ -38,7 +38,7 @@
|
||||||
<string name="content">Contingut</string>
|
<string name="content">Contingut</string>
|
||||||
<string name="show_age_restricted_content_title">Desactiva les restriccions per edat</string>
|
<string name="show_age_restricted_content_title">Desactiva les restriccions per edat</string>
|
||||||
<string name="video_is_age_restricted">Mostra el vídeo restringit per edat. Podeu permetre aquesta mena de continguts des dels paràmetres.</string>
|
<string name="video_is_age_restricted">Mostra el vídeo restringit per edat. Podeu permetre aquesta mena de continguts des dels paràmetres.</string>
|
||||||
<string name="duration_live">EN DIRECTE</string>
|
<string name="duration_live">Directe</string>
|
||||||
<string name="downloads">Baixades</string>
|
<string name="downloads">Baixades</string>
|
||||||
<string name="downloads_title">Baixades</string>
|
<string name="downloads_title">Baixades</string>
|
||||||
<string name="all">Tot</string>
|
<string name="all">Tot</string>
|
||||||
|
@ -105,7 +105,7 @@
|
||||||
<string name="create_playlist">Crea una llista de reproducció</string>
|
<string name="create_playlist">Crea una llista de reproducció</string>
|
||||||
<string name="delete_playlist">Elimina</string>
|
<string name="delete_playlist">Elimina</string>
|
||||||
<string name="rename_playlist">Canvia el nom</string>
|
<string name="rename_playlist">Canvia el nom</string>
|
||||||
<string name="playlist_name_input">Nom</string>
|
<string name="name">Nom</string>
|
||||||
<string name="append_playlist">Afegeix a una llista de reproducció</string>
|
<string name="append_playlist">Afegeix a una llista de reproducció</string>
|
||||||
<string name="import_title">Importa</string>
|
<string name="import_title">Importa</string>
|
||||||
<string name="import_from">Importa des de</string>
|
<string name="import_from">Importa des de</string>
|
||||||
|
@ -123,7 +123,7 @@
|
||||||
<string name="share_dialog_title">Comparteix-ho amb</string>
|
<string name="share_dialog_title">Comparteix-ho amb</string>
|
||||||
<string name="screen_rotation">rotació</string>
|
<string name="screen_rotation">rotació</string>
|
||||||
<string name="use_external_video_player_title">Reproductor de vídeo extern</string>
|
<string name="use_external_video_player_title">Reproductor de vídeo extern</string>
|
||||||
<string name="popup_mode_share_menu_title">Mode emergent del NewPipe</string>
|
<string name="popup_mode_share_menu_title">Mode emergent</string>
|
||||||
<string name="channel_unsubscribed">Heu eliminat la subscripció a aquest canal</string>
|
<string name="channel_unsubscribed">Heu eliminat la subscripció a aquest canal</string>
|
||||||
<string name="subscription_change_failed">No s\'ha pogut modificar la subscripció</string>
|
<string name="subscription_change_failed">No s\'ha pogut modificar la subscripció</string>
|
||||||
<string name="subscription_update_failed">No s\'ha pogut actualitzar la subscripció</string>
|
<string name="subscription_update_failed">No s\'ha pogut actualitzar la subscripció</string>
|
||||||
|
@ -140,13 +140,13 @@
|
||||||
<string name="show_higher_resolutions_title">Mostra resolucions superiors</string>
|
<string name="show_higher_resolutions_title">Mostra resolucions superiors</string>
|
||||||
<string name="show_higher_resolutions_summary">No tots els dispositius són compatibles amb la reproducció de vídeos en 2K/4K</string>
|
<string name="show_higher_resolutions_summary">No tots els dispositius són compatibles amb la reproducció de vídeos en 2K/4K</string>
|
||||||
<string name="play_with_kodi_title">Reprodueix amb el Kodi</string>
|
<string name="play_with_kodi_title">Reprodueix amb el Kodi</string>
|
||||||
<string name="kore_not_found">No s\'ha trobat l\'aplicació Kodi. Voleu instal·lar-la\?</string>
|
<string name="kore_not_found">No s\'ha trobat l\'aplicació Kore. Voleu instal·lar-la\?</string>
|
||||||
<string name="show_play_with_kodi_title">Mostra «Reprodueix amb el Kodi»</string>
|
<string name="show_play_with_kodi_title">Mostra «Reprodueix amb el Kodi»</string>
|
||||||
<string name="show_play_with_kodi_summary">Mostra una opció per reproduir un vídeo amb el centre multimèdia Kodi</string>
|
<string name="show_play_with_kodi_summary">Mostra una opció per reproduir un vídeo amb el centre multimèdia Kodi</string>
|
||||||
<string name="popup_remember_size_pos_title">Reproductor emergent intel·ligent</string>
|
<string name="popup_remember_size_pos_title">Reproductor emergent intel·ligent</string>
|
||||||
<string name="popup_remember_size_pos_summary">Recorda la darrera mida i posició del reproductor emergent</string>
|
<string name="popup_remember_size_pos_summary">Recorda la darrera mida i posició del reproductor emergent</string>
|
||||||
<string name="use_inexact_seek_title">Cerca ràpida poc precisa</string>
|
<string name="use_inexact_seek_title">Cerca ràpida poc precisa</string>
|
||||||
<string name="use_inexact_seek_summary">La cerca poc precisa permet que el reproductor cerqui una posició més ràpidament amb menys precisió</string>
|
<string name="use_inexact_seek_summary">La cerca poc precisa permet que el reproductor cerqui una posició més ràpidament amb menys precisió. Cerques de 5, 15 o 25 segons no hi funcionaran.</string>
|
||||||
<string name="download_thumbnail_title">Carrega les miniatures</string>
|
<string name="download_thumbnail_title">Carrega les miniatures</string>
|
||||||
<string name="thumbnail_cache_wipe_complete_notice">S\'ha eliminat la memòria cau d\'imatges</string>
|
<string name="thumbnail_cache_wipe_complete_notice">S\'ha eliminat la memòria cau d\'imatges</string>
|
||||||
<string name="metadata_cache_wipe_title">Elimina les metadades de la memòria cau</string>
|
<string name="metadata_cache_wipe_title">Elimina les metadades de la memòria cau</string>
|
||||||
|
@ -193,7 +193,7 @@
|
||||||
<string name="error_occurred_detail">S\'ha produït un error: %1$s</string>
|
<string name="error_occurred_detail">S\'ha produït un error: %1$s</string>
|
||||||
<string name="error_report_button_text">Informeu de l\'error per correu electrònic</string>
|
<string name="error_report_button_text">Informeu de l\'error per correu electrònic</string>
|
||||||
<string name="error_snackbar_message">S\'han produït alguns errors.</string>
|
<string name="error_snackbar_message">S\'han produït alguns errors.</string>
|
||||||
<string name="error_snackbar_action">INFORME</string>
|
<string name="error_snackbar_action">Informe</string>
|
||||||
<string name="what_device_headline">Informació:</string>
|
<string name="what_device_headline">Informació:</string>
|
||||||
<string name="what_happened_headline">Què ha passat:</string>
|
<string name="what_happened_headline">Què ha passat:</string>
|
||||||
<string name="your_comment">Comentari (en anglès):</string>
|
<string name="your_comment">Comentari (en anglès):</string>
|
||||||
|
@ -205,7 +205,7 @@
|
||||||
<string name="detail_dislikes_img_view_description">No m\'agrada</string>
|
<string name="detail_dislikes_img_view_description">No m\'agrada</string>
|
||||||
<string name="use_tor_title">Fes servir el Tor</string>
|
<string name="use_tor_title">Fes servir el Tor</string>
|
||||||
<string name="use_tor_summary">(En proves) Força el trànsit de baixada a través del Tor per a més privadesa (no compatible encara amb les emissions de vídeo).</string>
|
<string name="use_tor_summary">(En proves) Força el trànsit de baixada a través del Tor per a més privadesa (no compatible encara amb les emissions de vídeo).</string>
|
||||||
<string name="report_error">Notifiqueu un error</string>
|
<string name="report_error">Notifiqueu error</string>
|
||||||
<string name="user_report">Informe de l\'usuari</string>
|
<string name="user_report">Informe de l\'usuari</string>
|
||||||
<string name="search_no_results">Cap resultat</string>
|
<string name="search_no_results">Cap resultat</string>
|
||||||
<string name="empty_subscription_feed_subtitle">No hi ha res aquí</string>
|
<string name="empty_subscription_feed_subtitle">No hi ha res aquí</string>
|
||||||
|
@ -272,7 +272,7 @@
|
||||||
<string name="playback_speed_control">Controls de la velocitat de reproducció</string>
|
<string name="playback_speed_control">Controls de la velocitat de reproducció</string>
|
||||||
<string name="playback_tempo">Tempo</string>
|
<string name="playback_tempo">Tempo</string>
|
||||||
<string name="playback_pitch">To</string>
|
<string name="playback_pitch">To</string>
|
||||||
<string name="main_bg_subtitle">Feu un toc al botó de cerca per començar</string>
|
<string name="main_bg_subtitle">Toca \"Cercar\" per començar</string>
|
||||||
<string name="use_external_video_player_summary">Elimina l\'àudio en algunes resolucions</string>
|
<string name="use_external_video_player_summary">Elimina l\'àudio en algunes resolucions</string>
|
||||||
<string name="use_external_audio_player_title">Reproductor d\'àudio extern</string>
|
<string name="use_external_audio_player_title">Reproductor d\'àudio extern</string>
|
||||||
<string name="download_thumbnail_summary">Desactiveu-ho per no generar miniatures i estalviar dades i memòria. Canviant aquesta opció, s\'eliminarà la memòria cau d\'imatges tant de la memòria com de l\'emmagatzematge.</string>
|
<string name="download_thumbnail_summary">Desactiveu-ho per no generar miniatures i estalviar dades i memòria. Canviant aquesta opció, s\'eliminarà la memòria cau d\'imatges tant de la memòria com de l\'emmagatzematge.</string>
|
||||||
|
@ -329,7 +329,6 @@
|
||||||
<string name="resize_fill">Omple</string>
|
<string name="resize_fill">Omple</string>
|
||||||
<string name="resize_zoom">Escala</string>
|
<string name="resize_zoom">Escala</string>
|
||||||
<string name="caption_auto_generated">Generats automàticament</string>
|
<string name="caption_auto_generated">Generats automàticament</string>
|
||||||
<string name="enable_leak_canary_title">Habilita el LeakCanary</string>
|
|
||||||
<string name="previous_export">Darrera exportació</string>
|
<string name="previous_export">Darrera exportació</string>
|
||||||
<string name="subscriptions_import_unsuccessful">No s\'han pogut importar les subscripcions</string>
|
<string name="subscriptions_import_unsuccessful">No s\'han pogut importar les subscripcions</string>
|
||||||
<string name="subscriptions_export_unsuccessful">No s\'han pogut exportar les subscripcions</string>
|
<string name="subscriptions_export_unsuccessful">No s\'han pogut exportar les subscripcions</string>
|
||||||
|
@ -503,8 +502,34 @@
|
||||||
<string name="no_one_listening">Cap reproducció</string>
|
<string name="no_one_listening">Cap reproducció</string>
|
||||||
<plurals name="listening">
|
<plurals name="listening">
|
||||||
<item quantity="one">%s escoltant</item>
|
<item quantity="one">%s escoltant</item>
|
||||||
<item quantity="other">%s escoltant</item>
|
<item quantity="other">%s escoltants</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
<string name="localization_changes_requires_app_restart">Es canviarà la llengua en reiniciar l\'aplicació.</string>
|
<string name="localization_changes_requires_app_restart">Es canviarà la llengua en reiniciar l\'aplicació.</string>
|
||||||
<string name="default_kiosk_page_summary">Tendències</string>
|
<string name="default_kiosk_page_summary">Tendències</string>
|
||||||
|
<string name="show_original_time_ago_title">Ensenya el temps passat original sobre els \"items\"</string>
|
||||||
|
<string name="playlist_no_uploader">Auto-generat (no es troba cap uploader)</string>
|
||||||
|
<string name="unmute">Desactivar Silenci</string>
|
||||||
|
<string name="mute">Silenciar</string>
|
||||||
|
<string name="subtitle_activity_recaptcha">Prem \"Fet\" quan estigui resolt</string>
|
||||||
|
<string name="infinite_videos">∞ vídeos</string>
|
||||||
|
<string name="more_than_100_videos">100+ vídeos</string>
|
||||||
|
<string name="permission_display_over_apps">Dona permís per mostrarse per sobre d\'altres apps.</string>
|
||||||
|
<string name="help">Ajuda</string>
|
||||||
|
<string name="artists">Artistes</string>
|
||||||
|
<string name="albums">Albums</string>
|
||||||
|
<string name="songs">Cançons</string>
|
||||||
|
<string name="videos_string">Vídeos</string>
|
||||||
|
<string name="restricted_video">Aquest vídeo té una restricció per edat.
|
||||||
|
\n
|
||||||
|
\nSi desitges visualitzar-lo, activa el «Contingut restringit per edat» en la configuració.</string>
|
||||||
|
<string name="youtube_restricted_mode_enabled_title">Mode de restricció del YouTube</string>
|
||||||
|
<string name="peertube_instance_add_exists">La instància introduïda ja existeix</string>
|
||||||
|
<string name="peertube_instance_add_https_only">Només estàn soportades URLs HTTPS</string>
|
||||||
|
<string name="peertube_instance_add_fail">No ha estat possible validar la instància</string>
|
||||||
|
<string name="peertube_instance_add_help">Introdueix l\'enllaç d\'una instància</string>
|
||||||
|
<string name="peertube_instance_add_title">Afegeix-hi una instància</string>
|
||||||
|
<string name="peertube_instance_url_help">Troba les instàncies que t\'agraden en %s</string>
|
||||||
|
<string name="peertube_instance_url_summary">Selecciona les teves instàncies preferides del PeerTube</string>
|
||||||
|
<string name="peertube_instance_url_title">Instàncies del PeerTube</string>
|
||||||
|
<string name="seek_duration_title">Avançar/-rebobinar duració cerca</string>
|
||||||
</resources>
|
</resources>
|
|
@ -31,7 +31,7 @@
|
||||||
<string name="settings_category_player_title">کارپێکەر</string>
|
<string name="settings_category_player_title">کارپێکەر</string>
|
||||||
<string name="import_title">هێنانەوە</string>
|
<string name="import_title">هێنانەوە</string>
|
||||||
<string name="error_report_button_text">سکاڵا لەسەر کێشە لەڕێگای ئیمێڵ</string>
|
<string name="error_report_button_text">سکاڵا لەسەر کێشە لەڕێگای ئیمێڵ</string>
|
||||||
<string name="playlist_name_input">ناو</string>
|
<string name="name">ناو</string>
|
||||||
<string name="error_postprocessing_failed">چارەسەرکردن شکستی هێنا</string>
|
<string name="error_postprocessing_failed">چارەسەرکردن شکستی هێنا</string>
|
||||||
<string name="minimize_on_exit_title">بچوکبوونەوە لەکاتی گۆڕینی داوانامە</string>
|
<string name="minimize_on_exit_title">بچوکبوونەوە لەکاتی گۆڕینی داوانامە</string>
|
||||||
<string name="feed_page_summary">پەڕەی نوێترینەکان</string>
|
<string name="feed_page_summary">پەڕەی نوێترینەکان</string>
|
||||||
|
@ -141,7 +141,6 @@
|
||||||
<item quantity="other">%s گوێی لێدەگرن</item>
|
<item quantity="other">%s گوێی لێدەگرن</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
<string name="msg_running">داگرتنەکانی داوانامە</string>
|
<string name="msg_running">داگرتنەکانی داوانامە</string>
|
||||||
<string name="feed_group_dialog_name_input">ناو</string>
|
|
||||||
<string name="channels">کەناڵەکان</string>
|
<string name="channels">کەناڵەکان</string>
|
||||||
<string name="action_history">مێژوو</string>
|
<string name="action_history">مێژوو</string>
|
||||||
<string name="subscriptions_export_unsuccessful">ناتوانرێ بەشدارییەکان پاشەکەوت بکرێن</string>
|
<string name="subscriptions_export_unsuccessful">ناتوانرێ بەشدارییەکان پاشەکەوت بکرێن</string>
|
||||||
|
@ -248,7 +247,6 @@
|
||||||
<string name="channel_page_summary">پەڕەی کەناڵەکان</string>
|
<string name="channel_page_summary">پەڕەی کەناڵەکان</string>
|
||||||
<string name="switch_to_background">گۆڕین بۆ پاشبنەما</string>
|
<string name="switch_to_background">گۆڕین بۆ پاشبنەما</string>
|
||||||
<string name="infinite_videos">∞ ڤیدیۆ</string>
|
<string name="infinite_videos">∞ ڤیدیۆ</string>
|
||||||
<string name="enable_leak_canary_title">چالاککردنی LeakCanary</string>
|
|
||||||
<string name="use_inexact_seek_title">بەکارهێنانی بردنەپێشی ناتەواوی خێرا</string>
|
<string name="use_inexact_seek_title">بەکارهێنانی بردنەپێشی ناتەواوی خێرا</string>
|
||||||
<string name="error_occurred_detail">هەڵەیەک ڕوویدا : %1$s</string>
|
<string name="error_occurred_detail">هەڵەیەک ڕوویدا : %1$s</string>
|
||||||
<string name="download_path_dialog_title">بوخچەی داگرتن بۆ پەڕگەی ڤیدیۆکان هەڵبژێرە</string>
|
<string name="download_path_dialog_title">بوخچەی داگرتن بۆ پەڕگەی ڤیدیۆکان هەڵبژێرە</string>
|
||||||
|
@ -556,7 +554,7 @@
|
||||||
<string name="install">دامەزراندن</string>
|
<string name="install">دامەزراندن</string>
|
||||||
<string name="info_dir_created">شوێنی داگرتن دانرا \'%1$s\'</string>
|
<string name="info_dir_created">شوێنی داگرتن دانرا \'%1$s\'</string>
|
||||||
<string name="videos_string">ڤیدیۆکان</string>
|
<string name="videos_string">ڤیدیۆکان</string>
|
||||||
<string name="url_not_supported_toast">بەستەرەکە پشتگیری نەکراوە</string>
|
<string name="unsupported_url">بەستەرەکە پشتگیری نەکراوە</string>
|
||||||
<string name="playback_pitch">شەپۆلی دەنگ</string>
|
<string name="playback_pitch">شەپۆلی دەنگ</string>
|
||||||
<string name="minimize_on_exit_summary">کرداری کاتی گۆڕین بۆ داوانامەیەکی تر لە کارپێکەری ڤیدیۆییەوە — %s</string>
|
<string name="minimize_on_exit_summary">کرداری کاتی گۆڕین بۆ داوانامەیەکی تر لە کارپێکەری ڤیدیۆییەوە — %s</string>
|
||||||
<string name="msg_copied">لەبەرگیرایەوە</string>
|
<string name="msg_copied">لەبەرگیرایەوە</string>
|
||||||
|
|
|
@ -108,7 +108,7 @@
|
||||||
<string name="checksum">Kontrolní součet</string>
|
<string name="checksum">Kontrolní součet</string>
|
||||||
<string name="no_available_dir">Určete prosím složku pro stahování později v nastavení</string>
|
<string name="no_available_dir">Určete prosím složku pro stahování později v nastavení</string>
|
||||||
<string name="user_report">Hlášení uživatele</string>
|
<string name="user_report">Hlášení uživatele</string>
|
||||||
<string name="info_labels">Co:\\nŽádost:\\nJazyk obsahu:\\nSlužba:\\nČas GMT:\\nBalíček:\\nVerze:\\nVerze OS:</string>
|
<string name="info_labels">Co:\\nŽádost:\\nJazyk obsahu\\nZemě obsahu:\\nJazyk aplikace:\\nSlužba:\\nČas GMT:\\nBalíček:\\nVerze:\\nVerze OS:</string>
|
||||||
<string name="all">Vše</string>
|
<string name="all">Vše</string>
|
||||||
<string name="channel">Kanál</string>
|
<string name="channel">Kanál</string>
|
||||||
<string name="yes">Ano</string>
|
<string name="yes">Ano</string>
|
||||||
|
@ -116,8 +116,8 @@
|
||||||
<string name="short_thousand">tis.</string>
|
<string name="short_thousand">tis.</string>
|
||||||
<string name="open_in_popup_mode">Otevřít ve vyskakovacím okně</string>
|
<string name="open_in_popup_mode">Otevřít ve vyskakovacím okně</string>
|
||||||
<string name="short_million">mil.</string>
|
<string name="short_million">mil.</string>
|
||||||
<string name="msg_popup_permission">Toto oprávnění je vyžadováno pro
|
<string name="msg_popup_permission">Toto oprávnění je vyžadováno
|
||||||
otevření ve vyskakovacím okně</string>
|
\npro otevření ve vyskakovacím okně</string>
|
||||||
<string name="use_external_video_player_summary">Odstraňuje zvuk v některých rozlišeních</string>
|
<string name="use_external_video_player_summary">Odstraňuje zvuk v některých rozlišeních</string>
|
||||||
<string name="show_higher_resolutions_title">Zobrazovat vyšší rozlišení</string>
|
<string name="show_higher_resolutions_title">Zobrazovat vyšší rozlišení</string>
|
||||||
<string name="show_higher_resolutions_summary">Pouze některá zařízení dokáží přehrát 2K/4K videa</string>
|
<string name="show_higher_resolutions_summary">Pouze některá zařízení dokáží přehrát 2K/4K videa</string>
|
||||||
|
@ -297,7 +297,7 @@ otevření ve vyskakovacím okně</string>
|
||||||
<string name="create_playlist">Nový playlist</string>
|
<string name="create_playlist">Nový playlist</string>
|
||||||
<string name="delete_playlist">Vymazat</string>
|
<string name="delete_playlist">Vymazat</string>
|
||||||
<string name="rename_playlist">Přejmenovat</string>
|
<string name="rename_playlist">Přejmenovat</string>
|
||||||
<string name="playlist_name_input">Jméno</string>
|
<string name="name">Jméno</string>
|
||||||
<string name="append_playlist">Přidat do playlistu</string>
|
<string name="append_playlist">Přidat do playlistu</string>
|
||||||
<string name="set_as_playlist_thumbnail">Nastavit jako náhled playlistu</string>
|
<string name="set_as_playlist_thumbnail">Nastavit jako náhled playlistu</string>
|
||||||
<string name="bookmark_playlist">Přidat playlist do záložek</string>
|
<string name="bookmark_playlist">Přidat playlist do záložek</string>
|
||||||
|
@ -312,8 +312,7 @@ otevření ve vyskakovacím okně</string>
|
||||||
<string name="resize_fill">Vyplnit</string>
|
<string name="resize_fill">Vyplnit</string>
|
||||||
<string name="resize_zoom">Zvětšit</string>
|
<string name="resize_zoom">Zvětšit</string>
|
||||||
<string name="settings_category_debug_title">Ladění</string>
|
<string name="settings_category_debug_title">Ladění</string>
|
||||||
<string name="caption_auto_generated">"Automaticky generováno "</string>
|
<string name="caption_auto_generated">Automaticky generováno</string>
|
||||||
<string name="enable_leak_canary_title">LeakCanary</string>
|
|
||||||
<string name="enable_leak_canary_summary">Monitoring úniku paměti může způsobit nereagování aplikace při heap dumpingu</string>
|
<string name="enable_leak_canary_summary">Monitoring úniku paměti může způsobit nereagování aplikace při heap dumpingu</string>
|
||||||
<string name="enable_disposed_exceptions_title">Nahlásit mimo-cyklické chyby</string>
|
<string name="enable_disposed_exceptions_title">Nahlásit mimo-cyklické chyby</string>
|
||||||
<string name="enable_disposed_exceptions_summary">Vynutit hlášení nedoručitelných výjimek Rx mimo životnost fragmentu nebo aktivity po odstranění</string>
|
<string name="enable_disposed_exceptions_summary">Vynutit hlášení nedoručitelných výjimek Rx mimo životnost fragmentu nebo aktivity po odstranění</string>
|
||||||
|
@ -333,12 +332,9 @@ otevření ve vyskakovacím okně</string>
|
||||||
<string name="invalid_file">Soubor neexistuje nebo chybí oprávnění k jeho čtení či zápisu</string>
|
<string name="invalid_file">Soubor neexistuje nebo chybí oprávnění k jeho čtení či zápisu</string>
|
||||||
<string name="file_name_empty_error">Název souboru nesmí být prázdný</string>
|
<string name="file_name_empty_error">Název souboru nesmí být prázdný</string>
|
||||||
<string name="error_occurred_detail">Došlo k chybě: %1$s</string>
|
<string name="error_occurred_detail">Došlo k chybě: %1$s</string>
|
||||||
<string name="import_export_title">Import/export
|
<string name="import_export_title">Import/export</string>
|
||||||
\n</string>
|
<string name="import_title">Importovat</string>
|
||||||
<string name="import_title">Importovat
|
<string name="import_from">Importovat z</string>
|
||||||
\n</string>
|
|
||||||
<string name="import_from">Importovat z
|
|
||||||
\n</string>
|
|
||||||
<string name="export_to">Exportovat do</string>
|
<string name="export_to">Exportovat do</string>
|
||||||
<string name="import_ongoing">Importuji…</string>
|
<string name="import_ongoing">Importuji…</string>
|
||||||
<string name="export_ongoing">Exportuji…</string>
|
<string name="export_ongoing">Exportuji…</string>
|
||||||
|
@ -580,7 +576,6 @@ otevření ve vyskakovacím okně</string>
|
||||||
<item quantity="other">%d vybráno</item>
|
<item quantity="other">%d vybráno</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
<string name="feed_group_dialog_empty_name">Prázdné jméno skupiny</string>
|
<string name="feed_group_dialog_empty_name">Prázdné jméno skupiny</string>
|
||||||
<string name="feed_group_dialog_name_input">Jméno</string>
|
|
||||||
<string name="feed_group_dialog_delete_message">Přejete si smazat tuto skupinu\?</string>
|
<string name="feed_group_dialog_delete_message">Přejete si smazat tuto skupinu\?</string>
|
||||||
<string name="feed_create_new_group_button_title">Nová</string>
|
<string name="feed_create_new_group_button_title">Nová</string>
|
||||||
<string name="settings_category_feed_title">Novinky</string>
|
<string name="settings_category_feed_title">Novinky</string>
|
||||||
|
@ -624,4 +619,12 @@ otevření ve vyskakovacím okně</string>
|
||||||
<string name="video_detail_by">%s</string>
|
<string name="video_detail_by">%s</string>
|
||||||
<string name="channel_created_by">Založil %s</string>
|
<string name="channel_created_by">Založil %s</string>
|
||||||
<string name="detail_sub_channel_thumbnail_view_description">Miniatura avatara kanálu</string>
|
<string name="detail_sub_channel_thumbnail_view_description">Miniatura avatara kanálu</string>
|
||||||
|
<string name="playlist_page_summary">Strana playlistů</string>
|
||||||
|
<string name="feed_group_show_only_ungrouped_subscriptions">Ukázat jen neseskupené objednávky</string>
|
||||||
|
<string name="no_playlist_bookmarked_yet">Zatím žádné záložky playlistů</string>
|
||||||
|
<string name="select_a_playlist">Vybrat playlist</string>
|
||||||
|
<string name="error_report_open_github_notice">Prosím, ověřte, zda chyba již existuje. Pokud založíte duplikovaný tiket, obíráte nás o čas, který bychom mohli věnovat řešení skutečných chyb.</string>
|
||||||
|
<string name="error_report_open_issue_button_text">Podat hlášení na GitHubu</string>
|
||||||
|
<string name="copy_for_github">Zkopírovat formátované hlášení</string>
|
||||||
|
<string name="search_showing_result_for">Ukazuji výsledky pro: %s</string>
|
||||||
</resources>
|
</resources>
|
|
@ -332,7 +332,7 @@
|
||||||
<string name="create_playlist">Ny spilleliste</string>
|
<string name="create_playlist">Ny spilleliste</string>
|
||||||
<string name="delete_playlist">Slet</string>
|
<string name="delete_playlist">Slet</string>
|
||||||
<string name="rename_playlist">Omdøb</string>
|
<string name="rename_playlist">Omdøb</string>
|
||||||
<string name="playlist_name_input">Navn</string>
|
<string name="name">Navn</string>
|
||||||
<string name="append_playlist">Føj til spilleliste</string>
|
<string name="append_playlist">Føj til spilleliste</string>
|
||||||
<string name="delete_playlist_prompt">Slet denne spilleliste\?</string>
|
<string name="delete_playlist_prompt">Slet denne spilleliste\?</string>
|
||||||
<string name="playlist_creation_success">Spilleliste oprettet</string>
|
<string name="playlist_creation_success">Spilleliste oprettet</string>
|
||||||
|
@ -409,7 +409,6 @@
|
||||||
<string name="playlist_thumbnail_change_success">Miniaturebillede for spilleliste ændret.</string>
|
<string name="playlist_thumbnail_change_success">Miniaturebillede for spilleliste ændret.</string>
|
||||||
<string name="playlist_delete_failure">Kunne ikke slette spilleliste.</string>
|
<string name="playlist_delete_failure">Kunne ikke slette spilleliste.</string>
|
||||||
<string name="caption_setting_description">Ændr undertekststørrelse og baggrundsstil. Kræver genstart af appen for at træde i kraft.</string>
|
<string name="caption_setting_description">Ændr undertekststørrelse og baggrundsstil. Kræver genstart af appen for at træde i kraft.</string>
|
||||||
<string name="enable_leak_canary_title">Aktiver LeakCanary</string>
|
|
||||||
<string name="enable_leak_canary_summary">Monitorering for hukommelseslækager kan få appen til ikke at svare under heap dumping</string>
|
<string name="enable_leak_canary_summary">Monitorering for hukommelseslækager kan få appen til ikke at svare under heap dumping</string>
|
||||||
<string name="enable_disposed_exceptions_title">Rapporter out-of-lifecycle-fejl</string>
|
<string name="enable_disposed_exceptions_title">Rapporter out-of-lifecycle-fejl</string>
|
||||||
<string name="import_export_title">Importer/eksporter</string>
|
<string name="import_export_title">Importer/eksporter</string>
|
||||||
|
|
|
@ -33,8 +33,8 @@
|
||||||
<string name="list_thumbnail_view_description">Video-Vorschaubild</string>
|
<string name="list_thumbnail_view_description">Video-Vorschaubild</string>
|
||||||
<string name="detail_thumbnail_view_description">Video abspielen, Dauer:</string>
|
<string name="detail_thumbnail_view_description">Video abspielen, Dauer:</string>
|
||||||
<string name="detail_uploader_thumbnail_view_description">Avatarbild des Uploaders</string>
|
<string name="detail_uploader_thumbnail_view_description">Avatarbild des Uploaders</string>
|
||||||
<string name="detail_dislikes_img_view_description">Gefällt nicht</string>
|
<string name="detail_dislikes_img_view_description">Gefällt mir nicht</string>
|
||||||
<string name="detail_likes_img_view_description">Gefällt</string>
|
<string name="detail_likes_img_view_description">Gefällt mir</string>
|
||||||
<string name="use_external_video_player_title">Externen Video-Player verwenden</string>
|
<string name="use_external_video_player_title">Externen Video-Player verwenden</string>
|
||||||
<string name="use_external_audio_player_title">Externen Audio-Player verwenden</string>
|
<string name="use_external_audio_player_title">Externen Audio-Player verwenden</string>
|
||||||
<string name="background_player_playing_toast">Im Hintergrund abspielen</string>
|
<string name="background_player_playing_toast">Im Hintergrund abspielen</string>
|
||||||
|
@ -310,7 +310,6 @@
|
||||||
<string name="file_name_empty_error">Dateiname darf nicht leer sein</string>
|
<string name="file_name_empty_error">Dateiname darf nicht leer sein</string>
|
||||||
<string name="error_occurred_detail">Ein Fehler ist aufgetreten: %1$s</string>
|
<string name="error_occurred_detail">Ein Fehler ist aufgetreten: %1$s</string>
|
||||||
<string name="caption_auto_generated">Automatisch erzeugt</string>
|
<string name="caption_auto_generated">Automatisch erzeugt</string>
|
||||||
<string name="enable_leak_canary_title">LeakCanary</string>
|
|
||||||
<string name="import_from">Import von</string>
|
<string name="import_from">Import von</string>
|
||||||
<string name="export_to">Export nach</string>
|
<string name="export_to">Export nach</string>
|
||||||
<string name="import_ongoing">Importiere…</string>
|
<string name="import_ongoing">Importiere…</string>
|
||||||
|
@ -329,7 +328,7 @@
|
||||||
<string name="invalid_source">Datei-/Inhaltsquelle existiert nicht</string>
|
<string name="invalid_source">Datei-/Inhaltsquelle existiert nicht</string>
|
||||||
<string name="export_complete_toast">Exportiert</string>
|
<string name="export_complete_toast">Exportiert</string>
|
||||||
<string name="import_complete_toast">Importiert</string>
|
<string name="import_complete_toast">Importiert</string>
|
||||||
<string name="playlist_name_input">Name</string>
|
<string name="name">Name</string>
|
||||||
<string name="import_export_title">Import/Export</string>
|
<string name="import_export_title">Import/Export</string>
|
||||||
<string name="import_title">Import</string>
|
<string name="import_title">Import</string>
|
||||||
<string name="subscriptions_import_unsuccessful">Abonnements konnten nicht importiert werden</string>
|
<string name="subscriptions_import_unsuccessful">Abonnements konnten nicht importiert werden</string>
|
||||||
|
@ -562,7 +561,6 @@
|
||||||
<item quantity="other">%d ausgewählte</item>
|
<item quantity="other">%d ausgewählte</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
<string name="feed_group_dialog_empty_name">Leerer Gruppenname</string>
|
<string name="feed_group_dialog_empty_name">Leerer Gruppenname</string>
|
||||||
<string name="feed_group_dialog_name_input">Name</string>
|
|
||||||
<string name="feed_group_dialog_delete_message">Möchtest du diese Gruppe löschen\?</string>
|
<string name="feed_group_dialog_delete_message">Möchtest du diese Gruppe löschen\?</string>
|
||||||
<string name="feed_create_new_group_button_title">Neu</string>
|
<string name="feed_create_new_group_button_title">Neu</string>
|
||||||
<string name="feed_update_threshold_option_always_update">Immer aktualisieren</string>
|
<string name="feed_update_threshold_option_always_update">Immer aktualisieren</string>
|
||||||
|
@ -579,7 +577,7 @@
|
||||||
<string name="feed_oldest_subscription_update">Abos zuletzt aktualisiert: %s</string>
|
<string name="feed_oldest_subscription_update">Abos zuletzt aktualisiert: %s</string>
|
||||||
<string name="feed_update_threshold_title">Grenzwert für Feed-Aktualisierung</string>
|
<string name="feed_update_threshold_title">Grenzwert für Feed-Aktualisierung</string>
|
||||||
<string name="feed_use_dedicated_fetch_method_title">Aus fest zugeordnetem Feed abholen wenn verfügbar</string>
|
<string name="feed_use_dedicated_fetch_method_title">Aus fest zugeordnetem Feed abholen wenn verfügbar</string>
|
||||||
<string name="feed_use_dedicated_fetch_method_summary">Steht in manchen Diensten zur Verfügung, ist meist viel schneller, liefert aber eventuell eine eingeschränkte Anzahl an Elementen und oft inkomplette Informationen (z. B. keine Videolänge, keinen Elementtyp, keinen Live-Status).</string>
|
<string name="feed_use_dedicated_fetch_method_summary">Steht in manchen Diensten zur Verfügung, ist meist viel schneller, liefert aber eventuell eine eingeschränkte Anzahl an Elementen und oft unvollständige Informationen (z. B. keine Videolänge, keinen Elementtyp, keinen Live-Status).</string>
|
||||||
<string name="feed_use_dedicated_fetch_method_help_text">Glaubst du, dass das Laden von Feeds zu langsam ist\? Wenn ja, versuche den Schnelllademodus einzuschalten (du kannst ihn in den Einstellungen oder über die Schaltfläche unten ändern).
|
<string name="feed_use_dedicated_fetch_method_help_text">Glaubst du, dass das Laden von Feeds zu langsam ist\? Wenn ja, versuche den Schnelllademodus einzuschalten (du kannst ihn in den Einstellungen oder über die Schaltfläche unten ändern).
|
||||||
\n
|
\n
|
||||||
\nNewPipe bietet zwei Feed-Ladestrategien:
|
\nNewPipe bietet zwei Feed-Ladestrategien:
|
||||||
|
@ -614,4 +612,9 @@
|
||||||
<string name="channel_created_by">Erstellt von %s</string>
|
<string name="channel_created_by">Erstellt von %s</string>
|
||||||
<string name="video_detail_by">Von %s</string>
|
<string name="video_detail_by">Von %s</string>
|
||||||
<string name="feed_group_show_only_ungrouped_subscriptions">Nur nicht gruppierte Abonnements anzeigen</string>
|
<string name="feed_group_show_only_ungrouped_subscriptions">Nur nicht gruppierte Abonnements anzeigen</string>
|
||||||
|
<string name="playlist_page_summary">Playlist-Seite</string>
|
||||||
|
<string name="no_playlist_bookmarked_yet">Bisher keine Lesezeichen für Wiedergabelisten</string>
|
||||||
|
<string name="select_a_playlist">Playlist auswählen</string>
|
||||||
|
<string name="error_report_open_github_notice">Bitte überprüfen Sie, ob es schon Fragen zu diesem Thema gibt. Doppelt erstellte Tickets kosten uns Zeit, die wir nutzen könnten, um diesen Fehler zu beheben.</string>
|
||||||
|
<string name="search_showing_result_for">Zeige Ergebnisse für: %s</string>
|
||||||
</resources>
|
</resources>
|
|
@ -122,15 +122,15 @@
|
||||||
<string name="auto_queue_title">Αυτόματη πρόσθεση της επόμενης ροής στην ουρά</string>
|
<string name="auto_queue_title">Αυτόματη πρόσθεση της επόμενης ροής στην ουρά</string>
|
||||||
<string name="auto_queue_summary">Αυτόματη πρόσθεση μιας σχετικής ροής όταν αναπαράγεται η προηγούμενη ροή σε μια μη-επαναλαμβανόμενη ουρά</string>
|
<string name="auto_queue_summary">Αυτόματη πρόσθεση μιας σχετικής ροής όταν αναπαράγεται η προηγούμενη ροή σε μια μη-επαναλαμβανόμενη ουρά</string>
|
||||||
<string name="player_gesture_controls_title">Έλεγχος αναπαραγωγής με χειρονομίες</string>
|
<string name="player_gesture_controls_title">Έλεγχος αναπαραγωγής με χειρονομίες</string>
|
||||||
<string name="player_gesture_controls_summary">Χρήση χειρονομιών για τον έλεγχο της φωτεινότητας και της έντασης ήχου της εφαρμογής</string>
|
<string name="player_gesture_controls_summary">Χρήση χειρονομιών για τον έλεγχο της φωτεινότητας και της έντασης ήχου</string>
|
||||||
<string name="show_search_suggestions_summary">Εμφάνιση υποδείξεων ενώ κάνετε αναζήτηση</string>
|
<string name="show_search_suggestions_summary">Εμφάνιση υποδείξεων ενώ κάνετε αναζήτηση</string>
|
||||||
<string name="enable_search_history_summary">Αποθήκευση αναζητήσεων στη συσκευή</string>
|
<string name="enable_search_history_summary">Αποθήκευση αναζητήσεων στη συσκευή</string>
|
||||||
<string name="enable_watch_history_title">Προβολή Ιστορικού</string>
|
<string name="enable_watch_history_title">Προβολή Ιστορικού</string>
|
||||||
<string name="enable_watch_history_summary">Κρατήστε ιστορικό των βίντεο που έχετε δει</string>
|
<string name="enable_watch_history_summary">Κρατήστε ιστορικό των βίντεο που έχετε δει</string>
|
||||||
<string name="resume_on_audio_focus_gain_title">Συνέχεια όταν η εφαρμογή έρθει σε πρώτο πλάνο</string>
|
<string name="resume_on_audio_focus_gain_title">Συνέχεια αναπαραγωγής</string>
|
||||||
<string name="resume_on_audio_focus_gain_summary">Συνέχιση της αναπαραγωγής έπειτα από διακοπές (π.χ.: κλήσεις)</string>
|
<string name="resume_on_audio_focus_gain_summary">Συνέχιση της αναπαραγωγής έπειτα από διακοπές (π.χ. κλήσεις)</string>
|
||||||
<string name="show_hold_to_append_title">Εμφάνιση της βοήθειας \"Πιέστε παρατεταμένα για πρόσθεση\"</string>
|
<string name="show_hold_to_append_title">Εμφάνιση της βοήθειας \"Πιέστε παρατεταμένα για πρόσθεση\"</string>
|
||||||
<string name="show_hold_to_append_summary">Εμφάνιση της βοήθειας όταν έχει πατηθεί το κουμπί Παρασκηνίου ή Αναδυόμενου παραθύρου στη σελίδα λεπτομερειών του βίντεο</string>
|
<string name="show_hold_to_append_summary">Εμφάνιση υπόδειξης όταν πατηθεί το κουμπί Παρασκηνίου ή Αναδυόμενου παραθύρου στη σελίδα λεπτομερειών του βίντεο</string>
|
||||||
<string name="default_content_country_title">Προεπιλεγμένη χώρα περιεχομένου</string>
|
<string name="default_content_country_title">Προεπιλεγμένη χώρα περιεχομένου</string>
|
||||||
<string name="service_title">Υπηρεσία</string>
|
<string name="service_title">Υπηρεσία</string>
|
||||||
<string name="settings_category_player_title">Συσκευή Αναπαραγωγής</string>
|
<string name="settings_category_player_title">Συσκευή Αναπαραγωγής</string>
|
||||||
|
@ -332,7 +332,7 @@
|
||||||
<string name="create_playlist">Νέα Λίστα Αναπαραγωγής</string>
|
<string name="create_playlist">Νέα Λίστα Αναπαραγωγής</string>
|
||||||
<string name="delete_playlist">Διαγραφή</string>
|
<string name="delete_playlist">Διαγραφή</string>
|
||||||
<string name="rename_playlist">Μετονομασία</string>
|
<string name="rename_playlist">Μετονομασία</string>
|
||||||
<string name="playlist_name_input">Όνομα</string>
|
<string name="name">Όνομα</string>
|
||||||
<string name="append_playlist">Προσθήκη στη Λίστα</string>
|
<string name="append_playlist">Προσθήκη στη Λίστα</string>
|
||||||
<string name="set_as_playlist_thumbnail">Ορισμός ως μικρογραφία λίστας αναπαραγωγής</string>
|
<string name="set_as_playlist_thumbnail">Ορισμός ως μικρογραφία λίστας αναπαραγωγής</string>
|
||||||
<string name="bookmark_playlist">Προσθήκη Σελιδοδείκτη στη Λίστα</string>
|
<string name="bookmark_playlist">Προσθήκη Σελιδοδείκτη στη Λίστα</string>
|
||||||
|
@ -349,7 +349,6 @@
|
||||||
<string name="caption_auto_generated">Αυτόματοι</string>
|
<string name="caption_auto_generated">Αυτόματοι</string>
|
||||||
<string name="caption_setting_title">Υπότιτλοι</string>
|
<string name="caption_setting_title">Υπότιτλοι</string>
|
||||||
<string name="caption_setting_description">Τροποποίηση του μεγέθους και του φόντου των υπότιτλων. Απαιτεί επανεκκίνηση της εφαρμογής.</string>
|
<string name="caption_setting_description">Τροποποίηση του μεγέθους και του φόντου των υπότιτλων. Απαιτεί επανεκκίνηση της εφαρμογής.</string>
|
||||||
<string name="enable_leak_canary_title">Ενεργοποίηση του LeakCanary</string>
|
|
||||||
<string name="enable_leak_canary_summary">Η παρακολούθηση των διαρροών μνήμης μπορεί να προκαλέσει την διακοπή της εφαρμογής</string>
|
<string name="enable_leak_canary_summary">Η παρακολούθηση των διαρροών μνήμης μπορεί να προκαλέσει την διακοπή της εφαρμογής</string>
|
||||||
<string name="enable_disposed_exceptions_summary">Υποχρεωτική αναφορά μη παραδοτέων Rx εξαιρέσεων έξω από το κομμάτι ή τον κύκλο δραστηριότητας μετά από απόρριψη</string>
|
<string name="enable_disposed_exceptions_summary">Υποχρεωτική αναφορά μη παραδοτέων Rx εξαιρέσεων έξω από το κομμάτι ή τον κύκλο δραστηριότητας μετά από απόρριψη</string>
|
||||||
<string name="import_export_title">Εισαγωγή/Εξαγωγή</string>
|
<string name="import_export_title">Εισαγωγή/Εξαγωγή</string>
|
||||||
|
@ -402,9 +401,9 @@
|
||||||
<string name="tab_new">Νέα Καρτέλα</string>
|
<string name="tab_new">Νέα Καρτέλα</string>
|
||||||
<string name="tab_choose">Επιλογή Καρτέλας</string>
|
<string name="tab_choose">Επιλογή Καρτέλας</string>
|
||||||
<string name="volume_gesture_control_title">Ρυθμίσεις χειρονομιών ήχου</string>
|
<string name="volume_gesture_control_title">Ρυθμίσεις χειρονομιών ήχου</string>
|
||||||
<string name="volume_gesture_control_summary">Χρησιμοποιήστε χειρονομίες για τον έλεγχο της έντασης του ήχου</string>
|
<string name="volume_gesture_control_summary">Χρησιμοποιήστε χειρονομίες για τον έλεγχο έντασης του ήχου</string>
|
||||||
<string name="brightness_gesture_control_title">Ρυθμίσεις χειρονομιών φωτεινότητας</string>
|
<string name="brightness_gesture_control_title">Ρυθμίσεις χειρονομιών φωτεινότητας</string>
|
||||||
<string name="brightness_gesture_control_summary">Χρησιμοποιήστε χειρονομίες για τον έλεγχο της φωτεινότητας</string>
|
<string name="brightness_gesture_control_summary">Χρησιμοποιήστε χειρονομίες για τον έλεγχο φωτεινότητας</string>
|
||||||
<string name="settings_category_updates_title">Ενημερώσεις</string>
|
<string name="settings_category_updates_title">Ενημερώσεις</string>
|
||||||
<string name="events">Συμβάντα</string>
|
<string name="events">Συμβάντα</string>
|
||||||
<string name="file_deleted">Το αρχείο διαγράφηκε</string>
|
<string name="file_deleted">Το αρχείο διαγράφηκε</string>
|
||||||
|
@ -517,4 +516,15 @@
|
||||||
<string name="enable_lock_screen_video_thumbnail_title">Μικρογραφίας βίντεο στην οθόνη κλειδώματος</string>
|
<string name="enable_lock_screen_video_thumbnail_title">Μικρογραφίας βίντεο στην οθόνη κλειδώματος</string>
|
||||||
<string name="enable_lock_screen_video_thumbnail_summary">Χρήση μικρογραφίας βίντεο στην οθόνη κλειδώματος</string>
|
<string name="enable_lock_screen_video_thumbnail_summary">Χρήση μικρογραφίας βίντεο στην οθόνη κλειδώματος</string>
|
||||||
<string name="seek_duration_title">Διάρκεια fastforward και rewind</string>
|
<string name="seek_duration_title">Διάρκεια fastforward και rewind</string>
|
||||||
|
<string name="systems_language">Προεπιλογή συστήματος</string>
|
||||||
|
<string name="videos_string">Βίντεο</string>
|
||||||
|
<string name="search_showing_result_for">Εμφάνιση αποτελεσμάτων για:</string>
|
||||||
|
<string name="feed_use_dedicated_fetch_method_enable_button">Ενεργοποίηση γρήγορης λειτουργίας</string>
|
||||||
|
<string name="feed_use_dedicated_fetch_method_disable_button">Απενεργοποίηση γρήγορης λειτουργίας</string>
|
||||||
|
<string name="content_not_supported">Αυτό το περιεχόμενο δεν υποστηρίζεται ακόμη από το NewPipe.
|
||||||
|
\n
|
||||||
|
\nΕλπίζουμε ότι θα υποστηριχθεί σε μια μελλοντική έκδοση.</string>
|
||||||
|
<string name="detail_sub_channel_thumbnail_view_description">Μικρογραφία avatar του καναλιού</string>
|
||||||
|
<string name="channel_created_by">Δημιουργήθηκε από %s</string>
|
||||||
|
<string name="playlist_page_summary">Σελίδα λίστας αναπαραγωγής</string>
|
||||||
</resources>
|
</resources>
|
|
@ -190,7 +190,7 @@
|
||||||
<string name="create_playlist">Nova ludlisto</string>
|
<string name="create_playlist">Nova ludlisto</string>
|
||||||
<string name="delete_playlist">Forviŝi</string>
|
<string name="delete_playlist">Forviŝi</string>
|
||||||
<string name="rename_playlist">Alinomi</string>
|
<string name="rename_playlist">Alinomi</string>
|
||||||
<string name="playlist_name_input">Nomo</string>
|
<string name="name">Nomo</string>
|
||||||
<string name="append_playlist">Aldoni al la ludlisto</string>
|
<string name="append_playlist">Aldoni al la ludlisto</string>
|
||||||
<string name="set_as_playlist_thumbnail">Meti kiel bildeto de ludlisto</string>
|
<string name="set_as_playlist_thumbnail">Meti kiel bildeto de ludlisto</string>
|
||||||
<string name="bookmark_playlist">Legosigno Ludlisto</string>
|
<string name="bookmark_playlist">Legosigno Ludlisto</string>
|
||||||
|
@ -402,7 +402,6 @@
|
||||||
<string name="resize_zoom">Zomi</string>
|
<string name="resize_zoom">Zomi</string>
|
||||||
<string name="drawer_header_action_paceholder_text">Io aperos ĉi tie baldaŭ ;D</string>
|
<string name="drawer_header_action_paceholder_text">Io aperos ĉi tie baldaŭ ;D</string>
|
||||||
<string name="caption_auto_generated">Aŭtomate generita</string>
|
<string name="caption_auto_generated">Aŭtomate generita</string>
|
||||||
<string name="enable_leak_canary_title">LeakCanary</string>
|
|
||||||
<string name="enable_leak_canary_summary">La monitorado de la memorlikadoj povas frostigi la apon dum la hejta dumpingo</string>
|
<string name="enable_leak_canary_summary">La monitorado de la memorlikadoj povas frostigi la apon dum la hejta dumpingo</string>
|
||||||
<string name="enable_disposed_exceptions_title">Signali ekster-vivciklajn erarojn</string>
|
<string name="enable_disposed_exceptions_title">Signali ekster-vivciklajn erarojn</string>
|
||||||
<string name="enable_disposed_exceptions_summary">Perforti signalante neenretigaj Rx esceptoj eksere la fragmento aŭ aktiveco vivciklo post dispono</string>
|
<string name="enable_disposed_exceptions_summary">Perforti signalante neenretigaj Rx esceptoj eksere la fragmento aŭ aktiveco vivciklo post dispono</string>
|
||||||
|
@ -564,7 +563,6 @@
|
||||||
</plurals>
|
</plurals>
|
||||||
<string name="feed_group_dialog_empty_selection">No subscription selected</string>
|
<string name="feed_group_dialog_empty_selection">No subscription selected</string>
|
||||||
<string name="feed_group_dialog_empty_name">Malplena grupa nomo</string>
|
<string name="feed_group_dialog_empty_name">Malplena grupa nomo</string>
|
||||||
<string name="feed_group_dialog_name_input">Nomo</string>
|
|
||||||
<string name="feed_group_dialog_delete_message">Ĉu vi volas forviŝi tion grupon?</string>
|
<string name="feed_group_dialog_delete_message">Ĉu vi volas forviŝi tion grupon?</string>
|
||||||
<string name="feed_create_new_group_button_title">Nova</string>
|
<string name="feed_create_new_group_button_title">Nova</string>
|
||||||
<string name="settings_category_feed_title">Abonfluo</string>
|
<string name="settings_category_feed_title">Abonfluo</string>
|
||||||
|
|
|
@ -51,17 +51,17 @@
|
||||||
<string name="could_not_load_thumbnails">No se pudo cargar las miniaturas</string>
|
<string name="could_not_load_thumbnails">No se pudo cargar las miniaturas</string>
|
||||||
<string name="youtube_signature_decryption_error">No se pudo descifrar la URL del vídeo</string>
|
<string name="youtube_signature_decryption_error">No se pudo descifrar la URL del vídeo</string>
|
||||||
<string name="parsing_error">No se pudo analizar el sitio web</string>
|
<string name="parsing_error">No se pudo analizar el sitio web</string>
|
||||||
<string name="show_next_and_similar_title">Mostrar vídeos «siguientes» y «similares»</string>
|
<string name="show_next_and_similar_title">Mostrar vídeos \'Siguientes\' y \'Similares\'</string>
|
||||||
<string name="content_language_title">Idioma predeterminado del contenido</string>
|
<string name="content_language_title">Idioma predeterminado del contenido</string>
|
||||||
<string name="list_thumbnail_view_description">Miniatura de previsualización del vídeo</string>
|
<string name="list_thumbnail_view_description">Miniatura de previsualización del vídeo</string>
|
||||||
<string name="detail_thumbnail_view_description">Reproducir vídeo; duración:</string>
|
<string name="detail_thumbnail_view_description">Reproducir vídeo; duración:</string>
|
||||||
<string name="detail_likes_img_view_description">Me gusta</string>
|
<string name="detail_likes_img_view_description">Me gusta</string>
|
||||||
<string name="detail_dislikes_img_view_description">No me gusta</string>
|
<string name="detail_dislikes_img_view_description">No me gusta</string>
|
||||||
<string name="detail_uploader_thumbnail_view_description">Miniatura del avatar del usuario</string>
|
<string name="detail_uploader_thumbnail_view_description">Miniatura del avatar del usuario</string>
|
||||||
<string name="live_streams_not_supported">Aún no se admiten las transmisiones en vivo</string>
|
<string name="live_streams_not_supported">Las transmisiones en vivo no son soportadas aún</string>
|
||||||
<string name="content">Contenido</string>
|
<string name="content">Contenido</string>
|
||||||
<string name="show_age_restricted_content_title">Contenido restringido por edad</string>
|
<string name="show_age_restricted_content_title">Contenido restringido por edad</string>
|
||||||
<string name="video_is_age_restricted">Mostrar vídeo restringido por edad. Se pueden realizar más cambios desde los ajustes.</string>
|
<string name="video_is_age_restricted">Mostrar vídeo restringido por edad. Se pueden realizar cambios futuros desde los ajustes.</string>
|
||||||
<string name="main_bg_subtitle">Toque «Buscar» para empezar</string>
|
<string name="main_bg_subtitle">Toque «Buscar» para empezar</string>
|
||||||
<string name="autoplay_by_calling_app_title">Reproducción automática</string>
|
<string name="autoplay_by_calling_app_title">Reproducción automática</string>
|
||||||
<string name="autoplay_by_calling_app_summary">Reproducir un vídeo cuando NewPipe es llamado desde otra app</string>
|
<string name="autoplay_by_calling_app_summary">Reproducir un vídeo cuando NewPipe es llamado desde otra app</string>
|
||||||
|
@ -101,12 +101,12 @@
|
||||||
<string name="msg_url_malform">URL mal escrito o Internet no disponible</string>
|
<string name="msg_url_malform">URL mal escrito o Internet no disponible</string>
|
||||||
<string name="msg_running">NewPipe está descargando</string>
|
<string name="msg_running">NewPipe está descargando</string>
|
||||||
<string name="msg_running_detail">Toque para ver detalles</string>
|
<string name="msg_running_detail">Toque para ver detalles</string>
|
||||||
<string name="msg_wait">Espere…</string>
|
<string name="msg_wait">Espere, por favor…</string>
|
||||||
<string name="msg_copied">Copiado en el portapapeles</string>
|
<string name="msg_copied">Copiado en el portapapeles</string>
|
||||||
<string name="no_available_dir">Defina una carpeta de descargas más tarde en la configuración</string>
|
<string name="no_available_dir">Defina una carpeta de descargas más tarde en la configuración</string>
|
||||||
<string name="could_not_load_image">No se pudo cargar la imagen</string>
|
<string name="could_not_load_image">No se pudo cargar la imagen</string>
|
||||||
<string name="app_ui_crash">La interfaz de la app dejó de funcionar</string>
|
<string name="app_ui_crash">La interfaz de la app dejó de funcionar</string>
|
||||||
<string name="info_labels">Lo sucedido:\\nPetición:\\nIdioma del contenido:\\nServicio:\\nHora GMT:\\nPaquete:\\nVersión:\\nVersión del SO:</string>
|
<string name="info_labels">Lo sucedido:\\nPetición:\\nIdioma del Contenido:\\nPaís del contenido:\\nIdioma de la aplicación:\\nServicio:\\nHora GMT:\\nPaquete:\\nVersión:\\nVersión del SO:</string>
|
||||||
<string name="black_theme_title">Negro</string>
|
<string name="black_theme_title">Negro</string>
|
||||||
<string name="all">Todo</string>
|
<string name="all">Todo</string>
|
||||||
<string name="channel">Canal</string>
|
<string name="channel">Canal</string>
|
||||||
|
@ -114,7 +114,7 @@
|
||||||
<string name="later">Después</string>
|
<string name="later">Después</string>
|
||||||
<string name="short_thousand">k</string>
|
<string name="short_thousand">k</string>
|
||||||
<string name="short_million">M</string>
|
<string name="short_million">M</string>
|
||||||
<string name="short_billion">MM</string>
|
<string name="short_billion">G</string>
|
||||||
<string name="open_in_popup_mode">Abrir en modo emergente</string>
|
<string name="open_in_popup_mode">Abrir en modo emergente</string>
|
||||||
<string name="msg_popup_permission">Se necesita este permiso
|
<string name="msg_popup_permission">Se necesita este permiso
|
||||||
\npara abrir en modo emergente</string>
|
\npara abrir en modo emergente</string>
|
||||||
|
@ -193,7 +193,7 @@
|
||||||
<string name="playlist">Lista de reproducción</string>
|
<string name="playlist">Lista de reproducción</string>
|
||||||
<string name="undo">Deshacer</string>
|
<string name="undo">Deshacer</string>
|
||||||
<string name="search_no_results">No hay resultados</string>
|
<string name="search_no_results">No hay resultados</string>
|
||||||
<string name="empty_subscription_feed_subtitle">Aquí no hay nada más que grillos</string>
|
<string name="empty_subscription_feed_subtitle">Nada aquí, sino grillos</string>
|
||||||
<string name="no_subscribers">Sin suscriptores</string>
|
<string name="no_subscribers">Sin suscriptores</string>
|
||||||
<plurals name="subscribers">
|
<plurals name="subscribers">
|
||||||
<item quantity="one">%s suscriptor</item>
|
<item quantity="one">%s suscriptor</item>
|
||||||
|
@ -239,12 +239,12 @@
|
||||||
<string name="start_here_on_main">Comenzar a reproducir aquí</string>
|
<string name="start_here_on_main">Comenzar a reproducir aquí</string>
|
||||||
<string name="start_here_on_background">Comenzar a reproducir en segundo plano</string>
|
<string name="start_here_on_background">Comenzar a reproducir en segundo plano</string>
|
||||||
<string name="start_here_on_popup">Reproducir en modo emergente</string>
|
<string name="start_here_on_popup">Reproducir en modo emergente</string>
|
||||||
<string name="show_hold_to_append_title">Mostrar consejo «Mantener presionado para agregar»</string>
|
<string name="show_hold_to_append_title">Mostrar consejo \"Mantener presionado para añadir\"</string>
|
||||||
<string name="new_and_hot">Novedades</string>
|
<string name="new_and_hot">Novedades</string>
|
||||||
<string name="hold_to_append">Mantener presionado para agregar a la cola</string>
|
<string name="hold_to_append">Mantener presionado para agregar a la cola</string>
|
||||||
<string name="donation_title">Donar</string>
|
<string name="donation_title">Donar</string>
|
||||||
<string name="donation_encouragement">NewPipe es desarrollado por voluntarios que emplean su tiempo libre para brindarle la mejor experiencia. Haga una aportación para ayudarlos a crear un NewPipe mejor mientras disfrutan de una taza de café.</string>
|
<string name="donation_encouragement">NewPipe es desarrollado por voluntarios que emplean su tiempo libre para brindarle la mejor experiencia. Haga una aportación para ayudarlos a crear un NewPipe mejor mientras disfrutan de una taza de café.</string>
|
||||||
<string name="give_back">Donar</string>
|
<string name="give_back">Dar de vuelta</string>
|
||||||
<string name="website_title">Sitio web</string>
|
<string name="website_title">Sitio web</string>
|
||||||
<string name="website_encouragement">Visite el sitio web de NewPipe para más información y noticias.</string>
|
<string name="website_encouragement">Visite el sitio web de NewPipe para más información y noticias.</string>
|
||||||
<string name="default_content_country_title">País predeterminado del contenido</string>
|
<string name="default_content_country_title">País predeterminado del contenido</string>
|
||||||
|
@ -294,7 +294,7 @@
|
||||||
<string name="create_playlist">Lista de reproducción nueva</string>
|
<string name="create_playlist">Lista de reproducción nueva</string>
|
||||||
<string name="delete_playlist">Eliminar</string>
|
<string name="delete_playlist">Eliminar</string>
|
||||||
<string name="rename_playlist">Cambiar nombre</string>
|
<string name="rename_playlist">Cambiar nombre</string>
|
||||||
<string name="playlist_name_input">Nombre</string>
|
<string name="name">Nombre</string>
|
||||||
<string name="append_playlist">Añadir a la lista de reproducción</string>
|
<string name="append_playlist">Añadir a la lista de reproducción</string>
|
||||||
<string name="set_as_playlist_thumbnail">Definir como miniatura de lista de reproducción</string>
|
<string name="set_as_playlist_thumbnail">Definir como miniatura de lista de reproducción</string>
|
||||||
<string name="bookmark_playlist">Marcar lista de reproducción</string>
|
<string name="bookmark_playlist">Marcar lista de reproducción</string>
|
||||||
|
@ -311,7 +311,6 @@
|
||||||
<string name="resize_zoom">Zoom</string>
|
<string name="resize_zoom">Zoom</string>
|
||||||
<string name="settings_category_debug_title">Depuración</string>
|
<string name="settings_category_debug_title">Depuración</string>
|
||||||
<string name="caption_auto_generated">Auto generados</string>
|
<string name="caption_auto_generated">Auto generados</string>
|
||||||
<string name="enable_leak_canary_title">Activar LeakCanary</string>
|
|
||||||
<string name="enable_leak_canary_summary">La monitorización de fugas de memoria puede causar que la app no responda cuando hay Heap Dump</string>
|
<string name="enable_leak_canary_summary">La monitorización de fugas de memoria puede causar que la app no responda cuando hay Heap Dump</string>
|
||||||
<string name="enable_disposed_exceptions_title">Reportar errores fuera del ciclo de duración</string>
|
<string name="enable_disposed_exceptions_title">Reportar errores fuera del ciclo de duración</string>
|
||||||
<string name="enable_disposed_exceptions_summary">Forzar reporte de excepciones no entregables de RX fuera del fragmento o del ciclo de actividad después del descarte</string>
|
<string name="enable_disposed_exceptions_summary">Forzar reporte de excepciones no entregables de RX fuera del fragmento o del ciclo de actividad después del descarte</string>
|
||||||
|
@ -322,7 +321,7 @@
|
||||||
<string name="file">Archivo</string>
|
<string name="file">Archivo</string>
|
||||||
<string name="missing_file">Archivo movido o eliminado</string>
|
<string name="missing_file">Archivo movido o eliminado</string>
|
||||||
<string name="invalid_directory">La carpeta no existe</string>
|
<string name="invalid_directory">La carpeta no existe</string>
|
||||||
<string name="invalid_source">No existe la fuente del archivo/contenido</string>
|
<string name="invalid_source">No existe tal archivo/origen del contenido</string>
|
||||||
<string name="invalid_file">El archivo no existe o carece de los permisos para leer o escribir en él</string>
|
<string name="invalid_file">El archivo no existe o carece de los permisos para leer o escribir en él</string>
|
||||||
<string name="file_name_empty_error">El nombre del archivo no puede estar vacío</string>
|
<string name="file_name_empty_error">El nombre del archivo no puede estar vacío</string>
|
||||||
<string name="error_occurred_detail">Ocurrió un error: %1$s</string>
|
<string name="error_occurred_detail">Ocurrió un error: %1$s</string>
|
||||||
|
@ -379,9 +378,11 @@
|
||||||
<string name="app_license">NewPipe es un software copyleft libre: puedes usarlo, estudiarlo, compartirlo y mejorarlo a voluntad. Específicamente, puedes redistribuirlo y/o modificarlo bajo los términos de la Licencia Pública General GNU publicada por la Free Software Foundation, ya sea la versión 3 de la Licencia, o (a tu elección) cualquier versión posterior.</string>
|
<string name="app_license">NewPipe es un software copyleft libre: puedes usarlo, estudiarlo, compartirlo y mejorarlo a voluntad. Específicamente, puedes redistribuirlo y/o modificarlo bajo los términos de la Licencia Pública General GNU publicada por la Free Software Foundation, ya sea la versión 3 de la Licencia, o (a tu elección) cualquier versión posterior.</string>
|
||||||
<string name="import_settings">¿Quiere importar también la configuración\?</string>
|
<string name="import_settings">¿Quiere importar también la configuración\?</string>
|
||||||
<string name="privacy_policy_title">Normativa de privacidad de NewPipe</string>
|
<string name="privacy_policy_title">Normativa de privacidad de NewPipe</string>
|
||||||
<string name="privacy_policy_encouragement">El proyecto NewPipe toma su privacidad muy en serio. Por ello, la aplicación no recopila ningún dato sin su consentimiento. La normativa de privacidad de NewPipe explica en detalle qué datos se envían y almacenan cuando envía un informe de fallo.</string>
|
<string name="privacy_policy_encouragement">El proyecto NewPipe toma su privacidad muy en serio. Por ello, la aplicación no recopila algún dato sin su consentimiento.
|
||||||
|
\nLa normativa de privacidad de NewPipe explica en detalle qué datos se envían y almacenan cuando envía un informe de fallo.</string>
|
||||||
<string name="read_privacy_policy">Leer la normativa de privacidad</string>
|
<string name="read_privacy_policy">Leer la normativa de privacidad</string>
|
||||||
<string name="start_accept_privacy_policy">Para cumplir con el Reglamento general europeo de protección de datos (GDPR), podemos llamar su atención sobre la política de privacidad de NewPipe. Por favor léelo cuidadosamente. Debe aceptarlo para enviarnos el informe de error.</string>
|
<string name="start_accept_privacy_policy">Para cumplir con el Reglamento general europeo de protección de datos (GDPR), atraemos su atención sobre la política de privacidad de NewPipe. Por favor léase cuidadosamente.
|
||||||
|
\nDebe aceptarlo para enviarnos el informe de error.</string>
|
||||||
<string name="accept">Aceptar</string>
|
<string name="accept">Aceptar</string>
|
||||||
<string name="decline">Declinar</string>
|
<string name="decline">Declinar</string>
|
||||||
<string name="limit_data_usage_none_description">Sin límite</string>
|
<string name="limit_data_usage_none_description">Sin límite</string>
|
||||||
|
@ -456,7 +457,7 @@
|
||||||
<string name="downloads_storage_use_saf_title">Usar SAF</string>
|
<string name="downloads_storage_use_saf_title">Usar SAF</string>
|
||||||
<string name="downloads_storage_use_saf_summary">El \'Framework de Acceso al Almacenamiento\' permite descargar en una tarjeta SD externa.
|
<string name="downloads_storage_use_saf_summary">El \'Framework de Acceso al Almacenamiento\' permite descargar en una tarjeta SD externa.
|
||||||
\nAlgunos dispositivos no son compatibles</string>
|
\nAlgunos dispositivos no son compatibles</string>
|
||||||
<string name="unsubscribe">Cancelar suscripción</string>
|
<string name="unsubscribe">Desuscribirse</string>
|
||||||
<string name="tab_new">Pestaña nueva</string>
|
<string name="tab_new">Pestaña nueva</string>
|
||||||
<string name="tab_choose">Elija la pestaña</string>
|
<string name="tab_choose">Elija la pestaña</string>
|
||||||
<string name="volume_gesture_control_title">Control de volumen por gestos</string>
|
<string name="volume_gesture_control_title">Control de volumen por gestos</string>
|
||||||
|
@ -526,11 +527,11 @@
|
||||||
<string name="playlist_no_uploader">Generado automáticamente (no se encontró creador)</string>
|
<string name="playlist_no_uploader">Generado automáticamente (no se encontró creador)</string>
|
||||||
<string name="choose_instance_prompt">Elige una instancia</string>
|
<string name="choose_instance_prompt">Elige una instancia</string>
|
||||||
<string name="enable_lock_screen_video_thumbnail_title">Miniatura de vídeo en pantalla de bloqueo</string>
|
<string name="enable_lock_screen_video_thumbnail_title">Miniatura de vídeo en pantalla de bloqueo</string>
|
||||||
<string name="enable_lock_screen_video_thumbnail_summary">Se mostrará una miniatura del vídeo en la pantalla de bloqueo al usar el reproductor en segundo plano</string>
|
<string name="enable_lock_screen_video_thumbnail_summary">Una miniatura del vídeo es mostrada en la pantalla de bloqueo cuando se está usando el reproductor en segundo plano</string>
|
||||||
<string name="clear_download_history">Limpiar historial de descargas</string>
|
<string name="clear_download_history">Limpiar historial de descargas</string>
|
||||||
<string name="delete_downloaded_files">Eliminar archivos descargados</string>
|
<string name="delete_downloaded_files">Eliminar archivos descargados</string>
|
||||||
<string name="deleted_downloads">Eliminadas %1$d descargas</string>
|
<string name="deleted_downloads">Eliminadas %1$d descargas</string>
|
||||||
<string name="permission_display_over_apps">Dar permisos para que se muestre por sobre otras apps</string>
|
<string name="permission_display_over_apps">Permitir mostrar sobre otras aplicaciones</string>
|
||||||
<string name="app_language_title">Idioma de aplicación</string>
|
<string name="app_language_title">Idioma de aplicación</string>
|
||||||
<string name="systems_language">Predeterminado del sistema</string>
|
<string name="systems_language">Predeterminado del sistema</string>
|
||||||
<string name="subtitle_activity_recaptcha">Pulse en «Hecho» cuando esté resuelto</string>
|
<string name="subtitle_activity_recaptcha">Pulse en «Hecho» cuando esté resuelto</string>
|
||||||
|
@ -568,7 +569,6 @@
|
||||||
<item quantity="other">%d elegidas</item>
|
<item quantity="other">%d elegidas</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
<string name="feed_group_dialog_empty_name">Nombre de grupo vacío</string>
|
<string name="feed_group_dialog_empty_name">Nombre de grupo vacío</string>
|
||||||
<string name="feed_group_dialog_name_input">Nombre</string>
|
|
||||||
<string name="feed_group_dialog_delete_message">¿Borrar este grupo\?</string>
|
<string name="feed_group_dialog_delete_message">¿Borrar este grupo\?</string>
|
||||||
<string name="feed_create_new_group_button_title">Nuevo</string>
|
<string name="feed_create_new_group_button_title">Nuevo</string>
|
||||||
<string name="settings_category_feed_title">Contenido</string>
|
<string name="settings_category_feed_title">Contenido</string>
|
||||||
|
@ -612,4 +612,12 @@
|
||||||
<string name="show_original_time_ago_summary">Los textos originales de los servicios serán visibles en los ítems de transmisiones</string>
|
<string name="show_original_time_ago_summary">Los textos originales de los servicios serán visibles en los ítems de transmisiones</string>
|
||||||
<string name="show_original_time_ago_title">Mostrar tiempo atrás original en ítems</string>
|
<string name="show_original_time_ago_title">Mostrar tiempo atrás original en ítems</string>
|
||||||
<string name="youtube_restricted_mode_enabled_title">Modo restringido de YouTube</string>
|
<string name="youtube_restricted_mode_enabled_title">Modo restringido de YouTube</string>
|
||||||
|
<string name="playlist_page_summary">Página de lista de reproducción</string>
|
||||||
|
<string name="feed_group_show_only_ungrouped_subscriptions">Mostrar sólo suscripciones desagrupadas</string>
|
||||||
|
<string name="no_playlist_bookmarked_yet">Sin marcadores de lista de reproducción aún</string>
|
||||||
|
<string name="select_a_playlist">Seleccione una lista de reproducción</string>
|
||||||
|
<string name="error_report_open_github_notice">Por favor revise si ya existe una discusión sobre su problema. Cuando se crean entradas duplicadas, toma tiempo de nosotros que podríamos usar para arreglar tal problema.</string>
|
||||||
|
<string name="error_report_open_issue_button_text">Reportar error en Github</string>
|
||||||
|
<string name="copy_for_github">Copiar reporte con formato</string>
|
||||||
|
<string name="search_showing_result_for">Mostrando resultados para: %s</string>
|
||||||
</resources>
|
</resources>
|
|
@ -318,7 +318,7 @@
|
||||||
<string name="create_playlist">Uus pleilist</string>
|
<string name="create_playlist">Uus pleilist</string>
|
||||||
<string name="delete_playlist">Kustuta</string>
|
<string name="delete_playlist">Kustuta</string>
|
||||||
<string name="rename_playlist">Nimeta ümber</string>
|
<string name="rename_playlist">Nimeta ümber</string>
|
||||||
<string name="playlist_name_input">Nimi</string>
|
<string name="name">Nimi</string>
|
||||||
<string name="append_playlist">Lisa pleilisti</string>
|
<string name="append_playlist">Lisa pleilisti</string>
|
||||||
<string name="set_as_playlist_thumbnail">Määra pleilisti pisipildiks</string>
|
<string name="set_as_playlist_thumbnail">Määra pleilisti pisipildiks</string>
|
||||||
<string name="bookmark_playlist">Lisa pleilist järjehoidjaks</string>
|
<string name="bookmark_playlist">Lisa pleilist järjehoidjaks</string>
|
||||||
|
@ -376,7 +376,6 @@
|
||||||
<string name="privacy_policy_encouragement">NewPipe võtab privaatsust väga tõsiselt. Seetõttu ei kogu rakendus ilma nõusolekuta mingeid andmeid.
|
<string name="privacy_policy_encouragement">NewPipe võtab privaatsust väga tõsiselt. Seetõttu ei kogu rakendus ilma nõusolekuta mingeid andmeid.
|
||||||
\nNewPipe privaatsuspoliitika selgitab üksikasjalikult, milliseid andmeid saadetakse ja kogutakse veateate saatmisel.</string>
|
\nNewPipe privaatsuspoliitika selgitab üksikasjalikult, milliseid andmeid saadetakse ja kogutakse veateate saatmisel.</string>
|
||||||
<string name="app_license">NewPipe vaba avatud koodiga tarkvara. Seada võib kasutada, uurida, jagada ja parandada. Täpsemalt - seda võib levitada ja/või muuta vastavalt Vaba Tarkvara Sihtasutuse avaldatud GNU Üldise Avaliku Litsentsi v.3 (või hilisem) tingimustele.</string>
|
<string name="app_license">NewPipe vaba avatud koodiga tarkvara. Seada võib kasutada, uurida, jagada ja parandada. Täpsemalt - seda võib levitada ja/või muuta vastavalt Vaba Tarkvara Sihtasutuse avaldatud GNU Üldise Avaliku Litsentsi v.3 (või hilisem) tingimustele.</string>
|
||||||
<string name="enable_leak_canary_title">Luba LeakCanary</string>
|
|
||||||
<string name="enable_disposed_exceptions_title">Teavita elutsüklist väljas vigadest</string>
|
<string name="enable_disposed_exceptions_title">Teavita elutsüklist väljas vigadest</string>
|
||||||
<string name="import_soundcloud_instructions">Impordi SoundCloudi profiil trükkides URL või oma ID:
|
<string name="import_soundcloud_instructions">Impordi SoundCloudi profiil trükkides URL või oma ID:
|
||||||
\n
|
\n
|
||||||
|
|
|
@ -322,7 +322,7 @@
|
||||||
<string name="create_playlist">Erreprodukzio-zerrenda berria</string>
|
<string name="create_playlist">Erreprodukzio-zerrenda berria</string>
|
||||||
<string name="delete_playlist">Ezabatu</string>
|
<string name="delete_playlist">Ezabatu</string>
|
||||||
<string name="rename_playlist">Aldatu izena</string>
|
<string name="rename_playlist">Aldatu izena</string>
|
||||||
<string name="playlist_name_input">Izena</string>
|
<string name="name">Izena</string>
|
||||||
<string name="append_playlist">Gehitu erreprodukzio-zerrendara</string>
|
<string name="append_playlist">Gehitu erreprodukzio-zerrendara</string>
|
||||||
<string name="set_as_playlist_thumbnail">Ezarri erreprodukzio-zerrendaren iruditxo gisa</string>
|
<string name="set_as_playlist_thumbnail">Ezarri erreprodukzio-zerrendaren iruditxo gisa</string>
|
||||||
<string name="bookmark_playlist">Gogoko erreprodukzio-zerrenda</string>
|
<string name="bookmark_playlist">Gogoko erreprodukzio-zerrenda</string>
|
||||||
|
@ -339,7 +339,6 @@
|
||||||
<string name="caption_auto_generated">Automatikoki sortuak</string>
|
<string name="caption_auto_generated">Automatikoki sortuak</string>
|
||||||
<string name="caption_setting_title">Azpitituluak</string>
|
<string name="caption_setting_title">Azpitituluak</string>
|
||||||
<string name="caption_setting_description">Aldatu azpitituluen testuaren eskala eta atzealdeko estiloa. Aplikazioa berrabiarazi behar da aldaketak aplikatzeko.</string>
|
<string name="caption_setting_description">Aldatu azpitituluen testuaren eskala eta atzealdeko estiloa. Aplikazioa berrabiarazi behar da aldaketak aplikatzeko.</string>
|
||||||
<string name="enable_leak_canary_title">LeakCanary</string>
|
|
||||||
<string name="enable_leak_canary_summary">Memoria galeren monitorizazioa. Aplikazioak agian ez du erantzungo memoriaren aitortza egin bitartean</string>
|
<string name="enable_leak_canary_summary">Memoria galeren monitorizazioa. Aplikazioak agian ez du erantzungo memoriaren aitortza egin bitartean</string>
|
||||||
<string name="enable_disposed_exceptions_title">Eman bizitza-ziklo kanpoko erroreen berri</string>
|
<string name="enable_disposed_exceptions_title">Eman bizitza-ziklo kanpoko erroreen berri</string>
|
||||||
<string name="import_export_title">Inportatu/esportatu</string>
|
<string name="import_export_title">Inportatu/esportatu</string>
|
||||||
|
@ -561,7 +560,6 @@
|
||||||
<string name="settings_category_feed_title">Jarioa</string>
|
<string name="settings_category_feed_title">Jarioa</string>
|
||||||
<string name="feed_create_new_group_button_title">Berria</string>
|
<string name="feed_create_new_group_button_title">Berria</string>
|
||||||
<string name="feed_group_dialog_delete_message">Talde hau ezabatu nahi duzu\?</string>
|
<string name="feed_group_dialog_delete_message">Talde hau ezabatu nahi duzu\?</string>
|
||||||
<string name="feed_group_dialog_name_input">Izena</string>
|
|
||||||
<string name="feed_group_dialog_empty_name">Talde izena hutsik</string>
|
<string name="feed_group_dialog_empty_name">Talde izena hutsik</string>
|
||||||
<plurals name="feed_group_dialog_selection_count">
|
<plurals name="feed_group_dialog_selection_count">
|
||||||
<item quantity="one">%d hautatuta</item>
|
<item quantity="one">%d hautatuta</item>
|
||||||
|
|
|
@ -269,7 +269,7 @@
|
||||||
<string name="create_playlist">فهرست پخش جدید</string>
|
<string name="create_playlist">فهرست پخش جدید</string>
|
||||||
<string name="delete_playlist">پاککردن</string>
|
<string name="delete_playlist">پاککردن</string>
|
||||||
<string name="rename_playlist">تغییر نام</string>
|
<string name="rename_playlist">تغییر نام</string>
|
||||||
<string name="playlist_name_input">نام</string>
|
<string name="name">نام</string>
|
||||||
<string name="append_playlist">افزودن به فهرست پخش</string>
|
<string name="append_playlist">افزودن به فهرست پخش</string>
|
||||||
<string name="set_as_playlist_thumbnail">استفاده به عنوان تصویر فهرست پخش</string>
|
<string name="set_as_playlist_thumbnail">استفاده به عنوان تصویر فهرست پخش</string>
|
||||||
<string name="delete_playlist_prompt">این فهرست پخش پاک شود؟</string>
|
<string name="delete_playlist_prompt">این فهرست پخش پاک شود؟</string>
|
||||||
|
@ -416,7 +416,7 @@
|
||||||
<string name="metadata_cache_wipe_complete_notice">فرادادههای کش شده پاکش شدند</string>
|
<string name="metadata_cache_wipe_complete_notice">فرادادههای کش شده پاکش شدند</string>
|
||||||
<string name="playback_tempo">تندا</string>
|
<string name="playback_tempo">تندا</string>
|
||||||
<string name="playback_pitch">زیر و بمی</string>
|
<string name="playback_pitch">زیر و بمی</string>
|
||||||
<string name="unhook_checkbox">قطع پیوند (ممکن است باعث اعوجاج شود)</string>
|
<string name="unhook_checkbox">قطع پیوند (ممکن است باعث انحراف شود)</string>
|
||||||
<string name="preferred_open_action_settings_title">ترجیح کنش «باز کردن»</string>
|
<string name="preferred_open_action_settings_title">ترجیح کنش «باز کردن»</string>
|
||||||
<string name="preferred_open_action_settings_summary">کنش پیشفرض در زمان باز کردن محتوا — %s</string>
|
<string name="preferred_open_action_settings_summary">کنش پیشفرض در زمان باز کردن محتوا — %s</string>
|
||||||
<string name="caption_setting_description">سبک پسزمینه و اندازه متن توضیحات پخشکننده را تغییر بده. برای تاثیرگذاری، نیازمند بازراهاندازی برنامه است.</string>
|
<string name="caption_setting_description">سبک پسزمینه و اندازه متن توضیحات پخشکننده را تغییر بده. برای تاثیرگذاری، نیازمند بازراهاندازی برنامه است.</string>
|
||||||
|
@ -431,7 +431,7 @@
|
||||||
<string name="playback_reset">بازنشانی</string>
|
<string name="playback_reset">بازنشانی</string>
|
||||||
<string name="download_to_sdcard_error_title">فضای ذخیرهسازی خارجی در دسترس نیست</string>
|
<string name="download_to_sdcard_error_title">فضای ذخیرهسازی خارجی در دسترس نیست</string>
|
||||||
<string name="download_to_sdcard_error_message">بارگیری روی کارت SD خارجی ممکن نیست. مایلید محل پوشه بارگیری را دوباره تعیین کنید؟</string>
|
<string name="download_to_sdcard_error_message">بارگیری روی کارت SD خارجی ممکن نیست. مایلید محل پوشه بارگیری را دوباره تعیین کنید؟</string>
|
||||||
<string name="saved_tabs_invalid_json">استفاده از برگههای پیشفرض، خطا حین خواندن برگههای ذخیره شده</string>
|
<string name="saved_tabs_invalid_json">به دلیل ناتوانی در خواندن برگههای ذخیره شده، برگههای پیشفرض استفاده میشوند</string>
|
||||||
<string name="subscribers_count_not_available">تعداد مشترکشدگان دردسترس نیست</string>
|
<string name="subscribers_count_not_available">تعداد مشترکشدگان دردسترس نیست</string>
|
||||||
<string name="main_page_content_summary">چه برگههایی در صفحه اصلی نمایش پیدا کنند</string>
|
<string name="main_page_content_summary">چه برگههایی در صفحه اصلی نمایش پیدا کنند</string>
|
||||||
<string name="updates_setting_description">زمانی که نسخه جدید برنامه دردسرس است، اعلانی برای بهروزرسانی نمایش بده</string>
|
<string name="updates_setting_description">زمانی که نسخه جدید برنامه دردسرس است، اعلانی برای بهروزرسانی نمایش بده</string>
|
||||||
|
@ -516,7 +516,6 @@
|
||||||
<string name="settings_category_feed_title">خوراک</string>
|
<string name="settings_category_feed_title">خوراک</string>
|
||||||
<string name="feed_create_new_group_button_title">جدید</string>
|
<string name="feed_create_new_group_button_title">جدید</string>
|
||||||
<string name="feed_group_dialog_delete_message">میخواهید این گروه را پاک کنید؟</string>
|
<string name="feed_group_dialog_delete_message">میخواهید این گروه را پاک کنید؟</string>
|
||||||
<string name="feed_group_dialog_name_input">نام</string>
|
|
||||||
<plurals name="feed_group_dialog_selection_count">
|
<plurals name="feed_group_dialog_selection_count">
|
||||||
<item quantity="one">%d مورد انتخاب شده</item>
|
<item quantity="one">%d مورد انتخاب شده</item>
|
||||||
<item quantity="other">%d مورد انتخاب شده</item>
|
<item quantity="other">%d مورد انتخاب شده</item>
|
||||||
|
@ -582,4 +581,17 @@
|
||||||
<string name="albums">آلبومها</string>
|
<string name="albums">آلبومها</string>
|
||||||
<string name="songs">موسیقیها</string>
|
<string name="songs">موسیقیها</string>
|
||||||
<string name="youtube_restricted_mode_enabled_title">حالت محدودیت یوتیوب</string>
|
<string name="youtube_restricted_mode_enabled_title">حالت محدودیت یوتیوب</string>
|
||||||
|
<string name="search_showing_result_for">نمایش نتایج برای: %s</string>
|
||||||
|
<string name="error_report_open_issue_button_text">گزارش خطا در گیتهاب</string>
|
||||||
|
<string name="error_report_open_github_notice">لطفا بررسی کنید که آیا گفتگویی درباره مشکلتان از قبل وجود دارد یا خیر. ایجاد گزارش تکراری، وقتی را از ما میگیرد که ما میتوانستیم صرف رفع مشکلات واقعی کنیم.</string>
|
||||||
|
<string name="select_a_playlist">انتخاب یک فهرست پخش</string>
|
||||||
|
<string name="no_playlist_bookmarked_yet">نشانکهای فهرست پخش هنوز موجود نیستند</string>
|
||||||
|
<string name="copy_for_github">گزارش قالب بندی شده را رونویسی کنید</string>
|
||||||
|
<string name="playlist_no_uploader">تولید شده به صورت خودکار (بارگذارکننده ای یافت نشد)</string>
|
||||||
|
<string name="feed_groups_header_title">دسته بندی کانال ها</string>
|
||||||
|
<string name="feed_group_dialog_select_subscriptions">انتخاب اشتراک ها</string>
|
||||||
|
<string name="feed_group_dialog_empty_selection">اشتراکی انتخاب نشده است</string>
|
||||||
|
<string name="feed_group_show_only_ungrouped_subscriptions">نمایش اشتراک های دسته بندی نشده</string>
|
||||||
|
<string name="detail_sub_channel_thumbnail_view_description">آواتار بندانگشتی کانال</string>
|
||||||
|
<string name="playlist_page_summary">صفحه فهرست پخش</string>
|
||||||
</resources>
|
</resources>
|
|
@ -17,14 +17,14 @@
|
||||||
<string name="choose_browser">Valitse selain</string>
|
<string name="choose_browser">Valitse selain</string>
|
||||||
<string name="screen_rotation">kierto</string>
|
<string name="screen_rotation">kierto</string>
|
||||||
<string name="use_external_video_player_title">Käytä ulkoista videosoitinta</string>
|
<string name="use_external_video_player_title">Käytä ulkoista videosoitinta</string>
|
||||||
<string name="use_external_video_player_summary">Poistaa äänen joillakin resoluutioilla</string>
|
<string name="use_external_video_player_summary">Ääni saattaa lakata toimimasta joillakin resoluutioilla</string>
|
||||||
<string name="use_external_audio_player_title">Käytä ulkoista äänisoitinta</string>
|
<string name="use_external_audio_player_title">Käytä ulkoista äänisoitinta</string>
|
||||||
<string name="popup_mode_share_menu_title">Ponnahdusikkunatila</string>
|
<string name="popup_mode_share_menu_title">Ponnahdusikkunatila</string>
|
||||||
<string name="subscribe_button_title">Tilaa</string>
|
<string name="subscribe_button_title">Tilaa</string>
|
||||||
<string name="subscribed_button_title">Tilattu</string>
|
<string name="subscribed_button_title">Tilattu</string>
|
||||||
<string name="channel_unsubscribed">Kanavan tilaus peruttu</string>
|
<string name="channel_unsubscribed">Kanavan tilaus peruttu</string>
|
||||||
<string name="subscription_change_failed">Tilauksen vaihtaminen epäonnistui</string>
|
<string name="subscription_change_failed">Tilauksen vaihtaminen epäonnistui</string>
|
||||||
<string name="subscription_update_failed">Ei pystytty päivittämään tilausta</string>
|
<string name="subscription_update_failed">Tilausta ei voitu päivittää</string>
|
||||||
<string name="tab_main">Päävalikko</string>
|
<string name="tab_main">Päävalikko</string>
|
||||||
<string name="tab_subscriptions">Tilaukset</string>
|
<string name="tab_subscriptions">Tilaukset</string>
|
||||||
<string name="fragment_feed_title">Uudet</string>
|
<string name="fragment_feed_title">Uudet</string>
|
||||||
|
@ -40,12 +40,12 @@
|
||||||
<string name="autoplay_by_calling_app_summary">Toistaa videon automaattisesti, kun NewPipe avataan toisesta ohjelmasta</string>
|
<string name="autoplay_by_calling_app_summary">Toistaa videon automaattisesti, kun NewPipe avataan toisesta ohjelmasta</string>
|
||||||
<string name="default_resolution_title">Oletusresoluutio</string>
|
<string name="default_resolution_title">Oletusresoluutio</string>
|
||||||
<string name="default_popup_resolution_title">Ponnahdusikkunan oletusresoluutio</string>
|
<string name="default_popup_resolution_title">Ponnahdusikkunan oletusresoluutio</string>
|
||||||
<string name="show_higher_resolutions_title">Näytä korkeampia resoluutioita</string>
|
<string name="show_higher_resolutions_title">Näytä korkeammat resoluutiot</string>
|
||||||
<string name="show_higher_resolutions_summary">Vain jotkin laitteet voivat toistaa 2K/4K-videoa</string>
|
<string name="show_higher_resolutions_summary">Vain jotkin laitteet voivat toistaa 2K/4K-videota</string>
|
||||||
<string name="play_with_kodi_title">Toista Kodi:ssa</string>
|
<string name="play_with_kodi_title">Toista Kodissa</string>
|
||||||
<string name="kore_not_found">Asennetaanko puuttuva Kore-sovellus\?</string>
|
<string name="kore_not_found">Asennetaanko puuttuva Kore-sovellus\?</string>
|
||||||
<string name="show_play_with_kodi_title">Näytä \"Toista Kodi:ssa\" vaihtoehto</string>
|
<string name="show_play_with_kodi_title">Näytä \"Toista Kodissa\"-vaihtoehto</string>
|
||||||
<string name="show_play_with_kodi_summary">Näyttää painikkeen, jolla voi toistaa videon Kodi media center:llä</string>
|
<string name="show_play_with_kodi_summary">Näyttää vaihtoehdon videon toistamiseen Kodi-mediasoittimessa</string>
|
||||||
<string name="play_audio">Ääni</string>
|
<string name="play_audio">Ääni</string>
|
||||||
<string name="default_audio_format_title">Oletusääniformaatti</string>
|
<string name="default_audio_format_title">Oletusääniformaatti</string>
|
||||||
<string name="default_video_format_title">Oletusvideoformaatti</string>
|
<string name="default_video_format_title">Oletusvideoformaatti</string>
|
||||||
|
@ -68,7 +68,7 @@
|
||||||
<string name="next_video_title">Seuraava</string>
|
<string name="next_video_title">Seuraava</string>
|
||||||
<string name="show_next_and_similar_title">Näytä seuraavia ja samankaltaisia videoita</string>
|
<string name="show_next_and_similar_title">Näytä seuraavia ja samankaltaisia videoita</string>
|
||||||
<string name="unsupported_url">URL ei tuettu</string>
|
<string name="unsupported_url">URL ei tuettu</string>
|
||||||
<string name="content_language_title">Oletus-sisällon kieli</string>
|
<string name="content_language_title">Sisällon oletuskieli</string>
|
||||||
<string name="settings_category_player_title">Soitin</string>
|
<string name="settings_category_player_title">Soitin</string>
|
||||||
<string name="settings_category_player_behavior_title">Käyttäytyminen</string>
|
<string name="settings_category_player_behavior_title">Käyttäytyminen</string>
|
||||||
<string name="settings_category_video_audio_title">Video & ääni</string>
|
<string name="settings_category_video_audio_title">Video & ääni</string>
|
||||||
|
@ -102,12 +102,12 @@
|
||||||
<string name="notification_channel_description">Ilmoitukset NewPipen tausta- ja ponnahdusikkunasoittimille</string>
|
<string name="notification_channel_description">Ilmoitukset NewPipen tausta- ja ponnahdusikkunasoittimille</string>
|
||||||
<string name="general_error">Virhe</string>
|
<string name="general_error">Virhe</string>
|
||||||
<string name="network_error">Verkkovirhe</string>
|
<string name="network_error">Verkkovirhe</string>
|
||||||
<string name="could_not_load_thumbnails">Ei pystytty lataamaan kaikkia esikatselukuvia</string>
|
<string name="could_not_load_thumbnails">Kaikkia esikatselukuvia ei voitu ladata</string>
|
||||||
<string name="youtube_signature_decryption_error">Ei pystytty purkamaan salausta videon URL allekirjoitukselle</string>
|
<string name="youtube_signature_decryption_error">Videon URL-allekirjoituksen salausta ei voitu purkaa</string>
|
||||||
<string name="parsing_error">Ei pystytty jäsentämään websivua</string>
|
<string name="parsing_error">Verkkosivua ei voitu jäsentää</string>
|
||||||
<string name="light_parsing_error">Ei pystytty jäsentämään websivua kokonaan</string>
|
<string name="light_parsing_error">Verkkosivua ei voitu täysin jäsentää</string>
|
||||||
<string name="content_not_available">Sisältö ei ole saatavilla</string>
|
<string name="content_not_available">Sisältö ei ole saatavilla</string>
|
||||||
<string name="could_not_setup_download_menu">Ei pystytty asettamaan latausvalikkoa</string>
|
<string name="could_not_setup_download_menu">Latausvalikkoa ei voitu asettaa</string>
|
||||||
<string name="live_streams_not_supported">Live-suoratoistoa ei vielä tueta</string>
|
<string name="live_streams_not_supported">Live-suoratoistoa ei vielä tueta</string>
|
||||||
<string name="could_not_get_stream">Suoratoistosisältöä ei saatu</string>
|
<string name="could_not_get_stream">Suoratoistosisältöä ei saatu</string>
|
||||||
<string name="could_not_load_image">Kuvan lataus epäonnistui</string>
|
<string name="could_not_load_image">Kuvan lataus epäonnistui</string>
|
||||||
|
@ -178,9 +178,9 @@
|
||||||
<string name="recaptcha_request_toast">reCAPTCHA-haaste pyydetty</string>
|
<string name="recaptcha_request_toast">reCAPTCHA-haaste pyydetty</string>
|
||||||
<string name="settings_category_downloads_title">Lataus</string>
|
<string name="settings_category_downloads_title">Lataus</string>
|
||||||
<string name="settings_file_charset_title">Sallitut merkit tiedostonimissä</string>
|
<string name="settings_file_charset_title">Sallitut merkit tiedostonimissä</string>
|
||||||
<string name="settings_file_replacement_character_summary">Epäkelvot merkit korvataan tällä arvolla</string>
|
<string name="settings_file_replacement_character_summary">Kielletyt merkit korvataan tällä arvolla</string>
|
||||||
<string name="settings_file_replacement_character_title">Korvaava merkki</string>
|
<string name="settings_file_replacement_character_title">Korvaava merkki</string>
|
||||||
<string name="charset_letters_and_digits">Kirjaimia ja numeroita</string>
|
<string name="charset_letters_and_digits">Kirjaimet ja numerot</string>
|
||||||
<string name="charset_most_special_characters">Suurin osa erikoismerkeistä</string>
|
<string name="charset_most_special_characters">Suurin osa erikoismerkeistä</string>
|
||||||
<string name="title_activity_about">Tietoja NewPipe</string>
|
<string name="title_activity_about">Tietoja NewPipe</string>
|
||||||
<string name="action_settings">Asetukset</string>
|
<string name="action_settings">Asetukset</string>
|
||||||
|
@ -208,7 +208,7 @@
|
||||||
<string name="delete_item_search_history">Haluatko poistaa tämän hakuhistoriasta?</string>
|
<string name="delete_item_search_history">Haluatko poistaa tämän hakuhistoriasta?</string>
|
||||||
<string name="resume_on_audio_focus_gain_title">Jatka toistoa</string>
|
<string name="resume_on_audio_focus_gain_title">Jatka toistoa</string>
|
||||||
<string name="what_device_headline">Info:</string>
|
<string name="what_device_headline">Info:</string>
|
||||||
<string name="info_labels">Mikä:\\nPyyntö:\\nSisällön kieli:\\nPalvelu:\\nGMT Aika:\\nPaketti:\\nVersio:\\nOS versio:</string>
|
<string name="info_labels">Mikä:\\nPyyntö:\\nSisällön kieli:\\nSisällön maa:\\n:Sovelluksen kieli:\\nPalvelu:\\nGMT Aika:\\nPaketti:\\nVersio:\\nOS versio:</string>
|
||||||
<string name="copyright" formatted="true">© %1$s %2$s %3$s alla</string>
|
<string name="copyright" formatted="true">© %1$s %2$s %3$s alla</string>
|
||||||
<string name="main_page_content">Pääsivun sisältö</string>
|
<string name="main_page_content">Pääsivun sisältö</string>
|
||||||
<string name="blank_page_summary">Tyhjä sivu</string>
|
<string name="blank_page_summary">Tyhjä sivu</string>
|
||||||
|
@ -249,7 +249,7 @@
|
||||||
<string name="no_player_found_toast">Suoratoistosoitinta ei löytynyt (voit asentaa VLC:n toistaaksesi).</string>
|
<string name="no_player_found_toast">Suoratoistosoitinta ei löytynyt (voit asentaa VLC:n toistaaksesi).</string>
|
||||||
<string name="controls_download_desc">Lataa suoratoistotiedosto</string>
|
<string name="controls_download_desc">Lataa suoratoistotiedosto</string>
|
||||||
<string name="show_info">Näytä lisätietoja</string>
|
<string name="show_info">Näytä lisätietoja</string>
|
||||||
<string name="tab_bookmarks">Kirjanmerkityt soittolistat</string>
|
<string name="tab_bookmarks">Soittolistakirjanmerkit</string>
|
||||||
<string name="controls_add_to_playlist_title">Lisää soittolistaan</string>
|
<string name="controls_add_to_playlist_title">Lisää soittolistaan</string>
|
||||||
<string name="use_inexact_seek_title">Käytä nopeampaa epätarkkaa pikakelausta</string>
|
<string name="use_inexact_seek_title">Käytä nopeampaa epätarkkaa pikakelausta</string>
|
||||||
<string name="use_inexact_seek_summary">Epätarkka kelaus mahdollistaa videon kelauksen nopeammin huonommalla tarkkuudella. Kelaaminen 5, 15 tai 25 sekuntia ei toimi tämän kanssa.</string>
|
<string name="use_inexact_seek_summary">Epätarkka kelaus mahdollistaa videon kelauksen nopeammin huonommalla tarkkuudella. Kelaaminen 5, 15 tai 25 sekuntia ei toimi tämän kanssa.</string>
|
||||||
|
@ -334,7 +334,7 @@
|
||||||
<string name="create_playlist">Uusi soittolista</string>
|
<string name="create_playlist">Uusi soittolista</string>
|
||||||
<string name="delete_playlist">Poista</string>
|
<string name="delete_playlist">Poista</string>
|
||||||
<string name="rename_playlist">Uudelleennimeä</string>
|
<string name="rename_playlist">Uudelleennimeä</string>
|
||||||
<string name="playlist_name_input">Nimi</string>
|
<string name="name">Nimi</string>
|
||||||
<string name="append_playlist">Lisää soittolistaan</string>
|
<string name="append_playlist">Lisää soittolistaan</string>
|
||||||
<string name="set_as_playlist_thumbnail">Aseta soittolistan kuvakkeeksi</string>
|
<string name="set_as_playlist_thumbnail">Aseta soittolistan kuvakkeeksi</string>
|
||||||
<string name="bookmark_playlist">Tallenna soittolista kirjanmerkkeihin</string>
|
<string name="bookmark_playlist">Tallenna soittolista kirjanmerkkeihin</string>
|
||||||
|
@ -351,7 +351,6 @@
|
||||||
<string name="caption_auto_generated">Automaattisesti luotu</string>
|
<string name="caption_auto_generated">Automaattisesti luotu</string>
|
||||||
<string name="caption_setting_title">Tekstitykset</string>
|
<string name="caption_setting_title">Tekstitykset</string>
|
||||||
<string name="caption_setting_description">Muokkaa soittimen tekstitysten kokoa ja taustatyylejä. Asetusten käyttöönotto vaatii uudelleenkäynnistyksen.</string>
|
<string name="caption_setting_description">Muokkaa soittimen tekstitysten kokoa ja taustatyylejä. Asetusten käyttöönotto vaatii uudelleenkäynnistyksen.</string>
|
||||||
<string name="enable_leak_canary_title">LeakCanary</string>
|
|
||||||
<string name="enable_leak_canary_summary">Muistivuotojen valvonta voi aiheuttaa ohjelman hidastumisen virhetilanteissa</string>
|
<string name="enable_leak_canary_summary">Muistivuotojen valvonta voi aiheuttaa ohjelman hidastumisen virhetilanteissa</string>
|
||||||
<string name="enable_disposed_exceptions_title">Raportoi yhteensopivuusvirheitä, jotka aiheutuvat vanhoista ohjelmista</string>
|
<string name="enable_disposed_exceptions_title">Raportoi yhteensopivuusvirheitä, jotka aiheutuvat vanhoista ohjelmista</string>
|
||||||
<string name="enable_disposed_exceptions_summary">Pakota raportointi toimituskelvottomille Rx-poikkeuksille, jotka ovat poiston jälkeen muistisirpaleiden tai aktiviteettielämänkaaren ulkopuolella</string>
|
<string name="enable_disposed_exceptions_summary">Pakota raportointi toimituskelvottomille Rx-poikkeuksille, jotka ovat poiston jälkeen muistisirpaleiden tai aktiviteettielämänkaaren ulkopuolella</string>
|
||||||
|
@ -392,7 +391,7 @@
|
||||||
<string name="accept">Hyväksy</string>
|
<string name="accept">Hyväksy</string>
|
||||||
<string name="decline">Hylkää</string>
|
<string name="decline">Hylkää</string>
|
||||||
<string name="limit_data_usage_none_description">Ei rajaa</string>
|
<string name="limit_data_usage_none_description">Ei rajaa</string>
|
||||||
<string name="limit_mobile_data_usage_title">Rajoita resoluutiota kun mobiilidata on käytössä</string>
|
<string name="limit_mobile_data_usage_title">Rajoita resoluutiota mobiilidataa käytettäessä</string>
|
||||||
<string name="minimize_on_exit_title">Pienennä vaihdettaessa ohjelmaa</string>
|
<string name="minimize_on_exit_title">Pienennä vaihdettaessa ohjelmaa</string>
|
||||||
<string name="minimize_on_exit_summary">Toiminto vaihdettaessa toiseen ohjelmaan päävideosoittimesta — %s</string>
|
<string name="minimize_on_exit_summary">Toiminto vaihdettaessa toiseen ohjelmaan päävideosoittimesta — %s</string>
|
||||||
<string name="minimize_on_exit_none_description">Ei koskaan</string>
|
<string name="minimize_on_exit_none_description">Ei koskaan</string>
|
||||||
|
@ -510,11 +509,10 @@
|
||||||
<string name="feed_use_dedicated_fetch_method_title">Hae erityisestä syötteestä, kun sellainen on saatavilla</string>
|
<string name="feed_use_dedicated_fetch_method_title">Hae erityisestä syötteestä, kun sellainen on saatavilla</string>
|
||||||
<string name="feed_update_threshold_option_always_update">Päivitä aina</string>
|
<string name="feed_update_threshold_option_always_update">Päivitä aina</string>
|
||||||
<string name="feed_update_threshold_summary">Edellisestä päivityksestä kulunut aika, jonka jälkeen tilaus katsotaan vanhentuneeksi</string>
|
<string name="feed_update_threshold_summary">Edellisestä päivityksestä kulunut aika, jonka jälkeen tilaus katsotaan vanhentuneeksi</string>
|
||||||
<string name="feed_update_threshold_title">Syötteen päivitysvälin kynnysarvo</string>
|
<string name="feed_update_threshold_title">Syötteen päivitysväli</string>
|
||||||
<string name="settings_category_feed_title">Syöte</string>
|
<string name="settings_category_feed_title">Syöte</string>
|
||||||
<string name="feed_create_new_group_button_title">Uusi</string>
|
<string name="feed_create_new_group_button_title">Uusi</string>
|
||||||
<string name="feed_group_dialog_delete_message">Haluatko poistaa tämän ryhmän\?</string>
|
<string name="feed_group_dialog_delete_message">Haluatko poistaa tämän ryhmän\?</string>
|
||||||
<string name="feed_group_dialog_name_input">Nimi</string>
|
|
||||||
<string name="feed_group_dialog_empty_name">Tyhjä ryhmän nimi</string>
|
<string name="feed_group_dialog_empty_name">Tyhjä ryhmän nimi</string>
|
||||||
<plurals name="feed_group_dialog_selection_count">
|
<plurals name="feed_group_dialog_selection_count">
|
||||||
<item quantity="one">%d valittu</item>
|
<item quantity="one">%d valittu</item>
|
||||||
|
@ -550,13 +548,13 @@
|
||||||
<string name="choose_instance_prompt">Valitse instanssi</string>
|
<string name="choose_instance_prompt">Valitse instanssi</string>
|
||||||
<string name="downloads_storage_use_saf_summary">\'Storage Access Framework\' sallii lataukset ulkoiselle SD-kortille.
|
<string name="downloads_storage_use_saf_summary">\'Storage Access Framework\' sallii lataukset ulkoiselle SD-kortille.
|
||||||
\nJotkin laitteet eivät ole yhteensopivia</string>
|
\nJotkin laitteet eivät ole yhteensopivia</string>
|
||||||
<string name="downloads_storage_use_saf_title">Käytä SAF:ää</string>
|
<string name="downloads_storage_use_saf_title">Ota SAF käyttöön</string>
|
||||||
<string name="downloads_storage_ask_summary_kitkat">Jokaisen latauksen kohde kysytään.
|
<string name="downloads_storage_ask_summary_kitkat">Jokaisen latauksen kohde kysytään.
|
||||||
\nValitse SAF, jos haluat ladata ulkoiselle SD-kortille</string>
|
\nValitse SAF, jos haluat ladata ulkoiselle SD-kortille</string>
|
||||||
<string name="downloads_storage_ask_summary">Jokaisen latauksen kohde kysytään</string>
|
<string name="downloads_storage_ask_summary">Jokaisen latauksen kohde kysytään</string>
|
||||||
<string name="downloads_storage_ask_title">Kysy mihin ladataan</string>
|
<string name="downloads_storage_ask_title">Kysy mihin ladataan</string>
|
||||||
<string name="start_downloads">Aloita lataukset</string>
|
<string name="start_downloads">Aloita lataukset</string>
|
||||||
<string name="enable_queue_limit_desc">Yksi lataus kerrallaan on käynnissä</string>
|
<string name="enable_queue_limit_desc">Salli vain yksi lataus kerrallaan</string>
|
||||||
<string name="enable_queue_limit">Rajoita latausjonon kokoa</string>
|
<string name="enable_queue_limit">Rajoita latausjonon kokoa</string>
|
||||||
<string name="max_retry_desc">Suurin määrä yrityksiä ennen kuin lataus perutaan</string>
|
<string name="max_retry_desc">Suurin määrä yrityksiä ennen kuin lataus perutaan</string>
|
||||||
<string name="max_retry_msg">Uudelleenyritysten maksimimäärä</string>
|
<string name="max_retry_msg">Uudelleenyritysten maksimimäärä</string>
|
||||||
|
@ -596,7 +594,7 @@
|
||||||
<string name="show_original_time_ago_title">Näytä alkuperäinen aika sisällölle</string>
|
<string name="show_original_time_ago_title">Näytä alkuperäinen aika sisällölle</string>
|
||||||
<string name="pause_downloads">Tauota lataukset</string>
|
<string name="pause_downloads">Tauota lataukset</string>
|
||||||
<string name="pause_downloads_on_mobile_desc">Hyödyllinen vaihdettaessa mobiilidataan, vaikka joitakin latauksia ei voi pysäyttää</string>
|
<string name="pause_downloads_on_mobile_desc">Hyödyllinen vaihdettaessa mobiilidataan, vaikka joitakin latauksia ei voi pysäyttää</string>
|
||||||
<string name="pause_downloads_on_mobile">Keskeytä käytön mukaan laskutettavilla yhteyksillä</string>
|
<string name="pause_downloads_on_mobile">Keskeytä, kun yhteys on käytön mukaan laskutettava</string>
|
||||||
<string name="paused">tauotettu</string>
|
<string name="paused">tauotettu</string>
|
||||||
<string name="app_update_notification_content_text">Napauta ladataksesi</string>
|
<string name="app_update_notification_content_text">Napauta ladataksesi</string>
|
||||||
<string name="app_update_notification_content_title">NewPipe-päivitys on saatavilla!</string>
|
<string name="app_update_notification_content_title">NewPipe-päivitys on saatavilla!</string>
|
||||||
|
@ -611,4 +609,12 @@
|
||||||
<string name="unmute">Poista mykistys</string>
|
<string name="unmute">Poista mykistys</string>
|
||||||
<string name="mute">Mykistä</string>
|
<string name="mute">Mykistä</string>
|
||||||
<string name="conferences">Konferenssit</string>
|
<string name="conferences">Konferenssit</string>
|
||||||
|
<string name="playlist_page_summary">Soittolistasivu</string>
|
||||||
|
<string name="feed_group_show_only_ungrouped_subscriptions">Näytä vain ryhmittelemättömät tilaukset</string>
|
||||||
|
<string name="no_playlist_bookmarked_yet">Ei soittolistakirjanmerkkejä vielä</string>
|
||||||
|
<string name="select_a_playlist">Valitse soittolista</string>
|
||||||
|
<string name="error_report_open_github_notice">Ole hyvä ja tarkasta onko kaatumiseen liittyvä ongelma jo raportoitu. Tikettien kaksoiskappaleiden selvittely vie aikaa varsinaisten ohjelmavirheiden korjaamiselta.</string>
|
||||||
|
<string name="error_report_open_issue_button_text">Raportoi virhe GitHubissa</string>
|
||||||
|
<string name="copy_for_github">Kopioi muotoiltu raportti</string>
|
||||||
|
<string name="search_showing_result_for">Näytetään tulokset haulle: %s</string>
|
||||||
</resources>
|
</resources>
|
|
@ -42,4 +42,9 @@
|
||||||
<string name="download_path_dialog_title">Pumili ng folder kung saan ido-download ang mga bidyo</string>
|
<string name="download_path_dialog_title">Pumili ng folder kung saan ido-download ang mga bidyo</string>
|
||||||
<string name="download_path_audio_summary">Nakaimbak sa folder na ito ang mga nai-download na mga audio files</string>
|
<string name="download_path_audio_summary">Nakaimbak sa folder na ito ang mga nai-download na mga audio files</string>
|
||||||
<string name="download_path_audio_dialog_title">Pumili ng folder kung saan ido-download ang mga audio files</string>
|
<string name="download_path_audio_dialog_title">Pumili ng folder kung saan ido-download ang mga audio files</string>
|
||||||
|
<string name="light_theme_title">Maliwanag</string>
|
||||||
|
<string name="play_with_kodi_title">Buksan gamit Kodi</string>
|
||||||
|
<string name="dark_theme_title">Madilim</string>
|
||||||
|
<string name="download_choose_new_path">Palitan ang folder na paglalagyan ng download para umipekto</string>
|
||||||
|
<string name="download_path_summary">Dito makikita ang mga nadownload na video</string>
|
||||||
</resources>
|
</resources>
|
|
@ -116,7 +116,7 @@
|
||||||
<string name="yes">Oui</string>
|
<string name="yes">Oui</string>
|
||||||
<string name="later">Plus tard</string>
|
<string name="later">Plus tard</string>
|
||||||
<string name="disabled">Désactivés</string>
|
<string name="disabled">Désactivés</string>
|
||||||
<string name="info_labels">Quoi :\\nRequête :\\nLangue du contenu :\\nService :\\nHeure UTC :\\nPaquet :\\nVersion :\\nVersion du système d’exploitation :</string>
|
<string name="info_labels">Quoi :\\nRequête :\\nLangue du contenu :\\nPays du contenu :\\nLangue de l’application :\\nService :\\nDate UTC :\\nPaquet :\\nVersion :\\nVersion du système d’exploitation :</string>
|
||||||
<string name="short_thousand">k</string>
|
<string name="short_thousand">k</string>
|
||||||
<string name="short_million">M</string>
|
<string name="short_million">M</string>
|
||||||
<string name="msg_popup_permission">Cette autorisation est nécessaire pour
|
<string name="msg_popup_permission">Cette autorisation est nécessaire pour
|
||||||
|
@ -290,7 +290,7 @@
|
||||||
<string name="create_playlist">Nouvelle liste de lecture</string>
|
<string name="create_playlist">Nouvelle liste de lecture</string>
|
||||||
<string name="delete_playlist">Supprimer</string>
|
<string name="delete_playlist">Supprimer</string>
|
||||||
<string name="rename_playlist">Renommer</string>
|
<string name="rename_playlist">Renommer</string>
|
||||||
<string name="playlist_name_input">Nom</string>
|
<string name="name">Nom</string>
|
||||||
<string name="append_playlist">Ajouter à la liste de lecture</string>
|
<string name="append_playlist">Ajouter à la liste de lecture</string>
|
||||||
<string name="set_as_playlist_thumbnail">Définir comme miniature de la liste de lecture</string>
|
<string name="set_as_playlist_thumbnail">Définir comme miniature de la liste de lecture</string>
|
||||||
<string name="bookmark_playlist">Enregister la liste de lecture</string>
|
<string name="bookmark_playlist">Enregister la liste de lecture</string>
|
||||||
|
@ -325,7 +325,6 @@
|
||||||
<string name="settings_category_debug_title">Débogage</string>
|
<string name="settings_category_debug_title">Débogage</string>
|
||||||
<string name="resize_fill">Remplir</string>
|
<string name="resize_fill">Remplir</string>
|
||||||
<string name="caption_auto_generated">Générés automatiquement</string>
|
<string name="caption_auto_generated">Générés automatiquement</string>
|
||||||
<string name="enable_leak_canary_title">LeakCanary</string>
|
|
||||||
<string name="enable_leak_canary_summary">La surveillance des fuites de mémoire peut geler l’application durant le vidage du tas</string>
|
<string name="enable_leak_canary_summary">La surveillance des fuites de mémoire peut geler l’application durant le vidage du tas</string>
|
||||||
<string name="enable_disposed_exceptions_title">Rapporter les erreurs hors cycle de vie</string>
|
<string name="enable_disposed_exceptions_title">Rapporter les erreurs hors cycle de vie</string>
|
||||||
<string name="enable_disposed_exceptions_summary">Forcer le rapport des exceptions Rx non livrables en dehors des fragments ou activités durant le cycle de vie après traitement</string>
|
<string name="enable_disposed_exceptions_summary">Forcer le rapport des exceptions Rx non livrables en dehors des fragments ou activités durant le cycle de vie après traitement</string>
|
||||||
|
@ -568,7 +567,6 @@
|
||||||
<item quantity="other">%d sélectionnés</item>
|
<item quantity="other">%d sélectionnés</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
<string name="feed_group_dialog_empty_name">Nom de groupe vide</string>
|
<string name="feed_group_dialog_empty_name">Nom de groupe vide</string>
|
||||||
<string name="feed_group_dialog_name_input">Nom</string>
|
|
||||||
<string name="feed_group_dialog_delete_message">Voulez-vous supprimer ce groupe ?</string>
|
<string name="feed_group_dialog_delete_message">Voulez-vous supprimer ce groupe ?</string>
|
||||||
<string name="feed_create_new_group_button_title">Nouveau</string>
|
<string name="feed_create_new_group_button_title">Nouveau</string>
|
||||||
<string name="settings_category_feed_title">Flux</string>
|
<string name="settings_category_feed_title">Flux</string>
|
||||||
|
@ -612,4 +610,11 @@
|
||||||
<string name="show_original_time_ago_title">Afficher la date originelle sur les items</string>
|
<string name="show_original_time_ago_title">Afficher la date originelle sur les items</string>
|
||||||
<string name="youtube_restricted_mode_enabled_title">Mode restreint de YouTube</string>
|
<string name="youtube_restricted_mode_enabled_title">Mode restreint de YouTube</string>
|
||||||
<string name="feed_group_show_only_ungrouped_subscriptions">Afficher les abonnements sans groupes uniquement</string>
|
<string name="feed_group_show_only_ungrouped_subscriptions">Afficher les abonnements sans groupes uniquement</string>
|
||||||
|
<string name="playlist_page_summary">Page des listes de lecture</string>
|
||||||
|
<string name="no_playlist_bookmarked_yet">Aucune liste de lecture encore enregistrée</string>
|
||||||
|
<string name="select_a_playlist">Sélectionner une liste de lecture</string>
|
||||||
|
<string name="error_report_open_github_notice">Veuillez vérifier si un ticket concernant votre problème existe déjà. Lorsque vous créez des tickets dupliqués, cela nous prend du temps que nous pourrions passer à résoudre effectivement le problème.</string>
|
||||||
|
<string name="error_report_open_issue_button_text">Rapporter l’erreur sur GitHub</string>
|
||||||
|
<string name="copy_for_github">Copier le rapport formaté</string>
|
||||||
|
<string name="search_showing_result_for">Affichage des résultats pour : %s</string>
|
||||||
</resources>
|
</resources>
|