package master.flame.danmaku.controller;
import master.flame.danmaku.danmaku.model.BaseDanmaku;
import master.flame.danmaku.danmaku.model.DanmakuTimer;
import master.flame.danmaku.danmaku.model.IDanmakuIterator;
import master.flame.danmaku.danmaku.model.IDanmakus;
import master.flame.danmaku.danmaku.model.android.Danmakus;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
public class DanmakuFilters {
public static interface IDanmakuFilter<T> {
/*
* 是否过滤
*/
public boolean filter(BaseDanmaku danmaku, int index, int totalsizeInScreen,
DanmakuTimer timer, boolean fromCachingTask);
public void setData(T data);
public void reset();
public void clear();
}
public static abstract class BaseDanmakuFilter<T> implements IDanmakuFilter<T> {
@Override
public void clear() {
}
}
/**
* 根据弹幕类型过滤
*
* @author ch
*/
public static class TypeDanmakuFilter extends BaseDanmakuFilter<List<Integer>> {
final List<Integer> mFilterTypes = Collections.synchronizedList(new ArrayList<Integer>());
public void enableType(Integer type) {
if (!mFilterTypes.contains(type))
mFilterTypes.add(type);
}
public void disableType(Integer type) {
if (mFilterTypes.contains(type))
mFilterTypes.remove(type);
}
@Override
public boolean filter(BaseDanmaku danmaku, int orderInScreen, int totalsizeInScreen,
DanmakuTimer timer, boolean fromCachingTask) {
return danmaku != null && mFilterTypes.contains(danmaku.getType());
}
@Override
public void setData(List<Integer> data) {
reset();
if (data != null) {
for (Integer i : data) {
enableType(i);
}
}
}
@Override
public void reset() {
mFilterTypes.clear();
}
}
/**
* 根据同屏数量过滤弹幕
*
* @author ch
*/
public static class QuantityDanmakuFilter extends BaseDanmakuFilter<Integer> {
protected int mMaximumSize = -1;
protected final IDanmakus danmakus = new Danmakus();
protected BaseDanmaku mLastSkipped = null;
@Override
public synchronized boolean filter(BaseDanmaku danmaku, int orderInScreen,
int totalsizeInScreen, DanmakuTimer timer, boolean fromCachingTask) {
BaseDanmaku last = danmakus.last();
if (last != null && last.isTimeOut()) {
danmakus.clear();
last = null;
}
if (mMaximumSize <= 0 || danmaku.getType() != BaseDanmaku.TYPE_SCROLL_RL) {
return false;
}
if (danmakus.contains(danmaku)) {
return true;
}
if (totalsizeInScreen < mMaximumSize || danmaku.isShown()
|| (mLastSkipped != null && (danmaku.time - mLastSkipped.time > 500))) {
mLastSkipped = danmaku;
return false;
}
if (orderInScreen > mMaximumSize && !danmaku.isTimeOut()) {
danmakus.addItem(danmaku);
return true;
}
mLastSkipped = danmaku;
return false;
}
@Override
public void setData(Integer data) {
reset();
if (data == null)
return;
if (data != mMaximumSize) {
mMaximumSize = data;
}
}
@Override
public synchronized void reset() {
danmakus.clear();
}
@Override
public void clear() {
reset();
}
}
/**
* 根据绘制耗时过滤弹幕
*
* @author ch
*/
public static class ElapsedTimeFilter extends BaseDanmakuFilter<Object> {
long mMaxTime = 20; // 绘制超过20ms就跳过 ,默认保持接近50fps
protected final IDanmakus danmakus = new Danmakus();
@Override
public synchronized boolean filter(BaseDanmaku danmaku, int orderInScreen,
int totalsizeInScreen, DanmakuTimer timer, boolean fromCachingTask) {
if (danmakus.last() != null && danmakus.last().isTimeOut()) {
danmakus.clear();
}
if (danmakus.contains(danmaku)) {
return true;
}
if (timer == null || !danmaku.isOutside()) {
return false;
}
long elapsedTime = System.currentTimeMillis() - timer.currMillisecond;
if (elapsedTime >= mMaxTime) {
danmakus.addItem(danmaku);
return true;
}
return false;
}
@Override
public void setData(Object data) {
reset();
}
@Override
public synchronized void reset() {
danmakus.clear();
}
@Override
public void clear() {
reset();
}
}
/**
* 根据文本颜色白名单过滤
*
* @author ch
*/
public static class TextColorFilter extends BaseDanmakuFilter<List<Integer>> {
public List<Integer> mWhiteList = new ArrayList<Integer>();
private void addToWhiteList(Integer color) {
if (!mWhiteList.contains(color)) {
mWhiteList.add(color);
}
}
@Override
public boolean filter(BaseDanmaku danmaku, int index, int totalsizeInScreen,
DanmakuTimer timer, boolean fromCachingTask) {
return danmaku != null && !mWhiteList.contains(danmaku.textColor);
}
@Override
public void setData(List<Integer> data) {
reset();
if (data != null) {
for (Integer i : data) {
addToWhiteList(i);
}
}
}
@Override
public void reset() {
mWhiteList.clear();
}
}
/**
* 根据用户标识黑名单过滤
*
* @author ch
*/
public static abstract class UserFilter<T> extends BaseDanmakuFilter<List<T>> {
public List<T> mBlackList = new ArrayList<T>();
private void addToBlackList(T id) {
if (!mBlackList.contains(id)) {
mBlackList.add(id);
}
}
@Override
public abstract boolean filter(BaseDanmaku danmaku, int index, int totalsizeInScreen,
DanmakuTimer timer, boolean fromCachingTask);
@Override
public void setData(List<T> data) {
reset();
if (data != null) {
for (T i : data) {
addToBlackList(i);
}
}
}
@Override
public void reset() {
mBlackList.clear();
}
}
/**
* 根据用户Id黑名单过滤
*
* @author ch
*/
public static class UserIdFilter extends UserFilter<Integer> {
@Override
public boolean filter(BaseDanmaku danmaku, int index, int totalsizeInScreen,
DanmakuTimer timer, boolean fromCachingTask) {
return danmaku != null && mBlackList.contains(danmaku.userId);
}
}
/**
* 根据用户hash黑名单过滤
*
* @author ch
*/
public static class UserHashFilter extends UserFilter<String> {
@Override
public boolean filter(BaseDanmaku danmaku, int index, int totalsizeInScreen,
DanmakuTimer timer, boolean fromCachingTask) {
return danmaku != null && mBlackList.contains(danmaku.userHash);
}
}
/**
* 屏蔽游客弹幕
*
* @author ch
*/
public static class GuestFilter extends BaseDanmakuFilter<Boolean> {
private Boolean mBlock = false;
@Override
public boolean filter(BaseDanmaku danmaku, int index, int totalsizeInScreen,
DanmakuTimer timer, boolean fromCachingTask) {
if (!mBlock) {
return false;
}
return danmaku.isGuest;
}
@Override
public void setData(Boolean data) {
mBlock = data;
}
@Override
public void reset() {
mBlock = false;
}
}
public static class DuplicateMergingFilter extends BaseDanmakuFilter<Void> {
protected final IDanmakus blockedDanmakus = new Danmakus(Danmakus.ST_BY_LIST);
protected final LinkedHashMap<String, BaseDanmaku> currentDanmakus = new LinkedHashMap<String, BaseDanmaku>();
private final IDanmakus passedDanmakus = new Danmakus(Danmakus.ST_BY_LIST);
private final void removeTimeoutDanmakus(final IDanmakus danmakus, long limitTime) {
IDanmakuIterator it = danmakus.iterator();
long startTime = System.currentTimeMillis();
while (it.hasNext()) {
try {
BaseDanmaku item = it.next();
if (item.isTimeOut()) {
it.remove();
} else {
break;
}
} catch (Exception e) {
break;
}
if (System.currentTimeMillis() - startTime > limitTime) {
break;
}
}
}
private void removeTimeoutDanmakus(LinkedHashMap<String, BaseDanmaku> danmakus,
int limitTime) {
Iterator<Entry<String, BaseDanmaku>> it = danmakus.entrySet().iterator();
long startTime = System.currentTimeMillis();
while (it.hasNext()) {
try {
Entry<String, BaseDanmaku> entry = it.next();
BaseDanmaku item = entry.getValue();
if (item.isTimeOut()) {
it.remove();
} else {
break;
}
} catch (Exception e) {
break;
}
if (System.currentTimeMillis() - startTime > limitTime) {
break;
}
}
}
@Override
public synchronized boolean filter(BaseDanmaku danmaku, int index, int totalsizeInScreen,
DanmakuTimer timer, boolean fromCachingTask) {
removeTimeoutDanmakus(blockedDanmakus, 2);
removeTimeoutDanmakus(passedDanmakus, 2);
removeTimeoutDanmakus(currentDanmakus, 3);
if (blockedDanmakus.contains(danmaku) && !danmaku.isOutside()) {
return true;
}
if (passedDanmakus.contains(danmaku)) {
return false;
}
if (currentDanmakus.containsKey(danmaku.text)) {
currentDanmakus.put(danmaku.text, danmaku);
blockedDanmakus.removeItem(danmaku);
blockedDanmakus.addItem(danmaku);
return true;
} else {
currentDanmakus.put(danmaku.text, danmaku);
passedDanmakus.addItem(danmaku);
return false;
}
}
@Override
public void setData(Void data) {
}
@Override
public synchronized void reset() {
passedDanmakus.clear();
blockedDanmakus.clear();
currentDanmakus.clear();
}
@Override
public void clear() {
reset();
}
}
public final static String TAG_TYPE_DANMAKU_FILTER = "1010_Filter";
public final static String TAG_QUANTITY_DANMAKU_FILTER = "1011_Filter";
public final static String TAG_ELAPSED_TIME_FILTER = "1012_Filter";
public final static String TAG_TEXT_COLOR_DANMAKU_FILTER = "1013_Filter";
public final static String TAG_USER_ID_FILTER = "1014_Filter";
public final static String TAG_USER_HASH_FILTER = "1015_Filter";
public static final String TAG_GUEST_FILTER = "1016_Filter";
public static final String TAG_DUPLICATE_FILTER = "1017_Filter";
private static DanmakuFilters instance = null;
public final Exception filterException = new Exception("not suuport this filter tag");
public boolean filter(BaseDanmaku danmaku, int index, int totalsizeInScreen,
DanmakuTimer timer, boolean fromCachingTask) {
for (IDanmakuFilter<?> f : mFilterArray) {
if (f != null && f.filter(danmaku, index, totalsizeInScreen, timer, fromCachingTask)) {
return true;
}
}
return false;
}
private final static Map<String, IDanmakuFilter<?>> filters = Collections
.synchronizedSortedMap(new TreeMap<String, IDanmakuFilter<?>>());
public IDanmakuFilter<?> get(String tag) {
IDanmakuFilter<?> f = filters.get(tag);
if (f == null) {
f = registerFilter(tag);
}
return f;
}
IDanmakuFilter<?>[] mFilterArray = new IDanmakuFilter[0];
public IDanmakuFilter<?> registerFilter(String tag) {
if (tag == null) {
throwFilterException();
return null;
}
IDanmakuFilter<?> filter = filters.get(tag);
if (filter == null) {
if (TAG_TYPE_DANMAKU_FILTER.equals(tag)) {
filter = new TypeDanmakuFilter();
} else if (TAG_QUANTITY_DANMAKU_FILTER.equals(tag)) {
filter = new QuantityDanmakuFilter();
} else if (TAG_ELAPSED_TIME_FILTER.equals(tag)) {
filter = new ElapsedTimeFilter();
} else if (TAG_TEXT_COLOR_DANMAKU_FILTER.equals(tag)) {
filter = new TextColorFilter();
} else if (TAG_USER_ID_FILTER.equals(tag)) {
filter = new UserIdFilter();
} else if (TAG_USER_HASH_FILTER.equals(tag)) {
filter = new UserHashFilter();
} else if (TAG_GUEST_FILTER.equals(tag)) {
filter = new GuestFilter();
} else if (TAG_DUPLICATE_FILTER.equals(tag)) {
filter = new DuplicateMergingFilter();
}
// add more filter
}
if (filter == null) {
throwFilterException();
return null;
}
filter.setData(null);
filters.put(tag, filter);
mFilterArray = filters.values().toArray(mFilterArray);
return filter;
}
public void unregisterFilter(String tag) {
IDanmakuFilter<?> f = filters.remove(tag);
if (f != null) {
f.clear();
f = null;
mFilterArray = filters.values().toArray(mFilterArray);
}
}
public void clear() {
for (IDanmakuFilter<?> f : mFilterArray) {
if (f != null)
f.clear();
}
}
public void reset() {
for (IDanmakuFilter<?> f : mFilterArray) {
if (f != null)
f.reset();
}
}
public void release() {
clear();
filters.clear();
mFilterArray = new IDanmakuFilter[0];
}
private void throwFilterException() {
try {
throw filterException;
} catch (Exception e) {
}
}
public static DanmakuFilters getDefault() {
if (instance == null) {
instance = new DanmakuFilters();
}
return instance;
}
}