summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Bazley <sambazley@fastmail.com>2021-07-26 18:55:39 +0100
committerSam Bazley <sambazley@fastmail.com>2021-07-26 19:02:47 +0100
commit57687779befd796b79b1a569daf9e48eac3aea26 (patch)
treeba1863706aa96714d3b90243df7b6c75a16d2045
parentd39b11ba5d0f9e36e59f1fdf7321bcd64c3bfe26 (diff)
Android: add option to pause MPD when headphones disconnect
-rw-r--r--android/AndroidManifest.xml3
-rw-r--r--android/res/layout/settings.xml6
-rw-r--r--android/res/values/strings.xml1
-rw-r--r--android/src/Bridge.java1
-rw-r--r--android/src/IMain.aidl1
-rw-r--r--android/src/Main.java58
-rw-r--r--android/src/Settings.java18
-rw-r--r--src/Main.cxx9
8 files changed, 97 insertions, 0 deletions
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 @@
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
+ <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
+ <uses-permission android:name="android.permission.BLUETOOTH" />
<application android:allowBackup="true"
android:requestLegacyExternalStorage="true"
@@ -41,6 +43,7 @@
<receiver android:name=".Receiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
+ <action android:name="android.intent.action.HEADSET_PLUG" />
</intent-filter>
</receiver>
<service android:name=".Main" android:process=":main"/>
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" />
+ <CheckBox
+ android:id="@+id/pause_on_headphones_disconnect"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/checkbox_pause_on_headphones_disconnect" />
+
<TextView
android:id="@+id/status"
android:layout_width="wrap_content"
diff --git a/android/res/values/strings.xml b/android/res/values/strings.xml
index fc5a15bda..2df00470e 100644
--- a/android/res/values/strings.xml
+++ b/android/res/values/strings.xml
@@ -8,4 +8,5 @@
<string name="toggle_button_run_off">MPD is not running</string>
<string name="checkbox_run_on_boot">Run MPD automatically on boot</string>
<string name="checkbox_wakelock">Prevent suspend when MPD is running (Wakelock)</string>
+ <string name="checkbox_pause_on_headphones_disconnect">Pause MPD when headphones disconnect</string>
</resources>
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<IMainCallback> mCallbacks = new RemoteCallbackList<IMainCallback>();
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