Initial changes to support lock-to-app on the foremost task. (Bug 16221876)
diff --git a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
index e375433..a9a606f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
@@ -68,6 +68,7 @@
// Recents service binding
Handler mHandler;
boolean mBootCompleted = false;
+ boolean mStartAnimationTriggered = false;
// Task launching
RecentsConfiguration mConfig;
@@ -252,6 +253,7 @@
* Creates the activity options for a unknown state->recents transition.
*/
ActivityOptions getUnknownTransitionActivityOptions() {
+ mStartAnimationTriggered = false;
return ActivityOptions.makeCustomAnimation(mContext,
R.anim.recents_from_unknown_enter,
R.anim.recents_from_unknown_exit, mHandler, this);
@@ -261,6 +263,7 @@
* Creates the activity options for a home->recents transition.
*/
ActivityOptions getHomeTransitionActivityOptions() {
+ mStartAnimationTriggered = false;
return ActivityOptions.makeCustomAnimation(mContext,
R.anim.recents_from_launcher_enter,
R.anim.recents_from_launcher_exit, mHandler, this);
@@ -279,6 +282,7 @@
// Take the full screenshot
sLastScreenshot = mSystemServicesProxy.takeAppScreenshot();
if (sLastScreenshot != null) {
+ mStartAnimationTriggered = false;
return ActivityOptions.makeCustomAnimation(mContext,
R.anim.recents_from_app_enter,
R.anim.recents_from_app_exit, mHandler, this);
@@ -302,6 +306,7 @@
c.setBitmap(null);
// Recycle the old thumbnail
firstThumbnail.recycle();
+ mStartAnimationTriggered = false;
return ActivityOptions.makeThumbnailScaleDownAnimation(mStatusBarView,
thumbnail, toTaskRect.left, toTaskRect.top, this);
}
@@ -449,9 +454,12 @@
@Override
public void onAnimationStarted() {
// Notify recents to start the enter animation
- Intent intent = new Intent(RecentsActivity.ACTION_START_ENTER_ANIMATION);
- intent.setPackage(mContext.getPackageName());
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- mContext.sendBroadcast(intent);
+ if (!mStartAnimationTriggered) {
+ Intent intent = new Intent(RecentsActivity.ACTION_START_ENTER_ANIMATION);
+ intent.setPackage(mContext.getPackageName());
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ mContext.sendBroadcast(intent);
+ mStartAnimationTriggered = true;
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index b039485..e62d989 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -92,6 +92,11 @@
public int taskBarExitAnimDuration;
public int taskBarDismissDozeDelaySeconds;
+ /** Lock to app */
+ public int taskViewLockToAppButtonHeight;
+ public int taskViewLockToAppShortAnimDuration;
+ public int taskViewLockToAppLongAnimDuration;
+
/** Nav bar scrim */
public int navBarScrimEnterDuration;
@@ -226,6 +231,14 @@
taskBarDismissDozeDelaySeconds =
res.getInteger(R.integer.recents_task_bar_dismiss_delay_seconds);
+ // Lock to app
+ taskViewLockToAppButtonHeight =
+ res.getDimensionPixelSize(R.dimen.recents_task_view_lock_to_app_button_height);
+ taskViewLockToAppShortAnimDuration =
+ res.getInteger(R.integer.recents_animate_lock_to_app_button_short_duration);
+ taskViewLockToAppLongAnimDuration =
+ res.getInteger(R.integer.recents_animate_lock_to_app_button_long_duration);
+
// Nav bar scrim
navBarScrimEnterDuration =
res.getInteger(R.integer.recents_nav_bar_scrim_enter_duration);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 05c0f58..b8beda6f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -17,8 +17,10 @@
package com.android.systemui.recents.misc;
import android.app.ActivityManager;
+import android.app.ActivityManagerNative;
import android.app.ActivityOptions;
import android.app.AppGlobals;
+import android.app.IActivityManager;
import android.app.SearchManager;
import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetManager;
@@ -66,6 +68,7 @@
final static String TAG = "SystemServicesProxy";
ActivityManager mAm;
+ IActivityManager mIam;
AppWidgetManager mAwm;
PackageManager mPm;
IPackageManager mIpm;
@@ -83,6 +86,7 @@
/** Private constructor */
public SystemServicesProxy(Context context) {
mAm = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+ mIam = ActivityManagerNative.getDefault();
mAwm = AppWidgetManager.getInstance(context);
mPm = context.getPackageManager();
mUm = (UserManager) context.getSystemService(Context.USER_SERVICE);
@@ -407,6 +411,17 @@
}
/**
+ * Locks the current task.
+ */
+ public void lockCurrentTask() {
+ if (mIam == null) return;
+
+ try {
+ mIam.startLockTaskModeOnCurrent();
+ } catch (RemoteException e) {}
+ }
+
+ /**
* Takes a screenshot of the current surface.
*/
public Bitmap takeScreenshot() {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
index 94474e9..86e8981 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -429,7 +429,8 @@
// Create a new task
Task task = new Task(t.persistentId, (t.id > -1), t.baseIntent, 0, activityLabel,
- activityIcon, activityColor, t.userId, t.firstActiveTime, t.lastActiveTime);
+ activityIcon, activityColor, t.userId, t.firstActiveTime, t.lastActiveTime,
+ (i == (taskCount - 1)));
// Preload the specified number of apps
if (i >= (taskCount - preloadCount)) {
@@ -523,7 +524,7 @@
if (info == null) continue;
stack.addTask(new Task(t.persistentId, true, t.baseIntent, 0, null, null, 0, 0,
- t.firstActiveTime, t.lastActiveTime));
+ t.firstActiveTime, t.lastActiveTime, (i == (taskCount - 1))));
}
stack.createSimulatedAffiliatedGroupings();
return stack;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index 0667e4c..88e9f40 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -87,6 +87,7 @@
public int colorPrimaryGreyscale;
public Bitmap thumbnail;
public boolean isActive;
+ public boolean canLockToTask;
public int userId;
TaskCallbacks mCb;
@@ -97,7 +98,7 @@
public Task(int id, boolean isActive, Intent intent, int taskAffiliation, String activityTitle,
Drawable activityIcon, int colorPrimary, int userId,
- long firstActiveTime, long lastActiveTime) {
+ long firstActiveTime, long lastActiveTime, boolean canLockToTask) {
this.key = new TaskKey(id, intent, userId, firstActiveTime, lastActiveTime);
this.taskAffiliation = taskAffiliation;
this.activityLabel = activityTitle;
@@ -105,6 +106,7 @@
this.colorPrimary = colorPrimary;
this.colorPrimaryGreyscale = Utilities.colorToGreyscale(colorPrimary);
this.isActive = isActive;
+ this.canLockToTask = canLockToTask;
this.userId = userId;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index 1ed0edd..7dd15a6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -150,7 +150,7 @@
/* Notifies when a task has been added to the stack */
public void onStackTaskAdded(TaskStack stack, Task t);
/* Notifies when a task has been removed from the stack */
- public void onStackTaskRemoved(TaskStack stack, Task t);
+ public void onStackTaskRemoved(TaskStack stack, Task removedTask, Task newFrontMostTask);
/** Notifies when the stack was filtered */
public void onStackFiltered(TaskStack newStack, ArrayList<Task> curTasks, Task t);
/** Notifies when the stack was un-filtered */
@@ -203,9 +203,15 @@
if (group.getTaskCount() == 0) {
removeGroup(group);
}
+ // Update the lock-to-app state
+ Task newFrontMostTask = getFrontMostTask();
+ t.canLockToTask = false;
+ if (newFrontMostTask != null) {
+ newFrontMostTask.canLockToTask = true;
+ }
if (mCb != null) {
// Notify that a task has been removed
- mCb.onStackTaskRemoved(this, t);
+ mCb.onStackTaskRemoved(this, t, newFrontMostTask);
}
}
}
@@ -226,7 +232,7 @@
}
if (mCb != null) {
// Notify that a task has been removed
- mCb.onStackTaskRemoved(this, t);
+ mCb.onStackTaskRemoved(this, t, null);
}
}
mTaskList.set(tasks);
@@ -239,6 +245,7 @@
/** Gets the front task */
public Task getFrontMostTask() {
+ if (mTaskList.size() == 0) return null;
return mTaskList.getTasks().get(mTaskList.size() - 1);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 85afb32..99b012e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -34,9 +34,10 @@
import android.view.View;
import android.view.WindowInsets;
import android.widget.FrameLayout;
-import com.android.systemui.recents.misc.Console;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.recents.misc.Console;
+import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.model.RecentsPackageMonitor;
import com.android.systemui.recents.model.RecentsTaskLoader;
import com.android.systemui.recents.model.SpaceNode;
@@ -144,7 +145,7 @@
Console.log(Constants.Log.UI.Focus, "[RecentsView|launchFocusedTask]",
"Found focused Task");
}
- onTaskViewClicked(stackView, tv, stack, task);
+ onTaskViewClicked(stackView, tv, stack, task, false);
return true;
}
}
@@ -180,7 +181,7 @@
tv = stv;
}
}
- onTaskViewClicked(stackView, tv, stack, task);
+ onTaskViewClicked(stackView, tv, stack, task, false);
return true;
}
}
@@ -431,7 +432,7 @@
@Override
public void onTaskViewClicked(final TaskStackView stackView, final TaskView tv,
- final TaskStack stack, final Task task) {
+ final TaskStack stack, final Task task, final boolean lockToTask) {
// Notify any callbacks of the launching of a new task
if (mCb != null) {
mCb.onTaskViewClicked();
@@ -456,6 +457,8 @@
}
// Compute the thumbnail to scale up from
+ final SystemServicesProxy ssp =
+ RecentsTaskLoader.getInstance().getSystemServicesProxy();
ActivityOptions opts = null;
int thumbnailWidth = transform.rect.width();
int thumbnailHeight = transform.rect.height();
@@ -469,8 +472,26 @@
new Rect(0, 0, task.thumbnail.getWidth(), task.thumbnail.getHeight()),
new Rect(0, 0, thumbnailWidth, thumbnailHeight), null);
c.setBitmap(null);
+ ActivityOptions.OnAnimationStartedListener animStartedListener = null;
+ if (lockToTask) {
+ animStartedListener = new ActivityOptions.OnAnimationStartedListener() {
+ boolean mTriggered = false;
+ @Override
+ public void onAnimationStarted() {
+ if (!mTriggered) {
+ postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ ssp.lockCurrentTask();
+ }
+ }, 350);
+ mTriggered = true;
+ }
+ }
+ };
+ }
opts = ActivityOptions.makeThumbnailScaleUpAnimation(sourceView,
- b, offsetX, offsetY);
+ b, offsetX, offsetY, animStartedListener);
}
final ActivityOptions launchOpts = opts;
@@ -496,8 +517,12 @@
UserHandle taskUser = new UserHandle(task.userId);
if (launchOpts != null) {
getContext().startActivityAsUser(i, launchOpts.toBundle(), taskUser);
+
} else {
getContext().startActivityAsUser(i, taskUser);
+ if (lockToTask) {
+ ssp.lockCurrentTask();
+ }
}
} catch (ActivityNotFoundException anfe) {
Console.logError(getContext(), "Could not start Activity");
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 35cf8ab..599c590 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -51,11 +51,12 @@
/* The visual representation of a task stack view */
public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCallbacks,
TaskView.TaskViewCallbacks, ViewPool.ViewPoolConsumer<TaskView, Task>,
- View.OnClickListener, RecentsPackageMonitor.PackageCallbacks {
+ RecentsPackageMonitor.PackageCallbacks {
/** The TaskView callbacks */
interface TaskStackViewCallbacks {
- public void onTaskViewClicked(TaskStackView stackView, TaskView tv, TaskStack stack, Task t);
+ public void onTaskViewClicked(TaskStackView stackView, TaskView tv, TaskStack stack, Task t,
+ boolean lockToTask);
public void onTaskViewAppInfoClicked(Task t);
public void onTaskViewDismissed(Task t);
public void onAllTaskViewsDismissed();
@@ -734,7 +735,8 @@
for (int i = 0; i < childCount; i++) {
TaskView t = (TaskView) getChildAt(i);
t.measure(MeasureSpec.makeMeasureSpec(mStackAlgorithm.mTaskRect.width(), MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(mStackAlgorithm.mTaskRect.height(), MeasureSpec.EXACTLY));
+ MeasureSpec.makeMeasureSpec(mStackAlgorithm.mTaskRect.height() +
+ mConfig.taskViewLockToAppButtonHeight, MeasureSpec.EXACTLY));
}
setMeasuredDimension(width, height);
@@ -766,7 +768,7 @@
TaskView t = (TaskView) getChildAt(i);
t.layout(mStackAlgorithm.mTaskRect.left, mStackAlgorithm.mStackRectSansPeek.top,
mStackAlgorithm.mTaskRect.right, mStackAlgorithm.mStackRectSansPeek.top +
- mStackAlgorithm.mTaskRect.height());
+ mStackAlgorithm.mTaskRect.height() + mConfig.taskViewLockToAppButtonHeight);
}
if (mAwaitingFirstLayout) {
@@ -903,11 +905,6 @@
mUIDozeTrigger.poke();
}
- /** Disables handling touch on this task view. */
- void setTouchOnTaskView(TaskView tv, boolean enabled) {
- tv.setOnClickListener(enabled ? this : null);
- }
-
/**** TaskStackCallbacks Implementation ****/
@Override
@@ -919,25 +916,33 @@
}
@Override
- public void onStackTaskRemoved(TaskStack stack, Task t) {
+ public void onStackTaskRemoved(TaskStack stack, Task removedTask, Task newFrontMostTask) {
// Update the task offsets
mStackAlgorithm.updateTaskOffsets(mStack.getTasks());
// Remove the view associated with this task, we can't rely on updateTransforms
// to work here because the task is no longer in the list
- TaskView tv = getChildViewForTask(t);
+ TaskView tv = getChildViewForTask(removedTask);
if (tv != null) {
mViewPool.returnViewToPool(tv);
}
// Notify the callback that we've removed the task and it can clean up after it
- mCb.onTaskViewDismissed(t);
+ mCb.onTaskViewDismissed(removedTask);
// Update the min/max scroll and animate other task views into their new positions
updateMinMaxScroll(true);
int movement = (int) mStackAlgorithm.getTaskOverlapHeight();
requestSynchronizeStackViewsWithModel(Utilities.calculateTranslationAnimationDuration(movement));
+ // Update the new front most task
+ if (newFrontMostTask != null) {
+ TaskView frontTv = getChildViewForTask(newFrontMostTask);
+ if (frontTv != null) {
+ frontTv.onTaskBound(newFrontMostTask);
+ }
+ }
+
// If there are no remaining tasks, then either unfilter the current stack, or just close
// the activity if there are no filtered stacks
if (mStack.getTaskCount() == 0) {
@@ -1086,7 +1091,7 @@
addView(tv, insertIndex);
// Set the callbacks and listeners for this new view
- setTouchOnTaskView(tv, true);
+ tv.setTouchEnabled(true);
tv.setCallbacks(this);
} else {
attachViewToParent(tv, insertIndex, tv.getLayoutParams());
@@ -1129,18 +1134,7 @@
}
@Override
- public void onTaskViewDismissed(TaskView tv) {
- Task task = tv.getTask();
- // Remove the task from the view
- mStack.removeTask(task);
- }
-
- /**** View.OnClickListener Implementation ****/
-
- @Override
- public void onClick(View v) {
- TaskView tv = (TaskView) v;
- Task task = tv.getTask();
+ public void onTaskViewClicked(TaskView tv, Task task, boolean lockToTask) {
if (Console.Enabled) {
Console.log(Constants.Log.UI.ClickEvents, "[TaskStack|Clicked|Thumbnail]",
task + " cb: " + mCb);
@@ -1150,10 +1144,17 @@
mUIDozeTrigger.stopDozing();
if (mCb != null) {
- mCb.onTaskViewClicked(this, tv, mStack, task);
+ mCb.onTaskViewClicked(this, tv, mStack, task, lockToTask);
}
}
+ @Override
+ public void onTaskViewDismissed(TaskView tv) {
+ Task task = tv.getTask();
+ // Remove the task from the view
+ mStack.removeTask(task);
+ }
+
/**** RecentsPackageMonitor.PackageCallbacks Implementation ****/
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
index 789b4f7..e1e682b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
@@ -95,9 +95,11 @@
if (numTasks <= 1) {
// If there is only one task, then center the task in the stack rect (sans peek)
- mMinScroll = mMaxScroll = -(stackHeight - taskHeight) / 2;
+ mMinScroll = mMaxScroll = -(stackHeight -
+ (taskHeight + mConfig.taskViewLockToAppButtonHeight)) / 2;
} else {
- int maxScrollHeight = taskHeight + getStackScrollForTaskIndex(tasks.get(tasks.size() - 1));
+ int maxScrollHeight = getStackScrollForTaskIndex(tasks.get(tasks.size() - 1))
+ + taskHeight + mConfig.taskViewLockToAppButtonHeight;
mMinScroll = Math.min(stackHeight, maxScrollHeight) - stackHeight;
mMaxScroll = maxScrollHeight - stackHeight;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index e186e2e..15ace13 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -377,7 +377,9 @@
// Enable HW layers on that task
tv.enableHwLayers();
// Disallow touch events from this task view
- mSv.setTouchOnTaskView(tv, false);
+ tv.setTouchEnabled(false);
+ // Hide the footer
+ tv.animateFooterVisibility(false, mSv.mConfig.taskViewLockToAppShortAnimDuration, 0);
// Disallow parents from intercepting touch events
final ViewParent parent = mSv.getParent();
if (parent != null) {
@@ -413,7 +415,9 @@
// Re-enable clipping with the stack
tv.setClipViewInStack(true);
// Re-enable touch events from this task view
- mSv.setTouchOnTaskView(tv, true);
+ tv.setTouchEnabled(true);
+ // Restore the footer
+ tv.animateFooterVisibility(true, mSv.mConfig.taskViewLockToAppShortAnimDuration, 0);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 33e3f58..125b018 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -35,6 +35,7 @@
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.model.TaskStack;
/* A task view */
@@ -44,11 +45,16 @@
interface TaskViewCallbacks {
public void onTaskViewAppIconClicked(TaskView tv);
public void onTaskViewAppInfoClicked(TaskView tv);
+ public void onTaskViewClicked(TaskView tv, Task t, boolean lockToTask);
public void onTaskViewDismissed(TaskView tv);
}
RecentsConfiguration mConfig;
+ int mFooterHeight;
+ int mMaxFooterHeight;
+ ObjectAnimator mFooterAnimator;
+
int mDim;
int mMaxDim;
AccelerateInterpolator mDimInterpolator = new AccelerateInterpolator();
@@ -60,9 +66,11 @@
boolean mClipViewInStack;
Rect mTmpRect = new Rect();
Paint mLayerPaint = new Paint();
+ Outline mOutline = new Outline();
TaskThumbnailView mThumbnailView;
TaskBarView mBarView;
+ View mLockToAppButtonView;
TaskViewCallbacks mCb;
// Optimizations
@@ -102,9 +110,11 @@
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());
}
@Override
@@ -117,6 +127,7 @@
// 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();
@@ -125,13 +136,33 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ int width = MeasureSpec.getSize(widthMeasureSpec);
+ int height = MeasureSpec.getSize(heightMeasureSpec);
- // Update the outline
- Outline o = new Outline();
- o.setRoundRect(0, 0, getMeasuredWidth(), getMeasuredHeight(),
+ // 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);
+ updateOutline();
+ }
+
+ /** Updates the outline to match whether the lock-to-app button is visible or not. */
+ void updateOutline() {
+ int height = getMeasuredHeight();
+ if (height == 0) return;
+
+ // Account for the current footer height
+ height = height - mMaxFooterHeight + mFooterHeight;
+
+ mOutline.setRoundRect(0, 0, getMeasuredWidth(), height,
mConfig.taskViewRoundedCornerRadiusPx);
- setOutline(o);
+ setOutline(mOutline);
}
/** Set callback */
@@ -289,6 +320,8 @@
// 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, 0);
// Decrement the post animation trigger
ctx.postAnimationTrigger.decrement();
}
@@ -335,6 +368,10 @@
});
anim.start();
ctx.postAnimationTrigger.increment();
+
+ // Animate the footer into view
+ animateFooterVisibility(true, mConfig.taskBarEnterAnimDuration,
+ mConfig.taskBarEnterAnimDelay);
} else {
mEnableThumbnailClip.run();
}
@@ -366,9 +403,16 @@
})
.start();
ctx.postAnimationTrigger.increment();
+
+ // Animate the footer into view
+ animateFooterVisibility(true, mConfig.taskViewEnterFromHomeDuration,
+ mConfig.taskBarEnterAnimDelay);
} else {
// Otherwise, just enable the thumbnail clip
mEnableThumbnailClip.run();
+
+ // Animate the footer into view
+ animateFooterVisibility(true, 0, 0);
}
}
@@ -457,12 +501,14 @@
void enableHwLayers() {
mThumbnailView.setLayerType(View.LAYER_TYPE_HARDWARE, mLayerPaint);
mBarView.enableHwLayers();
+ mLockToAppButtonView.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();
+ mLockToAppButtonView.setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
}
/** Sets the stubbed state of this task view. */
@@ -499,6 +545,57 @@
}
}
+ /** Sets the footer height. */
+ public void setFooterHeight(int height) {
+ mFooterHeight = height;
+ updateOutline();
+ invalidate(0, getMeasuredHeight() - mMaxFooterHeight, getMeasuredWidth(),
+ getMeasuredHeight());
+ }
+
+ /** Gets the footer height. */
+ public int getFooterHeight() {
+ return mFooterHeight;
+ }
+
+ /** Animates the footer into and out of view. */
+ public void animateFooterVisibility(boolean visible, int duration, int delay) {
+ if (!mTask.canLockToTask) 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);
+ }
+ }
+ }
+
/** Returns the current dim. */
public void setDim(int dim) {
mDim = dim;
@@ -584,6 +681,11 @@
public void onTaskBound(Task t) {
mTask = t;
mTask.setCallbacks(this);
+ if (getMeasuredWidth() == 0) {
+ animateFooterVisibility(t.canLockToTask, 0, 0);
+ } else {
+ animateFooterVisibility(t.canLockToTask, mConfig.taskViewLockToAppLongAnimDuration, 0);
+ }
}
@Override
@@ -597,6 +699,7 @@
mBarView.mApplicationIcon.setOnClickListener(this);
}
mBarView.mDismissButton.setOnClickListener(this);
+ mLockToAppButtonView.setOnClickListener(this);
if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
if (mConfig.developerOptionsEnabled) {
mBarView.mApplicationIcon.setOnLongClickListener(this);
@@ -618,6 +721,7 @@
mBarView.mApplicationIcon.setOnClickListener(null);
}
mBarView.mDismissButton.setOnClickListener(null);
+ mLockToAppButtonView.setOnClickListener(null);
if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
mBarView.mApplicationIcon.setOnLongClickListener(null);
}
@@ -625,6 +729,11 @@
mTaskDataLoaded = false;
}
+ /** Enables/disables handling touch on this task view. */
+ void setTouchEnabled(boolean enabled) {
+ setOnClickListener(enabled ? this : null);
+ }
+
@Override
public void onClick(final View v) {
// We purposely post the handler delayed to allow for the touch feedback to draw
@@ -642,6 +751,10 @@
mCb.onTaskViewDismissed(tv);
}
});
+ // Hide the footer
+ tv.animateFooterVisibility(false, mConfig.taskViewRemoveAnimDuration, 0);
+ } else if (v == tv || v == mLockToAppButtonView) {
+ mCb.onTaskViewClicked(tv, tv.getTask(), (v == mLockToAppButtonView));
}
}
}, 125);