Merge "Fix crash when trying to load SystemShortcut drawable." into ub-launcher3-master
diff --git a/go/quickstep/src/com/android/quickstep/TaskAdapter.java b/go/quickstep/src/com/android/quickstep/TaskAdapter.java
index d3fd240..99446d0 100644
--- a/go/quickstep/src/com/android/quickstep/TaskAdapter.java
+++ b/go/quickstep/src/com/android/quickstep/TaskAdapter.java
@@ -35,17 +35,23 @@
     private static final int MAX_TASKS_TO_DISPLAY = 6;
     private static final String TAG = "TaskAdapter";
     private final TaskListLoader mLoader;
+    private TaskInputController mInputController;
 
     public TaskAdapter(@NonNull TaskListLoader loader) {
         mLoader = loader;
     }
 
+    public void setInputController(TaskInputController inputController) {
+        mInputController = inputController;
+    }
+
     @Override
     public TaskHolder onCreateViewHolder(ViewGroup parent, int viewType) {
-        // TODO: Swap in an actual task view here (view w/ icon, label, etc.)
         TaskItemView itemView = (TaskItemView) LayoutInflater.from(parent.getContext())
                 .inflate(R.layout.task_item_view, parent, false);
-        return new TaskHolder(itemView);
+        TaskHolder holder = new TaskHolder(itemView);
+        itemView.setOnClickListener(view -> mInputController.onTaskClicked(holder));
+        return holder;
     }
 
     @Override
diff --git a/go/quickstep/src/com/android/quickstep/TaskHolder.java b/go/quickstep/src/com/android/quickstep/TaskHolder.java
index 47398c8..67e8ece 100644
--- a/go/quickstep/src/com/android/quickstep/TaskHolder.java
+++ b/go/quickstep/src/com/android/quickstep/TaskHolder.java
@@ -15,6 +15,7 @@
  */
 package com.android.quickstep;
 
+import androidx.annotation.NonNull;
 import androidx.recyclerview.widget.RecyclerView.ViewHolder;
 
 import com.android.quickstep.views.TaskItemView;
@@ -27,6 +28,7 @@
 final class TaskHolder extends ViewHolder {
 
     private final TaskItemView mTaskItemView;
+    private Task mTask;
 
     public TaskHolder(TaskItemView itemView) {
         super(itemView);
@@ -40,7 +42,17 @@
      * @param task the task to bind to the view
      */
     public void bindTask(Task task) {
+        mTask = task;
         mTaskItemView.setLabel(task.titleDescription);
         mTaskItemView.setIcon(task.icon);
     }
+
+    /**
+     * Gets the task currently bound to this view
+     *
+     * @return the current task
+     */
+    public @NonNull Task getTask() {
+        return mTask;
+    }
 }
diff --git a/go/quickstep/src/com/android/quickstep/TaskInputController.java b/go/quickstep/src/com/android/quickstep/TaskInputController.java
new file mode 100644
index 0000000..66c4496
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/TaskInputController.java
@@ -0,0 +1,45 @@
+/*
+ * 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 com.android.systemui.shared.system.ActivityManagerWrapper;
+
+/**
+ * Controller responsible for task logic that occurs on various input to the recents view.
+ */
+public final class TaskInputController {
+
+    TaskAdapter mAdapter;
+
+    public TaskInputController(TaskAdapter adapter) {
+        mAdapter = adapter;
+    }
+
+    /**
+     * Logic that occurs when a task view is tapped. Launches the respective task.
+     *
+     * @param viewHolder the task view holder that has been tapped
+     */
+    public void onTaskClicked(TaskHolder viewHolder) {
+        // TODO: Add app launch animation as part of the launch options here.
+        ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(viewHolder.getTask().key,
+                null /* options */, null /* resultCallback */, null /* resultCallbackHandler */);
+    }
+
+    // TODO: Implement swipe to delete and notify adapter that data has updated
+
+    // TODO: Implement "Clear all" and notify adapter that data has updated
+}
diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
index afb0540..e8a915f 100644
--- a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
+++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
@@ -28,6 +28,7 @@
 
 import com.android.launcher3.R;
 import com.android.quickstep.TaskAdapter;
+import com.android.quickstep.TaskInputController;
 import com.android.quickstep.TaskListLoader;
 
 /**
@@ -79,6 +80,7 @@
     private float mTranslationYFactor;
     private TaskAdapter mTaskAdapter;
     private RecyclerView mTaskRecyclerView;
+    private TaskInputController mTaskInputController;
     private TaskListLoader mTaskLoader;
 
     public IconRecentsView(Context context, AttributeSet attrs) {
@@ -91,6 +93,8 @@
         super.onFinishInflate();
         mTaskLoader = new TaskListLoader(mContext);
         mTaskAdapter = new TaskAdapter(mTaskLoader);
+        mTaskInputController = new TaskInputController(mTaskAdapter);
+        mTaskAdapter.setInputController(mTaskInputController);
         mTaskRecyclerView = findViewById(R.id.recent_task_recycler_view);
         mTaskRecyclerView.setAdapter(mTaskAdapter);
         mTaskRecyclerView.setLayoutManager(
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java
index e3afb92..4e010d2 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java
@@ -230,7 +230,6 @@
                             mTouchSlop) {
                         mPassedTouchSlop = true;
 
-                        TOUCH_INTERACTION_LOG.addLog("startQuickstep");
                         if (mIsDeferredDownTarget) {
                             // Deferred gesture, start the animation and gesture tracking once
                             // we pass the actual touch slop
@@ -272,6 +271,7 @@
     }
 
     private void notifyGestureStarted() {
+        TOUCH_INTERACTION_LOG.addLog("startQuickstep");
         if (mInteractionHandler == null) {
             return;
         }
@@ -310,6 +310,7 @@
         if (listenerSet != null) {
             listenerSet.addListener(handler);
             mSwipeSharedState.applyActiveRecentsAnimationState(handler);
+            notifyGestureStarted();
         } else {
             RecentsAnimationListenerSet newListenerSet =
                     mSwipeSharedState.newRecentsAnimationListenerSet();
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
index 7dc58a5..7bea593 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -415,6 +415,7 @@
         });
         mRecentsView.setRecentsAnimationWrapper(mRecentsAnimationWrapper);
         mRecentsView.setClipAnimationHelper(mClipAnimationHelper);
+        mRecentsView.setLiveTileOverlay(mLiveTileOverlay);
         mActivity.getRootView().getOverlay().add(mLiveTileOverlay);
 
         mStateCallback.setState(STATE_LAUNCHER_PRESENT);
@@ -822,6 +823,7 @@
             setShelfState(ShelfAnimState.CANCEL, LINEAR, 0);
             duration = Math.max(MIN_OVERSHOOT_DURATION, duration);
         } else if (endTarget == RECENTS) {
+            mLiveTileOverlay.startIconAnimation();
             mRecentsAnimationWrapper.enableInputProxy();
             if (mRecentsView != null) {
                 duration = Math.max(duration, mRecentsView.getScroller().getDuration());
@@ -1172,7 +1174,7 @@
         mActivityControlHelper.onSwipeUpComplete(mActivity);
 
         // Animate the first icon.
-        mRecentsView.animateUpRunningTaskIconScale();
+        mRecentsView.animateUpRunningTaskIconScale(mLiveTileOverlay.cancelIconAnimation());
         mRecentsView.setSwipeDownShouldLaunchApp(true);
 
         RecentsModel.INSTANCE.get(mContext).onOverviewShown(false, TAG);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java
index 0aa1beb..19e9cb4 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java
@@ -23,6 +23,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.LauncherApps;
+import android.content.pm.LauncherApps.AppUsageLimit;
 import android.content.res.Resources;
 import android.icu.text.MeasureFormat;
 import android.icu.text.MeasureFormat.FormatWidth;
@@ -44,13 +45,13 @@
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.systemui.shared.recents.model.Task;
 
-import java.lang.reflect.Method;
 import java.time.Duration;
 import java.util.Locale;
 
 public final class DigitalWellBeingToast extends LinearLayout {
     static final Intent OPEN_APP_USAGE_SETTINGS_TEMPLATE = new Intent(ACTION_APP_USAGE_SETTINGS);
     static final int MINUTE_MS = 60000;
+    private final LauncherApps mLauncherApps;
 
     public interface InitializeCallback {
         void call(float saturation, String contentDescription);
@@ -67,6 +68,7 @@
         setLayoutDirection(Utilities.isRtl(getResources()) ?
                 View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
         setOnClickListener((view) -> openAppUsageSettings());
+        mLauncherApps = context.getSystemService(LauncherApps.class);
     }
 
     @Override
@@ -87,47 +89,29 @@
         }
 
         Utilities.THREAD_POOL_EXECUTOR.execute(() -> {
-            long appUsageLimitTimeMs = -1;
-            long appRemainingTimeMs = -1;
+            final AppUsageLimit usageLimit = mLauncherApps.getAppUsageLimit(
+                    task.getTopComponent().getPackageName(),
+                    UserHandle.of(task.key.userId));
 
-            try {
-                final Method getAppUsageLimit = LauncherApps.class.getMethod(
-                        "getAppUsageLimit",
-                        String.class,
-                        UserHandle.class);
-                final Object usageLimit = getAppUsageLimit.invoke(
-                        getContext().getSystemService(LauncherApps.class),
-                        task.getTopComponent().getPackageName(),
-                        UserHandle.of(task.key.userId));
-
-                if (usageLimit != null) {
-                    final Class appUsageLimitClass = usageLimit.getClass();
-                    appUsageLimitTimeMs = (long) appUsageLimitClass.getMethod("getTotalUsageLimit").
-                            invoke(usageLimit);
-                    appRemainingTimeMs = (long) appUsageLimitClass.getMethod("getUsageRemaining").
-                            invoke(usageLimit);
-                }
-            } catch (Exception e) {
-                // Do nothing
-            }
-
-            final long appUsageLimitTimeMsFinal = appUsageLimitTimeMs;
-            final long appRemainingTimeMsFinal = appRemainingTimeMs;
+            final long appUsageLimitTimeMs =
+                    usageLimit != null ? usageLimit.getTotalUsageLimit() : -1;
+            final long appRemainingTimeMs =
+                    usageLimit != null ? usageLimit.getUsageRemaining() : -1;
 
             post(() -> {
-                if (appUsageLimitTimeMsFinal < 0) {
+                if (appUsageLimitTimeMs < 0) {
                     setVisibility(GONE);
                 } else {
                     setVisibility(VISIBLE);
-                    mText.setText(getText(appRemainingTimeMsFinal));
-                    mImage.setImageResource(appRemainingTimeMsFinal > 0 ?
+                    mText.setText(getText(appRemainingTimeMs));
+                    mImage.setImageResource(appRemainingTimeMs > 0 ?
                             R.drawable.hourglass_top : R.drawable.hourglass_bottom);
                 }
 
                 callback.call(
-                        appUsageLimitTimeMsFinal >= 0 && appRemainingTimeMsFinal <= 0 ? 0 : 1,
+                        appUsageLimitTimeMs >= 0 && appRemainingTimeMs <= 0 ? 0 : 1,
                         getContentDescriptionForTask(
-                                task, appUsageLimitTimeMsFinal, appRemainingTimeMsFinal));
+                                task, appUsageLimitTimeMs, appRemainingTimeMs));
             });
         });
     }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LiveTileOverlay.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LiveTileOverlay.java
index ab2b90f..a838797 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LiveTileOverlay.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LiveTileOverlay.java
@@ -1,5 +1,11 @@
 package com.android.quickstep.views;
 
+import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
 import android.graphics.Paint;
@@ -9,16 +15,37 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
+import android.util.FloatProperty;
+
+import com.android.launcher3.anim.Interpolators;
 
 public class LiveTileOverlay extends Drawable {
 
+    private static final long ICON_ANIM_DURATION = 120;
+
+    private static final FloatProperty<LiveTileOverlay> PROGRESS =
+            new FloatProperty<LiveTileOverlay>("progress") {
+                @Override
+                public void setValue(LiveTileOverlay liveTileOverlay, float progress) {
+                    liveTileOverlay.setIconAnimationProgress(progress);
+                }
+
+                @Override
+                public Float get(LiveTileOverlay liveTileOverlay) {
+                    return liveTileOverlay.mIconAnimationProgress;
+                }
+            };
+
     private final Paint mPaint = new Paint();
 
     private Rect mBoundsRect = new Rect();
     private RectF mCurrentRect;
     private float mCornerRadius;
+    private Drawable mIcon;
+    private Animator mIconAnimator;
 
     private boolean mDrawEnabled = true;
+    private float mIconAnimationProgress = 0f;
 
     public LiveTileOverlay() {
         mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
@@ -35,6 +62,33 @@
         invalidateSelf();
     }
 
+    public void setIcon(Drawable icon) {
+        mIcon = icon;
+    }
+
+    public void startIconAnimation() {
+        if (mIconAnimator != null) {
+            mIconAnimator.cancel();
+        }
+        // This animator must match the icon part of {@link TaskView#FOCUS_TRANSITION} animation.
+        mIconAnimator = ObjectAnimator.ofFloat(this, PROGRESS, 1);
+        mIconAnimator.setDuration(ICON_ANIM_DURATION).setInterpolator(LINEAR);
+        mIconAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mIconAnimator = null;
+            }
+        });
+        mIconAnimator.start();
+    }
+
+    public float cancelIconAnimation() {
+        if (mIconAnimator != null) {
+            mIconAnimator.cancel();
+        }
+        return mIconAnimationProgress;
+    }
+
     public void setDrawEnabled(boolean drawEnabled) {
         if (mDrawEnabled != drawEnabled) {
             mDrawEnabled = drawEnabled;
@@ -46,6 +100,16 @@
     public void draw(Canvas canvas) {
         if (mCurrentRect != null && mDrawEnabled) {
             canvas.drawRoundRect(mCurrentRect, mCornerRadius, mCornerRadius, mPaint);
+            if (mIcon != null && mIconAnimationProgress > 0f) {
+                canvas.save();
+                float scale = Interpolators.clampToProgress(FAST_OUT_SLOW_IN, 0f,
+                        1f).getInterpolation(mIconAnimationProgress);
+                canvas.translate(mCurrentRect.centerX() - mIcon.getBounds().width() / 2 * scale,
+                        mCurrentRect.top - mIcon.getBounds().height() / 2 * scale);
+                canvas.scale(scale, scale);
+                mIcon.draw(canvas);
+                canvas.restore();
+            }
         }
     }
 
@@ -59,4 +123,9 @@
     public int getOpacity() {
         return PixelFormat.TRANSLUCENT;
     }
+
+    private void setIconAnimationProgress(float progress) {
+        mIconAnimationProgress = progress;
+        invalidateSelf();
+    }
 }
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 3e0e8ae..2583ffb 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
@@ -278,6 +278,7 @@
     private final int mEmptyMessagePadding;
     private boolean mShowEmptyMessage;
     private Layout mEmptyTextLayout;
+    private LiveTileOverlay mLiveTileOverlay;
 
     private BaseActivity.MultiWindowModeChangedListener mMultiWindowModeChangedListener =
             (inMultiWindowMode) -> {
@@ -855,10 +856,15 @@
     }
 
     public void animateUpRunningTaskIconScale() {
+        animateUpRunningTaskIconScale(0);
+    }
+
+    public void animateUpRunningTaskIconScale(float startProgress) {
         mRunningTaskIconScaledDown = false;
         TaskView firstTask = getRunningTaskView();
         if (firstTask != null) {
             firstTask.animateIconScaleAndDimIntoView();
+            firstTask.setIconScaleAnimStartProgress(startProgress);
         }
     }
 
@@ -1567,6 +1573,14 @@
         mClipAnimationHelper = clipAnimationHelper;
     }
 
+    public void setLiveTileOverlay(LiveTileOverlay liveTileOverlay) {
+        mLiveTileOverlay = liveTileOverlay;
+    }
+
+    public void updateLiveTileIcon(Drawable icon) {
+        mLiveTileOverlay.setIcon(icon);
+    }
+
     public void finishRecentsAnimation(boolean toRecents, Runnable onFinishComplete) {
         if (mRecentsAnimationWrapper == null) {
             if (onFinishComplete != null) {
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 419a666..9eec584 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
@@ -156,7 +156,8 @@
     private float mZoomScale;
     private float mFullscreenProgress;
 
-    private Animator mIconAndDimAnimator;
+    private ObjectAnimator mIconAndDimAnimator;
+    private float mIconScaleAnimStartProgress = 0;
     private float mFocusTransitionProgress = 1;
 
     private boolean mShowScreenshot;
@@ -317,6 +318,9 @@
             mIconLoadRequest = iconCache.updateIconInBackground(mTask,
                     (task) -> {
                         setIcon(task.icon);
+                        if (isRunningTask()) {
+                            getRecentsView().updateLiveTileIcon(task.icon);
+                        }
                         mDigitalWellBeingToast.initialize(
                                 mTask,
                                 (saturation, contentDescription) -> {
@@ -380,11 +384,16 @@
         mIconView.setScaleY(scale);
     }
 
+    public void setIconScaleAnimStartProgress(float startProgress) {
+        mIconScaleAnimStartProgress = startProgress;
+    }
+
     public void animateIconScaleAndDimIntoView() {
         if (mIconAndDimAnimator != null) {
             mIconAndDimAnimator.cancel();
         }
         mIconAndDimAnimator = ObjectAnimator.ofFloat(this, FOCUS_TRANSITION, 1);
+        mIconAndDimAnimator.setCurrentFraction(mIconScaleAnimStartProgress);
         mIconAndDimAnimator.setDuration(DIM_ANIM_DURATION).setInterpolator(LINEAR);
         mIconAndDimAnimator.addListener(new AnimatorListenerAdapter() {
             @Override