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); + } +}