/*
* Created by Itzik Braun on 12/3/2015.
* Copyright (c) 2015 deluge. All rights reserved.
*
* Last Modification at: 3/12/15 4:34 PM
*/
package com.braunster.androidchatsdk.firebaseplugin.firebase;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import com.braunster.androidchatsdk.firebaseplugin.firebase.wrappers.BThreadWrapper;
import com.braunster.androidchatsdk.firebaseplugin.firebase.wrappers.BUserWrapper;
import com.braunster.androidchatsdk.firebaseplugin.firebase.wrappers.InMessagesListener;
import com.braunster.androidchatsdk.firebaseplugin.firebase.wrappers.ThreadUpdateChangeListener;
import com.braunster.androidchatsdk.firebaseplugin.firebase.wrappers.UserAddedListener;
import com.braunster.androidchatsdk.firebaseplugin.firebase.wrappers.UserMetaChangeListener;
import com.braunster.chatsdk.Utils.Debug;
import com.braunster.chatsdk.dao.FollowerLink;
import com.braunster.chatsdk.dao.BMessage;
import com.braunster.chatsdk.dao.BThread;
import com.braunster.chatsdk.dao.BUser;
import com.braunster.chatsdk.dao.core.DaoCore;
import com.braunster.chatsdk.dao.entities.BThreadEntity;
import com.braunster.chatsdk.interfaces.AppEvents;
import com.braunster.chatsdk.network.BDefines;
import com.braunster.chatsdk.network.BFirebaseDefines;
import com.braunster.chatsdk.network.BNetworkManager;
import com.braunster.chatsdk.network.BPath;
import com.braunster.chatsdk.network.events.AbstractEventManager;
import com.braunster.chatsdk.network.events.BatchedEvent;
import com.braunster.chatsdk.network.events.Event;
import com.google.firebase.database.ChildEventListener;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.Query;
import org.apache.commons.lang3.StringUtils;
import org.jdeferred.Deferred;
import org.jdeferred.DoneCallback;
import org.jdeferred.FailCallback;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import timber.log.Timber;
public class FirebaseEventsManager extends AbstractEventManager implements AppEvents {
private static final boolean DEBUG = Debug.EventManager;
public static final String THREAD_ID = "threadID";
public static final String USER_ID = "userID";
private static FirebaseEventsManager instance;
private static final String MSG_PREFIX = "msg_";
private static final String USER_PREFIX = "user_";
private static final String USER_META_PREFIX = "user_meta_";
private ConcurrentHashMap<String, Event> events = new ConcurrentHashMap<String, Event>();
private List<String> threadsIds = new ArrayList<String>();
private List<String> handledAddedUsersToThreadIDs = new ArrayList<String>();
private List<String> handledMessagesThreadsID = new ArrayList<String>();
private List<String> usersIds = new ArrayList<String>();
private List<String> handledUsersMetaIds = new ArrayList<String>();
private List<String> handleFollowDataChangeUsersId = new ArrayList<String>();
public ConcurrentHashMap<String, FirebaseEventCombo> listenerAndRefs = new ConcurrentHashMap<String, FirebaseEventCombo>();
public static FirebaseEventsManager getInstance(){
if (instance == null)
instance = new FirebaseEventsManager();
return instance;
}
private final EventHandler handlerThread = new EventHandler(this);
private final EventHandler handlerMessages = new EventHandler(this);
private final EventHandler handlerUserDetails = new EventHandler(this);
private final EventHandler handlerUserAdded = new EventHandler(this);
private String observedUserEntityID = "";
private FirebaseEventsManager(){
threadsIds = Collections.synchronizedList(threadsIds);
handledAddedUsersToThreadIDs = Collections.synchronizedList(handledAddedUsersToThreadIDs);;
handledMessagesThreadsID = Collections.synchronizedList(handledMessagesThreadsID);
usersIds = Collections.synchronizedList(usersIds);
handledUsersMetaIds = Collections.synchronizedList(handledUsersMetaIds);
}
static class EventHandler extends Handler{
WeakReference<FirebaseEventsManager> manager;
public EventHandler(FirebaseEventsManager manager){
super(Looper.getMainLooper());
this.manager = new WeakReference<>(manager);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what)
{
case AppEvents.USER_DETAILS_CHANGED:
if (notNull())
manager.get().onUserDetailsChange((BUser) msg.obj);
break;
case AppEvents.MESSAGE_RECEIVED:
if (notNull())
manager.get().onMessageReceived((BMessage) msg.obj);
break;
case AppEvents.THREAD_DETAILS_CHANGED:
if (notNull())
manager.get().onThreadDetailsChanged((String) msg.obj);
break;
case AppEvents.USER_ADDED_TO_THREAD:
if (notNull())
manager.get().onUserAddedToThread(msg.getData().getString(THREAD_ID), msg.getData().getString(USER_ID));
break;
case AppEvents.FOLLOWER_ADDED:
if (notNull())
manager.get().onFollowerAdded((FollowerLink) msg.obj);
break;
case AppEvents.FOLLOWER_REMOVED:
if (notNull())
manager.get().onFollowerRemoved();
break;
case AppEvents.USER_TO_FOLLOW_ADDED:
if (notNull())
manager.get().onUserToFollowAdded((FollowerLink) msg.obj);
break;
case AppEvents.USER_TO_FOLLOW_REMOVED:
if (notNull())
manager.get().onUserToFollowRemoved();
break;
}
}
private boolean notNull(){
return manager.get() != null;
}
}
@Override
public boolean onUserAddedToThread(String threadId, final String userId) {
if (DEBUG) Timber.i("onUserAddedToThread");
for (Event e : events.values())
{
if (e == null)
continue;
if (StringUtils.isNotEmpty(e.getEntityId()) && StringUtils.isNotEmpty(threadId)
&& !e.getEntityId().equals(threadId) )
continue;
if(e instanceof BatchedEvent)
((BatchedEvent) e).add(Event.Type.ThreadEvent, threadId);
e.onUserAddedToThread(threadId, userId);
}
return false;
}
@Override
public boolean onFollowerAdded(final FollowerLink follower) {
if (follower!=null)
for (Event e : events.values())
{
if (e == null)
continue;
if(e instanceof BatchedEvent)
((BatchedEvent) e).add(Event.Type.FollwerEvent, follower.getBUser().getEntityID());
e.onFollowerAdded(follower);
}
return false;
}
@Override
public boolean onFollowerRemoved() {
for ( Event e : events.values())
{
if (e == null )
continue;
if(e instanceof BatchedEvent)
((BatchedEvent) e).add(Event.Type.FollwerEvent);
e.onFollowerRemoved();
}
return false;
}
@Override
public boolean onUserToFollowAdded(final FollowerLink follower) {
if (follower!=null)
for (Event e : events.values())
{
if (e == null)
continue;
if(e instanceof BatchedEvent)
((BatchedEvent) e).add(Event.Type.FollwerEvent, follower.getBUser().getEntityID());
e.onUserToFollowAdded(follower);
}
return false;
}
@Override
public boolean onUserToFollowRemoved() {
for (Event e : events.values())
{
if (e == null )
continue;
if(e instanceof BatchedEvent)
((BatchedEvent) e).add(Event.Type.FollwerEvent);
e.onUserToFollowRemoved();
}
return false;
}
@Override
public boolean onUserDetailsChange(BUser user) {
if (DEBUG) Timber.i("onUserDetailsChange");
if (user == null)
return false;
for ( Event e : events.values())
{
if (e == null)
continue;
// We check to see if the listener specified a specific user that he wants to listen to.
// If we could find and match the data we ignore it.
if (StringUtils.isNotEmpty(e.getEntityId()) && StringUtils.isNotEmpty(user.getEntityID())
&& !e.getEntityId().equals(user.getEntityID()) )
continue;
if(e instanceof BatchedEvent)
((BatchedEvent) e ).add(Event.Type.UserDetailsEvent, user.getEntityID());
e.onUserDetailsChange(user);
}
return false;
}
@Override
public boolean onMessageReceived(BMessage message) {
if (DEBUG) Timber.i("onMessageReceived");
for (Event e : events.values())
{
if (e == null)
continue;
// We check to see if the listener specified a specific thread that he wants to listen to.
// If we could find and match the data we ignore it.
if (StringUtils.isNotEmpty(e.getEntityId()) && message.getThread() != null
&& message.getThread().getEntityID() != null
&& !message.getThread().getEntityID().equals(e.getEntityId()))
continue;
if(e instanceof BatchedEvent)
((BatchedEvent) e).add(Event.Type.MessageEvent, message.getEntityID());
e.onMessageReceived(message);
}
return false;
}
@Override
public boolean onThreadDetailsChanged(final String threadId) {
if (DEBUG) Timber.i("onThreadDetailsChanged");
for (Event e : events.values())
{
if (e == null)
continue;
if (StringUtils.isNotEmpty(e.getEntityId()) && !threadId.equals(e.getEntityId()))
continue;
if(e instanceof BatchedEvent)
((BatchedEvent) e).add(Event.Type.ThreadEvent, threadId);
e.onThreadDetailsChanged(threadId);
}
return false;
}
@Override
public boolean onThreadIsAdded(String threadId) {
if (DEBUG) Timber.i("onThreadIsAdded");
for (Event e : events.values())
{
if (e == null)
continue;
if (StringUtils.isNotEmpty(e.getEntityId()) && !threadId.equals(e.getEntityId()))
continue;
if(e instanceof BatchedEvent)
((BatchedEvent) e).add(Event.Type.ThreadEvent, threadId);
e.onThreadIsAdded(threadId);
}
return false;
}
/*##########################################################################################*/
@Override
public void userOn(final BUser user){
if (DEBUG) Timber.v("userOn, EntityID: %s", user.getEntityID());
observedUserEntityID = user.getEntityID();
DatabaseReference userRef = FirebasePaths.userRef(observedUserEntityID);
userRef.child(BFirebaseDefines.Path.BThreadPath).addChildEventListener(threadAddedListener);
userRef.child(BFirebaseDefines.Path.FollowerLinks).addChildEventListener(followerEventListener);
userRef.child(BFirebaseDefines.Path.BFollows).addChildEventListener(followsEventListener);
FirebasePaths.publicThreadsRef().addChildEventListener(threadAddedListener);
post(new Runnable() {
@Override
public void run() {
for (BUser contact : user.getContacts())
BUserWrapper.initWithModel(contact).metaOn();
}
});
}
@Override
public void userOff(final BUser user){
if (DEBUG) Timber.v("userOff, EntityID: $s", user.getEntityID());
BThreadWrapper wrapper;
for (BThread thread : user.getThreads())
{
wrapper = new BThreadWrapper(thread);
wrapper.off();
wrapper.messagesOff();
wrapper.usersOff();
}
post(new Runnable() {
@Override
public void run() {
for (BUser contact : user.getContacts())
BUserWrapper.initWithModel(contact).metaOff();
}
});
removeAll();
}
/**
* Handle user meta change.
**/
public void userMetaOn(String userID, Deferred<Void, Void, Void> promise){
if (userID.equals(getCurrentUserId()))
{
if (DEBUG) Timber.d("handleUsersDetailsChange, Current User: %s", userID);
return;
}
if (handledUsersMetaIds.contains(userID))
{
if (DEBUG) Timber.d("handleUsersDetailsChange, Listening.");
return;
}
handledUsersMetaIds.add(userID);
final DatabaseReference userRef = FirebasePaths.userMetaRef(userID);
if (DEBUG) Timber.v("handleUsersDetailsChange, User Ref: %s", userRef.getRef().toString());
UserMetaChangeListener userMetaChangeListener = new UserMetaChangeListener(userID, promise, handlerUserDetails);
FirebaseEventCombo combo = getCombo(USER_META_PREFIX + userID, userRef.toString(), userMetaChangeListener);
userRef.addValueEventListener(combo.getListener());
}
/**
* Stop handling user meta change.
**/
public void userMetaOff(String userID){
if (DEBUG) Timber.v("userMetaOff, UserId: %s", userID);
FirebaseEventCombo c = listenerAndRefs.get(USER_META_PREFIX + userID);
if (c != null)
c.breakCombo();
listenerAndRefs.remove(USER_META_PREFIX + userID);
handledUsersMetaIds.remove(userID);
}
public void threadUsersAddedOn(String threadId){
// Check if handled.
if (handledAddedUsersToThreadIDs.contains(threadId))
return;
handledAddedUsersToThreadIDs.add(threadId);
// Also listen to the thread users
// This will allow us to update the users in the database
DatabaseReference threadUsers = FirebasePaths.threadRef(threadId).child(BFirebaseDefines.Path.BUsersPath);
UserAddedListener userAddedToThreadListener= UserAddedListener.getNewInstance(observedUserEntityID, threadId, handlerUserAdded);
FirebaseEventCombo combo = getCombo(USER_PREFIX + threadId, threadUsers.toString(), userAddedToThreadListener);
threadUsers.addChildEventListener(combo.getListener());
}
public void threadUsersAddedOff(String threadId){
if (DEBUG) Timber.v("handleUsersDetailsChange, EntityId: %s", threadId);
FirebaseEventCombo c = listenerAndRefs.get(USER_PREFIX + threadId);
if (c != null)
c.breakCombo();
listenerAndRefs.remove(USER_PREFIX + threadId);
handledAddedUsersToThreadIDs.remove(threadId);
}
public void messagesOn(String threadId, Deferred<BThread, Void , Void> deferred){
if (DEBUG) Timber.v("messagesOn, EntityID: %s", threadId);
// Check if handled.
if (handledMessagesThreadsID.contains(threadId))
return;
handledMessagesThreadsID.add(threadId);
final DatabaseReference threadRef = FirebasePaths.threadRef(threadId);
Query messagesQuery = threadRef.child(BFirebaseDefines.Path.BMessagesPath);
final BThread thread = DaoCore.fetchOrCreateEntityWithEntityID(BThread.class, threadId);
final List<BMessage> messages = thread.getMessagesWithOrder(DaoCore.ORDER_DESC);
final InMessagesListener incomingMessagesListener = new InMessagesListener(handlerMessages, threadId, deferred);
/**
* If the thread was deleted or has no message we first check for his deletion date.
* If has deletion date we listen to message from this day on, Else we will get the last messages.
*
*
* Limiting the messages here can cause a problems,
* If we reach the limit with new messages the new one wont be triggered and he user wont see them.
* (He would see his if he kill the chat because they are saved locally).
*
* */
if (thread.isDeleted() || messages.size() == 0)
{
if (DEBUG) Timber.v("Thread is Deleted, ID: %s", threadId);
BThreadWrapper wrapper = new BThreadWrapper(thread);
wrapper.threadDeletedDate().done(new DoneCallback<Long>() {
@Override
public void onDone(Long aLong) {
Query query = threadRef.child(BFirebaseDefines.Path.BMessagesPath);
// Not deleted
if (aLong==null)
{
query = query.limitToLast(BDefines.MAX_MESSAGES_TO_PULL);
}
// Deleted.
else
{
if (DEBUG) Timber.d("Thread Deleted Value: %s", aLong);
// The plus 1 is needed so we wont receive the last message again.
query = query.startAt(aLong);
}
FirebaseEventCombo combo = getCombo(MSG_PREFIX + thread.getEntityID(), query.getRef().toString(), incomingMessagesListener);
query.addChildEventListener(combo.getListener());
}
})
.fail(new FailCallback<DatabaseError>() {
@Override
public void onFail(DatabaseError firebaseError) {
// Default behavior if failed.
Query query = threadRef.child(BFirebaseDefines.Path.BMessagesPath);
query = query.limitToLast(BDefines.MAX_MESSAGES_TO_PULL);
FirebaseEventCombo combo = getCombo(MSG_PREFIX + thread.getEntityID(), query.getRef().toString(), incomingMessagesListener);
query.addChildEventListener(combo.getListener());
}
});
return;
}
else if (messages.size() > 0)
{
if (DEBUG) Timber.d("messagesOn, Messages size: %s, LastMessage: %s", messages.size(), messages.get(0).getText());
// The plus 1 is needed so we wont receive the last message again.
messagesQuery = messagesQuery.startAt(messages.get(0).getDate().getTime() + 1);
// Set any message that received as new.
incomingMessagesListener.setNew(true);
}
else
{
messagesQuery = messagesQuery.limitToLast(BDefines.MAX_MESSAGES_TO_PULL);
}
FirebaseEventCombo combo = getCombo(MSG_PREFIX + thread.getEntityID(), messagesQuery.getRef().toString(), incomingMessagesListener);
messagesQuery.addChildEventListener(combo.getListener());
}
public void messagesOff(String threadId){
if (DEBUG) Timber.v("messagesOff, EntityID: %s", threadId);
if (DEBUG) Timber.v("handleUsersDetailsChange, EntityId: %s", threadId);
FirebaseEventCombo c = listenerAndRefs.get(MSG_PREFIX + threadId);
if (c != null)
c.breakCombo();
listenerAndRefs.remove(MSG_PREFIX + threadId);
handledMessagesThreadsID.remove(threadId);
}
public void threadOn(String threadId, Deferred<BThread, Void, Void> deferred){
if (DEBUG) Timber.v("threadOn, EntityID: %s",threadId);
if (!isListeningToThread(threadId))
{
threadsIds.add(threadId);
final DatabaseReference threadRef = FirebasePaths.threadRef(threadId);
// Add an observer to the thread details so we get
// updated when the thread details change
DatabaseReference detailsRef = threadRef.child(BFirebaseDefines.Path.BDetailsPath);
FirebaseEventCombo combo = getCombo(threadId, detailsRef.toString(), new ThreadUpdateChangeListener(threadId, handlerThread, deferred));
detailsRef.addValueEventListener(combo.getListener());
}
else if (DEBUG) Timber.e("Thread is already handled..");
}
public void threadOff(String threadId){
if (DEBUG) Timber.v("threadOff, EntityID: %s", threadId);
FirebaseEventCombo c = listenerAndRefs.get(threadId);
if (c != null)
c.breakCombo();
listenerAndRefs.remove(threadId);
threadsIds.remove(threadId);
}
private ChildEventListener threadAddedListener = new ChildEventListener() {
@Override
public void onChildAdded(final DataSnapshot snapshot, String s) {
post(new Runnable() {
@Override
public void run() {
boolean publicThread =false;
String threadFirebaseID;
BPath path = BPath.pathWithPath(snapshot.getRef().toString());
if (path.isEqualToComponent(BFirebaseDefines.Path.BPublicThreadPath))
{
threadFirebaseID = path.idForIndex(0);
publicThread = true;
}
else threadFirebaseID = path.idForIndex(1);
if (DEBUG) Timber.i("Thread is added, Thread EntityID: %s, Listening: %s", threadFirebaseID, isListeningToThread(threadFirebaseID));
if (!isListeningToThread(threadFirebaseID))
{
BThreadWrapper wrapper = new BThreadWrapper(threadFirebaseID);
// Starting to listen to thread changes.
wrapper.on();
wrapper.messagesOn();
wrapper.usersOn();
BUser currentUser = BNetworkManager.sharedManager().getNetworkAdapter().currentUserModel();
// Add the current user to the thread if needed. Only if not public.
if (!publicThread &&
!wrapper.getModel().hasUser(currentUser))
{
wrapper.addUser(BUserWrapper.initWithModel(currentUser));
BThread thread = wrapper.getModel();
thread.setType(BThreadEntity.Type.Private);
DaoCore.createEntity(thread);
DaoCore.connectUserAndThread(currentUser, thread);
}
// Triggering thread added events.
onThreadIsAdded(threadFirebaseID);
}
}
});
}
//region Not used.
@Override
public void onChildChanged(DataSnapshot snapshot, String s) {
}
@Override
public void onChildRemoved(DataSnapshot snapshot) {
}
@Override
public void onChildMoved(DataSnapshot snapshot, String s) {
}
@Override
public void onCancelled(DatabaseError error) {
}
//endregion
};
private ChildEventListener followerEventListener = new ChildEventListener() {
@Override
public void onChildAdded(final DataSnapshot snapshot, String s) {
post(new Runnable() {
@Override
public void run() {
FollowerLink follower = (FollowerLink) BFirebaseInterface.objectFromSnapshot(snapshot);
onFollowerAdded(follower);
BUserWrapper wrapper = BUserWrapper.initWithModel(follower.getBUser());
wrapper.once();
wrapper.metaOn();
}
});
}
@Override
public void onChildChanged(DataSnapshot snapshot, String s) {
}
@Override
public void onChildRemoved(DataSnapshot snapshot) {
FollowerLink follower = (FollowerLink) BFirebaseInterface.objectFromSnapshot(snapshot);
DaoCore.deleteEntity(follower);
onFollowerRemoved();
}
@Override
public void onChildMoved(DataSnapshot snapshot, String s) {
}
@Override
public void onCancelled(DatabaseError firebaseError) {
}
};
/** Check to see if the given thread id is already handled by this class.
* @return true if handled.*/
@Override
public boolean isListeningToThread(String entityID){
return threadsIds.contains(entityID);
}
private ChildEventListener followsEventListener = new ChildEventListener() {
@Override
public void onChildAdded(final DataSnapshot snapshot, String s) {
post(new Runnable() {
@Override
public void run() {
FollowerLink follower = (FollowerLink) BFirebaseInterface.objectFromSnapshot(snapshot);
BUserWrapper wrapper = BUserWrapper.initWithModel(follower.getBUser());
wrapper.once();
wrapper.metaOn();
}
});
}
@Override
public void onChildChanged(DataSnapshot snapshot, String s) {
}
@Override
public void onChildRemoved(DataSnapshot snapshot) {
FollowerLink follower = (FollowerLink) BFirebaseInterface.objectFromSnapshot(snapshot);
DaoCore.deleteEntity(follower);
onUserToFollowRemoved();
}
@Override
public void onChildMoved(DataSnapshot snapshot, String s) {
}
@Override
public void onCancelled(DatabaseError firebaseError) {
}
};
/** Remove listeners from thread id. The listener's are The thread details, messages and added users.*/
public void stopListeningToThread(String threadID){
if (DEBUG) Timber.v("stopListeningToThread, ThreadID: %s", threadID);
if (listenerAndRefs.containsKey(threadID) && listenerAndRefs.get(threadID) != null)
listenerAndRefs.get(threadID).breakCombo();
if (listenerAndRefs.containsKey(MSG_PREFIX + threadID) && listenerAndRefs.get(MSG_PREFIX + threadID) != null)
listenerAndRefs.get(MSG_PREFIX + threadID).breakCombo();
if (listenerAndRefs.containsKey(USER_PREFIX + threadID) && listenerAndRefs.get(USER_PREFIX + threadID) != null)
listenerAndRefs.get(USER_PREFIX + threadID).breakCombo();
// Removing the combo's from the Map.
listenerAndRefs.remove(threadID);
listenerAndRefs.remove(MSG_PREFIX + threadID);
listenerAndRefs.remove(USER_PREFIX + threadID);
threadsIds.remove(threadID);
handledMessagesThreadsID.remove(threadID);
handledAddedUsersToThreadIDs.remove(threadID);
}
/** Get a combo object for given index, ref and listener.
* The combo is used to keep the firebase ref path and their listeners so we can remove the listener when needed.*/
private FirebaseEventCombo getCombo(String index, String ref, FirebaseGeneralEvent listener){
FirebaseEventCombo combo = FirebaseEventCombo.getNewInstance(listener, ref);
saveCombo(index, combo);
return combo;
}
/** Save the combo to the combo map.*/
private void saveCombo(String index, FirebaseEventCombo combo){
listenerAndRefs.put(index, combo);
}
@Override
public void addEvent(Event appEvents){
Event e = events.put(appEvents.getTag(), appEvents);
if (e != null )
e.kill();
}
/** Removes an app event by tag.*/
@Override
public boolean removeEventByTag(String tag){
if (DEBUG) Timber.v("removeEventByTag, Tag: %s", tag);
if (StringUtils.isEmpty(tag)){
return false;
}
Event e = events.remove(tag);
if (e != null)
{
if (DEBUG) Timber.i("killing event, Tag: %s", e.getTag());
e.kill();
}
return e != null;
}
/** Check if there is a AppEvent listener with the currnt tag, Could be AppEvent or one of his child(MessageEventListener, ThreadEventListener, UserEventListener).
* @return true if found.*/
@Override
public boolean isEventTagExist(String tag){
return events.containsKey(tag);
}
/**
* Remove all firebase listeners and all app events listeners.
* After removing all class list will be cleared.
**/
public void removeAll(){
DatabaseReference userRef = FirebasePaths.userRef(observedUserEntityID);
userRef.child(BFirebaseDefines.Path.BThreadPath).removeEventListener(threadAddedListener);
userRef.child(BFirebaseDefines.Path.FollowerLinks).removeEventListener(followerEventListener);
userRef.child(BFirebaseDefines.Path.BFollows).removeEventListener(followsEventListener);
observedUserEntityID = "";
FirebasePaths.publicThreadsRef().removeEventListener(threadAddedListener);
Set<String> Keys = listenerAndRefs.keySet();
FirebaseEventCombo combo;
Iterator<String> iter = Keys.iterator();
String key;
while (iter.hasNext())
{
key = iter.next();
if (DEBUG) Timber.d("Removing listener, Key: %s", key);
combo = listenerAndRefs.get(key);
if (combo != null)
combo.breakCombo();
}
Executor.getInstance().restart();
clearLists();
}
/**
*Clearing all the lists.
**/
private void clearLists(){
listenerAndRefs.clear();
// Killing all events
for (Event e : events.values())
{
if (e != null)
e.kill();
}
events.clear();
threadsIds.clear();
usersIds.clear();
handledUsersMetaIds.clear();
handledMessagesThreadsID.clear();
handledAddedUsersToThreadIDs.clear();
handleFollowDataChangeUsersId.clear();
}
/** get the current user entity so we know not to listen to his details and so on.*/
public static String getCurrentUserId() {
return BNetworkManager.sharedManager().getNetworkAdapter().currentUserModel().getEntityID();
}
/** Print save data of this class. Id's List and listener and refs. Used for debugging.*/
public void printDataReport(){
for (String s : threadsIds)
Timber.i("Listening to thread ID: " + s);
for (String u: usersIds)
Timber.i("handled users details, user ID: %s", u);
for (String s : handledAddedUsersToThreadIDs)
Timber.i("handled added users, Thread ID: %s", s);
for (String s : handledMessagesThreadsID)
Timber.i("handled messages, Thread ID: %s", s);
}
private void post(Runnable runnable){
Executor.getInstance().execute(runnable);
}
public static class Executor {
// Sets the amount of time an idle thread waits before terminating
private static final int KEEP_ALIVE_TIME = 20;
// Sets the Time Unit to seconds
private static final TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS;
private LinkedBlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<Runnable>();
/*
* Gets the number of available cores
* (not always the same as the maximum number of cores)
*/
private static int NUMBER_OF_CORES =
Runtime.getRuntime().availableProcessors();
private static int MAX_THREADS = 15;
private ThreadPoolExecutor threadPool;
private static Executor instance;
public static Executor getInstance() {
if (instance == null)
instance = new Executor();
return instance;
}
private Executor(){
if (NUMBER_OF_CORES <= 0)
NUMBER_OF_CORES = 2;
// Creates a thread pool manager
threadPool = new ThreadPoolExecutor(
NUMBER_OF_CORES, // Initial pool size
NUMBER_OF_CORES, // Max pool size
KEEP_ALIVE_TIME,
KEEP_ALIVE_TIME_UNIT,
workQueue);
}
public void execute(Runnable runnable){
threadPool.execute(runnable);
}
private void restart(){
threadPool.shutdownNow();
instance = new Executor();
}
}
}