Android 视频回放时间轴,支持缩放,拖动,自动移动到最近视频开始时间

来源:互联网 发布:北京旅游酒店推荐知乎 编辑:程序博客网 时间:2024/06/04 07:33






package com.lancens.wxdoorbell.widget;
import android.content.Context;import android.graphics.Canvas;import android.graphics.Paint;import android.graphics.Rect;import android.graphics.RectF;import android.os.Handler;import android.os.Message;import android.support.annotation.Nullable;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.VelocityTracker;import android.view.View;import android.view.ViewConfiguration;import android.widget.Scroller;import java.io.Serializable;import java.text.SimpleDateFormat;import java.util.ArrayList;import java.util.Calendar;import java.util.Date;import java.util.List;import java.util.Locale;/** * Created by WuJinLin on 2017/7/7. */public class PlaybackView extends View {    private static String TAG = "PlaybackView>>";    private Paint mPaint;    private static final int SCALE_COLOR = 0xFF7C7C7C;    private static final int VIDEO_COLOR = 0xFFffab8a;    private static final int MID_COLOR = 0xFF039BE5;    private static final int MID_BACKGROUND_COLOR = 0x557C7C7C;    private int VIEW_BACKGROUND_COLOR = 0x00000000;    private static final float TWO_MIN_SCALE_HEIGHT = 5;    private static final float TEN_MIN_SCALE_HEIGHT = 10;    private static final float HOUR_SCALE_HEIGHT = 15;    private float widthTimes = 1.5f;    private List<PlaybackVo> videos = new ArrayList<>();    private float SECOND_WIDTH = 30;//px    private float MIN_SECOND_WIDTH = 5;    private float MAX_SECOND_WIDTH = 30;    private static final int TWO_MINUTE_SCALE_INTERVAL = 120;    private static final int TEN_MINUTE_SCALE_INTERVAL = 600;    private static final int TWENTY_MINUTE_SCALE_INTERVAL = 1200;    private static final int ONE_HOUR_SCALE_INTERVAL = 3600;    private static final int TWO_HOUR_SCALE_INTERVAL = 7200;    private static final int SCALE_TEXT_SIZE = 12;    private int midBackgroundWidth = 5;//dp    private float mWidth, mHeight;    private float mDensity;// 屏幕密度 dp*mDensity=px    private Scroller mScroller;    private VelocityTracker mVelocityTracker;    private int mMinVelocity;    private TimeAlgorithm mValue;// 当前刻度指示时间    private TimeAlgorithm selectValue;//指定移动时间    public PlaybackView(Context context) {        this(context, null);    }    public PlaybackView(Context context, @Nullable AttributeSet attrs) {        this(context, attrs, 0);    }    public PlaybackView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        init(context);    }    private void init(Context context) {        mScroller = new Scroller(context);        mVelocityTracker = VelocityTracker.obtain();        mDensity = context.getResources().getDisplayMetrics().density;        mPaint = new Paint();        mPaint.setColor(SCALE_COLOR);        mPaint.setAntiAlias(true);        mPaint.setDither(true);        mValue = new TimeAlgorithm(System.currentTimeMillis() * 1000);        mMinVelocity = ViewConfiguration.get(context).getScaledMinimumFlingVelocity();        //   Log.d(TAG, "init: " + mDensity);    }    public void moveToValue(long micros) {        if (micros == mValue.getTimeInMicros()) return;        Message msg = Message.obtain();        msg.what = SET_VALUE;        selectValue = new TimeAlgorithm(micros);        if (isCurrentDay(mValue, selectValue)) {            moveX = (selectValue.getTimeInSecond() - mValue.getTimeInSecond()) * SECOND_WIDTH / SET_VALUE_AUTO_MOVE_COUNT;            if (Math.abs(moveX) < SECOND_WIDTH && moveX != 0) {                moveX = moveX * SECOND_WIDTH / moveX;            }            //    Log.d(TAG, "moveToValue: " + moveX + ">>" + SECOND_WIDTH);            handler.sendMessage(msg);        }    }    public void setValue(long micros) {        removeAllMessage();        mValue = new TimeAlgorithm(micros);        postInvalidate();        if (listener != null) {            listener.onValueChanged(mValue.getStringTimeInSecond(), mValue.getTimeInMicros());        }        handler.sendEmptyMessageDelayed(DELAY_MOVE_TO_NEARBY_VIDEO, delayMoveToNearbyVideoMs);    }    private void removeAllMessage() {        handler.removeMessages(DELAY_MOVE_TO_NEARBY_VIDEO);        handler.removeMessages(SET_VALUE);    }    public void setViewBackgroundColor(int color) {        this.VIEW_BACKGROUND_COLOR = color;    }    public void setVideos(List<PlaybackVo> videos) {        if (videos == null || videos.size() == 0) return;        this.videos = videos;        moveToNearbyVideo();    }    @Override    protected void onSizeChanged(int w, int h, int oldw, int oldh) {        // Log.d(TAG, "onSizeChanged: ");        mWidth = w;        mHeight = h;        MAX_SECOND_WIDTH = mWidth / 1800;        MIN_SECOND_WIDTH = mWidth / (60 * 60 * 24);        SECOND_WIDTH = MAX_SECOND_WIDTH;        super.onSizeChanged(w, h, oldw, oldh);    }    @Override    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {        // Log.d(TAG, "onLayout:  " + changed + ">>left=" + left + ">>top=" + top + ">>right=" + right + ">>bottom=" + bottom);        super.onLayout(changed, left, top, right, bottom);    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        //  Log.d(TAG, "onDraw: ");    }    private int SET_VALUE_AUTO_MOVE_TIME = 100;//ms    private int SET_VALUE_AUTO_MOVE_COUNT = 10;//times    private float moveX = 0;    private static final int SET_VALUE = 0;    private static final int DELAY_MOVE_TO_NEARBY_VIDEO = 1;    private int delayMoveToNearbyVideoMs = 1000;//ms    private Handler handler = new Handler() {        @Override        public void handleMessage(Message msg) {            super.handleMessage(msg);            if (hasMessages(DELAY_MOVE_TO_NEARBY_VIDEO)) {                removeMessages(DELAY_MOVE_TO_NEARBY_VIDEO);            }            switch (msg.what) {                case SET_VALUE:                    autoMoving();                    break;                case DELAY_MOVE_TO_NEARBY_VIDEO:                    moveToNearbyVideo();                    break;            }        }    };    private void autoMoving() {        TimeAlgorithm value = mValue.addOrSub((int) (moveX / SECOND_WIDTH));        if (moveX > 0) {            if (value.getTimeInMicros() > selectValue.getTimeInMicros()) {                mValue = new TimeAlgorithm(selectValue.getTimeInMicros());                // Log.d(TAG, "autoMoving: >");                invalidate();                handler.removeMessages(SET_VALUE);            }        } else if (moveX < 0) {            if (value.getTimeInMicros() < selectValue.getTimeInMicros()) {                mValue = new TimeAlgorithm(selectValue.getTimeInMicros());                invalidate();                // Log.d(TAG, "autoMoving: <");                handler.removeMessages(SET_VALUE);            }        }        if (mValue.getTimeInMicros() != selectValue.getTimeInMicros()) {            moveX(moveX);            handler.sendEmptyMessageDelayed(SET_VALUE, SET_VALUE_AUTO_MOVE_TIME / SET_VALUE_AUTO_MOVE_COUNT);        } else {            if (listener != null) {                listener.onVideoStart(mValue.getStringTimeInSecond(), mValue.getTimeInMicros());            }        }    }    @Override    protected void dispatchDraw(Canvas canvas) {        super.dispatchDraw(canvas);        // Log.d(TAG, "dispatchDraw: ");        // canvas.drawColor(VIEW_BACKGROUND_COLOR);        drawVideos(canvas);        drawScale(canvas);        drawMidLine(canvas);    }    private int VIDEO_PADDING = 1;    private void drawVideos(Canvas canvas) {        if (videos == null || videos.size() == 0) return;        canvas.save();        for (int i = 0; i < videos.size(); i++) {            PlaybackVo vo = videos.get(i);            float startX = mWidth / 2 + (vo.getStartTA().getTimeInSecond() - mValue.getTimeInSecond()) * SECOND_WIDTH;            float endX = mWidth / 2 + (vo.getEndTA().getTimeInSecond() - mValue.getTimeInSecond()) * SECOND_WIDTH;            if (endX < 0 || startX > mWidth) continue;            RectF rectF = new RectF(startX, VIDEO_PADDING, endX, mHeight - VIDEO_PADDING);            mPaint.setColor(VIDEO_COLOR);            mPaint.setStyle(Paint.Style.FILL);            canvas.drawRect(rectF, mPaint);        }        canvas.restore();    }    private void drawScale(Canvas canvas) {        canvas.save();        mPaint.setColor(SCALE_COLOR);        mPaint.setStrokeWidth(mDensity);        int scaleMode = getScaleMode();        int mod = mValue.mod(scaleMode);        float drawWidth = 0;        for (int i = 0; drawWidth < mWidth * 1.1; i++) {            float height = TWO_MIN_SCALE_HEIGHT;            float x = 0;            int mode = 0;            x = mWidth / 2 + (scaleMode - mod + scaleMode * i) * SECOND_WIDTH;            TimeAlgorithm rightTime = mValue.addOrSub(scaleMode - mod + scaleMode * i);            String time = rightTime.getStringTimeInMinute();            Rect rect = new Rect();//            Log.d(TAG, "isCurrentDayContainZero: " + rightTime.getStringTimeInSecond() + ">>" + scaleMode//                    + ">>" + isCurrentDayContainZero(mValue, rightTime) + ">>" + isDrawScaleLine(rightTime)//                    + ">>" + mValue.getTimeInMillis() + ">>" + mValue.getCurrentMaxTimeInMillis() + ">>" + mValue.getCurrentMinTimeInMillis() + ">>" + rightTime.getTimeInMillis());            if (isCurrentDayContainZero(mValue, rightTime)) {                mode = rightTime.getScaleMode();                height = getScaleHeight(rightTime) * mDensity;                if (isDrawScaleLine(rightTime)) {                    canvas.drawLine(x, 0, x, height, mPaint);                    canvas.drawLine(x, mHeight, x, mHeight - height, mPaint);                }                mPaint.setTextSize(SCALE_TEXT_SIZE * mDensity);                mPaint.getTextBounds(time, 0, time.length(), rect);                if (isDrawText(rightTime, rect.width() * widthTimes)) {                    if (time.equals("00:00")) time = "24:00";                    canvas.drawText(time, x - rect.width() / 2, height + rect.height() + 2 * mDensity, mPaint);                }            }            x = mWidth / 2 - (mod + scaleMode * i) * SECOND_WIDTH;            TimeAlgorithm leftTime = mValue.addOrSub(-(mod + scaleMode * i));            height = getScaleHeight(leftTime) * mDensity;            if (isCurrentDayContainZero(mValue, leftTime)) {                if (isDrawScaleLine(leftTime)) {                    canvas.drawLine(x, 0, x, height, mPaint);                    canvas.drawLine(x, mHeight, x, mHeight - height, mPaint);                }                time = leftTime.getStringTimeInMinute();                mPaint.getTextBounds(time, 0, time.length(), rect);                mode = leftTime.getScaleMode();                if (isDrawText(leftTime, rect.width() * widthTimes)) {                    canvas.drawText(time, x - rect.width() / 2, height + rect.height() + 2 * mDensity, mPaint);                }            }            drawWidth += 2 * scaleMode * SECOND_WIDTH;        }        mPaint.setStrokeWidth(1.5f * mDensity);        canvas.drawLine(0, 0, mWidth, 0, mPaint);        canvas.drawLine(0, mHeight, mWidth, mHeight, mPaint);        canvas.restore();    }    private void drawMidLine(Canvas canvas) {        canvas.save();        //  Log.d(TAG, "drawMidLine: width=" + mWidth + ">>height=" + mHeight);        mPaint.setColor(MID_BACKGROUND_COLOR);        mPaint.setStyle(Paint.Style.FILL);        RectF rectF = new RectF((mWidth - midBackgroundWidth * mDensity) / 2, 0, (mWidth + midBackgroundWidth * mDensity) / 2, mHeight);        canvas.drawRect(rectF, mPaint);        mPaint.setStrokeWidth(1.5f * mDensity);        mPaint.setColor(MID_COLOR);        canvas.drawLine(mWidth / 2, 0, mWidth / 2, mHeight, mPaint);        canvas.restore();        // Log.d(TAG, "drawMidLine: " + mValue.getStringTimeInSecond());    }    private boolean isCurrentDayContainZero(TimeAlgorithm current, TimeAlgorithm compare) {        boolean isCurrentDay = true;        if (current.getCurrentMaxTimeInMillis() / 1000 + 1 < compare.getTimeInMillis() / 1000) {            isCurrentDay = false;        } else if (current.getCurrentMinTimeInMillis() > compare.getTimeInMillis()) {            isCurrentDay = false;        }        return isCurrentDay;    }    private boolean isCurrentDay(TimeAlgorithm current, TimeAlgorithm compare) {        boolean isCurrentDay = true;        if (current.getCurrentMaxTimeInMillis() / 1000 < compare.getTimeInMillis() / 1000) {            isCurrentDay = false;        } else if (current.getCurrentMinTimeInMillis() > compare.getTimeInMillis()) {            isCurrentDay = false;        }        return isCurrentDay;    }    private int getScaleMode() {        int mode = TWO_MINUTE_SCALE_INTERVAL;        if (ONE_HOUR_SCALE_INTERVAL * SECOND_WIDTH <= TWO_MINUTE_SCALE_INTERVAL * MAX_SECOND_WIDTH) {            mode = ONE_HOUR_SCALE_INTERVAL;        } else if (TWENTY_MINUTE_SCALE_INTERVAL * SECOND_WIDTH <= TWO_MINUTE_SCALE_INTERVAL * MAX_SECOND_WIDTH) {            mode = TWENTY_MINUTE_SCALE_INTERVAL;        } else if (TEN_MINUTE_SCALE_INTERVAL * SECOND_WIDTH <= 2 * TWO_MINUTE_SCALE_INTERVAL * MAX_SECOND_WIDTH) {            mode = TEN_MINUTE_SCALE_INTERVAL;        }        return mode;    }    private boolean isDrawScaleLine(TimeAlgorithm time) {        boolean draw = false;        switch (time.getScaleMode()) {            case TimeAlgorithm.MODE_TWO_MINUTE:                draw = TEN_MINUTE_SCALE_INTERVAL * SECOND_WIDTH > TWO_MINUTE_SCALE_INTERVAL * MAX_SECOND_WIDTH * 2;                break;            case TimeAlgorithm.MODE_TEN_MINUTE:                draw = (time.isTwentyMinuteMultiple() && ONE_HOUR_SCALE_INTERVAL * SECOND_WIDTH > 2 * TWO_MINUTE_SCALE_INTERVAL * MAX_SECOND_WIDTH)                        || (!time.isTwentyMinuteMultiple() && TWENTY_MINUTE_SCALE_INTERVAL * SECOND_WIDTH > 1.5 * TWO_MINUTE_SCALE_INTERVAL * MAX_SECOND_WIDTH);                break;            case TimeAlgorithm.MODE_HOUR:                draw = ONE_HOUR_SCALE_INTERVAL * SECOND_WIDTH > TWO_MINUTE_SCALE_INTERVAL * MAX_SECOND_WIDTH                        || time.isTwoHourMultiple();                break;        }        return draw;    }    private boolean isDrawText(TimeAlgorithm time, float width) {        if (time.getScaleMode() < TimeAlgorithm.MODE_TEN_MINUTE) return false;        //  Log.d(TAG, "isDrawText: " + time.isTwoHourMultiple() + ">>" + time.getStringTimeInSecond());        return TEN_MINUTE_SCALE_INTERVAL * SECOND_WIDTH > width                || (TWENTY_MINUTE_SCALE_INTERVAL * SECOND_WIDTH > width && time.isTwentyMinuteMultiple())                || (time.getScaleMode() == TimeAlgorithm.MODE_HOUR && ONE_HOUR_SCALE_INTERVAL * SECOND_WIDTH > width)                || time.isTwoHourMultiple();    }    private float getScaleHeight(TimeAlgorithm t) {        float height = TWO_MIN_SCALE_HEIGHT;        switch (t.getScaleMode()) {            case TimeAlgorithm.MODE_HOUR:                height = HOUR_SCALE_HEIGHT;                break;            case TimeAlgorithm.MODE_TEN_MINUTE:                height = TEN_MIN_SCALE_HEIGHT;                break;            case TimeAlgorithm.MODE_TWO_MINUTE:                height = TWO_MIN_SCALE_HEIGHT;                break;        }        return height;    }    private float downOneX, downTwoX, downOneY, downTwoY;    private int clickCount = 0;    private float distance;    private boolean needDelay;    private long clickTime;//ms    @Override    public boolean onTouchEvent(MotionEvent event) {        mVelocityTracker.addMovement(event);        lastX = 0;        mScroller.forceFinished(true);        //  Log.d(TAG, "onTouchEvent: ");        switch (event.getAction() & MotionEvent.ACTION_MASK) {            case MotionEvent.ACTION_DOWN:                handler.removeMessages(DELAY_MOVE_TO_NEARBY_VIDEO);                handler.removeMessages(SET_VALUE);                downOneX = event.getX(0);                downOneY = event.getY(0);                clickCount++;                if (clickCount == 2 && System.currentTimeMillis() - clickTime > 100) {//防抖                    SECOND_WIDTH = MAX_SECOND_WIDTH;//SECOND_WIDTH == MAX_SECOND_WIDTH ? MIN_SECOND_WIDTH : MAX_SECOND_WIDTH;                    // Log.d(TAG, "onTouchEvent: 恢复 >>" + (System.currentTimeMillis() - clickTime));                    invalidate();                }                clickTime = System.currentTimeMillis();                handler.postDelayed(new Runnable() {                    @Override                    public void run() {                        clickCount = 0;                    }                }, 200);                //  Log.d(TAG, "onTouchEvent: ACTION_DOWN " + downOneX + ">>" + event.getPointerCount());                return true;            case MotionEvent.ACTION_POINTER_DOWN:                if (event.getPointerCount() > 1) {                    downTwoX = event.getX(1);                    downTwoY = event.getY(1);                }                //   Log.d(TAG, "onTouchEvent: ACTION_POINTER_DOWN " + downTwoX + ">>" + event.getPointerCount());                break;            case MotionEvent.ACTION_MOVE:                move(event);                break;            case MotionEvent.ACTION_POINTER_UP:                distance = 0;                needDelay = true;                handler.postDelayed(new Runnable() {                    @Override                    public void run() {                        needDelay = false;                    }                }, 200);                //  Log.d(TAG, "onTouchEvent: ACTION_POINTER_UP " + event.getPointerCount());                break;            case MotionEvent.ACTION_UP:            case MotionEvent.ACTION_CANCEL:                // Log.d(TAG, "onTouchEvent: ACTION_UP");                distance = 0;                autoScroll();                needDelay = false;                break;        }        return super.onTouchEvent(event);    }    private void autoScroll() {        if (needDelay) {            handler.sendEmptyMessageDelayed(DELAY_MOVE_TO_NEARBY_VIDEO, delayMoveToNearbyVideoMs);            return;        }        mVelocityTracker.computeCurrentVelocity((int) mWidth, Float.MAX_VALUE);        float xVelocity = mVelocityTracker.getXVelocity();        if (Math.abs(xVelocity) > mMinVelocity) {            //   Log.d(TAG, "autoScroll: " + xVelocity + ">>" + mMinVelocity);            mScroller.forceFinished(false);            mScroller.fling(0, 0, (int) xVelocity, 0, Integer.MIN_VALUE, Integer.MAX_VALUE, 0, 0);            postInvalidate();        } else if (listener != null) {            //  Log.d(TAG, "autoScroll: DELAY_MOVE_TO_NEARBY_VIDEO");            handler.sendEmptyMessageDelayed(DELAY_MOVE_TO_NEARBY_VIDEO, delayMoveToNearbyVideoMs);        }    }    private synchronized void move(MotionEvent event) {        int num = event.getPointerCount();        if (num > 1) {            float d = Math.abs(event.getX(1) - event.getX(0));            distance = Math.abs(downTwoX - downOneX);            float scale = 1 + (d - distance) / distance;            float width = SECOND_WIDTH;            width *= scale;            if (width > MAX_SECOND_WIDTH) {                width = MAX_SECOND_WIDTH;            } else if (width < MIN_SECOND_WIDTH) {                width = MIN_SECOND_WIDTH;            }            if (width != SECOND_WIDTH) {                SECOND_WIDTH = width;                invalidate();            }//            Log.d(TAG, "onTouchEvent: ACTION_MOVE 22  scale= " + scale);            downOneX = event.getX(0);            downOneY = event.getY(0);            downTwoX = event.getX(1);            downTwoY = event.getY(1);            distance = d;        } else {            if (!needDelay) {                float move = downOneX - event.getX(0);                // Log.d(TAG, "onTouchEvent: ACTION_MOVE " + move + ">>" + SECOND_WIDTH);                moveX(move);            }            downOneX = event.getX(0);            downOneY = event.getY(0);        }    }    private void moveX(float move) {        int sec = (int) (move / SECOND_WIDTH);        TimeAlgorithm value = mValue.addOrSub(sec);        if (value.getTimeInMillis() < mValue.getCurrentMinTimeInMillis()) {            if (mScroller != null) mScroller.forceFinished(true);            value.setTimeInMillis(mValue.getCurrentMinTimeInMillis());        } else if (value.getTimeInMillis() > mValue.getCurrentMaxTimeInMillis()) {            if (mScroller != null) mScroller.forceFinished(true);            value.setTimeInMillis(mValue.getCurrentMaxTimeInMillis());        }        if (value.getTimeInMillis() != mValue.getTimeInMillis()) {            mValue = value;            if (listener != null) {                listener.onValueChanged(mValue.getStringTimeInSecond(), mValue.getTimeInMicros());            }            postInvalidate();        }    }    private int lastX = 0;    @Override    public void computeScroll() {        super.computeScroll();        if (mScroller.computeScrollOffset()) {            if (mScroller.getCurrX() == mScroller.getFinalX()) {                lastX = 0;                handler.sendEmptyMessageDelayed(DELAY_MOVE_TO_NEARBY_VIDEO, delayMoveToNearbyVideoMs);                //   Log.d(TAG, "computeScroll: end DELAY_MOVE_TO_NEARBY_VIDEO");            } else {                int xPosition = mScroller.getCurrX();                //   Log.d(TAG, "computeScroll: scroll " + lastX + ">>" + xPosition + ">>" + SECOND_WIDTH);                if ((lastX - xPosition) / SECOND_WIDTH == 0) {                    handler.sendEmptyMessageDelayed(DELAY_MOVE_TO_NEARBY_VIDEO, delayMoveToNearbyVideoMs);                } else {                    moveX(lastX - xPosition);                }                lastX = xPosition;            }        } else {            //  Log.d(TAG, "computeScrollOffset: false");        }    }    private void moveToNearbyVideo() {        if (videos == null || videos.size() == 0) {            if (listener != null)                listener.onNoneVideo(mValue.getStringTimeInSecond(), mValue.getTimeInMicros());            return;        }        long value = 0;        for (int i = 0; i < videos.size(); i++) {            PlaybackVo vo = videos.get(i);            if (vo.getStartTime() < mValue.getTimeInMicros() && vo.getEndTime() > mValue.getTimeInMicros()) {                 // Log.d(TAG, "moveToNearbyVideo: between video");                if (listener != null)                    listener.onVideoStart(mValue.getStringTimeInSecond(), mValue.getTimeInMicros());                return;            }            if (vo.getStartTime() > mValue.getTimeInMicros()&&isCurrentDay(mValue,vo.getStartTA())) {                value = vo.getStartTime();                break;            }        }       //   Log.d(TAG, "moveToNearbyVideo: " + value);        if (value != 0) {            moveToValue(value);        } else {            if (listener != null)                listener.onNoneVideo(mValue.getStringTimeInSecond(), mValue.getTimeInMicros());        }    }    private float getMaxAbs(float moveOneX, float moveTwoX) {        return Math.abs(moveOneX) > Math.abs(moveTwoX) ? Math.abs(moveOneX) : Math.abs(moveTwoX);    }    public void setOnPlaybackViewListener(PlaybackViewListener listener) {        this.listener = listener;        listener.onValueChanged(mValue.getStringTimeInSecond(), mValue.getTimeInMicros());    }    private PlaybackViewListener listener;    public interface PlaybackViewListener {        void onValueChanged(String timeInMillis, long timeInMicros);        void onVideoStart(String timeInMillis, long timeInMicros);        void onNoneVideo(String timeInMillis, long timeInMicros);    }    public static class TimeAlgorithm {        private long time;//microsecond 微秒        public static final int MODE_HOUR = 2;        public static final int MODE_TEN_MINUTE = 1;        public static final int MODE_TWO_MINUTE = 0;        public TimeAlgorithm(long micros) {            this.time = micros;        }        public void setTimeInMillis(long millis) {            time = millis * 1000;        }        public String getStringTimeInSecond() {            SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");            Date date = new Date(time / 1000);            return sdf.format(date);        }        public String getStringTimeInMinute() {            SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");            Date date = new Date(time / 1000);            return sdf.format(date);        }        public boolean isTwentyMinuteMultiple() {            Date date = new Date(time / 1000);            Calendar calendarObj = Calendar.getInstance();            calendarObj.setTime(date);            int m = calendarObj.get(Calendar.MINUTE);            int s = calendarObj.get(Calendar.SECOND);            return (m * 60 + s) % 1200 == 0;        }        public long getCurrentMaxTimeInMillis() {            Date date = new Date(time / 1000);            Calendar calendarObj = Calendar.getInstance();            calendarObj.setTime(date);            calendarObj.set(Calendar.HOUR_OF_DAY, 24);            calendarObj.set(Calendar.MINUTE, 0);            calendarObj.set(Calendar.SECOND, 0);            calendarObj.set(Calendar.MILLISECOND, 0);            // Log.d(TAG, "getCurrentMaxTimeInMillis: " + (calendarObj.getTimeInMillis() - 1000));            return calendarObj.getTimeInMillis() - 1000;        }        public long getCurrentMinTimeInMillis() {            Date date = new Date(time / 1000);            Calendar calendarObj = Calendar.getInstance();            calendarObj.setTime(date);            calendarObj.set(Calendar.HOUR_OF_DAY, 0);            calendarObj.set(Calendar.MINUTE, 0);            calendarObj.set(Calendar.SECOND, 0);            calendarObj.set(Calendar.MILLISECOND, 0);            // Log.d(TAG, "getCurrentMinTimeInMillis: " + calendarObj.getTimeInMillis());            return calendarObj.getTimeInMillis();        }        public boolean isTwoHourMultiple() {            Date date = new Date(time / 1000);            Calendar calendarObj = Calendar.getInstance();            calendarObj.setTime(date);            int h = calendarObj.get(Calendar.HOUR);            int m = calendarObj.get(Calendar.MINUTE);            int s = calendarObj.get(Calendar.SECOND);            return (h * 60 * 60 + m * 60 + s) % (2 * 60 * 60) == 0;        }        public int getScaleMode() {            Date date = new Date(time / 1000);            Calendar calendarObj = Calendar.getInstance();            calendarObj.setTime(date);            int h = calendarObj.get(Calendar.HOUR_OF_DAY);            int m = calendarObj.get(Calendar.MINUTE);            int s = calendarObj.get(Calendar.SECOND);            int mode = -1;            if ((h * 60 * 60 + 60 * m + s) % 3600 == 0) {                mode = MODE_HOUR;            } else if ((60 * m + s) % 600 == 0) {                mode = MODE_TEN_MINUTE;            } else if ((60 * m + s) % 120 == 0) {                mode = MODE_TWO_MINUTE;            }            return mode;        }        // 加上或减去_sec秒        public TimeAlgorithm addOrSub(int _sec) {            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");            Date date = new Date(time / 1000);            Calendar calendar = Calendar.getInstance();            calendar.setTime(date);            calendar.add(Calendar.SECOND, _sec);            SimpleDateFormat sdf2 = new SimpleDateFormat("HH:mm:ss");            TimeAlgorithm mt = new TimeAlgorithm(calendar.getTimeInMillis() * 1000);            return mt;        }        public long getTimeInMicros() {            return time;        }        public long getTimeInSecond() {            return time / (1000 * 1000);        }        public long getTimeInMillis() {            return time / 1000;        }        public int mod(int _timeInterval) {            Date date = new Date(time / 1000);            Calendar calendarObj = Calendar.getInstance(Locale.CHINA);            calendarObj.setTime(date);            int m = calendarObj.get(Calendar.MINUTE);            int s = calendarObj.get(Calendar.SECOND);            return (60 * m + s) % _timeInterval;        }    }    public static class PlaybackVo implements Serializable {        private long startTime;        private long endTime;        private long size;        private byte type;        private TimeAlgorithm startTA;        private TimeAlgorithm endTA;        public PlaybackVo(long startTime, long endTime, long size, byte type) {            this.startTime = startTime;            this.endTime = endTime;            this.size = size;            this.type = type;            this.startTA = new TimeAlgorithm(startTime);            this.endTA = new TimeAlgorithm(endTime);        }        public long getStartTime() {            return startTime;        }        public long getEndTime() {            return endTime;        }        public long getSize() {            return size;        }        public void setSize(long size) {            this.size = size;        }        public byte getType() {            return type;        }        public void setType(byte type) {            this.type = type;        }        public TimeAlgorithm getStartTA() {            return startTA;        }        public TimeAlgorithm getEndTA() {            return endTA;        }    }}