299 lines
11 KiB
Java
299 lines
11 KiB
Java
|
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;
|
||
|
import android.os.Handler;
|
||
|
import android.os.IBinder;
|
||
|
import android.os.Looper;
|
||
|
import android.util.Log;
|
||
|
import android.util.LongSparseArray;
|
||
|
import android.widget.Toast;
|
||
|
|
||
|
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;
|
||
|
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;
|
||
|
|
||
|
/**
|
||
|
* Started Service, which is responsible for managing {@link XMPPConnection XMPPConnections}
|
||
|
* affiliated with registered accounts.
|
||
|
*/
|
||
|
public class XmppStartedService extends Service {
|
||
|
|
||
|
private static final String TAG = MercuryImApplication.TAG;
|
||
|
|
||
|
private static final String APP = "org.olomono.mercury";
|
||
|
private static final String SERVICE = APP + ".XmppStartedService";
|
||
|
|
||
|
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";
|
||
|
|
||
|
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";
|
||
|
|
||
|
public static final String EVENT_INCOMING_MESSAGE = EVENT + ".INCOMING_MESSAGE";
|
||
|
public static final String EVENT_OUTGOING_MESSAGE = EVENT + ".OUTGOING_MESSAGE";
|
||
|
|
||
|
public static final String EXTRA_CONFIGURATION = EXTRA + ".CONFIGURATION";
|
||
|
public static final String EXTRA_ACCOUNT_ID = EXTRA + ".ACCOUNT_ID";
|
||
|
|
||
|
public static final String STATUS_SUCCESS = STATUS + ".SUCCESS";
|
||
|
public static final String STATUS_FAILURE = STATUS + ".FAILURE";
|
||
|
|
||
|
@Inject
|
||
|
AppDatabase database;
|
||
|
|
||
|
@Inject
|
||
|
ContactRepository rosterRepository;
|
||
|
|
||
|
@Inject
|
||
|
AccountRepository accountRepository;
|
||
|
|
||
|
private final LongSparseArray<XMPPConnection> connections = new LongSparseArray<>();
|
||
|
|
||
|
private Handler uiHandler;
|
||
|
|
||
|
@Nullable
|
||
|
@Override
|
||
|
public final IBinder onBind(Intent intent) {
|
||
|
// We are a started service, so we don't provide a binding and always return null.
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void onCreate() {
|
||
|
super.onCreate();
|
||
|
Log.d(TAG, "onCreate()");
|
||
|
MercuryImApplication.getApplication().getAppComponent().inject(this);
|
||
|
|
||
|
Intent intent = new Intent(this, XmppBoundService.class);
|
||
|
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
|
||
|
}
|
||
|
|
||
|
private ServiceConnection serviceConnection = new ServiceConnection() {
|
||
|
@Override
|
||
|
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
|
||
|
XmppBoundService.Binder binder = (XmppBoundService.Binder) iBinder;
|
||
|
XmppBoundService boundService = binder.getService();
|
||
|
boundService.setXmppStartedService(XmppStartedService.this);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void onServiceDisconnected(ComponentName componentName) {
|
||
|
|
||
|
}
|
||
|
};
|
||
|
|
||
|
@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;
|
||
|
Toast.makeText(XmppStartedService.this, (m.getFrom() != null ? m.getFrom().toString() : "null") + ": "
|
||
|
+ 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());
|
||
|
RoomContactModel m = new RoomContactModel(e.getJid().asEntityBareJidIfPossible(), e.getName(), e.getName());
|
||
|
m.setAccountId(accountId);
|
||
|
rosterRepository.updateOrInsertRosterEntry(m);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@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);
|
||
|
}
|
||
|
}
|