mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2026-07-31 07:44:24 +02:00
Generic Audio BT Device Support
Add support for any Audio Bluetooth Device recognized by the OS. The main aim is to support incoming calls and notifications being read aloud. The device must be paired with Android OS and be recognized as audio device. Reviewed-on: https://codeberg.org/Freeyourgadget/Gadgetbridge/pulls/4675 Reviewed-by: Arjan Schrijver <arjan5@noreply.codeberg.org> Co-authored-by: Daniele Gobbetti <daniele+cb@gobbetti.name> Co-committed-by: Daniele Gobbetti <daniele+cb@gobbetti.name>
This commit is contained in:
committed by
daniele
parent
6753eba880
commit
0fefa5de6b
+81
@@ -0,0 +1,81 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.generic_headphones;
|
||||
|
||||
import android.bluetooth.BluetoothClass;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBException;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettings;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.DeviceSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.generic_headphones.GenericHeadphonesSupport;
|
||||
|
||||
public class GenericHeadphonesCoordinator extends AbstractDeviceCoordinator {
|
||||
@Override
|
||||
public int getOrderPriority() {
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBondingStyle() {
|
||||
return BONDING_STYLE_NONE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(GBDeviceCandidate candidate) {
|
||||
BluetoothDevice device = candidate.getDevice();
|
||||
BluetoothClass deviceClass = device.getBluetoothClass();
|
||||
int deviceType = deviceClass.getDeviceClass();
|
||||
return deviceType == BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET ||
|
||||
deviceType == BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES ||
|
||||
deviceType == BluetoothClass.Device.AUDIO_VIDEO_LOUDSPEAKER ||
|
||||
deviceType == BluetoothClass.Device.AUDIO_VIDEO_VIDEO_DISPLAY_AND_LOUDSPEAKER ||
|
||||
deviceType == BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO ||
|
||||
deviceType == BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getManufacturer() {
|
||||
return "generic";
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Class<? extends DeviceSupport> getDeviceSupportClass() {
|
||||
return GenericHeadphonesSupport.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDeviceNameResource() {
|
||||
return R.string.devicetype_unknown;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDefaultIconResource() {
|
||||
return R.drawable.ic_device_headphones;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDisabledIconResource() {
|
||||
return R.drawable.ic_device_headphones_disabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceSpecificSettings getDeviceSpecificSettings(final GBDevice device) {
|
||||
final DeviceSpecificSettings deviceSpecificSettings = new DeviceSpecificSettings();
|
||||
deviceSpecificSettings.addRootScreen(R.xml.devicesettings_headphones);
|
||||
return deviceSpecificSettings;
|
||||
}
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
/* Copyright (C) 2016-2024 Andreas Shimokawa, Daniel Dakhno, José Rebelo
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gadgetbridge 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.externalevents;
|
||||
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
|
||||
|
||||
public class BluetoothDisconnectReceiver extends BroadcastReceiver {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(BluetoothDisconnectReceiver.class);
|
||||
|
||||
public BluetoothDisconnectReceiver() {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onReceive(final Context context, final Intent intent) {
|
||||
final String action = intent.getAction();
|
||||
if (action == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED) || !intent.hasExtra(BluetoothDevice.EXTRA_DEVICE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
||||
if (device == null) {
|
||||
LOG.error("Got no device for {}", action);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG.info("disconnect detected from {}", device.getAddress());
|
||||
|
||||
final GBDevice gbDevice = GBApplication.app().getDeviceManager().getDeviceByAddress(device.getAddress());
|
||||
if (gbDevice == null) {
|
||||
LOG.info("Connected device {} unknown", device.getAddress());
|
||||
return;
|
||||
}
|
||||
GBApplication.deviceService(gbDevice).disconnect();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -124,6 +124,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.garmin.watches.vivomove.Garm
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.garmin.watches.vivomove.GarminVivomoveTrendCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.garmin.watches.vivosmart.GarminVivosmart5Coordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.garmin.watches.vivosport.GarminVivosportCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.generic_headphones.GenericHeadphonesCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.gree.GreeAcCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.hama.fit6900.HamaFit6900DeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.hplus.EXRIZUK8Coordinator;
|
||||
@@ -628,6 +629,7 @@ public enum DeviceType {
|
||||
CYCLING_SENSOR(CyclingSensorCoordinator.class),
|
||||
BLE_GATT_CLIENT(BleGattClientCoordinator.class),
|
||||
MARSTEK_B2500(MarstekB2500DeviceCoordinator.class),
|
||||
GENERIC_HEADPHONES(GenericHeadphonesCoordinator.class),
|
||||
TEST(TestDeviceCoordinator.class),
|
||||
ULTRAHUMAN_RING_AIR(UltrahumanDeviceCoordinator.class);
|
||||
|
||||
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.generic_headphones;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothProfile;
|
||||
import android.content.Context;
|
||||
import android.content.IntentFilter;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.externalevents.BluetoothDisconnectReceiver;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.AbstractDeviceSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.HeadphoneHelper;
|
||||
|
||||
public class GenericHeadphonesSupport extends AbstractDeviceSupport implements HeadphoneHelper.Callback {
|
||||
private HeadphoneHelper headphoneHelper;
|
||||
private BluetoothDisconnectReceiver mBlueToothDisconnectReceiver = null;
|
||||
|
||||
private final BluetoothProfile.ServiceListener profileListener = new BluetoothProfile.ServiceListener() {
|
||||
@Override
|
||||
public void onServiceConnected(int profile, BluetoothProfile proxy) {
|
||||
gbDevice.setState(GBDevice.State.INITIALIZED);
|
||||
gbDevice.sendDeviceUpdateIntent(getContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(int profile) {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onSetCallState(CallSpec callSpec) {
|
||||
headphoneHelper.onSetCallState(callSpec);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNotification(NotificationSpec notificationSpec) {
|
||||
headphoneHelper.onNotification(notificationSpec);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContext(GBDevice gbDevice, BluetoothAdapter btAdapter, Context context) {
|
||||
super.setContext(gbDevice, btAdapter, context);
|
||||
headphoneHelper = new HeadphoneHelper(getContext(), getDevice(), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSendConfiguration(String config) {
|
||||
if (!headphoneHelper.onSendConfiguration(config))
|
||||
super.onSendConfiguration(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
if (headphoneHelper != null) {
|
||||
headphoneHelper.dispose();
|
||||
headphoneHelper = null;
|
||||
}
|
||||
if (mBlueToothDisconnectReceiver != null) {
|
||||
getContext().unregisterReceiver(mBlueToothDisconnectReceiver);
|
||||
mBlueToothDisconnectReceiver = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean connect() {
|
||||
if (isConnected()) {
|
||||
return false;
|
||||
}
|
||||
gbDevice.setState(GBDevice.State.CONNECTING);
|
||||
gbDevice.sendDeviceUpdateIntent(getContext(), GBDevice.DeviceUpdateSubject.CONNECTION_STATE);
|
||||
|
||||
getBluetoothAdapter().getProfileProxy(getContext(), profileListener, BluetoothProfile.HEADSET);
|
||||
|
||||
mBlueToothDisconnectReceiver = new BluetoothDisconnectReceiver();
|
||||
ContextCompat.registerReceiver(getContext(), mBlueToothDisconnectReceiver, new IntentFilter(BluetoothDevice.ACTION_ACL_DISCONNECTED), ContextCompat.RECEIVER_EXPORTED);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useAutoConnect() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user