From 41cf6b60d0db11f6c7d47da5b135e1278f954996 Mon Sep 17 00:00:00 2001 From: Sebastian Kaspari Date: Sun, 21 Mar 2010 19:57:30 +0100 Subject: [PATCH 1/3] permanent service with notification icon, stop service if not needed (work in progress) --- AndroidManifest.xml | 9 +- src/org/yaaic/Yaaic.java | 23 +++ src/org/yaaic/irc/IRCConnection.java | 6 +- src/org/yaaic/irc/IRCService.java | 173 ++++++++++++++++++- src/org/yaaic/view/ConversationActivity.java | 19 +- src/org/yaaic/view/ServersActivity.java | 22 +-- 6 files changed, 228 insertions(+), 24 deletions(-) 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; } From 13dde9ed0338b3efcb8b54b5e01584e242cea3aa Mon Sep 17 00:00:00 2001 From: Sebastian Kaspari Date: Wed, 24 Mar 2010 21:55:41 +0100 Subject: [PATCH 2/3] Imports --- src/org/yaaic/Yaaic.java | 2 -- src/org/yaaic/irc/IRCService.java | 1 - src/org/yaaic/view/ServersActivity.java | 2 -- 3 files changed, 5 deletions(-) diff --git a/src/org/yaaic/Yaaic.java b/src/org/yaaic/Yaaic.java index 4f59a99..f07bb94 100644 --- a/src/org/yaaic/Yaaic.java +++ b/src/org/yaaic/Yaaic.java @@ -24,11 +24,9 @@ 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 :) diff --git a/src/org/yaaic/irc/IRCService.java b/src/org/yaaic/irc/IRCService.java index e4ea423..ade4e74 100644 --- a/src/org/yaaic/irc/IRCService.java +++ b/src/org/yaaic/irc/IRCService.java @@ -24,7 +24,6 @@ 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; diff --git a/src/org/yaaic/view/ServersActivity.java b/src/org/yaaic/view/ServersActivity.java index d7ee4471..3d7ad46 100644 --- a/src/org/yaaic/view/ServersActivity.java +++ b/src/org/yaaic/view/ServersActivity.java @@ -43,8 +43,6 @@ import org.yaaic.R; import org.yaaic.Yaaic; import org.yaaic.adapter.ServerListAdapter; import org.yaaic.db.Database; -import org.yaaic.irc.IRCBinder; -import org.yaaic.irc.IRCService; import org.yaaic.layout.NonScalingBackgroundDrawable; import org.yaaic.listener.ServerListener; import org.yaaic.model.Broadcast; From 5fc8788a19abc675be9e7fa363361cefeaa7d980 Mon Sep 17 00:00:00 2001 From: Sebastian Kaspari Date: Mon, 29 Mar 2010 21:48:23 +0200 Subject: [PATCH 3/3] Persistent service if connected to a server --- src/org/yaaic/irc/IRCService.java | 56 +++++++++++++------- src/org/yaaic/view/ConversationActivity.java | 1 + src/org/yaaic/view/ServersActivity.java | 25 ++++----- 3 files changed, 50 insertions(+), 32 deletions(-) diff --git a/src/org/yaaic/irc/IRCService.java b/src/org/yaaic/irc/IRCService.java index ade4e74..f2a3140 100644 --- a/src/org/yaaic/irc/IRCService.java +++ b/src/org/yaaic/irc/IRCService.java @@ -27,6 +27,8 @@ import java.util.HashMap; 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; @@ -48,11 +50,15 @@ public class IRCService extends Service private IRCBinder binder; private HashMap connections; + private boolean foreground = false; @SuppressWarnings("unchecked") private static final Class[] mStartForegroundSignature = new Class[] { int.class, Notification.class }; @SuppressWarnings("unchecked") private static final Class[] mStopForegroundSignature = new Class[] { boolean.class }; + + public static final String ACTION_FOREGROUND = "org.yaaic.service.foreground"; + public static final String ACTION_BACKGROUND = "org.yaaic.service.background"; private NotificationManager mNM; private Method mStartForeground; @@ -90,14 +96,12 @@ public class IRCService extends Service } // Load servers from Database - /* 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)); } /** @@ -107,6 +111,8 @@ public class IRCService extends Service @Override public void onStart(Intent intent, int startId) { + Log.d(TAG, "Service onStart()"); + handleCommand(intent); } @@ -120,6 +126,8 @@ public class IRCService extends Service */ public int onStartCommand(Intent intent, int flags, int startId) { + Log.d(TAG, "Service onStartCommand()"); + handleCommand(intent); // We want this service to continue running until it is explicitly @@ -136,17 +144,25 @@ public class IRCService extends Service */ 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); + if (ACTION_FOREGROUND.equals(intent.getAction())) { + foreground = true; + + // Set the icon, scrolling text and timestamp + Notification notification = new Notification(R.drawable.icon, "Connected", 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), "Connected", contentIntent); + + startForegroundCompat(R.string.app_name, notification); + } else if (ACTION_BACKGROUND.equals(intent.getAction()) && !foreground) { + stopForegroundCompat(R.string.app_name); + } } + + /** * This is a wrapper around the new startForeground method, using the older @@ -176,7 +192,8 @@ public class IRCService extends Service * This is a wrapper around the new stopForeground method, using the older * APIs if it is not available. */ - private void stopForegroundCompat(int id) { + private void stopForegroundCompat(int id) + { // If we have the new stopForeground API, then use it. if (mStopForeground != null) { mStopForegroundArgs[0] = Boolean.TRUE; @@ -213,19 +230,17 @@ public class IRCService extends Service return connection; } + /** + * Check status of service + */ 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()); @@ -235,12 +250,13 @@ public class IRCService extends Service } if (shutDown) { + foreground = false; stopSelf(); } } /** - * + * On Destroy */ @Override public void onDestroy() diff --git a/src/org/yaaic/view/ConversationActivity.java b/src/org/yaaic/view/ConversationActivity.java index e44c0b1..8eefdfc 100644 --- a/src/org/yaaic/view/ConversationActivity.java +++ b/src/org/yaaic/view/ConversationActivity.java @@ -125,6 +125,7 @@ public class ConversationActivity extends Activity implements ServiceConnection, // Start service Intent intent = new Intent(this, IRCService.class); + intent.setAction(IRCService.ACTION_FOREGROUND); startService(intent); bindService(intent, this, 0); diff --git a/src/org/yaaic/view/ServersActivity.java b/src/org/yaaic/view/ServersActivity.java index 3d7ad46..1d7d3b4 100644 --- a/src/org/yaaic/view/ServersActivity.java +++ b/src/org/yaaic/view/ServersActivity.java @@ -43,6 +43,8 @@ import org.yaaic.R; import org.yaaic.Yaaic; import org.yaaic.adapter.ServerListAdapter; import org.yaaic.db.Database; +import org.yaaic.irc.IRCBinder; +import org.yaaic.irc.IRCService; import org.yaaic.layout.NonScalingBackgroundDrawable; import org.yaaic.listener.ServerListener; import org.yaaic.model.Broadcast; @@ -58,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; @@ -71,8 +73,6 @@ 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 +89,10 @@ 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); + intent.setAction(IRCService.ACTION_BACKGROUND); + startService(intent); + bindService(intent, this, 0); receiver = new ServerReceiver(this); registerReceiver(receiver, new IntentFilter(Broadcast.SERVER_UPDATE)); @@ -107,7 +108,7 @@ public class ServersActivity extends ListActivity implements ServiceConnection, { super.onPause(); - //unbindService(this); + unbindService(this); unregisterReceiver(receiver); } @@ -116,7 +117,7 @@ public class ServersActivity extends ListActivity implements ServiceConnection, */ public void onServiceConnected(ComponentName name, IBinder service) { - //binder = (IRCBinder) service; + binder = (IRCBinder) service; } /** @@ -124,7 +125,7 @@ public class ServersActivity extends ListActivity implements ServiceConnection, */ public void onServiceDisconnected(ComponentName name) { - //binder = null; + binder = null; } /** @@ -165,19 +166,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; }