diff --git a/src/com/android/settings/tts/TextToSpeechSettings.java b/src/com/android/settings/tts/TextToSpeechSettings.java index 2bfc6ea4998..8e671f4205f 100644 --- a/src/com/android/settings/tts/TextToSpeechSettings.java +++ b/src/com/android/settings/tts/TextToSpeechSettings.java @@ -38,6 +38,7 @@ import android.util.Log; import android.util.Pair; import androidx.appcompat.app.AlertDialog; +import androidx.lifecycle.ViewModelProviders; import androidx.preference.ListPreference; import androidx.preference.Preference; @@ -205,13 +206,18 @@ public class TextToSpeechSettings extends SettingsPreferenceFragment mLocalePreference.setEnabled(entries.length > 0); } - mTts = new TextToSpeech(getActivity().getApplicationContext(), mInitListener); + final TextToSpeechViewModel ttsViewModel = + ViewModelProviders.of(this).get(TextToSpeechViewModel.class); + Pair ttsAndNew = ttsViewModel.getTtsAndWhetherNew(mInitListener); + mTts = ttsAndNew.first; + // If the TTS object is not newly created, we need to run the setup on the settings side to + // ensure that we can use the TTS object. + if (!ttsAndNew.second) { + successSetup(); + } setTtsUtteranceProgressListener(); initSettings(); - - // Prevent restarting the TTS connection on rotation - setRetainInstance(true); } @Override @@ -227,13 +233,21 @@ public class TextToSpeechSettings extends SettingsPreferenceFragment return; } if (!mTts.getDefaultEngine().equals(mTts.getCurrentEngine())) { + final TextToSpeechViewModel ttsViewModel = + ViewModelProviders.of(this).get(TextToSpeechViewModel.class); try { - mTts.shutdown(); - mTts = null; + // If the current engine isn't the default engine shut down the current engine in + // preparation for creating the new engine. + ttsViewModel.shutdownTts(); } catch (Exception e) { Log.e(TAG, "Error shutting down TTS engine" + e); } - mTts = new TextToSpeech(getActivity().getApplicationContext(), mInitListener); + final Pair ttsAndNew = + ttsViewModel.getTtsAndWhetherNew(mInitListener); + mTts = ttsAndNew.first; + if (!ttsAndNew.second) { + successSetup(); + } setTtsUtteranceProgressListener(); initSettings(); } else { @@ -276,15 +290,6 @@ public class TextToSpeechSettings extends SettingsPreferenceFragment }); } - @Override - public void onDestroy() { - super.onDestroy(); - if (mTts != null) { - mTts.shutdown(); - mTts = null; - } - } - @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); @@ -375,8 +380,7 @@ public class TextToSpeechSettings extends SettingsPreferenceFragment public void onInitEngine(int status) { if (status == TextToSpeech.SUCCESS) { if (DBG) Log.d(TAG, "TTS engine for settings screen initialized."); - checkDefaultLocale(); - getActivity().runOnUiThread(() -> mLocalePreference.setEnabled(true)); + successSetup(); } else { if (DBG) { Log.d(TAG, @@ -386,6 +390,12 @@ public class TextToSpeechSettings extends SettingsPreferenceFragment } } + /** Initialize TTS Settings on successful engine init. */ + private void successSetup() { + checkDefaultLocale(); + getActivity().runOnUiThread(() -> mLocalePreference.setEnabled(true)); + } + private void checkDefaultLocale() { Locale defaultLocale = mTts.getDefaultLanguage(); if (defaultLocale == null) { diff --git a/src/com/android/settings/tts/TextToSpeechViewModel.java b/src/com/android/settings/tts/TextToSpeechViewModel.java new file mode 100644 index 00000000000..3c92010b67f --- /dev/null +++ b/src/com/android/settings/tts/TextToSpeechViewModel.java @@ -0,0 +1,70 @@ +/* + * Copyright 2020 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.app.Application; +import android.speech.tts.TextToSpeech; +import android.util.Pair; + +import androidx.lifecycle.AndroidViewModel; + +/** + * A helper view model to protect the TTS object from being destroyed and + * recreated on orientation change. +*/ +public class TextToSpeechViewModel extends AndroidViewModel { + private TextToSpeech mTts; + + // Save the application so we can use it as the TTS context + private final Application mApplication; + + public TextToSpeechViewModel(Application application) { + super(application); + + mApplication = application; + } + + /* + * Since the view model now controls the TTS object, we need to handle shutting it down + * ourselves. + */ + @Override + protected void onCleared() { + shutdownTts(); + } + + protected void shutdownTts() { + mTts.shutdown(); + mTts = null; + } + + /* + * An accessor method to get the TTS object. Returns a pair of the TTS object and a boolean + * indicating whether the TTS object was newly created or not. + */ + protected Pair getTtsAndWhetherNew( + TextToSpeech.OnInitListener listener) { + boolean ttsCreated = false; + if (mTts == null) { + mTts = new TextToSpeech(this.mApplication, listener); + ttsCreated = true; + } + + return Pair.create(mTts, ttsCreated); + } + +}