Intermediate refactoring to move towards in-app view transitions.
- Fixing bug where we weren't toggling to the right task when using affiliations - Refactoring task rect calculation to allow full screen task view to be laid out for transitions - Refactoring the view bounds animations into a separate class - Refactoring the footer view (for lock-to-task) out of TaskView - Refactoring some transform code out of TaskView - Removing fullscreen overlay view - Fixing case where extra invalidations and layouts were still happening in FixedSizeImageView - Adding debug overlay to replace specific debug drawing code Change-Id: Ibf98b6a0782a68cd84582203c807cece1ff3379f
This commit is contained in:
@ -62,7 +62,7 @@
|
||||
android:visibility="invisible"
|
||||
android:src="@drawable/recents_dismiss_light" />
|
||||
</com.android.systemui.recents.views.TaskBarView>
|
||||
<FrameLayout
|
||||
<com.android.systemui.recents.views.TaskFooterView
|
||||
android:id="@+id/lock_to_app"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/recents_task_view_lock_to_app_button_height"
|
||||
@ -82,7 +82,7 @@
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:singleLine="true"
|
||||
android:textAllCaps="true" />
|
||||
</FrameLayout>
|
||||
</com.android.systemui.recents.views.TaskFooterView>
|
||||
</com.android.systemui.recents.views.TaskView>
|
||||
|
||||
|
||||
|
@ -23,6 +23,7 @@ import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Rect;
|
||||
@ -70,10 +71,13 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
|
||||
|
||||
// Task launching
|
||||
RecentsConfiguration mConfig;
|
||||
Rect mWindowRect;
|
||||
Rect mTaskStackBounds;
|
||||
Rect mWindowRect = new Rect();
|
||||
Rect mTaskStackBounds = new Rect();
|
||||
Rect mSystemInsets = new Rect();
|
||||
TaskViewTransform mTmpTransform = new TaskViewTransform();
|
||||
int mStatusBarHeight;
|
||||
int mNavBarHeight;
|
||||
int mNavBarWidth;
|
||||
|
||||
// Variables to keep track of if we need to start recents after binding
|
||||
View mStatusBarView;
|
||||
@ -81,15 +85,23 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
|
||||
long mLastToggleTime;
|
||||
|
||||
public AlternateRecentsComponent(Context context) {
|
||||
Resources res = context.getResources();
|
||||
mContext = context;
|
||||
mSystemServicesProxy = new SystemServicesProxy(context);
|
||||
mHandler = new Handler();
|
||||
mConfig = RecentsConfiguration.reinitialize(context, mSystemServicesProxy);
|
||||
mWindowRect = mSystemServicesProxy.getWindowRect();
|
||||
mTaskStackBounds = new Rect();
|
||||
mConfig.getTaskStackBounds(mWindowRect.width(), mWindowRect.height(), mTaskStackBounds);
|
||||
mStatusBarHeight = mContext.getResources().getDimensionPixelSize(
|
||||
com.android.internal.R.dimen.status_bar_height);
|
||||
mStatusBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
|
||||
mNavBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_height);
|
||||
mNavBarWidth = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_width);
|
||||
mConfig.getTaskStackBounds(mWindowRect.width(), mWindowRect.height(), mStatusBarHeight,
|
||||
mNavBarWidth, mTaskStackBounds);
|
||||
if (mConfig.isLandscape && mConfig.transposeRecentsLayoutWithOrientation) {
|
||||
mSystemInsets.set(0, mStatusBarHeight, mNavBarWidth, 0);
|
||||
} else {
|
||||
mSystemInsets.set(0, mStatusBarHeight, 0, mNavBarHeight);
|
||||
}
|
||||
}
|
||||
|
||||
public void onStart() {
|
||||
@ -150,7 +162,13 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
|
||||
mConfig = RecentsConfiguration.reinitialize(mContext, mSystemServicesProxy);
|
||||
mConfig.updateOnConfigurationChange();
|
||||
mWindowRect = mSystemServicesProxy.getWindowRect();
|
||||
mConfig.getTaskStackBounds(mWindowRect.width(), mWindowRect.height(), mTaskStackBounds);
|
||||
mConfig.getTaskStackBounds(mWindowRect.width(), mWindowRect.height(), mStatusBarHeight,
|
||||
mNavBarWidth, mTaskStackBounds);
|
||||
if (mConfig.isLandscape && mConfig.transposeRecentsLayoutWithOrientation) {
|
||||
mSystemInsets.set(0, mStatusBarHeight, mNavBarWidth, 0);
|
||||
} else {
|
||||
mSystemInsets.set(0, mStatusBarHeight, 0, mNavBarHeight);
|
||||
}
|
||||
sLastScreenshot = null;
|
||||
}
|
||||
|
||||
@ -301,7 +319,9 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
|
||||
// Get the stack
|
||||
TaskStackView tsv = new TaskStackView(mContext, stack);
|
||||
TaskStackViewLayoutAlgorithm algo = tsv.getStackAlgorithm();
|
||||
tsv.computeRects(mTaskStackBounds.width(), mTaskStackBounds.height() - mStatusBarHeight, 0, 0);
|
||||
Rect taskStackBounds = new Rect(mTaskStackBounds);
|
||||
taskStackBounds.bottom -= mSystemInsets.bottom;
|
||||
tsv.computeRects(mWindowRect.width(), mWindowRect.height(), taskStackBounds);
|
||||
tsv.setStackScrollToInitialState();
|
||||
|
||||
// Find the running task in the TaskStack
|
||||
@ -325,8 +345,6 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
|
||||
|
||||
// Get the transform for the running task
|
||||
mTmpTransform = algo.getStackTransform(task, tsv.getStackScroll(), mTmpTransform);
|
||||
mTmpTransform.rect.offset(mTaskStackBounds.left, mTaskStackBounds.top);
|
||||
mTmpTransform.rect.offset(0, mStatusBarHeight);
|
||||
return new Rect(mTmpTransform.rect);
|
||||
}
|
||||
|
||||
|
@ -32,18 +32,20 @@ import android.os.UserHandle;
|
||||
import android.util.Pair;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewStub;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.Toast;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.recents.misc.Console;
|
||||
import com.android.systemui.recents.misc.DebugTrigger;
|
||||
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.RecentsTaskLoader;
|
||||
import com.android.systemui.recents.model.SpaceNode;
|
||||
import com.android.systemui.recents.model.Task;
|
||||
import com.android.systemui.recents.model.TaskStack;
|
||||
import com.android.systemui.recents.views.FullscreenTransitionOverlayView;
|
||||
import com.android.systemui.recents.views.DebugOverlayView;
|
||||
import com.android.systemui.recents.views.RecentsView;
|
||||
import com.android.systemui.recents.views.SystemBarScrimViews;
|
||||
import com.android.systemui.recents.views.ViewAnimation;
|
||||
@ -56,8 +58,7 @@ import java.util.ArrayList;
|
||||
* The main Recents activity that is started from AlternateRecentsComponent.
|
||||
*/
|
||||
public class RecentsActivity extends Activity implements RecentsView.RecentsViewCallbacks,
|
||||
RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks,
|
||||
FullscreenTransitionOverlayView.FullScreenTransitionViewCallbacks {
|
||||
RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks {
|
||||
|
||||
// Actions and Extras sent from AlternateRecentsComponent
|
||||
final static String EXTRA_TRIGGERED_FROM_ALT_TAB = "extra_triggered_from_alt_tab";
|
||||
@ -73,17 +74,14 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
|
||||
SystemBarScrimViews mScrimViews;
|
||||
ViewStub mEmptyViewStub;
|
||||
View mEmptyView;
|
||||
ViewStub mFullscreenOverlayStub;
|
||||
FullscreenTransitionOverlayView mFullScreenOverlayView;
|
||||
DebugOverlayView mDebugOverlay;
|
||||
|
||||
// Search AppWidget
|
||||
RecentsAppWidgetHost mAppWidgetHost;
|
||||
AppWidgetProviderInfo mSearchAppWidgetInfo;
|
||||
AppWidgetHostView mSearchAppWidgetHostView;
|
||||
|
||||
|
||||
// Runnables to finish the Recents activity
|
||||
FinishRecentsRunnable mFinishRunnable = new FinishRecentsRunnable();
|
||||
FinishRecentsRunnable mFinishLaunchHomeRunnable;
|
||||
|
||||
/**
|
||||
@ -97,10 +95,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
|
||||
Intent mLaunchIntent;
|
||||
ActivityOptions mLaunchOpts;
|
||||
|
||||
public FinishRecentsRunnable() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a finish runnable that starts the specified intent, using the given
|
||||
* ActivityOptions.
|
||||
@ -151,8 +145,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
|
||||
} else if (action.equals(ACTION_START_ENTER_ANIMATION)) {
|
||||
// Try and start the enter animation (or restart it on configuration changed)
|
||||
ReferenceCountedTrigger t = new ReferenceCountedTrigger(context, null, null, null);
|
||||
mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext(
|
||||
mFullScreenOverlayView, t));
|
||||
mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext(t));
|
||||
onEnterAnimationTriggered();
|
||||
}
|
||||
}
|
||||
@ -187,11 +180,12 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
|
||||
|
||||
/** Updates the set of recent tasks */
|
||||
void updateRecentsTasks(Intent launchIntent) {
|
||||
// Load all the tasks
|
||||
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
|
||||
SpaceNode root = loader.reload(this, Constants.Values.RecentsTaskLoader.PreloadFirstTasksCount);
|
||||
ArrayList<TaskStack> stacks = root.getStacks();
|
||||
if (!stacks.isEmpty()) {
|
||||
mRecentsView.setBSP(root);
|
||||
mRecentsView.setTaskStacks(root.getStacks());
|
||||
}
|
||||
|
||||
// Update the configuration based on the launch intent
|
||||
@ -207,6 +201,23 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
|
||||
mConfig.launchedToTaskId = launchIntent.getIntExtra(
|
||||
AlternateRecentsComponent.EXTRA_TRIGGERED_FROM_TASK_ID, -1);
|
||||
|
||||
// Mark the task that is the launch target
|
||||
int taskStackCount = stacks.size();
|
||||
if (mConfig.launchedToTaskId != -1) {
|
||||
for (int i = 0; i < taskStackCount; i++) {
|
||||
TaskStack stack = stacks.get(i);
|
||||
ArrayList<Task> tasks = stack.getTasks();
|
||||
int taskCount = tasks.size();
|
||||
for (int j = 0; j < taskCount; j++) {
|
||||
Task t = tasks.get(j);
|
||||
if (t.key.id == mConfig.launchedToTaskId) {
|
||||
t.isLaunchTarget = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update the top level view's visibilities
|
||||
if (mConfig.launchedWithNoRecentTasks) {
|
||||
if (mEmptyView == null) {
|
||||
@ -286,9 +297,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
|
||||
/** Dismisses recents if we are already visible and the intent is to toggle the recents view */
|
||||
boolean dismissRecentsToFocusedTaskOrHome(boolean checkFilteredStackState) {
|
||||
if (mVisible) {
|
||||
// If we are mid-animation into Recents, reverse the animation now
|
||||
if (mFullScreenOverlayView != null &&
|
||||
mFullScreenOverlayView.cancelAnimateOnEnterRecents(mFinishRunnable)) return true;
|
||||
// If we currently have filtered stacks, then unfilter those first
|
||||
if (checkFilteredStackState &&
|
||||
mRecentsView.unfilterFilteredStacks()) return true;
|
||||
@ -299,8 +307,8 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
|
||||
dismissRecentsToHomeRaw(true);
|
||||
return true;
|
||||
}
|
||||
// Otherwise, try and return to the first Task in the stack
|
||||
if (mRecentsView.launchFirstTask()) return true;
|
||||
// Otherwise, try and return to the Task that Recents was launched from
|
||||
if (mRecentsView.launchPreviousTask()) return true;
|
||||
// If none of the other cases apply, then just go Home
|
||||
dismissRecentsToHomeRaw(true);
|
||||
return true;
|
||||
@ -323,9 +331,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
|
||||
/** Dismisses Recents directly to Home if we currently aren't transitioning. */
|
||||
boolean dismissRecentsToHome(boolean animated) {
|
||||
if (mVisible) {
|
||||
// If we are mid-animation into Recents, reverse the animation now
|
||||
if (mFullScreenOverlayView != null &&
|
||||
mFullScreenOverlayView.cancelAnimateOnEnterRecents(mFinishRunnable)) return true;
|
||||
// Return to Home
|
||||
dismissRecentsToHomeRaw(animated);
|
||||
return true;
|
||||
@ -363,8 +368,8 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
|
||||
mEmptyViewStub = (ViewStub) findViewById(R.id.empty_view_stub);
|
||||
mFullscreenOverlayStub = (ViewStub) findViewById(R.id.fullscreen_overlay_stub);
|
||||
mScrimViews = new SystemBarScrimViews(this, mConfig);
|
||||
inflateDebugOverlay();
|
||||
|
||||
// Bind the search app widget when we first start up
|
||||
bindSearchBarAppWidget();
|
||||
@ -390,13 +395,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
// Prepare the screenshot transition if necessary
|
||||
if (Constants.DebugFlags.App.EnableScreenshotAppTransition) {
|
||||
mFullScreenOverlayView = (FullscreenTransitionOverlayView) mFullscreenOverlayStub.inflate();
|
||||
mFullScreenOverlayView.setCallbacks(this);
|
||||
mFullScreenOverlayView.prepareAnimateOnEnterRecents(AlternateRecentsComponent.getLastScreenshot());
|
||||
}
|
||||
|
||||
// Update if we are getting a configuration change
|
||||
if (savedInstanceState != null) {
|
||||
mConfig.updateOnConfigurationChange();
|
||||
@ -404,6 +402,19 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
|
||||
}
|
||||
}
|
||||
|
||||
/** Inflates the debug overlay if debug mode is enabled. */
|
||||
void inflateDebugOverlay() {
|
||||
if (mConfig.debugModeEnabled && mDebugOverlay == null) {
|
||||
ViewGroup parent = (ViewGroup) findViewById(android.R.id.content).getRootView();
|
||||
mDebugOverlay = new DebugOverlayView(this);
|
||||
FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT);
|
||||
parent.addView(mDebugOverlay, lp);
|
||||
mRecentsView.setDebugOverlay(mDebugOverlay);
|
||||
}
|
||||
}
|
||||
|
||||
void onConfigurationChange() {
|
||||
// Update RecentsConfiguration
|
||||
mConfig = RecentsConfiguration.reinitialize(this,
|
||||
@ -411,8 +422,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
|
||||
|
||||
// Try and start the enter animation (or restart it on configuration changed)
|
||||
ReferenceCountedTrigger t = new ReferenceCountedTrigger(this, null, null, null);
|
||||
mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext(
|
||||
mFullScreenOverlayView, t));
|
||||
mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext(t));
|
||||
onEnterAnimationTriggered();
|
||||
}
|
||||
|
||||
@ -421,13 +431,13 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
|
||||
super.onNewIntent(intent);
|
||||
setIntent(intent);
|
||||
|
||||
// Clear any debug rects
|
||||
if (mDebugOverlay != null) {
|
||||
mDebugOverlay.clear();
|
||||
}
|
||||
|
||||
// Update the recent tasks
|
||||
updateRecentsTasks(intent);
|
||||
|
||||
// Prepare the screenshot transition if necessary
|
||||
if (Constants.DebugFlags.App.EnableScreenshotAppTransition) {
|
||||
mFullScreenOverlayView.prepareAnimateOnEnterRecents(AlternateRecentsComponent.getLastScreenshot());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -531,17 +541,25 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
|
||||
|
||||
/** Called when debug mode is triggered */
|
||||
public void onDebugModeTriggered() {
|
||||
|
||||
if (mConfig.developerOptionsEnabled) {
|
||||
SharedPreferences settings = getSharedPreferences(getPackageName(), 0);
|
||||
if (settings.getBoolean(Constants.Values.App.Key_DebugModeEnabled, false)) {
|
||||
// Disable the debug mode
|
||||
settings.edit().remove(Constants.Values.App.Key_DebugModeEnabled).apply();
|
||||
mConfig.debugModeEnabled = false;
|
||||
inflateDebugOverlay();
|
||||
mDebugOverlay.disable();
|
||||
} else {
|
||||
// Enable the debug mode
|
||||
settings.edit().putBoolean(Constants.Values.App.Key_DebugModeEnabled, true).apply();
|
||||
mConfig.debugModeEnabled = true;
|
||||
inflateDebugOverlay();
|
||||
mDebugOverlay.enable();
|
||||
}
|
||||
Toast.makeText(this, "Debug mode (" + Constants.Values.App.DebugModeVersion +
|
||||
") toggled, please restart Recents now", Toast.LENGTH_SHORT).show();
|
||||
Toast.makeText(this, "Debug mode (" + Constants.Values.App.DebugModeVersion + ") " +
|
||||
(mConfig.debugModeEnabled ? "Enabled" : "Disabled") + ", please restart Recents now",
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
@ -551,19 +569,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
|
||||
mScrimViews.startEnterRecentsAnimation();
|
||||
}
|
||||
|
||||
/**** FullscreenTransitionOverlayView.FullScreenTransitionViewCallbacks Implementation ****/
|
||||
|
||||
@Override
|
||||
public void onEnterAnimationComplete() {
|
||||
// Reset the full screenshot transition view
|
||||
if (Constants.DebugFlags.App.EnableScreenshotAppTransition) {
|
||||
mFullScreenOverlayView.reset();
|
||||
|
||||
// Recycle the full screen screenshot
|
||||
AlternateRecentsComponent.consumeLastScreenshot();
|
||||
}
|
||||
}
|
||||
|
||||
/**** RecentsView.RecentsViewCallbacks Implementation ****/
|
||||
|
||||
@Override
|
||||
|
@ -309,19 +309,16 @@ public class RecentsConfiguration {
|
||||
* Returns the task stack bounds in the current orientation. These bounds do not account for
|
||||
* the system insets.
|
||||
*/
|
||||
public void getTaskStackBounds(int width, int height, Rect taskStackBounds) {
|
||||
if (hasSearchBarAppWidget()) {
|
||||
Rect searchBarBounds = new Rect();
|
||||
getSearchBarBounds(width, height, searchBarBounds);
|
||||
if (isLandscape && transposeRecentsLayoutWithOrientation) {
|
||||
// In landscape, the search bar appears on the left, so shift the task rect right
|
||||
taskStackBounds.set(searchBarBounds.width(), 0, width, height);
|
||||
} else {
|
||||
// In portrait, the search bar appears on the top, so shift the task rect below
|
||||
taskStackBounds.set(0, searchBarBounds.height(), width, height);
|
||||
}
|
||||
public void getTaskStackBounds(int windowWidth, int windowHeight, int topInset, int rightInset,
|
||||
Rect taskStackBounds) {
|
||||
Rect searchBarBounds = new Rect();
|
||||
getSearchBarBounds(windowWidth, windowHeight, topInset, searchBarBounds);
|
||||
if (isLandscape && transposeRecentsLayoutWithOrientation) {
|
||||
// In landscape, the search bar appears on the left
|
||||
taskStackBounds.set(searchBarBounds.right, topInset, windowWidth - rightInset, windowHeight);
|
||||
} else {
|
||||
taskStackBounds.set(0, 0, width, height);
|
||||
// In portrait, the search bar appears on the top (which already has the inset)
|
||||
taskStackBounds.set(0, searchBarBounds.bottom, windowWidth, windowHeight);
|
||||
}
|
||||
}
|
||||
|
||||
@ -329,19 +326,20 @@ public class RecentsConfiguration {
|
||||
* Returns the search bar bounds in the current orientation. These bounds do not account for
|
||||
* the system insets.
|
||||
*/
|
||||
public void getSearchBarBounds(int width, int height, Rect searchBarSpaceBounds) {
|
||||
public void getSearchBarBounds(int windowWidth, int windowHeight, int topInset,
|
||||
Rect searchBarSpaceBounds) {
|
||||
// Return empty rects if search is not enabled
|
||||
if (!Constants.DebugFlags.App.EnableSearchLayout) {
|
||||
searchBarSpaceBounds.set(0, 0, 0, 0);
|
||||
return;
|
||||
int searchBarSize = searchBarSpaceHeightPx;
|
||||
if (!Constants.DebugFlags.App.EnableSearchLayout || !hasSearchBarAppWidget()) {
|
||||
searchBarSize = 0;
|
||||
}
|
||||
|
||||
if (isLandscape && transposeRecentsLayoutWithOrientation) {
|
||||
// In landscape, the search bar appears on the left
|
||||
searchBarSpaceBounds.set(0, 0, searchBarSpaceHeightPx, height);
|
||||
searchBarSpaceBounds.set(0, topInset, searchBarSize, windowHeight);
|
||||
} else {
|
||||
// In portrait, the search bar appears on the top
|
||||
searchBarSpaceBounds.set(0, 0, width, searchBarSpaceHeightPx);
|
||||
searchBarSpaceBounds.set(0, topInset, windowWidth, topInset + searchBarSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ import android.graphics.BitmapFactory;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffXfermode;
|
||||
import android.graphics.Rect;
|
||||
@ -437,7 +438,9 @@ public class SystemServicesProxy {
|
||||
Rect windowRect = new Rect();
|
||||
if (mWm == null) return windowRect;
|
||||
|
||||
mWm.getDefaultDisplay().getRectSize(windowRect);
|
||||
Point p = new Point();
|
||||
mWm.getDefaultDisplay().getRealSize(p);
|
||||
windowRect.set(0, 0, p.x, p.y);
|
||||
return windowRect;
|
||||
}
|
||||
|
||||
|
@ -77,6 +77,7 @@ public class Task {
|
||||
public TaskKey key;
|
||||
public TaskGrouping group;
|
||||
public int taskAffiliation;
|
||||
public boolean isLaunchTarget;
|
||||
public Drawable applicationIcon;
|
||||
public Drawable activityIcon;
|
||||
public String activityLabel;
|
||||
|
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.recents.views;
|
||||
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.graphics.Outline;
|
||||
import android.graphics.Rect;
|
||||
import android.view.View;
|
||||
import android.view.ViewOutlineProvider;
|
||||
import com.android.systemui.recents.RecentsConfiguration;
|
||||
|
||||
/* An outline provider that has a clip and outline that can be animated. */
|
||||
public class AnimateableViewBounds extends ViewOutlineProvider {
|
||||
|
||||
RecentsConfiguration mConfig;
|
||||
|
||||
View mSourceView;
|
||||
Rect mClipRect = new Rect();
|
||||
Rect mOutlineClipRect = new Rect();
|
||||
int mCornerRadius;
|
||||
|
||||
ObjectAnimator mClipTopAnimator;
|
||||
ObjectAnimator mClipBottomAnimator;
|
||||
|
||||
public AnimateableViewBounds(View source, int cornerRadius) {
|
||||
mConfig = RecentsConfiguration.getInstance();
|
||||
mSourceView = source;
|
||||
mCornerRadius = cornerRadius;
|
||||
mSourceView.setClipToOutline(true);
|
||||
setClipTop(getClipTop());
|
||||
setClipBottom(getClipBottom());
|
||||
setOutlineClipBottom(getOutlineClipBottom());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getOutline(View view, Outline outline) {
|
||||
outline.setRoundRect(Math.max(mClipRect.left, mOutlineClipRect.left),
|
||||
Math.max(mClipRect.top, mOutlineClipRect.top),
|
||||
mSourceView.getMeasuredWidth() - Math.max(mClipRect.right, mOutlineClipRect.right),
|
||||
mSourceView.getMeasuredHeight() - Math.max(mClipRect.bottom, mOutlineClipRect.bottom),
|
||||
mCornerRadius);
|
||||
}
|
||||
|
||||
/** Animates the top clip. */
|
||||
void animateClipTop(int top, int duration) {
|
||||
if (mClipTopAnimator != null) {
|
||||
mClipTopAnimator.removeAllListeners();
|
||||
mClipTopAnimator.cancel();
|
||||
}
|
||||
mClipTopAnimator = ObjectAnimator.ofInt(this, "clipTop", top);
|
||||
mClipTopAnimator.setDuration(duration);
|
||||
mClipTopAnimator.setInterpolator(mConfig.fastOutSlowInInterpolator);
|
||||
mClipTopAnimator.start();
|
||||
}
|
||||
|
||||
/** Sets the top clip. */
|
||||
public void setClipTop(int top) {
|
||||
if (top != mClipRect.top) {
|
||||
mClipRect.top = top;
|
||||
mSourceView.invalidateOutline();
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the top clip. */
|
||||
public int getClipTop() {
|
||||
return mClipRect.top;
|
||||
}
|
||||
|
||||
/** Animates the bottom clip. */
|
||||
void animateClipBottom(int bottom, int duration) {
|
||||
if (mClipTopAnimator != null) {
|
||||
mClipTopAnimator.removeAllListeners();
|
||||
mClipTopAnimator.cancel();
|
||||
}
|
||||
mClipTopAnimator = ObjectAnimator.ofInt(this, "clipBottom", bottom);
|
||||
mClipTopAnimator.setDuration(duration);
|
||||
mClipTopAnimator.setInterpolator(mConfig.fastOutSlowInInterpolator);
|
||||
mClipTopAnimator.start();
|
||||
}
|
||||
|
||||
/** Sets the bottom clip. */
|
||||
public void setClipBottom(int bottom) {
|
||||
if (bottom != mClipRect.bottom) {
|
||||
mClipRect.bottom = bottom;
|
||||
mSourceView.invalidateOutline();
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the bottom clip. */
|
||||
public int getClipBottom() {
|
||||
return mClipRect.bottom;
|
||||
}
|
||||
|
||||
public void setOutlineClipBottom(int bottom) {
|
||||
if (bottom != mOutlineClipRect.bottom) {
|
||||
mOutlineClipRect.bottom = bottom;
|
||||
mSourceView.invalidateOutline();
|
||||
}
|
||||
}
|
||||
|
||||
public int getOutlineClipBottom() {
|
||||
return mOutlineClipRect.bottom;
|
||||
}
|
||||
}
|
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.recents.views;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.util.Pair;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* A full screen overlay layer that allows us to draw views from throughout the system on the top
|
||||
* most layer.
|
||||
*/
|
||||
public class DebugOverlayView extends FrameLayout {
|
||||
|
||||
final static int sCornerRectSize = 50;
|
||||
|
||||
ArrayList<Pair<Rect, Integer>> mRects = new ArrayList<Pair<Rect, Integer>>();
|
||||
Paint mDebugOutline = new Paint();
|
||||
Paint mTmpPaint = new Paint();
|
||||
boolean mEnabled = true;
|
||||
|
||||
public DebugOverlayView(Context context) {
|
||||
super(context);
|
||||
mDebugOutline.setColor(0xFFff0000);
|
||||
mDebugOutline.setStyle(Paint.Style.STROKE);
|
||||
mDebugOutline.setStrokeWidth(8f);
|
||||
setWillNotDraw(false);
|
||||
}
|
||||
|
||||
/** Enables the debug overlay drawing. */
|
||||
public void enable() {
|
||||
mEnabled = true;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/** Disables the debug overlay drawing. */
|
||||
public void disable() {
|
||||
mEnabled = false;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/** Clears all debug rects. */
|
||||
public void clear() {
|
||||
mRects.clear();
|
||||
}
|
||||
|
||||
/** Adds a rect to be drawn. */
|
||||
void addRect(Rect r, int color) {
|
||||
mRects.add(new Pair<Rect, Integer>(r, color));
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/** Adds a view's global rect to be drawn. */
|
||||
void addViewRect(View v, int color) {
|
||||
Rect vr = new Rect();
|
||||
v.getGlobalVisibleRect(vr);
|
||||
mRects.add(new Pair<Rect, Integer>(vr, color));
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/** Adds a rect, relative to a given view to be drawn. */
|
||||
void addRectRelativeToView(View v, Rect r, int color) {
|
||||
Rect vr = new Rect();
|
||||
v.getGlobalVisibleRect(vr);
|
||||
r.offsetTo(vr.left, vr.top);
|
||||
mRects.add(new Pair<Rect, Integer>(r, color));
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
addRect(new Rect(0, 0, sCornerRectSize, sCornerRectSize), 0xFFff0000);
|
||||
addRect(new Rect(getMeasuredWidth() - sCornerRectSize, getMeasuredHeight() - sCornerRectSize,
|
||||
getMeasuredWidth(), getMeasuredHeight()), 0xFFff0000);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
if (mEnabled) {
|
||||
// Draw the outline
|
||||
canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mDebugOutline);
|
||||
|
||||
// Draw the rects
|
||||
int numRects = mRects.size();
|
||||
for (int i = 0; i < numRects; i++) {
|
||||
Pair<Rect, Integer> r = mRects.get(i);
|
||||
mTmpPaint.setColor(r.second);
|
||||
canvas.drawRect(r.first, mTmpPaint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -17,6 +17,7 @@
|
||||
package com.android.systemui.recents.views;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.ImageView;
|
||||
@ -27,8 +28,6 @@ import android.widget.ImageView;
|
||||
*/
|
||||
public class FixedSizeImageView extends ImageView {
|
||||
|
||||
int mFixedWidth;
|
||||
int mFixedHeight;
|
||||
boolean mAllowRelayout = true;
|
||||
boolean mAllowInvalidate = true;
|
||||
|
||||
@ -48,13 +47,6 @@ public class FixedSizeImageView extends ImageView {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
mFixedWidth = getMeasuredWidth();
|
||||
mFixedHeight = getMeasuredHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestLayout() {
|
||||
if (mAllowRelayout) {
|
||||
@ -71,7 +63,9 @@ public class FixedSizeImageView extends ImageView {
|
||||
|
||||
@Override
|
||||
public void setImageDrawable(Drawable drawable) {
|
||||
if (drawable == null || (mFixedWidth > 0 && mFixedHeight > 0)) {
|
||||
boolean isNullBitmapDrawable = (drawable instanceof BitmapDrawable) &&
|
||||
(((BitmapDrawable) drawable).getBitmap() == null);
|
||||
if (drawable == null || isNullBitmapDrawable) {
|
||||
mAllowRelayout = false;
|
||||
mAllowInvalidate = false;
|
||||
}
|
||||
|
@ -1,285 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.recents.views;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffColorFilter;
|
||||
import android.graphics.Rect;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.animation.AccelerateInterpolator;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.recents.RecentsConfiguration;
|
||||
|
||||
|
||||
/**
|
||||
* The full screen transition view that gets animated down from the full screen into a task
|
||||
* thumbnail view.
|
||||
*/
|
||||
public class FullscreenTransitionOverlayView extends FrameLayout {
|
||||
|
||||
/** The FullscreenTransitionOverlayView callbacks */
|
||||
public interface FullScreenTransitionViewCallbacks {
|
||||
void onEnterAnimationComplete();
|
||||
}
|
||||
|
||||
RecentsConfiguration mConfig;
|
||||
|
||||
FullScreenTransitionViewCallbacks mCb;
|
||||
|
||||
ImageView mScreenshotView;
|
||||
Rect mClipRect = new Rect();
|
||||
Paint mLayerPaint = new Paint();
|
||||
PorterDuffColorFilter mDimColorFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.MULTIPLY);
|
||||
|
||||
int mDim;
|
||||
int mMaxDim;
|
||||
AccelerateInterpolator mDimInterpolator = new AccelerateInterpolator();
|
||||
|
||||
boolean mIsAnimating;
|
||||
AnimatorSet mEnterAnimation;
|
||||
|
||||
public FullscreenTransitionOverlayView(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public FullscreenTransitionOverlayView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public FullscreenTransitionOverlayView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
this(context, attrs, defStyleAttr, 0);
|
||||
}
|
||||
|
||||
public FullscreenTransitionOverlayView(Context context, AttributeSet attrs, int defStyleAttr,
|
||||
int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
mConfig = RecentsConfiguration.getInstance();
|
||||
mMaxDim = mConfig.taskStackMaxDim;
|
||||
setClipTop(getClipTop());
|
||||
setClipBottom(getClipBottom());
|
||||
setDim(getDim());
|
||||
setWillNotDraw(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
mScreenshotView = (ImageView) findViewById(R.id.image);
|
||||
}
|
||||
|
||||
/** Sets the callbacks */
|
||||
public void setCallbacks(FullScreenTransitionViewCallbacks cb) {
|
||||
mCb = cb;
|
||||
}
|
||||
|
||||
/** Sets the top clip */
|
||||
public void setClipTop(int clip) {
|
||||
mClipRect.top = clip;
|
||||
setClipBounds(mClipRect);
|
||||
}
|
||||
|
||||
/** Gets the top clip */
|
||||
public int getClipTop() {
|
||||
return mClipRect.top;
|
||||
}
|
||||
|
||||
/** Sets the bottom clip */
|
||||
public void setClipBottom(int clip) {
|
||||
mClipRect.bottom = clip;
|
||||
setClipBounds(mClipRect);
|
||||
}
|
||||
|
||||
/** Gets the top clip */
|
||||
public int getClipBottom() {
|
||||
return mClipRect.bottom;
|
||||
}
|
||||
|
||||
/** Returns the current dim. */
|
||||
public void setDim(int dim) {
|
||||
mDim = dim;
|
||||
/*
|
||||
int inverse = 255 - mDim;
|
||||
mDimColorFilter.setColor(Color.argb(0xFF, inverse, inverse, inverse));
|
||||
mLayerPaint.setColorFilter(mDimColorFilter);
|
||||
setLayerType(LAYER_TYPE_HARDWARE, mLayerPaint);
|
||||
*/
|
||||
}
|
||||
|
||||
/** Returns the current dim. */
|
||||
public int getDim() {
|
||||
return mDim;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
mClipRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasOverlappingRendering() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Prepares the screenshot view for the transition into Recents */
|
||||
public void prepareAnimateOnEnterRecents(Bitmap screenshot) {
|
||||
if (!mConfig.launchedFromAppWithScreenshot) return;
|
||||
|
||||
setClipTop(0);
|
||||
setClipBottom(getMeasuredHeight());
|
||||
setDim(0);
|
||||
setTranslationY(0f);
|
||||
setScaleX(1f);
|
||||
setScaleY(1f);
|
||||
setVisibility(mConfig.launchedFromAppWithScreenshot ? View.VISIBLE : View.INVISIBLE);
|
||||
if (screenshot != null) {
|
||||
mScreenshotView.setImageBitmap(screenshot);
|
||||
} else {
|
||||
mScreenshotView.setImageDrawable(null);
|
||||
}
|
||||
}
|
||||
|
||||
/** Resets the transition view */
|
||||
public void reset() {
|
||||
setVisibility(View.GONE);
|
||||
mScreenshotView.setImageDrawable(null);
|
||||
}
|
||||
|
||||
/** Animates this view as it enters recents */
|
||||
public void animateOnEnterRecents(ViewAnimation.TaskViewEnterContext ctx,
|
||||
final Runnable postAnimRunnable) {
|
||||
// Cancel the current animation
|
||||
if (mEnterAnimation != null) {
|
||||
mEnterAnimation.removeAllListeners();
|
||||
mEnterAnimation.cancel();
|
||||
}
|
||||
|
||||
// Calculate the bottom clip
|
||||
Rect targetTaskRect = ctx.targetTaskTransform.rect;
|
||||
float scale = (float) targetTaskRect.width() / getMeasuredWidth();
|
||||
float scaleYOffset = ((1f - scale) * getMeasuredHeight()) / 2;
|
||||
float scaledTopInset = (int) (scale * mConfig.systemInsets.top);
|
||||
int translationY = (int) -scaleYOffset + (int) (mConfig.systemInsets.top - scaledTopInset)
|
||||
+ targetTaskRect.top;
|
||||
int clipBottom = mConfig.systemInsets.top + (int) (targetTaskRect.height() / scale);
|
||||
|
||||
// Calculate the dim
|
||||
float minScale = TaskStackViewLayoutAlgorithm.StackPeekMinScale;
|
||||
float scaleRange = 1f - minScale;
|
||||
float dim = (1f - ctx.targetTaskTransform.scale) / scaleRange;
|
||||
dim = mDimInterpolator.getInterpolation(Math.min(dim, 1f));
|
||||
int toDim = Math.max(0, Math.min(mMaxDim, (int) (dim * 255)));
|
||||
|
||||
// Enable the HW Layers on the screenshot view
|
||||
mScreenshotView.setLayerType(View.LAYER_TYPE_HARDWARE, mLayerPaint);
|
||||
|
||||
// Compose the animation
|
||||
mEnterAnimation = new AnimatorSet();
|
||||
mEnterAnimation.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Mark that we are no longer animating
|
||||
mIsAnimating = false;
|
||||
// Disable the HW Layers on this view
|
||||
setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
|
||||
// Notify any callbacks
|
||||
mCb.onEnterAnimationComplete();
|
||||
// Run the given post-anim runnable
|
||||
postAnimRunnable.run();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
// XXX: Translation y should be negative initially to simulate moving from the top of the screen?
|
||||
mEnterAnimation.setStartDelay(0);
|
||||
mEnterAnimation.setDuration(475);
|
||||
mEnterAnimation.setInterpolator(mConfig.fastOutSlowInInterpolator);
|
||||
mEnterAnimation.playTogether(
|
||||
// ObjectAnimator.ofInt(this, "clipTop", mConfig.systemInsets.top),
|
||||
ObjectAnimator.ofInt(this, "clipBottom", clipBottom),
|
||||
ObjectAnimator.ofInt(this, "dim", toDim),
|
||||
ObjectAnimator.ofFloat(this, "translationY", translationY),
|
||||
ObjectAnimator.ofFloat(this, "scaleX", scale),
|
||||
ObjectAnimator.ofFloat(this, "scaleY", scale)
|
||||
);
|
||||
setClipTop(mConfig.systemInsets.top);
|
||||
mEnterAnimation.start();
|
||||
|
||||
mIsAnimating = true;
|
||||
}
|
||||
|
||||
/** Animates this view back out of Recents if we were in the process of animating in. */
|
||||
public boolean cancelAnimateOnEnterRecents(final Runnable postAnimRunnable) {
|
||||
if (mIsAnimating) {
|
||||
// Cancel the current animation
|
||||
if (mEnterAnimation != null) {
|
||||
mEnterAnimation.removeAllListeners();
|
||||
mEnterAnimation.cancel();
|
||||
}
|
||||
|
||||
// Enable the HW Layers on the screenshot view
|
||||
mScreenshotView.setLayerType(View.LAYER_TYPE_HARDWARE, mLayerPaint);
|
||||
|
||||
// Compose the animation
|
||||
mEnterAnimation = new AnimatorSet();
|
||||
mEnterAnimation.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Mark that we are no longer animating
|
||||
mIsAnimating = false;
|
||||
// Disable the HW Layers on the screenshot view
|
||||
mScreenshotView.setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
|
||||
// Notify any callbacks
|
||||
mCb.onEnterAnimationComplete();
|
||||
// Run the given post-anim runnable
|
||||
postAnimRunnable.run();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
mEnterAnimation.setDuration(475);
|
||||
mEnterAnimation.setInterpolator(mConfig.fastOutSlowInInterpolator);
|
||||
mEnterAnimation.playTogether(
|
||||
ObjectAnimator.ofInt(this, "clipTop", 0),
|
||||
ObjectAnimator.ofInt(this, "clipBottom", getMeasuredHeight()),
|
||||
ObjectAnimator.ofInt(this, "dim", 0),
|
||||
ObjectAnimator.ofFloat(this, "translationY", 0f),
|
||||
ObjectAnimator.ofFloat(this, "scaleX", 1f),
|
||||
ObjectAnimator.ofFloat(this, "scaleY", 1f)
|
||||
);
|
||||
mEnterAnimation.start();
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@ -24,7 +24,6 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.net.Uri;
|
||||
import android.provider.Settings;
|
||||
@ -40,7 +39,6 @@ import com.android.systemui.recents.misc.SystemServicesProxy;
|
||||
import com.android.systemui.recents.misc.Utilities;
|
||||
import com.android.systemui.recents.model.RecentsPackageMonitor;
|
||||
import com.android.systemui.recents.model.RecentsTaskLoader;
|
||||
import com.android.systemui.recents.model.SpaceNode;
|
||||
import com.android.systemui.recents.model.Task;
|
||||
import com.android.systemui.recents.model.TaskStack;
|
||||
|
||||
@ -63,13 +61,10 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
|
||||
|
||||
RecentsConfiguration mConfig;
|
||||
LayoutInflater mInflater;
|
||||
Paint mDebugModePaint;
|
||||
DebugOverlayView mDebugOverlay;
|
||||
|
||||
// The space partitioning root of this container
|
||||
SpaceNode mBSP;
|
||||
// Search bar view
|
||||
ArrayList<TaskStack> mStacks;
|
||||
View mSearchBar;
|
||||
// Recents view callbacks
|
||||
RecentsViewCallbacks mCb;
|
||||
|
||||
public RecentsView(Context context) {
|
||||
@ -95,10 +90,13 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
|
||||
mCb = cb;
|
||||
}
|
||||
|
||||
/** Set/get the bsp root node */
|
||||
public void setBSP(SpaceNode n) {
|
||||
mBSP = n;
|
||||
/** Sets the debug overlay */
|
||||
public void setDebugOverlay(DebugOverlayView overlay) {
|
||||
mDebugOverlay = overlay;
|
||||
}
|
||||
|
||||
/** Set/get the bsp root node */
|
||||
public void setTaskStacks(ArrayList<TaskStack> stacks) {
|
||||
// Remove all TaskStackViews (but leave the search bar)
|
||||
int childCount = getChildCount();
|
||||
for (int i = childCount - 1; i >= 0; i--) {
|
||||
@ -109,21 +107,18 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
|
||||
}
|
||||
|
||||
// Create and add all the stacks for this partition of space.
|
||||
ArrayList<TaskStack> stacks = mBSP.getStacks();
|
||||
for (TaskStack stack : stacks) {
|
||||
mStacks = stacks;
|
||||
int numStacks = mStacks.size();
|
||||
for (int i = 0; i < numStacks; i++) {
|
||||
TaskStack stack = mStacks.get(i);
|
||||
TaskStackView stackView = new TaskStackView(getContext(), stack);
|
||||
stackView.setCallbacks(this);
|
||||
// Enable debug mode drawing
|
||||
if (mConfig.debugModeEnabled) {
|
||||
stackView.setDebugOverlay(mDebugOverlay);
|
||||
}
|
||||
addView(stackView);
|
||||
}
|
||||
|
||||
// Enable debug mode drawing
|
||||
if (mConfig.debugModeEnabled) {
|
||||
mDebugModePaint = new Paint();
|
||||
mDebugModePaint.setColor(0xFFff0000);
|
||||
mDebugModePaint.setStyle(Paint.Style.STROKE);
|
||||
mDebugModePaint.setStrokeWidth(5f);
|
||||
setWillNotDraw(false);
|
||||
}
|
||||
}
|
||||
|
||||
/** Launches the focused task from the first stack if possible */
|
||||
@ -150,8 +145,8 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Launches the first task from the first stack if possible */
|
||||
public boolean launchFirstTask() {
|
||||
/** Launches the task that Recents was launched from, if possible */
|
||||
public boolean launchPreviousTask() {
|
||||
// Get the first stack view
|
||||
int childCount = getChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
@ -161,20 +156,17 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
|
||||
TaskStack stack = stackView.mStack;
|
||||
ArrayList<Task> tasks = stack.getTasks();
|
||||
|
||||
// Get the first task in the stack
|
||||
// Find the launch task in the stack
|
||||
if (!tasks.isEmpty()) {
|
||||
Task task = tasks.get(tasks.size() - 1);
|
||||
TaskView tv = null;
|
||||
|
||||
// Try and use the first child task view as the source of the launch animation
|
||||
if (stackView.getChildCount() > 0) {
|
||||
TaskView stv = (TaskView) stackView.getChildAt(stackView.getChildCount() - 1);
|
||||
if (stv.getTask() == task) {
|
||||
tv = stv;
|
||||
int taskCount = tasks.size();
|
||||
for (int j = 0; j < taskCount; j++) {
|
||||
if (tasks.get(j).isLaunchTarget) {
|
||||
Task task = tasks.get(j);
|
||||
TaskView tv = stackView.getChildViewForTask(task);
|
||||
onTaskViewClicked(stackView, tv, stack, task, false);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
onTaskViewClicked(stackView, tv, stack, task, false);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -242,35 +234,31 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
int width = MeasureSpec.getSize(widthMeasureSpec);
|
||||
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
|
||||
int height = MeasureSpec.getSize(heightMeasureSpec);
|
||||
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
|
||||
|
||||
// Get the search bar bounds and measure the search bar layout
|
||||
if (mSearchBar != null) {
|
||||
Rect searchBarSpaceBounds = new Rect();
|
||||
mConfig.getSearchBarBounds(width, height - mConfig.systemInsets.top, searchBarSpaceBounds);
|
||||
mConfig.getSearchBarBounds(width, height, mConfig.systemInsets.top, searchBarSpaceBounds);
|
||||
mSearchBar.measure(
|
||||
MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.width(), MeasureSpec.EXACTLY),
|
||||
MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.height(), MeasureSpec.EXACTLY));
|
||||
}
|
||||
|
||||
// We give the full width of the space, not including the right nav bar insets in landscape,
|
||||
// to the stack view, since we want the tasks to render under the search bar in landscape.
|
||||
// In addition, we give it the full height, not including the top inset or search bar space,
|
||||
// since we want the tasks to render under the navigation buttons in portrait.
|
||||
Rect taskStackBounds = new Rect();
|
||||
mConfig.getTaskStackBounds(width, height, taskStackBounds);
|
||||
int childWidth = width - mConfig.systemInsets.right;
|
||||
int childHeight = taskStackBounds.height() - mConfig.systemInsets.top;
|
||||
mConfig.getTaskStackBounds(width, height, mConfig.systemInsets.top,
|
||||
mConfig.systemInsets.right, taskStackBounds);
|
||||
|
||||
// Measure each TaskStackView
|
||||
// Measure each TaskStackView with the full width and height of the window since the
|
||||
// transition view is a child of that stack view
|
||||
int childCount = getChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
View child = getChildAt(i);
|
||||
if (child != mSearchBar && child.getVisibility() != GONE) {
|
||||
child.measure(MeasureSpec.makeMeasureSpec(childWidth, widthMode),
|
||||
MeasureSpec.makeMeasureSpec(childHeight, heightMode));
|
||||
TaskStackView tsv = (TaskStackView) child;
|
||||
// Set the insets to be the top/left inset + search bounds
|
||||
tsv.setStackInsetRect(taskStackBounds);
|
||||
tsv.measure(widthMeasureSpec, heightMeasureSpec);
|
||||
}
|
||||
}
|
||||
|
||||
@ -285,49 +273,30 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
|
||||
// Get the search bar bounds so that we lay it out
|
||||
if (mSearchBar != null) {
|
||||
Rect searchBarSpaceBounds = new Rect();
|
||||
mConfig.getSearchBarBounds(getMeasuredWidth(), getMeasuredHeight(), searchBarSpaceBounds);
|
||||
mSearchBar.layout(mConfig.systemInsets.left + searchBarSpaceBounds.left,
|
||||
mConfig.systemInsets.top + searchBarSpaceBounds.top,
|
||||
mConfig.systemInsets.left + mSearchBar.getMeasuredWidth(),
|
||||
mConfig.systemInsets.top + mSearchBar.getMeasuredHeight());
|
||||
mConfig.getSearchBarBounds(getMeasuredWidth(), getMeasuredHeight(),
|
||||
mConfig.systemInsets.top, searchBarSpaceBounds);
|
||||
mSearchBar.layout(searchBarSpaceBounds.left, searchBarSpaceBounds.top,
|
||||
searchBarSpaceBounds.right, searchBarSpaceBounds.bottom);
|
||||
}
|
||||
|
||||
// We offset the stack view by the left inset (if any), but lay it out under the search bar.
|
||||
// In addition, we offset our stack views by the top inset and search bar height, but not
|
||||
// the bottom insets because we want it to render under the navigation buttons.
|
||||
Rect taskStackBounds = new Rect();
|
||||
mConfig.getTaskStackBounds(getMeasuredWidth(), getMeasuredHeight(), taskStackBounds);
|
||||
left += mConfig.systemInsets.left;
|
||||
top += mConfig.systemInsets.top + taskStackBounds.top;
|
||||
|
||||
// Layout each child
|
||||
// XXX: Based on the space node for that task view
|
||||
// Layout each TaskStackView with the full width and height of the window since the
|
||||
// transition view is a child of that stack view
|
||||
int childCount = getChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
View child = getChildAt(i);
|
||||
if (child != mSearchBar && child.getVisibility() != GONE) {
|
||||
TaskStackView tsv = (TaskStackView) child;
|
||||
child.layout(left, top, left + tsv.getMeasuredWidth(), top + tsv.getMeasuredHeight());
|
||||
child.layout(left, top, left + child.getMeasuredWidth(),
|
||||
top + child.getMeasuredHeight());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
// Debug mode drawing
|
||||
if (mConfig.debugModeEnabled) {
|
||||
canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mDebugModePaint);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
|
||||
// Update the configuration with the latest system insets and trigger a relayout
|
||||
mConfig.updateSystemInsets(insets.getSystemWindowInsets());
|
||||
requestLayout();
|
||||
|
||||
return insets.consumeSystemWindowInsets(false, false, false, true);
|
||||
return insets.consumeSystemWindowInsets();
|
||||
}
|
||||
|
||||
/** Notifies each task view of the user interaction. */
|
||||
@ -364,11 +333,12 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
|
||||
|
||||
/** Unfilters any filtered stacks */
|
||||
public boolean unfilterFilteredStacks() {
|
||||
if (mBSP != null) {
|
||||
if (mStacks != null) {
|
||||
// Check if there are any filtered stacks and unfilter them before we back out of Recents
|
||||
boolean stacksUnfiltered = false;
|
||||
ArrayList<TaskStack> stacks = mBSP.getStacks();
|
||||
for (TaskStack stack : stacks) {
|
||||
int numStacks = mStacks.size();
|
||||
for (int i = 0; i < numStacks; i++) {
|
||||
TaskStack stack = mStacks.get(i);
|
||||
if (stack.hasFilteredTasks()) {
|
||||
stack.unfilterTasks();
|
||||
stacksUnfiltered = true;
|
||||
|
@ -43,8 +43,6 @@ class TaskBarView extends FrameLayout {
|
||||
|
||||
RecentsConfiguration mConfig;
|
||||
|
||||
Task mTask;
|
||||
|
||||
ImageView mDismissButton;
|
||||
ImageView mApplicationIcon;
|
||||
TextView mActivityDescription;
|
||||
@ -52,6 +50,8 @@ class TaskBarView extends FrameLayout {
|
||||
Drawable mLightDismissDrawable;
|
||||
Drawable mDarkDismissDrawable;
|
||||
|
||||
boolean mIsFullscreen;
|
||||
|
||||
Paint mLayerPaint = new Paint();
|
||||
static Paint sHighlightPaint;
|
||||
|
||||
@ -113,11 +113,18 @@ class TaskBarView extends FrameLayout {
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
// Draw the highlight at the top edge (but put the bottom edge just out of view)
|
||||
float offset = mConfig.taskViewHighlightPx / 2f;
|
||||
float radius = mConfig.taskViewRoundedCornerRadiusPx;
|
||||
canvas.drawRoundRect(-offset, 0f, (float) getMeasuredWidth() + offset,
|
||||
getMeasuredHeight() + radius, radius, radius, sHighlightPaint);
|
||||
if (!mIsFullscreen) {
|
||||
// Draw the highlight at the top edge (but put the bottom edge just out of view)
|
||||
float offset = mConfig.taskViewHighlightPx / 2f;
|
||||
float radius = mConfig.taskViewRoundedCornerRadiusPx;
|
||||
canvas.drawRoundRect(-offset, 0f, (float) getMeasuredWidth() + offset,
|
||||
getMeasuredHeight() + radius, radius, radius, sHighlightPaint);
|
||||
}
|
||||
}
|
||||
|
||||
/** Sets whether the current task is full screen or not. */
|
||||
void setIsFullscreen(boolean isFullscreen) {
|
||||
mIsFullscreen = isFullscreen;
|
||||
}
|
||||
|
||||
/** Synchronizes this bar view's properties with the task's transform */
|
||||
@ -149,7 +156,6 @@ class TaskBarView extends FrameLayout {
|
||||
|
||||
/** Binds the bar view to the task */
|
||||
void rebindToTask(Task t) {
|
||||
mTask = t;
|
||||
// If an activity icon is defined, then we use that as the primary icon to show in the bar,
|
||||
// otherwise, we fall back to the application icon
|
||||
if (t.activityIcon != null) {
|
||||
@ -170,7 +176,6 @@ class TaskBarView extends FrameLayout {
|
||||
|
||||
/** Unbinds the bar view from the task */
|
||||
void unbindFromTask() {
|
||||
mTask = null;
|
||||
mApplicationIcon.setImageDrawable(null);
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.recents.views;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
import com.android.systemui.recents.RecentsConfiguration;
|
||||
|
||||
|
||||
/** The task footer view */
|
||||
public class TaskFooterView extends FrameLayout {
|
||||
|
||||
interface TaskFooterViewCallbacks {
|
||||
public void onTaskFooterHeightChanged(int height, int maxHeight);
|
||||
}
|
||||
|
||||
RecentsConfiguration mConfig;
|
||||
|
||||
TaskFooterViewCallbacks mCb;
|
||||
int mFooterHeight;
|
||||
int mMaxFooterHeight;
|
||||
ObjectAnimator mFooterAnimator;
|
||||
|
||||
public TaskFooterView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public TaskFooterView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public TaskFooterView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
this(context, attrs, defStyleAttr, 0);
|
||||
}
|
||||
|
||||
public TaskFooterView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
mConfig = RecentsConfiguration.getInstance();
|
||||
mMaxFooterHeight = mConfig.taskViewLockToAppButtonHeight;
|
||||
setFooterHeight(getFooterHeight());
|
||||
}
|
||||
|
||||
/** Sets the callbacks for when the footer height changes. */
|
||||
void setCallbacks(TaskFooterViewCallbacks cb) {
|
||||
mCb = cb;
|
||||
mCb.onTaskFooterHeightChanged(mFooterHeight, mMaxFooterHeight);
|
||||
}
|
||||
|
||||
/** Sets the footer height. */
|
||||
public void setFooterHeight(int footerHeight) {
|
||||
if (footerHeight != mFooterHeight) {
|
||||
mFooterHeight = footerHeight;
|
||||
mCb.onTaskFooterHeightChanged(footerHeight, mMaxFooterHeight);
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the footer height. */
|
||||
public int getFooterHeight() {
|
||||
return mFooterHeight;
|
||||
}
|
||||
|
||||
/** Animates the footer into and out of view. */
|
||||
void animateFooterVisibility(final boolean visible, int duration) {
|
||||
// Return early if there is no footer
|
||||
if (mMaxFooterHeight <= 0) return;
|
||||
// Return early if we are already in the final state
|
||||
if (!visible && getVisibility() != View.VISIBLE) return;
|
||||
if (visible && getVisibility() == View.VISIBLE) return;
|
||||
|
||||
// Cancel the previous animation
|
||||
if (mFooterAnimator != null) {
|
||||
mFooterAnimator.removeAllListeners();
|
||||
mFooterAnimator.cancel();
|
||||
}
|
||||
int finalHeight = visible ? mMaxFooterHeight : 0;
|
||||
if (duration > 0) {
|
||||
// Make the view visible for the animation
|
||||
if (visible && getVisibility() != View.VISIBLE) {
|
||||
setVisibility(View.VISIBLE);
|
||||
}
|
||||
mFooterAnimator = ObjectAnimator.ofInt(this, "footerHeight", finalHeight);
|
||||
mFooterAnimator.setDuration(duration);
|
||||
mFooterAnimator.setInterpolator(mConfig.fastOutSlowInInterpolator);
|
||||
mFooterAnimator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
if (!visible) {
|
||||
setVisibility(View.INVISIBLE);
|
||||
}
|
||||
}
|
||||
});
|
||||
mFooterAnimator.start();
|
||||
} else {
|
||||
setFooterHeight(finalHeight);
|
||||
setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
|
||||
}
|
||||
}
|
||||
}
|
@ -23,7 +23,6 @@ import android.animation.ValueAnimator;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
@ -73,6 +72,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
ViewPool<TaskView, Task> mViewPool;
|
||||
ArrayList<TaskViewTransform> mCurrentTaskTransforms = new ArrayList<TaskViewTransform>();
|
||||
DozeTrigger mUIDozeTrigger;
|
||||
DebugOverlayView mDebugOverlay;
|
||||
Rect mTaskStackBounds = new Rect();
|
||||
|
||||
// The virtual stack scroll that we use for the card layout
|
||||
int mStackScroll;
|
||||
@ -94,8 +95,12 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
int[] mTmpVisibleRange = new int[2];
|
||||
Rect mTmpRect = new Rect();
|
||||
Rect mTmpRect2 = new Rect();
|
||||
TaskViewTransform mTmpTransform = new TaskViewTransform();
|
||||
HashMap<Task, TaskView> mTmpTaskViewMap = new HashMap<Task, TaskView>();
|
||||
LayoutInflater mInflater;
|
||||
|
||||
// A convenience runnable to return all views to the pool
|
||||
// XXX: After this is set, we should mark this task stack view as disabled and check that in synchronize model
|
||||
Runnable mReturnAllViewsToPoolRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@ -162,6 +167,11 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
mCb = cb;
|
||||
}
|
||||
|
||||
/** Sets the debug overlay */
|
||||
public void setDebugOverlay(DebugOverlayView overlay) {
|
||||
mDebugOverlay = overlay;
|
||||
}
|
||||
|
||||
/** Requests that the views be synchronized with the model */
|
||||
void requestSynchronizeStackViewsWithModel() {
|
||||
requestSynchronizeStackViewsWithModel(0);
|
||||
@ -179,19 +189,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns a mapping of child view to Task. */
|
||||
HashMap<Task, TaskView> getTaskChildViewMap() {
|
||||
HashMap<Task, TaskView> taskViewMap = new HashMap<Task, TaskView>();
|
||||
int childCount = getChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
TaskView tv = (TaskView) getChildAt(i);
|
||||
taskViewMap.put(tv.getTask(), tv);
|
||||
}
|
||||
return taskViewMap;
|
||||
}
|
||||
|
||||
/** Finds the child view given a specific task. */
|
||||
TaskView getChildViewForTask(Task t) {
|
||||
public TaskView getChildViewForTask(Task t) {
|
||||
int childCount = getChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
TaskView tv = (TaskView) getChildAt(i);
|
||||
@ -210,19 +209,23 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
/**
|
||||
* Gets the stack transforms of a list of tasks, and returns the visible range of tasks.
|
||||
*/
|
||||
private void updateStackTransforms(ArrayList<TaskViewTransform> taskTransforms,
|
||||
private boolean updateStackTransforms(ArrayList<TaskViewTransform> taskTransforms,
|
||||
ArrayList<Task> tasks,
|
||||
int stackScroll,
|
||||
int[] visibleRangeOut,
|
||||
boolean boundTranslationsToRect) {
|
||||
// XXX: We should be intelligent about where to look for the visible stack range using the
|
||||
// XXX: We should be intelligent about wheee to look for the visible stack range using the
|
||||
// current stack scroll.
|
||||
// XXX: We should log extra cases like the ones below where we don't expect to hit very often
|
||||
// XXX: Print out approximately how many indices we have to go through to find the first visible transform
|
||||
|
||||
int taskTransformCount = taskTransforms.size();
|
||||
int taskCount = tasks.size();
|
||||
int frontMostVisibleIndex = -1;
|
||||
int backMostVisibleIndex = -1;
|
||||
|
||||
|
||||
|
||||
// We can reuse the task transforms where possible to reduce object allocation
|
||||
if (taskTransformCount < taskCount) {
|
||||
// If there are less transforms than tasks, then add as many transforms as necessary
|
||||
@ -256,13 +259,14 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
|
||||
if (boundTranslationsToRect) {
|
||||
transform.translationY = Math.min(transform.translationY,
|
||||
mStackAlgorithm.mRect.bottom);
|
||||
mStackAlgorithm.mViewRect.bottom);
|
||||
}
|
||||
}
|
||||
if (visibleRangeOut != null) {
|
||||
visibleRangeOut[0] = frontMostVisibleIndex;
|
||||
visibleRangeOut[1] = backMostVisibleIndex;
|
||||
}
|
||||
return frontMostVisibleIndex != -1 && backMostVisibleIndex != -1;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -280,34 +284,33 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
}
|
||||
|
||||
/** Synchronizes the views with the model */
|
||||
void synchronizeStackViewsWithModel() {
|
||||
boolean synchronizeStackViewsWithModel() {
|
||||
if (mStackViewsDirty) {
|
||||
// Get all the task transforms
|
||||
ArrayList<Task> tasks = mStack.getTasks();
|
||||
int stackScroll = getStackScroll();
|
||||
int[] visibleRange = mTmpVisibleRange;
|
||||
updateStackTransforms(mCurrentTaskTransforms, tasks, stackScroll, visibleRange, false);
|
||||
TaskViewTransform tmpTransform = new TaskViewTransform();
|
||||
boolean isValidVisibleRange = updateStackTransforms(mCurrentTaskTransforms, tasks, stackScroll, visibleRange, false);
|
||||
|
||||
// Return all the invisible children to the pool
|
||||
HashMap<Task, TaskView> taskChildViewMap = getTaskChildViewMap();
|
||||
mTmpTaskViewMap.clear();
|
||||
int childCount = getChildCount();
|
||||
for (int i = childCount - 1; i >= 0; i--) {
|
||||
TaskView tv = (TaskView) getChildAt(i);
|
||||
Task task = tv.getTask();
|
||||
int taskIndex = mStack.indexOfTask(task);
|
||||
if (taskIndex < visibleRange[1] || taskIndex > visibleRange[0]) {
|
||||
taskChildViewMap.remove(task);
|
||||
if (visibleRange[1] <= taskIndex && taskIndex <= visibleRange[0]) {
|
||||
mTmpTaskViewMap.put(task, tv);
|
||||
} else {
|
||||
mViewPool.returnViewToPool(tv);
|
||||
}
|
||||
}
|
||||
|
||||
// Pick up all the newly visible children and update all the existing children
|
||||
boolean isValidVisibleRange = visibleRange[0] != -1 && visibleRange[1] != -1;
|
||||
for (int i = visibleRange[0]; isValidVisibleRange && i >= visibleRange[1]; i--) {
|
||||
Task task = tasks.get(i);
|
||||
TaskViewTransform transform = mCurrentTaskTransforms.get(i);
|
||||
TaskView tv = taskChildViewMap.get(task);
|
||||
TaskView tv = mTmpTaskViewMap.get(task);
|
||||
int taskIndex = mStack.indexOfTask(task);
|
||||
|
||||
if (tv == null) {
|
||||
@ -316,23 +319,26 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
// For items in the list, put them in start animating them from the
|
||||
// approriate ends of the list where they are expected to appear
|
||||
if (transform.t < 0) {
|
||||
tmpTransform = mStackAlgorithm.getStackTransform(tasks.get(0), stackScroll, tmpTransform);
|
||||
mTmpTransform = mStackAlgorithm.getStackTransform(tasks.get(0), stackScroll, mTmpTransform);
|
||||
} else {
|
||||
tmpTransform = mStackAlgorithm.getStackTransform(tasks.get(Math.min(tasks.size() - 1, visibleRange[0] + 1)),
|
||||
stackScroll, tmpTransform);
|
||||
mTmpTransform = mStackAlgorithm.getStackTransform(tasks.get(Math.min(tasks.size() - 1, visibleRange[0] + 1)),
|
||||
stackScroll, mTmpTransform);
|
||||
}
|
||||
tv.updateViewPropertiesToTaskTransform(tmpTransform, 0);
|
||||
tv.updateViewPropertiesToTaskTransform(mTmpTransform, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Update and animate the task into place
|
||||
// Animate the task into place
|
||||
tv.updateViewPropertiesToTaskTransform(mCurrentTaskTransforms.get(taskIndex),
|
||||
mStackViewsAnimationDuration);
|
||||
}
|
||||
|
||||
// Reset the request-synchronize params
|
||||
mStackViewsAnimationDuration = 0;
|
||||
mStackViewsDirty = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Updates the clip for each of the task views. */
|
||||
@ -368,13 +374,13 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
clipBottom = (mTmpRect.bottom - scaledMaxFooterHeight - mTmpRect2.top);
|
||||
}
|
||||
}
|
||||
tv.setClipFromBottom(clipBottom);
|
||||
tv.getViewBounds().setClipBottom(clipBottom);
|
||||
}
|
||||
if (getChildCount() > 0) {
|
||||
// The front most task should never be clipped
|
||||
TaskView tv = (TaskView) getChildAt(getChildCount() - 1);
|
||||
tv.getViewBounds().setClipBottom(0);
|
||||
}
|
||||
}
|
||||
if (getChildCount() > 0) {
|
||||
// The front most task should never be clipped
|
||||
TaskView tv = (TaskView) getChildAt(getChildCount() - 1);
|
||||
tv.setClipFromBottom(0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -384,18 +390,24 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
int childCount = getChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
TaskView tv = (TaskView) getChildAt(i);
|
||||
tv.setClipFromBottom(0);
|
||||
tv.getViewBounds().setClipBottom(0);
|
||||
}
|
||||
}
|
||||
mEnableStackClipping = stackClippingEnabled;
|
||||
}
|
||||
|
||||
/** The stack insets to apply to the stack contents */
|
||||
public void setStackInsetRect(Rect r) {
|
||||
mTaskStackBounds.set(r);
|
||||
}
|
||||
|
||||
/** Sets the current stack scroll */
|
||||
public void setStackScroll(int value) {
|
||||
mStackScroll = value;
|
||||
mUIDozeTrigger.poke();
|
||||
requestSynchronizeStackViewsWithModel();
|
||||
}
|
||||
|
||||
/** Sets the current stack scroll without synchronizing the stack view with the model */
|
||||
public void setStackScrollRaw(int value) {
|
||||
mStackScroll = value;
|
||||
@ -408,7 +420,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
/** Computes the initial stack scroll for the stack. */
|
||||
int getInitialStackScroll() {
|
||||
if (mStack.getTaskCount() > 2) {
|
||||
return mMaxScroll - (int) (mStackAlgorithm.mTaskRect.height() * (3f/4f));
|
||||
return Math.max(mMinScroll, mMaxScroll - (int) (mStackAlgorithm.mTaskRect.height() * (3f/4f)));
|
||||
}
|
||||
return mMaxScroll;
|
||||
}
|
||||
@ -521,7 +533,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
|
||||
/** Returns whether the specified scroll is out of bounds */
|
||||
boolean isScrollOutOfBounds() {
|
||||
return getScrollAmountOutOfBounds(getStackScroll()) != 0;
|
||||
return getScrollAmountOutOfBounds(mStackScroll) != 0;
|
||||
}
|
||||
|
||||
/** Updates the min and max virtual scroll bounds */
|
||||
@ -639,24 +651,24 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
|
||||
@Override
|
||||
public void dispatchDraw(Canvas canvas) {
|
||||
synchronizeStackViewsWithModel();
|
||||
clipTaskViews();
|
||||
if (synchronizeStackViewsWithModel()) {
|
||||
clipTaskViews();
|
||||
}
|
||||
super.dispatchDraw(canvas);
|
||||
}
|
||||
|
||||
/** Computes the stack and task rects */
|
||||
public void computeRects(int width, int height, int insetLeft, int insetBottom) {
|
||||
public void computeRects(int windowWidth, int windowHeight, Rect taskStackBounds) {
|
||||
// Compute the rects in the stack algorithm
|
||||
mStackAlgorithm.computeRects(mStack.getTasks(), width, height, insetLeft, insetBottom);
|
||||
mStackAlgorithm.computeRects(mStack.getTasks(), windowWidth, windowHeight, taskStackBounds);
|
||||
|
||||
// Update the scroll bounds
|
||||
updateMinMaxScroll(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called with the size of the space not including the top or right insets, or the
|
||||
* search bar height in portrait (but including the search bar width in landscape, since we want
|
||||
* to draw under it.
|
||||
* This is called with the full window width and height to allow stack view children to
|
||||
* perform the full screen transition down.
|
||||
*/
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
@ -664,25 +676,43 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
int height = MeasureSpec.getSize(heightMeasureSpec);
|
||||
|
||||
// Compute our stack/task rects
|
||||
Rect taskStackBounds = new Rect();
|
||||
mConfig.getTaskStackBounds(width, height, taskStackBounds);
|
||||
computeRects(width, height, taskStackBounds.left, mConfig.systemInsets.bottom);
|
||||
Rect taskStackBounds = new Rect(mTaskStackBounds);
|
||||
taskStackBounds.bottom -= mConfig.systemInsets.bottom;
|
||||
computeRects(width, height, taskStackBounds);
|
||||
|
||||
// If this is the first layout, then scroll to the front of the stack and synchronize the
|
||||
// stack views immediately
|
||||
// stack views immediately to load all the views
|
||||
if (mAwaitingFirstLayout) {
|
||||
setStackScrollToInitialState();
|
||||
requestSynchronizeStackViewsWithModel();
|
||||
synchronizeStackViewsWithModel();
|
||||
|
||||
// Find the first task and mark it as full screen
|
||||
if (mConfig.launchedFromAppWithScreenshot) {
|
||||
int childCount = getChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
TaskView tv = (TaskView) getChildAt(i);
|
||||
if (tv.getTask().isLaunchTarget) {
|
||||
tv.setIsFullScreen(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Measure each of the children
|
||||
// Measure each of the TaskViews
|
||||
int childCount = getChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
TaskView t = (TaskView) getChildAt(i);
|
||||
t.measure(MeasureSpec.makeMeasureSpec(mStackAlgorithm.mTaskRect.width(), MeasureSpec.EXACTLY),
|
||||
TaskView tv = (TaskView) getChildAt(i);
|
||||
if (tv.isFullScreenView()) {
|
||||
tv.measure(widthMeasureSpec, heightMeasureSpec);
|
||||
} else {
|
||||
tv.measure(
|
||||
MeasureSpec.makeMeasureSpec(mStackAlgorithm.mTaskRect.width(),
|
||||
MeasureSpec.EXACTLY),
|
||||
MeasureSpec.makeMeasureSpec(mStackAlgorithm.mTaskRect.height() +
|
||||
t.getMaxFooterHeight(), MeasureSpec.EXACTLY));
|
||||
tv.getMaxFooterHeight(), MeasureSpec.EXACTLY));
|
||||
}
|
||||
}
|
||||
|
||||
setMeasuredDimension(width, height);
|
||||
@ -698,54 +728,45 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
// Layout each of the children
|
||||
int childCount = getChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
TaskView t = (TaskView) getChildAt(i);
|
||||
t.layout(mStackAlgorithm.mTaskRect.left, mStackAlgorithm.mStackRectSansPeek.top,
|
||||
mStackAlgorithm.mTaskRect.right, mStackAlgorithm.mStackRectSansPeek.top +
|
||||
mStackAlgorithm.mTaskRect.height() + t.getMaxFooterHeight());
|
||||
TaskView tv = (TaskView) getChildAt(i);
|
||||
if (tv.isFullScreenView()) {
|
||||
tv.layout(left, top, left + tv.getMeasuredWidth(), top + tv.getMeasuredHeight());
|
||||
} else {
|
||||
tv.layout(mStackAlgorithm.mTaskRect.left, mStackAlgorithm.mTaskRect.top,
|
||||
mStackAlgorithm.mTaskRect.right, mStackAlgorithm.mTaskRect.bottom +
|
||||
tv.getMaxFooterHeight());
|
||||
}
|
||||
}
|
||||
|
||||
if (mAwaitingFirstLayout) {
|
||||
// Mark that we have completely the first layout
|
||||
mAwaitingFirstLayout = false;
|
||||
onFirstLayout();
|
||||
}
|
||||
}
|
||||
|
||||
// Find the target task with the specified id
|
||||
ArrayList<Task> tasks = mStack.getTasks();
|
||||
Task targetTask = null;
|
||||
int targetTaskId = mConfig.launchedToTaskId;
|
||||
if (targetTaskId != -1) {
|
||||
int taskCount = tasks.size();
|
||||
for (int i = 0; i < taskCount; i++) {
|
||||
Task t = tasks.get(i);
|
||||
if (t.key.id == targetTaskId) {
|
||||
targetTask = t;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
/** Handler for the first layout. */
|
||||
void onFirstLayout() {
|
||||
// Prepare the first view for its enter animation
|
||||
int offscreenY = mStackAlgorithm.mViewRect.bottom -
|
||||
(mStackAlgorithm.mTaskRect.top - mStackAlgorithm.mViewRect.top);
|
||||
int childCount = getChildCount();
|
||||
for (int i = childCount - 1; i >= 0; i--) {
|
||||
TaskView tv = (TaskView) getChildAt(i);
|
||||
tv.prepareEnterRecentsAnimation(tv.getTask().isLaunchTarget, offscreenY);
|
||||
}
|
||||
|
||||
// Prepare the first view for its enter animation
|
||||
int offsetTopAlign = -mStackAlgorithm.mTaskRect.top;
|
||||
int offscreenY = mStackAlgorithm.mRect.bottom -
|
||||
(mStackAlgorithm.mTaskRect.top - mStackAlgorithm.mRect.top);
|
||||
for (int i = childCount - 1; i >= 0; i--) {
|
||||
TaskView tv = (TaskView) getChildAt(i);
|
||||
tv.prepareEnterRecentsAnimation(tv.getTask() == targetTask, offsetTopAlign,
|
||||
offscreenY);
|
||||
}
|
||||
// If the enter animation started already and we haven't completed a layout yet, do the
|
||||
// enter animation now
|
||||
if (mStartEnterAnimationRequestedAfterLayout) {
|
||||
startEnterRecentsAnimation(mStartEnterAnimationContext);
|
||||
mStartEnterAnimationRequestedAfterLayout = false;
|
||||
mStartEnterAnimationContext = null;
|
||||
}
|
||||
|
||||
// If the enter animation started already and we haven't completed a layout yet, do the
|
||||
// enter animation now
|
||||
if (mStartEnterAnimationRequestedAfterLayout) {
|
||||
startEnterRecentsAnimation(mStartEnterAnimationContext);
|
||||
mStartEnterAnimationRequestedAfterLayout = false;
|
||||
mStartEnterAnimationContext = null;
|
||||
}
|
||||
|
||||
// Update the focused task index to be the next item to the top task
|
||||
if (mConfig.launchedWithAltTab) {
|
||||
// When alt-tabbing, we focus the next previous task
|
||||
focusTask(Math.max(0, mStack.getTaskCount() - 2), false);
|
||||
}
|
||||
// Update the focused task index to be the next item to the top task
|
||||
if (mConfig.launchedWithAltTab) {
|
||||
// When alt-tabbing, we focus the next previous task
|
||||
focusTask(Math.max(0, mStack.getTaskCount() - 2), false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -759,30 +780,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
}
|
||||
|
||||
if (mStack.getTaskCount() > 0) {
|
||||
// Find the target task with the specified id
|
||||
ArrayList<Task> tasks = mStack.getTasks();
|
||||
Task targetTask = null;
|
||||
int targetTaskId = mConfig.launchedToTaskId;
|
||||
if (targetTaskId != -1) {
|
||||
int taskCount = tasks.size();
|
||||
for (int i = 0; i < taskCount; i++) {
|
||||
Task t = tasks.get(i);
|
||||
if (t.key.id == targetTaskId) {
|
||||
targetTask = t;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find the transform for the target task
|
||||
if (targetTask != null) {
|
||||
ctx.targetTaskTransform = new TaskViewTransform();
|
||||
mStackAlgorithm.getStackTransform(targetTask, getStackScroll(), ctx.targetTaskTransform);
|
||||
Rect taskStackBounds = new Rect();
|
||||
mConfig.getTaskStackBounds(getMeasuredWidth(), getMeasuredHeight(), taskStackBounds);
|
||||
ctx.targetTaskTransform.rect.offset(taskStackBounds.left, taskStackBounds.top);
|
||||
}
|
||||
|
||||
// Animate all the task views into view
|
||||
int childCount = getChildCount();
|
||||
for (int i = childCount - 1; i >= 0; i--) {
|
||||
@ -791,7 +788,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
ctx.currentTaskTransform = new TaskViewTransform();
|
||||
ctx.currentStackViewIndex = i;
|
||||
ctx.currentStackViewCount = childCount;
|
||||
ctx.isCurrentTaskLaunchTarget = (task == targetTask);
|
||||
ctx.currentTaskRect = mStackAlgorithm.mTaskRect;
|
||||
mStackAlgorithm.getStackTransform(task, getStackScroll(), ctx.currentTaskTransform);
|
||||
tv.startEnterRecentsAnimation(ctx);
|
||||
}
|
||||
@ -802,6 +799,11 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
public void run() {
|
||||
// Start dozing
|
||||
mUIDozeTrigger.startDozing();
|
||||
// Request an update of the task views after the animation in to
|
||||
// relayout the fullscreen view back to its normal size
|
||||
if (mConfig.launchedFromAppWithScreenshot) {
|
||||
requestSynchronizeStackViewsWithModel();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -810,8 +812,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
/** Requests this task stacks to start it's exit-recents animation. */
|
||||
public void startExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx) {
|
||||
// Animate all the task views into view
|
||||
ctx.offscreenTranslationY = mStackAlgorithm.mRect.bottom -
|
||||
(mStackAlgorithm.mTaskRect.top - mStackAlgorithm.mRect.top);
|
||||
ctx.offscreenTranslationY = mStackAlgorithm.mViewRect.bottom -
|
||||
(mStackAlgorithm.mTaskRect.top - mStackAlgorithm.mViewRect.top);
|
||||
int childCount = getChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
TaskView tv = (TaskView) getChildAt(i);
|
||||
|
@ -17,7 +17,6 @@
|
||||
package com.android.systemui.recents.views;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import com.android.systemui.recents.Constants;
|
||||
import com.android.systemui.recents.RecentsConfiguration;
|
||||
import com.android.systemui.recents.misc.Utilities;
|
||||
import com.android.systemui.recents.model.Task;
|
||||
@ -30,14 +29,14 @@ public class TaskStackViewLayoutAlgorithm {
|
||||
|
||||
// These are all going to change
|
||||
static final float StackOverlapPct = 0.65f; // The overlap height relative to the task height
|
||||
static final float StackPeekHeightPct = 0.1f; // The height of the peek space relative to the stack height
|
||||
static final float StackPeekHeightPct = 0.075f; // The height of the peek space relative to the stack height
|
||||
static final float StackPeekMinScale = 0.8f; // The min scale of the last card in the peek area
|
||||
static final int StackPeekNumCards = 3; // The number of cards we see in the peek space
|
||||
|
||||
RecentsConfiguration mConfig;
|
||||
|
||||
// The various rects that define the stack view
|
||||
Rect mRect = new Rect();
|
||||
Rect mViewRect = new Rect();
|
||||
Rect mStackRect = new Rect();
|
||||
Rect mStackRectSansPeek = new Rect();
|
||||
Rect mTaskRect = new Rect();
|
||||
@ -53,29 +52,21 @@ public class TaskStackViewLayoutAlgorithm {
|
||||
}
|
||||
|
||||
/** Computes the stack and task rects */
|
||||
public void computeRects(ArrayList<Task> tasks, int width, int height, int insetLeft, int insetBottom) {
|
||||
public void computeRects(ArrayList<Task> tasks, int windowWidth, int windowHeight,
|
||||
Rect taskStackBounds) {
|
||||
// Note: We let the stack view be the full height because we want the cards to go under the
|
||||
// navigation bar if possible. However, the stack rects which we use to calculate
|
||||
// max scroll, etc. need to take the nav bar into account
|
||||
|
||||
// Compute the stack rects
|
||||
mRect.set(0, 0, width, height);
|
||||
mStackRect.set(mRect);
|
||||
mStackRect.left += insetLeft;
|
||||
mStackRect.bottom -= insetBottom;
|
||||
mViewRect.set(0, 0, windowWidth, windowHeight);
|
||||
mStackRect.set(taskStackBounds);
|
||||
|
||||
int widthPadding = (int) (mConfig.taskStackWidthPaddingPct * mStackRect.width());
|
||||
int heightPadding = mConfig.taskStackTopPaddingPx;
|
||||
if (Constants.DebugFlags.App.EnableSearchLayout) {
|
||||
mStackRect.top += heightPadding;
|
||||
mStackRect.left += widthPadding;
|
||||
mStackRect.right -= widthPadding;
|
||||
mStackRect.bottom -= heightPadding;
|
||||
} else {
|
||||
mStackRect.inset(widthPadding, heightPadding);
|
||||
}
|
||||
mStackRect.inset(widthPadding, heightPadding);
|
||||
mStackRectSansPeek.set(mStackRect);
|
||||
mStackRectSansPeek.top += StackPeekHeightPct * mStackRect.height();
|
||||
mStackRectSansPeek.top += StackPeekHeightPct * windowHeight;
|
||||
|
||||
// Compute the task rect
|
||||
int size = mStackRect.width();
|
||||
@ -91,7 +82,7 @@ public class TaskStackViewLayoutAlgorithm {
|
||||
// Compute the min and max scroll values
|
||||
int numTasks = Math.max(1, tasks.size());
|
||||
int taskHeight = mTaskRect.height();
|
||||
int stackHeight = mStackRectSansPeek.height();
|
||||
int stackHeight = mStackRect.height();
|
||||
|
||||
if (numTasks <= 1) {
|
||||
// If there is only one task, then center the task in the stack rect (sans peek)
|
||||
@ -156,7 +147,7 @@ public class TaskStackViewLayoutAlgorithm {
|
||||
} else {
|
||||
transformOut.rect.offset(0, transformOut.translationY);
|
||||
Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
|
||||
transformOut.visible = Rect.intersects(mRect, transformOut.rect);
|
||||
transformOut.visible = Rect.intersects(mViewRect, transformOut.rect);
|
||||
}
|
||||
transformOut.t = t;
|
||||
return transformOut;
|
||||
|
@ -17,7 +17,7 @@
|
||||
package com.android.systemui.recents.views;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Rect;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
@ -27,11 +27,8 @@ import com.android.systemui.recents.model.Task;
|
||||
/** The task thumbnail view */
|
||||
public class TaskThumbnailView extends FixedSizeImageView {
|
||||
|
||||
Task mTask;
|
||||
|
||||
// Task bar clipping
|
||||
Rect mClipRect;
|
||||
boolean mClipTaskBar = true;
|
||||
Rect mClipRect = new Rect();
|
||||
|
||||
public TaskThumbnailView(Context context) {
|
||||
this(context, null);
|
||||
@ -50,40 +47,51 @@ public class TaskThumbnailView extends FixedSizeImageView {
|
||||
setScaleType(ScaleType.FIT_XY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
if (mClipTaskBar && (mClipRect != null)) {
|
||||
int restoreCount = canvas.save(Canvas.CLIP_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
|
||||
canvas.clipRect(mClipRect);
|
||||
super.draw(canvas);
|
||||
canvas.restoreToCount(restoreCount);
|
||||
} else {
|
||||
super.draw(canvas);
|
||||
}
|
||||
/** Updates the clip rect based on the given task bar. */
|
||||
void enableTaskBarClip(View taskBar) {
|
||||
int top = (int) Math.max(0, taskBar.getTranslationY() +
|
||||
taskBar.getMeasuredHeight() - 1);
|
||||
mClipRect.set(0, top, getMeasuredWidth(), getMeasuredHeight());
|
||||
setClipBounds(mClipRect);
|
||||
}
|
||||
|
||||
/** Updates the clip rect based on the given task bar. */
|
||||
void updateTaskBarClip(View taskBar) {
|
||||
// If mClipTaskBar is unset first, then we don't bother setting mTaskBar
|
||||
if (mClipTaskBar) {
|
||||
int top = (int) Math.max(0, taskBar.getTranslationY() +
|
||||
taskBar.getMeasuredHeight() - 1);
|
||||
mClipRect = new Rect(0, top, getMeasuredWidth(), getMeasuredHeight());
|
||||
invalidate(0, 0, taskBar.getMeasuredWidth(), taskBar.getMeasuredHeight() + 1);
|
||||
}
|
||||
/** Convenience method to enable task bar clipping as a runnable. */
|
||||
Runnable enableTaskBarClipAsRunnable(final View taskBar) {
|
||||
return new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
enableTaskBarClip(taskBar);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/** Disables the task bar clipping. */
|
||||
void disableClipTaskBarView() {
|
||||
mClipTaskBar = false;
|
||||
if (mClipRect != null) {
|
||||
invalidate(0, 0, mClipRect.width(), mClipRect.top);
|
||||
Runnable disableTaskBarClipAsRunnable() {
|
||||
return new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mClipRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight());
|
||||
setClipBounds(mClipRect);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/** Binds the thumbnail view to the screenshot. */
|
||||
boolean bindToScreenshot(Bitmap ss) {
|
||||
if (ss != null) {
|
||||
setImageBitmap(ss);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Unbinds the thumbnail view from the screenshot. */
|
||||
void unbindFromScreenshot() {
|
||||
setImageBitmap(null);
|
||||
}
|
||||
|
||||
/** Binds the thumbnail view to the task */
|
||||
void rebindToTask(Task t) {
|
||||
mTask = t;
|
||||
if (t.thumbnail != null) {
|
||||
setImageBitmap(t.thumbnail);
|
||||
}
|
||||
@ -91,7 +99,6 @@ public class TaskThumbnailView extends FixedSizeImageView {
|
||||
|
||||
/** Unbinds the thumbnail view from the task */
|
||||
void unbindFromTask() {
|
||||
mTask = null;
|
||||
setImageDrawable(null);
|
||||
}
|
||||
}
|
||||
|
@ -22,24 +22,23 @@ import android.animation.ObjectAnimator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Outline;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.ViewOutlineProvider;
|
||||
import android.view.ViewPropertyAnimator;
|
||||
import android.view.animation.AccelerateInterpolator;
|
||||
import android.widget.FrameLayout;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.recents.AlternateRecentsComponent;
|
||||
import com.android.systemui.recents.Constants;
|
||||
import com.android.systemui.recents.RecentsConfiguration;
|
||||
import com.android.systemui.recents.model.Task;
|
||||
|
||||
// XXX: In debug mode, we should override invalidate() and check the layout type (do this in TaskStackView as well)
|
||||
|
||||
/* A task view */
|
||||
public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.OnClickListener,
|
||||
View.OnLongClickListener {
|
||||
public class TaskView extends FrameLayout implements Task.TaskCallbacks,
|
||||
TaskFooterView.TaskFooterViewCallbacks, View.OnClickListener, View.OnLongClickListener {
|
||||
/** The TaskView callbacks */
|
||||
interface TaskViewCallbacks {
|
||||
public void onTaskViewAppIconClicked(TaskView tv);
|
||||
@ -51,10 +50,6 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
|
||||
|
||||
RecentsConfiguration mConfig;
|
||||
|
||||
int mFooterHeight;
|
||||
int mMaxFooterHeight;
|
||||
ObjectAnimator mFooterAnimator;
|
||||
|
||||
int mDim;
|
||||
int mMaxDim;
|
||||
AccelerateInterpolator mDimInterpolator = new AccelerateInterpolator();
|
||||
@ -62,14 +57,15 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
|
||||
Task mTask;
|
||||
boolean mTaskDataLoaded;
|
||||
boolean mIsFocused;
|
||||
boolean mIsFullScreenView;
|
||||
boolean mIsStub;
|
||||
boolean mClipViewInStack;
|
||||
int mClipFromBottom;
|
||||
AnimateableViewBounds mViewBounds;
|
||||
Paint mLayerPaint = new Paint();
|
||||
|
||||
TaskThumbnailView mThumbnailView;
|
||||
TaskBarView mBarView;
|
||||
View mLockToAppButtonView;
|
||||
TaskFooterView mFooterView;
|
||||
TaskViewCallbacks mCb;
|
||||
|
||||
// Optimizations
|
||||
@ -80,18 +76,6 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
|
||||
updateDimOverlayFromScale();
|
||||
}
|
||||
};
|
||||
Runnable mEnableThumbnailClip = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mThumbnailView.updateTaskBarClip(mBarView);
|
||||
}
|
||||
};
|
||||
Runnable mDisableThumbnailClip = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mThumbnailView.disableClipTaskBarView();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public TaskView(Context context) {
|
||||
@ -109,55 +93,12 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
|
||||
public TaskView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
mConfig = RecentsConfiguration.getInstance();
|
||||
mMaxFooterHeight = mConfig.taskViewLockToAppButtonHeight;
|
||||
setWillNotDraw(false);
|
||||
setClipToOutline(true);
|
||||
setDim(getDim());
|
||||
setFooterHeight(getFooterHeight());
|
||||
setOutlineProvider(new ViewOutlineProvider() {
|
||||
@Override
|
||||
public void getOutline(View view, Outline outline) {
|
||||
// The current height is measured with the footer, so account for the footer height
|
||||
// and the current clip (in the stack)
|
||||
int height = getMeasuredHeight() - mClipFromBottom - mMaxFooterHeight + mFooterHeight;
|
||||
outline.setRoundRect(0, 0, getWidth(), height,
|
||||
mConfig.taskViewRoundedCornerRadiusPx);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
mMaxDim = mConfig.taskStackMaxDim;
|
||||
|
||||
// By default, all views are clipped to other views in their stack
|
||||
mClipViewInStack = true;
|
||||
|
||||
// Bind the views
|
||||
mBarView = (TaskBarView) findViewById(R.id.task_view_bar);
|
||||
mThumbnailView = (TaskThumbnailView) findViewById(R.id.task_view_thumbnail);
|
||||
mLockToAppButtonView = findViewById(R.id.lock_to_app);
|
||||
|
||||
if (mTaskDataLoaded) {
|
||||
onTaskDataLoaded();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
int width = MeasureSpec.getSize(widthMeasureSpec);
|
||||
int height = MeasureSpec.getSize(heightMeasureSpec);
|
||||
|
||||
// Measure the bar view, thumbnail, and lock-to-app buttons
|
||||
mBarView.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
|
||||
MeasureSpec.makeMeasureSpec(mConfig.taskBarHeight, MeasureSpec.EXACTLY));
|
||||
mLockToAppButtonView.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
|
||||
MeasureSpec.makeMeasureSpec(mConfig.taskViewLockToAppButtonHeight,
|
||||
MeasureSpec.EXACTLY));
|
||||
// Measure the thumbnail height to be the same as the width
|
||||
mThumbnailView.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
|
||||
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY));
|
||||
setMeasuredDimension(width, height);
|
||||
mViewBounds = new AnimateableViewBounds(this, mConfig.taskViewRoundedCornerRadiusPx);
|
||||
setWillNotDraw(false);
|
||||
setDim(getDim());
|
||||
setOutlineProvider(mViewBounds);
|
||||
}
|
||||
|
||||
/** Set callback */
|
||||
@ -170,74 +111,67 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
|
||||
return mTask;
|
||||
}
|
||||
|
||||
/** Returns the view bounds. */
|
||||
AnimateableViewBounds getViewBounds() {
|
||||
return mViewBounds;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
// Bind the views
|
||||
mBarView = (TaskBarView) findViewById(R.id.task_view_bar);
|
||||
mThumbnailView = (TaskThumbnailView) findViewById(R.id.task_view_thumbnail);
|
||||
mFooterView = (TaskFooterView) findViewById(R.id.lock_to_app);
|
||||
mFooterView.setCallbacks(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
int width = MeasureSpec.getSize(widthMeasureSpec);
|
||||
int height = MeasureSpec.getSize(heightMeasureSpec);
|
||||
|
||||
// Measure the bar view, thumbnail, and footer
|
||||
mBarView.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
|
||||
MeasureSpec.makeMeasureSpec(mConfig.taskBarHeight, MeasureSpec.EXACTLY));
|
||||
mFooterView.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
|
||||
MeasureSpec.makeMeasureSpec(mConfig.taskViewLockToAppButtonHeight,
|
||||
MeasureSpec.EXACTLY));
|
||||
if (mIsFullScreenView) {
|
||||
// Measure the thumbnail height to be the full dimensions
|
||||
mThumbnailView.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
|
||||
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
|
||||
} else {
|
||||
// Measure the thumbnail to be square
|
||||
mThumbnailView.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
|
||||
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY));
|
||||
}
|
||||
setMeasuredDimension(width, height);
|
||||
}
|
||||
|
||||
/** Synchronizes this view's properties with the task's transform */
|
||||
void updateViewPropertiesToTaskTransform(TaskViewTransform toTransform, int duration) {
|
||||
// Update the bar view
|
||||
mBarView.updateViewPropertiesToTaskTransform(toTransform, duration);
|
||||
|
||||
// Check to see if any properties have changed, and update the task view
|
||||
if (duration > 0) {
|
||||
ViewPropertyAnimator anim = animate();
|
||||
boolean useLayers = false;
|
||||
|
||||
// Animate to the final state
|
||||
if (toTransform.hasTranslationYChangedFrom(getTranslationY())) {
|
||||
anim.translationY(toTransform.translationY);
|
||||
}
|
||||
if (Constants.DebugFlags.App.EnableShadows &&
|
||||
toTransform.hasTranslationZChangedFrom(getTranslationZ())) {
|
||||
anim.translationZ(toTransform.translationZ);
|
||||
}
|
||||
if (toTransform.hasScaleChangedFrom(getScaleX())) {
|
||||
anim.scaleX(toTransform.scale)
|
||||
.scaleY(toTransform.scale)
|
||||
.setUpdateListener(mUpdateDimListener);
|
||||
useLayers = true;
|
||||
}
|
||||
if (toTransform.hasAlphaChangedFrom(getAlpha())) {
|
||||
// Use layers if we animate alpha
|
||||
anim.alpha(toTransform.alpha);
|
||||
useLayers = true;
|
||||
}
|
||||
if (useLayers) {
|
||||
anim.withLayer();
|
||||
}
|
||||
anim.setStartDelay(toTransform.startDelay)
|
||||
.setDuration(duration)
|
||||
.setInterpolator(mConfig.fastOutSlowInInterpolator)
|
||||
.start();
|
||||
} else {
|
||||
// Set the changed properties
|
||||
if (toTransform.hasTranslationYChangedFrom(getTranslationY())) {
|
||||
setTranslationY(toTransform.translationY);
|
||||
}
|
||||
// If we are a full screen view, then only update the Z to keep it in order
|
||||
// XXX: Also update/animate the dim as well
|
||||
if (mIsFullScreenView) {
|
||||
if (Constants.DebugFlags.App.EnableShadows &&
|
||||
toTransform.hasTranslationZChangedFrom(getTranslationZ())) {
|
||||
setTranslationZ(toTransform.translationZ);
|
||||
}
|
||||
if (toTransform.hasScaleChangedFrom(getScaleX())) {
|
||||
setScaleX(toTransform.scale);
|
||||
setScaleY(toTransform.scale);
|
||||
updateDimOverlayFromScale();
|
||||
}
|
||||
if (toTransform.hasAlphaChangedFrom(getAlpha())) {
|
||||
setAlpha(toTransform.alpha);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Apply the transform
|
||||
toTransform.applyToTaskView(this, duration, mConfig.fastOutSlowInInterpolator,
|
||||
mUpdateDimListener);
|
||||
}
|
||||
|
||||
/** Resets this view's properties */
|
||||
void resetViewProperties() {
|
||||
setTranslationX(0f);
|
||||
setTranslationY(0f);
|
||||
if (Constants.DebugFlags.App.EnableShadows) {
|
||||
setTranslationZ(0f);
|
||||
}
|
||||
setScaleX(1f);
|
||||
setScaleY(1f);
|
||||
setAlpha(1f);
|
||||
setDim(0);
|
||||
invalidate();
|
||||
TaskViewTransform.reset(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -262,20 +196,13 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
|
||||
|
||||
/** Prepares this task view for the enter-recents animations. This is called earlier in the
|
||||
* first layout because the actual animation into recents may take a long time. */
|
||||
public void prepareEnterRecentsAnimation(boolean isTaskViewLaunchTargetTask, int offsetY,
|
||||
int offscreenY) {
|
||||
public void prepareEnterRecentsAnimation(boolean isTaskViewLaunchTargetTask, int offscreenY) {
|
||||
if (mConfig.launchedFromAppWithScreenshot) {
|
||||
if (isTaskViewLaunchTargetTask) {
|
||||
// Hide the task view as we are going to animate the full screenshot into view
|
||||
// and then replace it with this view once we are done
|
||||
setVisibility(View.INVISIBLE);
|
||||
// Also hide the front most task bar view so we can animate it in
|
||||
mBarView.prepareEnterRecentsAnimation();
|
||||
} else {
|
||||
// Top align the task views
|
||||
setTranslationY(offsetY);
|
||||
setScaleX(1f);
|
||||
setScaleY(1f);
|
||||
// Don't do anything for the side views
|
||||
}
|
||||
|
||||
} else if (mConfig.launchedFromAppWithThumbnail) {
|
||||
@ -300,49 +227,57 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
|
||||
/** Animates this task view as it enters recents */
|
||||
public void startEnterRecentsAnimation(final ViewAnimation.TaskViewEnterContext ctx) {
|
||||
TaskViewTransform transform = ctx.currentTaskTransform;
|
||||
Rect taskRect = ctx.currentTaskRect;
|
||||
|
||||
if (mConfig.launchedFromAppWithScreenshot) {
|
||||
if (ctx.isCurrentTaskLaunchTarget) {
|
||||
// Animate the full screenshot down first, before swapping with this task view
|
||||
ctx.fullScreenshotView.animateOnEnterRecents(ctx, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Animate the task bar of the first task view
|
||||
mBarView.startEnterRecentsAnimation(0, mEnableThumbnailClip);
|
||||
setVisibility(View.VISIBLE);
|
||||
// Animate the footer into view
|
||||
animateFooterVisibility(true, mConfig.taskBarEnterAnimDuration);
|
||||
// Decrement the post animation trigger
|
||||
ctx.postAnimationTrigger.decrement();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Animate the tasks down behind the full screenshot
|
||||
animate()
|
||||
.scaleX(transform.scale)
|
||||
.scaleY(transform.scale)
|
||||
.translationY(transform.translationY)
|
||||
.setStartDelay(0)
|
||||
.setUpdateListener(null)
|
||||
.setInterpolator(mConfig.linearOutSlowInInterpolator)
|
||||
.setDuration(475)
|
||||
.withLayer()
|
||||
if (mTask.isLaunchTarget) {
|
||||
// XXX: We would have to animate the trasnlationY of the task view bar along with the clip and
|
||||
// reset it at the bottom
|
||||
|
||||
// XXX: This should actually be the inset on the current app...
|
||||
mViewBounds.animateClipTop(taskRect.top, mConfig.taskViewEnterFromHomeDuration * 5);
|
||||
mViewBounds.animateClipBottom(getMeasuredHeight() - taskRect.bottom, mConfig.taskViewEnterFromHomeDuration * 5);
|
||||
|
||||
animate().scaleX(((float) taskRect.width() / getMeasuredWidth()) * transform.scale)
|
||||
.scaleY(((float) taskRect.width() / getMeasuredWidth()) * transform.scale)
|
||||
.translationY(taskRect.top + transform.translationY)
|
||||
.setDuration(mConfig.taskViewEnterFromHomeDuration * 5)
|
||||
.withEndAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mEnableThumbnailClip.run();
|
||||
// Animate the task bar of the first task view
|
||||
mBarView.startEnterRecentsAnimation(0, mThumbnailView.enableTaskBarClipAsRunnable(mBarView));
|
||||
// Animate the footer into view (if it is the front most task)
|
||||
animateFooterVisibility(true, mConfig.taskBarEnterAnimDuration);
|
||||
// Decrement the post animation trigger
|
||||
ctx.postAnimationTrigger.decrement();
|
||||
|
||||
// XXX Request layout and only start hte next animation after the next
|
||||
// layout
|
||||
|
||||
setIsFullScreen(false);
|
||||
mThumbnailView.unbindFromScreenshot();
|
||||
|
||||
// Recycle the full screen screenshot
|
||||
AlternateRecentsComponent.consumeLastScreenshot();
|
||||
}
|
||||
})
|
||||
.withLayer()
|
||||
.start();
|
||||
} else {
|
||||
// Otherwise, just enable the thumbnail clip
|
||||
mThumbnailView.enableTaskBarClip(mBarView);
|
||||
|
||||
// Animate the footer into view
|
||||
animateFooterVisibility(true, 0);
|
||||
}
|
||||
ctx.postAnimationTrigger.increment();
|
||||
|
||||
} else if (mConfig.launchedFromAppWithThumbnail) {
|
||||
if (ctx.isCurrentTaskLaunchTarget) {
|
||||
if (mTask.isLaunchTarget) {
|
||||
// Animate the task bar of the first task view
|
||||
mBarView.startEnterRecentsAnimation(mConfig.taskBarEnterAnimDelay, mEnableThumbnailClip);
|
||||
mBarView.startEnterRecentsAnimation(mConfig.taskBarEnterAnimDelay,
|
||||
mThumbnailView.enableTaskBarClipAsRunnable(mBarView));
|
||||
|
||||
// Animate the dim into view as well
|
||||
ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", getDimOverlayFromScale());
|
||||
@ -360,10 +295,9 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
|
||||
ctx.postAnimationTrigger.increment();
|
||||
|
||||
// Animate the footer into view
|
||||
animateFooterVisibility(true, mConfig.taskBarEnterAnimDuration
|
||||
);
|
||||
animateFooterVisibility(true, mConfig.taskBarEnterAnimDuration);
|
||||
} else {
|
||||
mEnableThumbnailClip.run();
|
||||
mThumbnailView.enableTaskBarClip(mBarView);
|
||||
}
|
||||
|
||||
} else if (mConfig.launchedFromHome) {
|
||||
@ -386,7 +320,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
|
||||
.withEndAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mEnableThumbnailClip.run();
|
||||
mThumbnailView.enableTaskBarClip(mBarView);
|
||||
// Decrement the post animation trigger
|
||||
ctx.postAnimationTrigger.decrement();
|
||||
}
|
||||
@ -395,11 +329,11 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
|
||||
ctx.postAnimationTrigger.increment();
|
||||
|
||||
// Animate the footer into view
|
||||
animateFooterVisibility(true, mConfig.taskViewEnterFromHomeDuration
|
||||
);
|
||||
animateFooterVisibility(true, mConfig.taskViewEnterFromHomeDuration);
|
||||
|
||||
} else {
|
||||
// Otherwise, just enable the thumbnail clip
|
||||
mEnableThumbnailClip.run();
|
||||
mThumbnailView.enableTaskBarClip(mBarView);
|
||||
|
||||
// Animate the footer into view
|
||||
animateFooterVisibility(true, 0);
|
||||
@ -424,7 +358,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
|
||||
public void startLaunchTaskAnimation(final Runnable r, boolean isLaunchingTask) {
|
||||
if (isLaunchingTask) {
|
||||
// Disable the thumbnail clip and animate the bar out
|
||||
mBarView.startLaunchTaskAnimation(mDisableThumbnailClip, r);
|
||||
mBarView.startLaunchTaskAnimation(mThumbnailView.disableTaskBarClipAsRunnable(), r);
|
||||
|
||||
// Animate the dim
|
||||
if (mDim > 0) {
|
||||
@ -478,32 +412,23 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
|
||||
mBarView.setNoUserInteractionState();
|
||||
}
|
||||
|
||||
/** Enable the hw layers on this task view */
|
||||
void enableHwLayers() {
|
||||
mThumbnailView.setLayerType(View.LAYER_TYPE_HARDWARE, mLayerPaint);
|
||||
mBarView.enableHwLayers();
|
||||
mLockToAppButtonView.setLayerType(View.LAYER_TYPE_HARDWARE, mLayerPaint);
|
||||
/** Sets whether this task view is full screen or not. */
|
||||
void setIsFullScreen(boolean isFullscreen) {
|
||||
mIsFullScreenView = isFullscreen;
|
||||
mBarView.setIsFullscreen(isFullscreen);
|
||||
if (isFullscreen) {
|
||||
// If we are full screen, then disable the bottom outline clip for the footer
|
||||
mViewBounds.setOutlineClipBottom(0);
|
||||
}
|
||||
}
|
||||
|
||||
/** Disable the hw layers on this task view */
|
||||
void disableHwLayers() {
|
||||
mThumbnailView.setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
|
||||
mBarView.disableHwLayers();
|
||||
mLockToAppButtonView.setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
|
||||
/** Returns whether this task view should currently be drawn as a full screen view. */
|
||||
boolean isFullScreenView() {
|
||||
return mIsFullScreenView;
|
||||
}
|
||||
|
||||
/** Sets the stubbed state of this task view. */
|
||||
void setStubState(boolean isStub) {
|
||||
if (!mIsStub && isStub) {
|
||||
// This is now a stub task view, so clip to the bar height, hide the thumbnail
|
||||
setClipBounds(new Rect(0, 0, getMeasuredWidth(), mBarView.getMeasuredHeight()));
|
||||
mThumbnailView.setVisibility(View.INVISIBLE);
|
||||
// Temporary
|
||||
mBarView.mActivityDescription.setText("Stub");
|
||||
} else if (mIsStub && !isStub) {
|
||||
setClipBounds(null);
|
||||
mThumbnailView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
mIsStub = isStub;
|
||||
}
|
||||
|
||||
@ -512,7 +437,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
|
||||
* view.
|
||||
*/
|
||||
boolean shouldClipViewInStack() {
|
||||
return mClipViewInStack && (getVisibility() == View.VISIBLE);
|
||||
return mClipViewInStack && !mIsFullScreenView && (getVisibility() == View.VISIBLE);
|
||||
}
|
||||
|
||||
/** Sets whether this view should be clipped, or clipped against. */
|
||||
@ -523,75 +448,19 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
|
||||
}
|
||||
}
|
||||
|
||||
void setClipFromBottom(int clipFromBottom) {
|
||||
clipFromBottom = Math.max(0, Math.min(getMeasuredHeight(), clipFromBottom));
|
||||
if (mClipFromBottom != clipFromBottom) {
|
||||
mClipFromBottom = clipFromBottom;
|
||||
invalidateOutline();
|
||||
}
|
||||
}
|
||||
|
||||
/** Sets the footer height. */
|
||||
public void setFooterHeight(int footerHeight) {
|
||||
if (footerHeight != mFooterHeight) {
|
||||
mFooterHeight = footerHeight;
|
||||
invalidateOutline();
|
||||
invalidate(0, getMeasuredHeight() - mMaxFooterHeight, getMeasuredWidth(),
|
||||
getMeasuredHeight());
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the footer height. */
|
||||
public int getFooterHeight() {
|
||||
return mFooterHeight;
|
||||
}
|
||||
|
||||
/** Gets the max footer height. */
|
||||
public int getMaxFooterHeight() {
|
||||
return mMaxFooterHeight;
|
||||
return mFooterView.mMaxFooterHeight;
|
||||
}
|
||||
|
||||
/** Animates the footer into and out of view. */
|
||||
public void animateFooterVisibility(boolean visible, int duration) {
|
||||
if (!mTask.lockToThisTask) {
|
||||
if (mLockToAppButtonView.getVisibility() == View.VISIBLE) {
|
||||
mLockToAppButtonView.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (mMaxFooterHeight <= 0) return;
|
||||
|
||||
if (mFooterAnimator != null) {
|
||||
mFooterAnimator.removeAllListeners();
|
||||
mFooterAnimator.cancel();
|
||||
}
|
||||
int height = visible ? mMaxFooterHeight : 0;
|
||||
if (visible && mLockToAppButtonView.getVisibility() != View.VISIBLE) {
|
||||
if (duration > 0) {
|
||||
setFooterHeight(0);
|
||||
} else {
|
||||
setFooterHeight(mMaxFooterHeight);
|
||||
}
|
||||
mLockToAppButtonView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
if (duration > 0) {
|
||||
mFooterAnimator = ObjectAnimator.ofInt(this, "footerHeight", height);
|
||||
mFooterAnimator.setDuration(duration);
|
||||
mFooterAnimator.setInterpolator(mConfig.fastOutSlowInInterpolator);
|
||||
if (!visible) {
|
||||
mFooterAnimator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
mLockToAppButtonView.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
});
|
||||
}
|
||||
mFooterAnimator.start();
|
||||
} else {
|
||||
if (!visible) {
|
||||
mLockToAppButtonView.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
}
|
||||
void animateFooterVisibility(boolean visible, int duration) {
|
||||
// Hide the footer if we are a full screen view
|
||||
if (mIsFullScreenView) return;
|
||||
// Hide the footer if the current task can not be locked to
|
||||
if (!mTask.lockToTaskEnabled || !mTask.lockToThisTask) return;
|
||||
// Otherwise, animate the visibility
|
||||
mFooterView.animateFooterVisibility(visible, duration);
|
||||
}
|
||||
|
||||
/** Returns the current dim. */
|
||||
@ -619,6 +488,8 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
|
||||
setDim(getDimOverlayFromScale());
|
||||
}
|
||||
|
||||
/**** View drawing ****/
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
super.draw(canvas);
|
||||
@ -631,13 +502,29 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
|
||||
|
||||
@Override
|
||||
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
|
||||
if (mIsStub && (child == mThumbnailView)) {
|
||||
if (mIsStub && (child != mBarView)) {
|
||||
// Skip the thumbnail view if we are in stub mode
|
||||
return false;
|
||||
}
|
||||
return super.drawChild(canvas, child, drawingTime);
|
||||
}
|
||||
|
||||
/** Enable the hw layers on this task view */
|
||||
void enableHwLayers() {
|
||||
mThumbnailView.setLayerType(View.LAYER_TYPE_HARDWARE, mLayerPaint);
|
||||
mBarView.enableHwLayers();
|
||||
mFooterView.setLayerType(View.LAYER_TYPE_HARDWARE, mLayerPaint);
|
||||
}
|
||||
|
||||
/** Disable the hw layers on this task view */
|
||||
void disableHwLayers() {
|
||||
mThumbnailView.setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
|
||||
mBarView.disableHwLayers();
|
||||
mFooterView.setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
|
||||
}
|
||||
|
||||
/**** View focus state ****/
|
||||
|
||||
/**
|
||||
* Sets the focused task explicitly. We need a separate flag because requestFocus() won't happen
|
||||
* if the view is not currently visible, or we are in touch state (where we still want to keep
|
||||
@ -691,14 +578,18 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
|
||||
public void onTaskDataLoaded() {
|
||||
if (mThumbnailView != null && mBarView != null) {
|
||||
// Bind each of the views to the new task data
|
||||
mThumbnailView.rebindToTask(mTask);
|
||||
if (mIsFullScreenView) {
|
||||
mThumbnailView.bindToScreenshot(AlternateRecentsComponent.getLastScreenshot());
|
||||
} else {
|
||||
mThumbnailView.rebindToTask(mTask);
|
||||
}
|
||||
mBarView.rebindToTask(mTask);
|
||||
// Rebind any listeners
|
||||
if (Constants.DebugFlags.App.EnableTaskFiltering) {
|
||||
mBarView.mApplicationIcon.setOnClickListener(this);
|
||||
}
|
||||
mBarView.mDismissButton.setOnClickListener(this);
|
||||
mLockToAppButtonView.setOnClickListener(this);
|
||||
mFooterView.setOnClickListener(this);
|
||||
if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
|
||||
if (mConfig.developerOptionsEnabled) {
|
||||
mBarView.mApplicationIcon.setOnLongClickListener(this);
|
||||
@ -720,7 +611,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
|
||||
mBarView.mApplicationIcon.setOnClickListener(null);
|
||||
}
|
||||
mBarView.mDismissButton.setOnClickListener(null);
|
||||
mLockToAppButtonView.setOnClickListener(null);
|
||||
mFooterView.setOnClickListener(null);
|
||||
if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
|
||||
mBarView.mApplicationIcon.setOnLongClickListener(null);
|
||||
}
|
||||
@ -733,6 +624,21 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
|
||||
setOnClickListener(enabled ? this : null);
|
||||
}
|
||||
|
||||
/**** TaskFooterView.TaskFooterViewCallbacks ****/
|
||||
|
||||
@Override
|
||||
public void onTaskFooterHeightChanged(int height, int maxHeight) {
|
||||
if (mIsFullScreenView) {
|
||||
// Disable the bottom outline clip when fullscreen
|
||||
mViewBounds.setOutlineClipBottom(0);
|
||||
} else {
|
||||
// Update the bottom clip in our outline provider
|
||||
mViewBounds.setOutlineClipBottom(maxHeight - height);
|
||||
}
|
||||
}
|
||||
|
||||
/**** View.OnClickListener Implementation ****/
|
||||
|
||||
@Override
|
||||
public void onClick(final View v) {
|
||||
// We purposely post the handler delayed to allow for the touch feedback to draw
|
||||
@ -752,13 +658,15 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
|
||||
});
|
||||
// Hide the footer
|
||||
tv.animateFooterVisibility(false, mConfig.taskViewRemoveAnimDuration);
|
||||
} else if (v == tv || v == mLockToAppButtonView) {
|
||||
mCb.onTaskViewClicked(tv, tv.getTask(), (v == mLockToAppButtonView));
|
||||
} else if (v == tv || v == mFooterView) {
|
||||
mCb.onTaskViewClicked(tv, tv.getTask(), (v == mFooterView));
|
||||
}
|
||||
}
|
||||
}, 125);
|
||||
}
|
||||
|
||||
/**** View.OnLongClickListener Implementation ****/
|
||||
|
||||
@Override
|
||||
public boolean onLongClick(View v) {
|
||||
if (v == mBarView.mApplicationIcon) {
|
||||
|
@ -16,7 +16,12 @@
|
||||
|
||||
package com.android.systemui.recents.views;
|
||||
|
||||
import android.animation.ValueAnimator;
|
||||
import android.graphics.Rect;
|
||||
import android.view.View;
|
||||
import android.view.ViewPropertyAnimator;
|
||||
import android.view.animation.Interpolator;
|
||||
import com.android.systemui.recents.Constants;
|
||||
|
||||
|
||||
/* The transform state for a task view */
|
||||
@ -77,6 +82,73 @@ public class TaskViewTransform {
|
||||
return (Float.compare(translationZ, v) != 0);
|
||||
}
|
||||
|
||||
/** Applies this transform to a view. */
|
||||
public void applyToTaskView(View v, int duration, Interpolator interp,
|
||||
ValueAnimator.AnimatorUpdateListener scaleUpdateListener) {
|
||||
// Check to see if any properties have changed, and update the task view
|
||||
if (duration > 0) {
|
||||
ViewPropertyAnimator anim = v.animate();
|
||||
boolean useLayers = false;
|
||||
|
||||
// Animate to the final state
|
||||
if (hasTranslationYChangedFrom(v.getTranslationY())) {
|
||||
anim.translationY(translationY);
|
||||
}
|
||||
if (Constants.DebugFlags.App.EnableShadows &&
|
||||
hasTranslationZChangedFrom(v.getTranslationZ())) {
|
||||
anim.translationZ(translationZ);
|
||||
}
|
||||
if (hasScaleChangedFrom(v.getScaleX())) {
|
||||
anim.scaleX(scale)
|
||||
.scaleY(scale)
|
||||
.setUpdateListener(scaleUpdateListener);
|
||||
useLayers = true;
|
||||
}
|
||||
if (hasAlphaChangedFrom(v.getAlpha())) {
|
||||
// Use layers if we animate alpha
|
||||
anim.alpha(alpha);
|
||||
useLayers = true;
|
||||
}
|
||||
if (useLayers) {
|
||||
anim.withLayer();
|
||||
}
|
||||
anim.setStartDelay(startDelay)
|
||||
.setDuration(duration)
|
||||
.setInterpolator(interp)
|
||||
.start();
|
||||
} else {
|
||||
// Set the changed properties
|
||||
if (hasTranslationYChangedFrom(v.getTranslationY())) {
|
||||
v.setTranslationY(translationY);
|
||||
}
|
||||
if (Constants.DebugFlags.App.EnableShadows &&
|
||||
hasTranslationZChangedFrom(v.getTranslationZ())) {
|
||||
v.setTranslationZ(translationZ);
|
||||
}
|
||||
if (hasScaleChangedFrom(v.getScaleX())) {
|
||||
v.setScaleX(scale);
|
||||
v.setScaleY(scale);
|
||||
scaleUpdateListener.onAnimationUpdate(null);
|
||||
}
|
||||
if (hasAlphaChangedFrom(v.getAlpha())) {
|
||||
v.setAlpha(alpha);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Reset the transform on a view. */
|
||||
public static void reset(View v) {
|
||||
v.setTranslationX(0f);
|
||||
v.setTranslationY(0f);
|
||||
if (Constants.DebugFlags.App.EnableShadows) {
|
||||
v.setTranslationZ(0f);
|
||||
}
|
||||
v.setScaleX(1f);
|
||||
v.setScaleY(1f);
|
||||
v.setAlpha(1f);
|
||||
v.invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TaskViewTransform delay: " + startDelay + " y: " + translationY + " z: " + translationZ +
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.systemui.recents.views;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import com.android.systemui.recents.misc.ReferenceCountedTrigger;
|
||||
|
||||
/* Common code related to view animations */
|
||||
@ -23,27 +24,22 @@ public class ViewAnimation {
|
||||
|
||||
/* The animation context for a task view animation into Recents */
|
||||
public static class TaskViewEnterContext {
|
||||
// The full screenshot view that we are animating down
|
||||
FullscreenTransitionOverlayView fullScreenshotView;
|
||||
// The transform of the target task view that we are animating into
|
||||
TaskViewTransform targetTaskTransform;
|
||||
// A trigger to run some logic when all the animations complete. This works around the fact
|
||||
// that it is difficult to coordinate ViewPropertyAnimators
|
||||
ReferenceCountedTrigger postAnimationTrigger;
|
||||
|
||||
// These following properties are updated for each task view we start the enter animation on
|
||||
|
||||
// The task rect for the current stack
|
||||
Rect currentTaskRect;
|
||||
// The transform of the current task view
|
||||
TaskViewTransform currentTaskTransform;
|
||||
// Whether this is the front most task view
|
||||
boolean isCurrentTaskLaunchTarget;
|
||||
// The view index of the current task view
|
||||
int currentStackViewIndex;
|
||||
// The total number of task views
|
||||
int currentStackViewCount;
|
||||
|
||||
public TaskViewEnterContext(FullscreenTransitionOverlayView fss, ReferenceCountedTrigger t) {
|
||||
fullScreenshotView = fss;
|
||||
public TaskViewEnterContext(ReferenceCountedTrigger t) {
|
||||
postAnimationTrigger = t;
|
||||
}
|
||||
}
|
||||
@ -53,6 +49,7 @@ public class ViewAnimation {
|
||||
// A trigger to run some logic when all the animations complete. This works around the fact
|
||||
// that it is difficult to coordinate ViewPropertyAnimators
|
||||
ReferenceCountedTrigger postAnimationTrigger;
|
||||
|
||||
// The translationY to apply to a TaskView to move it off the bottom of the task stack
|
||||
int offscreenTranslationY;
|
||||
|
||||
|
Reference in New Issue
Block a user