1 /* 2 * Copyright (C) 2023 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_INVALID; 21 22 import android.annotation.Nullable; 23 import android.app.AlertDialog; 24 import android.content.Context; 25 import android.content.DialogInterface.OnClickListener; 26 import android.content.Intent; 27 import android.hardware.display.DisplayManager; 28 import android.hardware.display.HdrConversionMode; 29 import android.os.UserHandle; 30 import android.view.Display; 31 32 import com.android.tv.settings.R; 33 34 import java.util.Arrays; 35 import java.util.Set; 36 import java.util.stream.Collectors; 37 38 /** 39 * Helper methods for display and sound settings. 40 * 41 * @hide 42 */ 43 public class DisplaySoundUtils { 44 private static final String ACTION_HDR_SETTINGS_CHANGED = 45 "com.android.tv.settings.display.HDR_SETTINGS_CHANGED"; 46 sendHdrSettingsChangedBroadcast(Context context)47 public static void sendHdrSettingsChangedBroadcast(Context context) { 48 final String target_package = 49 context.getResources().getString(R.string.hdr_settings_changed_broadcast_package); 50 if (target_package.isEmpty()) { 51 return; 52 } 53 context.sendBroadcastAsUser( 54 new Intent(ACTION_HDR_SETTINGS_CHANGED) 55 .setPackage(target_package) 56 .setFlags( 57 Intent.FLAG_INCLUDE_STOPPED_PACKAGES 58 | Intent.FLAG_RECEIVER_FOREGROUND 59 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND), 60 UserHandle.SYSTEM); 61 } 62 63 /** Gets the match-content dynamic range status */ getMatchContentDynamicRangeStatus(DisplayManager displayManager)64 public static boolean getMatchContentDynamicRangeStatus(DisplayManager displayManager) { 65 return displayManager.getHdrConversionModeSetting().getConversionMode() 66 == HdrConversionMode.HDR_CONVERSION_PASSTHROUGH; 67 } 68 69 /** Sets the match-content dynamic range status */ setMatchContentDynamicRangeStatus(Context context, DisplayManager displayManager, boolean status)70 public static void setMatchContentDynamicRangeStatus(Context context, 71 DisplayManager displayManager, 72 boolean status) { 73 if (status) { 74 // When enabling Match Content Dymanic Range, check if the current Preferred Dynamic 75 // Range is set to Force SDR. Since Force SDR means that all HDR types have been 76 // disabled, when enabling Match Content Dynamic Range, Format Selection must be reset 77 // to Auto to allow for all supported HDR formats 78 if (displayManager.getHdrConversionModeSetting().equals(new HdrConversionMode( 79 HdrConversionMode.HDR_CONVERSION_FORCE, HDR_TYPE_INVALID))) { 80 displayManager.setAreUserDisabledHdrTypesAllowed(true); 81 } 82 displayManager.setHdrConversionMode( 83 new HdrConversionMode(HdrConversionMode.HDR_CONVERSION_PASSTHROUGH)); 84 } else { 85 displayManager.setHdrConversionMode( 86 new HdrConversionMode(HdrConversionMode.HDR_CONVERSION_SYSTEM)); 87 } 88 sendHdrSettingsChangedBroadcast(context); 89 } 90 91 /** Returns if Dolby vision is supported by the device */ isDolbyVisionSupported(Display.Mode[] modes)92 public static boolean isDolbyVisionSupported(Display.Mode[] modes) { 93 for (int i = 0; i < modes.length; i++) { 94 if (isHdrFormatSupported(modes[i], HDR_TYPE_DOLBY_VISION)) { 95 return true; 96 } 97 } 98 return false; 99 } 100 101 /** Returns if the passed HDR format is supported by the device in case of a specific mode */ isHdrFormatSupported(Display.Mode mode, int hdrFormat)102 public static boolean isHdrFormatSupported(Display.Mode mode, int hdrFormat) { 103 return Arrays.stream(mode.getSupportedHdrTypes()).anyMatch( 104 hdr -> hdr == hdrFormat); 105 } 106 107 /** 108 * Returns true if the current mode is above 4k30Hz and this is a device that does not support 109 * Dolby Vision at resolutions above 4k30Hz 110 */ doesCurrentModeNotSupportDvBecauseLimitedTo4k30(Display display)111 public static boolean doesCurrentModeNotSupportDvBecauseLimitedTo4k30(Display display) { 112 Display.Mode[] supportedModes = display.getSupportedModes(); 113 Display.Mode currentMode = display.getMode(); 114 boolean is4k60HzMode = (currentMode.getPhysicalHeight() >= 2160 115 || currentMode.getPhysicalWidth() >= 2160) 116 && currentMode.getRefreshRate() >= 59.9; 117 118 return is4k60HzMode && isDolbyVisionSupported(supportedModes) 119 && !isHdrFormatSupported(currentMode, HDR_TYPE_DOLBY_VISION); 120 } 121 122 /** 123 * Returns a 1080p 60Hz mode if one is supported by the display, null otherwise 124 */ 125 @Nullable findMode1080p60(Display display)126 public static Display.Mode findMode1080p60(Display display) { 127 for (Display.Mode mode : display.getSupportedModes()) { 128 if (mode.getPhysicalWidth() == 1920 && mode.getPhysicalHeight() == 1080 129 && mode.getRefreshRate() >= 59.9) { 130 return mode; 131 } 132 } 133 return null; 134 } 135 createAlertDialog(Context context, String title, String description, OnClickListener onOkClicked, OnClickListener onCancelClicked)136 static AlertDialog createAlertDialog(Context context, String title, String description, 137 OnClickListener onOkClicked, OnClickListener onCancelClicked) { 138 return new AlertDialog.Builder(context) 139 .setTitle(title) 140 .setMessage(description) 141 .setPositiveButton(R.string.resolution_selection_dialog_ok, onOkClicked) 142 .setNegativeButton(R.string.resolution_selection_dialog_cancel, onCancelClicked) 143 .create(); 144 } 145 enableHdrType(DisplayManager displayManager, @Display.HdrCapabilities.HdrType int hdrType)146 static void enableHdrType(DisplayManager displayManager, 147 @Display.HdrCapabilities.HdrType int hdrType) { 148 Set<Integer> disabledHdrTypes = toSet(displayManager.getUserDisabledHdrTypes()); 149 disabledHdrTypes.remove(hdrType); 150 displayManager.setUserDisabledHdrTypes(toArray(disabledHdrTypes)); 151 } 152 disableHdrType(DisplayManager displayManager, @Display.HdrCapabilities.HdrType int hdrType)153 static void disableHdrType(DisplayManager displayManager, 154 @Display.HdrCapabilities.HdrType int hdrType) { 155 Set<Integer> disabledHdrTypes = toSet(displayManager.getUserDisabledHdrTypes()); 156 disabledHdrTypes.add(hdrType); 157 displayManager.setUserDisabledHdrTypes(toArray(disabledHdrTypes)); 158 } 159 160 /** Returns true when Preferred Dynamic Range is set to Force SDR */ isForceSdr(DisplayManager displayManager)161 static boolean isForceSdr(DisplayManager displayManager) { 162 return displayManager.getHdrConversionModeSetting().equals(new HdrConversionMode( 163 HdrConversionMode.HDR_CONVERSION_FORCE, HDR_TYPE_INVALID)); 164 } 165 166 /** Converts set to int array */ toArray(Set<Integer> set)167 public static int[] toArray(Set<Integer> set) { 168 return set.stream().mapToInt(Integer::intValue).toArray(); 169 } 170 171 /** Converts int array to set */ toSet(int[] array)172 public static Set<Integer> toSet(int[] array) { 173 return Arrays.stream(array).boxed().collect(Collectors.toSet()); 174 } 175 } 176