diff --git a/res/xml/tts_engine_picker.xml b/res/xml/tts_engine_picker.xml index 92bfedee7bb..2550fe0ff5e 100644 --- a/res/xml/tts_engine_picker.xml +++ b/res/xml/tts_engine_picker.xml @@ -18,6 +18,4 @@ android:key="tts_engine_picker_screen" android:title="@string/tts_engine_preference_title"> - - diff --git a/src/com/android/settings/tts/TtsEnginePreference.java b/src/com/android/settings/tts/TtsEnginePreference.java deleted file mode 100644 index 8f15db266e2..00000000000 --- a/src/com/android/settings/tts/TtsEnginePreference.java +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright (C) 2011 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.tts; - -import android.content.Context; -import android.content.DialogInterface; -import android.speech.tts.TextToSpeech.EngineInfo; -import android.util.Log; -import android.widget.Checkable; -import android.widget.CompoundButton; -import android.widget.RadioButton; - -import androidx.appcompat.app.AlertDialog; -import androidx.preference.Preference; -import androidx.preference.PreferenceViewHolder; - -import com.android.settings.R; - -import androidx.annotation.VisibleForTesting; - - -public class TtsEnginePreference extends Preference { - - private static final String TAG = "TtsEnginePreference"; - - /** - * The engine information for the engine this preference represents. - * Contains it's name, label etc. which are used for display. - */ - private final EngineInfo mEngineInfo; - - /** - * The shared radio button state, which button is checked etc. - */ - private final RadioButtonGroupState mSharedState; - private RadioButton mRadioButton; - - /** - * When true, the change callbacks on the radio button will not - * fire. - */ - private volatile boolean mPreventRadioButtonCallbacks; - - private final CompoundButton.OnCheckedChangeListener mRadioChangeListener = - new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - onRadioButtonClicked(buttonView, isChecked); - } - }; - - public TtsEnginePreference(Context context, EngineInfo info, RadioButtonGroupState state) { - super(context); - - setWidgetLayoutResource(R.layout.preference_widget_radiobutton); - setLayoutResource(R.layout.preference_radio); - setIconSpaceReserved(false); - - mSharedState = state; - mEngineInfo = info; - mPreventRadioButtonCallbacks = false; - - setKey(mEngineInfo.name); - setTitle(mEngineInfo.label); - } - - @Override - public void onBindViewHolder(PreferenceViewHolder view) { - super.onBindViewHolder(view); - - if (mSharedState == null) { - throw new IllegalStateException("Call to getView() before a call to" + - "setSharedState()"); - } - - final RadioButton rb = view.itemView.findViewById(android.R.id.checkbox); - rb.setOnCheckedChangeListener(mRadioChangeListener); - - boolean isChecked = getKey().equals(mSharedState.getCurrentKey()); - if (isChecked) { - mSharedState.setCurrentChecked(rb); - } - - mPreventRadioButtonCallbacks = true; - rb.setChecked(isChecked); - mPreventRadioButtonCallbacks = false; - mRadioButton = rb; - } - - @Override - public void onClick() { - mRadioButton.setChecked(true); - } - - private boolean shouldDisplayDataAlert() { - return !mEngineInfo.system; - } - - - private void displayDataAlert( - DialogInterface.OnClickListener positiveOnClickListener, - DialogInterface.OnClickListener negativeOnClickListener) { - Log.i(TAG, "Displaying data alert for :" + mEngineInfo.name); - - AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); - builder.setTitle(android.R.string.dialog_alert_title) - .setMessage(getContext().getString( - R.string.tts_engine_security_warning, mEngineInfo.label)) - .setCancelable(true) - .setPositiveButton(android.R.string.ok, positiveOnClickListener) - .setNegativeButton(android.R.string.cancel, negativeOnClickListener); - - AlertDialog dialog = builder.create(); - dialog.show(); - } - - - private void onRadioButtonClicked(final CompoundButton buttonView, - boolean isChecked) { - if (mPreventRadioButtonCallbacks || - (mSharedState.getCurrentChecked() == buttonView)) { - return; - } - - if (isChecked) { - // Should we alert user? if that's true, delay making engine current one. - if (shouldDisplayDataAlert()) { - displayDataAlert(new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - makeCurrentEngine(buttonView); - } - }, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - // Undo the click. - buttonView.setChecked(false); - } - }); - } else { - // Privileged engine, set it current - makeCurrentEngine(buttonView); - } - } - } - - private void makeCurrentEngine(Checkable current) { - if (mSharedState.getCurrentChecked() != null) { - mSharedState.getCurrentChecked().setChecked(false); - } - mSharedState.setCurrentChecked(current); - mSharedState.setCurrentKey(getKey()); - callChangeListener(mSharedState.getCurrentKey()); - } - - - /** - * Holds all state that is common to this group of radio buttons, such - * as the currently selected key and the currently checked compound button. - * (which corresponds to this key). - */ - public interface RadioButtonGroupState { - String getCurrentKey(); - - Checkable getCurrentChecked(); - - void setCurrentKey(String key); - - void setCurrentChecked(Checkable current); - } - -} diff --git a/src/com/android/settings/tts/TtsEnginePreferenceFragment.java b/src/com/android/settings/tts/TtsEnginePreferenceFragment.java index 244e5b154c6..fb8072b39b1 100644 --- a/src/com/android/settings/tts/TtsEnginePreferenceFragment.java +++ b/src/com/android/settings/tts/TtsEnginePreferenceFragment.java @@ -4,111 +4,44 @@ import static android.provider.Settings.Secure.TTS_DEFAULT_SYNTH; import android.app.settings.SettingsEnums; import android.content.Context; +import android.content.DialogInterface; +import android.graphics.drawable.Drawable; import android.os.Bundle; import android.provider.SearchIndexableResource; import android.speech.tts.TextToSpeech; import android.speech.tts.TextToSpeech.EngineInfo; import android.speech.tts.TtsEngines; import android.util.Log; -import android.widget.Checkable; -import androidx.preference.PreferenceCategory; +import androidx.appcompat.app.AlertDialog; import com.android.settings.R; -import com.android.settings.SettingsPreferenceFragment; import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.search.Indexable; -import com.android.settings.tts.TtsEnginePreference.RadioButtonGroupState; +import com.android.settings.widget.RadioButtonPickerFragment; import com.android.settingslib.search.SearchIndexable; +import com.android.settingslib.widget.CandidateInfo; +import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; @SearchIndexable -public class TtsEnginePreferenceFragment extends SettingsPreferenceFragment - implements RadioButtonGroupState { +public class TtsEnginePreferenceFragment extends RadioButtonPickerFragment { private static final String TAG = "TtsEnginePrefFragment"; - private static final int VOICE_DATA_INTEGRITY_CHECK = 1977; - - /** The currently selected engine. */ - private String mCurrentEngine; - - /** - * The engine checkbox that is currently checked. Saves us a bit of effort in deducing the right - * one from the currently selected engine. - */ - private Checkable mCurrentChecked; - /** * The previously selected TTS engine. Useful for rollbacks if the users choice is not loaded or * fails a voice integrity check. */ private String mPreviousEngine; - private PreferenceCategory mEnginePreferenceCategory; - private TextToSpeech mTts = null; private TtsEngines mEnginesHelper = null; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - addPreferencesFromResource(R.xml.tts_engine_picker); - - mEnginePreferenceCategory = - (PreferenceCategory) findPreference("tts_engine_preference_category"); - mEnginesHelper = new TtsEngines(getActivity().getApplicationContext()); - - mTts = new TextToSpeech(getActivity().getApplicationContext(), null); - - initSettings(); - } - - @Override - public int getMetricsCategory() { - return SettingsEnums.TTS_ENGINE_SETTINGS; - } - - @Override - public void onDestroy() { - super.onDestroy(); - if (mTts != null) { - mTts.shutdown(); - mTts = null; - } - } - - private void initSettings() { - if (mTts != null) { - mCurrentEngine = mTts.getCurrentEngine(); - } - - mEnginePreferenceCategory.removeAll(); - - List engines = mEnginesHelper.getEngines(); - for (EngineInfo engine : engines) { - TtsEnginePreference enginePref = - new TtsEnginePreference(getPrefContext(), engine, this); - mEnginePreferenceCategory.addPreference(enginePref); - } - } - - @Override - public Checkable getCurrentChecked() { - return mCurrentChecked; - } - - @Override - public String getCurrentKey() { - return mCurrentEngine; - } - - @Override - public void setCurrentChecked(Checkable current) { - mCurrentChecked = current; - } - + private Context mContext; + private Map mEngineMap; /** * The initialization listener used when the user changes his choice of engine (as opposed to * when then screen is being initialized for the first time). @@ -121,6 +54,116 @@ public class TtsEnginePreferenceFragment extends SettingsPreferenceFragment } }; + @Override + public void onCreate(Bundle savedInstanceState) { + mContext = getContext().getApplicationContext(); + mEnginesHelper = new TtsEngines(mContext); + mEngineMap = new HashMap<>(); + mTts = new TextToSpeech(mContext, null); + + super.onCreate(savedInstanceState); + } + + @Override + public void onDestroy() { + super.onDestroy(); + if (mTts != null) { + mTts.shutdown(); + mTts = null; + } + } + + @Override + public int getMetricsCategory() { + return SettingsEnums.TTS_ENGINE_SETTINGS; + } + + /** + * Step 3: We have now bound to the TTS engine the user requested. We will attempt to check + * voice data for the engine if we successfully bound to it, or revert to the previous engine if + * we didn't. + */ + public void onUpdateEngine(int status) { + if (status == TextToSpeech.SUCCESS) { + Log.d(TAG, "Updating engine: Successfully bound to the engine: " + + mTts.getCurrentEngine()); + android.provider.Settings.Secure.putString( + mContext.getContentResolver(), TTS_DEFAULT_SYNTH, mTts.getCurrentEngine()); + } else { + Log.d(TAG, "Updating engine: Failed to bind to engine, reverting."); + if (mPreviousEngine != null) { + // This is guaranteed to at least bind, since mPreviousEngine would be + // null if the previous bind to this engine failed. + mTts = new TextToSpeech(mContext, null, mPreviousEngine); + updateCheckedState(mPreviousEngine); + } + mPreviousEngine = null; + } + } + + @Override + protected void onRadioButtonConfirmed(String selectedKey) { + final EngineCandidateInfo info = mEngineMap.get(selectedKey); + // Should we alert user? if that's true, delay making engine current one. + if (shouldDisplayDataAlert(info)) { + displayDataAlert(info, (dialog, which) -> { + setDefaultKey(selectedKey); + }); + } else { + // Privileged engine, set it current + setDefaultKey(selectedKey); + } + } + + @Override + protected List getCandidates() { + final List infos = new ArrayList<>(); + final List engines = mEnginesHelper.getEngines(); + for (EngineInfo engine : engines) { + final EngineCandidateInfo info = new EngineCandidateInfo(engine); + infos.add(info); + mEngineMap.put(engine.name, info); + } + return infos; + } + + @Override + protected String getDefaultKey() { + return mEnginesHelper.getDefaultEngine(); + } + + @Override + protected boolean setDefaultKey(String key) { + updateDefaultEngine(key); + updateCheckedState(key); + return true; + } + + @Override + protected int getPreferenceScreenResId() { + return R.xml.tts_engine_picker; + } + + private boolean shouldDisplayDataAlert(EngineCandidateInfo info) { + return !info.isSystem(); + } + + private void displayDataAlert(EngineCandidateInfo info, + DialogInterface.OnClickListener positiveOnClickListener) { + Log.i(TAG, "Displaying data alert for :" + info.getKey()); + + final AlertDialog dialog = new AlertDialog.Builder(getPrefContext()) + .setTitle(android.R.string.dialog_alert_title) + .setMessage(mContext.getString( + R.string.tts_engine_security_warning, info.loadLabel())) + .setCancelable(true) + .setPositiveButton(android.R.string.ok, positiveOnClickListener) + .setNegativeButton(android.R.string.cancel, null) + .create(); + + dialog.show(); + } + private void updateDefaultEngine(String engine) { Log.d(TAG, "Updating default synth to : " + engine); @@ -146,41 +189,36 @@ public class TtsEnginePreferenceFragment extends SettingsPreferenceFragment // Step 3 is continued on #onUpdateEngine (below) which is called when // the app binds successfully to the engine. Log.i(TAG, "Updating engine : Attempting to connect to engine: " + engine); - mTts = new TextToSpeech(getActivity().getApplicationContext(), mUpdateListener, engine); + mTts = new TextToSpeech(mContext, mUpdateListener, engine); Log.i(TAG, "Success"); } - /** - * Step 3: We have now bound to the TTS engine the user requested. We will attempt to check - * voice data for the engine if we successfully bound to it, or revert to the previous engine if - * we didn't. - */ - public void onUpdateEngine(int status) { - if (status == TextToSpeech.SUCCESS) { - Log.d( + public static class EngineCandidateInfo extends CandidateInfo { + private final EngineInfo mEngineInfo; - TAG, - "Updating engine: Successfully bound to the engine: " - + mTts.getCurrentEngine()); - android.provider.Settings.Secure.putString( - getContentResolver(), TTS_DEFAULT_SYNTH, mTts.getCurrentEngine()); - } else { - Log.d(TAG, "Updating engine: Failed to bind to engine, reverting."); - if (mPreviousEngine != null) { - // This is guaranteed to at least bind, since mPreviousEngine would be - // null if the previous bind to this engine failed. - mTts = - new TextToSpeech( - getActivity().getApplicationContext(), null, mPreviousEngine); - } - mPreviousEngine = null; + EngineCandidateInfo(EngineInfo engineInfo) { + super(true /* enabled */); + mEngineInfo = engineInfo; } - } - @Override - public void setCurrentKey(String key) { - mCurrentEngine = key; - updateDefaultEngine(mCurrentEngine); + @Override + public CharSequence loadLabel() { + return mEngineInfo.label; + } + + @Override + public Drawable loadIcon() { + return null; + } + + @Override + public String getKey() { + return mEngineInfo.name; + } + + public boolean isSystem() { + return mEngineInfo.system; + } } public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowTtsEngines.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowTtsEngines.java new file mode 100644 index 00000000000..e102e30e9e7 --- /dev/null +++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowTtsEngines.java @@ -0,0 +1,87 @@ +/* + * 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.testutils.shadow; + +import android.content.Intent; +import android.speech.tts.TextToSpeech; +import android.speech.tts.TtsEngines; + +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; +import org.robolectric.annotation.Resetter; + +import java.util.List; +import java.util.Locale; + +@Implements(TtsEngines.class) +public class ShadowTtsEngines { + private static TtsEngines sInstance; + + public static void setInstance(TtsEngines ttsEngines) { + sInstance = ttsEngines; + } + + @Resetter + public static void reset() { + sInstance = null; + } + + @Implementation + protected List getEngines() { + return sInstance.getEngines(); + } + + @Implementation + protected TextToSpeech.EngineInfo getEngineInfo(String packageName) { + return sInstance.getEngineInfo(packageName); + } + + @Implementation + protected String getDefaultEngine() { + return sInstance.getDefaultEngine(); + } + + @Implementation + protected Intent getSettingsIntent(String engine) { + return sInstance.getSettingsIntent(engine); + } + + @Implementation + protected boolean isEngineInstalled(String engine) { + return sInstance.isEngineInstalled(engine); + } + + @Implementation + protected boolean isLocaleSetToDefaultForEngine(String engineName) { + return sInstance.isLocaleSetToDefaultForEngine(engineName); + } + + @Implementation + protected Locale getLocalePrefForEngine(String engineName) { + return sInstance.getLocalePrefForEngine(engineName); + } + + @Implementation + protected synchronized void updateLocalePrefForEngine(String engineName, Locale newLocale) { + sInstance.updateLocalePrefForEngine(engineName, newLocale); + } + + @Implementation + protected Locale parseLocaleString(String localeString) { + return sInstance.parseLocaleString(localeString); + } +} \ No newline at end of file diff --git a/tests/robotests/src/com/android/settings/tts/TtsEnginePreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/tts/TtsEnginePreferenceFragmentTest.java new file mode 100644 index 00000000000..00b8e869a9f --- /dev/null +++ b/tests/robotests/src/com/android/settings/tts/TtsEnginePreferenceFragmentTest.java @@ -0,0 +1,129 @@ +/* + * 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.tts; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.os.Bundle; +import android.speech.tts.TextToSpeech; +import android.speech.tts.TtsEngines; + +import com.android.settings.testutils.shadow.ShadowTtsEngines; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; +import org.robolectric.shadow.api.Shadow; +import org.robolectric.shadows.ShadowPackageManager; +import org.robolectric.shadows.androidx.fragment.FragmentController; + +@RunWith(RobolectricTestRunner.class) +public class TtsEnginePreferenceFragmentTest { + + private Context mContext; + private TtsEnginePreferenceFragment mTtsEnginePreferenceFragment; + + @Before + public void setUp() { + mContext = RuntimeEnvironment.application; + + final ResolveInfo info = new ResolveInfo(); + final ServiceInfo serviceInfo = spy(new ServiceInfo()); + serviceInfo.packageName = mContext.getPackageName(); + serviceInfo.name = mContext.getClass().getName(); + info.serviceInfo = serviceInfo; + doReturn("title").when(serviceInfo).loadLabel(any(PackageManager.class)); + doReturn(1).when(serviceInfo).getIconResource(); + + final ShadowPackageManager spm = Shadow.extract(mContext.getPackageManager()); + spm.addResolveInfoForIntent( + new Intent(TextToSpeech.Engine.INTENT_ACTION_TTS_SERVICE), info); + } + + @After + public void tearDown() { + ShadowTtsEngines.reset(); + } + + @Test + public void getCandidates_AddEngines_returnCorrectEngines() { + mTtsEnginePreferenceFragment = FragmentController.of(new TtsEnginePreferenceFragment(), + new Bundle()) + .create() + .get(); + + assertThat(mTtsEnginePreferenceFragment.getCandidates().size()).isEqualTo(1); + } + + @Test + @Config(shadows = {ShadowTtsEngines.class}) + public void getDefaultKey_validKey_returnCorrectKey() { + final String TEST_ENGINE = "test_engine"; + final TtsEngines engine = mock(TtsEngines.class); + ShadowTtsEngines.setInstance(engine); + mTtsEnginePreferenceFragment = FragmentController.of(new TtsEnginePreferenceFragment(), + new Bundle()) + .create() + .get(); + when(engine.getDefaultEngine()).thenReturn(TEST_ENGINE); + + assertThat(mTtsEnginePreferenceFragment.getDefaultKey()).isEqualTo(TEST_ENGINE); + } + + @Test + @Config(shadows = {ShadowTtsEngines.class}) + public void setDefaultKey_validKey_callingTtsEngineFunction() { + final TtsEngines engine = mock(TtsEngines.class); + ShadowTtsEngines.setInstance(engine); + mTtsEnginePreferenceFragment = FragmentController.of(new TtsEnginePreferenceFragment(), + new Bundle()) + .create() + .get(); + + mTtsEnginePreferenceFragment.setDefaultKey(mContext.getPackageName()); + + verify(engine).isEngineInstalled(mContext.getPackageName()); + } + + @Test + public void setDefaultKey_validKey_updateCheckedState() { + mTtsEnginePreferenceFragment = spy(FragmentController.of(new TtsEnginePreferenceFragment(), + new Bundle()) + .create() + .get()); + + mTtsEnginePreferenceFragment.setDefaultKey(mContext.getPackageName()); + + verify(mTtsEnginePreferenceFragment).updateCheckedState(mContext.getPackageName()); + } +} diff --git a/tests/robotests/src/com/android/settings/tts/TtsEnginePreferenceTest.java b/tests/robotests/src/com/android/settings/tts/TtsEnginePreferenceTest.java deleted file mode 100644 index 5cd948824a9..00000000000 --- a/tests/robotests/src/com/android/settings/tts/TtsEnginePreferenceTest.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * 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.tts; - -import static com.google.common.truth.Truth.assertThat; - -import android.content.Context; -import android.speech.tts.TextToSpeech; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Checkable; - -import androidx.preference.PreferenceViewHolder; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; - -@RunWith(RobolectricTestRunner.class) -public class TtsEnginePreferenceTest { - - private static final String KEY = "test_key"; - - private TtsEnginePreference mPreference; - private Context mContext; - private PreferenceViewHolder mViewHolder; - private View mRootView; - private FakeRadioButtonGroupState mState; - - @Before - public void setUp() { - mContext = RuntimeEnvironment.application; - - final TextToSpeech.EngineInfo info = new TextToSpeech.EngineInfo(); - info.system = true; - mState = new FakeRadioButtonGroupState(); - mPreference = new TtsEnginePreference(mContext, info, mState); - mPreference.setKey(KEY); - - // Create preference view holder - final LayoutInflater inflater = LayoutInflater.from(mContext); - mRootView = View.inflate(mContext, mPreference.getLayoutResource(), null /* parent */); - final ViewGroup widgetFrame = mRootView.findViewById(android.R.id.widget_frame); - inflater.inflate(mPreference.getWidgetLayoutResource(), widgetFrame); - mViewHolder = PreferenceViewHolder.createInstanceForTests(mRootView); - } - - @Test - public void onClick_shouldInvokeOnCheckedChangeListener() { - mPreference.onBindViewHolder(mViewHolder); - - mPreference.onClick(); - - assertThat(mState.getCurrentKey()).isEqualTo(mPreference.getKey()); - } - - public static class FakeRadioButtonGroupState implements - TtsEnginePreference.RadioButtonGroupState { - - private String mKey; - - @Override - public Checkable getCurrentChecked() { - return null; - } - - @Override - public String getCurrentKey() { - return mKey; - } - - @Override - public void setCurrentChecked(Checkable current) { - } - - @Override - public void setCurrentKey(String key) { - mKey = key; - } - } -}