Android Open Source - netmbuddy Video Player Activity






From Project

Back to project page netmbuddy.

License

The source code is released under:

------- Default ------- Copyright (C) 2012, 2013, 2014 Younghyung Cho. <yhcting77@gmail.com> All rights reserved. This file is part of NetMBuddy This program is licensed under the FreeBSD license R...

If you think the Android project netmbuddy listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/******************************************************************************
 * Copyright (C) 2012, 2013, 2014/*ww w . j av  a  2s  . co  m*/
 * Younghyung Cho. <yhcting77@gmail.com>
 * All rights reserved.
 *
 * This file is part of NetMBuddy
 *
 * This program is licensed under the FreeBSD license
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * The views and conclusions contained in the software and documentation
 * are those of the authors and should not be interpreted as representing
 * official policies, either expressed or implied, of the FreeBSD Project.
 *****************************************************************************/

package free.yhc.netmbuddy;

import java.util.ArrayList;

import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import android.widget.TextView;
import free.yhc.netmbuddy.model.RTState;
import free.yhc.netmbuddy.model.UnexpectedExceptionHandler;
import free.yhc.netmbuddy.model.YTHacker;
import free.yhc.netmbuddy.model.YTPlayer;
import free.yhc.netmbuddy.model.YTPlayer.StopState;
import free.yhc.netmbuddy.utils.ImageUtils;
import free.yhc.netmbuddy.utils.UiUtils;
import free.yhc.netmbuddy.utils.Utils;

public class VideoPlayerActivity extends Activity implements
YTPlayer.PlayerStateListener,
YTPlayer.VideosStateListener,
UnexpectedExceptionHandler.Evidence {
    private static final boolean DBG = false;
    private static final Utils.Logger P = new Utils.Logger(VideoPlayerActivity.class);

    private static final boolean sNavUiCanBeHidden
        = android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH;

    private final YTPlayer      mMp = YTPlayer.get();
    private SurfaceView         mSurfv;
    private Utils.PrefQuality   mVQuality = Utils.getPrefQuality();
    private int                 mStatusBarHeight    = 0;

    // If : Interface
    private boolean             mDelayedSetIfVisibility     = true;
    private boolean             mUserIfVisible  = false;
    private int                 mLastSysUiVis   = 0;

    private static enum Orientation {
        PORTRAIT,
        LANDSCAPE,
        SYSTEM,     // current system status
    }

    private void
    printWindowFrames() {
        if (DBG) {
            View dv = getWindow().getDecorView();
            P.v("DecorView : " + dv.getLeft() + ", "
                               + dv.getTop() + ", "
                               + dv.getRight() + ","
                               + dv.getBottom());
            Rect rect = new Rect();
            dv.getWindowVisibleDisplayFrame(rect);
            P.v("VisibleFrame : " + rect.left + ", "
                                  + rect.top + ","
                                  + rect.right + ","
                                  + rect.bottom);
        }
    }

    @TargetApi(14)
    private void
    setNavVisibility(boolean visible) {
        if (visible)
            getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
        else
            getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
    }

    @TargetApi(14)
    private void
    setOnSystemUiVisibilityChangeListener() {
        getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(
                new View.OnSystemUiVisibilityChangeListener() {
                    @Override
                    public void
                    onSystemUiVisibilityChange(int visibility) {
                        int diff = mLastSysUiVis ^ visibility;
                        mLastSysUiVis = visibility;
                        // NOTE
                        // There is one issue here.
                        // Android Framework calls this function more than once especially
                        //   in case hiding system ui.
                        // (At my test, this function is calls 3 times for one
                        //   setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)
                        // This leads to some strange UI bug.
                        // CASE
                        // ----
                        //   Initial state : system ui is hidden.
                        // - showing system ui by touching ground view
                        // - touch ground view again in short time to hide system ui again.
                        // => result
                        //   - system ui is disappeared.
                        //   - But soon, system ui is showing again
                        //     (this callback(onSystemUiVisibilityChange) is called again
                        //        - may be 2nd or 3rd one - after system ui is hidden.)
                        //
                        // To avoid this case, 'diff' is used.
                        // So, UserInterface becomes visible only when SYSTEM_UI_FLAG_HIDE_NAVIGATION bit
                        //   is changed from 'set' to 'clear'
                        // But, still, Android frameworks has bug of this case.
                        // So, even if UserInterface works well, Navigation UI still visible in case
                        //   hiding navigation bar as soon as showing it (like above CASE).
                        //
                        // But, this is NOT critical. So, ignore it at this time.
                        if (DBG) P.v("visibility(" + visibility + ")");
                        if (0 != (diff & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)
                            && 0 == (visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION))
                            updateUserInterfaceVisibility(true);
                    }
                });
    }

    private void
    fitVideoSurfaceToScreen(Orientation ori) {
        SurfaceHolder holder = mSurfv.getHolder();

        //Scale video with fixed-ratio.
        int vw = mMp.getVideoWidth();
        int vh = mMp.getVideoHeight();

        if (0 >= vw || 0 >= vh)
            return;

        // TODO
        // Is there good way to get screen size without branching two cases?
        // Below codes looks dirty to me....
        // But, at this moment, I failed to find any other way...
        //
        // NOTE
        // Status bar hiding/showing has animation effect.
        // So, getting status bar height sometimes returns unexpected value.
        // (Not perfectly matching window's FLAG_FULLSCREEN flag)
        //
        // Showing animation means "status bar is shown".
        // So, even if FLAG_FULLSCREEN flag is cleared, rect.top of visible frame is NOT 0
        //   because (I think) status bar is still in animation and it is not hidden perfectly yet.
        // But, in case of showing status bar, even if status bar is not fully shown,
        //   status bar already hold space for it. So, rect.top of visible frame is unexpected value.
        //
        // To handle above issue, below hack is used.
        //
        // Because of the reason described above, at this moment, we can't get height of status bar with sure.
        // So, we should get in advance when we are sure to get valid height of status bar.
        // mStatusBarHeight is used for that reason.
        // And onWindowFocusChanged is the right place to get status height.
        Rect rect = Utils.getVisibleFrame(this);
        int sw = rect.width();
        // default is full screen.
        int sh = rect.bottom;

        // HACK! : if user interface is NOT shown, even if 0 != rect.top, it is ignored.
        if (isUserInterfaceVisible())
            // When user interface is shown, status bar is also shown.
            sh -= mStatusBarHeight;

        if ((Orientation.LANDSCAPE == ori && sw < sh)
            || (Orientation.PORTRAIT == ori && sw > sh)) {
             // swap
            int tmp = sw;
            sw = sh;
            sh = tmp;
        }

        // Now, sw is always length of longer axis.
        int[] sz = new int[2];
        ImageUtils.fitFixedRatio(sw, sh, vw, vh, sz);
        holder.setFixedSize(sz[0], sz[1]);

        ViewGroup.LayoutParams lp = mSurfv.getLayoutParams();
        lp.width = sz[0];
        lp.height = sz[1];
        mSurfv.setLayoutParams(lp);
        mSurfv.requestLayout();
    }

    private void
    setController(boolean withSurface) {
        SurfaceView surfv = withSurface? (SurfaceView)findViewById(R.id.surface): null;
        View.OnClickListener onClick = new View.OnClickListener() {
            @Override
            public void
            onClick(View v) {
                changeVideoQuality(v);
            }
        };
        // toolBtn for changing video quality is not implemented yet.
        // This is for future use.
        YTPlayer.ToolButton toolBtn = new YTPlayer.ToolButton(R.drawable.ic_preferences, onClick);

        mMp.setController(this,
                          (ViewGroup)findViewById(R.id.player),
                          (ViewGroup)findViewById(R.id.list_drawer),
                          surfv,
                          toolBtn);
    }

    private void
    startAnimation(View v, int animation, Animation.AnimationListener listener) {
        if (null != v.getAnimation()) {
            v.getAnimation().cancel();
            v.getAnimation().reset();
        }
        Animation anim = AnimationUtils.loadAnimation(this, animation);
        if (null != listener)
            anim.setAnimationListener(listener);
        v.startAnimation(anim);
    }

    private void
    stopAnimation(View v) {
        if (null != v.getAnimation()) {
            v.getAnimation().cancel();
            v.getAnimation().reset();
        }
    }

    private boolean
    isUserInterfaceVisible() {
        return mUserIfVisible;
    }

    private void
    updateUserInterfaceVisibility(boolean visibility) {
        mUserIfVisible = visibility;

        ViewGroup playerv = (ViewGroup)findViewById(R.id.player);
        ViewGroup drawer = (ViewGroup)findViewById(R.id.list_drawer);

        if (visibility) {
            getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN
                                   | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN);
            playerv.setVisibility(View.VISIBLE);
            drawer.setVisibility(View.VISIBLE);
        } else {
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN
                                 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN);
            playerv.setVisibility(View.GONE);
            drawer.setVisibility(View.GONE);
        }

        if (sNavUiCanBeHidden)
            setNavVisibility(visibility);

        fitVideoSurfaceToScreen(Orientation.SYSTEM);
    }

    private void
    showLoadingSpinProgress() {
        View infov = findViewById(R.id.infolayout);
        if (View.VISIBLE == infov.getVisibility())
            return; // nothing to do

        ImageView iv = (ImageView)infov.findViewById(R.id.infoimg);
        TextView  tv = (TextView)infov.findViewById(R.id.infomsg);
        tv.setText(R.string.loading);
        infov.setVisibility(View.VISIBLE);
        startAnimation(iv, R.anim.rotate, null);
    }

    private void
    hideLoadingSpinProgress() {
        View infov = findViewById(R.id.infolayout);
        if (View.GONE == infov.getVisibility())
            return; // nothing to do

        stopAnimation(infov.findViewById(R.id.infoimg));
        infov.setVisibility(View.GONE);
    }

    private void
    doChangeVideoQuality(Utils.PrefQuality quality) {
        SharedPreferences.Editor prefEdit = PreferenceManager.getDefaultSharedPreferences(getApplicationContext())
                                                             .edit();
        prefEdit.putString(Utils.getResString(R.string.csquality), quality.name());
        prefEdit.commit();

        // Show toast to bottom of screen
        UiUtils.showTextToastAtBottom(this, R.string.msg_post_changing_video_quality, false);
        mMp.restartFromCurrentPosition();
    }

    private void
    changeVideoQuality(View anchor) {
        String ytvid = mMp.getActiveVideoYtId();
        if (null == ytvid)
            return;

        YTHacker hack = RTState.get().getCachedYtHacker(ytvid);
        final ArrayList<Integer> opts = new ArrayList<Integer>();
        int i = 0;
        for (Utils.PrefQuality q : Utils.PrefQuality.values()) {
            if (mVQuality != q
                && null != hack
                && null != hack.getVideo(YTPlayer.mapPrefToQScore(q), true))
                opts.add(q.getText());
        }

        final CharSequence[] items = new CharSequence[opts.size()];
        for (i = 0; i < items.length; i++)
            items[i] = getResources().getText(opts.get(i));

        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle(R.string.set_video_quality);
        builder.setItems(items, new DialogInterface.OnClickListener() {
            @Override
            public void
            onClick(DialogInterface dialog, int item) {
                doChangeVideoQuality(Utils.PrefQuality.getMatchingQuality(opts.get(item)));
            }
        });
        builder.create().show();

    }

    // ========================================================================
    //
    // Overriding 'YTPlayer.VideosStateListener'
    //
    // ========================================================================
    @Override
    public void
    onStarted() {
    }

    @Override
    public void
    onStopped(StopState state) {
        getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        hideLoadingSpinProgress();
        finish();
    }

    @Override
    public void
    onChanged() {

    }

    // ========================================================================
    //
    // Overriding 'YTPlayer.PlayerStateListener'
    //
    // ========================================================================
    @Override
    public void
    onStateChanged(YTPlayer.MPState from, int fromFlag,
                   YTPlayer.MPState to,   int toFlag) {
        switch (to) {
        case IDLE:
            mVQuality = Utils.getPrefQuality();
            showLoadingSpinProgress();
            break;

        case PREPARED:
            fitVideoSurfaceToScreen(Orientation.SYSTEM);
            // missing break is intentional.
        case STARTED:
        case PAUSED:
        case STOPPED:
        case ERROR:
            if (mMp.isPlayerSeeking(toFlag)
                || mMp.isPlayerBuffering(toFlag))
                showLoadingSpinProgress();
            else
                hideLoadingSpinProgress();
            break;

        default:
            ; // ignore it.
        }
    }

    @Override
    public void
    onBufferingChanged(int percent) {
    }

    // ========================================================================
    //
    // Overriding 'Activity'
    //
    // ========================================================================
    @Override
    public String
    dump(UnexpectedExceptionHandler.DumpLevel lvl) {
        return this.getClass().getName();
    }

    @Override
    public void
    onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        UnexpectedExceptionHandler.get().registerModule(this);

        setContentView(R.layout.videoplayer);
        mSurfv = (SurfaceView)findViewById(R.id.surface);
        mMp.setSurfaceHolder(mSurfv.getHolder());
        findViewById(R.id.touch_ground).setOnClickListener(new View.OnClickListener() {
            @Override
            public void
            onClick(View v) {
                if (DBG) P.v("touch_ground : On Click");
                updateUserInterfaceVisibility(!isUserInterfaceVisible());
            }
        });

        if (mMp.hasActiveVideo())
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

        if (sNavUiCanBeHidden)
            setOnSystemUiVisibilityChangeListener();

        mMp.addPlayerStateListener(this, this);
        mMp.addVideosStateListener(this, this);
    }

    @Override
    protected void
    onResume() {
        super.onResume();

        if (!mMp.hasActiveVideo()) {
            // There is no video to play.
            // So, exit from video player
            // (Video player doens't have any interface for starting new videos)
            finish();
            return;
        }

        setController(true);
        // This is for workaround SlidingDrawer bug of Android Widget.
        //
        // Even if Visibility of SlidingDrawer is set to "GONE" here or "layout xml"
        //   'handler' of SlidingDrawer is still shown!!
        // Without below code, handler View of SlidingDrawer is always shown
        //   even if after 'hideController()' is called.
        // Step for issues.
        // - enter this activity by viewing video
        // - touching outside controller to hide controller.
        // - turn off backlight by pushing power key
        // - turn on backlight again and this activity is resumed.
        // ==> Handler View of SlidingDrawer is shown.
        //
        // To workaround above issue, visibility of user-interface is set at onWindowFocusChanged.
        mDelayedSetIfVisibility = true;
    }

    @Override
    public void
    onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        if (hasFocus) {
            // At this moment, status bar exists.
            // So, height of status bar can be get.
            // See comments in fitVideoSurfaceToScreen() for details.
            if (0 == mStatusBarHeight) // if valid height is not gotten yet..
                mStatusBarHeight = Utils.getStatusBarHeight(this);

            if (mDelayedSetIfVisibility) {
                mDelayedSetIfVisibility = false;
                // At first, full screen mode is used.
                updateUserInterfaceVisibility(false);
            }
        }
    }

    @Override
    protected void
    onPause() {
        mMp.detachVideoSurface(mSurfv.getHolder());
        mMp.unsetController(this);
        super.onPause();
    }

    @Override
    protected void
    onStop() {
        super.onStop();
    }

    @Override
    protected void
    onDestroy() {
        mMp.unsetSurfaceHolder(mSurfv.getHolder());
        mMp.removePlayerStateListener(this);
        mMp.removeVideosStateListener(this);
        UnexpectedExceptionHandler.get().unregisterModule(this);
        super.onDestroy();
    }

    @Override
    public void
    onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        switch (newConfig.orientation) {
        case Configuration.ORIENTATION_LANDSCAPE:
            fitVideoSurfaceToScreen(Orientation.LANDSCAPE);
            break;

        case Configuration.ORIENTATION_PORTRAIT:
            fitVideoSurfaceToScreen(Orientation.PORTRAIT);
            break;
        }
    }

    @Override
    public void
    onBackPressed() {
        super.onBackPressed();
    }
}




Java Source Code List

free.yhc.netmbuddy.DiagAsyncTask.java
free.yhc.netmbuddy.Err.java
free.yhc.netmbuddy.FragmentPagerAdapterEx.java
free.yhc.netmbuddy.ImportShareActivity.java
free.yhc.netmbuddy.ImportShareContentActivity.java
free.yhc.netmbuddy.ImportShareFileActivity.java
free.yhc.netmbuddy.LockScreenActivity.java
free.yhc.netmbuddy.MusicsActivity.java
free.yhc.netmbuddy.MusicsAdapter.java
free.yhc.netmbuddy.PlaylistActivity.java
free.yhc.netmbuddy.PlaylistAdapter.java
free.yhc.netmbuddy.VideoPlayerActivity.java
free.yhc.netmbuddy.YTMPActivity.java
free.yhc.netmbuddy.YTMPApp.java
free.yhc.netmbuddy.YTMPPreferenceActivity.java
free.yhc.netmbuddy.YTPlaylistSearchActivity.java
free.yhc.netmbuddy.YTPlaylistSearchAdapter.java
free.yhc.netmbuddy.YTPlaylistSearchFragment.java
free.yhc.netmbuddy.YTSearchActivity.java
free.yhc.netmbuddy.YTSearchAdapter.java
free.yhc.netmbuddy.YTSearchFragment.java
free.yhc.netmbuddy.YTSearchPagerAdapter.java
free.yhc.netmbuddy.YTVideoSearchActivity.java
free.yhc.netmbuddy.YTVideoSearchAdapter.java
free.yhc.netmbuddy.YTVideoSearchAuthorActivity.java
free.yhc.netmbuddy.YTVideoSearchFragment.java
free.yhc.netmbuddy.YTVideoSearchKeywordActivity.java
free.yhc.netmbuddy.YTVideoSearchPlaylistActivity.java
free.yhc.netmbuddy.db.ColPlaylist.java
free.yhc.netmbuddy.db.ColVideoRef.java
free.yhc.netmbuddy.db.ColVideo.java
free.yhc.netmbuddy.db.DBHelper.java
free.yhc.netmbuddy.db.DBHistory.java
free.yhc.netmbuddy.db.DBManager.java
free.yhc.netmbuddy.db.DBUpgrader.java
free.yhc.netmbuddy.db.DBUtils.java
free.yhc.netmbuddy.db.DB.java
free.yhc.netmbuddy.model.AtomicFloat.java
free.yhc.netmbuddy.model.BGTask.java
free.yhc.netmbuddy.model.HttpUtils.java
free.yhc.netmbuddy.model.KBLinkedList.java
free.yhc.netmbuddy.model.MultiThreadRunner.java
free.yhc.netmbuddy.model.NetLoader.java
free.yhc.netmbuddy.model.NotiManager.java
free.yhc.netmbuddy.model.Policy.java
free.yhc.netmbuddy.model.RTState.java
free.yhc.netmbuddy.model.SearchSuggestionProvider.java
free.yhc.netmbuddy.model.UnexpectedExceptionHandler.java
free.yhc.netmbuddy.model.YTConstants.java
free.yhc.netmbuddy.model.YTDownloader.java
free.yhc.netmbuddy.model.YTFeed.java
free.yhc.netmbuddy.model.YTHacker.java
free.yhc.netmbuddy.model.YTPlayerLifeSupportService.java
free.yhc.netmbuddy.model.YTPlayerUI.java
free.yhc.netmbuddy.model.YTPlayerVideoListAdapter.java
free.yhc.netmbuddy.model.YTPlayerVideoListManager.java
free.yhc.netmbuddy.model.YTPlayer.java
free.yhc.netmbuddy.model.YTPlaylistFeed.java
free.yhc.netmbuddy.model.YTSearchHelper.java
free.yhc.netmbuddy.model.YTVideoFeed.java
free.yhc.netmbuddy.scmp.DNLoop.java
free.yhc.netmbuddy.scmp.SCElemChar.java
free.yhc.netmbuddy.scmp.SCElemI.java
free.yhc.netmbuddy.scmp.SCElemSStr.java
free.yhc.netmbuddy.scmp.SCElemSTok.java
free.yhc.netmbuddy.scmp.SCElemTok.java
free.yhc.netmbuddy.scmp.SCElem.java
free.yhc.netmbuddy.scmp.SCmpPolicy.java
free.yhc.netmbuddy.scmp.SCmp.java
free.yhc.netmbuddy.share.ExporterPlaylist.java
free.yhc.netmbuddy.share.ImporterPlaylist.java
free.yhc.netmbuddy.share.Importer.java
free.yhc.netmbuddy.share.Json.java
free.yhc.netmbuddy.share.Share.java
free.yhc.netmbuddy.utils.BookmarkListAdapter.java
free.yhc.netmbuddy.utils.FileUtils.java
free.yhc.netmbuddy.utils.ImageUtils.java
free.yhc.netmbuddy.utils.ReportUtils.java
free.yhc.netmbuddy.utils.SimilarTitlesListAdapter.java
free.yhc.netmbuddy.utils.UiUtils.java
free.yhc.netmbuddy.utils.Utils.java
free.yhc.netmbuddy.utils.YTUtils.java