Adding callback for widget size changed, and potentially other extra info

Change-Id: I57738c92b6a0ba68ae66b19a533559470c64e6f1
diff --git a/api/current.txt b/api/current.txt
index 6d8e9a0..c4d8968 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4301,10 +4301,13 @@
     method protected void prepareView(android.view.View);
     method public void setAppWidget(int, android.appwidget.AppWidgetProviderInfo);
     method public void updateAppWidget(android.widget.RemoteViews);
+    method public void updateAppWidgetExtras(android.os.Bundle);
+    method public void updateAppWidgetSize(android.os.Bundle, int, int, int, int);
   }
 
   public class AppWidgetManager {
     method public void bindAppWidgetId(int, android.content.ComponentName);
+    method public android.os.Bundle getAppWidgetExtras(int);
     method public int[] getAppWidgetIds(android.content.ComponentName);
     method public android.appwidget.AppWidgetProviderInfo getAppWidgetInfo(int);
     method public java.util.List<android.appwidget.AppWidgetProviderInfo> getInstalledProviders();
@@ -4316,14 +4319,21 @@
     method public void updateAppWidget(int[], android.widget.RemoteViews);
     method public void updateAppWidget(int, android.widget.RemoteViews);
     method public void updateAppWidget(android.content.ComponentName, android.widget.RemoteViews);
+    method public void updateAppWidgetExtras(int, android.os.Bundle);
     field public static final java.lang.String ACTION_APPWIDGET_CONFIGURE = "android.appwidget.action.APPWIDGET_CONFIGURE";
     field public static final java.lang.String ACTION_APPWIDGET_DELETED = "android.appwidget.action.APPWIDGET_DELETED";
     field public static final java.lang.String ACTION_APPWIDGET_DISABLED = "android.appwidget.action.APPWIDGET_DISABLED";
     field public static final java.lang.String ACTION_APPWIDGET_ENABLED = "android.appwidget.action.APPWIDGET_ENABLED";
+    field public static final java.lang.String ACTION_APPWIDGET_EXTRAS_CHANGED = "android.appwidget.action.APPWIDGET_UPDATE_EXTRAS";
     field public static final java.lang.String ACTION_APPWIDGET_PICK = "android.appwidget.action.APPWIDGET_PICK";
     field public static final java.lang.String ACTION_APPWIDGET_UPDATE = "android.appwidget.action.APPWIDGET_UPDATE";
+    field public static final java.lang.String EXTRA_APPWIDGET_EXTRAS = "appWidgetExtras";
     field public static final java.lang.String EXTRA_APPWIDGET_ID = "appWidgetId";
     field public static final java.lang.String EXTRA_APPWIDGET_IDS = "appWidgetIds";
+    field public static final java.lang.String EXTRA_APPWIDGET_MAX_HEIGHT = "appWidgetMaxHeight";
+    field public static final java.lang.String EXTRA_APPWIDGET_MAX_WIDTH = "appWidgetMaxWidth";
+    field public static final java.lang.String EXTRA_APPWIDGET_MIN_HEIGHT = "appWidgetMinHeight";
+    field public static final java.lang.String EXTRA_APPWIDGET_MIN_WIDTH = "appWidgetMinWidth";
     field public static final java.lang.String EXTRA_CUSTOM_EXTRAS = "customExtras";
     field public static final java.lang.String EXTRA_CUSTOM_INFO = "customInfo";
     field public static final int INVALID_APPWIDGET_ID = 0; // 0x0
@@ -4332,6 +4342,7 @@
 
   public class AppWidgetProvider extends android.content.BroadcastReceiver {
     ctor public AppWidgetProvider();
+    method public void onAppWidgetExtrasChanged(android.content.Context, android.appwidget.AppWidgetManager, int, android.os.Bundle);
     method public void onDeleted(android.content.Context, int[]);
     method public void onDisabled(android.content.Context);
     method public void onEnabled(android.content.Context);
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index 61a9dce..c1b8e7c 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -28,6 +28,7 @@
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.os.Build;
+import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemClock;
@@ -206,6 +207,45 @@
         super.dispatchRestoreInstanceState(jail);
     }
 
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        int oldWidth = getMeasuredWidth();
+        int oldHeight = getMeasuredHeight();
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        int newWidth = getMeasuredWidth();
+        int newHeight = getMeasuredHeight();
+
+        // TODO: this is just a hack for now -- we actually have the AppWidgetHost
+        // be responsible for updating the size of the widget.
+        if (oldWidth != newWidth || oldHeight != newHeight) {
+            final float density = mContext.getResources().getDisplayMetrics().density;
+            final int newWidthDips = (int) (newWidth / density);
+            final int newHeightDips = (int) (newHeight / density);
+            updateAppWidgetSize(null, newWidthDips, newHeightDips, newWidthDips, newHeightDips);
+        }
+    }
+
+    /**
+     * Provide guidance about the size of this widget to the AppWidgetManager. This information
+     * gets embedded into the AppWidgetExtras and causes a callback to the AppWidgetProvider.
+     *
+     *  @see AppWidgetProvider#onAppWidgetExtrasChanged(Context, AppWidgetManager, int, Bundle)
+     */
+    public void updateAppWidgetSize(Bundle extras, int minWidth, int minHeight, int maxWidth, int maxHeight) {
+        if (extras == null) {
+            extras = new Bundle();
+        }
+        extras.putInt(AppWidgetManager.EXTRA_APPWIDGET_MIN_WIDTH, minWidth);
+        extras.putInt(AppWidgetManager.EXTRA_APPWIDGET_MIN_HEIGHT, minHeight);
+        extras.putInt(AppWidgetManager.EXTRA_APPWIDGET_MAX_WIDTH, maxWidth);
+        extras.putInt(AppWidgetManager.EXTRA_APPWIDGET_MAX_HEIGHT, maxHeight);
+        updateAppWidgetExtras(extras);
+    }
+
+    public void updateAppWidgetExtras(Bundle extras) {
+        AppWidgetManager.getInstance(mContext).updateAppWidgetExtras(mAppWidgetId, extras);
+    }
+
     /** {@inheritDoc} */
     @Override
     public LayoutParams generateLayoutParams(AttributeSet attrs) {
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index a7f7792..83ab817 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -19,6 +19,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -109,6 +110,32 @@
     public static final String EXTRA_APPWIDGET_ID = "appWidgetId";
 
     /**
+     * An bundle extra that contains the lower bound on the current width, in dips, of a widget instance.
+     */
+    public static final String EXTRA_APPWIDGET_MIN_WIDTH = "appWidgetMinWidth";
+
+    /**
+     * An bundle extra that contains the lower bound on the current height, in dips, of a widget instance.
+     */
+    public static final String EXTRA_APPWIDGET_MIN_HEIGHT = "appWidgetMinHeight";
+
+    /**
+     * An bundle extra that contains the upper bound on the current width, in dips, of a widget instance.
+     */
+    public static final String EXTRA_APPWIDGET_MAX_WIDTH = "appWidgetMaxWidth";
+
+    /**
+     * An bundle extra that contains the upper bound on the current width, in dips, of a widget instance.
+     */
+    public static final String EXTRA_APPWIDGET_MAX_HEIGHT = "appWidgetMaxHeight";
+
+    /**
+     * An intent extra which points to a bundle of extra information for a particular widget id.
+     * In particular this bundle can contain EXTRA_APPWIDGET_WIDTH and EXTRA_APPWIDGET_HEIGHT.
+     */
+    public static final String EXTRA_APPWIDGET_EXTRAS = "appWidgetExtras";
+
+    /**
      * An intent extra that contains multiple appWidgetIds.
      * <p>
      * The value will be an int array that can be retrieved like this:
@@ -161,6 +188,14 @@
     public static final String ACTION_APPWIDGET_UPDATE = "android.appwidget.action.APPWIDGET_UPDATE";
 
     /**
+     * Sent when the custom extras for an AppWidget change.
+     *
+     * @see AppWidgetProvider#onAppWidgetExtrasChanged AppWidgetProvider#onAppWidgetExtrasChanged(
+     *      Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newExtras)
+     */
+    public static final String ACTION_APPWIDGET_EXTRAS_CHANGED = "android.appwidget.action.APPWIDGET_UPDATE_EXTRAS";
+
+    /**
      * Sent when an instance of an AppWidget is deleted from its host.
      *
      * @see AppWidgetProvider#onDeleted AppWidgetProvider.onDeleted(Context context, int[] appWidgetIds)
@@ -252,6 +287,46 @@
     }
 
     /**
+     * Update the extras for a given widget instance.
+     *
+     * The extras can be used to embed additional information about this widget to be accessed
+     * by the associated widget's AppWidgetProvider.
+     *
+     * @see #getAppWidgetExtras(int)
+     *
+     * @param appWidgetId    The AppWidget instances for which to set the RemoteViews.
+     * @param extras         The extras to associate with this widget
+     */
+    public void updateAppWidgetExtras(int appWidgetId, Bundle extras) {
+        try {
+            sService.updateAppWidgetExtras(appWidgetId, extras);
+        }
+        catch (RemoteException e) {
+            throw new RuntimeException("system server dead?", e);
+        }
+    }
+
+    /**
+     * Get the extras associated with a given widget instance.
+     *
+     * The extras can be used to embed additional information about this widget to be accessed
+     * by the associated widget's AppWidgetProvider.
+     *
+     * @see #updateAppWidgetExtras(int, Bundle)
+     *
+     * @param appWidgetId     The AppWidget instances for which to set the RemoteViews.
+     * @return                The extras associated with the given widget instance.
+     */
+    public Bundle getAppWidgetExtras(int appWidgetId) {
+        try {
+            return sService.getAppWidgetExtras(appWidgetId);
+        }
+        catch (RemoteException e) {
+            throw new RuntimeException("system server dead?", e);
+        }
+    }
+
+    /**
      * Set the RemoteViews to use for the specified appWidgetId.
      *
      * Note that the RemoteViews parameter will be cached by the AppWidgetService, and hence should
diff --git a/core/java/android/appwidget/AppWidgetProvider.java b/core/java/android/appwidget/AppWidgetProvider.java
index 00a5f0c..3cf40ae 100755
--- a/core/java/android/appwidget/AppWidgetProvider.java
+++ b/core/java/android/appwidget/AppWidgetProvider.java
@@ -74,6 +74,16 @@
                 this.onDeleted(context, new int[] { appWidgetId });
             }
         }
+        else if (AppWidgetManager.ACTION_APPWIDGET_EXTRAS_CHANGED.equals(action)) {
+            Bundle extras = intent.getExtras();
+            if (extras != null && extras.containsKey(AppWidgetManager.EXTRA_APPWIDGET_ID)
+                    && extras.containsKey(AppWidgetManager.EXTRA_APPWIDGET_EXTRAS)) {
+                int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID);
+                Bundle widgetExtras = extras.getBundle(AppWidgetManager.EXTRA_APPWIDGET_EXTRAS);
+                this.onAppWidgetExtrasChanged(context, AppWidgetManager.getInstance(context),
+                        appWidgetId, widgetExtras);
+            }
+        }
         else if (AppWidgetManager.ACTION_APPWIDGET_ENABLED.equals(action)) {
             this.onEnabled(context);
         }
@@ -82,7 +92,7 @@
         }
     }
     // END_INCLUDE(onReceive)
-    
+
     /**
      * Called in response to the {@link AppWidgetManager#ACTION_APPWIDGET_UPDATE} broadcast when
      * this AppWidget provider is being asked to provide {@link android.widget.RemoteViews RemoteViews}
@@ -102,7 +112,26 @@
      */
     public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
     }
-    
+
+    /**
+     * Called in response to the {@link AppWidgetManager#ACTION_APPWIDGET_EXTRAS_CHANGED}
+     * broadcast when this widget has been layed out at a new size.
+     *
+     * {@more}
+     *
+     * @param context   The {@link android.content.Context Context} in which this receiver is
+     *                  running.
+     * @param appWidgetManager A {@link AppWidgetManager} object you can call {@link
+     *                  AppWidgetManager#updateAppWidget} on.
+     * @param appWidgetId The appWidgetId of the widget who's size changed.
+     * @param newExtras The appWidgetId of the widget who's size changed.
+     *
+     * @see AppWidgetManager#ACTION_APPWIDGET_EXTRAS_CHANGED
+     */
+    public void onAppWidgetExtrasChanged(Context context, AppWidgetManager appWidgetManager,
+            int appWidgetId, Bundle newExtras) {
+    }
+
     /**
      * Called in response to the {@link AppWidgetManager#ACTION_APPWIDGET_DELETED} broadcast when
      * one or more AppWidget instances have been deleted.  Override this method to implement
diff --git a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
index fa0873d..b1b57e7 100644
--- a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
+++ b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
@@ -20,6 +20,7 @@
 import android.content.Intent;
 import android.appwidget.AppWidgetProviderInfo;
 import com.android.internal.appwidget.IAppWidgetHost;
+import android.os.Bundle;
 import android.os.IBinder;
 import android.widget.RemoteViews;
 
@@ -42,6 +43,8 @@
     // for AppWidgetManager
     //
     void updateAppWidgetIds(in int[] appWidgetIds, in RemoteViews views);
+    void updateAppWidgetExtras(int appWidgetId, in Bundle extras);
+    Bundle getAppWidgetExtras(int appWidgetId);
     void partiallyUpdateAppWidgetIds(in int[] appWidgetIds, in RemoteViews views);
     void updateAppWidgetProvider(in ComponentName provider, in RemoteViews views);
     void notifyAppWidgetViewDataChanged(in int[] appWidgetIds, int viewId);
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index eb024e9..bf958a5 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -28,6 +28,7 @@
 import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
 import android.os.Binder;
+import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Pair;
@@ -260,6 +261,16 @@
         return getImplForUser().getAppWidgetViews(appWidgetId);
     }
 
+    @Override
+    public void updateAppWidgetExtras(int appWidgetId, Bundle extras) {
+        getImplForUser().updateAppWidgetExtras(appWidgetId, extras);
+    }
+
+    @Override
+    public Bundle getAppWidgetExtras(int appWidgetId) {
+        return getImplForUser().getAppWidgetExtras(appWidgetId);
+    }
+
     static int[] getAppWidgetIds(Provider p) {
         int instancesSize = p.instances.size();
         int appWidgetIds[] = new int[instancesSize];
diff --git a/services/java/com/android/server/AppWidgetServiceImpl.java b/services/java/com/android/server/AppWidgetServiceImpl.java
index b24823e..3b43b9b 100644
--- a/services/java/com/android/server/AppWidgetServiceImpl.java
+++ b/services/java/com/android/server/AppWidgetServiceImpl.java
@@ -113,6 +113,7 @@
         int appWidgetId;
         Provider provider;
         RemoteViews views;
+        Bundle extras;
         Host host;
     }
 
@@ -760,6 +761,38 @@
         }
     }
 
+    public void updateAppWidgetExtras(int appWidgetId, Bundle extras) {
+        synchronized (mAppWidgetIds) {
+            ensureStateLoadedLocked();
+            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
+
+            if (id == null) {
+                return;
+            }
+            Provider p = id.provider;
+            id.extras = extras;
+
+            // send the broacast saying that this appWidgetId has been deleted
+            Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_EXTRAS_CHANGED);
+            intent.setComponent(p.info.provider);
+            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
+            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_EXTRAS, extras);
+            mContext.sendBroadcast(intent, mUserId);
+        }
+    }
+
+    public Bundle getAppWidgetExtras(int appWidgetId) {
+        synchronized (mAppWidgetIds) {
+            ensureStateLoadedLocked();
+            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
+            if (id != null && id.extras != null) {
+                return id.extras;
+            } else {
+                return Bundle.EMPTY;
+            }
+        }
+    }
+
     public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
         if (appWidgetIds == null) {
             return;