implements
return System.currentTimeMillis() >= expireTimestamp;
}
- /**
- * Delegates the preparation of child {@link MediaSource}s to the
- * {@link CompositeMediaSource} wrapper. Since all {@link LoadedMediaSource}s use only
- * a single child media, the child id of 0 is always used (sonar doesn't like null as id here).
- *
- * @param mediaTransferListener A data transfer listener that will be registered by the
- * {@link CompositeMediaSource} for child source preparation.
- */
- @Override
- protected void prepareSourceInternal(@Nullable final TransferListener mediaTransferListener) {
- super.prepareSourceInternal(mediaTransferListener);
- prepareChildSource(0, source);
- }
-
- /**
- * When any child {@link MediaSource} is prepared, the refreshed {@link Timeline} can
- * be listened to here. But since {@link LoadedMediaSource} has only a single child source,
- * this method is called only once until {@link #releaseSourceInternal()} is called.
- *
- * On refresh, the {@link CompositeMediaSource} delegate will be notified with the
- * new {@link Timeline}, otherwise {@link #createPeriod(MediaPeriodId, Allocator, long)}
- * will not be called and playback may be stalled.
- *
- * @param id The unique id used to prepare the child source.
- * @param mediaSource The child source whose source info has been refreshed.
- * @param timeline The new timeline of the child source.
- */
- @Override
- protected void onChildSourceInfoRefreshed(final Integer id,
- final MediaSource mediaSource,
- final Timeline timeline) {
- refreshSourceInfo(timeline);
- }
-
- @Override
- public MediaPeriod createPeriod(final MediaPeriodId id, final Allocator allocator,
- final long startPositionUs) {
- return source.createPeriod(id, allocator, startPositionUs);
- }
-
- @Override
- public void releasePeriod(final MediaPeriod mediaPeriod) {
- source.releasePeriod(mediaPeriod);
- }
-
@NonNull
@Override
public MediaItem getMediaItem() {
diff --git a/app/src/main/java/org/schabi/newpipe/player/ui/MainPlayerUi.java b/app/src/main/java/org/schabi/newpipe/player/ui/MainPlayerUi.java
index a5c745ae6..683629c25 100644
--- a/app/src/main/java/org/schabi/newpipe/player/ui/MainPlayerUi.java
+++ b/app/src/main/java/org/schabi/newpipe/player/ui/MainPlayerUi.java
@@ -32,6 +32,7 @@ import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
+import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
@@ -39,8 +40,6 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.content.res.AppCompatResources;
-import androidx.core.view.WindowCompat;
-import androidx.core.view.WindowInsetsCompat;
import androidx.fragment.app.FragmentActivity;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;
@@ -453,9 +452,11 @@ public final class MainPlayerUi extends VideoPlayerUi implements View.OnLayoutCh
getParentActivity().map(Activity::getWindow).ifPresent(window -> {
window.setStatusBarColor(Color.TRANSPARENT);
window.setNavigationBarColor(Color.TRANSPARENT);
- WindowCompat.setDecorFitsSystemWindows(window, false);
- WindowCompat.getInsetsController(window, window.getDecorView())
- .show(WindowInsetsCompat.Type.systemBars());
+ final int visibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+ | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
+ window.getDecorView().setSystemUiVisibility(visibility);
+ window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
});
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/player/ui/VideoPlayerUi.java b/app/src/main/java/org/schabi/newpipe/player/ui/VideoPlayerUi.java
index e4f5b05e1..9afd1bf24 100644
--- a/app/src/main/java/org/schabi/newpipe/player/ui/VideoPlayerUi.java
+++ b/app/src/main/java/org/schabi/newpipe/player/ui/VideoPlayerUi.java
@@ -1420,14 +1420,7 @@ public abstract class VideoPlayerUi extends PlayerUi implements SeekBar.OnSeekBa
private void onPlayWithKodiClicked() {
if (player.getCurrentMetadata() != null) {
player.pause();
- try {
- NavigationHelper.playWithKore(context, Uri.parse(player.getVideoUrl()));
- } catch (final Exception e) {
- if (DEBUG) {
- Log.i(TAG, "Failed to start kore", e);
- }
- KoreUtils.showInstallKoreDialog(player.getContext());
- }
+ KoreUtils.playWithKore(context, Uri.parse(player.getVideoUrl()));
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/util/DependentPreferenceHelper.java b/app/src/main/java/org/schabi/newpipe/util/DependentPreferenceHelper.java
new file mode 100644
index 000000000..9591beddb
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/util/DependentPreferenceHelper.java
@@ -0,0 +1,51 @@
+package org.schabi.newpipe.util;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+import androidx.preference.PreferenceManager;
+
+import org.schabi.newpipe.R;
+
+/**
+ * For preferences with dependencies and multiple use case,
+ * this class can be used to reduce the lines of code.
+ */
+public final class DependentPreferenceHelper {
+
+ private DependentPreferenceHelper() {
+ // no instance
+ }
+
+ /**
+ * Option `Resume playback` depends on `Watch history`, this method can be used to retrieve if
+ * `Resume playback` and its dependencies are all enabled.
+ *
+ * @param context the Android context
+ * @return returns true if `Resume playback` and `Watch history` are both enabled
+ */
+ public static boolean getResumePlaybackEnabled(final Context context) {
+ final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+
+ return prefs.getBoolean(context.getString(
+ R.string.enable_watch_history_key), true)
+ && prefs.getBoolean(context.getString(
+ R.string.enable_playback_resume_key), true);
+ }
+
+ /**
+ * Option `Position in lists` depends on `Watch history`, this method can be used to retrieve if
+ * `Position in lists` and its dependencies are all enabled.
+ *
+ * @param context the Android context
+ * @return returns true if `Positions in lists` and `Watch history` are both enabled
+ */
+ public static boolean getPositionsInListsEnabled(final Context context) {
+ final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+
+ return prefs.getBoolean(context.getString(
+ R.string.enable_watch_history_key), true)
+ && prefs.getBoolean(context.getString(
+ R.string.enable_playback_state_lists_key), true);
+ }
+}
diff --git a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java
index b4556507c..2c8db77e0 100644
--- a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java
+++ b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java
@@ -1,6 +1,6 @@
package org.schabi.newpipe.util;
-import static org.schabi.newpipe.util.external_communication.ShareUtils.installApp;
+import static org.schabi.newpipe.util.ListHelper.getUrlAndNonTorrentStreams;
import android.annotation.SuppressLint;
import android.app.Activity;
@@ -50,9 +50,9 @@ import org.schabi.newpipe.local.history.StatisticsPlaylistFragment;
import org.schabi.newpipe.local.playlist.LocalPlaylistFragment;
import org.schabi.newpipe.local.subscription.SubscriptionFragment;
import org.schabi.newpipe.local.subscription.SubscriptionsImportFragment;
-import org.schabi.newpipe.player.PlayerService;
import org.schabi.newpipe.player.PlayQueueActivity;
import org.schabi.newpipe.player.Player;
+import org.schabi.newpipe.player.PlayerService;
import org.schabi.newpipe.player.PlayerType;
import org.schabi.newpipe.player.helper.PlayerHelper;
import org.schabi.newpipe.player.helper.PlayerHolder;
@@ -63,8 +63,6 @@ import org.schabi.newpipe.util.external_communication.ShareUtils;
import java.util.List;
-import static org.schabi.newpipe.util.ListHelper.getUrlAndNonTorrentStreams;
-
public final class NavigationHelper {
public static final String MAIN_FRAGMENT_TAG = "main_fragment_tag";
public static final String SEARCH_FRAGMENT_TAG = "search_fragment_tag";
@@ -323,15 +321,13 @@ public final class NavigationHelper {
public static void resolveActivityOrAskToInstall(@NonNull final Context context,
@NonNull final Intent intent) {
- if (intent.resolveActivity(context.getPackageManager()) != null) {
- ShareUtils.openIntentInApp(context, intent, false);
- } else {
+ if (!ShareUtils.tryOpenIntentInApp(context, intent)) {
if (context instanceof Activity) {
new AlertDialog.Builder(context)
.setMessage(R.string.no_player_found)
.setPositiveButton(R.string.install,
- (dialog, which) -> ShareUtils.openUrlInBrowser(context,
- context.getString(R.string.fdroid_vlc_url), false))
+ (dialog, which) -> ShareUtils.installApp(context,
+ context.getString(R.string.vlc_package)))
.setNegativeButton(R.string.cancel, (dialog, which)
-> Log.i("NavigationHelper", "You unlocked a secret unicorn."))
.show();
@@ -684,34 +680,6 @@ public final class NavigationHelper {
return getOpenIntent(context, url, serviceId, StreamingService.LinkType.CHANNEL);
}
- /**
- * Start an activity to install Kore.
- *
- * @param context the context
- */
- public static void installKore(final Context context) {
- installApp(context, context.getString(R.string.kore_package));
- }
-
- /**
- * Start Kore app to show a video on Kodi.
- *
- * For a list of supported urls see the
- *
- * Kore source code
- * .
- *
- * @param context the context to use
- * @param videoURL the url to the video
- */
- public static void playWithKore(final Context context, final Uri videoURL) {
- final Intent intent = new Intent(Intent.ACTION_VIEW);
- intent.setPackage(context.getString(R.string.kore_package));
- intent.setData(videoURL);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- context.startActivity(intent);
- }
-
/**
* Finish this Activity
as well as all Activities
running below it
* and then start MainActivity
.
diff --git a/app/src/main/java/org/schabi/newpipe/util/external_communication/KoreUtils.java b/app/src/main/java/org/schabi/newpipe/util/external_communication/KoreUtils.java
index 0df579d88..4dc9c7c07 100644
--- a/app/src/main/java/org/schabi/newpipe/util/external_communication/KoreUtils.java
+++ b/app/src/main/java/org/schabi/newpipe/util/external_communication/KoreUtils.java
@@ -1,6 +1,11 @@
package org.schabi.newpipe.util.external_communication;
+import static org.schabi.newpipe.util.external_communication.ShareUtils.installApp;
+import static org.schabi.newpipe.util.external_communication.ShareUtils.tryOpenIntentInApp;
+
import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
@@ -8,7 +13,6 @@ import androidx.preference.PreferenceManager;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.ServiceList;
-import org.schabi.newpipe.util.NavigationHelper;
/**
* Util class that provides methods which are related to the Kodi Media Center and its Kore app.
@@ -29,13 +33,39 @@ public final class KoreUtils {
.getBoolean(context.getString(R.string.show_play_with_kodi_key), false);
}
- public static void showInstallKoreDialog(@NonNull final Context context) {
- final AlertDialog.Builder builder = new AlertDialog.Builder(context);
- builder.setMessage(R.string.kore_not_found)
- .setPositiveButton(R.string.install, (dialog, which) ->
- NavigationHelper.installKore(context))
- .setNegativeButton(R.string.cancel, (dialog, which) -> {
- });
- builder.create().show();
+ /**
+ * Start an activity to install Kore.
+ *
+ * @param context the context to use
+ */
+ public static void installKore(final Context context) {
+ installApp(context, context.getString(R.string.kore_package));
+ }
+
+ /**
+ * Start Kore app to show a video on Kodi, and if the app is not installed ask the user to
+ * install it.
+ *
+ * For a list of supported urls see the
+ *
+ * Kore source code
+ * .
+ *
+ * @param context the context to use
+ * @param streamUrl the url to the stream to play
+ */
+ public static void playWithKore(final Context context, final Uri streamUrl) {
+ final Intent intent = new Intent(Intent.ACTION_VIEW)
+ .setPackage(context.getString(R.string.kore_package))
+ .setData(streamUrl)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ if (!tryOpenIntentInApp(context, intent)) {
+ final AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setMessage(R.string.kore_not_found)
+ .setPositiveButton(R.string.install, (dialog, which) -> installKore(context))
+ .setNegativeButton(R.string.cancel, (dialog, which) -> { });
+ builder.create().show();
+ }
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/util/external_communication/ShareUtils.java b/app/src/main/java/org/schabi/newpipe/util/external_communication/ShareUtils.java
index 06dd3f945..118b77026 100644
--- a/app/src/main/java/org/schabi/newpipe/util/external_communication/ShareUtils.java
+++ b/app/src/main/java/org/schabi/newpipe/util/external_communication/ShareUtils.java
@@ -41,60 +41,71 @@ public final class ShareUtils {
* second param (a system chooser will be opened if there are multiple markets and no default)
* and falls back to Google Play Store web URL if no app to handle the market scheme was found.
*
- * It uses {@link #openIntentInApp(Context, Intent, boolean)} to open market scheme
- * and {@link #openUrlInBrowser(Context, String, boolean)} to open Google Play Store
- * web URL with false for the boolean param.
+ * It uses {@link #openIntentInApp(Context, Intent)} to open market scheme and {@link
+ * #openUrlInBrowser(Context, String)} to open Google Play Store web URL.
*
* @param context the context to use
* @param packageId the package id of the app to be installed
*/
public static void installApp(@NonNull final Context context, final String packageId) {
// Try market scheme
- final boolean marketSchemeResult = openIntentInApp(context, new Intent(Intent.ACTION_VIEW,
+ final Intent marketSchemeIntent = new Intent(Intent.ACTION_VIEW,
Uri.parse("market://details?id=" + packageId))
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK), false);
- if (!marketSchemeResult) {
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ if (!tryOpenIntentInApp(context, marketSchemeIntent)) {
// Fall back to Google Play Store Web URL (F-Droid can handle it)
- openUrlInBrowser(context,
- "https://play.google.com/store/apps/details?id=" + packageId, false);
+ openUrlInApp(context, "https://play.google.com/store/apps/details?id=" + packageId);
}
}
/**
- * Open the url with the system default browser.
+ * Open the url with the system default browser. If no browser is set as default, falls back to
+ * {@link #openAppChooser(Context, Intent, boolean)}.
*
- * If no browser is set as default, fallbacks to
- * {@link #openAppChooser(Context, Intent, boolean)}
+ * This function selects the package to open based on which apps respond to the {@code http://}
+ * schema alone, which should exclude special non-browser apps that are can handle the url (e.g.
+ * the official YouTube app).
+ *
+ * Therefore please prefer {@link #openUrlInApp(Context, String)}, that handles package
+ * resolution in a standard way, unless this is the action of an explicit "Open in browser"
+ * button.
*
- * @param context the context to use
- * @param url the url to browse
- * @param httpDefaultBrowserTest the boolean to set if the test for the default browser will be
- * for HTTP protocol or for the created intent
- * @return true if the URL can be opened or false if it cannot
- */
- public static boolean openUrlInBrowser(@NonNull final Context context,
- final String url,
- final boolean httpDefaultBrowserTest) {
- final String defaultPackageName;
+ * @param context the context to use
+ * @param url the url to browse
+ **/
+ public static void openUrlInBrowser(@NonNull final Context context, final String url) {
+ // Resolve using a generic http://, so we are sure to get a browser and not e.g. the yt app.
+ // Note that this requires the `http` schema to be added to `` in the manifest.
+ final ResolveInfo defaultBrowserInfo;
+ final Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://"));
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ defaultBrowserInfo = context.getPackageManager().resolveActivity(browserIntent,
+ PackageManager.ResolveInfoFlags.of(PackageManager.MATCH_DEFAULT_ONLY));
+ } else {
+ defaultBrowserInfo = context.getPackageManager().resolveActivity(browserIntent,
+ PackageManager.MATCH_DEFAULT_ONLY);
+ }
+
final Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url))
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- if (httpDefaultBrowserTest) {
- defaultPackageName = getDefaultAppPackageName(context, new Intent(Intent.ACTION_VIEW,
- Uri.parse("http://")).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
- } else {
- defaultPackageName = getDefaultAppPackageName(context, intent);
+ if (defaultBrowserInfo == null) {
+ // No app installed to open a web URL, but it may be handled by other apps so try
+ // opening a system chooser for the link in this case (it could be bypassed by the
+ // system if there is only one app which can open the link or a default app associated
+ // with the link domain on Android 12 and higher)
+ openAppChooser(context, intent, true);
+ return;
}
- if (defaultPackageName.equals("android")) {
+ final String defaultBrowserPackage = defaultBrowserInfo.activityInfo.packageName;
+
+ if (defaultBrowserPackage.equals("android")) {
// No browser set as default (doesn't work on some devices)
openAppChooser(context, intent, true);
} else {
try {
- // will be empty on Android 12+
- if (!defaultPackageName.isEmpty()) {
- intent.setPackage(defaultPackageName);
- }
+ intent.setPackage(defaultBrowserPackage);
context.startActivity(intent);
} catch (final ActivityNotFoundException e) {
// Not a browser but an app chooser because of OEMs changes
@@ -102,61 +113,56 @@ public final class ShareUtils {
openAppChooser(context, intent, true);
}
}
-
- return true;
}
/**
- * Open the url with the system default browser.
- *
- * If no browser is set as default, fallbacks to
- * {@link #openAppChooser(Context, Intent, boolean)}
- *
- * This calls {@link #openUrlInBrowser(Context, String, boolean)} with true
- * for the boolean parameter
+ * Open a url with the system default app using {@link Intent#ACTION_VIEW}, showing a toast in
+ * case of failure.
*
* @param context the context to use
- * @param url the url to browse
- * @return true if the URL can be opened or false if it cannot be
- **/
- public static boolean openUrlInBrowser(@NonNull final Context context, final String url) {
- return openUrlInBrowser(context, url, true);
+ * @param url the url to open
+ */
+ public static void openUrlInApp(@NonNull final Context context, final String url) {
+ openIntentInApp(context, new Intent(Intent.ACTION_VIEW, Uri.parse(url))
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
}
/**
* Open an intent with the system default app.
*
- * The intent can be of every type, excepted a web intent for which
- * {@link #openUrlInBrowser(Context, String, boolean)} should be used.
- *
- * If no app can open the intent, a toast with the message {@code No app on your device can
- * open this} is shown.
+ * Use {@link #openIntentInApp(Context, Intent)} to show a toast in case of failure.
*
- * @param context the context to use
- * @param intent the intent to open
- * @param showToast a boolean to set if a toast is displayed to user when no app is installed
- * to open the intent (true) or not (false)
- * @return true if the intent can be opened or false if it cannot be
+ * @param context the context to use
+ * @param intent the intent to open
+ * @return true if the intent could be opened successfully, false otherwise
*/
- public static boolean openIntentInApp(@NonNull final Context context,
- @NonNull final Intent intent,
- final boolean showToast) {
- final String defaultPackageName = getDefaultAppPackageName(context, intent);
-
- if (defaultPackageName.isEmpty()) {
- // No app installed to open the intent
- if (showToast) {
- Toast.makeText(context, R.string.no_app_to_open_intent, Toast.LENGTH_LONG)
- .show();
- }
- return false;
- } else {
+ public static boolean tryOpenIntentInApp(@NonNull final Context context,
+ @NonNull final Intent intent) {
+ try {
context.startActivity(intent);
+ } catch (final ActivityNotFoundException e) {
+ return false;
}
-
return true;
}
+ /**
+ * Open an intent with the system default app, showing a toast in case of failure.
+ *
+ * Use {@link #tryOpenIntentInApp(Context, Intent)} if you don't want the toast. Use {@link
+ * #openUrlInApp(Context, String)} as a shorthand for {@link Intent#ACTION_VIEW} with urls.
+ *
+ * @param context the context to use
+ * @param intent the intent to
+ */
+ public static void openIntentInApp(@NonNull final Context context,
+ @NonNull final Intent intent) {
+ if (!tryOpenIntentInApp(context, intent)) {
+ Toast.makeText(context, R.string.no_app_to_open_intent, Toast.LENGTH_LONG)
+ .show();
+ }
+ }
+
/**
* Open the system chooser to launch an intent.
*
@@ -203,31 +209,11 @@ public final class ShareUtils {
chooserIntent.addFlags(permFlags);
}
}
- context.startActivity(chooserIntent);
- }
- /**
- * Get the default app package name.
- *
- * If no app is set as default, it will return "android" (not on some devices because some
- * OEMs changed the app chooser).
- *
- * If no app is installed on user's device to handle the intent, it will return an empty string.
- *
- * @param context the context to use
- * @param intent the intent to get default app
- * @return the package name of the default app, an empty string if there's no app installed to
- * handle the intent or the app chooser if there's no default
- */
- private static String getDefaultAppPackageName(@NonNull final Context context,
- @NonNull final Intent intent) {
- final ResolveInfo resolveInfo = context.getPackageManager().resolveActivity(intent,
- PackageManager.MATCH_DEFAULT_ONLY);
-
- if (resolveInfo == null) {
- return "";
- } else {
- return resolveInfo.activityInfo.packageName;
+ try {
+ context.startActivity(chooserIntent);
+ } catch (final ActivityNotFoundException e) {
+ Toast.makeText(context, R.string.no_app_to_open_intent, Toast.LENGTH_LONG).show();
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/util/text/UrlLongPressClickableSpan.java b/app/src/main/java/org/schabi/newpipe/util/text/UrlLongPressClickableSpan.java
index eb0d7425e..61c1a546d 100644
--- a/app/src/main/java/org/schabi/newpipe/util/text/UrlLongPressClickableSpan.java
+++ b/app/src/main/java/org/schabi/newpipe/util/text/UrlLongPressClickableSpan.java
@@ -30,7 +30,7 @@ final class UrlLongPressClickableSpan extends LongPressClickableSpan {
public void onClick(@NonNull final View view) {
if (!InternalUrlsHandler.handleUrlDescriptionTimestamp(
disposables, context, url)) {
- ShareUtils.openUrlInBrowser(context, url, false);
+ ShareUtils.openUrlInApp(context, url);
}
}
diff --git a/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java b/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java
index bfb6a15e2..695e7aead 100644
--- a/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java
+++ b/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java
@@ -1,6 +1,5 @@
package us.shandian.giga.ui.adapter;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_GRANT_PREFIX_URI_PERMISSION;
import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
import static us.shandian.giga.get.DownloadMission.ERROR_CONNECT_HOST;
@@ -345,16 +344,7 @@ public class MissionAdapter extends Adapter implements Handler.Callb
intent.setDataAndType(resolveShareableUri(mission), mimeType);
intent.addFlags(FLAG_GRANT_READ_URI_PERMISSION);
intent.addFlags(FLAG_GRANT_PREFIX_URI_PERMISSION);
-
- if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
- intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
- }
-
- if (intent.resolveActivity(mContext.getPackageManager()) != null) {
- ShareUtils.openIntentInApp(mContext, intent, false);
- } else {
- Toast.makeText(mContext, R.string.toast_no_player, Toast.LENGTH_LONG).show();
- }
+ ShareUtils.openIntentInApp(mContext, intent);
}
private void shareFile(Mission mission) {
diff --git a/app/src/main/res/layout/dialog_playlists.xml b/app/src/main/res/layout/dialog_playlists.xml
index 18b08d93c..ab4691fa9 100644
--- a/app/src/main/res/layout/dialog_playlists.xml
+++ b/app/src/main/res/layout/dialog_playlists.xml
@@ -34,11 +34,26 @@
tools:ignore="RtlHardcoded" />
+
+
diff --git a/app/src/main/res/layout/list_channel_card_item.xml b/app/src/main/res/layout/list_channel_card_item.xml
new file mode 100644
index 000000000..09696ff38
--- /dev/null
+++ b/app/src/main/res/layout/list_channel_card_item.xml
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/list_stream_playlist_card_item.xml b/app/src/main/res/layout/list_stream_playlist_card_item.xml
index 9cc6b326c..581a9d72e 100644
--- a/app/src/main/res/layout/list_stream_playlist_card_item.xml
+++ b/app/src/main/res/layout/list_stream_playlist_card_item.xml
@@ -86,7 +86,7 @@
-
-
+
+
+
diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml
index 02c471d63..95e620ad8 100644
--- a/app/src/main/res/values-ar/strings.xml
+++ b/app/src/main/res/values-ar/strings.xml
@@ -105,7 +105,7 @@
حدث خطأ للمشغل غير قابل للاسترداد
استرداد المشغل من الخطأ
عذرًا، لم ينبغِ أن يحدث ذلك.
- الإبلاغ عن هذا الخطأ عبر البريد الإلكتروني
+ الإبلاغ عن طريق البريد الإلكتروني
عذرًا، حدث خطأ ما.
أبلِغ
معلومات:
@@ -288,7 +288,6 @@
الملف غير موجود أو الإذن بالقراءة أو الكتابة إليه غير موجود
لا يوجد بث متاح للتنزيل
تم حذف عنصر واحد.
- لم يتم تثبيت أي تطبيق لتشغيل هذا الملف
NewPipe هو برنامج مفتوح المصدر وبحقوق متروكة: يمكنك استخدام الكود ودراسته وتحسينه كما شئت. وعلى وجه التحديد يمكنك إعادة توزيعه / أو تعديله تحت شروط رخصة GNU العمومية والتي نشرتها مؤسسة البرمجيات الحرة، سواء الإصدار 3 من الرخصة، أو (باختيارك) أي إصدار أحدث.
آخر ما تم تشغيله
الأكثر تشغيلا
@@ -784,4 +783,14 @@
البطاقة
تمت إضافة وقت (أوقات) مكررة %d
تحتوي قوائم التشغيل رمادية اللون بالفعل على هذا العنصر.
+ مفيد ، على سبيل المثال ، إذا كنت تستخدم سماعة رأس بأزرار مادية مكسورة
+ تجاهل أحداث ازرار الوسائط الأجهزة
+ هل تريد إزالة جميع التدفقات المكررة في قائمة التشغيل هذه؟
+ إظهار/إخفاء التدفقات
+ تمت مشاهدته جزئيا
+ القادمة
+ إزالة التكرارات
+ إزالة التكرارات؟
+ إظهار التدفقات التالية
+ شاهدت بالكامل
\ No newline at end of file
diff --git a/app/src/main/res/values-as/strings.xml b/app/src/main/res/values-as/strings.xml
index be845845a..93e9e363e 100644
--- a/app/src/main/res/values-as/strings.xml
+++ b/app/src/main/res/values-as/strings.xml
@@ -39,7 +39,7 @@
উচ্চ ৰিজ\'লিউচন দেখুৱাওক
কেৱল কিছুমান ডিভাইচেহে 2K/4K ভিডিঅ’ বজাব পাৰে
Kodi ৰ সৈতে বজাওক
- Kore এপ ইনষ্টল\?
+ Kode এপ ইনষ্টল\?
\"Kodi ৰ সৈতে খোলক\" বিকল্প দেখুৱাওক
Kodi মিডিয়া চেণ্টাৰৰ জৰিয়তে এটা ভিডিঅ\' চলাবলৈ এটা বিকল্প প্ৰদৰ্শন কৰক
প্লেয়াৰটো ক্ৰেচ কৰক
diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml
index 0cce71fb0..8779fb867 100644
--- a/app/src/main/res/values-az/strings.xml
+++ b/app/src/main/res/values-az/strings.xml
@@ -303,7 +303,7 @@
Saxlanmış tabları oxumaq mümkün olmadı, buna görə standart tabları istifadə et
NewPipe xəta ilə qarşılaşdı, bildirmək üçün toxun
Bağışla, o baş verməməli idi.
- Bu xətanı e-poçt-dan bildir
+ E-poçt- dan məlumat ver
GitHub\'da Məlumat Ver
Zəhmət olmasa, xətanızı müzakirə edən məsələnin mövcud olub-olmadığını yoxlayın. Dublikat biletləri yaradarkən, bizdən faktiki səhvi düzəltməyə sərf edəcəyimiz vaxt alırsınız.
Məlumat Ver
@@ -588,7 +588,6 @@
Sil
Android\'də pulsuz yüngül yayımlayıcı.
© %1$s, %2$s tərəfindən %3$s altında
- Bu faylı oynatmaq üçün heç bir tətbiq quraşdırılmayıb
Endirmə
Bu icazə, ani görüntü rejimində
\naçmaq üçün lazımdır
@@ -732,4 +731,14 @@
Buferə kopyalamaq alınmadı
Boz rəngdə olan pleylistlərdə artıq bu element var.
Dublikat %d dəfə əlavə edildi
+ Aparat mühiti media düyməsi hadisələrinə məhəl qoyma
+ Məsələn, fiziki düymələri qırılan qulaqlıq işlədirsinizsə faydalıdır
+ Dublikatları sil
+ Dublikatlar silinsin\?
+ Aşağıdakı yayımları göstər
+ Yayımları Göstər/Gizlə
+ Tam baxılıb
+ Qismən baxılıb
+ Bu pleylistdəki bütün dublikat yayımları silmək istəyirsiniz\?
+ Yaxınlaşan
\ No newline at end of file
diff --git a/app/src/main/res/values-b+ast/strings.xml b/app/src/main/res/values-b+ast/strings.xml
index 12739ba41..15beb49bb 100644
--- a/app/src/main/res/values-b+ast/strings.xml
+++ b/app/src/main/res/values-b+ast/strings.xml
@@ -277,7 +277,7 @@
Desanicia tolos datos na caché de les páxines web
Llimpiar los metadatos de la caché
Llimpióse la caché d\'imáxenes
- ¿Instalar Kore\?
+ ¿Instalar Kode\?
Qué asocedió:\\nSolicitú:\\nLlingua del conteníu:\\nPaís del conteníu:\\nLlingua de l\'aplicación:\\nServiciu:\\nHora en GMT:\\nPaquete:\\nVersión de l\'aplicación:\\nVersión del SO:
Nun s\'atopó nengún reproductor de fluxos (pues instalar VLC pa reproducilos).
Amuesa una miniatura nel fondu de la pantalla de bloquéu y dientro de los avisos
@@ -425,7 +425,6 @@
El proyeutu de NewPipe toma mui en serio la privacidá. Poro, l\'aplicación nun recueye nengún datu ensin el to consentimientu.
\nLa política de privacidá de NewPipe desplica en detalle los datos que s\'unvien y atroxen cuando unvies un informe de casque.
Un aplicación llibre pa ver/sentir plataformes de tresmisión n\'Android.
- Nun hai nenguna aplicación pa reproducir esti ficheru
Caráuteres de troquéu
Los caráuteres que nun son válidos van trocase por esti valor
Fecho
diff --git a/app/src/main/res/values-b+uz+Latn/strings.xml b/app/src/main/res/values-b+uz+Latn/strings.xml
index 323dda73d..a4101a9be 100644
--- a/app/src/main/res/values-b+uz+Latn/strings.xml
+++ b/app/src/main/res/values-b+uz+Latn/strings.xml
@@ -214,7 +214,6 @@
© %1$s tomonidan %2$s gacha %3$s
Uchinchi tomon litsenziyalari
NewPipe haqida
- Ushbu faylni ijro etish uchun dastur o\'rnatilmagan
Ko\'pchilik maxsus belgilar
Yozuvlar va raqamlar
O\'zgartirish belgisi
diff --git a/app/src/main/res/values-be/strings.xml b/app/src/main/res/values-be/strings.xml
index 8ed10b126..7bbe49ebd 100644
--- a/app/src/main/res/values-be/strings.xml
+++ b/app/src/main/res/values-be/strings.xml
@@ -1,8 +1,8 @@
- Націсніце \"Пошук\", каб пачаць
+ Націсніце «Пошук», каб пачаць.
Апублікавана %1$s
- Патокавы плэер не знойдзены. Усталяваць VLC?
+ Патокавы прайгравальнік не знойдзены. Усталяваць VLC\?
Патокавы плэер не знойдзены (вы можаце ўсталяваць VLC).
Усталяваць
Скасаваць
@@ -16,7 +16,7 @@
Магчыма, вы мелі на ўвазе \"%1$s\"\?
Падзяліцца з дапамогай
Знешні відэаплэер
- Прыбірае гук у некаторых разрозненнях
+ Адключае гук для некаторых раздзяленнях
Знешні аўдыяплэер
Падпісацца
Вы падпісаныя
@@ -30,19 +30,19 @@
У фоне
У акне
Дадаць да
- Каталог для спампаванага відэа
- Папка для спампаванга відэа
- Увядзіце шлях да папкі для спампавання відэа
+ Тэчка загрузкі відэа
+ Загружаныя відэафайлы захоўваюцца тут
+ Абярыце тэчку загрузкі для відэафайлаў
Тэчка загрузкі аўдыё
- Папка для спампаванага аўдыя
- Увядзіце шлях да папкі для спампавання аўдыя
+ Загружаныя аўдыёфайлы захоўваюцца тут
+ Абярыце тэчку загрузкі для аўдыёфайлаў
Разрознянне па змаўчанні
Разрозненне усплываючага акна
Высокія разрозненні
Толькі некаторыя прылады могуць прайграваць відэа ў 2K/4K
Прайграць у Kodi
- Дадатак Kore не знойдзены. Усталяваць яго?
- \"Прайграць у Kodi\"
+ Усталяваць адсутную праграму Kore\?
+ Паказаць опцыю \"Прайграць у Kodi\"
Паказаць опцыю прайгравання відэа праз медыяцэнтр Kodi
Аўдыё
Фармат аўдыё па змаўчанні
@@ -54,27 +54,27 @@
Аднавіць акно
Запамінаць памер і становішча ўсплываючага акна
Хуткі пошук пазіцыі
- Недакладны пошук дазваляе плэеру шукаць пазіцыю хутчэй, але менш дакладна. Не працуе для перамоткі на 5, 15 ці 25 секунд
+ Недакладны пошук дазваляе плэеру знаходзіць пазіцыі хутчэй са зніжанай дакладнасцю. Пошук цягам 5, 15 ці 25 секунд пры гэтым немажлівы
Загружаць мініяцюры
Адключыце, каб не загружаць мініяцюры і зэканоміць трафік і памяць. Змена налады ачысьціць кэш малюнкаў
Кэш малюнкаў ачышчаны
Ачысціць кэш метададзеных
Выдаліць усе загружаныя дадзеныя вэб-старонак
Кэш метададзеных ачышчаны
- Аўтадапаўненне чаргі
+ Аўта- чарга наступнага патока
Дадаваць падобныя патокі ў чаргу пры прайграванні апошняга, калі не ўключаны паўтор
Варыянты пошуку
- Адлюстроўваць падказкі пры пошуку
+ Выберыце прапановы для паказу пры пошуку
Гісторыя пошуку
Захоўваць пошукавыя запыты лакальна
Гісторыя праглядаў
Запамінаць прагледжаныя відэа
- Аднавіць пры фокусе
- Аднаўляць прайграванне пасля перапынкаў (напрыклад, тэлефонных званкоў)
- Спампаваць
+ Узнавіць прайграванне
+ Працягваць прайграванне пасля перапынкаў (напрыклад, тэлефонных званкоў)
+ Загрузіць
\"Наступнае\" и \"Прапанаванае\" відэа
- \"Зацісніце, каб дадаць\"
- Паказаць падказку пры націсканні \"У акне\" ці \"У фоне\" на старонцы звестак аб відэа
+ Паказаць падказку \"Утрымлівайце, каб паставіць у чаргу\"
+ Паказаць падказку пры націсканні фонавай або ўсплывальнай кнопкі ў відэа \"Падрабязнасці:\"
URL не падтрымліваецца
Краіна кантэнту па змаўчанні
Мова кантэнту па змаўчанні
@@ -87,7 +87,7 @@
Прайграванне ў фонавым рэжыме
Прайграванне ва ўсплываючым акне
Кантэнт
- Кантэнт 18+
+ Паказаць кантэнт 18+
Трансляцыя
Загрузкі
Загрузкі
@@ -106,23 +106,23 @@
Толькі цяпер
Файл
Апавяшчэнне NewPipe
- Апавяшчэнні для NewPipe ў фоне і ва ўсплываючым акне
+ Апавяшчэнні для прайгравальніка NewPipe
[Невядома]
Перайсці ў фон
Перайсці ў акно
Перайсці ў галоўнае акно
Імпарт дадзеных
Экспарт дадзеных
- Бягучыя падпіскі, плэйлісты і гісторыя будуць заменены
- Экспарт гісторыі, падпісак і плэйлістоў
+ Перавызначае вашу бягучую гісторыю, падпіскі, плэйлісты і (неабавязкова) налады
+ Экспарт гісторыі, падпісак, плэйлістоў і налад
Ачысціць гісторыю праглядаў
- Выдаліць гісторыю прайграных патокаў
+ Выдаліць гісторыю прайграных патокаў і пазіцыі прайгравання
Выдаліць усю гісторыю праглядаў\?
- Гісторыя праглядаў выдалена.
+ Гісторыя праглядаў выдалена
Ачысціць гісторыю пошуку
Выдаліць гісторыю пошукавых запытаў
Выдаліць усю гісторыю пошуку\?
- Гісторыя пошуку выдалена.
+ Гісторыя пошуку выдалена
Памылка
Памылка сеткі
Не атрымалася загрузіць усе мініяцюры
@@ -142,22 +142,22 @@
Файл не існуе або няма дазволу на яго чытанне або запіс
Імя файла не можа быць пустым
Адбылася памылка: %1$s
- Няма патокаў, даступных для загрузкі
+ Няма трансляцый, даступных для загрузкі
Прабачце, гэта не павінна было адбыцца.
- Адправіць справаздачу па e-mail
- Прабачце, адбыліся памылкі.
- СПРАВАЗДАЧА
+ Паведаміць па электроннай пошце
+ На жаль, нешта пайшло не так.
+ Справаздача
Інфармацыя:
Што адбылося:
- Што:\\nЗапыт:\\nМова кантэнту:\\nСэрвіс:\\nЧас па Грынвічы:\\nПакет:\\nВерсія:\\nВерсія АС:
+ Што:\\nЗапыт:\\nМова кантэнту:\\nКраіна кантэнту:\\nМова праграмы:\\nСэрвіс:\\nЧас GMT:\\nПакет:\\nВерсія:\\nВерсія АС:
Ваш каментар (English):
Падрабязнасці:
- Мініяцюра відэа-прэв\'ю
+ Прайграць відэа, працягласць:
Мініяцюра аватара карыстальніка
Спадабалася
Не спадабалася
Няма вынікаў
- Нічога няма
+ Нічога няма, акрамя цвыркуноў
Перацягніце, каб змяніць парадак
Відэа
Аўдыё
@@ -182,6 +182,7 @@
- %s Відэа
- %s відэа
- %s відэа
+ - %s відэа
Пачаць
Паўза
@@ -210,18 +211,17 @@
Сімвал для замены
Літары і лічбы
Большасць спецзнакаў
- Прыкладанне для прайгравання гэтага файла не ўстаноўлена
Аб NewPipe
Іншыя ліцэнзіі
© %1$s %2$s пад ліцэнзіяй %3$s
- Аб дадатку
+ Аб праграме
Ліцэнзіі
Свабоднае легкавагавае патокавае прайграванне на Android.
Дапамога праекту
Вітаецца ўсё - ідэі, пераклад, змены дызайну, чыстка кода або велізарныя змены ў кодзе. Чым больш зроблена, тым лепш!
Адкрыць на GitHub
Ахвяраваць
- Распрацоўшчыкі NewPipe цаной свайго вольнага часу робяць ваша жыццё крышачку зручней. Адплаціце ім тым жа - атрымліваючы асалоду ад кубачка кавы, яны змогуць зрабіць NewPipe яшчэ лепей.
+ NewPipe распрацаваны добраахвотнікамі, якія праводзяць свой вольны час, забяспечваючы лепшы карыстацкі досвед. Дапамажыце распрацоўшчыкам зрабіць NewPipe яшчэ лепшым, пакуль яны атрымліваюць асалоду ад кавы.
Аддаць належнае
Вэб-сайт
Дзеля атрымання больш падрабязнай інфармацыі і апошніх навін аб NewPipe наведайце наш вэб-сайт.
@@ -230,7 +230,7 @@
\nПалітыка прыватнасці NewPipe падрабязна тлумачыць, якія дадзеныя адпраўляюцца і захоўваюцца пры адпраўцы справаздачы аб збоях.