1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.android.tv.settings.device.sound; 18 19 import android.content.ContentResolver; 20 import android.content.Context; 21 import android.media.AudioFormat; 22 import android.media.AudioManager; 23 import android.os.Bundle; 24 import android.provider.Settings; 25 import android.text.TextUtils; 26 27 import androidx.annotation.Keep; 28 import androidx.preference.ListPreference; 29 import androidx.preference.Preference; 30 import androidx.preference.PreferenceCategory; 31 import androidx.preference.PreferenceViewHolder; 32 import androidx.preference.SwitchPreference; 33 import androidx.preference.TwoStatePreference; 34 35 import com.android.internal.logging.nano.MetricsProto; 36 import com.android.settingslib.core.AbstractPreferenceController; 37 import com.android.tv.settings.PreferenceControllerFragment; 38 import com.android.tv.settings.R; 39 40 import java.util.ArrayList; 41 import java.util.List; 42 import java.util.Map; 43 44 /** 45 * The "Sound" screen in TV Settings. 46 */ 47 @Keep 48 public class SoundFragment extends PreferenceControllerFragment implements 49 Preference.OnPreferenceChangeListener { 50 51 static final String KEY_SOUND_EFFECTS = "sound_effects"; 52 static final String KEY_SURROUND_PASSTHROUGH = "surround_passthrough"; 53 static final String KEY_SURROUND_SOUND_CATEGORY = "surround_sound_category"; 54 static final String KEY_SURROUND_SOUND_FORMAT_PREFIX = "surround_sound_format_"; 55 56 static final String VAL_SURROUND_SOUND_AUTO = "auto"; 57 static final String VAL_SURROUND_SOUND_NEVER = "never"; 58 static final String VAL_SURROUND_SOUND_ALWAYS = "always"; 59 static final String VAL_SURROUND_SOUND_MANUAL = "manual"; 60 61 private AudioManager mAudioManager; 62 private Map<Integer, Boolean> mFormats; 63 private Map<Integer, Boolean> mReportedFormats; 64 private List<AbstractPreferenceController> mPreferenceControllers; 65 private PreferenceCategory mSurroundSoundCategoryPref; 66 newInstance()67 public static SoundFragment newInstance() { 68 return new SoundFragment(); 69 } 70 71 @Override onAttach(Context context)72 public void onAttach(Context context) { 73 mAudioManager = context.getSystemService(AudioManager.class); 74 mFormats = mAudioManager.getSurroundFormats(); 75 mReportedFormats = mAudioManager.getReportedSurroundFormats(); 76 super.onAttach(context); 77 } 78 79 @Override getPreferenceScreenResId()80 protected int getPreferenceScreenResId() { 81 return R.xml.sound; 82 } 83 84 @Override onCreatePreferences(Bundle savedInstanceState, String rootKey)85 public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 86 setPreferencesFromResource(R.xml.sound, null); 87 88 final TwoStatePreference soundPref = (TwoStatePreference) findPreference(KEY_SOUND_EFFECTS); 89 soundPref.setChecked(getSoundEffectsEnabled()); 90 91 final ListPreference surroundPref = 92 (ListPreference) findPreference(KEY_SURROUND_PASSTHROUGH); 93 surroundPref.setValue(getSurroundPassthroughSetting(getContext())); 94 surroundPref.setOnPreferenceChangeListener(this); 95 96 mSurroundSoundCategoryPref = 97 (PreferenceCategory) findPreference(KEY_SURROUND_SOUND_CATEGORY); 98 createFormatPreferences(); 99 updateFormatPreferencesStates(); 100 } 101 102 @Override onCreatePreferenceControllers(Context context)103 protected List<AbstractPreferenceController> onCreatePreferenceControllers(Context context) { 104 mPreferenceControllers = new ArrayList<>(mFormats.size()); 105 for (Map.Entry<Integer, Boolean> format : mFormats.entrySet()) { 106 mPreferenceControllers.add(new SoundFormatPreferenceController(context, 107 format.getKey() /*formatId*/, mFormats, mReportedFormats)); 108 } 109 return mPreferenceControllers; 110 } 111 112 /** Creates and adds switches for each surround sound format. */ createFormatPreferences()113 private void createFormatPreferences() { 114 for (Map.Entry<Integer, Boolean> format : mFormats.entrySet()) { 115 int formatId = format.getKey(); 116 boolean enabled = format.getValue(); 117 SwitchPreference pref = new SwitchPreference(getPreferenceManager().getContext()) { 118 @Override 119 public void onBindViewHolder(PreferenceViewHolder holder) { 120 super.onBindViewHolder(holder); 121 // Enabling the view will ensure that the preference is focusable even if it 122 // the preference is disabled. This allows the user to scroll down over the 123 // disabled surround sound formats and see them all. 124 holder.itemView.setEnabled(true); 125 } 126 }; 127 pref.setTitle(getFormatDisplayName(formatId)); 128 pref.setKey(KEY_SURROUND_SOUND_FORMAT_PREFIX + formatId); 129 pref.setChecked(enabled); 130 mSurroundSoundCategoryPref.addPreference(pref); 131 } 132 } 133 134 /** 135 * @return the display name for each surround sound format. 136 */ getFormatDisplayName(int formatId)137 private String getFormatDisplayName(int formatId) { 138 switch (formatId) { 139 case AudioFormat.ENCODING_AC3: 140 return getContext().getResources().getString(R.string.surround_sound_format_ac3); 141 case AudioFormat.ENCODING_E_AC3: 142 return getContext().getResources().getString(R.string.surround_sound_format_e_ac3); 143 case AudioFormat.ENCODING_DTS: 144 return getContext().getResources().getString(R.string.surround_sound_format_dts); 145 case AudioFormat.ENCODING_DTS_HD: 146 return getContext().getResources().getString(R.string.surround_sound_format_dts_hd); 147 default: 148 // Fallback in case new formats have been added that we don't know of. 149 return AudioFormat.toDisplayName(formatId); 150 } 151 } 152 updateFormatPreferencesStates()153 private void updateFormatPreferencesStates() { 154 for (AbstractPreferenceController controller : mPreferenceControllers) { 155 Preference preference = mSurroundSoundCategoryPref.findPreference( 156 controller.getPreferenceKey()); 157 if (preference != null) { 158 controller.updateState(preference); 159 } 160 } 161 } 162 163 @Override onPreferenceTreeClick(Preference preference)164 public boolean onPreferenceTreeClick(Preference preference) { 165 if (TextUtils.equals(preference.getKey(), KEY_SOUND_EFFECTS)) { 166 final TwoStatePreference soundPref = (TwoStatePreference) preference; 167 setSoundEffectsEnabled(soundPref.isChecked()); 168 } 169 return super.onPreferenceTreeClick(preference); 170 } 171 172 @Override onPreferenceChange(Preference preference, Object newValue)173 public boolean onPreferenceChange(Preference preference, Object newValue) { 174 if (TextUtils.equals(preference.getKey(), KEY_SURROUND_PASSTHROUGH)) { 175 final String selection = (String) newValue; 176 switch (selection) { 177 case VAL_SURROUND_SOUND_AUTO: 178 setSurroundPassthroughSetting(Settings.Global.ENCODED_SURROUND_OUTPUT_AUTO); 179 break; 180 case VAL_SURROUND_SOUND_NEVER: 181 setSurroundPassthroughSetting(Settings.Global.ENCODED_SURROUND_OUTPUT_NEVER); 182 break; 183 case VAL_SURROUND_SOUND_ALWAYS: 184 setSurroundPassthroughSetting(Settings.Global.ENCODED_SURROUND_OUTPUT_ALWAYS); 185 break; 186 case VAL_SURROUND_SOUND_MANUAL: 187 setSurroundPassthroughSetting(Settings.Global.ENCODED_SURROUND_OUTPUT_MANUAL); 188 break; 189 default: 190 throw new IllegalArgumentException("Unknown surround sound pref value: " 191 + selection); 192 } 193 updateFormatPreferencesStates(); 194 return true; 195 } 196 return true; 197 } 198 getSoundEffectsEnabled()199 private boolean getSoundEffectsEnabled() { 200 return getSoundEffectsEnabled(getActivity().getContentResolver()); 201 } 202 getSoundEffectsEnabled(ContentResolver contentResolver)203 public static boolean getSoundEffectsEnabled(ContentResolver contentResolver) { 204 return Settings.System.getInt(contentResolver, Settings.System.SOUND_EFFECTS_ENABLED, 1) 205 != 0; 206 } 207 setSoundEffectsEnabled(boolean enabled)208 private void setSoundEffectsEnabled(boolean enabled) { 209 if (enabled) { 210 mAudioManager.loadSoundEffects(); 211 } else { 212 mAudioManager.unloadSoundEffects(); 213 } 214 Settings.System.putInt(getActivity().getContentResolver(), 215 Settings.System.SOUND_EFFECTS_ENABLED, enabled ? 1 : 0); 216 } 217 setSurroundPassthroughSetting(int newVal)218 private void setSurroundPassthroughSetting(int newVal) { 219 Settings.Global.putInt(getContext().getContentResolver(), 220 Settings.Global.ENCODED_SURROUND_OUTPUT, newVal); 221 } 222 getSurroundPassthroughSetting(Context context)223 static String getSurroundPassthroughSetting(Context context) { 224 final int value = Settings.Global.getInt(context.getContentResolver(), 225 Settings.Global.ENCODED_SURROUND_OUTPUT, 226 Settings.Global.ENCODED_SURROUND_OUTPUT_AUTO); 227 228 switch (value) { 229 case Settings.Global.ENCODED_SURROUND_OUTPUT_AUTO: 230 default: 231 return VAL_SURROUND_SOUND_AUTO; 232 case Settings.Global.ENCODED_SURROUND_OUTPUT_NEVER: 233 return VAL_SURROUND_SOUND_NEVER; 234 // On Android P ALWAYS is replaced by MANUAL. 235 case Settings.Global.ENCODED_SURROUND_OUTPUT_ALWAYS: 236 case Settings.Global.ENCODED_SURROUND_OUTPUT_MANUAL: 237 return VAL_SURROUND_SOUND_MANUAL; 238 } 239 } 240 241 @Override getMetricsCategory()242 public int getMetricsCategory() { 243 return MetricsProto.MetricsEvent.SOUND; 244 } 245 } 246