Merge "Change the task layout to better match mocks, add "in" animation"
This commit is contained in:
committed by
Android (Google) Code Review
commit
a6fa8e5432
@@ -297,7 +297,7 @@
|
||||
android:screenOrientation="behind"
|
||||
android:resizeableActivity="true"
|
||||
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
|
||||
android:theme="@style/RecentsTheme.Wallpaper">
|
||||
android:theme="@style/RecentsTheme.Grid">
|
||||
<intent-filter>
|
||||
<action android:name="com.android.systemui.recents.TOGGLE_RECENTS" />
|
||||
</intent-filter>
|
||||
|
||||
@@ -13,22 +13,11 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingTop="24dp"
|
||||
android:paddingBottom="54dp"
|
||||
android:orientation="vertical"
|
||||
android:id="@+id/recents_container"
|
||||
android:id="@+id/recents_view"
|
||||
android:gravity="center"
|
||||
android:background="#99000000">
|
||||
<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:gravity="center">
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
@@ -33,4 +33,5 @@
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:background="@drawable/recents_stack_action_background"
|
||||
android:visibility="invisible"
|
||||
android:forceHasOverlappingRendering="false" />
|
||||
android:forceHasOverlappingRendering="false"
|
||||
style="?attr/clearAllStyle" />
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
</declare-styleable>
|
||||
<declare-styleable name="RecentsPanelView">
|
||||
<attr name="recentItemLayout" format="reference" />
|
||||
<!-- Style for the "Clear all" button. -->
|
||||
<attr name="clearAllStyle" format="reference" />
|
||||
</declare-styleable>
|
||||
<declare-styleable name="DeadZone">
|
||||
<attr name="minSize" format="dimension" />
|
||||
|
||||
@@ -697,4 +697,19 @@
|
||||
|
||||
<!-- The size of the PIP dismiss target. -->
|
||||
<dimen name="pip_dismiss_target_size">48dp</dimen>
|
||||
|
||||
<!-- Values specific to grid-based recents. -->
|
||||
<!-- Margins around recent tasks. -->
|
||||
<dimen name="recents_grid_margin_left">15dp</dimen>
|
||||
<dimen name="recents_grid_margin_top">70dp</dimen>
|
||||
<dimen name="recents_grid_margin_right">15dp</dimen>
|
||||
<dimen name="recents_grid_margin_bottom">90dp</dimen>
|
||||
<!-- Margins around the "Clear all" button. -->
|
||||
<dimen name="recents_grid_clear_all_margin_left">0dp</dimen>
|
||||
<dimen name="recents_grid_clear_all_margin_top">30dp</dimen>
|
||||
<dimen name="recents_grid_clear_all_margin_right">15dp</dimen>
|
||||
<dimen name="recents_grid_clear_all_margin_bottom">0dp</dimen>
|
||||
<!-- Padding in between task views. -->
|
||||
<dimen name="recents_grid_inter_task_padding">15dp</dimen>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -34,6 +34,30 @@
|
||||
<item name="android:colorBackgroundCacheHint">@null</item>
|
||||
<item name="android:windowShowWallpaper">true</item>
|
||||
<item name="android:windowDisablePreview">true</item>
|
||||
<item name="clearAllStyle">@style/ClearAllButtonDefaultMargins</item>
|
||||
</style>
|
||||
|
||||
<style name="ClearAllButtonDefaultMargins">
|
||||
<item name="android:layout_marginStart">0dp</item>
|
||||
<item name="android:layout_marginTop">0dp</item>
|
||||
<item name="android:layout_marginEnd">0dp</item>
|
||||
<item name="android:layout_marginBottom">0dp</item>
|
||||
</style>
|
||||
|
||||
<!-- Grid-based Recents theme. -->
|
||||
<style name="RecentsTheme.Grid">
|
||||
<item name="android:windowBackground">@color/transparent</item>
|
||||
<item name="android:colorBackgroundCacheHint">@null</item>
|
||||
<item name="android:windowShowWallpaper">true</item>
|
||||
<item name="android:windowDisablePreview">true</item>
|
||||
<item name="clearAllStyle">@style/ClearAllButtonLargeMargins</item>
|
||||
</style>
|
||||
|
||||
<style name="ClearAllButtonLargeMargins">
|
||||
<item name="android:layout_marginStart">@dimen/recents_grid_clear_all_margin_left</item>
|
||||
<item name="android:layout_marginTop">@dimen/recents_grid_clear_all_margin_top</item>
|
||||
<item name="android:layout_marginEnd">@dimen/recents_grid_clear_all_margin_right</item>
|
||||
<item name="android:layout_marginBottom">@dimen/recents_grid_clear_all_margin_bottom</item>
|
||||
</style>
|
||||
|
||||
<!-- Performance optimized Recents theme (no wallpaper) -->
|
||||
|
||||
@@ -20,16 +20,22 @@ import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Bundle;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.view.WindowManager;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.AnimationSet;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.view.animation.ScaleAnimation;
|
||||
import android.view.animation.TranslateAnimation;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.internal.logging.MetricsLogger;
|
||||
@@ -61,17 +67,23 @@ import com.android.systemui.recents.model.TaskStack;
|
||||
import com.android.systemui.recents.views.TaskView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* The main grid recents activity started by the RecentsImpl.
|
||||
*/
|
||||
public class RecentsGridActivity extends Activity implements ViewTreeObserver.OnPreDrawListener {
|
||||
public class RecentsGridActivity extends Activity {
|
||||
public final static int MAX_VISIBLE_TASKS = 9;
|
||||
|
||||
private final static String TAG = "RecentsGridActivity";
|
||||
private final static int TITLE_BAR_HEIGHT_DP = 64;
|
||||
|
||||
private ArrayList<Integer> mMargins = new ArrayList<>();
|
||||
|
||||
private TaskStack mTaskStack;
|
||||
private List<Task> mTasks = new ArrayList<>();
|
||||
private List<TaskView> mTaskViews = new ArrayList<>();
|
||||
private ArrayList<Task> mTasks = new ArrayList<>();
|
||||
private ArrayList<TaskView> mTaskViews = new ArrayList<>();
|
||||
private ArrayList<Rect> mTaskViewRects;
|
||||
private FrameLayout mRecentsView;
|
||||
private TextView mEmptyView;
|
||||
private View mClearAllButton;
|
||||
@@ -80,6 +92,10 @@ public class RecentsGridActivity extends Activity implements ViewTreeObserver.On
|
||||
private Rect mDisplayRect = new Rect();
|
||||
private LayoutInflater mInflater;
|
||||
private boolean mTouchExplorationEnabled;
|
||||
private Point mScreenSize;
|
||||
private int mTitleBarHeightPx;
|
||||
private int mStatusBarHeightPx;
|
||||
private int mNavigationBarHeightPx;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
@@ -87,12 +103,28 @@ public class RecentsGridActivity extends Activity implements ViewTreeObserver.On
|
||||
setContentView(R.layout.recents_grid);
|
||||
SystemServicesProxy ssp = Recents.getSystemServices();
|
||||
|
||||
Resources res = getResources();
|
||||
Integer[] margins = {
|
||||
res.getDimensionPixelSize(R.dimen.recents_grid_margin_left),
|
||||
res.getDimensionPixelSize(R.dimen.recents_grid_margin_top),
|
||||
res.getDimensionPixelSize(R.dimen.recents_grid_margin_right),
|
||||
res.getDimensionPixelSize(R.dimen.recents_grid_margin_bottom),
|
||||
};
|
||||
mMargins.addAll(Arrays.asList(margins));
|
||||
|
||||
mInflater = LayoutInflater.from(this);
|
||||
Configuration appConfiguration = Utilities.getAppConfiguration(this);
|
||||
mDisplayRect = ssp.getDisplayRect();
|
||||
mLastDisplayOrientation = appConfiguration.orientation;
|
||||
mLastDisplayDensity = appConfiguration.densityDpi;
|
||||
mTouchExplorationEnabled = ssp.isTouchExplorationEnabled();
|
||||
mScreenSize = new Point();
|
||||
getWindowManager().getDefaultDisplay().getRealSize(mScreenSize);
|
||||
DisplayMetrics metrics = res.getDisplayMetrics();
|
||||
mTitleBarHeightPx = (int) (TITLE_BAR_HEIGHT_DP *
|
||||
((float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT));
|
||||
mStatusBarHeightPx = res.getDimensionPixelSize(R.dimen.status_bar_height);
|
||||
mNavigationBarHeightPx = res.getDimensionPixelSize(R.dimen.navigation_bar_height);
|
||||
|
||||
mRecentsView = (FrameLayout) findViewById(R.id.recents_view);
|
||||
mRecentsView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
|
||||
@@ -100,8 +132,7 @@ public class RecentsGridActivity extends Activity implements ViewTreeObserver.On
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
|
||||
getWindow().getAttributes().privateFlags |=
|
||||
WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
|
||||
LinearLayout recentsContainer = (LinearLayout) findViewById(R.id.recents_container);
|
||||
mEmptyView = (TextView) mInflater.inflate(R.layout.recents_empty, recentsContainer, false);
|
||||
mEmptyView = (TextView) mInflater.inflate(R.layout.recents_empty, mRecentsView, false);
|
||||
mClearAllButton = findViewById(R.id.button);
|
||||
|
||||
FrameLayout.LayoutParams emptyViewLayoutParams = new FrameLayout.LayoutParams(
|
||||
@@ -111,8 +142,8 @@ public class RecentsGridActivity extends Activity implements ViewTreeObserver.On
|
||||
mRecentsView.addView(mEmptyView);
|
||||
|
||||
mClearAllButton.setVisibility(View.VISIBLE);
|
||||
LinearLayout.LayoutParams lp =
|
||||
(LinearLayout.LayoutParams) mClearAllButton.getLayoutParams();
|
||||
FrameLayout.LayoutParams lp =
|
||||
(FrameLayout.LayoutParams) mClearAllButton.getLayoutParams();
|
||||
lp.gravity = Gravity.END;
|
||||
|
||||
mClearAllButton.setOnClickListener(v -> {
|
||||
@@ -154,6 +185,57 @@ public class RecentsGridActivity extends Activity implements ViewTreeObserver.On
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts animations for each task view to either enlarge it to the size of the screen (when
|
||||
* launching a task), or (if {@code reverse} is true, to reduce it from the size of the screen
|
||||
* back to its place in the recents layout (when opening recents).
|
||||
* @param animationListener An animation listener for executing code before or after the
|
||||
* animations run.
|
||||
* @param reverse Whether the blow-up animations should be run in reverse.
|
||||
*/
|
||||
private void startBlowUpAnimations(Animation.AnimationListener animationListener,
|
||||
boolean reverse) {
|
||||
if (mTaskViews.size() == 0) {
|
||||
return;
|
||||
}
|
||||
int screenWidth = mLastDisplayOrientation == Configuration.ORIENTATION_LANDSCAPE
|
||||
? mScreenSize.x : mScreenSize.y;
|
||||
int screenHeight = mLastDisplayOrientation == Configuration.ORIENTATION_LANDSCAPE
|
||||
? mScreenSize.y : mScreenSize.x;
|
||||
screenHeight -= mStatusBarHeightPx + mNavigationBarHeightPx;
|
||||
for (int i = 0; i < mTaskViews.size(); i++) {
|
||||
View tv = mTaskViews.get(i);
|
||||
AnimationSet animations = new AnimationSet(true /* shareInterpolator */);
|
||||
animations.setInterpolator(new DecelerateInterpolator());
|
||||
if (i == 0 && animationListener != null) {
|
||||
animations.setAnimationListener(animationListener);
|
||||
}
|
||||
animations.setFillBefore(reverse);
|
||||
animations.setFillAfter(!reverse);
|
||||
Rect initialRect = mTaskViewRects.get(mTaskViewRects.size() - 1 - i);
|
||||
int xDelta = - initialRect.left;
|
||||
int yDelta = - initialRect.top - mTitleBarHeightPx + mStatusBarHeightPx;
|
||||
TranslateAnimation translate = new TranslateAnimation(
|
||||
reverse ? xDelta : 0, reverse ? 0 : xDelta,
|
||||
reverse ? yDelta : 0, reverse ? 0 : yDelta);
|
||||
translate.setDuration(250);
|
||||
animations.addAnimation(translate);
|
||||
|
||||
|
||||
float xScale = (float) screenWidth / (float) initialRect.width();
|
||||
float yScale = (float) screenHeight /
|
||||
((float) initialRect.height() - mTitleBarHeightPx);
|
||||
ScaleAnimation scale = new ScaleAnimation(
|
||||
reverse ? xScale : 1, reverse ? 1 : xScale,
|
||||
reverse ? yScale : 1, reverse ? 1 : yScale,
|
||||
Animation.ABSOLUTE, 0, Animation.ABSOLUTE, mStatusBarHeightPx);
|
||||
scale.setDuration(300);
|
||||
animations.addAnimation(scale);
|
||||
|
||||
tv.startAnimation(animations);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateControlVisibility() {
|
||||
boolean empty = (mTasks.size() == 0);
|
||||
mClearAllButton.setVisibility(empty ? View.INVISIBLE : View.VISIBLE);
|
||||
@@ -163,7 +245,7 @@ public class RecentsGridActivity extends Activity implements ViewTreeObserver.On
|
||||
}
|
||||
}
|
||||
|
||||
private void updateRecentsTasks() {
|
||||
private void updateModel() {
|
||||
RecentsTaskLoader loader = Recents.getTaskLoader();
|
||||
RecentsTaskLoadPlan plan = RecentsImpl.consumeInstanceLoadPlan();
|
||||
if (plan == null) {
|
||||
@@ -174,29 +256,66 @@ public class RecentsGridActivity extends Activity implements ViewTreeObserver.On
|
||||
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;
|
||||
loadOpts.numVisibleTasks = MAX_VISIBLE_TASKS;
|
||||
loadOpts.numVisibleTaskThumbnails = MAX_VISIBLE_TASKS;
|
||||
loader.loadTasks(this, plan, loadOpts);
|
||||
|
||||
mTasks = mTaskStack.getStackTasks();
|
||||
}
|
||||
|
||||
updateControlVisibility();
|
||||
|
||||
clearTaskViews();
|
||||
private void updateViews() {
|
||||
int screenWidth = mLastDisplayOrientation == Configuration.ORIENTATION_LANDSCAPE
|
||||
? mScreenSize.x : mScreenSize.y;
|
||||
int screenHeight = mLastDisplayOrientation == Configuration.ORIENTATION_LANDSCAPE
|
||||
? mScreenSize.y : mScreenSize.x;
|
||||
int paddingPixels = getResources().getDimensionPixelSize(
|
||||
R.dimen.recents_grid_inter_task_padding);
|
||||
mTaskViewRects = TaskGridLayoutAlgorithm.getRectsForTaskCount(
|
||||
mTasks.size(), screenWidth, screenHeight, getAppRectRatio(), paddingPixels,
|
||||
mMargins, mTitleBarHeightPx);
|
||||
boolean recycleViews = (mTaskViews.size() == mTasks.size());
|
||||
if (!recycleViews) {
|
||||
clearTaskViews();
|
||||
}
|
||||
for (int i = 0; i < mTasks.size(); i++) {
|
||||
Task task = mTasks.get(i);
|
||||
TaskView taskView = createView();
|
||||
// We keep the same ordering in the model as other Recents flavors (older tasks are
|
||||
// first in the stack) so that the logic can be similar, but we reverse the order
|
||||
// when placing views on the screen so that most recent tasks are displayed first.
|
||||
Rect rect = mTaskViewRects.get(mTaskViewRects.size() - 1 - i);
|
||||
TaskView taskView;
|
||||
if (recycleViews) {
|
||||
taskView = mTaskViews.get(i);
|
||||
} else {
|
||||
taskView = createView();
|
||||
}
|
||||
taskView.onTaskBound(task, mTouchExplorationEnabled, mLastDisplayOrientation,
|
||||
mDisplayRect);
|
||||
Recents.getTaskLoader().loadTaskData(task);
|
||||
taskView.setTouchEnabled(true);
|
||||
// Show dismiss button right away.
|
||||
taskView.startNoUserInteractionAnimation();
|
||||
mTaskViews.add(taskView);
|
||||
taskView.setLayoutParams(new FrameLayout.LayoutParams(rect.width(), rect.height()));
|
||||
taskView.setTranslationX(rect.left);
|
||||
taskView.setTranslationY(rect.top);
|
||||
if (!recycleViews) {
|
||||
mRecentsView.addView(taskView);
|
||||
mTaskViews.add(taskView);
|
||||
}
|
||||
}
|
||||
updateControlVisibility();
|
||||
}
|
||||
|
||||
private float getAppRectRatio() {
|
||||
if (mLastDisplayOrientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
return (float) mScreenSize.x /
|
||||
(float) (mScreenSize.y - mStatusBarHeightPx - mNavigationBarHeightPx);
|
||||
} else {
|
||||
return (float) mScreenSize.y /
|
||||
(float) (mScreenSize.x - mStatusBarHeightPx - mNavigationBarHeightPx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,13 +323,23 @@ public class RecentsGridActivity extends Activity implements ViewTreeObserver.On
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, true));
|
||||
mRecentsView.getViewTreeObserver().addOnPreDrawListener(this);
|
||||
}
|
||||
updateModel();
|
||||
updateViews();
|
||||
if (mTaskViews.size() > 0) {
|
||||
mTaskViews.get(mTaskViews.size() - 1).bringToFront();
|
||||
}
|
||||
startBlowUpAnimations(new Animation.AnimationListener() {
|
||||
@Override
|
||||
public void onAnimationStart(Animation animation) { }
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
updateRecentsTasks();
|
||||
@Override
|
||||
public void onAnimationEnd(Animation animation) {
|
||||
updateViews();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animation animation) { }
|
||||
}, true /* reverse */);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -237,37 +366,13 @@ public class RecentsGridActivity extends Activity implements ViewTreeObserver.On
|
||||
// Notify of the config change.
|
||||
Configuration newDeviceConfiguration = Utilities.getAppConfiguration(this);
|
||||
mDisplayRect = Recents.getSystemServices().getDisplayRect();
|
||||
mRecentsView.getViewTreeObserver().addOnPreDrawListener(this);
|
||||
mRecentsView.requestLayout();
|
||||
int numStackTasks = mTaskStack.getStackTaskCount();
|
||||
EventBus.getDefault().send(new ConfigurationChangedEvent(false /* fromMultiWindow */,
|
||||
mLastDisplayOrientation != newDeviceConfiguration.orientation,
|
||||
mLastDisplayDensity != newDeviceConfiguration.densityDpi, numStackTasks > 0));
|
||||
mLastDisplayOrientation = newDeviceConfiguration.orientation;
|
||||
mLastDisplayDensity = newDeviceConfiguration.densityDpi;
|
||||
}
|
||||
|
||||
@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 */);
|
||||
removeTaskViews();
|
||||
for (int i = 0; i < rects.size(); i++) {
|
||||
Rect rect = rects.get(i);
|
||||
// We keep the same ordering in the model as other Recents flavors (older tasks are
|
||||
// first in the stack) so that the logic can be similar, but we reverse the order
|
||||
// when placing views on the screen so that most recent tasks are displayed first.
|
||||
View taskView = mTaskViews.get(rects.size() - 1 - i);
|
||||
taskView.setLayoutParams(new FrameLayout.LayoutParams(rect.width(), rect.height()));
|
||||
taskView.setTranslationX(rect.left);
|
||||
taskView.setTranslationY(rect.top);
|
||||
mRecentsView.addView(taskView);
|
||||
}
|
||||
return true;
|
||||
updateViews();
|
||||
}
|
||||
|
||||
void dismissRecentsToHome() {
|
||||
@@ -360,7 +465,8 @@ public class RecentsGridActivity extends Activity implements ViewTreeObserver.On
|
||||
EventBus.getDefault().send(new DeleteTaskDataEvent(tasks.get(i)));
|
||||
}
|
||||
mTasks = new ArrayList<>();
|
||||
updateRecentsTasks();
|
||||
updateModel();
|
||||
updateViews();
|
||||
|
||||
MetricsLogger.action(this, MetricsEvent.OVERVIEW_DISMISS_ALL);
|
||||
}
|
||||
@@ -390,6 +496,9 @@ public class RecentsGridActivity extends Activity implements ViewTreeObserver.On
|
||||
}
|
||||
|
||||
public final void onBusEvent(LaunchTaskEvent event) {
|
||||
event.taskView.bringToFront();
|
||||
startActivity(event.task.key.baseIntent);
|
||||
// Eventually we should start blow-up animations here, but we need to make sure it's done
|
||||
// in parallel with starting the activity so that we don't introduce unneeded latency.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,66 +19,111 @@ import android.graphics.Rect;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
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);
|
||||
public enum VerticalGravity {
|
||||
START, END, CENTER
|
||||
}
|
||||
|
||||
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);
|
||||
public static final List<Integer> ZERO_MARGIN = new ArrayList<>();
|
||||
static {
|
||||
Integer[] zero = {0, 0, 0, 0};
|
||||
ZERO_MARGIN.addAll(Arrays.asList(zero));
|
||||
}
|
||||
private static final String TAG = "TaskGridLayoutAlgorithm";
|
||||
|
||||
/**
|
||||
* Calculates the adequate rectangles for the specified number of tasks to be layed out on
|
||||
* the screen.
|
||||
* @param count The number of task views to layout.
|
||||
* @param containerWidth The width of the whole area containing those tasks.
|
||||
* @param containerHeight The height of the whole area containing those tasks.
|
||||
* @param screenRatio The ratio of the device's screen, so that tasks have the same aspect
|
||||
* ratio (ignoring the title bar).
|
||||
* @param padding The amount of padding, in pixels, in between task views.
|
||||
* @param margins The amount of space to be left blank around the area on the left, top, right
|
||||
* and bottom.
|
||||
* @param titleBarHeight The height, in pixels, of the task views title bar.
|
||||
* @return A list of rectangles to be used for layout.
|
||||
*/
|
||||
static ArrayList<Rect> getRectsForTaskCount(int count, int containerWidth, int containerHeight,
|
||||
float screenRatio, int padding, List<Integer> margins, int titleBarHeight) {
|
||||
return getRectsForTaskCount(count, containerWidth, containerHeight, screenRatio, padding,
|
||||
margins, titleBarHeight, null, VerticalGravity.CENTER);
|
||||
}
|
||||
|
||||
private static ArrayList<Rect> getRectsForTaskCount(int count, int containerWidth,
|
||||
int containerHeight, float screenRatio, int padding, List<Integer> margins,
|
||||
int titleBarHeight, Rect preCalculatedTile, VerticalGravity gravity) {
|
||||
ArrayList<Rect> rects = new ArrayList<>(count);
|
||||
boolean landscape = (containerWidth > containerHeight);
|
||||
containerWidth -= margins.get(0) + margins.get(2);
|
||||
containerHeight -= margins.get(1) + margins.get(3);
|
||||
|
||||
// We support at most 9 tasks in this layout.
|
||||
count = Math.min(count, 9);
|
||||
count = Math.min(count, RecentsGridActivity.MAX_VISIBLE_TASKS);
|
||||
|
||||
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));
|
||||
}
|
||||
if (count <= 3) {
|
||||
// Base case: single line.
|
||||
int taskWidth, taskHeight;
|
||||
if (preCalculatedTile != null) {
|
||||
taskWidth = preCalculatedTile.width();
|
||||
taskHeight = preCalculatedTile.height();
|
||||
} 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));
|
||||
// Divide available width in equal parts.
|
||||
int maxTaskWidth = (containerWidth - (count - 1) * padding) / count;
|
||||
int maxTaskHeight = containerHeight;
|
||||
if (maxTaskHeight >= maxTaskWidth / screenRatio + titleBarHeight) {
|
||||
// Width bound.
|
||||
taskWidth = maxTaskWidth;
|
||||
taskHeight = (int) (maxTaskWidth / screenRatio + titleBarHeight);
|
||||
} else {
|
||||
// Height bound.
|
||||
taskHeight = maxTaskHeight;
|
||||
taskWidth = (int) ((taskHeight - titleBarHeight) * screenRatio);
|
||||
}
|
||||
}
|
||||
int emptySpaceX = containerWidth - (count * taskWidth) - (count - 1) * padding;
|
||||
int emptySpaceY = containerHeight - taskHeight;
|
||||
for (int i = 0; i < count; i++) {
|
||||
int left = emptySpaceX / 2 + i * taskWidth + i * padding;
|
||||
int top;
|
||||
switch (gravity) {
|
||||
case CENTER:
|
||||
top = emptySpaceY / 2;
|
||||
break;
|
||||
case END:
|
||||
top = emptySpaceY;
|
||||
break;
|
||||
case START:
|
||||
default:
|
||||
top = 0;
|
||||
break;
|
||||
}
|
||||
Rect rect = new Rect(left, top, left + taskWidth, top + taskHeight);
|
||||
rect.offset(margins.get(0), margins.get(1));
|
||||
rects.add(rect);
|
||||
}
|
||||
} 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);
|
||||
List<Rect> rectsA = getRectsForTaskCount(lineTaskCount, containerWidth, lineHeight,
|
||||
screenRatio, padding, ZERO_MARGIN, titleBarHeight, null, VerticalGravity.END);
|
||||
List<Rect> rectsB = getRectsForTaskCount(count - lineTaskCount, containerWidth,
|
||||
lineHeight, screenRatio, padding, ZERO_MARGIN, titleBarHeight, rectsA.get(0),
|
||||
VerticalGravity.START);
|
||||
for (int i = 0; i < rectsA.size(); i++) {
|
||||
rectsA.get(i).offset(margins.get(0), margins.get(1));
|
||||
}
|
||||
for (int i = 0; i < rectsB.size(); i++) {
|
||||
rectsB.get(i).offset(margins.get(0), margins.get(1) + lineHeight + padding);
|
||||
}
|
||||
rects.addAll(rectsA);
|
||||
rects.addAll(rectsB);
|
||||
@@ -86,19 +131,22 @@ class TaskGridLayoutAlgorithm {
|
||||
// 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);
|
||||
List<Rect> rectsA = getRectsForTaskCount(lineTaskCount, containerWidth, lineHeight,
|
||||
screenRatio, padding, ZERO_MARGIN, titleBarHeight, null, VerticalGravity.END);
|
||||
List<Rect> rectsB = getRectsForTaskCount(lineTaskCount, containerWidth, lineHeight,
|
||||
screenRatio, padding, ZERO_MARGIN, titleBarHeight, rectsA.get(0),
|
||||
VerticalGravity.END);
|
||||
List<Rect> rectsC = getRectsForTaskCount(count - (2 * lineTaskCount), containerWidth,
|
||||
lineHeight, screenRatio, padding, ZERO_MARGIN, titleBarHeight, rectsA.get(0),
|
||||
VerticalGravity.START);
|
||||
for (int i = 0; i < rectsA.size(); i++) {
|
||||
rectsA.get(i).offset(margins.get(0), margins.get(1));
|
||||
}
|
||||
for (Rect rect : rectsC) {
|
||||
rect.offset(0, 2 * (lineHeight + padding));
|
||||
for (int i = 0; i < rectsB.size(); i++) {
|
||||
rectsB.get(i).offset(margins.get(0), margins.get(1) + lineHeight + padding);
|
||||
}
|
||||
for (int i = 0; i < rectsC.size(); i++) {
|
||||
rectsC.get(i).offset(margins.get(0), margins.get(1) + 2 * (lineHeight + padding));
|
||||
}
|
||||
rects.addAll(rectsA);
|
||||
rects.addAll(rectsB);
|
||||
|
||||
@@ -19,39 +19,44 @@ import android.graphics.Rect;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
@SmallTest
|
||||
public class TaskGridLayoutAlgorithmTest extends SysuiTestCase {
|
||||
|
||||
public void testMethodName_ExpectedBehavior() {
|
||||
assertTrue(true);
|
||||
}
|
||||
private static final List<Integer> ZERO_MARGIN = TaskGridLayoutAlgorithm.ZERO_MARGIN;
|
||||
|
||||
@Test
|
||||
public void testOneTile() {
|
||||
List<Rect> rects = TaskGridLayoutAlgorithm.getRectsForTaskCount(
|
||||
1, 1000, 500, false /* allowLineOfThree */, 0 /* padding */);
|
||||
1, 1000, 1000, 1 /* screenRatio */, 0 /* padding */, ZERO_MARGIN, 0);
|
||||
assertEquals(1, rects.size());
|
||||
Rect singleRect = rects.get(0);
|
||||
assertEquals(1000, singleRect.width());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTwoTilesLandscape() {
|
||||
List<Rect> rects = TaskGridLayoutAlgorithm.getRectsForTaskCount(
|
||||
2, 1200, 500, false /* allowLineOfThree */, 0 /* padding */);
|
||||
2, 1200, 500, 1.2f /* screenRatio */, 0 /* padding */, ZERO_MARGIN, 0);
|
||||
assertEquals(2, rects.size());
|
||||
for (Rect rect : rects) {
|
||||
assertEquals(600, rect.width());
|
||||
assertEquals(500, rect.height());
|
||||
assertEquals(499, rect.height());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTwoTilesLandscapeWithPadding() {
|
||||
List<Rect> rects = TaskGridLayoutAlgorithm.getRectsForTaskCount(
|
||||
2, 1200, 500, false /* allowLineOfThree */, 10 /* padding */);
|
||||
2, 1200, 500, 1.19f /* screenRatio */, 10 /* padding */, ZERO_MARGIN, 0);
|
||||
assertEquals(2, rects.size());
|
||||
Rect rectA = rects.get(0);
|
||||
Rect rectB = rects.get(1);
|
||||
@@ -60,46 +65,46 @@ public class TaskGridLayoutAlgorithmTest extends SysuiTestCase {
|
||||
assertEquals(605, rectB.left);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTwoTilesPortrait() {
|
||||
List<Rect> rects = TaskGridLayoutAlgorithm.getRectsForTaskCount(
|
||||
2, 500, 1200, false /* allowLineOfThree */, 0 /* padding */);
|
||||
2, 500, 1200, 1 /* screenRatio */, 0 /* padding */, ZERO_MARGIN, 0);
|
||||
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.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);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThreeTiles() {
|
||||
List<Rect> rects = TaskGridLayoutAlgorithm.getRectsForTaskCount(
|
||||
3, 1200, 500, 2 /* screenRatio */, 0 /* padding */, ZERO_MARGIN, 0);
|
||||
assertEquals(3, rects.size());
|
||||
for (Rect rect : rects) {
|
||||
assertEquals(400, rect.width());
|
||||
assertEquals(200, rect.height());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFourTiles() {
|
||||
List<Rect> rects = TaskGridLayoutAlgorithm.getRectsForTaskCount(
|
||||
4, 1200, 500, false /* allowLineOfThree */, 0 /* padding */);
|
||||
4, 1200, 500, 2.4f /* screenRatio */, 0 /* padding */, ZERO_MARGIN, 0);
|
||||
assertEquals(4, rects.size());
|
||||
for (Rect rect : rects) {
|
||||
assertEquals(600, rect.width());
|
||||
assertEquals(250, rect.height());
|
||||
assertEquals(249, rect.height());
|
||||
}
|
||||
Rect rectD = rects.get(3);
|
||||
assertEquals(600, rectD.left);
|
||||
assertEquals(250, rectD.top);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNineTiles() {
|
||||
List<Rect> rects = TaskGridLayoutAlgorithm.getRectsForTaskCount(
|
||||
9, 1200, 600, false /* allowLineOfThree */, 0 /* padding */);
|
||||
9, 1200, 600, 2 /* screenRatio */, 0 /* padding */, ZERO_MARGIN, 0);
|
||||
assertEquals(9, rects.size());
|
||||
for (Rect rect : rects) {
|
||||
assertEquals(400, rect.width());
|
||||
|
||||
Reference in New Issue
Block a user