Merge remote-tracking branch 'newpipe/dev' into rebase
This commit is contained in:
commit
fa6823599a
49 changed files with 825 additions and 421 deletions
|
|
@ -38,7 +38,7 @@ import okhttp3.ResponseBody;
|
|||
import static org.schabi.newpipe.MainActivity.DEBUG;
|
||||
|
||||
public class DownloaderImpl extends Downloader {
|
||||
public static final String USER_AGENT = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:43.0) Gecko/20100101 Firefox/43.0";
|
||||
public static final String USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:68.0) Gecko/20100101 Firefox/68.0";
|
||||
|
||||
private static DownloaderImpl instance;
|
||||
private String mCookies;
|
||||
|
|
@ -171,7 +171,8 @@ public class DownloaderImpl extends Downloader {
|
|||
responseBodyToReturn = body.string();
|
||||
}
|
||||
|
||||
return new Response(response.code(), response.message(), response.headers().toMultimap(), responseBodyToReturn);
|
||||
final String latestUrl = response.request().url().toString();
|
||||
return new Response(response.code(), response.message(), response.headers().toMultimap(), responseBodyToReturn, latestUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
package org.schabi.newpipe;
|
||||
|
||||
import androidx.room.Room;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.room.Room;
|
||||
|
||||
import org.schabi.newpipe.database.AppDatabase;
|
||||
|
||||
|
|
@ -39,4 +41,14 @@ public final class NewPipeDatabase {
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void checkpoint() {
|
||||
if (databaseInstance == null) {
|
||||
throw new IllegalStateException("database is not initialized");
|
||||
}
|
||||
Cursor c = databaseInstance.query("pragma wal_checkpoint(full)", null);
|
||||
if (c.moveToFirst() && c.getInt(0) == 1) {
|
||||
throw new RuntimeException("Checkpoint was blocked from completing");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -136,16 +136,17 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
|
|||
// Tabs
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
public void setupTabs() {
|
||||
private void setupTabs() {
|
||||
tabsList.clear();
|
||||
tabsList.addAll(tabsManager.getTabs());
|
||||
|
||||
if (pagerAdapter == null || !pagerAdapter.sameTabs(tabsList)) {
|
||||
pagerAdapter = new SelectedTabsPagerAdapter(requireContext(), getChildFragmentManager(), tabsList);
|
||||
}
|
||||
// Clear previous tabs/fragments and set new adapter
|
||||
viewPager.setAdapter(pagerAdapter);
|
||||
|
||||
viewPager.setAdapter(null);
|
||||
viewPager.setOffscreenPageLimit(tabsList.size());
|
||||
viewPager.setAdapter(pagerAdapter);
|
||||
|
||||
updateTabsIconAndDescription();
|
||||
updateTitleForTab(viewPager.getCurrentItem());
|
||||
|
|
@ -194,6 +195,7 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
|
|||
this.internalTabsList = new ArrayList<>(tabsList);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Fragment getItem(int position) {
|
||||
final Tab tab = internalTabsList.get(position);
|
||||
|
|
|
|||
|
|
@ -723,7 +723,7 @@ public class SearchFragment
|
|||
showError(getString(R.string.url_not_supported_toast), false)));
|
||||
return;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
} catch (Exception ignored) {
|
||||
// Exception occurred, it's not a url
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,16 +30,16 @@ import android.content.res.Resources;
|
|||
import android.graphics.Bitmap;
|
||||
import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
|
||||
import com.google.android.exoplayer2.PlaybackParameters;
|
||||
import com.google.android.exoplayer2.Player;
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
|
|
@ -341,7 +341,7 @@ public final class BackgroundPlayer extends Service {
|
|||
@Override
|
||||
public void handleIntent(final Intent intent) {
|
||||
super.handleIntent(intent);
|
||||
|
||||
|
||||
resetNotification();
|
||||
if (bigNotRemoteView != null)
|
||||
bigNotRemoteView.setProgressBar(R.id.notificationProgressBar, 100, 0, false);
|
||||
|
|
@ -389,7 +389,6 @@ public final class BackgroundPlayer extends Service {
|
|||
@Override
|
||||
public void onPrepared(boolean playWhenReady) {
|
||||
super.onPrepared(playWhenReady);
|
||||
simpleExoPlayer.setVolume(1f);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -398,6 +397,12 @@ public final class BackgroundPlayer extends Service {
|
|||
updatePlayback();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMuteUnmuteButtonClicked() {
|
||||
super.onMuteUnmuteButtonClicked();
|
||||
updatePlayback();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdateProgress(int currentProgress, int duration, int bufferPercent) {
|
||||
updateProgress(currentProgress, duration, bufferPercent);
|
||||
|
|
|
|||
|
|
@ -153,6 +153,8 @@ public abstract class BasePlayer implements
|
|||
public static final String START_PAUSED = "start_paused";
|
||||
@NonNull
|
||||
public static final String SELECT_ON_APPEND = "select_on_append";
|
||||
@NonNull
|
||||
public static final String IS_MUTED = "is_muted";
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Playback
|
||||
|
|
@ -275,6 +277,7 @@ public abstract class BasePlayer implements
|
|||
final float playbackPitch = intent.getFloatExtra(PLAYBACK_PITCH, getPlaybackPitch());
|
||||
final boolean playbackSkipSilence = intent.getBooleanExtra(PLAYBACK_SKIP_SILENCE,
|
||||
getPlaybackSkipSilence());
|
||||
final boolean isMuted = intent.getBooleanExtra(IS_MUTED, simpleExoPlayer == null ? false : isMuted());
|
||||
|
||||
// seek to timestamp if stream is already playing
|
||||
if (simpleExoPlayer != null
|
||||
|
|
@ -283,7 +286,7 @@ public abstract class BasePlayer implements
|
|||
&& playQueue.getItem() != null
|
||||
&& queue.getItem().getUrl().equals(playQueue.getItem().getUrl())
|
||||
&& queue.getItem().getRecoveryPosition() != PlayQueueItem.RECOVERY_UNSET
|
||||
) {
|
||||
) {
|
||||
simpleExoPlayer.seekTo(playQueue.getIndex(), queue.getItem().getRecoveryPosition());
|
||||
return;
|
||||
|
||||
|
|
@ -293,7 +296,7 @@ public abstract class BasePlayer implements
|
|||
stateLoader = recordManager.loadStreamState(item)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doFinally(() -> initPlayback(queue, repeatMode, playbackSpeed, playbackPitch, playbackSkipSilence,
|
||||
/*playOnInit=*/true))
|
||||
/*playOnInit=*/true, isMuted))
|
||||
.subscribe(
|
||||
state -> queue.setRecovery(queue.getIndex(), state.getProgressTime()),
|
||||
error -> {
|
||||
|
|
@ -306,7 +309,7 @@ public abstract class BasePlayer implements
|
|||
}
|
||||
// Good to go...
|
||||
initPlayback(queue, repeatMode, playbackSpeed, playbackPitch, playbackSkipSilence,
|
||||
/*playOnInit=*/!intent.getBooleanExtra(START_PAUSED, false));
|
||||
/*playOnInit=*/!intent.getBooleanExtra(START_PAUSED, false), isMuted);
|
||||
}
|
||||
|
||||
protected void initPlayback(@NonNull final PlayQueue queue,
|
||||
|
|
@ -314,7 +317,8 @@ public abstract class BasePlayer implements
|
|||
final float playbackSpeed,
|
||||
final float playbackPitch,
|
||||
final boolean playbackSkipSilence,
|
||||
final boolean playOnReady) {
|
||||
final boolean playOnReady,
|
||||
final boolean isMuted) {
|
||||
destroyPlayer();
|
||||
initPlayer(playOnReady);
|
||||
setRepeatMode(repeatMode);
|
||||
|
|
@ -327,6 +331,8 @@ public abstract class BasePlayer implements
|
|||
|
||||
if (playQueueAdapter != null) playQueueAdapter.dispose();
|
||||
playQueueAdapter = new PlayQueueAdapter(context, playQueue);
|
||||
|
||||
simpleExoPlayer.setVolume(isMuted ? 0 : 1);
|
||||
}
|
||||
|
||||
public void destroyPlayer() {
|
||||
|
|
@ -532,6 +538,18 @@ public abstract class BasePlayer implements
|
|||
if (simpleExoPlayer == null) return;
|
||||
simpleExoPlayer.setShuffleModeEnabled(!simpleExoPlayer.getShuffleModeEnabled());
|
||||
}
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Mute / Unmute
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
public void onMuteUnmuteButtonClicked() {
|
||||
if (DEBUG) Log.d(TAG, "onMuteUnmuteButtonClicled() called");
|
||||
simpleExoPlayer.setVolume(isMuted() ? 1 : 0);
|
||||
}
|
||||
|
||||
public boolean isMuted() {
|
||||
return simpleExoPlayer.getVolume() == 0;
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Progress Updates
|
||||
|
|
|
|||
|
|
@ -35,21 +35,26 @@ import android.os.Bundle;
|
|||
import android.os.Handler;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.annotation.ColorInt;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.content.res.AppCompatResources;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
import android.view.DisplayCutout;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.WindowInsets;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageButton;
|
||||
|
|
@ -119,7 +124,8 @@ public final class MainVideoPlayer extends AppCompatActivity
|
|||
|
||||
private SharedPreferences defaultPreferences;
|
||||
|
||||
@Nullable private PlayerState playerState;
|
||||
@Nullable
|
||||
private PlayerState playerState;
|
||||
private boolean isInMultiWindow;
|
||||
private boolean isBackPressed;
|
||||
|
||||
|
|
@ -133,11 +139,13 @@ public final class MainVideoPlayer extends AppCompatActivity
|
|||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
assureCorrectAppLanguage(this);
|
||||
super.onCreate(savedInstanceState);
|
||||
if (DEBUG) Log.d(TAG, "onCreate() called with: savedInstanceState = [" + savedInstanceState + "]");
|
||||
if (DEBUG)
|
||||
Log.d(TAG, "onCreate() called with: savedInstanceState = [" + savedInstanceState + "]");
|
||||
defaultPreferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
ThemeHelper.setTheme(this);
|
||||
getWindow().setBackgroundDrawable(new ColorDrawable(Color.BLACK));
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) getWindow().setStatusBarColor(Color.BLACK);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
|
||||
getWindow().setStatusBarColor(Color.BLACK);
|
||||
setVolumeControlStream(AudioManager.STREAM_MUSIC);
|
||||
|
||||
WindowManager.LayoutParams lp = getWindow().getAttributes();
|
||||
|
|
@ -147,7 +155,7 @@ public final class MainVideoPlayer extends AppCompatActivity
|
|||
hideSystemUi();
|
||||
setContentView(R.layout.activity_main_player);
|
||||
|
||||
playerImpl = new VideoPlayerImpl(this);
|
||||
playerImpl = new VideoPlayerImpl(this);
|
||||
playerImpl.setup(findViewById(android.R.id.content));
|
||||
|
||||
if (savedInstanceState != null && savedInstanceState.get(KEY_SAVED_STATE) != null) {
|
||||
|
|
@ -263,7 +271,7 @@ public final class MainVideoPlayer extends AppCompatActivity
|
|||
playerImpl.setPlaybackQuality(playerState.getPlaybackQuality());
|
||||
playerImpl.initPlayback(playerState.getPlayQueue(), playerState.getRepeatMode(),
|
||||
playerState.getPlaybackSpeed(), playerState.getPlaybackPitch(),
|
||||
playerState.isPlaybackSkipSilence(), playerState.wasPlaying());
|
||||
playerState.isPlaybackSkipSilence(), playerState.wasPlaying(), playerImpl.isMuted());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -291,7 +299,7 @@ public final class MainVideoPlayer extends AppCompatActivity
|
|||
if (playerImpl == null) return;
|
||||
|
||||
playerImpl.setRecovery();
|
||||
if(!playerImpl.gotDestroyed()) {
|
||||
if (!playerImpl.gotDestroyed()) {
|
||||
playerState = createPlayerState();
|
||||
}
|
||||
StateSaver.tryToSave(isChangingConfigurations(), null, outState, this);
|
||||
|
|
@ -439,6 +447,11 @@ public final class MainVideoPlayer extends AppCompatActivity
|
|||
shuffleButton.setImageAlpha(shuffleAlpha);
|
||||
}
|
||||
|
||||
protected void setMuteButton(final ImageButton muteButton, final boolean isMuted) {
|
||||
muteButton.setColorFilter(ContextCompat.getColor(getApplicationContext(), isMuted ? R.color.white : R.color.gray));
|
||||
}
|
||||
|
||||
|
||||
private boolean isInMultiWindow() {
|
||||
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && isInMultiWindowMode();
|
||||
}
|
||||
|
|
@ -491,6 +504,7 @@ public final class MainVideoPlayer extends AppCompatActivity
|
|||
private ImageButton toggleOrientationButton;
|
||||
private ImageButton switchPopupButton;
|
||||
private ImageButton switchBackgroundButton;
|
||||
private ImageButton muteButton;
|
||||
|
||||
private RelativeLayout windowRootLayout;
|
||||
private View secondaryControls;
|
||||
|
|
@ -527,6 +541,7 @@ public final class MainVideoPlayer extends AppCompatActivity
|
|||
this.shareButton = rootView.findViewById(R.id.share);
|
||||
this.toggleOrientationButton = rootView.findViewById(R.id.toggleOrientation);
|
||||
this.switchBackgroundButton = rootView.findViewById(R.id.switchBackground);
|
||||
this.muteButton = rootView.findViewById(R.id.switchMute);
|
||||
this.switchPopupButton = rootView.findViewById(R.id.switchPopup);
|
||||
|
||||
this.queueLayout = findViewById(R.id.playQueuePanel);
|
||||
|
|
@ -536,7 +551,7 @@ public final class MainVideoPlayer extends AppCompatActivity
|
|||
titleTextView.setSelected(true);
|
||||
channelTextView.setSelected(true);
|
||||
boolean showKodiButton = PreferenceManager.getDefaultSharedPreferences(this.context).getBoolean(
|
||||
this.context.getString(R.string.show_play_with_kodi_key), false);
|
||||
this.context.getString(R.string.show_play_with_kodi_key), false);
|
||||
kodiButton.setVisibility(showKodiButton ? View.VISIBLE : View.GONE);
|
||||
|
||||
getRootView().setKeepScreenOn(true);
|
||||
|
|
@ -578,6 +593,7 @@ public final class MainVideoPlayer extends AppCompatActivity
|
|||
shareButton.setOnClickListener(this);
|
||||
toggleOrientationButton.setOnClickListener(this);
|
||||
switchBackgroundButton.setOnClickListener(this);
|
||||
muteButton.setOnClickListener(this);
|
||||
switchPopupButton.setOnClickListener(this);
|
||||
|
||||
getRootView().addOnLayoutChangeListener((view, l, t, r, b, ol, ot, or, ob) -> {
|
||||
|
|
@ -595,6 +611,19 @@ public final class MainVideoPlayer extends AppCompatActivity
|
|||
setInitialGestureValues();
|
||||
}
|
||||
});
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
queueLayout.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
|
||||
@Override
|
||||
public WindowInsets onApplyWindowInsets(View view, WindowInsets windowInsets) {
|
||||
final DisplayCutout cutout = windowInsets.getDisplayCutout();
|
||||
if (cutout != null)
|
||||
view.setPadding(cutout.getSafeInsetLeft(), cutout.getSafeInsetTop(),
|
||||
cutout.getSafeInsetRight(), cutout.getSafeInsetBottom());
|
||||
return windowInsets;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void minimize() {
|
||||
|
|
@ -683,7 +712,8 @@ public final class MainVideoPlayer extends AppCompatActivity
|
|||
this.getPlaybackSkipSilence(),
|
||||
this.getPlaybackQuality(),
|
||||
false,
|
||||
!isPlaying()
|
||||
!isPlaying(),
|
||||
isMuted()
|
||||
);
|
||||
context.startService(intent);
|
||||
|
||||
|
|
@ -707,7 +737,8 @@ public final class MainVideoPlayer extends AppCompatActivity
|
|||
this.getPlaybackSkipSilence(),
|
||||
this.getPlaybackQuality(),
|
||||
false,
|
||||
!isPlaying()
|
||||
!isPlaying(),
|
||||
isMuted()
|
||||
);
|
||||
context.startService(intent);
|
||||
|
||||
|
|
@ -716,6 +747,12 @@ public final class MainVideoPlayer extends AppCompatActivity
|
|||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMuteUnmuteButtonClicked() {
|
||||
super.onMuteUnmuteButtonClicked();
|
||||
setMuteButton(muteButton, playerImpl.isMuted());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
|
|
@ -753,11 +790,14 @@ public final class MainVideoPlayer extends AppCompatActivity
|
|||
} else if (v.getId() == switchBackgroundButton.getId()) {
|
||||
onPlayBackgroundButtonClicked();
|
||||
|
||||
} else if (v.getId() == muteButton.getId()) {
|
||||
onMuteUnmuteButtonClicked();
|
||||
|
||||
} else if (v.getId() == closeButton.getId()) {
|
||||
onPlaybackShutdown();
|
||||
return;
|
||||
} else if (v.getId() == kodiButton.getId()) {
|
||||
onKodiShare();
|
||||
onKodiShare();
|
||||
}
|
||||
|
||||
if (getCurrentState() != STATE_COMPLETED) {
|
||||
|
|
@ -800,13 +840,14 @@ public final class MainVideoPlayer extends AppCompatActivity
|
|||
animateView(secondaryControls, SLIDE_AND_ALPHA, !isMoreControlsVisible,
|
||||
DEFAULT_CONTROLS_DURATION);
|
||||
showControls(DEFAULT_CONTROLS_DURATION);
|
||||
setMuteButton(muteButton, playerImpl.isMuted());
|
||||
}
|
||||
|
||||
private void onShareClicked() {
|
||||
// share video at the current time (youtube.com/watch?v=ID&t=SECONDS)
|
||||
ShareUtils.shareUrl(MainVideoPlayer.this,
|
||||
playerImpl.getVideoTitle(),
|
||||
playerImpl.getVideoUrl() + "&t=" + String.valueOf(playerImpl.getPlaybackSeekBar().getProgress()/1000));
|
||||
playerImpl.getVideoUrl() + "&t=" + String.valueOf(playerImpl.getPlaybackSeekBar().getProgress() / 1000));
|
||||
}
|
||||
|
||||
private void onScreenRotationClicked() {
|
||||
|
|
@ -1051,7 +1092,7 @@ public final class MainVideoPlayer extends AppCompatActivity
|
|||
|
||||
@Override
|
||||
public void onSwiped(int index) {
|
||||
if(index != -1) playQueue.remove(index);
|
||||
if (index != -1) playQueue.remove(index);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -1116,6 +1157,10 @@ public final class MainVideoPlayer extends AppCompatActivity
|
|||
return repeatButton;
|
||||
}
|
||||
|
||||
public ImageButton getMuteButton() {
|
||||
return muteButton;
|
||||
}
|
||||
|
||||
public ImageButton getPlayPauseButton() {
|
||||
return playPauseButton;
|
||||
}
|
||||
|
|
@ -1130,7 +1175,8 @@ public final class MainVideoPlayer extends AppCompatActivity
|
|||
|
||||
@Override
|
||||
public boolean onDoubleTap(MotionEvent e) {
|
||||
if (DEBUG) Log.d(TAG, "onDoubleTap() called with: e = [" + e + "]" + "rawXy = " + e.getRawX() + ", " + e.getRawY() + ", xy = " + e.getX() + ", " + e.getY());
|
||||
if (DEBUG)
|
||||
Log.d(TAG, "onDoubleTap() called with: e = [" + e + "]" + "rawXy = " + e.getRawX() + ", " + e.getRawY() + ", xy = " + e.getX() + ", " + e.getY());
|
||||
|
||||
if (e.getX() > playerImpl.getRootView().getWidth() * 2 / 3) {
|
||||
playerImpl.onFastForward();
|
||||
|
|
@ -1227,7 +1273,8 @@ public final class MainVideoPlayer extends AppCompatActivity
|
|||
layoutParams.screenBrightness = currentProgressPercent;
|
||||
getWindow().setAttributes(layoutParams);
|
||||
|
||||
if (DEBUG) Log.d(TAG, "onScroll().brightnessControl, currentBrightness = " + currentProgressPercent);
|
||||
if (DEBUG)
|
||||
Log.d(TAG, "onScroll().brightnessControl, currentBrightness = " + currentProgressPercent);
|
||||
|
||||
final int resId =
|
||||
currentProgressPercent < 0.25 ? R.drawable.ic_brightness_low_white_72dp
|
||||
|
|
@ -1266,7 +1313,8 @@ public final class MainVideoPlayer extends AppCompatActivity
|
|||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
//noinspection PointlessBooleanExpression
|
||||
if (DEBUG && false) Log.d(TAG, "onTouch() called with: v = [" + v + "], event = [" + event + "]");
|
||||
if (DEBUG && false)
|
||||
Log.d(TAG, "onTouch() called with: v = [" + v + "], event = [" + event + "]");
|
||||
gestureDetector.onTouchEvent(event);
|
||||
if (event.getAction() == MotionEvent.ACTION_UP && isMoving) {
|
||||
isMoving = false;
|
||||
|
|
|
|||
|
|
@ -571,7 +571,8 @@ public final class PopupVideoPlayer extends Service {
|
|||
this.getPlaybackSkipSilence(),
|
||||
this.getPlaybackQuality(),
|
||||
false,
|
||||
!isPlaying()
|
||||
!isPlaying(),
|
||||
isMuted()
|
||||
);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
context.startActivity(intent);
|
||||
|
|
@ -607,6 +608,12 @@ public final class PopupVideoPlayer extends Service {
|
|||
updatePlayback();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMuteUnmuteButtonClicked() {
|
||||
super.onMuteUnmuteButtonClicked();
|
||||
updatePlayback();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdateProgress(int currentProgress, int duration, int bufferPercent) {
|
||||
updateProgress(currentProgress, duration, bufferPercent);
|
||||
|
|
|
|||
|
|
@ -3,14 +3,19 @@ package org.schabi.newpipe.player;
|
|||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
|
|
@ -92,6 +97,8 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
|||
private TextView playbackSpeedButton;
|
||||
private TextView playbackPitchButton;
|
||||
|
||||
private Menu menu;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Abstracts
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -145,8 +152,10 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
|||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
this.menu = menu;
|
||||
getMenuInflater().inflate(R.menu.menu_play_queue, menu);
|
||||
getMenuInflater().inflate(getPlayerOptionMenuResource(), menu);
|
||||
onMaybeMuteChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -162,6 +171,9 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
|||
case R.id.action_append_playlist:
|
||||
appendAllToPlaylist();
|
||||
return true;
|
||||
case R.id.action_mute:
|
||||
player.onMuteUnmuteButtonClicked();
|
||||
return true;
|
||||
case R.id.action_system_audio:
|
||||
startActivity(new Intent(Settings.ACTION_SOUND_SETTINGS));
|
||||
return true;
|
||||
|
|
@ -169,8 +181,8 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
|||
this.player.setRecovery();
|
||||
getApplicationContext().sendBroadcast(getPlayerShutdownIntent());
|
||||
getApplicationContext().startActivity(
|
||||
getSwitchIntent(MainVideoPlayer.class)
|
||||
.putExtra(BasePlayer.START_PAUSED, !this.player.isPlaying())
|
||||
getSwitchIntent(MainVideoPlayer.class)
|
||||
.putExtra(BasePlayer.START_PAUSED, !this.player.isPlaying())
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -194,7 +206,8 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
|||
this.player.getPlaybackSkipSilence(),
|
||||
null,
|
||||
false,
|
||||
false
|
||||
false,
|
||||
this.player.isMuted()
|
||||
).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
.putExtra(BasePlayer.START_PAUSED, !this.player.isPlaying());
|
||||
}
|
||||
|
|
@ -212,7 +225,7 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
|||
}
|
||||
|
||||
private void unbind() {
|
||||
if(serviceBound) {
|
||||
if (serviceBound) {
|
||||
unbindService(serviceConnection);
|
||||
serviceBound = false;
|
||||
stopPlayerListener();
|
||||
|
|
@ -554,6 +567,7 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
|||
onPlayModeChanged(repeatMode, shuffled);
|
||||
onPlaybackParameterChanged(parameters);
|
||||
onMaybePlaybackAdapterChanged();
|
||||
onMaybeMuteChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -676,4 +690,21 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
|||
itemsList.setAdapter(maybeNewAdapter);
|
||||
}
|
||||
}
|
||||
|
||||
private void onMaybeMuteChanged() {
|
||||
if (menu != null && player != null) {
|
||||
MenuItem item = menu.findItem(R.id.action_mute);
|
||||
|
||||
//Change the mute-button item in ActionBar
|
||||
//1) Text change:
|
||||
item.setTitle(player.isMuted() ? R.string.unmute : R.string.mute);
|
||||
|
||||
//2) Icon change accordingly to current App Theme
|
||||
TypedArray a = getTheme().obtainStyledAttributes(R.style.Theme_AppCompat, new int[]{R.attr.volume_off});
|
||||
int attributeResourceId = a.getResourceId(0, 0);
|
||||
Drawable drawableMuted = getResources().getDrawable(attributeResourceId);
|
||||
Drawable drawableUnmuted = getResources().getDrawable(R.drawable.ic_volume_off_gray_24dp);
|
||||
item.setIcon(player.isMuted() ? drawableMuted : drawableUnmuted);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import androidx.preference.Preference;
|
|||
import com.nononsenseapps.filepicker.Utils;
|
||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||
|
||||
import org.schabi.newpipe.NewPipeDatabase;
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.localization.ContentCountry;
|
||||
|
|
@ -168,6 +169,9 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
|
|||
|
||||
private void exportDatabase(String path) {
|
||||
try {
|
||||
//checkpoint before export
|
||||
NewPipeDatabase.checkpoint();
|
||||
|
||||
ZipOutputStream outZip = new ZipOutputStream(
|
||||
new BufferedOutputStream(
|
||||
new FileOutputStream(path)));
|
||||
|
|
|
|||
|
|
@ -6,11 +6,15 @@ import android.os.Build;
|
|||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
|
||||
import android.text.format.DateUtils;
|
||||
import android.widget.Toast;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.preference.ListPreference;
|
||||
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.util.PermissionHelper;
|
||||
|
||||
|
|
@ -22,23 +26,7 @@ public class VideoAudioSettingsFragment extends BasePreferenceFragment {
|
|||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
//initializing R.array.seek_duration_description to display the translation of seconds
|
||||
Resources res = getResources();
|
||||
String[] durationsValues = res.getStringArray(R.array.seek_duration_value);
|
||||
String[] durationsDescriptions = res.getStringArray(R.array.seek_duration_description);
|
||||
int currentDurationValue;
|
||||
for (int i = 0; i < durationsDescriptions.length; i++) {
|
||||
currentDurationValue = Integer.parseInt(durationsValues[i]) / 1000;
|
||||
try {
|
||||
durationsDescriptions[i] = String.format(
|
||||
res.getQuantityString(R.plurals.dynamic_seek_duration_description, currentDurationValue),
|
||||
currentDurationValue);
|
||||
} catch (Resources.NotFoundException ignored) {
|
||||
//if this happens, the translation is missing, and the english string will be displayed instead
|
||||
}
|
||||
}
|
||||
ListPreference durations = (ListPreference) findPreference(getString(R.string.seek_duration_key));
|
||||
durations.setEntries(durationsDescriptions);
|
||||
updateSeekOptions();
|
||||
|
||||
listener = (sharedPreferences, s) -> {
|
||||
|
||||
|
|
@ -58,10 +46,59 @@ public class VideoAudioSettingsFragment extends BasePreferenceFragment {
|
|||
.show();
|
||||
|
||||
}
|
||||
} else if (s.equals(getString(R.string.use_inexact_seek_key))) {
|
||||
updateSeekOptions();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Update fast-forward/-rewind seek duration options according to language and inexact seek setting.
|
||||
* Exoplayer can't seek 5 seconds in audio when using inexact seek.
|
||||
*/
|
||||
private void updateSeekOptions() {
|
||||
//initializing R.array.seek_duration_description to display the translation of seconds
|
||||
final Resources res = getResources();
|
||||
final String[] durationsValues = res.getStringArray(R.array.seek_duration_value);
|
||||
final List<String> displayedDurationValues = new LinkedList<>();
|
||||
final List<String> displayedDescriptionValues = new LinkedList<>();
|
||||
int currentDurationValue;
|
||||
final boolean inexactSeek = getPreferenceManager().getSharedPreferences()
|
||||
.getBoolean(res.getString(R.string.use_inexact_seek_key), false);
|
||||
|
||||
for (String durationsValue : durationsValues) {
|
||||
currentDurationValue =
|
||||
Integer.parseInt(durationsValue) / (int) DateUtils.SECOND_IN_MILLIS;
|
||||
if (inexactSeek && currentDurationValue % 10 == 5) {
|
||||
continue;
|
||||
}
|
||||
|
||||
displayedDurationValues.add(durationsValue);
|
||||
try {
|
||||
displayedDescriptionValues.add(String.format(
|
||||
res.getQuantityString(R.plurals.dynamic_seek_duration_description,
|
||||
currentDurationValue),
|
||||
currentDurationValue));
|
||||
} catch (Resources.NotFoundException ignored) {
|
||||
//if this happens, the translation is missing, and the english string will be displayed instead
|
||||
}
|
||||
}
|
||||
|
||||
final ListPreference durations = (ListPreference) findPreference(getString(R.string.seek_duration_key));
|
||||
durations.setEntryValues(displayedDurationValues.toArray(new CharSequence[0]));
|
||||
durations.setEntries(displayedDescriptionValues.toArray(new CharSequence[0]));
|
||||
final int selectedDuration = Integer.parseInt(durations.getValue());
|
||||
if (selectedDuration / (int) DateUtils.SECOND_IN_MILLIS % 10 == 5) {
|
||||
final int newDuration = selectedDuration / (int) DateUtils.SECOND_IN_MILLIS + 5;
|
||||
durations.setValue(Integer.toString(newDuration * (int) DateUtils.SECOND_IN_MILLIS));
|
||||
|
||||
Toast toast = Toast
|
||||
.makeText(getContext(),
|
||||
getString(R.string.new_seek_duration_toast, newDuration),
|
||||
Toast.LENGTH_LONG);
|
||||
toast.show();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ import org.ocpsoft.prettytime.units.Decade;
|
|||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.localization.ContentCountry;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.text.DateFormat;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.Arrays;
|
||||
|
|
@ -26,6 +28,7 @@ import java.util.Date;
|
|||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
|
||||
/*
|
||||
* Created by chschtsch on 12/29/15.
|
||||
*
|
||||
|
|
@ -110,7 +113,7 @@ public class Localization {
|
|||
if (languageCode.length() == 2) {
|
||||
return new Locale(languageCode);
|
||||
} else if (languageCode.contains("_")) {
|
||||
String country = languageCode.substring(languageCode.indexOf("_"), languageCode.length());
|
||||
String country = languageCode.substring(languageCode.indexOf("_"));
|
||||
return new Locale(languageCode.substring(0, 2), country);
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
|
|
@ -120,6 +123,10 @@ public class Localization {
|
|||
}
|
||||
|
||||
public static String localizeNumber(Context context, long number) {
|
||||
return localizeNumber(context, (double) number);
|
||||
}
|
||||
|
||||
public static String localizeNumber(Context context, double number) {
|
||||
NumberFormat nf = NumberFormat.getInstance(getAppLocale(context));
|
||||
return nf.format(number);
|
||||
}
|
||||
|
|
@ -146,14 +153,15 @@ public class Localization {
|
|||
}
|
||||
|
||||
public static String shortCount(Context context, long count) {
|
||||
double value = (double) count;
|
||||
if (count >= 1000000000) {
|
||||
return Long.toString(count / 1000000000) + context.getString(R.string.short_billion);
|
||||
return localizeNumber(context, round(value / 1000000000, 1)) + context.getString(R.string.short_billion);
|
||||
} else if (count >= 1000000) {
|
||||
return Long.toString(count / 1000000) + context.getString(R.string.short_million);
|
||||
return localizeNumber(context, round(value / 1000000, 1)) + context.getString(R.string.short_million);
|
||||
} else if (count >= 1000) {
|
||||
return Long.toString(count / 1000) + context.getString(R.string.short_thousand);
|
||||
return localizeNumber(context, round(value / 1000, 1)) + context.getString(R.string.short_thousand);
|
||||
} else {
|
||||
return Long.toString(count);
|
||||
return localizeNumber(context, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -254,4 +262,8 @@ public class Localization {
|
|||
public static void assureCorrectAppLanguage(Context c) {
|
||||
changeAppLanguage(getAppLocale(c), c.getResources());
|
||||
}
|
||||
|
||||
private static double round(double value, int places) {
|
||||
return new BigDecimal(value).setScale(places, RoundingMode.HALF_UP).doubleValue();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -110,13 +110,15 @@ public class NavigationHelper {
|
|||
final boolean playbackSkipSilence,
|
||||
@Nullable final String playbackQuality,
|
||||
final boolean resumePlayback,
|
||||
final boolean startPaused) {
|
||||
final boolean startPaused,
|
||||
final boolean isMuted) {
|
||||
return getPlayerIntent(context, targetClazz, playQueue, playbackQuality, resumePlayback)
|
||||
.putExtra(BasePlayer.REPEAT_MODE, repeatMode)
|
||||
.putExtra(BasePlayer.PLAYBACK_SPEED, playbackSpeed)
|
||||
.putExtra(BasePlayer.PLAYBACK_PITCH, playbackPitch)
|
||||
.putExtra(BasePlayer.PLAYBACK_SKIP_SILENCE, playbackSkipSilence)
|
||||
.putExtra(BasePlayer.START_PAUSED, startPaused);
|
||||
.putExtra(BasePlayer.START_PAUSED, startPaused)
|
||||
.putExtra(BasePlayer.IS_MUTED, isMuted);
|
||||
}
|
||||
|
||||
public static void playOnMainPlayer(final Context context, final PlayQueue queue, final boolean resumePlayback) {
|
||||
|
|
|
|||
|
|
@ -341,17 +341,7 @@ public class MissionAdapter extends Adapter<ViewHolder> implements Handler.Callb
|
|||
if (BuildConfig.DEBUG)
|
||||
Log.v(TAG, "Mime: " + mimeType + " package: " + BuildConfig.APPLICATION_ID + ".provider");
|
||||
|
||||
Uri uri;
|
||||
|
||||
if (mission.storage.isDirect()) {
|
||||
uri = FileProvider.getUriForFile(
|
||||
mContext,
|
||||
BuildConfig.APPLICATION_ID + ".provider",
|
||||
new File(URI.create(mission.storage.getUri().toString()))
|
||||
);
|
||||
} else {
|
||||
uri = mission.storage.getUri();
|
||||
}
|
||||
Uri uri = resolveShareableUri(mission);
|
||||
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(Intent.ACTION_VIEW);
|
||||
|
|
@ -379,11 +369,30 @@ public class MissionAdapter extends Adapter<ViewHolder> implements Handler.Callb
|
|||
|
||||
Intent intent = new Intent(Intent.ACTION_SEND);
|
||||
intent.setType(resolveMimeType(mission));
|
||||
intent.putExtra(Intent.EXTRA_STREAM, mission.storage.getUri());
|
||||
intent.putExtra(Intent.EXTRA_STREAM, resolveShareableUri(mission));
|
||||
intent.addFlags(FLAG_GRANT_READ_URI_PERMISSION);
|
||||
|
||||
mContext.startActivity(Intent.createChooser(intent, null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Uri which can be shared to other applications.
|
||||
*
|
||||
* @see <a href="https://stackoverflow.com/questions/38200282/android-os-fileuriexposedexception-file-storage-emulated-0-test-txt-exposed">
|
||||
* https://stackoverflow.com/questions/38200282/android-os-fileuriexposedexception-file-storage-emulated-0-test-txt-exposed</a>
|
||||
*/
|
||||
private Uri resolveShareableUri(Mission mission) {
|
||||
if (mission.storage.isDirect()) {
|
||||
return FileProvider.getUriForFile(
|
||||
mContext,
|
||||
BuildConfig.APPLICATION_ID + ".provider",
|
||||
new File(URI.create(mission.storage.getUri().toString()))
|
||||
);
|
||||
} else {
|
||||
return mission.storage.getUri();
|
||||
}
|
||||
}
|
||||
|
||||
private static String resolveMimeType(@NonNull Mission mission) {
|
||||
String mimeType;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue