</intent-filter>
</activity>
+ <activity android:name=".recents.grid.RecentsGridActivity"
+ android:label="@string/accessibility_desc_recent_apps"
+ android:exported="false"
+ android:launchMode="singleInstance"
+ android:excludeFromRecents="true"
+ android:stateNotNeeded="true"
+ android:resumeWhilePausing="true"
+ android:screenOrientation="behind"
+ android:resizeableActivity="true"
+ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
+ android:theme="@style/RecentsTheme.Wallpaper">
+ <intent-filter>
+ <action android:name="com.android.systemui.recents.TOGGLE_RECENTS" />
+ </intent-filter>
+ </activity>
+
<activity android:name=".recents.tv.RecentsTvActivity"
android:label="@string/accessibility_desc_recent_apps"
android:exported="false"
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:id="@+id/recents_container">
+ <include layout="@layout/recents_stack_action_button" />
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/recents_view"
+ android:layout_marginLeft="12dp"
+ android:layout_marginTop="10dp"
+ android:layout_marginRight="12dp"
+ android:layout_marginBottom="5dp"
+ android:gravity="center">
+ </FrameLayout>
+</LinearLayout>
+
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;
static {
RECENTS_ACTIVITIES.add(RecentsImpl.RECENTS_ACTIVITY);
RECENTS_ACTIVITIES.add(RecentsTvImpl.RECENTS_TV_ACTIVITY);
+ RECENTS_ACTIVITIES.add(RecentsGridImpl.RECENTS_MOSAIC_ACTIVITY);
}
// Purely for experimentation
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);
}
--- /dev/null
+/*
+ * 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<Task> mTasks = new ArrayList<>();
+ private List<View> 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<Task> 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<Rect> 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<Task> 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);
+ }
+}
+
--- /dev/null
+/*
+ * 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());
+ }
+}
+
--- /dev/null
+/*
+ * 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<Rect> getRectsForTaskCount(int count, int containerWidth, int containerHeight,
+ boolean allowLineOfThree, int padding) {
+ return getRectsForTaskCount(count, containerWidth, containerHeight, allowLineOfThree,
+ padding, null);
+ }
+
+ static List<Rect> getRectsForTaskCount(int count, int containerWidth, int containerHeight,
+ boolean allowLineOfThree, int padding, Rect preCalculatedTile) {
+ int singleLineMaxCount = allowLineOfThree ? 3 : 2;
+ List<Rect> 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<Rect> rectsA = getRectsForTaskCount(
+ lineTaskCount, containerWidth, lineHeight, true /* allowLineOfThree */, padding,
+ null);
+ List<Rect> 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<Rect> rectsA = getRectsForTaskCount(
+ lineTaskCount, containerWidth, lineHeight, true /* allowLineOfThree */, padding, null);
+ List<Rect> rectsB = getRectsForTaskCount(
+ lineTaskCount, containerWidth, lineHeight, true /* allowLineOfThree */, padding,
+ rectsA.get(0));
+ List<Rect> 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;
+ }
+}
+
}
/** 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();
}
--- /dev/null
+/*
+ * 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<Rect> 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<Rect> 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<Rect> 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<Rect> 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<Rect> 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<Rect> 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<Rect> 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);
+ }
+}
+