From dc66de02fb39163762a570d048639472fd93a786 Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Fri, 24 May 2019 22:51:13 +0400 Subject: [PATCH] Implemented showing user presence --- .../chronosx88/influence/XMPPConnection.java | 7 +++ .../influence/contracts/CoreContracts.kt | 2 + .../influence/helpers/HashUtils.java | 55 ------------------- .../influence/helpers/NetworkHandler.java | 34 +++++++++++- .../chronosx88/influence/logic/ChatLogic.java | 19 ++++++- .../appEvents/UserPresenceChangedEvent.java | 27 +++++++++ .../influence/presenters/ChatPresenter.kt | 27 +++++++++ .../presenters/DialogListPresenter.java | 7 ++- .../influence/views/ChatActivity.kt | 9 ++- app/src/main/res/layout/activity_chat.xml | 24 ++++++-- app/src/main/res/values-ru-rRU/strings.xml | 2 + app/src/main/res/values/strings.xml | 2 + 12 files changed, 150 insertions(+), 65 deletions(-) delete mode 100644 app/src/main/java/io/github/chronosx88/influence/helpers/HashUtils.java create mode 100644 app/src/main/java/io/github/chronosx88/influence/models/appEvents/UserPresenceChangedEvent.java diff --git a/app/src/main/java/io/github/chronosx88/influence/XMPPConnection.java b/app/src/main/java/io/github/chronosx88/influence/XMPPConnection.java index c8d2e44..bc7e945 100644 --- a/app/src/main/java/io/github/chronosx88/influence/XMPPConnection.java +++ b/app/src/main/java/io/github/chronosx88/influence/XMPPConnection.java @@ -38,7 +38,9 @@ import org.jivesoftware.smack.tcp.XMPPTCPConnection; import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration; import org.jivesoftware.smackx.mam.MamManager; import org.jivesoftware.smackx.vcardtemp.VCardManager; +import org.jxmpp.jid.BareJid; import org.jxmpp.jid.EntityBareJid; +import org.jxmpp.jid.impl.JidCreate; import java.io.IOException; import java.util.Set; @@ -117,6 +119,7 @@ public class XMPPConnection implements ConnectionListener { reconnectionManager.enableAutomaticReconnection(); roster = roster.getInstanceFor(connection); roster.setSubscriptionMode(Roster.SubscriptionMode.accept_all); + roster.addPresenceEventListener(networkHandler); mamManager = MamManager.getInstanceFor(connection); try { if(mamManager.isSupported()) { @@ -217,4 +220,8 @@ public class XMPPConnection implements ConnectionListener { return false; } } + + public Presence getUserPresence(BareJid jid) { + return roster.getPresence(jid); + } } diff --git a/app/src/main/java/io/github/chronosx88/influence/contracts/CoreContracts.kt b/app/src/main/java/io/github/chronosx88/influence/contracts/CoreContracts.kt index 4a913ed..6f0ac54 100644 --- a/app/src/main/java/io/github/chronosx88/influence/contracts/CoreContracts.kt +++ b/app/src/main/java/io/github/chronosx88/influence/contracts/CoreContracts.kt @@ -78,6 +78,7 @@ interface CoreContracts { interface IChatLogicContract { fun sendMessage(text: String): MessageEntity? + fun getUserStatus(): Boolean } interface IChatPresenterContract { @@ -88,6 +89,7 @@ interface CoreContracts { interface IChatViewContract { fun setAdapter(adapter: MessagesListAdapter) + fun setUserStatus(status: String) } // -----SettingsFragment----- diff --git a/app/src/main/java/io/github/chronosx88/influence/helpers/HashUtils.java b/app/src/main/java/io/github/chronosx88/influence/helpers/HashUtils.java deleted file mode 100644 index 0069bb1..0000000 --- a/app/src/main/java/io/github/chronosx88/influence/helpers/HashUtils.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2019 ChronosX88 - * - * 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 io.github.chronosx88.influence.helpers; - -import java.io.UnsupportedEncodingException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; - -public class HashUtils { - public static String sha1(final String text) { - try { - MessageDigest md = MessageDigest.getInstance("SHA-1"); - md.update(text.getBytes("UTF-8"), 0, text.length()); - byte[] sha1hash = md.digest(); - return hashToString(sha1hash); - } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); - } catch (UnsupportedEncodingException e) { - e.printStackTrace(); - } - return null; - } - - private static String hashToString(final byte[] buf) { - if (buf == null) return ""; - int l = buf.length; - StringBuffer result = new StringBuffer(2 * l); - for (int i = 0; i < buf.length; i++) { - appendByte(result, buf[i]); - } - return result.toString(); - } - - private final static String HEX_PACK = "0123456789ABCDEF"; - - private static void appendByte(final StringBuffer sb, final byte b) { - sb - .append(HEX_PACK.charAt((b >> 4) & 0x0f)) - .append(HEX_PACK.charAt(b & 0x0f)); - } -} diff --git a/app/src/main/java/io/github/chronosx88/influence/helpers/NetworkHandler.java b/app/src/main/java/io/github/chronosx88/influence/helpers/NetworkHandler.java index 14bd2e2..adb0369 100644 --- a/app/src/main/java/io/github/chronosx88/influence/helpers/NetworkHandler.java +++ b/app/src/main/java/io/github/chronosx88/influence/helpers/NetworkHandler.java @@ -19,16 +19,23 @@ package io.github.chronosx88.influence.helpers; import com.instacart.library.truetime.TrueTime; import org.greenrobot.eventbus.EventBus; +import org.jivesoftware.smack.PresenceListener; import org.jivesoftware.smack.chat2.Chat; import org.jivesoftware.smack.chat2.IncomingChatMessageListener; import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smack.packet.Presence; +import org.jivesoftware.smack.roster.PresenceEventListener; +import org.jxmpp.jid.BareJid; import org.jxmpp.jid.EntityBareJid; +import org.jxmpp.jid.FullJid; +import org.jxmpp.jid.Jid; import io.github.chronosx88.influence.models.GenericMessage; import io.github.chronosx88.influence.models.appEvents.LastMessageEvent; import io.github.chronosx88.influence.models.appEvents.NewMessageEvent; +import io.github.chronosx88.influence.models.appEvents.UserPresenceChangedEvent; -public class NetworkHandler implements IncomingChatMessageListener { +public class NetworkHandler implements IncomingChatMessageListener, PresenceEventListener { private final static String LOG_TAG = "NetworkHandler"; @Override @@ -44,4 +51,29 @@ public class NetworkHandler implements IncomingChatMessageListener { EventBus.getDefault().post(new NewMessageEvent(chatID, messageID)); EventBus.getDefault().post(new LastMessageEvent(chatID, new GenericMessage(LocalDBWrapper.getMessageByID(messageID)))); } + + @Override + public void presenceAvailable(FullJid address, Presence availablePresence) { + EventBus.getDefault().post(new UserPresenceChangedEvent(address.asBareJid().asUnescapedString(), availablePresence.isAvailable())); + } + + @Override + public void presenceUnavailable(FullJid address, Presence presence) { + EventBus.getDefault().post(new UserPresenceChangedEvent(address.asBareJid().asUnescapedString(), presence.isAvailable())); + } + + @Override + public void presenceError(Jid address, Presence errorPresence) { + EventBus.getDefault().post(new UserPresenceChangedEvent(address.asBareJid().asUnescapedString(), errorPresence.isAvailable())); + } + + @Override + public void presenceSubscribed(BareJid address, Presence subscribedPresence) { + + } + + @Override + public void presenceUnsubscribed(BareJid address, Presence unsubscribedPresence) { + + } } \ No newline at end of file diff --git a/app/src/main/java/io/github/chronosx88/influence/logic/ChatLogic.java b/app/src/main/java/io/github/chronosx88/influence/logic/ChatLogic.java index e625e3e..07f28be 100644 --- a/app/src/main/java/io/github/chronosx88/influence/logic/ChatLogic.java +++ b/app/src/main/java/io/github/chronosx88/influence/logic/ChatLogic.java @@ -18,6 +18,7 @@ package io.github.chronosx88.influence.logic; import com.instacart.library.truetime.TrueTime; +import org.jivesoftware.smack.packet.Presence; import org.jxmpp.jid.EntityBareJid; import org.jxmpp.jid.impl.JidCreate; import org.jxmpp.stringprep.XmppStringprepException; @@ -58,10 +59,26 @@ public class ChatLogic implements CoreContracts.IChatLogicContract { } }).start(); } - long messageID = LocalDBWrapper.createMessageEntry(chatID, AppHelper.getJid(), TrueTime.now().getTime(), text, false, false); + long messageID = LocalDBWrapper.createMessageEntry(chatID, AppHelper.getJid(), TrueTime.now().getTime(), text, true, false); return LocalDBWrapper.getMessageByID(messageID); } else { return null; } } + + @Override + public boolean getUserStatus() { + if(AppHelper.getXmppConnection() != null) { + if(AppHelper.getXmppConnection().isConnectionAlive()) { + Presence presence = null; + try { + presence = AppHelper.getXmppConnection().getUserPresence(JidCreate.bareFrom(chatID)); + } catch (XmppStringprepException e) { + e.printStackTrace(); + } + return presence.isAvailable(); + } + } + return false; + } } diff --git a/app/src/main/java/io/github/chronosx88/influence/models/appEvents/UserPresenceChangedEvent.java b/app/src/main/java/io/github/chronosx88/influence/models/appEvents/UserPresenceChangedEvent.java new file mode 100644 index 0000000..9334507 --- /dev/null +++ b/app/src/main/java/io/github/chronosx88/influence/models/appEvents/UserPresenceChangedEvent.java @@ -0,0 +1,27 @@ +/* + * Copyright 2019 ChronosX88 + * + * 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 io.github.chronosx88.influence.models.appEvents; + +public class UserPresenceChangedEvent { + public final String jid; + public final boolean status; + + public UserPresenceChangedEvent(String jid, boolean status) { + this.jid = jid; + this.status = status; + } +} diff --git a/app/src/main/java/io/github/chronosx88/influence/presenters/ChatPresenter.kt b/app/src/main/java/io/github/chronosx88/influence/presenters/ChatPresenter.kt index a661c2d..7a5839c 100644 --- a/app/src/main/java/io/github/chronosx88/influence/presenters/ChatPresenter.kt +++ b/app/src/main/java/io/github/chronosx88/influence/presenters/ChatPresenter.kt @@ -28,11 +28,16 @@ import io.github.chronosx88.influence.logic.ChatLogic import io.github.chronosx88.influence.models.GenericMessage import io.github.chronosx88.influence.models.appEvents.LastMessageEvent import io.github.chronosx88.influence.models.appEvents.NewMessageEvent +import io.github.chronosx88.influence.models.appEvents.UserPresenceChangedEvent import io.github.chronosx88.influence.models.roomEntities.ChatEntity import io.github.chronosx88.influence.models.roomEntities.MessageEntity +import java9.util.concurrent.CompletableFuture +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.async import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode +import kotlin.math.log class ChatPresenter(private val view: CoreContracts.IChatViewContract, private val chatID: String) : CoreContracts.IChatPresenterContract { private val logic: CoreContracts.IChatLogicContract @@ -48,6 +53,7 @@ class ChatPresenter(private val view: CoreContracts.IChatViewContract, private v holdersConfig.setIncomingTextLayout(R.layout.item_incoming_text_message_custom) chatAdapter = MessagesListAdapter(AppHelper.getJid(), holdersConfig, AvatarImageLoader()) view.setAdapter(chatAdapter) + getUserStatus() EventBus.getDefault().register(this) } @@ -85,4 +91,25 @@ class ChatPresenter(private val view: CoreContracts.IChatViewContract, private v LocalDBWrapper.updateChatUnreadMessagesCount(chatEntity.jid, 0) } } + + @Subscribe(threadMode = ThreadMode.MAIN) + public fun onPresenceChanged(event: UserPresenceChangedEvent) { + if(event.jid == (chatID)) { + if(event.status) view.setUserStatus(AppHelper.getContext().getString(R.string.online)) else view.setUserStatus(AppHelper.getContext().getString(R.string.offline)) + } + } + + private fun getUserStatus() { + CompletableFuture.supplyAsync { + return@supplyAsync logic.getUserStatus() + }.thenAccept { status -> + AppHelper.getMainUIThread().post({ + if(status) { + view.setUserStatus(AppHelper.getContext().getString(R.string.online)) + } else { + view.setUserStatus(AppHelper.getContext().getString(R.string.offline)) + } + }) + } + } } diff --git a/app/src/main/java/io/github/chronosx88/influence/presenters/DialogListPresenter.java b/app/src/main/java/io/github/chronosx88/influence/presenters/DialogListPresenter.java index 5081a6c..a501447 100644 --- a/app/src/main/java/io/github/chronosx88/influence/presenters/DialogListPresenter.java +++ b/app/src/main/java/io/github/chronosx88/influence/presenters/DialogListPresenter.java @@ -52,7 +52,12 @@ public class DialogListPresenter implements CoreContracts.IDialogListPresenterCo private CoreContracts.IChatListViewContract view; private CoreContracts.IDialogListLogicContract logic; private DialogsListAdapter dialogListAdapter = new DialogsListAdapter<>(R.layout.item_dialog_custom, new AvatarImageLoader()); - private Comparator dialogComparator = (dialog1, dialog2) -> Long.compare(dialog2.getLastMessage().getCreatedAt().getTime(), dialog1.getLastMessage().getCreatedAt().getTime()); + private Comparator dialogComparator = (dialog1, dialog2) -> { + if(dialog2.getLastMessage() != null && dialog1.getLastMessage() != null) { + return Long.compare(dialog2.getLastMessage().getCreatedAt().getTime(), dialog1.getLastMessage().getCreatedAt().getTime()); + } + return 0; + }; public DialogListPresenter(CoreContracts.IChatListViewContract view) { this.view = view; diff --git a/app/src/main/java/io/github/chronosx88/influence/views/ChatActivity.kt b/app/src/main/java/io/github/chronosx88/influence/views/ChatActivity.kt index d2d96f8..46cb4f9 100644 --- a/app/src/main/java/io/github/chronosx88/influence/views/ChatActivity.kt +++ b/app/src/main/java/io/github/chronosx88/influence/views/ChatActivity.kt @@ -38,12 +38,14 @@ import io.github.chronosx88.influence.models.GenericMessage import io.github.chronosx88.influence.models.roomEntities.MessageEntity import io.github.chronosx88.influence.presenters.ChatPresenter import kotlinx.android.synthetic.main.activity_chat.view.* +import org.jetbrains.anko.find class ChatActivity : AppCompatActivity(), CoreContracts.IChatViewContract { private var messageList: MessagesList? = null private var messageInput: MessageInput? = null private var chatNameTextView: TextView? = null private var chatAvatar: ImageView? = null + private var userStatus: TextView? = null private var presenter: ChatPresenter? = null override fun onCreate(savedInstanceState: Bundle?) { @@ -59,7 +61,8 @@ class ChatActivity : AppCompatActivity(), CoreContracts.IChatViewContract { supportActionBar!!.setHomeButtonEnabled(true) messageList = findViewById(R.id.messages_list) messageList!!.layoutManager = LinearLayoutManager(this) - chatNameTextView = findViewById(R.id.appbar_username) + chatNameTextView = find(R.id.appbar_username) + userStatus = find(R.id.user_status_text) chatAvatar = findViewById(R.id.profile_image_chat_activity) messageInput = findViewById(R.id.message_input) messageInput!!.setInputListener { @@ -107,4 +110,8 @@ class ChatActivity : AppCompatActivity(), CoreContracts.IChatViewContract { .buildRound(firstLetter, ColorGenerator.MATERIAL.getColor(firstLetter))) } } + + override fun setUserStatus(status: String) { + userStatus!!.text = status + } } diff --git a/app/src/main/res/layout/activity_chat.xml b/app/src/main/res/layout/activity_chat.xml index f790b33..e5c242c 100644 --- a/app/src/main/res/layout/activity_chat.xml +++ b/app/src/main/res/layout/activity_chat.xml @@ -39,15 +39,27 @@ android:layout_height="40dp" android:id="@+id/profile_image_chat_activity"/> - + android:orientation="vertical"> + + + diff --git a/app/src/main/res/values-ru-rRU/strings.xml b/app/src/main/res/values-ru-rRU/strings.xml index dbef96d..720dfb4 100644 --- a/app/src/main/res/values-ru-rRU/strings.xml +++ b/app/src/main/res/values-ru-rRU/strings.xml @@ -24,4 +24,6 @@ Выйти из аккаунта Войти Неверный JabberID! + Не в сети + В сети \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4469e8e..352faed 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -23,4 +23,6 @@ Log out from account Sign In Invalid JabberID! + Offline + Online