[Audiosharing] Handle source remove plus small refactor.

Bug: 308368124
Test: manual
Change-Id: I99011feb762445e75652cbe59c2653dced7dd4f7
This commit is contained in:
chelseahao
2024-01-19 15:43:17 +08:00
parent 8b5da73e4d
commit a6680963fa
4 changed files with 254 additions and 184 deletions

View File

@@ -71,10 +71,23 @@ class AudioStreamPreference extends TwoTargetPreference {
mAudioStream.setState(state); mAudioStream.setState(state);
} }
void setAudioStreamMetadata(BluetoothLeBroadcastMetadata metadata) {
mAudioStream.setMetadata(metadata);
}
int getAudioStreamBroadcastId() {
return mAudioStream.getBroadcastId();
}
int getAudioStreamRssi() { int getAudioStreamRssi() {
return mAudioStream.getRssi(); return mAudioStream.getRssi();
} }
@Nullable
BluetoothLeBroadcastMetadata getAudioStreamMetadata() {
return mAudioStream.getMetadata();
}
AudioStreamsProgressCategoryController.AudioStreamState getAudioStreamState() { AudioStreamsProgressCategoryController.AudioStreamState getAudioStreamState() {
return mAudioStream.getState(); return mAudioStream.getState();
} }
@@ -102,25 +115,18 @@ class AudioStreamPreference extends TwoTargetPreference {
} }
static AudioStreamPreference fromMetadata( static AudioStreamPreference fromMetadata(
Context context, Context context, BluetoothLeBroadcastMetadata source) {
BluetoothLeBroadcastMetadata source,
AudioStreamsProgressCategoryController.AudioStreamState streamState) {
AudioStreamPreference preference = new AudioStreamPreference(context, /* attrs= */ null); AudioStreamPreference preference = new AudioStreamPreference(context, /* attrs= */ null);
preference.setTitle(getBroadcastName(source)); preference.setTitle(getBroadcastName(source));
preference.setAudioStream( preference.setAudioStream(new AudioStream(source));
new AudioStream(source.getBroadcastId(), streamState, source.getRssi()));
return preference; return preference;
} }
static AudioStreamPreference fromReceiveState( static AudioStreamPreference fromReceiveState(
Context context, Context context, BluetoothLeBroadcastReceiveState receiveState) {
BluetoothLeBroadcastReceiveState receiveState,
AudioStreamsProgressCategoryController.AudioStreamState streamState) {
AudioStreamPreference preference = new AudioStreamPreference(context, /* attrs= */ null); AudioStreamPreference preference = new AudioStreamPreference(context, /* attrs= */ null);
preference.setTitle(getBroadcastName(receiveState)); preference.setTitle(getBroadcastName(receiveState));
preference.setAudioStream( preference.setAudioStream(new AudioStream(receiveState));
new AudioStream(
receiveState.getSourceId(), receiveState.getBroadcastId(), streamState));
return preference; return preference;
} }
@@ -145,49 +151,45 @@ class AudioStreamPreference extends TwoTargetPreference {
} }
private static final class AudioStream { private static final class AudioStream {
private int mSourceId; private static final int UNAVAILABLE = -1;
private int mBroadcastId; @Nullable private BluetoothLeBroadcastMetadata mMetadata;
private int mRssi = Integer.MIN_VALUE; @Nullable private BluetoothLeBroadcastReceiveState mReceiveState;
private AudioStreamsProgressCategoryController.AudioStreamState mState; private AudioStreamsProgressCategoryController.AudioStreamState mState =
AudioStreamsProgressCategoryController.AudioStreamState.UNKNOWN;
private AudioStream( private AudioStream(BluetoothLeBroadcastMetadata metadata) {
int broadcastId, mMetadata = metadata;
AudioStreamsProgressCategoryController.AudioStreamState state,
int rssi) {
mBroadcastId = broadcastId;
mState = state;
mRssi = rssi;
} }
private AudioStream( private AudioStream(BluetoothLeBroadcastReceiveState receiveState) {
int sourceId, mReceiveState = receiveState;
int broadcastId,
AudioStreamsProgressCategoryController.AudioStreamState state) {
mSourceId = sourceId;
mBroadcastId = broadcastId;
mState = state;
} }
// TODO(chelseahao): use this to handleSourceRemoved
private int getSourceId() {
return mSourceId;
}
// TODO(chelseahao): use this to handleSourceRemoved
private int getBroadcastId() { private int getBroadcastId() {
return mBroadcastId; return mMetadata != null
? mMetadata.getBroadcastId()
: mReceiveState != null ? mReceiveState.getBroadcastId() : UNAVAILABLE;
} }
private int getRssi() { private int getRssi() {
return mRssi; return mMetadata != null ? mMetadata.getRssi() : Integer.MAX_VALUE;
} }
private AudioStreamsProgressCategoryController.AudioStreamState getState() { private AudioStreamsProgressCategoryController.AudioStreamState getState() {
return mState; return mState;
} }
@Nullable
private BluetoothLeBroadcastMetadata getMetadata() {
return mMetadata;
}
private void setState(AudioStreamsProgressCategoryController.AudioStreamState state) { private void setState(AudioStreamsProgressCategoryController.AudioStreamState state) {
mState = state; mState = state;
} }
private void setMetadata(BluetoothLeBroadcastMetadata metadata) {
mMetadata = metadata;
}
} }
} }

View File

@@ -112,8 +112,6 @@ public class AudioStreamsProgressCategoryCallback extends AudioStreamsBroadcastA
@Override @Override
public void onSourceRemoved(BluetoothDevice sink, int sourceId, int reason) { public void onSourceRemoved(BluetoothDevice sink, int sourceId, int reason) {
super.onSourceRemoved(sink, sourceId, reason); super.onSourceRemoved(sink, sourceId, reason);
mCategoryController.showToast( mCategoryController.handleSourceRemoved();
String.format(
Locale.US, "Source %d removed for sink %s", sourceId, sink.getAddress()));
} }
} }

View File

@@ -16,6 +16,8 @@
package com.android.settings.connecteddevice.audiosharing.audiostreams; package com.android.settings.connecteddevice.audiosharing.audiostreams;
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsScanQrCodeController.REQUEST_SCAN_BT_BROADCAST_QR_CODE;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
import android.app.AlertDialog; import android.app.AlertDialog;
@@ -43,8 +45,10 @@ import androidx.preference.PreferenceScreen;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.bluetooth.Utils; import com.android.settings.bluetooth.Utils;
import com.android.settings.connecteddevice.audiosharing.AudioSharingUtils; import com.android.settings.connecteddevice.audiosharing.AudioSharingUtils;
import com.android.settings.connecteddevice.audiosharing.audiostreams.qrcode.QrCodeScanModeActivity;
import com.android.settings.core.BasePreferenceController; import com.android.settings.core.BasePreferenceController;
import com.android.settings.core.SubSettingLauncher; import com.android.settings.core.SubSettingLauncher;
import com.android.settingslib.bluetooth.BluetoothBroadcastUtils;
import com.android.settingslib.bluetooth.BluetoothCallback; import com.android.settingslib.bluetooth.BluetoothCallback;
import com.android.settingslib.bluetooth.BluetoothUtils; import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.CachedBluetoothDevice;
@@ -75,16 +79,56 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
} }
}; };
private final Preference.OnPreferenceClickListener mAddSourceOrShowDialog =
preference -> {
var p = (AudioStreamPreference) preference;
if (DEBUG) {
Log.d(
TAG,
"preferenceClicked(): attempt to join broadcast id : "
+ p.getAudioStreamBroadcastId());
}
var source = p.getAudioStreamMetadata();
if (source != null) {
if (source.isEncrypted()) {
ThreadUtils.postOnMainThread(() -> launchPasswordDialog(source, p));
} else {
moveToState(p, AudioStreamState.ADD_SOURCE_WAIT_FOR_RESPONSE);
}
}
return true;
};
private final Preference.OnPreferenceClickListener mLaunchDetailFragment =
preference -> {
var p = (AudioStreamPreference) preference;
Bundle broadcast = new Bundle();
broadcast.putString(
AudioStreamDetailsFragment.BROADCAST_NAME_ARG, (String) p.getTitle());
broadcast.putInt(
AudioStreamDetailsFragment.BROADCAST_ID_ARG, p.getAudioStreamBroadcastId());
new SubSettingLauncher(mContext)
.setTitleText("Audio stream details")
.setDestination(AudioStreamDetailsFragment.class.getName())
// TODO(chelseahao): Add logging enum
.setSourceMetricsCategory(SettingsEnums.PAGE_UNKNOWN)
.setArguments(broadcast)
.launch();
return true;
};
private final AudioStreamsRepository mAudioStreamsRepository = private final AudioStreamsRepository mAudioStreamsRepository =
AudioStreamsRepository.getInstance(); AudioStreamsRepository.getInstance();
enum AudioStreamState { enum AudioStreamState {
UNKNOWN,
// When mTimedSourceFromQrCode is present and this source has not been synced. // When mTimedSourceFromQrCode is present and this source has not been synced.
WAIT_FOR_SYNC, WAIT_FOR_SYNC,
// When source has been synced but not added to any sink. // When source has been synced but not added to any sink.
SYNCED, SYNCED,
// When addSource is called for this source and waiting for response. // When addSource is called for this source and waiting for response.
WAIT_FOR_SOURCE_ADD, ADD_SOURCE_WAIT_FOR_RESPONSE,
// Source is added to active sink. // Source is added to active sink.
SOURCE_ADDED, SOURCE_ADDED,
} }
@@ -105,7 +149,7 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
private final @Nullable LocalBluetoothManager mBluetoothManager; private final @Nullable LocalBluetoothManager mBluetoothManager;
private final ConcurrentHashMap<Integer, AudioStreamPreference> mBroadcastIdToPreferenceMap = private final ConcurrentHashMap<Integer, AudioStreamPreference> mBroadcastIdToPreferenceMap =
new ConcurrentHashMap<>(); new ConcurrentHashMap<>();
private TimedSourceFromQrCode mTimedSourceFromQrCode; private @Nullable TimedSourceFromQrCode mTimedSourceFromQrCode;
private AudioStreamsProgressCategoryPreference mCategoryPreference; private AudioStreamsProgressCategoryPreference mCategoryPreference;
private AudioStreamsDashboardFragment mFragment; private AudioStreamsDashboardFragment mFragment;
@@ -168,42 +212,18 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
} }
void handleSourceFound(BluetoothLeBroadcastMetadata source) { void handleSourceFound(BluetoothLeBroadcastMetadata source) {
Preference.OnPreferenceClickListener addSourceOrShowDialog =
preference -> {
if (DEBUG) {
Log.d(
TAG,
"preferenceClicked(): attempt to join broadcast id : "
+ source.getBroadcastId());
}
if (source.isEncrypted()) {
ThreadUtils.postOnMainThread(
() ->
launchPasswordDialog(
source, (AudioStreamPreference) preference));
} else {
mAudioStreamsHelper.addSource(source);
mAudioStreamsRepository.cacheMetadata(source);
((AudioStreamPreference) preference)
.setAudioStreamState(AudioStreamState.WAIT_FOR_SOURCE_ADD);
updatePreferenceConnectionState(
(AudioStreamPreference) preference,
AudioStreamState.WAIT_FOR_SOURCE_ADD,
null);
}
return true;
};
var broadcastIdFound = source.getBroadcastId(); var broadcastIdFound = source.getBroadcastId();
mBroadcastIdToPreferenceMap.compute( mBroadcastIdToPreferenceMap.compute(
broadcastIdFound, broadcastIdFound,
(k, v) -> { (k, v) -> {
if (v == null) { if (v == null) {
return addNewPreference( // No existing preference for this source founded, add one and set initial
source, AudioStreamState.SYNCED, addSourceOrShowDialog); // state to SYNCED.
return addNewPreference(source, AudioStreamState.SYNCED);
} }
var fromState = v.getAudioStreamState(); var fromState = v.getAudioStreamState();
if (fromState == AudioStreamState.WAIT_FOR_SYNC) { if (fromState == AudioStreamState.WAIT_FOR_SYNC
&& mTimedSourceFromQrCode != null) {
var pendingSource = mTimedSourceFromQrCode.get(); var pendingSource = mTimedSourceFromQrCode.get();
if (pendingSource == null) { if (pendingSource == null) {
Log.w( Log.w(
@@ -212,16 +232,20 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
+ fromState + fromState
+ " for broadcastId : " + " for broadcastId : "
+ broadcastIdFound); + broadcastIdFound);
v.setAudioStreamState(AudioStreamState.SYNCED); v.setAudioStreamMetadata(source);
moveToState(v, AudioStreamState.SYNCED);
return v; return v;
} }
mAudioStreamsHelper.addSource(pendingSource); // A preference with source founded is existed from a QR code scan. As the
mAudioStreamsRepository.cacheMetadata(pendingSource); // source is now synced, we update the preference with pendingSource from QR
mTimedSourceFromQrCode.consumed(); // code scan and add source with it (since it has the password).
v.setAudioStreamState(AudioStreamState.WAIT_FOR_SOURCE_ADD); v.setAudioStreamMetadata(pendingSource);
updatePreferenceConnectionState( moveToState(v, AudioStreamState.ADD_SOURCE_WAIT_FOR_RESPONSE);
v, AudioStreamState.WAIT_FOR_SOURCE_ADD, null);
} else { } else {
// A preference with source founded existed either because it's already
// connected (SOURCE_ADDED), or other unexpected reason. We update the
// preference with this source and won't change it's state.
v.setAudioStreamMetadata(source);
if (fromState != AudioStreamState.SOURCE_ADDED) { if (fromState != AudioStreamState.SOURCE_ADDED) {
Log.w( Log.w(
TAG, TAG,
@@ -244,19 +268,18 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
metadataFromQrCode.getBroadcastId(), metadataFromQrCode.getBroadcastId(),
(k, v) -> { (k, v) -> {
if (v == null) { if (v == null) {
mTimedSourceFromQrCode.waitForConsume(); // No existing preference for this source from the QR code scan, add one and
return addNewPreference( // set initial state to WAIT_FOR_SYNC.
metadataFromQrCode, AudioStreamState.WAIT_FOR_SYNC, null); return addNewPreference(metadataFromQrCode, AudioStreamState.WAIT_FOR_SYNC);
} }
var fromState = v.getAudioStreamState(); var fromState = v.getAudioStreamState();
if (fromState == AudioStreamState.SYNCED) { if (fromState == AudioStreamState.SYNCED) {
mAudioStreamsHelper.addSource(metadataFromQrCode); // A preference with source from the QR code is existed because it has been
mAudioStreamsRepository.cacheMetadata(metadataFromQrCode); // founded during scanning, now we have the password, we can add source.
mTimedSourceFromQrCode.consumed(); v.setAudioStreamMetadata(metadataFromQrCode);
v.setAudioStreamState(AudioStreamState.WAIT_FOR_SOURCE_ADD); moveToState(v, AudioStreamState.ADD_SOURCE_WAIT_FOR_RESPONSE);
updatePreferenceConnectionState(
v, AudioStreamState.WAIT_FOR_SOURCE_ADD, null);
} else { } else {
v.setAudioStreamMetadata(metadataFromQrCode);
Log.w( Log.w(
TAG, TAG,
"handleSourceFromQrCode(): unexpected state : " "handleSourceFromQrCode(): unexpected state : "
@@ -281,62 +304,69 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
mAudioStreamsHelper.removeSource(broadcastId); mAudioStreamsHelper.removeSource(broadcastId);
} }
void handleSourceRemoved() {
for (var entry : mBroadcastIdToPreferenceMap.entrySet()) {
var preference = entry.getValue();
// Look for preference has SOURCE_ADDED state, re-check if they are still connected. If
// not, means the source is removed from the sink, we move back the preference to SYNCED
// state.
if (preference.getAudioStreamState() == AudioStreamState.SOURCE_ADDED
&& mAudioStreamsHelper.getAllConnectedSources().stream()
.noneMatch(
connected ->
connected.getBroadcastId()
== preference.getAudioStreamBroadcastId())) {
ThreadUtils.postOnMainThread(
() -> {
var metadata = preference.getAudioStreamMetadata();
if (metadata != null) {
moveToState(preference, AudioStreamState.SYNCED);
} else {
handleSourceLost(preference.getAudioStreamBroadcastId());
}
});
return;
}
}
}
void handleSourceConnected(BluetoothLeBroadcastReceiveState receiveState) { void handleSourceConnected(BluetoothLeBroadcastReceiveState receiveState) {
if (!mAudioStreamsHelper.isConnected(receiveState)) { if (!mAudioStreamsHelper.isConnected(receiveState)) {
return; return;
} }
var sourceAddedState = AudioStreamState.SOURCE_ADDED;
var broadcastIdConnected = receiveState.getBroadcastId(); var broadcastIdConnected = receiveState.getBroadcastId();
mBroadcastIdToPreferenceMap.compute( mBroadcastIdToPreferenceMap.compute(
broadcastIdConnected, broadcastIdConnected,
(k, v) -> { (k, v) -> {
if (v == null) { if (v == null) {
return addNewPreference( // No existing preference for this source even if it's already connected,
receiveState, // add one and set initial state to SOURCE_ADDED. This could happen because
sourceAddedState, // we retrieves the connected source during onStart() from
p -> launchDetailFragment(broadcastIdConnected)); // AudioStreamsHelper#getAllConnectedSources() even before the source is
// founded by scanning.
return addNewPreference(receiveState, AudioStreamState.SOURCE_ADDED);
} }
var fromState = v.getAudioStreamState(); var fromState = v.getAudioStreamState();
if (fromState == AudioStreamState.WAIT_FOR_SOURCE_ADD if (fromState == AudioStreamState.ADD_SOURCE_WAIT_FOR_RESPONSE
|| fromState == AudioStreamState.SYNCED || fromState == AudioStreamState.SYNCED
|| fromState == AudioStreamState.WAIT_FOR_SYNC) { || fromState == AudioStreamState.WAIT_FOR_SYNC
if (mTimedSourceFromQrCode != null) { || fromState == AudioStreamState.SOURCE_ADDED) {
mTimedSourceFromQrCode.consumed(); // Expected state, do nothing
}
} else { } else {
if (fromState != AudioStreamState.SOURCE_ADDED) { Log.w(
Log.w( TAG,
TAG, "handleSourceConnected(): unexpected state : "
"handleSourceConnected(): unexpected state : " + fromState
+ fromState + " for broadcastId : "
+ " for broadcastId : " + broadcastIdConnected);
+ broadcastIdConnected);
}
} }
v.setAudioStreamState(sourceAddedState); moveToState(v, AudioStreamState.SOURCE_ADDED);
updatePreferenceConnectionState(
v, sourceAddedState, p -> launchDetailFragment(broadcastIdConnected));
return v; return v;
}); });
// Saved connected metadata for user to re-join this broadcast later.
var unused =
ThreadUtils.postOnBackgroundThread(
() -> {
var cached =
mAudioStreamsRepository.getCachedMetadata(broadcastIdConnected);
if (cached != null) {
mAudioStreamsRepository.saveMetadata(mContext, cached);
}
});
}
private static String getPreferenceSummary(AudioStreamState state) {
return switch (state) {
case WAIT_FOR_SYNC -> "Scanning...";
case WAIT_FOR_SOURCE_ADD -> "Connecting...";
case SOURCE_ADDED -> "Listening now";
default -> "";
};
} }
void showToast(String msg) { void showToast(String msg) {
@@ -374,7 +404,6 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
Log.d(TAG, "startScanning()"); Log.d(TAG, "startScanning()");
} }
mLeBroadcastAssistant.registerServiceCallBack(mExecutor, mBroadcastAssistantCallback); mLeBroadcastAssistant.registerServiceCallBack(mExecutor, mBroadcastAssistantCallback);
mLeBroadcastAssistant.startSearchingForSources(emptyList());
// Handle QR code scan and display currently connected streams // Handle QR code scan and display currently connected streams
var unused = var unused =
@@ -384,6 +413,7 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
mAudioStreamsHelper mAudioStreamsHelper
.getAllConnectedSources() .getAllConnectedSources()
.forEach(this::handleSourceConnected); .forEach(this::handleSourceConnected);
mLeBroadcastAssistant.startSearchingForSources(emptyList());
}); });
} }
@@ -400,68 +430,93 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
} }
mLeBroadcastAssistant.unregisterServiceCallBack(mBroadcastAssistantCallback); mLeBroadcastAssistant.unregisterServiceCallBack(mBroadcastAssistantCallback);
if (mTimedSourceFromQrCode != null) { if (mTimedSourceFromQrCode != null) {
mTimedSourceFromQrCode.consumed(); mTimedSourceFromQrCode.cleanup();
mTimedSourceFromQrCode = null;
} }
} }
private AudioStreamPreference addNewPreference( private AudioStreamPreference addNewPreference(
BluetoothLeBroadcastReceiveState receiveState, BluetoothLeBroadcastReceiveState receiveState, AudioStreamState state) {
AudioStreamState state, var preference = AudioStreamPreference.fromReceiveState(mContext, receiveState);
Preference.OnPreferenceClickListener onClickListener) { moveToState(preference, state);
var preference = AudioStreamPreference.fromReceiveState(mContext, receiveState, state);
updatePreferenceConnectionState(preference, state, onClickListener);
return preference; return preference;
} }
private AudioStreamPreference addNewPreference( private AudioStreamPreference addNewPreference(
BluetoothLeBroadcastMetadata metadata, BluetoothLeBroadcastMetadata metadata, AudioStreamState state) {
AudioStreamState state, var preference = AudioStreamPreference.fromMetadata(mContext, metadata);
Preference.OnPreferenceClickListener onClickListener) { moveToState(preference, state);
var preference = AudioStreamPreference.fromMetadata(mContext, metadata, state);
updatePreferenceConnectionState(preference, state, onClickListener);
return preference; return preference;
} }
private void updatePreferenceConnectionState( private void moveToState(AudioStreamPreference preference, AudioStreamState state) {
AudioStreamPreference preference, if (preference.getAudioStreamState() == state) {
AudioStreamState state, return;
Preference.OnPreferenceClickListener onClickListener) { }
preference.setAudioStreamState(state);
// Perform action according to the new state
if (state == AudioStreamState.ADD_SOURCE_WAIT_FOR_RESPONSE) {
if (mTimedSourceFromQrCode != null) {
mTimedSourceFromQrCode.consumed(preference.getAudioStreamBroadcastId());
}
var metadata = preference.getAudioStreamMetadata();
if (metadata != null) {
mAudioStreamsHelper.addSource(metadata);
// Cache the metadata that used for add source, if source is added successfully, we
// will save it persistently.
mAudioStreamsRepository.cacheMetadata(metadata);
}
} else if (state == AudioStreamState.SOURCE_ADDED) {
if (mTimedSourceFromQrCode != null) {
mTimedSourceFromQrCode.consumed(preference.getAudioStreamBroadcastId());
}
// Saved connected metadata for user to re-join this broadcast later.
var cached =
mAudioStreamsRepository.getCachedMetadata(
preference.getAudioStreamBroadcastId());
if (cached != null) {
mAudioStreamsRepository.saveMetadata(mContext, cached);
}
} else if (state == AudioStreamState.WAIT_FOR_SYNC) {
if (mTimedSourceFromQrCode != null) {
mTimedSourceFromQrCode.waitForConsume();
}
}
// Get preference click listener according to the new state
Preference.OnPreferenceClickListener listener;
if (state == AudioStreamState.SYNCED) {
listener = mAddSourceOrShowDialog;
} else if (state == AudioStreamState.SOURCE_ADDED) {
listener = mLaunchDetailFragment;
} else {
listener = null;
}
// Get preference summary according to the new state
String summary;
if (state == AudioStreamState.WAIT_FOR_SYNC) {
summary = "Scanning...";
} else if (state == AudioStreamState.ADD_SOURCE_WAIT_FOR_RESPONSE) {
summary = "Connecting...";
} else if (state == AudioStreamState.SOURCE_ADDED) {
summary = "Listening now";
} else {
summary = "";
}
// Update UI
ThreadUtils.postOnMainThread( ThreadUtils.postOnMainThread(
() -> { () -> {
preference.setIsConnected( preference.setIsConnected(
state == AudioStreamState.SOURCE_ADDED, state == AudioStreamState.SOURCE_ADDED, summary, listener);
getPreferenceSummary(state),
onClickListener);
if (mCategoryPreference != null) { if (mCategoryPreference != null) {
mCategoryPreference.addAudioStreamPreference(preference, mComparator); mCategoryPreference.addAudioStreamPreference(preference, mComparator);
} }
}); });
} }
private boolean launchDetailFragment(int broadcastId) {
if (!mBroadcastIdToPreferenceMap.containsKey(broadcastId)) {
Log.w(
TAG,
"launchDetailFragment(): broadcastId not exist in BroadcastIdToPreferenceMap!");
return false;
}
AudioStreamPreference preference = mBroadcastIdToPreferenceMap.get(broadcastId);
Bundle broadcast = new Bundle();
broadcast.putString(
AudioStreamDetailsFragment.BROADCAST_NAME_ARG, (String) preference.getTitle());
broadcast.putInt(AudioStreamDetailsFragment.BROADCAST_ID_ARG, broadcastId);
new SubSettingLauncher(mContext)
.setTitleText("Audio stream details")
.setDestination(AudioStreamDetailsFragment.class.getName())
// TODO(chelseahao): Add logging enum
.setSourceMetricsCategory(SettingsEnums.PAGE_UNKNOWN)
.setArguments(broadcast)
.launch();
return true;
}
private void launchPasswordDialog( private void launchPasswordDialog(
BluetoothLeBroadcastMetadata source, AudioStreamPreference preference) { BluetoothLeBroadcastMetadata source, AudioStreamPreference preference) {
View layout = View layout =
@@ -488,12 +543,11 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
.setBroadcastCode( .setBroadcastCode(
code.getBytes(StandardCharsets.UTF_8)) code.getBytes(StandardCharsets.UTF_8))
.build(); .build();
mAudioStreamsHelper.addSource(metadata); // Update the metadata after user entered the password
mAudioStreamsRepository.cacheMetadata(metadata); preference.setAudioStreamMetadata(metadata);
preference.setAudioStreamState( moveToState(
AudioStreamState.WAIT_FOR_SOURCE_ADD); preference,
updatePreferenceConnectionState( AudioStreamState.ADD_SOURCE_WAIT_FOR_RESPONSE);
preference, AudioStreamState.WAIT_FOR_SOURCE_ADD, null);
}) })
.create(); .create();
alertDialog.show(); alertDialog.show();
@@ -509,9 +563,10 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
.setLeftButtonOnClickListener(AlertDialog::dismiss) .setLeftButtonOnClickListener(AlertDialog::dismiss)
.setRightButtonText("Connect a device") .setRightButtonText("Connect a device")
.setRightButtonOnClickListener( .setRightButtonOnClickListener(
unused -> dialog -> {
mContext.startActivity( mContext.startActivity(new Intent(Settings.ACTION_BLUETOOTH_SETTINGS));
new Intent(Settings.ACTION_BLUETOOTH_SETTINGS))); dialog.dismiss();
});
} }
private AudioStreamsDialogFragment.DialogBuilder getBroadcastUnavailableDialog( private AudioStreamsDialogFragment.DialogBuilder getBroadcastUnavailableDialog(
@@ -523,8 +578,18 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
.setLeftButtonText("Close") .setLeftButtonText("Close")
.setLeftButtonOnClickListener(AlertDialog::dismiss) .setLeftButtonOnClickListener(AlertDialog::dismiss)
.setRightButtonText("Retry") .setRightButtonText("Retry")
// TODO(chelseahao): Add retry action .setRightButtonOnClickListener(
.setRightButtonOnClickListener(AlertDialog::dismiss); dialog -> {
if (mFragment != null) {
Intent intent = new Intent(mContext, QrCodeScanModeActivity.class);
intent.setAction(
BluetoothBroadcastUtils
.ACTION_BLUETOOTH_LE_AUDIO_QR_CODE_SCANNER);
mFragment.startActivityForResult(
intent, REQUEST_SCAN_BT_BROADCAST_QR_CODE);
dialog.dismiss();
}
});
} }
private class TimedSourceFromQrCode { private class TimedSourceFromQrCode {
@@ -557,11 +622,18 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
mTimer.start(); mTimer.start();
} }
private void consumed() { private void cleanup() {
mTimer.cancel(); mTimer.cancel();
mSourceFromQrCode = null; mSourceFromQrCode = null;
} }
private void consumed(int broadcastId) {
if (mSourceFromQrCode == null || broadcastId != mSourceFromQrCode.getBroadcastId()) {
return;
}
cleanup();
}
private BluetoothLeBroadcastMetadata get() { private BluetoothLeBroadcastMetadata get() {
return mSourceFromQrCode; return mSourceFromQrCode;
} }

View File

@@ -57,14 +57,12 @@ public class AudioStreamsScanQrCodeController extends BasePreferenceController
}; };
private final LocalBluetoothManager mLocalBtManager; private final LocalBluetoothManager mLocalBtManager;
private final AudioStreamsHelper mAudioStreamsHelper;
private AudioStreamsDashboardFragment mFragment; private AudioStreamsDashboardFragment mFragment;
private Preference mPreference; private Preference mPreference;
public AudioStreamsScanQrCodeController(Context context, String preferenceKey) { public AudioStreamsScanQrCodeController(Context context, String preferenceKey) {
super(context, preferenceKey); super(context, preferenceKey);
mLocalBtManager = Utils.getLocalBtManager(mContext); mLocalBtManager = Utils.getLocalBtManager(mContext);
mAudioStreamsHelper = new AudioStreamsHelper(mLocalBtManager);
} }
public void setFragment(AudioStreamsDashboardFragment fragment) { public void setFragment(AudioStreamsDashboardFragment fragment) {