Merge remote-tracking branch 'origin/dev' into notifications-1
This commit is contained in:
commit
cea14c9d0d
95 changed files with 1133 additions and 789 deletions
|
|
@ -5,7 +5,6 @@ import android.content.SharedPreferences;
|
|||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.app.NotificationChannelCompat;
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
import androidx.multidex.MultiDexApplication;
|
||||
|
|
@ -37,7 +36,6 @@ import java.util.Arrays;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.rxjava3.disposables.Disposable;
|
||||
import io.reactivex.rxjava3.exceptions.CompositeException;
|
||||
import io.reactivex.rxjava3.exceptions.MissingBackpressureException;
|
||||
import io.reactivex.rxjava3.exceptions.OnErrorNotImplementedException;
|
||||
|
|
@ -45,6 +43,8 @@ import io.reactivex.rxjava3.exceptions.UndeliverableException;
|
|||
import io.reactivex.rxjava3.functions.Consumer;
|
||||
import io.reactivex.rxjava3.plugins.RxJavaPlugins;
|
||||
|
||||
import static org.schabi.newpipe.CheckForNewAppVersion.startNewVersionCheckService;
|
||||
|
||||
/*
|
||||
* Copyright (C) Hans-Christoph Steiner 2016 <hans@eds.org>
|
||||
* App.java is part of NewPipe.
|
||||
|
|
@ -68,9 +68,6 @@ public class App extends MultiDexApplication {
|
|||
private static final String TAG = App.class.toString();
|
||||
private static App app;
|
||||
|
||||
@Nullable
|
||||
private Disposable disposable = null;
|
||||
|
||||
@NonNull
|
||||
public static App getApp() {
|
||||
return app;
|
||||
|
|
@ -117,14 +114,11 @@ public class App extends MultiDexApplication {
|
|||
|
||||
configureRxJavaErrorHandler();
|
||||
// Check for new version
|
||||
disposable = CheckForNewAppVersion.checkNewVersion(this);
|
||||
startNewVersionCheckService();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTerminate() {
|
||||
if (disposable != null) {
|
||||
disposable.dispose();
|
||||
}
|
||||
super.onTerminate();
|
||||
PicassoHelper.terminate();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package org.schabi.newpipe;
|
||||
|
||||
import android.app.Application;
|
||||
import android.app.IntentService;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
|
|
@ -25,8 +26,11 @@ import com.grack.nanojson.JsonParserException;
|
|||
import org.schabi.newpipe.error.ErrorActivity;
|
||||
import org.schabi.newpipe.error.ErrorInfo;
|
||||
import org.schabi.newpipe.error.UserAction;
|
||||
import org.schabi.newpipe.extractor.downloader.Response;
|
||||
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
|
@ -36,13 +40,10 @@ import java.security.cert.CertificateFactory;
|
|||
import java.security.cert.X509Certificate;
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.core.Maybe;
|
||||
import io.reactivex.rxjava3.disposables.Disposable;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
|
||||
public final class CheckForNewAppVersion {
|
||||
private CheckForNewAppVersion() { }
|
||||
public final class CheckForNewAppVersion extends IntentService {
|
||||
public CheckForNewAppVersion() {
|
||||
super("CheckForNewAppVersion");
|
||||
}
|
||||
|
||||
private static final boolean DEBUG = MainActivity.DEBUG;
|
||||
private static final String TAG = CheckForNewAppVersion.class.getSimpleName();
|
||||
|
|
@ -168,78 +169,87 @@ public final class CheckForNewAppVersion {
|
|||
return getCertificateSHA1Fingerprint(app).equals(GITHUB_APK_SHA1);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Disposable checkNewVersion(@NonNull final App app) {
|
||||
private void checkNewVersion() throws IOException, ReCaptchaException {
|
||||
final App app = App.getApp();
|
||||
|
||||
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(app);
|
||||
final NewVersionManager manager = new NewVersionManager();
|
||||
|
||||
// Check if user has enabled/disabled update checking
|
||||
// and if the current apk is a github one or not.
|
||||
if (!prefs.getBoolean(app.getString(R.string.update_app_key), true) || !isGithubApk(app)) {
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the last request has happened a certain time ago
|
||||
// to reduce the number of API requests.
|
||||
final long expiry = prefs.getLong(app.getString(R.string.update_expiry_key), 0);
|
||||
if (!manager.isExpired(expiry)) {
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
return Maybe
|
||||
.fromCallable(() -> {
|
||||
if (!isConnected(app)) {
|
||||
return null;
|
||||
}
|
||||
// Make a network request to get latest NewPipe data.
|
||||
final Response response = DownloaderImpl.getInstance().get(NEWPIPE_API_URL);
|
||||
handleResponse(response, manager, prefs, app);
|
||||
}
|
||||
|
||||
// Make a network request to get latest NewPipe data.
|
||||
return DownloaderImpl.getInstance().get(NEWPIPE_API_URL);
|
||||
})
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
response -> {
|
||||
try {
|
||||
// Store a timestamp which needs to be exceeded,
|
||||
// before a new request to the API is made.
|
||||
final long newExpiry = manager
|
||||
.coerceExpiry(response.getHeader("expires"));
|
||||
prefs.edit()
|
||||
.putLong(app.getString(R.string.update_expiry_key), newExpiry)
|
||||
.apply();
|
||||
} catch (final Exception e) {
|
||||
if (DEBUG) {
|
||||
Log.w(TAG, "Could not extract and save new expiry date", e);
|
||||
}
|
||||
}
|
||||
private void handleResponse(@NonNull final Response response,
|
||||
@NonNull final NewVersionManager manager,
|
||||
@NonNull final SharedPreferences prefs,
|
||||
@NonNull final App app) {
|
||||
try {
|
||||
// Store a timestamp which needs to be exceeded,
|
||||
// before a new request to the API is made.
|
||||
final long newExpiry = manager
|
||||
.coerceExpiry(response.getHeader("expires"));
|
||||
prefs.edit()
|
||||
.putLong(app.getString(R.string.update_expiry_key), newExpiry)
|
||||
.apply();
|
||||
} catch (final Exception e) {
|
||||
if (DEBUG) {
|
||||
Log.w(TAG, "Could not extract and save new expiry date", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse the json from the response.
|
||||
try {
|
||||
final JsonObject githubStableObject = JsonParser.object()
|
||||
.from(response.responseBody()).getObject("flavors")
|
||||
.getObject("github").getObject("stable");
|
||||
// Parse the json from the response.
|
||||
try {
|
||||
final JsonObject githubStableObject = JsonParser.object()
|
||||
.from(response.responseBody()).getObject("flavors")
|
||||
.getObject("github").getObject("stable");
|
||||
|
||||
final String versionName = githubStableObject
|
||||
.getString("version");
|
||||
final int versionCode = githubStableObject
|
||||
.getInt("version_code");
|
||||
final String apkLocationUrl = githubStableObject
|
||||
.getString("apk");
|
||||
final String versionName = githubStableObject
|
||||
.getString("version");
|
||||
final int versionCode = githubStableObject
|
||||
.getInt("version_code");
|
||||
final String apkLocationUrl = githubStableObject
|
||||
.getString("apk");
|
||||
|
||||
compareAppVersionAndShowNotification(app, versionName,
|
||||
apkLocationUrl, versionCode);
|
||||
} catch (final JsonParserException e) {
|
||||
// Most likely something is wrong in data received from NEWPIPE_API_URL.
|
||||
// Do not alarm user and fail silently.
|
||||
if (DEBUG) {
|
||||
Log.w(TAG, "Could not get NewPipe API: invalid json", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void startNewVersionCheckService() {
|
||||
final Intent intent = new Intent(App.getApp().getApplicationContext(),
|
||||
CheckForNewAppVersion.class);
|
||||
App.getApp().startService(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onHandleIntent(@Nullable final Intent intent) {
|
||||
try {
|
||||
checkNewVersion();
|
||||
} catch (final IOException e) {
|
||||
Log.w(TAG, "Could not fetch NewPipe API: probably network problem", e);
|
||||
} catch (final ReCaptchaException e) {
|
||||
Log.e(TAG, "ReCaptchaException should never happen here.", e);
|
||||
}
|
||||
|
||||
compareAppVersionAndShowNotification(app, versionName,
|
||||
apkLocationUrl, versionCode);
|
||||
} catch (final JsonParserException e) {
|
||||
// connectivity problems, do not alarm user and fail silently
|
||||
if (DEBUG) {
|
||||
Log.w(TAG, "Could not get NewPipe API: invalid json", e);
|
||||
}
|
||||
}
|
||||
},
|
||||
e -> {
|
||||
// connectivity problems, do not alarm user and fail silently
|
||||
if (DEBUG) {
|
||||
Log.w(TAG, "Could not get NewPipe API: network problem", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
package org.schabi.newpipe.info_list
|
||||
|
||||
import android.util.Log
|
||||
import com.xwray.groupie.GroupAdapter
|
||||
import com.xwray.groupie.GroupieViewHolder
|
||||
import com.xwray.groupie.GroupieAdapter
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfo
|
||||
import kotlin.math.max
|
||||
|
||||
|
|
@ -11,7 +10,7 @@ import kotlin.math.max
|
|||
*/
|
||||
class StreamSegmentAdapter(
|
||||
private val listener: StreamSegmentListener
|
||||
) : GroupAdapter<GroupieViewHolder>() {
|
||||
) : GroupieAdapter() {
|
||||
|
||||
var currentIndex: Int = 0
|
||||
private set
|
||||
|
|
|
|||
|
|
@ -40,8 +40,7 @@ import androidx.core.view.isVisible
|
|||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import com.xwray.groupie.GroupAdapter
|
||||
import com.xwray.groupie.GroupieViewHolder
|
||||
import com.xwray.groupie.GroupieAdapter
|
||||
import com.xwray.groupie.Item
|
||||
import com.xwray.groupie.OnItemClickListener
|
||||
import com.xwray.groupie.OnItemLongClickListener
|
||||
|
|
@ -91,7 +90,7 @@ class FeedFragment : BaseStateFragment<FeedState>() {
|
|||
private var groupName = ""
|
||||
private var oldestSubscriptionUpdate: OffsetDateTime? = null
|
||||
|
||||
private lateinit var groupAdapter: GroupAdapter<GroupieViewHolder>
|
||||
private lateinit var groupAdapter: GroupieAdapter
|
||||
@State @JvmField var showPlayedItems: Boolean = true
|
||||
|
||||
private var onSettingsChangeListener: SharedPreferences.OnSharedPreferenceChangeListener? = null
|
||||
|
|
@ -131,7 +130,7 @@ class FeedFragment : BaseStateFragment<FeedState>() {
|
|||
viewModel = ViewModelProvider(this, factory).get(FeedViewModel::class.java)
|
||||
viewModel.stateLiveData.observe(viewLifecycleOwner, { it?.let(::handleResult) })
|
||||
|
||||
groupAdapter = GroupAdapter<GroupieViewHolder>().apply {
|
||||
groupAdapter = GroupieAdapter().apply {
|
||||
setOnItemClickListener(listenerStreamItem)
|
||||
setOnItemLongClickListener(listenerStreamItem)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import org.schabi.newpipe.extractor.stream.StreamType.LIVE_STREAM
|
|||
import org.schabi.newpipe.extractor.stream.StreamType.VIDEO_STREAM
|
||||
import org.schabi.newpipe.util.Localization
|
||||
import org.schabi.newpipe.util.PicassoHelper
|
||||
import org.schabi.newpipe.util.StreamTypeUtil
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
data class StreamItem(
|
||||
|
|
@ -58,8 +59,6 @@ data class StreamItem(
|
|||
viewBinding.itemVideoTitleView.text = stream.title
|
||||
viewBinding.itemUploaderView.text = stream.uploader
|
||||
|
||||
val isLiveStream = stream.streamType == LIVE_STREAM || stream.streamType == AUDIO_LIVE_STREAM
|
||||
|
||||
if (stream.duration > 0) {
|
||||
viewBinding.itemDurationView.text = Localization.getDurationString(stream.duration)
|
||||
viewBinding.itemDurationView.setBackgroundColor(
|
||||
|
|
@ -77,7 +76,7 @@ data class StreamItem(
|
|||
} else {
|
||||
viewBinding.itemProgressView.visibility = View.GONE
|
||||
}
|
||||
} else if (isLiveStream) {
|
||||
} else if (StreamTypeUtil.isLiveStream(stream.streamType)) {
|
||||
viewBinding.itemDurationView.setText(R.string.duration_live)
|
||||
viewBinding.itemDurationView.setBackgroundColor(
|
||||
ContextCompat.getColor(
|
||||
|
|
|
|||
|
|
@ -334,9 +334,9 @@ public class HistoryRecordManager {
|
|||
.getState(entities.get(0).getUid()).blockingFirst();
|
||||
if (states.isEmpty()) {
|
||||
result.add(null);
|
||||
continue;
|
||||
} else {
|
||||
result.add(states.get(0));
|
||||
}
|
||||
result.add(states.get(0));
|
||||
}
|
||||
return result;
|
||||
}).subscribeOn(Schedulers.io());
|
||||
|
|
@ -362,9 +362,9 @@ public class HistoryRecordManager {
|
|||
.blockingFirst();
|
||||
if (states.isEmpty()) {
|
||||
result.add(null);
|
||||
continue;
|
||||
} else {
|
||||
result.add(states.get(0));
|
||||
}
|
||||
result.add(states.get(0));
|
||||
}
|
||||
return result;
|
||||
}).subscribeOn(Schedulers.io());
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ enum class FeedGroupIcon(
|
|||
COMPUTER(5, R.drawable.ic_computer),
|
||||
GAMING(6, R.drawable.ic_videogame_asset),
|
||||
SPORTS(7, R.drawable.ic_directions_bike),
|
||||
NEWS(8, R.drawable.ic_megaphone),
|
||||
NEWS(8, R.drawable.ic_campaign),
|
||||
FAVORITES(9, R.drawable.ic_favorite),
|
||||
CAR(10, R.drawable.ic_directions_car),
|
||||
MOTORCYCLE(11, R.drawable.ic_motorcycle),
|
||||
|
|
|
|||
|
|
@ -21,8 +21,7 @@ import androidx.lifecycle.Observer
|
|||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.xwray.groupie.GroupAdapter
|
||||
import com.xwray.groupie.GroupieViewHolder
|
||||
import com.xwray.groupie.GroupieAdapter
|
||||
import com.xwray.groupie.OnItemClickListener
|
||||
import com.xwray.groupie.Section
|
||||
import icepick.Icepick
|
||||
|
|
@ -78,7 +77,7 @@ class FeedGroupDialog : DialogFragment(), BackPressable {
|
|||
|
||||
private val subscriptionMainSection = Section()
|
||||
private val subscriptionEmptyFooter = Section()
|
||||
private lateinit var subscriptionGroupAdapter: GroupAdapter<GroupieViewHolder>
|
||||
private lateinit var subscriptionGroupAdapter: GroupieAdapter
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
|
@ -153,7 +152,7 @@ class FeedGroupDialog : DialogFragment(), BackPressable {
|
|||
}
|
||||
}
|
||||
|
||||
subscriptionGroupAdapter = GroupAdapter<GroupieViewHolder>().apply {
|
||||
subscriptionGroupAdapter = GroupieAdapter().apply {
|
||||
add(subscriptionMainSection)
|
||||
add(subscriptionEmptyFooter)
|
||||
spanCount = 4
|
||||
|
|
@ -379,7 +378,7 @@ class FeedGroupDialog : DialogFragment(), BackPressable {
|
|||
}
|
||||
|
||||
private fun setupIconPicker() {
|
||||
val groupAdapter = GroupAdapter<GroupieViewHolder>()
|
||||
val groupAdapter = GroupieAdapter()
|
||||
groupAdapter.addAll(FeedGroupIcon.values().map { PickerIconItem(it) })
|
||||
|
||||
feedGroupCreateBinding.iconSelector.apply {
|
||||
|
|
|
|||
|
|
@ -11,8 +11,7 @@ import androidx.recyclerview.widget.ItemTouchHelper
|
|||
import androidx.recyclerview.widget.ItemTouchHelper.SimpleCallback
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.xwray.groupie.GroupAdapter
|
||||
import com.xwray.groupie.GroupieViewHolder
|
||||
import com.xwray.groupie.GroupieAdapter
|
||||
import com.xwray.groupie.TouchCallback
|
||||
import icepick.Icepick
|
||||
import icepick.State
|
||||
|
|
@ -38,7 +37,7 @@ class FeedGroupReorderDialog : DialogFragment() {
|
|||
@State
|
||||
@JvmField
|
||||
var groupOrderedIdList = ArrayList<Long>()
|
||||
private val groupAdapter = GroupAdapter<GroupieViewHolder>()
|
||||
private val groupAdapter = GroupieAdapter()
|
||||
private val itemTouchHelper = ItemTouchHelper(getItemTouchCallback())
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,53 @@
|
|||
package org.schabi.newpipe.player;
|
||||
|
||||
import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_AD_INSERTION;
|
||||
import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_INTERNAL;
|
||||
import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_PERIOD_TRANSITION;
|
||||
import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_SEEK;
|
||||
import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_SEEK_ADJUSTMENT;
|
||||
import static com.google.android.exoplayer2.Player.DiscontinuityReason;
|
||||
import static com.google.android.exoplayer2.Player.EventListener;
|
||||
import static com.google.android.exoplayer2.Player.REPEAT_MODE_ALL;
|
||||
import static com.google.android.exoplayer2.Player.REPEAT_MODE_OFF;
|
||||
import static com.google.android.exoplayer2.Player.REPEAT_MODE_ONE;
|
||||
import static com.google.android.exoplayer2.Player.RepeatMode;
|
||||
import static org.schabi.newpipe.QueueItemMenuUtil.openPopupMenu;
|
||||
import static org.schabi.newpipe.extractor.ServiceList.YouTube;
|
||||
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
|
||||
import static org.schabi.newpipe.ktx.ViewUtils.animate;
|
||||
import static org.schabi.newpipe.ktx.ViewUtils.animateRotation;
|
||||
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_RECREATE_NOTIFICATION;
|
||||
import static org.schabi.newpipe.player.MainPlayer.ACTION_REPEAT;
|
||||
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_NONE;
|
||||
import static org.schabi.newpipe.player.helper.PlayerHelper.MinimizeMode.MINIMIZE_ON_EXIT_MODE_POPUP;
|
||||
import static org.schabi.newpipe.player.helper.PlayerHelper.buildCloseOverlayLayoutParams;
|
||||
import static org.schabi.newpipe.player.helper.PlayerHelper.formatSpeed;
|
||||
import static org.schabi.newpipe.player.helper.PlayerHelper.getMinimizeOnExitAction;
|
||||
import static org.schabi.newpipe.player.helper.PlayerHelper.getMinimumVideoHeight;
|
||||
import static org.schabi.newpipe.player.helper.PlayerHelper.getTimeString;
|
||||
import static org.schabi.newpipe.player.helper.PlayerHelper.globalScreenOrientationLocked;
|
||||
import static org.schabi.newpipe.player.helper.PlayerHelper.isPlaybackResumeEnabled;
|
||||
import static org.schabi.newpipe.player.helper.PlayerHelper.nextRepeatMode;
|
||||
import static org.schabi.newpipe.player.helper.PlayerHelper.nextResizeModeAndSaveToPrefs;
|
||||
import static org.schabi.newpipe.player.helper.PlayerHelper.retrievePlaybackParametersFromPrefs;
|
||||
import static org.schabi.newpipe.player.helper.PlayerHelper.retrievePlayerTypeFromIntent;
|
||||
import static org.schabi.newpipe.player.helper.PlayerHelper.retrievePopupLayoutParamsFromPrefs;
|
||||
import static org.schabi.newpipe.player.helper.PlayerHelper.retrieveSeekDurationFromPreferences;
|
||||
import static org.schabi.newpipe.player.helper.PlayerHelper.savePlaybackParametersToPrefs;
|
||||
import static org.schabi.newpipe.util.ListHelper.getPopupResolutionIndex;
|
||||
import static org.schabi.newpipe.util.ListHelper.getResolutionIndex;
|
||||
import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
|
||||
import static org.schabi.newpipe.util.Localization.containsCaseInsensitive;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ObjectAnimator;
|
||||
|
|
@ -94,7 +142,6 @@ import org.schabi.newpipe.databinding.PlayerPopupCloseOverlayBinding;
|
|||
import org.schabi.newpipe.extractor.MediaFormat;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||
import org.schabi.newpipe.extractor.stream.StreamSegment;
|
||||
import org.schabi.newpipe.extractor.stream.StreamType;
|
||||
import org.schabi.newpipe.extractor.stream.VideoStream;
|
||||
import org.schabi.newpipe.fragments.OnScrollBelowItemsListener;
|
||||
import org.schabi.newpipe.fragments.detail.VideoDetailFragment;
|
||||
|
|
@ -127,12 +174,13 @@ import org.schabi.newpipe.player.resolver.MediaSourceTag;
|
|||
import org.schabi.newpipe.player.resolver.VideoPlaybackResolver;
|
||||
import org.schabi.newpipe.player.seekbarpreview.SeekbarPreviewThumbnailHelper;
|
||||
import org.schabi.newpipe.player.seekbarpreview.SeekbarPreviewThumbnailHolder;
|
||||
import org.schabi.newpipe.util.StreamTypeUtil;
|
||||
import org.schabi.newpipe.util.DeviceUtils;
|
||||
import org.schabi.newpipe.util.external_communication.KoreUtils;
|
||||
import org.schabi.newpipe.util.ListHelper;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
import org.schabi.newpipe.util.PicassoHelper;
|
||||
import org.schabi.newpipe.util.SerializedCache;
|
||||
import org.schabi.newpipe.util.external_communication.KoreUtils;
|
||||
import org.schabi.newpipe.util.external_communication.ShareUtils;
|
||||
import org.schabi.newpipe.views.ExpandableSurfaceView;
|
||||
|
||||
|
|
@ -140,6 +188,7 @@ import java.io.IOException;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.core.Observable;
|
||||
|
|
@ -147,54 +196,6 @@ import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
|||
import io.reactivex.rxjava3.disposables.Disposable;
|
||||
import io.reactivex.rxjava3.disposables.SerialDisposable;
|
||||
|
||||
import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_AD_INSERTION;
|
||||
import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_INTERNAL;
|
||||
import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_PERIOD_TRANSITION;
|
||||
import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_SEEK;
|
||||
import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_SEEK_ADJUSTMENT;
|
||||
import static com.google.android.exoplayer2.Player.DiscontinuityReason;
|
||||
import static com.google.android.exoplayer2.Player.EventListener;
|
||||
import static com.google.android.exoplayer2.Player.REPEAT_MODE_ALL;
|
||||
import static com.google.android.exoplayer2.Player.REPEAT_MODE_OFF;
|
||||
import static com.google.android.exoplayer2.Player.REPEAT_MODE_ONE;
|
||||
import static com.google.android.exoplayer2.Player.RepeatMode;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static org.schabi.newpipe.QueueItemMenuUtil.openPopupMenu;
|
||||
import static org.schabi.newpipe.extractor.ServiceList.YouTube;
|
||||
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
|
||||
import static org.schabi.newpipe.ktx.ViewUtils.animate;
|
||||
import static org.schabi.newpipe.ktx.ViewUtils.animateRotation;
|
||||
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_RECREATE_NOTIFICATION;
|
||||
import static org.schabi.newpipe.player.MainPlayer.ACTION_REPEAT;
|
||||
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_NONE;
|
||||
import static org.schabi.newpipe.player.helper.PlayerHelper.MinimizeMode.MINIMIZE_ON_EXIT_MODE_POPUP;
|
||||
import static org.schabi.newpipe.player.helper.PlayerHelper.buildCloseOverlayLayoutParams;
|
||||
import static org.schabi.newpipe.player.helper.PlayerHelper.formatSpeed;
|
||||
import static org.schabi.newpipe.player.helper.PlayerHelper.getMinimizeOnExitAction;
|
||||
import static org.schabi.newpipe.player.helper.PlayerHelper.getMinimumVideoHeight;
|
||||
import static org.schabi.newpipe.player.helper.PlayerHelper.getTimeString;
|
||||
import static org.schabi.newpipe.player.helper.PlayerHelper.globalScreenOrientationLocked;
|
||||
import static org.schabi.newpipe.player.helper.PlayerHelper.isPlaybackResumeEnabled;
|
||||
import static org.schabi.newpipe.player.helper.PlayerHelper.nextRepeatMode;
|
||||
import static org.schabi.newpipe.player.helper.PlayerHelper.nextResizeModeAndSaveToPrefs;
|
||||
import static org.schabi.newpipe.player.helper.PlayerHelper.retrievePlaybackParametersFromPrefs;
|
||||
import static org.schabi.newpipe.player.helper.PlayerHelper.retrievePlayerTypeFromIntent;
|
||||
import static org.schabi.newpipe.player.helper.PlayerHelper.retrievePopupLayoutParamsFromPrefs;
|
||||
import static org.schabi.newpipe.player.helper.PlayerHelper.retrieveSeekDurationFromPreferences;
|
||||
import static org.schabi.newpipe.player.helper.PlayerHelper.savePlaybackParametersToPrefs;
|
||||
import static org.schabi.newpipe.util.ListHelper.getPopupResolutionIndex;
|
||||
import static org.schabi.newpipe.util.ListHelper.getResolutionIndex;
|
||||
import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
|
||||
import static org.schabi.newpipe.util.Localization.containsCaseInsensitive;
|
||||
|
||||
public final class Player implements
|
||||
EventListener,
|
||||
PlaybackListener,
|
||||
|
|
@ -1596,10 +1597,7 @@ public final class Player implements
|
|||
setVideoDurationToControls(duration);
|
||||
}
|
||||
if (currentState != STATE_PAUSED) {
|
||||
if (currentState != STATE_PAUSED_SEEK) {
|
||||
binding.playbackSeekBar.setProgress(currentProgress);
|
||||
}
|
||||
binding.playbackCurrentTime.setText(getTimeString(currentProgress));
|
||||
updatePlayBackElementsCurrentDuration(currentProgress);
|
||||
}
|
||||
if (simpleExoPlayer.isLoading() || bufferPercent > 90) {
|
||||
binding.playbackSeekBar.setSecondaryProgress(
|
||||
|
|
@ -1622,12 +1620,6 @@ public final class Player implements
|
|||
if (isQueueVisible) {
|
||||
updateQueueTime(currentProgress);
|
||||
}
|
||||
|
||||
final boolean showThumbnail = prefs.getBoolean(
|
||||
context.getString(R.string.show_thumbnail_key), true);
|
||||
// setMetadata only updates the metadata when any of the metadata keys are null
|
||||
mediaSessionManager.setMetadata(getVideoTitle(), getUploaderName(),
|
||||
showThumbnail ? getThumbnail() : null, duration);
|
||||
}
|
||||
|
||||
private void startProgressLoop() {
|
||||
|
|
@ -1652,10 +1644,10 @@ public final class Player implements
|
|||
// TODO: revert #6307 when introducing proper HLS support
|
||||
final int duration;
|
||||
if (currentItem != null
|
||||
&& currentItem.getStreamType() != StreamType.AUDIO_LIVE_STREAM
|
||||
&& currentItem.getStreamType() != StreamType.LIVE_STREAM) {
|
||||
&& !StreamTypeUtil.isLiveStream(currentItem.getStreamType())
|
||||
) {
|
||||
// convert seconds to milliseconds
|
||||
duration = (int) (currentItem.getDuration() * 1000);
|
||||
duration = (int) (currentItem.getDuration() * 1000);
|
||||
} else {
|
||||
duration = (int) simpleExoPlayer.getDuration();
|
||||
}
|
||||
|
|
@ -2253,6 +2245,9 @@ public final class Player implements
|
|||
stopProgressLoop();
|
||||
}
|
||||
|
||||
// When a (short) video ends the elements have to display the correct values - see #6180
|
||||
updatePlayBackElementsCurrentDuration(binding.playbackSeekBar.getMax());
|
||||
|
||||
showControls(500);
|
||||
animate(binding.currentDisplaySeek, false, 200, AnimationType.SCALE_AND_ALPHA);
|
||||
binding.loadingPanel.setVisibility(View.GONE);
|
||||
|
|
@ -2595,6 +2590,18 @@ public final class Player implements
|
|||
//////////////////////////////////////////////////////////////////////////*/
|
||||
//region Playback position and seek
|
||||
|
||||
/**
|
||||
* Sets the current duration into the corresponding elements.
|
||||
* @param currentProgress
|
||||
*/
|
||||
private void updatePlayBackElementsCurrentDuration(final int currentProgress) {
|
||||
// Don't set seekbar progress while user is seeking
|
||||
if (currentState != STATE_PAUSED_SEEK) {
|
||||
binding.playbackSeekBar.setProgress(currentProgress);
|
||||
}
|
||||
binding.playbackCurrentTime.setText(getTimeString(currentProgress));
|
||||
}
|
||||
|
||||
@Override // own playback listener (this is a getter)
|
||||
public boolean isApproachingPlaybackEdge(final long timeToEndMillis) {
|
||||
// If live, then not near playback edge
|
||||
|
|
@ -2946,6 +2953,18 @@ public final class Player implements
|
|||
tag.getMetadata().getPreviewFrames());
|
||||
|
||||
NotificationUtil.getInstance().createNotificationIfNeededAndUpdate(this, false);
|
||||
|
||||
final boolean showThumbnail = prefs.getBoolean(
|
||||
context.getString(R.string.show_thumbnail_key), true);
|
||||
mediaSessionManager.setMetadata(
|
||||
getVideoTitle(),
|
||||
getUploaderName(),
|
||||
showThumbnail ? Optional.ofNullable(getThumbnail()) : Optional.empty(),
|
||||
StreamTypeUtil.isLiveStream(tag.getMetadata().getStreamType())
|
||||
? -1
|
||||
: tag.getMetadata().getDuration()
|
||||
);
|
||||
|
||||
notifyMetadataUpdateToListeners();
|
||||
|
||||
if (areSegmentsVisible) {
|
||||
|
|
@ -3023,9 +3042,11 @@ public final class Player implements
|
|||
|
||||
@Nullable
|
||||
public Bitmap getThumbnail() {
|
||||
return currentThumbnail == null
|
||||
? BitmapFactory.decodeResource(context.getResources(), R.drawable.dummy_thumbnail)
|
||||
: currentThumbnail;
|
||||
if (currentThumbnail == null) {
|
||||
currentThumbnail = BitmapFactory.decodeResource(
|
||||
context.getResources(), R.drawable.dummy_thumbnail);
|
||||
}
|
||||
return currentThumbnail;
|
||||
}
|
||||
//endregion
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ import org.schabi.newpipe.player.mediasession.MediaSessionCallback;
|
|||
import org.schabi.newpipe.player.mediasession.PlayQueueNavigator;
|
||||
import org.schabi.newpipe.player.mediasession.PlayQueuePlaybackController;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class MediaSessionManager {
|
||||
private static final String TAG = MediaSessionManager.class.getSimpleName();
|
||||
public static final boolean DEBUG = MainActivity.DEBUG;
|
||||
|
|
@ -30,6 +32,9 @@ public class MediaSessionManager {
|
|||
@NonNull
|
||||
private final MediaSessionConnector sessionConnector;
|
||||
|
||||
private int lastTitleHashCode;
|
||||
private int lastArtistHashCode;
|
||||
private long lastDuration;
|
||||
private int lastAlbumArtHashCode;
|
||||
|
||||
public MediaSessionManager(@NonNull final Context context,
|
||||
|
|
@ -65,67 +70,143 @@ public class MediaSessionManager {
|
|||
return mediaSession.getSessionToken();
|
||||
}
|
||||
|
||||
public void setMetadata(final String title,
|
||||
final String artist,
|
||||
final Bitmap albumArt,
|
||||
final long duration) {
|
||||
if (albumArt == null || !mediaSession.isActive()) {
|
||||
/**
|
||||
* sets the Metadata - if required.
|
||||
*
|
||||
* @param title {@link MediaMetadataCompat#METADATA_KEY_TITLE}
|
||||
* @param artist {@link MediaMetadataCompat#METADATA_KEY_ARTIST}
|
||||
* @param optAlbumArt {@link MediaMetadataCompat#METADATA_KEY_ALBUM_ART}
|
||||
* @param duration {@link MediaMetadataCompat#METADATA_KEY_DURATION}
|
||||
* - should be a negative value for unknown durations, e.g. for livestreams
|
||||
*/
|
||||
public void setMetadata(@NonNull final String title,
|
||||
@NonNull final String artist,
|
||||
@NonNull final Optional<Bitmap> optAlbumArt,
|
||||
final long duration
|
||||
) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "setMetadata called:"
|
||||
+ " t: " + title
|
||||
+ " a: " + artist
|
||||
+ " thumb: " + (
|
||||
optAlbumArt.isPresent()
|
||||
? optAlbumArt.get().hashCode()
|
||||
: "<none>")
|
||||
+ " d: " + duration);
|
||||
}
|
||||
|
||||
if (!mediaSession.isActive()) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "setMetadata: mediaSession not active - exiting");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!checkIfMetadataShouldBeSet(title, artist, optAlbumArt, duration)) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "setMetadata: No update required - exiting");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
if (getMetadataAlbumArt() == null) {
|
||||
Log.d(TAG, "N_getMetadataAlbumArt: thumb == null");
|
||||
}
|
||||
if (getMetadataTitle() == null) {
|
||||
Log.d(TAG, "N_getMetadataTitle: title == null");
|
||||
}
|
||||
if (getMetadataArtist() == null) {
|
||||
Log.d(TAG, "N_getMetadataArtist: artist == null");
|
||||
}
|
||||
if (getMetadataDuration() <= 1) {
|
||||
Log.d(TAG, "N_getMetadataDuration: duration <= 1; " + getMetadataDuration());
|
||||
}
|
||||
Log.d(TAG, "setMetadata: N_Metadata update:"
|
||||
+ " t: " + title
|
||||
+ " a: " + artist
|
||||
+ " thumb: " + (
|
||||
optAlbumArt.isPresent()
|
||||
? optAlbumArt.get().hashCode()
|
||||
: "<none>")
|
||||
+ " d: " + duration);
|
||||
}
|
||||
|
||||
if (getMetadataAlbumArt() == null || getMetadataTitle() == null
|
||||
|| getMetadataArtist() == null || getMetadataDuration() <= 1
|
||||
|| albumArt.hashCode() != lastAlbumArtHashCode) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "setMetadata: N_Metadata update: t: " + title + " a: " + artist
|
||||
+ " thumb: " + albumArt.hashCode() + " d: " + duration);
|
||||
}
|
||||
final MediaMetadataCompat.Builder builder = new MediaMetadataCompat.Builder()
|
||||
.putString(MediaMetadataCompat.METADATA_KEY_TITLE, title)
|
||||
.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, artist)
|
||||
.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, duration);
|
||||
|
||||
mediaSession.setMetadata(new MediaMetadataCompat.Builder()
|
||||
.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();
|
||||
if (optAlbumArt.isPresent()) {
|
||||
builder.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, optAlbumArt.get());
|
||||
builder.putBitmap(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON, optAlbumArt.get());
|
||||
}
|
||||
|
||||
mediaSession.setMetadata(builder.build());
|
||||
|
||||
lastTitleHashCode = title.hashCode();
|
||||
lastArtistHashCode = artist.hashCode();
|
||||
lastDuration = duration;
|
||||
if (optAlbumArt.isPresent()) {
|
||||
lastAlbumArtHashCode = optAlbumArt.get().hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkIfMetadataShouldBeSet(
|
||||
@NonNull final String title,
|
||||
@NonNull final String artist,
|
||||
@NonNull final Optional<Bitmap> optAlbumArt,
|
||||
final long duration
|
||||
) {
|
||||
// Check if the values have changed since the last time
|
||||
if (title.hashCode() != lastTitleHashCode
|
||||
|| artist.hashCode() != lastArtistHashCode
|
||||
|| duration != lastDuration
|
||||
|| (optAlbumArt.isPresent() && optAlbumArt.get().hashCode() != lastAlbumArtHashCode)
|
||||
) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG,
|
||||
"checkIfMetadataShouldBeSet: true - reason: changed values since last");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if the currently set metadata is valid
|
||||
if (getMetadataTitle() == null
|
||||
|| getMetadataArtist() == null
|
||||
// Note that the duration can be <= 0 for live streams
|
||||
) {
|
||||
if (DEBUG) {
|
||||
if (getMetadataTitle() == null) {
|
||||
Log.d(TAG,
|
||||
"N_getMetadataTitle: title == null");
|
||||
} else if (getMetadataArtist() == null) {
|
||||
Log.d(TAG,
|
||||
"N_getMetadataArtist: artist == null");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we got an album art check if the current set AlbumArt is null
|
||||
if (optAlbumArt.isPresent() && getMetadataAlbumArt() == null) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "N_getMetadataAlbumArt: thumb == null");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Default - no update required
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
private Bitmap getMetadataAlbumArt() {
|
||||
return mediaSession.getController().getMetadata()
|
||||
.getBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String getMetadataTitle() {
|
||||
return mediaSession.getController().getMetadata()
|
||||
.getString(MediaMetadataCompat.METADATA_KEY_TITLE);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
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.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import com.google.android.exoplayer2.util.Util;
|
|||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||
import org.schabi.newpipe.extractor.stream.StreamType;
|
||||
import org.schabi.newpipe.player.helper.PlayerDataSource;
|
||||
import org.schabi.newpipe.util.StreamTypeUtil;
|
||||
|
||||
public interface PlaybackResolver extends Resolver<StreamInfo, MediaSource> {
|
||||
|
||||
|
|
@ -21,7 +22,7 @@ public interface PlaybackResolver extends Resolver<StreamInfo, MediaSource> {
|
|||
default MediaSource maybeBuildLiveMediaSource(@NonNull final PlayerDataSource dataSource,
|
||||
@NonNull final StreamInfo info) {
|
||||
final StreamType streamType = info.getStreamType();
|
||||
if (!(streamType == StreamType.AUDIO_LIVE_STREAM || streamType == StreamType.LIVE_STREAM)) {
|
||||
if (!StreamTypeUtil.isLiveStream(streamType)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -91,6 +91,11 @@ class ContentSettingsManager(private val fileLocator: NewPipeFileLocator) {
|
|||
is String -> {
|
||||
preferenceEditor.putString(key, value)
|
||||
}
|
||||
is Set<*> -> {
|
||||
// There are currently only Sets with type String possible
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
preferenceEditor.putStringSet(key, value as Set<String>?)
|
||||
}
|
||||
}
|
||||
}
|
||||
preferenceEditor.commit()
|
||||
|
|
|
|||
|
|
@ -6,11 +6,21 @@ import androidx.preference.Preference;
|
|||
|
||||
import org.schabi.newpipe.R;
|
||||
|
||||
import static org.schabi.newpipe.CheckForNewAppVersion.startNewVersionCheckService;
|
||||
|
||||
public class UpdateSettingsFragment extends BasePreferenceFragment {
|
||||
private final Preference.OnPreferenceChangeListener updatePreferenceChange
|
||||
= (preference, newValue) -> {
|
||||
= (preference, checkForUpdates) -> {
|
||||
defaultPreferences.edit()
|
||||
.putBoolean(getString(R.string.update_app_key), (boolean) newValue).apply();
|
||||
.putBoolean(getString(R.string.update_app_key), (boolean) checkForUpdates).apply();
|
||||
|
||||
if ((boolean) checkForUpdates) {
|
||||
// Search for updates immediately when update checks are enabled.
|
||||
// Reset the expire time. This is necessary to check for an update immediately.
|
||||
defaultPreferences.edit()
|
||||
.putLong(getString(R.string.update_expiry_key), 0).apply();
|
||||
startNewVersionCheckService();
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,9 @@ public final class DeviceUtils {
|
|||
// Zephir TS43UHD-2
|
||||
private static final boolean CVT_MT5886_EU_1G = Build.VERSION.SDK_INT == 24
|
||||
&& Build.DEVICE.equals("cvt_mt5886_eu_1g");
|
||||
// Hilife TV
|
||||
private static final boolean REALTEKATV = Build.VERSION.SDK_INT == 25
|
||||
&& Build.DEVICE.equals("RealtekATV");
|
||||
|
||||
private DeviceUtils() {
|
||||
}
|
||||
|
|
@ -129,7 +132,8 @@ public final class DeviceUtils {
|
|||
public static boolean shouldSupportMediaTunneling() {
|
||||
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
|
||||
&& !HI3798MV200
|
||||
&& !CVT_MT5886_EU_1G;
|
||||
&& !CVT_MT5886_EU_1G
|
||||
&& !REALTEKATV;
|
||||
}
|
||||
|
||||
public static boolean isLandscape(final Context context) {
|
||||
|
|
|
|||
|
|
@ -46,7 +46,6 @@ import org.schabi.newpipe.local.subscription.SubscriptionFragment;
|
|||
import org.schabi.newpipe.local.subscription.SubscriptionsImportFragment;
|
||||
import org.schabi.newpipe.player.MainPlayer;
|
||||
import org.schabi.newpipe.player.MainPlayer.PlayerType;
|
||||
import org.schabi.newpipe.player.NotificationUtil;
|
||||
import org.schabi.newpipe.player.PlayQueueActivity;
|
||||
import org.schabi.newpipe.player.Player;
|
||||
import org.schabi.newpipe.player.helper.PlayerHelper;
|
||||
|
|
@ -65,7 +64,8 @@ import com.jakewharton.processphoenix.ProcessPhoenix;
|
|||
public final class NavigationHelper {
|
||||
public static final String MAIN_FRAGMENT_TAG = "main_fragment_tag";
|
||||
public static final String SEARCH_FRAGMENT_TAG = "search_fragment_tag";
|
||||
private static final String TAG = NotificationUtil.class.getSimpleName();
|
||||
|
||||
private static final String TAG = NavigationHelper.class.getSimpleName();
|
||||
|
||||
private NavigationHelper() {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
package org.schabi.newpipe.util;
|
||||
|
||||
import org.schabi.newpipe.extractor.stream.StreamType;
|
||||
|
||||
/**
|
||||
* Utility class for {@link org.schabi.newpipe.extractor.stream.StreamType}.
|
||||
*/
|
||||
public final class StreamTypeUtil {
|
||||
private StreamTypeUtil() {
|
||||
// No impl pls
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the streamType is a livestream.
|
||||
*
|
||||
* @param streamType
|
||||
* @return <code>true</code> when the streamType is a
|
||||
* {@link StreamType#LIVE_STREAM} or {@link StreamType#AUDIO_LIVE_STREAM}
|
||||
*/
|
||||
public static boolean isLiveStream(final StreamType streamType) {
|
||||
return streamType == StreamType.LIVE_STREAM
|
||||
|| streamType == StreamType.AUDIO_LIVE_STREAM;
|
||||
}
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ import android.content.pm.PackageManager;
|
|||
import android.content.pm.ResolveInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.text.TextUtils;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
|
@ -246,7 +247,7 @@ public final class ShareUtils {
|
|||
final Intent shareIntent = new Intent(Intent.ACTION_SEND);
|
||||
shareIntent.setType("text/plain");
|
||||
shareIntent.putExtra(Intent.EXTRA_TEXT, content);
|
||||
if (!title.isEmpty()) {
|
||||
if (!TextUtils.isEmpty(title)) {
|
||||
shareIntent.putExtra(Intent.EXTRA_TITLE, title);
|
||||
shareIntent.putExtra(Intent.EXTRA_SUBJECT, title);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue