Merge "Stress test for starting Launcher via gestures" into ub-launcher3-master
diff --git a/go/quickstep/src/com/android/quickstep/TaskAdapter.java b/go/quickstep/src/com/android/quickstep/TaskAdapter.java
index d3fd240..4f3d1e4 100644
--- a/go/quickstep/src/com/android/quickstep/TaskAdapter.java
+++ b/go/quickstep/src/com/android/quickstep/TaskAdapter.java
@@ -25,7 +25,8 @@
import com.android.quickstep.views.TaskItemView;
import com.android.systemui.shared.recents.model.Task;
-import java.util.ArrayList;
+import java.util.List;
+
/**
* Recycler view adapter that dynamically inflates and binds {@link TaskHolder} instances with the
* appropriate {@link Task} from the recents task list.
@@ -35,22 +36,28 @@
private static final int MAX_TASKS_TO_DISPLAY = 6;
private static final String TAG = "TaskAdapter";
private final TaskListLoader mLoader;
+ private TaskInputController mInputController;
public TaskAdapter(@NonNull TaskListLoader loader) {
mLoader = loader;
}
+ public void setInputController(TaskInputController inputController) {
+ mInputController = inputController;
+ }
+
@Override
public TaskHolder onCreateViewHolder(ViewGroup parent, int viewType) {
- // TODO: Swap in an actual task view here (view w/ icon, label, etc.)
TaskItemView itemView = (TaskItemView) LayoutInflater.from(parent.getContext())
.inflate(R.layout.task_item_view, parent, false);
- return new TaskHolder(itemView);
+ TaskHolder holder = new TaskHolder(itemView);
+ itemView.setOnClickListener(view -> mInputController.onTaskClicked(holder));
+ return holder;
}
@Override
public void onBindViewHolder(TaskHolder holder, int position) {
- ArrayList<Task> tasks = mLoader.getCurrentTaskList();
+ List<Task> tasks = mLoader.getCurrentTaskList();
if (position >= tasks.size()) {
// Task list has updated.
return;
diff --git a/go/quickstep/src/com/android/quickstep/TaskHolder.java b/go/quickstep/src/com/android/quickstep/TaskHolder.java
index 47398c8..67e8ece 100644
--- a/go/quickstep/src/com/android/quickstep/TaskHolder.java
+++ b/go/quickstep/src/com/android/quickstep/TaskHolder.java
@@ -15,6 +15,7 @@
*/
package com.android.quickstep;
+import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
import com.android.quickstep.views.TaskItemView;
@@ -27,6 +28,7 @@
final class TaskHolder extends ViewHolder {
private final TaskItemView mTaskItemView;
+ private Task mTask;
public TaskHolder(TaskItemView itemView) {
super(itemView);
@@ -40,7 +42,17 @@
* @param task the task to bind to the view
*/
public void bindTask(Task task) {
+ mTask = task;
mTaskItemView.setLabel(task.titleDescription);
mTaskItemView.setIcon(task.icon);
}
+
+ /**
+ * Gets the task currently bound to this view
+ *
+ * @return the current task
+ */
+ public @NonNull Task getTask() {
+ return mTask;
+ }
}
diff --git a/go/quickstep/src/com/android/quickstep/TaskInputController.java b/go/quickstep/src/com/android/quickstep/TaskInputController.java
new file mode 100644
index 0000000..d97ac8d
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/TaskInputController.java
@@ -0,0 +1,55 @@
+/*
+ * 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;
+
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+
+/**
+ * Controller responsible for task logic that occurs on various input to the recents view.
+ */
+public final class TaskInputController {
+
+ private final TaskListLoader mLoader;
+ private final TaskAdapter mAdapter;
+
+ public TaskInputController(TaskListLoader loader,TaskAdapter adapter) {
+ mLoader = loader;
+ mAdapter = adapter;
+ }
+
+ /**
+ * Logic that occurs when a task view is tapped. Launches the respective task.
+ *
+ * @param viewHolder the task view holder that has been tapped
+ */
+ public void onTaskClicked(TaskHolder viewHolder) {
+ // TODO: Add app launch animation as part of the launch options here.
+ ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(viewHolder.getTask().key,
+ null /* options */, null /* resultCallback */, null /* resultCallbackHandler */);
+ }
+
+ public void onTaskSwiped(TaskHolder viewHolder) {
+ int position = viewHolder.getAdapterPosition();
+ Task task = viewHolder.getTask();
+ ActivityManagerWrapper.getInstance().removeTask(task.key.id);
+ mLoader.removeTask(task);
+ mAdapter.notifyItemRemoved(position);
+ }
+
+ // TODO: Implement "Clear all" and notify adapter that data has updated
+
+}
diff --git a/go/quickstep/src/com/android/quickstep/TaskListLoader.java b/go/quickstep/src/com/android/quickstep/TaskListLoader.java
index c798cef..e6d1a22 100644
--- a/go/quickstep/src/com/android/quickstep/TaskListLoader.java
+++ b/go/quickstep/src/com/android/quickstep/TaskListLoader.java
@@ -24,6 +24,7 @@
import java.util.ArrayList;
import java.util.Collections;
+import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
@@ -45,13 +46,13 @@
/**
* Returns the current task list as of the last completed load (see
- * {@link #loadTaskList}). This list of tasks is guaranteed to always have all its task
- * content loaded.
+ * {@link #loadTaskList}) as a read-only list. This list of tasks is guaranteed to always have
+ * all its task content loaded.
*
* @return the current list of tasks w/ all content loaded
*/
- public ArrayList<Task> getCurrentTaskList() {
- return mTaskList;
+ public List<Task> getCurrentTaskList() {
+ return Collections.unmodifiableList(mTaskList);
}
/**
@@ -85,6 +86,13 @@
}
/**
+ * Removes the task from the current task list.
+ */
+ void removeTask(Task task) {
+ mTaskList.remove(task);
+ }
+
+ /**
* Loads task content for a list of tasks, including the label, icon, and thumbnail. For content
* that isn't cached, load the content asynchronously in the background.
*
diff --git a/go/quickstep/src/com/android/quickstep/TaskSwipeCallback.java b/go/quickstep/src/com/android/quickstep/TaskSwipeCallback.java
new file mode 100644
index 0000000..2a53917
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/TaskSwipeCallback.java
@@ -0,0 +1,48 @@
+/*
+ * 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;
+
+import static androidx.recyclerview.widget.ItemTouchHelper.RIGHT;
+
+import androidx.recyclerview.widget.ItemTouchHelper;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.RecyclerView.ViewHolder;
+
+/**
+ * Callback for swipe input on {@link TaskHolder} views in the recents view.
+ */
+public final class TaskSwipeCallback extends ItemTouchHelper.SimpleCallback {
+
+ private final TaskInputController mTaskInputController;
+
+ public TaskSwipeCallback(TaskInputController inputController) {
+ super(0 /* dragDirs */, RIGHT);
+ mTaskInputController = inputController;
+ }
+
+ @Override
+ public boolean onMove(RecyclerView recyclerView, ViewHolder viewHolder,
+ ViewHolder target) {
+ return false;
+ }
+
+ @Override
+ public void onSwiped(ViewHolder viewHolder, int direction) {
+ if (direction == RIGHT) {
+ mTaskInputController.onTaskSwiped((TaskHolder) viewHolder);
+ }
+ }
+}
diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
index afb0540..b7740eb 100644
--- a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
+++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
@@ -23,12 +23,15 @@
import android.view.ViewDebug;
import android.widget.FrameLayout;
+import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.android.launcher3.R;
import com.android.quickstep.TaskAdapter;
+import com.android.quickstep.TaskInputController;
import com.android.quickstep.TaskListLoader;
+import com.android.quickstep.TaskSwipeCallback;
/**
* Root view for the icon recents view. Acts as the main interface to the rest of the Launcher code
@@ -75,26 +78,35 @@
@ViewDebug.ExportedProperty(category = "launcher")
private final Context mContext;
+ private final TaskListLoader mTaskLoader;
+ private final TaskAdapter mTaskAdapter;
+ private final TaskInputController mTaskInputController;
private float mTranslationYFactor;
- private TaskAdapter mTaskAdapter;
private RecyclerView mTaskRecyclerView;
- private TaskListLoader mTaskLoader;
+
public IconRecentsView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
+ mTaskLoader = new TaskListLoader(mContext);
+ mTaskAdapter = new TaskAdapter(mTaskLoader);
+ mTaskInputController = new TaskInputController(mTaskLoader, mTaskAdapter);
+ mTaskAdapter.setInputController(mTaskInputController);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mTaskLoader = new TaskListLoader(mContext);
- mTaskAdapter = new TaskAdapter(mTaskLoader);
- mTaskRecyclerView = findViewById(R.id.recent_task_recycler_view);
- mTaskRecyclerView.setAdapter(mTaskAdapter);
- mTaskRecyclerView.setLayoutManager(
- new LinearLayoutManager(mContext, VERTICAL, true /* reverseLayout */));
+ if (mTaskRecyclerView == null) {
+ mTaskRecyclerView = findViewById(R.id.recent_task_recycler_view);
+ mTaskRecyclerView.setAdapter(mTaskAdapter);
+ mTaskRecyclerView.setLayoutManager(
+ new LinearLayoutManager(mContext, VERTICAL, true /* reverseLayout */));
+ ItemTouchHelper helper = new ItemTouchHelper(
+ new TaskSwipeCallback(mTaskInputController));
+ helper.attachToRecyclerView(mTaskRecyclerView);
+ }
}
/**
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java
index e3afb92..4e010d2 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java
@@ -230,7 +230,6 @@
mTouchSlop) {
mPassedTouchSlop = true;
- TOUCH_INTERACTION_LOG.addLog("startQuickstep");
if (mIsDeferredDownTarget) {
// Deferred gesture, start the animation and gesture tracking once
// we pass the actual touch slop
@@ -272,6 +271,7 @@
}
private void notifyGestureStarted() {
+ TOUCH_INTERACTION_LOG.addLog("startQuickstep");
if (mInteractionHandler == null) {
return;
}
@@ -310,6 +310,7 @@
if (listenerSet != null) {
listenerSet.addListener(handler);
mSwipeSharedState.applyActiveRecentsAnimationState(handler);
+ notifyGestureStarted();
} else {
RecentsAnimationListenerSet newListenerSet =
mSwipeSharedState.newRecentsAnimationListenerSet();
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
index 7dc58a5..7bea593 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -415,6 +415,7 @@
});
mRecentsView.setRecentsAnimationWrapper(mRecentsAnimationWrapper);
mRecentsView.setClipAnimationHelper(mClipAnimationHelper);
+ mRecentsView.setLiveTileOverlay(mLiveTileOverlay);
mActivity.getRootView().getOverlay().add(mLiveTileOverlay);
mStateCallback.setState(STATE_LAUNCHER_PRESENT);
@@ -822,6 +823,7 @@
setShelfState(ShelfAnimState.CANCEL, LINEAR, 0);
duration = Math.max(MIN_OVERSHOOT_DURATION, duration);
} else if (endTarget == RECENTS) {
+ mLiveTileOverlay.startIconAnimation();
mRecentsAnimationWrapper.enableInputProxy();
if (mRecentsView != null) {
duration = Math.max(duration, mRecentsView.getScroller().getDuration());
@@ -1172,7 +1174,7 @@
mActivityControlHelper.onSwipeUpComplete(mActivity);
// Animate the first icon.
- mRecentsView.animateUpRunningTaskIconScale();
+ mRecentsView.animateUpRunningTaskIconScale(mLiveTileOverlay.cancelIconAnimation());
mRecentsView.setSwipeDownShouldLaunchApp(true);
RecentsModel.INSTANCE.get(mContext).onOverviewShown(false, TAG);
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 0aa1beb..19e9cb4 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
@@ -23,6 +23,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.LauncherApps;
+import android.content.pm.LauncherApps.AppUsageLimit;
import android.content.res.Resources;
import android.icu.text.MeasureFormat;
import android.icu.text.MeasureFormat.FormatWidth;
@@ -44,13 +45,13 @@
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.systemui.shared.recents.model.Task;
-import java.lang.reflect.Method;
import java.time.Duration;
import java.util.Locale;
public final class DigitalWellBeingToast extends LinearLayout {
static final Intent OPEN_APP_USAGE_SETTINGS_TEMPLATE = new Intent(ACTION_APP_USAGE_SETTINGS);
static final int MINUTE_MS = 60000;
+ private final LauncherApps mLauncherApps;
public interface InitializeCallback {
void call(float saturation, String contentDescription);
@@ -67,6 +68,7 @@
setLayoutDirection(Utilities.isRtl(getResources()) ?
View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
setOnClickListener((view) -> openAppUsageSettings());
+ mLauncherApps = context.getSystemService(LauncherApps.class);
}
@Override
@@ -87,47 +89,29 @@
}
Utilities.THREAD_POOL_EXECUTOR.execute(() -> {
- long appUsageLimitTimeMs = -1;
- long appRemainingTimeMs = -1;
+ final AppUsageLimit usageLimit = mLauncherApps.getAppUsageLimit(
+ task.getTopComponent().getPackageName(),
+ UserHandle.of(task.key.userId));
- try {
- final Method getAppUsageLimit = LauncherApps.class.getMethod(
- "getAppUsageLimit",
- String.class,
- UserHandle.class);
- final Object usageLimit = getAppUsageLimit.invoke(
- getContext().getSystemService(LauncherApps.class),
- task.getTopComponent().getPackageName(),
- UserHandle.of(task.key.userId));
-
- if (usageLimit != null) {
- final Class appUsageLimitClass = usageLimit.getClass();
- appUsageLimitTimeMs = (long) appUsageLimitClass.getMethod("getTotalUsageLimit").
- invoke(usageLimit);
- appRemainingTimeMs = (long) appUsageLimitClass.getMethod("getUsageRemaining").
- invoke(usageLimit);
- }
- } catch (Exception e) {
- // Do nothing
- }
-
- final long appUsageLimitTimeMsFinal = appUsageLimitTimeMs;
- final long appRemainingTimeMsFinal = appRemainingTimeMs;
+ final long appUsageLimitTimeMs =
+ usageLimit != null ? usageLimit.getTotalUsageLimit() : -1;
+ final long appRemainingTimeMs =
+ usageLimit != null ? usageLimit.getUsageRemaining() : -1;
post(() -> {
- if (appUsageLimitTimeMsFinal < 0) {
+ if (appUsageLimitTimeMs < 0) {
setVisibility(GONE);
} else {
setVisibility(VISIBLE);
- mText.setText(getText(appRemainingTimeMsFinal));
- mImage.setImageResource(appRemainingTimeMsFinal > 0 ?
+ mText.setText(getText(appRemainingTimeMs));
+ mImage.setImageResource(appRemainingTimeMs > 0 ?
R.drawable.hourglass_top : R.drawable.hourglass_bottom);
}
callback.call(
- appUsageLimitTimeMsFinal >= 0 && appRemainingTimeMsFinal <= 0 ? 0 : 1,
+ appUsageLimitTimeMs >= 0 && appRemainingTimeMs <= 0 ? 0 : 1,
getContentDescriptionForTask(
- task, appUsageLimitTimeMsFinal, appRemainingTimeMsFinal));
+ task, appUsageLimitTimeMs, appRemainingTimeMs));
});
});
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LiveTileOverlay.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LiveTileOverlay.java
index ab2b90f..a838797 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LiveTileOverlay.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LiveTileOverlay.java
@@ -1,5 +1,11 @@
package com.android.quickstep.views;
+import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
@@ -9,16 +15,37 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
+import android.util.FloatProperty;
+
+import com.android.launcher3.anim.Interpolators;
public class LiveTileOverlay extends Drawable {
+ private static final long ICON_ANIM_DURATION = 120;
+
+ private static final FloatProperty<LiveTileOverlay> PROGRESS =
+ new FloatProperty<LiveTileOverlay>("progress") {
+ @Override
+ public void setValue(LiveTileOverlay liveTileOverlay, float progress) {
+ liveTileOverlay.setIconAnimationProgress(progress);
+ }
+
+ @Override
+ public Float get(LiveTileOverlay liveTileOverlay) {
+ return liveTileOverlay.mIconAnimationProgress;
+ }
+ };
+
private final Paint mPaint = new Paint();
private Rect mBoundsRect = new Rect();
private RectF mCurrentRect;
private float mCornerRadius;
+ private Drawable mIcon;
+ private Animator mIconAnimator;
private boolean mDrawEnabled = true;
+ private float mIconAnimationProgress = 0f;
public LiveTileOverlay() {
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
@@ -35,6 +62,33 @@
invalidateSelf();
}
+ public void setIcon(Drawable icon) {
+ mIcon = icon;
+ }
+
+ public void startIconAnimation() {
+ if (mIconAnimator != null) {
+ mIconAnimator.cancel();
+ }
+ // This animator must match the icon part of {@link TaskView#FOCUS_TRANSITION} animation.
+ mIconAnimator = ObjectAnimator.ofFloat(this, PROGRESS, 1);
+ mIconAnimator.setDuration(ICON_ANIM_DURATION).setInterpolator(LINEAR);
+ mIconAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mIconAnimator = null;
+ }
+ });
+ mIconAnimator.start();
+ }
+
+ public float cancelIconAnimation() {
+ if (mIconAnimator != null) {
+ mIconAnimator.cancel();
+ }
+ return mIconAnimationProgress;
+ }
+
public void setDrawEnabled(boolean drawEnabled) {
if (mDrawEnabled != drawEnabled) {
mDrawEnabled = drawEnabled;
@@ -46,6 +100,16 @@
public void draw(Canvas canvas) {
if (mCurrentRect != null && mDrawEnabled) {
canvas.drawRoundRect(mCurrentRect, mCornerRadius, mCornerRadius, mPaint);
+ if (mIcon != null && mIconAnimationProgress > 0f) {
+ canvas.save();
+ float scale = Interpolators.clampToProgress(FAST_OUT_SLOW_IN, 0f,
+ 1f).getInterpolation(mIconAnimationProgress);
+ canvas.translate(mCurrentRect.centerX() - mIcon.getBounds().width() / 2 * scale,
+ mCurrentRect.top - mIcon.getBounds().height() / 2 * scale);
+ canvas.scale(scale, scale);
+ mIcon.draw(canvas);
+ canvas.restore();
+ }
}
}
@@ -59,4 +123,9 @@
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
+
+ private void setIconAnimationProgress(float progress) {
+ mIconAnimationProgress = progress;
+ invalidateSelf();
+ }
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index 3e0e8ae..2583ffb 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
@@ -278,6 +278,7 @@
private final int mEmptyMessagePadding;
private boolean mShowEmptyMessage;
private Layout mEmptyTextLayout;
+ private LiveTileOverlay mLiveTileOverlay;
private BaseActivity.MultiWindowModeChangedListener mMultiWindowModeChangedListener =
(inMultiWindowMode) -> {
@@ -855,10 +856,15 @@
}
public void animateUpRunningTaskIconScale() {
+ animateUpRunningTaskIconScale(0);
+ }
+
+ public void animateUpRunningTaskIconScale(float startProgress) {
mRunningTaskIconScaledDown = false;
TaskView firstTask = getRunningTaskView();
if (firstTask != null) {
firstTask.animateIconScaleAndDimIntoView();
+ firstTask.setIconScaleAnimStartProgress(startProgress);
}
}
@@ -1567,6 +1573,14 @@
mClipAnimationHelper = clipAnimationHelper;
}
+ public void setLiveTileOverlay(LiveTileOverlay liveTileOverlay) {
+ mLiveTileOverlay = liveTileOverlay;
+ }
+
+ public void updateLiveTileIcon(Drawable icon) {
+ mLiveTileOverlay.setIcon(icon);
+ }
+
public void finishRecentsAnimation(boolean toRecents, Runnable onFinishComplete) {
if (mRecentsAnimationWrapper == null) {
if (onFinishComplete != null) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
index 419a666..9eec584 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
@@ -156,7 +156,8 @@
private float mZoomScale;
private float mFullscreenProgress;
- private Animator mIconAndDimAnimator;
+ private ObjectAnimator mIconAndDimAnimator;
+ private float mIconScaleAnimStartProgress = 0;
private float mFocusTransitionProgress = 1;
private boolean mShowScreenshot;
@@ -317,6 +318,9 @@
mIconLoadRequest = iconCache.updateIconInBackground(mTask,
(task) -> {
setIcon(task.icon);
+ if (isRunningTask()) {
+ getRecentsView().updateLiveTileIcon(task.icon);
+ }
mDigitalWellBeingToast.initialize(
mTask,
(saturation, contentDescription) -> {
@@ -380,11 +384,16 @@
mIconView.setScaleY(scale);
}
+ public void setIconScaleAnimStartProgress(float startProgress) {
+ mIconScaleAnimStartProgress = startProgress;
+ }
+
public void animateIconScaleAndDimIntoView() {
if (mIconAndDimAnimator != null) {
mIconAndDimAnimator.cancel();
}
mIconAndDimAnimator = ObjectAnimator.ofFloat(this, FOCUS_TRANSITION, 1);
+ mIconAndDimAnimator.setCurrentFraction(mIconScaleAnimStartProgress);
mIconAndDimAnimator.setDuration(DIM_ANIM_DURATION).setInterpolator(LINEAR);
mIconAndDimAnimator.addListener(new AnimatorListenerAdapter() {
@Override
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index d96855e..d75006e 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -276,14 +276,6 @@
}
@Override
- public void setTag(Object tag) {
- if (tag != null) {
- LauncherModel.checkItemInfo((ItemInfo) tag);
- }
- super.setTag(tag);
- }
-
- @Override
public void refreshDrawableState() {
if (!mIgnorePressedStateChange) {
super.refreshDrawableState();
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 3b054c2..7919d29 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -133,6 +133,7 @@
private final Rect mInsets = new Rect();
public final Rect workspacePadding = new Rect();
private final Rect mHotseatPadding = new Rect();
+ // When true, nav bar is on the left side of the screen.
private boolean mIsSeascape;
// Notification dots
@@ -587,28 +588,40 @@
/**
* Gets an item's location on the home screen. This is useful if the home screen
* is animating, otherwise use {@link View#getLocationOnScreen(int[])}.
- *
- * TODO(b/123900446): Handle landscape mode
* @param pageDiff The page difference relative to the current page.
*/
public void getItemLocation(int cellX, int cellY, int spanX, int spanY, int container,
int pageDiff, Rect outBounds) {
outBounds.setEmpty();
- outBounds.left = mInsets.left
- + workspacePadding.left + cellLayoutPaddingLeftRightPx + (cellX * getCellSize().x);
- outBounds.top = mInsets.top;
if (container == CONTAINER_HOTSEAT) {
- outBounds.top += workspacePadding.top
- + (inv.numRows * getCellSize().y)
- + verticalDragHandleSizePx
- - verticalDragHandleOverlapWorkspace;
- outBounds.bottom = outBounds.top + hotseatBarSizePx - hotseatBarBottomPaddingPx;
+ final int actualHotseatCellHeight;
+ if (isVerticalBarLayout()) {
+ actualHotseatCellHeight = availableHeightPx / inv.numRows;
+ if (mIsSeascape) {
+ outBounds.left = mHotseatPadding.left;
+ } else {
+ outBounds.left = availableWidthPx - hotseatBarSizePx + mHotseatPadding.left;
+ }
+ outBounds.right = outBounds.left + iconSizePx;
+ outBounds.top = mHotseatPadding.top
+ + actualHotseatCellHeight * (inv.numRows - cellX - 1);
+ outBounds.bottom = outBounds.top + actualHotseatCellHeight;
+ } else {
+ actualHotseatCellHeight = hotseatBarSizePx - hotseatBarBottomPaddingPx
+ - hotseatBarTopPaddingPx;
+ outBounds.left = mInsets.left + workspacePadding.left + cellLayoutPaddingLeftRightPx
+ + (cellX * getCellSize().x);
+ outBounds.right = outBounds.left + getCellSize().x;
+ outBounds.top = mInsets.top + availableHeightPx - hotseatBarSizePx;
+ outBounds.bottom = outBounds.top + actualHotseatCellHeight;
+ }
} else {
- outBounds.top += workspacePadding.top + (cellY * getCellSize().y);
+ outBounds.left = mInsets.left + workspacePadding.left + cellLayoutPaddingLeftRightPx
+ + (cellX * getCellSize().x) + (pageDiff * availableWidthPx);
+ outBounds.right = outBounds.left + (getCellSize().x * spanX);
+ outBounds.top = mInsets.top + workspacePadding.top + (cellY * getCellSize().y);
outBounds.bottom = outBounds.top + (getCellSize().y * spanY);
- outBounds.left += (pageDiff) * availableWidthPx;
}
- outBounds.right = outBounds.left + (getCellSize().x * spanX);
}
public float getAspectRatioWithInsets() {
diff --git a/src/com/android/launcher3/ItemInfo.java b/src/com/android/launcher3/ItemInfo.java
index a130604..134e116 100644
--- a/src/com/android/launcher3/ItemInfo.java
+++ b/src/com/android/launcher3/ItemInfo.java
@@ -114,8 +114,6 @@
ItemInfo(ItemInfo info) {
copyFrom(info);
- // tempdebug:
- LauncherModel.checkItemInfo(this);
}
public void copyFrom(ItemInfo info) {
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 61cd2f1..99343aa 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -378,14 +378,7 @@
}
if ((diff & (CONFIG_ORIENTATION | CONFIG_SCREEN_SIZE)) != 0) {
- mUserEventDispatcher = null;
- initDeviceProfile(mDeviceProfile.inv);
- dispatchDeviceProfileChanged();
- reapplyUi();
- mDragLayer.recreateControllers();
-
- // TODO: We can probably avoid rebind when only screen size changed.
- rebindModel();
+ onIdpChanged(mDeviceProfile.inv);
}
mOldConfig.setTo(newConfig);
@@ -410,8 +403,19 @@
@Override
public void onIdpChanged(int changeFlags, InvariantDeviceProfile idp) {
+ onIdpChanged(idp);
+ }
+
+ private void onIdpChanged(InvariantDeviceProfile idp) {
+ mUserEventDispatcher = null;
+
initDeviceProfile(idp);
- getRootView().dispatchInsets();
+ dispatchDeviceProfileChanged();
+ reapplyUi();
+ mDragLayer.recreateControllers();
+
+ // TODO: We can probably avoid rebind when only screen size changed.
+ rebindModel();
}
private void initDeviceProfile(InvariantDeviceProfile idp) {
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index b1664f1..c559f2b 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -34,7 +34,6 @@
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.model.AddWorkspaceItemsTask;
@@ -207,57 +206,6 @@
hasVerticalHotseat, verifyChanges);
}
- static void checkItemInfoLocked(
- final int itemId, final ItemInfo item, StackTraceElement[] stackTrace) {
- ItemInfo modelItem = sBgDataModel.itemsIdMap.get(itemId);
- if (modelItem != null && item != modelItem) {
- // If it is a release build on a release device, check all the data is consistent as
- // we don't want to crash non-dev users.
- if (!Utilities.IS_DEBUG_DEVICE && !FeatureFlags.IS_DOGFOOD_BUILD &&
- modelItem instanceof ShortcutInfo && item instanceof ShortcutInfo) {
- if (modelItem.title.toString().equals(item.title.toString()) &&
- modelItem.getIntent().filterEquals(item.getIntent()) &&
- modelItem.id == item.id &&
- modelItem.itemType == item.itemType &&
- modelItem.container == item.container &&
- modelItem.screenId == item.screenId &&
- modelItem.cellX == item.cellX &&
- modelItem.cellY == item.cellY &&
- modelItem.spanX == item.spanX &&
- modelItem.spanY == item.spanY) {
- // For all intents and purposes, this is the same object
- return;
- }
- }
-
- // the modelItem needs to match up perfectly with item if our model is
- // to be consistent with the database-- for now, just require
- // modelItem == item or the equality check above
- String msg = "item: " + ((item != null) ? item.toString() : "null") +
- "modelItem: " +
- ((modelItem != null) ? modelItem.toString() : "null") +
- "Error: ItemInfo passed to checkItemInfo doesn't match original";
- RuntimeException e = new RuntimeException(msg);
- if (stackTrace != null) {
- e.setStackTrace(stackTrace);
- }
- throw e;
- }
- }
-
- static void checkItemInfo(final ItemInfo item) {
- final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
- final int itemId = item.id;
- Runnable r = new Runnable() {
- public void run() {
- synchronized (sBgDataModel) {
- checkItemInfoLocked(itemId, item, stackTrace);
- }
- }
- };
- runOnWorkerThread(r);
- }
-
/**
* Set this as the current Launcher activity object for the loader.
*/
diff --git a/src/com/android/launcher3/model/BaseLoaderResults.java b/src/com/android/launcher3/model/BaseLoaderResults.java
index 210f744..97cf267 100644
--- a/src/com/android/launcher3/model/BaseLoaderResults.java
+++ b/src/com/android/launcher3/model/BaseLoaderResults.java
@@ -61,6 +61,8 @@
protected final WeakReference<Callbacks> mCallbacks;
+ private int mMyBindingId;
+
public BaseLoaderResults(LauncherAppState app, BgDataModel dataModel,
AllAppsList allAppsList, int pageToBindFirst, WeakReference<Callbacks> callbacks) {
mUiExecutor = new MainThreadExecutor();
@@ -94,6 +96,7 @@
appWidgets.addAll(mBgDataModel.appWidgets);
orderedScreenIds.addAll(mBgDataModel.collectWorkspaceScreens());
mBgDataModel.lastBindId++;
+ mMyBindingId = mBgDataModel.lastBindId;
}
final int currentScreen;
@@ -285,6 +288,10 @@
protected void executeCallbacksTask(CallbackTask task, Executor executor) {
executor.execute(() -> {
+ if (mMyBindingId != mBgDataModel.lastBindId) {
+ Log.d(TAG, "Too many consecutive reloads, skipping obsolete data-bind");
+ return;
+ }
Callbacks callbacks = mCallbacks.get();
if (callbacks != null) {
task.execute(callbacks);
diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java
index ac5076c..daf99e9 100644
--- a/src/com/android/launcher3/model/ModelWriter.java
+++ b/src/com/android/launcher3/model/ModelWriter.java
@@ -37,6 +37,7 @@
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.LauncherSettings.Settings;
import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.ContentWriter;
import com.android.launcher3.util.ItemInfoMatcher;
@@ -112,19 +113,18 @@
ItemInfo modelItem = mBgDataModel.itemsIdMap.get(itemId);
if (modelItem != null && item != modelItem) {
// check all the data is consistent
- if (modelItem instanceof ShortcutInfo && item instanceof ShortcutInfo) {
- ShortcutInfo modelShortcut = (ShortcutInfo) modelItem;
- ShortcutInfo shortcut = (ShortcutInfo) item;
- if (modelShortcut.title.toString().equals(shortcut.title.toString()) &&
- modelShortcut.intent.filterEquals(shortcut.intent) &&
- modelShortcut.id == shortcut.id &&
- modelShortcut.itemType == shortcut.itemType &&
- modelShortcut.container == shortcut.container &&
- modelShortcut.screenId == shortcut.screenId &&
- modelShortcut.cellX == shortcut.cellX &&
- modelShortcut.cellY == shortcut.cellY &&
- modelShortcut.spanX == shortcut.spanX &&
- modelShortcut.spanY == shortcut.spanY) {
+ if (!Utilities.IS_DEBUG_DEVICE && !FeatureFlags.IS_DOGFOOD_BUILD &&
+ modelItem instanceof ShortcutInfo && item instanceof ShortcutInfo) {
+ if (modelItem.title.toString().equals(item.title.toString()) &&
+ modelItem.getIntent().filterEquals(item.getIntent()) &&
+ modelItem.id == item.id &&
+ modelItem.itemType == item.itemType &&
+ modelItem.container == item.container &&
+ modelItem.screenId == item.screenId &&
+ modelItem.cellX == item.cellX &&
+ modelItem.cellY == item.cellY &&
+ modelItem.spanX == item.spanX &&
+ modelItem.spanY == item.spanY) {
// For all intents and purposes, this is the same object
return;
}
@@ -310,7 +310,7 @@
/**
* Delete operations tracked using {@link #enqueueDeleteRunnable} will only be called
* if {@link #commitDelete} is called. Note that one of {@link #commitDelete()} or
- * {@link #abortDelete()} MUST be called after this method, or else all delete
+ * {@link #abortDelete} MUST be called after this method, or else all delete
* operations will remain uncommitted indefinitely.
*/
public void prepareToUndoDelete() {
@@ -325,7 +325,7 @@
/**
* If {@link #prepareToUndoDelete} has been called, we store the Runnable to be run when
- * {@link #commitDelete()} is called (or abandoned if {@link #abortDelete()} is called).
+ * {@link #commitDelete()} is called (or abandoned if {@link #abortDelete} is called).
* Otherwise, we run the Runnable immediately.
*/
private void enqueueDeleteRunnable(Runnable r) {
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index ec0462b..7af2bd8 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -34,6 +34,7 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
+import android.widget.ImageView;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.InsettableFrameLayout.LayoutParams;
@@ -48,6 +49,7 @@
import com.android.launcher3.folder.FolderShape;
import com.android.launcher3.graphics.ShiftedBitmapDrawable;
import com.android.launcher3.icons.LauncherIcons;
+import com.android.launcher3.popup.SystemShortcut;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
@@ -65,7 +67,6 @@
private Runnable mStartRunnable;
private Runnable mEndRunnable;
- private Drawable mDrawable;
private int mOriginalHeight;
private final int mBlurSizeOutline;
@@ -189,70 +190,79 @@
private void getIcon(Launcher launcher, View v, ItemInfo info, boolean useDrawableAsIs,
float aspectRatio) {
final LayoutParams lp = (LayoutParams) getLayoutParams();
-
+ Drawable drawable = null;
boolean supportsAdaptiveIcons = ADAPTIVE_ICON_WINDOW_ANIM.get() && !useDrawableAsIs
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
if (!supportsAdaptiveIcons && v instanceof BubbleTextView) {
// Similar to DragView, we simply use the BubbleTextView icon here.
- mDrawable = ((BubbleTextView) v).getIcon();
+ drawable = ((BubbleTextView) v).getIcon();
}
- if (mDrawable == null) {
- mDrawable = Utilities.getFullDrawable(launcher, info, lp.width, lp.height,
+ if (v instanceof ImageView && info instanceof SystemShortcut) {
+ drawable = ((ImageView) v).getDrawable();
+ }
+ if (drawable == null) {
+ drawable = Utilities.getFullDrawable(launcher, info, lp.width, lp.height,
useDrawableAsIs, new Object[1]);
}
- if (supportsAdaptiveIcons && mDrawable instanceof AdaptiveIconDrawable) {
- mIsAdaptiveIcon = true;
-
- AdaptiveIconDrawable adaptiveIcon = (AdaptiveIconDrawable) mDrawable;
- Drawable background = adaptiveIcon.getBackground();
- if (background == null) {
- background = new ColorDrawable(Color.TRANSPARENT);
- }
- mBackground = background;
- Drawable foreground = adaptiveIcon.getForeground();
- if (foreground == null) {
- foreground = new ColorDrawable(Color.TRANSPARENT);
- }
- mForeground = foreground;
-
- int offset = getOffsetForAdaptiveIconBounds();
- mFinalDrawableBounds.set(offset, offset, lp.width - offset, mOriginalHeight - offset);
- if (mForeground instanceof ShiftedBitmapDrawable && v instanceof FolderIcon) {
- ShiftedBitmapDrawable sbd = (ShiftedBitmapDrawable) mForeground;
- ((FolderIcon) v).getPreviewBounds(sTmpRect);
- sbd.setShiftX(sbd.getShiftX() - sTmpRect.left);
- sbd.setShiftY(sbd.getShiftY() - sTmpRect.top);
- }
- mForeground.setBounds(mFinalDrawableBounds);
- mBackground.setBounds(mFinalDrawableBounds);
-
- int blurMargin = mBlurSizeOutline / 2;
- mStartRevealRect.set(blurMargin, blurMargin , lp.width - blurMargin,
- mOriginalHeight - blurMargin);
-
- if (aspectRatio > 0) {
- lp.height = (int) Math.max(lp.height, lp.width * aspectRatio);
- layout(lp.leftMargin, lp.topMargin, lp.leftMargin + lp.width, lp.topMargin
- + lp.height);
- }
- mBgDrawableStartScale = (float) lp.height / mOriginalHeight;
- setBackgroundDrawableBounds(mBgDrawableStartScale);
-
- // Set up outline
- mOutline.set(0, 0, lp.width, lp.height);
- setOutlineProvider(new ViewOutlineProvider() {
- @Override
- public void getOutline(View view, Outline outline) {
- outline.setRoundRect(mOutline, mTaskCornerRadius);
- }
- });
- setClipToOutline(true);
- } else {
- setBackground(mDrawable);
- }
+ Drawable finalDrawable = drawable == null ? null
+ : drawable.getConstantState().newDrawable();
+ boolean isAdaptiveIcon = supportsAdaptiveIcons
+ && finalDrawable instanceof AdaptiveIconDrawable;
+ int iconOffset = getOffsetForIconBounds(finalDrawable);
new Handler(Looper.getMainLooper()).post(() -> {
+ if (isAdaptiveIcon) {
+ mIsAdaptiveIcon = true;
+
+ AdaptiveIconDrawable adaptiveIcon = (AdaptiveIconDrawable) finalDrawable;
+ Drawable background = adaptiveIcon.getBackground();
+ if (background == null) {
+ background = new ColorDrawable(Color.TRANSPARENT);
+ }
+ mBackground = background;
+ Drawable foreground = adaptiveIcon.getForeground();
+ if (foreground == null) {
+ foreground = new ColorDrawable(Color.TRANSPARENT);
+ }
+ mForeground = foreground;
+
+ mFinalDrawableBounds.set(iconOffset, iconOffset, lp.width -
+ iconOffset, mOriginalHeight - iconOffset);
+ if (mForeground instanceof ShiftedBitmapDrawable && v instanceof FolderIcon) {
+ ShiftedBitmapDrawable sbd = (ShiftedBitmapDrawable) mForeground;
+ ((FolderIcon) v).getPreviewBounds(sTmpRect);
+ sbd.setShiftX(sbd.getShiftX() - sTmpRect.left);
+ sbd.setShiftY(sbd.getShiftY() - sTmpRect.top);
+ }
+ mForeground.setBounds(mFinalDrawableBounds);
+ mBackground.setBounds(mFinalDrawableBounds);
+
+ int blurMargin = mBlurSizeOutline / 2;
+ mStartRevealRect.set(blurMargin, blurMargin , lp.width - blurMargin,
+ mOriginalHeight - blurMargin);
+
+ if (aspectRatio > 0) {
+ lp.height = (int) Math.max(lp.height, lp.width * aspectRatio);
+ layout(lp.leftMargin, lp.topMargin, lp.leftMargin + lp.width, lp.topMargin
+ + lp.height);
+ }
+ mBgDrawableStartScale = (float) lp.height / mOriginalHeight;
+ setBackgroundDrawableBounds(mBgDrawableStartScale);
+
+ // Set up outline
+ mOutline.set(0, 0, lp.width, lp.height);
+ setOutlineProvider(new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ outline.setRoundRect(mOutline, mTaskCornerRadius);
+ }
+ });
+ setClipToOutline(true);
+ } else {
+ setBackground(finalDrawable);
+ }
+
invalidate();
invalidateOutline();
});
@@ -267,9 +277,10 @@
mBackground.setBounds(mBgDrawableBounds);
}
- private int getOffsetForAdaptiveIconBounds() {
+ @WorkerThread
+ private int getOffsetForIconBounds(Drawable drawable) {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.O ||
- !(mDrawable instanceof AdaptiveIconDrawable)) {
+ !(drawable instanceof AdaptiveIconDrawable)) {
return 0;
}
@@ -278,7 +289,7 @@
bounds.inset(mBlurSizeOutline / 2, mBlurSizeOutline / 2);
try (LauncherIcons li = LauncherIcons.obtain(Launcher.fromContext(getContext()))) {
- Utilities.scaleRectAboutCenter(bounds, li.getNormalizer().getScale(mDrawable, null));
+ Utilities.scaleRectAboutCenter(bounds, li.getNormalizer().getScale(drawable, null));
}
bounds.inset(