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 android.view.Display.HdrCapabilities.HDR_TYPE_DOLBY_VISION; 20 import static android.view.Display.HdrCapabilities.HDR_TYPE_HDR10; 21 import static android.view.Display.HdrCapabilities.HDR_TYPE_HDR10_PLUS; 22 import static android.view.Display.HdrCapabilities.HDR_TYPE_HLG; 23 24 import static com.android.tv.settings.overlay.FlavorUtils.FLAVOR_CLASSIC; 25 import static com.android.tv.settings.util.InstrumentationUtils.logEntrySelected; 26 import static com.android.tv.settings.util.InstrumentationUtils.logToggleInteracted; 27 28 import android.app.tvsettings.TvSettingsEnums; 29 import android.content.Context; 30 import android.hardware.display.DisplayManager; 31 import android.os.Bundle; 32 import android.view.Display; 33 import android.widget.Toast; 34 35 import androidx.annotation.Keep; 36 import androidx.annotation.VisibleForTesting; 37 import androidx.preference.Preference; 38 import androidx.preference.PreferenceCategory; 39 import androidx.preference.PreferenceGroup; 40 import androidx.preference.PreferenceViewHolder; 41 import androidx.preference.SwitchPreference; 42 43 import com.android.settingslib.core.AbstractPreferenceController; 44 import com.android.tv.settings.PreferenceControllerFragment; 45 import com.android.tv.settings.R; 46 import com.android.tv.settings.RadioPreference; 47 import com.android.tv.settings.overlay.FlavorUtils; 48 49 import java.util.ArrayList; 50 import java.util.Arrays; 51 import java.util.List; 52 import java.util.Set; 53 import java.util.stream.Collectors; 54 55 /** 56 * This Fragment is responsible for allowing the user enable or disable the Hdr types which are 57 * supported by device. 58 */ 59 @Keep 60 public class HdrFormatSelectionFragment extends PreferenceControllerFragment { 61 62 static final String KEY_HDR_FORMAT_SELECTION = "hdr_format_selection_option"; 63 static final String KEY_SUPPORTED_HDR_FORMATS = "supported_formats"; 64 static final String KEY_UNSUPPORTED_HDR_FORMATS = "unsupported_formats"; 65 static final String KEY_HDR_FORMAT_SELECTION_AUTO = "hdr_format_selection_auto"; 66 static final String KEY_HDR_FORMAT_SELECTION_MANUAL = "hdr_format_selection_manual"; 67 static final String KEY_HDR_FORMAT_PREFIX = "hdr_format_"; 68 static final String KEY_HDR_FORMAT_INFO_PREFIX = "hdr_format_info_"; 69 static final String KEY_FORMAT_INFO = "hdr_format_info"; 70 static final String KEY_SHOW_HIDE_FORMAT_INFO = "hdr_show_hide_format_info"; 71 static final String KEY_ENABLED_FORMATS = "enabled_formats"; 72 static final String KEY_DISABLED_FORMATS = "disabled_formats"; 73 74 static final int[] HDR_FORMATS_DISPLAY_ORDER = { 75 HDR_TYPE_DOLBY_VISION, HDR_TYPE_HDR10, HDR_TYPE_HDR10_PLUS, HDR_TYPE_HLG 76 }; 77 78 private PreferenceCategory mSupportedFormatsPreferenceCategory; 79 private PreferenceCategory mUnsupportedFormatsPreferenceCategory; 80 private PreferenceCategory mFormatsInfoPreferenceCategory; 81 private PreferenceCategory mEnabledFormatsPreferenceCategory; 82 private PreferenceCategory mDisabledFormatsPreferenceCategory; 83 84 private List<AbstractPreferenceController> mPreferenceControllers; 85 86 private Set<Integer> mDeviceHdrTypes; 87 private Set<Integer> mDisplayReportedHdrTypes; 88 private Set<Integer> mUserDisabledHdrTypes; 89 90 private DisplayManager mDisplayManager; 91 92 /** @return the new instance of the class */ newInstance()93 public static HdrFormatSelectionFragment newInstance() { 94 return new HdrFormatSelectionFragment(); 95 } 96 97 @Override onAttach(Context context)98 public void onAttach(Context context) { 99 mDisplayManager = getDisplayManager(); 100 mDeviceHdrTypes = toSet(getDeviceSupportedHdrTypes()); 101 mUserDisabledHdrTypes = toSet(mDisplayManager.getUserDisabledHdrTypes()); 102 103 Display display = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY); 104 mDisplayReportedHdrTypes = toSet(display.getReportedHdrTypes()); 105 106 super.onAttach(context); 107 } 108 109 @Override getPreferenceScreenResId()110 protected int getPreferenceScreenResId() { 111 return R.xml.hdr_format_selection; 112 } 113 114 @Override onCreatePreferences(Bundle savedInstanceState, String rootKey)115 public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 116 setPreferencesFromResource(R.xml.hdr_format_selection, null); 117 118 createFormatInfoPreferences(); 119 createFormatPreferences(); 120 121 String currentPreferenceKey; 122 if (mDisplayManager.areUserDisabledHdrTypesAllowed()) { 123 currentPreferenceKey = KEY_HDR_FORMAT_SELECTION_AUTO; 124 hideFormatPreferences(); 125 } else { 126 currentPreferenceKey = KEY_HDR_FORMAT_SELECTION_MANUAL; 127 showFormatPreferences(); 128 } 129 selectRadioPreference(findPreference(currentPreferenceKey)); 130 131 // Do not show sidebar info texts in case of 1 panel settings. 132 if (FlavorUtils.getFlavor(getContext()) != FLAVOR_CLASSIC) { 133 createInfoFragments(); 134 } 135 } 136 137 @Override onCreatePreferenceControllers(Context context)138 protected List<AbstractPreferenceController> onCreatePreferenceControllers(Context context) { 139 mPreferenceControllers = new ArrayList<>(); 140 for (int hdrType : mDeviceHdrTypes) { 141 if (mDisplayReportedHdrTypes.contains(hdrType)) { 142 mPreferenceControllers.add( 143 new HdrFormatPreferenceController(getContext(), hdrType, mDisplayManager)); 144 } 145 } 146 return mPreferenceControllers; 147 } 148 149 @Override onPreferenceTreeClick(Preference preference)150 public boolean onPreferenceTreeClick(Preference preference) { 151 String key = preference.getKey(); 152 153 if (key == null) { 154 return super.onPreferenceTreeClick(preference); 155 } 156 157 if (preference instanceof RadioPreference) { 158 selectRadioPreference(preference); 159 160 switch (key) { 161 case KEY_HDR_FORMAT_SELECTION_AUTO: { 162 logEntrySelected( 163 TvSettingsEnums.DISPLAY_SOUND_ADVANCED_DISPLAY_FORMAT_SELECTION_AUTO); 164 mDisplayManager.setAreUserDisabledHdrTypesAllowed(true); 165 hideFormatPreferences(); 166 break; 167 } 168 case KEY_HDR_FORMAT_SELECTION_MANUAL: { 169 logEntrySelected( 170 TvSettingsEnums.DISPLAY_SOUND_ADVANCED_DISPLAY_FORMAT_SELECTION_MANUAL); 171 mDisplayManager.setAreUserDisabledHdrTypesAllowed(false); 172 showFormatPreferences(); 173 break; 174 } 175 default: 176 throw new IllegalArgumentException("Unknown hdr type selection pref value" 177 + ": " + key); 178 } 179 } 180 181 if (key.equals(KEY_SHOW_HIDE_FORMAT_INFO)) { 182 if (preference.getTitle().equals( 183 getContext().getString(R.string.hdr_hide_formats))) { 184 hideFormatInfoPreferences(); 185 preference.setTitle(R.string.hdr_show_formats); 186 } else { 187 showFormatInfoPreferences(); 188 preference.setTitle(R.string.hdr_hide_formats); 189 } 190 } 191 192 if (key.contains(KEY_HDR_FORMAT_INFO_PREFIX)) { 193 if (preference.getParent() == mEnabledFormatsPreferenceCategory) { 194 showToast(R.string.hdr_enabled_format_info_clicked); 195 } 196 } 197 return super.onPreferenceTreeClick(preference); 198 } 199 getPreferenceGroup()200 private PreferenceGroup getPreferenceGroup() { 201 return (PreferenceGroup) findPreference(KEY_HDR_FORMAT_SELECTION); 202 } 203 204 @VisibleForTesting getDisplayManager()205 DisplayManager getDisplayManager() { 206 return getContext().getSystemService(DisplayManager.class); 207 } 208 209 @VisibleForTesting getDeviceSupportedHdrTypes()210 int[] getDeviceSupportedHdrTypes() { 211 return getContext().getResources().getIntArray(R.array.config_deviceSupportedHdrFormats); 212 } 213 selectRadioPreference(Preference preference)214 private void selectRadioPreference(Preference preference) { 215 final RadioPreference radioPreference = (RadioPreference) preference; 216 radioPreference.setChecked(true); 217 radioPreference.clearOtherRadioPreferences(getPreferenceGroup()); 218 } 219 createFormatPreferences()220 private void createFormatPreferences() { 221 mSupportedFormatsPreferenceCategory = createPreferenceCategory( 222 R.string.hdr_format_supported_title, 223 KEY_SUPPORTED_HDR_FORMATS); 224 getPreferenceScreen().addPreference(mSupportedFormatsPreferenceCategory); 225 mUnsupportedFormatsPreferenceCategory = createPreferenceCategory( 226 R.string.hdr_format_unsupported_title, 227 KEY_UNSUPPORTED_HDR_FORMATS); 228 getPreferenceScreen().addPreference(mUnsupportedFormatsPreferenceCategory); 229 230 for (int hdrType : HDR_FORMATS_DISPLAY_ORDER) { 231 if (mDeviceHdrTypes.contains(hdrType)) { 232 int titleId = getFormatPreferenceTitleId(hdrType); 233 if (titleId == -1) { 234 continue; 235 } 236 if (mDisplayReportedHdrTypes.contains(hdrType)) { 237 boolean enabled = !mUserDisabledHdrTypes.contains(hdrType); 238 mSupportedFormatsPreferenceCategory.addPreference( 239 createSupportedFormatPreference(titleId, hdrType, enabled)); 240 } else { 241 mUnsupportedFormatsPreferenceCategory.addPreference( 242 createUnsupportedFormatPreference(titleId, hdrType)); 243 } 244 } 245 } 246 } 247 248 /** Creates titles and preferences for each hdr format. */ createFormatInfoPreferences()249 private void createFormatInfoPreferences() { 250 mFormatsInfoPreferenceCategory = createPreferenceCategory( 251 R.string.hdr_format_info, KEY_FORMAT_INFO); 252 getPreferenceScreen().addPreference(mFormatsInfoPreferenceCategory); 253 254 Preference pref = createPreference( 255 R.string.hdr_show_formats, KEY_SHOW_HIDE_FORMAT_INFO); 256 mFormatsInfoPreferenceCategory.addPreference(pref); 257 258 mEnabledFormatsPreferenceCategory = createPreferenceCategory( 259 R.string.hdr_enabled_formats, KEY_ENABLED_FORMATS); 260 mFormatsInfoPreferenceCategory.addPreference(mEnabledFormatsPreferenceCategory); 261 262 mDisabledFormatsPreferenceCategory = createPreferenceCategory( 263 R.string.hdr_disabled_formats, KEY_DISABLED_FORMATS); 264 mFormatsInfoPreferenceCategory.addPreference(mDisabledFormatsPreferenceCategory); 265 266 for (int hdrType : HDR_FORMATS_DISPLAY_ORDER) { 267 if (mDeviceHdrTypes.contains(hdrType)) { 268 int titleId = getFormatPreferenceTitleId(hdrType); 269 if (titleId == -1) { 270 continue; 271 } 272 273 pref = createPreference(titleId, KEY_HDR_FORMAT_INFO_PREFIX + hdrType); 274 if (mDisplayReportedHdrTypes.contains(hdrType)) { 275 mEnabledFormatsPreferenceCategory.addPreference(pref); 276 } else { 277 mDisabledFormatsPreferenceCategory.addPreference(pref); 278 } 279 } 280 } 281 hideFormatInfoPreferences(); 282 } 283 284 285 /** Returns a switch preference for each supported HDR format. */ createSupportedFormatPreference(int titleId, int hdrType, boolean enabled)286 private Preference createSupportedFormatPreference(int titleId, int hdrType, boolean enabled) { 287 final SwitchPreference pref = new SwitchPreference(getContext()) { 288 @Override 289 public void onBindViewHolder(PreferenceViewHolder holder) { 290 super.onBindViewHolder(holder); 291 holder.itemView.setEnabled(true); 292 } 293 }; 294 pref.setTitle(titleId); 295 pref.setKey(KEY_HDR_FORMAT_PREFIX + hdrType); 296 pref.setChecked(enabled); 297 if (getLogEntryId(hdrType) != -1) { 298 pref.setOnPreferenceClickListener( 299 preference -> { 300 logToggleInteracted(getLogEntryId(hdrType), pref.isChecked()); 301 return false; 302 }); 303 } 304 return pref; 305 } 306 307 /** Returns a non-switch preference for each unsupported HDR format. */ createUnsupportedFormatPreference(int titleId, int hdrType)308 private Preference createUnsupportedFormatPreference(int titleId, int hdrType) { 309 Preference pref = new Preference(getContext()); 310 pref.setTitle(titleId); 311 pref.setKey(KEY_HDR_FORMAT_PREFIX + hdrType); 312 return pref; 313 } 314 showFormatPreferences()315 private void showFormatPreferences() { 316 getPreferenceScreen().addPreference(mSupportedFormatsPreferenceCategory); 317 getPreferenceScreen().addPreference(mUnsupportedFormatsPreferenceCategory); 318 updateFormatPreferencesStates(); 319 // hide the formats info section. 320 getPreferenceScreen().removePreference(mFormatsInfoPreferenceCategory); 321 } 322 hideFormatPreferences()323 private void hideFormatPreferences() { 324 getPreferenceScreen().removePreference(mSupportedFormatsPreferenceCategory); 325 getPreferenceScreen().removePreference(mUnsupportedFormatsPreferenceCategory); 326 updateFormatPreferencesStates(); 327 // show the formats info section. 328 getPreferenceScreen().addPreference(mFormatsInfoPreferenceCategory); 329 } 330 showFormatInfoPreferences()331 private void showFormatInfoPreferences() { 332 mFormatsInfoPreferenceCategory.addPreference(mEnabledFormatsPreferenceCategory); 333 mFormatsInfoPreferenceCategory.addPreference(mDisabledFormatsPreferenceCategory); 334 } 335 hideFormatInfoPreferences()336 private void hideFormatInfoPreferences() { 337 mFormatsInfoPreferenceCategory.removePreference(mEnabledFormatsPreferenceCategory); 338 mFormatsInfoPreferenceCategory.removePreference(mDisabledFormatsPreferenceCategory); 339 } 340 showToast(int resId)341 private void showToast(int resId) { 342 Toast.makeText(getContext(), getContext().getString(resId), Toast.LENGTH_SHORT) 343 .show(); 344 } 345 createPreferenceCategory(int titleResourceId, String key)346 private PreferenceCategory createPreferenceCategory(int titleResourceId, String key) { 347 PreferenceCategory preferenceCategory = new PreferenceCategory(getContext()); 348 preferenceCategory.setTitle(titleResourceId); 349 preferenceCategory.setKey(key); 350 return preferenceCategory; 351 } 352 createPreference(int titleResourceId, String key)353 private Preference createPreference(int titleResourceId, String key) { 354 Preference preference = new Preference(getContext()); 355 preference.setTitle(titleResourceId); 356 preference.setKey(key); 357 return preference; 358 } 359 updateFormatPreferencesStates()360 private void updateFormatPreferencesStates() { 361 for (AbstractPreferenceController controller : mPreferenceControllers) { 362 Preference preference = findPreference(controller.getPreferenceKey()); 363 if (preference != null && preference instanceof SwitchPreference) { 364 controller.updateState(preference); 365 } 366 } 367 } 368 369 /** 370 * @return the display id for each hdr type. 371 */ getFormatPreferenceTitleId(int hdrType)372 private int getFormatPreferenceTitleId(int hdrType) { 373 switch (hdrType) { 374 case HDR_TYPE_DOLBY_VISION: 375 return R.string.hdr_format_dolby_vision; 376 case HDR_TYPE_HDR10: 377 return R.string.hdr_format_hdr10; 378 case HDR_TYPE_HLG: 379 return R.string.hdr_format_hlg; 380 case HDR_TYPE_HDR10_PLUS: 381 return R.string.hdr_format_hdr10plus; 382 default: 383 return -1; 384 } 385 } 386 getLogEntryId(int hdrType)387 private int getLogEntryId(int hdrType) { 388 switch(hdrType) { 389 case HDR_TYPE_DOLBY_VISION: 390 return TvSettingsEnums.DISPLAY_SOUND_ADVANCED_DISPLAY_FORMAT_SELECTION_DOLBY_VISION; 391 case HDR_TYPE_HDR10: 392 return TvSettingsEnums.DISPLAY_SOUND_ADVANCED_DISPLAY_FORMAT_SELECTION_HDR10; 393 case HDR_TYPE_HLG: 394 return TvSettingsEnums.DISPLAY_SOUND_ADVANCED_DISPLAY_FORMAT_SELECTION_HLG; 395 case HDR_TYPE_HDR10_PLUS: 396 return TvSettingsEnums.DISPLAY_SOUND_ADVANCED_DISPLAY_FORMAT_SELECTION_HDR10_PLUS; 397 default: 398 return -1; 399 } 400 } 401 toSet(int[] array)402 private Set<Integer> toSet(int[] array) { 403 return Arrays.stream(array).boxed().collect(Collectors.toSet()); 404 } 405 createInfoFragments()406 private void createInfoFragments() { 407 Preference autoPreference = findPreference(KEY_HDR_FORMAT_SELECTION_AUTO); 408 autoPreference.setFragment(HdrFormatSelectionInfo.AutoInfoFragment.class.getName()); 409 410 Preference manualPreference = findPreference(KEY_HDR_FORMAT_SELECTION_MANUAL); 411 manualPreference.setFragment(HdrFormatSelectionInfo.ManualInfoFragment.class.getName()); 412 } 413 } 414