/*
* Copyright (C) 2013 Chen Hui <calmer91@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package master.flame.danmaku.ui.widget;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.PixelFormat;
import android.os.HandlerThread;
import android.os.Looper;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import master.flame.danmaku.controller.DrawHandler;
import master.flame.danmaku.controller.IDanmakuView;
import master.flame.danmaku.controller.IDanmakuViewController;
import master.flame.danmaku.controller.DrawHandler.Callback;
import master.flame.danmaku.controller.DrawHelper;
import master.flame.danmaku.danmaku.model.BaseDanmaku;
import master.flame.danmaku.danmaku.parser.BaseDanmakuParser;
import master.flame.danmaku.danmaku.renderer.IRenderer.RenderingState;
import java.util.LinkedList;
import java.util.Locale;
public class DanmakuSurfaceView extends SurfaceView implements IDanmakuView, IDanmakuViewController, SurfaceHolder.Callback {
public static final String TAG = "DanmakuSurfaceView";
private Callback mCallback;
private SurfaceHolder mSurfaceHolder;
private HandlerThread mHandlerThread;
private DrawHandler handler;
private boolean isSurfaceCreated;
private boolean mEnableDanmakuDrwaingCache = true;
private boolean mShowFps;
private boolean mDanmakuVisible = true;
protected int mDrawingThreadType = THREAD_TYPE_NORMAL_PRIORITY;
public DanmakuSurfaceView(Context context) {
super(context);
init();
}
private void init() {
setZOrderMediaOverlay(true);
setWillNotCacheDrawing(true);
setDrawingCacheEnabled(false);
setWillNotDraw(true);
mSurfaceHolder = getHolder();
mSurfaceHolder.addCallback(this);
mSurfaceHolder.setFormat(PixelFormat.TRANSPARENT);
DrawHelper.useDrawColorToClearCanvas(true, true);
}
public DanmakuSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public DanmakuSurfaceView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public void addDanmaku(BaseDanmaku item) {
if (handler != null) {
handler.addDanmaku(item);
}
}
@Override
public void removeAllDanmakus() {
if (handler != null) {
handler.removeAllDanmakus();
}
}
@Override
public void removeAllLiveDanmakus() {
if (handler != null) {
handler.removeAllLiveDanmakus();
}
}
public void setCallback(Callback callback) {
mCallback = callback;
if (handler != null) {
handler.setCallback(callback);
}
}
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
isSurfaceCreated = true;
Canvas canvas = surfaceHolder.lockCanvas();
if (canvas != null) {
DrawHelper.clearCanvas(canvas);
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
if (handler != null) {
handler.notifyDispSizeChanged(width, height);
}
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
isSurfaceCreated = false;
}
@Override
public void release() {
stop();
if(mDrawTimes!= null) mDrawTimes.clear();
}
@Override
public void stop() {
stopDraw();
}
private void stopDraw() {
if (handler != null) {
handler.quit();
handler = null;
}
if (mHandlerThread != null) {
try {
mHandlerThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
mHandlerThread.quit();
mHandlerThread = null;
}
}
protected Looper getLooper(int type){
if (mHandlerThread != null) {
mHandlerThread.quit();
mHandlerThread = null;
}
int priority;
switch (type) {
case THREAD_TYPE_MAIN_THREAD:
return Looper.getMainLooper();
case THREAD_TYPE_HIGH_PRIORITY:
priority = android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY;
break;
case THREAD_TYPE_LOW_PRIORITY:
priority = android.os.Process.THREAD_PRIORITY_LOWEST;
break;
case THREAD_TYPE_NORMAL_PRIORITY:
default:
priority = android.os.Process.THREAD_PRIORITY_DEFAULT;
break;
}
String threadName = "DFM Handler Thread #"+priority;
mHandlerThread = new HandlerThread(threadName, priority);
mHandlerThread.start();
return mHandlerThread.getLooper();
}
private void prepare() {
if (handler == null)
handler = new DrawHandler(getLooper(mDrawingThreadType), this, mDanmakuVisible);
}
@Override
public void prepare(BaseDanmakuParser parser) {
prepare();
handler.setParser(parser);
handler.setCallback(mCallback);
handler.prepare();
}
@Override
public boolean isPrepared() {
return handler != null && handler.isPrepared();
}
@Override
public void showFPS(boolean show){
mShowFps = show;
}
private static final int MAX_RECORD_SIZE = 50;
private static final int ONE_SECOND = 1000;
private LinkedList<Long> mDrawTimes;
private float fps() {
long lastTime = System.currentTimeMillis();
mDrawTimes.addLast(lastTime);
float dtime = lastTime - mDrawTimes.getFirst();
int frames = mDrawTimes.size();
if (frames > MAX_RECORD_SIZE) {
mDrawTimes.removeFirst();
}
return dtime > 0 ? mDrawTimes.size() * ONE_SECOND / dtime : 0.0f;
}
@Override
public long drawDanmakus() {
if (!isSurfaceCreated)
return 0;
if (!isShown())
return -1;
long stime = System.currentTimeMillis();
long dtime = 0;
Canvas canvas = mSurfaceHolder.lockCanvas();
if (canvas != null){
if (handler != null) {
RenderingState rs = handler.draw(canvas);
if (mShowFps) {
if (mDrawTimes == null)
mDrawTimes = new LinkedList<Long>();
dtime = System.currentTimeMillis() - stime;
String fps = String.format(Locale.getDefault(),
"fps %.2f,time:%d s,cache:%d,miss:%d", fps(), getCurrentTime() / 1000,
rs.cacheHitCount, rs.cacheMissCount);
DrawHelper.drawFPS(canvas, fps);
}
}
if (isSurfaceCreated)
mSurfaceHolder.unlockCanvasAndPost(canvas);
}
dtime = System.currentTimeMillis() - stime;
return dtime;
}
public void toggle() {
if (isSurfaceCreated) {
if (handler == null)
start();
else if (handler.isStop()) {
resume();
} else
pause();
}
}
@Override
public void pause() {
if (handler != null)
handler.pause();
}
@Override
public void resume() {
if (handler != null && handler.isPrepared())
handler.resume();
else {
restart();
}
}
@Override
public boolean isPaused() {
if(handler != null) {
return handler.isStop();
}
return false;
}
public void restart() {
stop();
start();
}
@Override
public void start() {
start(0);
}
@Override
public void start(long postion) {
if (handler == null) {
prepare();
}else{
handler.removeCallbacksAndMessages(null);
}
handler.obtainMessage(DrawHandler.START, postion).sendToTarget();
}
public void seekTo(Long ms) {
if(handler != null){
handler.seekTo(ms);
}
}
public void enableDanmakuDrawingCache(boolean enable) {
mEnableDanmakuDrwaingCache = enable;
}
@Override
public boolean isDanmakuDrawingCacheEnabled() {
return mEnableDanmakuDrwaingCache;
}
@Override
public boolean isViewReady() {
return isSurfaceCreated;
}
@Override
public View getView() {
return this;
}
@Override
public void show() {
showAndResumeDrawTask(null);
}
@Override
public void showAndResumeDrawTask(Long position) {
mDanmakuVisible = true;
if (handler == null) {
return;
}
handler.showDanmakus(position);
}
@Override
public void hide() {
mDanmakuVisible = false;
if (handler == null) {
return;
}
handler.hideDanmakus(false);
}
@Override
public long hideAndPauseDrawTask() {
mDanmakuVisible = false;
if (handler == null) {
return 0;
}
return handler.hideDanmakus(true);
}
@Override
public void clear() {
if (!isViewReady()) {
return;
}
Canvas canvas = mSurfaceHolder.lockCanvas();
if (canvas != null) {
DrawHelper.clearCanvas(canvas);
mSurfaceHolder.unlockCanvasAndPost(canvas);
}
}
@Override
public boolean isShown() {
return !(handler == null || !isViewReady()) && handler.getVisibility();
}
@Override
public void setDrawingThreadType(int type) {
mDrawingThreadType = type;
}
@Override
public long getCurrentTime() {
if (handler != null) {
return handler.getCurrentTime();
}
return 0;
}
@Override
public boolean isHardwareAccelerated() {
return false;
}
@Override
public void clearDanmakusOnScreen() {
if (handler != null) {
handler.clearDanmakusOnScreen();
}
}
}