[Audiosharing] Handle sync, add source via qrcode.
Bug: 305620450 Test: manual Change-Id: I32c14607035d8f37f44186175657c42307780e7b
This commit is contained in:
@@ -35,21 +35,27 @@ import com.google.common.base.Strings;
|
||||
*/
|
||||
class AudioStreamPreference extends TwoTargetPreference {
|
||||
private boolean mIsConnected = false;
|
||||
private AudioStream mAudioStream;
|
||||
|
||||
/**
|
||||
* Update preference UI based on connection status
|
||||
*
|
||||
* @param isConnected Is this streams connected
|
||||
* @param isConnected Is this stream connected
|
||||
* @param summary Summary text
|
||||
* @param onPreferenceClickListener Click listener for the preference
|
||||
*/
|
||||
void setIsConnected(
|
||||
boolean isConnected, @Nullable OnPreferenceClickListener onPreferenceClickListener) {
|
||||
boolean isConnected,
|
||||
String summary,
|
||||
@Nullable OnPreferenceClickListener onPreferenceClickListener) {
|
||||
if (mIsConnected == isConnected
|
||||
&& getSummary() == summary
|
||||
&& getOnPreferenceClickListener() == onPreferenceClickListener) {
|
||||
// Nothing to update.
|
||||
return;
|
||||
}
|
||||
mIsConnected = isConnected;
|
||||
setSummary(isConnected ? "Listening now" : "");
|
||||
setSummary(summary);
|
||||
setOrder(isConnected ? 0 : 1);
|
||||
setOnPreferenceClickListener(onPreferenceClickListener);
|
||||
notifyChanged();
|
||||
@@ -60,6 +66,14 @@ class AudioStreamPreference extends TwoTargetPreference {
|
||||
setIcon(R.drawable.ic_bt_audio_sharing);
|
||||
}
|
||||
|
||||
void setAudioStreamState(AudioStreamsProgressCategoryController.AudioStreamState state) {
|
||||
mAudioStream.setState(state);
|
||||
}
|
||||
|
||||
AudioStreamsProgressCategoryController.AudioStreamState getAudioStreamState() {
|
||||
return mAudioStream.getState();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldHideSecondTarget() {
|
||||
return mIsConnected;
|
||||
@@ -71,19 +85,31 @@ class AudioStreamPreference extends TwoTargetPreference {
|
||||
}
|
||||
|
||||
static AudioStreamPreference fromMetadata(
|
||||
Context context, BluetoothLeBroadcastMetadata source) {
|
||||
Context context,
|
||||
BluetoothLeBroadcastMetadata source,
|
||||
AudioStreamsProgressCategoryController.AudioStreamState streamState) {
|
||||
AudioStreamPreference preference = new AudioStreamPreference(context, /* attrs= */ null);
|
||||
preference.setTitle(getBroadcastName(source));
|
||||
preference.setAudioStream(new AudioStream(source.getBroadcastId(), streamState));
|
||||
return preference;
|
||||
}
|
||||
|
||||
static AudioStreamPreference fromReceiveState(
|
||||
Context context, BluetoothLeBroadcastReceiveState state) {
|
||||
Context context,
|
||||
BluetoothLeBroadcastReceiveState receiveState,
|
||||
AudioStreamsProgressCategoryController.AudioStreamState streamState) {
|
||||
AudioStreamPreference preference = new AudioStreamPreference(context, /* attrs= */ null);
|
||||
preference.setTitle(getBroadcastName(state));
|
||||
preference.setTitle(getBroadcastName(receiveState));
|
||||
preference.setAudioStream(
|
||||
new AudioStream(
|
||||
receiveState.getSourceId(), receiveState.getBroadcastId(), streamState));
|
||||
return preference;
|
||||
}
|
||||
|
||||
private void setAudioStream(AudioStream audioStream) {
|
||||
mAudioStream = audioStream;
|
||||
}
|
||||
|
||||
private static String getBroadcastName(BluetoothLeBroadcastMetadata source) {
|
||||
return source.getSubgroups().stream()
|
||||
.map(s -> s.getContentMetadata().getProgramInfo())
|
||||
@@ -99,4 +125,43 @@ class AudioStreamPreference extends TwoTargetPreference {
|
||||
.findFirst()
|
||||
.orElse("Broadcast Id: " + state.getBroadcastId());
|
||||
}
|
||||
|
||||
private static final class AudioStream {
|
||||
private int mSourceId;
|
||||
private int mBroadcastId;
|
||||
private AudioStreamsProgressCategoryController.AudioStreamState mState;
|
||||
|
||||
private AudioStream(
|
||||
int broadcastId, AudioStreamsProgressCategoryController.AudioStreamState state) {
|
||||
mBroadcastId = broadcastId;
|
||||
mState = state;
|
||||
}
|
||||
|
||||
private AudioStream(
|
||||
int sourceId,
|
||||
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() {
|
||||
return mBroadcastId;
|
||||
}
|
||||
|
||||
private AudioStreamsProgressCategoryController.AudioStreamState getState() {
|
||||
return mState;
|
||||
}
|
||||
|
||||
private void setState(AudioStreamsProgressCategoryController.AudioStreamState state) {
|
||||
mState = state;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -34,7 +34,7 @@ import com.android.settingslib.bluetooth.BluetoothUtils;
|
||||
public class AudioStreamsDashboardFragment extends DashboardFragment {
|
||||
private static final String TAG = "AudioStreamsDashboardFrag";
|
||||
private static final boolean DEBUG = BluetoothUtils.D;
|
||||
private AudioStreamsScanQrCodeController mAudioStreamsScanQrCodeController;
|
||||
private AudioStreamsProgressCategoryController mAudioStreamsProgressCategoryController;
|
||||
|
||||
public AudioStreamsDashboardFragment() {
|
||||
super();
|
||||
@@ -69,8 +69,8 @@ public class AudioStreamsDashboardFragment extends DashboardFragment {
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
mAudioStreamsScanQrCodeController = use(AudioStreamsScanQrCodeController.class);
|
||||
mAudioStreamsScanQrCodeController.setFragment(this);
|
||||
use(AudioStreamsScanQrCodeController.class).setFragment(this);
|
||||
mAudioStreamsProgressCategoryController = use(AudioStreamsProgressCategoryController.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -103,11 +103,13 @@ public class AudioStreamsDashboardFragment extends DashboardFragment {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onActivityResult() broadcastId : " + source.getBroadcastId());
|
||||
}
|
||||
if (mAudioStreamsScanQrCodeController == null) {
|
||||
Log.w(TAG, "onActivityResult() AudioStreamsScanQrCodeController is null!");
|
||||
if (mAudioStreamsProgressCategoryController == null) {
|
||||
Log.w(
|
||||
TAG,
|
||||
"onActivityResult() AudioStreamsProgressCategoryController is null!");
|
||||
return;
|
||||
}
|
||||
mAudioStreamsScanQrCodeController.addSource(source);
|
||||
mAudioStreamsProgressCategoryController.setSourceFromQrCode(source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -109,13 +109,14 @@ class AudioStreamsHelper {
|
||||
}
|
||||
|
||||
/** Retrieves a list of all LE broadcast receive states from active sinks. */
|
||||
List<BluetoothLeBroadcastReceiveState> getAllSources() {
|
||||
List<BluetoothLeBroadcastReceiveState> getAllConnectedSources() {
|
||||
if (mLeBroadcastAssistant == null) {
|
||||
Log.w(TAG, "getAllSources(): LeBroadcastAssistant is null!");
|
||||
return emptyList();
|
||||
}
|
||||
return getActiveSinksOnAssistant(mBluetoothManager).stream()
|
||||
.flatMap(sink -> mLeBroadcastAssistant.getAllSources(sink).stream())
|
||||
.filter(this::isConnected)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@@ -124,7 +125,7 @@ class AudioStreamsHelper {
|
||||
return mLeBroadcastAssistant;
|
||||
}
|
||||
|
||||
static boolean isConnected(BluetoothLeBroadcastReceiveState state) {
|
||||
boolean isConnected(BluetoothLeBroadcastReceiveState state) {
|
||||
return state.getPaSyncState() == BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED
|
||||
&& state.getBigEncryptionState()
|
||||
== BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING;
|
||||
|
@@ -25,6 +25,7 @@ import android.bluetooth.BluetoothLeBroadcastReceiveState;
|
||||
import android.bluetooth.BluetoothProfile;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.os.CountDownTimer;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -71,6 +72,17 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
|
||||
}
|
||||
};
|
||||
|
||||
enum AudioStreamState {
|
||||
// When mTimedSourceFromQrCode is present and this source has not been synced.
|
||||
WAIT_FOR_SYNC,
|
||||
// When source has been synced but not added to any sink.
|
||||
SYNCED,
|
||||
// When addSource is called for this source and waiting for response.
|
||||
WAIT_FOR_SOURCE_ADD,
|
||||
// Source is added to active sink.
|
||||
SOURCE_ADDED,
|
||||
}
|
||||
|
||||
private final Executor mExecutor;
|
||||
private final AudioStreamsBroadcastAssistantCallback mBroadcastAssistantCallback;
|
||||
private final AudioStreamsHelper mAudioStreamsHelper;
|
||||
@@ -78,6 +90,7 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
|
||||
private final @Nullable LocalBluetoothManager mBluetoothManager;
|
||||
private final ConcurrentHashMap<Integer, AudioStreamPreference> mBroadcastIdToPreferenceMap =
|
||||
new ConcurrentHashMap<>();
|
||||
private TimedSourceFromQrCode mTimedSourceFromQrCode;
|
||||
private AudioStreamsProgressCategoryPreference mCategoryPreference;
|
||||
|
||||
public AudioStreamsProgressCategoryController(Context context, String preferenceKey) {
|
||||
@@ -122,6 +135,12 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
|
||||
mExecutor.execute(this::stopScanning);
|
||||
}
|
||||
|
||||
void setSourceFromQrCode(BluetoothLeBroadcastMetadata source) {
|
||||
mTimedSourceFromQrCode =
|
||||
new TimedSourceFromQrCode(
|
||||
mContext, source, () -> handleSourceLost(source.getBroadcastId()));
|
||||
}
|
||||
|
||||
void setScanning(boolean isScanning) {
|
||||
ThreadUtils.postOnMainThread(
|
||||
() -> {
|
||||
@@ -140,24 +159,90 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
|
||||
}
|
||||
if (source.isEncrypted()) {
|
||||
ThreadUtils.postOnMainThread(
|
||||
() -> launchPasswordDialog(source, preference));
|
||||
() ->
|
||||
launchPasswordDialog(
|
||||
source, (AudioStreamPreference) preference));
|
||||
} else {
|
||||
mAudioStreamsHelper.addSource(source);
|
||||
((AudioStreamPreference) preference)
|
||||
.setAudioStreamState(AudioStreamState.WAIT_FOR_SOURCE_ADD);
|
||||
updatePreferenceConnectionState(
|
||||
(AudioStreamPreference) preference,
|
||||
AudioStreamState.WAIT_FOR_SOURCE_ADD,
|
||||
null);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
mBroadcastIdToPreferenceMap.computeIfAbsent(
|
||||
source.getBroadcastId(),
|
||||
k -> {
|
||||
var preference = AudioStreamPreference.fromMetadata(mContext, source);
|
||||
ThreadUtils.postOnMainThread(
|
||||
() -> {
|
||||
preference.setIsConnected(false, addSourceOrShowDialog);
|
||||
if (mCategoryPreference != null) {
|
||||
mCategoryPreference.addPreference(preference);
|
||||
|
||||
var broadcastIdFound = source.getBroadcastId();
|
||||
mBroadcastIdToPreferenceMap.compute(
|
||||
broadcastIdFound,
|
||||
(k, v) -> {
|
||||
if (v == null) {
|
||||
return addNewPreference(
|
||||
source, AudioStreamState.SYNCED, addSourceOrShowDialog);
|
||||
}
|
||||
var fromState = v.getAudioStreamState();
|
||||
if (fromState == AudioStreamState.WAIT_FOR_SYNC) {
|
||||
var pendingSource = mTimedSourceFromQrCode.get();
|
||||
if (pendingSource == null) {
|
||||
Log.w(
|
||||
TAG,
|
||||
"handleSourceFound(): unexpected state with null pendingSource:"
|
||||
+ fromState
|
||||
+ " for broadcastId : "
|
||||
+ broadcastIdFound);
|
||||
v.setAudioStreamState(AudioStreamState.SYNCED);
|
||||
return v;
|
||||
}
|
||||
mAudioStreamsHelper.addSource(pendingSource);
|
||||
mTimedSourceFromQrCode.consumed();
|
||||
v.setAudioStreamState(AudioStreamState.WAIT_FOR_SOURCE_ADD);
|
||||
updatePreferenceConnectionState(
|
||||
v, AudioStreamState.WAIT_FOR_SOURCE_ADD, null);
|
||||
} else {
|
||||
if (fromState != AudioStreamState.SOURCE_ADDED) {
|
||||
Log.w(
|
||||
TAG,
|
||||
"handleSourceFound(): unexpected state : "
|
||||
+ fromState
|
||||
+ " for broadcastId : "
|
||||
+ broadcastIdFound);
|
||||
}
|
||||
}
|
||||
return v;
|
||||
});
|
||||
return preference;
|
||||
}
|
||||
|
||||
private void handleSourceFromQrCodeIfExists() {
|
||||
if (mTimedSourceFromQrCode == null || mTimedSourceFromQrCode.get() == null) {
|
||||
return;
|
||||
}
|
||||
var metadataFromQrCode = mTimedSourceFromQrCode.get();
|
||||
mBroadcastIdToPreferenceMap.compute(
|
||||
metadataFromQrCode.getBroadcastId(),
|
||||
(k, v) -> {
|
||||
if (v == null) {
|
||||
mTimedSourceFromQrCode.waitForConsume();
|
||||
return addNewPreference(
|
||||
metadataFromQrCode, AudioStreamState.WAIT_FOR_SYNC, null);
|
||||
}
|
||||
var fromState = v.getAudioStreamState();
|
||||
if (fromState == AudioStreamState.SYNCED) {
|
||||
mAudioStreamsHelper.addSource(metadataFromQrCode);
|
||||
mTimedSourceFromQrCode.consumed();
|
||||
v.setAudioStreamState(AudioStreamState.WAIT_FOR_SOURCE_ADD);
|
||||
updatePreferenceConnectionState(
|
||||
v, AudioStreamState.WAIT_FOR_SOURCE_ADD, null);
|
||||
} else {
|
||||
Log.w(
|
||||
TAG,
|
||||
"handleSourceFromQrCode(): unexpected state : "
|
||||
+ fromState
|
||||
+ " for broadcastId : "
|
||||
+ metadataFromQrCode.getBroadcastId());
|
||||
}
|
||||
return v;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -174,30 +259,52 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
|
||||
mAudioStreamsHelper.removeSource(broadcastId);
|
||||
}
|
||||
|
||||
void handleSourceConnected(BluetoothLeBroadcastReceiveState state) {
|
||||
if (!AudioStreamsHelper.isConnected(state)) {
|
||||
void handleSourceConnected(BluetoothLeBroadcastReceiveState receiveState) {
|
||||
if (!mAudioStreamsHelper.isConnected(receiveState)) {
|
||||
return;
|
||||
}
|
||||
var sourceAddedState = AudioStreamState.SOURCE_ADDED;
|
||||
var broadcastIdConnected = receiveState.getBroadcastId();
|
||||
mBroadcastIdToPreferenceMap.compute(
|
||||
state.getBroadcastId(),
|
||||
broadcastIdConnected,
|
||||
(k, v) -> {
|
||||
// True if this source has been added either by scanning, or it's currently
|
||||
// connected to another active sink.
|
||||
boolean existed = v != null;
|
||||
AudioStreamPreference preference =
|
||||
existed ? v : AudioStreamPreference.fromReceiveState(mContext, state);
|
||||
|
||||
ThreadUtils.postOnMainThread(
|
||||
() -> {
|
||||
preference.setIsConnected(
|
||||
true, p -> launchDetailFragment(state.getBroadcastId()));
|
||||
if (mCategoryPreference != null && !existed) {
|
||||
mCategoryPreference.addPreference(preference);
|
||||
if (v == null) {
|
||||
return addNewPreference(
|
||||
receiveState,
|
||||
sourceAddedState,
|
||||
p -> launchDetailFragment(broadcastIdConnected));
|
||||
}
|
||||
var fromState = v.getAudioStreamState();
|
||||
if (fromState == AudioStreamState.WAIT_FOR_SOURCE_ADD
|
||||
|| fromState == AudioStreamState.SYNCED
|
||||
|| fromState == AudioStreamState.WAIT_FOR_SYNC) {
|
||||
if (mTimedSourceFromQrCode != null) {
|
||||
mTimedSourceFromQrCode.consumed();
|
||||
}
|
||||
} else {
|
||||
if (fromState != AudioStreamState.SOURCE_ADDED) {
|
||||
Log.w(
|
||||
TAG,
|
||||
"handleSourceConnected(): unexpected state : "
|
||||
+ fromState
|
||||
+ " for broadcastId : "
|
||||
+ broadcastIdConnected);
|
||||
}
|
||||
}
|
||||
v.setAudioStreamState(sourceAddedState);
|
||||
updatePreferenceConnectionState(
|
||||
v, sourceAddedState, p -> launchDetailFragment(broadcastIdConnected));
|
||||
return v;
|
||||
});
|
||||
}
|
||||
|
||||
return preference;
|
||||
});
|
||||
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) {
|
||||
@@ -235,13 +342,15 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
|
||||
mLeBroadcastAssistant.registerServiceCallBack(mExecutor, mBroadcastAssistantCallback);
|
||||
mLeBroadcastAssistant.startSearchingForSources(emptyList());
|
||||
|
||||
// Display currently connected streams
|
||||
// Handle QR code scan and display currently connected streams
|
||||
var unused =
|
||||
ThreadUtils.postOnBackgroundThread(
|
||||
() ->
|
||||
() -> {
|
||||
handleSourceFromQrCodeIfExists();
|
||||
mAudioStreamsHelper
|
||||
.getAllSources()
|
||||
.forEach(this::handleSourceConnected));
|
||||
.getAllConnectedSources()
|
||||
.forEach(this::handleSourceConnected);
|
||||
});
|
||||
}
|
||||
|
||||
private void stopScanning() {
|
||||
@@ -256,6 +365,43 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
|
||||
mLeBroadcastAssistant.stopSearchingForSources();
|
||||
}
|
||||
mLeBroadcastAssistant.unregisterServiceCallBack(mBroadcastAssistantCallback);
|
||||
if (mTimedSourceFromQrCode != null) {
|
||||
mTimedSourceFromQrCode.consumed();
|
||||
}
|
||||
}
|
||||
|
||||
private AudioStreamPreference addNewPreference(
|
||||
BluetoothLeBroadcastReceiveState receiveState,
|
||||
AudioStreamState state,
|
||||
Preference.OnPreferenceClickListener onClickListener) {
|
||||
var preference = AudioStreamPreference.fromReceiveState(mContext, receiveState, state);
|
||||
updatePreferenceConnectionState(preference, state, onClickListener);
|
||||
return preference;
|
||||
}
|
||||
|
||||
private AudioStreamPreference addNewPreference(
|
||||
BluetoothLeBroadcastMetadata metadata,
|
||||
AudioStreamState state,
|
||||
Preference.OnPreferenceClickListener onClickListener) {
|
||||
var preference = AudioStreamPreference.fromMetadata(mContext, metadata, state);
|
||||
updatePreferenceConnectionState(preference, state, onClickListener);
|
||||
return preference;
|
||||
}
|
||||
|
||||
private void updatePreferenceConnectionState(
|
||||
AudioStreamPreference preference,
|
||||
AudioStreamState state,
|
||||
Preference.OnPreferenceClickListener onClickListener) {
|
||||
ThreadUtils.postOnMainThread(
|
||||
() -> {
|
||||
preference.setIsConnected(
|
||||
state == AudioStreamState.SOURCE_ADDED,
|
||||
getPreferenceSummary(state),
|
||||
onClickListener);
|
||||
if (mCategoryPreference != null) {
|
||||
mCategoryPreference.addPreference(preference);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean launchDetailFragment(int broadcastId) {
|
||||
@@ -282,7 +428,8 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
|
||||
return true;
|
||||
}
|
||||
|
||||
private void launchPasswordDialog(BluetoothLeBroadcastMetadata source, Preference preference) {
|
||||
private void launchPasswordDialog(
|
||||
BluetoothLeBroadcastMetadata source, AudioStreamPreference preference) {
|
||||
View layout =
|
||||
LayoutInflater.from(mContext)
|
||||
.inflate(R.layout.bluetooth_find_broadcast_password_dialog, null);
|
||||
@@ -307,8 +454,49 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
|
||||
.setBroadcastCode(
|
||||
code.getBytes(StandardCharsets.UTF_8))
|
||||
.build());
|
||||
preference.setAudioStreamState(
|
||||
AudioStreamState.WAIT_FOR_SOURCE_ADD);
|
||||
updatePreferenceConnectionState(
|
||||
preference, AudioStreamState.WAIT_FOR_SOURCE_ADD, null);
|
||||
})
|
||||
.create();
|
||||
alertDialog.show();
|
||||
}
|
||||
|
||||
private static class TimedSourceFromQrCode {
|
||||
private static final int WAIT_FOR_SYNC_TIMEOUT_MILLIS = 15000;
|
||||
private final CountDownTimer mTimer;
|
||||
private BluetoothLeBroadcastMetadata mSourceFromQrCode;
|
||||
|
||||
private TimedSourceFromQrCode(
|
||||
Context context,
|
||||
BluetoothLeBroadcastMetadata sourceFromQrCode,
|
||||
Runnable timeoutAction) {
|
||||
mSourceFromQrCode = sourceFromQrCode;
|
||||
mTimer =
|
||||
new CountDownTimer(WAIT_FOR_SYNC_TIMEOUT_MILLIS, 1000) {
|
||||
@Override
|
||||
public void onTick(long millisUntilFinished) {}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
timeoutAction.run();
|
||||
AudioSharingUtils.toastMessage(context, "Audio steam isn't available");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void waitForConsume() {
|
||||
mTimer.start();
|
||||
}
|
||||
|
||||
private void consumed() {
|
||||
mTimer.cancel();
|
||||
mSourceFromQrCode = null;
|
||||
}
|
||||
|
||||
private BluetoothLeBroadcastMetadata get() {
|
||||
return mSourceFromQrCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user