Home | History | Annotate | Download | only in widget
      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.SurfaceTexture;
     21 import android.media.MediaPlayer2;
     22 import android.support.annotation.NonNull;
     23 import android.support.annotation.RequiresApi;
     24 import android.util.AttributeSet;
     25 import android.util.Log;
     26 import android.view.Surface;
     27 import android.view.TextureView;
     28 import android.view.View;
     29 
     30 import static android.widget.VideoView2.VIEW_TYPE_TEXTUREVIEW;
     31 
     32 @RequiresApi(26)
     33 class VideoTextureView extends TextureView
     34         implements VideoViewInterface, TextureView.SurfaceTextureListener {
     35     private static final String TAG = "VideoTextureView";
     36     private static final boolean DEBUG = true; // STOPSHIP: Log.isLoggable(TAG, Log.DEBUG);
     37 
     38     private SurfaceTexture mSurfaceTexture;
     39     private Surface mSurface;
     40     private SurfaceListener mSurfaceListener;
     41     private MediaPlayer2 mMediaPlayer;
     42     // A flag to indicate taking over other view should be proceed.
     43     private boolean mIsTakingOverOldView;
     44     private VideoViewInterface mOldView;
     45 
     46     public VideoTextureView(Context context) {
     47         this(context, null);
     48     }
     49 
     50     public VideoTextureView(Context context, AttributeSet attrs) {
     51         this(context, attrs, 0);
     52     }
     53 
     54     public VideoTextureView(Context context, AttributeSet attrs, int defStyleAttr) {
     55         this(context, attrs, defStyleAttr, 0);
     56     }
     57 
     58     public VideoTextureView(
     59             Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
     60         super(context, attrs, defStyleAttr, defStyleRes);
     61         setSurfaceTextureListener(this);
     62     }
     63 
     64     ////////////////////////////////////////////////////
     65     // implements VideoViewInterface
     66     ////////////////////////////////////////////////////
     67 
     68     @Override
     69     public boolean assignSurfaceToMediaPlayer(MediaPlayer2 mp) {
     70         Log.d(TAG, "assignSurfaceToMediaPlayer(): mSurfaceTexture: " + mSurfaceTexture);
     71         if (mp == null || !hasAvailableSurface()) {
     72             // Surface is not ready.
     73             return false;
     74         }
     75         mp.setSurface(mSurface);
     76         return true;
     77     }
     78 
     79     @Override
     80     public void setSurfaceListener(SurfaceListener l) {
     81         mSurfaceListener = l;
     82     }
     83 
     84     @Override
     85     public int getViewType() {
     86         return VIEW_TYPE_TEXTUREVIEW;
     87     }
     88 
     89     @Override
     90     public void setMediaPlayer(MediaPlayer2 mp) {
     91         mMediaPlayer = mp;
     92         if (mIsTakingOverOldView) {
     93             takeOver(mOldView);
     94         }
     95     }
     96 
     97     @Override
     98     public void takeOver(@NonNull VideoViewInterface oldView) {
     99         if (assignSurfaceToMediaPlayer(mMediaPlayer)) {
    100             ((View) oldView).setVisibility(GONE);
    101             mIsTakingOverOldView = false;
    102             mOldView = null;
    103             if (mSurfaceListener != null) {
    104                 mSurfaceListener.onSurfaceTakeOverDone(this);
    105             }
    106         } else {
    107             mIsTakingOverOldView = true;
    108             mOldView = oldView;
    109         }
    110     }
    111 
    112     @Override
    113     public boolean hasAvailableSurface() {
    114         return (mSurfaceTexture != null && !mSurfaceTexture.isReleased() && mSurface != null);
    115     }
    116 
    117     ////////////////////////////////////////////////////
    118     // implements TextureView.SurfaceTextureListener
    119     ////////////////////////////////////////////////////
    120 
    121     @Override
    122     public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) {
    123         Log.d(TAG, "onSurfaceTextureAvailable: mSurfaceTexture: " + mSurfaceTexture
    124                 + ", new surface: " + surfaceTexture);
    125         mSurfaceTexture = surfaceTexture;
    126         mSurface = new Surface(mSurfaceTexture);
    127         if (mIsTakingOverOldView) {
    128             takeOver(mOldView);
    129         } else {
    130             assignSurfaceToMediaPlayer(mMediaPlayer);
    131         }
    132         if (mSurfaceListener != null) {
    133             mSurfaceListener.onSurfaceCreated(this, width, height);
    134         }
    135     }
    136 
    137     @Override
    138     public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int width, int height) {
    139         if (mSurfaceListener != null) {
    140             mSurfaceListener.onSurfaceChanged(this, width, height);
    141         }
    142         // requestLayout();  // TODO: figure out if it should be called here?
    143     }
    144 
    145     @Override
    146     public void onSurfaceTextureUpdated(SurfaceTexture surface) {
    147         // no-op
    148     }
    149 
    150     @Override
    151     public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
    152         if (mSurfaceListener != null) {
    153             mSurfaceListener.onSurfaceDestroyed(this);
    154         }
    155         mSurfaceTexture = null;
    156         mSurface = null;
    157         return true;
    158     }
    159 
    160     @Override
    161     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    162         int videoWidth = (mMediaPlayer == null) ? 0 : mMediaPlayer.getVideoWidth();
    163         int videoHeight = (mMediaPlayer == null) ? 0 : mMediaPlayer.getVideoHeight();
    164         if (DEBUG) {
    165             Log.d(TAG, "onMeasure(" + MeasureSpec.toString(widthMeasureSpec) + ", "
    166                     + MeasureSpec.toString(heightMeasureSpec) + ")");
    167             Log.i(TAG, " measuredSize: " + getMeasuredWidth() + "/" + getMeasuredHeight());
    168             Log.i(TAG, " viewSize: " + getWidth() + "/" + getHeight());
    169             Log.i(TAG, " mVideoWidth/height: " + videoWidth + ", " + videoHeight);
    170         }
    171 
    172         int width = getDefaultSize(videoWidth, widthMeasureSpec);
    173         int height = getDefaultSize(videoHeight, heightMeasureSpec);
    174 
    175         if (videoWidth > 0 && videoHeight > 0) {
    176             int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
    177             int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
    178 
    179             width = widthSpecSize;
    180             height = heightSpecSize;
    181 
    182             // for compatibility, we adjust size based on aspect ratio
    183             if (videoWidth * height < width * videoHeight) {
    184                 width = height * videoWidth / videoHeight;
    185                 if (DEBUG) {
    186                     Log.d(TAG, "image too wide, correcting. width: " + width);
    187                 }
    188             } else if (videoWidth * height > width * videoHeight) {
    189                 height = width * videoHeight / videoWidth;
    190                 if (DEBUG) {
    191                     Log.d(TAG, "image too tall, correcting. height: " + height);
    192                 }
    193             }
    194         } else {
    195             // no size yet, just adopt the given spec sizes
    196         }
    197         setMeasuredDimension(width, height);
    198         if (DEBUG) {
    199             Log.i(TAG, "end of onMeasure()");
    200             Log.i(TAG, " measuredSize: " + getMeasuredWidth() + "/" + getMeasuredHeight());
    201         }
    202     }
    203 
    204     @Override
    205     public String toString() {
    206         return "ViewType: TextureView / Visibility: " + getVisibility()
    207                 + " / surfaceTexture: " + mSurfaceTexture;
    208 
    209     }
    210 }
    211