文章目录

    • 起因
    • 配置
      • 依赖
      • 配置文件
        • AndroidMediaController.java
        • *FileMediaDataSource.java
        • *IjkVideoView
        • *IMediaController.java
        • *InfoHudViewHolder
        • *IRenderView.java
        • *MeasureHelper.java
        • *MediaPlayerCompat.java
        • *MediaPlayerService.java
        • PlayerManager.java
        • *Settings.java
        • SurfaceRenderView.java
        • TableLayoutBinder.java
        • TextureRenderView.java
      • 引入库
      • 代码引入
    • 缺点
    • 实在太卡了,我也不知道我为什么这么傻把代码全放上来,这就是基本的配置使用,等有空我会把文件上传了。

起因

最近需要使用到播放器,自带的播放器真的烂,网上其实资料根本不多,虽然有几个开源的大项目,但是都没有什么教程,说的非常模糊,找到的博客也同样说的很模糊,很难自己二次开发啊。
先说收ijkplayer的使用。引入项目大概需要以下四步

  1. 引入依赖
  2. 添加配置文件
  3. 引入so库(我不知道有了依赖后还要不要这个库)
  4. 代码引入

配置

依赖

build.gradle中添加依赖,根据自己需求添加

dependencies {    implementation fileTree(dir: 'libs', include: ['*.jar'])    implementation 'com.android.support:appcompat-v7:26.1.0'    implementation 'com.android.support.constraint:constraint-layout:1.1.3'    testImplementation 'junit:junit:4.12'    androidTestImplementation 'com.android.support.test:runner:1.0.2'    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'    androidTestCompile('com.android.support:support-annotations:26.1.0') {        force = true    }    ......    //哔站的ijkplayer    compile 'tv.danmaku.ijk.media:ijkplayer-java:0.8.8'    compile 'tv.danmaku.ijk.media:ijkplayer-armv7a:0.8.8'    compile 'tv.danmaku.ijk.media:ijkplayer-armv5:0.8.8'    compile 'tv.danmaku.ijk.media:ijkplayer-arm64:0.8.8'    compile 'tv.danmaku.ijk.media:ijkplayer-x86:0.8.8'    compile 'tv.danmaku.ijk.media:ijkplayer-x86_64:0.8.8'//    compile 'tv.danmaku.ijk.media:ijkplayer-exo:0.8.8'//    compile 'tv.danmaku.ijk.media:ijkplayer-java:0.6.2'}

配置文件

这里使用了14个配置文件,真的多,分别说说吧

AndroidMediaController.java

/* * Copyright (C) 2015 Bilibili * Copyright (C) 2015 Zhang Rui  * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *      http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package com.example.k.bilibiliapi.video;import android.content.Context;import android.support.annotation.NonNull;import android.support.annotation.Nullable;import android.support.v7.app.ActionBar;import android.util.AttributeSet;import android.view.View;import android.widget.MediaController;import java.util.ArrayList;public class AndroidMediaController extends MediaController implements IMediaController {         private ActionBar mActionBar;    public AndroidMediaController(Context context, AttributeSet attrs) {             super(context, attrs);        initView(context);    }    public AndroidMediaController(Context context, boolean useFastForward) {             super(context, useFastForward);        initView(context);    }    public AndroidMediaController(Context context) {             super(context);        initView(context);    }    private void initView(Context context) {         }    public void setSupportActionBar(@Nullable ActionBar actionBar) {             mActionBar = actionBar;        if (isShowing()) {                 actionBar.show();        } else {                 actionBar.hide();        }    }    @Override    public void show() {             super.show();        if (mActionBar != null)            mActionBar.show();    }    @Override    public void hide() {             super.hide();        if (mActionBar != null)            mActionBar.hide();        for (View view : mShowOnceArray)            view.setVisibility(View.GONE);        mShowOnceArray.clear();    }    //----------    // Extends    //----------    private ArrayList<View> mShowOnceArray = new ArrayList<View>();    public void showOnce(@NonNull View view) {             mShowOnceArray.add(view);        view.setVisibility(View.VISIBLE);        show();    }}

*FileMediaDataSource.java

/* * Copyright (C) 2015 Bilibili * Copyright (C) 2015 Zhang Rui  * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *      http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package com.example.k.bilibiliapi.video;import java.io.File;import java.io.IOException;import java.io.RandomAccessFile;import tv.danmaku.ijk.media.player.misc.IMediaDataSource;public class FileMediaDataSource implements IMediaDataSource {         private RandomAccessFile mFile;    private long mFileSize;    public FileMediaDataSource(File file) throws IOException {             mFile = new RandomAccessFile(file, "r");        mFileSize = mFile.length();    }    @Override    public int readAt(long position, byte[] buffer, int offset, int size) throws IOException {             if (mFile.getFilePointer() != position)            mFile.seek(position);        if (size == 0)            return 0;        return mFile.read(buffer, 0, size);    }    @Override    public long getSize() throws IOException {             return mFileSize;    }    @Override    public void close() throws IOException {             mFileSize = 0;        mFile.close();        mFile = null;    }}

*IjkVideoView

/* * Copyright (C) 2015 Bilibili * Copyright (C) 2015 Zhang Rui  * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *      http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package com.example.k.bilibiliapi.video;import android.annotation.TargetApi;import android.content.Context;import android.content.DialogInterface;import android.content.res.Resources;import android.media.AudioManager;import android.media.MediaPlayer;import android.net.Uri;import android.os.Build;import android.support.annotation.NonNull;import android.support.v7.app.AlertDialog;import android.text.TextUtils;import android.util.AttributeSet;import android.util.Log;import android.view.Gravity;import android.view.KeyEvent;import android.view.MotionEvent;import android.view.View;import android.widget.FrameLayout;import android.widget.MediaController;import android.widget.TableLayout;import android.widget.TextView;import com.example.k.bilibiliapi.R;import java.io.File;import java.io.IOException;import java.util.ArrayList;import java.util.List;import java.util.Locale;import java.util.Map;import tv.danmaku.ijk.media.exo.IjkExoMediaPlayer;import tv.danmaku.ijk.media.player.AndroidMediaPlayer;import tv.danmaku.ijk.media.player.IMediaPlayer;import tv.danmaku.ijk.media.player.IjkMediaPlayer;import tv.danmaku.ijk.media.player.IjkTimedText;import tv.danmaku.ijk.media.player.TextureMediaPlayer;import tv.danmaku.ijk.media.player.misc.IMediaDataSource;import tv.danmaku.ijk.media.player.misc.IMediaFormat;import tv.danmaku.ijk.media.player.misc.ITrackInfo;import tv.danmaku.ijk.media.player.misc.IjkMediaFormat;public class IjkVideoView extends FrameLayout implements MediaController.MediaPlayerControl {         private String TAG = "IjkVideoView";    // settable by the client    private Uri mUri;    private Map<String, String> mHeaders;    // all possible internal states    private static final int STATE_ERROR = -1;    private static final int STATE_IDLE = 0;    private static final int STATE_PREPARING = 1;    private static final int STATE_PREPARED = 2;    private static final int STATE_PLAYING = 3;    private static final int STATE_PAUSED = 4;    private static final int STATE_PLAYBACK_COMPLETED = 5;    // mCurrentState is a VideoView object's current state.    // mTargetState is the state that a method caller intends to reach.    // For instance, regardless the VideoView object's current state,    // calling pause() intends to bring the object to a target state    // of STATE_PAUSED.    private int mCurrentState = STATE_IDLE;    private int mTargetState = STATE_IDLE;    // All the stuff we need for playing and showing a video    private IRenderView.ISurfaceHolder mSurfaceHolder = null;    private IMediaPlayer mMediaPlayer = null;    // private int         mAudioSession;    private int mVideoWidth;    private int mVideoHeight;    private int mSurfaceWidth;    private int mSurfaceHeight;    private int mVideoRotationDegree;    private IMediaController mMediaController;    private IMediaPlayer.OnCompletionListener mOnCompletionListener;    private IMediaPlayer.OnPreparedListener mOnPreparedListener;    private int mCurrentBufferPercentage;    private IMediaPlayer.OnErrorListener mOnErrorListener;    private IMediaPlayer.OnInfoListener mOnInfoListener;    private int mSeekWhenPrepared;  // recording the seek position while preparing    private boolean mCanPause = true;    private boolean mCanSeekBack = true;    private boolean mCanSeekForward = true;    /** Subtitle rendering widget overlaid on top of the video. */    // private RenderingWidget mSubtitleWidget;    /**     * Listener for changes to subtitle data, used to redraw when needed.     */    // private RenderingWidget.OnChangedListener mSubtitlesChangedListener;    private Context mAppContext;    private Settings mSettings;    private IRenderView mRenderView;    private int mVideoSarNum;    private int mVideoSarDen;    private InfoHudViewHolder mHudViewHolder;    private long mPrepareStartTime = 0;    private long mPrepareEndTime = 0;    private long mSeekStartTime = 0;    private long mSeekEndTime = 0;    private TextView subtitleDisplay;    public IjkVideoView(Context context) {             super(context);        initVideoView(context);    }    public IjkVideoView(Context context, AttributeSet attrs) {             super(context, attrs);        initVideoView(context);    }    public IjkVideoView(Context context, AttributeSet attrs, int defStyleAttr) {             super(context, attrs, defStyleAttr);        initVideoView(context);    }    @TargetApi(Build.VERSION_CODES.LOLLIPOP)    public IjkVideoView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {             super(context, attrs, defStyleAttr, defStyleRes);        initVideoView(context);    }    // REMOVED: onMeasure    // REMOVED: onInitializeAccessibilityEvent    // REMOVED: onInitializeAccessibilityNodeInfo    // REMOVED: resolveAdjustedSize    private void initVideoView(Context context) {             mAppContext = context.getApplicationContext();        mSettings = new Settings(mAppContext);        initBackground();        initRenders();        mVideoWidth = 0;        mVideoHeight = 0;        // REMOVED: getHolder().addCallback(mSHCallback);        // REMOVED: getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);        setFocusable(true);        setFocusableInTouchMode(true);        requestFocus();        // REMOVED: mPendingSubtitleTracks = new Vector>();        mCurrentState = STATE_IDLE;        mTargetState = STATE_IDLE;        subtitleDisplay = new TextView(context);        subtitleDisplay.setTextSize(24);        subtitleDisplay.setGravity(Gravity.CENTER);        LayoutParams layoutParams_txt = new LayoutParams(                LayoutParams.MATCH_PARENT,                LayoutParams.WRAP_CONTENT,                Gravity.BOTTOM);        addView(subtitleDisplay, layoutParams_txt);    }    public void setRenderView(IRenderView renderView) {             if (mRenderView != null) {                 if (mMediaPlayer != null)                mMediaPlayer.setDisplay(null);            View renderUIView = mRenderView.getView();            mRenderView.removeRenderCallback(mSHCallback);            mRenderView = null;            removeView(renderUIView);        }        if (renderView == null)            return;        mRenderView = renderView;        renderView.setAspectRatio(mCurrentAspectRatio);        if (mVideoWidth > 0 && mVideoHeight > 0)            renderView.setVideoSize(mVideoWidth, mVideoHeight);        if (mVideoSarNum > 0 && mVideoSarDen > 0)            renderView.setVideoSampleAspectRatio(mVideoSarNum, mVideoSarDen);        View renderUIView = mRenderView.getView();        LayoutParams lp = new LayoutParams(                LayoutParams.WRAP_CONTENT,                LayoutParams.WRAP_CONTENT,                Gravity.CENTER);        renderUIView.setLayoutParams(lp);        addView(renderUIView);        mRenderView.addRenderCallback(mSHCallback);        mRenderView.setVideoRotation(mVideoRotationDegree);    }    public void setAspectRatio(int arAspectFitParent){             if (mRenderView != null) {                 mRenderView.setAspectRatio(arAspectFitParent);        }    }    public void setRender(int render) {             switch (render) {                 case RENDER_NONE:                setRenderView(null);                break;            case RENDER_SURFACE_VIEW:            case RENDER_TEXTURE_VIEW: {                     TextureRenderView renderView = new TextureRenderView(getContext());                if (mMediaPlayer != null) {                         renderView.getSurfaceHolder().bindToMediaPlayer(mMediaPlayer);                    renderView.setVideoSize(mMediaPlayer.getVideoWidth(), mMediaPlayer.getVideoHeight());                    renderView.setVideoSampleAspectRatio(mMediaPlayer.getVideoSarNum(), mMediaPlayer.getVideoSarDen());                    renderView.setAspectRatio(mCurrentAspectRatio);                }                setRenderView(renderView);                break;            }            default:                Log.e(TAG, String.format(Locale.getDefault(), "invalid render %d\n", render));                break;        }    }    public void setHudView(TableLayout tableLayout) {             mHudViewHolder = new InfoHudViewHolder(getContext(), tableLayout);    }    /**     * Sets video path.     *     * @param path the path of the video.     */    public void setVideoPath(String path) {             setVideoURI(Uri.parse(path));    }    /**     * Sets video URI.     *     * @param uri the URI of the video.     */    public void setVideoURI(Uri uri) {             setVideoURI(uri, null);    }    /**     * Sets video URI using specific headers.     *     * @param uri     the URI of the video.     * @param headers the headers for the URI request.     *                Note that the cross domain redirection is allowed by default, but that can be     *                changed with key/value pairs through the headers parameter with     *                "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value     *                to disallow or allow cross domain redirection.     */    private void setVideoURI(Uri uri, Map<String, String> headers) {             mUri = uri;        mHeaders = headers;        mSeekWhenPrepared = 0;        openVideo();        requestLayout();        invalidate();    }    // REMOVED: addSubtitleSource    // REMOVED: mPendingSubtitleTracks    public void stopPlayback() {             if (mMediaPlayer != null) {                 mMediaPlayer.stop();            mMediaPlayer.release();            mMediaPlayer = null;            if (mHudViewHolder != null)                mHudViewHolder.setMediaPlayer(null);            mCurrentState = STATE_IDLE;            mTargetState = STATE_IDLE;            AudioManager am = (AudioManager) mAppContext.getSystemService(Context.AUDIO_SERVICE);            am.abandonAudioFocus(null);        }    }    @TargetApi(Build.VERSION_CODES.M)    private void openVideo() {             if (mUri == null || mSurfaceHolder == null) {                 // not ready for playback just yet, will try again later            return;        }        // we shouldn't clear the target state, because somebody might have        // called start() previously        release(false);        AudioManager am = (AudioManager) mAppContext.getSystemService(Context.AUDIO_SERVICE);        am.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);        try {                 mMediaPlayer = createPlayer(mSettings.getPlayer());            // TODO: create SubtitleController in MediaPlayer, but we need            // a context for the subtitle renderers            final Context context = getContext();            // REMOVED: SubtitleController            // REMOVED: mAudioSession            mMediaPlayer.setOnPreparedListener(mPreparedListener);            mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener);            mMediaPlayer.setOnCompletionListener(mCompletionListener);            mMediaPlayer.setOnErrorListener(mErrorListener);            mMediaPlayer.setOnInfoListener(mInfoListener);            mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);            mMediaPlayer.setOnSeekCompleteListener(mSeekCompleteListener);            mMediaPlayer.setOnTimedTextListener(mOnTimedTextListener);            mCurrentBufferPercentage = 0;            String scheme = mUri.getScheme();            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&                    mSettings.getUsingMediaDataSource() &&                    (TextUtils.isEmpty(scheme) || scheme.equalsIgnoreCase("file"))) {                     IMediaDataSource dataSource = new FileMediaDataSource(new File(mUri.toString()));                mMediaPlayer.setDataSource(dataSource);            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {                     mMediaPlayer.setDataSource(mAppContext, mUri, mHeaders);            } else {                     mMediaPlayer.setDataSource(mUri.toString());            }            bindSurfaceHolder(mMediaPlayer, mSurfaceHolder);            mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);            mMediaPlayer.setScreenOnWhilePlaying(true);            mPrepareStartTime = System.currentTimeMillis();            mMediaPlayer.prepareAsync();            if (mHudViewHolder != null)                mHudViewHolder.setMediaPlayer(mMediaPlayer);            // REMOVED: mPendingSubtitleTracks            // we don't set the target state here either, but preserve the            // target state that was there before.            mCurrentState = STATE_PREPARING;            attachMediaController();        } catch (IOException ex) {                 Log.w(TAG, "Unable to open content: " + mUri, ex);            mCurrentState = STATE_ERROR;            mTargetState = STATE_ERROR;            mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);        } catch (IllegalArgumentException ex) {                 Log.w(TAG, "Unable to open content: " + mUri, ex);            mCurrentState = STATE_ERROR;            mTargetState = STATE_ERROR;            mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);        } finally {                 // REMOVED: mPendingSubtitleTracks.clear();        }    }    public void setMediaController(IMediaController controller) {             if (mMediaController != null) {                 mMediaController.hide();        }        mMediaController = controller;        attachMediaController();    }    private void attachMediaController() {             if (mMediaPlayer != null && mMediaController != null) {                 mMediaController.setMediaPlayer(this);            View anchorView = this.getParent() instanceof View ?                    (View) this.getParent() : this;            mMediaController.setAnchorView(anchorView);            mMediaController.setEnabled(isInPlaybackState());        }    }    IMediaPlayer.OnVideoSizeChangedListener mSizeChangedListener =            new IMediaPlayer.OnVideoSizeChangedListener() {                     public void onVideoSizeChanged(IMediaPlayer mp, int width, int height, int sarNum, int sarDen) {                         mVideoWidth = mp.getVideoWidth();                    mVideoHeight = mp.getVideoHeight();                    mVideoSarNum = mp.getVideoSarNum();                    mVideoSarDen = mp.getVideoSarDen();                    if (mVideoWidth != 0 && mVideoHeight != 0) {                             if (mRenderView != null) {                                 mRenderView.setVideoSize(mVideoWidth, mVideoHeight);                            mRenderView.setVideoSampleAspectRatio(mVideoSarNum, mVideoSarDen);                        }                        // REMOVED: getHolder().setFixedSize(mVideoWidth, mVideoHeight);                        requestLayout();                    }                }            };    IMediaPlayer.OnPreparedListener mPreparedListener = new IMediaPlayer.OnPreparedListener() {             public void onPrepared(IMediaPlayer mp) {                 mPrepareEndTime = System.currentTimeMillis();            if (mHudViewHolder != null) {                     mHudViewHolder.updateLoadCost(mPrepareEndTime - mPrepareStartTime);            }            mCurrentState = STATE_PREPARED;            // Get the capabilities of the player for this stream            // REMOVED: Metadata            if (mOnPreparedListener != null) {                     mOnPreparedListener.onPrepared(mMediaPlayer);            }            if (mMediaController != null) {                     mMediaController.setEnabled(true);            }            mVideoWidth = mp.getVideoWidth();            mVideoHeight = mp.getVideoHeight();            int seekToPosition = mSeekWhenPrepared;  // mSeekWhenPrepared may be changed after seekTo() call            if (seekToPosition != 0) {                     seekTo(seekToPosition);            }            if (mVideoWidth != 0 && mVideoHeight != 0) {                     //Log.i("@@@@", "video size: " + mVideoWidth +"/"+ mVideoHeight);                // REMOVED: getHolder().setFixedSize(mVideoWidth, mVideoHeight);                if (mRenderView != null) {                         mRenderView.setVideoSize(mVideoWidth, mVideoHeight);                    mRenderView.setVideoSampleAspectRatio(mVideoSarNum, mVideoSarDen);                    if (!mRenderView.shouldWaitForResize() || mSurfaceWidth == mVideoWidth && mSurfaceHeight == mVideoHeight) {                             // We didn't actually change the size (it was already at the size                        // we need), so we won't get a "surface changed" callback, so                        // start the video here instead of in the callback.                        if (mTargetState == STATE_PLAYING) {                                 start();                            if (mMediaController != null) {                                     mMediaController.show();                            }                        } else if (!isPlaying() &&                                (seekToPosition != 0 || getCurrentPosition() > 0)) {                                 if (mMediaController != null) {                                     // Show the media controls when we're paused into a video and make 'em stick.                                mMediaController.show(0);                            }                        }                    }                }            } else {                     // We don't know the video size yet, but should start anyway.                // The video size might be reported to us later.                if (mTargetState == STATE_PLAYING) {                         start();                }            }        }    };    private IMediaPlayer.OnCompletionListener mCompletionListener =            new IMediaPlayer.OnCompletionListener() {                     public void onCompletion(IMediaPlayer mp) {                         mCurrentState = STATE_PLAYBACK_COMPLETED;                    mTargetState = STATE_PLAYBACK_COMPLETED;                    if (mMediaController != null) {                             mMediaController.hide();                    }                    if (mOnCompletionListener != null) {                             mOnCompletionListener.onCompletion(mMediaPlayer);                    }                }            };    private IMediaPlayer.OnInfoListener mInfoListener =            new IMediaPlayer.OnInfoListener() {                     public boolean onInfo(IMediaPlayer mp, int arg1, int arg2) {                         if (mOnInfoListener != null) {                             mOnInfoListener.onInfo(mp, arg1, arg2);                    }                    switch (arg1) {                             case IMediaPlayer.MEDIA_INFO_VIDEO_TRACK_LAGGING:                            Log.d(TAG, "MEDIA_INFO_VIDEO_TRACK_LAGGING:");                            break;                        case IMediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START:                            Log.d(TAG, "MEDIA_INFO_VIDEO_RENDERING_START:");                            break;                        case IMediaPlayer.MEDIA_INFO_BUFFERING_START:                            Log.d(TAG, "MEDIA_INFO_BUFFERING_START:");                            break;                        case IMediaPlayer.MEDIA_INFO_BUFFERING_END:                            Log.d(TAG, "MEDIA_INFO_BUFFERING_END:");                            break;                        case IMediaPlayer.MEDIA_INFO_NETWORK_BANDWIDTH:                            Log.d(TAG, "MEDIA_INFO_NETWORK_BANDWIDTH: " + arg2);                            break;                        case IMediaPlayer.MEDIA_INFO_BAD_INTERLEAVING:                            Log.d(TAG, "MEDIA_INFO_BAD_INTERLEAVING:");                            break;                        case IMediaPlayer.MEDIA_INFO_NOT_SEEKABLE:                            Log.d(TAG, "MEDIA_INFO_NOT_SEEKABLE:");                            break;                        case IMediaPlayer.MEDIA_INFO_METADATA_UPDATE:                            Log.d(TAG, "MEDIA_INFO_METADATA_UPDATE:");                            break;                        case IMediaPlayer.MEDIA_INFO_UNSUPPORTED_SUBTITLE:                            Log.d(TAG, "MEDIA_INFO_UNSUPPORTED_SUBTITLE:");                            break;                        case IMediaPlayer.MEDIA_INFO_SUBTITLE_TIMED_OUT:                            Log.d(TAG, "MEDIA_INFO_SUBTITLE_TIMED_OUT:");                            break;                        case IMediaPlayer.MEDIA_INFO_VIDEO_ROTATION_CHANGED:                            mVideoRotationDegree = arg2;                            Log.d(TAG, "MEDIA_INFO_VIDEO_ROTATION_CHANGED: " + arg2);                            if (mRenderView != null)                                mRenderView.setVideoRotation(arg2);                            break;                        case IMediaPlayer.MEDIA_INFO_AUDIO_RENDERING_START:                            Log.d(TAG, "MEDIA_INFO_AUDIO_RENDERING_START:");                            break;                    }                    return true;                }            };    private IMediaPlayer.OnErrorListener mErrorListener =            new IMediaPlayer.OnErrorListener() {                     public boolean onError(IMediaPlayer mp, int framework_err, int impl_err) {                         Log.d(TAG, "Error: " + framework_err + "," + impl_err);                    mCurrentState = STATE_ERROR;                    mTargetState = STATE_ERROR;                    if (mMediaController != null) {                             mMediaController.hide();                    }                    /* If an error handler has been supplied, use it and finish. */                    if (mOnErrorListener != null) {                             if (mOnErrorListener.onError(mMediaPlayer, framework_err, impl_err)) {                                 return true;                        }                    }                    /* Otherwise, pop up an error dialog so the user knows that                     * something bad has happened. Only try and pop up the dialog                     * if we're attached to a window. When we're going away and no                     * longer have a window, don't bother showing the user an error.                     */                    if (getWindowToken() != null) {                             Resources r = mAppContext.getResources();                        int messageId;                        if (framework_err == MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK) {                                 messageId = R.string.VideoView_error_text_invalid_progressive_playback;                        } else {                                 messageId = R.string.VideoView_error_text_unknown;                        }                        new AlertDialog.Builder(getContext())                                .setMessage(messageId)                                .setPositiveButton(R.string.VideoView_error_button,                                        new DialogInterface.OnClickListener() {                                                 public void onClick(DialogInterface dialog, int whichButton) {                                                 /* If we get here, there is no onError listener, so                                             * at least inform them that the video is over.                                             */                                                if (mOnCompletionListener != null) {                                                         mOnCompletionListener.onCompletion(mMediaPlayer);                                                }                                            }                                        })                                .setCancelable(false)                                .show();                    }                    return true;                }            };    private IMediaPlayer.OnBufferingUpdateListener mBufferingUpdateListener =            new IMediaPlayer.OnBufferingUpdateListener() {                     public void onBufferingUpdate(IMediaPlayer mp, int percent) {                         mCurrentBufferPercentage = percent;                }            };    private IMediaPlayer.OnSeekCompleteListener mSeekCompleteListener = new IMediaPlayer.OnSeekCompleteListener() {             @Override        public void onSeekComplete(IMediaPlayer mp) {                 mSeekEndTime = System.currentTimeMillis();            mHudViewHolder.updateSeekCost(mSeekEndTime - mSeekStartTime);        }    };    private IMediaPlayer.OnTimedTextListener mOnTimedTextListener = new IMediaPlayer.OnTimedTextListener() {             @Override        public void onTimedText(IMediaPlayer mp, IjkTimedText text) {                 if (text != null) {                     subtitleDisplay.setText(text.getText());            }        }    };    /**     * Register a callback to be invoked when the media file     * is loaded and ready to go.     *     * @param l The callback that will be run     */    public void setOnPreparedListener(IMediaPlayer.OnPreparedListener l) {             mOnPreparedListener = l;    }    /**     * Register a callback to be invoked when the end of a media file     * has been reached during playback.     *     * @param l The callback that will be run     */    public void setOnCompletionListener(IMediaPlayer.OnCompletionListener l) {             mOnCompletionListener = l;    }    /**     * Register a callback to be invoked when an error occurs     * during playback or setup.  If no listener is specified,     * or if the listener returned false, VideoView will inform     * the user of any errors.     *     * @param l The callback that will be run     */    public void setOnErrorListener(IMediaPlayer.OnErrorListener l) {             mOnErrorListener = l;    }    /**     * Register a callback to be invoked when an informational event     * occurs during playback or setup.     *     * @param l The callback that will be run     */    public void setOnInfoListener(IMediaPlayer.OnInfoListener l) {             mOnInfoListener = l;    }    // REMOVED: mSHCallback    private void bindSurfaceHolder(IMediaPlayer mp, IRenderView.ISurfaceHolder holder) {             if (mp == null)            return;        if (holder == null) {                 mp.setDisplay(null);            return;        }        holder.bindToMediaPlayer(mp);    }    IRenderView.IRenderCallback mSHCallback = new IRenderView.IRenderCallback() {             @Override        public void onSurfaceChanged(@NonNull IRenderView.ISurfaceHolder holder, int format, int w, int h) {                 if (holder.getRenderView() != mRenderView) {                     Log.e(TAG, "onSurfaceChanged: unmatched render callback\n");                return;            }            mSurfaceWidth = w;            mSurfaceHeight = h;            boolean isValidState = (mTargetState == STATE_PLAYING);            boolean hasValidSize = !mRenderView.shouldWaitForResize() || (mVideoWidth == w && mVideoHeight == h);            if (mMediaPlayer != null && isValidState && hasValidSize) {                     if (mSeekWhenPrepared != 0) {                         seekTo(mSeekWhenPrepared);                }                start();            }        }        @Override        public void onSurfaceCreated(@NonNull IRenderView.ISurfaceHolder holder, int width, int height) {                 if (holder.getRenderView() != mRenderView) {                     Log.e(TAG, "onSurfaceCreated: unmatched render callback\n");                return;            }            mSurfaceHolder = holder;            if (mMediaPlayer != null)                bindSurfaceHolder(mMediaPlayer, holder);            else                openVideo();        }        @Override        public void onSurfaceDestroyed(@NonNull IRenderView.ISurfaceHolder holder) {                 if (holder.getRenderView() != mRenderView) {                     Log.e(TAG, "onSurfaceDestroyed: unmatched render callback\n");                return;            }            // after we return from this we can't use the surface any more            mSurfaceHolder = null;            // REMOVED: if (mMediaController != null) mMediaController.hide();            // REMOVED: release(true);            releaseWithoutStop();        }    };    public void releaseWithoutStop() {             if (mMediaPlayer != null)            mMediaPlayer.setDisplay(null);    }    /*     * release the media player in any state     */    public void release(boolean cleartargetstate) {             if (mMediaPlayer != null) {                 mMediaPlayer.reset();            mMediaPlayer.release();            mMediaPlayer = null;            // REMOVED: mPendingSubtitleTracks.clear();            mCurrentState = STATE_IDLE;            if (cleartargetstate) {                     mTargetState = STATE_IDLE;            }            AudioManager am = (AudioManager) mAppContext.getSystemService(Context.AUDIO_SERVICE);            am.abandonAudioFocus(null);        }    }    @Override    public boolean onTouchEvent(MotionEvent ev) {             if (isInPlaybackState() && mMediaController != null) {                 toggleMediaControlsVisiblity();        }        return false;    }    @Override    public boolean onTrackballEvent(MotionEvent ev) {             if (isInPlaybackState() && mMediaController != null) {                 toggleMediaControlsVisiblity();        }        return false;    }    @Override    public boolean onKeyDown(int keyCode, KeyEvent event) {             boolean isKeyCodeSupported = keyCode != KeyEvent.KEYCODE_BACK &&                keyCode != KeyEvent.KEYCODE_VOLUME_UP &&                keyCode != KeyEvent.KEYCODE_VOLUME_DOWN &&                keyCode != KeyEvent.KEYCODE_VOLUME_MUTE &&                keyCode != KeyEvent.KEYCODE_MENU &&                keyCode != KeyEvent.KEYCODE_CALL &&                keyCode != KeyEvent.KEYCODE_ENDCALL;        if (isInPlaybackState() && isKeyCodeSupported && mMediaController != null) {                 if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK ||                    keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) {                     if (mMediaPlayer.isPlaying()) {                         pause();                    mMediaController.show();                } else {                         start();                    mMediaController.hide();                }                return true;            } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) {                     if (!mMediaPlayer.isPlaying()) {                         start();                    mMediaController.hide();                }                return true;            } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP                    || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) {                     if (mMediaPlayer.isPlaying()) {                         pause();                    mMediaController.show();                }                return true;            } else {                     toggleMediaControlsVisiblity();            }        }        return super.onKeyDown(keyCode, event);    }    private void toggleMediaControlsVisiblity() {             if (mMediaController.isShowing()) {                 mMediaController.hide();        } else {                 mMediaController.show();        }    }    @Override    public void start() {             if (isInPlaybackState()) {                 mMediaPlayer.start();            mCurrentState = STATE_PLAYING;        }        mTargetState = STATE_PLAYING;    }    @Override    public void pause() {             if (isInPlaybackState()) {                 if (mMediaPlayer.isPlaying()) {                     mMediaPlayer.pause();                mCurrentState = STATE_PAUSED;            }        }        mTargetState = STATE_PAUSED;    }    public void suspend() {             release(false);    }    public void resume() {             openVideo();    }    @Override    public int getDuration() {             if (isInPlaybackState()) {                 return (int) mMediaPlayer.getDuration();        }        return -1;    }    @Override    public int getCurrentPosition() {             if (isInPlaybackState()) {                 return (int) mMediaPlayer.getCurrentPosition();        }        return 0;    }    @Override    public void seekTo(int msec) {             if (isInPlaybackState()) {                 mSeekStartTime = System.currentTimeMillis();            mMediaPlayer.seekTo(msec);            mSeekWhenPrepared = 0;        } else {                 mSeekWhenPrepared = msec;        }    }    @Override    public boolean isPlaying() {             return isInPlaybackState() && mMediaPlayer.isPlaying();    }    @Override    public int getBufferPercentage() {             if (mMediaPlayer != null) {                 return mCurrentBufferPercentage;        }        return 0;    }    private boolean isInPlaybackState() {             return (mMediaPlayer != null &&                mCurrentState != STATE_ERROR &&                mCurrentState != STATE_IDLE &&                mCurrentState != STATE_PREPARING);    }    @Override    public boolean canPause() {             return mCanPause;    }    @Override    public boolean canSeekBackward() {             return mCanSeekBack;    }    @Override    public boolean canSeekForward() {             return mCanSeekForward;    }    @Override    public int getAudioSessionId() {             return 0;    }    // REMOVED: getAudioSessionId();    // REMOVED: onAttachedToWindow();    // REMOVED: onDetachedFromWindow();    // REMOVED: onLayout();    // REMOVED: draw();    // REMOVED: measureAndLayoutSubtitleWidget();    // REMOVED: setSubtitleWidget();    // REMOVED: getSubtitleLooper();    //-------------------------    // Extend: Aspect Ratio    //-------------------------    private static final int[] s_allAspectRatio = {                 IRenderView.AR_ASPECT_FIT_PARENT,            IRenderView.AR_ASPECT_FILL_PARENT,            IRenderView.AR_ASPECT_WRAP_CONTENT,            // IRenderView.AR_MATCH_PARENT,            IRenderView.AR_16_9_FIT_PARENT,            IRenderView.AR_4_3_FIT_PARENT};    private int mCurrentAspectRatioIndex = 0;    private int mCurrentAspectRatio = s_allAspectRatio[0];    public int toggleAspectRatio() {             mCurrentAspectRatioIndex++;        mCurrentAspectRatioIndex %= s_allAspectRatio.length;        mCurrentAspectRatio = s_allAspectRatio[mCurrentAspectRatioIndex];        if (mRenderView != null)            mRenderView.setAspectRatio(mCurrentAspectRatio);        return mCurrentAspectRatio;    }    //-------------------------    // Extend: Render    //-------------------------    public static final int RENDER_NONE = 0;    public static final int RENDER_SURFACE_VIEW = 1;    public static final int RENDER_TEXTURE_VIEW = 2;    private List<Integer> mAllRenders = new ArrayList<Integer>();    private int mCurrentRenderIndex = 0;    private int mCurrentRender = RENDER_NONE;    private void initRenders() {             mAllRenders.clear();        if (mSettings.getEnableSurfaceView())            mAllRenders.add(RENDER_SURFACE_VIEW);        if (mSettings.getEnableTextureView() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)            mAllRenders.add(RENDER_TEXTURE_VIEW);        if (mSettings.getEnableNoView())            mAllRenders.add(RENDER_NONE);        if (mAllRenders.isEmpty())            mAllRenders.add(RENDER_SURFACE_VIEW);        mCurrentRender = mAllRenders.get(mCurrentRenderIndex);        setRender(mCurrentRender);    }    public int toggleRender() {             mCurrentRenderIndex++;        mCurrentRenderIndex %= mAllRenders.size();        mCurrentRender = mAllRenders.get(mCurrentRenderIndex);        setRender(mCurrentRender);        return mCurrentRender;    }    @NonNull    public static String getRenderText(Context context, int render) {             String text;        switch (render) {                 case RENDER_NONE:                text = context.getString(R.string.VideoView_render_none);                break;            case RENDER_SURFACE_VIEW:                text = context.getString(R.string.VideoView_render_surface_view);                break;            case RENDER_TEXTURE_VIEW:                text = context.getString(R.string.VideoView_render_texture_view);                break;            default:                text = context.getString(R.string.N_A);                break;        }        return text;    }    //-------------------------    // Extend: Player    //-------------------------    public int togglePlayer() {             if (mMediaPlayer != null)            mMediaPlayer.release();        if (mRenderView != null)            mRenderView.getView().invalidate();        openVideo();        return mSettings.getPlayer();    }    @NonNull    public static String getPlayerText(Context context, int player) {             String text;        switch (player) {                 case Settings.PV_PLAYER__AndroidMediaPlayer:                text = context.getString(R.string.VideoView_player_AndroidMediaPlayer);                break;            case Settings.PV_PLAYER__IjkMediaPlayer:                text = context.getString(R.string.VideoView_player_IjkMediaPlayer);                break;            case Settings.PV_PLAYER__IjkExoMediaPlayer:                text = context.getString(R.string.VideoView_player_IjkExoMediaPlayer);                break;            default:                text = context.getString(R.string.N_A);                break;        }        return text;    }    public IMediaPlayer createPlayer(int playerType) {             IMediaPlayer mediaPlayer = null;        switch (playerType) {                 case Settings.PV_PLAYER__IjkExoMediaPlayer: {                     IjkExoMediaPlayer IjkExoMediaPlayer = new IjkExoMediaPlayer(mAppContext);                mediaPlayer = IjkExoMediaPlayer;            }            break;            case Settings.PV_PLAYER__AndroidMediaPlayer: {                     AndroidMediaPlayer androidMediaPlayer = new AndroidMediaPlayer();                mediaPlayer = androidMediaPlayer;            }            break;            case Settings.PV_PLAYER__IjkMediaPlayer:            default: {                     IjkMediaPlayer ijkMediaPlayer = null;                if (mUri != null) {                         ijkMediaPlayer = new IjkMediaPlayer();                    ijkMediaPlayer.native_setLogLevel(IjkMediaPlayer.IJK_LOG_DEBUG);                    if (mSettings.getUsingMediaCodec()) {                             ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 1);                        if (mSettings.getUsingMediaCodecAutoRotate()) {                                 ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 1);                        } else {                                 ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 0);                        }                        if (mSettings.getMediaCodecHandleResolutionChange()) {                                 ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-handle-resolution-change", 1);                        } else {                                 ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-handle-resolution-change", 0);                        }                    } else {                             ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 0);                    }                    if (mSettings.getUsingOpenSLES()) {                             ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "opensles", 1);                    } else {                             ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "opensles", 0);                    }                    String pixelFormat = mSettings.getPixelFormat();                    if (TextUtils.isEmpty(pixelFormat)) {                             ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "overlay-format", IjkMediaPlayer.SDL_FCC_RV32);                    } else {                             ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "overlay-format", pixelFormat);                    }                    ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", 1);                    ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 0);                    ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "http-detect-range-support", 0);                    ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 48);                }                mediaPlayer = ijkMediaPlayer;            }            break;        }        if (mSettings.getEnableDetachedSurfaceTextureView()) {                 mediaPlayer = new TextureMediaPlayer(mediaPlayer);        }        return mediaPlayer;    }    //-------------------------    // Extend: Background    //-------------------------    private boolean mEnableBackgroundPlay = false;    private void initBackground() {             mEnableBackgroundPlay = mSettings.getEnableBackgroundPlay();        if (mEnableBackgroundPlay) {                 MediaPlayerService.intentToStart(getContext());            mMediaPlayer = MediaPlayerService.getMediaPlayer();            if (mHudViewHolder != null)                mHudViewHolder.setMediaPlayer(mMediaPlayer);        }    }    public boolean isBackgroundPlayEnabled() {             return mEnableBackgroundPlay;    }    public void enterBackground() {             MediaPlayerService.setMediaPlayer(mMediaPlayer);    }    public void stopBackgroundPlay() {             MediaPlayerService.setMediaPlayer(null);    }    //-------------------------    // Extend: Background    //-------------------------    public void showMediaInfo() {             if (mMediaPlayer == null)            return;        int selectedVideoTrack = MediaPlayerCompat.getSelectedTrack(mMediaPlayer, ITrackInfo.MEDIA_TRACK_TYPE_VIDEO);        int selectedAudioTrack = MediaPlayerCompat.getSelectedTrack(mMediaPlayer, ITrackInfo.MEDIA_TRACK_TYPE_AUDIO);        int selectedSubtitleTrack = MediaPlayerCompat.getSelectedTrack(mMediaPlayer, ITrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT);        TableLayoutBinder builder = new TableLayoutBinder(getContext());        builder.appendSection(R.string.mi_player);        builder.appendRow2(R.string.mi_player, MediaPlayerCompat.getName(mMediaPlayer));        builder.appendSection(R.string.mi_media);        builder.appendRow2(R.string.mi_resolution, buildResolution(mVideoWidth, mVideoHeight, mVideoSarNum, mVideoSarDen));        builder.appendRow2(R.string.mi_length, buildTimeMilli(mMediaPlayer.getDuration()));        ITrackInfo trackInfos[] = mMediaPlayer.getTrackInfo();        if (trackInfos != null) {                 int index = -1;            for (ITrackInfo trackInfo : trackInfos) {                     index++;                int trackType = trackInfo.getTrackType();                if (index == selectedVideoTrack) {                         builder.appendSection(getContext().getString(R.string.mi_stream_fmt1, index) + " " + getContext().getString(R.string.mi__selected_video_track));                } else if (index == selectedAudioTrack) {                         builder.appendSection(getContext().getString(R.string.mi_stream_fmt1, index) + " " + getContext().getString(R.string.mi__selected_audio_track));                } else if (index == selectedSubtitleTrack) {                         builder.appendSection(getContext().getString(R.string.mi_stream_fmt1, index) + " " + getContext().getString(R.string.mi__selected_subtitle_track));                } else {                         builder.appendSection(getContext().getString(R.string.mi_stream_fmt1, index));                }                builder.appendRow2(R.string.mi_type, buildTrackType(trackType));                builder.appendRow2(R.string.mi_language, buildLanguage(trackInfo.getLanguage()));                IMediaFormat mediaFormat = trackInfo.getFormat();                if (mediaFormat == null) {                     } else if (mediaFormat instanceof IjkMediaFormat) {                         switch (trackType) {                             case ITrackInfo.MEDIA_TRACK_TYPE_VIDEO:                            builder.appendRow2(R.string.mi_codec, mediaFormat.getString(IjkMediaFormat.KEY_IJK_CODEC_LONG_NAME_UI));                            builder.appendRow2(R.string.mi_profile_level, mediaFormat.getString(IjkMediaFormat.KEY_IJK_CODEC_PROFILE_LEVEL_UI));                            builder.appendRow2(R.string.mi_pixel_format, mediaFormat.getString(IjkMediaFormat.KEY_IJK_CODEC_PIXEL_FORMAT_UI));                            builder.appendRow2(R.string.mi_resolution, mediaFormat.getString(IjkMediaFormat.KEY_IJK_RESOLUTION_UI));                            builder.appendRow2(R.string.mi_frame_rate, mediaFormat.getString(IjkMediaFormat.KEY_IJK_FRAME_RATE_UI));                            builder.appendRow2(R.string.mi_bit_rate, mediaFormat.getString(IjkMediaFormat.KEY_IJK_BIT_RATE_UI));                            break;                        case ITrackInfo.MEDIA_TRACK_TYPE_AUDIO:                            builder.appendRow2(R.string.mi_codec, mediaFormat.getString(IjkMediaFormat.KEY_IJK_CODEC_LONG_NAME_UI));                            builder.appendRow2(R.string.mi_profile_level, mediaFormat.getString(IjkMediaFormat.KEY_IJK_CODEC_PROFILE_LEVEL_UI));                            builder.appendRow2(R.string.mi_sample_rate, mediaFormat.getString(IjkMediaFormat.KEY_IJK_SAMPLE_RATE_UI));                            builder.appendRow2(R.string.mi_channels, mediaFormat.getString(IjkMediaFormat.KEY_IJK_CHANNEL_UI));                            builder.appendRow2(R.string.mi_bit_rate, mediaFormat.getString(IjkMediaFormat.KEY_IJK_BIT_RATE_UI));                            break;                        default:                            break;                    }                }            }        }        AlertDialog.Builder adBuilder = builder.buildAlertDialogBuilder();        adBuilder.setTitle(R.string.media_information);        adBuilder.setNegativeButton(R.string.close, null);        adBuilder.show();    }    private String buildResolution(int width, int height, int sarNum, int sarDen) {             StringBuilder sb = new StringBuilder();        sb.append(width);        sb.append(" x ");        sb.append(height);        if (sarNum > 1 || sarDen > 1) {                 sb.append("[");            sb.append(sarNum);            sb.append(":");            sb.append(sarDen);            sb.append("]");        }        return sb.toString();    }    private String buildTimeMilli(long duration) {             long total_seconds = duration / 1000;        long hours = total_seconds / 3600;        long minutes = (total_seconds % 3600) / 60;        long seconds = total_seconds % 60;        if (duration <= 0) {                 return "--:--";        }        if (hours >= 100) {                 return String.format(Locale.US, "%d:%02d:%02d", hours, minutes, seconds);        } else if (hours > 0) {                 return String.format(Locale.US, "%02d:%02d:%02d", hours, minutes, seconds);        } else {                 return String.format(Locale.US, "%02d:%02d", minutes, seconds);        }    }    private String buildTrackType(int type) {             Context context = getContext();        switch (type) {                 case ITrackInfo.MEDIA_TRACK_TYPE_VIDEO:                return context.getString(R.string.TrackType_video);            case ITrackInfo.MEDIA_TRACK_TYPE_AUDIO:                return context.getString(R.string.TrackType_audio);            case ITrackInfo.MEDIA_TRACK_TYPE_SUBTITLE:                return context.getString(R.string.TrackType_subtitle);            case ITrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT:                return context.getString(R.string.TrackType_timedtext);            case ITrackInfo.MEDIA_TRACK_TYPE_METADATA:                return context.getString(R.string.TrackType_metadata);            case ITrackInfo.MEDIA_TRACK_TYPE_UNKNOWN:            default:                return context.getString(R.string.TrackType_unknown);        }    }    private String buildLanguage(String language) {             if (TextUtils.isEmpty(language))            return "und";        return language;    }    public ITrackInfo[] getTrackInfo() {             if (mMediaPlayer == null)            return null;        return mMediaPlayer.getTrackInfo();    }    public void selectTrack(int stream) {             MediaPlayerCompat.selectTrack(mMediaPlayer, stream);    }    public void deselectTrack(int stream) {             MediaPlayerCompat.deselectTrack(mMediaPlayer, stream);    }    public int getSelectedTrack(int trackType) {             return MediaPlayerCompat.getSelectedTrack(mMediaPlayer, trackType);    }}

*IMediaController.java

/* * Copyright (C) 2015 Bilibili * Copyright (C) 2015 Zhang Rui  * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *      http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package com.example.k.bilibiliapi.video;import android.view.View;import android.widget.MediaController;public interface IMediaController {         void hide();    boolean isShowing();    void setAnchorView(View view);    void setEnabled(boolean enabled);    void setMediaPlayer(MediaController.MediaPlayerControl player);    void show(int timeout);    void show();    //----------    // Extends    //----------    void showOnce(View view);}

*InfoHudViewHolder

package com.example.k.bilibiliapi.video;import android.content.Context;import android.os.Handler;import android.os.Message;import android.util.SparseArray;import android.view.View;import android.widget.TableLayout;import com.example.k.bilibiliapi.R;import java.util.Locale;import tv.danmaku.ijk.media.player.IMediaPlayer;import tv.danmaku.ijk.media.player.IjkMediaPlayer;import tv.danmaku.ijk.media.player.MediaPlayerProxy;public class InfoHudViewHolder {         private TableLayoutBinder mTableLayoutBinder;    private SparseArray<View> mRowMap = new SparseArray<View>();    private IMediaPlayer mMediaPlayer;    private long mLoadCost = 0;    private long mSeekCost = 0;    public InfoHudViewHolder(Context context, TableLayout tableLayout) {             mTableLayoutBinder = new TableLayoutBinder(context, tableLayout);    }    private void appendSection(int nameId) {             mTableLayoutBinder.appendSection(nameId);    }    private void appendRow(int nameId) {             View rowView = mTableLayoutBinder.appendRow2(nameId, null);        mRowMap.put(nameId, rowView);    }    private void setRowValue(int id, String value) {             View rowView = mRowMap.get(id);        if (rowView == null) {                 rowView = mTableLayoutBinder.appendRow2(id, value);            mRowMap.put(id, rowView);        } else {                 mTableLayoutBinder.setValueText(rowView, value);        }    }    public void setMediaPlayer(IMediaPlayer mp) {             mMediaPlayer = mp;        if (mMediaPlayer != null) {                 mHandler.sendEmptyMessageDelayed(MSG_UPDATE_HUD, 500);        } else {                 mHandler.removeMessages(MSG_UPDATE_HUD);        }    }    private static String formatedDurationMilli(long duration) {             if (duration >=  1000) {                 return String.format(Locale.US, "%.2f sec", ((float)duration) / 1000);        } else {                 return String.format(Locale.US, "%d msec", duration);        }    }    private static String formatedSpeed(long bytes,long elapsed_milli) {             if (elapsed_milli <= 0) {                 return "0 B/s";        }        if (bytes <= 0) {                 return "0 B/s";        }        float bytes_per_sec = ((float)bytes) * 1000.f /  elapsed_milli;        if (bytes_per_sec >= 1000 * 1000) {                 return String.format(Locale.US, "%.2f MB/s", ((float)bytes_per_sec) / 1000 / 1000);        } else if (bytes_per_sec >= 1000) {                 return String.format(Locale.US, "%.1f KB/s", ((float)bytes_per_sec) / 1000);        } else {                 return String.format(Locale.US, "%d B/s", (long)bytes_per_sec);        }    }    public void updateLoadCost(long time)  {             mLoadCost = time;    }    public void updateSeekCost(long time)  {             mSeekCost = time;    }    private static String formatedSize(long bytes) {             if (bytes >= 100 * 1000) {                 return String.format(Locale.US, "%.2f MB", ((float)bytes) / 1000 / 1000);        } else if (bytes >= 100) {                 return String.format(Locale.US, "%.1f KB", ((float)bytes) / 1000);        } else {                 return String.format(Locale.US, "%d B", bytes);        }    }    private static final int MSG_UPDATE_HUD = 1;    private Handler mHandler = new Handler() {             @Override        public void handleMessage(Message msg) {                 switch (msg.what) {                     case MSG_UPDATE_HUD: {                         InfoHudViewHolder holder = InfoHudViewHolder.this;                    IjkMediaPlayer mp = null;                    if (mMediaPlayer == null)                        break;                    if (mMediaPlayer instanceof IjkMediaPlayer) {                             mp = (IjkMediaPlayer) mMediaPlayer;                    } else if (mMediaPlayer instanceof MediaPlayerProxy) {                             MediaPlayerProxy proxy = (MediaPlayerProxy) mMediaPlayer;                        IMediaPlayer internal = proxy.getInternalMediaPlayer();                        if (internal != null && internal instanceof IjkMediaPlayer)                            mp = (IjkMediaPlayer) internal;                    }                    if (mp == null)                        break;                    int vdec = mp.getVideoDecoder();                    switch (vdec) {                             case IjkMediaPlayer.FFP_PROPV_DECODER_AVCODEC:                            setRowValue(R.string.vdec, "avcodec");                            break;                        case IjkMediaPlayer.FFP_PROPV_DECODER_MEDIACODEC:                            setRowValue(R.string.vdec, "MediaCodec");                            break;                        default:                            setRowValue(R.string.vdec, "");                            break;                    }                    float fpsOutput = mp.getVideoOutputFramesPerSecond();                    float fpsDecode = mp.getVideoDecodeFramesPerSecond();                    setRowValue(R.string.fps, String.format(Locale.US, "%.2f / %.2f", fpsDecode, fpsOutput));                    long videoCachedDuration = mp.getVideoCachedDuration();                    long audioCachedDuration = mp.getAudioCachedDuration();                    long videoCachedBytes    = mp.getVideoCachedBytes();                    long audioCachedBytes    = mp.getAudioCachedBytes();                    long tcpSpeed            = mp.getTcpSpeed();                    long bitRate             = mp.getBitRate();                    long seekLoadDuration    = mp.getSeekLoadDuration();                    setRowValue(R.string.v_cache, String.format(Locale.US, "%s, %s", formatedDurationMilli(videoCachedDuration), formatedSize(videoCachedBytes)));                    setRowValue(R.string.a_cache, String.format(Locale.US, "%s, %s", formatedDurationMilli(audioCachedDuration), formatedSize(audioCachedBytes)));                    setRowValue(R.string.load_cost, String.format(Locale.US, "%d ms", mLoadCost));                    setRowValue(R.string.seek_cost, String.format(Locale.US, "%d ms", mSeekCost));                    setRowValue(R.string.seek_load_cost, String.format(Locale.US, "%d ms", seekLoadDuration));                    setRowValue(R.string.tcp_speed, String.format(Locale.US, "%s", formatedSpeed(tcpSpeed, 1000)));                    setRowValue(R.string.bit_rate, String.format(Locale.US, "%.2f kbs", bitRate/1000f));                    mHandler.removeMessages(MSG_UPDATE_HUD);                    mHandler.sendEmptyMessageDelayed(MSG_UPDATE_HUD, 500);                }            }        }    };}

*IRenderView.java

/* * Copyright (C) 2015 Bilibili * Copyright (C) 2015 Zhang Rui  * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *      http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package com.example.k.bilibiliapi.video;import android.graphics.SurfaceTexture;import android.support.annotation.NonNull;import android.support.annotation.Nullable;import android.view.Surface;import android.view.SurfaceHolder;import android.view.View;import tv.danmaku.ijk.media.player.IMediaPlayer;public interface IRenderView {         int AR_ASPECT_FIT_PARENT = 0; // without clip    int AR_ASPECT_FILL_PARENT = 1; // may clip    int AR_ASPECT_WRAP_CONTENT = 2;    int AR_MATCH_PARENT = 3;    int AR_16_9_FIT_PARENT = 4;    int AR_4_3_FIT_PARENT = 5;    View getView();    boolean shouldWaitForResize();    void setVideoSize(int videoWidth, int videoHeight);    void setVideoSampleAspectRatio(int videoSarNum, int videoSarDen);    void setVideoRotation(int degree);    void setAspectRatio(int aspectRatio);    void addRenderCallback(@NonNull IRenderCallback callback);    void removeRenderCallback(@NonNull IRenderCallback callback);    interface ISurfaceHolder {             void bindToMediaPlayer(IMediaPlayer mp);        @NonNull        IRenderView getRenderView();        @Nullable        SurfaceHolder getSurfaceHolder();        @Nullable        Surface openSurface();        @Nullable        SurfaceTexture getSurfaceTexture();    }    interface IRenderCallback {             /**         * @param holder         * @param width  could be 0         * @param height could be 0         */        void onSurfaceCreated(@NonNull ISurfaceHolder holder, int width, int height);        /**         * @param holder         * @param format could be 0         * @param width         * @param height         */        void onSurfaceChanged(@NonNull ISurfaceHolder holder, int format, int width, int height);        void onSurfaceDestroyed(@NonNull ISurfaceHolder holder);    }}

*MeasureHelper.java

/* * Copyright (C) 2015 Bilibili * Copyright (C) 2015 Zhang Rui  * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *      http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package com.example.k.bilibiliapi.video;import android.content.Context;import android.support.annotation.NonNull;import android.view.View;import com.example.k.bilibiliapi.R;import java.lang.ref.WeakReference;public final class MeasureHelper {         private WeakReference<View> mWeakView;    private int mVideoWidth;    private int mVideoHeight;    private int mVideoSarNum;    private int mVideoSarDen;    private int mVideoRotationDegree;    private int mMeasuredWidth;    private int mMeasuredHeight;    private int mCurrentAspectRatio = IRenderView.AR_ASPECT_FIT_PARENT;    public MeasureHelper(View view) {             mWeakView = new WeakReference<View>(view);    }    public View getView() {             if (mWeakView == null)            return null;        return mWeakView.get();    }    public void setVideoSize(int videoWidth, int videoHeight) {             mVideoWidth = videoWidth;        mVideoHeight = videoHeight;    }    public void setVideoSampleAspectRatio(int videoSarNum, int videoSarDen) {             mVideoSarNum = videoSarNum;        mVideoSarDen = videoSarDen;    }    public void setVideoRotation(int videoRotationDegree) {             mVideoRotationDegree = videoRotationDegree;    }    /**     * Must be called by View.onMeasure(int, int)     *     * @param widthMeasureSpec     * @param heightMeasureSpec     */    public void doMeasure(int widthMeasureSpec, int heightMeasureSpec) {             //Log.i("@@@@", "onMeasure(" + MeasureSpec.toString(widthMeasureSpec) + ", "        //        + MeasureSpec.toString(heightMeasureSpec) + ")");        if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270) {                 int tempSpec = widthMeasureSpec;            widthMeasureSpec  = heightMeasureSpec;            heightMeasureSpec = tempSpec;        }        int width = View.getDefaultSize(mVideoWidth, widthMeasureSpec);        int height = View.getDefaultSize(mVideoHeight, heightMeasureSpec);        if (mCurrentAspectRatio == IRenderView.AR_MATCH_PARENT) {                 width = widthMeasureSpec;            height = heightMeasureSpec;        } else if (mVideoWidth > 0 && mVideoHeight > 0) {                 int widthSpecMode = View.MeasureSpec.getMode(widthMeasureSpec);            int widthSpecSize = View.MeasureSpec.getSize(widthMeasureSpec);            int heightSpecMode = View.MeasureSpec.getMode(heightMeasureSpec);            int heightSpecSize = View.MeasureSpec.getSize(heightMeasureSpec);            if (widthSpecMode == View.MeasureSpec.AT_MOST && heightSpecMode == View.MeasureSpec.AT_MOST) {                     float specAspectRatio = (float) widthSpecSize / (float) heightSpecSize;                float displayAspectRatio;                switch (mCurrentAspectRatio) {                         case IRenderView.AR_16_9_FIT_PARENT:                        displayAspectRatio = 16.0f / 9.0f;                        if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270)                            displayAspectRatio = 1.0f / displayAspectRatio;                        break;                    case IRenderView.AR_4_3_FIT_PARENT:                        displayAspectRatio = 4.0f / 3.0f;                        if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270)                            displayAspectRatio = 1.0f / displayAspectRatio;                        break;                    case IRenderView.AR_ASPECT_FIT_PARENT:                    case IRenderView.AR_ASPECT_FILL_PARENT:                    case IRenderView.AR_ASPECT_WRAP_CONTENT:                    default:                        displayAspectRatio = (float) mVideoWidth / (float) mVideoHeight;                        if (mVideoSarNum > 0 && mVideoSarDen > 0)                            displayAspectRatio = displayAspectRatio * mVideoSarNum / mVideoSarDen;                        break;                }                boolean shouldBeWider = displayAspectRatio > specAspectRatio;                switch (mCurrentAspectRatio) {                         case IRenderView.AR_ASPECT_FIT_PARENT:                    case IRenderView.AR_16_9_FIT_PARENT:                    case IRenderView.AR_4_3_FIT_PARENT:                        if (shouldBeWider) {                                 // too wide, fix width                            width = widthSpecSize;                            height = (int) (width / displayAspectRatio);                        } else {                                 // too high, fix height                            height = heightSpecSize;                            width = (int) (height * displayAspectRatio);                        }                        break;                    case IRenderView.AR_ASPECT_FILL_PARENT:                        if (shouldBeWider) {                                 // not high enough, fix height                            height = heightSpecSize;                            width = (int) (height * displayAspectRatio);                        } else {                                 // not wide enough, fix width                            width = widthSpecSize;                            height = (int) (width / displayAspectRatio);                        }                        break;                    case IRenderView.AR_ASPECT_WRAP_CONTENT:                    default:                        if (shouldBeWider) {                                 // too wide, fix width                            width = Math.min(mVideoWidth, widthSpecSize);                            height = (int) (width / displayAspectRatio);                        } else {                                 // too high, fix height                            height = Math.min(mVideoHeight, heightSpecSize);                            width = (int) (height * displayAspectRatio);                        }                        break;                }            } else if (widthSpecMode == View.MeasureSpec.EXACTLY && heightSpecMode == View.MeasureSpec.EXACTLY) {                     // the size is fixed                width = widthSpecSize;                height = heightSpecSize;                // for compatibility, we adjust size based on aspect ratio                if (mVideoWidth * height < width * mVideoHeight) {                         //Log.i("@@@", "image too wide, correcting");                    width = height * mVideoWidth / mVideoHeight;                } else if (mVideoWidth * height > width * mVideoHeight) {                         //Log.i("@@@", "image too tall, correcting");                    height = width * mVideoHeight / mVideoWidth;                }            } else if (widthSpecMode == View.MeasureSpec.EXACTLY) {                     // only the width is fixed, adjust the height to match aspect ratio if possible                width = widthSpecSize;                height = width * mVideoHeight / mVideoWidth;                if (heightSpecMode == View.MeasureSpec.AT_MOST && height > heightSpecSize) {                         // couldn't match aspect ratio within the constraints                    height = heightSpecSize;                }            } else if (heightSpecMode == View.MeasureSpec.EXACTLY) {                     // only the height is fixed, adjust the width to match aspect ratio if possible                height = heightSpecSize;                width = height * mVideoWidth / mVideoHeight;                if (widthSpecMode == View.MeasureSpec.AT_MOST && width > widthSpecSize) {                         // couldn't match aspect ratio within the constraints                    width = widthSpecSize;                }            } else {                     // neither the width nor the height are fixed, try to use actual video size                width = mVideoWidth;                height = mVideoHeight;                if (heightSpecMode == View.MeasureSpec.AT_MOST && height > heightSpecSize) {                         // too tall, decrease both width and height                    height = heightSpecSize;                    width = height * mVideoWidth / mVideoHeight;                }                if (widthSpecMode == View.MeasureSpec.AT_MOST && width > widthSpecSize) {                         // too wide, decrease both width and height                    width = widthSpecSize;                    height = width * mVideoHeight / mVideoWidth;                }            }        } else {                 // no size yet, just adopt the given spec sizes        }        mMeasuredWidth = width;        mMeasuredHeight = height;    }    public int getMeasuredWidth() {             return mMeasuredWidth;    }    public int getMeasuredHeight() {             return mMeasuredHeight;    }    public void setAspectRatio(int aspectRatio) {             mCurrentAspectRatio = aspectRatio;    }    @NonNull    public static String getAspectRatioText(Context context, int aspectRatio) {             String text;        switch (aspectRatio) {                 case IRenderView.AR_ASPECT_FIT_PARENT:                text = context.getString(R.string.VideoView_ar_aspect_fit_parent);                break;            case IRenderView.AR_ASPECT_FILL_PARENT:                text = context.getString(R.string.VideoView_ar_aspect_fill_parent);                break;            case IRenderView.AR_ASPECT_WRAP_CONTENT:                text = context.getString(R.string.VideoView_ar_aspect_wrap_content);                break;            case IRenderView.AR_MATCH_PARENT:                text = context.getString(R.string.VideoView_ar_match_parent);                break;            case IRenderView.AR_16_9_FIT_PARENT:                text = context.getString(R.string.VideoView_ar_16_9_fit_parent);                break;            case IRenderView.AR_4_3_FIT_PARENT:                text = context.getString(R.string.VideoView_ar_4_3_fit_parent);                break;            default:                text = context.getString(R.string.N_A);                break;        }        return text;    }}

*MediaPlayerCompat.java

/* * Copyright (C) 2015 Bilibili * Copyright (C) 2015 Zhang Rui  * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *      http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package com.example.k.bilibiliapi.video;import tv.danmaku.ijk.media.player.IMediaPlayer;import tv.danmaku.ijk.media.player.IjkMediaPlayer;import tv.danmaku.ijk.media.player.MediaPlayerProxy;import tv.danmaku.ijk.media.player.TextureMediaPlayer;public class MediaPlayerCompat {         public static String getName(IMediaPlayer mp) {             if (mp == null) {                 return "null";        } else if (mp instanceof TextureMediaPlayer) {                 StringBuilder sb = new StringBuilder("TextureMediaPlayer <");            IMediaPlayer internalMediaPlayer = ((TextureMediaPlayer) mp).getInternalMediaPlayer();            if (internalMediaPlayer == null) {                     sb.append("null>");            } else {                     sb.append(internalMediaPlayer.getClass().getSimpleName());                sb.append(">");            }            return sb.toString();        } else {                 return mp.getClass().getSimpleName();        }    }    public static IjkMediaPlayer getIjkMediaPlayer(IMediaPlayer mp) {             IjkMediaPlayer ijkMediaPlayer = null;        if (mp == null) {                 return null;        } if (mp instanceof IjkMediaPlayer) {                 ijkMediaPlayer = (IjkMediaPlayer) mp;        } else if (mp instanceof MediaPlayerProxy && ((MediaPlayerProxy) mp).getInternalMediaPlayer() instanceof IjkMediaPlayer) {                 ijkMediaPlayer = (IjkMediaPlayer) ((MediaPlayerProxy) mp).getInternalMediaPlayer();        }        return ijkMediaPlayer;    }    public static void selectTrack(IMediaPlayer mp, int stream) {             IjkMediaPlayer ijkMediaPlayer = getIjkMediaPlayer(mp);        if (ijkMediaPlayer == null)            return;        ijkMediaPlayer.selectTrack(stream);    }    public static void deselectTrack(IMediaPlayer mp, int stream) {             IjkMediaPlayer ijkMediaPlayer = getIjkMediaPlayer(mp);        if (ijkMediaPlayer == null)            return;        ijkMediaPlayer.deselectTrack(stream);    }    public static int getSelectedTrack(IMediaPlayer mp, int trackType) {             IjkMediaPlayer ijkMediaPlayer = getIjkMediaPlayer(mp);        if (ijkMediaPlayer == null)            return -1;        return ijkMediaPlayer.getSelectedTrack(trackType);    }}

*MediaPlayerService.java

/* * Copyright (C) 2015 Bilibili * Copyright (C) 2015 Zhang Rui  * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *      http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package com.example.k.bilibiliapi.video;import android.app.Service;import android.content.Context;import android.content.Intent;import android.os.IBinder;import android.support.annotation.Nullable;import tv.danmaku.ijk.media.player.IMediaPlayer;public class MediaPlayerService extends Service {         private static IMediaPlayer sMediaPlayer;    public static Intent newIntent(Context context) {             Intent intent = new Intent(context, MediaPlayerService.class);        return intent;    }    public static void intentToStart(Context context) {             context.startService(newIntent(context));    }    public static void intentToStop(Context context) {             context.stopService(newIntent(context));    }    @Nullable    @Override    public IBinder onBind(Intent intent) {             return null;    }    public static void setMediaPlayer(IMediaPlayer mp) {             if (sMediaPlayer != null && sMediaPlayer != mp) {                 if (sMediaPlayer.isPlaying())                sMediaPlayer.stop();            sMediaPlayer.release();            sMediaPlayer = null;        }        sMediaPlayer = mp;    }    public static IMediaPlayer getMediaPlayer() {             return sMediaPlayer;    }}

PlayerManager.java

package com.example.k.bilibiliapi.video;import android.app.Activity;import android.content.Context;import android.content.pm.ActivityInfo;import android.content.res.Resources;import android.media.AudioManager;import android.support.v7.app.ActionBar;import android.support.v7.app.AppCompatActivity;import android.util.DisplayMetrics;import android.util.Log;import android.util.TypedValue;import android.view.GestureDetector;import android.view.MotionEvent;import android.view.OrientationEventListener;import android.view.Surface;import android.view.View;import android.view.ViewGroup;import android.view.WindowManager;import android.widget.ImageView;import android.widget.TextView;import com.example.k.bilibiliapi.R;import tv.danmaku.ijk.media.player.IMediaPlayer;import tv.danmaku.ijk.media.player.IjkMediaPlayer;import tv.danmaku.ijk.media.player.pragma.DebugLog;/** * Created by yilv on 2018/3/29. */public class PlayerManager {         /**     * 可能会剪裁,保持原视频的大小,显示在中心,当原视频的大小超过view的大小超过部分裁剪处理     */    public static final String SCALETYPE_FITPARENT="fitParent";    /**     * 可能会剪裁,等比例放大视频,直到填满View为止,超过View的部分作裁剪处理     */    public static final String SCALETYPE_FILLPARENT="fillParent";    /**     * 将视频的内容完整居中显示,如果视频大于view,则按比例缩视频直到完全显示在view中     */    public static final String SCALETYPE_WRAPCONTENT="wrapContent";    /**     * 不剪裁,非等比例拉伸画面填满整个View     */    public static final String SCALETYPE_FITXY="fitXY";    /**     * 不剪裁,非等比例拉伸画面到16:9,并完全显示在View中     */    public static final String SCALETYPE_16_9="16:9";    /**     * 不剪裁,非等比例拉伸画面到4:3,并完全显示在View中     */    public static final String SCALETYPE_4_3="4:3";    /**     * 状态常量     */    private final int STATUS_ERROR=-1;    private final int STATUS_IDLE=0;    private final int STATUS_LOADING=1;    private final int STATUS_PLAYING=2;    private final int STATUS_PAUSE=3;    private final int STATUS_COMPLETED=4;    private final Activity activity;    private final IjkVideoView videoView;    private final AudioManager audioManager;    public GestureDetector gestureDetector;    private boolean playerSupport;    private boolean isLive = false;//是否为直播    private boolean fullScreenOnly;    private boolean portrait;    private final int mMaxVolume;    private int screenWidthPixels;    private int currentPosition;    private int status=STATUS_IDLE;    private long pauseTime;    private String url;    private float brightness=-1;    private int volume=-1;    private long newPosition = -1;    private long defaultRetryTime=5000;    private OrientationEventListener orientationEventListener;    private PlayerStateListener playerStateListener;    public void setPlayerStateListener(PlayerStateListener playerStateListener) {             this.playerStateListener = playerStateListener;    }    private OnErrorListener onErrorListener=new OnErrorListener() {             @Override        public void onError(int what, int extra) {             }    };    private OnCompleteListener onCompleteListener=new OnCompleteListener() {             @Override        public void onComplete() {             }    };    private OnInfoListener onInfoListener=new OnInfoListener(){             @Override        public void onInfo(int what, int extra) {             }    };    private OnControlPanelVisibilityChangeListener onControlPanelVisibilityChangeListener=new OnControlPanelVisibilityChangeListener() {             @Override        public void change(boolean isShowing) {             }    };    /**     * try to play when error(only for live video)     * @param defaultRetryTime millisecond,0 will stop retry,default is 5000 millisecond     */    public void setDefaultRetryTime(long defaultRetryTime) {             this.defaultRetryTime = defaultRetryTime;    }    public PlayerManager(final Activity activity) {             try {                 IjkMediaPlayer.loadLibrariesOnce(null);            IjkMediaPlayer.native_profileBegin("libijkplayer.so");            playerSupport=true;        } catch (Throwable e) {                 Log.e("GiraffePlayer", "loadLibraries error", e);        }        this.activity=activity;        screenWidthPixels = activity.getResources().getDisplayMetrics().widthPixels;        videoView = (IjkVideoView) activity.findViewById(R.id.video_view);        videoView.setOnCompletionListener(new IMediaPlayer.OnCompletionListener() {                 @Override            public void onCompletion(IMediaPlayer mp) {                     statusChange(STATUS_COMPLETED);                onCompleteListener.onComplete();            }        });        videoView.setOnErrorListener(new IMediaPlayer.OnErrorListener() {                 @Override            public boolean onError(IMediaPlayer mp, int what, int extra) {                     statusChange(STATUS_ERROR);                onErrorListener.onError(what,extra);                return true;            }        });        videoView.setOnInfoListener(new IMediaPlayer.OnInfoListener() {                 @Override            public boolean onInfo(IMediaPlayer mp, int what, int extra) {                     switch (what) {                         case IMediaPlayer.MEDIA_INFO_BUFFERING_START:                        statusChange(STATUS_LOADING);                        break;                    case IMediaPlayer.MEDIA_INFO_BUFFERING_END:                        statusChange(STATUS_PLAYING);                        break;                    case IMediaPlayer.MEDIA_INFO_NETWORK_BANDWIDTH:                        //显示下载速度//                      Toast.show("download rate:" + extra);                        break;                    case IMediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START:                        statusChange(STATUS_PLAYING);                        break;                }                onInfoListener.onInfo(what,extra);                return false;            }        });        audioManager = (AudioManager) activity.getSystemService(Context.AUDIO_SERVICE);        mMaxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);        gestureDetector = new GestureDetector(activity, new PlayerGestureListener());        if (fullScreenOnly) {                 activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);        }        portrait=getScreenOrientation()== ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;        if (!playerSupport) {                 DebugLog.e("","播放器不支持此设备");        }    }    private void statusChange(int newStatus) {             status = newStatus;        if (!isLive && newStatus==STATUS_COMPLETED) {                 DebugLog.d("","statusChange STATUS_COMPLETED...");            if (playerStateListener != null){                     playerStateListener.onComplete();            }        }else if (newStatus == STATUS_ERROR) {                 DebugLog.d("","statusChange STATUS_ERROR...");            if (playerStateListener != null){                     playerStateListener.onError();            }        } else if(newStatus==STATUS_LOADING){     //            $.id(R.id.app_video_loading).visible();            if (playerStateListener != null){                     playerStateListener.onLoading();            }            DebugLog.d("","statusChange STATUS_LOADING...");        } else if (newStatus == STATUS_PLAYING) {                 DebugLog.d("","statusChange STATUS_PLAYING...");            if (playerStateListener != null){                     playerStateListener.onPlay();            }        }    }    public void onPause() {             pauseTime= System.currentTimeMillis();        if (status==STATUS_PLAYING) {                 videoView.pause();            if (!isLive) {                     currentPosition = videoView.getCurrentPosition();            }        }    }    public void onResume() {             pauseTime=0;        if (status==STATUS_PLAYING) {                 if (isLive) {                     videoView.seekTo(0);            } else {                     if (currentPosition>0) {                         videoView.seekTo(currentPosition);                }            }            videoView.start();        }    }    public void onDestroy() {             orientationEventListener.disable();        videoView.stopPlayback();    }    public void play(String url) {             this.url = url;        if (playerSupport) {                 videoView.setVideoPath(url);            videoView.start();        }    }    private String generateTime(long time) {             int totalSeconds = (int) (time / 1000);        int seconds = totalSeconds % 60;        int minutes = (totalSeconds / 60) % 60;        int hours = totalSeconds / 3600;        return hours > 0 ? String.format("%02d:%02d:%02d", hours, minutes, seconds) : String.format("%02d:%02d", minutes, seconds);    }    private int getScreenOrientation() {             int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();        DisplayMetrics dm = new DisplayMetrics();        activity.getWindowManager().getDefaultDisplay().getMetrics(dm);        int width = dm.widthPixels;        int height = dm.heightPixels;        int orientation;        // if the device's natural orientation is portrait:        if ((rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) && height > width ||                (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) && width > height) {                 switch (rotation) {                     case Surface.ROTATION_0:                    orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;                    break;                case Surface.ROTATION_90:                    orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;                    break;                case Surface.ROTATION_180:                    orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;                    break;                case Surface.ROTATION_270:                    orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;                    break;                default:                    orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;                    break;            }        }        // if the device's natural orientation is landscape or if the device        // is square:        else {                 switch (rotation) {                     case Surface.ROTATION_0:                    orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;                    break;                case Surface.ROTATION_90:                    orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;                    break;                case Surface.ROTATION_180:                    orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;                    break;                case Surface.ROTATION_270:                    orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;                    break;                default:                    orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;                    break;            }        }        return orientation;    }    /**     * 滑动改变声音大小     *     * @param percent     */    private void onVolumeSlide(float percent) {             if (volume == -1) {                 volume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);            if (volume < 0)                volume = 0;        }        int index = (int) (percent * mMaxVolume) + volume;        if (index > mMaxVolume) {                 index = mMaxVolume;        } else if (index < 0){                 index = 0;        }        // 变更声音        audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, index, 0);        // 变更进度条        int i = (int) (index * 1.0 / mMaxVolume * 100);        String s = i + "%";        if (i == 0) {                 s = "off";        }        DebugLog.d("","onVolumeSlide:"+s);    }    private void onProgressSlide(float percent) {             long position = videoView.getCurrentPosition();        long duration = videoView.getDuration();        long deltaMax = Math.min(100 * 1000, duration - position);        long delta = (long) (deltaMax * percent);        newPosition = delta + position;        if (newPosition > duration) {                 newPosition = duration;        } else if (newPosition <= 0) {                 newPosition=0;            delta=-position;        }        int showDelta = (int) delta / 1000;        if (showDelta != 0) {                 String text = showDelta > 0 ? ("+" + showDelta) : "" + showDelta;            DebugLog.d("","onProgressSlide:" + text);        }    }    /**     * 滑动改变亮度     *     * @param percent     */    private void onBrightnessSlide(float percent) {             if (brightness < 0) {                 brightness = activity.getWindow().getAttributes().screenBrightness;            if (brightness <= 0.00f){                     brightness = 0.50f;            }else if (brightness < 0.01f){                     brightness = 0.01f;            }        }        DebugLog.d("","brightness:"+brightness+",percent:"+ percent);        WindowManager.LayoutParams lpa = activity.getWindow().getAttributes();        lpa.screenBrightness = brightness + percent;        if (lpa.screenBrightness > 1.0f){                 lpa.screenBrightness = 1.0f;        }else if (lpa.screenBrightness < 0.01f){                 lpa.screenBrightness = 0.01f;        }        activity.getWindow().setAttributes(lpa);    }    public void setFullScreenOnly(boolean fullScreenOnly) {             this.fullScreenOnly = fullScreenOnly;        tryFullScreen(fullScreenOnly);        if (fullScreenOnly) {                 activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);        } else {                 activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);        }    }    private void tryFullScreen(boolean fullScreen) {             if (activity instanceof AppCompatActivity) {                 ActionBar supportActionBar = ((AppCompatActivity) activity).getSupportActionBar();            if (supportActionBar != null) {                     if (fullScreen) {                         supportActionBar.hide();                } else {                         supportActionBar.show();                }            }        }        setFullScreen(fullScreen);    }    private void setFullScreen(boolean fullScreen) {             if (activity != null) {                 WindowManager.LayoutParams attrs = activity.getWindow().getAttributes();            if (fullScreen) {                     attrs.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN;                activity.getWindow().setAttributes(attrs);                activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);            } else {                     attrs.flags &= (~WindowManager.LayoutParams.FLAG_FULLSCREEN);                activity.getWindow().setAttributes(attrs);                activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);            }        }    }    /**     * 
     *     fitParent:可能会剪裁,保持原视频的大小,显示在中心,当原视频的大小超过view的大小超过部分裁剪处理     *     fillParent:可能会剪裁,等比例放大视频,直到填满View为止,超过View的部分作裁剪处理     *     wrapContent:将视频的内容完整居中显示,如果视频大于view,则按比例缩视频直到完全显示在view中     *     fitXY:不剪裁,非等比例拉伸画面填满整个View     *     16:9:不剪裁,非等比例拉伸画面到16:9,并完全显示在View中     *     4:3:不剪裁,非等比例拉伸画面到4:3,并完全显示在View中     * 
* @param scaleType */
public void setScaleType(String scaleType) { if (SCALETYPE_FITPARENT.equals(scaleType)) { videoView.setAspectRatio(IRenderView.AR_ASPECT_FIT_PARENT); }else if (SCALETYPE_FILLPARENT.equals(scaleType)) { videoView.setAspectRatio(IRenderView.AR_ASPECT_FILL_PARENT); }else if (SCALETYPE_WRAPCONTENT.equals(scaleType)) { videoView.setAspectRatio(IRenderView.AR_ASPECT_WRAP_CONTENT); }else if (SCALETYPE_FITXY.equals(scaleType)) { videoView.setAspectRatio(IRenderView.AR_MATCH_PARENT); }else if (SCALETYPE_16_9.equals(scaleType)) { videoView.setAspectRatio(IRenderView.AR_16_9_FIT_PARENT); }else if (SCALETYPE_4_3.equals(scaleType)) { videoView.setAspectRatio(IRenderView.AR_4_3_FIT_PARENT); } } public void start() { videoView.start(); } public void pause() { videoView.pause(); } public boolean onBackPressed() { if (!fullScreenOnly && getScreenOrientation() == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) { activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); return true; } return false; } class Query { private final Activity activity; private View view; public Query(Activity activity) { this.activity=activity; } public Query id(int id) { view = activity.findViewById(id); return this; } public Query image(int resId) { if (view instanceof ImageView) { ((ImageView) view).setImageResource(resId); } return this; } public Query visible() { if (view != null) { view.setVisibility(View.VISIBLE); } return this; } public Query gone() { if (view != null) { view.setVisibility(View.GONE); } return this; } public Query invisible() { if (view != null) { view.setVisibility(View.INVISIBLE); } return this; } public Query clicked(View.OnClickListener handler) { if (view != null) { view.setOnClickListener(handler); } return this; } public Query text(CharSequence text) { if (view!=null && view instanceof TextView) { ((TextView) view).setText(text); } return this; } public Query visibility(int visible) { if (view != null) { view.setVisibility(visible); } return this; } private void size(boolean width, int n, boolean dip){ if(view != null){ ViewGroup.LayoutParams lp = view.getLayoutParams(); if(n > 0 && dip){ n = dip2pixel(activity, n); } if(width){ lp.width = n; }else{ lp.height = n; } view.setLayoutParams(lp); } } public void height(int height, boolean dip) { size(false,height,dip); } public int dip2pixel(Context context, float n){ int value = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, n, context.getResources().getDisplayMetrics()); return value; } public float pixel2dip(Context context, float n){ Resources resources = context.getResources(); DisplayMetrics metrics = resources.getDisplayMetrics(); float dp = n / (metrics.densityDpi / 160f); return dp; } } public class PlayerGestureListener extends GestureDetector.SimpleOnGestureListener { private boolean firstTouch; private boolean volumeControl; private boolean toSeek; /** * 双击 */ @Override public boolean onDoubleTap(MotionEvent e) { videoView.toggleAspectRatio(); return true; } @Override public boolean onDown(MotionEvent e) { firstTouch = true; return super.onDown(e); } /** * 滑动 */ @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { float mOldX = e1.getX(), mOldY = e1.getY(); float deltaY = mOldY - e2.getY(); float deltaX = mOldX - e2.getX(); if (firstTouch) { toSeek = Math.abs(distanceX) >= Math.abs(distanceY); volumeControl=mOldX > screenWidthPixels * 0.5f; firstTouch = false; } if (toSeek) { if (!isLive) { onProgressSlide(-deltaX / videoView.getWidth()); } } else { float percent = deltaY / videoView.getHeight(); if (volumeControl) { onVolumeSlide(percent); } else { onBrightnessSlide(percent); } } return super.onScroll(e1, e2, distanceX, distanceY); } @Override public boolean onSingleTapUp(MotionEvent e) { return true; } } /** * is player support this device * @return */ public boolean isPlayerSupport() { return playerSupport; } /** * 是否正在播放 * @return */ public boolean isPlaying() { return videoView!=null?videoView.isPlaying():false; } public void stop(){ videoView.stopPlayback(); } public int getCurrentPosition(){ return videoView.getCurrentPosition(); } public IjkVideoView getVideoView(){ if (videoView != null){ return videoView; } return null; } /** * get video duration * @return */ public int getDuration(){ return videoView.getDuration(); } public PlayerManager playInFullScreen(boolean fullScreen){ if (fullScreen) { activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); } return this; } public PlayerManager onError(OnErrorListener onErrorListener) { this.onErrorListener = onErrorListener; return this; } public PlayerManager onComplete(OnCompleteListener onCompleteListener) { this.onCompleteListener = onCompleteListener; return this; } public PlayerManager onInfo(OnInfoListener onInfoListener) { this.onInfoListener = onInfoListener; return this; } public PlayerManager onControlPanelVisibilityChange(OnControlPanelVisibilityChangeListener listener){ this.onControlPanelVisibilityChangeListener = listener; return this; } /** * set is live (can't seek forward) * @param isLive * @return */ public PlayerManager live(boolean isLive) { this.isLive = isLive; return this; } public PlayerManager toggleAspectRatio(){ if (videoView != null) { videoView.toggleAspectRatio(); } return this; } public interface PlayerStateListener{ void onComplete(); void onError(); void onLoading(); void onPlay(); } public interface OnErrorListener{ void onError(int what, int extra); } public interface OnCompleteListener{ void onComplete(); } public interface OnControlPanelVisibilityChangeListener{ void change(boolean isShowing); } public interface OnInfoListener{ void onInfo(int what, int extra); }}

*Settings.java

/* * Copyright (C) 2015 Bilibili * Copyright (C) 2015 Zhang Rui  * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *      http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package com.example.k.bilibiliapi.video;import android.content.Context;import android.content.SharedPreferences;import android.preference.PreferenceManager;import com.example.k.bilibiliapi.R;public class Settings {         private Context mAppContext;    private SharedPreferences mSharedPreferences;    public static final int PV_PLAYER__Auto = 0;    public static final int PV_PLAYER__AndroidMediaPlayer = 1;    public static final int PV_PLAYER__IjkMediaPlayer = 2;    public static final int PV_PLAYER__IjkExoMediaPlayer = 3;    public Settings(Context context) {             mAppContext = context.getApplicationContext();        mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(mAppContext);    }    public boolean getEnableBackgroundPlay() {             String key = mAppContext.getString(R.string.pref_key_enable_background_play);        return mSharedPreferences.getBoolean(key, false);    }    public int getPlayer() {             String key = mAppContext.getString(R.string.pref_key_player);        String value = mSharedPreferences.getString(key, "");        try {                 return Integer.valueOf(value).intValue();        } catch (NumberFormatException e) {                 return 0;        }    }    public boolean getUsingMediaCodec() {             String key = mAppContext.getString(R.string.pref_key_using_media_codec);        return mSharedPreferences.getBoolean(key, false);    }    public boolean getUsingMediaCodecAutoRotate() {             String key = mAppContext.getString(R.string.pref_key_using_media_codec_auto_rotate);        return mSharedPreferences.getBoolean(key, false);    }    public boolean getMediaCodecHandleResolutionChange() {             String key = mAppContext.getString(R.string.pref_key_media_codec_handle_resolution_change);        return mSharedPreferences.getBoolean(key, false);    }    public boolean getUsingOpenSLES() {             String key = mAppContext.getString(R.string.pref_key_using_opensl_es);        return mSharedPreferences.getBoolean(key, false);    }    public String getPixelFormat() {             String key = mAppContext.getString(R.string.pref_key_pixel_format);        return mSharedPreferences.getString(key, "");    }    public boolean getEnableNoView() {             String key = mAppContext.getString(R.string.pref_key_enable_no_view);        return mSharedPreferences.getBoolean(key, false);    }    public boolean getEnableSurfaceView() {             String key = mAppContext.getString(R.string.pref_key_enable_surface_view);        return mSharedPreferences.getBoolean(key, false);    }    public boolean getEnableTextureView() {             String key = mAppContext.getString(R.string.pref_key_enable_texture_view);        return mSharedPreferences.getBoolean(key, false);    }    public boolean getEnableDetachedSurfaceTextureView() {             String key = mAppContext.getString(R.string.pref_key_enable_detached_surface_texture);        return mSharedPreferences.getBoolean(key, false);    }    public boolean getUsingMediaDataSource() {             String key = mAppContext.getString(R.string.pref_key_using_mediadatasource);        return mSharedPreferences.getBoolean(key, false);    }    public String getLastDirectory() {             String key = mAppContext.getString(R.string.pref_key_last_directory);        return mSharedPreferences.getString(key, "/");    }    public void setLastDirectory(String path) {             String key = mAppContext.getString(R.string.pref_key_last_directory);        mSharedPreferences.edit().putString(key, path).apply();    }}

SurfaceRenderView.java

/* * Copyright (C) 2015 Bilibili * Copyright (C) 2015 Zhang Rui  * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *      http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package com.example.k.bilibiliapi.video;import android.annotation.TargetApi;import android.content.Context;import android.graphics.SurfaceTexture;import android.os.Build;import android.support.annotation.NonNull;import android.support.annotation.Nullable;import android.util.AttributeSet;import android.util.Log;import android.view.Surface;import android.view.SurfaceHolder;import android.view.SurfaceView;import android.view.View;import android.view.accessibility.AccessibilityEvent;import android.view.accessibility.AccessibilityNodeInfo;import java.lang.ref.WeakReference;import java.util.Map;import java.util.concurrent.ConcurrentHashMap;import tv.danmaku.ijk.media.player.IMediaPlayer;import tv.danmaku.ijk.media.player.ISurfaceTextureHolder;public class SurfaceRenderView extends SurfaceView implements IRenderView {         private MeasureHelper mMeasureHelper;    public SurfaceRenderView(Context context) {             super(context);        initView(context);    }    public SurfaceRenderView(Context context, AttributeSet attrs) {             super(context, attrs);        initView(context);    }    public SurfaceRenderView(Context context, AttributeSet attrs, int defStyleAttr) {             super(context, attrs, defStyleAttr);        initView(context);    }    @TargetApi(Build.VERSION_CODES.LOLLIPOP)    public SurfaceRenderView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {             super(context, attrs, defStyleAttr, defStyleRes);        initView(context);    }    private void initView(Context context) {             mMeasureHelper = new MeasureHelper(this);        mSurfaceCallback = new SurfaceCallback(this);        getHolder().addCallback(mSurfaceCallback);        //noinspection deprecation        getHolder().setType(SurfaceHolder.SURFACE_TYPE_NORMAL);    }    @Override    public View getView() {             return this;    }    @Override    public boolean shouldWaitForResize() {             return true;    }    //--------------------    // Layout & Measure    //--------------------    @Override    public void setVideoSize(int videoWidth, int videoHeight) {             if (videoWidth > 0 && videoHeight > 0) {                 mMeasureHelper.setVideoSize(videoWidth, videoHeight);            getHolder().setFixedSize(videoWidth, videoHeight);            requestLayout();        }    }    @Override    public void setVideoSampleAspectRatio(int videoSarNum, int videoSarDen) {             if (videoSarNum > 0 && videoSarDen > 0) {                 mMeasureHelper.setVideoSampleAspectRatio(videoSarNum, videoSarDen);            requestLayout();        }    }    @Override    public void setVideoRotation(int degree) {             Log.e("", "SurfaceView doesn't support rotation (" + degree + ")!\n");    }    @Override    public void setAspectRatio(int aspectRatio) {             mMeasureHelper.setAspectRatio(aspectRatio);        requestLayout();    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {             if (this.getWidth() != 0) {                 setMeasuredDimension(this.getWidth(), this.getHeight());            return;        }        int width = getDefaultSize(0, widthMeasureSpec);        int height = getDefaultSize(0, heightMeasureSpec);        setMeasuredDimension(width, height);    }    //--------------------    // SurfaceViewHolder    //--------------------    private static final class InternalSurfaceHolder implements IRenderView.ISurfaceHolder {             private SurfaceRenderView mSurfaceView;        private SurfaceHolder mSurfaceHolder;        public InternalSurfaceHolder(@NonNull SurfaceRenderView surfaceView,                                     @Nullable SurfaceHolder surfaceHolder) {                 mSurfaceView = surfaceView;            mSurfaceHolder = surfaceHolder;        }        public void bindToMediaPlayer(IMediaPlayer mp) {                 if (mp != null) {                     if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) &&                        (mp instanceof ISurfaceTextureHolder)) {                         ISurfaceTextureHolder textureHolder = (ISurfaceTextureHolder) mp;                    textureHolder.setSurfaceTexture(null);                }                mp.setDisplay(mSurfaceHolder);            }        }        @NonNull        @Override        public IRenderView getRenderView() {                 return mSurfaceView;        }        @Nullable        @Override        public SurfaceHolder getSurfaceHolder() {                 return mSurfaceHolder;        }        @Nullable        @Override        public SurfaceTexture getSurfaceTexture() {                 return null;        }        @Nullable        @Override        public Surface openSurface() {                 if (mSurfaceHolder == null)                return null;            return mSurfaceHolder.getSurface();        }    }    //-------------------------    // SurfaceHolder.Callback    //-------------------------    @Override    public void addRenderCallback(IRenderCallback callback) {             mSurfaceCallback.addRenderCallback(callback);    }    @Override    public void removeRenderCallback(IRenderCallback callback) {             mSurfaceCallback.removeRenderCallback(callback);    }    private SurfaceCallback mSurfaceCallback;    private static final class SurfaceCallback implements SurfaceHolder.Callback {             private SurfaceHolder mSurfaceHolder;        private boolean mIsFormatChanged;        private int mFormat;        private int mWidth;        private int mHeight;        private WeakReference<SurfaceRenderView> mWeakSurfaceView;        private Map<IRenderCallback, Object> mRenderCallbackMap = new ConcurrentHashMap<IRenderCallback, Object>();        public SurfaceCallback(@NonNull SurfaceRenderView surfaceView) {                 mWeakSurfaceView = new WeakReference<SurfaceRenderView>(surfaceView);        }        public void addRenderCallback(@NonNull IRenderCallback callback) {                 mRenderCallbackMap.put(callback, callback);            ISurfaceHolder surfaceHolder = null;            if (mSurfaceHolder != null) {                     if (surfaceHolder == null)                    surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder);                callback.onSurfaceCreated(surfaceHolder, mWidth, mHeight);            }            if (mIsFormatChanged) {                     if (surfaceHolder == null)                    surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder);                callback.onSurfaceChanged(surfaceHolder, mFormat, mWidth, mHeight);            }        }        public void removeRenderCallback(@NonNull IRenderCallback callback) {                 mRenderCallbackMap.remove(callback);        }        @Override        public void surfaceCreated(SurfaceHolder holder) {                 mSurfaceHolder = holder;            mIsFormatChanged = false;            mFormat = 0;            mWidth = 0;            mHeight = 0;            ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder);            for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) {                     renderCallback.onSurfaceCreated(surfaceHolder, 0, 0);            }        }        @Override        public void surfaceDestroyed(SurfaceHolder holder) {                 mSurfaceHolder = null;            mIsFormatChanged = false;            mFormat = 0;            mWidth = 0;            mHeight = 0;            ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder);            for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) {                     renderCallback.onSurfaceDestroyed(surfaceHolder);            }        }        @Override        public void surfaceChanged(SurfaceHolder holder, int format,                                   int width, int height) {                 mSurfaceHolder = holder;            mIsFormatChanged = true;            mFormat = format;            mWidth = width;            mHeight = height;            // mMeasureHelper.setVideoSize(width, height);            ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder);            for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) {                     renderCallback.onSurfaceChanged(surfaceHolder, format, width, height);            }        }    }    //--------------------    // Accessibility    //--------------------    @Override    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {             super.onInitializeAccessibilityEvent(event);        event.setClassName(SurfaceRenderView.class.getName());    }    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)    @Override    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {             super.onInitializeAccessibilityNodeInfo(info);        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {                 info.setClassName(SurfaceRenderView.class.getName());        }    }}

TableLayoutBinder.java

/* * Copyright (C) 2015 Bilibili * Copyright (C) 2015 Zhang Rui  * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *      http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package com.example.k.bilibiliapi.video;import android.content.Context;import android.support.v7.app.AlertDialog;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.TableLayout;import android.widget.TextView;import com.example.k.bilibiliapi.R;public class TableLayoutBinder {         private Context mContext;    public ViewGroup mTableView;    public TableLayout mTableLayout;    public TableLayoutBinder(Context context) {             this(context, R.layout.table_media_info);    }    public TableLayoutBinder(Context context, int layoutResourceId) {             mContext = context;        mTableView = (ViewGroup) LayoutInflater.from(mContext).inflate(layoutResourceId, null);        mTableLayout = (TableLayout) mTableView.findViewById(R.id.table);    }    public TableLayoutBinder(Context context, TableLayout tableLayout) {             mContext = context;        mTableView = tableLayout;        mTableLayout = tableLayout;    }    public View appendRow1(String name, String value) {             return appendRow(R.layout.table_media_info_row1, name, value);    }    public View appendRow1(int nameId, String value) {             return appendRow1(mContext.getString(nameId), value);    }    public View appendRow2(String name, String value) {             return appendRow(R.layout.table_media_info_row2, name, value);    }    public View appendRow2(int nameId, String value) {             return appendRow2(mContext.getString(nameId), value);    }    public View appendSection(String name) {             return appendRow(R.layout.table_media_info_section, name, null);    }    public View appendSection(int nameId) {             return appendSection(mContext.getString(nameId));    }    public View appendRow(int layoutId, String name, String value) {             ViewGroup rowView = (ViewGroup) LayoutInflater.from(mContext).inflate(layoutId, mTableLayout, false);        setNameValueText(rowView, name, value);        mTableLayout.addView(rowView);        return rowView;    }    public ViewHolder obtainViewHolder(View rowView) {             ViewHolder viewHolder = (ViewHolder) rowView.getTag();        if (viewHolder == null) {                 viewHolder = new ViewHolder();            viewHolder.mNameTextView = (TextView) rowView.findViewById(R.id.name);            viewHolder.mValueTextView = (TextView) rowView.findViewById(R.id.value);            rowView.setTag(viewHolder);        }        return viewHolder;    }    public void setNameValueText(View rowView, String name, String value) {             ViewHolder viewHolder = obtainViewHolder(rowView);        viewHolder.setName(name);        viewHolder.setValue(value);    }    public void setValueText(View rowView, String value) {             ViewHolder viewHolder = obtainViewHolder(rowView);        viewHolder.setValue(value);    }    public ViewGroup buildLayout() {             return mTableView;    }    public AlertDialog.Builder buildAlertDialogBuilder() {             AlertDialog.Builder dlgBuilder = new AlertDialog.Builder(mContext);        dlgBuilder.setView(buildLayout());        return dlgBuilder;    }    private static class ViewHolder {             public TextView mNameTextView;        public TextView mValueTextView;        public void setName(String name) {                 if (mNameTextView != null) {                     mNameTextView.setText(name);            }        }        public void setValue(String value) {                 if (mValueTextView != null) {                     mValueTextView.setText(value);            }        }    }}

TextureRenderView.java

/* * Copyright (C) 2015 Bilibili * Copyright (C) 2015 Zhang Rui  * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *      http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package com.example.k.bilibiliapi.video;import android.annotation.TargetApi;import android.content.Context;import android.graphics.SurfaceTexture;import android.os.Build;import android.support.annotation.NonNull;import android.support.annotation.Nullable;import android.util.AttributeSet;import android.util.Log;import android.view.Surface;import android.view.SurfaceHolder;import android.view.TextureView;import android.view.View;import android.view.accessibility.AccessibilityEvent;import android.view.accessibility.AccessibilityNodeInfo;import java.lang.ref.WeakReference;import java.util.Map;import java.util.concurrent.ConcurrentHashMap;import tv.danmaku.ijk.media.player.IMediaPlayer;import tv.danmaku.ijk.media.player.ISurfaceTextureHolder;import tv.danmaku.ijk.media.player.ISurfaceTextureHost;@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)public class TextureRenderView extends TextureView implements IRenderView {         private static final String TAG = "TextureRenderView";    private MeasureHelper mMeasureHelper;    public TextureRenderView(Context context) {             super(context);        initView(context);    }    public TextureRenderView(Context context, AttributeSet attrs) {             super(context, attrs);        initView(context);    }    public TextureRenderView(Context context, AttributeSet attrs, int defStyleAttr) {             super(context, attrs, defStyleAttr);        initView(context);    }    @TargetApi(Build.VERSION_CODES.LOLLIPOP)    public TextureRenderView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {             super(context, attrs, defStyleAttr, defStyleRes);        initView(context);    }    private void initView(Context context) {             mMeasureHelper = new MeasureHelper(this);        mSurfaceCallback = new SurfaceCallback(this);        setSurfaceTextureListener(mSurfaceCallback);    }    @Override    public View getView() {             return this;    }    @Override    public boolean shouldWaitForResize() {             return false;    }    @Override    protected void onDetachedFromWindow() {             mSurfaceCallback.willDetachFromWindow();        super.onDetachedFromWindow();        mSurfaceCallback.didDetachFromWindow();    }    //--------------------    // Layout & Measure    //--------------------    @Override    public void setVideoSize(int videoWidth, int videoHeight) {             if (videoWidth > 0 && videoHeight > 0) {                 mMeasureHelper.setVideoSize(videoWidth, videoHeight);            requestLayout();        }    }    @Override    public void setVideoSampleAspectRatio(int videoSarNum, int videoSarDen) {             if (videoSarNum > 0 && videoSarDen > 0) {                 mMeasureHelper.setVideoSampleAspectRatio(videoSarNum, videoSarDen);            requestLayout();        }    }    @Override    public void setVideoRotation(int degree) {             mMeasureHelper.setVideoRotation(degree);        setRotation(degree);    }    @Override    public void setAspectRatio(int aspectRatio) {             mMeasureHelper.setAspectRatio(aspectRatio);        requestLayout();    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {             mMeasureHelper.doMeasure(widthMeasureSpec, heightMeasureSpec);        setMeasuredDimension(mMeasureHelper.getMeasuredWidth(), mMeasureHelper.getMeasuredHeight());    }    //--------------------    // TextureViewHolder    //--------------------    public IRenderView.ISurfaceHolder getSurfaceHolder() {             return new InternalSurfaceHolder(this, mSurfaceCallback.mSurfaceTexture, mSurfaceCallback);    }    private static final class InternalSurfaceHolder implements IRenderView.ISurfaceHolder {             private TextureRenderView mTextureView;        private SurfaceTexture mSurfaceTexture;        private ISurfaceTextureHost mSurfaceTextureHost;        public InternalSurfaceHolder(@NonNull TextureRenderView textureView,                                     @Nullable SurfaceTexture surfaceTexture,                                     @NonNull ISurfaceTextureHost surfaceTextureHost) {                 mTextureView = textureView;            mSurfaceTexture = surfaceTexture;            mSurfaceTextureHost = surfaceTextureHost;        }        @TargetApi(Build.VERSION_CODES.JELLY_BEAN)        public void bindToMediaPlayer(IMediaPlayer mp) {                 if (mp == null)                return;            if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) &&                    (mp instanceof ISurfaceTextureHolder)) {                     ISurfaceTextureHolder textureHolder = (ISurfaceTextureHolder) mp;                mTextureView.mSurfaceCallback.setOwnSurfaceTexture(false);                SurfaceTexture surfaceTexture = textureHolder.getSurfaceTexture();                if (surfaceTexture != null) {                         mTextureView.setSurfaceTexture(surfaceTexture);                } else {                         textureHolder.setSurfaceTexture(mSurfaceTexture);                    textureHolder.setSurfaceTextureHost(mTextureView.mSurfaceCallback);                }            } else {                     mp.setSurface(openSurface());            }        }        @NonNull        @Override        public IRenderView getRenderView() {                 return mTextureView;        }        @Nullable        @Override        public SurfaceHolder getSurfaceHolder() {                 return null;        }        @Nullable        @Override        public SurfaceTexture getSurfaceTexture() {                 return mSurfaceTexture;        }        @Nullable        @Override        public Surface openSurface() {                 if (mSurfaceTexture == null)                return null;            return new Surface(mSurfaceTexture);        }    }    //-------------------------    // SurfaceHolder.Callback    //-------------------------    @Override    public void addRenderCallback(IRenderCallback callback) {             mSurfaceCallback.addRenderCallback(callback);    }    @Override    public void removeRenderCallback(IRenderCallback callback) {             mSurfaceCallback.removeRenderCallback(callback);    }    private SurfaceCallback mSurfaceCallback;    private static final class SurfaceCallback implements SurfaceTextureListener, ISurfaceTextureHost {             private SurfaceTexture mSurfaceTexture;        private boolean mIsFormatChanged;        private int mWidth;        private int mHeight;        private boolean mOwnSurfaceTexture = true;        private boolean mWillDetachFromWindow = false;        private boolean mDidDetachFromWindow = false;        private WeakReference<TextureRenderView> mWeakRenderView;        private Map<IRenderCallback, Object> mRenderCallbackMap = new ConcurrentHashMap<IRenderCallback, Object>();        public SurfaceCallback(@NonNull TextureRenderView renderView) {                 mWeakRenderView = new WeakReference<TextureRenderView>(renderView);        }        public void setOwnSurfaceTexture(boolean ownSurfaceTexture) {                 mOwnSurfaceTexture = ownSurfaceTexture;        }        public void addRenderCallback(@NonNull IRenderCallback callback) {                 mRenderCallbackMap.put(callback, callback);            ISurfaceHolder surfaceHolder = null;            if (mSurfaceTexture != null) {                     if (surfaceHolder == null)                    surfaceHolder = new InternalSurfaceHolder(mWeakRenderView.get(), mSurfaceTexture, this);                callback.onSurfaceCreated(surfaceHolder, mWidth, mHeight);            }            if (mIsFormatChanged) {                     if (surfaceHolder == null)                    surfaceHolder = new InternalSurfaceHolder(mWeakRenderView.get(), mSurfaceTexture, this);                callback.onSurfaceChanged(surfaceHolder, 0, mWidth, mHeight);            }        }        public void removeRenderCallback(@NonNull IRenderCallback callback) {                 mRenderCallbackMap.remove(callback);        }        @Override        public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {                 mSurfaceTexture = surface;            mIsFormatChanged = false;            mWidth = 0;            mHeight = 0;            ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakRenderView.get(), surface, this);            for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) {                     renderCallback.onSurfaceCreated(surfaceHolder, 0, 0);            }        }        @Override        public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {                 mSurfaceTexture = surface;            mIsFormatChanged = true;            mWidth = width;            mHeight = height;            ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakRenderView.get(), surface, this);            for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) {                     renderCallback.onSurfaceChanged(surfaceHolder, 0, width, height);            }        }        @Override        public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {                 mSurfaceTexture = surface;            mIsFormatChanged = false;            mWidth = 0;            mHeight = 0;            ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakRenderView.get(), surface, this);            for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) {                     renderCallback.onSurfaceDestroyed(surfaceHolder);            }            Log.d(TAG, "onSurfaceTextureDestroyed: destroy: " + mOwnSurfaceTexture);            return mOwnSurfaceTexture;        }        @Override        public void onSurfaceTextureUpdated(SurfaceTexture surface) {             }        //-------------------------        // ISurfaceTextureHost        //-------------------------        @Override        public void releaseSurfaceTexture(SurfaceTexture surfaceTexture) {                 if (surfaceTexture == null) {                     Log.d(TAG, "releaseSurfaceTexture: null");            } else if (mDidDetachFromWindow) {                     if (surfaceTexture != mSurfaceTexture) {                         Log.d(TAG, "releaseSurfaceTexture: didDetachFromWindow(): release different SurfaceTexture");                    surfaceTexture.release();                } else if (!mOwnSurfaceTexture) {                         Log.d(TAG, "releaseSurfaceTexture: didDetachFromWindow(): release detached SurfaceTexture");                    surfaceTexture.release();                } else {                         Log.d(TAG, "releaseSurfaceTexture: didDetachFromWindow(): already released by TextureView");                }            } else if (mWillDetachFromWindow) {                     if (surfaceTexture != mSurfaceTexture) {                         Log.d(TAG, "releaseSurfaceTexture: willDetachFromWindow(): release different SurfaceTexture");                    surfaceTexture.release();                } else if (!mOwnSurfaceTexture) {                         Log.d(TAG, "releaseSurfaceTexture: willDetachFromWindow(): re-attach SurfaceTexture to TextureView");                    setOwnSurfaceTexture(true);                } else {                         Log.d(TAG, "releaseSurfaceTexture: willDetachFromWindow(): will released by TextureView");                }            } else {                     if (surfaceTexture != mSurfaceTexture) {                         Log.d(TAG, "releaseSurfaceTexture: alive: release different SurfaceTexture");                    surfaceTexture.release();                } else if (!mOwnSurfaceTexture) {                         Log.d(TAG, "releaseSurfaceTexture: alive: re-attach SurfaceTexture to TextureView");                    setOwnSurfaceTexture(true);                } else {                         Log.d(TAG, "releaseSurfaceTexture: alive: will released by TextureView");                }            }        }        public void willDetachFromWindow() {                 Log.d(TAG, "willDetachFromWindow()");            mWillDetachFromWindow = true;        }        public void didDetachFromWindow() {                 Log.d(TAG, "didDetachFromWindow()");            mDidDetachFromWindow = true;        }    }    //--------------------    // Accessibility    //--------------------    @Override    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {             super.onInitializeAccessibilityEvent(event);        event.setClassName(TextureRenderView.class.getName());    }    @Override    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {             super.onInitializeAccessibilityNodeInfo(info);        info.setClassName(TextureRenderView.class.getName());    }}

这就是所有需要的文件了。

引入库

点击此处下载编译好的so库。
ijkplayerso文件夹中将libs文件夹中的几个文件夹复制到自己项目的app的libs文件夹中。

代码引入

布局文件

<FrameLayout        android:layout_width="400dp"        android:layout_height="300dp">        <com.example.k.bilibiliapi.video.IjkVideoView            android:id="@+id/video_view"            android:layout_width="match_parent"            android:layout_height="match_parent">        com.example.k.bilibiliapi.video.IjkVideoView>

java文件中

IjkVideoView VideoPlayer=findViewById(R.id.video_view);VideoPlayer.setAspectRatio(IRenderView.AR_ASPECT_FIT_PARENT);VideoPlayer.setVideoURI(Uri.parse(url));VideoPlayer.start();

缺点

只有基本的播放,没有设置缓冲图,没有控制按钮,返回的时候没有释放资源,会一直播放停不下来。

实在太卡了,我也不知道我为什么这么傻把代码全放上来,这就是基本的配置使用,等有空我会把文件上传了。

更多相关文章

  1. 节操视频播放器
  2. Android 中的 R.class,减小 Apk 包大小
  3. Android 改变AlertDialog的大小
  4. 修改android桌面图标默认大小
  5. 2010.11.27———android 展示网络上的图片和播放视频
  6. 【视频课程】Android应用开发典型案例分析—主流mp3播放器
  7. Android中获取屏幕相关信息(屏幕大小,状态栏、标题栏高度)

随机推荐

  1. android的事物
  2. Android/iso客户端与服务器安全通信(Oauth
  3. android Edittext焦点获取和清除
  4. Android 铃声设置界面,起始位置为当前已选
  5. Android震动和播放资源文件中的声音文件
  6. Android和Linux版本对应
  7. [置顶] Android自定义标题栏
  8. Lint found fatal errors while assembli
  9. Android(安卓)Retrofit 2.0自定义JSONObj
  10. Android Volley 详解