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