/* * Kontalk Java client * Copyright (C) 2016 Kontalk Devteam <[email protected]> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.kontalk.system; import java.util.Map; import java.util.Timer; import java.util.TimerTask; import java.util.WeakHashMap; import java.util.concurrent.TimeUnit; import org.jivesoftware.smackx.chatstates.ChatState; import org.kontalk.client.Client; import org.kontalk.model.Contact; import org.kontalk.model.chat.Chat; import org.kontalk.model.chat.SingleChat; import org.kontalk.persistence.Config; /** * Manager handling own chat status for all chats. * @author Alexander Bikadorov {@literal <[email protected]>} */ final class ChatStateManager { private static final int COMPOSING_TO_PAUSED = 15; // seconds private final Client mClient; private final Map<Chat, MyChatState> mChatStateCache = new WeakHashMap<>(); private final Timer mTimer = new Timer("Chat State Timer", true); public ChatStateManager(Client client) { mClient = client; } void handleOwnChatStateEvent(Chat chat, ChatState state) { if (!mChatStateCache.containsKey(chat)) { if (state == ChatState.gone) // weare and stay at the default state return; mChatStateCache.put(chat, new MyChatState(chat)); } mChatStateCache.get(chat).handleState(state); } void imGone() { mChatStateCache.values().forEach(chatState -> chatState.handleState(ChatState.gone)); } private class MyChatState { private final Chat mChat; private ChatState mCurrentState; private TimerTask mScheduledStateSet = null; private MyChatState(Chat chat) { mChat = chat; } private void handleState(ChatState state) { if (mScheduledStateSet != null) // whatever we wanted to set next, thats obsolete now mScheduledStateSet.cancel(); if (state != mCurrentState) this.setNewState(state); if (state == ChatState.composing) { mScheduledStateSet = new TimerTask() { @Override public void run() { // NOTE: using 'inactive' instead of 'paused' here as // 'inactive' isn't send at all MyChatState.this.handleState(ChatState.inactive); } }; mTimer.schedule(mScheduledStateSet, TimeUnit.SECONDS.toMillis(COMPOSING_TO_PAUSED)); } } private void setNewState(ChatState state) { // currently set states from XEP-0085: active, inactive, composing mCurrentState = state; if (state == ChatState.active || !(mChat instanceof SingleChat)) // don't send for groups (TODO (?)) // 'active' is send inside a message return; Contact contact = ((SingleChat) mChat).getMember().getContact(); if (contact.isMe() || contact.isBlocked() || contact.isDeleted()) return; if (Config.getInstance().getBoolean(Config.NET_SEND_CHAT_STATE)) mClient.sendChatState(contact.getJID(), mChat.getXMPPID(), state); } } }