2019-05-27 21:34:11 +02:00
|
|
|
package org.mercury_im.messenger.service;
|
|
|
|
|
|
|
|
import android.app.Notification;
|
|
|
|
import android.app.PendingIntent;
|
|
|
|
import android.app.Service;
|
|
|
|
import android.content.ComponentName;
|
|
|
|
import android.content.Context;
|
|
|
|
import android.content.Intent;
|
|
|
|
import android.content.ServiceConnection;
|
2019-06-06 23:32:41 +02:00
|
|
|
import android.os.Binder;
|
2019-05-27 21:34:11 +02:00
|
|
|
import android.os.Handler;
|
|
|
|
import android.os.IBinder;
|
|
|
|
import android.os.Looper;
|
|
|
|
import android.util.Log;
|
|
|
|
import android.util.LongSparseArray;
|
|
|
|
import android.widget.Toast;
|
|
|
|
|
2019-06-10 02:52:08 +02:00
|
|
|
import androidx.annotation.NonNull;
|
2019-05-27 21:34:11 +02:00
|
|
|
import androidx.annotation.Nullable;
|
|
|
|
import androidx.core.app.NotificationCompat;
|
|
|
|
import androidx.core.app.NotificationManagerCompat;
|
|
|
|
|
|
|
|
import org.jivesoftware.smack.SmackException;
|
|
|
|
import org.jivesoftware.smack.XMPPConnection;
|
|
|
|
import org.jivesoftware.smack.XMPPException;
|
|
|
|
import org.jivesoftware.smack.chat2.Chat;
|
|
|
|
import org.jivesoftware.smack.chat2.ChatManager;
|
|
|
|
import org.jivesoftware.smack.chat2.IncomingChatMessageListener;
|
|
|
|
import org.jivesoftware.smack.packet.Message;
|
|
|
|
import org.jivesoftware.smack.roster.Roster;
|
|
|
|
import org.jivesoftware.smack.roster.RosterEntry;
|
|
|
|
import org.jivesoftware.smack.roster.RosterLoadedListener;
|
|
|
|
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
|
|
|
|
import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration;
|
2019-06-10 02:52:08 +02:00
|
|
|
import org.jivesoftware.smackx.ping.android.ServerPingWithAlarmManager;
|
2019-05-27 21:34:11 +02:00
|
|
|
import org.jxmpp.jid.EntityBareJid;
|
|
|
|
import org.mercury_im.messenger.MercuryImApplication;
|
|
|
|
import org.mercury_im.messenger.Notifications;
|
|
|
|
import org.mercury_im.messenger.R;
|
|
|
|
import org.mercury_im.messenger.persistence.model.AccountModel;
|
|
|
|
import org.mercury_im.messenger.persistence.repository.AccountRepository;
|
|
|
|
import org.mercury_im.messenger.persistence.repository.ContactRepository;
|
|
|
|
import org.mercury_im.messenger.persistence.room.AppDatabase;
|
|
|
|
import org.mercury_im.messenger.persistence.room.model.RoomAccountModel;
|
|
|
|
import org.mercury_im.messenger.persistence.room.model.RoomContactModel;
|
|
|
|
import org.mercury_im.messenger.ui.MainActivity;
|
|
|
|
import org.mercury_im.messenger.xmpp_android.ParcelableXMPPTCPConnectionConfiguration;
|
|
|
|
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.Set;
|
|
|
|
|
|
|
|
import javax.inject.Inject;
|
|
|
|
|
|
|
|
/**
|
2019-06-06 23:32:41 +02:00
|
|
|
* Started, Bound Service, which is responsible for managing {@link XMPPConnection XMPPConnections}
|
2019-05-27 21:34:11 +02:00
|
|
|
* affiliated with registered accounts.
|
|
|
|
*/
|
2019-06-06 23:32:41 +02:00
|
|
|
public class XmppConnectionService extends Service {
|
2019-05-27 21:34:11 +02:00
|
|
|
|
|
|
|
private static final String TAG = MercuryImApplication.TAG;
|
|
|
|
|
|
|
|
private static final String APP = "org.olomono.mercury";
|
2019-06-06 23:32:41 +02:00
|
|
|
private static final String SERVICE = APP + ".XmppConnectionService";
|
2019-05-27 21:34:11 +02:00
|
|
|
|
|
|
|
private static final String ACTION = SERVICE + ".ACTION";
|
|
|
|
private static final String EVENT = SERVICE + ".EVENT";
|
|
|
|
private static final String EXTRA = SERVICE + ".EXTRA";
|
|
|
|
private static final String STATUS = SERVICE + ".STATUS";
|
|
|
|
|
2019-06-06 23:32:41 +02:00
|
|
|
// ACTIONS
|
2019-05-27 21:34:11 +02:00
|
|
|
public static final String ACTION_START = ACTION + ".START";
|
|
|
|
public static final String ACTION_STOP = ACTION + ".STOP";
|
|
|
|
public static final String ACTION_CONNECT = ACTION + ".CONNECT";
|
|
|
|
public static final String ACTION_DISCONNECT = ACTION + ".DISCONNECT";
|
|
|
|
public static final String ACTION_PING = ACTION + ".PING";
|
|
|
|
|
2019-06-06 23:32:41 +02:00
|
|
|
// EVENTS
|
2019-05-27 21:34:11 +02:00
|
|
|
public static final String EVENT_INCOMING_MESSAGE = EVENT + ".INCOMING_MESSAGE";
|
|
|
|
public static final String EVENT_OUTGOING_MESSAGE = EVENT + ".OUTGOING_MESSAGE";
|
|
|
|
|
2019-06-06 23:32:41 +02:00
|
|
|
// EXTRAS
|
2019-05-27 21:34:11 +02:00
|
|
|
public static final String EXTRA_CONFIGURATION = EXTRA + ".CONFIGURATION";
|
|
|
|
public static final String EXTRA_ACCOUNT_ID = EXTRA + ".ACCOUNT_ID";
|
|
|
|
|
2019-06-06 23:32:41 +02:00
|
|
|
// STATUSES
|
2019-05-27 21:34:11 +02:00
|
|
|
public static final String STATUS_SUCCESS = STATUS + ".SUCCESS";
|
|
|
|
public static final String STATUS_FAILURE = STATUS + ".FAILURE";
|
|
|
|
|
|
|
|
|
|
|
|
@Inject
|
|
|
|
ContactRepository rosterRepository;
|
|
|
|
|
|
|
|
@Inject
|
|
|
|
AccountRepository accountRepository;
|
|
|
|
|
|
|
|
private final LongSparseArray<XMPPConnection> connections = new LongSparseArray<>();
|
|
|
|
|
|
|
|
private Handler uiHandler;
|
|
|
|
|
2019-06-10 02:52:08 +02:00
|
|
|
@NonNull
|
2019-05-27 21:34:11 +02:00
|
|
|
@Override
|
|
|
|
public final IBinder onBind(Intent intent) {
|
2019-06-10 02:52:08 +02:00
|
|
|
return new Binder(this);
|
2019-05-27 21:34:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onCreate() {
|
|
|
|
super.onCreate();
|
|
|
|
Log.d(TAG, "onCreate()");
|
|
|
|
MercuryImApplication.getApplication().getAppComponent().inject(this);
|
2019-06-10 02:52:08 +02:00
|
|
|
|
|
|
|
// Begin life cycle of Ping Manager.
|
|
|
|
// The Manager will automatically detect newly created connections and ping the server
|
|
|
|
// every half hour if necessary.
|
|
|
|
ServerPingWithAlarmManager.onCreate(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onDestroy() {
|
|
|
|
super.onDestroy();
|
|
|
|
Log.d(TAG, "onDestroy()");
|
|
|
|
|
|
|
|
// End life cycle of Ping Manager.
|
|
|
|
ServerPingWithAlarmManager.onDestroy();
|
2019-05-27 21:34:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
|
|
|
|
|
|
|
if (uiHandler == null) {
|
|
|
|
uiHandler = new Handler(Looper.getMainLooper()) {
|
|
|
|
@Override
|
|
|
|
public void handleMessage(android.os.Message msg) {
|
|
|
|
switch (msg.what) {
|
|
|
|
case 1:
|
|
|
|
Message m = (Message) msg.obj;
|
2019-06-06 23:32:41 +02:00
|
|
|
Toast.makeText(XmppConnectionService.this, (m.getFrom() != null ? m.getFrom().toString() : "null") + ": "
|
2019-05-27 21:34:11 +02:00
|
|
|
+ m.getBody(), Toast.LENGTH_LONG).show();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
Log.d(TAG, "onStartCommand(" + intent + ")");
|
|
|
|
if (intent == null) {
|
|
|
|
startAndDisplayForegroundNotification();
|
|
|
|
} else {
|
|
|
|
String action = intent.getAction();
|
|
|
|
action = action == null ? "" : action;
|
|
|
|
|
|
|
|
switch (action) {
|
|
|
|
case ACTION_START:
|
|
|
|
startAndDisplayForegroundNotification();
|
|
|
|
startConnections();
|
|
|
|
break;
|
|
|
|
case ACTION_STOP:
|
|
|
|
stopForeground(true);
|
|
|
|
break;
|
|
|
|
case ACTION_CONNECT:
|
|
|
|
ParcelableXMPPTCPConnectionConfiguration configuration = intent.getParcelableExtra(EXTRA_CONFIGURATION);
|
|
|
|
if (configuration == null) {
|
|
|
|
Log.e(TAG, "Configuration is null.");
|
|
|
|
return START_STICKY_COMPATIBILITY;
|
|
|
|
}
|
|
|
|
|
|
|
|
long accountId = intent.getLongExtra(EXTRA_ACCOUNT_ID, -1);
|
|
|
|
if (accountId == -1) {
|
|
|
|
Log.d(TAG, "No AccountID provided.");
|
|
|
|
return START_STICKY_COMPATIBILITY;
|
|
|
|
}
|
|
|
|
|
|
|
|
new Thread() {
|
|
|
|
public void run() {
|
|
|
|
XMPPConnection connection = startConnection(configuration, accountId);
|
|
|
|
connections.put(accountId, connection);
|
|
|
|
}
|
|
|
|
}.start();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return START_STICKY_COMPATIBILITY;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void startAndDisplayForegroundNotification() {
|
|
|
|
Log.d(TAG, "startAndDisplayForegroundNotification()");
|
|
|
|
Notification notification = getForegroundNotification(getApplicationContext(), connections.size());
|
|
|
|
|
|
|
|
startForeground(Notifications.FOREGROUND_SERVICE_ID, notification);
|
|
|
|
}
|
|
|
|
|
|
|
|
static Notification getForegroundNotification(Context context, int numConnections) {
|
|
|
|
Intent startMainActivityIntent = new Intent(context, MainActivity.class);
|
|
|
|
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0,
|
|
|
|
startMainActivityIntent, 0);
|
|
|
|
return new NotificationCompat.Builder(context, Notifications.NOTIFICATION_CHANNEL__FOREGROUND_SERVICE)
|
|
|
|
.setContentTitle("Mercury")
|
|
|
|
.setContentText(numConnections + " connections.")
|
|
|
|
.setSmallIcon(R.drawable.ic_send_black_24dp)
|
|
|
|
.setContentIntent(pendingIntent)
|
|
|
|
.build();
|
|
|
|
}
|
|
|
|
|
|
|
|
public void startConnections() {
|
|
|
|
new Thread() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
synchronized (connections) {
|
|
|
|
List<RoomAccountModel> accounts = accountRepository.getAllAccounts();
|
|
|
|
if (accounts == null) return;
|
|
|
|
|
|
|
|
for (AccountModel a : accounts) {
|
|
|
|
if (connections.get(a.getId()) != null) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// XMPPConnection connection = startConnection(a.getJid(), a.getPassword(), a.getId());
|
|
|
|
// connections.put(a.getId(), connection);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}.start();
|
|
|
|
}
|
|
|
|
|
|
|
|
private XMPPConnection startConnection(ParcelableXMPPTCPConnectionConfiguration connectionConfiguration, long accountId) {
|
|
|
|
XMPPTCPConnection con = null;
|
|
|
|
try {
|
|
|
|
XMPPTCPConnectionConfiguration conf = connectionConfiguration.getConfiguration();
|
|
|
|
con = new XMPPTCPConnection(conf);
|
|
|
|
con.connect().login();
|
|
|
|
} catch (
|
|
|
|
XMPPException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
} catch (
|
|
|
|
SmackException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
} catch (
|
|
|
|
IOException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
} catch (
|
|
|
|
InterruptedException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
|
|
|
|
NotificationManagerCompat.from(getApplicationContext()).notify(Notifications.FOREGROUND_SERVICE_ID,
|
|
|
|
getForegroundNotification(getApplicationContext(), connections.size()));
|
|
|
|
|
|
|
|
Roster roster = Roster.getInstanceFor(con);
|
|
|
|
roster.addRosterLoadedListener(new RosterLoadedListener() {
|
|
|
|
@Override
|
|
|
|
public void onRosterLoaded(Roster roster) {
|
|
|
|
Set<RosterEntry> entries = roster.getEntries();
|
|
|
|
for (RosterEntry e : entries) {
|
|
|
|
Log.d(TAG, "Inserting Roster entry " + e.getJid().toString());
|
2019-06-10 02:52:08 +02:00
|
|
|
|
|
|
|
RoomContactModel contact = new RoomContactModel();
|
|
|
|
contact.setAccountId(accountId);
|
|
|
|
// TODO: Fix
|
2019-05-27 21:34:11 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onRosterLoadingFailed(Exception exception) {
|
|
|
|
Log.d(TAG, "Roster Loading failed", exception);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
try {
|
|
|
|
roster.reload();
|
|
|
|
} catch (SmackException.NotLoggedInException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
} catch (SmackException.NotConnectedException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
} catch (InterruptedException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
|
|
|
|
ChatManager chatManager = ChatManager.getInstanceFor(con);
|
|
|
|
chatManager.addIncomingListener(new IncomingChatMessageListener() {
|
|
|
|
@Override
|
|
|
|
public void newIncomingMessage(EntityBareJid from, Message message, Chat chat) {
|
|
|
|
android.os.Message msg = uiHandler.obtainMessage(1, message);
|
|
|
|
msg.sendToTarget();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return con;
|
|
|
|
}
|
|
|
|
|
|
|
|
public XMPPConnection getConnection(long accountId) {
|
|
|
|
return connections.get(accountId);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void putConnection(int accountId, XMPPConnection connection) {
|
|
|
|
connections.put(accountId, connection);
|
|
|
|
}
|
2019-06-10 02:52:08 +02:00
|
|
|
|
|
|
|
public class Binder extends android.os.Binder {
|
|
|
|
|
|
|
|
private final XmppConnectionService service;
|
|
|
|
|
|
|
|
public Binder(XmppConnectionService service) {
|
|
|
|
this.service = service;
|
|
|
|
}
|
|
|
|
|
|
|
|
public XmppConnectionService getService() {
|
|
|
|
return service;
|
|
|
|
}
|
|
|
|
}
|
2019-05-27 21:34:11 +02:00
|
|
|
}
|