--- /dev/null
+/*
+ * Copyright (C) 2019 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.development.bluetooth;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.widget.RadioButton;
+import android.widget.RadioGroup;
+import android.widget.TextView;
+
+import com.android.settings.R;
+import com.android.settingslib.CustomDialogPreferenceCompat;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Abstract class for Bluetooth A2DP config preference in developer option.
+ */
+public abstract class BaseBluetoothDialogPreference extends CustomDialogPreferenceCompat implements
+ RadioGroup.OnCheckedChangeListener{
+ private static final String TAG = "BaseBluetoothDlgPref";
+
+ protected List<Integer> mRadioButtonIds = new ArrayList<>();
+ protected List<String> mRadioButtonStrings = new ArrayList<>();
+ protected List<String> mSummaryStrings = new ArrayList<>();
+
+ private Callback mCallback;
+
+ public BaseBluetoothDialogPreference(Context context) {
+ super(context);
+ }
+
+ public BaseBluetoothDialogPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public BaseBluetoothDialogPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public BaseBluetoothDialogPreference(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ protected void onBindDialogView(View view) {
+ super.onBindDialogView(view);
+ if (mCallback == null) {
+ Log.e(TAG, "Unable to show dialog by the callback is null");
+ return;
+ }
+ if (mRadioButtonStrings.size() != mRadioButtonIds.size()) {
+ Log.e(TAG, "Unable to show dialog by the view and string size are not matched");
+ return;
+ }
+ final int currentIndex = mCallback.getCurrentConfigIndex();
+ if (currentIndex < 0 || currentIndex >= mRadioButtonIds.size()) {
+ Log.e(TAG, "Unable to show dialog by the incorrect index: " + currentIndex);
+ return;
+ }
+ // Initial radio button group
+ final RadioGroup radioGroup = view.findViewById(getRadioButtonGroupId());
+ if (radioGroup == null) {
+ Log.e(TAG, "Unable to show dialog by no radio button group: "
+ + getRadioButtonGroupId());
+ return;
+ }
+ radioGroup.check(mRadioButtonIds.get(currentIndex));
+ radioGroup.setOnCheckedChangeListener(this);
+ // Initial radio button
+ final List<Integer> selectableIndex = mCallback.getSelectableIndex();
+ RadioButton radioButton;
+ for (int i = 0; i < mRadioButtonStrings.size(); i++) {
+ radioButton = view.findViewById(mRadioButtonIds.get(i));
+ if (radioButton == null) {
+ Log.e(TAG, "Unable to show dialog by no radio button:" + mRadioButtonIds.get(i));
+ return;
+ }
+ radioButton.setText(mRadioButtonStrings.get(i));
+ radioButton.setEnabled(selectableIndex.contains(i));
+ }
+ // View will be invisible when all options are enabled.
+ if (selectableIndex.size() == mRadioButtonIds.size()) {
+ final TextView helpTextView = view.findViewById(R.id.bluetooth_audio_codec_help_info);
+ helpTextView.setVisibility(View.GONE);
+ }
+ }
+
+ @Override
+ public void onCheckedChanged(RadioGroup group, int checkedId) {
+ if (mCallback == null) {
+ Log.e(TAG, "Callback is null");
+ return;
+ }
+ mCallback.onIndexUpdated(mRadioButtonIds.indexOf(checkedId));
+ getDialog().dismiss();
+ }
+
+ /**
+ * Method to set callback.
+ */
+ public void setCallback(Callback callback) {
+ mCallback = callback;
+ }
+
+ /**
+ * Method to get summary strings by index.
+ */
+ protected String generateSummary(int index) {
+ if (index > mSummaryStrings.size()) {
+ Log.e(TAG, "Unable to get summary of " + index + ". Size is " + mSummaryStrings.size());
+ return null;
+ }
+ return index == getDefaultIndex() ? mSummaryStrings.get(getDefaultIndex()) :
+ String.format(getContext().getResources().getString(
+ R.string.bluetooth_select_a2dp_codec_streaming_label),
+ mSummaryStrings.get(index));
+ }
+
+ /**
+ * Method to get default index.
+ */
+ protected int getDefaultIndex() {
+ return 0;
+ }
+
+ /**
+ * Method to get radio button group id.
+ */
+ protected abstract int getRadioButtonGroupId();
+
+ /**
+ * Callback interface for this class to manipulate data from controller.
+ */
+ public interface Callback {
+ /**
+ * Method to get current Bluetooth A2DP config index.
+ */
+ int getCurrentConfigIndex();
+ /**
+ * Method to get selectable config index which means supported by phone and device.
+ *
+ * @return the available {@link List} of the Bluetooth A2DP config.
+ */
+ List<Integer> getSelectableIndex();
+ /**
+ * Method to notify controller when user changes config.
+ *
+ * @param index for the selected index.
+ */
+ void onIndexUpdated(int index);
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2019 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.development.bluetooth;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.view.View;
+import android.widget.RadioButton;
+import android.widget.RadioGroup;
+import android.widget.TextView;
+
+import com.android.settings.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+public class BaseBluetoothDialogPreferenceTest {
+
+ private static final int ID1 = 99;
+ private static final int ID2 = 100;
+ private static final int RADIOGROUP_ID = 101;
+ private static final int TEXT_VIEW_ID = R.id.bluetooth_audio_codec_help_info;
+ private static final String BUTTON1 = "Test button1";
+ private static final String BUTTON2 = "Test button2";
+ private static final String SUMMARY1 = "Test summary1";
+ private static final String SUMMARY2 = "Test summary2";
+
+ @Mock
+ private BaseBluetoothDialogPreference.Callback mCallback;
+ @Mock
+ private Dialog mDialog;
+ @Mock
+ private View mView;
+
+ private BaseBluetoothDialogPreference mPreference;
+ private Context mContext;
+ private RadioButton mRadioButton1;
+ private RadioButton mRadioButton2;
+ private RadioGroup mRadioGroup;
+ private TextView mTextView;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+ mPreference = spy(new BaseBluetoothDialogPreferenceImpl(mContext));
+ mRadioButton1 = new RadioButton(mContext);
+ mRadioButton1.setId(ID1);
+ mRadioButton2 = new RadioButton(mContext);
+ mRadioButton2.setId(ID2);
+ mRadioGroup = new RadioGroup(mContext);
+ mRadioGroup.addView(mRadioButton1);
+ mRadioGroup.addView(mRadioButton2);
+ mTextView = new TextView(mContext);
+ mPreference.mRadioButtonIds.add(ID1);
+ mPreference.mRadioButtonIds.add(ID2);
+ mPreference.mRadioButtonStrings.add(BUTTON1);
+ mPreference.mRadioButtonStrings.add(BUTTON2);
+ mPreference.mSummaryStrings.add(SUMMARY1);
+ mPreference.mSummaryStrings.add(SUMMARY2);
+ mPreference.setCallback(mCallback);
+ when(mView.findViewById(mPreference.getRadioButtonGroupId())).thenReturn(mRadioGroup);
+ }
+
+ @Test
+ public void onBindDialogView_checkRadioButtonsSelection() {
+ when(mCallback.getCurrentConfigIndex()).thenReturn(1);
+
+ assertThat(mRadioGroup.getCheckedRadioButtonId()).isNotEqualTo(ID2);
+ mPreference.onBindDialogView(mView);
+
+ assertThat(mRadioGroup.getCheckedRadioButtonId()).isEqualTo(ID2);
+ }
+
+ @Test
+ public void onBindDialogView_checkRadioButtonsText() {
+ when(mView.findViewById(ID1)).thenReturn(mRadioButton1);
+ when(mView.findViewById(ID2)).thenReturn(mRadioButton2);
+
+ assertThat(mRadioButton1.getText()).isNotEqualTo(BUTTON1);
+ assertThat(mRadioButton2.getText()).isNotEqualTo(BUTTON2);
+ mPreference.onBindDialogView(mView);
+
+ assertThat(mRadioButton1.getText()).isEqualTo(BUTTON1);
+ assertThat(mRadioButton2.getText()).isEqualTo(BUTTON2);
+ }
+
+ @Test
+ public void onBindDialogView_checkRadioButtonsState() {
+ when(mView.findViewById(ID1)).thenReturn(mRadioButton1);
+ when(mView.findViewById(ID2)).thenReturn(mRadioButton2);
+ List<Integer> indexes = new ArrayList<>();
+ indexes.add(0);
+ when(mCallback.getSelectableIndex()).thenReturn(indexes);
+
+ assertThat(mRadioButton1.isEnabled()).isTrue();
+ assertThat(mRadioButton2.isEnabled()).isTrue();
+ mPreference.onBindDialogView(mView);
+
+ assertThat(mRadioButton1.isEnabled()).isTrue();
+ assertThat(mRadioButton2.isEnabled()).isFalse();
+ }
+
+ @Test
+ public void onBindDialogView_allButtonsEnabled_hideHelpText() {
+ when(mView.findViewById(ID1)).thenReturn(mRadioButton1);
+ when(mView.findViewById(ID2)).thenReturn(mRadioButton2);
+ when(mView.findViewById(TEXT_VIEW_ID)).thenReturn(mTextView);
+ List<Integer> indexes = new ArrayList<>();
+ indexes.add(0);
+ indexes.add(1);
+ when(mCallback.getSelectableIndex()).thenReturn(indexes);
+
+ assertThat(mTextView.getVisibility()).isEqualTo(View.VISIBLE);
+ mPreference.onBindDialogView(mView);
+ assertThat(mTextView.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void onBindDialogView_buttonDisabled_showHelpText() {
+ when(mView.findViewById(ID1)).thenReturn(mRadioButton1);
+ when(mView.findViewById(ID2)).thenReturn(mRadioButton2);
+ when(mView.findViewById(TEXT_VIEW_ID)).thenReturn(mTextView);
+ List<Integer> indexes = new ArrayList<>();
+ indexes.add(0);
+ when(mCallback.getSelectableIndex()).thenReturn(indexes);
+
+ mPreference.onBindDialogView(mView);
+ assertThat(mTextView.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void onCheckedChanged_verifyIndex() {
+ when(mPreference.getDialog()).thenReturn(mDialog);
+
+ mPreference.onCheckedChanged(mRadioGroup, ID2);
+ verify(mCallback).onIndexUpdated(1);
+ }
+
+ @Test
+ public void generateSummary_checkString() {
+ final String summary = String.format(mContext.getResources().getString(
+ R.string.bluetooth_select_a2dp_codec_streaming_label), SUMMARY2);
+
+ assertThat(mPreference.generateSummary(1)).isEqualTo(summary);
+ }
+
+
+ private static class BaseBluetoothDialogPreferenceImpl extends BaseBluetoothDialogPreference {
+
+ private BaseBluetoothDialogPreferenceImpl(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected int getRadioButtonGroupId() {
+ return RADIOGROUP_ID;
+ }
+ }
+}