/* * Copyright (C) 2007-2008 Esmertec AG. Copyright (C) 2007-2008 The Android Open * Source Project * * 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 org.awesomeapp.messenger.service.adapters; import org.awesomeapp.messenger.crypto.IOtrChatSession; import org.awesomeapp.messenger.crypto.otr.OtrChatListener; import org.awesomeapp.messenger.crypto.otr.OtrChatManager; import org.awesomeapp.messenger.crypto.otr.OtrChatSessionAdapter; import org.awesomeapp.messenger.crypto.otr.OtrDataHandler; import org.awesomeapp.messenger.crypto.otr.OtrDataHandler.Transfer; import org.awesomeapp.messenger.crypto.otr.OtrDebugLogger; import org.awesomeapp.messenger.model.Address; import org.awesomeapp.messenger.plugin.xmpp.XmppAddress; import org.awesomeapp.messenger.service.IChatListener; import org.awesomeapp.messenger.service.IDataListener; import im.zom.messenger.R; import org.awesomeapp.messenger.ImApp; import org.awesomeapp.messenger.model.ChatGroup; import org.awesomeapp.messenger.model.ChatGroupManager; import org.awesomeapp.messenger.model.ChatSession; import org.awesomeapp.messenger.model.Contact; import org.awesomeapp.messenger.model.GroupListener; import org.awesomeapp.messenger.model.GroupMemberListener; import org.awesomeapp.messenger.model.ImConnection; import org.awesomeapp.messenger.model.ImEntity; import org.awesomeapp.messenger.model.ImErrorInfo; import org.awesomeapp.messenger.model.MessageListener; import org.awesomeapp.messenger.model.Presence; import org.awesomeapp.messenger.provider.Imps; import org.awesomeapp.messenger.util.SystemServices; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import net.java.otr4j.OtrEngineListener; import net.java.otr4j.session.SessionID; import net.java.otr4j.session.SessionStatus; import org.awesomeapp.messenger.service.RemoteImService; import org.awesomeapp.messenger.service.StatusBarNotifier; import org.jivesoftware.smack.util.StringUtils; import android.content.ContentResolver; import android.content.ContentUris; import android.content.ContentValues; import android.database.Cursor; import android.net.Uri; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.provider.BaseColumns; import android.support.annotation.NonNull; import android.util.Log; public class ChatSessionAdapter extends org.awesomeapp.messenger.service.IChatSession.Stub { private static final String NON_CHAT_MESSAGE_SELECTION = Imps.Messages.TYPE + "!=" + Imps.MessageType.INCOMING + " AND " + Imps.Messages.TYPE + "!=" + Imps.MessageType.OUTGOING; /** The registered remote listeners. */ final RemoteCallbackList<IChatListener> mRemoteListeners = new RemoteCallbackList<IChatListener>(); ImConnectionAdapter mConnection; ChatSessionManagerAdapter mChatSessionManager; ChatSession mChatSession; ListenerAdapter mListenerAdapter; boolean mIsGroupChat; StatusBarNotifier mStatusBarNotifier; private ContentResolver mContentResolver; /*package*/Uri mChatURI; private Uri mMessageURI; private boolean mConvertingToGroupChat; private static final int MAX_HISTORY_COPY_COUNT = 10; private HashMap<String, Integer> mContactStatusMap = new HashMap<String, Integer>(); private boolean mHasUnreadMessages; private RemoteImService service = null; private HashMap<String, OtrChatSessionAdapter> mOtrChatSessions; private SessionStatus mLastSessionStatus = null; private OtrDataHandler mDataHandler; private IDataListener mDataListener; private DataHandlerListenerImpl mDataHandlerListener; private boolean mAcceptTransfer = false; private boolean mWaitingForResponse = false; private boolean mAcceptAllTransfer = true;//TODO set this via preference, but default true private String mLastFileUrl = null; private long mContactId; public ChatSessionAdapter(ChatSession chatSession, ImConnectionAdapter connection, boolean isNewSession) { mChatSession = chatSession; mConnection = connection; service = connection.getContext(); mContentResolver = service.getContentResolver(); mStatusBarNotifier = service.getStatusBarNotifier(); mChatSessionManager = (ChatSessionManagerAdapter) connection.getChatSessionManager(); mListenerAdapter = new ListenerAdapter(); mOtrChatSessions = new HashMap<String, OtrChatSessionAdapter>(); ImEntity participant = mChatSession.getParticipant(); if (participant instanceof ChatGroup) { init((ChatGroup) participant,isNewSession); } else { init((Contact) participant,isNewSession); } initOtrChatSession(participant); } private void initOtrChatSession (ImEntity participant) { try { if (mConnection != null) { mDataHandler = new OtrDataHandler(mChatSession); mDataHandlerListener = new DataHandlerListenerImpl(); mDataHandler.setDataListener(mDataHandlerListener); OtrChatManager cm = service.getOtrChatManager(); cm.addOtrEngineListener(mListenerAdapter); mChatSession.setMessageListener(new OtrChatListener(cm, mListenerAdapter)); if (participant instanceof Contact) { String key = participant.getAddress().getAddress(); if (!mOtrChatSessions.containsKey(key)) { OtrChatSessionAdapter adapter = new OtrChatSessionAdapter(mConnection.getLoginUser().getAddress().getAddress(), participant, cm); mOtrChatSessions.put(key, adapter); } } else if (participant instanceof ChatGroup) { ChatGroup group = (ChatGroup)mChatSession.getParticipant(); for (Contact contact : group.getMembers()) { String key = contact.getAddress().getAddress(); if (!mOtrChatSessions.containsKey(key)) { OtrChatSessionAdapter adapter = new OtrChatSessionAdapter(mConnection.getLoginUser().getAddress().getAddress(), contact, cm); mOtrChatSessions.put(key, adapter); } } } mDataHandler.setChatId(getId()); } } catch (NullPointerException npe) { Log.e(ImApp.LOG_TAG,"error init OTR session",npe); } } public synchronized IOtrChatSession getDefaultOtrChatSession () { if (mOtrChatSessions.size() > 0) return mOtrChatSessions.entrySet().iterator().next().getValue(); else return null; } private int lastPresence = -1; public void presenceChanged (int newPresence) { if (mChatSession.getParticipant() instanceof Contact) { ((Contact) mChatSession.getParticipant()).getPresence().setStatus(newPresence); if (lastPresence != newPresence && newPresence == Presence.AVAILABLE) sendPostponedMessages(); lastPresence = newPresence; } } private void init(ChatGroup group, boolean isNewSession) { mIsGroupChat = true; mContactId = insertOrUpdateGroupContactInDb(group); group.addMemberListener(mListenerAdapter); try { mChatSessionManager.getChatGroupManager().joinChatGroupAsync(group.getAddress(),group.getName()); mMessageURI = Imps.Messages.getContentUriByThreadId(mContactId); mChatURI = ContentUris.withAppendedId(Imps.Chats.CONTENT_URI, mContactId); if (isNewSession) insertOrUpdateChat(""); for (Contact c : group.getMembers()) { mContactStatusMap.put(c.getName(), c.getPresence().getStatus()); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } private void init(Contact contact, boolean isNewSession) { mIsGroupChat = false; ContactListManagerAdapter listManager = (ContactListManagerAdapter) mConnection.getContactListManager(); mContactId = listManager.queryOrInsertContact(contact); mChatURI = ContentUris.withAppendedId(Imps.Chats.CONTENT_URI, mContactId); if (isNewSession) insertOrUpdateChat(null); mMessageURI = Imps.Messages.getContentUriByThreadId(mContactId); mContactStatusMap.put(contact.getName(), contact.getPresence().getStatus()); } public void reInit () { // insertOrUpdateChat(null); } private ChatGroupManager getGroupManager() { return mConnection.getAdaptee().getChatGroupManager(); } public ChatSession getAdaptee() { return mChatSession; } public Uri getChatUri() { return mChatURI; } public String[] getParticipants() { if (mIsGroupChat) { Contact self = mConnection.getLoginUser(); ChatGroup group = (ChatGroup) mChatSession.getParticipant(); List<Contact> members = group.getMembers(); String[] result = new String[members.size() - 1]; int index = 0; for (Contact c : members) { if (!c.equals(self)) { result[index++] = c.getAddress().getAddress(); } } return result; } else { return new String[] { mChatSession.getParticipant().getAddress().getAddress() }; } } /** * Convert this chat session to a group chat. If it's already a group chat, * nothing will happen. The method works in async mode and the registered * listener will be notified when it's converted to group chat successfully. * * Note that the method is not thread-safe since it's always called from the * UI and Android uses single thread mode for UI. */ public void convertToGroupChat(String nickname) { if (mIsGroupChat || mConvertingToGroupChat) { return; } mConvertingToGroupChat = true; new ChatConvertor().convertToGroupChat(nickname); } public boolean isGroupChatSession() { return mIsGroupChat; } public String getName() { if (isGroupChatSession()) return ((ChatGroup)mChatSession.getParticipant()).getName(); else return ((Contact)mChatSession.getParticipant()).getName(); } public String getAddress() { return mChatSession.getParticipant().getAddress().getAddress(); } public long getId() { return ContentUris.parseId(mChatURI); } public void inviteContact(String contact) { if (!mIsGroupChat) { return; } ContactListManagerAdapter listManager = (ContactListManagerAdapter) mConnection .getContactListManager(); Contact invitee = new Contact(new XmppAddress(contact),contact); getGroupManager().inviteUserAsync((ChatGroup) mChatSession.getParticipant(), invitee); } public void leave() { if (mIsGroupChat) { getGroupManager().leaveChatGroupAsync((ChatGroup) mChatSession.getParticipant()); } mContentResolver.delete(mMessageURI, null, null); mContentResolver.delete(mChatURI, null, null); mStatusBarNotifier.dismissChatNotification(mConnection.getProviderId(), getAddress()); mChatSessionManager.closeChatSession(this); } public void leaveIfInactive() { // if (mChatSession.getHistoryMessages().isEmpty()) { leave(); // } } public boolean sendKnock () { return mChatSession.sendKnock(mConnection.getLoginUser().getAddress().getAddress()); } public void sendMessage(String text, boolean isResend) { if (mConnection.getState() != ImConnection.LOGGED_IN) { // connection has been suspended, save the message without send it long now = System.currentTimeMillis(); insertMessageInDb(null, text, now, Imps.MessageType.QUEUED); return; } org.awesomeapp.messenger.model.Message msg = new org.awesomeapp.messenger.model.Message(text); msg.setID(nextID()); msg.setFrom(mConnection.getLoginUser().getAddress()); msg.setType(Imps.MessageType.QUEUED); long sendTime = System.currentTimeMillis(); if (!isResend) { insertMessageInDb(null, text, sendTime, msg.getType(), 0, msg.getID()); insertOrUpdateChat(text); } int newType = mChatSession.sendMessageAsync(msg); if (msg.getDateTime() != null) sendTime = msg.getDateTime().getTime(); updateMessageInDb(msg.getID(),newType,sendTime); } public boolean offerData(String offerId, String url, String type) { if (mConnection.getState() == ImConnection.SUSPENDED) { // TODO send later return false; } HashMap<String, String> headers = null; if (type != null) { headers = new HashMap<>(); headers.put("Mime-Type", type); } try { Address localUser = mConnection.getLoginUser().getAddress(); if (mChatSession.getParticipant() instanceof Contact) { Address remoteUser = new XmppAddress(getDefaultOtrChatSession().getRemoteUserId()); mDataHandler.offerData(offerId, localUser, remoteUser, url, headers); } else if (mChatSession.getParticipant() instanceof ChatGroup) { ChatGroup group = (ChatGroup)mChatSession.getParticipant(); for (Contact member : group.getMembers()) { mDataHandler.offerData(offerId, localUser, member.getAddress(), url, headers); } } return true; } catch (Exception ioe) { Log.w(ImApp.LOG_TAG,"unable to offer data",ioe); return false; } } /** * Sends a message to other participant(s) in this session without adding it * to the history. * * */ /* public void sendMessageWithoutHistory(String text) { Message msg = new Message(text); // TODO OTRCHAT use a lower level method mChatSession.sendMessageAsync(msg); }*/ /** boolean hasPostponedMessages() { String[] projection = new String[] { BaseColumns._ID, Imps.Messages.BODY, Imps.Messages.PACKET_ID, Imps.Messages.DATE, Imps.Messages.TYPE, Imps.Messages.IS_DELIVERED }; String selection = Imps.Messages.TYPE + "=?"; boolean result = false; Cursor c = mContentResolver.query(mMessageURI, projection, selection, new String[] { Integer.toString(Imps.MessageType.QUEUED) }, null); if (c == null) { RemoteImService.debug("Query error while querying postponed messages"); return false; } else if (c.getCount() > 0) { result = true; } c.close(); return true; }**/ boolean sendingPostponed = false; void sendPostponedMessages() { if (!sendingPostponed) { sendingPostponed = true; String[] projection = new String[]{BaseColumns._ID, Imps.Messages.BODY, Imps.Messages.PACKET_ID, Imps.Messages.DATE, Imps.Messages.TYPE, Imps.Messages.IS_DELIVERED}; String selection = Imps.Messages.TYPE + "=?"; Cursor c = mContentResolver.query(mMessageURI, projection, selection, new String[]{Integer.toString(Imps.MessageType.QUEUED)}, null); if (c == null) { RemoteImService.debug("Query error while querying postponed messages"); return; } if (c.getCount() > 0) { ArrayList<String> messages = new ArrayList<String>(); while (c.moveToNext()) messages.add(c.getString(1)); removeMessageInDb(Imps.MessageType.QUEUED); for (String body : messages) sendMessage(body, false); } c.close(); sendingPostponed = false; } } public void registerChatListener(IChatListener listener) { if (listener != null) { mRemoteListeners.register(listener); if (mDataHandlerListener != null) mDataHandlerListener.checkLastTransferRequest (); } } public void unregisterChatListener(IChatListener listener) { if (listener != null) { mRemoteListeners.unregister(listener); } } public void markAsRead() { if (mHasUnreadMessages) { /** * we want to keep the last message now ContentValues values = new ContentValues(1); values.put(Imps.Chats.LAST_UNREAD_MESSAGE, (String) null); mConnection.getContext().getContentResolver().update(mChatURI, values, null, null); */ String baseUsername = mChatSession.getParticipant().getAddress().getBareAddress(); mStatusBarNotifier.dismissChatNotification(mConnection.getProviderId(), baseUsername); mHasUnreadMessages = false; } } String getNickName(String username) { ImEntity participant = mChatSession.getParticipant(); if (mIsGroupChat) { ChatGroup group = (ChatGroup) participant; List<Contact> members = group.getMembers(); for (Contact c : members) { if (username.equals(c.getAddress().getAddress())) { return c.getAddress().getResource(); } } // not found, impossible String[] parts = username.split("/"); return parts[parts.length-1]; } else { return ((Contact) participant).getName(); } } void onConvertToGroupChatSuccess(ChatGroup group) { Contact oldParticipant = (Contact) mChatSession.getParticipant(); String oldAddress = getAddress(); // mChatSession.setParticipant(group); mChatSessionManager.updateChatSession(oldAddress, this); Uri oldChatUri = mChatURI; Uri oldMessageUri = mMessageURI; init(group,false); //copyHistoryMessages(oldParticipant); mContentResolver.delete(oldMessageUri, NON_CHAT_MESSAGE_SELECTION, null); mContentResolver.delete(oldChatUri, null, null); mListenerAdapter.notifyChatSessionConverted(); mConvertingToGroupChat = false; } /** private void copyHistoryMessages(Contact oldParticipant) { List<org.awesomeapp.messenger.model.Message> historyMessages = mChatSession.getHistoryMessages(); int total = historyMessages.size(); int start = total > MAX_HISTORY_COPY_COUNT ? total - MAX_HISTORY_COPY_COUNT : 0; for (int i = start; i < total; i++) { org.awesomeapp.messenger.model.Message msg = historyMessages.get(i); boolean incoming = msg.getFrom().equals(oldParticipant.getAddress()); String contact = incoming ? oldParticipant.getName() : null; long time = msg.getDateTime().getTime(); insertMessageInDb(contact, msg.getBody(), time, incoming ? Imps.MessageType.INCOMING : Imps.MessageType.OUTGOING); } }*/ void insertOrUpdateChat(String message) { ContentValues values = new ContentValues(2); values.put(Imps.Chats.LAST_MESSAGE_DATE, System.currentTimeMillis()); values.put(Imps.Chats.LAST_UNREAD_MESSAGE, message); values.put(Imps.Chats.GROUP_CHAT, mIsGroupChat); // ImProvider.insert() will replace the chat if it already exist. mContentResolver.insert(mChatURI, values); } private long insertOrUpdateGroupContactInDb(ChatGroup group) { // Insert a record in contacts table ContentValues values = new ContentValues(4); values.put(Imps.Contacts.USERNAME, group.getAddress().getAddress()); values.put(Imps.Contacts.NICKNAME, group.getName()); values.put(Imps.Contacts.CONTACTLIST, ContactListManagerAdapter.LOCAL_GROUP_LIST_ID); values.put(Imps.Contacts.TYPE, Imps.Contacts.TYPE_GROUP); Uri contactUri = ContentUris.withAppendedId( ContentUris.withAppendedId(Imps.Contacts.CONTENT_URI, mConnection.mProviderId), mConnection.mAccountId); ContactListManagerAdapter listManager = (ContactListManagerAdapter) mConnection .getContactListManager(); long id = listManager.queryGroup(group); if (id == -1) { id = ContentUris.parseId(mContentResolver.insert(contactUri, values)); ArrayList<ContentValues> memberValues = new ArrayList<ContentValues>(); Contact self = mConnection.getLoginUser(); for (Contact member : group.getMembers()) { if (!member.equals(self)) { // avoid to insert the user himself ContentValues memberValue = new ContentValues(2); memberValue.put(Imps.GroupMembers.USERNAME, member.getAddress().getAddress()); memberValue.put(Imps.GroupMembers.NICKNAME, member.getName()); memberValues.add(memberValue); } } if (!memberValues.isEmpty()) { ContentValues[] result = new ContentValues[memberValues.size()]; memberValues.toArray(result); Uri memberUri = ContentUris.withAppendedId(Imps.GroupMembers.CONTENT_URI, id); mContentResolver.bulkInsert(memberUri, result); } } return id; } void insertGroupMemberInDb(Contact member) { if (mChatURI != null) { ContentValues values1 = new ContentValues(2); values1.put(Imps.GroupMembers.USERNAME, member.getAddress().getAddress()); values1.put(Imps.GroupMembers.NICKNAME, member.getName()); ContentValues values = values1; long groupId = ContentUris.parseId(mChatURI); Uri uri = ContentUris.withAppendedId(Imps.GroupMembers.CONTENT_URI, groupId); mContentResolver.insert(uri, values); // insertMessageInDb(member.getName(), null, System.currentTimeMillis(), // Imps.MessageType.PRESENCE_AVAILABLE); } } void deleteGroupMemberInDb(Contact member) { String where = Imps.GroupMembers.USERNAME + "=?"; String[] selectionArgs = { member.getAddress().getAddress() }; if (mChatURI != null) { long groupId = ContentUris.parseId(mChatURI); Uri uri = ContentUris.withAppendedId(Imps.GroupMembers.CONTENT_URI, groupId); mContentResolver.delete(uri, where, selectionArgs); } // insertMessageInDb(member.getName(), null, System.currentTimeMillis(), // Imps.MessageType.PRESENCE_UNAVAILABLE); } void insertPresenceUpdatesMsg(String contact, Presence presence) { int status = presence.getStatus(); Integer previousStatus = mContactStatusMap.get(contact); if (previousStatus != null && previousStatus == status) { // don't insert the presence message if it's the same status // with the previous presence update notification return; } mContactStatusMap.put(contact, status); int messageType; switch (status) { case Presence.AVAILABLE: messageType = Imps.MessageType.PRESENCE_AVAILABLE; break; case Presence.AWAY: case Presence.IDLE: messageType = Imps.MessageType.PRESENCE_AWAY; break; case Presence.DO_NOT_DISTURB: messageType = Imps.MessageType.PRESENCE_DND; break; default: messageType = Imps.MessageType.PRESENCE_UNAVAILABLE; break; } if (mIsGroupChat) { insertMessageInDb(contact, null, System.currentTimeMillis(), messageType); } else { insertMessageInDb(null, null, System.currentTimeMillis(), messageType); } } void removeMessageInDb(int type) { mContentResolver.delete(mMessageURI, Imps.Messages.TYPE + "=?", new String[] { Integer.toString(type) }); } Uri insertMessageInDb(String contact, String body, long time, int type) { return insertMessageInDb(contact, body, time, type, 0/*No error*/, nextID()); } /** * A prefix helps to make sure that ID's are unique across mutliple instances. */ private static String prefix = StringUtils.randomString(5) + "-"; /** * Keeps track of the current increment, which is appended to the prefix to * forum a unique ID. */ private static long id = 0; static String nextID () { return prefix + Long.toString(id++); } Uri insertMessageInDb(String contact, String body, long time, int type, int errCode, String id) { boolean isEncrypted = true; try { isEncrypted = getDefaultOtrChatSession().isChatEncrypted(); } catch (RemoteException e) { // Leave it as encrypted so it gets stored in memory // FIXME(miron) } return Imps.insertMessageInDb(mContentResolver, mIsGroupChat, mContactId, isEncrypted, contact, body, time, type, errCode, id, null); } int updateMessageInDb(String id, int type, long time) { return Imps.updateMessageInDb(mContentResolver, id, type, time, mContactId); } class ListenerAdapter implements MessageListener, GroupMemberListener, OtrEngineListener { public synchronized boolean onIncomingMessage(ChatSession ses, final org.awesomeapp.messenger.model.Message msg) { String body = msg.getBody(); String username = msg.getFrom().getAddress(); String bareUsername = msg.getFrom().getBareAddress(); String nickname = getNickName(username); long time = msg.getDateTime().getTime(); if (msg.getID() != null && Imps.messageExists(mContentResolver, msg.getID())) { return false; //this message is a duplicate } insertOrUpdateChat(body); boolean wasMessageSeen = false; if (msg.getID() == null) insertMessageInDb(nickname, body, time, msg.getType()); else insertMessageInDb(nickname, body, time, msg.getType(),0,msg.getID()); int N = mRemoteListeners.beginBroadcast(); for (int i = 0; i < N; i++) { IChatListener listener = mRemoteListeners.getBroadcastItem(i); try { boolean wasSeen = listener.onIncomingMessage(ChatSessionAdapter.this, msg); if (wasSeen) wasMessageSeen = wasSeen; } catch (RemoteException e) { // The RemoteCallbackList will take care of removing the // dead listeners. } } mRemoteListeners.finishBroadcast(); // Due to the move to fragments, we could have listeners for ChatViews that are not visible on the screen. // This is for fragments adjacent to the current one. Therefore we can't use the existence of listeners // as a filter on notifications. if (!wasMessageSeen) { //reinstated body display here in the notification; perhaps add preferences to turn that off mStatusBarNotifier.notifyChat(mConnection.getProviderId(), mConnection.getAccountId(), getId(), bareUsername, nickname, body, false); } mHasUnreadMessages = true; return true; } public void onSendMessageError(ChatSession ses, final org.awesomeapp.messenger.model.Message msg, final ImErrorInfo error) { insertMessageInDb(null, null, System.currentTimeMillis(), Imps.MessageType.OUTGOING, error.getCode(), null); final int N = mRemoteListeners.beginBroadcast(); for (int i = 0; i < N; i++) { IChatListener listener = mRemoteListeners.getBroadcastItem(i); try { listener.onSendMessageError(ChatSessionAdapter.this, msg, error); } catch (RemoteException e) { // The RemoteCallbackList will take care of removing the // dead listeners. } } mRemoteListeners.finishBroadcast(); } public void onSubjectChanged(ChatGroup group, String subject) { if (mChatURI != null) { ContentValues values1 = new ContentValues(1); values1.put(Imps.Contacts.NICKNAME,subject); ContentValues values = values1; Uri uriContact = ContentUris.withAppendedId(Imps.Contacts.CONTENT_URI, mContactId); mContentResolver.update(uriContact, values, null, null); // insertMessageInDb(member.getName(), null, System.currentTimeMillis(), // Imps.MessageType.PRESENCE_AVAILABLE); } } public void onMemberJoined(ChatGroup group, final Contact contact) { insertGroupMemberInDb(contact); final int N = mRemoteListeners.beginBroadcast(); for (int i = 0; i < N; i++) { IChatListener listener = mRemoteListeners.getBroadcastItem(i); try { listener.onContactJoined(ChatSessionAdapter.this, contact); } catch (RemoteException e) { // The RemoteCallbackList will take care of removing the // dead listeners. } } mRemoteListeners.finishBroadcast(); } public void onMemberLeft(ChatGroup group, final Contact contact) { deleteGroupMemberInDb(contact); final int N = mRemoteListeners.beginBroadcast(); for (int i = 0; i < N; i++) { IChatListener listener = mRemoteListeners.getBroadcastItem(i); try { listener.onContactLeft(ChatSessionAdapter.this, contact); } catch (RemoteException e) { // The RemoteCallbackList will take care of removing the // dead listeners. } } mRemoteListeners.finishBroadcast(); } public void onError(ChatGroup group, final ImErrorInfo error) { // TODO: insert an error message? final int N = mRemoteListeners.beginBroadcast(); for (int i = 0; i < N; i++) { IChatListener listener = mRemoteListeners.getBroadcastItem(i); try { listener.onInviteError(ChatSessionAdapter.this, error); } catch (RemoteException e) { // The RemoteCallbackList will take care of removing the // dead listeners. } } mRemoteListeners.finishBroadcast(); } public void notifyChatSessionConverted() { final int N = mRemoteListeners.beginBroadcast(); for (int i = 0; i < N; i++) { IChatListener listener = mRemoteListeners.getBroadcastItem(i); try { listener.onConvertedToGroupChat(ChatSessionAdapter.this); } catch (RemoteException e) { // The RemoteCallbackList will take care of removing the // dead listeners. } } mRemoteListeners.finishBroadcast(); } @Override public void onIncomingReceipt(ChatSession ses, String id) { Imps.updateConfirmInDb(mContentResolver, mContactId, id, true); synchronized (mRemoteListeners) { int N = mRemoteListeners.beginBroadcast(); for (int i = 0; i < N; i++) { IChatListener listener = mRemoteListeners.getBroadcastItem(i); try { listener.onIncomingReceipt(ChatSessionAdapter.this, id); } catch (RemoteException e) { // The RemoteCallbackList will take care of removing the // dead listeners. } } mRemoteListeners.finishBroadcast(); } } @Override public void onMessagePostponed(ChatSession ses, String id) { updateMessageInDb(id, Imps.MessageType.QUEUED, -1); } @Override public void onReceiptsExpected(ChatSession ses, boolean isExpected) { // TODO } @Override public void sessionStatusChanged(SessionID sessionID) { if (sessionID.getRemoteUserId().equals(mChatSession.getParticipant().getAddress().getAddress())) onStatusChanged(mChatSession, OtrChatManager.getInstance().getSessionStatus(sessionID)); } @Override public void onStatusChanged(ChatSession session, SessionStatus status) { final int N = mRemoteListeners.beginBroadcast(); for (int i = 0; i < N; i++) { IChatListener listener = mRemoteListeners.getBroadcastItem(i); try { listener.onStatusChanged(ChatSessionAdapter.this); } catch (RemoteException e) { // The RemoteCallbackList will take care of removing the // dead listeners. // TODO Auto-generated method stub } } mRemoteListeners.finishBroadcast(); mDataHandler.onOtrStatusChanged(status); if (status == SessionStatus.ENCRYPTED) { sendPostponedMessages (); } mLastSessionStatus = status; } @Override public void onIncomingDataRequest(ChatSession session, org.awesomeapp.messenger.model.Message msg, byte[] value) { mDataHandler.onIncomingRequest(msg.getFrom(),msg.getTo(), value); } @Override public void onIncomingDataResponse(ChatSession session, org.awesomeapp.messenger.model.Message msg, byte[] value) { mDataHandler.onIncomingResponse(msg.getFrom(), msg.getTo(), value); } @Override public void onIncomingTransferRequest(final Transfer transfer) { } } class ChatConvertor implements GroupListener, GroupMemberListener { private ChatGroupManager mGroupMgr; private String mGroupName; public ChatConvertor() { mGroupMgr = mConnection.mGroupManager; } public void convertToGroupChat(String nickname) { mGroupMgr.addGroupListener(this); mGroupName = "G" + System.currentTimeMillis(); try { mGroupMgr.createChatGroupAsync(mGroupName, nickname, nickname); } catch (Exception e){ e.printStackTrace(); } } public void onGroupCreated(ChatGroup group) { if (mGroupName.equalsIgnoreCase(group.getName())) { mGroupMgr.removeGroupListener(this); group.addMemberListener(this); mGroupMgr.inviteUserAsync(group, (Contact) mChatSession.getParticipant()); } } public void onMemberJoined(ChatGroup group, Contact contact) { if (mChatSession.getParticipant().equals(contact)) { onConvertToGroupChatSuccess(group); } mContactStatusMap.put(contact.getName(), contact.getPresence().getStatus()); } public void onSubjectChanged(ChatGroup group, String subject){} public void onGroupDeleted(ChatGroup group) { } public void onGroupError(int errorType, String groupName, ImErrorInfo error) { } public void onJoinedGroup(ChatGroup group) { } public void onLeftGroup(ChatGroup group) { } public void onError(ChatGroup group, ImErrorInfo error) { } public void onMemberLeft(ChatGroup group, Contact contact) { mContactStatusMap.remove(contact.getName()); } } @Override public void setDataListener(IDataListener dataListener) throws RemoteException { mDataListener = dataListener; mDataHandler.setDataListener(mDataListener); } @Override public void setIncomingFileResponse (String transferForm, boolean acceptThis, boolean acceptAll) { mAcceptTransfer = acceptThis; mAcceptAllTransfer = acceptAll; mWaitingForResponse = false; mDataHandler.acceptTransfer(mLastFileUrl, transferForm); } class DataHandlerListenerImpl extends IDataListener.Stub { @Override public void onTransferComplete(boolean outgoing, String offerId, String from, String url, String mimeType, String filePath) { try { if (outgoing) { Imps.updateConfirmInDb(service.getContentResolver(), mContactId, offerId, true); } else { try { boolean isVerified = getDefaultOtrChatSession().isKeyVerified(from); int type = isVerified ? Imps.MessageType.INCOMING_ENCRYPTED_VERIFIED : Imps.MessageType.INCOMING_ENCRYPTED; insertOrUpdateChat(filePath); Uri messageUri = Imps.insertMessageInDb(service.getContentResolver(), mIsGroupChat, getId(), true, from, filePath, System.currentTimeMillis(), type, 0, offerId, mimeType); int percent = (int)(100); String[] path = url.split("/"); String sanitizedPath = SystemServices.sanitize(path[path.length - 1]); final int N = mRemoteListeners.beginBroadcast(); for (int i = 0; i < N; i++) { IChatListener listener = mRemoteListeners.getBroadcastItem(i); try { listener.onIncomingFileTransferProgress(sanitizedPath, percent); } catch (RemoteException e) { // The RemoteCallbackList will take care of removing the // dead listeners. } } mRemoteListeners.finishBroadcast(); if (N == 0) { String nickname = getNickName(from); mStatusBarNotifier.notifyChat(mConnection.getProviderId(), mConnection.getAccountId(), getId(), from, nickname,service.getString(R.string.file_notify_text,mimeType,nickname) , false); } } catch (Exception e) { Log.e(ImApp.LOG_TAG,"Error updating file transfer progress",e); } } /** if (mimeType != null && mimeType.startsWith("audio")) { MediaPlayer mp = new MediaPlayer(); try { mp.setDataSource(file.getCanonicalPath()); mp.prepare(); mp.start(); } catch (IOException e) { // TODO Auto-generated catch block //e.printStackTrace(); } }*/ } catch (Exception e) { // mHandler.showAlert(service.getString(R.string.error_chat_file_transfer_title), service.getString(R.string.error_chat_file_transfer_body)); OtrDebugLogger.log("error reading file", e); } } @Override public synchronized void onTransferFailed(boolean outgoing, String offerId, String from, String url, String reason) { String[] path = url.split("/"); String sanitizedPath = SystemServices.sanitize(path[path.length - 1]); final int N = mRemoteListeners.beginBroadcast(); for (int i = 0; i < N; i++) { IChatListener listener = mRemoteListeners.getBroadcastItem(i); try { listener.onIncomingFileTransferError(sanitizedPath, reason); } catch (RemoteException e) { // The RemoteCallbackList will take care of removing the // dead listeners. } } mRemoteListeners.finishBroadcast(); } @Override public synchronized void onTransferProgress(boolean outgoing, String offerId, String from, String url, float percentF) { int percent = (int)(100*percentF); String[] path = url.split("/"); String sanitizedPath = SystemServices.sanitize(path[path.length - 1]); try { final int N = mRemoteListeners.beginBroadcast(); for (int i = 0; i < N; i++) { IChatListener listener = mRemoteListeners.getBroadcastItem(i); try { listener.onIncomingFileTransferProgress(sanitizedPath, percent); } catch (RemoteException e) { // The RemoteCallbackList will take care of removing the // dead listeners. } } } catch (Exception e) { Log.e(ImApp.LOG_TAG,"error broadcasting progress",e); } finally { mRemoteListeners.finishBroadcast(); } } private String mLastTransferFrom; private String mLastTransferUrl; public void checkLastTransferRequest () { if (mLastTransferFrom != null) { onTransferRequested(mLastTransferUrl,mLastTransferFrom,mLastTransferFrom,mLastTransferUrl); mLastTransferFrom = null; mLastTransferUrl = null; } } @Override public synchronized boolean onTransferRequested(String offerId, String from, String to, String transferUrl) { mAcceptTransfer = false; mWaitingForResponse = true; mLastFileUrl = transferUrl; if (mAcceptAllTransfer) { mAcceptTransfer = true; mWaitingForResponse = false; mLastTransferFrom = from; mLastTransferUrl = transferUrl; mDataHandler.acceptTransfer(mLastFileUrl, from); } else { try { final int N = mRemoteListeners.beginBroadcast(); if (N > 0) { for (int i = 0; i < N; i++) { IChatListener listener = mRemoteListeners.getBroadcastItem(i); try { listener.onIncomingFileTransfer(from, transferUrl); } catch (RemoteException e) { // The RemoteCallbackList will take care of removing the // dead listeners. } } } else { mLastTransferFrom = from; mLastTransferUrl = transferUrl; String nickname = getNickName(from); //reinstated body display here in the notification; perhaps add preferences to turn that off mStatusBarNotifier.notifyChat(mConnection.getProviderId(), mConnection.getAccountId(), getId(), from, nickname, "Incoming file request", false); } } finally { mRemoteListeners.finishBroadcast(); } mAcceptTransfer = false; //for now, wait for the user callback } return mAcceptTransfer; } } public boolean sendPushWhitelistToken(@NonNull String token) { if (mConnection.getState() == ImConnection.SUSPENDED) { // TODO Is it possible to postpone a TLV message? e.g: insertMessageInDb with type QUEUED return false; } // Whitelist tokens are intended for one recipient, for now if (isGroupChatSession()) return false; org.awesomeapp.messenger.model.Message msg = new org.awesomeapp.messenger.model.Message(""); msg.setFrom(mConnection.getLoginUser().getAddress()); msg.setType(Imps.MessageType.OUTGOING); mChatSession.sendPushWhitelistTokenAsync(msg, new String[]{token}); return true; } public synchronized void setContactTyping (Contact contact, boolean isTyping) { int N = mRemoteListeners.beginBroadcast(); for (int i = 0; i < N; i++) { IChatListener listener = mRemoteListeners.getBroadcastItem(i); try { listener.onContactTyping(ChatSessionAdapter.this,contact, isTyping); } catch (RemoteException e) { // The RemoteCallbackList will take care of removing the // dead listeners. } } mRemoteListeners.finishBroadcast(); } public void sendTypingStatus (boolean isTyping) { // mConnection.sendTypingStatus("fpp", isTyping); } public boolean isEncrypted () { if (mChatSession.canOmemo()) { return true; } else { IOtrChatSession otrChatSession = getDefaultOtrChatSession(); if (otrChatSession != null) { try { SessionStatus chatStatus = SessionStatus.values()[otrChatSession.getChatStatus()]; if (chatStatus == SessionStatus.ENCRYPTED) { //boolean isVerified = otrChatSession.isKeyVerified(mChatSession.getParticipant().getAddress().toString()); // holder.mStatusIcon.setImageDrawable(getResources().getDrawable(R.drawable.ic_lock_outline_black_18dp)); return true; } } catch (Exception e) { } } } return false; } }