1 /* 2 * Copyright 2018 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.widget; 18 19 import android.content.Context; 20 import android.graphics.Rect; 21 import android.media.MediaPlayer2; 22 import android.support.annotation.NonNull; 23 import android.util.AttributeSet; 24 import android.util.Log; 25 import android.view.SurfaceHolder; 26 import android.view.SurfaceView; 27 import android.view.View; 28 29 import static android.widget.VideoView2.VIEW_TYPE_SURFACEVIEW; 30 31 class VideoSurfaceView extends SurfaceView implements VideoViewInterface, SurfaceHolder.Callback { 32 private static final String TAG = "VideoSurfaceView"; 33 private static final boolean DEBUG = true; // STOPSHIP: Log.isLoggable(TAG, Log.DEBUG); 34 private SurfaceHolder mSurfaceHolder = null; 35 private SurfaceListener mSurfaceListener = null; 36 private MediaPlayer2 mMediaPlayer; 37 // A flag to indicate taking over other view should be proceed. 38 private boolean mIsTakingOverOldView; 39 private VideoViewInterface mOldView; 40 41 VideoSurfaceView(Context context)42 public VideoSurfaceView(Context context) { 43 this(context, null); 44 } 45 VideoSurfaceView(Context context, AttributeSet attrs)46 public VideoSurfaceView(Context context, AttributeSet attrs) { 47 this(context, attrs, 0); 48 } 49 VideoSurfaceView(Context context, AttributeSet attrs, int defStyleAttr)50 public VideoSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) { 51 this(context, attrs, defStyleAttr, 0); 52 } 53 VideoSurfaceView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)54 public VideoSurfaceView(Context context, AttributeSet attrs, int defStyleAttr, 55 int defStyleRes) { 56 super(context, attrs, defStyleAttr, defStyleRes); 57 getHolder().addCallback(this); 58 } 59 60 //////////////////////////////////////////////////// 61 // implements VideoViewInterface 62 //////////////////////////////////////////////////// 63 64 @Override assignSurfaceToMediaPlayer(MediaPlayer2 mp)65 public boolean assignSurfaceToMediaPlayer(MediaPlayer2 mp) { 66 Log.d(TAG, "assignSurfaceToMediaPlayer(): mSurfaceHolder: " + mSurfaceHolder); 67 if (mp == null || !hasAvailableSurface()) { 68 return false; 69 } 70 mp.setDisplay(mSurfaceHolder); 71 return true; 72 } 73 74 @Override setSurfaceListener(SurfaceListener l)75 public void setSurfaceListener(SurfaceListener l) { 76 mSurfaceListener = l; 77 } 78 79 @Override getViewType()80 public int getViewType() { 81 return VIEW_TYPE_SURFACEVIEW; 82 } 83 84 @Override setMediaPlayer(MediaPlayer2 mp)85 public void setMediaPlayer(MediaPlayer2 mp) { 86 mMediaPlayer = mp; 87 if (mIsTakingOverOldView) { 88 takeOver(mOldView); 89 } 90 } 91 92 @Override takeOver(@onNull VideoViewInterface oldView)93 public void takeOver(@NonNull VideoViewInterface oldView) { 94 if (assignSurfaceToMediaPlayer(mMediaPlayer)) { 95 ((View) oldView).setVisibility(GONE); 96 mIsTakingOverOldView = false; 97 mOldView = null; 98 if (mSurfaceListener != null) { 99 mSurfaceListener.onSurfaceTakeOverDone(this); 100 } 101 } else { 102 mIsTakingOverOldView = true; 103 mOldView = oldView; 104 } 105 } 106 107 @Override hasAvailableSurface()108 public boolean hasAvailableSurface() { 109 return (mSurfaceHolder != null && mSurfaceHolder.getSurface() != null); 110 } 111 112 //////////////////////////////////////////////////// 113 // implements SurfaceHolder.Callback 114 //////////////////////////////////////////////////// 115 116 @Override surfaceCreated(SurfaceHolder holder)117 public void surfaceCreated(SurfaceHolder holder) { 118 Log.d(TAG, "surfaceCreated: mSurfaceHolder: " + mSurfaceHolder + ", new holder: " + holder); 119 mSurfaceHolder = holder; 120 if (mIsTakingOverOldView) { 121 takeOver(mOldView); 122 } else { 123 assignSurfaceToMediaPlayer(mMediaPlayer); 124 } 125 126 if (mSurfaceListener != null) { 127 Rect rect = mSurfaceHolder.getSurfaceFrame(); 128 mSurfaceListener.onSurfaceCreated(this, rect.width(), rect.height()); 129 } 130 } 131 132 @Override surfaceChanged(SurfaceHolder holder, int format, int width, int height)133 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { 134 if (mSurfaceListener != null) { 135 mSurfaceListener.onSurfaceChanged(this, width, height); 136 } 137 } 138 139 @Override surfaceDestroyed(SurfaceHolder holder)140 public void surfaceDestroyed(SurfaceHolder holder) { 141 // After we return from this we can't use the surface any more 142 mSurfaceHolder = null; 143 if (mSurfaceListener != null) { 144 mSurfaceListener.onSurfaceDestroyed(this); 145 } 146 } 147 148 // TODO: Investigate the way to move onMeasure() code into FrameLayout. 149 @Override onMeasure(int widthMeasureSpec, int heightMeasureSpec)150 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 151 int videoWidth = (mMediaPlayer == null) ? 0 : mMediaPlayer.getVideoWidth(); 152 int videoHeight = (mMediaPlayer == null) ? 0 : mMediaPlayer.getVideoHeight(); 153 if (DEBUG) { 154 Log.d(TAG, "onMeasure(" + MeasureSpec.toString(widthMeasureSpec) + ", " 155 + MeasureSpec.toString(heightMeasureSpec) + ")"); 156 Log.i(TAG, " measuredSize: " + getMeasuredWidth() + "/" + getMeasuredHeight()); 157 Log.i(TAG, " viewSize: " + getWidth() + "/" + getHeight()); 158 Log.i(TAG, " mVideoWidth/height: " + videoWidth + ", " + videoHeight); 159 } 160 161 int width = getDefaultSize(videoWidth, widthMeasureSpec); 162 int height = getDefaultSize(videoHeight, heightMeasureSpec); 163 164 if (videoWidth > 0 && videoHeight > 0) { 165 int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec); 166 int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec); 167 168 width = widthSpecSize; 169 height = heightSpecSize; 170 171 // for compatibility, we adjust size based on aspect ratio 172 if (videoWidth * height < width * videoHeight) { 173 width = height * videoWidth / videoHeight; 174 if (DEBUG) { 175 Log.d(TAG, "image too wide, correcting. width: " + width); 176 } 177 } else if (videoWidth * height > width * videoHeight) { 178 height = width * videoHeight / videoWidth; 179 if (DEBUG) { 180 Log.d(TAG, "image too tall, correcting. height: " + height); 181 } 182 } 183 } else { 184 // no size yet, just adopt the given spec sizes 185 } 186 setMeasuredDimension(width, height); 187 if (DEBUG) { 188 Log.i(TAG, "end of onMeasure()"); 189 Log.i(TAG, " measuredSize: " + getMeasuredWidth() + "/" + getMeasuredHeight()); 190 } 191 } 192 193 @Override toString()194 public String toString() { 195 return "ViewType: SurfaceView / Visibility: " + getVisibility() 196 + " / surfaceHolder: " + mSurfaceHolder; 197 } 198 } 199