From 57687779befd796b79b1a569daf9e48eac3aea26 Mon Sep 17 00:00:00 2001 From: Sam Bazley Date: Mon, 26 Jul 2021 18:55:39 +0100 Subject: [PATCH] Android: add option to pause MPD when headphones disconnect --- android/AndroidManifest.xml | 3 ++ android/res/layout/settings.xml | 6 ++++ android/res/values/strings.xml | 1 + android/src/Bridge.java | 1 + android/src/IMain.aidl | 1 + android/src/Main.java | 58 +++++++++++++++++++++++++++++++++ android/src/Settings.java | 18 ++++++++++ src/Main.cxx | 9 +++++ 8 files changed, 97 insertions(+) diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 36e636804..6d55e5481 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -17,6 +17,8 @@ + + + diff --git a/android/res/layout/settings.xml b/android/res/layout/settings.xml index 46e471b05..3b0a103c3 100644 --- a/android/res/layout/settings.xml +++ b/android/res/layout/settings.xml @@ -23,6 +23,12 @@ android:layout_height="wrap_content" android:text="@string/checkbox_wakelock" /> + + MPD is not running Run MPD automatically on boot Prevent suspend when MPD is running (Wakelock) + Pause MPD when headphones disconnect diff --git a/android/src/Bridge.java b/android/src/Bridge.java index 192094b74..ae02806f9 100644 --- a/android/src/Bridge.java +++ b/android/src/Bridge.java @@ -33,4 +33,5 @@ public class Bridge { public static native void run(Context context, LogListener logListener); public static native void shutdown(); + public static native void pause(); } diff --git a/android/src/IMain.aidl b/android/src/IMain.aidl index ba7050d79..4eaf577fb 100644 --- a/android/src/IMain.aidl +++ b/android/src/IMain.aidl @@ -5,6 +5,7 @@ interface IMain { void start(); void stop(); + void setPauseOnHeadphonesDisconnect(boolean enabled); void setWakelockEnabled(boolean enabled); boolean isRunning(); void registerCallback(IMainCallback cb); diff --git a/android/src/Main.java b/android/src/Main.java index 15c7ba419..4f314f8aa 100644 --- a/android/src/Main.java +++ b/android/src/Main.java @@ -24,9 +24,13 @@ import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothClass; +import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.ServiceConnection; import android.os.Build; import android.os.IBinder; @@ -55,6 +59,7 @@ public class Main extends Service implements Runnable { private String mError = null; private final RemoteCallbackList mCallbacks = new RemoteCallbackList(); private final IBinder mBinder = new MainStub(this); + private boolean mPauseOnHeadphonesDisconnect = false; private PowerManager.WakeLock mWakelock = null; static class MainStub extends IMain.Stub { @@ -68,6 +73,9 @@ public class Main extends Service implements Runnable { public void stop() { mService.stop(); } + public void setPauseOnHeadphonesDisconnect(boolean enabled) { + mService.setPauseOnHeadphonesDisconnect(enabled); + } public void setWakelockEnabled(boolean enabled) { mService.setWakelockEnabled(enabled); } @@ -191,6 +199,28 @@ public class Main extends Service implements Runnable { if (mThread != null) return; + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_HEADSET_PLUG); + filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED); + filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED); + registerReceiver(new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (!mPauseOnHeadphonesDisconnect) { + return; + } + + if (intent.getAction().equals(Intent.ACTION_HEADSET_PLUG)) { + if (intent.hasExtra("state") && intent.getIntExtra("state", 0) == 0) + pause(); + } else { + BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); + if (device.getBluetoothClass().hasService(BluetoothClass.Service.AUDIO)) + pause(); + } + } + }, filter); + final Intent mainIntent = new Intent(this, Settings.class); mainIntent.setAction("android.intent.action.MAIN"); mainIntent.addCategory("android.intent.category.LAUNCHER"); @@ -241,6 +271,21 @@ public class Main extends Service implements Runnable { stopSelf(); } + private void pause() { + if (mThread != null) { + if (mThread.isAlive()) { + synchronized (this) { + if (mStatus == MAIN_STATUS_STARTED) + Bridge.pause(); + } + } + } + } + + private void setPauseOnHeadphonesDisconnect(boolean enabled) { + mPauseOnHeadphonesDisconnect = enabled; + } + private void setWakelockEnabled(boolean enabled) { if (enabled && mWakelock == null) { PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE); @@ -368,6 +413,19 @@ public class Main extends Service implements Runnable { } } + public boolean setPauseOnHeadphonesDisconnect(boolean enabled) { + synchronized (this) { + if (mIMain != null) { + try { + mIMain.setPauseOnHeadphonesDisconnect(enabled); + return true; + } catch (RemoteException e) { + } + } + return false; + } + } + public boolean setWakelockEnabled(boolean enabled) { synchronized (this) { if (mIMain != null) { diff --git a/android/src/Settings.java b/android/src/Settings.java index 63d911eed..f3f461295 100644 --- a/android/src/Settings.java +++ b/android/src/Settings.java @@ -59,6 +59,7 @@ public class Settings extends Activity { public static class Preferences { public static final String KEY_RUN_ON_BOOT ="run_on_boot"; public static final String KEY_WAKELOCK ="wakelock"; + public static final String KEY_PAUSE_ON_HEADPHONES_DISCONNECT ="pause_on_headphones_disconnect"; public static SharedPreferences get(Context context) { return context.getSharedPreferences(TAG, MODE_PRIVATE); @@ -154,6 +155,9 @@ public class Settings extends Activity { if (Preferences.getBoolean(Settings.this, Preferences.KEY_WAKELOCK, false)) mClient.setWakelockEnabled(true); + if (Preferences.getBoolean(Settings.this, + Preferences.KEY_PAUSE_ON_HEADPHONES_DISCONNECT, false)) + mClient.setPauseOnHeadphonesDisconnect(true); } else { mClient.stop(); } @@ -179,6 +183,15 @@ public class Settings extends Activity { } }; + private final OnCheckedChangeListener mOnPauseOnHeadphonesDisconnectChangeListener = new OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + Preferences.putBoolean(Settings.this, Preferences.KEY_PAUSE_ON_HEADPHONES_DISCONNECT, isChecked); + if (mClient != null && mClient.isRunning()) + mClient.setPauseOnHeadphonesDisconnect(isChecked); + } + }; + @Override protected void onCreate(Bundle savedInstanceState) { /* TODO: this sure is the wrong place to request @@ -211,6 +224,11 @@ public class Settings extends Activity { if (Preferences.getBoolean(this, Preferences.KEY_WAKELOCK, false)) checkbox.setChecked(true); + checkbox = (CheckBox) findViewById(R.id.pause_on_headphones_disconnect); + checkbox.setOnCheckedChangeListener(mOnPauseOnHeadphonesDisconnectChangeListener); + if (Preferences.getBoolean(this, Preferences.KEY_PAUSE_ON_HEADPHONES_DISCONNECT, false)) + checkbox.setChecked(true); + super.onCreate(savedInstanceState); } diff --git a/src/Main.cxx b/src/Main.cxx index c38946b1c..2b2540e45 100644 --- a/src/Main.cxx +++ b/src/Main.cxx @@ -612,6 +612,15 @@ Java_org_musicpd_Bridge_shutdown(JNIEnv *, jclass) global_instance->Break(); } +gcc_visibility_default +JNIEXPORT void JNICALL +Java_org_musicpd_Bridge_pause(JNIEnv *, jclass) +{ + if (global_instance != nullptr) + for (auto &partition : global_instance->partitions) + partition.pc.LockSetPause(true); +} + #else static inline void