Simplifying and removing unnecessary checks from app-drag tests
am: ab472e8c06
Change-Id: I084294421d3493f39bf6360a77bc3f3be800073e
diff --git a/go/quickstep/res/drawable/default_thumbnail.xml b/go/quickstep/res/drawable/default_thumbnail.xml
new file mode 100644
index 0000000..0a2dbf0
--- /dev/null
+++ b/go/quickstep/res/drawable/default_thumbnail.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2019 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.
+-->
+<shape
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="@android:color/darker_gray"/>
+ <corners android:radius="2dp"/>
+</shape>
diff --git a/go/quickstep/res/drawable/empty_content_box.xml b/go/quickstep/res/drawable/empty_content_box.xml
new file mode 100644
index 0000000..a488388
--- /dev/null
+++ b/go/quickstep/res/drawable/empty_content_box.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2019 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.
+-->
+<shape
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="@android:color/transparent"/>
+ <stroke android:color="@android:color/white" android:width="4px"/>
+ <corners android:radius="2dp"/>
+</shape>
\ No newline at end of file
diff --git a/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
index d1d697c..c228bb9 100644
--- a/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
+++ b/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
@@ -131,10 +131,11 @@
return anim;
}
- View thumbnailView = mRecentsView.getThumbnailViewForTask(mTargetTaskId);
+ View thumbnailView = mRecentsView.getBottomThumbnailView();
if (thumbnailView == null) {
- // TODO: We should either 1) guarantee the view is loaded before attempting this
- // or 2) have a backup animation.
+ // This can be null if there were previously 0 tasks and the recycler view has not had
+ // enough time to take in the data change, bind a new view, and lay out the new view.
+ // TODO: Have a fallback to animate to
if (Log.isLoggable(TAG, Log.WARN)) {
Log.w(TAG, "No thumbnail view for running task. Using stub animation.");
}
diff --git a/go/quickstep/src/com/android/quickstep/TaskAdapter.java b/go/quickstep/src/com/android/quickstep/TaskAdapter.java
index 674fcae..02cbf4e 100644
--- a/go/quickstep/src/com/android/quickstep/TaskAdapter.java
+++ b/go/quickstep/src/com/android/quickstep/TaskAdapter.java
@@ -15,12 +15,10 @@
*/
package com.android.quickstep;
-import android.util.ArrayMap;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView.Adapter;
import com.android.launcher3.R;
@@ -39,7 +37,6 @@
private static final int MAX_TASKS_TO_DISPLAY = 6;
private static final String TAG = "TaskAdapter";
private final TaskListLoader mLoader;
- private final ArrayMap<Integer, TaskItemView> mTaskIdToViewMap = new ArrayMap<>();
private TaskActionController mTaskActionController;
private boolean mIsShowingLoadingUi;
@@ -63,16 +60,6 @@
mIsShowingLoadingUi = isShowingLoadingUi;
}
- /**
- * Get task item view for a given task id if it's attached to the view.
- *
- * @param taskId task id to search for
- * @return corresponding task item view if it's attached, null otherwise
- */
- public @Nullable TaskItemView getTaskItemView(int taskId) {
- return mTaskIdToViewMap.get(taskId);
- }
-
@Override
public TaskHolder onCreateViewHolder(ViewGroup parent, int viewType) {
TaskItemView itemView = (TaskItemView) LayoutInflater.from(parent.getContext())
@@ -94,7 +81,7 @@
return;
}
Task task = tasks.get(position);
- holder.bindTask(task);
+ holder.bindTask(task, false /* willAnimate */);
mLoader.loadTaskIconAndLabel(task, () -> {
// Ensure holder still has the same task.
if (Objects.equals(task, holder.getTask())) {
@@ -110,19 +97,10 @@
}
@Override
- public void onViewAttachedToWindow(@NonNull TaskHolder holder) {
- if (holder.getTask() == null) {
- return;
- }
- mTaskIdToViewMap.put(holder.getTask().key.id, (TaskItemView) holder.itemView);
- }
-
- @Override
- public void onViewDetachedFromWindow(@NonNull TaskHolder holder) {
- if (holder.getTask() == null) {
- return;
- }
- mTaskIdToViewMap.remove(holder.getTask().key.id);
+ public void onBindViewHolder(@NonNull TaskHolder holder, int position,
+ @NonNull List<Object> payloads) {
+ // TODO: Bind task in preparation for animation. For now, we apply UI changes immediately.
+ super.onBindViewHolder(holder, position, payloads);
}
@Override
diff --git a/go/quickstep/src/com/android/quickstep/TaskHolder.java b/go/quickstep/src/com/android/quickstep/TaskHolder.java
index 98dc989..a3fa5c1 100644
--- a/go/quickstep/src/com/android/quickstep/TaskHolder.java
+++ b/go/quickstep/src/com/android/quickstep/TaskHolder.java
@@ -15,6 +15,9 @@
*/
package com.android.quickstep;
+import android.graphics.Bitmap;
+
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
@@ -40,13 +43,28 @@
}
/**
- * Bind a task to the holder, resetting the view and preparing it for content to load in.
+ * Bind the task model to the holder. This will take the current task content in the task
+ * object (i.e. icon, thumbnail, label) and either apply the content immediately or simply bind
+ * the content to animate to at a later time. If the task does not have all its content loaded,
+ * the view will prepare appropriate default placeholders and it is the callers responsibility
+ * to change them at a later time.
+ *
+ * Regardless of whether it is animating, input handlers will be bound immediately (see
+ * {@link TaskActionController}).
*
* @param task the task to bind to the view
+ * @param willAnimate true if UI should animate in later, false if it should apply immediately
*/
- public void bindTask(Task task) {
+ public void bindTask(@NonNull Task task, boolean willAnimate) {
mTask = task;
- mTaskItemView.resetTaskItemView();
+ Bitmap thumbnail = (task.thumbnail != null) ? task.thumbnail.thumbnail : null;
+ if (willAnimate) {
+ mTaskItemView.startContentAnimation(task.icon, thumbnail, task.titleDescription);
+ } else {
+ mTaskItemView.setIcon(task.icon);
+ mTaskItemView.setThumbnail(thumbnail);
+ mTaskItemView.setLabel(task.titleDescription);
+ }
}
/**
@@ -55,10 +73,7 @@
*/
public void bindEmptyUi() {
mTask = null;
- // TODO: Set the task view to a loading, empty UI.
- // Temporarily using the one below for visual confirmation but should be swapped out to new
- // UI later.
- mTaskItemView.resetTaskItemView();
+ mTaskItemView.resetToEmptyUi();
}
/**
diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
index c06b6ec..59755bc 100644
--- a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
+++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
@@ -24,14 +24,12 @@
import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.content.Context;
+import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.view.View;
import android.view.ViewDebug;
-import android.view.animation.AlphaAnimation;
-import android.view.animation.Animation;
-import android.view.animation.AnimationSet;
-import android.view.animation.LayoutAnimationController;
+import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
@@ -40,6 +38,7 @@
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver;
+import androidx.recyclerview.widget.RecyclerView.OnChildAttachStateChangeListener;
import com.android.launcher3.R;
import com.android.quickstep.RecentsToActivityHelper;
@@ -90,7 +89,6 @@
private final TaskListLoader mTaskLoader;
private final TaskAdapter mTaskAdapter;
private final TaskActionController mTaskActionController;
- private final LayoutAnimationController mLayoutAnimation;
private RecentsToActivityHelper mActivityHelper;
private RecyclerView mTaskRecyclerView;
@@ -98,6 +96,9 @@
private View mContentView;
private View mClearAllView;
private boolean mTransitionedFromApp;
+ private AnimatorSet mLayoutAnimation;
+ private final ArraySet<View> mLayingOutViews = new ArraySet<>();
+
public IconRecentsView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -106,7 +107,6 @@
mTaskAdapter = new TaskAdapter(mTaskLoader);
mTaskActionController = new TaskActionController(mTaskLoader, mTaskAdapter);
mTaskAdapter.setActionController(mTaskActionController);
- mLayoutAnimation = createLayoutAnimation();
}
@Override
@@ -120,7 +120,20 @@
ItemTouchHelper helper = new ItemTouchHelper(
new TaskSwipeCallback(mTaskActionController));
helper.attachToRecyclerView(mTaskRecyclerView);
- mTaskRecyclerView.setLayoutAnimation(mLayoutAnimation);
+ mTaskRecyclerView.addOnChildAttachStateChangeListener(
+ new OnChildAttachStateChangeListener() {
+ @Override
+ public void onChildViewAttachedToWindow(@NonNull View view) {
+ if (mLayoutAnimation != null && !mLayingOutViews.contains(view)) {
+ // Child view was added that is not part of current layout animation
+ // so restart the animation.
+ animateFadeInLayoutAnimation();
+ }
+ }
+
+ @Override
+ public void onChildViewDetachedFromWindow(@NonNull View view) { }
+ });
mEmptyView = findViewById(R.id.recent_task_empty_view);
mContentView = findViewById(R.id.recent_task_content_view);
@@ -165,8 +178,7 @@
* becomes visible.
*/
public void onBeginTransitionToOverview() {
- mTaskRecyclerView.scheduleLayoutAnimation();
-
+ scheduleFadeInLayoutAnimation();
// Load any task changes
if (!mTaskLoader.needsToLoad()) {
return;
@@ -213,16 +225,15 @@
}
/**
- * Get the thumbnail view associated with a task for the purposes of animation.
+ * Get the bottom most thumbnail view to animate to.
*
- * @param taskId task id of thumbnail view to get
- * @return the thumbnail view for the task if attached, null otherwise
+ * @return the thumbnail view if laid out
*/
- public @Nullable View getThumbnailViewForTask(int taskId) {
- TaskItemView view = mTaskAdapter.getTaskItemView(taskId);
- if (view == null) {
+ public @Nullable View getBottomThumbnailView() {
+ if (mTaskRecyclerView.getChildCount() == 0) {
return null;
}
+ TaskItemView view = (TaskItemView) mTaskRecyclerView.getChildAt(0);
return view.getThumbnailView();
}
@@ -338,17 +349,56 @@
});
}
- private static LayoutAnimationController createLayoutAnimation() {
- AnimationSet anim = new AnimationSet(false /* shareInterpolator */);
+ /**
+ * Schedule a one-shot layout animation on the next layout. Separate from
+ * {@link #scheduleLayoutAnimation()} as the animation is {@link Animator} based and acts on the
+ * view properties themselves, allowing more controllable behavior and making it easier to
+ * manage when the animation conflicts with another animation.
+ */
+ private void scheduleFadeInLayoutAnimation() {
+ ViewTreeObserver viewTreeObserver = mTaskRecyclerView.getViewTreeObserver();
+ viewTreeObserver.addOnGlobalLayoutListener(
+ new ViewTreeObserver.OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ animateFadeInLayoutAnimation();
+ viewTreeObserver.removeOnGlobalLayoutListener(this);
+ }
+ });
+ }
- Animation alphaAnim = new AlphaAnimation(0, 1);
- alphaAnim.setDuration(LAYOUT_ITEM_ANIMATE_IN_DURATION);
- anim.addAnimation(alphaAnim);
-
- LayoutAnimationController layoutAnim = new LayoutAnimationController(anim);
- layoutAnim.setDelay(
- (float) LAYOUT_ITEM_ANIMATE_IN_DELAY_BETWEEN / LAYOUT_ITEM_ANIMATE_IN_DURATION);
-
- return layoutAnim;
+ /**
+ * Start animating the layout animation where items fade in.
+ */
+ private void animateFadeInLayoutAnimation() {
+ if (mLayoutAnimation != null) {
+ // If layout animation still in progress, cancel and restart.
+ mLayoutAnimation.cancel();
+ }
+ TaskItemView[] views = getTaskViews();
+ int delay = 0;
+ mLayoutAnimation = new AnimatorSet();
+ for (TaskItemView view : views) {
+ view.setAlpha(0.0f);
+ Animator alphaAnim = ObjectAnimator.ofFloat(view, ALPHA, 0.0f, 1.0f);
+ alphaAnim.setDuration(LAYOUT_ITEM_ANIMATE_IN_DURATION).setStartDelay(delay);
+ alphaAnim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ view.setAlpha(1.0f);
+ mLayingOutViews.remove(view);
+ }
+ });
+ delay += LAYOUT_ITEM_ANIMATE_IN_DELAY_BETWEEN;
+ mLayoutAnimation.play(alphaAnim);
+ mLayingOutViews.add(view);
+ }
+ mLayoutAnimation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mLayoutAnimation = null;
+ }
+ });
+ mLayoutAnimation.start();
}
}
diff --git a/go/quickstep/src/com/android/quickstep/views/TaskItemView.java b/go/quickstep/src/com/android/quickstep/views/TaskItemView.java
index d831b20..572747b 100644
--- a/go/quickstep/src/com/android/quickstep/views/TaskItemView.java
+++ b/go/quickstep/src/com/android/quickstep/views/TaskItemView.java
@@ -16,15 +16,18 @@
package com.android.quickstep.views;
import android.content.Context;
+import android.content.res.Resources;
import android.graphics.Bitmap;
-import android.graphics.Color;
+import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
+import android.util.FloatProperty;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.launcher3.R;
@@ -34,16 +37,41 @@
*/
public final class TaskItemView extends LinearLayout {
+ private static final String EMPTY_LABEL = "";
private static final String DEFAULT_LABEL = "...";
private final Drawable mDefaultIcon;
+ private final Drawable mDefaultThumbnail;
+ private final TaskLayerDrawable mIconDrawable;
+ private final TaskLayerDrawable mThumbnailDrawable;
private TextView mLabelView;
private ImageView mIconView;
private ImageView mThumbnailView;
+ private float mContentTransitionProgress;
+
+ /**
+ * Property representing the content transition progress of the view. 1.0f represents that the
+ * currently bound icon, thumbnail, and label are fully animated in and visible.
+ */
+ public static FloatProperty CONTENT_TRANSITION_PROGRESS =
+ new FloatProperty<TaskItemView>("taskContentTransitionProgress") {
+ @Override
+ public void setValue(TaskItemView view, float progress) {
+ view.setContentTransitionProgress(progress);
+ }
+
+ @Override
+ public Float get(TaskItemView view) {
+ return view.mContentTransitionProgress;
+ }
+ };
public TaskItemView(Context context, AttributeSet attrs) {
super(context, attrs);
- mDefaultIcon = context.getResources().getDrawable(
- android.R.drawable.sym_def_app_icon, context.getTheme());
+ Resources res = context.getResources();
+ mDefaultIcon = res.getDrawable(android.R.drawable.sym_def_app_icon, context.getTheme());
+ mDefaultThumbnail = res.getDrawable(R.drawable.default_thumbnail, context.getTheme());
+ mIconDrawable = new TaskLayerDrawable(context);
+ mThumbnailDrawable = new TaskLayerDrawable(context);
}
@Override
@@ -52,15 +80,21 @@
mLabelView = findViewById(R.id.task_label);
mThumbnailView = findViewById(R.id.task_thumbnail);
mIconView = findViewById(R.id.task_icon);
+
+ mThumbnailView.setImageDrawable(mThumbnailDrawable);
+ mIconView.setImageDrawable(mIconDrawable);
+
+ resetToEmptyUi();
+ CONTENT_TRANSITION_PROGRESS.setValue(this, 1.0f);
}
/**
- * Resets task item view to default values.
+ * Resets task item view to empty, loading UI.
*/
- public void resetTaskItemView() {
- setLabel(DEFAULT_LABEL);
- setIcon(null);
- setThumbnail(null);
+ public void resetToEmptyUi() {
+ mIconDrawable.resetDrawable();
+ mThumbnailDrawable.resetDrawable();
+ setLabel(EMPTY_LABEL);
}
/**
@@ -69,11 +103,8 @@
* @param label task label
*/
public void setLabel(@Nullable String label) {
- if (label == null) {
- mLabelView.setText(DEFAULT_LABEL);
- return;
- }
- mLabelView.setText(label);
+ mLabelView.setText(getSafeLabel(label));
+ // TODO: Animation for label
}
/**
@@ -86,11 +117,7 @@
// The icon proper is actually smaller than the drawable and has "padding" on the side for
// the purpose of drawing the shadow, allowing the icon to pop up, so we need to scale the
// view if we want the icon to be flush with the bottom of the thumbnail.
- if (icon == null) {
- mIconView.setImageDrawable(mDefaultIcon);
- return;
- }
- mIconView.setImageDrawable(icon);
+ mIconDrawable.setCurrentDrawable(getSafeIcon(icon));
}
/**
@@ -99,16 +126,48 @@
* @param thumbnail task thumbnail for the task
*/
public void setThumbnail(@Nullable Bitmap thumbnail) {
- if (thumbnail == null) {
- mThumbnailView.setImageBitmap(null);
- mThumbnailView.setBackgroundColor(Color.GRAY);
- return;
- }
- mThumbnailView.setBackgroundColor(Color.TRANSPARENT);
- mThumbnailView.setImageBitmap(thumbnail);
+ mThumbnailDrawable.setCurrentDrawable(getSafeThumbnail(thumbnail));
}
public View getThumbnailView() {
return mThumbnailView;
}
+
+ /**
+ * Start a new animation from the current task content to the specified new content. The caller
+ * is responsible for the actual animation control via the property
+ * {@link #CONTENT_TRANSITION_PROGRESS}.
+ *
+ * @param endIcon the icon to animate to
+ * @param endThumbnail the thumbnail to animate to
+ * @param endLabel the label to animate to
+ */
+ public void startContentAnimation(@Nullable Drawable endIcon, @Nullable Bitmap endThumbnail,
+ @Nullable String endLabel) {
+ mIconDrawable.startNewTransition(getSafeIcon(endIcon));
+ mThumbnailDrawable.startNewTransition(getSafeThumbnail(endThumbnail));
+ // TODO: Animation for label
+
+ setContentTransitionProgress(0.0f);
+ }
+
+ private void setContentTransitionProgress(float progress) {
+ mContentTransitionProgress = progress;
+ mIconDrawable.setTransitionProgress(progress);
+ mThumbnailDrawable.setTransitionProgress(progress);
+ // TODO: Animation for label
+ }
+
+ private @NonNull Drawable getSafeIcon(@Nullable Drawable icon) {
+ return (icon != null) ? icon : mDefaultIcon;
+ }
+
+ private @NonNull Drawable getSafeThumbnail(@Nullable Bitmap thumbnail) {
+ return (thumbnail != null) ? new BitmapDrawable(getResources(), thumbnail)
+ : mDefaultThumbnail;
+ }
+
+ private @NonNull String getSafeLabel(@Nullable String label) {
+ return (label != null) ? label : DEFAULT_LABEL;
+ }
}
diff --git a/go/quickstep/src/com/android/quickstep/views/TaskLayerDrawable.java b/go/quickstep/src/com/android/quickstep/views/TaskLayerDrawable.java
new file mode 100644
index 0000000..98b66b9
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/views/TaskLayerDrawable.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2019 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.quickstep.views;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+
+import androidx.annotation.NonNull;
+
+import com.android.launcher3.R;
+
+/**
+ * A layer drawable for task content that transitions between two drawables by crossfading. Similar
+ * to {@link android.graphics.drawable.TransitionDrawable} but allows callers to control transition
+ * progress and provides a default, empty drawable.
+ */
+public final class TaskLayerDrawable extends LayerDrawable {
+ private final Drawable mEmptyDrawable;
+ private float mProgress;
+
+ public TaskLayerDrawable(Context context) {
+ super(new Drawable[0]);
+
+ // Use empty drawable for both layers initially.
+ mEmptyDrawable = context.getResources().getDrawable(
+ R.drawable.empty_content_box, context.getTheme());
+ addLayer(mEmptyDrawable);
+ addLayer(mEmptyDrawable);
+ setTransitionProgress(1.0f);
+ }
+
+ /**
+ * Immediately set the front-most drawable layer.
+ *
+ * @param drawable drawable to set
+ */
+ public void setCurrentDrawable(@NonNull Drawable drawable) {
+ setDrawable(0, drawable);
+ applyTransitionProgress(mProgress);
+ }
+
+ /**
+ * Immediately reset the drawable to showing the empty drawable.
+ */
+ public void resetDrawable() {
+ setCurrentDrawable(mEmptyDrawable);
+ }
+
+ /**
+ * Prepare to start animating the transition by pushing the current drawable to the back and
+ * setting a new drawable to the front layer and making it invisible.
+ *
+ * @param endDrawable drawable to animate to
+ */
+ public void startNewTransition(@NonNull Drawable endDrawable) {
+ Drawable oldDrawable = getDrawable(0);
+ setDrawable(1, oldDrawable);
+ setDrawable(0, endDrawable);
+ setTransitionProgress(0.0f);
+ }
+
+ /**
+ * Set the progress of the transition animation to crossfade the two drawables.
+ *
+ * @param progress current transition progress between 0 (front view invisible) and 1
+ * (front view visible)
+ */
+ public void setTransitionProgress(float progress) {
+ if (progress > 1 || progress < 0) {
+ throw new IllegalArgumentException("Transition progress should be between 0 and 1");
+ }
+ mProgress = progress;
+ applyTransitionProgress(progress);
+ }
+
+ private void applyTransitionProgress(float progress) {
+ int drawableAlpha = (int) (progress * 255);
+ getDrawable(0).setAlpha(drawableAlpha);
+ if (getDrawable(0) != getDrawable(1)) {
+ // Only do this if it's a different drawable so that it fades out.
+ // Otherwise, we'd just be overwriting the front drawable's alpha.
+ getDrawable(1).setAlpha(255 - drawableAlpha);
+ }
+ invalidateSelf();
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java
index 446fb39..b9791bf 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java
@@ -59,7 +59,6 @@
private static final String TAG = DigitalWellBeingToast.class.getSimpleName();
private Task mTask;
- private ImageView mImage;
private TextView mText;
public DigitalWellBeingToast(Context context, AttributeSet attrs) {
@@ -75,7 +74,6 @@
super.onFinishInflate();
mText = findViewById(R.id.digital_well_being_remaining_time);
- mImage = findViewById(R.id.digital_well_being_hourglass);
}
public void initialize(Task task, InitializeCallback callback) {
@@ -103,8 +101,6 @@
} else {
setVisibility(VISIBLE);
mText.setText(getText(appRemainingTimeMs));
- mImage.setImageResource(appRemainingTimeMs > 0 ?
- R.drawable.hourglass_top : R.drawable.hourglass_bottom);
}
callback.call(getContentDescriptionForTask(
diff --git a/quickstep/res/drawable/hourglass_bottom.xml b/quickstep/res/drawable/hourglass_bottom.xml
deleted file mode 100644
index b5ef008..0000000
--- a/quickstep/res/drawable/hourglass_bottom.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24">
- <group>
- <clip-path android:pathData="M0,0H24V24H0Z M 0,0"/>
- <path
- android:fillColor="#FFFFFFFF"
- android:pathData="M6,2V8H6l4,4L6,16H6v6H18V16h0l-4,-4,4,-4h0V2Zm6,9.5,-4,-4V4h8V7.5Z"/>
- </group>
-</vector>
diff --git a/quickstep/res/drawable/hourglass_top.xml b/quickstep/res/drawable/ic_hourglass_top.xml
similarity index 100%
rename from quickstep/res/drawable/hourglass_top.xml
rename to quickstep/res/drawable/ic_hourglass_top.xml
diff --git a/quickstep/res/layout/task.xml b/quickstep/res/layout/task.xml
index f96a66f..1d1c272 100644
--- a/quickstep/res/layout/task.xml
+++ b/quickstep/res/layout/task.xml
@@ -49,6 +49,7 @@
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginEnd="8dp"
+ android:src="@drawable/ic_hourglass_top"
/>
<TextView
android:id="@+id/digital_well_being_remaining_time"