[Audiosharing] Add logging 2.

Test: atest -c com.android.settings.connecteddevice.audiosharing.audiostreams
Bug: 308368124
Change-Id: I50fc57427aa135a13566c4627ba193aed9d73e0d
This commit is contained in:
chelseahao
2024-06-12 14:23:38 +08:00
committed by Chelsea Hao
parent f017332c91
commit a3b1638c4b
14 changed files with 261 additions and 103 deletions

View File

@@ -16,6 +16,8 @@
package com.android.settings.connecteddevice.audiosharing.audiostreams; package com.android.settings.connecteddevice.audiosharing.audiostreams;
import android.app.settings.SettingsEnums;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
@@ -37,6 +39,17 @@ class AddSourceBadCodeState extends SyncedState {
return sInstance; return sInstance;
} }
@Override
void performAction(
AudioStreamPreference preference,
AudioStreamsProgressCategoryController controller,
AudioStreamsHelper helper) {
mMetricsFeatureProvider.action(
preference.getContext(),
SettingsEnums.ACTION_AUDIO_STREAM_JOIN_FAILED_BAD_CODE,
preference.getSourceOriginForLogging().ordinal());
}
@Override @Override
int getSummary() { int getSummary() {
return AUDIO_STREAM_ADD_SOURCE_BAD_CODE_STATE_SUMMARY; return AUDIO_STREAM_ADD_SOURCE_BAD_CODE_STATE_SUMMARY;

View File

@@ -16,6 +16,8 @@
package com.android.settings.connecteddevice.audiosharing.audiostreams; package com.android.settings.connecteddevice.audiosharing.audiostreams;
import android.app.settings.SettingsEnums;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
@@ -37,6 +39,17 @@ class AddSourceFailedState extends SyncedState {
return sInstance; return sInstance;
} }
@Override
void performAction(
AudioStreamPreference preference,
AudioStreamsProgressCategoryController controller,
AudioStreamsHelper helper) {
mMetricsFeatureProvider.action(
preference.getContext(),
SettingsEnums.ACTION_AUDIO_STREAM_JOIN_FAILED_OTHER,
preference.getSourceOriginForLogging().ordinal());
}
@Override @Override
int getSummary() { int getSummary() {
return AUDIO_STREAM_ADD_SOURCE_FAILED_STATE_SUMMARY; return AUDIO_STREAM_ADD_SOURCE_FAILED_STATE_SUMMARY;

View File

@@ -17,6 +17,7 @@
package com.android.settings.connecteddevice.audiosharing.audiostreams; package com.android.settings.connecteddevice.audiosharing.audiostreams;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.settings.SettingsEnums;
import android.content.Context; import android.content.Context;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@@ -52,6 +53,10 @@ class AddSourceWaitForResponseState extends AudioStreamStateHandler {
var metadata = preference.getAudioStreamMetadata(); var metadata = preference.getAudioStreamMetadata();
if (metadata != null) { if (metadata != null) {
helper.addSource(metadata); helper.addSource(metadata);
mMetricsFeatureProvider.action(
preference.getContext(),
SettingsEnums.ACTION_AUDIO_STREAM_JOIN,
preference.getSourceOriginForLogging().ordinal());
// Cache the metadata that used for add source, if source is added successfully, we // Cache the metadata that used for add source, if source is added successfully, we
// will save it persistently. // will save it persistently.
mAudioStreamsRepository.cacheMetadata(metadata); mAudioStreamsRepository.cacheMetadata(metadata);
@@ -66,6 +71,10 @@ class AddSourceWaitForResponseState extends AudioStreamStateHandler {
&& preference.getAudioStreamState() == getStateEnum()) { && preference.getAudioStreamState() == getStateEnum()) {
controller.handleSourceFailedToConnect( controller.handleSourceFailedToConnect(
preference.getAudioStreamBroadcastId()); preference.getAudioStreamBroadcastId());
mMetricsFeatureProvider.action(
preference.getContext(),
SettingsEnums.ACTION_AUDIO_STREAM_JOIN_FAILED_TIMEOUT,
preference.getSourceOriginForLogging().ordinal());
ThreadUtils.postOnMainThread( ThreadUtils.postOnMainThread(
() -> { () -> {
if (controller.getFragment() != null) { if (controller.getFragment() != null) {

View File

@@ -16,6 +16,7 @@
package com.android.settings.connecteddevice.audiosharing.audiostreams; package com.android.settings.connecteddevice.audiosharing.audiostreams;
import android.app.settings.SettingsEnums;
import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeBroadcastAssistant; import android.bluetooth.BluetoothLeBroadcastAssistant;
import android.bluetooth.BluetoothLeBroadcastMetadata; import android.bluetooth.BluetoothLeBroadcastMetadata;
@@ -33,7 +34,9 @@ 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.core.BasePreferenceController; import com.android.settings.core.BasePreferenceController;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant; import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.utils.ThreadUtils; import com.android.settingslib.utils.ThreadUtils;
import com.android.settingslib.widget.ActionButtonsPreference; import com.android.settingslib.widget.ActionButtonsPreference;
@@ -44,6 +47,7 @@ public class AudioStreamButtonController extends BasePreferenceController
implements DefaultLifecycleObserver { implements DefaultLifecycleObserver {
private static final String TAG = "AudioStreamButtonController"; private static final String TAG = "AudioStreamButtonController";
private static final String KEY = "audio_stream_button"; private static final String KEY = "audio_stream_button";
private static final int SOURCE_ORIGIN_REPOSITORY = SourceOriginForLogging.REPOSITORY.ordinal();
private final BluetoothLeBroadcastAssistant.Callback mBroadcastAssistantCallback = private final BluetoothLeBroadcastAssistant.Callback mBroadcastAssistantCallback =
new AudioStreamsBroadcastAssistantCallback() { new AudioStreamsBroadcastAssistantCallback() {
@Override @Override
@@ -56,6 +60,8 @@ public class AudioStreamButtonController extends BasePreferenceController
public void onSourceRemoveFailed(BluetoothDevice sink, int sourceId, int reason) { public void onSourceRemoveFailed(BluetoothDevice sink, int sourceId, int reason) {
super.onSourceRemoveFailed(sink, sourceId, reason); super.onSourceRemoveFailed(sink, sourceId, reason);
updateButton(); updateButton();
mMetricsFeatureProvider.action(
mContext, SettingsEnums.ACTION_AUDIO_STREAM_LEAVE_FAILED);
} }
@Override @Override
@@ -66,6 +72,10 @@ public class AudioStreamButtonController extends BasePreferenceController
super.onReceiveStateChanged(sink, sourceId, state); super.onReceiveStateChanged(sink, sourceId, state);
if (AudioStreamsHelper.isConnected(state)) { if (AudioStreamsHelper.isConnected(state)) {
updateButton(); updateButton();
mMetricsFeatureProvider.action(
mContext,
SettingsEnums.ACTION_AUDIO_STREAM_JOIN_SUCCEED,
SOURCE_ORIGIN_REPOSITORY);
} }
} }
@@ -74,6 +84,10 @@ public class AudioStreamButtonController extends BasePreferenceController
BluetoothDevice sink, BluetoothLeBroadcastMetadata source, int reason) { BluetoothDevice sink, BluetoothLeBroadcastMetadata source, int reason) {
super.onSourceAddFailed(sink, source, reason); super.onSourceAddFailed(sink, source, reason);
updateButton(); updateButton();
mMetricsFeatureProvider.action(
mContext,
SettingsEnums.ACTION_AUDIO_STREAM_JOIN_FAILED_OTHER,
SOURCE_ORIGIN_REPOSITORY);
} }
@Override @Override
@@ -88,6 +102,7 @@ public class AudioStreamButtonController extends BasePreferenceController
private final Executor mExecutor; private final Executor mExecutor;
private final AudioStreamsHelper mAudioStreamsHelper; private final AudioStreamsHelper mAudioStreamsHelper;
private final @Nullable LocalBluetoothLeBroadcastAssistant mLeBroadcastAssistant; private final @Nullable LocalBluetoothLeBroadcastAssistant mLeBroadcastAssistant;
private final MetricsFeatureProvider mMetricsFeatureProvider;
private @Nullable ActionButtonsPreference mPreference; private @Nullable ActionButtonsPreference mPreference;
private int mBroadcastId = -1; private int mBroadcastId = -1;
@@ -96,6 +111,7 @@ public class AudioStreamButtonController extends BasePreferenceController
mExecutor = Executors.newSingleThreadExecutor(); mExecutor = Executors.newSingleThreadExecutor();
mAudioStreamsHelper = new AudioStreamsHelper(Utils.getLocalBtManager(context)); mAudioStreamsHelper = new AudioStreamsHelper(Utils.getLocalBtManager(context));
mLeBroadcastAssistant = mAudioStreamsHelper.getLeBroadcastAssistant(); mLeBroadcastAssistant = mAudioStreamsHelper.getLeBroadcastAssistant();
mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
} }
@Override @Override
@@ -124,59 +140,77 @@ public class AudioStreamButtonController extends BasePreferenceController
} }
private void updateButton() { private void updateButton() {
if (mPreference != null) { if (mPreference == null) {
if (mAudioStreamsHelper.getAllConnectedSources().stream() Log.w(TAG, "updateButton(): preference is null!");
.map(BluetoothLeBroadcastReceiveState::getBroadcastId) return;
.anyMatch(connectedBroadcastId -> connectedBroadcastId == mBroadcastId)) { }
ThreadUtils.postOnMainThread( boolean isConnected =
() -> { mAudioStreamsHelper.getAllConnectedSources().stream()
if (mPreference != null) { .map(BluetoothLeBroadcastReceiveState::getBroadcastId)
mPreference.setButton1Enabled(true); .anyMatch(connectedBroadcastId -> connectedBroadcastId == mBroadcastId);
mPreference
.setButton1Text(R.string.audio_streams_disconnect) View.OnClickListener onClickListener;
.setButton1Icon(
com.android.settings.R.drawable.ic_settings_close) if (isConnected) {
.setButton1OnClickListener( onClickListener =
unused -> { unused ->
ThreadUtils.postOnBackgroundThread(
() -> {
mAudioStreamsHelper.removeSource(mBroadcastId);
mMetricsFeatureProvider.action(
mContext,
SettingsEnums
.ACTION_AUDIO_STREAM_LEAVE_BUTTON_CLICK);
ThreadUtils.postOnMainThread(
() -> {
if (mPreference != null) { if (mPreference != null) {
mPreference.setButton1Enabled(false); mPreference.setButton1Enabled(false);
} }
mAudioStreamsHelper.removeSource(mBroadcastId);
}); });
} });
}); ThreadUtils.postOnMainThread(
} else { () -> {
View.OnClickListener clickToRejoin = if (mPreference != null) {
unused -> mPreference.setButton1Enabled(true);
ThreadUtils.postOnBackgroundThread( mPreference
() -> { .setButton1Text(R.string.audio_streams_disconnect)
var metadata = .setButton1Icon(
mAudioStreamsRepository.getSavedMetadata( com.android.settings.R.drawable.ic_settings_close)
mContext, mBroadcastId); .setButton1OnClickListener(onClickListener);
if (metadata != null) { }
mAudioStreamsHelper.addSource(metadata); });
ThreadUtils.postOnMainThread(
() -> {
if (mPreference != null) {
mPreference.setButton1Enabled(
false);
}
});
}
});
ThreadUtils.postOnMainThread(
() -> {
if (mPreference != null) {
mPreference.setButton1Enabled(true);
mPreference
.setButton1Text(R.string.audio_streams_connect)
.setButton1Icon(com.android.settings.R.drawable.ic_add_24dp)
.setButton1OnClickListener(clickToRejoin);
}
});
}
} else { } else {
Log.w(TAG, "updateButton(): preference is null!"); onClickListener =
unused ->
ThreadUtils.postOnBackgroundThread(
() -> {
var metadata =
mAudioStreamsRepository.getSavedMetadata(
mContext, mBroadcastId);
if (metadata != null) {
mAudioStreamsHelper.addSource(metadata);
mMetricsFeatureProvider.action(
mContext,
SettingsEnums.ACTION_AUDIO_STREAM_JOIN,
SOURCE_ORIGIN_REPOSITORY);
ThreadUtils.postOnMainThread(
() -> {
if (mPreference != null) {
mPreference.setButton1Enabled(false);
}
});
}
});
ThreadUtils.postOnMainThread(
() -> {
if (mPreference != null) {
mPreference.setButton1Enabled(true);
mPreference
.setButton1Text(R.string.audio_streams_connect)
.setButton1Icon(com.android.settings.R.drawable.ic_add_24dp)
.setButton1OnClickListener(onClickListener);
}
});
} }
} }

View File

@@ -107,6 +107,12 @@ class AudioStreamPreference extends TwoTargetPreference {
: AudioStreamsProgressCategoryController.AudioStreamState.UNKNOWN; : AudioStreamsProgressCategoryController.AudioStreamState.UNKNOWN;
} }
SourceOriginForLogging getSourceOriginForLogging() {
return mAudioStream != null
? mAudioStream.getSourceOriginForLogging()
: SourceOriginForLogging.UNKNOWN;
}
@Override @Override
protected boolean shouldHideSecondTarget() { protected boolean shouldHideSecondTarget() {
return mIsConnected || !mIsEncrypted; return mIsConnected || !mIsEncrypted;
@@ -130,11 +136,13 @@ class AudioStreamPreference extends TwoTargetPreference {
} }
static AudioStreamPreference fromMetadata( static AudioStreamPreference fromMetadata(
Context context, BluetoothLeBroadcastMetadata source) { Context context,
BluetoothLeBroadcastMetadata source,
SourceOriginForLogging sourceOriginForLogging) {
AudioStreamPreference preference = new AudioStreamPreference(context, /* attrs= */ null); AudioStreamPreference preference = new AudioStreamPreference(context, /* attrs= */ null);
preference.setIsEncrypted(source.isEncrypted()); preference.setIsEncrypted(source.isEncrypted());
preference.setTitle(AudioStreamsHelper.getBroadcastName(source)); preference.setTitle(AudioStreamsHelper.getBroadcastName(source));
preference.setAudioStream(new AudioStream(source)); preference.setAudioStream(new AudioStream(source, sourceOriginForLogging));
return preference; return preference;
} }
@@ -158,11 +166,15 @@ class AudioStreamPreference extends TwoTargetPreference {
private static final int UNAVAILABLE = -1; private static final int UNAVAILABLE = -1;
@Nullable private BluetoothLeBroadcastMetadata mMetadata; @Nullable private BluetoothLeBroadcastMetadata mMetadata;
@Nullable private BluetoothLeBroadcastReceiveState mReceiveState; @Nullable private BluetoothLeBroadcastReceiveState mReceiveState;
private SourceOriginForLogging mSourceOriginForLogging = SourceOriginForLogging.UNKNOWN;
private AudioStreamsProgressCategoryController.AudioStreamState mState = private AudioStreamsProgressCategoryController.AudioStreamState mState =
AudioStreamsProgressCategoryController.AudioStreamState.UNKNOWN; AudioStreamsProgressCategoryController.AudioStreamState.UNKNOWN;
private AudioStream(BluetoothLeBroadcastMetadata metadata) { private AudioStream(
BluetoothLeBroadcastMetadata metadata,
SourceOriginForLogging sourceOriginForLogging) {
mMetadata = metadata; mMetadata = metadata;
mSourceOriginForLogging = sourceOriginForLogging;
} }
private AudioStream(BluetoothLeBroadcastReceiveState receiveState) { private AudioStream(BluetoothLeBroadcastReceiveState receiveState) {
@@ -191,6 +203,10 @@ class AudioStreamPreference extends TwoTargetPreference {
return mState; return mState;
} }
private SourceOriginForLogging getSourceOriginForLogging() {
return mSourceOriginForLogging;
}
@Nullable @Nullable
private BluetoothLeBroadcastMetadata getMetadata() { private BluetoothLeBroadcastMetadata getMetadata() {
return mMetadata; return mMetadata;

View File

@@ -25,7 +25,9 @@ import androidx.annotation.StringRes;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference; import androidx.preference.Preference;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.bluetooth.BluetoothUtils; import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.utils.ThreadUtils; import com.android.settingslib.utils.ThreadUtils;
class AudioStreamStateHandler { class AudioStreamStateHandler {
@@ -35,6 +37,8 @@ class AudioStreamStateHandler {
final AudioStreamsRepository mAudioStreamsRepository = AudioStreamsRepository.getInstance(); final AudioStreamsRepository mAudioStreamsRepository = AudioStreamsRepository.getInstance();
final Handler mHandler = new Handler(Looper.getMainLooper()); final Handler mHandler = new Handler(Looper.getMainLooper());
final MetricsFeatureProvider mMetricsFeatureProvider =
FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
AudioStreamStateHandler() {} AudioStreamStateHandler() {}

View File

@@ -19,6 +19,7 @@ package com.android.settings.connecteddevice.audiosharing.audiostreams;
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsScanQrCodeController.REQUEST_SCAN_BT_BROADCAST_QR_CODE; import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsScanQrCodeController.REQUEST_SCAN_BT_BROADCAST_QR_CODE;
import android.app.Activity; import android.app.Activity;
import android.app.settings.SettingsEnums;
import android.bluetooth.BluetoothLeBroadcastMetadata; import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
@@ -32,8 +33,6 @@ import com.android.settings.dashboard.DashboardFragment;
import com.android.settingslib.bluetooth.BluetoothLeBroadcastMetadataExt; import com.android.settingslib.bluetooth.BluetoothLeBroadcastMetadataExt;
import com.android.settingslib.bluetooth.BluetoothUtils; import com.android.settingslib.bluetooth.BluetoothUtils;
import com.google.common.base.Strings;
public class AudioStreamsDashboardFragment extends DashboardFragment { public class AudioStreamsDashboardFragment extends DashboardFragment {
public static final String KEY_BROADCAST_METADATA = "key_broadcast_metadata"; public static final String KEY_BROADCAST_METADATA = "key_broadcast_metadata";
private static final String TAG = "AudioStreamsDashboardFrag"; private static final String TAG = "AudioStreamsDashboardFrag";
@@ -46,8 +45,7 @@ public class AudioStreamsDashboardFragment extends DashboardFragment {
@Override @Override
public int getMetricsCategory() { public int getMetricsCategory() {
// TODO: update category id. return SettingsEnums.AUDIO_STREAM_MAIN;
return 0;
} }
@Override @Override
@@ -78,16 +76,17 @@ public class AudioStreamsDashboardFragment extends DashboardFragment {
mAudioStreamsProgressCategoryController.setFragment(this); mAudioStreamsProgressCategoryController.setFragment(this);
if (getArguments() != null) { if (getArguments() != null) {
String broadcastMetadataStr = getArguments().getString(KEY_BROADCAST_METADATA); var broadcastMetadata =
if (!Strings.isNullOrEmpty(broadcastMetadataStr)) { getArguments()
BluetoothLeBroadcastMetadata broadcastMetadata = .getParcelable(
BluetoothLeBroadcastMetadataExt.INSTANCE.convertToBroadcastMetadata( KEY_BROADCAST_METADATA, BluetoothLeBroadcastMetadata.class);
broadcastMetadataStr); if (broadcastMetadata != null) {
if (broadcastMetadata == null) { mAudioStreamsProgressCategoryController.setSourceFromQrCode(
Log.w(TAG, "onAttach() broadcastMetadata is null!"); broadcastMetadata, SourceOriginForLogging.QR_CODE_SCAN_OTHER);
} else { mMetricsFeatureProvider.action(
mAudioStreamsProgressCategoryController.setSourceFromQrCode(broadcastMetadata); getContext(),
} SettingsEnums.ACTION_AUDIO_STREAM_QR_CODE_SCAN_SUCCEED,
SourceOriginForLogging.QR_CODE_SCAN_OTHER.ordinal());
} }
} }
} }
@@ -128,7 +127,12 @@ public class AudioStreamsDashboardFragment extends DashboardFragment {
"onActivityResult() AudioStreamsProgressCategoryController is null!"); "onActivityResult() AudioStreamsProgressCategoryController is null!");
return; return;
} }
mAudioStreamsProgressCategoryController.setSourceFromQrCode(source); mAudioStreamsProgressCategoryController.setSourceFromQrCode(
source, SourceOriginForLogging.QR_CODE_SCAN_SETTINGS);
mMetricsFeatureProvider.action(
getContext(),
SettingsEnums.ACTION_AUDIO_STREAM_QR_CODE_SCAN_SUCCEED,
SourceOriginForLogging.QR_CODE_SCAN_SETTINGS.ordinal());
} }
} }
} }

View File

@@ -19,12 +19,11 @@ package com.android.settings.connecteddevice.audiosharing.audiostreams;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.settings.SettingsEnums;
import android.bluetooth.BluetoothLeBroadcastMetadata; import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.bluetooth.BluetoothLeBroadcastReceiveState; import android.bluetooth.BluetoothLeBroadcastReceiveState;
import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProfile;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.provider.Settings;
import android.util.Log; import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@@ -34,8 +33,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.ConnectedDeviceDashboardFragment;
import com.android.settings.connecteddevice.audiosharing.AudioSharingUtils; import com.android.settings.connecteddevice.audiosharing.AudioSharingUtils;
import com.android.settings.core.BasePreferenceController; import com.android.settings.core.BasePreferenceController;
import com.android.settings.core.SubSettingLauncher;
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;
@@ -100,6 +101,7 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
private final ConcurrentHashMap<Integer, AudioStreamPreference> mBroadcastIdToPreferenceMap = private final ConcurrentHashMap<Integer, AudioStreamPreference> mBroadcastIdToPreferenceMap =
new ConcurrentHashMap<>(); new ConcurrentHashMap<>();
private @Nullable BluetoothLeBroadcastMetadata mSourceFromQrCode; private @Nullable BluetoothLeBroadcastMetadata mSourceFromQrCode;
private SourceOriginForLogging mSourceFromQrCodeOriginForLogging;
@Nullable private AudioStreamsProgressCategoryPreference mCategoryPreference; @Nullable private AudioStreamsProgressCategoryPreference mCategoryPreference;
@Nullable private AudioStreamsDashboardFragment mFragment; @Nullable private AudioStreamsDashboardFragment mFragment;
@@ -149,11 +151,13 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
return mFragment; return mFragment;
} }
void setSourceFromQrCode(BluetoothLeBroadcastMetadata source) { void setSourceFromQrCode(
BluetoothLeBroadcastMetadata source, SourceOriginForLogging sourceOriginForLogging) {
if (DEBUG) { if (DEBUG) {
Log.d(TAG, "setSourceFromQrCode(): broadcastId " + source.getBroadcastId()); Log.d(TAG, "setSourceFromQrCode(): broadcastId " + source.getBroadcastId());
} }
mSourceFromQrCode = source; mSourceFromQrCode = source;
mSourceFromQrCodeOriginForLogging = sourceOriginForLogging;
} }
void setScanning(boolean isScanning) { void setScanning(boolean isScanning) {
@@ -196,7 +200,10 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
broadcastIdFound, broadcastIdFound,
(k, existingPreference) -> { (k, existingPreference) -> {
if (existingPreference == null) { if (existingPreference == null) {
return addNewPreference(source, AudioStreamState.SYNCED); return addNewPreference(
source,
AudioStreamState.SYNCED,
SourceOriginForLogging.BROADCAST_SEARCH);
} }
var fromState = existingPreference.getAudioStreamState(); var fromState = existingPreference.getAudioStreamState();
if (fromState == AudioStreamState.WAIT_FOR_SYNC && mSourceFromQrCode != null) { if (fromState == AudioStreamState.WAIT_FOR_SYNC && mSourceFromQrCode != null) {
@@ -268,7 +275,9 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
// Check nullability to bypass NullAway check. // Check nullability to bypass NullAway check.
if (mSourceFromQrCode != null) { if (mSourceFromQrCode != null) {
return addNewPreference( return addNewPreference(
mSourceFromQrCode, AudioStreamState.WAIT_FOR_SYNC); mSourceFromQrCode,
AudioStreamState.WAIT_FOR_SYNC,
mSourceFromQrCodeOriginForLogging);
} }
} }
Log.w( Log.w(
@@ -525,23 +534,27 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
} }
private AudioStreamPreference addNewPreference( private AudioStreamPreference addNewPreference(
BluetoothLeBroadcastMetadata metadata, AudioStreamState state) { BluetoothLeBroadcastMetadata metadata,
var preference = AudioStreamPreference.fromMetadata(mContext, metadata); AudioStreamState state,
SourceOriginForLogging sourceOriginForLogging) {
var preference =
AudioStreamPreference.fromMetadata(mContext, metadata, sourceOriginForLogging);
moveToState(preference, state); moveToState(preference, state);
return preference; return preference;
} }
private void moveToState(AudioStreamPreference preference, AudioStreamState state) { private void moveToState(AudioStreamPreference preference, AudioStreamState state) {
AudioStreamStateHandler stateHandler = switch (state) { AudioStreamStateHandler stateHandler =
case SYNCED -> SyncedState.getInstance(); switch (state) {
case WAIT_FOR_SYNC -> WaitForSyncState.getInstance(); case SYNCED -> SyncedState.getInstance();
case ADD_SOURCE_WAIT_FOR_RESPONSE -> case WAIT_FOR_SYNC -> WaitForSyncState.getInstance();
AddSourceWaitForResponseState.getInstance(); case ADD_SOURCE_WAIT_FOR_RESPONSE ->
case ADD_SOURCE_BAD_CODE -> AddSourceBadCodeState.getInstance(); AddSourceWaitForResponseState.getInstance();
case ADD_SOURCE_FAILED -> AddSourceFailedState.getInstance(); case ADD_SOURCE_BAD_CODE -> AddSourceBadCodeState.getInstance();
case SOURCE_ADDED -> SourceAddedState.getInstance(); case ADD_SOURCE_FAILED -> AddSourceFailedState.getInstance();
default -> throw new IllegalArgumentException("Unsupported state: " + state); case SOURCE_ADDED -> SourceAddedState.getInstance();
}; default -> throw new IllegalArgumentException("Unsupported state: " + state);
};
stateHandler.handleStateChange(preference, this, mAudioStreamsHelper); stateHandler.handleStateChange(preference, this, mAudioStreamsHelper);
@@ -566,9 +579,12 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
mContext.getString(R.string.audio_streams_dialog_no_le_device_button)) mContext.getString(R.string.audio_streams_dialog_no_le_device_button))
.setRightButtonOnClickListener( .setRightButtonOnClickListener(
dialog -> { dialog -> {
mContext.startActivity( new SubSettingLauncher(mContext)
new Intent(Settings.ACTION_BLUETOOTH_SETTINGS) .setDestination(
.setPackage(mContext.getPackageName())); ConnectedDeviceDashboardFragment.class.getName())
.setSourceMetricsCategory(
SettingsEnums.DIALOG_AUDIO_STREAM_MAIN_NO_LE_DEVICE)
.launch();
dialog.dismiss(); dialog.dismiss();
}); });
} }

View File

@@ -57,6 +57,10 @@ class SourceAddedState extends AudioStreamStateHandler {
context, context,
preference.getAudioStreamBroadcastId(), preference.getAudioStreamBroadcastId(),
String.valueOf(preference.getTitle())); String.valueOf(preference.getTitle()));
mMetricsFeatureProvider.action(
preference.getContext(),
SettingsEnums.ACTION_AUDIO_STREAM_JOIN_SUCCEED,
preference.getSourceOriginForLogging().ordinal());
} }
@Override @Override
@@ -79,8 +83,10 @@ class SourceAddedState extends AudioStreamStateHandler {
.setTitleText( .setTitleText(
p.getContext().getString(R.string.audio_streams_detail_page_title)) p.getContext().getString(R.string.audio_streams_detail_page_title))
.setDestination(AudioStreamDetailsFragment.class.getName()) .setDestination(AudioStreamDetailsFragment.class.getName())
// TODO(chelseahao): Add logging enum .setSourceMetricsCategory(
.setSourceMetricsCategory(SettingsEnums.PAGE_UNKNOWN) controller.getFragment() == null
? SettingsEnums.PAGE_UNKNOWN
: controller.getFragment().getMetricsCategory())
.setArguments(broadcast) .setArguments(broadcast)
.launch(); .launch();
return true; return true;

View File

@@ -0,0 +1,25 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.connecteddevice.audiosharing.audiostreams;
enum SourceOriginForLogging {
UNKNOWN,
QR_CODE_SCAN_SETTINGS,
QR_CODE_SCAN_OTHER,
BROADCAST_SEARCH,
REPOSITORY,
}

View File

@@ -24,6 +24,7 @@ import android.content.Context;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import androidx.fragment.app.Fragment;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.core.SubSettingLauncher; import com.android.settings.core.SubSettingLauncher;
@@ -59,6 +60,11 @@ class WaitForSyncState extends AudioStreamStateHandler {
if (preference.isShown() if (preference.isShown()
&& preference.getAudioStreamState() == getStateEnum()) { && preference.getAudioStreamState() == getStateEnum()) {
controller.handleSourceLost(preference.getAudioStreamBroadcastId()); controller.handleSourceLost(preference.getAudioStreamBroadcastId());
mMetricsFeatureProvider.action(
preference.getContext(),
SettingsEnums
.ACTION_AUDIO_STREAM_JOIN_FAILED_WAIT_FOR_SYNC_TIMEOUT,
preference.getSourceOriginForLogging().ordinal());
ThreadUtils.postOnMainThread( ThreadUtils.postOnMainThread(
() -> { () -> {
if (controller.getFragment() != null) { if (controller.getFragment() != null) {
@@ -101,18 +107,19 @@ class WaitForSyncState extends AudioStreamStateHandler {
.setRightButtonOnClickListener( .setRightButtonOnClickListener(
dialog -> { dialog -> {
if (controller.getFragment() != null) { if (controller.getFragment() != null) {
new SubSettingLauncher(context) launchQrCodeScanFragment(context, controller.getFragment());
.setTitleRes(
R.string.audio_streams_main_page_scan_qr_code_title)
.setDestination(
AudioStreamsQrCodeScanFragment.class.getName())
.setResultListener(
controller.getFragment(),
REQUEST_SCAN_BT_BROADCAST_QR_CODE)
.setSourceMetricsCategory(SettingsEnums.PAGE_UNKNOWN)
.launch();
dialog.dismiss(); dialog.dismiss();
} }
}); });
} }
private void launchQrCodeScanFragment(Context context, Fragment fragment) {
new SubSettingLauncher(context)
.setTitleRes(R.string.audio_streams_main_page_scan_qr_code_title)
.setDestination(AudioStreamsQrCodeScanFragment.class.getName())
.setResultListener(fragment, REQUEST_SCAN_BT_BROADCAST_QR_CODE)
.setSourceMetricsCategory(
SettingsEnums.DIALOG_AUDIO_STREAM_MAIN_WAIT_FOR_SYNC_TIMEOUT)
.launch();
}
} }

View File

@@ -92,6 +92,8 @@ public class AddSourceWaitForResponseStateTest {
@Test @Test
public void testPerformAction_metadataIsNotNull_addSource() { public void testPerformAction_metadataIsNotNull_addSource() {
when(mMockPreference.getAudioStreamMetadata()).thenReturn(mMockMetadata); when(mMockPreference.getAudioStreamMetadata()).thenReturn(mMockMetadata);
when(mMockPreference.getSourceOriginForLogging())
.thenReturn(SourceOriginForLogging.UNKNOWN);
mInstance.performAction(mMockPreference, mMockController, mMockHelper); mInstance.performAction(mMockPreference, mMockController, mMockHelper);
@@ -105,6 +107,8 @@ public class AddSourceWaitForResponseStateTest {
when(mMockPreference.isShown()).thenReturn(true); when(mMockPreference.isShown()).thenReturn(true);
when(mMockPreference.getAudioStreamState()).thenReturn(mInstance.getStateEnum()); when(mMockPreference.getAudioStreamState()).thenReturn(mInstance.getStateEnum());
when(mMockPreference.getAudioStreamBroadcastId()).thenReturn(BROADCAST_ID); when(mMockPreference.getAudioStreamBroadcastId()).thenReturn(BROADCAST_ID);
when(mMockPreference.getSourceOriginForLogging())
.thenReturn(SourceOriginForLogging.UNKNOWN);
mInstance.performAction(mMockPreference, mMockController, mMockHelper); mInstance.performAction(mMockPreference, mMockController, mMockHelper);
ShadowLooper.idleMainLooper(ADD_SOURCE_WAIT_FOR_RESPONSE_TIMEOUT_MILLIS, TimeUnit.SECONDS); ShadowLooper.idleMainLooper(ADD_SOURCE_WAIT_FOR_RESPONSE_TIMEOUT_MILLIS, TimeUnit.SECONDS);

View File

@@ -107,7 +107,8 @@ public class AudioStreamPreferenceTest {
@Test @Test
public void setAudioStreamMetadata_shouldUpdateMetadata() { public void setAudioStreamMetadata_shouldUpdateMetadata() {
AudioStreamPreference p = AudioStreamPreference p =
AudioStreamPreference.fromMetadata(mContext, mBluetoothLeBroadcastMetadata); AudioStreamPreference.fromMetadata(
mContext, mBluetoothLeBroadcastMetadata, SourceOriginForLogging.UNKNOWN);
BluetoothLeBroadcastMetadata metadata = mock(BluetoothLeBroadcastMetadata.class); BluetoothLeBroadcastMetadata metadata = mock(BluetoothLeBroadcastMetadata.class);
p.setAudioStreamMetadata(metadata); p.setAudioStreamMetadata(metadata);
@@ -117,7 +118,8 @@ public class AudioStreamPreferenceTest {
@Test @Test
public void setAudioStreamState_shouldUpdateState() { public void setAudioStreamState_shouldUpdateState() {
AudioStreamPreference p = AudioStreamPreference p =
AudioStreamPreference.fromMetadata(mContext, mBluetoothLeBroadcastMetadata); AudioStreamPreference.fromMetadata(
mContext, mBluetoothLeBroadcastMetadata, SourceOriginForLogging.UNKNOWN);
AudioStreamState state = AudioStreamState.SOURCE_ADDED; AudioStreamState state = AudioStreamState.SOURCE_ADDED;
p.setAudioStreamState(state); p.setAudioStreamState(state);
@@ -127,7 +129,8 @@ public class AudioStreamPreferenceTest {
@Test @Test
public void fromMetadata_shouldReturnBroadcastInfo() { public void fromMetadata_shouldReturnBroadcastInfo() {
AudioStreamPreference p = AudioStreamPreference p =
AudioStreamPreference.fromMetadata(mContext, mBluetoothLeBroadcastMetadata); AudioStreamPreference.fromMetadata(
mContext, mBluetoothLeBroadcastMetadata, SourceOriginForLogging.UNKNOWN);
assertThat(p.getAudioStreamBroadcastId()).isEqualTo(BROADCAST_ID); assertThat(p.getAudioStreamBroadcastId()).isEqualTo(BROADCAST_ID);
assertThat(p.getAudioStreamBroadcastName()).isEqualTo(BROADCAST_NAME); assertThat(p.getAudioStreamBroadcastName()).isEqualTo(BROADCAST_NAME);
assertThat(p.getAudioStreamRssi()).isEqualTo(BROADCAST_RSSI); assertThat(p.getAudioStreamRssi()).isEqualTo(BROADCAST_RSSI);
@@ -152,7 +155,8 @@ public class AudioStreamPreferenceTest {
public void shouldHideSecondTarget_notEncrypted() { public void shouldHideSecondTarget_notEncrypted() {
when(mBluetoothLeBroadcastMetadata.isEncrypted()).thenReturn(false); when(mBluetoothLeBroadcastMetadata.isEncrypted()).thenReturn(false);
AudioStreamPreference p = AudioStreamPreference p =
AudioStreamPreference.fromMetadata(mContext, mBluetoothLeBroadcastMetadata); AudioStreamPreference.fromMetadata(
mContext, mBluetoothLeBroadcastMetadata, SourceOriginForLogging.UNKNOWN);
assertThat(p.shouldHideSecondTarget()).isTrue(); assertThat(p.shouldHideSecondTarget()).isTrue();
} }
@@ -160,7 +164,8 @@ public class AudioStreamPreferenceTest {
public void shouldShowSecondTarget_encrypted() { public void shouldShowSecondTarget_encrypted() {
when(mBluetoothLeBroadcastMetadata.isEncrypted()).thenReturn(true); when(mBluetoothLeBroadcastMetadata.isEncrypted()).thenReturn(true);
AudioStreamPreference p = AudioStreamPreference p =
AudioStreamPreference.fromMetadata(mContext, mBluetoothLeBroadcastMetadata); AudioStreamPreference.fromMetadata(
mContext, mBluetoothLeBroadcastMetadata, SourceOriginForLogging.UNKNOWN);
assertThat(p.shouldHideSecondTarget()).isFalse(); assertThat(p.shouldHideSecondTarget()).isFalse();
} }

View File

@@ -93,6 +93,8 @@ public class WaitForSyncStateTest {
.thenReturn(AudioStreamsProgressCategoryController.AudioStreamState.WAIT_FOR_SYNC); .thenReturn(AudioStreamsProgressCategoryController.AudioStreamState.WAIT_FOR_SYNC);
when(mMockPreference.getAudioStreamBroadcastId()).thenReturn(1); when(mMockPreference.getAudioStreamBroadcastId()).thenReturn(1);
when(mMockPreference.getAudioStreamMetadata()).thenReturn(mMockMetadata); when(mMockPreference.getAudioStreamMetadata()).thenReturn(mMockMetadata);
when(mMockPreference.getSourceOriginForLogging())
.thenReturn(SourceOriginForLogging.UNKNOWN);
mInstance.performAction(mMockPreference, mMockController, mMockHelper); mInstance.performAction(mMockPreference, mMockController, mMockHelper);
ShadowLooper.idleMainLooper(WAIT_FOR_SYNC_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); ShadowLooper.idleMainLooper(WAIT_FOR_SYNC_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);