diff --git a/src/com/android/settings/development/bluetooth/BaseBluetoothDialogPreference.java b/src/com/android/settings/development/bluetooth/BaseBluetoothDialogPreference.java new file mode 100644 index 00000000000..08a450bde0f --- /dev/null +++ b/src/com/android/settings/development/bluetooth/BaseBluetoothDialogPreference.java @@ -0,0 +1,171 @@ +/* + * 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 mRadioButtonIds = new ArrayList<>(); + protected List mRadioButtonStrings = new ArrayList<>(); + protected List 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 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 getSelectableIndex(); + /** + * Method to notify controller when user changes config. + * + * @param index for the selected index. + */ + void onIndexUpdated(int index); + } +} diff --git a/tests/robotests/src/com/android/settings/development/bluetooth/BaseBluetoothDialogPreferenceTest.java b/tests/robotests/src/com/android/settings/development/bluetooth/BaseBluetoothDialogPreferenceTest.java new file mode 100644 index 00000000000..8a3493087a0 --- /dev/null +++ b/tests/robotests/src/com/android/settings/development/bluetooth/BaseBluetoothDialogPreferenceTest.java @@ -0,0 +1,189 @@ +/* + * 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 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 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 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; + } + } +}