Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e070b6b5 authored by chelseahao's avatar chelseahao
Browse files

Create new state `SourceLostState`.

Previously when a source is lost, we remove the preference directly from the controller. Now we set the state to `SourceLostState` and let the `AudioStreamStateHandler` handle the state transition and preference removal. This is to get ready for adding target scanning functionality.

Flag: com.android.settingslib.flags.enable_le_audio_sharing
Test: atest
Bug: 395978182
Change-Id: I69c6eb1255682f66215626db21c64981f4ca6581
parent 2dd4d6af
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -74,6 +74,10 @@ class AudioStreamStateHandler {
        // Update UI
        ThreadUtils.postOnMainThread(
                () -> {
                    if (shouldRemovePreference()) {
                        controller.removePreference(preference);
                        return;
                    }
                    String summary =
                            getSummary() != EMPTY_STRING_RES
                                    ? preference.getContext().getString(getSummary())
@@ -134,6 +138,13 @@ class AudioStreamStateHandler {
        return null;
    }

    /**
     * Indicates if the preference should be removed from the screen when the state is reached.
     */
    boolean shouldRemovePreference() {
        return false;
    }

    /** Subclasses should always override. */
    AudioStreamsProgressCategoryController.AudioStreamState getStateEnum() {
        return AudioStreamsProgressCategoryController.AudioStreamState.UNKNOWN;
+24 −9
Original line number Diff line number Diff line
@@ -140,6 +140,8 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
        SOURCE_PRESENT,
        // Source is added to active sink.
        SOURCE_ADDED,
        // Source is no longer synced or wait for sync timed out
        SOURCE_LOST,
    }

    @VisibleForTesting Executor mExecutor;
@@ -374,16 +376,15 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
                    "handleSourceLost() : keep this preference as the source is still connected.");
            return;
        }
        var toRemove = mBroadcastIdToPreferenceMap.remove(broadcastId);
        if (toRemove != null) {
            ThreadUtils.postOnMainThread(
                    () -> {
                        if (mCategoryPreference != null) {
                            mCategoryPreference.removePreference(toRemove);
        mBroadcastIdToPreferenceMap.compute(
                broadcastId,
                (k, existingPreference) -> {
                    if (existingPreference != null) {
                        moveToState(existingPreference, AudioStreamState.SOURCE_LOST);
                    }
                    return null;
                });
    }
    }

    @Override
    public void handleSourceRemoved() {
@@ -567,6 +568,19 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
                });
    }

    void removePreference(AudioStreamPreference preference) {
        if (DEBUG) {
            Log.d(TAG, "removePreference()");
        }
        mBroadcastIdToPreferenceMap.remove(preference.getAudioStreamBroadcastId());
        ThreadUtils.postOnMainThread(
                () -> {
                    if (mCategoryPreference != null) {
                        mCategoryPreference.removePreference(preference);
                    }
                });
    }

    void showToast(String msg) {
        AudioSharingUtils.toastMessage(mContext, msg);
    }
@@ -730,6 +744,7 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
                    case ADD_SOURCE_FAILED -> AddSourceFailedState.getInstance();
                    case SOURCE_PRESENT -> SourcePresentState.getInstance();
                    case SOURCE_ADDED -> SourceAddedState.getInstance();
                    case SOURCE_LOST -> SourceLostState.getInstance();
                    default -> throw new IllegalArgumentException("Unsupported state: " + state);
                };

+44 −0
Original line number Diff line number Diff line
/*
 * 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;

import androidx.annotation.Nullable;

class SourceLostState extends AudioStreamStateHandler {
    @Nullable
    private static SourceLostState sInstance = null;

    SourceLostState() {
    }

    static SourceLostState getInstance() {
        if (sInstance == null) {
            sInstance = new SourceLostState();
        }
        return sInstance;
    }

    @Override
    AudioStreamsProgressCategoryController.AudioStreamState getStateEnum() {
        return AudioStreamsProgressCategoryController.AudioStreamState.SOURCE_LOST;
    }

    @Override
    boolean shouldRemovePreference() {
        return true;
    }
}
+17 −0
Original line number Diff line number Diff line
@@ -180,6 +180,23 @@ public class AudioStreamStateHandlerTest {
        verify(mPreference).setOnPreferenceClickListener(eq(listener));
    }

    @Test
    public void testHandleStateChange_shouldRemovePreference() {
        when(mHandler.getStateEnum())
                .thenReturn(
                        AudioStreamsProgressCategoryController.AudioStreamState
                                .SOURCE_LOST);
        when(mHandler.shouldRemovePreference()).thenReturn(true);
        when(mPreference.getAudioStreamState())
                .thenReturn(
                        AudioStreamsProgressCategoryController.AudioStreamState
                                .SYNCED);

        mHandler.handleStateChange(mPreference, mController, mHelper);

        verify(mController).removePreference(mPreference);
    }

    @Test
    public void testGetSummary() {
        int res = mHandler.getSummary();
+27 −22
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import static com.android.settings.connecteddevice.audiosharing.audiostreams.Aud
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsProgressCategoryController.AudioStreamState.ADD_SOURCE_FAILED;
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsProgressCategoryController.AudioStreamState.ADD_SOURCE_WAIT_FOR_RESPONSE;
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsProgressCategoryController.AudioStreamState.SOURCE_ADDED;
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsProgressCategoryController.AudioStreamState.SOURCE_LOST;
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsProgressCategoryController.AudioStreamState.SOURCE_PRESENT;
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsProgressCategoryController.AudioStreamState.SYNCED;
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsProgressCategoryController.AudioStreamState.WAIT_FOR_SYNC;
@@ -652,7 +653,7 @@ public class AudioStreamsProgressCategoryControllerTest {
    }

    @Test
    public void testHandleSourceLost_removed() {
    public void testHandleSourceLost_updateMetadataAndState() {
        // Setup a device
        ShadowAudioStreamsHelper.setCachedBluetoothDeviceInSharingOrLeConnected(mDevice);

@@ -668,25 +669,27 @@ public class AudioStreamsProgressCategoryControllerTest {
        mController.handleSourceLost(NEWLY_FOUND_BROADCAST_ID);
        shadowOf(Looper.getMainLooper()).idle();

        ArgumentCaptor<AudioStreamPreference> preferenceToAdd =
                ArgumentCaptor.forClass(AudioStreamPreference.class);
        ArgumentCaptor<AudioStreamPreference> preferenceToRemove =
        ArgumentCaptor<AudioStreamPreference> preference =
                ArgumentCaptor.forClass(AudioStreamPreference.class);
        ArgumentCaptor<AudioStreamsProgressCategoryController.AudioStreamState> state =
                ArgumentCaptor.forClass(
                        AudioStreamsProgressCategoryController.AudioStreamState.class);

        verify(mController, times(2)).moveToState(preference.capture(), state.capture());
        List<AudioStreamPreference> preferences = preference.getAllValues();
        assertThat(preferences.size()).isEqualTo(2);
        List<AudioStreamsProgressCategoryController.AudioStreamState> states = state.getAllValues();
        assertThat(states.size()).isEqualTo(2);

        // Verify a new preference is created with state SYNCED.
        verify(mController).moveToState(preferenceToAdd.capture(), state.capture());
        assertThat(preferenceToAdd.getValue()).isNotNull();
        assertThat(preferenceToAdd.getValue().getAudioStreamBroadcastId())
        assertThat(preferences.get(0).getAudioStreamBroadcastId())
                .isEqualTo(NEWLY_FOUND_BROADCAST_ID);
        assertThat(state.getValue()).isEqualTo(SYNCED);
        assertThat(states.get(0)).isEqualTo(SYNCED);

        // Verify the preference with NEWLY_FOUND_BROADCAST_ID is removed.
        verify(mPreference).removePreference(preferenceToRemove.capture());
        assertThat(preferenceToRemove.getValue().getAudioStreamBroadcastId())
        // Verify the new source is updated to state SOURCE_LOST
        assertThat(preferences.get(1).getAudioStreamBroadcastId())
                .isEqualTo(NEWLY_FOUND_BROADCAST_ID);
        assertThat(states.get(1)).isEqualTo(SOURCE_LOST);
    }

    @Test
@@ -746,25 +749,27 @@ public class AudioStreamsProgressCategoryControllerTest {
        mController.handleSourceRemoved();
        shadowOf(Looper.getMainLooper()).idle();

        ArgumentCaptor<AudioStreamPreference> preferenceToAdd =
                ArgumentCaptor.forClass(AudioStreamPreference.class);
        ArgumentCaptor<AudioStreamPreference> preferenceToRemove =
        ArgumentCaptor<AudioStreamPreference> preference =
                ArgumentCaptor.forClass(AudioStreamPreference.class);
        ArgumentCaptor<AudioStreamsProgressCategoryController.AudioStreamState> state =
                ArgumentCaptor.forClass(
                        AudioStreamsProgressCategoryController.AudioStreamState.class);

        // Verify a new preference is created with state SOURCE_ADDED.
        verify(mController).moveToState(preferenceToAdd.capture(), state.capture());
        assertThat(preferenceToAdd.getValue()).isNotNull();
        assertThat(preferenceToAdd.getValue().getAudioStreamBroadcastId())
        verify(mController, times(2)).moveToState(preference.capture(), state.capture());
        List<AudioStreamPreference> preferences = preference.getAllValues();
        assertThat(preferences.size()).isEqualTo(2);
        List<AudioStreamsProgressCategoryController.AudioStreamState> states = state.getAllValues();
        assertThat(states.size()).isEqualTo(2);

        // Verify the connected preference is created with state SOURCE_ADDED.
        assertThat(preferences.get(0).getAudioStreamBroadcastId())
                .isEqualTo(ALREADY_CONNECTED_BROADCAST_ID);
        assertThat(state.getValue()).isEqualTo(SOURCE_ADDED);
        assertThat(states.get(0)).isEqualTo(SOURCE_ADDED);

        // Verify the preference with ALREADY_CONNECTED_BROADCAST_ID is removed.
        verify(mPreference).removePreference(preferenceToRemove.capture());
        assertThat(preferenceToRemove.getValue().getAudioStreamBroadcastId())
        // Verify the connected preference is updated to state SOURCE_LOST
        assertThat(preferences.get(1).getAudioStreamBroadcastId())
                .isEqualTo(ALREADY_CONNECTED_BROADCAST_ID);
        assertThat(states.get(1)).isEqualTo(SOURCE_LOST);
    }

    @Test
Loading