Android: add option to pause MPD when headphones disconnect
This commit is contained in:
parent
d39b11ba5d
commit
57687779be
|
@ -17,6 +17,8 @@
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<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"
|
<application android:allowBackup="true"
|
||||||
android:requestLegacyExternalStorage="true"
|
android:requestLegacyExternalStorage="true"
|
||||||
|
@ -41,6 +43,7 @@
|
||||||
<receiver android:name=".Receiver">
|
<receiver android:name=".Receiver">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||||
|
<action android:name="android.intent.action.HEADSET_PLUG" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
<service android:name=".Main" android:process=":main"/>
|
<service android:name=".Main" android:process=":main"/>
|
||||||
|
|
|
@ -23,6 +23,12 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/checkbox_wakelock" />
|
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
|
<TextView
|
||||||
android:id="@+id/status"
|
android:id="@+id/status"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
|
|
@ -8,4 +8,5 @@
|
||||||
<string name="toggle_button_run_off">MPD is not running</string>
|
<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_run_on_boot">Run MPD automatically on boot</string>
|
||||||
<string name="checkbox_wakelock">Prevent suspend when MPD is running (Wakelock)</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>
|
</resources>
|
||||||
|
|
|
@ -33,4 +33,5 @@ public class Bridge {
|
||||||
|
|
||||||
public static native void run(Context context, LogListener logListener);
|
public static native void run(Context context, LogListener logListener);
|
||||||
public static native void shutdown();
|
public static native void shutdown();
|
||||||
|
public static native void pause();
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ interface IMain
|
||||||
{
|
{
|
||||||
void start();
|
void start();
|
||||||
void stop();
|
void stop();
|
||||||
|
void setPauseOnHeadphonesDisconnect(boolean enabled);
|
||||||
void setWakelockEnabled(boolean enabled);
|
void setWakelockEnabled(boolean enabled);
|
||||||
boolean isRunning();
|
boolean isRunning();
|
||||||
void registerCallback(IMainCallback cb);
|
void registerCallback(IMainCallback cb);
|
||||||
|
|
|
@ -24,9 +24,13 @@ import android.app.Notification;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.app.Service;
|
import android.app.Service;
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
import android.bluetooth.BluetoothClass;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
import android.content.ServiceConnection;
|
import android.content.ServiceConnection;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
|
@ -55,6 +59,7 @@ public class Main extends Service implements Runnable {
|
||||||
private String mError = null;
|
private String mError = null;
|
||||||
private final RemoteCallbackList<IMainCallback> mCallbacks = new RemoteCallbackList<IMainCallback>();
|
private final RemoteCallbackList<IMainCallback> mCallbacks = new RemoteCallbackList<IMainCallback>();
|
||||||
private final IBinder mBinder = new MainStub(this);
|
private final IBinder mBinder = new MainStub(this);
|
||||||
|
private boolean mPauseOnHeadphonesDisconnect = false;
|
||||||
private PowerManager.WakeLock mWakelock = null;
|
private PowerManager.WakeLock mWakelock = null;
|
||||||
|
|
||||||
static class MainStub extends IMain.Stub {
|
static class MainStub extends IMain.Stub {
|
||||||
|
@ -68,6 +73,9 @@ public class Main extends Service implements Runnable {
|
||||||
public void stop() {
|
public void stop() {
|
||||||
mService.stop();
|
mService.stop();
|
||||||
}
|
}
|
||||||
|
public void setPauseOnHeadphonesDisconnect(boolean enabled) {
|
||||||
|
mService.setPauseOnHeadphonesDisconnect(enabled);
|
||||||
|
}
|
||||||
public void setWakelockEnabled(boolean enabled) {
|
public void setWakelockEnabled(boolean enabled) {
|
||||||
mService.setWakelockEnabled(enabled);
|
mService.setWakelockEnabled(enabled);
|
||||||
}
|
}
|
||||||
|
@ -191,6 +199,28 @@ public class Main extends Service implements Runnable {
|
||||||
if (mThread != null)
|
if (mThread != null)
|
||||||
return;
|
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);
|
final Intent mainIntent = new Intent(this, Settings.class);
|
||||||
mainIntent.setAction("android.intent.action.MAIN");
|
mainIntent.setAction("android.intent.action.MAIN");
|
||||||
mainIntent.addCategory("android.intent.category.LAUNCHER");
|
mainIntent.addCategory("android.intent.category.LAUNCHER");
|
||||||
|
@ -241,6 +271,21 @@ public class Main extends Service implements Runnable {
|
||||||
stopSelf();
|
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) {
|
private void setWakelockEnabled(boolean enabled) {
|
||||||
if (enabled && mWakelock == null) {
|
if (enabled && mWakelock == null) {
|
||||||
PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
|
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) {
|
public boolean setWakelockEnabled(boolean enabled) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (mIMain != null) {
|
if (mIMain != null) {
|
||||||
|
|
|
@ -59,6 +59,7 @@ public class Settings extends Activity {
|
||||||
public static class Preferences {
|
public static class Preferences {
|
||||||
public static final String KEY_RUN_ON_BOOT ="run_on_boot";
|
public static final String KEY_RUN_ON_BOOT ="run_on_boot";
|
||||||
public static final String KEY_WAKELOCK ="wakelock";
|
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) {
|
public static SharedPreferences get(Context context) {
|
||||||
return context.getSharedPreferences(TAG, MODE_PRIVATE);
|
return context.getSharedPreferences(TAG, MODE_PRIVATE);
|
||||||
|
@ -154,6 +155,9 @@ public class Settings extends Activity {
|
||||||
if (Preferences.getBoolean(Settings.this,
|
if (Preferences.getBoolean(Settings.this,
|
||||||
Preferences.KEY_WAKELOCK, false))
|
Preferences.KEY_WAKELOCK, false))
|
||||||
mClient.setWakelockEnabled(true);
|
mClient.setWakelockEnabled(true);
|
||||||
|
if (Preferences.getBoolean(Settings.this,
|
||||||
|
Preferences.KEY_PAUSE_ON_HEADPHONES_DISCONNECT, false))
|
||||||
|
mClient.setPauseOnHeadphonesDisconnect(true);
|
||||||
} else {
|
} else {
|
||||||
mClient.stop();
|
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
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
/* TODO: this sure is the wrong place to request
|
/* 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))
|
if (Preferences.getBoolean(this, Preferences.KEY_WAKELOCK, false))
|
||||||
checkbox.setChecked(true);
|
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);
|
super.onCreate(savedInstanceState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -612,6 +612,15 @@ Java_org_musicpd_Bridge_shutdown(JNIEnv *, jclass)
|
||||||
global_instance->Break();
|
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
|
#else
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
|
|
Loading…
Reference in New Issue