TvOptionsRowAdapter.java revision 65fda1eaa94968bb55d5ded10dcb0b3f37fb05f2
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
17package com.android.tv.menu;
18
19import android.content.Context;
20import android.media.tv.TvTrackInfo;
21import android.support.annotation.VisibleForTesting;
22
23import com.android.tv.Features;
24import com.android.tv.R;
25import com.android.tv.TvOptionsManager;
26import com.android.tv.customization.CustomAction;
27import com.android.tv.data.DisplayMode;
28import com.android.tv.ui.TvViewUiManager;
29import com.android.tv.ui.sidepanel.ClosedCaptionFragment;
30import com.android.tv.ui.sidepanel.DeveloperOptionFragment;
31import com.android.tv.ui.sidepanel.DisplayModeFragment;
32import com.android.tv.ui.sidepanel.MultiAudioFragment;
33import com.android.tv.util.PipInputManager;
34
35import java.util.ArrayList;
36import java.util.List;
37
38/*
39 * An adapter of options.
40 */
41public 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
48    public TvOptionsRowAdapter(Context context, List<CustomAction> customActions) {
49        super(context, customActions);
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        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
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
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
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
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
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