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

Commit acb6e89c authored by hughchen's avatar hughchen
Browse files

Update DialogFragment UI when BT device is add/remove/rename

* For fix the "Automatic merge failed" in pi-dev, cherry pick the ag/3937781 in
  master. Change android.support.* to androidx.*
* Add AudioSwitchCallback() in AudioSwitchPreferenceController.
  This callback is used to notify SoudSettings to update the dialogFragment UI.
* Add UpdatableListPreferenceDialogFragment that updates the available
  options when dialog is shown
* Add test to verify the adapter count when
  onListPreferenceUpdated() is called.

Bug: 77783217
Test: make -j50 RunSettingsRoboTests
Change-Id: I8cac1b30ec50df026f4b7722dd1cd2f69e77a4cb
parent 82e7aa89
Loading
Loading
Loading
Loading
+48 −2
Original line number Diff line number Diff line
@@ -25,17 +25,24 @@ import android.os.Message;
import android.os.UserHandle;
import android.preference.SeekBarVolumizer;
import android.provider.SearchIndexableResource;
import android.text.TextUtils;

import androidx.annotation.VisibleForTesting;
import androidx.preference.ListPreference;
import androidx.preference.Preference;
import android.text.TextUtils;

import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.RingtonePreference;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.sound.HandsFreeProfileOutputPreferenceController;
import com.android.settings.sound.MediaOutputPreferenceController;
import com.android.settings.widget.PreferenceCategoryController;
import com.android.settings.widget.UpdatableListPreferenceDialogFragment;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.instrumentation.Instrumentable;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.search.SearchIndexable;

@@ -71,6 +78,9 @@ public class SoundSettings extends DashboardFragment {
    };

    private RingtonePreference mRequestPreference;
    private UpdatableListPreferenceDialogFragment mDialogFragment;
    private String mMediaOutputControllerKey;
    private String mHfpOutputControllerKey;

    @Override
    public int getMetricsCategory() {
@@ -85,6 +95,11 @@ public class SoundSettings extends DashboardFragment {
            if (!TextUtils.isEmpty(selectedPreference)) {
                mRequestPreference = (RingtonePreference) findPreference(selectedPreference);
            }

            UpdatableListPreferenceDialogFragment dialogFragment =
                    (UpdatableListPreferenceDialogFragment) getFragmentManager()
                            .findFragmentByTag(TAG);
            mDialogFragment = dialogFragment;
        }
    }

@@ -114,6 +129,23 @@ public class SoundSettings extends DashboardFragment {
        return super.onPreferenceTreeClick(preference);
    }

    @Override
    public void onDisplayPreferenceDialog(Preference preference) {
        final int metricsCategory;
        if (mHfpOutputControllerKey.equals(preference.getKey())) {
            metricsCategory = MetricsProto.MetricsEvent.DIALOG_SWITCH_HFP_DEVICES;
        } else if (mMediaOutputControllerKey.equals(preference.getKey())) {
            metricsCategory = MetricsProto.MetricsEvent.DIALOG_SWITCH_A2DP_DEVICES;
        } else {
            metricsCategory = Instrumentable.METRICS_CATEGORY_UNKNOWN;
        }

        mDialogFragment = UpdatableListPreferenceDialogFragment.
                newInstance(preference.getKey(), metricsCategory);
        mDialogFragment.setTargetFragment(this, 0);
        mDialogFragment.show(getFragmentManager(), TAG);
    }

    @Override
    protected String getLogTag() {
        return TAG;
@@ -155,6 +187,14 @@ public class SoundSettings extends DashboardFragment {
        volumeControllers.add(use(NotificationVolumePreferenceController.class));
        volumeControllers.add(use(CallVolumePreferenceController.class));

        use(MediaOutputPreferenceController.class).setCallback(listPreference ->
                onPreferenceDataChanged(listPreference));
        mMediaOutputControllerKey = use(MediaOutputPreferenceController.class).getPreferenceKey();
        use(HandsFreeProfileOutputPreferenceController.class).setCallback(listPreference ->
            onPreferenceDataChanged(listPreference));
        mHfpOutputControllerKey =
                use(HandsFreeProfileOutputPreferenceController.class).getPreferenceKey();

        for (VolumeSeekBarPreferenceController controller : volumeControllers) {
            controller.setCallback(mVolumeCallback);
            getLifecycle().addObserver(controller);
@@ -291,4 +331,10 @@ public class SoundSettings extends DashboardFragment {
            workSoundController.enableWorkSync();
        }
    }

    private void onPreferenceDataChanged(ListPreference preference) {
        if (mDialogFragment != null) {
            mDialogFragment.onListPreferenceUpdated(preference);
        }
    }
}
 No newline at end of file
+10 −0
Original line number Diff line number Diff line
@@ -78,6 +78,7 @@ public abstract class AudioSwitchPreferenceController extends BasePreferenceCont
    protected final LocalBluetoothProfileManager mProfileManager;
    protected int mSelectedIndex;
    protected Preference mPreference;
    protected AudioSwitchCallback mAudioSwitchPreferenceCallback;

    private final AudioManagerAudioDeviceCallback mAudioManagerAudioDeviceCallback;
    private final LocalBluetoothManager mLocalBluetoothManager;
@@ -85,6 +86,10 @@ public abstract class AudioSwitchPreferenceController extends BasePreferenceCont
    private final WiredHeadsetBroadcastReceiver mReceiver;
    private final Handler mHandler;

    public interface AudioSwitchCallback {
        void onPreferenceDataChanged(ListPreference preference);
    }

    public AudioSwitchPreferenceController(Context context, String preferenceKey) {
        super(context, preferenceKey);
        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
@@ -207,6 +212,10 @@ public abstract class AudioSwitchPreferenceController extends BasePreferenceCont
    public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {
    }

    public void setCallback(AudioSwitchCallback callback) {
        mAudioSwitchPreferenceCallback = callback;
    }

    protected boolean isStreamFromOutputDevice(int streamType, int device) {
        return (device & mAudioManager.getDevicesForStream(streamType)) != 0;
    }
@@ -335,6 +344,7 @@ public abstract class AudioSwitchPreferenceController extends BasePreferenceCont
        listPreference.setEntryValues(mediaValues);
        listPreference.setValueIndex(mSelectedIndex);
        listPreference.setSummary(mediaOutputs[mSelectedIndex]);
        mAudioSwitchPreferenceCallback.onPreferenceDataChanged(listPreference);
    }

    private int getConnectedDeviceIndex(String hardwareAddress) {
+164 −0
Original line number Diff line number Diff line
/*
 * Copyright 2018 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.widget;

import android.app.AlertDialog;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import androidx.annotation.VisibleForTesting;
import androidx.preference.ListPreference;
import androidx.preference.PreferenceDialogFragment;
import com.android.settingslib.core.instrumentation.Instrumentable;

import java.util.ArrayList;

/**
 * {@link PreferenceDialogFragment} that updates the available options
 * when {@code onListPreferenceUpdated} is called."
 */
public class UpdatableListPreferenceDialogFragment extends PreferenceDialogFragment implements
        Instrumentable {

    private static final String SAVE_STATE_INDEX = "UpdatableListPreferenceDialogFragment.index";
    private static final String SAVE_STATE_ENTRIES =
            "UpdatableListPreferenceDialogFragment.entries";
    private static final String SAVE_STATE_ENTRY_VALUES =
            "UpdatableListPreferenceDialogFragment.entryValues";
    private static final String METRICS_CATEGORY_KEY = "metrics_category_key";
    private ArrayAdapter mAdapter;
    private int mClickedDialogEntryIndex;
    private ArrayList<CharSequence> mEntries;
    private CharSequence[] mEntryValues;
    private int mMetricsCategory = Instrumentable.METRICS_CATEGORY_UNKNOWN;

    public static UpdatableListPreferenceDialogFragment newInstance(
            String key, int metricsCategory) {
        UpdatableListPreferenceDialogFragment fragment =
                new UpdatableListPreferenceDialogFragment();
        Bundle args = new Bundle(1);
        args.putString(ARG_KEY, key);
        args.putInt(METRICS_CATEGORY_KEY, metricsCategory);
        fragment.setArguments(args);
        return fragment;
    }

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        final Bundle bundle = getArguments();
        mMetricsCategory =
                bundle.getInt(METRICS_CATEGORY_KEY, Instrumentable.METRICS_CATEGORY_UNKNOWN);
        if (savedInstanceState == null) {
            mEntries = new ArrayList<>();
            setPreferenceData(getListPreference());
        } else {
            mClickedDialogEntryIndex = savedInstanceState.getInt(SAVE_STATE_INDEX, 0);
            mEntries = savedInstanceState.getCharSequenceArrayList(SAVE_STATE_ENTRIES);
            mEntryValues =
                    savedInstanceState.getCharSequenceArray(SAVE_STATE_ENTRY_VALUES);
        }
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt(SAVE_STATE_INDEX, mClickedDialogEntryIndex);
        outState.putCharSequenceArrayList(SAVE_STATE_ENTRIES, mEntries);
        outState.putCharSequenceArray(SAVE_STATE_ENTRY_VALUES, mEntryValues);
    }

    @Override
    public void onDialogClosed(boolean positiveResult) {
        final ListPreference preference = getListPreference();
        if (positiveResult && mClickedDialogEntryIndex >= 0) {
            final String value = mEntryValues[mClickedDialogEntryIndex].toString();
            if (preference.callChangeListener(value)) {
                preference.setValue(value);
            }
        }
    }

    @VisibleForTesting
    void setAdapter(ArrayAdapter adapter) {
        mAdapter = adapter;
    }

    @VisibleForTesting
    void setEntries(ArrayList<CharSequence> entries) {
        mEntries = entries;
    }

    @VisibleForTesting
    ArrayAdapter getAdapter() {
        return mAdapter;
    }

    @VisibleForTesting
    void setMetricsCategory(Bundle bundle) {
        mMetricsCategory =
                bundle.getInt(METRICS_CATEGORY_KEY, Instrumentable.METRICS_CATEGORY_UNKNOWN);
    }

    @Override
    protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
        super.onPrepareDialogBuilder(builder);
        final TypedArray a = getContext().obtainStyledAttributes(
                null,
                com.android.internal.R.styleable.AlertDialog,
                com.android.internal.R.attr.alertDialogStyle, 0);

        mAdapter = new ArrayAdapter<>(
                getContext(),
                a.getResourceId(
                        com.android.internal.R.styleable.AlertDialog_singleChoiceItemLayout,
                        com.android.internal.R.layout.select_dialog_singlechoice),
                mEntries);

        builder.setSingleChoiceItems(mAdapter, mClickedDialogEntryIndex,
                (dialog, which) -> {
                    mClickedDialogEntryIndex = which;
                    onClick(dialog, -1);
                    dialog.dismiss();
                });
        builder.setPositiveButton(null, null);
        a.recycle();
    }

    @Override
    public int getMetricsCategory() {
        return mMetricsCategory;
    }

    private ListPreference getListPreference() {
        return (ListPreference) getPreference();
    }

    private void setPreferenceData(ListPreference preference) {
        mEntries.clear();
        mClickedDialogEntryIndex = preference.findIndexOfValue(preference.getValue());
        for (CharSequence entry : preference.getEntries()) {
            mEntries.add(entry);
        }
        mEntryValues = preference.getEntryValues();
    }

    public void onListPreferenceUpdated(ListPreference preference) {
        if (mAdapter != null) {
            setPreferenceData(preference);
            mAdapter.notifyDataSetChanged();
        }
    }
}
+3 −1
Original line number Diff line number Diff line
@@ -16,7 +16,6 @@

package com.android.settings.sound;


import static android.media.AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
import static android.media.AudioSystem.DEVICE_OUT_HEARING_AID;
import static android.media.AudioSystem.DEVICE_OUT_USB_HEADSET;
@@ -93,6 +92,8 @@ public class HandsFreeProfileOutputPreferenceControllerTest {
    private HeadsetProfile mHeadsetProfile;
    @Mock
    private HearingAidProfile mHearingAidProfile;
    @Mock
    private AudioSwitchPreferenceController.AudioSwitchCallback mAudioSwitchPreferenceCallback;

    private Context mContext;
    private PreferenceScreen mScreen;
@@ -156,6 +157,7 @@ public class HandsFreeProfileOutputPreferenceControllerTest {
        when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
        mScreen.addPreference(mPreference);
        mController.displayPreference(mScreen);
        mController.setCallback(mAudioSwitchPreferenceCallback);
    }

    @After
+3 −0
Original line number Diff line number Diff line
@@ -94,6 +94,8 @@ public class MediaOutputPreferenceControllerTest {
    private A2dpProfile mA2dpProfile;
    @Mock
    private HearingAidProfile mHearingAidProfile;
    @Mock
    private AudioSwitchPreferenceController.AudioSwitchCallback mAudioSwitchPreferenceCallback;

    private Context mContext;
    private PreferenceScreen mScreen;
@@ -157,6 +159,7 @@ public class MediaOutputPreferenceControllerTest {
        when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
        mScreen.addPreference(mPreference);
        mController.displayPreference(mScreen);
        mController.setCallback(mAudioSwitchPreferenceCallback);
    }

    @After
Loading