Merge "Slowing down dismiss gesture to reduce flakiness" into ub-launcher3-master
diff --git a/Android.mk b/Android.mk
index 628b632..74b96d4 100644
--- a/Android.mk
+++ b/Android.mk
@@ -319,7 +319,7 @@
 LOCAL_PROGUARD_FLAG_FILES := proguard.flags
 LOCAL_PROGUARD_ENABLED := full
 
-LOCAL_PACKAGE_NAME := Launcher3QuickStepGoIconRecents
+LOCAL_PACKAGE_NAME := Launcher3GoIconRecents
 LOCAL_PRIVILEGED_MODULE := true
 LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3 Launcher3QuickStep
 LOCAL_REQUIRED_MODULES := privapp_whitelist_com.android.launcher3
diff --git a/go/quickstep/src/com/android/quickstep/FallbackActivityControllerHelper.java b/go/quickstep/src/com/android/quickstep/FallbackActivityControllerHelper.java
index 9a18b45..abb9242 100644
--- a/go/quickstep/src/com/android/quickstep/FallbackActivityControllerHelper.java
+++ b/go/quickstep/src/com/android/quickstep/FallbackActivityControllerHelper.java
@@ -20,7 +20,6 @@
 
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
-import android.content.ComponentName;
 import android.graphics.Rect;
 
 import androidx.annotation.Nullable;
@@ -30,7 +29,6 @@
 import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
 import com.android.quickstep.util.RemoteAnimationTargetSet;
 import com.android.quickstep.views.IconRecentsView;
-import com.android.quickstep.views.RecentsView;
 
 import java.util.function.BiPredicate;
 import java.util.function.Consumer;
@@ -43,7 +41,7 @@
 public final class FallbackActivityControllerHelper extends
         GoActivityControlHelper<RecentsActivity> {
 
-    public FallbackActivityControllerHelper(ComponentName homeComponent) { }
+    public FallbackActivityControllerHelper() { }
 
     @Override
     public AnimationFactory prepareRecentsUI(RecentsActivity activity, boolean activityVisible,
@@ -98,7 +96,7 @@
 
     @Nullable
     @Override
-    public RecentsView getVisibleRecentsView() {
+    public IconRecentsView getVisibleRecentsView() {
         RecentsActivity activity = getCreatedActivity();
         if (activity != null && activity.hasWindowFocus()) {
             return activity.getOverviewPanel();
diff --git a/go/quickstep/src/com/android/quickstep/TaskAdapter.java b/go/quickstep/src/com/android/quickstep/TaskAdapter.java
new file mode 100644
index 0000000..77c3f33
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/TaskAdapter.java
@@ -0,0 +1,58 @@
+/*
+ * 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.quickstep;
+
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView.Adapter;
+
+import com.android.systemui.shared.recents.model.Task;
+
+import java.util.ArrayList;
+
+/**
+ * Recycler view adapter that dynamically inflates and binds {@link TaskHolder} instances with the
+ * appropriate {@link Task} from the recents task list.
+ */
+public final class TaskAdapter extends Adapter<TaskHolder> {
+
+    private static final int MAX_TASKS_TO_DISPLAY = 6;
+    private static final String TAG = "TaskAdapter";
+    private final ArrayList<Task> mTaskList;
+
+    public TaskAdapter(@NonNull ArrayList<Task> taskList) {
+        mTaskList = taskList;
+    }
+
+    @Override
+    public TaskHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        // TODO: Swap in an actual task view here (view w/ icon, label, etc.)
+        TextView stubView = new TextView(parent.getContext());
+        return new TaskHolder(stubView);
+    }
+
+    @Override
+    public void onBindViewHolder(TaskHolder holder, int position) {
+        holder.bindTask(mTaskList.get(position));
+    }
+
+    @Override
+    public int getItemCount() {
+        return Math.min(mTaskList.size(), MAX_TASKS_TO_DISPLAY);
+    }
+}
diff --git a/go/quickstep/src/com/android/quickstep/TaskHolder.java b/go/quickstep/src/com/android/quickstep/TaskHolder.java
new file mode 100644
index 0000000..1ea6d76
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/TaskHolder.java
@@ -0,0 +1,48 @@
+/*
+ * 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.quickstep;
+
+import android.widget.TextView;
+
+import androidx.recyclerview.widget.RecyclerView.ViewHolder;
+
+import com.android.systemui.shared.recents.model.Task;
+
+/**
+ * A recycler view holder that holds the task view and binds {@link Task} content (app title, icon,
+ * etc.) to the view.
+ */
+final class TaskHolder extends ViewHolder {
+
+    // TODO: Implement the actual task view to be held.
+    // For now, we just use a simple text view.
+    private final TextView mStubView;
+
+    public TaskHolder(TextView stubView) {
+        super(stubView);
+        mStubView = stubView;
+    }
+
+    /**
+     * Bind task content to the view. This includes the task icon and title as well as binding
+     * input handlers such as which task to launch/remove.
+     *
+     * @param task the task to bind to the view this
+     */
+    public void bindTask(Task task) {
+        mStubView.setText("Stub task view: " + task.titleDescription);
+    }
+}
diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
index e4741e9..00415fe 100644
--- a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
+++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
@@ -21,8 +21,14 @@
 import android.view.ViewDebug;
 import android.widget.FrameLayout;
 
+import com.android.quickstep.TaskAdapter;
+import com.android.systemui.shared.recents.model.Task;
+
+import java.util.ArrayList;
+
 /**
- * Root view for the icon recents view.
+ * Root view for the icon recents view. Acts as the main interface to the rest of the Launcher code
+ * base.
  */
 public final class IconRecentsView extends FrameLayout {
 
@@ -45,6 +51,11 @@
                 @Override
                 public void setValue(IconRecentsView view, float v) {
                     ALPHA.set(view, v);
+                    if (view.getVisibility() != VISIBLE && v > 0) {
+                        view.setVisibility(VISIBLE);
+                    } else if (view.getVisibility() != GONE && v == 0){
+                        view.setVisibility(GONE);
+                    }
                 }
 
                 @Override
@@ -58,12 +69,24 @@
      * is top aligned and 0.5 is centered vertically.
      */
     @ViewDebug.ExportedProperty(category = "launcher")
+
+    // TODO: Write a recents task list observer that creates/updates tasks and signals task adapter.
+    private static final ArrayList<Task> DUMMY_TASK_LIST = new ArrayList<>();
+
     private float mTranslationYFactor;
+    private TaskAdapter mTaskAdapter;
 
     public IconRecentsView(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
 
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mTaskAdapter = new TaskAdapter(DUMMY_TASK_LIST);
+        // TODO: Hook task adapter up to recycler view.
+    }
+
     public void setTranslationYFactor(float translationFactor) {
         mTranslationYFactor = translationFactor;
         setTranslationY(computeTranslationYForFactor(mTranslationYFactor));
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java
index 6d374c6..4450b4b 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java
@@ -185,6 +185,7 @@
                     if (mSupportsRoundedCornersOnWindows) {
                         cornerRadius = Utilities.mapRange(params.progress, mWindowCornerRadius,
                                 mTaskCornerRadius);
+                        mCurrentCornerRadius = cornerRadius;
                     }
                 }
                 alpha = mTaskAlphaCallback.apply(app, params.targetAlpha);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index 8faf95d..a7bf2c3 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
@@ -90,6 +90,7 @@
 import com.android.quickstep.OverviewCallbacks;
 import com.android.quickstep.RecentsAnimationWrapper;
 import com.android.quickstep.RecentsModel;
+import com.android.quickstep.RecentsModel.TaskThumbnailChangeListener;
 import com.android.quickstep.TaskThumbnailCache;
 import com.android.quickstep.TaskUtils;
 import com.android.quickstep.util.ClipAnimationHelper;
@@ -113,7 +114,7 @@
 @TargetApi(Build.VERSION_CODES.P)
 public abstract class RecentsView<T extends BaseActivity> extends PagedView implements Insettable,
         TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback,
-        InvariantDeviceProfile.OnIDPChangeListener {
+        InvariantDeviceProfile.OnIDPChangeListener, TaskThumbnailChangeListener {
 
     private static final String TAG = RecentsView.class.getSimpleName();
 
@@ -171,14 +172,6 @@
      */
     private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
         @Override
-        public void onTaskSnapshotChanged(int taskId, ThumbnailData snapshot) {
-            if (!mHandleTaskStackChanges) {
-                return;
-            }
-            updateThumbnail(taskId, snapshot);
-        }
-
-        @Override
         public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
             if (!mHandleTaskStackChanges) {
                 return;
@@ -262,7 +255,6 @@
 
     private boolean mOverviewStateEnabled;
     private boolean mHandleTaskStackChanges;
-    private Runnable mNextPageSwitchRunnable;
     private boolean mSwipeDownShouldLaunchApp;
     private boolean mTouchDownToStartHome;
     private final int mTouchSlop;
@@ -340,6 +332,19 @@
         return mIsRtl;
     }
 
+    @Override
+    public Task onTaskThumbnailChanged(int taskId, ThumbnailData thumbnailData) {
+        if (mHandleTaskStackChanges) {
+            TaskView taskView = getTaskView(taskId);
+            if (taskView != null) {
+                Task task = taskView.getTask();
+                taskView.getThumbnail().setThumbnail(task, thumbnailData);
+                return task;
+            }
+        }
+        return null;
+    }
+
     public TaskView updateThumbnail(int taskId, ThumbnailData thumbnailData) {
         TaskView taskView = getTaskView(taskId);
         if (taskView != null) {
@@ -371,6 +376,7 @@
         mActivity.addMultiWindowModeChangedListener(mMultiWindowModeChangedListener);
         ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
         mSyncTransactionApplier = new SyncRtSurfaceTransactionApplierCompat(this);
+        RecentsModel.INSTANCE.get(getContext()).addThumbnailChangeListener(this);
         mIdp.addOnChangeListener(this);
     }
 
@@ -382,6 +388,7 @@
         mActivity.removeMultiWindowModeChangedListener(mMultiWindowModeChangedListener);
         ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mTaskStackListener);
         mSyncTransactionApplier = null;
+        RecentsModel.INSTANCE.get(getContext()).removeThumbnailChangeListener(this);
         mIdp.removeOnChangeListener(this);
     }
 
@@ -421,17 +428,9 @@
         updateTaskStackListenerState();
     }
 
-    public void setNextPageSwitchRunnable(Runnable r) {
-        mNextPageSwitchRunnable = r;
-    }
-
     @Override
     protected void onPageEndTransition() {
         super.onPageEndTransition();
-        if (mNextPageSwitchRunnable != null) {
-            mNextPageSwitchRunnable.run();
-            mNextPageSwitchRunnable = null;
-        }
         if (getNextPage() > 0) {
             setSwipeDownShouldLaunchApp(true);
         }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
index 9422141..fb58c24 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
@@ -29,7 +29,6 @@
 import android.animation.TimeInterpolator;
 import android.app.ActivityOptions;
 import android.content.Context;
-import android.content.Intent;
 import android.content.res.Resources;
 import android.graphics.Outline;
 import android.graphics.drawable.Drawable;
@@ -215,6 +214,7 @@
      * Updates this task view to the given {@param task}.
      */
     public void bind(Task task) {
+        cancelPendingLoadTasks();
         mTask = task;
         mSnapshotView.bind(task);
     }
@@ -305,15 +305,15 @@
         if (mTask == null) {
             return;
         }
+        cancelPendingLoadTasks();
         if (visible) {
             // These calls are no-ops if the data is already loaded, try and load the high
             // resolution thumbnail if the state permits
             RecentsModel model = RecentsModel.INSTANCE.get(getContext());
             TaskThumbnailCache thumbnailCache = model.getThumbnailCache();
             TaskIconCache iconCache = model.getIconCache();
-            mThumbnailLoadRequest = thumbnailCache.updateThumbnailInBackground(mTask,
-                    !thumbnailCache.getHighResLoadingState().isEnabled() /* reducedResolution */,
-                    (task) -> mSnapshotView.setThumbnail(task, task.thumbnail));
+            mThumbnailLoadRequest = thumbnailCache.updateThumbnailInBackground(
+                    mTask, thumbnail -> mSnapshotView.setThumbnail(mTask, thumbnail));
             mIconLoadRequest = iconCache.updateIconInBackground(mTask,
                     (task) -> {
                         setIcon(task.icon);
@@ -325,17 +325,22 @@
                                 });
                     });
         } else {
-            if (mThumbnailLoadRequest != null) {
-                mThumbnailLoadRequest.cancel();
-            }
-            if (mIconLoadRequest != null) {
-                mIconLoadRequest.cancel();
-            }
             mSnapshotView.setThumbnail(null, null);
             setIcon(null);
         }
     }
 
+    private void cancelPendingLoadTasks() {
+        if (mThumbnailLoadRequest != null) {
+            mThumbnailLoadRequest.cancel();
+            mThumbnailLoadRequest = null;
+        }
+        if (mIconLoadRequest != null) {
+            mIconLoadRequest.cancel();
+            mIconLoadRequest = null;
+        }
+    }
+
     private boolean showTaskMenu() {
         getRecentsView().snapToPage(getRecentsView().indexOfChild(this));
         mMenuView = TaskMenuView.showForTask(this);
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index f3e1545..81a22a1 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -15,33 +15,29 @@
  */
 package com.android.quickstep;
 
+import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
+
 import android.annotation.TargetApi;
 import android.app.ActivityManager;
 import android.content.ComponentCallbacks2;
 import android.content.Context;
 import android.os.Build;
-import android.os.Bundle;
 import android.os.HandlerThread;
 import android.os.Process;
 import android.os.RemoteException;
 import android.util.Log;
-import android.util.SparseArray;
 
-import com.android.launcher3.MainThreadExecutor;
 import com.android.launcher3.util.MainThreadInitializedObject;
-import com.android.launcher3.util.Preconditions;
 import com.android.systemui.shared.recents.ISystemUiProxy;
 import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.TaskStackChangeListener;
 
 import java.util.ArrayList;
+import java.util.List;
 import java.util.function.Consumer;
 
-import androidx.annotation.WorkerThread;
-
-import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
-
 /**
  * Singleton class to load and manage recents model.
  */
@@ -54,8 +50,8 @@
     public static final MainThreadInitializedObject<RecentsModel> INSTANCE =
             new MainThreadInitializedObject<>(c -> new RecentsModel(c));
 
+    private final List<TaskThumbnailChangeListener> mThumbnailChangeListeners = new ArrayList<>();
     private final Context mContext;
-    private final MainThreadExecutor mMainThreadExecutor;
 
     private ISystemUiProxy mSystemUiProxy;
 
@@ -68,9 +64,6 @@
 
     private RecentsModel(Context context) {
         mContext = context;
-
-        mMainThreadExecutor = new MainThreadExecutor();
-
         HandlerThread loaderThread = new HandlerThread("TaskThumbnailIconCache",
                 Process.THREAD_PRIORITY_BACKGROUND);
         loaderThread.start();
@@ -168,6 +161,18 @@
         });
     }
 
+    @Override
+    public void onTaskSnapshotChanged(int taskId, ThumbnailData snapshot) {
+        mThumbnailCache.updateTaskSnapShot(taskId, snapshot);
+
+        for (int i = mThumbnailChangeListeners.size() - 1; i >= 0; i--) {
+            Task task = mThumbnailChangeListeners.get(i).onTaskThumbnailChanged(taskId, snapshot);
+            if (task != null) {
+                task.thumbnail = snapshot;
+            }
+        }
+    }
+
     public void setSystemUiProxy(ISystemUiProxy systemUiProxy) {
         mSystemUiProxy = systemUiProxy;
     }
@@ -239,4 +244,17 @@
                             + ": ", e);
         }
     }
+
+    public void addThumbnailChangeListener(TaskThumbnailChangeListener listener) {
+        mThumbnailChangeListeners.add(listener);
+    }
+
+    public void removeThumbnailChangeListener(TaskThumbnailChangeListener listener) {
+        mThumbnailChangeListeners.remove(listener);
+    }
+
+    public interface TaskThumbnailChangeListener {
+
+        Task onTaskThumbnailChanged(int taskId, ThumbnailData thumbnailData);
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
index 7a216ed..d05196b 100644
--- a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
+++ b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
@@ -26,6 +26,7 @@
 import com.android.launcher3.icons.cache.HandlerRunnable;
 import com.android.launcher3.util.Preconditions;
 import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.model.Task.TaskKey;
 import com.android.systemui.shared.recents.model.TaskKeyLruCache;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -38,7 +39,7 @@
     private final MainThreadExecutor mMainThreadExecutor;
 
     private final int mCacheSize;
-    private final TaskKeyLruCache<ThumbnailData> mCache;
+    private final ThumbnailCache mCache;
     private final HighResLoadingState mHighResLoadingState;
 
     public static class HighResLoadingState {
@@ -98,7 +99,7 @@
 
         Resources res = context.getResources();
         mCacheSize = res.getInteger(R.integer.recentsThumbnailCacheSize);
-        mCache = new TaskKeyLruCache<>(mCacheSize);
+        mCache = new ThumbnailCache(mCacheSize);
     }
 
     /**
@@ -106,13 +107,20 @@
      */
     public void updateThumbnailInCache(Task task) {
         Preconditions.assertUIThread();
-
         // Fetch the thumbnail for this task and put it in the cache
-        updateThumbnailInBackground(task, true /* reducedResolution */, (t) -> {
-            mCache.put(task.key, t.thumbnail);
-        });
+        if (task.thumbnail == null) {
+            updateThumbnailInBackground(task.key, true /* reducedResolution */,
+                    t -> task.thumbnail = t);
+        }
     }
 
+    /**
+     * Synchronously updates the thumbnail in the cache if it is already there.
+     */
+    public void updateTaskSnapShot(int taskId, ThumbnailData thumbnail) {
+        Preconditions.assertUIThread();
+        mCache.updateIfAlreadyInCache(taskId, thumbnail);
+    }
 
     /**
      * Asynchronously fetches the icon and other task data for the given {@param task}.
@@ -120,22 +128,33 @@
      * @param callback The callback to receive the task after its data has been populated.
      * @return A cancelable handle to the request
      */
-    public ThumbnailLoadRequest updateThumbnailInBackground(Task task, boolean reducedResolution,
-            Consumer<Task> callback) {
+    public ThumbnailLoadRequest updateThumbnailInBackground(
+            Task task, Consumer<ThumbnailData> callback) {
         Preconditions.assertUIThread();
 
+        boolean reducedResolution = !mHighResLoadingState.isEnabled();
         if (task.thumbnail != null && (!task.thumbnail.reducedResolution || reducedResolution)) {
             // Nothing to load, the thumbnail is already high-resolution or matches what the
             // request, so just callback
-            callback.accept(task);
+            callback.accept(task.thumbnail);
             return null;
         }
 
-        ThumbnailData cachedThumbnail = mCache.getAndInvalidateIfModified(task.key);
+
+        return updateThumbnailInBackground(task.key, !mHighResLoadingState.isEnabled(), t -> {
+            task.thumbnail = t;
+            callback.accept(t);
+        });
+    }
+
+    private ThumbnailLoadRequest updateThumbnailInBackground(TaskKey key, boolean reducedResolution,
+            Consumer<ThumbnailData> callback) {
+        Preconditions.assertUIThread();
+
+        ThumbnailData cachedThumbnail = mCache.getAndInvalidateIfModified(key);
         if (cachedThumbnail != null && (!cachedThumbnail.reducedResolution || reducedResolution)) {
             // Already cached, lets use that thumbnail
-            task.thumbnail = cachedThumbnail;
-            callback.accept(task);
+            callback.accept(cachedThumbnail);
             return null;
         }
 
@@ -144,14 +163,14 @@
             @Override
             public void run() {
                 ThumbnailData thumbnail = ActivityManagerWrapper.getInstance().getTaskThumbnail(
-                        task.key.id, reducedResolution);
+                        key.id, reducedResolution);
                 if (isCanceled()) {
                     // We don't call back to the provided callback in this case
                     return;
                 }
                 mMainThreadExecutor.execute(() -> {
-                    task.thumbnail = thumbnail;
-                    callback.accept(task);
+                    mCache.put(key, thumbnail);
+                    callback.accept(thumbnail);
                     onEnd();
                 });
             }
@@ -196,4 +215,21 @@
             this.reducedResolution = reducedResolution;
         }
     }
+
+    private static class ThumbnailCache extends TaskKeyLruCache<ThumbnailData> {
+
+        public ThumbnailCache(int cacheSize) {
+            super(cacheSize);
+        }
+
+        /**
+         * Updates the cache entry if it is already present in the cache
+         */
+        public void updateIfAlreadyInCache(int taskId, ThumbnailData thumbnailData) {
+            ThumbnailData oldData = getCacheEntry(taskId);
+            if (oldData != null) {
+                putCacheEntry(taskId, thumbnailData);
+            }
+        }
+    }
 }