Change the task layout to better match mocks, add "in" animation
This only adds an animation when transitioning into recents. "Out" animations will come later as they need to be run in parallel with launching the activity not to introduce any latency. Test: Open recents on sw600dp device, check recents layout/animations Bug: 32101881 Change-Id: I367f8e5c106cd06d2a7833c165ecb960a7821ed9
This commit is contained in:
@@ -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