diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index c74ddf472d7..26e88062a96 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -245,6 +245,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/textsecure_appwidget.xml b/res/layout/textsecure_appwidget.xml
new file mode 100644
index 00000000000..b12f568ca1e
--- /dev/null
+++ b/res/layout/textsecure_appwidget.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/xml/textsecure_appwidget_info.xml b/res/xml/textsecure_appwidget_info.xml
new file mode 100644
index 00000000000..3480f9cece8
--- /dev/null
+++ b/res/xml/textsecure_appwidget_info.xml
@@ -0,0 +1,11 @@
+
+
+
+
\ No newline at end of file
diff --git a/src/org/thoughtcrime/securesms/notifications/MessageNotifier.java b/src/org/thoughtcrime/securesms/notifications/MessageNotifier.java
index db3c0c08627..bdcaf1d5373 100644
--- a/src/org/thoughtcrime/securesms/notifications/MessageNotifier.java
+++ b/src/org/thoughtcrime/securesms/notifications/MessageNotifier.java
@@ -19,6 +19,8 @@
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
+import android.appwidget.AppWidgetManager;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
@@ -41,6 +43,7 @@
import org.thoughtcrime.securesms.RoutingActivity;
import org.thoughtcrime.securesms.contacts.ContactPhotoFactory;
import org.thoughtcrime.securesms.database.PushDatabase;
+import org.thoughtcrime.securesms.providers.TextSecureAppWidgetProvider;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
import org.whispersystems.textsecure.crypto.MasterSecret;
@@ -98,7 +101,6 @@ public static void notifyMessageDeliveryFailed(Context context, Recipients recip
}
}
-
public static void updateNotification(Context context, MasterSecret masterSecret) {
if (!TextSecurePreferences.isNotificationsEnabled(context)) {
return;
@@ -120,6 +122,7 @@ public static void updateNotification(Context context, MasterSecret masterSecret
}
}
+
private static void updateNotification(Context context, MasterSecret masterSecret, boolean signal) {
Cursor telcoCursor = null;
Cursor pushCursor = null;
@@ -133,6 +136,7 @@ private static void updateNotification(Context context, MasterSecret masterSecre
{
((NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE))
.cancel(NOTIFICATION_ID);
+ TextSecureAppWidgetProvider.triggerUpdate(context, 0);
return;
}
@@ -140,6 +144,8 @@ private static void updateNotification(Context context, MasterSecret masterSecre
appendPushNotificationState(context, masterSecret, notificationState, pushCursor);
+ TextSecureAppWidgetProvider.triggerUpdate(context, notificationState.getMessageCount());
+
if (notificationState.hasMultipleThreads()) {
sendMultipleThreadNotification(context, masterSecret, notificationState, signal);
} else {
diff --git a/src/org/thoughtcrime/securesms/providers/TextSecureAppWidgetProvider.java b/src/org/thoughtcrime/securesms/providers/TextSecureAppWidgetProvider.java
new file mode 100644
index 00000000000..f5fbdd68948
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/providers/TextSecureAppWidgetProvider.java
@@ -0,0 +1,136 @@
+/**
+ * Copyright (C) 2014 Open Whisper Systems
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package org.thoughtcrime.securesms.providers;
+
+import android.app.PendingIntent;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProvider;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.RemoteViews;
+
+import org.thoughtcrime.securesms.R;
+import org.thoughtcrime.securesms.RoutingActivity;
+import org.thoughtcrime.securesms.database.DatabaseFactory;
+
+import java.util.List;
+
+/**
+ * The provider for the TextSecure AppWidget
+ *
+ * @author Lukas Barth
+ */
+public class TextSecureAppWidgetProvider extends AppWidgetProvider {
+
+ final static String UNREAD_COUNT = "unreadCount";
+
+ public static void triggerUpdate(Context context, int unread) {
+ AppWidgetManager widgetManager = AppWidgetManager.getInstance(context);
+ ComponentName widgetComponent = new ComponentName(context, TextSecureAppWidgetProvider.class);
+ int[] widgetIds = widgetManager.getAppWidgetIds(widgetComponent);
+ // widget notification
+ Intent updateIntent = new Intent();
+ updateIntent.putExtra(UNREAD_COUNT, unread);
+ updateIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, widgetIds);
+ updateIntent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
+ updateIntent.setClass(context, TextSecureAppWidgetProvider.class);
+ context.sendBroadcast(updateIntent);
+ // Samsung TouchWiz notification
+ String launcherClassName = getLauncherClassName(context);
+ if (launcherClassName != null) {
+ Intent intent = new Intent("android.intent.action.BADGE_COUNT_UPDATE");
+ intent.putExtra("badge_count", unread);
+ intent.putExtra("badge_count_package_name", context.getPackageName());
+ intent.putExtra("badge_count_class_name", launcherClassName);
+ context.sendBroadcast(intent);
+ }
+ // Sony BadgeReceiver notification
+ Intent intent = new Intent();
+ intent.setAction("com.sonyericsson.home.action.UPDATE_BADGE");
+ intent.putExtra("com.sonyericsson.home.intent.extra.badge.ACTIVITY_NAME", RoutingActivity.class.getCanonicalName());
+ intent.putExtra("com.sonyericsson.home.intent.extra.badge.PACKAGE_NAME", "org.thoughtcrime.securesms");
+ intent.putExtra("com.sonyericsson.home.intent.extra.badge.MESSAGE", Integer.toString(unread));
+ if(unread>0)
+ intent.putExtra("com.sonyericsson.home.intent.extra.badge.SHOW_MESSAGE", true);
+ else
+ intent.putExtra("com.sonyericsson.home.intent.extra.badge.SHOW_MESSAGE", false);
+ context.sendBroadcast(intent);
+ }
+
+ private static String getLauncherClassName(Context context) {
+ PackageManager pm = context.getPackageManager();
+
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.addCategory(Intent.CATEGORY_LAUNCHER);
+
+ List resolveInfos = pm.queryIntentActivities(intent, 0);
+ for (ResolveInfo resolveInfo : resolveInfos) {
+ String pkgName = resolveInfo.activityInfo.applicationInfo.packageName;
+ if (pkgName.equalsIgnoreCase(context.getPackageName())) {
+ String className = resolveInfo.activityInfo.name;
+ return className;
+ }
+ }
+ return null;
+ }
+
+ public void onReceive(Context context, Intent intent) {
+ // Protect against rogue update broadcasts (not really a security issue,
+ // just filter bad broacasts out so subclasses are less likely to crash).
+ String action = intent.getAction();
+ if (AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
+ Bundle extras = intent.getExtras();
+ if (extras != null) {
+ int[] appWidgetIds = extras.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_IDS);
+ int unread = extras.getInt(UNREAD_COUNT);
+ if (appWidgetIds != null && appWidgetIds.length > 0) {
+ final int n = appWidgetIds.length;
+
+ for (int i = 0; i < n; i++) {
+ int appWidgetId = appWidgetIds[i];
+
+ Intent clickIntent = new Intent(context, RoutingActivity.class);
+ PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, clickIntent, 0);
+
+ RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.textsecure_appwidget);
+ views.setOnClickPendingIntent(R.id.icon_view, pendingIntent);
+
+ if (unread > 0) {
+ if (unread > 9)
+ views.setTextViewText(R.id.unread_count_text, "9+");
+ else
+ views.setTextViewText(R.id.unread_count_text, Integer.toString(unread));
+ views.setViewVisibility(R.id.unread_count_text, View.VISIBLE);
+ } else {
+ views.setViewVisibility(R.id.unread_count_text, View.INVISIBLE);
+ }
+
+ AppWidgetManager.getInstance(context).updateAppWidget(appWidgetId, views);
+ }
+ }
+ }
+ } else
+ super.onReceive(context, intent);
+ }
+}