Fix widget updates

Fix widgets that stop updating.  If the Launcher got killed, the Email
widget service gets unbound.  When this happens the cursor loader for the
widget is stopped.  Since widget is relying on the loader to update the data,
when the loader is stopped, the widget would never update.

Now when email recognizes a change, it will send a broadcast intent, which
will cause the widget service to be started, if it isn't.

Bug: 5811810
Change-Id: Ia840e58f10e780b94440119662c2e48e7785c507
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index a6407a6..8a449c3 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -486,6 +486,9 @@
                 <action
                     android:name="android.appwidget.action.APPWIDGET_UPDATE" />
             </intent-filter>
+            <intent-filter>
+                <action android:name="com.android.email.MESSAGE_LIST_DATASET_CHANGED" />
+            </intent-filter>
             <meta-data
                 android:name="android.appwidget.provider"
                 android:resource="@xml/widget_info" />
diff --git a/src/com/android/email/provider/EmailProvider.java b/src/com/android/email/provider/EmailProvider.java
index 798bfb0..bbedfdb 100644
--- a/src/com/android/email/provider/EmailProvider.java
+++ b/src/com/android/email/provider/EmailProvider.java
@@ -23,6 +23,7 @@
 import android.content.ContentResolver;
 import android.content.ContentUris;
 import android.content.ContentValues;
+import android.content.Intent;
 import android.content.Context;
 import android.content.OperationApplicationException;
 import android.content.UriMatcher;
@@ -86,6 +87,14 @@
     public static final String ATTACHMENT_UPDATED_EXTRA_FLAGS =
         "com.android.email.ATTACHMENT_UPDATED_FLAGS";
 
+    /**
+     * Notifies that changes happened. Certain UI components, e.g., widgets, can register for this
+     * {@link android.content.Intent} and update accordingly. However, this can be very broad and
+     * is NOT the preferred way of getting notification.
+     */
+    public static final String ACTION_NOTIFY_MESSAGE_LIST_DATASET_CHANGED =
+            "com.android.email.MESSAGE_LIST_DATASET_CHANGED";
+
     public static final String EMAIL_MESSAGE_MIME_TYPE =
         "vnd.android.cursor.item/email-message";
     public static final String EMAIL_ATTACHMENT_MIME_TYPE =
@@ -2345,6 +2354,20 @@
         } else {
             resolver.notifyChange(baseUri, null);
         }
+
+        // We want to send the message list changed notification if baseUri is Message.NOTIFIER_URI.
+        if (baseUri.equals(Message.NOTIFIER_URI)) {
+            sendMessageListDataChangedNotification();
+        }
+    }
+
+    private void sendMessageListDataChangedNotification() {
+        final Context context = getContext();
+        final Intent intent = new Intent(ACTION_NOTIFY_MESSAGE_LIST_DATASET_CHANGED);
+        // Ideally this intent would contain information about which account changed, to limit the
+        // updates to that particular account.  Unfortunately, that information is not available in
+        // sendNotifierChange().
+        context.sendBroadcast(intent);
     }
 
     @Override
diff --git a/src/com/android/email/provider/WidgetProvider.java b/src/com/android/email/provider/WidgetProvider.java
index 9f7fc85..0f6731b 100644
--- a/src/com/android/email/provider/WidgetProvider.java
+++ b/src/com/android/email/provider/WidgetProvider.java
@@ -19,12 +19,14 @@
 import android.app.Service;
 import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProvider;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.util.Log;
 import android.widget.RemoteViewsService;
 
 import com.android.email.Email;
+import com.android.email.R;
 import com.android.email.widget.EmailWidget;
 import com.android.email.widget.WidgetManager;
 import com.android.emailcommon.Logging;
@@ -74,6 +76,20 @@
             Log.d(EmailWidget.TAG, "onReceive");
         }
         super.onReceive(context, intent);
+
+        if (EmailProvider.ACTION_NOTIFY_MESSAGE_LIST_DATASET_CHANGED.equals(intent.getAction())) {
+            // Retrieve the list of current widgets.
+            final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
+            final ComponentName component = new ComponentName(context, WidgetProvider.class);
+            final int[] widgetIds = appWidgetManager.getAppWidgetIds(component);
+
+            // Ideally, this would only call notify AppWidgetViewDataChanged for the widgets, where
+            // the account had the change, but the current intent doesn't include this information.
+
+            // Calling notifyAppWidgetViewDataChanged will cause onDataSetChanged() to be called
+            // on the RemoteViewsService.RemoteViewsFactory, starting the service if necessary.
+            appWidgetManager.notifyAppWidgetViewDataChanged(widgetIds, R.id.message_list);
+        }
     }
 
     /**
diff --git a/src/com/android/email/widget/EmailWidget.java b/src/com/android/email/widget/EmailWidget.java
index 3f6a5c0..67a82f5 100644
--- a/src/com/android/email/widget/EmailWidget.java
+++ b/src/com/android/email/widget/EmailWidget.java
@@ -505,6 +505,10 @@
 
     @Override
     public void onDataSetChanged() {
+        // Note: we are not doing anything special in onDataSetChanged().  Since this service has
+        // a reference to a loader that will keep itself updated, if the service is running, it
+        // shouldn't be necessary to for the query to be run again.  If the service hadn't been
+        // running, the act of starting the service will also start the loader.
     }
 
     public void onDeleted() {