Merge "Skip to the end of the folder open animation in battery saver mode." into ub-launcher3-burnaby-polish
diff --git a/WallpaperPicker/res/values-sw720dp-v19/styles.xml b/WallpaperPicker/res/values-sw720dp-v19/styles.xml
deleted file mode 100644
index d8dab22..0000000
--- a/WallpaperPicker/res/values-sw720dp-v19/styles.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-* Copyright (C) 2013 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.
-*/
--->
-
-<resources>
-    <style name="Theme" parent="@android:style/Theme.DeviceDefault.Wallpaper.NoTitleBar">
-        <item name="android:windowActionModeOverlay">true</item>
-        <item name="android:windowTranslucentStatus">true</item>
-        <item name="android:windowTranslucentNavigation">true</item>
-    </style>
-</resources>
diff --git a/WallpaperPicker/res/values-sw720dp/styles.xml b/WallpaperPicker/res/values-sw720dp/styles.xml
index 12f8884..0058f7e 100644
--- a/WallpaperPicker/res/values-sw720dp/styles.xml
+++ b/WallpaperPicker/res/values-sw720dp/styles.xml
@@ -18,7 +18,11 @@
 -->
 
 <resources>
-    <style name="Theme" parent="@android:style/Theme.DeviceDefault.Wallpaper.NoTitleBar">
+    <style name="BaseWallpaperTheme" parent="@android:style/Theme.DeviceDefault.Light.NoActionBar">
+        <item name="android:windowBackground">@android:color/transparent</item>
+        <item name="android:colorBackgroundCacheHint">@null</item>
+        <item name="android:windowShowWallpaper">true</item>
+        <item name="android:windowNoTitle">true</item>
         <item name="android:windowActionModeOverlay">true</item>
     </style>
 </resources>
diff --git a/WallpaperPicker/res/values-v21/styles.xml b/WallpaperPicker/res/values-v21/styles.xml
index 70220ed..de4b2f2 100644
--- a/WallpaperPicker/res/values-v21/styles.xml
+++ b/WallpaperPicker/res/values-v21/styles.xml
@@ -34,8 +34,8 @@
     </style>
 
     <style name="Theme" parent="@style/BaseWallpaperTheme">
-        <item name="android:windowTranslucentStatus">true</item>
-        <item name="android:windowTranslucentNavigation">true</item>
+        <item name="android:statusBarColor">#00000000</item>
+        <item name="android:navigationBarColor">#00000000</item>
         <item name="android:colorControlActivated">@color/launcher_accent_color</item>
         <item name="android:colorAccent">@color/launcher_accent_color</item>
         <item name="android:colorPrimary">@color/launcher_accent_color</item>
diff --git a/WallpaperPicker/src/com/android/photos/BitmapRegionTileSource.java b/WallpaperPicker/src/com/android/photos/BitmapRegionTileSource.java
index 2d496a5..6baac6a 100644
--- a/WallpaperPicker/src/com/android/photos/BitmapRegionTileSource.java
+++ b/WallpaperPicker/src/com/android/photos/BitmapRegionTileSource.java
@@ -159,6 +159,7 @@
         public enum State { NOT_LOADED, LOADED, ERROR_LOADING };
         private State mState = State.NOT_LOADED;
 
+        /** Returns whether loading was successful. */
         public boolean loadInBackground(InBitmapProvider bitmapProvider) {
             ExifInterface ei = new ExifInterface();
             if (readExif(ei)) {
@@ -193,7 +194,7 @@
                         try {
                             mPreview = loadPreviewBitmap(opts);
                         } catch (IllegalArgumentException e) {
-                            Log.d(TAG, "Unable to reusage bitmap", e);
+                            Log.d(TAG, "Unable to reuse bitmap", e);
                             opts.inBitmap = null;
                             mPreview = null;
                         }
@@ -202,6 +203,10 @@
                 if (mPreview == null) {
                     mPreview = loadPreviewBitmap(opts);
                 }
+                if (mPreview == null) {
+                    mState = State.ERROR_LOADING;
+                    return false;
+                }
 
                 // Verify that the bitmap can be used on GL surface
                 try {
@@ -212,7 +217,7 @@
                     Log.d(TAG, "Image cannot be rendered on a GL surface", e);
                     mState = State.ERROR_LOADING;
                 }
-                return true;
+                return mState == State.LOADED;
             }
         }
 
@@ -310,7 +315,7 @@
                 Bitmap b = BitmapFactory.decodeStream(is, null, options);
                 Utils.closeSilently(is);
                 return b;
-            } catch (FileNotFoundException e) {
+            } catch (FileNotFoundException | OutOfMemoryError e) {
                 Log.e("BitmapRegionTileSource", "Failed to load URI " + mUri, e);
                 return null;
             }
@@ -412,7 +417,8 @@
                         "Failed to create preview of apropriate size! "
                         + " in: %dx%d, out: %dx%d",
                         mWidth, mHeight,
-                        preview.getWidth(), preview.getHeight()));
+                        preview == null ? -1 : preview.getWidth(),
+                        preview == null ? -1 : preview.getHeight()));
             }
         }
     }
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index c0ad516..ccbfba1 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -245,7 +245,9 @@
         hotseatCellHeightPx = iconSizePx;
 
         // Folder
-        folderCellWidthPx = Math.min(cellWidthPx + 6 * edgeMarginPx,
+        int folderCellPadding = isTablet || isLandscape ? 6 * edgeMarginPx : 3 * edgeMarginPx;
+        // Don't let the folder get too close to the edges of the screen.
+        folderCellWidthPx = Math.min(cellWidthPx + folderCellPadding,
                 (availableWidthPx - 4 * edgeMarginPx) / inv.numFolderColumns);
         folderCellHeightPx = cellHeightPx + edgeMarginPx;
         folderBackgroundOffset = -edgeMarginPx;
diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java
index 9377bad..da895c6 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -318,9 +318,10 @@
             sendCustomAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
                     String.format(getContext().getString(R.string.folder_renamed), newTitle));
         }
-        // In order to clear the focus from the text field, we set the focus on ourself. This
-        // ensures that every time the field is clicked, focus is gained, giving reliable behavior.
-        requestFocus();
+
+        // This ensures that focus is gained every time the field is clicked, which selects all
+        // the text and brings up the soft keyboard if necessary.
+        mFolderName.clearFocus();
 
         Selection.setSelection((Spannable) mFolderName.getText(), 0, 0);
         mIsEditingName = false;
@@ -451,6 +452,11 @@
             mContent.snapToPageImmediately(0);
         }
 
+        // This is set to true in close(), but isn't reset to false until onDropCompleted(). This
+        // leads to an consistent state if you drag out of the folder and drag back in without
+        // dropping. One resulting issue is that replaceFolderWithFinalItem() can be called twice.
+        mDeleteFolderOnDropCompleted = false;
+
         Animator openFolderAnim = null;
         final Runnable onCompleteRunnable;
         if (!Utilities.ATLEAST_LOLLIPOP) {
@@ -640,7 +646,7 @@
             @Override
             public void onAnimationEnd(Animator animation) {
                 setLayerType(LAYER_TYPE_NONE, null);
-                close();
+                close(true);
             }
             @Override
             public void onAnimationStart(Animator animation) {
@@ -654,7 +660,7 @@
         oa.start();
     }
 
-    public void close() {
+    public void close(boolean wasAnimated) {
         // TODO: Clear all active animations.
         DragLayer parent = (DragLayer) getParent();
         if (parent != null) {
@@ -662,7 +668,9 @@
         }
         mDragController.removeDropTarget(this);
         clearFocus();
-        mFolderIcon.requestFocus();
+        if (wasAnimated) {
+            mFolderIcon.requestFocus();
+        }
 
         if (mRearrangeOnClose) {
             rearrangeChildren();
@@ -1144,10 +1152,10 @@
                         // addInScreenFromBind() to ensure that hotseat items are placed correctly.
                         mLauncher.getWorkspace().addInScreenFromBind(newIcon, mInfo.container,
                                 mInfo.screenId, mInfo.cellX, mInfo.cellY, mInfo.spanX, mInfo.spanY);
-                    }
 
-                    // Focus the newly created child
-                    newIcon.requestFocus();
+                        // Focus the newly created child
+                        newIcon.requestFocus();
+                    }
                 }
             }
         };
@@ -1164,15 +1172,37 @@
         return mDestroyed;
     }
 
-    // This method keeps track of the last item in the folder for the purposes
+    // This method keeps track of the first and last item in the folder for the purposes
     // of keyboard focus
     public void updateTextViewFocus() {
-        View lastChild = mContent.getLastItem();
-        if (lastChild != null) {
+        final View firstChild = mContent.getFirstItem();
+        final View lastChild = mContent.getLastItem();
+        if (firstChild != null && lastChild != null) {
             mFolderName.setNextFocusDownId(lastChild.getId());
             mFolderName.setNextFocusRightId(lastChild.getId());
             mFolderName.setNextFocusLeftId(lastChild.getId());
             mFolderName.setNextFocusUpId(lastChild.getId());
+            // Hitting TAB from the folder name wraps around to the first item on the current
+            // folder page, and hitting SHIFT+TAB from that item wraps back to the folder name.
+            mFolderName.setNextFocusForwardId(firstChild.getId());
+            // When clicking off the folder when editing the name, this Folder gains focus. When
+            // pressing an arrow key from that state, give the focus to the first item.
+            this.setNextFocusDownId(firstChild.getId());
+            this.setNextFocusRightId(firstChild.getId());
+            this.setNextFocusLeftId(firstChild.getId());
+            this.setNextFocusUpId(firstChild.getId());
+            // When pressing shift+tab in the above state, give the focus to the last item.
+            setOnKeyListener(new OnKeyListener() {
+                @Override
+                public boolean onKey(View v, int keyCode, KeyEvent event) {
+                    boolean isShiftPlusTab = keyCode == KeyEvent.KEYCODE_TAB &&
+                            event.hasModifiers(KeyEvent.META_SHIFT_ON);
+                    if (isShiftPlusTab && Folder.this.isFocused()) {
+                        return lastChild.requestFocus();
+                    }
+                    return false;
+                }
+            });
         }
     }
 
@@ -1293,7 +1323,11 @@
             rearrangeChildren();
         }
         if (getItemCount() <= 1) {
-            replaceFolderWithFinalItem();
+            if (mInfo.opened) {
+                mLauncher.closeFolder(this, true);
+            } else {
+                replaceFolderWithFinalItem();
+            }
         }
     }
 
@@ -1337,6 +1371,8 @@
     public void onFocusChange(View v, boolean hasFocus) {
         if (v == mFolderName && hasFocus) {
             startEditingFolderName();
+        } else if (v == mFolderName && !hasFocus) {
+            dismissEditingName();
         }
     }
 
diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java
index cc9c573..d503d2c 100644
--- a/src/com/android/launcher3/FolderPagedView.java
+++ b/src/com/android/launcher3/FolderPagedView.java
@@ -402,16 +402,28 @@
         return !ALLOW_FOLDER_SCROLL && getItemCount() >= mMaxItemsPerPage;
     }
 
+    public View getFirstItem() {
+        if (getChildCount() < 1) {
+            return null;
+        }
+        ShortcutAndWidgetContainer currContainer = getCurrentCellLayout().getShortcutsAndWidgets();
+        if (mGridCountX > 0) {
+            return currContainer.getChildAt(0, 0);
+        } else {
+            return currContainer.getChildAt(0);
+        }
+    }
+
     public View getLastItem() {
         if (getChildCount() < 1) {
             return null;
         }
-        ShortcutAndWidgetContainer lastContainer = getCurrentCellLayout().getShortcutsAndWidgets();
-        int lastRank = lastContainer.getChildCount() - 1;
+        ShortcutAndWidgetContainer currContainer = getCurrentCellLayout().getShortcutsAndWidgets();
+        int lastRank = currContainer.getChildCount() - 1;
         if (mGridCountX > 0) {
-            return lastContainer.getChildAt(lastRank % mGridCountX, lastRank / mGridCountX);
+            return currContainer.getChildAt(lastRank % mGridCountX, lastRank / mGridCountX);
         } else {
-            return lastContainer.getChildAt(lastRank);
+            return currContainer.getChildAt(lastRank);
         }
     }
 
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index 9a75193..571d99a 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -171,7 +171,6 @@
 
     private static void queuePendingShortcutInfo(PendingInstallShortcutInfo info, Context context) {
         // Queue the item up for adding if launcher has not loaded properly yet
-        LauncherAppState.setApplicationContext(context.getApplicationContext());
         LauncherAppState app = LauncherAppState.getInstance();
         boolean launcherNotLoaded = app.getModel().getCallback() == null;
 
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 658272f..ad30877 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -85,7 +85,6 @@
 import android.view.ViewGroup;
 import android.view.ViewStub;
 import android.view.ViewTreeObserver;
-import android.view.Window;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.OvershootInterpolator;
@@ -418,7 +417,6 @@
 
         super.onCreate(savedInstanceState);
 
-        LauncherAppState.setApplicationContext(getApplicationContext());
         LauncherAppState app = LauncherAppState.getInstance();
 
         // Load configuration-specific DeviceProfile
@@ -1684,31 +1682,10 @@
         }
         registerReceiver(mReceiver, filter);
         FirstFrameAnimatorHelper.initializeDrawListener(getWindow().getDecorView());
-        setupTransparentSystemBarsForLollipop();
         mAttached = true;
         mVisible = true;
     }
 
-    /**
-     * Sets up transparent navigation and status bars in Lollipop.
-     * This method is a no-op for other platform versions.
-     */
-    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
-    private void setupTransparentSystemBarsForLollipop() {
-        if (Utilities.ATLEAST_LOLLIPOP) {
-            Window window = getWindow();
-            window.getAttributes().systemUiVisibility |=
-                    (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
-                            | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
-                            | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
-            window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
-                    | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
-            window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
-            window.setStatusBarColor(Color.TRANSPARENT);
-            window.setNavigationBarColor(Color.TRANSPARENT);
-        }
-    }
-
     @Override
     public void onDetachedFromWindow() {
         super.onDetachedFromWindow();
@@ -3230,7 +3207,7 @@
         if (animate) {
             folder.animateClosed();
         } else {
-            folder.close();
+            folder.close(false);
         }
 
         // Notify the accessibility manager that this folder "window" has disappeared and no
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index ef79cf8..f095a05 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -84,6 +84,10 @@
     @Override
     public boolean onCreate() {
         final Context context = getContext();
+        // The content provider exists for the entire duration of the launcher main process and
+        // is the first component to get created. Initializing application context here ensures
+        // that LauncherAppState always exists in the main process.
+        LauncherAppState.setApplicationContext(context.getApplicationContext());
         StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
         mOpenHelper = new DatabaseHelper(context);
         StrictMode.setThreadPolicy(oldPolicy);
diff --git a/src/com/android/launcher3/WallpaperChangedReceiver.java b/src/com/android/launcher3/WallpaperChangedReceiver.java
index 2d5612f..c24fbff 100644
--- a/src/com/android/launcher3/WallpaperChangedReceiver.java
+++ b/src/com/android/launcher3/WallpaperChangedReceiver.java
@@ -22,8 +22,6 @@
 
 public class WallpaperChangedReceiver extends BroadcastReceiver {
     public void onReceive(Context context, Intent data) {
-        LauncherAppState.setApplicationContext(context.getApplicationContext());
-        LauncherAppState appState = LauncherAppState.getInstance();
-        appState.onWallpaperChanged();
+        LauncherAppState.getInstance().onWallpaperChanged();
     }
 }
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
index 6424e03..4aa667e 100644
--- a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
+++ b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
@@ -43,7 +43,6 @@
 
     PackageInstallerCompatVL(Context context) {
         mInstaller = context.getPackageManager().getPackageInstaller();
-        LauncherAppState.setApplicationContext(context.getApplicationContext());
         mCache = LauncherAppState.getInstance().getIconCache();
         mWorker = new Handler(LauncherModel.getWorkerLooper());