Merge "Not precalculating launcher package in Tapl" into ub-launcher3-master
diff --git a/quickstep/res/layout/task.xml b/quickstep/res/layout/task.xml
index 43ae9b8..8f43192 100644
--- a/quickstep/res/layout/task.xml
+++ b/quickstep/res/layout/task.xml
@@ -42,14 +42,8 @@
         android:importantForAccessibility="noHideDescendants"
         android:background="#800000FF"
         android:layout_gravity="bottom"
+        android:gravity="center"
+        android:textColor="@android:color/white"
         android:visibility="gone"
-    >
-        <TextView
-            android:id="@+id/remaining_time"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center_vertical"
-            android:textColor="@android:color/white"
         />
-    </com.android.quickstep.views.DigitalWellBeingToast>
 </com.android.quickstep.views.TaskView>
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BackgroundAppState.java b/quickstep/src/com/android/launcher3/uioverrides/BackgroundAppState.java
index fdb13b1..8d28f33 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BackgroundAppState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BackgroundAppState.java
@@ -41,7 +41,8 @@
         if (launcher.getDeviceProfile().isVerticalBarLayout()) {
             return super.getVerticalProgress(launcher);
         }
-        int transitionLength = LayoutUtils.getShelfTrackingDistance(launcher.getDeviceProfile());
+        int transitionLength = LayoutUtils.getShelfTrackingDistance(launcher,
+                launcher.getDeviceProfile());
         AllAppsTransitionController controller = launcher.getAllAppsController();
         float scrollRange = Math.max(controller.getShiftRange(), 1);
         float progressDelta = (transitionLength / scrollRange);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
index 8684c58..330dc87 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
@@ -213,7 +213,8 @@
             mCurrentAnimation = AnimatorPlaybackController.wrap(mPendingAnimation.anim, maxAccuracy,
                     onCancelRunnable);
             mLauncher.getStateManager().setCurrentUserControlledAnimation(mCurrentAnimation);
-            totalShift = LayoutUtils.getShelfTrackingDistance(mLauncher.getDeviceProfile());
+            totalShift = LayoutUtils.getShelfTrackingDistance(mLauncher,
+                    mLauncher.getDeviceProfile());
         } else {
             mCurrentAnimation = mLauncher.getStateManager()
                     .createAnimationToNewWorkspace(mToState, builder, maxAccuracy, this::clearState,
diff --git a/quickstep/src/com/android/quickstep/ActivityControlHelper.java b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
index 8293083..1678f06 100644
--- a/quickstep/src/com/android/quickstep/ActivityControlHelper.java
+++ b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
@@ -216,7 +216,7 @@
                 int hotseatInset = dp.isSeascape() ? targetInsets.left : targetInsets.right;
                 return dp.hotseatBarSizePx + hotseatInset;
             } else {
-                return LayoutUtils.getShelfTrackingDistance(dp);
+                return LayoutUtils.getShelfTrackingDistance(context, dp);
             }
         }
 
diff --git a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
index 93f1ee6..9712eb3 100644
--- a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
+++ b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
@@ -383,9 +383,9 @@
             // Deferred gesture, start the animation and gesture tracking once we pass the actual
             // touch slop
             startTouchTrackingForWindowAnimation(ev.getEventTime());
-            mPassedInitialSlop = true;
-            mStartDisplacement = getDisplacement(ev);
         }
+        mPassedInitialSlop = true;
+        mStartDisplacement = getDisplacement(ev);
         notifyGestureStarted();
     }
 
diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
index cb214af..cc49d46 100644
--- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
@@ -16,16 +16,13 @@
 
 package com.android.quickstep;
 
-import android.content.Context;
 import android.graphics.Matrix;
 import android.view.View;
 
-import androidx.annotation.AnyThread;
-
 import com.android.launcher3.BaseActivity;
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.R;
-import com.android.launcher3.util.Preconditions;
+import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.ResourceBasedOverride;
 import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.recents.model.Task;
@@ -34,11 +31,12 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import androidx.annotation.AnyThread;
+
 /**
  * Factory class to create and add an overlays on the TaskView
  */
 public class TaskOverlayFactory implements ResourceBasedOverride {
-    private static TaskOverlayFactory sInstance;
 
     /** Note that these will be shown in order from top to bottom, if available for the task. */
     private static final TaskSystemShortcut[] MENU_OPTIONS = new TaskSystemShortcut[]{
@@ -49,14 +47,9 @@
             new TaskSystemShortcut.Freeform()
     };
 
-    public static TaskOverlayFactory get(Context context) {
-        Preconditions.assertUIThread();
-        if (sInstance == null) {
-            sInstance = Overrides.getObject(TaskOverlayFactory.class,
-                    context.getApplicationContext(), R.string.task_overlay_factory_class);
-        }
-        return sInstance;
-    }
+    public static final MainThreadInitializedObject<TaskOverlayFactory> INSTANCE =
+            new MainThreadInitializedObject<>(c -> Overrides.getObject(TaskOverlayFactory.class,
+                    c, R.string.task_overlay_factory_class));
 
     @AnyThread
     public boolean needAssist() {
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 7a6b135..8b6867f 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -203,7 +203,7 @@
         mEventQueue = new MotionEventQueue(mMainThreadChoreographer, TouchConsumer.NO_OP);
         mOverviewInteractionState = OverviewInteractionState.INSTANCE.get(this);
         mOverviewCallbacks = OverviewCallbacks.get(this);
-        mTaskOverlayFactory = TaskOverlayFactory.get(this);
+        mTaskOverlayFactory = TaskOverlayFactory.INSTANCE.get(this);
         mTouchInteractionLog = new TouchInteractionLog();
         mInputConsumer = InputConsumerController.getRecentsAnimationInputConsumer();
         mInputConsumer.registerInputConsumer();
diff --git a/quickstep/src/com/android/quickstep/util/LayoutUtils.java b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
index 6ca0dce..ed585c1 100644
--- a/quickstep/src/com/android/quickstep/util/LayoutUtils.java
+++ b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
@@ -21,14 +21,15 @@
 import android.content.res.Resources;
 import android.graphics.Rect;
 
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.R;
-
-import java.lang.annotation.Retention;
-
 import androidx.annotation.AnyThread;
 import androidx.annotation.IntDef;
 
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.R;
+import com.android.launcher3.config.FeatureFlags;
+
+import java.lang.annotation.Retention;
+
 public class LayoutUtils {
 
     private static final int MULTI_WINDOW_STRATEGY_HALF_SCREEN = 1;
@@ -112,7 +113,14 @@
                 Math.round(x + outWidth), Math.round(y + outHeight));
     }
 
-    public static int getShelfTrackingDistance(DeviceProfile dp) {
+    public static int getShelfTrackingDistance(Context context, DeviceProfile dp) {
+        if (FeatureFlags.SWIPE_HOME.get()) {
+            // Track the bottom of the window rather than the top of the shelf.
+            int shelfHeight = dp.hotseatBarSizePx + dp.getInsets().bottom;
+            int spaceBetweenShelfAndRecents = (int) context.getResources().getDimension(
+                    R.dimen.task_card_vert_space);
+            return shelfHeight + spaceBetweenShelfAndRecents;
+        }
         // Start from a third of bottom inset to provide some shelf overlap.
         return dp.hotseatBarSizePx + dp.getInsets().bottom / 3 - dp.edgeMarginPx * 2;
     }
diff --git a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
index a92295e..b34d2bf 100644
--- a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
+++ b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
@@ -23,7 +23,6 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.View;
-import android.widget.LinearLayout;
 import android.widget.TextView;
 
 import com.android.launcher3.Launcher;
@@ -32,7 +31,8 @@
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.systemui.shared.recents.model.Task;
 
-public final class DigitalWellBeingToast extends LinearLayout {
+public final class DigitalWellBeingToast extends TextView {
+
     public interface InitializeCallback {
         void call(float saturation, String contentDescription);
     }
@@ -56,12 +56,11 @@
             final long appRemainingTimeMs = -1;
             final boolean isGroupLimit = true;
             post(() -> {
-                final TextView remainingTimeText = findViewById(R.id.remaining_time);
                 if (appUsageLimitTimeMs < 0) {
                     setVisibility(GONE);
                 } else {
                     setVisibility(VISIBLE);
-                    remainingTimeText.setText(getText(appRemainingTimeMs, isGroupLimit));
+                    setText(getText(appRemainingTimeMs, isGroupLimit));
                 }
 
                 callback.call(
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 4cfd8cb..5cbae65 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -81,6 +81,7 @@
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
 import com.android.launcher3.util.PendingAnimation;
 import com.android.launcher3.util.Themes;
+import com.android.launcher3.util.ViewPool;
 import com.android.quickstep.OverviewCallbacks;
 import com.android.quickstep.QuickScrubController;
 import com.android.quickstep.RecentsAnimationWrapper;
@@ -156,6 +157,8 @@
 
     private final InvariantDeviceProfile mIdp;
 
+    private final ViewPool<TaskView> mTaskViewPool;
+
     /**
      * TODO: Call reloadIdNeeded in onTaskStackChanged.
      */
@@ -304,6 +307,9 @@
                 .inflate(R.layout.overview_clear_all_button, this, false);
         mClearAllButton.setOnClickListener(this::dismissAllTasks);
 
+        mTaskViewPool = new ViewPool<>(context, this, R.layout.task, 20 /* max size */,
+                10 /* initial size */);
+
         mIsRtl = !Utilities.isRtl(getResources());
         setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
         mTaskTopMargin = getResources()
@@ -384,6 +390,7 @@
                 mHasVisibleTaskData.delete(task.key.id);
                 taskView.onTaskListVisibilityChanged(false /* visible */);
             }
+            mTaskViewPool.recycle(taskView);
         }
     }
 
@@ -487,10 +494,6 @@
 
         int oldChildCount = getChildCount();
 
-        // Ensure there are as many views as there are tasks in the stack (adding and trimming as
-        // necessary)
-        final LayoutInflater inflater = LayoutInflater.from(getContext());
-
         // Unload existing visible task data
         unloadVisibleTaskData();
 
@@ -503,7 +506,7 @@
                 removeView(mClearAllButton);
             }
             for (int i = getChildCount(); i < requiredTaskCount; i++) {
-                addView(inflater.inflate(R.layout.task, this, false));
+                addView(mTaskViewPool.getView());
             }
             while (getChildCount() > requiredTaskCount) {
                 removeView(getChildAt(getChildCount() - 1));
@@ -754,8 +757,7 @@
     public void showTask(int runningTaskId) {
         if (getChildCount() == 0) {
             // Add an empty view for now until the task plan is loaded and applied
-            final TaskView taskView = (TaskView) LayoutInflater.from(getContext())
-                    .inflate(R.layout.task, this, false);
+            final TaskView taskView = mTaskViewPool.getView();
             addView(taskView);
             addView(mClearAllButton);
 
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
index e5bed54..8169d73 100644
--- a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -107,7 +107,7 @@
     public TaskThumbnailView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
         mCornerRadius = getResources().getDimension(R.dimen.task_corner_radius);
-        mOverlay = TaskOverlayFactory.get(context).createOverlay(this);
+        mOverlay = TaskOverlayFactory.INSTANCE.get(context).createOverlay(this);
         mPaint.setFilterBitmap(true);
         mBackgroundPaint.setColor(Color.WHITE);
         mClearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
@@ -116,8 +116,12 @@
         mIsDarkTextTheme = Themes.getAttrBoolean(mActivity, R.attr.isWorkspaceDarkText);
     }
 
-    public void bind() {
+    public void bind(Task task) {
         mOverlay.reset();
+        mTask = task;
+        int color = task == null ? Color.BLACK : task.colorBackground | 0xFF000000;
+        mPaint.setColor(color);
+        mBackgroundPaint.setColor(color);
     }
 
     /**
@@ -125,10 +129,6 @@
      */
     public void setThumbnail(Task task, ThumbnailData thumbnailData) {
         mTask = task;
-        int color = task == null ? Color.BLACK : task.colorBackground | 0xFF000000;
-        mPaint.setColor(color);
-        mBackgroundPaint.setColor(color);
-
         if (thumbnailData != null && thumbnailData.thumbnail != null) {
             Bitmap bm = thumbnailData.thumbnail;
             bm.prepareToDraw();
@@ -255,15 +255,14 @@
 
     private void updateThumbnailPaintFilter() {
         int mul = (int) ((1 - mDimAlpha * mDimAlphaMultiplier) * 255);
+        ColorFilter filter = getColorFilter(mul, mIsDarkTextTheme, mSaturation);
+        mBackgroundPaint.setColorFilter(filter);
         mDimmingPaintAfterClearing.setAlpha(255 - mul);
         if (mBitmapShader != null) {
-            ColorFilter filter = getColorFilter(mul, mIsDarkTextTheme, mSaturation);
             mPaint.setColorFilter(filter);
-            mBackgroundPaint.setColorFilter(filter);
         } else {
             mPaint.setColorFilter(null);
             mPaint.setColor(Color.argb(255, mul, mul, mul));
-            mBackgroundPaint.setColorFilter(null);
         }
         invalidate();
     }
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 456a022..ad63c24 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -52,6 +52,7 @@
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
 import com.android.launcher3.util.PendingAnimation;
+import com.android.launcher3.util.ViewPool.Reusable;
 import com.android.quickstep.RecentsModel;
 import com.android.quickstep.TaskIconCache;
 import com.android.quickstep.TaskOverlayFactory;
@@ -70,7 +71,7 @@
 /**
  * A task in the Recents view.
  */
-public class TaskView extends FrameLayout implements PageCallbacks {
+public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
 
     private static final String TAG = TaskView.class.getSimpleName();
 
@@ -214,7 +215,7 @@
      */
     public void bind(Task task) {
         mTask = task;
-        mSnapshotView.bind();
+        mSnapshotView.bind(task);
     }
 
     public Task getTask() {
@@ -399,13 +400,17 @@
         setIconAndDimTransitionProgress(iconScale, invert);
     }
 
-    public void resetVisualProperties() {
+    private void resetViewTransforms() {
         setZoomScale(1);
         setTranslationX(0f);
         setTranslationY(0f);
         setTranslationZ(0);
         setAlpha(1f);
         setIconScaleAndDim(1);
+    }
+
+    public void resetVisualProperties() {
+        resetViewTransforms();
         if (!getRecentsView().getQuickScrubController().isQuickSwitch()) {
             // Reset full screen progress unless we are doing back to back quick switch.
             setFullscreenProgress(0);
@@ -413,6 +418,12 @@
     }
 
     @Override
+    public void onRecycle() {
+        resetViewTransforms();
+        setFullscreenProgress(0);
+    }
+
+    @Override
     public void onPageScroll(ScrollState scrollState) {
         float curveInterpolation =
                 CURVE_INTERPOLATOR.getInterpolation(scrollState.linearInterpolation);
diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java
index 1b1b152..da99142 100644
--- a/src/com/android/launcher3/config/BaseFlags.java
+++ b/src/com/android/launcher3/config/BaseFlags.java
@@ -104,6 +104,10 @@
     public static final TogglableFlag ENABLE_QUICKSTEP_LIVE_TILE = new TogglableFlag(
             "ENABLE_QUICKSTEP_LIVE_TILE", false, "Enable live tile in Quickstep overview");
 
+    public static final ToggleableGlobalSettingsFlag SWIPE_HOME
+            = new ToggleableGlobalSettingsFlag("SWIPE_HOME", false,
+            "[WIP] Swiping up on the nav bar goes home. Swipe and hold goes to recent apps.");
+
     public static void initialize(Context context) {
         // Avoid the disk read for user builds
         if (Utilities.IS_DEBUG_DEVICE) {
diff --git a/src/com/android/launcher3/graphics/DragPreviewProvider.java b/src/com/android/launcher3/graphics/DragPreviewProvider.java
index 75d3425..7eb4015 100644
--- a/src/com/android/launcher3/graphics/DragPreviewProvider.java
+++ b/src/com/android/launcher3/graphics/DragPreviewProvider.java
@@ -36,6 +36,7 @@
 import com.android.launcher3.icons.BitmapRenderer;
 import com.android.launcher3.util.UiThreadHelper;
 import com.android.launcher3.widget.LauncherAppWidgetHostView;
+import com.android.launcher3.widget.PendingAppWidgetHostView;
 
 import java.nio.ByteBuffer;
 
@@ -131,9 +132,15 @@
             width = (int) (mView.getWidth() * scale);
             height = (int) (mView.getHeight() * scale);
 
-            // Use software renderer for widgets as we know that they already work
-            return BitmapRenderer.createSoftwareBitmap(width + blurSizeOutline,
-                    height + blurSizeOutline, (c) -> drawDragView(c, scale));
+            if (mView instanceof PendingAppWidgetHostView) {
+                // Use hardware renderer as the icon for the pending app widget may be a hw bitmap
+                return BitmapRenderer.createHardwareBitmap(width + blurSizeOutline,
+                        height + blurSizeOutline, (c) -> drawDragView(c, scale));
+            } else {
+                // Use software renderer for widgets as we know that they already work
+                return BitmapRenderer.createSoftwareBitmap(width + blurSizeOutline,
+                        height + blurSizeOutline, (c) -> drawDragView(c, scale));
+            }
         }
 
         return BitmapRenderer.createHardwareBitmap(width + blurSizeOutline,
diff --git a/src/com/android/launcher3/util/ViewPool.java b/src/com/android/launcher3/util/ViewPool.java
new file mode 100644
index 0000000..8af048d
--- /dev/null
+++ b/src/com/android/launcher3/util/ViewPool.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2019 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.util;
+
+import android.content.Context;
+import android.os.Handler;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.launcher3.util.ViewPool.Reusable;
+
+import androidx.annotation.AnyThread;
+import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
+
+/**
+ * Utility class to maintain a pool of reusable views.
+ * During initialization, views are inflated on the background thread.
+ */
+public class ViewPool<T extends View & Reusable> {
+
+    private final Object[] mPool;
+
+    private final LayoutInflater mInflater;
+    private final ViewGroup mParent;
+    private final int mLayoutId;
+
+    private int mCurrentSize = 0;
+
+    public ViewPool(Context context, @Nullable ViewGroup parent,
+            int layoutId, int maxSize, int initialSize) {
+        mLayoutId = layoutId;
+        mParent = parent;
+        mInflater = LayoutInflater.from(context);
+        mPool = new Object[maxSize];
+
+        if (initialSize > 0) {
+            initPool(initialSize);
+        }
+    }
+
+    @UiThread
+    private void initPool(int initialSize) {
+        Preconditions.assertUIThread();
+        Handler handler = new Handler();
+
+        // Inflate views on a non looper thread. This allows us to catch errors like calling
+        // "new Handler()" in constructor easily.
+        new Thread(() -> {
+            for (int i = 0; i < initialSize; i++) {
+                T view = inflateNewView();
+                handler.post(() -> addToPool(view));
+            }
+        }).start();
+    }
+
+    @UiThread
+    public void recycle(T view) {
+        Preconditions.assertUIThread();
+        view.onRecycle();
+        addToPool(view);
+    }
+
+    @UiThread
+    private void addToPool(T view) {
+        Preconditions.assertUIThread();
+        if (mCurrentSize >= mPool.length) {
+            // pool is full
+            return;
+        }
+
+        mPool[mCurrentSize] = view;
+        mCurrentSize++;
+    }
+
+    @UiThread
+    public T getView() {
+        Preconditions.assertUIThread();
+        if (mCurrentSize > 0) {
+            mCurrentSize--;
+            return (T) mPool[mCurrentSize];
+        }
+        return inflateNewView();
+    }
+
+    @AnyThread
+    private T inflateNewView() {
+        return (T) mInflater.inflate(mLayoutId, mParent, false);
+    }
+
+    /**
+     * Interface to indicate that a view is reusable
+     */
+    public interface Reusable {
+
+        /**
+         * Called when a view is recycled / added back to the pool
+         */
+        void onRecycle();
+    }
+}