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