Skip to content

Commit 4604339

Browse files
committed
Merge branch 'dev' into refactor
Conflicts: app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java app/src/main/java/org/schabi/newpipe/player/PlayerService.java app/src/main/java/org/schabi/newpipe/player/helper/PlayerHolder.java build.gradle
2 parents abfde87 + 965eea2 commit 4604339

13 files changed

Lines changed: 154 additions & 106 deletions

File tree

app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1294,9 +1294,8 @@ class VideoDetailFragment :
12941294
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED)
12951295
}
12961296
// Rebound to the service if it was closed via notification or mini player
1297-
if (!PlayerHolder.isBound) {
1298-
PlayerHolder.startService(false, this@VideoDetailFragment)
1299-
}
1297+
PlayerHolder.setListener(this@VideoDetailFragment)
1298+
PlayerHolder.tryBindIfNeeded(requireContext())
13001299
}
13011300
}
13021301
}

app/src/main/java/org/schabi/newpipe/player/PlayerService.kt

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import org.schabi.newpipe.player.mediabrowser.MediaBrowserImpl
3434
import org.schabi.newpipe.player.mediabrowser.MediaBrowserPlaybackPreparer
3535
import org.schabi.newpipe.player.mediasession.MediaSessionPlayerUi
3636
import org.schabi.newpipe.player.notification.NotificationPlayerUi
37+
import org.schabi.newpipe.player.notification.NotificationUtil
3738
import org.schabi.newpipe.util.ThemeHelper
3839
import java.lang.ref.WeakReference
3940
import java.util.function.Consumer
@@ -140,23 +141,23 @@ class PlayerService : MediaBrowserServiceCompat() {
140141
}
141142
}
142143

143-
val p = player
144-
if (Intent.ACTION_MEDIA_BUTTON == intent.action && p?.playQueue == null) {
145-
// No need to process media button's actions if the player is not working, otherwise
146-
// the player service would strangely start with nothing to play
147-
// Stop the service in this case, which will be removed from the foreground and its
148-
// notification cancelled in its destruction
149-
destroyPlayerAndStopService()
144+
if (player == null) {
145+
// No need to process media button's actions or other system intents if the player is
146+
// not running. However, since the current intent might have been issued by the system
147+
// with `startForegroundService()` (for unknown reasons), we need to ensure that we post
148+
// a (dummy) foreground notification, otherwise we'd incur in
149+
// "Context.startForegroundService() did not then call Service.startForeground()". Then
150+
// we stop the service again.
151+
Log.d(TAG, "onStartCommand() got a useless intent, closing the service");
152+
NotificationUtil.startForegroundWithDummyNotification(this);
150153
return START_NOT_STICKY
151154
}
152155

153-
if (p != null) {
154-
val oldPlayerType = p.playerType
155-
p.handleIntent(intent)
156-
p.handleIntentPost(oldPlayerType)
157-
p.UIs().get(MediaSessionPlayerUi::class)
158-
?.handleMediaButtonIntent(intent)
159-
}
156+
val oldPlayerType = player?.playerType
157+
player?.handleIntent(intent)
158+
player?.handleIntentPost(oldPlayerType)
159+
player?.UIs()?.get(MediaSessionPlayerUi::class.java)
160+
?.handleMediaButtonIntent(intent)
160161

161162
return START_NOT_STICKY
162163
}

app/src/main/java/org/schabi/newpipe/player/helper/AudioReactor.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,9 +154,6 @@ public void onAudioSessionIdChanged(@NonNull final EventTime eventTime,
154154
notifyAudioSessionUpdate(true, audioSessionId);
155155
}
156156
private void notifyAudioSessionUpdate(final boolean active, final int audioSessionId) {
157-
if (!PlayerHelper.isUsingDSP()) {
158-
return;
159-
}
160157
final Intent intent = new Intent(active
161158
? AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION
162159
: AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION);

app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -296,10 +296,6 @@ public static ExoTrackSelection.Factory getQualitySelector() {
296296
AdaptiveTrackSelection.DEFAULT_BANDWIDTH_FRACTION);
297297
}
298298

299-
public static boolean isUsingDSP() {
300-
return true;
301-
}
302-
303299
@NonNull
304300
public static CaptionStyleCompat getCaptionStyle(@NonNull final Context context) {
305301
final CaptioningManager captioningManager = ContextCompat.getSystemService(context,

app/src/main/java/org/schabi/newpipe/player/helper/PlayerHolder.kt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -172,9 +172,11 @@ object PlayerHolder {
172172
startPlayerListener()
173173
// ^ will call listener.onPlayerConnected() down the line if there is an active player
174174

175-
// notify the main activity that binding the service has completed, so that it can
176-
// open the bottom mini-player
177-
NavigationHelper.sendPlayerStartedEvent(s)
175+
if (playerService != null && playerService?.player != null) {
176+
// notify the main activity that binding the service has completed and that there is
177+
// a player, so that it can open the bottom mini-player
178+
NavigationHelper.sendPlayerStartedEvent(localBinder.service)
179+
}
178180
}
179181
}
180182

app/src/main/java/org/schabi/newpipe/player/notification/NotificationUtil.java

Lines changed: 40 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_CLOSE;
66

77
import android.annotation.SuppressLint;
8+
import android.app.Notification;
89
import android.app.PendingIntent;
10+
import android.content.Context;
911
import android.content.Intent;
1012
import android.content.pm.ServiceInfo;
1113
import android.graphics.Bitmap;
@@ -24,6 +26,7 @@
2426
import org.schabi.newpipe.R;
2527
import org.schabi.newpipe.player.Player;
2628
import org.schabi.newpipe.player.PlayerIntentType;
29+
import org.schabi.newpipe.player.PlayerService;
2730
import org.schabi.newpipe.player.mediasession.MediaSessionPlayerUi;
2831
import org.schabi.newpipe.util.NavigationHelper;
2932

@@ -90,12 +93,9 @@ private synchronized NotificationCompat.Builder createNotification() {
9093
Log.d(TAG, "createNotification()");
9194
}
9295
notificationManager = NotificationManagerCompat.from(player.getContext());
93-
final NotificationCompat.Builder builder =
94-
new NotificationCompat.Builder(player.getContext(),
95-
player.getContext().getString(R.string.notification_channel_id));
96-
final MediaStyle mediaStyle = new MediaStyle();
9796

9897
// setup media style (compact notification slots and media session)
98+
final MediaStyle mediaStyle = new MediaStyle();
9999
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
100100
// notification actions are ignored on Android 13+, and are replaced by code in
101101
// MediaSessionPlayerUi
@@ -108,18 +108,9 @@ private synchronized NotificationCompat.Builder createNotification() {
108108
}
109109

110110
// setup notification builder
111-
builder.setStyle(mediaStyle)
112-
.setPriority(NotificationCompat.PRIORITY_HIGH)
113-
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
114-
.setCategory(NotificationCompat.CATEGORY_TRANSPORT)
115-
.setShowWhen(false)
116-
.setSmallIcon(R.drawable.ic_newpipe_triangle_white)
117-
.setColor(ContextCompat.getColor(player.getContext(),
118-
R.color.dark_background_color))
111+
final var builder = setupNotificationBuilder(player.getContext(), mediaStyle)
119112
.setColorized(player.getPrefs().getBoolean(
120-
player.getContext().getString(R.string.notification_colorize_key), true))
121-
.setDeleteIntent(PendingIntentCompat.getBroadcast(player.getContext(),
122-
NOTIFICATION_ID, new Intent(ACTION_CLOSE), FLAG_UPDATE_CURRENT, false));
113+
player.getContext().getString(R.string.notification_colorize_key), true));
123114

124115
// set the initial value for the video thumbnail, updatable with updateNotificationThumbnail
125116
setLargeIcon(builder);
@@ -168,17 +159,17 @@ public boolean shouldUpdateBufferingSlot() {
168159
&& notificationBuilder.mActions.get(2).actionIntent != null);
169160
}
170161

162+
public static void startForegroundWithDummyNotification(final PlayerService service) {
163+
final var builder = setupNotificationBuilder(service, new MediaStyle());
164+
startForeground(service, builder.build());
165+
}
166+
171167
public void createNotificationAndStartForeground() {
172168
if (notificationBuilder == null) {
173169
notificationBuilder = createNotification();
174170
}
175171
updateNotification();
176-
177-
// ServiceInfo constants are not used below Android Q, so 0 is set here
178-
final int serviceType = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
179-
? ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK : 0;
180-
ServiceCompat.startForeground(player.getService(), NOTIFICATION_ID,
181-
notificationBuilder.build(), serviceType);
172+
startForeground(player.getService(), notificationBuilder.build());
182173
}
183174

184175
public void cancelNotificationAndStopForeground() {
@@ -192,6 +183,34 @@ public void cancelNotificationAndStopForeground() {
192183
}
193184

194185

186+
/////////////////////////////////////////////////////
187+
// STATIC FUNCTIONS IN COMMON BETWEEN DUMMY AND REAL NOTIFICATION
188+
/////////////////////////////////////////////////////
189+
190+
private static NotificationCompat.Builder setupNotificationBuilder(final Context context,
191+
final MediaStyle style) {
192+
return new NotificationCompat.Builder(context,
193+
context.getString(R.string.notification_channel_id))
194+
.setStyle(style)
195+
.setPriority(NotificationCompat.PRIORITY_HIGH)
196+
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
197+
.setCategory(NotificationCompat.CATEGORY_TRANSPORT)
198+
.setShowWhen(false)
199+
.setSmallIcon(R.drawable.ic_newpipe_triangle_white)
200+
.setColor(ContextCompat.getColor(context, R.color.dark_background_color))
201+
.setDeleteIntent(PendingIntentCompat.getBroadcast(context,
202+
NOTIFICATION_ID, new Intent(ACTION_CLOSE), FLAG_UPDATE_CURRENT, false));
203+
}
204+
205+
private static void startForeground(final PlayerService service,
206+
final Notification notification) {
207+
// ServiceInfo constants are not used below Android Q, so 0 is set here
208+
final int serviceType = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
209+
? ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK : 0;
210+
ServiceCompat.startForeground(service, NOTIFICATION_ID, notification, serviceType);
211+
}
212+
213+
195214
/////////////////////////////////////////////////////
196215
// ACTIONS
197216
/////////////////////////////////////////////////////

app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -103,12 +103,12 @@ private static String getNewPipeChildFolderPathForDir(final File dir) {
103103
}
104104

105105
public static boolean useStorageAccessFramework(final Context context) {
106-
// There's a FireOS bug which prevents SAF open/close dialogs from being confirmed with a
107-
// remote (see #6455).
108-
if (DeviceUtils.isFireTv()) {
109-
return false;
110-
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
106+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
111107
return true;
108+
} else if (DeviceUtils.isFireTv()) {
109+
// There's a FireOS bug which prevents SAF open/close dialogs from being confirmed with
110+
// a remote (see #6455).
111+
return false;
112112
}
113113

114114
final String key = context.getString(R.string.storage_use_saf);

app/src/main/java/org/schabi/newpipe/streams/SrtFromTtmlWriter.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,11 @@ public class SrtFromTtmlWriter {
2424
private final boolean ignoreEmptyFrames;
2525
private final Charset charset = StandardCharsets.UTF_8;
2626

27-
private int frameIndex = 0;
27+
// According to the SubRip (.srt) specification, subtitle
28+
// numbering must start from 1.
29+
// Some players accept 0 or even negative indices,
30+
// but to ensure compliance we start at 1.
31+
private int frameIndex = 1;
2832

2933
public SrtFromTtmlWriter(final SharpStream out, final boolean ignoreEmptyFrames) {
3034
this.out = out;
@@ -39,7 +43,8 @@ private static String getTimestamp(final Element frame, final String attr) {
3943

4044
private void writeFrame(final String begin, final String end, final StringBuilder text)
4145
throws IOException {
42-
writeString(String.valueOf(frameIndex++));
46+
writeString(String.valueOf(frameIndex));
47+
frameIndex += 1;
4348
writeString(NEW_LINE);
4449
writeString(begin);
4550
writeString(" --> ");

app/src/main/java/org/schabi/newpipe/util/external_communication/ShareUtils.java

Lines changed: 30 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,9 @@
66
import android.content.ActivityNotFoundException;
77
import android.content.ClipData;
88
import android.content.ClipboardManager;
9+
import android.content.ComponentName;
910
import android.content.Context;
1011
import android.content.Intent;
11-
import android.content.pm.PackageManager;
12-
import android.content.pm.ResolveInfo;
1312
import android.graphics.Bitmap;
1413
import android.graphics.BitmapFactory;
1514
import android.net.Uri;
@@ -25,6 +24,7 @@
2524

2625
import org.schabi.newpipe.BuildConfig;
2726
import org.schabi.newpipe.R;
27+
import org.schabi.newpipe.RouterActivity;
2828
import org.schabi.newpipe.extractor.Image;
2929
import org.schabi.newpipe.util.image.ImageStrategy;
3030

@@ -67,8 +67,9 @@ public static void installApp(@NonNull final Context context, final String packa
6767
}
6868

6969
/**
70-
* Open the url with the system default browser. If no browser is set as default, falls back to
71-
* {@link #openAppChooser(Context, Intent, boolean)}.
70+
* Open the url with the system default browser. If no browser is installed, falls back to
71+
* {@link #openAppChooser(Context, Intent, boolean)} (for displaying that no apps are available
72+
* to handle the action, or possible OEM-related edge cases).
7273
* <p>
7374
* This function selects the package to open based on which apps respond to the {@code http://}
7475
* schema alone, which should exclude special non-browser apps that are can handle the url (e.g.
@@ -82,44 +83,26 @@ public static void installApp(@NonNull final Context context, final String packa
8283
* @param url the url to browse
8384
**/
8485
public static void openUrlInBrowser(@NonNull final Context context, final String url) {
85-
// Resolve using a generic http://, so we are sure to get a browser and not e.g. the yt app.
86+
// Target a generic http://, so we are sure to get a browser and not e.g. the yt app.
8687
// Note that this requires the `http` schema to be added to `<queries>` in the manifest.
87-
final ResolveInfo defaultBrowserInfo;
8888
final Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://"));
89-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
90-
defaultBrowserInfo = context.getPackageManager().resolveActivity(browserIntent,
91-
PackageManager.ResolveInfoFlags.of(PackageManager.MATCH_DEFAULT_ONLY));
92-
} else {
93-
defaultBrowserInfo = context.getPackageManager().resolveActivity(browserIntent,
94-
PackageManager.MATCH_DEFAULT_ONLY);
95-
}
9689

9790
final Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url))
9891
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
9992

100-
if (defaultBrowserInfo == null) {
101-
// No app installed to open a web URL, but it may be handled by other apps so try
102-
// opening a system chooser for the link in this case (it could be bypassed by the
103-
// system if there is only one app which can open the link or a default app associated
104-
// with the link domain on Android 12 and higher)
105-
openAppChooser(context, intent, true);
106-
return;
107-
}
108-
109-
final String defaultBrowserPackage = defaultBrowserInfo.activityInfo.packageName;
110-
111-
if (defaultBrowserPackage.equals("android")) {
112-
// No browser set as default (doesn't work on some devices)
93+
// See https://stackoverflow.com/a/58801285 and `setSelector` documentation
94+
intent.setSelector(browserIntent);
95+
try {
96+
context.startActivity(intent);
97+
} catch (final ActivityNotFoundException e) {
98+
// No browser is available. This should, in the end, yield a nice AOSP error message
99+
// indicating that no app is available to handle this action.
100+
//
101+
// Note: there are some situations where modified OEM ROMs have apps that appear
102+
// to be browsers but are actually app choosers. If starting the Activity fails
103+
// related to this, opening the system app chooser is still the correct behavior.
104+
intent.setSelector(null);
113105
openAppChooser(context, intent, true);
114-
} else {
115-
try {
116-
intent.setPackage(defaultBrowserPackage);
117-
context.startActivity(intent);
118-
} catch (final ActivityNotFoundException e) {
119-
// Not a browser but an app chooser because of OEMs changes
120-
intent.setPackage(null);
121-
openAppChooser(context, intent, true);
122-
}
123106
}
124107
}
125108

@@ -195,6 +178,18 @@ private static void openAppChooser(@NonNull final Context context,
195178
chooserIntent.putExtra(Intent.EXTRA_TITLE, context.getString(R.string.open_with));
196179
}
197180

181+
// Avoid opening in NewPipe
182+
// (Implementation note: if the URL is one for which NewPipe itself
183+
// is set as handler on Android >= 12, we actually remove the only eligible app
184+
// for this link, and browsers will not be offered to the user. For that, use
185+
// `openUrlInBrowser`.)
186+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
187+
chooserIntent.putExtra(
188+
Intent.EXTRA_EXCLUDE_COMPONENTS,
189+
new ComponentName[]{new ComponentName(context, RouterActivity.class)}
190+
);
191+
}
192+
198193
// Migrate any clip data and flags from the original intent.
199194
final int permFlags = intent.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION
200195
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION

app/src/main/java/us/shandian/giga/get/DownloadMission.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -661,7 +661,8 @@ public void psContinue(boolean recover) {
661661
* @return {@code true}, if storage is invalid and cannot be used
662662
*/
663663
public boolean hasInvalidStorage() {
664-
return errCode == ERROR_PROGRESS_LOST || storage == null || !storage.existsAsFile();
664+
// Don't consider ERROR_PROGRESS_LOST as invalid storage - it can be recovered
665+
return storage == null || !storage.existsAsFile();
665666
}
666667

667668
/**

0 commit comments

Comments
 (0)