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.menu; 18 19 import android.content.Context; 20 import android.media.tv.TvTrackInfo; 21 import android.support.annotation.VisibleForTesting; 22 23 import com.android.tv.Features; 24 import com.android.tv.R; 25 import com.android.tv.TvOptionsManager; 26 import com.android.tv.customization.CustomAction; 27 import com.android.tv.data.DisplayMode; 28 import com.android.tv.ui.TvViewUiManager; 29 import com.android.tv.ui.sidepanel.ClosedCaptionFragment; 30 import com.android.tv.ui.sidepanel.DeveloperOptionFragment; 31 import com.android.tv.ui.sidepanel.DisplayModeFragment; 32 import com.android.tv.ui.sidepanel.MultiAudioFragment; 33 import com.android.tv.util.PipInputManager; 34 35 import java.util.ArrayList; 36 import java.util.List; 37 38 /* 39 * An adapter of options. 40 */ 41 public class TvOptionsRowAdapter extends CustomizableOptionsRowAdapter { 42 private static final boolean ENABLE_IN_APP_PIP = false; 43 44 private int mPositionPipAction; 45 // If mInAppPipAction is false, system-wide PIP is used. 46 private boolean mInAppPipAction = true; 47 TvOptionsRowAdapter(Context context, List<CustomAction> customActions)48 public TvOptionsRowAdapter(Context context, List<CustomAction> customActions) { 49 super(context, customActions); 50 } 51 52 @Override createBaseActions()53 protected List<MenuAction> createBaseActions() { 54 List<MenuAction> actionList = new ArrayList<>(); 55 actionList.add(MenuAction.SELECT_CLOSED_CAPTION_ACTION); 56 setOptionChangedListener(MenuAction.SELECT_CLOSED_CAPTION_ACTION); 57 actionList.add(MenuAction.SELECT_DISPLAY_MODE_ACTION); 58 setOptionChangedListener(MenuAction.SELECT_DISPLAY_MODE_ACTION); 59 actionList.add(MenuAction.PIP_IN_APP_ACTION); 60 setOptionChangedListener(MenuAction.PIP_IN_APP_ACTION); 61 mPositionPipAction = actionList.size() - 1; 62 actionList.add(MenuAction.SELECT_AUDIO_LANGUAGE_ACTION); 63 setOptionChangedListener(MenuAction.SELECT_AUDIO_LANGUAGE_ACTION); 64 actionList.add(MenuAction.MORE_CHANNELS_ACTION); 65 if (DeveloperOptionFragment.shouldShow()) { 66 actionList.add(MenuAction.DEV_ACTION); 67 } 68 actionList.add(MenuAction.SETTINGS_ACTION); 69 70 if (getCustomActions() != null) { 71 // Adjust Pip action position which will be changed by applying custom actions. 72 for (CustomAction customAction : getCustomActions()) { 73 if (customAction.isFront()) { 74 mPositionPipAction++; 75 } 76 } 77 } 78 79 return actionList; 80 } 81 82 @Override updateActions()83 protected boolean updateActions() { 84 boolean changed = false; 85 if (updatePipAction()) { 86 changed = true; 87 } 88 if (updateMultiAudioAction()) { 89 changed = true; 90 } 91 if (updateDisplayModeAction()) { 92 changed = true; 93 } 94 return changed; 95 } 96 updatePipAction()97 private boolean updatePipAction() { 98 // There are four states. 99 // Case 1. The device doesn't even have any input for PIP. (e.g. OTT box without HDMI input) 100 // => Remove the icon. 101 // Case 2. The device has one or more inputs for PIP but none of them are currently 102 // available. 103 // => Show the icon but disable it. 104 // Case 3. The device has one or more available PIP inputs and now it's tuned off. 105 // => Show the icon with "Off". 106 // Case 4. The device has one or more available PIP inputs but it's already turned on. 107 // => Show the icon with "On". 108 109 boolean changed = false; 110 111 // Case 1 112 PipInputManager pipInputManager = getMainActivity().getPipInputManager(); 113 if (ENABLE_IN_APP_PIP && pipInputManager.getPipInputSize(false) > 1) { 114 if (!mInAppPipAction) { 115 removeAction(mPositionPipAction); 116 addAction(mPositionPipAction, MenuAction.PIP_IN_APP_ACTION); 117 mInAppPipAction = true; 118 changed = true; 119 } 120 } else { 121 if (mInAppPipAction) { 122 removeAction(mPositionPipAction); 123 mInAppPipAction = false; 124 if (Features.PICTURE_IN_PICTURE.isEnabled(getMainActivity())) { 125 addAction(mPositionPipAction, MenuAction.SYSTEMWIDE_PIP_ACTION); 126 } 127 return true; 128 } 129 return false; 130 } 131 132 // Case 2 133 boolean isPipEnabled = getMainActivity().isPipEnabled(); 134 boolean oldEnabled = MenuAction.PIP_IN_APP_ACTION.isEnabled(); 135 boolean newEnabled = pipInputManager.getPipInputSize(true) > 0; 136 if (oldEnabled != newEnabled) { 137 // Should not disable the item if the PIP is already turned on so that the user can 138 // force exit it. 139 if (newEnabled || !isPipEnabled) { 140 MenuAction.PIP_IN_APP_ACTION.setEnabled(newEnabled); 141 changed = true; 142 } 143 } 144 145 // Case 3 & 4 - we just need to update the icon. 146 MenuAction.PIP_IN_APP_ACTION.setDrawableResId( 147 isPipEnabled ? R.drawable.ic_tvoption_pip : R.drawable.ic_tvoption_pip_off); 148 return changed; 149 } 150 151 @VisibleForTesting updateMultiAudioAction()152 boolean updateMultiAudioAction() { 153 List<TvTrackInfo> audioTracks = getMainActivity().getTracks(TvTrackInfo.TYPE_AUDIO); 154 boolean oldEnabled = MenuAction.SELECT_AUDIO_LANGUAGE_ACTION.isEnabled(); 155 boolean newEnabled = audioTracks != null && audioTracks.size() > 1; 156 if (oldEnabled != newEnabled) { 157 MenuAction.SELECT_AUDIO_LANGUAGE_ACTION.setEnabled(newEnabled); 158 return true; 159 } 160 return false; 161 } 162 updateDisplayModeAction()163 private boolean updateDisplayModeAction() { 164 TvViewUiManager uiManager = getMainActivity().getTvViewUiManager(); 165 boolean oldEnabled = MenuAction.SELECT_DISPLAY_MODE_ACTION.isEnabled(); 166 boolean newEnabled = uiManager.isDisplayModeAvailable(DisplayMode.MODE_FULL) 167 || uiManager.isDisplayModeAvailable(DisplayMode.MODE_ZOOM); 168 if (oldEnabled != newEnabled) { 169 MenuAction.SELECT_DISPLAY_MODE_ACTION.setEnabled(newEnabled); 170 return true; 171 } 172 return false; 173 } 174 175 @Override executeBaseAction(int type)176 protected void executeBaseAction(int type) { 177 switch (type) { 178 case TvOptionsManager.OPTION_CLOSED_CAPTIONS: 179 getMainActivity().getOverlayManager().getSideFragmentManager() 180 .show(new ClosedCaptionFragment()); 181 break; 182 case TvOptionsManager.OPTION_DISPLAY_MODE: 183 getMainActivity().getOverlayManager().getSideFragmentManager() 184 .show(new DisplayModeFragment()); 185 break; 186 case TvOptionsManager.OPTION_IN_APP_PIP: 187 getMainActivity().togglePipView(); 188 break; 189 case TvOptionsManager.OPTION_SYSTEMWIDE_PIP: 190 getMainActivity().enterPictureInPictureMode(); 191 break; 192 case TvOptionsManager.OPTION_MULTI_AUDIO: 193 getMainActivity().getOverlayManager().getSideFragmentManager() 194 .show(new MultiAudioFragment()); 195 break; 196 case TvOptionsManager.OPTION_MORE_CHANNELS: 197 getMainActivity().showMerchantCollection(); 198 break; 199 case TvOptionsManager.OPTION_DEVELOPER: 200 getMainActivity().getOverlayManager().getSideFragmentManager() 201 .show(new DeveloperOptionFragment()); 202 break; 203 case TvOptionsManager.OPTION_SETTINGS: 204 getMainActivity().showSettingsFragment(); 205 break; 206 } 207 } 208 } 209