Merge "Refresh the all apps container search result when apps are installed" into ub-launcher3-calgary
diff --git a/res/layout/all_apps.xml b/res/layout/all_apps.xml
index 822c8b6..5c2c4b8 100644
--- a/res/layout/all_apps.xml
+++ b/res/layout/all_apps.xml
@@ -65,18 +65,6 @@
             android:orientation="horizontal"
             android:saveEnabled="false">
 
-            <TextView
-                android:layout_width="wrap_content"
-                android:layout_height="match_parent"
-                android:gravity="center_vertical"
-                android:id="@+id/search_hint"
-                android:layout_gravity="center_horizontal"
-                android:drawablePadding="@dimen/all_apps_search_bar_icon_margin_right"
-                android:drawableStart="@drawable/ic_allapps_search"
-                android:text="@string/all_apps_search_bar_hint"
-                android:textColor="@drawable/all_apps_search_hint"
-                android:textSize="16sp" />
-
             <com.android.launcher3.ExtendedEditText
                 android:id="@+id/search_box_input"
                 android:layout_width="match_parent"
@@ -90,7 +78,7 @@
                 android:scrollHorizontally="true"
                 android:singleLine="true"
                 android:textColor="#4c4c4c"
-                android:contentDescription="@string/all_apps_search_bar_hint"
+                android:hint="@string/all_apps_search_bar_hint"
                 android:textColorHint="@drawable/all_apps_search_hint"
                 android:textSize="16sp" />
         </FrameLayout>
diff --git a/res/values-ms-rMY/strings.xml b/res/values-ms-rMY/strings.xml
index 9c7c609..073a9db 100644
--- a/res/values-ms-rMY/strings.xml
+++ b/res/values-ms-rMY/strings.xml
@@ -51,7 +51,7 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Persediaan"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Ini ialah apl sistem dan tidak boleh dinyahpasang."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Folder Tanpa Nama"</string>
-    <string name="disabled_app_label" msgid="6673129024321402780">"Melumpuhkan <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> dilumpuhkan"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Halaman %1$d daripada %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Skrin Laman Utama %1$d daripada %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Halaman skrin utama baharu"</string>
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index c9d5cff..15f47b4 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -378,12 +378,32 @@
                 padding.set(desiredWorkspaceLeftRightMarginPx,
                         topWorkspacePadding,
                         desiredWorkspaceLeftRightMarginPx,
-                        hotseatBarHeightPx + pageIndicatorHeightPx);
+                        paddingBottom);
             }
         }
         return padding;
     }
 
+    /**
+     * @return the bounds for which the open folders should be contained within
+     */
+    public Rect getAbsoluteOpenFolderBounds() {
+        if (isVerticalBarLayout()) {
+            // Folders should only appear right of the drop target bar and left of the hotseat
+            return new Rect(mInsets.left + dropTargetBarSizePx + edgeMarginPx,
+                    mInsets.top,
+                    mInsets.left + availableWidthPx - hotseatBarHeightPx - edgeMarginPx,
+                    mInsets.top + availableHeightPx);
+        } else {
+            // Folders should only appear below the drop target bar and above the hotseat
+            return new Rect(mInsets.left,
+                    mInsets.top + dropTargetBarSizePx + edgeMarginPx,
+                    mInsets.left + availableWidthPx,
+                    mInsets.top + availableHeightPx - hotseatBarHeightPx - pageIndicatorHeightPx -
+                            edgeMarginPx);
+        }
+    }
+
     private int getWorkspacePageSpacing() {
         if (isVerticalBarLayout() || isLargeTablet) {
             // In landscape mode the page spacing is set to the default.
diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
index 5c7e670..1fe0813 100644
--- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java
+++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
@@ -250,7 +250,7 @@
 
         final View contentView = toView.getContentView();
         playCommonTransitionAnimations(toWorkspaceState, fromView, toView,
-                animated, initialized, animation, revealDuration, layerViews);
+                animated, initialized, animation, layerViews);
         if (!animated || !initialized) {
             if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP &&
                     toWorkspaceState == Workspace.State.NORMAL_HIDDEN) {
@@ -414,11 +414,22 @@
 
             return animation;
         } else if (animType == PULLUP) {
+            // We are animating the content view alpha, so ensure we have a layer for it
+            layerViews.put(contentView, BUILD_AND_SET_LAYER);
+
             animation.addListener(new AnimatorListenerAdapter() {
                   @Override
                   public void onAnimationEnd(Animator animation) {
                       dispatchOnLauncherTransitionEnd(fromView, animated, false);
                       dispatchOnLauncherTransitionEnd(toView, animated, false);
+
+                      // Disable all necessary layers
+                      for (View v : layerViews.keySet()) {
+                          if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
+                              v.setLayerType(View.LAYER_TYPE_NONE, null);
+                          }
+                      }
+
                       cleanupAnimation();
                       pCb.onTransitionComplete();
                   }
@@ -435,9 +446,20 @@
                     // we waited for a layout/draw pass
                     if (mCurrentAnimation != stateAnimation)
                         return;
+
                     dispatchOnLauncherTransitionStart(fromView, animated, false);
                     dispatchOnLauncherTransitionStart(toView, animated, false);
 
+                    // Enable all necessary layers
+                    for (View v : layerViews.keySet()) {
+                        if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
+                            v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+                        }
+                        if (Utilities.ATLEAST_LOLLIPOP && v.isAttachedToWindow()) {
+                            v.buildLayer();
+                        }
+                    }
+
                     toView.requestFocus();
                     stateAnimation.start();
                 }
@@ -453,7 +475,7 @@
      */
     private void playCommonTransitionAnimations(
             Workspace.State toWorkspaceState, View fromView, View toView,
-            boolean animated, boolean initialized, AnimatorSet animation, int revealDuration,
+            boolean animated, boolean initialized, AnimatorSet animation,
             HashMap<View, Integer> layerViews) {
         // Create the workspace animation.
         // NOTE: this call apparently also sets the state for the workspace if !animated
@@ -580,7 +602,7 @@
         boolean multiplePagesVisible = toWorkspaceState.hasMultipleVisiblePages;
 
         playCommonTransitionAnimations(toWorkspaceState, fromWorkspace, null,
-                animated, animated, animation, revealDuration, layerViews);
+                animated, animated, animation, layerViews);
 
         if (animated) {
             dispatchOnLauncherTransitionPrepare(fromWorkspace, animated, multiplePagesVisible);
@@ -661,6 +683,8 @@
                 res.getInteger(R.integer.config_overlayItemsAlphaStagger);
 
         final View toView = mLauncher.getWorkspace();
+        final View revealView = fromView.getRevealView();
+        final View contentView = fromView.getContentView();
 
         final HashMap<View, Integer> layerViews = new HashMap<>();
 
@@ -673,7 +697,7 @@
         boolean multiplePagesVisible = toWorkspaceState.hasMultipleVisiblePages;
 
         playCommonTransitionAnimations(toWorkspaceState, fromView, toView,
-                animated, initialized, animation, revealDuration, layerViews);
+                animated, initialized, animation, layerViews);
         if (!animated || !initialized) {
             if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP &&
                     fromWorkspaceState == Workspace.State.NORMAL_HIDDEN) {
@@ -695,9 +719,6 @@
             return null;
         }
         if (animType == CIRCULAR_REVEAL) {
-            final View revealView = fromView.getRevealView();
-            final View contentView = fromView.getContentView();
-
             // hideAppsCustomizeHelper is called in some cases when it is already hidden
             // don't perform all these no-op animations. In particularly, this was causing
             // the all-apps button to pop in and out.
@@ -864,6 +885,9 @@
 
             return animation;
         } else if (animType == PULLUP) {
+            // We are animating the content view alpha, so ensure we have a layer for it
+            layerViews.put(contentView, BUILD_AND_SET_LAYER);
+
             animation.addListener(new AnimatorListenerAdapter() {
                 boolean canceled = false;
                 @Override
@@ -876,10 +900,19 @@
                     if (canceled) return;
                     dispatchOnLauncherTransitionEnd(fromView, animated, false);
                     dispatchOnLauncherTransitionEnd(toView, animated, false);
+
                     // Run any queued runnables
                     if (onCompleteRunnable != null) {
                         onCompleteRunnable.run();
                     }
+
+                    // Disable all necessary layers
+                    for (View v : layerViews.keySet()) {
+                        if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
+                            v.setLayerType(View.LAYER_TYPE_NONE, null);
+                        }
+                    }
+
                     cleanupAnimation();
                     pCb.onTransitionComplete();
                 }
@@ -898,9 +931,20 @@
                     // we waited for a layout/draw pass
                     if (mCurrentAnimation != stateAnimation)
                         return;
+
                     dispatchOnLauncherTransitionStart(fromView, animated, false);
                     dispatchOnLauncherTransitionStart(toView, animated, false);
 
+                    // Enable all necessary layers
+                    for (View v : layerViews.keySet()) {
+                        if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
+                            v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+                        }
+                        if (Utilities.ATLEAST_LOLLIPOP && v.isAttachedToWindow()) {
+                            v.buildLayer();
+                        }
+                    }
+
                     // Focus the new view
                     toView.requestFocus();
                     stateAnimation.start();
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index c2631b0..598ba74 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -394,24 +394,14 @@
             overviewPanelAlpha.addListener(new AlphaUpdateListener(overviewPanel,
                     accessibilityEnabled));
 
-            // For animation optimations, we may need to provide the Launcher transition
-            // with a set of views on which to force build layers in certain scenarios.
-            overviewPanel.setLayerType(View.LAYER_TYPE_HARDWARE, null);
-            qsbContainer.setLayerType(View.LAYER_TYPE_HARDWARE, null);
-            if (layerViews != null) {
-                // If layerViews is not null, we add these views, and indicate that
-                // the caller can manage layer state.
-                layerViews.put(overviewPanel, LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER);
-                layerViews.put(qsbContainer, LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER);
-
-                layerViews.put(mLauncher.getHotseat(),
-                        LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER);
-                layerViews.put(mWorkspace.getPageIndicator(),
-                        LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER);
-            } else {
-                // Otherwise let the animator handle layer management.
-                overviewPanelAlpha.withLayer();
-            }
+            // For animation optimization, we may need to provide the Launcher transition
+            // with a set of views on which to force build and manage layers in certain scenarios.
+            layerViews.put(overviewPanel, LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER);
+            layerViews.put(qsbContainer, LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER);
+            layerViews.put(mLauncher.getHotseat(),
+                    LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER);
+            layerViews.put(mWorkspace.getPageIndicator(),
+                    LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER);
 
             if (states.workspaceToOverview) {
                 hotseatAlpha.setInterpolator(new DecelerateInterpolator(2));
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index d81300c..428f784 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -22,6 +22,8 @@
 import android.graphics.Rect;
 import android.support.v7.widget.RecyclerView;
 import android.text.Selection;
+import android.text.Spannable;
+import android.text.SpannableString;
 import android.text.SpannableStringBuilder;
 import android.text.method.TextKeyListener;
 import android.util.AttributeSet;
@@ -47,6 +49,7 @@
 import com.android.launcher3.Workspace;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.folder.Folder;
+import com.android.launcher3.graphics.TintedDrawableSpan;
 import com.android.launcher3.keyboard.FocusedItemDecorator;
 import com.android.launcher3.util.ComponentKey;
 
@@ -243,7 +246,6 @@
         }
         mSearchBarController = searchController;
         mSearchBarController.initialize(mApps, mSearchInput, mLauncher, this);
-        mSearchBarController.setHintView(findViewById(R.id.search_hint));
         mAdapter.setSearchController(mSearchBarController);
     }
 
@@ -311,6 +313,16 @@
 
         mSearchContainer = findViewById(R.id.search_container);
         mSearchInput = (ExtendedEditText) findViewById(R.id.search_box_input);
+
+        // Update the hint to contain the icon.
+        // Prefix the original hint with two spaces. The first space gets replaced by the icon
+        // using span. The second space is used for a singe space character between the hint
+        // and the icon.
+        SpannableString spanned = new SpannableString("  " + mSearchInput.getHint());
+        spanned.setSpan(new TintedDrawableSpan(getContext(), R.drawable.ic_allapps_search),
+                0, 1, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
+        mSearchInput.setHint(spanned);
+
         mSearchContainerOffsetTop = getResources().getDimensionPixelSize(
                 R.dimen.all_apps_search_bar_margin_top);
 
diff --git a/src/com/android/launcher3/allapps/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/AllAppsSearchBarController.java
index a99139c..e75210b 100644
--- a/src/com/android/launcher3/allapps/AllAppsSearchBarController.java
+++ b/src/com/android/launcher3/allapps/AllAppsSearchBarController.java
@@ -39,8 +39,7 @@
  * An interface to a search box that AllApps can command.
  */
 public abstract class AllAppsSearchBarController
-        implements TextWatcher, OnEditorActionListener, ExtendedEditText.OnBackKeyListener,
-        View.OnFocusChangeListener {
+        implements TextWatcher, OnEditorActionListener, ExtendedEditText.OnBackKeyListener {
 
     protected Launcher mLauncher;
     protected AlphabeticalAppsList mApps;
@@ -51,8 +50,6 @@
     protected DefaultAppSearchAlgorithm mSearchAlgorithm;
     protected InputMethodManager mInputMethodManager;
 
-    protected View mHintView;
-
     public void setVisibility(int visibility) {
         mInput.setVisibility(visibility);
     }
@@ -70,7 +67,6 @@
         mInput.addTextChangedListener(this);
         mInput.setOnEditorActionListener(this);
         mInput.setOnBackKeyListener(this);
-        mInput.setOnFocusChangeListener(this);
 
         mInputMethodManager = (InputMethodManager)
                 mInput.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
@@ -140,27 +136,6 @@
         return false;
     }
 
-    @Override
-    public void onFocusChange(View view, boolean focused) {
-        if (mHintView != null) {
-            mHintView.setVisibility(focused ? View.INVISIBLE : View.VISIBLE);
-        }
-    }
-
-    /**
-     * Sets a view to serve as the search field's hint.
-     */
-    public void setHintView(View hintView) {
-        mHintView = hintView;
-    }
-
-    /**
-     * Returns the search field's hint view.
-     */
-    public View getHintView() {
-        return mHintView;
-    }
-
     /**
      * Resets the search bar state.
      */
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 2fbbad5..fdbb214 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -1030,27 +1030,25 @@
     }
 
     private void centerAboutIcon() {
-        DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
+        DeviceProfile grid = mLauncher.getDeviceProfile();
 
+        DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
         DragLayer parent = (DragLayer) mLauncher.findViewById(R.id.drag_layer);
         int width = getPaddingLeft() + getPaddingRight() + mContent.getDesiredWidth();
         int height = getFolderHeight();
 
         float scale = parent.getDescendantRectRelativeToSelf(mFolderIcon, sTempRect);
-
-        DeviceProfile grid = mLauncher.getDeviceProfile();
-
-        int centerX = (int) (sTempRect.left + sTempRect.width() * scale / 2);
-        int centerY = (int) (sTempRect.top + sTempRect.height() * scale / 2);
+        int centerX = sTempRect.centerX();
+        int centerY = sTempRect.centerY();
         int centeredLeft = centerX - width / 2;
         int centeredTop = centerY - height / 2;
 
         // We need to bound the folder to the currently visible workspace area
         mLauncher.getWorkspace().getPageAreaRelativeToDragLayer(sTempRect);
         int left = Math.min(Math.max(sTempRect.left, centeredLeft),
-                sTempRect.left + sTempRect.width() - width);
+                sTempRect.right- width);
         int top = Math.min(Math.max(sTempRect.top, centeredTop),
-                sTempRect.top + sTempRect.height() - height);
+                sTempRect.bottom - height);
 
         int distFromEdgeOfScreen = mLauncher.getWorkspace().getPaddingLeft() + getPaddingLeft();
 
@@ -1064,7 +1062,14 @@
             left = sTempRect.left + (sTempRect.width() - width) / 2;
         }
         if (height >= sTempRect.height()) {
+            // Folder height is greater than page height, center on page
             top = sTempRect.top + (sTempRect.height() - height) / 2;
+        } else {
+            // Folder height is less than page height, so bound it to the absolute open folder
+            // bounds if necessary
+            Rect folderBounds = grid.getAbsoluteOpenFolderBounds();
+            left = Math.max(folderBounds.left, Math.min(left, folderBounds.right - width));
+            top = Math.max(folderBounds.top, Math.min(top, folderBounds.bottom - height));
         }
 
         int folderPivotX = width / 2 + (centeredLeft - left);
diff --git a/src/com/android/launcher3/graphics/TintedDrawableSpan.java b/src/com/android/launcher3/graphics/TintedDrawableSpan.java
new file mode 100644
index 0000000..f72ce03
--- /dev/null
+++ b/src/com/android/launcher3/graphics/TintedDrawableSpan.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 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.graphics;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Paint.FontMetricsInt;
+import android.graphics.drawable.Drawable;
+import android.text.style.DynamicDrawableSpan;
+
+/**
+ * {@link DynamicDrawableSpan} which draws a drawable tinted with the current paint color.
+ */
+public class TintedDrawableSpan extends DynamicDrawableSpan {
+
+    private final Drawable mDrawable;
+    private int mOldTint;
+
+    public TintedDrawableSpan(Context context, int resourceId) {
+        super(ALIGN_BOTTOM);
+        mDrawable = context.getDrawable(resourceId);
+        mOldTint = 0;
+    }
+
+    @Override
+    public int getSize(Paint paint, CharSequence text, int start, int end, FontMetricsInt fm) {
+        fm = fm == null ? paint.getFontMetricsInt() : fm;
+        int iconSize = fm.bottom - fm.top;
+        mDrawable.setBounds(0, 0, iconSize, iconSize);
+        return super.getSize(paint, text, start, end, fm);
+    }
+
+    @Override
+    public void draw(Canvas canvas, CharSequence text,
+            int start, int end, float x, int top, int y, int bottom, Paint paint) {
+        int color = paint.getColor();
+        if (mOldTint != color) {
+            mOldTint = color;
+            mDrawable.setTint(mOldTint);
+        }
+        super.draw(canvas, text, start, end, x, top, y, bottom, paint);
+    }
+
+    @Override
+    public Drawable getDrawable() {
+        return mDrawable;
+    }
+}
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java b/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java
index 8091825..c9eee81 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java
@@ -388,6 +388,8 @@
                     break;
                 }
             }
+        } else if (action == MotionEvent.ACTION_CANCEL) {
+            cleanupDeferredDrag();
         }
         return true;
     }
diff --git a/src/com/android/launcher3/shortcuts/ShortcutsContainerListener.java b/src/com/android/launcher3/shortcuts/ShortcutsContainerListener.java
index 962fbac..f3b0d04 100644
--- a/src/com/android/launcher3/shortcuts/ShortcutsContainerListener.java
+++ b/src/com/android/launcher3/shortcuts/ShortcutsContainerListener.java
@@ -56,6 +56,9 @@
     private final int[] mTouchDown;
     private boolean mHasMappedTouchDownToContainerCoord;
 
+    /** If true, the gesture is not handled. The value is reset when next gesture starts. */
+    private boolean mIgnoreCurrentGesture;
+
     public ShortcutsContainerListener(BubbleTextView icon) {
         mSrcIcon = icon;
         mScaledTouchSlop = ViewConfiguration.get(icon.getContext()).getScaledTouchSlop();
@@ -72,18 +75,22 @@
 
     @Override
     public boolean onTouch(View v, MotionEvent event) {
-        if (mLauncher.getShortcutIdsForItem((ItemInfo) v.getTag()).isEmpty()) {
-            // There are no shortcuts associated with this item, so return to normal touch handling.
-            return false;
-        }
-
         if (event.getAction() == MotionEvent.ACTION_DOWN) {
+            // There are no shortcuts associated with this item,
+            // so return to normal touch handling.
+            mIgnoreCurrentGesture =
+                    (mLauncher.getShortcutIdsForItem((ItemInfo) v.getTag())).isEmpty();
+
             mTouchDown[0] = (int) event.getX();
             mTouchDown[1] = (int) event.getY();
             mDragLayer.getDescendantCoordRelativeToSelf(mSrcIcon, mTouchDown);
             mHasMappedTouchDownToContainerCoord = false;
         }
 
+        if (mIgnoreCurrentGesture) {
+            return false;
+        }
+
         final boolean wasForwarding = mForwarding;
         final boolean forwarding;
         if (wasForwarding) {