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