From e8bfa5dc049116ced3258fdf3990fbf68fa71766 Mon Sep 17 00:00:00 2001 From: birdbird <6892457-tzugen@users.noreply.gitlab.com> Date: Tue, 16 May 2023 07:37:36 +0000 Subject: [PATCH] Fix missing bluetooth permissions --- ultrasonic/src/main/AndroidManifest.xml | 1 + .../receiver/BluetoothIntentReceiver.java | 100 -------------- .../receiver/BluetoothIntentReceiver.kt | 127 ++++++++++++++++++ 3 files changed, 128 insertions(+), 100 deletions(-) delete mode 100644 ultrasonic/src/main/java/org/moire/ultrasonic/receiver/BluetoothIntentReceiver.java create mode 100644 ultrasonic/src/main/java/org/moire/ultrasonic/receiver/BluetoothIntentReceiver.kt diff --git a/ultrasonic/src/main/AndroidManifest.xml b/ultrasonic/src/main/AndroidManifest.xml index a9b35248..75fb06d2 100644 --- a/ultrasonic/src/main/AndroidManifest.xml +++ b/ultrasonic/src/main/AndroidManifest.xml @@ -3,6 +3,7 @@ xmlns:tools="http://schemas.android.com/tools" android:installLocation="auto"> + diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/receiver/BluetoothIntentReceiver.java b/ultrasonic/src/main/java/org/moire/ultrasonic/receiver/BluetoothIntentReceiver.java deleted file mode 100644 index cf7844e7..00000000 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/receiver/BluetoothIntentReceiver.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - This file is part of Subsonic. - - Subsonic is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Subsonic is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Subsonic. If not, see . - - Copyright 2010 (C) Sindre Mehus - */ -package org.moire.ultrasonic.receiver; - -import android.annotation.SuppressLint; -import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothProfile; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; - -import org.moire.ultrasonic.util.Constants; -import org.moire.ultrasonic.util.Settings; - -import timber.log.Timber; - -/** - * Resume or pause playback on Bluetooth A2DP connect/disconnect. - * - * @author Sindre Mehus - */ -@SuppressLint("MissingPermission") -public class BluetoothIntentReceiver extends BroadcastReceiver -{ - @Override - public void onReceive(Context context, Intent intent) - { - int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); - BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); - String action = intent.getAction(); - String name = device != null ? device.getName() : "Unknown"; - String address = device != null ? device.getAddress() : "Unknown"; - - Timber.d("A2DP State: %d; Action: %s; Device: %s; Address: %s", state, action, name, address); - - boolean actionBluetoothDeviceConnected = false; - boolean actionBluetoothDeviceDisconnected = false; - boolean actionA2dpConnected = false; - boolean actionA2dpDisconnected = false; - - if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(action)) - { - actionBluetoothDeviceConnected = true; - } - else if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action) || BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED.equals(action)) - { - actionBluetoothDeviceDisconnected = true; - } - - if (state == android.bluetooth.BluetoothA2dp.STATE_CONNECTED) actionA2dpConnected = true; - else if (state == android.bluetooth.BluetoothA2dp.STATE_DISCONNECTED) actionA2dpDisconnected = true; - - boolean resume = false; - boolean pause = false; - - switch (Settings.getResumeOnBluetoothDevice()) - { - case Constants.PREFERENCE_VALUE_ALL: resume = actionA2dpConnected || actionBluetoothDeviceConnected; - break; - case Constants.PREFERENCE_VALUE_A2DP: resume = actionA2dpConnected; - break; - } - - switch (Settings.getPauseOnBluetoothDevice()) - { - case Constants.PREFERENCE_VALUE_ALL: pause = actionA2dpDisconnected || actionBluetoothDeviceDisconnected; - break; - case Constants.PREFERENCE_VALUE_A2DP: pause = actionA2dpDisconnected; - break; - } - - if (resume) - { - Timber.i("Connected to Bluetooth device %s address %s, resuming playback.", name, address); - context.sendBroadcast(new Intent(Constants.CMD_RESUME_OR_PLAY).setPackage(context.getPackageName())); - } - - if (pause) - { - Timber.i("Disconnected from Bluetooth device %s address %s, requesting pause.", name, address); - context.sendBroadcast(new Intent(Constants.CMD_PAUSE).setPackage(context.getPackageName())); - } - } -} diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/receiver/BluetoothIntentReceiver.kt b/ultrasonic/src/main/java/org/moire/ultrasonic/receiver/BluetoothIntentReceiver.kt new file mode 100644 index 00000000..31fc0210 --- /dev/null +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/receiver/BluetoothIntentReceiver.kt @@ -0,0 +1,127 @@ +/* + * BluetoothIntentReceiver.kt + * Copyright (C) 2009-2022 Ultrasonic developers + * + * Distributed under terms of the GNU GPLv3 license. + */ + +package org.moire.ultrasonic.receiver + +import android.Manifest +import android.bluetooth.BluetoothA2dp +import android.bluetooth.BluetoothDevice +import android.bluetooth.BluetoothDevice.ACTION_ACL_CONNECTED +import android.bluetooth.BluetoothDevice.ACTION_ACL_DISCONNECTED +import android.bluetooth.BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED +import android.bluetooth.BluetoothProfile +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.os.Build +import androidx.core.app.ActivityCompat +import org.moire.ultrasonic.app.UApp +import org.moire.ultrasonic.util.Constants +import org.moire.ultrasonic.util.Constants.PREFERENCE_VALUE_A2DP +import org.moire.ultrasonic.util.Constants.PREFERENCE_VALUE_ALL +import org.moire.ultrasonic.util.Constants.PREFERENCE_VALUE_DISABLED +import org.moire.ultrasonic.util.Settings +import timber.log.Timber + +/** + * Resume or pause playback on Bluetooth A2DP connect/disconnect. + */ +class BluetoothIntentReceiver : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + val state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1) + val device = intent.getBluetoothDevice() + val action = intent.action + + // Whether to log the name of the bluetooth device + val name = device.getNameSafely() + Timber.d("Bluetooth device: $name; State: $state; Action: $action") + + // In these flags we store what kind of device (any or a2dp) has (dis)connected + var connectionStatus = PREFERENCE_VALUE_DISABLED + var disconnectionStatus = PREFERENCE_VALUE_DISABLED + + // First check for general devices + when (action) { + ACTION_ACL_CONNECTED -> { + connectionStatus = PREFERENCE_VALUE_ALL + } + ACTION_ACL_DISCONNECTED, + ACTION_ACL_DISCONNECT_REQUESTED -> { + disconnectionStatus = PREFERENCE_VALUE_ALL + } + } + + // Then check for A2DP devices + when (state) { + BluetoothA2dp.STATE_CONNECTED -> { + connectionStatus = PREFERENCE_VALUE_A2DP + } + BluetoothA2dp.STATE_DISCONNECTED -> { + disconnectionStatus = PREFERENCE_VALUE_A2DP + } + } + + // Flags to store which action should be performed + var shouldResume = false + var shouldPause = false + + // Now check the settings and set the appropriate flags + when (Settings.resumeOnBluetoothDevice) { + PREFERENCE_VALUE_ALL -> { + shouldResume = (connectionStatus != PREFERENCE_VALUE_DISABLED) + } + PREFERENCE_VALUE_A2DP -> { + shouldResume = (connectionStatus == PREFERENCE_VALUE_A2DP) + } + } + + when (Settings.pauseOnBluetoothDevice) { + PREFERENCE_VALUE_ALL -> { + shouldPause = (disconnectionStatus != PREFERENCE_VALUE_DISABLED) + } + PREFERENCE_VALUE_A2DP -> { + shouldPause = (disconnectionStatus == PREFERENCE_VALUE_A2DP) + } + } + + if (shouldResume) { + Timber.i("Connected to Bluetooth device $name; Resuming playback.") + context.sendBroadcast( + Intent(Constants.CMD_RESUME_OR_PLAY) + .setPackage(context.packageName) + ) + } + + if (shouldPause) { + Timber.i("Disconnected from Bluetooth device $name; Requesting pause.") + context.sendBroadcast( + Intent(Constants.CMD_PAUSE) + .setPackage(context.packageName) + ) + } + } +} + +private fun BluetoothDevice?.getNameSafely(): String? { + val logBluetoothName = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && + ( + ActivityCompat.checkSelfPermission( + UApp.applicationContext(), Manifest.permission.BLUETOOTH_CONNECT + ) != PackageManager.PERMISSION_GRANTED + ) + + return if (logBluetoothName) this?.name else "Unknown" +} + +private fun Intent.getBluetoothDevice(): BluetoothDevice? { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + getParcelableExtra(BluetoothDevice.EXTRA_DEVICE, BluetoothDevice::class.java) + } else { + getParcelableExtra(BluetoothDevice.EXTRA_DEVICE) + } +}