Private
Public Access
Minamp 1.9: install internally, resume after Bluetooth dropouts, log why playback stops
Installed on the SD card (installLocation auto), Minamp was killed whenever USB storage was turned on. A2DP dropouts send AUDIO_BECOMING_NOISY like an unplug: after a Bluetooth loss, resume if the speaker is back within 20 s. Focus loss, media buttons and stream failures now go to the connection log. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
9b88520ec3
commit
a610b090f0
3 files changed
+45
-4
No files matched your search
+3
-3
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.minamp"
|
||||
android:versionCode="9"
|
||||
android:versionName="1.8"
|
||||
android:installLocation="auto">
|
||||
android:versionCode="10"
|
||||
android:versionName="1.9"
|
||||
android:installLocation="internalOnly">
|
||||
|
||||
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="10" />
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ public class MediaButtonReceiver extends BroadcastReceiver {
|
||||
if (ev == null) return;
|
||||
if (isOrderedBroadcast()) abortBroadcast();
|
||||
if (ev.getAction() != KeyEvent.ACTION_DOWN || ev.getRepeatCount() > 0) return;
|
||||
com.minamp.net.DiagLog.log("media button " + ev.getKeyCode() + " (headset / Bluetooth)");
|
||||
final Context app = context.getApplicationContext();
|
||||
switch (ev.getKeyCode()) {
|
||||
case KeyEvent.KEYCODE_HEADSETHOOK:
|
||||
|
||||
@@ -144,6 +144,9 @@ public class PlayerService extends Service implements MediaPlayer.OnPreparedList
|
||||
private boolean hasFocus;
|
||||
private boolean pausedByFocus;
|
||||
private boolean pausedByCall;
|
||||
private boolean pausedByRoute; // the Bluetooth speaker dropped: resume if it's back soon
|
||||
private long routeLostAt;
|
||||
private boolean btOn; // audio was going to Bluetooth (A2DP), as last seen while playing
|
||||
private WifiManager.WifiLock wifiLock;
|
||||
private boolean noisyRegistered;
|
||||
private boolean foreground;
|
||||
@@ -486,6 +489,7 @@ public class PlayerService extends Service implements MediaPlayer.OnPreparedList
|
||||
}
|
||||
|
||||
private void trackFailed(String msg) {
|
||||
com.minamp.net.DiagLog.log("track failed: " + msg + (refusal != null ? " / " + refusal : "") + " (retry " + (retries + 1) + ")");
|
||||
long pos = Math.max(currentPosition(), lastPos);
|
||||
if (refusal != null) msg = refusal; // the server's own reason beats "Playback error (1/-1004)"
|
||||
boolean permanent = refusalCode >= 400 && refusalCode < 500 && refusalCode != 408 && refusalCode != 429;
|
||||
@@ -536,6 +540,7 @@ public class PlayerService extends Service implements MediaPlayer.OnPreparedList
|
||||
|
||||
private void pause() {
|
||||
pausedByFocus = false;
|
||||
pausedByRoute = false;
|
||||
playWhenReady = false;
|
||||
if (mp != null && prepared) {
|
||||
try { mp.pause(); } catch (Exception ignored) {}
|
||||
@@ -611,6 +616,7 @@ public class PlayerService extends Service implements MediaPlayer.OnPreparedList
|
||||
|
||||
private final Runnable ticker = new Runnable() {
|
||||
public void run() {
|
||||
if (state == PLAYING) btOn = audio.isBluetoothA2dpOn();
|
||||
App.get().handler.postDelayed(this, 1000);
|
||||
long now = System.currentTimeMillis();
|
||||
if (sleepAt > 0 && now >= sleepAt) {
|
||||
@@ -692,10 +698,12 @@ public class PlayerService extends Service implements MediaPlayer.OnPreparedList
|
||||
if (pausedByFocus) { pausedByFocus = false; play(); }
|
||||
break;
|
||||
case AudioManager.AUDIOFOCUS_LOSS:
|
||||
com.minamp.net.DiagLog.log("paused: another app took the audio (focus lost)");
|
||||
hasFocus = false;
|
||||
pause();
|
||||
break;
|
||||
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
|
||||
com.minamp.net.DiagLog.log("paused for a moment: another app needs the audio");
|
||||
if (state == PLAYING) { pause(); pausedByFocus = true; }
|
||||
break;
|
||||
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
|
||||
@@ -716,8 +724,40 @@ public class PlayerService extends Service implements MediaPlayer.OnPreparedList
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Headphones unplugged, or the Bluetooth speaker went away. Android 2.x sends this for any A2DP dropout too
|
||||
* (Wi-Fi and Bluetooth share one radio on these phones, and cheap speakers drop the link now and then):
|
||||
* after a Bluetooth loss, keep watching for the speaker and resume if it's back within 20 s.
|
||||
*/
|
||||
private final BroadcastReceiver noisy = new BroadcastReceiver() {
|
||||
@Override public void onReceive(Context c, Intent i) { pause(); }
|
||||
@Override public void onReceive(Context c, Intent i) {
|
||||
boolean bt = btOn;
|
||||
com.minamp.net.DiagLog.log("paused: audio output went away (" + (bt ? "Bluetooth speaker dropped" : "headphones unplugged") + ")");
|
||||
pause();
|
||||
if (bt && track != null) {
|
||||
pausedByRoute = true;
|
||||
routeLostAt = System.currentTimeMillis();
|
||||
App.get().handler.removeCallbacks(routeWatch);
|
||||
App.get().handler.postDelayed(routeWatch, 1000);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final Runnable routeWatch = new Runnable() {
|
||||
public void run() {
|
||||
if (!pausedByRoute) return;
|
||||
long gone = System.currentTimeMillis() - routeLostAt;
|
||||
if (audio.isBluetoothA2dpOn()) {
|
||||
com.minamp.net.DiagLog.log("Bluetooth speaker back after " + gone / 1000 + "s: resuming");
|
||||
pausedByRoute = false;
|
||||
play();
|
||||
} else if (gone < 20000) {
|
||||
App.get().handler.postDelayed(this, 1000);
|
||||
} else {
|
||||
com.minamp.net.DiagLog.log("Bluetooth speaker didn't come back: staying paused");
|
||||
pausedByRoute = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private void registerNoisy() {
|
||||
|
||||
Reference in new issue
Block a user