1 /* 2 * Copyright (C) 2020 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.displaysound; 18 19 import static com.android.tv.settings.overlay.FlavorUtils.FLAVOR_CLASSIC; 20 import static com.android.tv.settings.util.InstrumentationUtils.logEntrySelected; 21 import static com.android.tv.settings.util.InstrumentationUtils.logToggleInteracted; 22 23 import android.app.tvsettings.TvSettingsEnums; 24 import android.content.Context; 25 import android.media.AudioFormat; 26 import android.media.AudioManager; 27 import android.os.Bundle; 28 import android.provider.Settings; 29 import android.widget.Toast; 30 31 import androidx.annotation.Keep; 32 import androidx.preference.Preference; 33 import androidx.preference.PreferenceCategory; 34 import androidx.preference.PreferenceGroup; 35 import androidx.preference.PreferenceViewHolder; 36 import androidx.preference.SwitchPreference; 37 38 import com.android.internal.annotations.VisibleForTesting; 39 import com.android.settingslib.core.AbstractPreferenceController; 40 import com.android.tv.settings.PreferenceControllerFragment; 41 import com.android.tv.settings.R; 42 import com.android.tv.settings.RadioPreference; 43 import com.android.tv.settings.overlay.FlavorUtils; 44 45 import java.util.ArrayList; 46 import java.util.List; 47 import java.util.Map; 48 49 /** 50 * The "Advanced sound settings" screen in TV Settings. 51 */ 52 @Keep 53 public class AdvancedVolumeFragment extends PreferenceControllerFragment { 54 static final String KEY_ADVANCED_SOUND_OPTION = "advanced_sound_settings_option"; 55 static final String KEY_SURROUND_SOUND_AUTO = "surround_sound_auto"; 56 static final String KEY_SURROUND_SOUND_NONE = "surround_sound_none"; 57 static final String KEY_SURROUND_SOUND_MANUAL = "surround_sound_manual"; 58 static final String KEY_SURROUND_SOUND_FORMAT_PREFIX = "surround_sound_format_"; 59 static final String KEY_SURROUND_SOUND_FORMAT_INFO_PREFIX = "surround_sound_format_info_"; 60 static final String KEY_SUPPORTED_SURROUND_SOUND = "supported_formats"; 61 static final String KEY_UNSUPPORTED_SURROUND_SOUND = "unsupported_formats"; 62 static final String KEY_FORMAT_INFO = "surround_sound_format_info"; 63 static final String KEY_SHOW_HIDE_FORMAT_INFO = "surround_sound_show_hide_format_info"; 64 static final String KEY_ENABLED_FORMATS = "enabled_formats"; 65 static final String KEY_DISABLED_FORMATS = "disabled_formats"; 66 67 static final int[] SURROUND_SOUND_DISPLAY_ORDER = { 68 AudioFormat.ENCODING_AC3, AudioFormat.ENCODING_E_AC3, AudioFormat.ENCODING_DOLBY_TRUEHD, 69 AudioFormat.ENCODING_E_AC3_JOC, AudioFormat.ENCODING_DOLBY_MAT, 70 AudioFormat.ENCODING_DTS, AudioFormat.ENCODING_DTS_HD, AudioFormat.ENCODING_DTS_UHD, 71 AudioFormat.ENCODING_DRA 72 }; 73 74 private Map<Integer, Boolean> mFormats; 75 private List<Integer> mReportedFormats; 76 private AudioManager mAudioManager; 77 private List<AbstractPreferenceController> mPreferenceControllers; 78 private PreferenceCategory mSupportedFormatsPreferenceCategory; 79 private PreferenceCategory mUnsupportedFormatsPreferenceCategory; 80 private PreferenceCategory mFormatsInfoPreferenceCategory; 81 private PreferenceCategory mEnabledFormatsPreferenceCategory; 82 private PreferenceCategory mDisabledFormatsPreferenceCategory; 83 84 @Override onAttach(Context context)85 public void onAttach(Context context) { 86 mAudioManager = getAudioManager(); 87 mFormats = mAudioManager.getSurroundFormats(); 88 mReportedFormats = mAudioManager.getReportedSurroundFormats(); 89 super.onAttach(context); 90 } 91 92 @Override getPreferenceScreenResId()93 protected int getPreferenceScreenResId() { 94 return R.xml.advanced_sound; 95 } 96 97 @Override onCreatePreferences(Bundle savedInstanceState, String rootKey)98 public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 99 setPreferencesFromResource(R.xml.advanced_sound, null /* key */); 100 101 String surroundSoundSettingKey = getSurroundPassthroughSetting(getContext()); 102 selectRadioPreference(findPreference(surroundSoundSettingKey)); 103 104 // Do not show sidebar info texts in case of 1 panel settings. 105 if (FlavorUtils.getFlavor(getContext()) != FLAVOR_CLASSIC) { 106 createInfoFragments(); 107 } 108 109 createFormatInfoPreferences(); 110 createFormatPreferences(); 111 if (surroundSoundSettingKey == KEY_SURROUND_SOUND_MANUAL) { 112 showFormatPreferences(); 113 } else { 114 hideFormatPreferences(); 115 } 116 } 117 118 @Override onCreatePreferenceControllers(Context context)119 protected List<AbstractPreferenceController> onCreatePreferenceControllers(Context context) { 120 mPreferenceControllers = new ArrayList<>(mFormats.size()); 121 for (Map.Entry<Integer, Boolean> format : mFormats.entrySet()) { 122 mPreferenceControllers.add(new SoundFormatPreferenceController(context, 123 format.getKey() /*formatId*/, mAudioManager, mFormats, mReportedFormats)); 124 } 125 return mPreferenceControllers; 126 } 127 128 @Override onPreferenceTreeClick(Preference preference)129 public boolean onPreferenceTreeClick(Preference preference) { 130 final String key = preference.getKey(); 131 if (key == null) { 132 return super.onPreferenceTreeClick(preference); 133 } 134 135 if (preference instanceof RadioPreference) { 136 selectRadioPreference(preference); 137 138 switch (key) { 139 case KEY_SURROUND_SOUND_AUTO: { 140 logEntrySelected( 141 TvSettingsEnums.DISPLAY_SOUND_ADVANCED_SOUNDS_SELECT_FORMATS_AUTO); 142 mAudioManager.setEncodedSurroundMode( 143 Settings.Global.ENCODED_SURROUND_OUTPUT_AUTO); 144 hideFormatPreferences(); 145 break; 146 } 147 case KEY_SURROUND_SOUND_NONE: { 148 logEntrySelected( 149 TvSettingsEnums.DISPLAY_SOUND_ADVANCED_SOUNDS_SELECT_FORMATS_NONE); 150 mAudioManager.setEncodedSurroundMode( 151 Settings.Global.ENCODED_SURROUND_OUTPUT_NEVER); 152 hideFormatPreferences(); 153 break; 154 } 155 case KEY_SURROUND_SOUND_MANUAL: { 156 logEntrySelected( 157 TvSettingsEnums.DISPLAY_SOUND_ADVANCED_SOUNDS_SELECT_FORMATS_MANUAL); 158 mAudioManager.setEncodedSurroundMode( 159 Settings.Global.ENCODED_SURROUND_OUTPUT_MANUAL); 160 showFormatPreferences(); 161 break; 162 } 163 default: 164 throw new IllegalArgumentException("Unknown surround sound pref value: " 165 + key); 166 } 167 updateFormatPreferencesStates(); 168 } 169 170 if (key.equals(KEY_SHOW_HIDE_FORMAT_INFO)) { 171 if (preference.getTitle().equals( 172 getContext().getString(R.string.surround_sound_hide_formats))) { 173 hideFormatInfoPreferences(); 174 preference.setTitle(R.string.surround_sound_show_formats); 175 } else { 176 showFormatInfoPreferences(); 177 preference.setTitle(R.string.surround_sound_hide_formats); 178 } 179 } 180 181 if (key.contains(KEY_SURROUND_SOUND_FORMAT_INFO_PREFIX)) { 182 if (preference.getParent() == mEnabledFormatsPreferenceCategory) { 183 showToast(R.string.surround_sound_enabled_format_info_clicked); 184 } else { 185 showToast(R.string.surround_sound_disabled_format_info_clicked); 186 } 187 } 188 189 return super.onPreferenceTreeClick(preference); 190 } 191 192 @VisibleForTesting getAudioManager()193 AudioManager getAudioManager() { 194 return getContext().getSystemService(AudioManager.class); 195 } 196 getPreferenceGroup()197 private PreferenceGroup getPreferenceGroup() { 198 return (PreferenceGroup) findPreference(KEY_ADVANCED_SOUND_OPTION); 199 } 200 selectRadioPreference(Preference preference)201 private void selectRadioPreference(Preference preference) { 202 final RadioPreference radioPreference = (RadioPreference) preference; 203 radioPreference.setChecked(true); 204 radioPreference.clearOtherRadioPreferences(getPreferenceGroup()); 205 } 206 207 /** Creates titles and switches for each surround sound format. */ createFormatPreferences()208 private void createFormatPreferences() { 209 mSupportedFormatsPreferenceCategory = createPreferenceCategory( 210 R.string.surround_sound_supported_title, KEY_SUPPORTED_SURROUND_SOUND); 211 getPreferenceScreen().addPreference(mSupportedFormatsPreferenceCategory); 212 mUnsupportedFormatsPreferenceCategory = createPreferenceCategory( 213 R.string.surround_sound_unsupported_title, KEY_UNSUPPORTED_SURROUND_SOUND); 214 getPreferenceScreen().addPreference(mUnsupportedFormatsPreferenceCategory); 215 216 for (int formatId : SURROUND_SOUND_DISPLAY_ORDER) { 217 if (mFormats.containsKey(formatId)) { 218 boolean enabled = mFormats.get(formatId); 219 220 // If the format is not a known surround sound format, do not create a preference 221 // for it. 222 int titleId = getFormatDisplayResourceId(formatId); 223 if (titleId == -1) { 224 continue; 225 } 226 final SwitchPreference pref = new SwitchPreference(getContext()) { 227 @Override 228 public void onBindViewHolder(PreferenceViewHolder holder) { 229 super.onBindViewHolder(holder); 230 // Enabling the view will ensure that the preference is focusable even if it 231 // the preference is disabled. This allows the user to scroll down over the 232 // disabled surround sound formats and see them all. 233 holder.itemView.setEnabled(true); 234 } 235 }; 236 pref.setTitle(titleId); 237 pref.setKey(KEY_SURROUND_SOUND_FORMAT_PREFIX + formatId); 238 pref.setChecked(enabled); 239 if (getEntryId(formatId) != -1) { 240 pref.setOnPreferenceClickListener( 241 preference -> { 242 logToggleInteracted(getEntryId(formatId), pref.isChecked()); 243 return false; 244 } 245 ); 246 } 247 if (mReportedFormats.contains(formatId)) { 248 mSupportedFormatsPreferenceCategory.addPreference(pref); 249 } else { 250 mUnsupportedFormatsPreferenceCategory.addPreference(pref); 251 } 252 } 253 } 254 } 255 256 /** Creates titles and preferences for each surround sound format. */ createFormatInfoPreferences()257 private void createFormatInfoPreferences() { 258 mFormatsInfoPreferenceCategory = createPreferenceCategory( 259 R.string.surround_sound_format_info, KEY_FORMAT_INFO); 260 getPreferenceScreen().addPreference(mFormatsInfoPreferenceCategory); 261 262 Preference pref = createPreference( 263 R.string.surround_sound_show_formats, KEY_SHOW_HIDE_FORMAT_INFO); 264 mFormatsInfoPreferenceCategory.addPreference(pref); 265 266 mEnabledFormatsPreferenceCategory = createPreferenceCategory( 267 R.string.surround_sound_enabled_formats, KEY_ENABLED_FORMATS); 268 mFormatsInfoPreferenceCategory.addPreference(mEnabledFormatsPreferenceCategory); 269 270 mDisabledFormatsPreferenceCategory = createPreferenceCategory( 271 R.string.surround_sound_disabled_formats, KEY_DISABLED_FORMATS); 272 mFormatsInfoPreferenceCategory.addPreference(mDisabledFormatsPreferenceCategory); 273 274 for (int formatId : SURROUND_SOUND_DISPLAY_ORDER) { 275 if (mFormats.containsKey(formatId)) { 276 // If the format is not a known surround sound format, do not create a preference 277 // for it. 278 int titleId = getFormatDisplayResourceId(formatId); 279 if (titleId == -1) { 280 continue; 281 } 282 pref = createPreference(titleId, KEY_SURROUND_SOUND_FORMAT_INFO_PREFIX + formatId); 283 if (mReportedFormats.contains(formatId)) { 284 mEnabledFormatsPreferenceCategory.addPreference(pref); 285 } else { 286 mDisabledFormatsPreferenceCategory.addPreference(pref); 287 } 288 } 289 } 290 hideFormatInfoPreferences(); 291 } 292 showFormatPreferences()293 private void showFormatPreferences() { 294 getPreferenceScreen().addPreference(mSupportedFormatsPreferenceCategory); 295 getPreferenceScreen().addPreference(mUnsupportedFormatsPreferenceCategory); 296 updateFormatPreferencesStates(); 297 // hide the formats info section. 298 getPreferenceScreen().removePreference(mFormatsInfoPreferenceCategory); 299 } 300 hideFormatPreferences()301 private void hideFormatPreferences() { 302 getPreferenceScreen().removePreference(mSupportedFormatsPreferenceCategory); 303 getPreferenceScreen().removePreference(mUnsupportedFormatsPreferenceCategory); 304 updateFormatPreferencesStates(); 305 // show the formats info section. 306 getPreferenceScreen().addPreference(mFormatsInfoPreferenceCategory); 307 } 308 showFormatInfoPreferences()309 private void showFormatInfoPreferences() { 310 mFormatsInfoPreferenceCategory.addPreference(mEnabledFormatsPreferenceCategory); 311 mFormatsInfoPreferenceCategory.addPreference(mDisabledFormatsPreferenceCategory); 312 } 313 hideFormatInfoPreferences()314 private void hideFormatInfoPreferences() { 315 mFormatsInfoPreferenceCategory.removePreference(mEnabledFormatsPreferenceCategory); 316 mFormatsInfoPreferenceCategory.removePreference(mDisabledFormatsPreferenceCategory); 317 } 318 showToast(int resId)319 private void showToast(int resId) { 320 Toast.makeText(getContext(), getContext().getString(resId), Toast.LENGTH_SHORT) 321 .show(); 322 } 323 createPreferenceCategory(int titleResourceId, String key)324 private PreferenceCategory createPreferenceCategory(int titleResourceId, String key) { 325 PreferenceCategory preferenceCategory = new PreferenceCategory(getContext()); 326 preferenceCategory.setTitle(titleResourceId); 327 preferenceCategory.setKey(key); 328 return preferenceCategory; 329 } 330 createPreference(int titleResourceId, String key)331 private Preference createPreference(int titleResourceId, String key) { 332 Preference preference = new Preference(getContext()); 333 preference.setTitle(titleResourceId); 334 preference.setKey(key); 335 return preference; 336 } 337 338 /** 339 * @return the display id for each surround sound format. 340 */ getFormatDisplayResourceId(int formatId)341 private int getFormatDisplayResourceId(int formatId) { 342 switch (formatId) { 343 case AudioFormat.ENCODING_AC3: 344 return R.string.surround_sound_format_ac3; 345 case AudioFormat.ENCODING_E_AC3: 346 return R.string.surround_sound_format_e_ac3; 347 case AudioFormat.ENCODING_DTS: 348 return R.string.surround_sound_format_dts; 349 case AudioFormat.ENCODING_DTS_HD: 350 return R.string.surround_sound_format_dts_hd; 351 case AudioFormat.ENCODING_DTS_UHD: 352 return R.string.surround_sound_format_dts_uhd; 353 case AudioFormat.ENCODING_DOLBY_TRUEHD: 354 return R.string.surround_sound_format_dolby_truehd; 355 case AudioFormat.ENCODING_E_AC3_JOC: 356 return R.string.surround_sound_format_e_ac3_joc; 357 case AudioFormat.ENCODING_DOLBY_MAT: 358 return R.string.surround_sound_format_dolby_mat; 359 case AudioFormat.ENCODING_DRA: 360 return R.string.surround_sound_format_dra; 361 default: 362 return -1; 363 } 364 } 365 updateFormatPreferencesStates()366 private void updateFormatPreferencesStates() { 367 for (AbstractPreferenceController controller : mPreferenceControllers) { 368 Preference preference = findPreference( 369 controller.getPreferenceKey()); 370 if (preference != null) { 371 controller.updateState(preference); 372 } 373 } 374 } 375 getSurroundPassthroughSetting(Context context)376 static String getSurroundPassthroughSetting(Context context) { 377 final int value = Settings.Global.getInt(context.getContentResolver(), 378 Settings.Global.ENCODED_SURROUND_OUTPUT, 379 Settings.Global.ENCODED_SURROUND_OUTPUT_AUTO); 380 381 switch (value) { 382 case Settings.Global.ENCODED_SURROUND_OUTPUT_MANUAL: 383 return KEY_SURROUND_SOUND_MANUAL; 384 case Settings.Global.ENCODED_SURROUND_OUTPUT_NEVER: 385 return KEY_SURROUND_SOUND_NONE; 386 case Settings.Global.ENCODED_SURROUND_OUTPUT_AUTO: 387 default: 388 return KEY_SURROUND_SOUND_AUTO; 389 } 390 } 391 getEntryId(int formatId)392 private int getEntryId(int formatId) { 393 switch(formatId) { 394 case AudioFormat.ENCODING_AC4: 395 return TvSettingsEnums.DISPLAY_SOUND_ADVANCED_SOUNDS_DAC4; 396 case AudioFormat.ENCODING_E_AC3_JOC: 397 return TvSettingsEnums.DISPLAY_SOUND_ADVANCED_SOUNDS_DADDP; 398 case AudioFormat.ENCODING_AC3: 399 return TvSettingsEnums.DISPLAY_SOUND_ADVANCED_SOUNDS_DD; 400 case AudioFormat.ENCODING_E_AC3: 401 return TvSettingsEnums.DISPLAY_SOUND_ADVANCED_SOUNDS_DDP; 402 case AudioFormat.ENCODING_DTS: 403 return TvSettingsEnums.DISPLAY_SOUND_ADVANCED_SOUNDS_DTS; 404 case AudioFormat.ENCODING_DTS_HD: 405 return TvSettingsEnums.DISPLAY_SOUND_ADVANCED_SOUNDS_DTSHD; 406 case AudioFormat.ENCODING_DTS_UHD: 407 return TvSettingsEnums.DISPLAY_SOUND_ADVANCED_SOUNDS_DTSUHD; 408 case AudioFormat.ENCODING_AAC_LC: 409 return TvSettingsEnums.DISPLAY_SOUND_ADVANCED_SOUNDS_AAC; 410 case AudioFormat.ENCODING_DOLBY_TRUEHD: 411 return TvSettingsEnums.DISPLAY_SOUND_ADVANCED_SOUNDS_DTHD; 412 case AudioFormat.ENCODING_DRA: 413 return TvSettingsEnums.DISPLAY_SOUND_ADVANCED_SOUNDS_DRA; 414 default: 415 return -1; 416 } 417 } 418 createInfoFragments()419 private void createInfoFragments() { 420 Preference autoPreference = findPreference(KEY_SURROUND_SOUND_AUTO); 421 autoPreference.setFragment(AdvancedVolumeInfo.AutoInfoFragment.class.getName()); 422 423 Preference manualPreference = findPreference(KEY_SURROUND_SOUND_MANUAL); 424 manualPreference.setFragment(AdvancedVolumeInfo.ManualInfoFragment.class.getName()); 425 } 426 427 @Override getPageId()428 protected int getPageId() { 429 return TvSettingsEnums.DISPLAY_SOUND_ADVANCED_SOUNDS; 430 } 431 } 432