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.displaysound; 18 19 import static com.android.tv.settings.device.displaysound.DisplaySoundUtils.getMatchContentDynamicRangeStatus; 20 import static com.android.tv.settings.device.displaysound.DisplaySoundUtils.setMatchContentDynamicRangeStatus; 21 import static com.android.tv.settings.overlay.FlavorUtils.FLAVOR_CLASSIC; 22 import static com.android.tv.settings.overlay.FlavorUtils.FLAVOR_TWO_PANEL; 23 import static com.android.tv.settings.overlay.FlavorUtils.FLAVOR_VENDOR; 24 import static com.android.tv.settings.overlay.FlavorUtils.FLAVOR_X; 25 import static com.android.tv.settings.util.InstrumentationUtils.logToggleInteracted; 26 27 import android.app.tvsettings.TvSettingsEnums; 28 import android.content.ContentResolver; 29 import android.content.Context; 30 import android.hardware.display.DisplayManager; 31 import android.hardware.hdmi.HdmiControlManager; 32 import android.media.AudioManager; 33 import android.os.Bundle; 34 import android.provider.Settings; 35 import android.text.TextUtils; 36 import android.view.Display; 37 38 import androidx.annotation.Keep; 39 import androidx.annotation.VisibleForTesting; 40 import androidx.preference.Preference; 41 import androidx.preference.SwitchPreference; 42 import androidx.preference.TwoStatePreference; 43 44 import com.android.tv.settings.R; 45 import com.android.tv.settings.SettingsPreferenceFragment; 46 import com.android.tv.settings.device.util.DeviceUtils; 47 import com.android.tv.settings.overlay.FlavorUtils; 48 import com.android.tv.settings.util.ResolutionSelectionUtils; 49 import com.android.tv.settings.util.SliceUtils; 50 import com.android.tv.twopanelsettings.slices.SlicePreference; 51 52 import java.util.Objects; 53 54 /** 55 * The "Display & sound" screen in TV Settings. 56 */ 57 @Keep 58 public class DisplaySoundFragment extends SettingsPreferenceFragment implements 59 DisplayManager.DisplayListener { 60 61 static final String KEY_SOUND_EFFECTS = "sound_effects"; 62 private static final String KEY_CEC = "cec"; 63 private static final String KEY_DEFAULT_AUDIO_OUTPUT_SETTINGS_SLICE = 64 "default_audio_output_settings"; 65 private static final String KEY_RESOLUTION_TITLE = "resolution_selection"; 66 private static final String KEY_DYNAMIC_RANGE = "match_content_dynamic_range"; 67 68 private static final String VOLUME_CHANGE = "volume_change"; 69 70 private AudioManager mAudioManager; 71 private HdmiControlManager mHdmiControlManager; 72 private String mCurrentDeviceName; 73 74 private Display.Mode mCurrentMode = null; 75 private DisplayManager mDisplayManager; 76 newInstance()77 public static DisplaySoundFragment newInstance() { 78 return new DisplaySoundFragment(); 79 } 80 getSoundEffectsEnabled(ContentResolver contentResolver)81 public static boolean getSoundEffectsEnabled(ContentResolver contentResolver) { 82 return Settings.System.getInt(contentResolver, Settings.System.SOUND_EFFECTS_ENABLED, 1) 83 != 0; 84 } 85 86 @Override onAttach(Context context)87 public void onAttach(Context context) { 88 mAudioManager = context.getSystemService(AudioManager.class); 89 mHdmiControlManager = context.getSystemService(HdmiControlManager.class); 90 super.onAttach(context); 91 } 92 getPreferenceScreenResId()93 private int getPreferenceScreenResId() { 94 switch (FlavorUtils.getFlavor(getContext())) { 95 case FLAVOR_CLASSIC: 96 case FLAVOR_TWO_PANEL: 97 return R.xml.display_sound; 98 case FLAVOR_X: 99 case FLAVOR_VENDOR: 100 return R.xml.display_sound_x; 101 default: 102 return R.xml.display_sound; 103 } 104 } 105 106 @Override onCreatePreferences(Bundle savedInstanceState, String rootKey)107 public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 108 setPreferencesFromResource(getPreferenceScreenResId(), null); 109 110 final TwoStatePreference soundPref = findPreference(KEY_SOUND_EFFECTS); 111 mCurrentDeviceName = DeviceUtils.getDeviceName(getContext()); 112 soundPref.setChecked(getSoundEffectsEnabled()); 113 soundPref.setVisible(false); 114 updateCecPreference(); 115 updateDefaultAudioOutputSettings(); 116 updateVolumeChangePreference(); 117 118 mDisplayManager = getDisplayManager(); 119 Display display = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY); 120 if (display.getSystemPreferredDisplayMode() != null) { 121 mDisplayManager.registerDisplayListener(this, null); 122 mCurrentMode = mDisplayManager.getGlobalUserPreferredDisplayMode(); 123 updateResolutionTitleDescription(ResolutionSelectionUtils.modeToString( 124 mCurrentMode, getContext())); 125 } else { 126 removePreference(findPreference(KEY_RESOLUTION_TITLE)); 127 } 128 129 SwitchPreference dynamicRangePreference = findPreference(KEY_DYNAMIC_RANGE); 130 if (mDisplayManager.getSupportedHdrOutputTypes().length == 0) { 131 removePreference(dynamicRangePreference); 132 return; 133 } 134 // Do not show sidebar info texts in case of 1 panel settings. 135 if (FlavorUtils.getFlavor(getContext()) != FLAVOR_CLASSIC) { 136 createInfoFragments(); 137 } 138 } 139 140 @Override onDestroy()141 public void onDestroy() { 142 super.onDestroy(); 143 mDisplayManager.unregisterDisplayListener(this); 144 } 145 146 147 @Override onResume()148 public void onResume() { 149 super.onResume(); 150 // Update the subtitle of CEC setting when navigating back to this page. 151 updateCecPreference(); 152 153 SwitchPreference dynamicRangePreference = findPreference(KEY_DYNAMIC_RANGE); 154 if (dynamicRangePreference != null) { 155 dynamicRangePreference.setChecked(getMatchContentDynamicRangeStatus(mDisplayManager)); 156 } 157 } 158 159 @Override onPreferenceTreeClick(Preference preference)160 public boolean onPreferenceTreeClick(Preference preference) { 161 if (TextUtils.equals(preference.getKey(), KEY_SOUND_EFFECTS)) { 162 final TwoStatePreference soundPref = (TwoStatePreference) preference; 163 logToggleInteracted(TvSettingsEnums.DISPLAY_SOUND_SYSTEM_SOUNDS, soundPref.isChecked()); 164 setSoundEffectsEnabled(soundPref.isChecked()); 165 } 166 if (TextUtils.equals(preference.getKey(), KEY_DYNAMIC_RANGE)) { 167 final SwitchPreference dynamicPref = (SwitchPreference) preference; 168 setMatchContentDynamicRangeStatus( 169 getContext(), mDisplayManager, dynamicPref.isChecked()); 170 } 171 return super.onPreferenceTreeClick(preference); 172 } 173 getSoundEffectsEnabled()174 private boolean getSoundEffectsEnabled() { 175 return getSoundEffectsEnabled(getActivity().getContentResolver()); 176 } 177 setSoundEffectsEnabled(boolean enabled)178 private void setSoundEffectsEnabled(boolean enabled) { 179 if (enabled) { 180 mAudioManager.loadSoundEffects(); 181 } else { 182 mAudioManager.unloadSoundEffects(); 183 } 184 Settings.System.putInt(getActivity().getContentResolver(), 185 Settings.System.SOUND_EFFECTS_ENABLED, enabled ? 1 : 0); 186 } 187 updateCecPreference()188 private void updateCecPreference() { 189 Preference cecPreference = findPreference(KEY_CEC); 190 if (cecPreference instanceof SlicePreference 191 && SliceUtils.isSliceProviderValid( 192 getContext(), ((SlicePreference) cecPreference).getUri())) { 193 ContentResolver resolver = getContext().getContentResolver(); 194 boolean cecEnabled = mHdmiControlManager.getHdmiCecEnabled() 195 == HdmiControlManager.HDMI_CEC_CONTROL_ENABLED; 196 cecPreference.setSummary(cecEnabled ? R.string.enabled : R.string.disabled); 197 cecPreference.setVisible(true); 198 } else { 199 cecPreference.setVisible(false); 200 } 201 } 202 updateDefaultAudioOutputSettings()203 private void updateDefaultAudioOutputSettings() { 204 final SlicePreference defaultAudioOutputSlicePref = findPreference( 205 KEY_DEFAULT_AUDIO_OUTPUT_SETTINGS_SLICE); 206 if (defaultAudioOutputSlicePref != null) { 207 defaultAudioOutputSlicePref.setVisible( 208 SliceUtils.isSliceProviderValid(getContext(), 209 defaultAudioOutputSlicePref.getUri()) 210 && SliceUtils.isSettingsSliceEnabled(getContext(), 211 defaultAudioOutputSlicePref.getUri(), null)); 212 } 213 } 214 updateVolumeChangePreference()215 private void updateVolumeChangePreference() { 216 Preference volumeChangePreference = findPreference(VOLUME_CHANGE); 217 String volumeChangeTitle = String.format( 218 getContext().getResources().getString(R.string.volume_change_settings_title), 219 mCurrentDeviceName 220 ); 221 volumeChangePreference.setTitle(volumeChangeTitle); 222 volumeChangePreference.setVisible(getContext().getResources() 223 .getBoolean(R.bool.config_volume_change)); 224 } 225 226 @Override getPageId()227 protected int getPageId() { 228 return TvSettingsEnums.DISPLAY_SOUND; 229 } 230 231 @Override onDisplayAdded(int displayId)232 public void onDisplayAdded(int displayId) {} 233 234 @Override onDisplayRemoved(int displayId)235 public void onDisplayRemoved(int displayId) {} 236 237 @Override onDisplayChanged(int displayId)238 public void onDisplayChanged(int displayId) { 239 Display.Mode newMode = mDisplayManager.getGlobalUserPreferredDisplayMode(); 240 if (!Objects.equals(mCurrentMode, newMode)) { 241 updateResolutionTitleDescription( 242 ResolutionSelectionUtils.modeToString(newMode, getContext())); 243 mCurrentMode = newMode; 244 } 245 } 246 247 @VisibleForTesting getDisplayManager()248 DisplayManager getDisplayManager() { 249 return getContext().getSystemService(DisplayManager.class); 250 } 251 updateResolutionTitleDescription(String summary)252 private void updateResolutionTitleDescription(String summary) { 253 Preference titlePreference = findPreference(KEY_RESOLUTION_TITLE); 254 if (titlePreference != null) { 255 titlePreference.setSummary(summary); 256 } 257 } 258 removePreference(Preference preference)259 private void removePreference(Preference preference) { 260 if (preference != null) { 261 getPreferenceScreen().removePreference(preference); 262 } 263 } 264 createInfoFragments()265 private void createInfoFragments() { 266 Preference dynamicRangePref = findPreference(KEY_DYNAMIC_RANGE); 267 if (dynamicRangePref != null) { 268 dynamicRangePref.setFragment( 269 PreferredDynamicRangeInfo.MatchContentDynamicRangeInfoFragment.class.getName()); 270 } 271 } 272 } 273