Part 1: Tweaking history transition to match design.

- Adding background scrim that can be controlled across transitions 
  within recents.  As a result, we can remove the status bar scrim.
- Moving the history view into the RecentsView now that it animates in
  parallel with the task stack
- Transition home from history no longer goes back to stack view first
- Removing some extra allocations when going into history and loading 
  tasks

Change-Id: I665baefcdd619de5e9366923eaaf4c558261141a
This commit is contained in:
Winson
2016-01-25 17:33:34 -08:00
parent c0b2f09a92
commit 49df4209cf
26 changed files with 534 additions and 256 deletions

View File

@@ -0,0 +1,28 @@
<?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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFFFFF"
android:pathData="M13 3c-4.97 0-9 4.03-9 9H1l3.89 3.89 .07 .14L9 12H6c0-3.87 3.13-7 7-7s7 3.13 7
7-3.13 7-7 7c-1.93 0-3.68-.79-4.94-2.06l-1.42 1.42C8.27 19.99 10.51 21 13
21c4.97 0 9-4.03 9-9s-4.03-9-9-9zm-1 5v5l4.28 2.54 .72 -1.21-3.5-2.08V8H12z" />
</vector>

View File

@@ -17,14 +17,6 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Status Bar Scrim View -->
<ImageView
android:id="@+id/status_bar_scrim"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|top"
android:scaleType="fitXY"
android:src="@drawable/recents_status_gradient" />
<!-- Recents View -->
<com.android.systemui.recents.views.RecentsView
@@ -33,12 +25,6 @@
android:layout_height="match_parent">
</com.android.systemui.recents.views.RecentsView>
<!-- History View -->
<ViewStub android:id="@+id/history_view_stub"
android:layout="@layout/recents_history"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<!-- Nav Bar Scrim View -->
<ImageView
android:id="@+id/nav_bar_scrim"

View File

@@ -24,5 +24,4 @@
android:textColor="#ffffffff"
android:text="@string/recents_empty_message"
android:fontFamily="sans-serif"
android:background="#80000000"
android:visibility="gone" />

View File

@@ -17,7 +17,6 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#99000000"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
android:id="@+id/list"

View File

@@ -15,15 +15,24 @@
-->
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="start|center_vertical"
android:text="@string/recents_show_history_button_label"
android:paddingStart="14dp"
android:paddingEnd="14dp"
android:paddingTop="12dp"
android:paddingBottom="12dp"
android:text="@string/recents_history_button_label"
android:textSize="14sp"
android:textColor="#FFFFFF"
android:textAllCaps="true"
android:drawableStart="@drawable/ic_history"
android:drawablePadding="6dp"
android:shadowColor="#99000000"
android:shadowDx="0"
android:shadowDy="2"
android:shadowRadius="5"
android:fontFamily="sans-serif-medium" />
android:fontFamily="sans-serif-medium"
android:background="?android:selectableItemBackground"
android:visibility="invisible" />

View File

@@ -15,10 +15,12 @@
-->
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:theme="@android:style/Theme.Material"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="12dp"
android:paddingStart="4dp"
android:paddingEnd="4dp"
android:paddingTop="12dp"
android:paddingBottom="12dp"
android:gravity="start"
android:textSize="14sp"
android:textColor="#009688"

View File

@@ -15,7 +15,6 @@
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:theme="@android:style/Theme.Material"
android:layout_width="match_parent"
android:layout_height="48dp"
android:orientation="horizontal"
@@ -27,7 +26,8 @@
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="center"
android:layout_marginStart="16dp" />
android:layout_marginStart="4dp"
android:layout_marginEnd="12dp" />
<TextView
android:id="@+id/description"
android:layout_width="0dp"

View File

@@ -715,9 +715,7 @@
<!-- Recents: Launch error string. [CHAR LIMIT=NONE] -->
<string name="recents_launch_error_message">Could not start <xliff:g id="app" example="Calendar">%s</xliff:g>.</string>
<!-- Recents: Show history string. [CHAR LIMIT=NONE] -->
<string name="recents_show_history_button_label">More</string>
<!-- Recents: A format string to set the number of availabe historical tasks in recents. [CHAR LIMIT=NONE] -->
<string name="recents_history_label_format"><xliff:g id="number">%d</xliff:g> More</string>
<string name="recents_history_button_label">History</string>
<!-- Recents: MultiStack add stack split horizontal radio button. [CHAR LIMIT=NONE] -->
<string name="recents_multistack_add_stack_dialog_split_horizontal">Split Horizontal</string>

View File

@@ -20,7 +20,7 @@
<item name="android:windowAnimationStyle">@style/Animation.RecentsActivity</item>
</style>
<style name="RecentsTheme" parent="@android:style/Theme.Material.Light">
<style name="RecentsTheme" parent="@android:style/Theme.Material">
<!-- NoTitle -->
<item name="android:windowNoTitle">true</item>
<!-- Misc -->

View File

@@ -57,6 +57,7 @@ import com.android.systemui.recents.events.activity.LaunchTaskFailedEvent;
import com.android.systemui.recents.events.activity.LaunchTaskSucceededEvent;
import com.android.systemui.recents.events.activity.ShowHistoryEvent;
import com.android.systemui.recents.events.activity.TaskStackUpdatedEvent;
import com.android.systemui.recents.events.activity.ToggleHistoryEvent;
import com.android.systemui.recents.events.activity.ToggleRecentsEvent;
import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
@@ -105,8 +106,6 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
// Top level views
private RecentsView mRecentsView;
private SystemBarScrimViews mScrimViews;
private ViewStub mHistoryViewStub;
private RecentsHistoryView mHistoryView;
// Search AppWidget
private AppWidgetProviderInfo mSearchWidgetInfo;
@@ -127,12 +126,14 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
* just finishing the activity since we don't know what is behind Recents in the task stack.
*/
class FinishRecentsRunnable implements Runnable {
Intent mLaunchIntent;
ActivityOptions mOpts;
/**
* Creates a finish runnable that starts the specified intent.
*/
public FinishRecentsRunnable(Intent launchIntent) {
public FinishRecentsRunnable(Intent launchIntent, ActivityOptions opts) {
mLaunchIntent = launchIntent;
}
@@ -141,13 +142,16 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
try {
RecentsActivityLaunchState launchState =
Recents.getConfiguration().getLaunchState();
ActivityOptions opts = ActivityOptions.makeCustomAnimation(RecentsActivity.this,
launchState.launchedFromSearchHome ?
R.anim.recents_to_search_launcher_enter :
R.anim.recents_to_launcher_enter,
launchState.launchedFromSearchHome ?
R.anim.recents_to_search_launcher_exit :
R.anim.recents_to_launcher_exit);
ActivityOptions opts = mOpts;
if (opts == null) {
opts = ActivityOptions.makeCustomAnimation(RecentsActivity.this,
launchState.launchedFromSearchHome ?
R.anim.recents_to_search_launcher_enter :
R.anim.recents_to_launcher_enter,
launchState.launchedFromSearchHome ?
R.anim.recents_to_search_launcher_exit :
R.anim.recents_to_launcher_exit);
}
startActivityAsUser(mLaunchIntent, opts.toBundle(), UserHandle.CURRENT);
} catch (Exception e) {
Log.e(TAG, getString(R.string.recents_launch_error_message, "Home"), e);
@@ -205,12 +209,9 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
int launchTaskIndexInStack = launchTarget != null
? stack.indexOfStackTask(launchTarget)
: 0;
boolean hasStatusBarScrim = taskCount > 0;
boolean animateStatusBarScrim = launchState.launchedFromHome;
boolean hasNavBarScrim = (taskCount > 0) && !config.hasTransposedNavBar;
boolean animateNavBarScrim = true;
mScrimViews.prepareEnterRecentsAnimation(hasStatusBarScrim, animateStatusBarScrim,
hasNavBarScrim, animateNavBarScrim);
mScrimViews.prepareEnterRecentsAnimation(hasNavBarScrim, animateNavBarScrim);
// Keep track of whether we launched from the nav bar button or via alt-tab
if (launchState.launchedWithAltTab) {
@@ -234,8 +235,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
* Dismisses the history view back into the stack view.
*/
boolean dismissHistory() {
// Try and hide the history view first
if (mHistoryView != null && mHistoryView.isVisible()) {
if (mRecentsView.isHistoryVisible()) {
EventBus.getDefault().send(new HideHistoryEvent(true /* animate */));
return true;
}
@@ -287,9 +287,24 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
* Dismisses Recents directly to Home without checking whether it is currently visible.
*/
void dismissRecentsToHome(boolean animateTaskViews) {
dismissRecentsToHome(animateTaskViews, null);
}
/**
* Dismisses Recents directly to Home without checking whether it is currently visible.
*
* @param overrideAnimation If not null, will override the default animation that is based on
* how Recents was launched.
*/
void dismissRecentsToHome(boolean animateTaskViews, ActivityOptions overrideAnimation) {
DismissRecentsToHomeAnimationStarted dismissEvent =
new DismissRecentsToHomeAnimationStarted(animateTaskViews);
dismissEvent.addPostAnimationCallback(mFinishLaunchHomeRunnable);
if (overrideAnimation != null) {
dismissEvent.addPostAnimationCallback(new FinishRecentsRunnable(
mFinishLaunchHomeRunnable.mLaunchIntent, overrideAnimation));
} else {
dismissEvent.addPostAnimationCallback(mFinishLaunchHomeRunnable);
}
dismissEvent.addPostAnimationCallback(new Runnable() {
@Override
public void run() {
@@ -342,7 +357,6 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
mRecentsView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
mHistoryViewStub = (ViewStub) findViewById(R.id.history_view_stub);
mScrimViews = new SystemBarScrimViews(this);
getWindow().getAttributes().privateFlags |=
WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
@@ -360,7 +374,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
homeIntent.addCategory(Intent.CATEGORY_HOME);
homeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
mFinishLaunchHomeRunnable = new FinishRecentsRunnable(homeIntent);
mFinishLaunchHomeRunnable = new FinishRecentsRunnable(homeIntent, null);
// Bind the search app widget when we first start up
if (RecentsDebugFlags.Static.EnableSearchBar) {
@@ -441,7 +455,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
// Reset some states
mIgnoreAltTabRelease = false;
if (mHistoryView != null) {
if (mRecentsView.isHistoryVisible()) {
EventBus.getDefault().send(new HideHistoryEvent(false /* animate */));
}
@@ -503,8 +517,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(KEY_SAVED_STATE_HISTORY_VISIBLE,
(mHistoryView != null) && mHistoryView.isVisible());
outState.putBoolean(KEY_SAVED_STATE_HISTORY_VISIBLE, mRecentsView.isHistoryVisible());
}
@Override
@@ -651,15 +664,12 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
}
} else if (event.triggeredFromHomeKey) {
// Otherwise, dismiss Recents to Home
if (mHistoryView != null && mHistoryView.isVisible()) {
HideHistoryEvent hideEvent = new HideHistoryEvent(true /* animate */);
hideEvent.addPostAnimationCallback(new Runnable() {
@Override
public void run() {
dismissRecentsToHome(true /* animateTaskViews */);
}
});
EventBus.getDefault().send(hideEvent);
if (mRecentsView.isHistoryVisible()) {
// If the history view is visible, then just cross-fade home
ActivityOptions opts = ActivityOptions.makeCustomAnimation(RecentsActivity.this,
R.anim.recents_to_launcher_enter,
R.anim.recents_to_launcher_exit);
dismissRecentsToHome(false /* animate */, opts);
} else {
dismissRecentsToHome(true /* animateTaskViews */);
@@ -795,21 +805,6 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
mIgnoreAltTabRelease = true;
}
public final void onBusEvent(ShowHistoryEvent event) {
if (mHistoryView == null) {
mHistoryView = (RecentsHistoryView) mHistoryViewStub.inflate();
// Since this history view is inflated by a view stub after the insets have already
// been applied, we have to set them ourselves initial from the insets that were last
// provided.
mHistoryView.setSystemInsets(mRecentsView.getSystemInsets());
}
mHistoryView.show(mRecentsView.getTaskStack(), event.getAnimationTrigger());
}
public final void onBusEvent(HideHistoryEvent event) {
mHistoryView.hide(event.animate, event.getAnimationTrigger());
}
private void refreshSearchWidgetView() {
if (mSearchWidgetInfo != null) {
SystemServicesProxy ssp = Recents.getSystemServices();

View File

@@ -21,7 +21,7 @@ import com.android.systemui.recents.events.EventBus;
/**
* This is sent when the history view will be closed.
*/
public class HideHistoryEvent extends EventBus.AnimatedEvent {
public class HideHistoryEvent extends EventBus.Event {
public final boolean animate;

View File

@@ -22,5 +22,11 @@ import com.android.systemui.recents.events.EventBus;
* This is sent when the history view button should be shown.
*/
public class ShowHistoryButtonEvent extends EventBus.Event {
// Simple event
// Whether or not to translate the history button when showing it
public final boolean translate;
public ShowHistoryButtonEvent(boolean translate) {
this.translate = translate;
}
}

View File

@@ -21,7 +21,7 @@ import com.android.systemui.recents.events.EventBus;
/**
* This is sent when the history view button is clicked.
*/
public class ShowHistoryEvent extends EventBus.AnimatedEvent {
public class ShowHistoryEvent extends EventBus.Event {
// Simple event

View File

@@ -0,0 +1,28 @@
/*
* 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.events.activity;
import com.android.systemui.recents.events.EventBus;
/**
* This is sent when the history view button is clicked.
*/
public class ToggleHistoryEvent extends EventBus.AnimatedEvent {
// Simple event
}

View File

@@ -0,0 +1,26 @@
/*
* 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.events.ui;
import com.android.systemui.recents.events.EventBus;
/**
* This is sent to reset the background scrim back to the initial state.
*/
public class ResetBackgroundScrimEvent extends EventBus.Event {
// Simple event
}

View File

@@ -0,0 +1,31 @@
/*
* 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.events.ui;
import com.android.systemui.recents.events.EventBus;
/**
* This is sent to request an update to the background scrim.
*/
public class UpdateBackgroundScrimEvent extends EventBus.Event {
public final float alpha;
public UpdateBackgroundScrimEvent(float alpha) {
this.alpha = alpha;
}
}

View File

@@ -166,14 +166,16 @@ public class RecentsHistoryAdapter extends RecyclerView.Adapter<RecentsHistoryAd
final List<Task> tasksMostRecent = new ArrayList<>(stack.getHistoricalTasks());
Collections.reverse(tasksMostRecent);
int prevDateKey = -1;
int taskCount = tasksMostRecent.size();
mRows.clear();
mTaskRowCount.clear();
for (Task task : tasksMostRecent) {
Calendar cal = Calendar.getInstance(l);
for (int i = 0; i < taskCount; i++) {
Task task = tasksMostRecent.get(i);
if (task.isFreeformTask()) {
continue;
}
Calendar cal = Calendar.getInstance(l);
cal.setTimeInMillis(task.key.lastActiveTime);
int dateKey = Objects.hash(cal.get(Calendar.YEAR), cal.get(Calendar.DAY_OF_YEAR));
if (dateKey != prevDateKey) {

View File

@@ -16,6 +16,7 @@
package com.android.systemui.recents.history;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Rect;
@@ -37,22 +38,29 @@ import com.android.systemui.recents.RecentsActivity;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.PackagesChangedEvent;
import com.android.systemui.recents.events.ui.ResetBackgroundScrimEvent;
import com.android.systemui.recents.events.ui.UpdateBackgroundScrimEvent;
import com.android.systemui.recents.misc.ReferenceCountedTrigger;
import com.android.systemui.recents.model.TaskStack;
import com.android.systemui.recents.views.AnimateableViewBounds;
import com.android.systemui.statusbar.phone.PhoneStatusBar;
/**
* A list of the recent tasks that are not in the stack.
*/
public class RecentsHistoryView extends LinearLayout {
public class RecentsHistoryView extends LinearLayout
implements ValueAnimator.AnimatorUpdateListener {
private static final String TAG = "RecentsHistoryView";
private static final boolean DEBUG = false;
private static final float TRANSLATION_Y_PCT = 0.25f;
private static final float BG_SCRIM_ALPHA = 0.625f;
private RecyclerView mRecyclerView;
private RecentsHistoryAdapter mAdapter;
private RecentsHistoryItemTouchCallbacks mItemTouchHandler;
private AnimateableViewBounds mViewBounds;
private boolean mIsVisible;
private Rect mSystemInsets = new Rect();
private int mHeaderHeight;
private Interpolator mFastOutSlowInInterpolator;
private Interpolator mFastOutLinearInInterpolator;
@@ -81,27 +89,28 @@ public class RecentsHistoryView extends LinearLayout {
com.android.internal.R.interpolator.fast_out_slow_in);
mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
com.android.internal.R.interpolator.fast_out_linear_in);
mViewBounds = new AnimateableViewBounds(this, 0);
setOutlineProvider(mViewBounds);
}
/**
* Updates this history view with the recent tasks, and then shows it.
*/
public void show(TaskStack stack, ReferenceCountedTrigger postHideAnimationTrigger) {
public void show(TaskStack stack, int stackHeight) {
setVisibility(View.VISIBLE);
setAlpha(0f);
postHideAnimationTrigger.addLastDecrementRunnable(new Runnable() {
@Override
public void run() {
animate()
.alpha(1f)
.setDuration(mHistoryTransitionDuration)
.setInterpolator(mFastOutSlowInInterpolator)
.withLayer()
.start();
}
});
setTranslationY(-stackHeight * TRANSLATION_Y_PCT);
animate()
.alpha(1f)
.translationY(0f)
.setDuration(mHistoryTransitionDuration)
.setInterpolator(mFastOutSlowInInterpolator)
.setUpdateListener(this)
.withLayer()
.start();
mAdapter.updateTasks(getContext(), stack);
mIsVisible = true;
EventBus.getDefault().send(new UpdateBackgroundScrimEvent(BG_SCRIM_ALPHA));
MetricsLogger.visible(mRecyclerView.getContext(), MetricsEvent.OVERVIEW_HISTORY);
}
@@ -109,31 +118,28 @@ public class RecentsHistoryView extends LinearLayout {
/**
* Hides this history view.
*/
public void hide(boolean animate, final ReferenceCountedTrigger postAnimationTrigger) {
public void hide(boolean animate, int stackHeight) {
if (animate) {
animate()
.alpha(0f)
.translationY(-stackHeight * TRANSLATION_Y_PCT)
.setDuration(mHistoryTransitionDuration)
.setInterpolator(mFastOutLinearInInterpolator)
.setUpdateListener(this)
.withEndAction(new Runnable() {
@Override
public void run() {
setVisibility(View.INVISIBLE);
if (postAnimationTrigger != null) {
postAnimationTrigger.decrement();
}
}
})
.withLayer()
.start();
if (postAnimationTrigger != null) {
postAnimationTrigger.increment();
}
} else {
setAlpha(0f);
setVisibility(View.INVISIBLE);
}
mIsVisible = false;
EventBus.getDefault().send(new ResetBackgroundScrimEvent());
MetricsLogger.hidden(mRecyclerView.getContext(), MetricsEvent.OVERVIEW_HISTORY);
}
@@ -142,7 +148,15 @@ public class RecentsHistoryView extends LinearLayout {
* Updates the system insets of this history view to the provided values.
*/
public void setSystemInsets(Rect systemInsets) {
mSystemInsets.set(systemInsets.left, systemInsets.top, systemInsets.right, systemInsets.bottom);
mSystemInsets.set(systemInsets);
requestLayout();
}
/**
* Updates the header height to account for the history button bar.
*/
public void setHeaderHeight(int height) {
mHeaderHeight = height;
requestLayout();
}
@@ -177,7 +191,7 @@ public class RecentsHistoryView extends LinearLayout {
int stackHeightPadding = mContext.getResources().getDimensionPixelSize(
R.dimen.recents_stack_top_padding);
mRecyclerView.setPadding(stackWidthPadding + mSystemInsets.left,
stackHeightPadding + mSystemInsets.top,
stackHeightPadding + mSystemInsets.top + mHeaderHeight,
stackWidthPadding + mSystemInsets.right, mSystemInsets.bottom);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
@@ -201,6 +215,13 @@ public class RecentsHistoryView extends LinearLayout {
return insets;
}
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// Clip the top of the view by the header bar height
int top = Math.max(0, (int) -getTranslationY()) + mSystemInsets.top + mHeaderHeight;
mViewBounds.setClipTop(top);
}
/**** EventBus Events ****/
public final void onBusEvent(PackagesChangedEvent event) {

View File

@@ -37,6 +37,7 @@ import com.android.systemui.recents.misc.SystemServicesProxy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Formatter;
import java.util.List;
@@ -130,6 +131,7 @@ public class RecentsTaskLoadPlan {
SparseIntArray affiliatedTaskCounts = new SparseIntArray();
String dismissDescFormat = mContext.getString(
R.string.accessibility_recents_item_will_be_dismissed);
Formatter dismissDescFormatter = new Formatter();
long lastStackActiveTime = Prefs.getLong(mContext,
Prefs.Key.OVERVIEW_LAST_STACK_TASK_ACTIVE_TIME, 0);
if (RecentsDebugFlags.Static.EnableMockTasks) {
@@ -168,7 +170,8 @@ public class RecentsTaskLoadPlan {
// Load the title, icon, and color
String title = loader.getAndUpdateActivityTitle(taskKey, t.taskDescription);
String contentDescription = loader.getAndUpdateContentDescription(taskKey, res);
String dismissDescription = String.format(dismissDescFormat, contentDescription);
String dismissDescription = dismissDescFormatter.format(dismissDescFormat,
contentDescription).toString();
Drawable icon = isStackTask
? loader.getAndUpdateActivityIcon(taskKey, t.taskDescription, res, false)
: null;

View File

@@ -48,10 +48,16 @@ public class AnimateableViewBounds extends ViewOutlineProvider {
@Override
public void getOutline(View view, Outline outline) {
outline.setAlpha(mMinAlpha + mAlpha / (1f - mMinAlpha));
outline.setRoundRect(mClipRect.left, mClipRect.top,
mSourceView.getWidth() - mClipRect.right,
mSourceView.getHeight() - mClipRect.bottom,
mCornerRadius);
if (mCornerRadius > 0) {
outline.setRoundRect(mClipRect.left, mClipRect.top,
mSourceView.getWidth() - mClipRect.right,
mSourceView.getHeight() - mClipRect.bottom,
mCornerRadius);
} else {
outline.setRect(mClipRect.left, mClipRect.top,
mSourceView.getWidth() - mClipRect.right,
mSourceView.getHeight() - mClipRect.bottom);
}
}
/** Sets the view outline alpha. */
@@ -63,6 +69,17 @@ public class AnimateableViewBounds extends ViewOutlineProvider {
}
}
/** Sets the top clip. */
public void setClipTop(int top) {
mClipRect.top = top;
updateClipBounds();
}
/** Returns the top clip. */
public int getClipTop() {
return mClipRect.top;
}
/** Sets the bottom clip. */
public void setClipBottom(int bottom) {
mClipRect.bottom = bottom;

View File

@@ -18,9 +18,13 @@ package com.android.systemui.recents.views;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Outline;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.util.ArraySet;
@@ -28,7 +32,9 @@ import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewOutlineProvider;
import android.view.ViewPropertyAnimator;
import android.view.ViewStub;
import android.view.WindowInsets;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
@@ -45,20 +51,24 @@ import com.android.systemui.recents.RecentsAppWidgetHostView;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.RecentsDebugFlags;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
import com.android.systemui.recents.events.activity.HideHistoryButtonEvent;
import com.android.systemui.recents.events.activity.HideHistoryEvent;
import com.android.systemui.recents.events.activity.LaunchTaskEvent;
import com.android.systemui.recents.events.activity.ShowHistoryButtonEvent;
import com.android.systemui.recents.events.activity.ShowHistoryEvent;
import com.android.systemui.recents.events.activity.TaskStackUpdatedEvent;
import com.android.systemui.recents.events.activity.ToggleHistoryEvent;
import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
import com.android.systemui.recents.events.ui.DraggingInRecentsEndedEvent;
import com.android.systemui.recents.events.ui.DraggingInRecentsEvent;
import com.android.systemui.recents.events.ui.ResetBackgroundScrimEvent;
import com.android.systemui.recents.events.ui.UpdateBackgroundScrimEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragDropTargetChangedEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
import com.android.systemui.recents.history.RecentsHistoryView;
import com.android.systemui.recents.misc.ReferenceCountedTrigger;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.Utilities;
@@ -80,6 +90,8 @@ import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
public class RecentsView extends FrameLayout {
private static final int DOCK_AREA_OVERLAY_TRANSITION_DURATION = 135;
private static final int DEFAULT_UPDATE_SCRIM_DURATION = 200;
private static final float DEFAULT_SCRIM_ALPHA = 0.33f;
private final Handler mHandler;
@@ -88,11 +100,16 @@ public class RecentsView extends FrameLayout {
private RecentsAppWidgetHostView mSearchBar;
private TextView mHistoryButton;
private View mEmptyView;
private RecentsHistoryView mHistoryView;
private boolean mAwaitingFirstLayout = true;
private boolean mLastTaskLaunchedWasFreeform;
private Rect mSystemInsets = new Rect();
private int mDividerSize;
private ColorDrawable mBackgroundScrim = new ColorDrawable(Color.BLACK);
private Animator mBackgroundScrimAnimator;
private RecentsTransitionHelper mTransitionHelper;
private RecentsViewTouchHandler mTouchHandler;
@@ -127,17 +144,28 @@ public class RecentsView extends FrameLayout {
mTouchHandler = new RecentsViewTouchHandler(this);
mFlingAnimationUtils = new FlingAnimationUtils(context, 0.3f);
final float cornerRadius = context.getResources().getDimensionPixelSize(
R.dimen.recents_task_view_rounded_corners_radius);
LayoutInflater inflater = LayoutInflater.from(context);
mHistoryButton = (TextView) inflater.inflate(R.layout.recents_history_button, this, false);
mHistoryButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
EventBus.getDefault().send(new ShowHistoryEvent());
EventBus.getDefault().send(new ToggleHistoryEvent());
}
});
mHistoryButton.setClipToOutline(true);
mHistoryButton.setOutlineProvider(new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), cornerRadius);
}
});
addView(mHistoryButton);
mEmptyView = inflater.inflate(R.layout.recents_empty, this, false);
addView(mEmptyView);
setBackground(mBackgroundScrim);
}
/** Set/get the bsp root node */
@@ -162,6 +190,15 @@ public class RecentsView extends FrameLayout {
addView(mTaskStackView);
}
// If we are already occluded by the app, then just set the default background scrim now.
// Otherwise, defer until the enter animation completes to animate the scrim with the
// tasks for the home animation.
if (launchState.launchedFromAppWithThumbnail || mStack.getTaskCount() == 0) {
mBackgroundScrim.setAlpha((int) (DEFAULT_SCRIM_ALPHA * 255));
} else {
mBackgroundScrim.setAlpha(0);
}
// Update the top level view's visibilities
if (stack.getTaskCount() > 0) {
hideEmptyView();
@@ -180,6 +217,13 @@ public class RecentsView extends FrameLayout {
return mLastTaskLaunchedWasFreeform;
}
/**
* Returns whether the history is visible or not.
*/
public boolean isHistoryVisible() {
return mHistoryView != null && mHistoryView.isVisible();
}
/**
* Returns the currently set task stack.
*/
@@ -304,13 +348,6 @@ public class RecentsView extends FrameLayout {
mHistoryButton.bringToFront();
}
/**
* Returns the last known system insets.
*/
public Rect getSystemInsets() {
return mSystemInsets;
}
@Override
protected void onAttachedToWindow() {
EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
@@ -352,17 +389,23 @@ public class RecentsView extends FrameLayout {
mTaskStackView.measure(widthMeasureSpec, heightMeasureSpec);
}
// Measure the empty view
measureChild(mEmptyView, MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
// Measure the empty view to the full size of the screen
if (mEmptyView.getVisibility() != GONE) {
measureChild(mEmptyView, MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
}
// Measure the history button with the full space above the stack, but width-constrained
// to the stack
// Measure the history view
if (mHistoryView != null && mHistoryView.getVisibility() != GONE) {
measureChild(mHistoryView, MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
}
// Measure the history button within the constraints of the space above the stack
Rect historyButtonRect = mTaskStackView.mLayoutAlgorithm.mHistoryButtonRect;
measureChild(mHistoryButton,
MeasureSpec.makeMeasureSpec(historyButtonRect.width(), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(historyButtonRect.height(),
MeasureSpec.EXACTLY));
MeasureSpec.makeMeasureSpec(historyButtonRect.width(), MeasureSpec.AT_MOST),
MeasureSpec.makeMeasureSpec(historyButtonRect.height(), MeasureSpec.AT_MOST));
setMeasuredDimension(width, height);
}
@@ -389,13 +432,24 @@ public class RecentsView extends FrameLayout {
}
// Layout the empty view
mEmptyView.layout(left, top, right, bottom);
if (mEmptyView.getVisibility() != GONE) {
mEmptyView.layout(left, top, right, bottom);
}
// Layout the history button left-aligned with the stack, but offset from the top of the
// view
// Layout the history view
if (mHistoryView != null && mHistoryView.getVisibility() != GONE) {
mHistoryView.layout(left, top, right, bottom);
}
// Layout the history button such that its drawable is left-aligned with the stack,
// vertically centered in the available space above the stack
Rect historyButtonRect = mTaskStackView.mLayoutAlgorithm.mHistoryButtonRect;
mHistoryButton.layout(historyButtonRect.left, historyButtonRect.top,
historyButtonRect.right, historyButtonRect.bottom);
int historyLeft = historyButtonRect.left - mHistoryButton.getPaddingStart();
int historyTop = historyButtonRect.top +
(historyButtonRect.height() - mHistoryButton.getMeasuredHeight()) / 2;
mHistoryButton.layout(historyLeft, historyTop,
historyLeft + mHistoryButton.getMeasuredWidth(),
historyTop + mHistoryButton.getMeasuredHeight());
if (mAwaitingFirstLayout) {
mAwaitingFirstLayout = false;
@@ -464,10 +518,8 @@ public class RecentsView extends FrameLayout {
// Hide the history button
int taskViewExitToHomeDuration = getResources().getInteger(
R.integer.recents_task_exit_to_home_duration);
hideHistoryButton(taskViewExitToHomeDuration);
// If we are going home, cancel the previous task's window transition
EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(null));
hideHistoryButton(taskViewExitToHomeDuration, false /* translate */);
animateBackgroundScrim(0f, taskViewExitToHomeDuration);
}
public final void onBusEvent(DragStartEvent event) {
@@ -572,57 +624,127 @@ public class RecentsView extends FrameLayout {
animator.start();
}
public final void onBusEvent(ShowHistoryEvent event) {
// Hide the history button when the history view is shown
hideHistoryButton(getResources().getInteger(R.integer.recents_history_transition_duration),
event.getAnimationTrigger());
event.addPostAnimationCallback(new Runnable() {
@Override
public void run() {
setAlpha(0f);
}
});
}
public final void onBusEvent(HideHistoryEvent event) {
// Show the history button when the history view is hidden
setAlpha(1f);
showHistoryButton(getResources().getInteger(R.integer.recents_history_transition_duration),
event.getAnimationTrigger());
}
public final void onBusEvent(ShowHistoryButtonEvent event) {
showHistoryButton(150);
}
public final void onBusEvent(HideHistoryButtonEvent event) {
hideHistoryButton(100);
}
public final void onBusEvent(TaskStackUpdatedEvent event) {
mStack.setTasks(event.stack.computeAllTasksList(), true /* notifyStackChanges */);
mStack.createAffiliatedGroupings(getContext());
}
public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
if (!launchState.launchedFromAppWithThumbnail && mStack.getTaskCount() > 0) {
int taskViewEnterFromHomeDuration = getResources().getInteger(
R.integer.recents_task_enter_from_home_duration);
animateBackgroundScrim(DEFAULT_SCRIM_ALPHA, taskViewEnterFromHomeDuration);
}
}
public final void onBusEvent(UpdateBackgroundScrimEvent event) {
animateBackgroundScrim(event.alpha, DEFAULT_UPDATE_SCRIM_DURATION);
}
public final void onBusEvent(ResetBackgroundScrimEvent event) {
animateBackgroundScrim(DEFAULT_SCRIM_ALPHA, DEFAULT_UPDATE_SCRIM_DURATION);
}
public final void onBusEvent(RecentsVisibilityChangedEvent event) {
if (!event.visible) {
// Reset the view state
mAwaitingFirstLayout = true;
mLastTaskLaunchedWasFreeform = false;
hideHistoryButton(0, false /* translate */);
}
}
public final void onBusEvent(ToggleHistoryEvent event) {
if (mHistoryView != null && mHistoryView.isVisible()) {
EventBus.getDefault().send(new HideHistoryEvent(true /* animate */));
} else {
EventBus.getDefault().send(new ShowHistoryEvent());
}
}
public final void onBusEvent(ShowHistoryEvent event) {
if (mHistoryView == null) {
mHistoryView = (RecentsHistoryView) LayoutInflater.from(getContext()).inflate(
R.layout.recents_history, this, false);
addView(mHistoryView);
// Since this history view is inflated by a view stub after the insets have already
// been applied, we have to set them ourselves initial from the insets that were last
// provided.
mHistoryView.setSystemInsets(mSystemInsets);
mHistoryView.setHeaderHeight(mHistoryButton.getMeasuredHeight());
mHistoryButton.bringToFront();
}
// Animate the empty view in parallel with the history view (the task view animations are
// handled in TaskStackView)
Rect stackRect = mTaskStackView.mLayoutAlgorithm.mStackRect;
if (mEmptyView.getVisibility() == View.VISIBLE) {
int historyTransitionDuration = getResources().getInteger(
R.integer.recents_history_transition_duration);
mEmptyView.animate()
.alpha(0f)
.translationY(stackRect.bottom)
.setDuration(historyTransitionDuration)
.setInterpolator(mFastOutSlowInInterpolator)
.withEndAction(new Runnable() {
@Override
public void run() {
mEmptyView.setVisibility(View.INVISIBLE);
}
})
.start();
}
mHistoryView.show(mStack, stackRect.height());
}
public final void onBusEvent(HideHistoryEvent event) {
// Animate the empty view in parallel with the history view (the task view animations are
// handled in TaskStackView)
Rect stackRect = mTaskStackView.mLayoutAlgorithm.mStackRect;
if (mStack.getTaskCount() == 0) {
int historyTransitionDuration = getResources().getInteger(
R.integer.recents_history_transition_duration);
mEmptyView.setVisibility(View.VISIBLE);
mEmptyView.animate()
.alpha(1f)
.translationY(0)
.setDuration(historyTransitionDuration)
.setInterpolator(mFastOutSlowInInterpolator)
.start();
}
mHistoryView.hide(event.animate, stackRect.height());
}
public final void onBusEvent(ShowHistoryButtonEvent event) {
showHistoryButton(150, event.translate);
}
public final void onBusEvent(HideHistoryButtonEvent event) {
hideHistoryButton(100, true /* translate */);
}
/**
* Shows the history button.
*/
private void showHistoryButton(final int duration) {
ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger();
showHistoryButton(duration, postAnimationTrigger);
postAnimationTrigger.flushLastDecrementRunnables();
}
private void showHistoryButton(final int duration,
final ReferenceCountedTrigger postHideHistoryAnimationTrigger) {
mHistoryButton.setText(getContext().getString(R.string.recents_history_label_format,
mStack.getHistoricalTasks().size()));
private void showHistoryButton(final int duration, final boolean translate) {
final ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger();
if (mHistoryButton.getVisibility() == View.INVISIBLE) {
mHistoryButton.setVisibility(View.VISIBLE);
mHistoryButton.setAlpha(0f);
postHideHistoryAnimationTrigger.addLastDecrementRunnable(new Runnable() {
if (translate) {
mHistoryButton.setTranslationY(-mHistoryButton.getMeasuredHeight() * 0.25f);
}
postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
@Override
public void run() {
if (translate) {
mHistoryButton.animate()
.translationY(0f);
}
mHistoryButton.animate()
.alpha(1f)
.setDuration(duration)
@@ -632,34 +754,42 @@ public class RecentsView extends FrameLayout {
}
});
}
postAnimationTrigger.flushLastDecrementRunnables();
}
/**
* Hides the history button.
*/
private void hideHistoryButton(int duration) {
ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger();
hideHistoryButton(duration, postAnimationTrigger);
private void hideHistoryButton(int duration, boolean translate) {
final ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger();
hideHistoryButton(duration, translate, postAnimationTrigger);
postAnimationTrigger.flushLastDecrementRunnables();
}
private void hideHistoryButton(int duration,
final ReferenceCountedTrigger postHideStackAnimationTrigger) {
/**
* Hides the history button.
*/
private void hideHistoryButton(int duration, boolean translate,
final ReferenceCountedTrigger postAnimationTrigger) {
if (mHistoryButton.getVisibility() == View.VISIBLE) {
if (translate) {
mHistoryButton.animate()
.translationY(-mHistoryButton.getMeasuredHeight() * 0.25f);
}
mHistoryButton.animate()
.alpha(0f)
.setDuration(duration)
.setInterpolator(mFastOutLinearInInterpolator)
.setInterpolator(mFastOutSlowInInterpolator)
.withEndAction(new Runnable() {
@Override
public void run() {
mHistoryButton.setVisibility(View.INVISIBLE);
postHideStackAnimationTrigger.decrement();
postAnimationTrigger.decrement();
}
})
.withLayer()
.start();
postHideStackAnimationTrigger.increment();
postAnimationTrigger.increment();
}
}
@@ -696,11 +826,18 @@ public class RecentsView extends FrameLayout {
}
}
public final void onBusEvent(RecentsVisibilityChangedEvent event) {
if (!event.visible) {
// Reset the view state
mAwaitingFirstLayout = true;
mLastTaskLaunchedWasFreeform = false;
}
/**
* Animates the background scrim to the given {@param alpha}.
*/
private void animateBackgroundScrim(float alpha, int duration) {
Utilities.cancelAnimationWithoutCallbacks(mBackgroundScrimAnimator);
int alphaInt = (int) (alpha * 255);
mBackgroundScrimAnimator = ObjectAnimator.ofInt(mBackgroundScrim, Utilities.DRAWABLE_ALPHA,
mBackgroundScrim.getAlpha(), alphaInt);
mBackgroundScrimAnimator.setDuration(duration);
mBackgroundScrimAnimator.setInterpolator(alphaInt > mBackgroundScrim.getAlpha()
? PhoneStatusBar.ALPHA_OUT
: PhoneStatusBar.ALPHA_IN);
mBackgroundScrimAnimator.start();
}
}

View File

@@ -30,12 +30,9 @@ public class SystemBarScrimViews {
Context mContext;
View mStatusBarScrimView;
View mNavBarScrimView;
boolean mHasNavBarScrim;
boolean mShouldAnimateStatusBarScrim;
boolean mHasStatusBarScrim;
boolean mShouldAnimateNavBarScrim;
int mNavBarScrimEnterDuration;
@@ -45,7 +42,6 @@ public class SystemBarScrimViews {
public SystemBarScrimViews(Activity activity) {
mContext = activity;
mStatusBarScrimView = activity.findViewById(R.id.status_bar_scrim);
mNavBarScrimView = activity.findViewById(R.id.nav_bar_scrim);
mNavBarScrimEnterDuration = activity.getResources().getInteger(
R.integer.recents_nav_bar_scrim_enter_duration);
@@ -59,17 +55,12 @@ public class SystemBarScrimViews {
* Prepares the scrim views for animating when entering Recents. This will be called before
* the first draw.
*/
public void prepareEnterRecentsAnimation(boolean hasStatusBarScrim, boolean animateStatusBarScrim,
boolean hasNavBarScrim, boolean animateNavBarScrim) {
mHasNavBarScrim = hasStatusBarScrim;
mShouldAnimateStatusBarScrim = animateStatusBarScrim;
mHasStatusBarScrim = hasNavBarScrim;
public void prepareEnterRecentsAnimation(boolean hasNavBarScrim, boolean animateNavBarScrim) {
mHasNavBarScrim = hasNavBarScrim;
mShouldAnimateNavBarScrim = animateNavBarScrim;
mNavBarScrimView.setVisibility(mHasNavBarScrim && !mShouldAnimateNavBarScrim ?
View.VISIBLE : View.INVISIBLE);
mStatusBarScrimView.setVisibility(mHasStatusBarScrim && !mShouldAnimateStatusBarScrim ?
View.VISIBLE : View.INVISIBLE);
}
/**** EventBus events ****/
@@ -78,20 +69,6 @@ public class SystemBarScrimViews {
* Starts animating the scrim views when entering Recents.
*/
public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
if (mHasStatusBarScrim && mShouldAnimateStatusBarScrim) {
mStatusBarScrimView.setTranslationY(-mStatusBarScrimView.getMeasuredHeight());
mStatusBarScrimView.animate()
.translationY(0)
.setDuration(mNavBarScrimEnterDuration)
.setInterpolator(mQuintOutInterpolator)
.withStartAction(new Runnable() {
@Override
public void run() {
mStatusBarScrimView.setVisibility(View.VISIBLE);
}
})
.start();
}
if (mHasNavBarScrim && mShouldAnimateNavBarScrim) {
mNavBarScrimView.setTranslationY(mNavBarScrimView.getMeasuredHeight());
mNavBarScrimView.animate()
@@ -115,14 +92,6 @@ public class SystemBarScrimViews {
public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) {
int taskViewExitToAppDuration = mContext.getResources().getInteger(
R.integer.recents_task_exit_to_app_duration);
if (mHasStatusBarScrim && mShouldAnimateStatusBarScrim) {
mStatusBarScrimView.animate()
.translationY(-mStatusBarScrimView.getMeasuredHeight())
.setStartDelay(0)
.setDuration(taskViewExitToAppDuration)
.setInterpolator(mFastOutSlowInInterpolator)
.start();
}
if (mHasNavBarScrim && mShouldAnimateNavBarScrim) {
mNavBarScrimView.animate()
.translationY(mNavBarScrimView.getMeasuredHeight())

View File

@@ -138,7 +138,7 @@ public class TaskStackAnimationHelper {
} else if (launchState.launchedFromHome) {
// Move the task view off screen (below) so we can animate it in
RectF bounds = new RectF(mTmpTransform.rect);
bounds.offset(0, offscreenY);
bounds.offsetTo(bounds.left, offscreenY);
tv.setLeftTopRightBottom((int) bounds.left, (int) bounds.top, (int) bounds.right,
(int) bounds.bottom);
}
@@ -259,7 +259,7 @@ public class TaskStackAnimationHelper {
stackLayout.getStackTransform(task, stackScroller.getStackScroll(), mTmpTransform,
null);
mTmpTransform.rect.offset(0, offscreenY);
mTmpTransform.rect.offsetTo(mTmpTransform.rect.left, offscreenY);
mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
}
}
@@ -346,61 +346,58 @@ public class TaskStackAnimationHelper {
}
/**
* Starts the animation to hide the {@link TaskView}s when the history is shown. The history
* view's animation will be deferred until all the {@link TaskView}s are finished animating.
* Starts the animation to hide the {@link TaskView}s when the history is shown.
*/
public void startShowHistoryAnimation(ReferenceCountedTrigger postAnimationTrigger) {
Resources res = mStackView.getResources();
TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
TaskStackViewScroller stackScroller = mStackView.getScroller();
int offscreenY = stackLayout.mStackRect.bottom;
int historyTransitionDuration = res.getInteger(
R.integer.recents_history_transition_duration);
int startDelayIncr = 16;
List<TaskView> taskViews = mStackView.getTaskViews();
int taskViewCount = taskViews.size();
for (int i = taskViewCount - 1; i >= 0; i--) {
TaskView tv = taskViews.get(i);
Task task = tv.getTask();
TaskViewAnimation taskAnimation = new TaskViewAnimation(
historyTransitionDuration, PhoneStatusBar.ALPHA_OUT,
TaskViewAnimation taskAnimation = new TaskViewAnimation(startDelayIncr * i,
historyTransitionDuration, mFastOutSlowInInterpolator,
postAnimationTrigger.decrementOnAnimationEnd());
postAnimationTrigger.increment();
stackLayout.getStackTransform(task, stackScroller.getStackScroll(), mTmpTransform,
null);
mTmpTransform.alpha = 0f;
mTmpTransform.rect.offsetTo(mTmpTransform.rect.left, offscreenY);
mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
}
}
/**
* Starts the animation to show the {@link TaskView}s when the history is hidden. The
* {@link TaskView} animations will be deferred until the history view has been animated away.
* Starts the animation to show the {@link TaskView}s when the history is hidden.
*/
public void startHideHistoryAnimation(final ReferenceCountedTrigger postAnimationTrigger) {
final Resources res = mStackView.getResources();
final TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
final TaskStackViewScroller stackScroller = mStackView.getScroller();
public void startHideHistoryAnimation() {
Resources res = mStackView.getResources();
TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
TaskStackViewScroller stackScroller = mStackView.getScroller();
final int historyTransitionDuration = res.getInteger(
int historyTransitionDuration = res.getInteger(
R.integer.recents_history_transition_duration);
int startDelayIncr = 16;
List<TaskView> taskViews = mStackView.getTaskViews();
int taskViewCount = taskViews.size();
for (int i = taskViewCount - 1; i >= 0; i--) {
final TaskView tv = taskViews.get(i);
postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
@Override
public void run() {
TaskViewAnimation taskAnimation = new TaskViewAnimation(
historyTransitionDuration, PhoneStatusBar.ALPHA_IN);
stackLayout.getStackTransform(tv.getTask(), stackScroller.getStackScroll(),
mTmpTransform, null);
mTmpTransform.alpha = 1f;
mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
}
});
TaskView tv = taskViews.get(i);
TaskViewAnimation taskAnimation = new TaskViewAnimation(startDelayIncr * i,
historyTransitionDuration, mFastOutSlowInInterpolator);
stackLayout.getStackTransform(tv.getTask(), stackScroller.getStackScroll(),
mTmpTransform, null);
mTmpTransform.alpha = 1f;
mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
}
}
}

View File

@@ -354,6 +354,7 @@ public class TaskStackLayoutAlgorithm {
mStackBottomOffset = mSystemInsets.bottom + heightPadding;
state.computeRects(mFreeformRect, mStackRect, taskStackBounds, widthPadding, heightPadding,
mStackBottomOffset);
// The history button will take the full un-padded header space above the stack
mHistoryButtonRect.set(mStackRect.left, mStackRect.top - heightPadding,
mStackRect.right, mStackRect.top + mFocusedPeekHeight);

View File

@@ -64,6 +64,7 @@ import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
import com.android.systemui.recents.events.ui.StackViewScrolledEvent;
import com.android.systemui.recents.events.ui.TaskViewDismissedEvent;
import com.android.systemui.recents.events.ui.UpdateBackgroundScrimEvent;
import com.android.systemui.recents.events.ui.UpdateFreeformTaskViewVisibilityEvent;
import com.android.systemui.recents.events.ui.UserInteractionEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragDropTargetChangedEvent;
@@ -74,11 +75,13 @@ import com.android.systemui.recents.events.ui.focus.DismissFocusedTaskViewEvent;
import com.android.systemui.recents.events.ui.focus.FocusNextTaskViewEvent;
import com.android.systemui.recents.events.ui.focus.FocusPreviousTaskViewEvent;
import com.android.systemui.recents.misc.DozeTrigger;
import com.android.systemui.recents.misc.ReferenceCountedTrigger;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
import java.lang.ref.Reference;
import java.util.ArrayList;
import java.util.List;
@@ -1117,12 +1120,19 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
// We can get spurious measure passes with the old bounds when docking, and since we are
// using the current stack bounds during drag and drop, don't overwrite them until we
// actually get new bounds
boolean requiresLayout = false;
if (!taskStackBounds.equals(mStableStackBounds)) {
mStableStackBounds.set(taskStackBounds);
mStackBounds.set(taskStackBounds);
requiresLayout = true;
}
if (!systemInsets.equals(mLayoutAlgorithm.mSystemInsets)) {
mLayoutAlgorithm.setSystemInsets(systemInsets);
requiresLayout = true;
}
if (requiresLayout) {
requestLayout();
}
mLayoutAlgorithm.setSystemInsets(systemInsets);
requestLayout();
}
/**
@@ -1235,7 +1245,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
// Update the history button visibility
if (shouldShowHistoryButton() &&
mStackScroller.getStackScroll() < SHOW_HISTORY_BUTTON_SCROLL_THRESHOLD) {
EventBus.getDefault().send(new ShowHistoryButtonEvent());
EventBus.getDefault().send(new ShowHistoryButtonEvent(false /* translate */));
} else {
EventBus.getDefault().send(new HideHistoryButtonEvent());
}
@@ -1468,13 +1478,15 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
relayoutTaskViewsOnNextFrame(animation);
}
if (shouldShowHistoryButton() &&
prevScroll > SHOW_HISTORY_BUTTON_SCROLL_THRESHOLD &&
curScroll <= SHOW_HISTORY_BUTTON_SCROLL_THRESHOLD) {
EventBus.getDefault().send(new ShowHistoryButtonEvent());
} else if (prevScroll < HIDE_HISTORY_BUTTON_SCROLL_THRESHOLD &&
curScroll >= HIDE_HISTORY_BUTTON_SCROLL_THRESHOLD) {
EventBus.getDefault().send(new HideHistoryButtonEvent());
if (mEnterAnimationComplete) {
if (shouldShowHistoryButton() &&
prevScroll > SHOW_HISTORY_BUTTON_SCROLL_THRESHOLD &&
curScroll <= SHOW_HISTORY_BUTTON_SCROLL_THRESHOLD) {
EventBus.getDefault().send(new ShowHistoryButtonEvent(true /* translate */));
} else if (prevScroll < HIDE_HISTORY_BUTTON_SCROLL_THRESHOLD &&
curScroll >= HIDE_HISTORY_BUTTON_SCROLL_THRESHOLD) {
EventBus.getDefault().send(new HideHistoryButtonEvent());
}
}
}
@@ -1753,11 +1765,19 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
public final void onBusEvent(ShowHistoryEvent event) {
mAnimationHelper.startShowHistoryAnimation(event.getAnimationTrigger());
ReferenceCountedTrigger postAnimTrigger = new ReferenceCountedTrigger();
postAnimTrigger.addLastDecrementRunnable(new Runnable() {
@Override
public void run() {
setVisibility(View.INVISIBLE);
}
});
mAnimationHelper.startShowHistoryAnimation(postAnimTrigger);
}
public final void onBusEvent(HideHistoryEvent event) {
mAnimationHelper.startHideHistoryAnimation(event.getAnimationTrigger());
setVisibility(View.VISIBLE);
mAnimationHelper.startHideHistoryAnimation();
}
/**

View File

@@ -45,6 +45,10 @@ public class TaskViewAnimation {
this(0 /* startDelay */, duration, interpolator, listener);
}
public TaskViewAnimation(int startDelay, int duration, Interpolator interpolator) {
this(startDelay, duration, interpolator, null);
}
public TaskViewAnimation(int startDelay, int duration, Interpolator interpolator,
Animator.AnimatorListener listener) {
this.startDelay = startDelay;