/*
* 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.controller;
import android.content.Context;
import android.graphics.Canvas;
import master.flame.danmaku.danmaku.model.AbsDisplayer;
import master.flame.danmaku.danmaku.model.BaseDanmaku;
import master.flame.danmaku.danmaku.model.DanmakuTimer;
import master.flame.danmaku.danmaku.model.GlobalFlagValues;
import master.flame.danmaku.danmaku.model.IDanmakuIterator;
import master.flame.danmaku.danmaku.model.IDanmakus;
import master.flame.danmaku.danmaku.model.android.DanmakuGlobalConfig;
import master.flame.danmaku.danmaku.model.android.DanmakuGlobalConfig.DanmakuConfigTag;
import master.flame.danmaku.danmaku.model.android.Danmakus;
import master.flame.danmaku.danmaku.model.android.DanmakuGlobalConfig.ConfigChangedCallback;
import master.flame.danmaku.danmaku.parser.BaseDanmakuParser;
import master.flame.danmaku.danmaku.parser.DanmakuFactory;
import master.flame.danmaku.danmaku.renderer.IRenderer;
import master.flame.danmaku.danmaku.renderer.IRenderer.RenderingState;
import master.flame.danmaku.danmaku.renderer.android.DanmakuRenderer;
import master.flame.danmaku.danmaku.renderer.android.DanmakusRetainer;
public class DrawTask implements IDrawTask, ConfigChangedCallback {
protected AbsDisplayer<?> mDisp;
protected IDanmakus danmakuList;
protected BaseDanmakuParser mParser;
TaskListener mTaskListener;
Context mContext;
IRenderer mRenderer;
DanmakuTimer mTimer;
private IDanmakus danmakus = new Danmakus(Danmakus.ST_BY_LIST);
protected boolean clearRetainerFlag;
private long mStartRenderTime = 0;
private RenderingState mRenderingState = new RenderingState();
protected boolean mReadyState;
private long mLastBeginMills;
private long mLastEndMills;
private boolean mIsHidden;
public DrawTask(DanmakuTimer timer, Context context, AbsDisplayer<?> disp,
TaskListener taskListener) {
mTaskListener = taskListener;
mContext = context;
mRenderer = new DanmakuRenderer();
mDisp = disp;
initTimer(timer);
Boolean enable = DanmakuGlobalConfig.DEFAULT.isDuplicateMergingEnabled();
if (enable != null) {
if(enable) {
DanmakuFilters.getDefault().registerFilter(DanmakuFilters.TAG_DUPLICATE_FILTER);
} else {
DanmakuFilters.getDefault().unregisterFilter(DanmakuFilters.TAG_DUPLICATE_FILTER);
}
}
}
protected void initTimer(DanmakuTimer timer) {
mTimer = timer;
}
@Override
public void addDanmaku(BaseDanmaku item) {
if (danmakuList == null)
return;
boolean added = false;
synchronized (danmakuList) {
if(item.isLive) {
removeUnusedLiveDanmakusIn(10);
}
item.index = danmakuList.size();
if (mLastBeginMills <= item.time && item.time <= mLastEndMills) {
synchronized (danmakus) {
added = danmakus.addItem(item);
}
} else if(item.isLive){
mLastBeginMills = mLastEndMills = 0;
}
added = danmakuList.addItem(item);
}
if (added && mTaskListener != null) {
mTaskListener.onDanmakuAdd(item);
}
}
@Override
public void removeAllDanmakus() {
if (danmakuList == null || danmakuList.isEmpty())
return;
synchronized (danmakuList) {
danmakuList.clear();
}
}
@Override
public void removeAllLiveDanmakus() {
if (danmakus == null || danmakus.isEmpty())
return;
synchronized (danmakus) {
IDanmakuIterator it = danmakus.iterator();
while (it.hasNext()) {
if (it.next().isLive) {
it.remove();
}
}
}
}
protected void removeUnusedLiveDanmakusIn(int msec) {
if (danmakuList == null || danmakuList.isEmpty())
return;
synchronized (danmakuList) {
long startTime = System.currentTimeMillis();
IDanmakuIterator it = danmakuList.iterator();
while (it.hasNext()) {
BaseDanmaku danmaku = it.next();
boolean isTimeout = danmaku.isTimeOut();
if (isTimeout && danmaku.isLive) {
it.remove();
}
if (!isTimeout || System.currentTimeMillis() - startTime > msec) {
break;
}
}
}
}
@Override
public RenderingState draw(AbsDisplayer<?> displayer) {
return drawDanmakus(displayer,mTimer);
}
@Override
public void reset() {
if (danmakus != null)
danmakus.clear();
if (mRenderer != null)
mRenderer.clear();
}
@Override
public void seek(long mills) {
reset();
// requestClear();
GlobalFlagValues.updateVisibleFlag();
mStartRenderTime = mills < 1000 ? 0 : mills;
}
@Override
public void clearDanmakusOnScreen(long currMillis) {
reset();
GlobalFlagValues.updateVisibleFlag();
mStartRenderTime = currMillis;
}
@Override
public void start() {
DanmakuGlobalConfig.DEFAULT.registerConfigChangedCallback(this);
}
@Override
public void quit() {
if (mRenderer != null)
mRenderer.release();
DanmakuGlobalConfig.DEFAULT.unregisterConfigChangedCallback(this);
}
public void prepare() {
assert (mParser != null);
loadDanmakus(mParser);
if (mTaskListener != null) {
mTaskListener.ready();
mReadyState = true;
}
}
protected void loadDanmakus(BaseDanmakuParser parser) {
danmakuList = parser.setDisplayer(mDisp).setTimer(mTimer).getDanmakus();
GlobalFlagValues.resetAll();
}
public void setParser(BaseDanmakuParser parser) {
mParser = parser;
mReadyState = false;
}
protected RenderingState drawDanmakus(AbsDisplayer<?> disp, DanmakuTimer timer) {
if (clearRetainerFlag) {
DanmakusRetainer.clear();
clearRetainerFlag = false;
}
if (danmakuList != null) {
Canvas canvas = (Canvas) disp.getExtraData();
DrawHelper.clearCanvas(canvas);
if (mIsHidden) {
return mRenderingState;
}
long beginMills = timer.currMillisecond - DanmakuFactory.MAX_DANMAKU_DURATION - 100;
long endMills = timer.currMillisecond + DanmakuFactory.MAX_DANMAKU_DURATION;
if(mLastBeginMills > beginMills || timer.currMillisecond > mLastEndMills) {
IDanmakus subDanmakus = danmakuList.sub(beginMills, endMills);
if(subDanmakus != null) {
danmakus = subDanmakus;
} else {
danmakus.clear();
}
mLastBeginMills = beginMills;
mLastEndMills = endMills;
} else {
beginMills = mLastBeginMills;
endMills = mLastEndMills;
}
if (danmakus != null && !danmakus.isEmpty()) {
RenderingState renderingState = mRenderingState = mRenderer.draw(mDisp, danmakus, mStartRenderTime);
if (renderingState.nothingRendered) {
if (renderingState.beginTime == RenderingState.UNKNOWN_TIME) {
renderingState.beginTime = beginMills;
}
if (renderingState.endTime == RenderingState.UNKNOWN_TIME) {
renderingState.endTime = endMills;
}
}
return renderingState;
} else {
mRenderingState.nothingRendered = true;
mRenderingState.beginTime = beginMills;
mRenderingState.endTime = endMills;
return mRenderingState;
}
}
return null;
}
public void requestClear() {
mLastBeginMills = mLastEndMills = 0;
mIsHidden = false;
}
public void requestClearRetainer() {
clearRetainerFlag = true;
}
@Override
public boolean onDanmakuConfigChanged(DanmakuGlobalConfig config, DanmakuConfigTag tag,
Object... values) {
boolean handled = handleOnDanmakuConfigChanged(config, tag, values);
if (mTaskListener != null) {
mTaskListener.onDanmakuConfigChanged();
}
return handled;
}
protected boolean handleOnDanmakuConfigChanged(DanmakuGlobalConfig config, DanmakuConfigTag tag, Object[] values) {
boolean handled = false;
if (tag == null || DanmakuConfigTag.MAXIMUM_NUMS_IN_SCREEN.equals(tag)) {
handled = true;
} else if (DanmakuConfigTag.DUPLICATE_MERGING_ENABLED.equals(tag)) {
Boolean enable = (Boolean) values[0];
if (enable != null) {
if (enable) {
DanmakuFilters.getDefault().registerFilter(DanmakuFilters.TAG_DUPLICATE_FILTER);
} else {
DanmakuFilters.getDefault().unregisterFilter(DanmakuFilters.TAG_DUPLICATE_FILTER);
}
handled = true;
}
} else if (DanmakuConfigTag.SCALE_TEXTSIZE.equals(tag) || DanmakuConfigTag.SCROLL_SPEED_FACTOR.equals(tag)) {
requestClearRetainer();
handled = false;
}
return handled;
}
@Override
public void requestHide() {
mIsHidden = true;
}
}