package com.abewy.android.apps.klyph.messenger.service;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.jivesoftware.smack.Chat;
import org.jivesoftware.smack.ChatManager;
import org.jivesoftware.smack.ChatManagerListener;
import org.jivesoftware.smack.Connection;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
import org.jivesoftware.smack.ConnectionCreationListener;
import org.jivesoftware.smack.ConnectionListener;
import org.jivesoftware.smack.MessageListener;
import org.jivesoftware.smack.Roster;
import org.jivesoftware.smack.RosterEntry;
import org.jivesoftware.smack.RosterListener;
import org.jivesoftware.smack.SASLAuthentication;
import org.jivesoftware.smack.SmackAndroid;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.packet.StreamError;
import org.jivesoftware.smack.provider.PrivacyProvider;
import org.jivesoftware.smack.provider.ProviderManager;
import org.jivesoftware.smackx.DefaultMessageEventRequestListener;
import org.jivesoftware.smackx.GroupChatInvitation;
import org.jivesoftware.smackx.MessageEventManager;
import org.jivesoftware.smackx.MessageEventNotificationListener;
import org.jivesoftware.smackx.PrivateDataManager;
import org.jivesoftware.smackx.bytestreams.socks5.provider.BytestreamsProvider;
import org.jivesoftware.smackx.packet.ChatStateExtension;
import org.jivesoftware.smackx.packet.LastActivity;
import org.jivesoftware.smackx.packet.OfflineMessageInfo;
import org.jivesoftware.smackx.packet.OfflineMessageRequest;
import org.jivesoftware.smackx.packet.SharedGroupsInfo;
import org.jivesoftware.smackx.provider.AdHocCommandDataProvider;
import org.jivesoftware.smackx.provider.DataFormProvider;
import org.jivesoftware.smackx.provider.DelayInformationProvider;
import org.jivesoftware.smackx.provider.DiscoverInfoProvider;
import org.jivesoftware.smackx.provider.DiscoverItemsProvider;
import org.jivesoftware.smackx.provider.MUCAdminProvider;
import org.jivesoftware.smackx.provider.MUCOwnerProvider;
import org.jivesoftware.smackx.provider.MUCUserProvider;
import org.jivesoftware.smackx.provider.MessageEventProvider;
import org.jivesoftware.smackx.provider.MultipleAddressesProvider;
import org.jivesoftware.smackx.provider.RosterExchangeProvider;
import org.jivesoftware.smackx.provider.StreamInitiationProvider;
import org.jivesoftware.smackx.provider.VCardProvider;
import org.jivesoftware.smackx.provider.XHTMLExtensionProvider;
import org.jivesoftware.smackx.receipts.DeliveryReceiptManager;
import org.jivesoftware.smackx.receipts.ReceiptReceivedListener;
import org.jivesoftware.smackx.search.UserSearch;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.support.v4.app.NotificationCompat.Builder;
import android.text.Html;
import android.util.Log;
import com.abewy.android.apps.klyph.core.imageloader.FakeImageLoaderListener;
import com.abewy.android.apps.klyph.core.imageloader.ImageLoader;
import com.abewy.android.apps.klyph.core.util.FacebookUtil;
import com.abewy.android.apps.klyph.messenger.KlyphMessengerNotification;
import com.abewy.android.apps.klyph.messenger.MessengerBundleExtras;
import com.abewy.android.apps.klyph.messenger.MessengerPreferences;
import com.abewy.android.apps.klyph.messenger.R;
import com.abewy.android.apps.klyph.messenger.app.MainActivity;
import com.abewy.android.apps.klyph.messenger.chat.SASLXFacebookPlatformMechanism;
import com.abewy.net.ConnectionState;
import com.facebook.Session;
import com.squareup.picasso.Picasso.LoadedFrom;
public class MessengerService extends Service
{
private static final String TAG = "MessengerService";
private static MessengerService instance;
private SmackAndroid smack;
private ArrayList<Messenger> mClients = new ArrayList<Messenger>();
private final RemoteCallbackList<IMessengerCallback> callbacks = new RemoteCallbackList<IMessengerCallback>();
private List<Chat> chats;
private List<PRosterEntry> roster;
private RosterListener rosterListener;
private Thread presenceRunnable;
private String selectedRecipient;
private boolean notificationsEnabled;
private String ringtone;
private String ringtoneUri;
private boolean vibrateEnabled;
private LinkedHashMap<String, Object> pendingActions;
private ReconnectionManager reconnectionManager;
private boolean isLoggedIn = false;
/** Holds last value set by a client. */
int mValue = 0;
public static final int REGISTER_CLIENT = 1;
public static final int UNREGISTER_CLIENT = 2;
public static final int SEND_MSG = 3;
public static final int REPORT_CONNECTED = 1;
public static final int REPORT_DISCONNECTED = 2;
public static final int REPORT_PRESENCE_CHANGED = 4;
public static final int REPORT_ROSTER_UPDATED = 5;
public static final int REPORT_ROSTER_ENTRIES_ADDED = 6;
public static final int REPORT_ROSTER_ENTRIES_DELETED = 7;
public static final int REPORT_MESSAGE_RECEIVED = 8;
private static final String ACTION_SEND_MSG = "actionSendMsg";
/**
* Handler of incoming messages from clients.
*/
class IncomingHandler extends Handler
{
@Override
public void handleMessage(Message msg)
{
switch (msg.what)
{
case REGISTER_CLIENT:
{
Log.d(TAG, "REGISTER_CLIENT");
if (mClients.indexOf(msg.replyTo) == -1)
mClients.add(msg.replyTo);
break;
}
case UNREGISTER_CLIENT:
{
Log.d(TAG, "UNREGISTER_CLIENT");
if (mClients != null)
mClients.remove(msg.replyTo);
break;
}
case SEND_MSG:
{
sendMessageTo((Bundle) msg.getData());
break;
}
default:
{
super.handleMessage(msg);
}
}
}
}
/**
* Target we publish for clients to send messages to IncomingHandler.
*/
final Messenger mMessenger = new Messenger(new IncomingHandler());
private XMPPConnection connection;
public MessengerService()
{
Log.d(TAG, "new instance");
if (instance != null)
Log.d(TAG, "instance is already defined !");
}
@Override
public void onCreate()
{
Log.d(TAG, "onCreate");
if (instance != null)
Log.d(TAG, "instance is already defined !");
instance = this;
notificationsEnabled = MessengerPreferences.areNotificationsEnabled();
ringtone = MessengerPreferences.getNotificationRingtone();
ringtoneUri = MessengerPreferences.getNotificationRingtoneUri();
vibrateEnabled = MessengerPreferences.isNotificationVibrationEnabled();
chats = new ArrayList<Chat>();
roster = new ArrayList<PRosterEntry>();
savedMessages = new ArrayList<org.jivesoftware.smack.packet.Message>();
pendingActions = new LinkedHashMap<String, Object>();
configure(ProviderManager.getInstance());
Connection.addConnectionCreationListener(new ConnectionCreationListener() {
public void connectionCreated(Connection connection)
{
reconnectionManager = new ReconnectionManager(connection, ConnectionState.getInstance(MessengerService.this).isOnline());
connection.addConnectionListener(reconnectionManager);
}
});
registerReceiver(new BroadcastReceiver() {
public void onReceive(Context context, Intent intent)
{
Log.i(TAG, "Connection status change to " + ConnectionState.getInstance(MessengerService.this).isOnline());
onConnectivityChange();
}
}, new IntentFilter("android.net.conn.CONNECTIVITY_CHANGE"));
connect();
}
private void onConnectivityChange()
{
if (!isLoggedIn && isInternetActive() && !connection.isConnected())
launchConnection();
else if (reconnectionManager != null)
reconnectionManager.setInternetIsActive(isInternetActive());
}
public static boolean isRunning()
{
return instance != null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId)
{
Log.d(TAG, "onStartCommand");
super.onStartCommand(intent, flags, startId);
return START_STICKY;
}
private void addPendingAction(String action, Object data)
{
pendingActions.put(action, data);
// if (ConnectionState.getInstance(this).isOnline() && !isConnecting)
// connect();
}
private void executePendingActions()
{
if (isInternetActive() && connection.isConnected())
{
for (String key : pendingActions.keySet())
{
if (key.equals(ACTION_SEND_MSG))
{
sendMessageTo((Bundle) pendingActions.get(key));
}
}
pendingActions = new LinkedHashMap<String, Object>();
}
}
private void connect()
{
Log.d(TAG, "connect");
if (smack == null)
{
smack = SmackAndroid.init(this);
smack = SmackAndroid.init(this);
}
// Connection.DEBUG_ENABLED = true;
SASLAuthentication.registerSASLMechanism("X-FACEBOOK-PLATFORM", SASLXFacebookPlatformMechanism.class);
SASLAuthentication.supportSASLMechanism("X-FACEBOOK-PLATFORM", 0);
ConnectionConfiguration config = new ConnectionConfiguration("chat.facebook.com", 5222);
config.setSASLAuthenticationEnabled(true);
config.setSecurityMode(SecurityMode.required);
config.setRosterLoadedAtLogin(true);
config.setCompressionEnabled(false);
// config.setDebuggerEnabled(true);
config.setReconnectionAllowed(false);
config.setSendPresence(true);
config.setTruststoreType("AndroidCAStore");
config.setTruststorePassword(null);
config.setTruststorePath(null);
connection = new XMPPConnection(config);
Roster roster = connection.getRoster();
rosterListener = new RosterListener() {
@Override
public void presenceChanged(Presence presence)
{
onPresenceChange(presence);
}
@Override
public void entriesUpdated(Collection<String> arg0)
{
onEntriesUpdated();
}
@Override
public void entriesDeleted(Collection<String> arg0)
{
onEntriesDeleted();
}
@Override
public void entriesAdded(Collection<String> arg0)
{
onEntriesAdded();
}
};
roster.addRosterListener(rosterListener);
if (Session.getActiveSession() == null)
Session.openActiveSessionFromCache(this);
// new ConnectionTask().execute(connection);
if (isInternetActive())
launchConnection();
}
private Thread reconnectionThread;
synchronized private void launchConnection()
{
// Since there is no thread running, creates a new one to attempt
// the reconnection.
// avoid to run duplicated reconnectionThread -- fd: 16/09/2010
if (reconnectionThread != null && reconnectionThread.isAlive())
return;
Log.d(TAG, "MessengerService launchConnection");
reconnectionThread = new Thread() {
private int numAttemps = 0;
/**
* The process will try the reconnection until the connection succeed or the user
* cancell it
*/
public void run()
{
// The process will try to reconnect until the connection is established or
// the user cancel the reconnection process {@link Connection#disconnect()}
while (isInternetActive() == true && !connection.isConnected())
{
// Wait 3 seconds before trying to reconnect again
int remainingSeconds = numAttemps == 0 ? 0 : 3;
// Sleep until we're ready for the next reconnection attempt. Notify
// listeners once per second about how much time remains before the next
// reconnection attempt.
while (isInternetActive() == true && !connection.isConnected() && remainingSeconds > 0)
{
try
{
Thread.sleep(1000);
remainingSeconds--;
// ReconnectionManager.this.notifyAttemptToReconnectIn(remainingSeconds);
}
catch (InterruptedException e1)
{
e1.printStackTrace();
// Notify the reconnection has failed
// ReconnectionManager.this.notifyReconnectionFailed(e1);
}
}
XMPPException exception = null;
// Makes a reconnection attempt
try
{
if (isInternetActive() == true)
{
Log.d(TAG, "MessengerService Reconnecting");
connection.connect();
connection.login(getString(R.string.app_id), Session.getActiveSession().getAccessToken());
}
}
catch (XMPPException e)
{
exception = e;
// Fires the failed reconnection notification
// ReconnectionManager.this.notifyReconnectionFailed(e);
}
if (exception == null)
{
onConnectionSuccess();
break;
}
numAttemps++;
}
}
};
// reconnectionThread.setName("Smack Reconnection Manager");
reconnectionThread.setDaemon(true);
reconnectionThread.start();
}
private boolean isInternetActive()
{
return ConnectionState.getInstance(this).isOnline();
}
protected void onConnectionSuccess()
{
Log.d(TAG, "onConnectionSuccess");
DeliveryReceiptManager.getInstanceFor(connection).enableAutoReceipts();
isLoggedIn = true;
//listenMsgEvents();
listenNewChats();
executePendingActions();
}
private void refreshRoster()
{
roster = getRosterEntries();
}
private ArrayList<PRosterEntry> getRosterEntries()
{
Log.d(TAG, "getRosterEntries");
if (connection != null && connection.isConnected() == true)
{
Roster roster = connection.getRoster();
Collection<RosterEntry> entries = roster.getEntries();
ArrayList<PRosterEntry> data = new ArrayList<PRosterEntry>();
for (RosterEntry rosterEntry : entries)
{
PRosterEntry re = new PRosterEntry();
Presence p = roster.getPresence(rosterEntry.getUser());
re.name = rosterEntry.getName();
re.user = rosterEntry.getUser();
re.presence = p.getType().name();
data.add(re);
}
Log.d(TAG, "getRosterEntries " + data.size());
return data;
}
else
{
return new ArrayList<PRosterEntry>();
}
}
private MessageEventManager messageEventManager;
private void listenMsgEvents()
{
messageEventManager = new MessageEventManager(connection);
messageEventManager.addMessageEventRequestListener(new DefaultMessageEventRequestListener() {
public void deliveredNotificationRequested(String from, String packetID, MessageEventManager messageEventManager)
{
super.deliveredNotificationRequested(from, packetID, messageEventManager);
// DefaultMessageEventRequestListener automatically responds that the message was delivered when receives this request
Log.d("MessengerService", "Delivered Notification Requested (" + from + ", " + packetID + ")");
}
public void displayedNotificationRequested(String from, String packetID, MessageEventManager messageEventManager)
{
super.displayedNotificationRequested(from, packetID, messageEventManager);
// Send to the message's sender that the message was displayed
messageEventManager.sendDisplayedNotification(from, packetID);
Log.d("MessengerService", "displayedNotificationRequested (" + from + ", " + packetID + ")");
}
public void composingNotificationRequested(String from, String packetID, MessageEventManager messageEventManager)
{
super.composingNotificationRequested(from, packetID, messageEventManager);
// Send to the message's sender that the message's receiver is composing a reply
messageEventManager.sendComposingNotification(from, packetID);
Log.d("MessengerService", "composingNotificationRequested (" + from + ", " + packetID + ")");
}
public void offlineNotificationRequested(String from, String packetID, MessageEventManager messageEventManager)
{
super.offlineNotificationRequested(from, packetID, messageEventManager);
// The XMPP server should take care of this request. Do nothing.
Log.d("MessengerService", "Offline Notification Requested (" + from + ", " + packetID + ")");
}
});
messageEventManager.addMessageEventNotificationListener(new MessageEventNotificationListener() {
@Override
public void offlineNotification(String arg0, String arg1)
{
Log.d("MessengerService", "offlineNotification (" + arg0 + ", " + arg1 + ")");
}
@Override
public void displayedNotification(String arg0, String arg1)
{
Log.d("MessengerService", "displayedNotification (" + arg0 + ", " + arg1 + ")");
}
@Override
public void deliveredNotification(String arg0, String arg1)
{
Log.d("MessengerService", "deliveredNotification (" + arg0 + ", " + arg1 + ")");
}
@Override
public void composingNotification(String arg0, String arg1)
{
Log.d("MessengerService", "composingNotification (" + arg0 + ", " + arg1 + ")");
}
@Override
public void cancelledNotification(String arg0, String arg1)
{
Log.d("MessengerService", "cancelledNotification (" + arg0 + ", " + arg1 + ")");
}
});
DeliveryReceiptManager.getInstanceFor(connection).addReceiptReceivedListener(new ReceiptReceivedListener() {
@Override
public void onReceiptReceived(String arg0, String arg1, String arg2)
{
Log.d("MessengerService", "onReceiptReceived (" + arg0 + ", " + arg1 + ", " + arg2 + ")");
}
});
}
private void listenNewChats()
{
connection.getChatManager().addChatListener(new ChatManagerListener() {
@Override
public void chatCreated(Chat chat, boolean createdLocally)
{
chats.add(chat);
Log.d(TAG, "New chat created " + chat.getThreadID() + " " + createdLocally + " " + chat.getParticipant());
if (!createdLocally)
chat.addMessageListener(new MessageListener() {
@Override
public void processMessage(Chat chat, org.jivesoftware.smack.packet.Message message)
{
onMessageReceived(chat, message);
}
});
}
});
}
private Chat createChat(String recipient)
{
ChatManager chatManager = connection.getChatManager();
Chat chat = chatManager.createChat(recipient /* "-1320153319@chat.facebook.com" */, new MessageListener() {
@Override
public void processMessage(Chat chat, org.jivesoftware.smack.packet.Message message)
{
onMessageReceived(chat, message);
}
});
chats.add(chat);
Log.d(TAG, "Create chat thread id " + chat.getThreadID());
return chat;
}
private void onMessageReceived(Chat chat, org.jivesoftware.smack.packet.Message message)
{
if (message.getBody() != null)
{
Log.d(TAG, "processMessage " + chat.getThreadID() + " " + message.getBody() + " " + message.getTo());
onMessageReceived(message);
messageEventManager.sendDeliveredNotification(message.getFrom(), message.getPacketID());
}
}
private void sendMessageTo(Bundle bundle)
{
if (!connection.isConnected())
{
addPendingAction(ACTION_SEND_MSG, bundle);
}
else
{
String to = bundle.getString("to");
String message = bundle.getString("message");
to = "-" + to + "@chat.facebook.com";
Log.d(TAG, "sendMessage to " + to + " " + message);
Chat toChat = null;
for (Chat chat : chats)
{
if (chat.getParticipant().equals(to))
{
toChat = chat;
}
}
if (toChat == null)
{
toChat = createChat(to);
}
org.jivesoftware.smack.packet.Message msg = new org.jivesoftware.smack.packet.Message();
msg.setBody(message);
msg.setTo(to);
msg.setType(org.jivesoftware.smack.packet.Message.Type.chat);
msg.setThread(toChat.getThreadID());
// Add to the message all the notifications requests (offline, delivered, displayed, composing)
MessageEventManager.addNotificationsRequests(msg, true, true, true, true);
DeliveryReceiptManager.addDeliveryReceiptRequest(msg);
try
{
toChat.sendMessage(msg);
}
catch (XMPPException e)
{
e.printStackTrace();
}
}
}
private void onPresenceChange(Presence presence)
{
if (presenceRunnable == null)
{
presenceRunnable = new Thread(new Runnable() {
@Override
public void run()
{
try
{
synchronized (this)
{
wait(1000);
}
}
catch (InterruptedException e)
{
e.printStackTrace();
}
broadcastPresenceChange();
}
});
presenceRunnable.start();
}
}
private void broadcastPresenceChange()
{
refreshRoster();
onRosterUpdated();
presenceRunnable = null;
/*
* PPresence pp = new PPresence();
* pp.from = presence.getFrom();
* pp.type = presence.getType().name();
*
* final int N = callbacks.beginBroadcast();
* Log.d(TAG, "callback count " + N + " roster count " + roster.size());
* for (int i = 0; i < N; i++)
* {
* try
* {
* callbacks.getBroadcastItem(i).onPresenceChange(pp);
* }
* catch (RemoteException e)
* {
* Log.d(TAG, "RemoteException " + e.getMessage());
* // Thef RemoteCallbackList will take care of removing the dead object for us.
* }
* }
* callbacks.finishBroadcast();
*/
}
private void onEntriesUpdated()
{
onRosterUpdated();
}
private void onEntriesDeleted()
{
onRosterUpdated();
}
private void onEntriesAdded()
{
onRosterUpdated();
}
private void onRosterUpdated()
{
refreshRoster();
final int N = callbacks.beginBroadcast();
for (int i = 0; i < N; i++)
{
try
{
callbacks.getBroadcastItem(i).onRosterUpdated(roster);
}
catch (RemoteException e)
{
// Thef RemoteCallbackList will take care of removing the dead object for us.
}
}
callbacks.finishBroadcast();
}
private void onMessageReceived(org.jivesoftware.smack.packet.Message message)
{
Bundle data = new Bundle();
String uid = getUidFromXmppId(message.getFrom());
data.putString("participant", uid);
data.putString("body", message.getBody());
data.putString("from", message.getFrom());
data.putString("to", message.getTo());
data.putString("date", String.valueOf(new Date().getTime()));
sendMsg(REPORT_MESSAGE_RECEIVED, data);
sendNotification(message);
}
private void sendMsg(int msg)
{
sendMsgToClients(Message.obtain(null, msg));
}
private void sendMsg(int msg, Bundle bundle)
{
Message message = Message.obtain(null, msg);
message.setData(bundle);
sendMsgToClients(message);
}
private void sendMsgToClients(Message msg)
{
for (int i = mClients.size() - 1; i >= 0; i--)
{
try
{
mClients.get(i).send(msg);
}
catch (RemoteException e)
{
// The client is dead. Remove it from the list;
// we are going through the list from back to front
// so this is safe to do inside the loop.
mClients.remove(i);
}
}
}
private boolean noBinding()
{
int n = mClients.size() + callbacks.getRegisteredCallbackCount();
return n == 0;
}
private List<org.jivesoftware.smack.packet.Message> savedMessages;
private void sendNotification(org.jivesoftware.smack.packet.Message message)
{
if (notificationsEnabled)
{
String from = getUidFromXmppId(message.getFrom());
Log.d(TAG, "sendNotification " + from + " " + selectedRecipient);
Log.d(TAG, "sendNotification " + ringtone + " " + ringtoneUri);
if (noBinding() || (selectedRecipient == null || !from.equals(selectedRecipient)))
{
savedMessages.add(message);
sendNotification();
}
}
}
private void sendNotification()
{
final Builder builder = KlyphMessengerNotification.getBuilder(this, true);
Intent notifyIntent = new Intent(Intent.ACTION_MAIN);
notifyIntent.setClass(getApplicationContext(), MainActivity.class);
int n = savedMessages.size();
org.jivesoftware.smack.packet.Message message = savedMessages.get(savedMessages.size() - 1);
String uid = getUidFromXmppId(message.getFrom());
String name = getNameForId(message.getFrom());
builder.setTicker(getString(R.string.new_message_from, getNameForId(message.getFrom()), message.getBody()));
if (n > 1)
{
if (areAllSavedMessageFromSameUser())
{
builder.setContentTitle(name);
builder.setContentText(getString(R.string.new_messages, n));
List<String> lines = new ArrayList<String>();
for (org.jivesoftware.smack.packet.Message msg : savedMessages)
{
lines.add(msg.getBody());
}
KlyphMessengerNotification.setInboxStyle(builder, name, lines);
}
else
{
builder.setContentTitle(getString(R.string.new_messages, n));
List<String> lines = new ArrayList<String>();
List<String> names = new ArrayList<String>();
for (org.jivesoftware.smack.packet.Message msg : savedMessages)
{
names.add(getNameForId(msg.getFrom()));
lines.add(Html.fromHtml(getString(R.string.new_message_from, getNameForId(msg.getFrom()), msg.getBody())).toString());
}
builder.setContentText(StringUtils.join(names, ","));
KlyphMessengerNotification.setInboxStyle(builder, name, lines);
}
}
else
{
builder.setContentTitle(name);
builder.setContentText(message.getBody());
}
builder.setNumber(savedMessages.size());
builder.setSubText(getString(R.string.app_name));
// Override builder defaults flags because preferences changes
// don't work in inter process
int defaults = 0;
if (ringtone != null && ringtone.equals("default"))
{
defaults |= android.app.Notification.DEFAULT_SOUND;
}
else if (ringtoneUri == null)
{
builder.setSound(null);
}
else
{
builder.setSound(Uri.parse(ringtoneUri));
}
if (vibrateEnabled == true)
defaults |= android.app.Notification.DEFAULT_VIBRATE;
builder.setDefaults(defaults);
// Gets a PendingIntent containing the entire back stack
PendingIntent intent = PendingIntent.getActivity(this, 0, notifyIntent, PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentIntent(intent);
// KlyphMessengerNotification.sendNotification(MessengerService.this, builder);
ImageLoader.loadImage(FacebookUtil.getLargeProfilePictureURLForId(uid), new FakeImageLoaderListener() {
@Override
public void onPrepareLoad(Drawable arg0)
{
// TODO Auto-generated method stub
}
@Override
public void onBitmapLoaded(Bitmap image, LoadedFrom arg1)
{
Log.d(TAG, "onLoadingComplete");
builder.setLargeIcon(image);
KlyphMessengerNotification.sendNotification(MessengerService.this, builder);
}
@Override
public void onBitmapFailed(Drawable arg0)
{
Log.d(TAG, "onLoadingFailed");
KlyphMessengerNotification.sendNotification(MessengerService.this, builder);
}
});
}
private String getUidFromXmppId(String xmppId)
{
return xmppId.substring(1, xmppId.indexOf("@"));
}
private boolean areAllSavedMessageFromSameUser()
{
String id = null;
for (org.jivesoftware.smack.packet.Message message : savedMessages)
{
if (id == null)
{
id = message.getFrom();
continue;
}
if (!id.equals(message.getFrom()))
{
return false;
}
}
return true;
}
private String getNameForId(String id)
{
for (PRosterEntry entry : getRosterEntries())
{
if (entry.user.equals(id))
{
return entry.name;
}
}
return "";
}
@Override
public void onDestroy()
{
Log.d(TAG, "onDestroy");
if (connection != null)
{
if (connection.getRoster() != null)
connection.getRoster().removeRosterListener(rosterListener);
// connection.removeConnectionListener(connectionListener);
new DeconnectionTask().execute(connection);
}
if (smack != null)
smack.onDestroy();
smack = null;
connection = null;
rosterListener = null;
mClients = null;
callbacks.kill();
chats = null;
roster = null;
instance = null;
}
/**
* When binding to the service, we return an interface to our messenger
* for sending messages to the service.
*/
@Override
public IBinder onBind(Intent intent)
{
Log.d(TAG, "onBind " + intent.getAction());
// if (!connection.isConnected())
// connect();
if (IMessengerService.class.getName().equals(intent.getAction()))
{
return mBinder;
}
return mMessenger.getBinder();
}
/**
* The IRemoteInterface is defined through IDL
*/
private final IMessengerService.Stub mBinder = new IMessengerService.Stub() {
@Override
public List<PRosterEntry> getRoster() throws RemoteException
{
Log.d(TAG, "getRoster callback " + roster.size());
return roster;
}
@Override
public void setSelectedRecipient(String id) throws RemoteException
{
Log.d(TAG, "setRecipientSelected");
selectedRecipient = id;
}
@Override
public void setNoSelectedRecipient() throws RemoteException
{
Log.d(TAG, "setNoSelectedRecipient");
selectedRecipient = null;
}
@Override
public void registerCallback(IMessengerCallback cb) throws RemoteException
{
if (cb != null)
callbacks.register(cb);
}
@Override
public void unregisterCallback(IMessengerCallback cb) throws RemoteException
{
if (cb != null)
callbacks.unregister(cb);
}
@Override
public void clearSavedMessages() throws RemoteException
{
savedMessages = new ArrayList<org.jivesoftware.smack.packet.Message>();
}
@Override
public void removeSavedMessages(String id) throws RemoteException
{
int n = savedMessages.size();
for (int i = 0; i < n; i++)
{
org.jivesoftware.smack.packet.Message message = savedMessages.get(i);
if (getUidFromXmppId(message.getFrom()).equals(id))
{
savedMessages.remove(message);
i--;
n--;
}
}
}
@Override
public void setNotificationsEnabled(boolean enabled) throws RemoteException
{
notificationsEnabled = enabled;
}
@Override
public void setRingtone(String ringtone) throws RemoteException
{
Log.d(TAG, "setRingtone" + ringtone);
MessengerService.this.ringtone = ringtone;
}
@Override
public void setRingtoneUri(String uri) throws RemoteException
{
Log.d(TAG, "setRingtoneUri" + uri);
ringtoneUri = uri;
}
@Override
public void setVibrateEnabled(boolean enabled) throws RemoteException
{
vibrateEnabled = enabled;
}
};
class DeconnectionTask extends AsyncTask<XMPPConnection, Void, Void>
{
protected Void doInBackground(XMPPConnection... connections)
{
connections[0].disconnect();
return null;
}
protected void onPostExecute(Void param)
{}
}
public void configure(ProviderManager pm)
{
// Private Data Storage
pm.addIQProvider("query", "jabber:iq:private", new PrivateDataManager.PrivateDataIQProvider());
// Time
try
{
pm.addIQProvider("query", "jabber:iq:time", Class.forName("org.jivesoftware.smackx.packet.Time"));
}
catch (ClassNotFoundException e)
{
Log.w("TestClient", "Can't load class for org.jivesoftware.smackx.packet.Time");
}
// Roster Exchange
pm.addExtensionProvider("x", "jabber:x:roster", new RosterExchangeProvider());
// Message Events
pm.addExtensionProvider("x", "jabber:x:event", new MessageEventProvider());
// Chat State
pm.addExtensionProvider("active", "http://jabber.org/protocol/chatstates", new ChatStateExtension.Provider());
pm.addExtensionProvider("composing", "http://jabber.org/protocol/chatstates", new ChatStateExtension.Provider());
pm.addExtensionProvider("paused", "http://jabber.org/protocol/chatstates", new ChatStateExtension.Provider());
pm.addExtensionProvider("inactive", "http://jabber.org/protocol/chatstates", new ChatStateExtension.Provider());
pm.addExtensionProvider("gone", "http://jabber.org/protocol/chatstates", new ChatStateExtension.Provider());
// XHTML
pm.addExtensionProvider("html", "http://jabber.org/protocol/xhtml-im", new XHTMLExtensionProvider());
// Group Chat Invitations
pm.addExtensionProvider("x", "jabber:x:conference", new GroupChatInvitation.Provider());
// Service Discovery # Items
pm.addIQProvider("query", "http://jabber.org/protocol/disco#items", new DiscoverItemsProvider());
// Service Discovery # Info
pm.addIQProvider("query", "http://jabber.org/protocol/disco#info", new DiscoverInfoProvider());
// Data Forms
pm.addExtensionProvider("x", "jabber:x:data", new DataFormProvider());
// MUC User
pm.addExtensionProvider("x", "http://jabber.org/protocol/muc#user", new MUCUserProvider());
// MUC Admin
pm.addIQProvider("query", "http://jabber.org/protocol/muc#admin", new MUCAdminProvider());
// MUC Owner
pm.addIQProvider("query", "http://jabber.org/protocol/muc#owner", new MUCOwnerProvider());
// Delayed Delivery
pm.addExtensionProvider("x", "jabber:x:delay", new DelayInformationProvider());
// Version
try
{
pm.addIQProvider("query", "jabber:iq:version", Class.forName("org.jivesoftware.smackx.packet.Version"));
}
catch (ClassNotFoundException e)
{
// Not sure what's happening here.
}
// VCard
pm.addIQProvider("vCard", "vcard-temp", new VCardProvider());
// Offline Message Requests
pm.addIQProvider("offline", "http://jabber.org/protocol/offline", new OfflineMessageRequest.Provider());
// Offline Message Indicator
pm.addExtensionProvider("offline", "http://jabber.org/protocol/offline", new OfflineMessageInfo.Provider());
// Last Activity
pm.addIQProvider("query", "jabber:iq:last", new LastActivity.Provider());
// User Search
pm.addIQProvider("query", "jabber:iq:search", new UserSearch.Provider());
// SharedGroupsInfo
pm.addIQProvider("sharedgroup", "http://www.jivesoftware.org/protocol/sharedgroup", new SharedGroupsInfo.Provider());
// JEP-33: Extended Stanza Addressing
pm.addExtensionProvider("addresses", "http://jabber.org/protocol/address", new MultipleAddressesProvider());
// FileTransfer
pm.addIQProvider("si", "http://jabber.org/protocol/si", new StreamInitiationProvider());
pm.addIQProvider("query", "http://jabber.org/protocol/bytestreams", new BytestreamsProvider());
// Privacy
pm.addIQProvider("query", "jabber:iq:privacy", new PrivacyProvider());
pm.addIQProvider("command", "http://jabber.org/protocol/commands", new AdHocCommandDataProvider());
pm.addExtensionProvider("malformed-action", "http://jabber.org/protocol/commands", new AdHocCommandDataProvider.MalformedActionError());
pm.addExtensionProvider("bad-locale", "http://jabber.org/protocol/commands", new AdHocCommandDataProvider.BadLocaleError());
pm.addExtensionProvider("bad-payload", "http://jabber.org/protocol/commands", new AdHocCommandDataProvider.BadPayloadError());
pm.addExtensionProvider("bad-sessionid", "http://jabber.org/protocol/commands", new AdHocCommandDataProvider.BadSessionIDError());
pm.addExtensionProvider("session-expired", "http://jabber.org/protocol/commands", new AdHocCommandDataProvider.SessionExpiredError());
}
/**
* Derived class from Asmack
* Reconnect only when there is an available internet connection
*
* Handles the automatic reconnection process. Every time a connection is dropped without
* the application explictly closing it, the manager automatically tries to reconnect to
* the server.
* <p>
*
* The reconnection mechanism will try to reconnect periodically:
* <ol>
* <li>For the first minute it will attempt to connect once every ten seconds.
* <li>For the next five minutes it will attempt to connect once a minute.
* <li>If that fails it will indefinitely try to connect once every five minutes.
* </ol>
*
* @author Francisco Vives
*/
public static class ReconnectionManager implements ConnectionListener
{
// Holds the connection to the server
private Connection connection;
private Thread reconnectionThread;
private boolean isInternetActive;
// Holds the state of the reconnection
boolean done = false;
private ReconnectionManager(Connection connection, boolean internetIsActive)
{
this.connection = connection;
}
/**
* Returns true if the reconnection mechanism is enabled.
*
* @return true if automatic reconnections are allowed.
*/
private boolean isReconnectionAllowed()
{
return !done && !connection.isConnected() && isInternetActive;
}
private void setInternetIsActive(boolean active)
{
isInternetActive = active;
if (isReconnectionAllowed())
reconnect();
}
/**
* Starts a reconnection mechanism if it was configured to do that.
* The algorithm is been executed when the first connection error is detected.
* <p/>
* The reconnection mechanism will try to reconnect periodically in this way:
* <ol>
* <li>First it will try 6 times every 10 seconds.
* <li>Then it will try 10 times every 1 minute.
* <li>Finally it will try indefinitely every 5 minutes.
* </ol>
*/
synchronized protected void reconnect()
{
Log.d(TAG, "reconnect");
if (this.isReconnectionAllowed())
{
// Since there is no thread running, creates a new one to attempt
// the reconnection.
// avoid to run duplicated reconnectionThread -- fd: 16/09/2010
if (reconnectionThread != null && reconnectionThread.isAlive())
return;
reconnectionThread = new Thread() {
private int numAttemps = 0;
/**
* The process will try the reconnection until the connection succeed or the user
* cancell it
*/
public void run()
{
// The process will try to reconnect until the connection is established or
// the user cancel the reconnection process {@link Connection#disconnect()}
while (ReconnectionManager.this.isReconnectionAllowed())
{
// Wait 3 seconds before trying to reconnect again
int remainingSeconds = numAttemps == 0 ? 0 : 3;
// Sleep until we're ready for the next reconnection attempt. Notify
// listeners once per second about how much time remains before the next
// reconnection attempt.
while (ReconnectionManager.this.isReconnectionAllowed() && remainingSeconds > 0)
{
try
{
Thread.sleep(1000);
remainingSeconds--;
ReconnectionManager.this.notifyAttemptToReconnectIn(remainingSeconds);
}
catch (InterruptedException e1)
{
e1.printStackTrace();
// Notify the reconnection has failed
ReconnectionManager.this.notifyReconnectionFailed(e1);
}
}
// Makes a reconnection attempt
try
{
if (ReconnectionManager.this.isReconnectionAllowed())
{
Log.d(TAG, "ReconnectionManager Reconnecting");
connection.connect();
}
}
catch (XMPPException e)
{
// Fires the failed reconnection notification
ReconnectionManager.this.notifyReconnectionFailed(e);
}
numAttemps++;
}
}
};
reconnectionThread.setName("Smack Reconnection Manager");
reconnectionThread.setDaemon(true);
reconnectionThread.start();
}
}
/**
* Fires listeners when a reconnection attempt has failed.
*
* @param exception the exception that occured.
*/
protected void notifyReconnectionFailed(Exception exception)
{
if (isReconnectionAllowed())
{
/*
* for (ConnectionListener listener : connection.connectionListeners) {
* listener.reconnectionFailed(exception);
* }
*/
}
}
/**
* Fires listeners when The Connection will retry a reconnection. Expressed in seconds.
*
* @param seconds the number of seconds that a reconnection will be attempted in.
*/
protected void notifyAttemptToReconnectIn(int seconds)
{
if (isReconnectionAllowed())
{
/*
* for (ConnectionListener listener : connection.connectionListeners) {
* listener.reconnectingIn(seconds);
* }
*/
}
}
public void connectionClosed()
{
done = true;
}
public void connectionClosedOnError(Exception e)
{
done = false;
if (e instanceof XMPPException)
{
XMPPException xmppEx = (XMPPException) e;
StreamError error = xmppEx.getStreamError();
// Make sure the error is not null
if (error != null)
{
String reason = error.getCode();
if ("conflict".equals(reason))
{
return;
}
}
}
if (this.isReconnectionAllowed())
{
this.reconnect();
}
}
public void reconnectingIn(int seconds)
{
Log.d(TAG, "reconnectionIn");
}
public void reconnectionFailed(Exception e)
{
Log.d(TAG, "reconnectionFailed");
}
/**
* The connection has successfull gotten connected.
*/
public void reconnectionSuccessful()
{
Log.d(TAG, "reconnectionSuccessful");
// MessengerService.this.executePendingActions();
}
}
}