Merge remote-tracking branch 'newpipe/dev' into rebase

This commit is contained in:
Alexander-- 2020-03-12 04:48:37 +06:59
commit fa6823599a
49 changed files with 825 additions and 421 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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