Merge "Smarter task laying out based off onMeasure" into ub-launcher3-master
diff --git a/go/quickstep/res/layout/icon_recents_root_view.xml b/go/quickstep/res/layout/icon_recents_root_view.xml
index 6300882..6dc293f 100644
--- a/go/quickstep/res/layout/icon_recents_root_view.xml
+++ b/go/quickstep/res/layout/icon_recents_root_view.xml
@@ -29,7 +29,6 @@
             android:id="@+id/recent_task_recycler_view"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
-            android:layout_weight="1"
             android:scrollbars="none"/>
         <Button
             android:id="@+id/clear_all_button"
diff --git a/go/quickstep/res/layout/task_item_view.xml b/go/quickstep/res/layout/task_item_view.xml
index 2198237..1483d4c 100644
--- a/go/quickstep/res/layout/task_item_view.xml
+++ b/go/quickstep/res/layout/task_item_view.xml
@@ -17,25 +17,23 @@
 <com.android.quickstep.views.TaskItemView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
+    android:layout_height="match_parent"
     android:orientation="horizontal">
-    <FrameLayout
+    <com.android.quickstep.views.TaskThumbnailIconView
         android:id="@+id/task_icon_and_thumbnail"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:layout_gravity="center_vertical"
-        android:layout_marginHorizontal="8dp">
+        android:layout_marginHorizontal="8dp"
+        android:layout_marginTop="16dp">
         <ImageView
             android:id="@+id/task_thumbnail"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:layout_gravity="top|start"/>
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"/>
         <ImageView
             android:id="@+id/task_icon"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="bottom|end"/>
-    </FrameLayout>
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"/>
+    </com.android.quickstep.views.TaskThumbnailIconView>
     <TextView
         android:id="@+id/task_label"
         android:layout_width="wrap_content"
diff --git a/go/quickstep/src/com/android/quickstep/TaskAdapter.java b/go/quickstep/src/com/android/quickstep/TaskAdapter.java
index f1fc9de..7e7f278 100644
--- a/go/quickstep/src/com/android/quickstep/TaskAdapter.java
+++ b/go/quickstep/src/com/android/quickstep/TaskAdapter.java
@@ -15,11 +15,6 @@
  */
 package com.android.quickstep;
 
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-
-import static com.android.quickstep.views.TaskLayoutUtils.getTaskHeight;
-import static com.android.quickstep.views.TaskLayoutUtils.getTaskTopMargin;
-
 import android.view.LayoutInflater;
 import android.view.ViewGroup;
 
@@ -45,13 +40,11 @@
 
     private static final String TAG = "TaskAdapter";
     private final TaskListLoader mLoader;
-    private final DeviceProfile mDeviceProfile;
     private TaskActionController mTaskActionController;
     private boolean mIsShowingLoadingUi;
 
-    public TaskAdapter(@NonNull TaskListLoader loader, DeviceProfile dp) {
+    public TaskAdapter(@NonNull TaskListLoader loader) {
         mLoader = loader;
-        mDeviceProfile = dp;
     }
 
     public void setActionController(TaskActionController taskActionController) {
@@ -74,11 +67,6 @@
     public TaskHolder onCreateViewHolder(ViewGroup parent, int viewType) {
         TaskItemView itemView = (TaskItemView) LayoutInflater.from(parent.getContext())
                 .inflate(R.layout.task_item_view, parent, false);
-        ViewGroup.MarginLayoutParams itemViewParams =
-                (ViewGroup.MarginLayoutParams) itemView.getLayoutParams();
-        itemViewParams.width = MATCH_PARENT;
-        itemViewParams.height = getTaskHeight(mDeviceProfile);
-        itemViewParams.topMargin = getTaskTopMargin(mDeviceProfile);
         TaskHolder holder = new TaskHolder(itemView);
         itemView.setOnClickListener(view -> mTaskActionController.launchTask(holder));
         return holder;
diff --git a/go/quickstep/src/com/android/quickstep/TaskLayoutManager.java b/go/quickstep/src/com/android/quickstep/TaskLayoutManager.java
new file mode 100644
index 0000000..ecb2499
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/TaskLayoutManager.java
@@ -0,0 +1,42 @@
+/*
+ * 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.content.Context;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.LinearLayoutManager;
+
+/**
+ * Layout manager for task list that restricts child height based off the max number of tasks the
+ * recycler view should hold and the height of the recycler view.
+ */
+public final class TaskLayoutManager extends LinearLayoutManager {
+
+    public TaskLayoutManager(Context context, int vertical, boolean b) {
+        super(context, vertical, b);
+    }
+
+    @Override
+    public void measureChildWithMargins(@NonNull View child, int widthUsed, int heightUsed) {
+        // Request child view takes up 1 / MAX_TASKS of the total view height.
+        int heightUsedByView = (int) (getHeight() *
+                (TaskAdapter.MAX_TASKS_TO_DISPLAY - 1.0f) / TaskAdapter.MAX_TASKS_TO_DISPLAY);
+        super.measureChildWithMargins(child, widthUsed, heightUsedByView);
+    }
+}
diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
index 2c4abc5..712b9dc 100644
--- a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
+++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
@@ -43,7 +43,6 @@
 import androidx.annotation.Nullable;
 import androidx.recyclerview.widget.DefaultItemAnimator;
 import androidx.recyclerview.widget.ItemTouchHelper;
-import androidx.recyclerview.widget.LinearLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
 import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver;
 import androidx.recyclerview.widget.RecyclerView.OnChildAttachStateChangeListener;
@@ -56,6 +55,7 @@
 import com.android.quickstep.TaskActionController;
 import com.android.quickstep.TaskAdapter;
 import com.android.quickstep.TaskHolder;
+import com.android.quickstep.TaskLayoutManager;
 import com.android.quickstep.TaskListLoader;
 import com.android.quickstep.TaskSwipeCallback;
 
@@ -121,7 +121,7 @@
         mContext = context;
         mDeviceProfile = activity.getDeviceProfile();
         mTaskLoader = new TaskListLoader(mContext);
-        mTaskAdapter = new TaskAdapter(mTaskLoader, mDeviceProfile);
+        mTaskAdapter = new TaskAdapter(mTaskLoader);
         mTaskActionController = new TaskActionController(mTaskLoader, mTaskAdapter);
         mTaskAdapter.setActionController(mTaskActionController);
     }
@@ -135,7 +135,7 @@
             recyclerViewParams.height = getTaskListHeight(mDeviceProfile);
             mTaskRecyclerView.setAdapter(mTaskAdapter);
             mTaskRecyclerView.setLayoutManager(
-                    new LinearLayoutManager(mContext, VERTICAL, true /* reverseLayout */));
+                    new TaskLayoutManager(mContext, VERTICAL, true /* reverseLayout */));
             ItemTouchHelper helper = new ItemTouchHelper(
                     new TaskSwipeCallback(mTaskActionController));
             helper.attachToRecyclerView(mTaskRecyclerView);
@@ -170,6 +170,8 @@
                     updateContentViewVisibility();
                 }
             });
+            // TODO: Move clear all button to recycler view so that it can scroll off screen.
+            // TODO: Move layout param logic into onMeasure
             mClearAllView = findViewById(R.id.clear_all_button);
             MarginLayoutParams clearAllParams =
                     (MarginLayoutParams) mClearAllView.getLayoutParams();
diff --git a/go/quickstep/src/com/android/quickstep/views/TaskItemView.java b/go/quickstep/src/com/android/quickstep/views/TaskItemView.java
index 4392c68..572747b 100644
--- a/go/quickstep/src/com/android/quickstep/views/TaskItemView.java
+++ b/go/quickstep/src/com/android/quickstep/views/TaskItemView.java
@@ -15,8 +15,6 @@
  */
 package com.android.quickstep.views;
 
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
@@ -25,8 +23,6 @@
 import android.util.AttributeSet;
 import android.util.FloatProperty;
 import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
@@ -43,7 +39,6 @@
 
     private static final String EMPTY_LABEL = "";
     private static final String DEFAULT_LABEL = "...";
-    private static final float SUBITEM_FRAME_RATIO = .6f;
     private final Drawable mDefaultIcon;
     private final Drawable mDefaultThumbnail;
     private final TaskLayerDrawable mIconDrawable;
@@ -51,7 +46,6 @@
     private TextView mLabelView;
     private ImageView mIconView;
     private ImageView mThumbnailView;
-    private FrameLayout mThumbnailIconFrame;
     private float mContentTransitionProgress;
 
     /**
@@ -86,7 +80,6 @@
         mLabelView = findViewById(R.id.task_label);
         mThumbnailView = findViewById(R.id.task_thumbnail);
         mIconView = findViewById(R.id.task_icon);
-        mThumbnailIconFrame = findViewById(R.id.task_icon_and_thumbnail);
 
         mThumbnailView.setImageDrawable(mThumbnailDrawable);
         mIconView.setImageDrawable(mIconDrawable);
@@ -95,31 +88,6 @@
         CONTENT_TRANSITION_PROGRESS.setValue(this, 1.0f);
     }
 
-    @Override
-    public void setLayoutParams(ViewGroup.LayoutParams params) {
-        super.setLayoutParams(params);
-
-        // TODO: Rather than setting child layout params, make custom views and override onMeasure.
-        if (mThumbnailIconFrame == null
-                || mIconView == null
-                || mThumbnailView == null) {
-            // Views not initialized yet.
-            return;
-        }
-
-        int frameSize = params.height;
-        ViewGroup.LayoutParams frameParams = mThumbnailIconFrame.getLayoutParams();
-        frameParams.width = frameSize;
-
-        int frameSubItemWidth = (int) (SUBITEM_FRAME_RATIO * frameSize);
-        ViewGroup.LayoutParams thumbnailParams = mThumbnailView.getLayoutParams();
-        thumbnailParams.width = frameSubItemWidth;
-
-        ViewGroup.LayoutParams iconParams = mIconView.getLayoutParams();
-        iconParams.width = frameSubItemWidth;
-        iconParams.height = frameSubItemWidth;
-    }
-
     /**
      * Resets task item view to empty, loading UI.
      */
diff --git a/go/quickstep/src/com/android/quickstep/views/TaskLayoutUtils.java b/go/quickstep/src/com/android/quickstep/views/TaskLayoutUtils.java
index 1b50707..5bb30ad 100644
--- a/go/quickstep/src/com/android/quickstep/views/TaskLayoutUtils.java
+++ b/go/quickstep/src/com/android/quickstep/views/TaskLayoutUtils.java
@@ -26,23 +26,9 @@
     private static final float BUTTON_TO_DEVICE_HEIGHT_RATIO = 36.0f/569;
     private static final float BUTTON_WIDTH_TO_HEIGHT_RATIO = 53.0f/18;
     private static final float BUTTON_MARGIN_TO_BUTTON_HEIGHT_RATIO = 5.0f/9;
-    private static final float TASK_TO_DEVICE_HEIGHT_RATIO = 15.0f/19;
-    private static final float TASK_MARGIN_TO_TASK_HEIGHT_RATIO = 4.0f/15;
 
     private TaskLayoutUtils() {}
 
-    public static int getTaskHeight(DeviceProfile dp) {
-        return (int) (TASK_TO_DEVICE_HEIGHT_RATIO * getTaskItemSpace(dp));
-    }
-
-    public static int getTaskTopMargin(DeviceProfile dp) {
-        return (int) (TASK_MARGIN_TO_TASK_HEIGHT_RATIO * getTaskHeight(dp));
-    }
-
-    private static int getTaskItemSpace(DeviceProfile dp) {
-        return getTaskListHeight(dp) / TaskAdapter.MAX_TASKS_TO_DISPLAY;
-    }
-
     public static int getTaskListHeight(DeviceProfile dp) {
         int clearAllSpace = getClearAllButtonHeight(dp) + 2 * getClearAllButtonTopBottomMargin(dp);
         return getDeviceLongWidth(dp) - clearAllSpace;
diff --git a/go/quickstep/src/com/android/quickstep/views/TaskThumbnailIconView.java b/go/quickstep/src/com/android/quickstep/views/TaskThumbnailIconView.java
new file mode 100644
index 0000000..b1c60dd
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/views/TaskThumbnailIconView.java
@@ -0,0 +1,100 @@
+/*
+ * 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.views;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.launcher3.R;
+
+/**
+ * Square view that holds thumbnail and icon and shrinks them appropriately so that both fit nicely
+ * within the view. Side length is determined by height.
+ */
+final class TaskThumbnailIconView extends ViewGroup {
+    private final Rect mTmpFrameRect = new Rect();
+    private final Rect mTmpChildRect = new Rect();
+    private View mThumbnailView;
+    private View mIconView;
+    private static final float SUBITEM_FRAME_RATIO = .6f;
+
+    public TaskThumbnailIconView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mThumbnailView = findViewById(R.id.task_thumbnail);
+        mIconView = findViewById(R.id.task_icon);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        int height = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);
+        int width = height;
+        setMeasuredDimension(width, height);
+
+        int subItemSize = (int) (SUBITEM_FRAME_RATIO * height);
+        if (mThumbnailView.getVisibility() != GONE) {
+            int thumbnailHeightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
+            int thumbnailWidthSpec = MeasureSpec.makeMeasureSpec(subItemSize, MeasureSpec.EXACTLY);
+            measureChild(mThumbnailView, thumbnailWidthSpec, thumbnailHeightSpec);
+        }
+        if (mIconView.getVisibility() != GONE) {
+            int iconHeightSpec = MeasureSpec.makeMeasureSpec(subItemSize, MeasureSpec.EXACTLY);
+            int iconWidthSpec = MeasureSpec.makeMeasureSpec(subItemSize, MeasureSpec.EXACTLY);
+            measureChild(mIconView, iconWidthSpec, iconHeightSpec);
+        }
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        mTmpFrameRect.left = getPaddingLeft();
+        mTmpFrameRect.right = right - left - getPaddingRight();
+        mTmpFrameRect.top = getPaddingTop();
+        mTmpFrameRect.bottom = bottom - top - getPaddingBottom();
+
+        // Layout the thumbnail to the top-start corner of the view
+        if (mThumbnailView.getVisibility() != GONE) {
+            final int width = mThumbnailView.getMeasuredWidth();
+            final int height = mThumbnailView.getMeasuredHeight();
+
+            final int thumbnailGravity = Gravity.TOP | Gravity.START;
+            Gravity.apply(thumbnailGravity, width, height, mTmpFrameRect, mTmpChildRect);
+
+            mThumbnailView.layout(mTmpChildRect.left, mTmpChildRect.top,
+                    mTmpChildRect.right, mTmpChildRect.bottom);
+        }
+
+        // Layout the icon to the bottom-end corner of the view
+        if (mIconView.getVisibility() != GONE) {
+            final int width = mIconView.getMeasuredWidth();
+            final int height = mIconView.getMeasuredHeight();
+
+            int thumbnailGravity = Gravity.BOTTOM | Gravity.END;
+            Gravity.apply(thumbnailGravity, width, height, mTmpFrameRect, mTmpChildRect);
+
+            mIconView.layout(mTmpChildRect.left, mTmpChildRect.top,
+                    mTmpChildRect.right, mTmpChildRect.bottom);
+        }
+    }
+}