android-ibc-forum

Unnamed repository; edit this file 'description' to name the repository.
git clone http://git.code.weiherhei.de/android-ibc-forum.git
Log | Files | Refs

SubscriptionService.java (10860B)


      1 /**
      2  * 
      3  */
      4 package de.mtbnews.android.service;
      5 
      6 import java.util.ArrayList;
      7 import java.util.List;
      8 import java.util.Timer;
      9 import java.util.TimerTask;
     10 
     11 import android.app.Notification;
     12 import android.app.NotificationManager;
     13 import android.app.PendingIntent;
     14 import android.app.Service;
     15 import android.content.Context;
     16 import android.content.Intent;
     17 import android.content.SharedPreferences;
     18 import android.net.Uri;
     19 import android.os.IBinder;
     20 import android.preference.PreferenceManager;
     21 import android.text.TextUtils;
     22 import android.util.Log;
     23 import de.mtbnews.android.MailboxActivity;
     24 import de.mtbnews.android.R;
     25 import de.mtbnews.android.SubscriptionForenActivity;
     26 import de.mtbnews.android.SubscriptionTopicsActivity;
     27 import de.mtbnews.android.tapatalk.TapatalkClient;
     28 import de.mtbnews.android.tapatalk.TapatalkException;
     29 import de.mtbnews.android.tapatalk.wrapper.Forum;
     30 import de.mtbnews.android.tapatalk.wrapper.ListHolder;
     31 import de.mtbnews.android.tapatalk.wrapper.Mailbox;
     32 import de.mtbnews.android.tapatalk.wrapper.Topic;
     33 import de.mtbnews.android.util.IBC;
     34 
     35 /**
     36  * Hintergrund-Service, der ungelesene Nachrichten, Themen und Beiträge
     37  * ermittelt und im Erfolgsfall eine Notification erzeugt.
     38  * 
     39  * @author dankert
     40  * 
     41  */
     42 public class SubscriptionService extends Service
     43 {
     44 	/**
     45 	 * Timer, der das zeitgesteuerte Abholen von neuen Nachrichten steuert.
     46 	 */
     47 	// Damit der Timer nicht nur erzeugt, sondern auch gestoppt werden kann,
     48 	// behalten wir hier eine Referenz.
     49 	private Timer timer;
     50 
     51 	/**
     52 	 * App-Einstellungen.
     53 	 */
     54 	// Notwendig für die Benutzeranmeldung.
     55 	private SharedPreferences prefs;
     56 
     57 	// Notification-Kategorien:
     58 	private static final int NOTIFICATION_EVENT_RUNNING = 1;
     59 	private static final int NOTIFICATION_TOPIC = 2;
     60 	private static final int NOTIFICATION_FORUM = 3;
     61 	private static final int NOTIFICATION_MESSAGES = 4;
     62 
     63 	/**
     64 	 * {@inheritDoc}
     65 	 * 
     66 	 * @see android.app.Service#onBind(android.content.Intent)
     67 	 */
     68 	public IBinder onBind(Intent arg0)
     69 	{
     70 		// Dieser Service läuft stets alleine und wird nicht gebunden.
     71 		return null;
     72 	}
     73 
     74 	/**
     75 	 * Service-Start. Erzeugt für diese Serviceinstanz einen Timer, der in
     76 	 * regelmäßigen Abständen auf neue Themen und Nachrichten prüft.
     77 	 * 
     78 	 * @see android.app.Service#onCreate()
     79 	 */
     80 	public void onCreate()
     81 	{
     82 		Log.i(IBC.TAG, "Starting service");
     83 		super.onCreate();
     84 		prefs = PreferenceManager.getDefaultSharedPreferences(this);
     85 
     86 		// Intervall in Minuten (Default = 3 Stunden)
     87 		int intervalInMinutes = Integer.parseInt(prefs.getString("subscription_service_interval", "180"));
     88 
     89 		// Prüfen, ob Service laufen soll und ein Benutzername vorhanden ist
     90 		if (prefs.getBoolean("autostart_subscription_service", false)
     91 				&& !TextUtils.isEmpty(prefs.getString("username", "")))
     92 		{
     93 			Log.d(IBC.TAG, "Creating the timer");
     94 			timer = new Timer();
     95 			timer.scheduleAtFixedRate(new SubscriptionTask(), 2000, intervalInMinutes * 60 * 1000);
     96 		}
     97 		else
     98 		{
     99 			// Service soll nicht laufen, also sofort wieder stoppen
    100 			Log.i(IBC.TAG, "Stopping service (should not start)");
    101 			stopSelf();
    102 		}
    103 
    104 	}
    105 
    106 	/**
    107 	 * Der Timer, der auf ungelesene Nachrichten und ungelesene Themen prüft.
    108 	 * Falls gefunden, wird dies über den NotificationService gemeldet. <em>Dies
    109 	 * darf eine interne Klasse sein, denn solange der Timer besteht, muss auch
    110 	 * der Service dazu laufen.</em>
    111 	 * 
    112 	 * @author dankert
    113 	 * 
    114 	 */
    115 	private class SubscriptionTask extends TimerTask
    116 	{
    117 		public void run()
    118 		{
    119 			Log.i(IBC.TAG, "Timer event fired");
    120 
    121 			// Für diesen Timer-Event erzeugen wir einen eigene Instanz des
    122 			// Tapatalk-Client. Die Laufzeit ist hier unkritisch, dafür belastet
    123 			// der Client nicht den HEAP, da nach dem Timeevent der
    124 			// Tapatalk-Client durch den GC weggeräum werden kann. Dieser
    125 			// Hintergrundprozess wird dadurch deutlich weniger
    126 			// speicherintensiv.
    127 			final TapatalkClient client = new TapatalkClient(IBC.IBC_FORUM_CONNECTOR_URL);
    128 
    129 			// Anzeigen einer Notification, damit der Benutzer weiß, dass neue
    130 			// Nachrichten ab
    131 			final NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    132 			final PendingIntent emptyIntent = PendingIntent.getActivity(getApplicationContext(), 0, new Intent(), 0);
    133 
    134 			final String tickerText1 = getResources().getString(R.string.checking_new);
    135 
    136 			if (prefs.getBoolean("show_hints", false))
    137 			{
    138 				final Notification notificationRunning = new Notification(R.drawable.ibc_logo, tickerText1, System
    139 						.currentTimeMillis());
    140 
    141 				notificationRunning.setLatestEventInfo(getApplicationContext(), getResources().getString(
    142 						R.string.checking_new), "", emptyIntent);
    143 
    144 				notificationRunning.defaults = 0;
    145 				notificationRunning.flags = Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR;
    146 				nm.notify(NOTIFICATION_EVENT_RUNNING, notificationRunning);
    147 			}
    148 
    149 			try
    150 			{
    151 				// Zuerst Login
    152 				client.login(prefs.getString("username", ""), prefs.getString("password", ""));
    153 
    154 				List<Forum> subscribedForum = client.getSubscribedForum(true);
    155 
    156 				final List<String> forumNameList = new ArrayList<String>();
    157 				for (Forum forum : subscribedForum)
    158 				{
    159 					forumNameList.add(forum.getTitle());
    160 				}
    161 				if (!forumNameList.isEmpty())
    162 				{
    163 					final Intent notificationIntent = new Intent(SubscriptionService.this,
    164 							SubscriptionForenActivity.class);
    165 					final PendingIntent contentIntent = PendingIntent.getActivity(SubscriptionService.this, 0,
    166 							notificationIntent, 0);
    167 
    168 					final String tickerText = getResources().getString(R.string.unread_forum) + "\n"
    169 							+ TextUtils.join("\n", forumNameList);
    170 
    171 					final Notification notification = createNotification(tickerText, R.string.unread_forum, "("
    172 							+ subscribedForum.size() + ")", TextUtils.join(", ", forumNameList), contentIntent);
    173 					nm.notify(NOTIFICATION_FORUM, notification);
    174 				}
    175 
    176 				ListHolder<Topic> subscribedTopic = client.getSubscribedTopics(0, 10, true);
    177 				final List<String> topicNameList = new ArrayList<String>();
    178 				for (Topic topic : subscribedTopic.getChildren())
    179 				{
    180 					topicNameList.add(topic.getTitle());
    181 				}
    182 				if (!topicNameList.isEmpty())
    183 				{
    184 
    185 					final Intent notificationIntent = new Intent(SubscriptionService.this,
    186 							SubscriptionTopicsActivity.class);
    187 					final PendingIntent contentIntent = PendingIntent.getActivity(SubscriptionService.this, 0,
    188 							notificationIntent, 0);
    189 
    190 					final String tickerText = getResources().getString(R.string.unread_topic) + "\n"
    191 							+ TextUtils.join("\n", topicNameList);
    192 
    193 					final Notification notification = createNotification(tickerText, R.string.unread_topic, "("
    194 							+ subscribedTopic.getChildren().size() + ")", TextUtils.join(", ", topicNameList),
    195 							contentIntent);
    196 
    197 					nm.notify(NOTIFICATION_TOPIC, notification);
    198 				}
    199 
    200 				List<Mailbox> mailboxList = client.getMailbox();
    201 				int unreadCount = 0;
    202 
    203 				final List<String> unreadBoxNames = new ArrayList<String>();
    204 
    205 				for (Mailbox mailbox : mailboxList)
    206 				{
    207 					if (mailbox.countUnread > 0)
    208 					{
    209 
    210 						unreadBoxNames.add(mailbox.getTitle());
    211 						unreadCount += mailbox.countUnread;
    212 					}
    213 				}
    214 
    215 				if (unreadCount > 0)
    216 				{
    217 					final Intent notificationIntent = new Intent(SubscriptionService.this, MailboxActivity.class);
    218 					final PendingIntent contentIntent = PendingIntent.getActivity(SubscriptionService.this, 0,
    219 							notificationIntent, 0);
    220 
    221 					final String tickerText = getResources().getString(R.string.unread_messages) + "\n"
    222 							+ TextUtils.join("\n", unreadBoxNames);
    223 
    224 					final Notification notification = createNotification(tickerText, R.string.unread_messages, "("
    225 							+ unreadCount + ")", TextUtils.join(", ", unreadBoxNames), contentIntent);
    226 					nm.notify(NOTIFICATION_MESSAGES, notification);
    227 				}
    228 			}
    229 			catch (TapatalkException e)
    230 			{
    231 				// Kann vorkommen, wenn Login fehlschlägt oder Verbindung
    232 				// abbricht
    233 				Log.w(IBC.TAG, e);
    234 
    235 				// Ganz bewusst wird hier keine Fehlermeldung erzeugt:
    236 				// Oft kann es passieren, dass der Empfang schlecht wird und die
    237 				// Verbindung einen Timeout bekommt. In diesem Fall möchten wir
    238 				// den Benutzer aber nicht mit Fehlermeldungen nerven. Beim
    239 				// nächsten Timerevent wird der Abruf sowieso wieder probiert.
    240 			}
    241 			catch (Exception e)
    242 			{
    243 				// Das sollte eigentlich nicht vorkommen. Hier scheint etwas
    244 				// schlimmeres kaputt zu sein, daher werfen wir hier eine
    245 				// RuntimeException weiter. Android wird dann den Service
    246 				// beenden und das Senden eines Berichtes ermöglichen. Das ist
    247 				// das beste, was wir hier noch tun können ;) Würden wir den
    248 				// Fehler nicht weiterwerfen, würde der Service eh nichts mehr
    249 				// machen und niemandem wäre geholfen.
    250 				Log.w(IBC.TAG, e);
    251 				throw new RuntimeException("Unrecoverable error in service", e);
    252 			}
    253 			finally
    254 			{
    255 				// In jedem Fall die Notification entfernen.
    256 				nm.cancel(NOTIFICATION_EVENT_RUNNING);
    257 			}
    258 		}
    259 
    260 		@Override
    261 		public boolean cancel()
    262 		{
    263 			Log.i(IBC.TAG, "Timer canceled");
    264 			return super.cancel();
    265 		}
    266 	}
    267 
    268 	/**
    269 	 * Wird vom System automatisch aufgerufen bevor der Service entfernt wird.
    270 	 * Hier räumen wir vor allem den Timer weg, damit keine weiteren Ereignisse
    271 	 * ausgelöst werden.
    272 	 * 
    273 	 * @see android.app.Service#onDestroy()
    274 	 */
    275 	public void onDestroy()
    276 	{
    277 		Log.d(IBC.TAG, "Destroying service");
    278 
    279 		if (timer != null)
    280 			timer.cancel(); // Alle Timer-Ereignisse stoppen.
    281 
    282 		super.onDestroy();
    283 	}
    284 
    285 	/**
    286 	 * Notification erzeugen.
    287 	 * 
    288 	 * @param tickerText
    289 	 *            Mehrzeiliger Ticker-Text, der in der Notification-Bar
    290 	 *            angezeigt wird.
    291 	 * @param titleResId
    292 	 *            Titel-Resource-Id der Notification
    293 	 * @param titleExtra
    294 	 *            Zusatz-Titeltext, kann <code>null</code> sein
    295 	 * @param content
    296 	 *            Inhaltstext der Noification
    297 	 * @param intent
    298 	 *            Auszulösender Intent
    299 	 * @return
    300 	 */
    301 	private Notification createNotification(String tickerText, int titleResId, String titleExtra, String content,
    302 			PendingIntent intent)
    303 	{
    304 
    305 		final Notification notification = new Notification(R.drawable.ibc_logo, tickerText, System.currentTimeMillis());
    306 		notification.setLatestEventInfo(getApplicationContext(), getResources().getString(titleResId)
    307 				+ (titleExtra != null ? " " + titleExtra : ""), content, intent);
    308 
    309 		notification.defaults = Notification.DEFAULT_LIGHTS;
    310 
    311 		final String ringtone = prefs.getString("ringtone", "");
    312 
    313 		if (!TextUtils.isEmpty(ringtone))
    314 			notification.sound = Uri.parse(ringtone);
    315 		else
    316 			notification.defaults |= Notification.DEFAULT_SOUND;
    317 
    318 		// Falls so konfiguriert, den Vibrationsalarm auslösen
    319 		if (prefs.getBoolean("use_vibration", false))
    320 			notification.defaults |= Notification.DEFAULT_VIBRATE;
    321 
    322 		notification.flags = Notification.FLAG_AUTO_CANCEL | Notification.FLAG_ONLY_ALERT_ONCE;
    323 
    324 		return notification;
    325 	}
    326 }