package i2p.bote.android.service;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.graphics.Bitmap;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v4.app.NotificationCompat;
import android.support.v4.content.LocalBroadcastManager;
import net.i2p.android.router.service.IRouterState;
import net.i2p.android.router.service.IRouterStateCallback;
import net.i2p.android.router.service.State;
import net.i2p.android.ui.I2PAndroidHelper;
import net.i2p.client.DomainSocketFactory;
import net.i2p.router.Router;
import net.i2p.router.RouterContext;
import net.i2p.router.RouterLaunch;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.HashSet;
import java.util.List;
import javax.mail.Address;
import javax.mail.MessagingException;
import i2p.bote.I2PBote;
import i2p.bote.android.EmailListActivity;
import i2p.bote.android.R;
import i2p.bote.android.ViewEmailActivity;
import i2p.bote.android.service.Init.RouterChoice;
import i2p.bote.android.util.BoteHelper;
import i2p.bote.android.util.LocaleManager;
import i2p.bote.email.Email;
import i2p.bote.fileencryption.PasswordException;
import i2p.bote.folder.EmailFolder;
import i2p.bote.folder.NewEmailListener;
import i2p.bote.network.NetworkStatus;
import i2p.bote.network.NetworkStatusListener;
public class BoteService extends Service implements NetworkStatusListener, NewEmailListener {
/**
* The locale has just changed.
*/
public static final String LOCAL_BROADCAST_LOCALE_CHANGED = "i2p.bote.android.LOCAL_BROADCAST_LOCALE_CHANGED";
public static final String ROUTER_CHOICE = "router_choice";
public static final int NOTIF_ID_SERVICE = 8073;
public static final int NOTIF_ID_NEW_EMAIL = 80739047;
private LocaleManager localeManager = new LocaleManager();
RouterChoice mRouterChoice;
NotificationCompat.Builder mStatusBuilder;
@Override
public void onCreate() {
LocalBroadcastManager.getInstance(this).registerReceiver(onLocaleChanged, new IntentFilter(LOCAL_BROADCAST_LOCALE_CHANGED));
}
private BroadcastReceiver onLocaleChanged = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
localeManager.updateServiceLocale(BoteService.this);
networkStatusChanged();
}
};
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
mRouterChoice = (RouterChoice) intent.getSerializableExtra(ROUTER_CHOICE);
if (mRouterChoice == RouterChoice.INTERNAL)
new Thread(new RouterStarter()).start();
I2PBote bote = I2PBote.getInstance();
bote.getConfiguration().setI2CPDomainSocket(DomainSocketFactory.I2CP_SOCKET_ADDRESS);
bote.startUp();
bote.addNewEmailListener(this);
if (mRouterChoice == RouterChoice.ANDROID) {
// Bind to I2P Android
mTriedBindState = (new I2PAndroidHelper(this)).bind(mStateConnection, 0);
} else if (mRouterChoice == RouterChoice.REMOTE)
bote.connectNow();
mStatusBuilder = new NotificationCompat.Builder(this)
.setContentTitle(getResources().getString(R.string.app_name))
.setSmallIcon(R.drawable.ic_notif)
.setOngoing(true)
.setOnlyAlertOnce(true);
Intent ni = new Intent(this, EmailListActivity.class);
ni.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pi = PendingIntent.getActivity(this, 0, ni, PendingIntent.FLAG_UPDATE_CURRENT);
mStatusBuilder.setContentIntent(pi);
updateServiceNotifText();
startForeground(NOTIF_ID_SERVICE, mStatusBuilder.build());
bote.addNetworkStatusListener(this);
return START_REDELIVER_INTENT;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
if (mTriedBindState && mStateConnection != null) {
if (mStateService != null) {
try {
mStateService.unregisterCallback(mStatusListener);
} catch (RemoteException e) {}
}
unbindService(mStateConnection);
}
mTriedBindState = false;
LocalBroadcastManager.getInstance(this).unregisterReceiver(onLocaleChanged);
I2PBote.getInstance().removeNetworkStatusListener(this);
I2PBote.getInstance().removeNewEmailListener(this);
new Thread(new Runnable() {
@Override
public void run() {
I2PBote.getInstance().shutDown();
}
}).start();
if (mRouterChoice == RouterChoice.INTERNAL)
new Thread(new RouterStopper()).start();
}
//
// Internal router helpers
//
private RouterContext mRouterContext;
private class RouterStarter implements Runnable {
public void run() {
RouterLaunch.main(null);
List<RouterContext> contexts = RouterContext.listContexts();
mRouterContext = contexts.get(0);
mRouterContext.router().setKillVMOnEnd(false);
}
}
private class RouterStopper implements Runnable {
public void run() {
RouterContext ctx = mRouterContext;
if (ctx != null)
ctx.router().shutdown(Router.EXIT_HARD);
}
}
//
// I2P Android helpers
//
private IRouterState mStateService = null;
private boolean mTriedBindState;
private ServiceConnection mStateConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder service) {
mStateService = IRouterState.Stub.asInterface(service);
try {
mStateService.registerCallback(mStatusListener);
final State state = mStateService.getState();
if (state == State.ACTIVE &&
I2PBote.getInstance().getNetworkStatus() == NetworkStatus.DELAY)
I2PBote.getInstance().connectNow();
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
mStateService = null;
}
};
private final IRouterStateCallback.Stub mStatusListener =
new IRouterStateCallback.Stub() {
public void stateChanged(State newState) throws RemoteException {
if (newState == State.ACTIVE &&
I2PBote.getInstance().getNetworkStatus() == NetworkStatus.DELAY)
I2PBote.getInstance().connectNow();
else if (newState == State.STOPPING ||
newState == State.MANUAL_STOPPING ||
newState == State.MANUAL_QUITTING ||
newState == State.NETWORK_STOPPING)
stopSelf();
}
};
// NetworkStatusListener
@Override
public void networkStatusChanged() {
updateServiceNotifText();
startForeground(NOTIF_ID_SERVICE, mStatusBuilder.build());
}
private void updateServiceNotifText() {
String statusText;
switch (I2PBote.getInstance().getNetworkStatus()) {
case DELAY:
statusText = getResources().getString(R.string.waiting_for_network);
break;
case CONNECTING:
statusText = getResources().getString(R.string.connecting_to_network);
break;
case CONNECTED:
statusText = getResources().getString(R.string.connected_to_network);
break;
case ERROR:
statusText = getResources().getString(R.string.error);
break;
case NOT_STARTED:
default:
statusText = getResources().getString(R.string.not_started);
}
mStatusBuilder.setContentText(statusText);
}
// NewEmailListener
@Override
public void emailReceived(String messageId) {
NotificationManager nm = (NotificationManager) getSystemService(
Context.NOTIFICATION_SERVICE);
NotificationCompat.Builder b = new NotificationCompat.Builder(this)
.setAutoCancel(true)
.setSmallIcon(R.drawable.ic_notif)
.setDefaults(Notification.DEFAULT_ALL);
try {
EmailFolder inbox = I2PBote.getInstance().getInbox();
// Set the new email as \Recent
inbox.setRecent(messageId, true);
// Now display/update notification with all \Recent emails
List<Email> newEmails = BoteHelper.getRecentEmails(inbox);
int numNew = newEmails.size();
switch (numNew) {
case 0:
nm.cancel(NOTIF_ID_NEW_EMAIL);
return;
case 1:
Email email = newEmails.get(0);
String fromAddress = email.getOneFromAddress();
Bitmap picture = BoteHelper.getPictureForAddress(fromAddress);
if (picture != null)
b.setLargeIcon(picture);
else if (!email.isAnonymous()) {
int width = getResources().getDimensionPixelSize(R.dimen.notification_large_icon_width);
int height = getResources().getDimensionPixelSize(R.dimen.notification_large_icon_height);
b.setLargeIcon(BoteHelper.getIdenticonForAddress(fromAddress, width, height));
} else
b.setSmallIcon(R.drawable.ic_contact_picture);
b.setContentTitle(BoteHelper.getNameAndShortDestination(
fromAddress));
b.setContentText(email.getSubject());
Intent vei = new Intent(this, ViewEmailActivity.class);
vei.putExtra(ViewEmailActivity.FOLDER_NAME, inbox.getName());
vei.putExtra(ViewEmailActivity.MESSAGE_ID, email.getMessageID());
vei.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pvei = PendingIntent.getActivity(this, 0, vei, PendingIntent.FLAG_UPDATE_CURRENT);
b.setContentIntent(pvei);
break;
default:
b.setContentTitle(getResources().getQuantityString(
R.plurals.n_new_emails, numNew, numNew));
HashSet<Address> recipients = new HashSet<Address>();
String bigText = "";
for (Email ne : newEmails) {
recipients.add(BoteHelper.getOneLocalRecipient(ne));
bigText += BoteHelper.getNameAndShortDestination(
ne.getOneFromAddress());
bigText += ": " + ne.getSubject() + "\n";
}
b.setContentText(BoteHelper.joinAddressNames(recipients));
b.setStyle(new NotificationCompat.BigTextStyle().bigText(bigText));
Intent eli = new Intent(this, EmailListActivity.class);
eli.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent peli = PendingIntent.getActivity(this, 0, eli, PendingIntent.FLAG_UPDATE_CURRENT);
b.setContentIntent(peli);
}
} catch (PasswordException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (MessagingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (GeneralSecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
nm.notify(NOTIF_ID_NEW_EMAIL, b.build());
}
}