/*
* 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 info.guardianproject.otr.app.im.service;
import info.guardianproject.otr.IOtrChatSession;
import info.guardianproject.otr.OtrChatListener;
import info.guardianproject.otr.OtrChatManager;
import info.guardianproject.otr.OtrChatSessionAdapter;
import info.guardianproject.otr.OtrDataHandler;
import info.guardianproject.otr.OtrDataHandler.Transfer;
import info.guardianproject.otr.OtrDebugLogger;
import info.guardianproject.otr.app.im.IChatListener;
import info.guardianproject.otr.app.im.IDataListener;
import info.guardianproject.otr.app.im.app.ImApp;
import info.guardianproject.otr.app.im.engine.ChatGroup;
import info.guardianproject.otr.app.im.engine.ChatGroupManager;
import info.guardianproject.otr.app.im.engine.ChatSession;
import info.guardianproject.otr.app.im.engine.Contact;
import info.guardianproject.otr.app.im.engine.ContactListManager;
import info.guardianproject.otr.app.im.engine.GroupListener;
import info.guardianproject.otr.app.im.engine.GroupMemberListener;
import info.guardianproject.otr.app.im.engine.ImConnection;
import info.guardianproject.otr.app.im.engine.ImEntity;
import info.guardianproject.otr.app.im.engine.ImErrorInfo;
import info.guardianproject.otr.app.im.engine.MessageListener;
import info.guardianproject.otr.app.im.engine.Presence;
import info.guardianproject.otr.app.im.provider.Imps;
import info.guardianproject.util.SystemServices;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import net.java.otr4j.session.SessionStatus;
import org.jivesoftware.smack.packet.Packet;
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.util.Log;
import com.google.common.collect.Maps;
public class ChatSessionAdapter extends info.guardianproject.otr.app.im.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;
OtrChatSessionAdapter mOtrChatSession;
private OtrDataHandler mDataHandler;
private IDataListener mDataListener;
private DataHandlerListenerImpl mDataHandlerListener;
private boolean mAcceptTransfer = false;
private boolean mWaitingForResponse = false;
private boolean mAcceptAllTransfer = false;
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();
initOtrChatSession();//setup first time
ImEntity participant = mChatSession.getParticipant();
if (participant instanceof ChatGroup) {
init((ChatGroup) participant,isNewSession);
} else {
init((Contact) participant,isNewSession);
}
mDataHandler.setChatId(getId());
}
private void initOtrChatSession ()
{
try
{
if (mConnection != null)
{
mDataHandler = new OtrDataHandler(mChatSession);
mDataHandlerListener = new DataHandlerListenerImpl();
mDataHandler.setDataListener(mDataHandlerListener);
String localUser = mConnection.getLoginUser().getAddress().getAddress();
String remoteUser = mChatSession.getParticipant().getAddress().getAddress();
OtrChatManager cm = service.getOtrChatManager();
mOtrChatSession = new OtrChatSessionAdapter(localUser, remoteUser, cm);
// add OtrChatListener as the intermediary to mListenerAdapter so it can filter OTR msgs
mChatSession.setMessageListener(new OtrChatListener(cm, mListenerAdapter));
// mChatSession.setOtrChatManager(cm);
}
}
catch (NullPointerException npe)
{
Log.e(ImApp.LOG_TAG,"error init OTR session",npe);
}
}
public synchronized IOtrChatSession getOtrChatSession() {
if (mOtrChatSession == null)
initOtrChatSession();
return mOtrChatSession;
}
private void init(ChatGroup group, boolean isNewSession) {
mIsGroupChat = true;
mContactId = insertOrUpdateGroupContactInDb(group);
group.addMemberListener(mListenerAdapter);
try {
mChatSessionManager.getChatGroupManager().joinChatGroupAsync(group.getAddress());
mMessageURI = Imps.Messages.getContentUriByThreadId(mContactId);
mChatURI = ContentUris.withAppendedId(Imps.Chats.CONTENT_URI, mContactId);
if (isNewSession)
insertOrUpdateChat(null);
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);
mMessageURI = Imps.Messages.getContentUriByThreadId(mContactId);
mChatURI = ContentUris.withAppendedId(Imps.Chats.CONTENT_URI, mContactId);
if (isNewSession)
insertOrUpdateChat(null);
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() {
return mChatSession.getParticipant().getAddress().getUser();
}
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 = listManager.getContactByAddress(contact);
if (invitee == null) {
ImErrorInfo error = new ImErrorInfo(ImErrorInfo.ILLEGAL_CONTACT_ADDRESS,
"Cannot find contact with address: " + contact);
mListenerAdapter.onError((ChatGroup) mChatSession.getParticipant(), error);
} else {
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 void sendMessage(String text) {
if (mConnection.getState() == ImConnection.SUSPENDED) {
// connection has been suspended, save the message without send it
long now = System.currentTimeMillis();
insertMessageInDb(null, text, now, Imps.MessageType.POSTPONED);
return;
}
info.guardianproject.otr.app.im.engine.Message msg = new info.guardianproject.otr.app.im.engine.Message(text);
msg.setFrom(mConnection.getLoginUser().getAddress());
msg.setType(Imps.MessageType.OUTGOING);
int newType = mChatSession.sendMessageAsync(msg);
long now = System.currentTimeMillis();
insertMessageInDb(null, text, now, newType, 0, msg.getID());
}
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 = Maps.newHashMap();
headers.put("Mime-Type", type);
}
try
{
mDataHandler.offerData(offerId, mConnection.getLoginUser().getAddress(), url, headers);
return true;
}
catch (IOException 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.
*
* @param msg the message to send.
*/
/*
public void sendMessageWithoutHistory(String text) {
Message msg = new Message(text);
// TODO OTRCHAT use a lower level method
mChatSession.sendMessageAsync(msg);
}*/
void sendPostponedMessages() {
String[] projection = new String[] { BaseColumns._ID, Imps.Messages.BODY,
Imps.Messages.PACKET_ID,
Imps.Messages.DATE, Imps.Messages.TYPE, };
String selection = Imps.Messages.TYPE + "=?";
Cursor c = mContentResolver.query(mMessageURI, projection, selection,
new String[] { Integer.toString(Imps.MessageType.POSTPONED) }, null);
if (c == null) {
RemoteImService.debug("Query error while querying postponed messages");
return;
}
ArrayList<String> messages = new ArrayList<String>();
while (c.moveToNext()) {
String body = c.getString(1);
messages.add(body);
}
c.close();
removeMessageInDb(Imps.MessageType.POSTPONED);
for (String body : messages)
sendMessage(body);
}
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<info.guardianproject.otr.app.im.engine.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++) {
info.guardianproject.otr.app.im.engine.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) {
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() };
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*/, Packet.nextID());
}
Uri insertMessageInDb(String contact, String body, long time, int type, int errCode, String id) {
boolean isEncrypted = true;
try {
isEncrypted = getOtrChatSession().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) {
int result = -1;
Uri.Builder builder = Imps.Messages.OTR_MESSAGES_CONTENT_URI_BY_PACKET_ID.buildUpon();
builder.appendPath(id);
ContentValues values = new ContentValues(1);
values.put(Imps.Messages.TYPE, type);
if (time != -1)
values.put(Imps.Messages.DATE, time);
result = mContentResolver.update(builder.build(), values, null, null);
if (result == 0)
{
builder = Imps.Messages.CONTENT_URI_MESSAGES_BY_PACKET_ID.buildUpon();
builder.appendPath(id);
result = mContentResolver.update(builder.build(), values, null, null);
}
return result;
}
class ListenerAdapter implements MessageListener, GroupMemberListener {
public boolean onIncomingMessage(ChatSession ses, final info.guardianproject.otr.app.im.engine.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();
insertOrUpdateChat(body);
insertMessageInDb(nickname, body, time, msg.getType());
boolean wasMessageSeen = false;
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 info.guardianproject.otr.app.im.engine.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 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, id, true);
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.POSTPONED, -1);
}
@Override
public void onReceiptsExpected(ChatSession ses) {
// TODO
}
@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 ();
}
}
@Override
public void onIncomingDataRequest(ChatSession session, info.guardianproject.otr.app.im.engine.Message msg, byte[] value) {
mDataHandler.onIncomingRequest(msg.getFrom(),msg.getTo(), value);
}
@Override
public void onIncomingDataResponse(ChatSession session, info.guardianproject.otr.app.im.engine.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);
}
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 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 (boolean acceptThis, boolean acceptAll)
{
mAcceptTransfer = acceptThis;
mAcceptAllTransfer = acceptAll;
mWaitingForResponse = false;
mDataHandler.acceptTransfer(mLastFileUrl);
}
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(), offerId, true);
} else {
try
{
boolean isVerified = getOtrChatSession().isKeyVerified(from);
int type = isVerified ? Imps.MessageType.INCOMING_ENCRYPTED_VERIFIED : Imps.MessageType.INCOMING_ENCRYPTED;
insertOrUpdateChat(filePath);
Uri messageUri = Imps.insertMessageInDb(service.getContentResolver(),
false, getId(),
true, null,
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();
}
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);
}
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;
//reinstated body display here in the notification; perhaps add preferences to turn that off
mStatusBarNotifier.notifyChat(mConnection.getProviderId(), mConnection.getAccountId(),
getId(), from, from, "Incoming file request", false);
}
}
finally
{
mRemoteListeners.finishBroadcast();
}
mAcceptTransfer = false; //for now, wait for the user callback
}
return mAcceptTransfer;
}
}
}