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
This commit is contained in:
@@ -267,6 +267,22 @@
|
||||
</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"
|
||||
|
||||
33
packages/SystemUI/res/layout-sw600dp/recents_grid.xml
Normal file
33
packages/SystemUI/res/layout-sw600dp/recents_grid.xml
Normal file
@@ -0,0 +1,33 @@
|
||||
<?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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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<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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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<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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user