diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 50e1da6..8c26da2 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -22,11 +22,14 @@ along with Yaaic. If not, see . - + android:versionName="0.2"> + + android:label="@string/app_name" + android:launchMode="singleTask"> diff --git a/src/org/yaaic/Yaaic.java b/src/org/yaaic/Yaaic.java index 990d920..4f59a99 100644 --- a/src/org/yaaic/Yaaic.java +++ b/src/org/yaaic/Yaaic.java @@ -23,8 +23,13 @@ package org.yaaic; import java.util.ArrayList; import java.util.HashMap; +import org.yaaic.db.Database; +import org.yaaic.model.Broadcast; import org.yaaic.model.Server; +import android.content.Context; +import android.content.Intent; + /** * Global Master Class :) * @@ -35,6 +40,7 @@ public class Yaaic public static Yaaic instance; private HashMap servers; + private boolean serversLoaded = false; /** * Private constructor, you may want to use static getInstance() @@ -44,6 +50,23 @@ public class Yaaic servers = new HashMap(); } + /** + * Load servers from database + * + * @param context + */ + public void loadServers(Context context) + { + if (!serversLoaded) { + Database db = new Database(context); + servers = db.getServers(); + db.close(); + + //context.sendBroadcast(new Intent(Broadcast.SERVER_UPDATE)); + serversLoaded = true; + } + } + /** * Get global Yaaic instance * diff --git a/src/org/yaaic/irc/IRCConnection.java b/src/org/yaaic/irc/IRCConnection.java index 2115407..f80348f 100644 --- a/src/org/yaaic/irc/IRCConnection.java +++ b/src/org/yaaic/irc/IRCConnection.java @@ -1036,6 +1036,10 @@ public class IRCConnection extends PircBot @Override public void quitServer() { - quitServer("Yaaic - Yet another Android IRC client - http://www.yaaic.org"); + new Thread() { + public void run() { + quitServer("Yaaic - Yet another Android IRC client - http://www.yaaic.org"); + } + }.start(); } } diff --git a/src/org/yaaic/irc/IRCService.java b/src/org/yaaic/irc/IRCService.java index fb54c58..e4ea423 100644 --- a/src/org/yaaic/irc/IRCService.java +++ b/src/org/yaaic/irc/IRCService.java @@ -20,12 +20,20 @@ along with Yaaic. If not, see . */ package org.yaaic.irc; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; import java.util.HashMap; +import java.util.Set; +import org.yaaic.R; import org.yaaic.Yaaic; -import org.yaaic.db.Database; -import org.yaaic.model.Broadcast; +import org.yaaic.model.Server; +import org.yaaic.view.ServersActivity; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; import android.app.Service; import android.content.Intent; import android.util.Log; @@ -42,6 +50,17 @@ public class IRCService extends Service private IRCBinder binder; private HashMap connections; + @SuppressWarnings("unchecked") + private static final Class[] mStartForegroundSignature = new Class[] { int.class, Notification.class }; + @SuppressWarnings("unchecked") + private static final Class[] mStopForegroundSignature = new Class[] { boolean.class }; + + private NotificationManager mNM; + private Method mStartForeground; + private Method mStopForeground; + private Object[] mStartForegroundArgs = new Object[2]; + private Object[] mStopForegroundArgs = new Object[1]; + /** * Create new service */ @@ -55,28 +74,127 @@ public class IRCService extends Service this.binder = new IRCBinder(this); } + /** + * On create + */ @Override public void onCreate() { - super.onCreate(); - - Log.d(TAG, "Loading servers from database"); + mNM = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + + try { + mStartForeground = getClass().getMethod("startForeground", mStartForegroundSignature); + mStopForeground = getClass().getMethod("stopForeground", mStopForegroundSignature); + } catch (NoSuchMethodException e) { + // Running on an older platform. + mStartForeground = mStopForeground = null; + } // Load servers from Database - Database db = new Database(this); + /* + Database db = new Database(this); Yaaic.getInstance().setServers(db.getServers()); db.close(); + */ // Broadcast changed server list - sendBroadcast(new Intent(Broadcast.SERVER_UPDATE)); + //sendBroadcast(new Intent(Broadcast.SERVER_UPDATE)); } + /** + * On start (will be called on pre-2.0 platform. On 2.0 or later onStartCommand() + * will be called) + */ @Override public void onStart(Intent intent, int startId) { - Log.d(TAG, "onStart()"); - super.onStart(intent, startId); + handleCommand(intent); } + + /** + * On start command (Android >= 2.0) + * + * @param intent + * @param flags + * @param startId + * @return + */ + public int onStartCommand(Intent intent, int flags, int startId) + { + handleCommand(intent); + + // We want this service to continue running until it is explicitly + // stopped, so return sticky. + //return START_STICKY; + return 1; + } + + + /** + * Handle command + * + * @param intent + */ + private void handleCommand(Intent intent) + { + // Set the icon, scrolling text and timestamp + Notification notification = new Notification(R.drawable.icon, "Mama", System.currentTimeMillis()); + + // The PendingIntent to launch our activity if the user selects this notification + PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, ServersActivity.class), 0); + + // Set the info for the views that show in the notification panel. + notification.setLatestEventInfo(this, getText(R.string.app_name), "Papa", contentIntent); + + startForegroundCompat(R.string.app_name, notification); + } + + /** + * This is a wrapper around the new startForeground method, using the older + * APIs if it is not available. + */ + private void startForegroundCompat(int id, Notification notification) + { + // If we have the new startForeground API, then use it. + if (mStartForeground != null) { + mStartForegroundArgs[0] = Integer.valueOf(id); + mStartForegroundArgs[1] = notification; + try { + mStartForeground.invoke(this, mStartForegroundArgs); + } catch (InvocationTargetException e) { + // Should not happen. + } catch (IllegalAccessException e) { + // Should not happen. + } + } else { + // Fall back on the old API. + setForeground(true); + mNM.notify(id, notification); + } + } + + /** + * This is a wrapper around the new stopForeground method, using the older + * APIs if it is not available. + */ + private void stopForegroundCompat(int id) { + // If we have the new stopForeground API, then use it. + if (mStopForeground != null) { + mStopForegroundArgs[0] = Boolean.TRUE; + try { + mStopForeground.invoke(this, mStopForegroundArgs); + } catch (InvocationTargetException e) { + // Should not happen. + } catch (IllegalAccessException e) { + // Should not happen. + } + } else { + // Fall back on the old API. Note to cancel BEFORE changing the + // foreground state, since we could be killed at that point. + mNM.cancel(id); + setForeground(false); + } + } /** * Get connection for given server @@ -96,6 +214,43 @@ public class IRCService extends Service return connection; } + public void checkServiceStatus() + { + Log.d("Yaaic", "(1)"); + + boolean shutDown = true; + ArrayList mServers = Yaaic.getInstance().getServersAsArrayList(); + int mSize = mServers.size(); + Server server; + + Log.d("Yaaic", "(2)"); + + for (int i = 0; i < mSize; i++) { + Log.d("Yaaic", " (3)"); + server = mServers.get(i); + if (server.isDisconnected()) { + connections.remove(server.getId()); + } else { + shutDown = false; + } + } + + if (shutDown) { + stopSelf(); + } + } + + /** + * + */ + @Override + public void onDestroy() + { + // Make sure our notification is gone. + stopForegroundCompat(R.string.app_name); + } + + /** * On Activity binding to this service * diff --git a/src/org/yaaic/view/ConversationActivity.java b/src/org/yaaic/view/ConversationActivity.java index e8874b2..e44c0b1 100644 --- a/src/org/yaaic/view/ConversationActivity.java +++ b/src/org/yaaic/view/ConversationActivity.java @@ -98,7 +98,7 @@ public class ConversationActivity extends Activity implements ServiceConnection, ((TextView) findViewById(R.id.title)).setText(server.getTitle()); ((EditText) findViewById(R.id.input)).setOnKeyListener(this); - + deckAdapter = new DeckAdapter(); deck = (Gallery) findViewById(R.id.deck); deck.setOnItemSelectedListener(this); @@ -123,7 +123,9 @@ public class ConversationActivity extends Activity implements ServiceConnection, ((ImageView) findViewById(R.id.status)).setImageResource(server.getStatusIcon()); + // Start service Intent intent = new Intent(this, IRCService.class); + startService(intent); bindService(intent, this, 0); channelReceiver = new ConversationReceiver(server.getId(), this); @@ -145,6 +147,10 @@ public class ConversationActivity extends Activity implements ServiceConnection, while (conversation.hasBufferedMessages()) { if (conversation.getMessageListAdapter() != null) { conversation.getMessageListAdapter().addMessage(conversation.pollBufferedMessage()); + } else { + // There's no adapter... so let's remove the buffer + conversation.clearBuffer(); + Log.d("Yaaic", "No MessageListAdapter found - clear buffer"); } } } @@ -158,6 +164,15 @@ public class ConversationActivity extends Activity implements ServiceConnection, { super.onPause(); + binder.getService().checkServiceStatus(); + + /*if (!binder.getService().hasConnections()) { + Log.d("Yaaic", "Stopping service"); + //binder.getService().stopSelf(); + } else { + Log.d("Yaaic", "Unbinding service"); + }*/ + unbindService(this); unregisterReceiver(channelReceiver); unregisterReceiver(serverReceiver); @@ -209,10 +224,12 @@ public class ConversationActivity extends Activity implements ServiceConnection, switch (item.getItemId()) { case R.id.disconnect: binder.getService().getConnection(serverId).quitServer(); + server.setStatus(Status.DISCONNECTED); server.clearConversations(); setResult(RESULT_OK); finish(); break; + case R.id.join: startActivityForResult(new Intent(this, JoinActivity.class), 0); break; diff --git a/src/org/yaaic/view/ServersActivity.java b/src/org/yaaic/view/ServersActivity.java index 061500b..d7ee4471 100644 --- a/src/org/yaaic/view/ServersActivity.java +++ b/src/org/yaaic/view/ServersActivity.java @@ -60,7 +60,7 @@ import org.yaaic.receiver.ServerReceiver; public class ServersActivity extends ListActivity implements ServiceConnection, ServerListener, OnItemLongClickListener { public static final String TAG = "Yaaic/ServersActivity"; - private IRCBinder binder; + //private IRCBinder binder; private ServerReceiver receiver; private ServerListAdapter adapter; @@ -73,6 +73,8 @@ public class ServersActivity extends ListActivity implements ServiceConnection, super.onCreate(savedInstanceState); setContentView(R.layout.servers); + Yaaic.getInstance().loadServers(this); + adapter = new ServerListAdapter(); setListAdapter(adapter); @@ -89,9 +91,9 @@ public class ServersActivity extends ListActivity implements ServiceConnection, super.onResume(); // Start and connect to service - Intent intent = new Intent(this, IRCService.class); - startService(intent); - bindService(intent, this, 0); + //Intent intent = new Intent(this, IRCService.class); + //startService(intent); + //bindService(intent, this, 0); receiver = new ServerReceiver(this); registerReceiver(receiver, new IntentFilter(Broadcast.SERVER_UPDATE)); @@ -107,7 +109,7 @@ public class ServersActivity extends ListActivity implements ServiceConnection, { super.onPause(); - unbindService(this); + //unbindService(this); unregisterReceiver(receiver); } @@ -116,7 +118,7 @@ public class ServersActivity extends ListActivity implements ServiceConnection, */ public void onServiceConnected(ComponentName name, IBinder service) { - binder = (IRCBinder) service; + //binder = (IRCBinder) service; } /** @@ -124,7 +126,7 @@ public class ServersActivity extends ListActivity implements ServiceConnection, */ public void onServiceDisconnected(ComponentName name) { - binder = null; + //binder = null; } /** @@ -165,19 +167,19 @@ public class ServersActivity extends ListActivity implements ServiceConnection, public void onClick(DialogInterface dialog, int item) { switch (item) { case 0: // Connect - binder.connect(server); + //binder.connect(server); server.setStatus(Status.CONNECTING); adapter.notifyDataSetChanged(); break; case 1: // Disconnect server.clearConversations(); - binder.getService().getConnection(server.getId()).quitServer(); + //binder.getService().getConnection(server.getId()).quitServer(); break; case 2: // Edit editServer(server.getId()); break; case 3: // Delete - binder.getService().getConnection(server.getId()).quitServer(); + //binder.getService().getConnection(server.getId()).quitServer(); deleteServer(server.getId()); break; }