Volume header changes

- Show always
- Update locale immediately

Bug: 31847162
Bug: 33277887
Test: manual
Change-Id: I89c5cbddf08df3a9eba20f2fb3b76eec69926777
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index c8b3b69d..967a84c 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1121,19 +1121,18 @@
     <!-- Toast shown when user unlocks screen and managed profile activity is in the foreground -->
     <string name="managed_profile_foreground_toast">You\'re using your work profile</string>
 
-    <string-array name="volume_stream_titles">
-        <item>Call</item> <!-- STREAM_VOICE_CALL -->
-        <item>System</item> <!-- STREAM_SYSTEM -->
-        <item>Ring</item> <!-- STREAM_RING -->
-        <item>Media</item> <!-- STREAM_MUSIC -->
-        <item>Alarm</item> <!-- STREAM_ALARM -->
-        <item></item> <!-- STREAM_NOTIFICATION -->
-        <item>Bluetooth</item> <!-- STREAM_BLUETOOTH_SCO -->
-        <item></item> <!-- STREAM_SYSTEM_ENFORCED -->
-        <item></item> <!-- STREAM_DTMF -->
-        <item></item> <!-- STREAM_TTS -->
-        <item>Accessibility</item> <!-- STREAM_ACCESSIBILITY -->
-    </string-array>
+    <!-- volume stream names. All nouns. -->
+    <string name="stream_voice_call">Call</string> <!-- STREAM_VOICE_CALL -->
+    <string name="stream_system">System</string> <!-- STREAM_SYSTEM -->
+    <string name="stream_ring">Ring</string> <!-- STREAM_RING -->
+    <string name="stream_music">Media</string> <!-- STREAM_MUSIC -->
+    <string name="stream_alarm">Alarm</string> <!-- STREAM_ALARM -->
+    <string name="stream_notification">Notification</string> <!-- STREAM_NOTIFICATION -->
+    <string name="stream_bluetooth_sco">Bluetooth</string> <!-- STREAM_BLUETOOTH_SCO -->
+    <string name="stream_system_enforced">System enforced</string> <!-- STREAM_SYSTEM_ENFORCED -->
+    <string name="stream_dtmf">Dual multi tone frequency</string> <!-- STREAM_DTMF -->
+    <string name="stream_tts">Text to speech</string> <!-- STREAM_TTS -->
+    <string name="stream_accessibility">Accessibility</string> <!-- STREAM_ACCESSIBILITY -->
 
     <string name="volume_stream_muted" translatable="false">%s silent</string>
     <string name="volume_stream_vibrate" translatable="false">%s vibrate</string>
diff --git a/packages/SystemUI/src/com/android/systemui/volume/SpTexts.java b/packages/SystemUI/src/com/android/systemui/volume/ConfigurableTexts.java
similarity index 74%
rename from packages/SystemUI/src/com/android/systemui/volume/SpTexts.java
rename to packages/SystemUI/src/com/android/systemui/volume/ConfigurableTexts.java
index d8e53db..20de5bb 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/SpTexts.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ConfigurableTexts.java
@@ -25,19 +25,23 @@
 import android.widget.TextView;
 
 /**
- * Capture initial sp values for registered textviews, and update properly when configuration
- * changes.
+ * Class for updating textviews on configuration change.
  */
-public class SpTexts {
+public class ConfigurableTexts {
 
     private final Context mContext;
     private final ArrayMap<TextView, Integer> mTexts = new ArrayMap<>();
+    private final ArrayMap<TextView, Integer> mTextLabels = new ArrayMap<>();
 
-    public SpTexts(Context context) {
+    public ConfigurableTexts(Context context) {
         mContext = context;
     }
 
     public int add(final TextView text) {
+        return add(text, -1);
+    }
+
+    public int add(final TextView text, final int labelResId) {
         if (text == null) return 0;
         final Resources res = mContext.getResources();
         final float fontScale = res.getConfiguration().fontScale;
@@ -55,6 +59,7 @@
                setTextSizeH(text, sp);
             }
         });
+        mTextLabels.put(text, labelResId);
         return sp;
     }
 
@@ -67,12 +72,25 @@
         text.setTextSize(TypedValue.COMPLEX_UNIT_SP, sp);
     }
 
+    private void setTextLabelH(TextView text, int labelResId) {
+        try {
+            if (labelResId >= 0) {
+                Util.setText(text, mContext.getString(labelResId));
+            }
+        } catch (Resources.NotFoundException e) {
+            // oh well.
+        }
+    }
+
     private final Runnable mUpdateAll = new Runnable() {
         @Override
         public void run() {
             for (int i = 0; i < mTexts.size(); i++) {
                 setTextSizeH(mTexts.keyAt(i), mTexts.valueAt(i));
             }
+            for (int i = 0; i < mTextLabels.size(); i++) {
+                setTextLabelH(mTextLabels.keyAt(i), mTextLabels.valueAt(i));
+            }
         }
     };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java b/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java
index c6e4356..89bc757 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java
@@ -36,7 +36,7 @@
 
     private final Context mContext;
     protected final LayoutInflater mInflater;
-    private final SpTexts mSpTexts;
+    private final ConfigurableTexts mConfigurableTexts;
 
     private Callback mCallback;
     protected Object mSelectedValue;
@@ -46,7 +46,7 @@
         mContext = context;
         mInflater = LayoutInflater.from(mContext);
         setOrientation(HORIZONTAL);
-        mSpTexts = new SpTexts(mContext);
+        mConfigurableTexts = new ConfigurableTexts(mContext);
     }
 
     public void setCallback(Callback callback) {
@@ -97,15 +97,11 @@
                 fireInteraction();
             }
         });
-        mSpTexts.add(b);
+        mConfigurableTexts.add(b, labelResId);
     }
 
-    public void updateLocale() {
-        for (int i = 0; i < getChildCount(); i++) {
-            final Button b = (Button) getChildAt(i);
-            final int labelResId = (Integer) b.getTag(LABEL_RES_KEY);
-            b.setText(labelResId);
-        }
+    public void update() {
+        mConfigurableTexts.update();
     }
 
     private void fireOnSelected(boolean fromClick) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
index 42b06b2..d23ebc1 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
@@ -46,6 +46,7 @@
 import android.transition.TransitionManager;
 import android.util.DisplayMetrics;
 import android.util.Log;
+import android.util.Slog;
 import android.util.SparseBooleanArray;
 import android.view.Gravity;
 import android.view.MotionEvent;
@@ -109,7 +110,7 @@
     private ViewGroup mDialogContentView;
     private ImageButton mExpandButton;
     private final List<VolumeRow> mRows = new ArrayList<>();
-    private SpTexts mSpTexts;
+    private ConfigurableTexts mConfigurableTexts;
     private final SparseBooleanArray mDynamic = new SparseBooleanArray();
     private final KeyguardManager mKeyguard;
     private final AudioManager mAudioManager;
@@ -128,7 +129,6 @@
     private boolean mExpanded;
 
     private int mActiveStream;
-    private boolean mShowHeaders = VolumePrefs.DEFAULT_SHOW_HEADERS;
     private boolean mAutomute = VolumePrefs.DEFAULT_ENABLE_AUTOMUTE;
     private boolean mSilentMode = VolumePrefs.DEFAULT_ENABLE_SILENT_MODE;
     private State mState;
@@ -173,7 +173,7 @@
     private void initDialog() {
         mDialog = new CustomDialog(mContext);
 
-        mSpTexts = new SpTexts(mContext);
+        mConfigurableTexts = new ConfigurableTexts(mContext);
         mHovering = false;
         mShowing = false;
         mWindow = mDialog.getWindow();
@@ -294,12 +294,6 @@
         mHandler.obtainMessage(H.SET_STREAM_IMPORTANT, stream, important ? 1 : 0).sendToTarget();
     }
 
-    public void setShowHeaders(boolean showHeaders) {
-        if (showHeaders == mShowHeaders) return;
-        mShowHeaders = showHeaders;
-        mHandler.sendEmptyMessage(H.RECHECK_ALL);
-    }
-
     public void setAutomute(boolean automute) {
         if (mAutomute == automute) return;
         mAutomute = automute;
@@ -357,7 +351,6 @@
         writer.println(mExpandButtonAnimationRunning);
         writer.print("  mActiveStream: "); writer.println(mActiveStream);
         writer.print("  mDynamic: "); writer.println(mDynamic);
-        writer.print("  mShowHeaders: "); writer.println(mShowHeaders);
         writer.print("  mAutomute: "); writer.println(mAutomute);
         writer.print("  mSilentMode: "); writer.println(mSilentMode);
         writer.print("  mCollapseTime: "); writer.println(mCollapseTime);
@@ -385,11 +378,9 @@
         row.view.setTag(row);
         row.header = (TextView) row.view.findViewById(R.id.volume_row_header);
         row.header.setId(20 * row.stream);
-        mSpTexts.add(row.header);
         row.slider = (SeekBar) row.view.findViewById(R.id.volume_row_slider);
         row.slider.setOnSeekBarChangeListener(new VolumeSeekBarChangeListener(row));
         row.anim = null;
-        row.cachedShowHeaders = VolumePrefs.DEFAULT_SHOW_HEADERS;
 
         // forward events above the slider into the slider
         row.view.setOnTouchListener(new OnTouchListener() {
@@ -617,8 +608,8 @@
             final boolean isActive = row == activeRow;
             final boolean shouldBeVisible = shouldBeVisibleH(row, isActive);
             Util.setVisOrGone(row.view, shouldBeVisible);
+            Util.setVisOrGone(row.header, shouldBeVisible);
             if (row.view.isShown()) {
-                updateVolumeRowHeaderVisibleH(row);
                 updateVolumeRowSliderTintH(row, isActive);
             }
         }
@@ -731,11 +722,9 @@
             row.slider.setMax(max);
         }
 
-        // update header visible
-        updateVolumeRowHeaderVisibleH(row);
-
         // update header text
-        Util.setText(row.header, ss.name);
+        Util.setText(row.header, getStreamLabelH(ss));
+        mConfigurableTexts.add(row.header, ss.name);
 
         // update icon
         final boolean iconEnabled = (mAutomute || ss.muteSupported) && !zenMuted;
@@ -768,31 +757,31 @@
                 if (isRingVibrate) {
                     row.icon.setContentDescription(mContext.getString(
                             R.string.volume_stream_content_description_unmute,
-                            ss.name));
+                            getStreamLabelH(ss)));
                 } else {
                     if (mController.hasVibrator()) {
                         row.icon.setContentDescription(mContext.getString(
                                 R.string.volume_stream_content_description_vibrate,
-                                ss.name));
+                                getStreamLabelH(ss)));
                     } else {
                         row.icon.setContentDescription(mContext.getString(
                                 R.string.volume_stream_content_description_mute,
-                                ss.name));
+                                getStreamLabelH(ss)));
                     }
                 }
             } else {
                 if (ss.muted || mAutomute && ss.level == 0) {
                    row.icon.setContentDescription(mContext.getString(
                            R.string.volume_stream_content_description_unmute,
-                           ss.name));
+                           getStreamLabelH(ss)));
                 } else {
                     row.icon.setContentDescription(mContext.getString(
                             R.string.volume_stream_content_description_mute,
-                            ss.name));
+                            getStreamLabelH(ss)));
                 }
             }
         } else {
-            row.icon.setContentDescription(ss.name);
+            row.icon.setContentDescription(getStreamLabelH(ss));
         }
 
         // update slider
@@ -802,15 +791,6 @@
         updateVolumeRowSliderH(row, enableSlider, vlevel);
     }
 
-    private void updateVolumeRowHeaderVisibleH(VolumeRow row) {
-        final boolean dynamic = row.ss != null && row.ss.dynamic;
-        final boolean showHeaders = mExpanded && (mShowHeaders || dynamic);
-        if (row.cachedShowHeaders != showHeaders) {
-            row.cachedShowHeaders = showHeaders;
-            Util.setVisOrGone(row.header, showHeaders);
-        }
-    }
-
     private void updateVolumeRowSliderTintH(VolumeRow row, boolean isActive) {
         if (isActive && mExpanded) {
             row.slider.requestFocus();
@@ -920,6 +900,18 @@
         rescheduleTimeoutH();
     }
 
+    private String getStreamLabelH(StreamState ss) {
+        if (ss.remoteLabel != null) {
+            return ss.remoteLabel;
+        }
+        try {
+            return mContext.getString(ss.name);
+        } catch (Resources.NotFoundException e) {
+            Slog.e(TAG, "Can't find translation for stream " + ss);
+            return "";
+        }
+    }
+
     private AutoTransition getTransistion() {
         AutoTransition transition = new AutoTransition();
         transition.setDuration(mExpandButtonAnimationDuration);
@@ -995,7 +987,7 @@
                 mDensity = density;
             }
             updateWindowWidthH();
-            mSpTexts.update();
+            mConfigurableTexts.update();
             mZenFooter.onConfigurationChanged();
         }
 
@@ -1125,7 +1117,7 @@
                 if (mShowing) {
                     event.getText().add(mContext.getString(
                             R.string.volume_dialog_accessibility_shown_message,
-                            getActiveRow().ss.name));
+                            getStreamLabelH(getActiveRow().ss)));
                     return true;
                 }
             }
@@ -1253,7 +1245,6 @@
         private int cachedIconRes;
         private ColorStateList cachedSliderTint;
         private int iconState;  // from Events
-        private boolean cachedShowHeaders = VolumePrefs.DEFAULT_SHOW_HEADERS;
         private ObjectAnimator anim;  // slider progress animation for non-touch-related updates
         private int animTargetProgress;
         private int lastAudibleLevel = 1;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java
index bb5632b..17dd8d6 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.volume;
 
+import android.annotation.IntegerRes;
 import android.app.NotificationManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -41,6 +42,7 @@
 import android.os.Vibrator;
 import android.provider.Settings;
 import android.service.notification.Condition;
+import android.util.ArrayMap;
 import android.util.Log;
 import android.util.SparseArray;
 
@@ -67,19 +69,20 @@
     private static final int DYNAMIC_STREAM_START_INDEX = 100;
     private static final int VIBRATE_HINT_DURATION = 50;
 
-    private static final int[] STREAMS = {
-        AudioSystem.STREAM_ALARM,
-        AudioSystem.STREAM_BLUETOOTH_SCO,
-        AudioSystem.STREAM_DTMF,
-        AudioSystem.STREAM_MUSIC,
-        AudioSystem.STREAM_NOTIFICATION,
-        AudioSystem.STREAM_RING,
-        AudioSystem.STREAM_SYSTEM,
-        AudioSystem.STREAM_SYSTEM_ENFORCED,
-        AudioSystem.STREAM_TTS,
-        AudioSystem.STREAM_VOICE_CALL,
-        AudioSystem.STREAM_ACCESSIBILITY,
-    };
+    private static final ArrayMap<Integer, Integer> STREAMS = new ArrayMap<>();
+    static {
+        STREAMS.put(AudioSystem.STREAM_ALARM, R.string.stream_alarm);
+        STREAMS.put(AudioSystem.STREAM_BLUETOOTH_SCO, R.string.stream_bluetooth_sco);
+        STREAMS.put(AudioSystem.STREAM_DTMF, R.string.stream_dtmf);
+        STREAMS.put(AudioSystem.STREAM_MUSIC, R.string.stream_music);
+        STREAMS.put(AudioSystem.STREAM_NOTIFICATION, R.string.stream_notification);
+        STREAMS.put(AudioSystem.STREAM_RING, R.string.stream_ring);
+        STREAMS.put(AudioSystem.STREAM_SYSTEM, R.string.stream_system);
+        STREAMS.put(AudioSystem.STREAM_SYSTEM_ENFORCED, R.string.stream_system_enforced);
+        STREAMS.put(AudioSystem.STREAM_TTS, R.string.stream_tts);
+        STREAMS.put(AudioSystem.STREAM_VOICE_CALL, R.string.stream_voice_call);
+        STREAMS.put(AudioSystem.STREAM_ACCESSIBILITY, R.string.stream_accessibility);
+    }
 
     private final HandlerThread mWorkerThread;
     private final W mWorker;
@@ -92,7 +95,6 @@
     private final MediaSessions mMediaSessions;
     private final C mCallbacks = new C();
     private final State mState = new State();
-    private final String[] mStreamTitles;
     private final MediaSessionsCallbacks mMediaSessionsCallbacksW = new MediaSessionsCallbacks();
     private final Vibrator mVibrator;
     private final boolean mHasVibrator;
@@ -120,26 +122,6 @@
         mObserver = new SettingObserver(mWorker);
         mObserver.init();
         mReceiver.init();
-        final String[] titles =
-                mContext.getResources().getStringArray(R.array.volume_stream_titles);
-        if (STREAMS.length == titles.length) {
-            mStreamTitles = titles;
-        } else if (STREAMS.length > titles.length) {
-            Log.e(TAG, String.format("Missing stream titles (found %d, expected %d): "
-                    + " invalid resources for volume_stream_titles",
-                    titles.length, STREAMS.length));
-            mStreamTitles = new String[STREAMS.length];
-            System.arraycopy(titles, 0, mStreamTitles, 0, titles.length);
-            for (int i = titles.length ; i < STREAMS.length ; i++) {
-                mStreamTitles[i] = "";
-            }
-        } else { // STREAMS.length < titles.length
-            Log.e(TAG, String.format("Too many stream titles (found %d, expected %d): "
-                    + " invalid resources for volume_stream_titles",
-                    titles.length, STREAMS.length));
-            mStreamTitles = new String[STREAMS.length];
-            System.arraycopy(titles, 0, mStreamTitles, 0, STREAMS.length);
-        }
         mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
         mHasVibrator = mVibrator != null && mVibrator.hasVibrator();
     }
@@ -385,14 +367,14 @@
     }
 
     private void onGetStateW() {
-        for (int stream : STREAMS) {
+        for (int stream : STREAMS.keySet()) {
             updateStreamLevelW(stream, getAudioManagerStreamVolume(stream));
             streamStateW(stream).levelMin = getAudioManagerStreamMinVolume(stream);
             streamStateW(stream).levelMax = getAudioManagerStreamMaxVolume(stream);
             updateStreamMuteW(stream, mAudio.isStreamMute(stream));
             final StreamState ss = streamStateW(stream);
             ss.muteSupported = mAudio.isStreamAffectedByMute(stream);
-            ss.name = mStreamTitles[stream];
+            ss.name = STREAMS.get(stream);
             checkRoutedToBluetoothW(stream);
         }
         updateRingerModeExternalW(mAudio.getRingerMode());
@@ -912,8 +894,9 @@
                 ss.level = pi.getCurrentVolume();
                 changed = true;
             }
-            if (!Objects.equals(ss.name, name)) {
-                ss.name = name;
+            if (!Objects.equals(ss.remoteLabel, name)) {
+                ss.name = -1;
+                ss.remoteLabel = name;
                 changed = true;
             }
             if (changed) {
@@ -975,7 +958,8 @@
         public int levelMax;
         public boolean muted;
         public boolean muteSupported;
-        public String name;
+        public @IntegerRes int name;
+        public String remoteLabel;
         public boolean routedToBluetooth;
 
         public StreamState copy() {
@@ -987,6 +971,7 @@
             rt.muted = muted;
             rt.muteSupported = muteSupported;
             rt.name = name;
+            rt.remoteLabel = remoteLabel;
             rt.routedToBluetooth = routedToBluetooth;
             return rt;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java b/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
index 995ecae..4e4832c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
@@ -38,7 +38,7 @@
     private static final String TAG = Util.logTag(ZenFooter.class);
 
     private final Context mContext;
-    private final SpTexts mSpTexts;
+    private final ConfigurableTexts mConfigurableTexts;
 
     private ImageView mIcon;
     private TextView mSummaryLine1;
@@ -51,7 +51,7 @@
     public ZenFooter(Context context, AttributeSet attrs) {
         super(context, attrs);
         mContext = context;
-        mSpTexts = new SpTexts(mContext);
+        mConfigurableTexts = new ConfigurableTexts(mContext);
         final LayoutTransition layoutTransition = new LayoutTransition();
         layoutTransition.setDuration(new ValueAnimator().getDuration() / 2);
         setLayoutTransition(layoutTransition);
@@ -64,9 +64,9 @@
         mSummaryLine1 = (TextView) findViewById(R.id.volume_zen_summary_line_1);
         mSummaryLine2 = (TextView) findViewById(R.id.volume_zen_summary_line_2);
         mEndNowButton = (TextView) findViewById(R.id.volume_zen_end_now);
-        mSpTexts.add(mSummaryLine1);
-        mSpTexts.add(mSummaryLine2);
-        mSpTexts.add(mEndNowButton);
+        mConfigurableTexts.add(mSummaryLine1);
+        mConfigurableTexts.add(mSummaryLine2);
+        mConfigurableTexts.add(mEndNowButton, R.string.volume_zen_end_now);
     }
 
     public void init(final ZenModeController controller) {
@@ -130,8 +130,7 @@
     }
 
     public void onConfigurationChanged() {
-        Util.setText(mEndNowButton, mContext.getString(R.string.volume_zen_end_now));
-        mSpTexts.update();
+        mConfigurableTexts.update();
     }
 
     private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
index 12a00e9..98e89e4 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
@@ -92,7 +92,7 @@
     private final ZenPrefs mPrefs;
     private final TransitionHelper mTransitionHelper = new TransitionHelper();
     private final Uri mForeverId;
-    private final SpTexts mSpTexts;
+    private final ConfigurableTexts mConfigurableTexts;
 
     private String mTag = TAG + "/" + Integer.toHexString(System.identityHashCode(this));
 
@@ -131,7 +131,7 @@
         mPrefs = new ZenPrefs();
         mInflater = LayoutInflater.from(mContext.getApplicationContext());
         mForeverId = Condition.newId(mContext).appendPath("forever").build();
-        mSpTexts = new SpTexts(mContext);
+        mConfigurableTexts = new ConfigurableTexts(mContext);
         mVoiceCapable = Util.isVoiceCapable(mContext);
         mZenModeConditionLayoutId = R.layout.zen_mode_condition;
         mZenModeButtonLayoutId = R.layout.zen_mode_button;
@@ -175,7 +175,6 @@
         createZenButtons();
         mZenIntroduction = findViewById(R.id.zen_introduction);
         mZenIntroductionMessage = (TextView) findViewById(R.id.zen_introduction_message);
-        mSpTexts.add(mZenIntroductionMessage);
         mZenIntroductionConfirm = findViewById(R.id.zen_introduction_confirm);
         mZenIntroductionConfirm.setOnClickListener(new OnClickListener() {
             @Override
@@ -193,7 +192,7 @@
                 }
             }
         });
-        mSpTexts.add(mZenIntroductionCustomize);
+        mConfigurableTexts.add(mZenIntroductionCustomize, R.string.zen_priority_customize_button);
 
         mZenConditions = (LinearLayout) findViewById(R.id.zen_conditions);
         mZenAlarmWarning = (TextView) findViewById(R.id.zen_alarm_warning);
@@ -204,11 +203,9 @@
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
+        mConfigurableTexts.update();
         if (mZenButtons != null) {
-            mZenButtons.updateLocale();
-        }
-        if (mZenIntroductionCustomize != null) {
-            mZenIntroductionCustomize.setText(R.string.zen_priority_customize_button);
+            mZenButtons.update();
         }
     }
 
@@ -341,10 +338,6 @@
         hideAllConditions();
     }
 
-    public void updateLocale() {
-        mZenButtons.updateLocale();
-    }
-
     private void setExitCondition(Condition exitCondition) {
         if (Objects.equals(mExitCondition, exitCondition)) return;
         mExitCondition = exitCondition;
@@ -439,9 +432,11 @@
         mZenButtons.setVisibility(mHidden ? GONE : VISIBLE);
         mZenIntroduction.setVisibility(introduction ? VISIBLE : GONE);
         if (introduction) {
-            mZenIntroductionMessage.setText(zenImportant ? R.string.zen_priority_introduction
+            mConfigurableTexts.add(mZenIntroductionMessage, zenImportant
+                    ? R.string.zen_priority_introduction
                     : mVoiceCapable ? R.string.zen_silence_introduction_voice
                     : R.string.zen_silence_introduction);
+            mConfigurableTexts.update();
             mZenIntroductionCustomize.setVisibility(zenImportant ? VISIBLE : GONE);
         }
         final String warning = computeAlarmWarningText(zenNone);
@@ -655,11 +650,11 @@
         }
         if (tag.line1 == null) {
             tag.line1 = (TextView) row.findViewById(android.R.id.text1);
-            mSpTexts.add(tag.line1);
+            mConfigurableTexts.add(tag.line1);
         }
         if (tag.line2 == null) {
             tag.line2 = (TextView) row.findViewById(android.R.id.text2);
-            mSpTexts.add(tag.line2);
+            mConfigurableTexts.add(tag.line2);
         }
         final String line1 = !TextUtils.isEmpty(condition.line1) ? condition.line1
                 : condition.summary;