blob: 15c7c87366d26932b64976bb7ac9080b453ac6bb [file] [log] [blame]
/*
* Copyright (C) 2017 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.views.lowram;
import android.content.Context;
import android.graphics.Rect;
import android.view.ViewConfiguration;
import com.android.systemui.R;
import com.android.systemui.recents.LegacyRecentsImpl;
import com.android.systemui.recents.RecentsActivityLaunchState;
import com.android.systemui.recents.utilities.Utilities;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.recents.views.TaskStackLayoutAlgorithm;
import com.android.systemui.recents.views.TaskViewTransform;
import java.util.ArrayList;
import static com.android.systemui.recents.views.TaskStackLayoutAlgorithm.VisibilityReport;
public class TaskStackLowRamLayoutAlgorithm {
private static final String TAG = "TaskStackLowRamLayoutAlgorithm";
private static final float MAX_OVERSCROLL = 0.2f / 0.3f;
public static final int MAX_LAYOUT_TASK_COUNT = 9;
public static final int NUM_TASK_VISIBLE_LAUNCHED_FROM_HOME = 2;
public static final int NUM_TASK_VISIBLE_LAUNCHED_FROM_APP =
NUM_TASK_VISIBLE_LAUNCHED_FROM_HOME + 1;
private Rect mWindowRect;
private int mFlingThreshold;
private int mPadding;
private int mPaddingLeftRight;
private int mTopOffset;
private int mPaddingEndTopBottom;
private Rect mTaskRect = new Rect();
private Rect mSystemInsets = new Rect();
public TaskStackLowRamLayoutAlgorithm(Context context) {
reloadOnConfigurationChange(context);
}
public void reloadOnConfigurationChange(Context context) {
mPadding = context.getResources()
.getDimensionPixelSize(R.dimen.recents_layout_side_margin_phone);
mFlingThreshold = ViewConfiguration.get(context).getScaledMinimumFlingVelocity();
}
public void initialize(Rect windowRect) {
mWindowRect = windowRect;
if (mWindowRect.height() > 0) {
int windowHeight = mWindowRect.height() - mSystemInsets.bottom;
int windowWidth = mWindowRect.width() - mSystemInsets.right - mSystemInsets.left;
int width = Math.min(windowWidth, windowHeight) - mPadding * 2;
boolean isLandscape = windowWidth > windowHeight;
mTaskRect.set(0, 0, width, isLandscape ? width * 2 / 3 : width);
mPaddingLeftRight = (windowWidth - mTaskRect.width()) / 2;
mPaddingEndTopBottom = (windowHeight - mTaskRect.height()) / 2;
// Compute the top offset to center tasks in the middle of the screen
mTopOffset = (getTotalHeightOfTasks(MAX_LAYOUT_TASK_COUNT) - windowHeight) / 2;
}
}
public void setSystemInsets(Rect systemInsets) {
mSystemInsets = systemInsets;
}
public VisibilityReport computeStackVisibilityReport(ArrayList<Task> tasks) {
RecentsActivityLaunchState launchState = LegacyRecentsImpl.getConfiguration().getLaunchState();
int maxVisible = launchState.launchedFromHome || launchState.launchedFromPipApp
|| launchState.launchedWithNextPipApp
? NUM_TASK_VISIBLE_LAUNCHED_FROM_HOME
: NUM_TASK_VISIBLE_LAUNCHED_FROM_APP;
int visibleCount = Math.min(maxVisible, tasks.size());
return new VisibilityReport(visibleCount, visibleCount);
}
public void getFrontOfStackTransform(TaskViewTransform transformOut,
TaskStackLayoutAlgorithm stackLayout) {
if (mWindowRect == null) {
transformOut.reset();
return;
}
// Calculate the static task y position 2 tasks after/below the middle/current task
int windowHeight = mWindowRect.height() - mSystemInsets.bottom;
int bottomOfCurrentTask = (windowHeight + mTaskRect.height()) / 2;
int y = bottomOfCurrentTask + mTaskRect.height() + mPadding * 2;
fillStackTransform(transformOut, y, stackLayout.mMaxTranslationZ, true);
}
public void getBackOfStackTransform(TaskViewTransform transformOut,
TaskStackLayoutAlgorithm stackLayout) {
if (mWindowRect == null) {
transformOut.reset();
return;
}
// Calculate the static task y position 2 tasks before/above the middle/current task
int windowHeight = mWindowRect.height() - mSystemInsets.bottom;
int topOfCurrentTask = (windowHeight - mTaskRect.height()) / 2;
int y = topOfCurrentTask - (mTaskRect.height() + mPadding) * 2;
fillStackTransform(transformOut, y, stackLayout.mMaxTranslationZ, true);
}
public TaskViewTransform getTransform(int taskIndex, float stackScroll,
TaskViewTransform transformOut, int taskCount, TaskStackLayoutAlgorithm stackLayout) {
if (taskCount == 0) {
transformOut.reset();
return transformOut;
}
boolean visible = true;
int y;
if (taskCount > 1) {
y = getTaskTopFromIndex(taskIndex) - percentageToScroll(stackScroll);
// Check visibility from the bottom of the task
visible = y + mPadding + getTaskRect().height() > 0;
} else {
int windowHeight = mWindowRect.height() - mSystemInsets.bottom;
y = (windowHeight - mTaskRect.height()) / 2 - percentageToScroll(stackScroll);
}
fillStackTransform(transformOut, y, stackLayout.mMaxTranslationZ, visible);
return transformOut;
}
/**
* Finds the closest task to the scroll percentage in the y axis and returns the percentage of
* the task to scroll to.
* @param scrollP percentage to find nearest to
* @param numTasks number of tasks in recents stack
* @param velocity speed of fling
*/
public float getClosestTaskP(float scrollP, int numTasks, int velocity) {
int y = percentageToScroll(scrollP);
int lastY = getTaskTopFromIndex(0) - mPaddingEndTopBottom;
for (int i = 1; i < numTasks; i++) {
int taskY = getTaskTopFromIndex(i) - mPaddingEndTopBottom;
int diff = taskY - y;
if (diff > 0) {
int diffPrev = Math.abs(y - lastY);
boolean useNext = diff > diffPrev;
if (Math.abs(velocity) > mFlingThreshold) {
useNext = velocity > 0;
}
return useNext
? scrollToPercentage(lastY) : scrollToPercentage(taskY);
}
lastY = taskY;
}
return scrollToPercentage(lastY);
}
/**
* Convert a scroll value to a percentage
* @param scroll a scroll value
* @return a percentage that represents the scroll from the total height of tasks
*/
public float scrollToPercentage(int scroll) {
return (float) scroll / (mTaskRect.height() + mPadding);
}
/**
* Converts a percentage to the scroll value from the total height of tasks
* @param p a percentage that represents the scroll value
* @return a scroll value in pixels
*/
public int percentageToScroll(float p) {
return (int) (p * (mTaskRect.height() + mPadding));
}
/**
* Get the min scroll progress for low ram layout. This computes the top position of the
* first task and reduce by the end padding to center the first task
* @return position of max scroll
*/
public float getMinScrollP() {
return getScrollPForTask(0);
}
/**
* Get the max scroll progress for low ram layout. This computes the top position of the last
* task and reduce by the end padding to center the last task
* @param taskCount the amount of tasks in the recents stack
* @return position of max scroll
*/
public float getMaxScrollP(int taskCount) {
return getScrollPForTask(taskCount - 1);
}
/**
* Get the initial scroll value whether launched from home or from an app.
* @param taskCount the amount of tasks currently in recents
* @param fromHome if launching recents from home or not
* @return from home it will return max value and from app it will return 2nd last task
*/
public float getInitialScrollP(int taskCount, boolean fromHome) {
if (fromHome) {
return getMaxScrollP(taskCount);
}
if (taskCount < 2) {
return 0;
}
return getScrollPForTask(taskCount - 2);
}
/**
* Get the scroll progress for any task
* @param taskIndex task index to get the scroll progress of
* @return scroll progress of task
*/
public float getScrollPForTask(int taskIndex) {
return scrollToPercentage(getTaskTopFromIndex(taskIndex) - mPaddingEndTopBottom);
}
public Rect getTaskRect() {
return mTaskRect;
}
public float getMaxOverscroll() {
return MAX_OVERSCROLL;
}
private int getTaskTopFromIndex(int index) {
return getTotalHeightOfTasks(index) - mTopOffset;
}
private int getTotalHeightOfTasks(int taskCount) {
return taskCount * mTaskRect.height() + (taskCount + 1) * mPadding;
}
private void fillStackTransform(TaskViewTransform transformOut, int y, int translationZ,
boolean visible) {
transformOut.scale = 1f;
transformOut.alpha = 1f;
transformOut.translationZ = translationZ;
transformOut.dimAlpha = 0f;
transformOut.viewOutlineAlpha = 1f;
transformOut.rect.set(getTaskRect());
transformOut.rect.offset(mPaddingLeftRight + mSystemInsets.left, y);
Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
transformOut.visible = visible;
}
}