am 18a53ab1: (-s ours) am 13157532: (-s ours) am 2ea40009: (-s ours) am 23b816b1: (-s ours) Import translations. DO NOT MERGE

* commit '18a53ab1aa846053e6de896e093bca5d9e42b6aa':
  Import translations. DO NOT MERGE
diff --git a/WallpaperPicker/src/com/android/launcher3/Partner.java b/WallpaperPicker/src/com/android/launcher3/Partner.java
index 79c763d..d3c825d 100644
--- a/WallpaperPicker/src/com/android/launcher3/Partner.java
+++ b/WallpaperPicker/src/com/android/launcher3/Partner.java
@@ -38,6 +38,7 @@
 
     public static final String RESOURCE_FOLDER = "partner_folder";
     public static final String RESOURCE_WALLPAPERS = "partner_wallpapers";
+    public static final String RESOURCE_DEFAULT_LAYOUT = "partner_default_layout";
 
     private static boolean sSearched = false;
     private static Partner sPartner;
@@ -81,4 +82,16 @@
     public Resources getResources() {
         return mResources;
     }
+
+    public boolean hasDefaultLayout() {
+        int defaultLayout = getResources().getIdentifier(Partner.RESOURCE_DEFAULT_LAYOUT,
+                "xml", getPackageName());
+        return defaultLayout != 0;
+    }
+
+    public boolean hasFolder() {
+        int folder = getResources().getIdentifier(Partner.RESOURCE_FOLDER,
+                "xml", getPackageName());
+        return folder != 0;
+    }
 }
diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
index 232c6d1..b5e34cf 100644
--- a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
+++ b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
@@ -674,13 +674,16 @@
                 new String[] { MediaStore.Images.ImageColumns._ID,
                     MediaStore.Images.ImageColumns.DATE_TAKEN},
                 null, null, MediaStore.Images.ImageColumns.DATE_TAKEN + " DESC LIMIT 1");
+
         Bitmap thumb = null;
-        if (cursor.moveToNext()) {
-            int id = cursor.getInt(0);
-            thumb = MediaStore.Images.Thumbnails.getThumbnail(getContentResolver(),
-                    id, MediaStore.Images.Thumbnails.MINI_KIND, null);
+        if (cursor != null) {
+            if (cursor.moveToNext()) {
+                int id = cursor.getInt(0);
+                thumb = MediaStore.Images.Thumbnails.getThumbnail(getContentResolver(),
+                        id, MediaStore.Images.Thumbnails.MINI_KIND, null);
+            }
+            cursor.close();
         }
-        cursor.close();
         return thumb;
     }
 
diff --git a/WallpaperPicker/src/com/android/photos/BitmapRegionTileSource.java b/WallpaperPicker/src/com/android/photos/BitmapRegionTileSource.java
index 764156d..66ece4f 100644
--- a/WallpaperPicker/src/com/android/photos/BitmapRegionTileSource.java
+++ b/WallpaperPicker/src/com/android/photos/BitmapRegionTileSource.java
@@ -283,9 +283,6 @@
             } catch (FileNotFoundException e) {
                 Log.e("BitmapRegionTileSource", "Failed to load URI " + mUri, e);
                 return null;
-            } catch (IOException e) {
-                Log.e("BitmapRegionTileSource", "Failure while reading URI " + mUri, e);
-                return null;
             }
         }
         @Override
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index acd0727..3b0deb8 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -41,7 +41,6 @@
     <string name="group_applications" msgid="2103752818818161976">"Rakendused"</string>
     <string name="group_shortcuts" msgid="9133529424900391877">"Otseteed"</string>
     <string name="group_widgets" msgid="6704978494073105844">"Vidinad"</string>
-    <string name="group_wallpapers" msgid="1568191644272224858">"Taustapildid"</string>
     <string name="completely_out_of_space" msgid="1759078539443491182">"Teie avakuvadel ei ole enam ruumi."</string>
     <string name="out_of_space" msgid="8365249326091984698">"Sellel avalehel pole enam ruumi."</string>
     <string name="hotseat_out_of_space" msgid="6304886797358479361">"Kohandataval dokialal pole rohkem ruumi."</string>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index d2f6bb1..f50a5db 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -41,7 +41,6 @@
     <string name="group_applications" msgid="2103752818818161976">"Aplikasi"</string>
     <string name="group_shortcuts" msgid="9133529424900391877">"Pintasan"</string>
     <string name="group_widgets" msgid="6704978494073105844">"Widget"</string>
-    <string name="group_wallpapers" msgid="1568191644272224858">"Kertas dinding"</string>
     <string name="completely_out_of_space" msgid="1759078539443491182">"Tiada lagi ruang pada skrin Utama anda."</string>
     <string name="out_of_space" msgid="8365249326091984698">"Tiada lagi ruang pada skrin Utama ini"</string>
     <string name="hotseat_out_of_space" msgid="6304886797358479361">"Tiada lagi ruang pada kerusi panas."</string>
diff --git a/src/com/android/launcher3/AddAdapter.java b/src/com/android/launcher3/AddAdapter.java
deleted file mode 100644
index 5308a3d..0000000
--- a/src/com/android/launcher3/AddAdapter.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.TextView;
-
-import java.util.ArrayList;
-
-/**
- * Adapter showing the types of items that can be added to a {@link Workspace}.
- */
-public class AddAdapter extends BaseAdapter {
-
-    private final LayoutInflater mInflater;
-
-    private final ArrayList<ListItem> mItems = new ArrayList<ListItem>();
-
-    public static final int ITEM_SHORTCUT = 0;
-    public static final int ITEM_APPWIDGET = 1;
-    public static final int ITEM_APPLICATION = 2;
-    public static final int ITEM_WALLPAPER = 3;
-
-    /**
-     * Specific item in our list.
-     */
-    public class ListItem {
-        public final CharSequence text;
-        public final Drawable image;
-        public final int actionTag;
-
-        public ListItem(Resources res, int textResourceId, int imageResourceId, int actionTag) {
-            text = res.getString(textResourceId);
-            if (imageResourceId != -1) {
-                image = res.getDrawable(imageResourceId);
-            } else {
-                image = null;
-            }
-            this.actionTag = actionTag;
-        }
-    }
-    
-    public AddAdapter(Launcher launcher) {
-        super();
-
-        mInflater = (LayoutInflater) launcher.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
-        // Create default actions
-        Resources res = launcher.getResources();
-
-        mItems.add(new ListItem(res, R.string.group_wallpapers,
-                R.mipmap.ic_launcher_wallpaper, ITEM_WALLPAPER));
-    }
-
-    public View getView(int position, View convertView, ViewGroup parent) {
-        ListItem item = (ListItem) getItem(position);
-
-        if (convertView == null) {
-            convertView = mInflater.inflate(R.layout.add_list_item, parent, false);
-        }
-
-        TextView textView = (TextView) convertView;
-        textView.setTag(item);
-        textView.setText(item.text);
-        textView.setCompoundDrawablesWithIntrinsicBounds(item.image, null, null, null);
-
-        return convertView;
-    }
-
-    public int getCount() {
-        return mItems.size();
-    }
-
-    public Object getItem(int position) {
-        return mItems.get(position);
-    }
-
-    public long getItemId(int position) {
-        return position;
-    }
-}
diff --git a/src/com/android/launcher3/DragController.java b/src/com/android/launcher3/DragController.java
index 4c3ea2a..6d0a2be 100644
--- a/src/com/android/launcher3/DragController.java
+++ b/src/com/android/launcher3/DragController.java
@@ -329,8 +329,8 @@
                     if (dragInfo != null &&
                             dragInfo.intent != null && info != null) {
                         ComponentName cn = dragInfo.intent.getComponent();
-                        boolean isSameComponent = cn.equals(info.componentName) ||
-                                packageNames.contains(cn.getPackageName());
+                        boolean isSameComponent = cn != null && (cn.equals(info.componentName) ||
+                                packageNames.contains(cn.getPackageName()));
                         if (isSameComponent) {
                             cancelDrag();
                             return;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 60efcea..f8c9f7b 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -286,9 +286,6 @@
     private ArrayList<Runnable> mBindOnResumeCallbacks = new ArrayList<Runnable>();
     private ArrayList<Runnable> mOnResumeCallbacks = new ArrayList<Runnable>();
 
-    // Keep track of whether the user has left launcher
-    private static boolean sPausedFromUserAction = false;
-
     private Bundle mSavedInstanceState;
 
     private LauncherModel mModel;
@@ -465,7 +462,7 @@
         }
 
         if (!mRestoring) {
-            if (DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE || sPausedFromUserAction) {
+            if (DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE) {
                 // If the user leaves launcher, then we should just load items asynchronously when
                 // they return.
                 mModel.startLoader(true, PagedView.INVALID_RESTORE_PAGE);
@@ -511,11 +508,6 @@
     @Override
     public void onLauncherProviderChange() { }
 
-    protected void onUserLeaveHint() {
-        super.onUserLeaveHint();
-        sPausedFromUserAction = true;
-    }
-
     /** To be overriden by subclasses to hint to Launcher that we have custom content */
     protected boolean hasCustomContentToLeft() {
         return false;
@@ -971,7 +963,6 @@
         setWorkspaceBackground(mState == State.WORKSPACE);
 
         mPaused = false;
-        sPausedFromUserAction = false;
         if (mRestoring || mOnResumeNeedsLoad) {
             setWorkspaceLoading(true);
             mModel.startLoader(true, PagedView.INVALID_RESTORE_PAGE);
@@ -3380,10 +3371,7 @@
     }
 
     void showWorkspace(boolean animated, Runnable onCompleteRunnable) {
-        if (mWorkspace.isInOverviewMode()) {
-            mWorkspace.exitOverviewMode(animated);
-        }
-        if (mState != State.WORKSPACE) {
+        if (mState != State.WORKSPACE || mWorkspace.getState() != Workspace.State.NORMAL) {
             boolean wasInSpringLoadedMode = (mState != State.WORKSPACE);
             mWorkspace.setVisibility(View.VISIBLE);
             hideAppsCustomizeHelper(Workspace.State.NORMAL, animated, false, onCompleteRunnable);
@@ -3432,7 +3420,13 @@
             mAppsCustomizeTabHost.reset();
         }
         showAppsCustomizeHelper(animated, false, contentType);
-        mAppsCustomizeTabHost.requestFocus();
+        mAppsCustomizeTabHost.post(new Runnable() {
+            @Override
+            public void run() {
+                // We post this in-case the all apps view isn't yet constructed.
+                mAppsCustomizeTabHost.requestFocus();
+            }
+        });
 
         // Change the state *after* we've called all the transition code
         mState = State.APPS_CUSTOMIZE;
@@ -3472,7 +3466,6 @@
                 }
             }
         }, delay);
-
     }
 
     void exitSpringLoadedDragMode() {
diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java
index 7b08d44..fa5e38f 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHost.java
@@ -20,6 +20,7 @@
 import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.Context;
+import android.os.TransactionTooLargeException;
 
 /**
  * Specific {@link AppWidgetHost} that creates our {@link LauncherAppWidgetHostView}
@@ -42,6 +43,22 @@
     }
 
     @Override
+    public void startListening() {
+        try {
+            super.startListening();
+        } catch (Exception e) {
+            if (e.getCause() instanceof TransactionTooLargeException) {
+                // We're willing to let this slide. The exception is being caused by the list of
+                // RemoteViews which is being passed back. The startListening relationship will
+                // have been established by this point, and we will end up populating the
+                // widgets upon bind anyway. See issue 14255011 for more context.
+            } else {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    @Override
     public void stopListening() {
         super.stopListening();
         clearViews();
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index bbc75b8..2256179 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -310,10 +310,21 @@
 
         if (sp.getBoolean(EMPTY_DATABASE_CREATED, false)) {
             Log.d(TAG, "loading default workspace");
+            // By default we use our resources
+            Resources res = getContext().getResources();
             int workspaceResId = origWorkspaceResId;
 
             // Use default workspace resource if none provided
             if (workspaceResId == 0) {
+                final Partner partner = Partner.get(getContext().getPackageManager());
+                if (partner != null && partner.hasDefaultLayout()) {
+                    final Resources partnerRes = partner.getResources();
+                    workspaceResId = partnerRes.getIdentifier(Partner.RESOURCE_DEFAULT_LAYOUT,
+                            "xml", partner.getPackageName());
+                    res = partnerRes;
+                }
+            }
+            if (workspaceResId == 0) {
                 workspaceResId =
                         sp.getInt(DEFAULT_WORKSPACE_RESOURCE_ID, getDefaultWorkspaceResourceId());
             }
@@ -325,7 +336,7 @@
                 editor.putInt(DEFAULT_WORKSPACE_RESOURCE_ID, origWorkspaceResId);
             }
 
-            mOpenHelper.loadFavorites(mOpenHelper.getWritableDatabase(), workspaceResId);
+            mOpenHelper.loadFavorites(mOpenHelper.getWritableDatabase(), res, workspaceResId);
             editor.commit();
         }
     }
@@ -380,11 +391,25 @@
         private static final String TAG_EXTRA = "extra";
         private static final String TAG_INCLUDE = "include";
 
-        private static final String ATTR_TITLE = "title";
-        private static final String ATTR_ICON = "icon";
-        private static final String ATTR_URI = "uri";
-        private static final String ATTR_PACKAGE_NAME = "packageName";
+        // Style attrs -- "Favorite"
         private static final String ATTR_CLASS_NAME = "className";
+        private static final String ATTR_PACKAGE_NAME = "packageName";
+        private static final String ATTR_CONTAINER = "container";
+        private static final String ATTR_SCREEN = "screen";
+        private static final String ATTR_X = "x";
+        private static final String ATTR_Y = "y";
+        private static final String ATTR_SPAN_X = "spanX";
+        private static final String ATTR_SPAN_Y = "spanY";
+        private static final String ATTR_ICON = "icon";
+        private static final String ATTR_TITLE = "title";
+        private static final String ATTR_URI = "uri";
+
+        // Style attrs -- "Include"
+        private static final String ATTR_WORKSPACE = "workspace";
+
+        // Style attrs -- "Extra"
+        private static final String ATTR_KEY = "key";
+        private static final String ATTR_VALUE = "value";
 
         private final Context mContext;
         private final PackageManager mPackageManager;
@@ -753,7 +778,7 @@
                 }
 
                 // Add default hotseat icons
-                loadFavorites(db, R.xml.update_workspace);
+                loadFavorites(db, mContext.getResources(), R.xml.update_workspace);
                 version = 9;
             }
 
@@ -1244,9 +1269,9 @@
             return intent;
         }
 
-        private int loadFavorites(SQLiteDatabase db, int workspaceResourceId) {
+        private int loadFavorites(SQLiteDatabase db, Resources res, int workspaceResourceId) {
             ArrayList<Long> screenIds = new ArrayList<Long>();
-            int count = loadFavoritesRecursive(db, workspaceResourceId, screenIds);
+            int count = loadFavoritesRecursive(db, res, workspaceResourceId, screenIds);
 
             // Add the screens specified by the items above
             Collections.sort(screenIds);
@@ -1276,18 +1301,15 @@
          * @param filterContainerId The specific container id of items to load
          * @param the set of screenIds which are used by the favorites
          */
-        private int loadFavoritesRecursive(SQLiteDatabase db, int workspaceResourceId,
+        private int loadFavoritesRecursive(SQLiteDatabase db, Resources res, int workspaceResourceId,
                 ArrayList<Long> screenIds) {
 
-
-
             ContentValues values = new ContentValues();
             if (LOGD) Log.v(TAG, String.format("Loading favorites from resid=0x%08x", workspaceResourceId));
 
             int count = 0;
             try {
-                XmlResourceParser parser = mContext.getResources().getXml(workspaceResourceId);
-                AttributeSet attrs = Xml.asAttributeSet(parser);
+                XmlResourceParser parser = res.getXml(workspaceResourceId);
                 beginDocument(parser, TAG_FAVORITES);
 
                 final int depth = parser.getDepth();
@@ -1304,38 +1326,34 @@
                     final String name = parser.getName();
 
                     if (TAG_INCLUDE.equals(name)) {
-                        final TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.Include);
 
-                        final int resId = a.getResourceId(R.styleable.Include_workspace, 0);
+                        final int resId = getAttributeResourceValue(parser, ATTR_WORKSPACE, 0);
 
                         if (LOGD) Log.v(TAG, String.format(("%" + (2*(depth+1)) + "s<include workspace=%08x>"),
                                 "", resId));
 
                         if (resId != 0 && resId != workspaceResourceId) {
                             // recursively load some more favorites, why not?
-                            count += loadFavoritesRecursive(db, resId, screenIds);
+                            count += loadFavoritesRecursive(db, res, resId, screenIds);
                             added = false;
                         } else {
                             Log.w(TAG, String.format("Skipping <include workspace=0x%08x>", resId));
                         }
 
-                        a.recycle();
-
                         if (LOGD) Log.v(TAG, String.format(("%" + (2*(depth+1)) + "s</include>"), ""));
                         continue;
                     }
 
                     // Assuming it's a <favorite> at this point
-                    TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.Favorite);
-
                     long container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
-                    if (a.hasValue(R.styleable.Favorite_container)) {
-                        container = Long.valueOf(a.getString(R.styleable.Favorite_container));
+                    String strContainer = getAttributeValue(parser, ATTR_CONTAINER);
+                    if (strContainer != null) {
+                        container = Long.valueOf(strContainer);
                     }
 
-                    String screen = a.getString(R.styleable.Favorite_screen);
-                    String x = a.getString(R.styleable.Favorite_x);
-                    String y = a.getString(R.styleable.Favorite_y);
+                    String screen = getAttributeValue(parser, ATTR_SCREEN);
+                    String x = getAttributeValue(parser, ATTR_X);
+                    String y = getAttributeValue(parser, ATTR_Y);
 
                     values.clear();
                     values.put(LauncherSettings.Favorites.CONTAINER, container);
@@ -1344,8 +1362,8 @@
                     values.put(LauncherSettings.Favorites.CELLY, y);
 
                     if (LOGD) {
-                        final String title = a.getString(R.styleable.Favorite_title);
-                        final String pkg = a.getString(R.styleable.Favorite_packageName);
+                        final String title = getAttributeValue(parser, ATTR_TITLE);
+                        final String pkg = getAttributeValue(parser, ATTR_PACKAGE_NAME);
                         final String something = title != null ? title : pkg;
                         Log.v(TAG, String.format(
                                 ("%" + (2*(depth+1)) + "s<%s%s c=%d s=%s x=%s y=%s>"),
@@ -1357,14 +1375,10 @@
                     if (TAG_FAVORITE.equals(name)) {
                         long id = addAppShortcut(db, values, parser);
                         added = id >= 0;
-                    } else if (TAG_SEARCH.equals(name)) {
-                        added = addSearchWidget(db, values);
-                    } else if (TAG_CLOCK.equals(name)) {
-                        added = addClockWidget(db, values);
                     } else if (TAG_APPWIDGET.equals(name)) {
-                        added = addAppWidget(parser, attrs, type, db, values, a);
+                        added = addAppWidget(parser, type, db, values);
                     } else if (TAG_SHORTCUT.equals(name)) {
-                        long id = addUriShortcut(db, values, mContext.getResources(), parser);
+                        long id = addUriShortcut(db, values, res, parser);
                         added = id >= 0;
                     } else if (TAG_RESOLVE.equals(name)) {
                         // This looks through the contained favorites (or meta-favorites) and
@@ -1378,18 +1392,15 @@
                                 continue;
                             }
                             final String fallback_item_name = parser.getName();
-                            final TypedArray ar = mContext.obtainStyledAttributes(attrs,
-                                    R.styleable.Favorite);
                             if (!added) {
                                 if (TAG_FAVORITE.equals(fallback_item_name)) {
                                     final long id = addAppShortcut(db, values, parser);
                                     added = id >= 0;
                                 } else {
-                                    Log.e(TAG, "Fallback groups can contain only favorites "
-                                            + ar.toString());
+                                    Log.e(TAG, "Fallback groups can contain only favorites, found "
+                                            + fallback_item_name);
                                 }
                             }
-                            ar.recycle();
                         }
                     } else if (TAG_FOLDER.equals(name)) {
                         // Folder contents are nested in this XML file
@@ -1418,7 +1429,6 @@
                         }
                         count++;
                     }
-                    a.recycle();
                 }
             } catch (XmlPullParserException e) {
                 Log.w(TAG, "Got exception parsing favorites.", e);
@@ -1681,23 +1691,12 @@
             return null;
         }
 
-        private boolean addSearchWidget(SQLiteDatabase db, ContentValues values) {
-            ComponentName cn = getSearchWidgetProvider();
-            return addAppWidget(db, values, cn, 4, 1, null);
-        }
-
-        private boolean addClockWidget(SQLiteDatabase db, ContentValues values) {
-            ComponentName cn = new ComponentName("com.android.alarmclock",
-                    "com.android.alarmclock.AnalogAppWidgetProvider");
-            return addAppWidget(db, values, cn, 2, 2, null);
-        }
-
-        private boolean addAppWidget(XmlResourceParser parser, AttributeSet attrs, int type,
-                SQLiteDatabase db, ContentValues values, TypedArray a)
+        private boolean addAppWidget(XmlResourceParser parser, int type,
+                SQLiteDatabase db, ContentValues values)
                 throws XmlPullParserException, IOException {
 
-            String packageName = a.getString(R.styleable.Favorite_packageName);
-            String className = a.getString(R.styleable.Favorite_className);
+            String packageName = getAttributeValue(parser, ATTR_PACKAGE_NAME);
+            String className = getAttributeValue(parser, ATTR_CLASS_NAME);
 
             if (packageName == null || className == null) {
                 return false;
@@ -1714,13 +1713,17 @@
                 try {
                     mPackageManager.getReceiverInfo(cn, 0);
                 } catch (Exception e1) {
+                    System.out.println("Can't find widget provider: " + className);
                     hasPackage = false;
                 }
             }
 
             if (hasPackage) {
-                int spanX = a.getInt(R.styleable.Favorite_spanX, 0);
-                int spanY = a.getInt(R.styleable.Favorite_spanY, 0);
+                String spanX = getAttributeValue(parser, ATTR_SPAN_X);
+                String spanY = getAttributeValue(parser, ATTR_SPAN_Y);
+
+                values.put(Favorites.SPANX, spanX);
+                values.put(Favorites.SPANY, spanY);
 
                 // Read the extras
                 Bundle extras = new Bundle();
@@ -1731,10 +1734,9 @@
                         continue;
                     }
 
-                    TypedArray ar = mContext.obtainStyledAttributes(attrs, R.styleable.Extra);
                     if (TAG_EXTRA.equals(parser.getName())) {
-                        String key = ar.getString(R.styleable.Extra_key);
-                        String value = ar.getString(R.styleable.Extra_value);
+                        String key = getAttributeValue(parser, ATTR_KEY);
+                        String value = getAttributeValue(parser, ATTR_VALUE);
                         if (key != null && value != null) {
                             extras.putString(key, value);
                         } else {
@@ -1743,16 +1745,15 @@
                     } else {
                         throw new RuntimeException("Widgets can contain only extras");
                     }
-                    ar.recycle();
                 }
 
-                return addAppWidget(db, values, cn, spanX, spanY, extras);
+                return addAppWidget(db, values, cn, extras);
             }
 
             return false;
         }
         private boolean addAppWidget(SQLiteDatabase db, ContentValues values, ComponentName cn,
-                int spanX, int spanY, Bundle extras) {
+               Bundle extras) {
             boolean allocatedAppWidgets = false;
             final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
 
@@ -1760,8 +1761,6 @@
                 int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
 
                 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPWIDGET);
-                values.put(Favorites.SPANX, spanX);
-                values.put(Favorites.SPANY, spanY);
                 values.put(Favorites.APPWIDGET_ID, appWidgetId);
                 values.put(Favorites.APPWIDGET_PROVIDER, cn.flattenToString());
                 values.put(Favorites._ID, generateNewItemId());
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 9763126..1037d98 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -547,6 +547,19 @@
         mNextPage = INVALID_PAGE;
     }
 
+    private int validateNewPage(int newPage) {
+        int validatedPage = newPage;
+        // When in free scroll mode, we need to clamp to the free scroll page range.
+        if (mFreeScroll) {
+            getFreeScrollPageRange(mTempVisiblePagesRange);
+            validatedPage = Math.max(mTempVisiblePagesRange[0],
+                    Math.min(newPage, mTempVisiblePagesRange[1]));
+        }
+        // Ensure that it is clamped by the actual set of children in all cases
+        validatedPage = Math.max(0, Math.min(validatedPage, getPageCount() - 1));
+        return validatedPage;
+    }
+
     /**
      * Sets the current page.
      */
@@ -560,7 +573,7 @@
             return;
         }
         mForceScreenScrolled = true;
-        mCurrentPage = Math.max(0, Math.min(currentPage, getPageCount() - 1));
+        mCurrentPage = validateNewPage(currentPage);
         updateCurrentPageScroll();
         notifyPageSwitchListener();
         invalidate();
@@ -727,7 +740,7 @@
         } else if (mNextPage != INVALID_PAGE) {
             sendScrollAccessibilityEvent();
 
-            mCurrentPage = Math.max(0, Math.min(mNextPage, getPageCount() - 1));
+            mCurrentPage = validateNewPage(mNextPage);
             mNextPage = INVALID_PAGE;
             notifyPageSwitchListener();
 
@@ -1105,7 +1118,7 @@
         return offset;
     }
 
-    protected void getOverviewModePages(int[] range) {
+    protected void getFreeScrollPageRange(int[] range) {
         range[0] = 0;
         range[1] = Math.max(0, getChildCount() - 1);
     }
@@ -1650,7 +1663,7 @@
     }
 
     void updateFreescrollBounds() {
-        getOverviewModePages(mTempVisiblePagesRange);
+        getFreeScrollPageRange(mTempVisiblePagesRange);
         if (isLayoutRtl()) {
             mFreeScrollMinScrollX = getScrollForPage(mTempVisiblePagesRange[1]);
             mFreeScrollMaxScrollX = getScrollForPage(mTempVisiblePagesRange[0]);
@@ -1665,7 +1678,7 @@
 
         if (mFreeScroll) {
             updateFreescrollBounds();
-            getOverviewModePages(mTempVisiblePagesRange);
+            getFreeScrollPageRange(mTempVisiblePagesRange);
             if (getCurrentPage() < mTempVisiblePagesRange[0]) {
                 setCurrentPage(mTempVisiblePagesRange[0]);
             } else if (getCurrentPage() > mTempVisiblePagesRange[1]) {
@@ -1684,7 +1697,7 @@
         if (mDragView != null) {
             int dragX = (int) (mDragView.getLeft() + (mDragView.getMeasuredWidth() / 2)
                     + mDragView.getTranslationX());
-            getOverviewModePages(mTempVisiblePagesRange);
+            getFreeScrollPageRange(mTempVisiblePagesRange);
             int minDistance = Integer.MAX_VALUE;
             int minIndex = indexOfChild(mDragView);
             for (int i = mTempVisiblePagesRange[0]; i <= mTempVisiblePagesRange[1]; i++) {
@@ -1801,7 +1814,7 @@
                         !isHoveringOverDelete) {
                     mTempVisiblePagesRange[0] = 0;
                     mTempVisiblePagesRange[1] = getPageCount() - 1;
-                    getOverviewModePages(mTempVisiblePagesRange);
+                    getFreeScrollPageRange(mTempVisiblePagesRange);
                     if (mTempVisiblePagesRange[0] <= pageUnderPointIndex &&
                             pageUnderPointIndex <= mTempVisiblePagesRange[1] &&
                             pageUnderPointIndex != mSidePageHoverIndex && mScroller.isFinished()) {
@@ -2149,7 +2162,7 @@
     }
 
     protected void snapToPageWithVelocity(int whichPage, int velocity) {
-        whichPage = Math.max(0, Math.min(whichPage, getChildCount() - 1));
+        whichPage = validateNewPage(whichPage);
         int halfScreenSize = getViewportWidth() / 2;
 
         final int newX = getScrollForPage(whichPage);
@@ -2200,7 +2213,7 @@
 
     protected void snapToPage(int whichPage, int duration, boolean immediate,
             TimeInterpolator interpolator) {
-        whichPage = Math.max(0, Math.min(whichPage, getPageCount() - 1));
+        whichPage = validateNewPage(whichPage);
 
         int newX = getScrollForPage(whichPage);
         final int sX = mUnboundedScrollX;
@@ -2214,6 +2227,8 @@
 
     protected void snapToPage(int whichPage, int delta, int duration, boolean immediate,
             TimeInterpolator interpolator) {
+        whichPage = validateNewPage(whichPage);
+
         mNextPage = whichPage;
         View focusedChild = getFocusedChild();
         if (focusedChild != null && whichPage != mCurrentPage &&
@@ -2486,7 +2501,7 @@
 
         mTempVisiblePagesRange[0] = 0;
         mTempVisiblePagesRange[1] = getPageCount() - 1;
-        getOverviewModePages(mTempVisiblePagesRange);
+        getFreeScrollPageRange(mTempVisiblePagesRange);
         mReorderingStarted = true;
 
         // Check if we are within the reordering range
@@ -2619,7 +2634,7 @@
                 // in the layout)
                 // NOTE: We can make an assumption here because we have side-bound pages that we
                 //       will always have pages to animate in from the left
-                getOverviewModePages(mTempVisiblePagesRange);
+                getFreeScrollPageRange(mTempVisiblePagesRange);
                 boolean isLastWidgetPage = (mTempVisiblePagesRange[0] == mTempVisiblePagesRange[1]);
                 boolean slideFromLeft = (isLastWidgetPage ||
                         dragViewIndex > mTempVisiblePagesRange[0]);
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 0015418..6afea82 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -2086,13 +2086,17 @@
     }
 
     @Override
-    protected void getOverviewModePages(int[] range) {
+    protected void getFreeScrollPageRange(int[] range) {
+        getOverviewModePages(range);
+    }
+
+    private void getOverviewModePages(int[] range) {
         int start = numCustomPages();
         int end = getChildCount() - 1;
 
         range[0] = Math.max(0, Math.min(start, getChildCount() - 1));
         range[1] = Math.max(0,  end);
-     }
+    }
 
     protected void onStartReordering() {
         super.onStartReordering();
@@ -2200,6 +2204,10 @@
         updateAccessibilityFlags();
     }
 
+    State getState() {
+        return mState;
+    }
+
     private void updateAccessibilityFlags() {
         int accessible = mState == State.NORMAL ?
                 ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES :