Android Open Source - netmbuddy Y T Player U I






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/*from w ww.  j av a 2 s .  com*/
 * 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.model;

import static free.yhc.netmbuddy.utils.Utils.eAssert;

import java.util.Iterator;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.res.Resources;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.SeekBar;
import android.widget.SlidingDrawer;
import android.widget.TextView;
import free.yhc.netmbuddy.DiagAsyncTask;
import free.yhc.netmbuddy.Err;
import free.yhc.netmbuddy.R;
import free.yhc.netmbuddy.db.ColVideo;
import free.yhc.netmbuddy.db.DB;
import free.yhc.netmbuddy.model.YTPlayer.DBUpdateType;
import free.yhc.netmbuddy.model.YTPlayer.Video;
import free.yhc.netmbuddy.utils.UiUtils;
import free.yhc.netmbuddy.utils.Utils;
import free.yhc.netmbuddy.utils.YTUtils;

public class YTPlayerUI implements
OnSharedPreferenceChangeListener {
    private static final boolean DBG = false;
    private static final Utils.Logger P = new Utils.Logger(YTPlayerUI.class);

    private static final int    SEEKBAR_MAX         = 1000;

    private static YTPlayerUI   sInstance = null;
    // ------------------------------------------------------------------------
    //
    // ------------------------------------------------------------------------
    private final Resources         mRes        = Utils.getAppContext().getResources();
    private final DB                mDb         = DB.get();
    private final UpdateProgress    mUpdateProg = new UpdateProgress();
    private final YTPlayer          mMp;
    private final TimeTickReceiver  mTTRcvr     = new TimeTickReceiver();


    // ------------------------------------------------------------------------
    // UI Control.
    // ------------------------------------------------------------------------
    private Activity            mVActivity      = null;
    private KBLinkedList<YTPlayer.OnDBUpdatedListener>    mDbUpdatedListenerl
        = new KBLinkedList<YTPlayer.OnDBUpdatedListener>();
    private LinearLayout        mPlayerv        = null;
    private LinearLayout        mPlayerLDrawer  = null;

    // To support video
    private SurfaceView         mSurfacev       = null;
    // For extra Button
    private YTPlayer.ToolButton mToolBtn        = null;

    public static class TimeTickReceiver extends BroadcastReceiver {
        private YTPlayerUI  _mYtpui = null;

        void
        setYTPlayerUI(YTPlayerUI ytpui) {
            _mYtpui = ytpui;
        }

        @Override
        public void
        onReceive(Context context, Intent intent) {
            // To minimize time spending in 'Broadcast Receiver'.
            Utils.getUiHandler().post(new Runnable() {
                @Override
                public void
                run() {
                    YTPlayer mp = YTPlayer.get();
                    if (null != _mYtpui)
                        _mYtpui.updateStatusAutoStopSet(mp.isAutoStopSet(), mp.getAutoStopTime());
                }
            });
        }
    }

    private enum PlayBtnState {
        START,
        PAUSE,
        STOP,
        // Temporal state to notify that user triggers 'pause'.
        // This state will be overwritten to the other state immediately
        //   at state machine that configures control buttons.
        USER_PAUSE,
    }

    private class UpdateProgress implements Runnable {
        private static final int UPDATE_INTERVAL_MS = 1000;

        private SeekBar     _mSeekbar = null;
        private TextView    _mCurposv = null;
        private TextView    _mMaxposv = null;
        private int         _mLastProgress = -1;
        private int         _mLastSecondaryProgress = -1; // For secondary progress

        private void
        resetProgressView() {
            if (null != _mSeekbar) {
                _mMaxposv.setText(Utils.secsToMinSecText(mMp.playerGetDuration() / 1000));
                update(1, 0);
                updateSecondary(0);
            }
            _mLastProgress = 0;
            _mLastSecondaryProgress = 0;
        }

        int
        getSecondaryProgressPercent() {
            int percent =  _mLastSecondaryProgress * 100 / SEEKBAR_MAX;
            if (percent > 100)
                percent = 100;
            if (percent < 0)
                percent = 0;
            return percent;
        }

        void
        setProgressView(ViewGroup progv) {
            eAssert(Utils.isUiThread());
            eAssert(null != progv.findViewById(R.id.mplayer_progress));
            _mCurposv = (TextView)progv.findViewById(R.id.mplayer_curpos);
            _mMaxposv = (TextView)progv.findViewById(R.id.mplayer_maxpos);
            _mSeekbar = (SeekBar)progv.findViewById(R.id.mplayer_seekbar);
            if (null != _mSeekbar) {
                _mMaxposv.setText(Utils.secsToMinSecText(mMp.playerGetDuration() / 1000));
                update(mMp.playerGetDuration(), _mLastProgress);
                updateSecondary(_mLastSecondaryProgress);
            }
        }

        void
        start() {
            //logI("Progress Start");
            _mMaxposv.setText(Utils.secsToMinSecText(mMp.playerGetDuration() / 1000));
            update(mMp.playerGetDuration(), _mLastProgress);
            updateSecondary(_mLastSecondaryProgress);
            run();
        }

        void
        resume() {
            Utils.getUiHandler().removeCallbacks(this);
            Utils.getUiHandler().postDelayed(this, UPDATE_INTERVAL_MS);
        }

        void
        pause() {
            Utils.getUiHandler().removeCallbacks(this);
        }

        void
        stop() {
            //logI("Progress End");
            Utils.getUiHandler().removeCallbacks(this);
            resetProgressView();
        }

        void
        update(int durms, int curms) {
            // ignore aDuration.
            // Sometimes youtube player returns incorrect duration value!
            if (null != _mSeekbar) {
                int curPv = (durms > 0)? (int)(curms * (long)SEEKBAR_MAX / durms)
                                               : 0;
                _mSeekbar.setProgress(curPv);
                _mCurposv.setText(Utils.secsToMinSecText(curms / 1000));
                _mLastProgress = curPv;
            }
        }

        /**
         * Update secondary progress
         * @param percent
         */
        void
        updateSecondary(int percent) {
            // Update secondary progress
            if (null != _mSeekbar) {
                int pv = percent * SEEKBAR_MAX / 100;
                _mSeekbar.setSecondaryProgress(pv);
                _mLastSecondaryProgress = pv;
            }
        }

        @Override
        public void
        run() {
            update(mMp.playerGetDuration(), mMp.playerGetPosition());
            Utils.getUiHandler().postDelayed(this, UPDATE_INTERVAL_MS);
        }
    }

    // ========================================================================
    //
    //
    //
    // ========================================================================
    private void
    registerTimeTickReceiver() {
        if (null == mVActivity)
            return;

        mTTRcvr.setYTPlayerUI(this);
        IntentFilter filter = new IntentFilter(Intent.ACTION_TIME_TICK);
        mVActivity.registerReceiver(mTTRcvr, filter);
    }

    private void
    unregisterTimeTickReceiver() {
        if (null == mVActivity)
            return;

        mVActivity.unregisterReceiver(mTTRcvr);
    }

    // ========================================================================
    //
    // Notification Handling
    //
    // ========================================================================
    private void
    notiConfigure(YTPlayer.MPState from, int fromFlag,
                  YTPlayer.MPState to,   int toFlag) {
        NotiManager nm = NotiManager.get();
        if (!mMp.hasActiveVideo()) {
            nm.removePlayerNotification();
            return;
        }
        String title = mMp.getActiveVideo().title;

        NotiManager.NotiType ntype = NotiManager.NotiType.BASE;
        switch (to) {
        case PREPARED:
        case PAUSED:
            ntype = NotiManager.NotiType.START;
            break;

        case STARTED:
            ntype = NotiManager.NotiType.PAUSE;
            break;

        case ERROR:
            ntype = NotiManager.NotiType.ALERT;
            break;

        case IDLE:
        case INITIALIZED:
        case PREPARED_AUDIO:
        case PREPARING:
        case PLAYBACK_COMPLETED:
            ntype = NotiManager.NotiType.STOP;
            break;

        default:
            ntype = NotiManager.NotiType.BASE;
        }
        nm.putPlayerNotification(ntype, title);
    }
    // ========================================================================
    //
    // Player View Handling
    //
    // ========================================================================
    private void
    pvDisableButton(ImageView btn) {
        btn.setVisibility(View.INVISIBLE);
    }

    private void
    pvEnableButton(ImageView btn, int image) {
        if (0 != image)
            btn.setImageResource(image);
        btn.setVisibility(View.VISIBLE);
    }

    private void
    pvSetTitle(TextView titlev, CharSequence title) {
        if (null == titlev || null == title)
            return;
        titlev.setText(title);
    }

    private void
    pvConfigureTitle(TextView titlev,
                     YTPlayer.MPState from, int fromFlag,
                     YTPlayer.MPState to,   int toFlag) {
        if (null == titlev)
            return;

        CharSequence videoTitle = "";
        if (mMp.hasActiveVideo())
            videoTitle = mMp.getActiveVideo().title;

        switch (to) {
        case PREPARED:
        case PAUSED:
        case STARTED:
            eAssert(null != videoTitle);
            if (mMp.isPlayerBuffering(toFlag)
                || mMp.isPlayerSeeking(toFlag))
                videoTitle = "(" + mRes.getText(R.string.buffering) + ") " + videoTitle;

            if (null != videoTitle)
                pvSetTitle(titlev, videoTitle);
            break;

        case ERROR:
            pvSetTitle(titlev, mRes.getText(R.string.msg_ytplayer_err));
            break;

        default:
            if (Utils.isValidValue(videoTitle))
                pvSetTitle(titlev, "(" + mRes.getText(R.string.preparing) + ") " + videoTitle);
            else
                pvSetTitle(titlev, "");
        }
    }


    private void
    pvDisableControlButton(ViewGroup playerv) {
        pvDisableButton((ImageView)playerv.findViewById(R.id.mplayer_btnplay));
        pvDisableButton((ImageView)playerv.findViewById(R.id.mplayer_btnnext));
        pvDisableButton((ImageView)playerv.findViewById(R.id.mplayer_btnprev));
        pvDisableButton((ImageView)playerv.findViewById(R.id.mplayer_btnmore));
        pvDisableButton((ImageView)playerv.findViewById(R.id.mplayer_btntool));
    }

    private void
    pvConfigureControl(ViewGroup controlv,
                       YTPlayer.MPState from, int fromFlag,
                       YTPlayer.MPState to,   int toFlag) {
        if (null == controlv)
            return;

        if (!mMp.hasActiveVideo()) {
            controlv.setVisibility(View.GONE);
            return;
        }


        controlv.setVisibility(View.VISIBLE);
        final ImageView nextv = (ImageView)controlv.findViewById(R.id.mplayer_btnnext);
        final ImageView prevv = (ImageView)controlv.findViewById(R.id.mplayer_btnprev);
        final ImageView playv = (ImageView)controlv.findViewById(R.id.mplayer_btnplay);
        final ImageView morev = (ImageView)controlv.findViewById(R.id.mplayer_btnmore);
        final ImageView toolv = (ImageView)controlv.findViewById(R.id.mplayer_btntool);

        // --------------------------------------------------------------------
        // configure prev/next/more
        // --------------------------------------------------------------------
        switch (to) {
        case PREPARED_AUDIO:
        case PREPARED:
        case PAUSED:
        case STARTED:
            pvEnableButton(morev, 0);
            // break is missed intentionally.
        case IDLE:
        case INITIALIZED:
        case PREPARING:
            if (mMp.hasNextVideo())
                pvEnableButton(nextv, 0);
            else
                pvDisableButton(nextv);

            if (mMp.hasPrevVideo())
                pvEnableButton(prevv, 0);
            else
                pvDisableButton(prevv);
            break;

        default:
            pvDisableButton(morev);
            pvDisableButton(prevv);
            pvDisableButton(nextv);
        }

        // --------------------------------------------------------------------
        // configure play/pause
        // --------------------------------------------------------------------
        switch (to) {
        case PREPARED:
        case PAUSED: {
            PlayBtnState st = (PlayBtnState)playv.getTag();
            if (PlayBtnState.USER_PAUSE == st) {
                pvEnableButton(playv, R.drawable.ic_media_stop);
                playv.setTag(PlayBtnState.STOP);

                // [ Implementing Usecase]
                // - Single touch while playing video pauses video.
                // - Double-touch while playing video stops videos.
                final long sessionId = mMp.getPlayerSessionId();
                final Activity activity = mVActivity;
                Utils.getUiHandler().postDelayed(new Runnable() {
                    @Override
                    public void
                    run() {
                        // Strict check to know that player UI is still at the same screen and state
                        //   with the moment when user-pause is triggered.
                        if (mMp.getPlayerSessionId() == sessionId
                            && YTPlayer.MPState.PAUSED == mMp.getPlayerState()
                            && PlayBtnState.STOP == (PlayBtnState)playv.getTag()
                            && activity == mVActivity) {
                            playv.setImageResource(R.drawable.ic_media_play);
                            playv.setTag(PlayBtnState.START);
                        }
                    }
                }, Policy.YTPLAYER_DOUBLE_TOUCH_INTERVAL);

            } else {
                pvEnableButton(playv, R.drawable.ic_media_play);
                playv.setTag(PlayBtnState.START);
            }
        } break;

        case STARTED:
            pvEnableButton(playv, R.drawable.ic_media_pause);
            // Set next state be moved to on click as 'Tag'
            playv.setTag(PlayBtnState.PAUSE);
            break;

        case IDLE:
        case INITIALIZED:
        case PREPARED_AUDIO:
        case PREPARING:
        case PLAYBACK_COMPLETED:
            pvEnableButton(playv, R.drawable.ic_media_stop);
            // Set next state be moved to on click as 'Tag'
            playv.setTag(PlayBtnState.STOP);
            break;

        default:
            playv.setTag(null);
            controlv.setVisibility(View.GONE);
        }

        // --------------------------------------------------------------------
        // configure tool
        // --------------------------------------------------------------------
        switch (to) {
        case INVALID:
        case END:
            pvDisableButton(toolv);
        break;

        default:
            if (null != mToolBtn)
                pvEnableButton(toolv, 0);
        }
    }

    private void
    pvConfigureProgress(ViewGroup progressv,
                        YTPlayer.MPState from, int fromFlag,
                        YTPlayer.MPState to,   int toFlag) {

        if (null == progressv)
            return;

        switch (to) {
        case PREPARED:
            mUpdateProg.start();
            break;

        case PLAYBACK_COMPLETED:
            // Workaround of Youtube player.
            // Sometimes Youtube player doesn't update progress 100% before playing is ended.
            // So, update to 100% in force at this ended state.
            mUpdateProg.update(1, 1);
            // Missing 'break' is intentional.

        case STARTED:
            mUpdateProg.resume();
            break;

        case PAUSED:
            mUpdateProg.pause();
            break;

        case INITIALIZED:
        case PREPARING:
        case PREPARED_AUDIO:
            ; // do nothing progress is now under update..
            break;

        default:
            mUpdateProg.stop();
            mUpdateProg.update(1, 0);
        }
    }

    private void
    pvEnableLDrawer(ViewGroup playerLDrawer) {
        if (null == playerLDrawer
            || !mMp.hasActiveVideo())
            return; // nothing to do
        eAssert(null != mVActivity);

        ListView lv = (ListView)playerLDrawer.findViewById(R.id.mplayer_list);
        SlidingDrawer drawer = (SlidingDrawer)playerLDrawer.findViewById(R.id.mplayer_ldrawer);
        playerLDrawer.setVisibility(View.VISIBLE);
        YTPlayerVideoListAdapter adapter = new YTPlayerVideoListAdapter(mVActivity, mMp.getVideoList());
        adapter.setActiveItem(mMp.getActiveVideoIndex());
        lv.setAdapter(adapter);
        drawer.close();
    }

    private void
    pvDisableLDrawer(ViewGroup playerLDrawer) {
        if (null == playerLDrawer
            || View.GONE == playerLDrawer.getVisibility())
            return; // nothing to do
        eAssert(null != mVActivity);
        ListView lv = (ListView)playerLDrawer.findViewById(R.id.mplayer_list);
        lv.setAdapter(null);
        SlidingDrawer drawer = (SlidingDrawer)playerLDrawer.findViewById(R.id.mplayer_ldrawer);
        drawer.close();
        playerLDrawer.setVisibility(View.GONE);
    }

    private void
    pvConfigureLDrawer(ViewGroup playerLDrawer,
                      YTPlayer.MPState from, int fromFlag,
                      YTPlayer.MPState to,   int toFlag) {
        if (null == playerLDrawer)
            return;

        if (!mMp.hasActiveVideo()) {
            pvDisableLDrawer(playerLDrawer);
            return;
        }

        ListView lv = (ListView)playerLDrawer.findViewById(R.id.mplayer_list);
        YTPlayerVideoListAdapter adapter = (YTPlayerVideoListAdapter)lv.getAdapter();
        if (null != adapter
            && mMp.getActiveVideoIndex() != adapter.getActiveItemPos()) {
            adapter.setActiveItem(mMp.getActiveVideoIndex());
        }
    }

    private void
    pvConfigureAll(ViewGroup playerv, ViewGroup playerLDrawer,
                   YTPlayer.MPState from, int fromFlag,
                   YTPlayer.MPState to,   int toFlag) {
        if (null == playerv) {
            eAssert(null == playerLDrawer);
            return; // nothing to do
        }

        pvConfigureTitle((TextView)playerv.findViewById(R.id.mplayer_title),
                          from, fromFlag, to, toFlag);
        pvConfigureProgress((ViewGroup)playerv.findViewById(R.id.mplayer_progress),
                            from, fromFlag, to, toFlag);
        pvConfigureControl((ViewGroup)playerv.findViewById(R.id.mplayer_control),
                           from, fromFlag, to, toFlag);
        pvConfigureLDrawer(playerLDrawer, from, fromFlag, to, toFlag);
    }

    private void
    pvMoreControlDetailInfo(long vid) {
        UiUtils.showVideoDetailInfo(mVActivity, vid);
    }

    private void
    pvMoreControlSetBookmark(final long vid) {
        final int posms = mMp.playerGetPosition();

        if (0 == posms) {
            UiUtils.showTextToast(mVActivity, R.string.msg_fail_set_bookmark);
            return;
        }

        final String title = Utils.getResString(R.string.set_bookmark)
                             + " : "
                             + Utils.secsToMinSecText(posms / 1000)
                             + Utils.getResString(R.string.seconds);

        UiUtils.EditTextAction action = new UiUtils.EditTextAction() {
            @Override
            public void
            prepare(Dialog dialog, EditText edit) { }

            @Override
            public void
            onOk(Dialog dialog, EditText edit) {
                String bmname = edit.getText().toString();
                if (bmname.contains("" + DB.BOOKMARK_DELIMITER)) {
                    String msg = Utils.getResString(R.string.msg_forbidden_characters) + "\n"
                                 + "    " + DB.BOOKMARK_DELIMITER;
                    UiUtils.showTextToast(mVActivity, msg);
                    UiUtils.buildOneLineEditTextDialog(mVActivity,
                                                       title,
                                                       bmname,
                                                       "",
                                                       this)
                           .show();
                } else
                    mDb.addBookmark(vid, bmname, posms);
            }
        };

        UiUtils.buildOneLineEditTextDialogWithHint(mVActivity,
                                                   title,
                                                   R.string.enter_bookmark_name,
                                                   action)
               .show();
    }

    private void
    pvMoreControlAddToWithYtid(final UiUtils.OnPostExecuteListener listener,
                               final Object user,
                               final long plid,
                               final YTPlayer.Video video) {
        final int volume;
        if (mMp.getActiveVideo() == video)
            volume = mMp.playerGetVolume();
        else
            volume = Policy.DEFAULT_VIDEO_VOLUME;

        DiagAsyncTask.Worker worker = new DiagAsyncTask.Worker() {
            @Override
            public void
            onPostExecute(DiagAsyncTask task, Err result) {
                listener.onPostExecute(result, user);
            }

            @Override
            public Err
            doBackgroundWork(DiagAsyncTask task) {
                if (YTUtils.insertVideoToPlaylist(plid,
                                                  video.ytvid,
                                                  video.title,
                                                  video.author,
                                                  video.playtime,
                                                  volume)) {
                    return Err.NO_ERR;
                } else
                    return Err.IO_NET;
            }
        };

        new DiagAsyncTask(mVActivity,
                          worker,
                          DiagAsyncTask.Style.SPIN,
                          Utils.getResString(R.string.adding),
                          true)
            .run();
    }

    private void
    pvMoreControlAddTo(Long vid, final Video video) {
        final UiUtils.OnPostExecuteListener listener = new UiUtils.OnPostExecuteListener() {
            @Override
            public void
            onPostExecute(Err result, Object user) {
                if (Err.NO_ERR != result)
                    return;

                Iterator<YTPlayer.OnDBUpdatedListener> iter = mDbUpdatedListenerl.iterator();
                while (iter.hasNext())
                    iter.next().onDbUpdated(DBUpdateType.PLAYLIST);
            }
        };

        if (null != vid) {
            UiUtils.addVideosTo(mVActivity,
                                null,
                                listener,
                                UiUtils.PLID_INVALID,
                                new long[] { vid },
                                false);
        } else {
            UiUtils.OnPlaylistSelected action = new UiUtils.OnPlaylistSelected() {
                @Override
                public void
                onPlaylist(long plid, Object user) {
                    pvMoreControlAddToWithYtid(listener, user, plid, video);
                }

                @Override
                public void
                onUserMenu(int pos, Object user) {}
            };

            // exclude current playlist
            UiUtils.buildSelectPlaylistDialog(DB.get(),
                                              mVActivity,
                                              R.string.add_to,
                                              null,
                                              action,
                                              UiUtils.PLID_INVALID,
                                              null)
                   .show();
        }
    }

    private void
    pvMoreControlDelete(final Long vid, final String ytvid) {
        final UiUtils.OnPostExecuteListener listener = new UiUtils.OnPostExecuteListener() {
            @Override
            public void
            onPostExecute(Err result, Object user) {
                if (Err.NO_ERR != result)
                    return;

                mMp.removeVideo(ytvid);
                if (null != vid) {
                    Iterator<YTPlayer.OnDBUpdatedListener> iter = mDbUpdatedListenerl.iterator();
                    while (iter.hasNext())
                        iter.next().onDbUpdated(DBUpdateType.PLAYLIST);
                }
            }
        };

        if (null == vid) {
            UiUtils.ConfirmAction action = new UiUtils.ConfirmAction() {
                @Override
                public void
                onOk(Dialog dialog) {
                    listener.onPostExecute(Err.NO_ERR, null);
                }

                @Override
                public void
                onCancel(Dialog dialog) { }
            };
            UiUtils.buildConfirmDialog(mVActivity,
                                       R.string.delete,
                                       R.string.msg_delete_musics_completely,
                                       action)
                   .show();

        } else {
            UiUtils.deleteVideos(mVActivity,
                                 null,
                                 listener,
                                 UiUtils.PLID_UNKNOWN,
                                 new long[] { vid });
        }
    }

    private void
    pvOnMoreButtonClicked(View v) {
        final YTPlayer.Video video  = mMp.getActiveVideo();

        if (null == video)
            return; // This line can be reached because of race-condition...

        final int[] opts;
        final Long vid = (Long)mDb.getVideoInfo(video.ytvid, ColVideo.ID);
        if (null != vid)
            opts = new int[] { R.string.detail_info,
                               R.string.set_bookmark,
                               R.string.bookmarks,
                               R.string.add_to,
                               R.string.volume,
                               R.string.delete };
        else
            opts = new int[] { R.string.add_to,
                               R.string.volume };

        final CharSequence[] items = new CharSequence[opts.length];
        for (int i = 0; i < opts.length; i++)
            items[i] = Utils.getResString(opts[i]);

        AlertDialog.Builder builder = new AlertDialog.Builder(mVActivity);
        builder.setTitle(R.string.player_extra_control_title);
        builder.setItems(items, new DialogInterface.OnClickListener() {
            @Override
            public void
            onClick(DialogInterface dialog, int item) {
                switch (opts[item]) {
                case R.string.detail_info:
                    pvMoreControlDetailInfo(vid);
                    break;

                case R.string.set_bookmark:
                    pvMoreControlSetBookmark(vid);
                    break;

                case R.string.bookmarks:
                    UiUtils.showBookmarkDialog(mVActivity, video.ytvid, video.title);
                    break;

                case R.string.add_to:
                    pvMoreControlAddTo(vid, video);
                    break;

                case R.string.volume:
                    changeVideoVolume(video.title, video.ytvid);
                    break;

                case R.string.delete:
                    pvMoreControlDelete(vid, video.ytvid);
                    break;

                default:
                    eAssert(false);
                }
            }
        });
        builder.create().show();
    }

    private void
    pvSetupControlButton(final ViewGroup playerv) {
        ImageView btn = (ImageView)playerv.findViewById(R.id.mplayer_btnplay);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void
            onClick(View v) {
                // See pvConfigControl() for details.
                PlayBtnState st = (PlayBtnState)v.getTag();
                if (null == st)
                    return; // Nothing to do.

                switch (st) {
                case START:
                    mMp.playerStart();
                    break;

                case PAUSE:
                    v.setTag(PlayBtnState.USER_PAUSE);
                    mMp.playerPause();
                    break;

                case STOP:
                    // This doesn't means "Stop only this video",
                    //   but means stop playing vidoes - previous user request.
                    mMp.stopVideos();
                    break;

                default:
                    ; // do nothing.
                }
            }
        });

        btn = (ImageView)playerv.findViewById(R.id.mplayer_btnprev);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void
            onClick(View v) {
                mMp.startPrevVideo();
            }
        });

        btn = (ImageView)playerv.findViewById(R.id.mplayer_btnnext);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void
            onClick(View v) {
                mMp.startNextVideo();
            }
        });

        btn = (ImageView)playerv.findViewById(R.id.mplayer_btnmore);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void
            onClick(View v) {
                pvOnMoreButtonClicked(v);
            }
        });

        btn = (ImageView)playerv.findViewById(R.id.mplayer_btntool);
        if (null == mToolBtn)
            btn.setVisibility(View.INVISIBLE);
        else {
            btn.setImageResource(mToolBtn.drawable);
            btn.setOnClickListener(mToolBtn.onClick);
        }
    }

    private void
    pvSetupStatusBar(ViewGroup playerv) {
        updateStatusAutoStopSet(mMp.isAutoStopSet(), mMp.getAutoStopTime());
        onSharedPreferenceChanged(Utils.getSharedPreference(), Utils.getResString(R.string.csquality));
        onSharedPreferenceChanged(Utils.getSharedPreference(), Utils.getResString(R.string.csrepeat));
        onSharedPreferenceChanged(Utils.getSharedPreference(), Utils.getResString(R.string.csshuffle));
    }

    private void
    pvInit(ViewGroup playerv, ViewGroup playerLDrawer, SurfaceView surfacev) {
        mMp.addPlayerStateListener(this, new YTPlayer.PlayerStateListener() {
            @Override
            public void
            onStateChanged(YTPlayer.MPState from, int fromFlag,
                           YTPlayer.MPState to,   int toFlag) {
                pvConfigureAll(mPlayerv, mPlayerLDrawer, from, fromFlag, to, toFlag);
                notiConfigure(from, fromFlag, to, toFlag);
            }

            @Override
            public void
            onBufferingChanged(int percent) {
                mUpdateProg.updateSecondary(percent);
            }
        });

        ViewGroup progv = (ViewGroup)playerv.findViewById(R.id.mplayer_progress);
        SeekBar sb = (SeekBar)progv.findViewById(R.id.mplayer_seekbar);
        sb.setMax(SEEKBAR_MAX);
        sb.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void
            onStopTrackingTouch(SeekBar seekBar) {
                mMp.playerSeekTo((int)(seekBar.getProgress() * (long)mMp.playerGetDuration() / SEEKBAR_MAX));
            }

            @Override
            public void
            onStartTrackingTouch(SeekBar seekBar) {
            }

            @Override
            public void
            onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
            }
        });
        mUpdateProg.setProgressView(progv);

        if (null != playerLDrawer) {
            ListView lv = (ListView)playerLDrawer.findViewById(R.id.mplayer_list);
            lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                @Override
                public void
                onItemClick(AdapterView<?> parent, View view, int position, long id) {
                    if (!mMp.hasActiveVideo())
                        return;
                    mMp.startVideoAt(position);
                }
            });

            mMp.addVideosStateListener(this, new YTPlayer.VideosStateListener() {
                @Override
                public void
                onStopped(YTPlayer.StopState state) {
                    boolean      needToNotification = true;
                    CharSequence msg = "";
                    switch (state) {
                    case DONE:
                        needToNotification = false;
                        msg = mRes.getText(R.string.msg_playing_done);
                        break;

                    case FORCE_STOPPED:
                        needToNotification = false;
                        msg = mRes.getText(R.string.msg_playing_stopped);
                        break;

                    case NETWORK_UNAVAILABLE:
                        msg = mRes.getText(R.string.err_network_unavailable);
                        break;

                    case UNKNOWN_ERROR:
                        msg = mRes.getText(R.string.msg_playing_err_unknown);
                        break;
                    }

                    if (null != mPlayerv) {
                        TextView titlev = (TextView)mPlayerv.findViewById(R.id.mplayer_title);
                        pvSetTitle(titlev, mRes.getText(R.string.msg_playing_done));
                    }

                    if (needToNotification)
                        NotiManager.get().putPlayerNotification(NotiManager.NotiType.ALERT, (String)msg);
                }

                @Override
                public void
                onStarted() {
                    if (null != mPlayerv)
                        pvEnableLDrawer(mPlayerLDrawer);
                }

                @Override
                public void
                onChanged() {
                    // Control button should be re-checked due to 'next' and 'prev' button.
                    if (null != mPlayerv)
                        pvConfigureControl((ViewGroup)mPlayerv.findViewById(R.id.mplayer_control),
                                           YTPlayer.MPState.INVALID, YTPlayer.MPSTATE_FLAG_IDLE,
                                           mMp.playerGetState(), mMp.playerGetStateFlag());

                    if (null == mPlayerLDrawer)
                        return;

                    if (mMp.hasActiveVideo()) {
                        ListView lv = (ListView)mPlayerLDrawer.findViewById(R.id.mplayer_list);
                        YTPlayerVideoListAdapter adapter = (YTPlayerVideoListAdapter)lv.getAdapter();
                        if (null != adapter)
                            adapter.setVidArray(mMp.getVideoList());
                    } else
                        pvDisableLDrawer(mPlayerLDrawer);
                }
            });

            final SlidingDrawer drawer = (SlidingDrawer)playerLDrawer.findViewById(R.id.mplayer_ldrawer);
            drawer.setOnDrawerOpenListener(new SlidingDrawer.OnDrawerOpenListener() {
                @Override
                public void
                onDrawerOpened() {
                    if (!mMp.hasActiveVideo()
                        || null == mPlayerLDrawer)
                        return;

                    ListView lv = (ListView)mPlayerLDrawer.findViewById(R.id.mplayer_list);
                    int topPos = mMp.getActiveVideoIndex() - 1;
                    if (topPos < 0)
                        topPos = 0;
                    lv.setSelectionFromTop(topPos, 0);
                }
            });
        }

        // Enable drawer by default.
        // If there is no active video, drawer will be disabled at the configure function.
        pvEnableLDrawer(playerLDrawer);

        pvSetupStatusBar(playerv);
        pvSetupControlButton(playerv);

        // Set progress state to right position.
        mUpdateProg.update(mMp.playerGetDuration(), mMp.playerGetPosition());

        pvConfigureAll(playerv, playerLDrawer,
                       YTPlayer.MPState.INVALID, YTPlayer.MPSTATE_FLAG_IDLE,
                       mMp.playerGetState(), mMp.playerGetStateFlag());
    }

    // ============================================================================
    //
    // Overrides
    //
    // ============================================================================
    @Override
    public void
    onSharedPreferenceChanged(SharedPreferences prefs, String key) {
        if (key.equals(Utils.getResString(R.string.csshuffle))) {
            updateStatusShuffle(Utils.isPrefSuffle());
        } else if (key.equals(Utils.getResString(R.string.csrepeat))) {
            updateStatusRepeat(Utils.isPrefRepeat());
        } else if (key.equals(Utils.getResString(R.string.csquality))) {
            updateStatusQuality(Utils.getPrefQuality());
        }
    }

    // ============================================================================
    //
    // Update status icons
    //
    // ============================================================================
    void
    updateStatusAutoStopSet(boolean set, long timeMillis) {
        if (null == mPlayerv)
            return;

        ImageView iv = (ImageView)mPlayerv.findViewById(R.id.mplayer_status_autostop);
        TextView tv = (TextView)mPlayerv.findViewById(R.id.mplayer_status_autostop_time);

        if (set) {
            long tmLeft = timeMillis - System.currentTimeMillis();
            if (tmLeft < 0)
                tmLeft = 0;
            String tmText = Utils.millisToHourMinText(tmLeft) + Utils.getResString(R.string.minutes);
            iv.setImageResource(R.drawable.status_autostop_on);
            tv.setVisibility(View.VISIBLE);
            tv.setText(tmText);
        } else {
            iv.setImageResource(R.drawable.status_autostop_off);
            tv.setVisibility(View.GONE);
        }
    }

    private void
    updateStatusQuality(Utils.PrefQuality quality) {
        if (null == mPlayerv)
            return;

        TextView tv = (TextView)mPlayerv.findViewById(R.id.mplayer_status_quality);
        CharSequence text = "?";
        switch (quality) {
        case VERYHIGH:  text = "VH";    break;
        case HIGH:      text = "H";     break;
        case NORMAL:    text = "M";     break;
        case MIDLOW:    text = "ML";    break;
        case LOW:       text = "L";     break;
        }
        tv.setText("[" + text + "]");
    }

    private void
    updateStatusShuffle(boolean set) {
        if (null == mPlayerv)
            return;

        ImageView iv = (ImageView)mPlayerv.findViewById(R.id.mplayer_status_shuffle);
        iv.setImageResource(set? R.drawable.status_shuffle_on: R.drawable.status_shuffle_off);
    }

    private void
    updateStatusRepeat(boolean set) {
        if (null == mPlayerv)
            return;

        ImageView iv = (ImageView)mPlayerv.findViewById(R.id.mplayer_status_repeat);
        iv.setImageResource(set? R.drawable.status_repeat_on: R.drawable.status_repeat_off);
    }

    // ============================================================================
    //
    // Package interfaces (for YTPlayer)
    //
    // ============================================================================
    YTPlayerUI(YTPlayer ytplayer) {
        mMp = ytplayer;
        if (null == sInstance)
            sInstance = this;
        else
            eAssert(false); // This SHOULD BE SINGLETON (Only YTPlayer has this!)
    }

    void
    addOnDbUpdatedListener(Object key, YTPlayer.OnDBUpdatedListener listener) {
        mDbUpdatedListenerl.add(key, listener);
    }

    void
    removeOnDbUpdatedListener(Object key) {
        mDbUpdatedListenerl.remove(key);
    }

    void
    setController(Activity  activity,
                  ViewGroup playerv,
                  ViewGroup playerLDrawer,
                  SurfaceView surfacev,
                  YTPlayer.ToolButton toolBtn) {
        if (null != mVActivity
            && activity != mVActivity)
            unsetController(mVActivity);

        Utils.getSharedPreference().registerOnSharedPreferenceChangeListener(this);


        // update notification by force
        notiConfigure(YTPlayer.MPState.INVALID, YTPlayer.MPSTATE_FLAG_IDLE,
                      mMp.playerGetState(), mMp.playerGetStateFlag());

        if (activity == mVActivity && mPlayerv == playerv)
            // controller is already set for this context.
            // So, nothing to do. just return!
            return;

        mVActivity = activity;
        mDbUpdatedListenerl.clear();
        mPlayerv = (LinearLayout)playerv;
        mPlayerLDrawer = (LinearLayout)playerLDrawer;
        mSurfacev = surfacev;
        mToolBtn = toolBtn;

        if (null == mPlayerv) {
            eAssert(null == mPlayerLDrawer);
            return;
        }

        eAssert(null != mPlayerv.findViewById(R.id.mplayer_layout_magic_id));
        registerTimeTickReceiver();
        pvInit(playerv, playerLDrawer, surfacev);
    }

    void
    unsetController(Activity activity) {
        if (activity == mVActivity) {
            Utils.getSharedPreference().unregisterOnSharedPreferenceChangeListener(this);
            unregisterTimeTickReceiver();
            mPlayerv = null;
            mVActivity = null;
            mDbUpdatedListenerl.clear();
            mPlayerLDrawer = null;
            mSurfacev = null;
        }
    }

    Activity
    getActivity() {
        return mVActivity;
    }

    SurfaceView
    getSurfaceView() {
        return mSurfacev;
    }

    void
    notifyToUser(String msg) {
        if (null != mVActivity)
            UiUtils.showTextToast(mVActivity, msg);
    }

    void
    setPlayerVisibility(int visibility) {
        if (null != mPlayerv)
            mPlayerv.setVisibility(visibility);
    }

    void
    updateLDrawerList() {
        if (null == mPlayerLDrawer)
            return;

        ListView lv = (ListView)mPlayerLDrawer.findViewById(R.id.mplayer_list);
        YTPlayer.Video[] vs =  mMp.getVideoList();
        if (null == vs)
            vs = new YTPlayer.Video[0];
        YTPlayerVideoListAdapter adapter = new YTPlayerVideoListAdapter(mVActivity, vs);
        adapter.setActiveItem(mMp.getActiveVideoIndex());
        lv.setAdapter(adapter);
    }

    void
    changeVideoVolume(final String title, final String ytvid) {
        if (null == mVActivity)
            return;

        final boolean runningVideo;
        // Retrieve current volume
        int curvol = Policy.DEFAULT_VIDEO_VOLUME;
        if (mMp.isVideoPlaying()
            && mMp.getActiveVideo().ytvid.equals(ytvid)) {
            runningVideo = true;
            curvol = mMp.playerGetVolume();
        } else {
            runningVideo = false;
            Long i = (Long)mDb.getVideoInfo(ytvid, ColVideo.VOLUME);
            if (null != i)
                curvol = i.intValue();
        }

        ViewGroup diagv = (ViewGroup)UiUtils.inflateLayout(mVActivity, R.layout.mplayer_vol_dialog);
        AlertDialog.Builder bldr = new AlertDialog.Builder(mVActivity);
        bldr.setView(diagv);
        bldr.setTitle(Utils.getAppContext().getResources().getText(R.string.volume)
                      + " : " + title);
        final AlertDialog aDiag = bldr.create();

        final SeekBar sbar = (SeekBar)diagv.findViewById(R.id.seekbar);
        sbar.setMax(100);
        sbar.setProgress(curvol);
        sbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void
            onStopTrackingTouch(SeekBar seekBar) { }
            @Override
            public void
            onStartTrackingTouch(SeekBar seekBar) { }
            @Override
            public void
            onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                if (runningVideo)
                    mMp.playerSetVolume(progress);
            }
        });

        final int oldVolume = curvol;
        aDiag.setOnDismissListener(new DialogInterface.OnDismissListener() {
            @Override
            public void
            onDismiss(DialogInterface dialog) {
                int newVolume = sbar.getProgress();
                if (oldVolume == newVolume)
                    return;
                // Save to database and update adapter
                // NOTE
                // Should I consider about performance?
                // Not yet. do something when performance is issued.
                mDb.updateVideoVolume(ytvid, newVolume);
            }
        });
        aDiag.show();
    }

}




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