1/* 2 * Copyright (C) 2012 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.gallery3d.app; 18 19import android.content.Context; 20import android.graphics.Rect; 21import android.view.Gravity; 22import android.view.KeyEvent; 23import android.view.MotionEvent; 24import android.view.View; 25import android.view.View.OnClickListener; 26import android.widget.FrameLayout; 27import android.widget.ImageView; 28import android.widget.ImageView.ScaleType; 29import android.widget.LinearLayout; 30import android.widget.ProgressBar; 31import android.widget.RelativeLayout; 32import android.widget.TextView; 33 34import com.android.gallery3d.R; 35 36/** 37 * The common playback controller for the Movie Player or Video Trimming. 38 */ 39public abstract class CommonControllerOverlay extends FrameLayout implements 40 ControllerOverlay, 41 OnClickListener, 42 TimeBar.Listener { 43 44 protected enum State { 45 PLAYING, 46 PAUSED, 47 ENDED, 48 ERROR, 49 LOADING 50 } 51 52 private static final float ERROR_MESSAGE_RELATIVE_PADDING = 1.0f / 6; 53 54 protected Listener mListener; 55 56 protected final View mBackground; 57 protected TimeBar mTimeBar; 58 59 protected View mMainView; 60 protected final LinearLayout mLoadingView; 61 protected final TextView mErrorView; 62 protected final ImageView mPlayPauseReplayView; 63 64 protected State mState; 65 66 protected boolean mCanReplay = true; 67 68 public CommonControllerOverlay(Context context) { 69 super(context); 70 71 mState = State.LOADING; 72 // TODO: Move the following layout code into xml file. 73 LayoutParams wrapContent = 74 new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); 75 LayoutParams matchParent = 76 new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); 77 78 mBackground = new View(context); 79 mBackground.setBackgroundColor(context.getResources().getColor(R.color.darker_transparent)); 80 addView(mBackground, matchParent); 81 82 // Depending on the usage, the timeBar can show a single scrubber, or 83 // multiple ones for trimming. 84 createTimeBar(context); 85 addView(mTimeBar, wrapContent); 86 87 mLoadingView = new LinearLayout(context); 88 mLoadingView.setOrientation(LinearLayout.VERTICAL); 89 mLoadingView.setGravity(Gravity.CENTER_HORIZONTAL); 90 ProgressBar spinner = new ProgressBar(context); 91 spinner.setIndeterminate(true); 92 mLoadingView.addView(spinner, wrapContent); 93 TextView loadingText = createOverlayTextView(context); 94 loadingText.setText(R.string.loading_video); 95 mLoadingView.addView(loadingText, wrapContent); 96 addView(mLoadingView, wrapContent); 97 98 mPlayPauseReplayView = new ImageView(context); 99 mPlayPauseReplayView.setImageResource(R.drawable.ic_vidcontrol_play); 100 mPlayPauseReplayView.setBackgroundResource(R.drawable.bg_vidcontrol); 101 mPlayPauseReplayView.setScaleType(ScaleType.CENTER); 102 mPlayPauseReplayView.setFocusable(true); 103 mPlayPauseReplayView.setClickable(true); 104 mPlayPauseReplayView.setOnClickListener(this); 105 addView(mPlayPauseReplayView, wrapContent); 106 107 mErrorView = createOverlayTextView(context); 108 addView(mErrorView, matchParent); 109 110 RelativeLayout.LayoutParams params = 111 new RelativeLayout.LayoutParams( 112 LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); 113 setLayoutParams(params); 114 hide(); 115 } 116 117 abstract protected void createTimeBar(Context context); 118 119 private TextView createOverlayTextView(Context context) { 120 TextView view = new TextView(context); 121 view.setGravity(Gravity.CENTER); 122 view.setTextColor(0xFFFFFFFF); 123 view.setPadding(0, 15, 0, 15); 124 return view; 125 } 126 127 @Override 128 public void setListener(Listener listener) { 129 this.mListener = listener; 130 } 131 132 @Override 133 public void setCanReplay(boolean canReplay) { 134 this.mCanReplay = canReplay; 135 } 136 137 @Override 138 public View getView() { 139 return this; 140 } 141 142 @Override 143 public void showPlaying() { 144 mState = State.PLAYING; 145 showMainView(mPlayPauseReplayView); 146 } 147 148 @Override 149 public void showPaused() { 150 mState = State.PAUSED; 151 showMainView(mPlayPauseReplayView); 152 } 153 154 @Override 155 public void showEnded() { 156 mState = State.ENDED; 157 showMainView(mPlayPauseReplayView); 158 } 159 160 @Override 161 public void showLoading() { 162 mState = State.LOADING; 163 showMainView(mLoadingView); 164 } 165 166 @Override 167 public void showErrorMessage(String message) { 168 mState = State.ERROR; 169 int padding = (int) (getMeasuredWidth() * ERROR_MESSAGE_RELATIVE_PADDING); 170 mErrorView.setPadding( 171 padding, mErrorView.getPaddingTop(), padding, mErrorView.getPaddingBottom()); 172 mErrorView.setText(message); 173 showMainView(mErrorView); 174 } 175 176 @Override 177 public void setTimes(int currentTime, int totalTime, 178 int trimStartTime, int trimEndTime) { 179 mTimeBar.setTime(currentTime, totalTime, trimStartTime, trimEndTime); 180 } 181 182 public void hide() { 183 mPlayPauseReplayView.setVisibility(View.INVISIBLE); 184 mLoadingView.setVisibility(View.INVISIBLE); 185 mBackground.setVisibility(View.INVISIBLE); 186 mTimeBar.setVisibility(View.INVISIBLE); 187 setVisibility(View.INVISIBLE); 188 setFocusable(true); 189 requestFocus(); 190 } 191 192 private void showMainView(View view) { 193 mMainView = view; 194 mErrorView.setVisibility(mMainView == mErrorView ? View.VISIBLE : View.INVISIBLE); 195 mLoadingView.setVisibility(mMainView == mLoadingView ? View.VISIBLE : View.INVISIBLE); 196 mPlayPauseReplayView.setVisibility( 197 mMainView == mPlayPauseReplayView ? View.VISIBLE : View.INVISIBLE); 198 show(); 199 } 200 201 @Override 202 public void show() { 203 updateViews(); 204 setVisibility(View.VISIBLE); 205 setFocusable(false); 206 } 207 208 @Override 209 public void onClick(View view) { 210 if (mListener != null) { 211 if (view == mPlayPauseReplayView) { 212 if (mState == State.ENDED) { 213 if (mCanReplay) { 214 mListener.onReplay(); 215 } 216 } else if (mState == State.PAUSED || mState == State.PLAYING) { 217 mListener.onPlayPause(); 218 } 219 } 220 } 221 } 222 223 @Override 224 public boolean onKeyDown(int keyCode, KeyEvent event) { 225 return super.onKeyDown(keyCode, event); 226 } 227 228 @Override 229 public boolean onTouchEvent(MotionEvent event) { 230 if (super.onTouchEvent(event)) { 231 return true; 232 } 233 return false; 234 } 235 236 // The paddings of 4 sides which covered by system components. E.g. 237 // +-----------------+\ 238 // | Action Bar | insets.top 239 // +-----------------+/ 240 // | | 241 // | Content Area | insets.right = insets.left = 0 242 // | | 243 // +-----------------+\ 244 // | Navigation Bar | insets.bottom 245 // +-----------------+/ 246 // Please see View.fitSystemWindows() for more details. 247 private final Rect mWindowInsets = new Rect(); 248 249 @Override 250 protected boolean fitSystemWindows(Rect insets) { 251 // We don't set the paddings of this View, otherwise, 252 // the content will get cropped outside window 253 mWindowInsets.set(insets); 254 return true; 255 } 256 257 @Override 258 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 259 Rect insets = mWindowInsets; 260 int pl = insets.left; // the left paddings 261 int pr = insets.right; 262 int pt = insets.top; 263 int pb = insets.bottom; 264 265 int h = bottom - top; 266 int w = right - left; 267 boolean error = mErrorView.getVisibility() == View.VISIBLE; 268 269 int y = h - pb; 270 // Put both TimeBar and Background just above the bottom system 271 // component. 272 // But extend the background to the width of the screen, since we don't 273 // care if it will be covered by a system component and it looks better. 274 mBackground.layout(0, y - mTimeBar.getBarHeight(), w, y); 275 mTimeBar.layout(pl, y - mTimeBar.getPreferredHeight(), w - pr, y); 276 277 // Needed, otherwise the framework will not re-layout in case only the 278 // padding is changed 279 mTimeBar.requestLayout(); 280 281 // Put the play/pause/next/ previous button in the center of the screen 282 layoutCenteredView(mPlayPauseReplayView, 0, 0, w, h); 283 284 if (mMainView != null) { 285 layoutCenteredView(mMainView, 0, 0, w, h); 286 } 287 } 288 289 private void layoutCenteredView(View view, int l, int t, int r, int b) { 290 int cw = view.getMeasuredWidth(); 291 int ch = view.getMeasuredHeight(); 292 int cl = (r - l - cw) / 2; 293 int ct = (b - t - ch) / 2; 294 view.layout(cl, ct, cl + cw, ct + ch); 295 } 296 297 @Override 298 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 299 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 300 measureChildren(widthMeasureSpec, heightMeasureSpec); 301 } 302 303 protected void updateViews() { 304 mBackground.setVisibility(View.VISIBLE); 305 mTimeBar.setVisibility(View.VISIBLE); 306 mPlayPauseReplayView.setImageResource( 307 mState == State.PAUSED ? R.drawable.ic_vidcontrol_play : 308 mState == State.PLAYING ? R.drawable.ic_vidcontrol_pause : 309 R.drawable.ic_vidcontrol_reload); 310 mPlayPauseReplayView.setVisibility( 311 (mState != State.LOADING && mState != State.ERROR && 312 !(mState == State.ENDED && !mCanReplay)) 313 ? View.VISIBLE : View.GONE); 314 requestLayout(); 315 } 316 317 // TimeBar listener 318 319 @Override 320 public void onScrubbingStart() { 321 mListener.onSeekStart(); 322 } 323 324 @Override 325 public void onScrubbingMove(int time) { 326 mListener.onSeekMove(time); 327 } 328 329 @Override 330 public void onScrubbingEnd(int time, int trimStartTime, int trimEndTime) { 331 mListener.onSeekEnd(time, trimStartTime, trimEndTime); 332 } 333} 334