From: Manu Cornet Date: Fri, 11 Nov 2016 19:36:08 +0000 (-0800) Subject: DO NOT MERGE - 2-dimensional Recents activity. X-Git-Tag: android-x86-7.1-r1~525^2 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=c13df1eb30;p=android-x86%2Fframeworks-base.git DO NOT MERGE - 2-dimensional Recents activity. This is a simple first version in the spirit of small, incremental CLs. It is fully functional but the following will come in later changes: * Split screen support * Potential animations * Alt-tab behavior * Relayout on orientation changes The new activity is only started when a specific system property is set. Test: Tested new activity behavior on local Ryu. Added tests for layout logic. Bug: 32101881 Merged-In: I550f6e7ea0de3937dbf80e5f0294676cfe567d47 Change-Id: I46a537646e98b312d831510e1d331948888ae5ce --- diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index a74fbf81a021..ba845bde4347 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -267,6 +267,22 @@ + + + + + + + + + + + + + diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java index ba50c663a6bd..eb9beb64d2be 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java +++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java @@ -57,6 +57,7 @@ import com.android.systemui.recents.events.component.ShowUserToastEvent; import com.android.systemui.recents.events.ui.RecentsDrawnEvent; import com.android.systemui.recents.misc.SystemServicesProxy; import com.android.systemui.recents.model.RecentsTaskLoader; +import com.android.systemui.recents.grid.RecentsGridImpl; import com.android.systemui.recents.tv.RecentsTvImpl; import com.android.systemui.stackdivider.Divider; @@ -83,6 +84,7 @@ public class Recents extends SystemUI static { RECENTS_ACTIVITIES.add(RecentsImpl.RECENTS_ACTIVITY); RECENTS_ACTIVITIES.add(RecentsTvImpl.RECENTS_TV_ACTIVITY); + RECENTS_ACTIVITIES.add(RecentsGridImpl.RECENTS_MOSAIC_ACTIVITY); } // Purely for experimentation @@ -205,6 +207,8 @@ public class Recents extends SystemUI getSystemService(Context.UI_MODE_SERVICE); if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION) { mImpl = new RecentsTvImpl(mContext); + } else if (SystemProperties.getBoolean("ro.recents.grid", false) == true) { + mImpl = new RecentsGridImpl(mContext); } else { mImpl = new RecentsImpl(mContext); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/grid/RecentsGridActivity.java b/packages/SystemUI/src/com/android/systemui/recents/grid/RecentsGridActivity.java new file mode 100644 index 000000000000..cf455708500f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/grid/RecentsGridActivity.java @@ -0,0 +1,317 @@ +/* + * 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.systemui.recents.grid; + +import android.app.Activity; +import android.content.Intent; +import android.content.res.Configuration; +import android.graphics.Rect; +import android.os.Bundle; +import android.util.Log; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import android.widget.FrameLayout; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.MetricsProto.MetricsEvent; +import com.android.systemui.R; +import com.android.systemui.recents.Recents; +import com.android.systemui.recents.RecentsActivity; +import com.android.systemui.recents.RecentsActivityLaunchState; +import com.android.systemui.recents.RecentsConfiguration; +import com.android.systemui.recents.RecentsImpl; +import com.android.systemui.recents.events.EventBus; +import com.android.systemui.recents.events.activity.ConfigurationChangedEvent; +import com.android.systemui.recents.events.activity.HideRecentsEvent; +import com.android.systemui.recents.events.activity.LaunchNextTaskRequestEvent; +import com.android.systemui.recents.events.activity.LaunchTaskEvent; +import com.android.systemui.recents.events.activity.ToggleRecentsEvent; +import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent; +import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent; +import com.android.systemui.recents.events.ui.DeleteTaskDataEvent; +import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent; +import com.android.systemui.recents.events.ui.DismissTaskViewEvent; +import com.android.systemui.recents.events.ui.TaskViewDismissedEvent; +import com.android.systemui.recents.misc.SystemServicesProxy; +import com.android.systemui.recents.misc.Utilities; +import com.android.systemui.recents.model.RecentsTaskLoadPlan; +import com.android.systemui.recents.model.RecentsTaskLoader; +import com.android.systemui.recents.model.Task; +import com.android.systemui.recents.model.TaskStack; +import com.android.systemui.recents.views.TaskView; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * The main grid recents activity started by the RecentsImpl. + */ +public class RecentsGridActivity extends Activity implements ViewTreeObserver.OnPreDrawListener { + private final static String TAG = "RecentsGridActivity"; + + private TaskStack mTaskStack; + private List mTasks = new ArrayList<>(); + private List mTaskViews = new ArrayList<>(); + private FrameLayout mRecentsView; + private TextView mEmptyView; + private View mClearAllButton; + private int mDisplayOrientation = Configuration.ORIENTATION_UNDEFINED; + private Rect mDisplayRect = new Rect(); + private LayoutInflater mInflater; + private boolean mTouchExplorationEnabled; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.recents_grid); + SystemServicesProxy ssp = Recents.getSystemServices(); + + mInflater = LayoutInflater.from(this); + mDisplayOrientation = Utilities.getAppConfiguration(this).orientation; + mDisplayRect = ssp.getDisplayRect(); + mTouchExplorationEnabled = ssp.isTouchExplorationEnabled(); + + mRecentsView = (FrameLayout) findViewById(R.id.recents_view); + LinearLayout recentsContainer = (LinearLayout) findViewById(R.id.recents_container); + mEmptyView = (TextView) mInflater.inflate(R.layout.recents_empty, recentsContainer, false); + mClearAllButton = findViewById(R.id.button); + + FrameLayout.LayoutParams emptyViewLayoutParams = new FrameLayout.LayoutParams( + FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT); + emptyViewLayoutParams.gravity = Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL; + mEmptyView.setLayoutParams(emptyViewLayoutParams); + mRecentsView.addView(mEmptyView); + + mClearAllButton.setVisibility(View.VISIBLE); + LinearLayout.LayoutParams lp = + (LinearLayout.LayoutParams) mClearAllButton.getLayoutParams(); + lp.gravity = Gravity.END; + + mClearAllButton.setOnClickListener(v -> { + EventBus.getDefault().send(new DismissAllTaskViewsEvent()); + }); + + mRecentsView.setOnClickListener(v -> { + EventBus.getDefault().send(new HideRecentsEvent( + false /* triggeredFromAltTab */, false /* triggeredFromHomeKey */)); + }); + + EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY); + } + + private TaskView createView() { + return (TaskView) mInflater.inflate(R.layout.recents_task_view, mRecentsView, false); + } + + private void clearTaskViews() { + for (View taskView : mTaskViews) { + ViewGroup parent = (ViewGroup) taskView.getParent(); + if (parent != null) { + parent.removeView(taskView); + } + } + mTaskViews.clear(); + } + + private void updateControlVisibility() { + boolean empty = (mTasks.size() == 0); + mClearAllButton.setVisibility(empty ? View.INVISIBLE : View.VISIBLE); + mEmptyView.setVisibility(empty ? View.VISIBLE : View.INVISIBLE); + if (empty) { + mEmptyView.bringToFront(); + } + } + + private void updateRecentsTasks() { + RecentsTaskLoader loader = Recents.getTaskLoader(); + RecentsTaskLoadPlan plan = RecentsImpl.consumeInstanceLoadPlan(); + if (plan == null) { + plan = loader.createLoadPlan(this); + } + RecentsConfiguration config = Recents.getConfiguration(); + RecentsActivityLaunchState launchState = config.getLaunchState(); + if (!plan.hasTasks()) { + loader.preloadTasks(plan, -1, !launchState.launchedFromHome); + } + int numVisibleTasks = 9; + mTaskStack = plan.getTaskStack(); + RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options(); + loadOpts.runningTaskId = launchState.launchedToTaskId; + loadOpts.numVisibleTasks = numVisibleTasks; + loadOpts.numVisibleTaskThumbnails = numVisibleTasks; + loader.loadTasks(this, plan, loadOpts); + + List stackTasks = mTaskStack.getStackTasks(); + Collections.reverse(stackTasks); + mTasks = stackTasks; + + updateControlVisibility(); + + clearTaskViews(); + for (int i = 0; i < mTasks.size(); i++) { + Task task = mTasks.get(i); + TaskView taskView = createView(); + taskView.onTaskBound(task, mTouchExplorationEnabled, mDisplayOrientation, mDisplayRect); + Recents.getTaskLoader().loadTaskData(task); + taskView.setTouchEnabled(true); + // Show dismiss button right away. + taskView.startNoUserInteractionAnimation(); + mTaskViews.add(taskView); + } + } + + @Override + protected void onStart() { + super.onStart(); + EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, true)); + mRecentsView.getViewTreeObserver().addOnPreDrawListener(this); + } + + @Override + public void onResume() { + super.onResume(); + updateRecentsTasks(); + } + + @Override + protected void onStop() { + super.onStop(); + EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, false)); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + EventBus.getDefault().unregister(this); + } + + @Override + public void onBackPressed() { + // Back behaves like the recents button so just trigger a toggle event. + EventBus.getDefault().send(new ToggleRecentsEvent()); + } + + @Override + public boolean onPreDraw() { + mRecentsView.getViewTreeObserver().removeOnPreDrawListener(this); + int width = mRecentsView.getWidth(); + int height = mRecentsView.getHeight(); + + List rects = TaskGridLayoutAlgorithm.getRectsForTaskCount( + mTasks.size(), width, height, false /* allowLineOfThree */, 30 /* padding */); + for (int i = 0; i < rects.size(); i++) { + Rect rect = rects.get(i); + View taskView = mTaskViews.get(i); + taskView.setLayoutParams(new FrameLayout.LayoutParams(rect.width(), rect.height())); + taskView.setTranslationX(rect.left); + taskView.setTranslationY(rect.top); + mRecentsView.addView(taskView); + } + return true; + } + + void dismissRecentsToHome() { + Intent startMain = new Intent(Intent.ACTION_MAIN); + startMain.addCategory(Intent.CATEGORY_HOME); + startActivity(startMain); + } + + /**** EventBus events ****/ + + public final void onBusEvent(HideRecentsEvent event) { + if (event.triggeredFromAltTab) { + // Do nothing for now. + } else if (event.triggeredFromHomeKey) { + dismissRecentsToHome(); + } + } + + public final void onBusEvent(ToggleRecentsEvent event) { + // Always go back home for simplicity for now. If recents is entered from another app, this + // code will eventually need to go back to the original app. + dismissRecentsToHome(); + } + + public final void onBusEvent(DismissTaskViewEvent event) { + int taskIndex = mTaskViews.indexOf(event.taskView); + if (taskIndex != -1) { + mTasks.remove(taskIndex); + ((ViewGroup) event.taskView.getParent()).removeView(event.taskView); + mTaskViews.remove(taskIndex); + EventBus.getDefault().send( + new TaskViewDismissedEvent(event.taskView.getTask(), event.taskView, null)); + } + } + + public final void onBusEvent(TaskViewDismissedEvent event) { + mRecentsView.announceForAccessibility(this.getString( + R.string.accessibility_recents_item_dismissed, event.task.title)); + updateControlVisibility(); + + EventBus.getDefault().send(new DeleteTaskDataEvent(event.task)); + + MetricsLogger.action(this, MetricsEvent.OVERVIEW_DISMISS, + event.task.key.getComponent().toString()); + } + + public final void onBusEvent(DeleteTaskDataEvent event) { + // Remove any stored data from the loader. + RecentsTaskLoader loader = Recents.getTaskLoader(); + loader.deleteTaskData(event.task, false); + + // Remove the task from activity manager. + SystemServicesProxy ssp = Recents.getSystemServices(); + ssp.removeTask(event.task.key.id); + } + + public final void onBusEvent(final DismissAllTaskViewsEvent event) { + // Keep track of the tasks which will have their data removed. + ArrayList tasks = new ArrayList<>(mTaskStack.getStackTasks()); + mRecentsView.announceForAccessibility(this.getString( + R.string.accessibility_recents_all_items_dismissed)); + mTaskStack.removeAllTasks(); + for (int i = tasks.size() - 1; i >= 0; i--) { + EventBus.getDefault().send(new DeleteTaskDataEvent(tasks.get(i))); + } + mTasks = new ArrayList<>(); + updateRecentsTasks(); + + MetricsLogger.action(this, MetricsEvent.OVERVIEW_DISMISS_ALL); + } + + public final void onBusEvent(AllTaskViewsDismissedEvent event) { + SystemServicesProxy ssp = Recents.getSystemServices(); + if (!ssp.hasDockedTask()) { + dismissRecentsToHome(); + } + } + + public final void onBusEvent(LaunchNextTaskRequestEvent event) { + // Always go back home for simplicity for now. Quick switch will be supported soon. + EventBus.getDefault().send(new HideRecentsEvent(false, true)); + } + + public final void onBusEvent(LaunchTaskEvent event) { + startActivity(event.task.key.baseIntent); + } +} + diff --git a/packages/SystemUI/src/com/android/systemui/recents/grid/RecentsGridImpl.java b/packages/SystemUI/src/com/android/systemui/recents/grid/RecentsGridImpl.java new file mode 100644 index 000000000000..ba7a4a736352 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/grid/RecentsGridImpl.java @@ -0,0 +1,48 @@ +/* + * 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.systemui.recents.grid; + +import android.app.ActivityManager; +import android.content.Context; +import android.content.Intent; +import android.os.UserHandle; + +import com.android.systemui.recents.RecentsImpl; +import com.android.systemui.recents.events.EventBus; +import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent; + +public class RecentsGridImpl extends RecentsImpl { + public static final String RECENTS_MOSAIC_ACTIVITY = + "com.android.systemui.recents.grid.RecentsGridActivity"; + + public RecentsGridImpl(Context context) { + super(context); + } + + @Override + protected void startRecentsActivity(ActivityManager.RunningTaskInfo runningTask, + boolean isHomeStackVisible, boolean animate, int growTarget) { + Intent intent = new Intent(); + intent.setClassName(RECENTS_PACKAGE, RECENTS_MOSAIC_ACTIVITY); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS + | Intent.FLAG_ACTIVITY_TASK_ON_HOME); + + mContext.startActivityAsUser(intent, UserHandle.CURRENT); + EventBus.getDefault().send(new RecentsActivityStartingEvent()); + } +} + diff --git a/packages/SystemUI/src/com/android/systemui/recents/grid/TaskGridLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/grid/TaskGridLayoutAlgorithm.java new file mode 100644 index 000000000000..6e2c6c03f188 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/grid/TaskGridLayoutAlgorithm.java @@ -0,0 +1,110 @@ +/* + * 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.systemui.recents.grid; + +import android.graphics.Rect; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; + +class TaskGridLayoutAlgorithm { + + private static final String TAG = "TaskGridLayoutAlgorithm"; + + static List getRectsForTaskCount(int count, int containerWidth, int containerHeight, + boolean allowLineOfThree, int padding) { + return getRectsForTaskCount(count, containerWidth, containerHeight, allowLineOfThree, + padding, null); + } + + static List getRectsForTaskCount(int count, int containerWidth, int containerHeight, + boolean allowLineOfThree, int padding, Rect preCalculatedTile) { + int singleLineMaxCount = allowLineOfThree ? 3 : 2; + List rects = new ArrayList<>(count); + boolean landscape = (containerWidth > containerHeight); + + // We support at most 9 tasks in this layout. + count = Math.min(count, 9); + + if (count == 0) { + return rects; + } + if (count <= singleLineMaxCount) { + if (landscape) { + // Single line. + int taskWidth = 0; + int emptySpace = 0; + if (preCalculatedTile != null) { + taskWidth = preCalculatedTile.width(); + emptySpace = containerWidth - (count * taskWidth) - (count - 1) * padding; + } else { + // Divide available space in equal parts. + taskWidth = (containerWidth - (count - 1) * padding) / count; + } + for (int i = 0; i < count; i++) { + int left = emptySpace / 2 + i * taskWidth + i * padding; + rects.add(new Rect(left, 0, left + taskWidth, containerHeight)); + } + } else { + // Single column. Divide available space in equal parts. + int taskHeight = (containerHeight - (count - 1) * padding) / count; + for (int i = 0; i < count; i++) { + int top = i * taskHeight + i * padding; + rects.add(new Rect(0, top, containerWidth, top + taskHeight)); + } + } + } else if (count < 7) { + // Two lines. + int lineHeight = (containerHeight - padding) / 2; + int lineTaskCount = (int) Math.ceil((double) count / 2); + List rectsA = getRectsForTaskCount( + lineTaskCount, containerWidth, lineHeight, true /* allowLineOfThree */, padding, + null); + List rectsB = getRectsForTaskCount( + count - lineTaskCount, containerWidth, lineHeight, true /* allowLineOfThree */, + padding, rectsA.get(0)); + for (Rect rect : rectsB) { + rect.offset(0, lineHeight + padding); + } + rects.addAll(rectsA); + rects.addAll(rectsB); + } else { + // Three lines. + int lineHeight = (containerHeight - 2 * padding) / 3; + int lineTaskCount = (int) Math.ceil((double) count / 3); + List rectsA = getRectsForTaskCount( + lineTaskCount, containerWidth, lineHeight, true /* allowLineOfThree */, padding, null); + List rectsB = getRectsForTaskCount( + lineTaskCount, containerWidth, lineHeight, true /* allowLineOfThree */, padding, + rectsA.get(0)); + List rectsC = getRectsForTaskCount( + count - (2 * lineTaskCount), containerWidth, lineHeight, + true /* allowLineOfThree */, padding, rectsA.get(0)); + for (Rect rect : rectsB) { + rect.offset(0, lineHeight + padding); + } + for (Rect rect : rectsC) { + rect.offset(0, 2 * (lineHeight + padding)); + } + rects.addAll(rectsA); + rects.addAll(rectsB); + rects.addAll(rectsC); + } + return rects; + } +} + diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java index 4ecdd7788001..115e65ad8c39 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java @@ -360,12 +360,12 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks } /** Enables/disables handling touch on this task view. */ - void setTouchEnabled(boolean enabled) { + public void setTouchEnabled(boolean enabled) { setOnClickListener(enabled ? this : null); } /** Animates this task view if the user does not interact with the stack after a certain time. */ - void startNoUserInteractionAnimation() { + public void startNoUserInteractionAnimation() { mHeaderView.startNoUserInteractionAnimation(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/grid/TaskGridLayoutAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/recents/grid/TaskGridLayoutAlgorithmTest.java new file mode 100644 index 000000000000..e5a74ae8eccd --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/recents/grid/TaskGridLayoutAlgorithmTest.java @@ -0,0 +1,112 @@ +/* + * 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.systemui.recents.grid; + +import android.graphics.Rect; +import android.test.suitebuilder.annotation.SmallTest; +import com.android.systemui.SysuiTestCase; + +import java.util.List; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertTrue; + +@SmallTest +public class TaskGridLayoutAlgorithmTest extends SysuiTestCase { + + public void testOneTile() { + List rects = TaskGridLayoutAlgorithm.getRectsForTaskCount( + 1, 1000, 500, false /* allowLineOfThree */, 0 /* padding */); + assertEquals(1, rects.size()); + Rect singleRect = rects.get(0); + assertEquals(1000, singleRect.width()); + } + + public void testTwoTilesLandscape() { + List rects = TaskGridLayoutAlgorithm.getRectsForTaskCount( + 2, 1200, 500, false /* allowLineOfThree */, 0 /* padding */); + assertEquals(2, rects.size()); + for (Rect rect : rects) { + assertEquals(600, rect.width()); + assertEquals(500, rect.height()); + } + } + + public void testTwoTilesLandscapeWithPadding() { + List rects = TaskGridLayoutAlgorithm.getRectsForTaskCount( + 2, 1200, 500, false /* allowLineOfThree */, 10 /* padding */); + assertEquals(2, rects.size()); + Rect rectA = rects.get(0); + Rect rectB = rects.get(1); + assertEquals(595, rectA.width()); + assertEquals(595, rectB.width()); + assertEquals(605, rectB.left); + } + + public void testTwoTilesPortrait() { + List rects = TaskGridLayoutAlgorithm.getRectsForTaskCount( + 2, 500, 1200, false /* allowLineOfThree */, 0 /* padding */); + assertEquals(2, rects.size()); + for (Rect rect : rects) { + assertEquals(500, rect.width()); + assertEquals(600, rect.height()); + } + } + + public void testThreeTiles() { + List rects = TaskGridLayoutAlgorithm.getRectsForTaskCount( + 3, 1200, 500, false /* allowLineOfThree */, 0 /* padding */); + assertEquals(3, rects.size()); + for (Rect rect : rects) { + assertEquals(600, rect.width()); + assertEquals(250, rect.height()); + } + // The third tile should be on the second line, in the middle. + Rect rectC = rects.get(2); + assertEquals(300, rectC.left); + assertEquals(250, rectC.top); + } + + public void testFourTiles() { + List rects = TaskGridLayoutAlgorithm.getRectsForTaskCount( + 4, 1200, 500, false /* allowLineOfThree */, 0 /* padding */); + assertEquals(4, rects.size()); + for (Rect rect : rects) { + assertEquals(600, rect.width()); + assertEquals(250, rect.height()); + } + Rect rectD = rects.get(3); + assertEquals(600, rectD.left); + assertEquals(250, rectD.top); + } + + public void testNineTiles() { + List rects = TaskGridLayoutAlgorithm.getRectsForTaskCount( + 9, 1200, 600, false /* allowLineOfThree */, 0 /* padding */); + assertEquals(9, rects.size()); + for (Rect rect : rects) { + assertEquals(400, rect.width()); + assertEquals(200, rect.height()); + } + Rect rectE = rects.get(4); + assertEquals(400, rectE.left); + assertEquals(200, rectE.top); + Rect rectI = rects.get(8); + assertEquals(800, rectI.left); + assertEquals(400, rectI.top); + } +} +