blob: 84ec93f9abc59d50157cbf1793001c3cf5b6e07a [file] [log] [blame]
/******************************************************************************
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
package com.android.settings.bluetooth;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothDeviceGroup;
import android.content.Context;
import android.util.Log;
import android.view.View;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceScreen;
import java.util.ArrayList;
import java.util.List;
import com.android.settings.R;
import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment;
import com.android.settings.widget.GroupOptionsPreference;
import com.android.settingslib.bluetooth.BluetoothCallback;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.core.lifecycle.Lifecycle;
/**
* This class adds Group action buttons: one to connect/disconnect from a device
* (depending on the current connected state of all devices in this group ), and
* one to "forget" (ie unpair) the device and one for to refresh devices.
*/
public class GroupBluetoothDetailsButtonsController extends GroupBluetoothDetailsController {
private static final boolean DBG = ConnectedDeviceDashboardFragment.DBG_GROUP;
private static String TAG = "GroupBluetoothDetailsButtonsController";
private static final String KEY_GROUP_OPTIONS = "group_options";
private boolean mConnectButtonInitialized;
private GroupOptionsPreference mGroupOptions;
private boolean mIsUpdate = false;
private int mGroupId;
private GroupUtils mGroupUtils;
private int mGroupSize = -1;
private int mDiscoveredSize = 0;
private boolean isRefreshClicked = false;
private ArrayList<CachedBluetoothDevice> mDevicesList = new ArrayList<CachedBluetoothDevice>();
public GroupBluetoothDetailsButtonsController(Context context,
PreferenceFragmentCompat fragment, int groupId, Lifecycle lifecycle) {
super(context, fragment, groupId, lifecycle);
mGroupId = groupId;
mGroupUtils = new GroupUtils(context);
mGroupSize = mGroupUtils.getGroupSize(mGroupId);
}
@Override
protected void init(PreferenceScreen screen) {
if (DBG) {
Log.d(TAG, "init ");
}
mGroupOptions = ((GroupOptionsPreference)
screen.findPreference(getPreferenceKey()));
mGroupOptions.setTextViewText(mContext.getString(R.string.group_id)
+ mGroupUtils.getGroupTitle(mGroupId));
mGroupOptions.setForgetButtonText(R.string.forget_group);
mGroupOptions.setForgetButtonOnClickListener((View) -> onForgetButtonPressed());
mGroupOptions.setForgetButtonEnabled(true);
mGroupOptions.setTexStatusText(R.string.active);
mGroupOptions.setConnectButtonText(R.string.connect_group);
mGroupOptions.setConnectButtonOnClickListener((View) -> onConnectButtonPressed());
mGroupOptions.setDisconnectButtonText(R.string.disconnect_group);
mGroupOptions.setDisconnectButtonOnClickListener((View) -> onDisConnectButtonPressed());
mGroupOptions.setCancelRefreshButtonText(R.string.cancel_refresh_group);
mGroupOptions.setCancelRefreshButtonOnClickListener(
(View) -> onCacelRefreshButtonPressed());
mGroupOptions.setCancelRefreshButtonVisible(false);
mGroupOptions.setRefreshButtonText(R.string.refresh_group);
mGroupOptions.setRefreshButtonOnClickListener((View) -> onRefreshButtonPressed());
mGroupOptions.setRefreshButtonVisible(false);
mIsUpdate = true;
BluetoothDevice bcMemberDevice = mGroupUtils.getAnyBCConnectedDevice(mGroupId);
boolean enableASButton = false;
if (bcMemberDevice != null) {
enableASButton = true;
}
mGroupOptions.setAddSourceGroupButtonText(R.string.add_source_group);
mGroupOptions.setAddSourceGroupButtonEnabled(enableASButton);
mGroupOptions.setAddSourceGroupButtonVisible(enableASButton);
if (enableASButton) {
mGroupOptions.setAddSourceGroupButtonOnClickListener((View) ->
onAddSourceGroupButtonPressed());
}
}
private void onAddSourceGroupButtonPressed() {
mGroupUtils.launchAddSourceGroup(mGroupId);
}
@Override
public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {
if (DBG) {
Log.d(TAG, "onConnectionStateChanged cachedDevice "+cachedDevice +" state "+state);
}
if (mGroupUtils.isUpdate(mGroupId, cachedDevice)) {
refresh();
}
}
@Override
public void onProfileConnectionStateChanged(CachedBluetoothDevice cachedDevice,
int state, int bluetoothProfile) {
if (DBG) {
Log.d(TAG, "onProfileConnectionStateChanged cachedDevice " + cachedDevice
+ " state " + state);
}
if (mGroupUtils.isUpdate(mGroupId, cachedDevice)) {
refresh();
}
}
@Override
public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {
boolean isUpdated = false;
if (bondState == BluetoothDevice.BOND_BONDED) {
isUpdated = mGroupUtils.addDevice(mDevicesList, mGroupId, cachedDevice);
} else if (bondState == BluetoothDevice.BOND_NONE) {
isUpdated = mGroupUtils.removeDevice(mDevicesList, mGroupId, cachedDevice);
}
if (isUpdated) {
mDiscoveredSize = mDevicesList.size();
}
if (DBG) {
Log.d(TAG, "onDeviceBondStateChanged cachedDevice " + cachedDevice
+ " name " + cachedDevice.getName() + " bondState " + bondState
+" isUpdated " + isUpdated + " mDiscoveredSize " + mDiscoveredSize);
}
if (isUpdated) {
updateProgressScan();
refresh();
}
}
@Override
public void onStop() {
if (DBG) {
Log.d(TAG, "onStop ");
}
super.onStop();
disableScanning();
}
@Override
public void onGroupDiscoveryStatusChanged (int groupId, int status, int reason) {
if (DBG) {
Log.d(TAG, "onSetDiscoveryStatusChanged " + groupId + " status :" + status
+ " Reason :" + reason);
}
if (groupId == mGroupId) {
if (isRefreshClicked && status == BluetoothDeviceGroup.GROUP_DISCOVERY_STOPPED ) {
isRefreshClicked = false;
}
updateProgressScan();
}
}
@Override
protected void loadDevices() {
mDevicesList = mGroupUtils.getCahcedDevice(mGroupId);
mDiscoveredSize = mDevicesList.size();
if (DBG) {
Log.d(TAG, "loadDevices mGroupId " + mGroupId + " mGroupSize " + mGroupSize
+ " mDiscoveredSize " + mDiscoveredSize);
}
updateProgressScan();
}
@Override
protected void refresh() {
boolean isBusy = false;
boolean showConnect = false;
boolean showDisconnect = false;
boolean isActive = false;
List<CachedBluetoothDevice> devicesList = new ArrayList<>(mDevicesList);
if (DBG) {
Log.d(TAG, "updateFlags list " + devicesList + " size " + devicesList.size());
}
for (CachedBluetoothDevice cacheDevice : devicesList) {
if (DBG) {
Log.d(TAG, "refresh cacheDevice " + cacheDevice + " connected "
+ cacheDevice.isConnected() +" busy " + cacheDevice.isBusy());
}
if (!isBusy && cacheDevice.isBusy()) {
isBusy = true;
mIsUpdate = true;
}
if (!showDisconnect && cacheDevice.isConnected()) {
showDisconnect = true;
mIsUpdate = true;
} else if (!showConnect && !cacheDevice.isConnected()) {
showConnect = true;
mIsUpdate = true;
} if((!isActive) && cacheDevice.isConnected()
&& (cacheDevice.isActiveDevice(BluetoothProfile.A2DP)
|| cacheDevice.isActiveDevice(BluetoothProfile.HEADSET)
|| cacheDevice.isActiveDevice(BluetoothProfile.HEARING_AID))) {
isActive = true;
}
}
if (DBG) {
Log.d(TAG, "refresh isBusy " + isBusy + " showConnect " + showConnect
+ " showDisconnect :" + showDisconnect +" isActive " + isActive
+ " mIsUpdate " + mIsUpdate);
}
if (!mIsUpdate) {
return;
}
mGroupOptions.setConnectButtonEnabled(!isBusy);
mGroupOptions.setDisconnectButtonEnabled(!isBusy);
mGroupOptions.setRefreshButtonEnabled(!isBusy);
mGroupOptions.setCancelRefreshButtonEnabled(!isBusy);
mGroupOptions.setDisconnectButtonEnabled(showDisconnect);
mGroupOptions.setDisconnectButtonVisible(showDisconnect);
mGroupOptions.setConnectButtonEnabled(showConnect);
mGroupOptions.setConnectButtonVisible(showConnect);
mGroupOptions.setTvStatusVisible(isActive);
mIsUpdate = false;
}
@Override
public String getPreferenceKey() {
return KEY_GROUP_OPTIONS;
}
private void onForgetButtonPressed() {
if (DBG) {
Log.d(TAG, "onForgetButtonPressed");
}
GroupForgetDialogFragment fragment = GroupForgetDialogFragment.newInstance(mGroupId);
fragment.show(mFragment.getFragmentManager(), GroupForgetDialogFragment.TAG);
}
private void onConnectButtonPressed() {
disableScanning();
boolean connect = mGroupUtils.connectGroup(mGroupId);
if (DBG) {
Log.d(TAG, "onConnectButtonPressed connect " + connect);
}
}
private void onDisConnectButtonPressed() {
disableScanning();
boolean disconnect = mGroupUtils.disconnectGroup(mGroupId);
if (DBG) {
Log.d(TAG, "onDisConnectButtonPressed disconnect " + disconnect);
}
}
private void onRefreshButtonPressed() {
isRefreshClicked = mGroupUtils.startGroupDiscovery(mGroupId);
if (DBG) {
Log.d(TAG, "onRefreshButtonPressed isRefreshClicked " + isRefreshClicked);
}
}
private void onCacelRefreshButtonPressed() {
isRefreshClicked = false;
boolean stopDiscovery = mGroupUtils.stopGroupDiscovery(mGroupId);
if (DBG) {
Log.d(TAG, "onCacelRefreshButtonPressed stopDiscovery " + stopDiscovery);
}
}
private void updateProgressScan() {
boolean showRefresh = false;
if ((mGroupSize >0) && (mGroupSize > mDiscoveredSize)) {
showRefresh = true;
}
boolean isRefreshing = mGroupUtils.isGroupDiscoveryInProgress(mGroupId);
if (showRefresh) {
if (isRefreshing) {
mGroupOptions.setProgressScanVisible(true);
mGroupOptions.setRefreshButtonVisible(false);
mGroupOptions.setCancelRefreshButtonVisible(true);
} else {
mGroupOptions.setProgressScanVisible(false);
mGroupOptions.setRefreshButtonVisible(true);
mGroupOptions.setCancelRefreshButtonVisible(false);
}
} else {
mGroupOptions.setProgressScanVisible(showRefresh);
mGroupOptions.setCancelRefreshButtonVisible(showRefresh);
mGroupOptions.setRefreshButtonVisible(showRefresh);
}
if (DBG) {
Log.d(TAG, "updateProgressScan showRefresh " + showRefresh
+ ", isRefreshing " + isRefreshing + " mDiscoveredSize " + mDiscoveredSize);
}
}
private void disableScanning () {
if (isRefreshClicked) {
mGroupUtils.stopGroupDiscovery(mGroupId);
}
}
}