1 /*
2  * Copyright (C) 2016 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.systemui.tv.pip;
18 
19 import android.content.Context;
20 import android.media.session.MediaController;
21 import android.media.session.PlaybackState;
22 import android.view.View;
23 import android.view.Gravity;
24 import android.view.LayoutInflater;
25 import android.view.View.OnFocusChangeListener;
26 import android.widget.ImageView;
27 import android.widget.TextView;
28 import android.widget.LinearLayout;
29 import android.util.AttributeSet;
30 
31 import com.android.systemui.R;
32 
33 import static android.media.session.PlaybackState.ACTION_PAUSE;
34 import static android.media.session.PlaybackState.ACTION_PLAY;
35 
36 import static com.android.systemui.tv.pip.PipManager.PLAYBACK_STATE_PLAYING;
37 import static com.android.systemui.tv.pip.PipManager.PLAYBACK_STATE_PAUSED;
38 import static com.android.systemui.tv.pip.PipManager.PLAYBACK_STATE_UNAVAILABLE;
39 
40 
41 /**
42  * A view containing PIP controls including fullscreen, close, and media controls.
43  */
44 public class PipControlsView extends LinearLayout {
45     /**
46      * An interface to listen user action.
47      */
48     public abstract static interface Listener {
49         /**
50          * Called when an user clicks close PIP button.
51          */
onClosed()52         public abstract void onClosed();
53     };
54 
55     private MediaController mMediaController;
56 
57     final PipManager mPipManager = PipManager.getInstance();
58     Listener mListener;
59 
60     private PipControlButtonView mFullButtonView;
61     private PipControlButtonView mCloseButtonView;
62     private PipControlButtonView mPlayPauseButtonView;
63 
64     private PipControlButtonView mFocusedChild;
65 
66     private MediaController.Callback mMediaControllerCallback = new MediaController.Callback() {
67         @Override
68         public void onPlaybackStateChanged(PlaybackState state) {
69             updatePlayPauseView();
70         }
71     };
72 
73     private final PipManager.MediaListener mPipMediaListener = new PipManager.MediaListener() {
74         @Override
75         public void onMediaControllerChanged() {
76             updateMediaController();
77         }
78     };
79 
80     private final OnFocusChangeListener mFocusChangeListener = new OnFocusChangeListener() {
81         @Override
82         public void onFocusChange(View view, boolean hasFocus) {
83             if (hasFocus) {
84                 mFocusedChild = (PipControlButtonView) view;
85             } else if (mFocusedChild == view) {
86                 mFocusedChild = null;
87             }
88         }
89     };
90 
PipControlsView(Context context)91     public PipControlsView(Context context) {
92         this(context, null, 0, 0);
93     }
94 
PipControlsView(Context context, AttributeSet attrs)95     public PipControlsView(Context context, AttributeSet attrs) {
96         this(context, attrs, 0, 0);
97     }
98 
PipControlsView(Context context, AttributeSet attrs, int defStyleAttr)99     public PipControlsView(Context context, AttributeSet attrs, int defStyleAttr) {
100         this(context, attrs, defStyleAttr, 0);
101     }
102 
PipControlsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)103     public PipControlsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
104         super(context, attrs, defStyleAttr, defStyleRes);
105         LayoutInflater inflater = (LayoutInflater) getContext()
106                 .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
107         inflater.inflate(R.layout.tv_pip_controls, this);
108 
109         setOrientation(LinearLayout.HORIZONTAL);
110         setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL);
111     }
112 
113     @Override
onFinishInflate()114     public void onFinishInflate() {
115         super.onFinishInflate();
116 
117         mFullButtonView = (PipControlButtonView) findViewById(R.id.full_button);
118         mFullButtonView.setOnFocusChangeListener(mFocusChangeListener);
119         mFullButtonView.setOnClickListener(new View.OnClickListener() {
120             @Override
121             public void onClick(View v) {
122                 mPipManager.movePipToFullscreen();
123             }
124         });
125 
126         mCloseButtonView = (PipControlButtonView) findViewById(R.id.close_button);
127         mCloseButtonView.setOnFocusChangeListener(mFocusChangeListener);
128         mCloseButtonView.setOnClickListener(new View.OnClickListener() {
129             @Override
130             public void onClick(View v) {
131                 mPipManager.closePip();
132                 if (mListener != null) {
133                     mListener.onClosed();
134                 }
135             }
136         });
137 
138         mPlayPauseButtonView = (PipControlButtonView) findViewById(R.id.play_pause_button);
139         mPlayPauseButtonView.setOnFocusChangeListener(mFocusChangeListener);
140         mPlayPauseButtonView.setOnClickListener(new View.OnClickListener() {
141             @Override
142             public void onClick(View v) {
143                 if (mMediaController == null || mMediaController.getPlaybackState() == null) {
144                     return;
145                 }
146                 long actions = mMediaController.getPlaybackState().getActions();
147                 int state = mMediaController.getPlaybackState().getState();
148                 if (mPipManager.getPlaybackState() == PLAYBACK_STATE_PAUSED) {
149                     mMediaController.getTransportControls().play();
150                 } else if (mPipManager.getPlaybackState() == PLAYBACK_STATE_PLAYING) {
151                     mMediaController.getTransportControls().pause();
152                 }
153                 // View will be updated later in {@link mMediaControllerCallback}
154             }
155         });
156     }
157 
158     @Override
onAttachedToWindow()159     public void onAttachedToWindow() {
160         super.onAttachedToWindow();
161         updateMediaController();
162         mPipManager.addMediaListener(mPipMediaListener);
163     }
164 
165     @Override
onDetachedFromWindow()166     public void onDetachedFromWindow() {
167         super.onDetachedFromWindow();
168         mPipManager.removeMediaListener(mPipMediaListener);
169         if (mMediaController != null) {
170             mMediaController.unregisterCallback(mMediaControllerCallback);
171         }
172     }
173 
updateMediaController()174     private void updateMediaController() {
175         MediaController newController = mPipManager.getMediaController();
176         if (mMediaController == newController) {
177             return;
178         }
179         if (mMediaController != null) {
180             mMediaController.unregisterCallback(mMediaControllerCallback);
181         }
182         mMediaController = newController;
183         if (mMediaController != null) {
184             mMediaController.registerCallback(mMediaControllerCallback);
185         }
186         updatePlayPauseView();
187     }
188 
updatePlayPauseView()189     private void updatePlayPauseView() {
190         int state = mPipManager.getPlaybackState();
191         if (state == PLAYBACK_STATE_UNAVAILABLE) {
192             mPlayPauseButtonView.setVisibility(View.GONE);
193         } else {
194             mPlayPauseButtonView.setVisibility(View.VISIBLE);
195             if (state == PLAYBACK_STATE_PLAYING) {
196                 mPlayPauseButtonView.setImageResource(R.drawable.ic_pause_white_24dp);
197                 mPlayPauseButtonView.setText(R.string.pip_pause);
198             } else {
199                 mPlayPauseButtonView.setImageResource(R.drawable.ic_play_arrow_white_24dp);
200                 mPlayPauseButtonView.setText(R.string.pip_play);
201             }
202         }
203     }
204 
205     /**
206      * Resets to initial state.
207      */
reset()208     public void reset() {
209         mFullButtonView.reset();
210         mCloseButtonView.reset();
211         mPlayPauseButtonView.reset();
212         mFullButtonView.requestFocus();
213     }
214 
215     /**
216      * Sets the {@link Listener} to listen user actions.
217      */
setListener(Listener listener)218     public void setListener(Listener listener) {
219         mListener = listener;
220     }
221 
222     /**
223      * Returns the focused control button view to animate focused button.
224      */
getFocusedButton()225     PipControlButtonView getFocusedButton() {
226         return mFocusedChild;
227     }
228 }
229