Merge "Update apps icon in prediction apps row when apps are updated" into ub-launcher3-master
diff --git a/quickstep/res/layout/overview_panel.xml b/quickstep/res/layout/overview_panel.xml
index 78238fa..9f4f8a1 100644
--- a/quickstep/res/layout/overview_panel.xml
+++ b/quickstep/res/layout/overview_panel.xml
@@ -26,32 +26,6 @@
<com.android.launcher3.uioverrides.WorkspaceCard
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingTop="@dimen/task_thumbnail_top_margin" >
-
- <View
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:id="@+id/workspace_click_target" />
-
- <FrameLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:id="@+id/widget_button"
- android:background="@drawable/bg_workspace_card_button" >
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/widget_button_text"
- android:drawableStart="@drawable/ic_widget"
- android:textColor="?attr/workspaceTextColor"
- android:drawableTint="?attr/workspaceTextColor"
- android:gravity="center"
- android:layout_gravity="center"
- android:drawablePadding="20dp" />
- </FrameLayout>
-
- </com.android.launcher3.uioverrides.WorkspaceCard>
+ android:layout_height="match_parent" />
</com.android.quickstep.RecentsView>
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/uioverrides/EdgeSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/EdgeSwipeController.java
index c1b26d4..356a144 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/EdgeSwipeController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/EdgeSwipeController.java
@@ -29,9 +29,6 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.anim.SpringAnimationHandler;
import com.android.launcher3.dragndrop.DragLayer;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.util.VerticalSwipeController;
import com.android.quickstep.RecentsView;
@@ -40,7 +37,7 @@
*/
public class EdgeSwipeController extends VerticalSwipeController {
- private final Rect mTempRect = new Rect();
+ private static final Rect sTempRect = new Rect();
public EdgeSwipeController(Launcher l) {
super(l, NORMAL, OVERVIEW, l.getDeviceProfile().isVerticalBarLayout()
@@ -78,18 +75,22 @@
@Override
protected float getShiftRange() {
- RecentsView.getPageRect(mLauncher, mTempRect);
- DragLayer dl = mLauncher.getDragLayer();
+ return getShiftRange(mLauncher);
+ }
+
+ public static float getShiftRange(Launcher launcher) {
+ RecentsView.getPageRect(launcher.getDeviceProfile(), launcher, sTempRect);
+ DragLayer dl = launcher.getDragLayer();
Rect insets = dl.getInsets();
- if (mLauncher.getDeviceProfile().isVerticalBarLayout()) {
+ if (launcher.getDeviceProfile().isVerticalBarLayout()) {
if (insets.left > insets.right) {
- return insets.left + mTempRect.left;
+ return insets.left + sTempRect.left;
} else {
- return dl.getWidth() - mTempRect.right + insets.right;
+ return dl.getWidth() - sTempRect.right + insets.right;
}
} else {
- return dl.getHeight() - mTempRect.bottom + insets.bottom;
+ return dl.getHeight() - sTempRect.bottom + insets.bottom;
}
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
index 2e5e75e..d2057cf 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
@@ -36,8 +36,6 @@
*/
public class OverviewState extends LauncherState {
- public static final float WORKSPACE_SCALE_ON_SCROLL = 0.9f;
-
private static final int STATE_FLAGS = FLAG_SHOW_SCRIM | FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED;
public OverviewState(int id) {
@@ -47,15 +45,15 @@
@Override
public float[] getWorkspaceScaleAndTranslation(Launcher launcher) {
Rect pageRect = new Rect();
- RecentsView.getPageRect(launcher, pageRect);
+ RecentsView.getScaledDownPageRect(launcher.getDeviceProfile(), launcher, pageRect);
+ RecentsView rv = launcher.getOverviewPanel();
+
if (launcher.getWorkspace().getNormalChildWidth() <= 0 || pageRect.isEmpty()) {
return super.getWorkspaceScaleAndTranslation(launcher);
}
- RecentsView rv = launcher.getOverviewPanel();
float overlap = 0;
if (rv.getCurrentPage() >= rv.getFirstTaskIndex()) {
- Utilities.scaleRectAboutCenter(pageRect, WORKSPACE_SCALE_ON_SCROLL);
overlap = launcher.getResources().getDimension(R.dimen.workspace_overview_offset_x);
}
return getScaleAndTranslationForPageRect(launcher, overlap, pageRect);
@@ -99,21 +97,23 @@
float childWidth = ws.getNormalChildWidth();
float childHeight = ws.getNormalChildHeight();
+ float scale = pageRect.height() / childHeight;
Rect insets = launcher.getDragLayer().getInsets();
- float scale = Math.min(pageRect.width() / childWidth, pageRect.height() / childHeight);
float halfHeight = ws.getHeight() / 2;
float childTop = halfHeight - scale * (halfHeight - ws.getPaddingTop() - insets.top);
float translationY = pageRect.top - childTop;
+ // Align the workspace horizontally centered with the task rect
float halfWidth = ws.getWidth() / 2;
- float translationX;
+ float childCenter = halfWidth -
+ scale * (halfWidth - ws.getPaddingLeft() - insets.left - childWidth / 2);
+ float translationX = pageRect.exactCenterX() - childCenter;
+
if (Utilities.isRtl(launcher.getResources())) {
- float childRight = halfWidth + scale * (halfWidth - ws.getPaddingRight() - insets.right);
- translationX = childRight - pageRect.right - offsetX / scale;
+ translationX -= offsetX / scale;
} else {
- float childLeft = halfWidth - scale * (halfWidth - ws.getPaddingLeft() - insets.left);
- translationX = pageRect.left - childLeft + offsetX / scale;
+ translationX += offsetX / scale;
}
return new float[] {scale, translationX, translationY};
diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeController.java
new file mode 100644
index 0000000..1fd541a
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeController.java
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2018 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.launcher3.uioverrides;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.touch.SwipeDetector;
+import com.android.launcher3.util.TouchController;
+import com.android.quickstep.RecentsView;
+import com.android.quickstep.TaskView;
+
+import static com.android.launcher3.LauncherState.ALL_APPS;
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.anim.Interpolators.DEACCEL_1_5;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
+
+/**
+ * Touch controller for swipe interaction in Overview state
+ */
+public class OverviewSwipeController extends AnimatorListenerAdapter
+ implements TouchController, SwipeDetector.Listener {
+
+ private static final String TAG = "OverviewSwipeController";
+
+ private static final float ALLOWED_FLING_DIRECTION_CHANGE_PROGRESS = 0.1f;
+ private static final int SINGLE_FRAME_MS = 16;
+
+ // Progress after which the transition is assumed to be a success in case user does not fling
+ private static final float SUCCESS_TRANSITION_PROGRESS = 0.5f;
+
+ private final Launcher mLauncher;
+ private final SwipeDetector mDetector;
+ private final RecentsView mRecentsView;
+ private final int[] mTempCords = new int[2];
+
+ private AnimatorPlaybackController mCurrentAnimation;
+ private boolean mCurrentAnimationIsGoingUp;
+
+ private boolean mNoIntercept;
+ private boolean mSwipeDownEnabled;
+
+ private float mDisplacementShift;
+ private float mProgressMultiplier;
+ private float mEndDisplacement;
+
+ private TaskView mTaskBeingDragged;
+
+ public OverviewSwipeController(Launcher launcher) {
+ mLauncher = launcher;
+ mRecentsView = launcher.getOverviewPanel();
+ mDetector = new SwipeDetector(launcher, this, SwipeDetector.VERTICAL);
+ }
+
+ private boolean canInterceptTouch() {
+ if (mCurrentAnimation != null) {
+ // If we are already animating from a previous state, we can intercept.
+ return true;
+ }
+ if (AbstractFloatingView.getTopOpenView(mLauncher) != null) {
+ return false;
+ }
+ return mLauncher.isInState(OVERVIEW);
+ }
+
+ private boolean isEventOverHotseat(MotionEvent ev) {
+ if (mLauncher.getDeviceProfile().isVerticalBarLayout()) {
+ return ev.getY() >
+ mLauncher.getDragLayer().getHeight() * OVERVIEW.getVerticalProgress(mLauncher);
+ } else {
+ return mLauncher.getDragLayer().isEventOverHotseat(ev);
+ }
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ if (mCurrentAnimation != null && animation == mCurrentAnimation.getTarget()) {
+ Log.e(TAG, "Who dare cancel the animation when I am in control", new Exception());
+ mDetector.finishedScrolling();
+ mCurrentAnimation = null;
+ }
+ }
+
+ @Override
+ public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ mNoIntercept = !canInterceptTouch();
+ if (mNoIntercept) {
+ return false;
+ }
+
+ // Now figure out which direction scroll events the controller will start
+ // calling the callbacks.
+ final int directionsToDetectScroll;
+ boolean ignoreSlopWhenSettling = false;
+
+ if (mCurrentAnimation != null) {
+ directionsToDetectScroll = SwipeDetector.DIRECTION_BOTH;
+ ignoreSlopWhenSettling = true;
+ } else {
+ mTaskBeingDragged = null;
+ mSwipeDownEnabled = true;
+
+ int currentPage = mRecentsView.getCurrentPage();
+ if (currentPage == 0) {
+ // User is on home tile
+ directionsToDetectScroll = SwipeDetector.DIRECTION_BOTH;
+ } else {
+ View view = mRecentsView.getChildAt(currentPage);
+ if (mLauncher.getDragLayer().isEventOverView(view, ev) &&
+ view instanceof TaskView) {
+ // The tile can be dragged down to open the task.
+ mTaskBeingDragged = (TaskView) view;
+ directionsToDetectScroll = SwipeDetector.DIRECTION_BOTH;
+ } else if (isEventOverHotseat(ev)) {
+ // The hotseat is being dragged
+ directionsToDetectScroll = SwipeDetector.DIRECTION_POSITIVE;
+ mSwipeDownEnabled = false;
+ } else {
+ mNoIntercept = true;
+ return false;
+ }
+ }
+ }
+
+ mDetector.setDetectableScrollConditions(
+ directionsToDetectScroll, ignoreSlopWhenSettling);
+ }
+
+ if (mNoIntercept) {
+ return false;
+ }
+
+ onControllerTouchEvent(ev);
+ return mDetector.isDraggingOrSettling();
+ }
+
+ @Override
+ public boolean onControllerTouchEvent(MotionEvent ev) {
+ return mDetector.onTouchEvent(ev);
+ }
+
+ private void reInitAnimationController(boolean goingUp) {
+ if (!goingUp && !mSwipeDownEnabled) {
+ goingUp = true;
+ }
+ if (mCurrentAnimation != null && mCurrentAnimationIsGoingUp == goingUp) {
+ // No need to init
+ return;
+ }
+ if (mCurrentAnimation != null) {
+ mCurrentAnimation.setPlayFraction(0);
+ }
+ mCurrentAnimationIsGoingUp = goingUp;
+ float range = mLauncher.getAllAppsController().getShiftRange();
+ long maxDuration = (long) (2 * range);
+ DragLayer dl = mLauncher.getDragLayer();
+
+ if (mTaskBeingDragged == null) {
+ // User is either going to all apps or home
+ mCurrentAnimation = mLauncher.getStateManager()
+ .createAnimationToNewWorkspace(goingUp ? ALL_APPS : NORMAL, maxDuration);
+ if (goingUp) {
+ mEndDisplacement = -range;
+ } else {
+ mEndDisplacement = EdgeSwipeController.getShiftRange(mLauncher);
+ }
+ } else {
+ if (goingUp) {
+ AnimatorSet anim = new AnimatorSet();
+ ObjectAnimator translate = ObjectAnimator.ofFloat(
+ mTaskBeingDragged, View.TRANSLATION_Y, -mTaskBeingDragged.getBottom());
+ translate.setInterpolator(LINEAR);
+ translate.setDuration(maxDuration);
+ anim.play(translate);
+
+ ObjectAnimator alpha = ObjectAnimator.ofFloat(mTaskBeingDragged, View.ALPHA, 0);
+ alpha.setInterpolator(DEACCEL_1_5);
+ alpha.setDuration(maxDuration);
+ anim.play(alpha);
+ mCurrentAnimation = AnimatorPlaybackController.wrap(anim, maxDuration);
+ mEndDisplacement = -mTaskBeingDragged.getBottom();
+ } else {
+ AnimatorSet anim = new AnimatorSet();
+ // TODO: Setup a zoom animation
+ mCurrentAnimation = AnimatorPlaybackController.wrap(anim, maxDuration);
+
+ mTempCords[1] = mTaskBeingDragged.getHeight();
+ dl.getDescendantCoordRelativeToSelf(mTaskBeingDragged, mTempCords);
+ mEndDisplacement = dl.getHeight() - mTempCords[1];
+ }
+ }
+
+ mCurrentAnimation.getTarget().addListener(this);
+ mCurrentAnimation.dispatchOnStart();
+ mProgressMultiplier = 1 / mEndDisplacement;
+ }
+
+ @Override
+ public void onDragStart(boolean start) {
+ if (mCurrentAnimation == null) {
+ reInitAnimationController(mDetector.wasInitialTouchPositive());
+ mDisplacementShift = 0;
+ } else {
+ mDisplacementShift = mCurrentAnimation.getProgressFraction() / mProgressMultiplier;
+ mCurrentAnimation.pause();
+ }
+ }
+
+ @Override
+ public boolean onDrag(float displacement, float velocity) {
+ float totalDisplacement = displacement + mDisplacementShift;
+ boolean isGoingUp =
+ totalDisplacement == 0 ? mCurrentAnimationIsGoingUp : totalDisplacement < 0;
+ if (isGoingUp != mCurrentAnimationIsGoingUp) {
+ reInitAnimationController(isGoingUp);
+ }
+ mCurrentAnimation.setPlayFraction(totalDisplacement * mProgressMultiplier);
+ return true;
+ }
+
+ @Override
+ public void onDragEnd(float velocity, boolean fling) {
+ final boolean goingToEnd;
+
+ if (fling) {
+ boolean goingUp = velocity < 0;
+ if (!goingUp && !mSwipeDownEnabled) {
+ goingToEnd = false;
+ } else if (goingUp != mCurrentAnimationIsGoingUp) {
+ // In case the fling is in opposite direction, make sure if is close enough
+ // from the start position
+ if (mCurrentAnimation.getProgressFraction()
+ >= ALLOWED_FLING_DIRECTION_CHANGE_PROGRESS) {
+ // Not allowed
+ goingToEnd = false;
+ } else {
+ reInitAnimationController(goingUp);
+ goingToEnd = true;
+ }
+ } else {
+ goingToEnd = true;
+ }
+ } else {
+ goingToEnd = mCurrentAnimation.getProgressFraction() > SUCCESS_TRANSITION_PROGRESS;
+ }
+
+ float progress = mCurrentAnimation.getProgressFraction();
+ long animationDuration = SwipeDetector.calculateDuration(
+ velocity, goingToEnd ? (1 - progress) : progress);
+
+ float nextFrameProgress = Utilities.boundToRange(
+ progress + velocity * SINGLE_FRAME_MS / Math.abs(mEndDisplacement), 0f, 1f);
+
+
+ mCurrentAnimation.setEndAction(() -> onCurrentAnimationEnd(goingToEnd));
+
+ ValueAnimator anim = mCurrentAnimation.getAnimationPlayer();
+ anim.setFloatValues(nextFrameProgress, goingToEnd ? 1f : 0f);
+ anim.setDuration(animationDuration);
+ anim.setInterpolator(scrollInterpolatorForVelocity(velocity));
+ anim.start();
+ }
+
+ private void onCurrentAnimationEnd(boolean wasSuccess) {
+ // TODO: Might be a good time to log something.
+ if (mTaskBeingDragged == null) {
+ LauncherState state = wasSuccess ?
+ (mCurrentAnimationIsGoingUp ? ALL_APPS : NORMAL) : OVERVIEW;
+ mLauncher.getStateManager().goToState(state, false);
+ } else if (wasSuccess) {
+ if (mCurrentAnimationIsGoingUp) {
+ mRecentsView.onTaskDismissed(mTaskBeingDragged);
+ } else {
+ mTaskBeingDragged.launchTask(false);
+ }
+ }
+ mDetector.finishedScrolling();
+ mTaskBeingDragged = null;
+ mCurrentAnimation = null;
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java
index 2481c32..fb59946 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java
@@ -90,7 +90,6 @@
private static final int FLAG_OVERVIEW_DISABLED_OUT_OF_RANGE = 1 << 0;
private static final int FLAG_OVERVIEW_DISABLED_FLING = 1 << 1;
private static final int FLAG_OVERVIEW_DISABLED_CANCEL_STATE = 1 << 2;
- private static final int FLAG_RECENTS_PLAN_LOADING = 1 << 3;
private static final int FLAG_OVERVIEW_DISABLED = 1 << 4;
private static final int FLAG_DISABLED_TWO_TARGETS = 1 << 5;
private static final int FLAG_DISABLED_BACK_TARGET = 1 << 6;
@@ -272,16 +271,7 @@
mCurrentAnimation = mLauncher.getStateManager().createAnimationToNewWorkspace(
mToState, mTaggedAnimatorSetBuilder, maxAccuracy);
- if (TouchInteractionService.isConnected()) {
- // Load recents plan
- RecentsModel recentsModel = RecentsModel.getInstance(mLauncher);
- if (recentsModel.getLastLoadPlan() != null) {
- onRecentsPlanLoaded(recentsModel.getLastLoadPlan());
- } else {
- mDragPauseDetector.addDisabledFlags(FLAG_RECENTS_PLAN_LOADING);
- recentsModel.loadTasks(-1, this::onRecentsPlanLoaded);
- }
- } else {
+ if (!TouchInteractionService.isConnected()) {
mDragPauseDetector.addDisabledFlags(FLAG_OVERVIEW_DISABLED);
}
@@ -302,14 +292,6 @@
}
}
- private void onRecentsPlanLoaded(RecentsTaskLoadPlan plan) {
- RecentsView recentsView = mLauncher.getOverviewPanel();
- recentsView.update(plan);
- recentsView.initToPage(0);
-
- mDragPauseDetector.clearDisabledFlags(FLAG_RECENTS_PLAN_LOADING);
- }
-
private float getShiftRange() {
return mLauncher.getAllAppsController().getShiftRange();
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
index bd443aa..e848688 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
@@ -30,6 +30,7 @@
import com.android.launcher3.graphics.BitmapRenderer;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.widget.WidgetsFullSheet;
+import com.android.quickstep.RecentsView;
import com.android.systemui.shared.recents.view.RecentsTransition;
public class UiFactory {
@@ -41,11 +42,11 @@
return new TouchController[]{
new EdgeSwipeController(launcher),
new TwoStepSwipeController(launcher),
- new OverviewSwipeUpController(launcher)};
+ new OverviewSwipeController(launcher)};
} else {
return new TouchController[]{
new TwoStepSwipeController(launcher),
- new OverviewSwipeUpController(launcher)};
+ new OverviewSwipeController(launcher)};
}
}
@@ -96,4 +97,9 @@
return result;
}
}
+
+ public static void resetOverview(Launcher launcher) {
+ RecentsView recents = launcher.getOverviewPanel();
+ recents.reset();
+ }
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/WorkspaceCard.java b/quickstep/src/com/android/launcher3/uioverrides/WorkspaceCard.java
index 4816e2a..92a09dd 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/WorkspaceCard.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/WorkspaceCard.java
@@ -16,170 +16,80 @@
package com.android.launcher3.uioverrides;
import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.uioverrides.OverviewState.WORKSPACE_SCALE_ON_SCROLL;
import static com.android.quickstep.RecentsView.SCROLL_TYPE_WORKSPACE;
-import android.animation.FloatArrayEvaluator;
import android.content.Context;
+import android.graphics.Canvas;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
import android.view.View.OnClickListener;
-import android.widget.FrameLayout;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
-import com.android.launcher3.widget.WidgetsFullSheet;
import com.android.quickstep.RecentsView;
import com.android.quickstep.RecentsView.PageCallbacks;
import com.android.quickstep.RecentsView.ScrollState;
-public class WorkspaceCard extends FrameLayout implements PageCallbacks, OnClickListener {
+public class WorkspaceCard extends View implements PageCallbacks, OnClickListener {
private final Rect mTempRect = new Rect();
- private final float[] mEvaluatedFloats = new float[3];
- private final FloatArrayEvaluator mEvaluator = new FloatArrayEvaluator(mEvaluatedFloats);
-
- // UI related information
- private float[] mScaleAndTranslatePage0, mScaleAndTranslatePage1;
- private boolean mUIDataValid = false;
private Launcher mLauncher;
private Workspace mWorkspace;
+ private float mLinearInterpolationForPage2 = 1;
+ private float mTranslateXPage0, mTranslateXPage1;
+ private float mExtraScrollShift;
+
private boolean mIsWorkspaceScrollingEnabled;
- private View mWorkspaceClickTarget;
- private View mWidgetsButton;
-
- private boolean mLayoutHorizontal;
-
public WorkspaceCard(Context context) {
- super(context);
+ this(context, null);
}
public WorkspaceCard(Context context, AttributeSet attrs) {
- super(context, attrs);
+ this(context, attrs, 0);
}
public WorkspaceCard(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
-
- mWorkspaceClickTarget = findViewById(R.id.workspace_click_target);
- mWidgetsButton = findViewById(R.id.widget_button);
-
- mWorkspaceClickTarget.setOnClickListener(this);
- mWidgetsButton.setOnClickListener(this);
setOnClickListener(this);
}
+ /**
+ * Draw nothing.
+ */
@Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- // We measure the dimensions of the PagedView to be larger than the pages so that when we
- // zoom out (and scale down), the view is still contained in the parent
- int widthMode = MeasureSpec.getMode(widthMeasureSpec);
- int widthSize = MeasureSpec.getSize(widthMeasureSpec);
- int heightMode = MeasureSpec.getMode(heightMeasureSpec);
- int heightSize = MeasureSpec.getSize(heightMeasureSpec);
-
- if (widthMode == MeasureSpec.UNSPECIFIED || heightMode == MeasureSpec.UNSPECIFIED) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- return;
- }
-
- // Return early if we aren't given a proper dimension
- if (widthSize <= 0 || heightSize <= 0) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- return;
- }
-
- float workspaceWidth = mWorkspace.getNormalChildWidth();
- float workspaceHeight = mWorkspace.getNormalChildHeight();
-
- int availableWidth = widthSize - getPaddingLeft() - getPaddingRight();
- float scaleX = availableWidth / workspaceWidth;
-
- int availableHeight = heightSize - getPaddingTop() - getPaddingBottom();
- float scaleY = availableHeight / workspaceHeight;
-
- if (scaleX < scaleY) {
- mLayoutHorizontal = false;
- int childWidthSpec = MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.EXACTLY);
-
- int pageHeight = Math.round(workspaceHeight * scaleX);
- mWorkspaceClickTarget.measure(childWidthSpec,
- MeasureSpec.makeMeasureSpec(pageHeight, MeasureSpec.EXACTLY));
-
- int buttonHeight = availableHeight - pageHeight;
- mWidgetsButton.measure(childWidthSpec,
- MeasureSpec.makeMeasureSpec(buttonHeight, MeasureSpec.EXACTLY));
- } else {
- mLayoutHorizontal = true;
- int childHeightSpec = MeasureSpec.makeMeasureSpec(availableHeight, MeasureSpec.EXACTLY);
-
- int pageWidth = Math.round(workspaceWidth * scaleY);
- mWorkspaceClickTarget.measure(
- MeasureSpec.makeMeasureSpec(pageWidth, MeasureSpec.EXACTLY), childHeightSpec);
-
- int buttonWidth = availableWidth - pageWidth;
- mWidgetsButton.measure(
- MeasureSpec.makeMeasureSpec(buttonWidth, MeasureSpec.EXACTLY), childHeightSpec);
- }
- setMeasuredDimension(widthSize, heightSize);
- }
+ public void draw(Canvas canvas) { }
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- int x = getPaddingLeft();
- int y = getPaddingTop();
+ super.onLayout(changed, left, top, right, bottom);
- if (mLayoutHorizontal) {
- final View first, second;
- if (Utilities.isRtl(getResources())) {
- first = mWidgetsButton;
- second = mWorkspaceClickTarget;
- } else {
- first = mWorkspaceClickTarget;
- second = mWidgetsButton;
- }
- int x2 = x + first.getMeasuredWidth();
- first.layout(x, y,
- x2, y + first.getMeasuredHeight());
- second.layout(x2, y,
- x2 + second.getMeasuredWidth(),
- y + second.getMeasuredHeight());
- } else {
- int y2 = y + mWorkspaceClickTarget.getMeasuredHeight();
- mWorkspaceClickTarget.layout(x, y,
- x + mWorkspaceClickTarget.getMeasuredWidth(), y2);
- mWidgetsButton.layout(x, y2,
- x + mWidgetsButton.getMeasuredWidth(),
- y2 + mWidgetsButton.getMeasuredHeight());
+ // Initiate data
+ mLinearInterpolationForPage2 = RecentsView.getScaledDownPageRect(
+ mLauncher.getDeviceProfile(), mLauncher, mTempRect);
+
+ float[] scale = OverviewState.getScaleAndTranslationForPageRect(mLauncher, 0, mTempRect);
+ mTranslateXPage0 = scale[1];
+ mTranslateXPage1 = OverviewState
+ .getScaleAndTranslationForPageRect(mLauncher,
+ getResources().getDimension(R.dimen.workspace_overview_offset_x),
+ mTempRect)[1];
+
+ mExtraScrollShift = 0;
+ if (mWorkspace != null && getWidth() > 0) {
+ float workspaceWidth = mWorkspace.getNormalChildWidth() * scale[0];
+ mExtraScrollShift = (workspaceWidth - getWidth()) / 2;
+ setScaleX(workspaceWidth / getWidth());
}
-
- mUIDataValid = false;
}
@Override
public void onClick(View view) {
- if (view == mWorkspaceClickTarget || view == this) {
- mLauncher.getStateManager().goToState(NORMAL);
- } else if (view == mWidgetsButton) {
- WidgetsFullSheet.show(mLauncher, true);
- }
- }
-
- @Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- super.onSizeChanged(w, h, oldw, oldh);
- mUIDataValid = false;
+ mLauncher.getStateManager().goToState(NORMAL);
}
public void setup(Launcher launcher) {
@@ -194,41 +104,24 @@
@Override
public int onPageScroll(ScrollState scrollState) {
float factor = scrollState.linearInterpolation;
- float scale = factor * WORKSPACE_SCALE_ON_SCROLL + (1 - factor);
- setScaleX(scale);
- setScaleY(scale);
-
float translateX = scrollState.distanceFromScreenCenter;
if (mIsWorkspaceScrollingEnabled) {
- initUiData();
-
- mEvaluator.evaluate(factor, mScaleAndTranslatePage0, mScaleAndTranslatePage1);
- mWorkspace.setScaleX(mEvaluatedFloats[0]);
- mWorkspace.setScaleY(mEvaluatedFloats[0]);
- mWorkspace.setTranslationX(mEvaluatedFloats[1]);
- mWorkspace.setTranslationY(mEvaluatedFloats[2]);
- translateX += mEvaluatedFloats[1] - mScaleAndTranslatePage0[1];
+ float shift = factor * (mTranslateXPage1 - mTranslateXPage0);
+ mWorkspace.setTranslationX(shift + mTranslateXPage0);
+ translateX += shift;
}
setTranslationX(translateX);
- return SCROLL_TYPE_WORKSPACE;
- }
-
- private void initUiData() {
- if (mUIDataValid && mScaleAndTranslatePage0 != null) {
- return;
+ // If the workspace card is still the first page, shift all the other pages.
+ if (scrollState.linearInterpolation > mLinearInterpolationForPage2) {
+ scrollState.prevPageExtraWidth = 0;
+ } else if (mLinearInterpolationForPage2 > 0) {
+ scrollState.prevPageExtraWidth = mExtraScrollShift *
+ (1 - scrollState.linearInterpolation / mLinearInterpolationForPage2);
+ } else {
+ scrollState.prevPageExtraWidth = mExtraScrollShift;
}
-
- float overlap = getResources().getDimension(R.dimen.workspace_overview_offset_x);
-
- RecentsView.getPageRect(mLauncher, mTempRect);
- mScaleAndTranslatePage0 = OverviewState
- .getScaleAndTranslationForPageRect(mLauncher, 0, mTempRect);
- Rect scaledDown = new Rect(mTempRect);
- Utilities.scaleRectAboutCenter(scaledDown, WORKSPACE_SCALE_ON_SCROLL);
- mScaleAndTranslatePage1 = OverviewState
- .getScaleAndTranslationForPageRect(mLauncher, overlap, scaledDown);
- mUIDataValid = true;
+ return SCROLL_TYPE_WORKSPACE;
}
}
diff --git a/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java b/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java
index ce528e3..168c1fe 100644
--- a/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java
+++ b/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java
@@ -24,6 +24,7 @@
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
+import android.graphics.Color;
import android.graphics.Rect;
import android.os.Build;
import android.os.Handler;
@@ -56,14 +57,10 @@
public class NavBarSwipeInteractionHandler extends InternalStateHandler {
private static final int STATE_LAUNCHER_READY = 1 << 0;
- private static final int STATE_RECENTS_DELAY_COMPLETE = 1 << 1;
- private static final int STATE_LOAD_PLAN_READY = 1 << 2;
- private static final int STATE_RECENTS_FULLY_VISIBLE = 1 << 3;
private static final int STATE_ACTIVITY_MULTIPLIER_COMPLETE = 1 << 4;
private static final int STATE_SCALED_SNAPSHOT_RECENTS = 1 << 5;
private static final int STATE_SCALED_SNAPSHOT_APP = 1 << 6;
- private static final long RECENTS_VIEW_VISIBILITY_DELAY = 120;
private static final long RECENTS_VIEW_VISIBILITY_DURATION = 150;
private static final long MAX_SWIPE_DURATION = 200;
private static final long MIN_SWIPE_DURATION = 80;
@@ -90,7 +87,7 @@
// animated to 1, so allow for a smooth transition.
private final AnimatedFloat mActivityMultiplier = new AnimatedFloat(this::updateFinalShift);
- private final int mRunningTaskId;
+ private final Task mRunningTask;
private final Context mContext;
private final MultiStateCallback mStateCallback;
@@ -102,7 +99,6 @@
private QuickScrubController mQuickScrubController;
private Hotseat mHotseat;
private AllAppsScrim mAllAppsScrim;
- private RecentsTaskLoadPlan mLoadPlan;
private boolean mLauncherReady;
private boolean mTouchEndHandled;
@@ -114,7 +110,11 @@
NavBarSwipeInteractionHandler(RunningTaskInfo runningTaskInfo, Context context,
@TouchInteractionService.InteractionType int interactionType) {
- mRunningTaskId = runningTaskInfo.id;
+ // TODO: We need a better way for this
+ TaskKey taskKey = new TaskKey(runningTaskInfo.id, 0, null, UserHandle.myUserId(), 0);
+ mRunningTask = new Task(taskKey, null, null, "", "", Color.BLACK, Color.BLACK,
+ true, false, false, false, null, 0, null, false);
+
mContext = context;
mInteractionType = interactionType;
WindowManagerWrapper.getInstance().getStableInsets(mStableInsets);
@@ -131,11 +131,9 @@
// Build the state callback
mStateCallback = new MultiStateCallback();
mStateCallback.addCallback(STATE_LAUNCHER_READY, this::onLauncherReady);
- mStateCallback.addCallback(STATE_LOAD_PLAN_READY | STATE_RECENTS_DELAY_COMPLETE,
- this::setTaskPlanToUi);
mStateCallback.addCallback(STATE_SCALED_SNAPSHOT_APP, this::resumeLastTask);
- mStateCallback.addCallback(STATE_RECENTS_FULLY_VISIBLE | STATE_SCALED_SNAPSHOT_RECENTS
- | STATE_ACTIVITY_MULTIPLIER_COMPLETE,
+ mStateCallback.addCallback(
+ STATE_SCALED_SNAPSHOT_RECENTS | STATE_ACTIVITY_MULTIPLIER_COMPLETE,
this::onAnimationToLauncherComplete);
mStateCallback.addCallback(STATE_LAUNCHER_READY | STATE_SCALED_SNAPSHOT_APP,
this::cleanupLauncher);
@@ -145,10 +143,6 @@
mLauncherReady = true;
executeFrameUpdate();
- // Wait for some time before loading recents so that the first frame is fast
- new Handler().postDelayed(() -> mStateCallback.setState(STATE_RECENTS_DELAY_COMPLETE),
- RECENTS_VIEW_VISIBILITY_DELAY);
-
long duration = Math.min(MAX_SWIPE_DURATION,
Math.max((long) (-mCurrentDisplacement / PIXEL_PER_MS), MIN_SWIPE_DURATION));
if (mCurrentShift.getCurrentAnimation() != null) {
@@ -189,18 +183,20 @@
@Override
protected void init(Launcher launcher, boolean alreadyOnHome) {
- AbstractFloatingView.closeAllOpenViews(launcher, alreadyOnHome);
- launcher.getStateManager().goToState(LauncherState.OVERVIEW, alreadyOnHome);
-
mLauncher = launcher;
+ mRecentsView = launcher.getOverviewPanel();
+ mRecentsView.showTask(mRunningTask);
+ mStateController = mRecentsView.getStateController();
+ mHotseat = mLauncher.getHotseat();
+ mAllAppsScrim = mLauncher.findViewById(R.id.all_apps_scrim);
+
+ AbstractFloatingView.closeAllOpenViews(mLauncher, alreadyOnHome);
+ mLauncher.getStateManager().goToState(LauncherState.OVERVIEW, alreadyOnHome);
+
mDragView = new SnapshotDragView(mLauncher, mTaskSnapshot);
mLauncher.getDragLayer().addView(mDragView);
mDragView.setPivotX(0);
mDragView.setPivotY(0);
- mRecentsView = launcher.getOverviewPanel();
- mStateController = mRecentsView.getStateController();
- mHotseat = mLauncher.getHotseat();
- mAllAppsScrim = mLauncher.findViewById(R.id.all_apps_scrim);
boolean interactionIsQuick
= mInteractionType == TouchInteractionService.INTERACTION_QUICK_SCRUB
@@ -220,8 +216,6 @@
// All-apps search box is visible in vertical bar layout.
mLauncher.getAppsView().setVisibility(View.GONE);
}
- mStateController.setTransitionProgress(1);
- mStateController.setVisibility(false);
TraceHelper.partitionSection("TouchInt", "Launcher on new intent");
}
@@ -273,26 +267,6 @@
}
@UiThread
- public void setRecentsTaskLoadPlan(RecentsTaskLoadPlan loadPlan) {
- mLoadPlan = loadPlan;
- mStateCallback.setState(STATE_LOAD_PLAN_READY);
- }
-
- private void setTaskPlanToUi() {
- mRecentsView.update(mLoadPlan);
- mRecentsView.initToPage(mStartedQuickScrubFromHome ? 0 : mRecentsView.getFirstTaskIndex());
- ObjectAnimator anim = mStateController.animateVisibility(true /* isVisible */)
- .setDuration(RECENTS_VIEW_VISIBILITY_DURATION);
- anim.addListener(new AnimationSuccessListener() {
- @Override
- public void onAnimationSuccess(Animator animator) {
- mStateCallback.setState(STATE_RECENTS_FULLY_VISIBLE);
- }
- });
- anim.start();
- }
-
- @UiThread
public void endTouch(float endVelocity) {
if (mTouchEndHandled) {
return;
@@ -339,19 +313,16 @@
@UiThread
private void resumeLastTask() {
- TaskKey key = null;
- if (mLoadPlan != null) {
- Task task = mLoadPlan.getTaskStack().findTaskWithId(mRunningTaskId);
+ // TODO: We need a better way for this
+ TaskKey key = mRunningTask.key;
+ RecentsTaskLoadPlan loadPlan = RecentsModel.getInstance(mContext).getLastLoadPlan();
+ if (loadPlan != null) {
+ Task task = loadPlan.getTaskStack().findTaskWithId(key.id);
if (task != null) {
key = task.key;
}
}
- if (key == null) {
- // TODO: We need a better way for this
- key = new TaskKey(mRunningTaskId, 0, null, UserHandle.myUserId(), 0);
- }
-
ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext, 0, 0);
ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(key, opts, null, null);
}
@@ -372,7 +343,8 @@
if (mInteractionType == TouchInteractionService.INTERACTION_QUICK_SWITCH) {
for (int i = mRecentsView.getFirstTaskIndex(); i < mRecentsView.getPageCount(); i++) {
TaskView taskView = (TaskView) mRecentsView.getPageAt(i);
- if (taskView.getTask().key.id != mRunningTaskId) {
+ // TODO: Match the keys directly
+ if (taskView.getTask().key.id != mRunningTask.key.id) {
mRecentsView.snapToPage(i, QUICK_SWITCH_SNAP_DURATION);
taskView.postDelayed(() -> {taskView.launchTask(true);},
QUICK_SWITCH_SNAP_DURATION);
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index 112f156..22658b2 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -15,8 +15,10 @@
*/
package com.android.quickstep;
+import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Resources;
+import android.os.Build;
import android.os.Looper;
import android.os.UserHandle;
@@ -25,7 +27,9 @@
import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan;
import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan.PreloadOptions;
import com.android.systemui.shared.recents.model.RecentsTaskLoader;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.BackgroundExecutor;
+import com.android.systemui.shared.system.TaskStackChangeListener;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
@@ -33,7 +37,8 @@
/**
* Singleton class to load and manage recents model.
*/
-public class RecentsModel {
+@TargetApi(Build.VERSION_CODES.O)
+public class RecentsModel extends TaskStackChangeListener {
// We do not need any synchronization for this variable as its only written on UI thread.
private static RecentsModel INSTANCE;
@@ -59,6 +64,9 @@
private final MainThreadExecutor mMainThreadExecutor;
private RecentsTaskLoadPlan mLastLoadPlan;
+ private int mLastLoadPlanId;
+ private int mTaskChangeId;
+
private RecentsModel(Context context) {
mContext = context;
@@ -69,6 +77,10 @@
mRecentsTaskLoader.startLoader(mContext);
mMainThreadExecutor = new MainThreadExecutor();
+ ActivityManagerWrapper.getInstance().registerTaskStackListener(this);
+
+ mTaskChangeId = 1;
+ loadTasks(-1, null);
}
public RecentsTaskLoader getRecentsTaskLoader() {
@@ -80,8 +92,20 @@
* @param taskId The running task id or -1
* @param callback The callback to receive the task plan once its complete or null. This is
* always called on the UI thread.
+ * @return the request id associated with this call.
*/
- public void loadTasks(int taskId, Consumer<RecentsTaskLoadPlan> callback) {
+ public int loadTasks(int taskId, Consumer<RecentsTaskLoadPlan> callback) {
+ final int requestId = mTaskChangeId;
+
+ // Fail fast if nothing has changed.
+ if (mLastLoadPlanId == mTaskChangeId) {
+ if (callback != null) {
+ final RecentsTaskLoadPlan plan = mLastLoadPlan;
+ mMainThreadExecutor.execute(() -> callback.accept(plan));
+ }
+ return requestId;
+ }
+
BackgroundExecutor.get().submit(() -> {
// Preload the plan
RecentsTaskLoadPlan loadPlan = new RecentsTaskLoadPlan(mContext);
@@ -91,11 +115,23 @@
// Set the load plan on UI thread
mMainThreadExecutor.execute(() -> {
mLastLoadPlan = loadPlan;
+ mLastLoadPlanId = requestId;
+
if (callback != null) {
callback.accept(loadPlan);
}
});
});
+ return requestId;
+ }
+
+ @Override
+ public void onTaskStackChanged() {
+ mTaskChangeId++;
+ }
+
+ public boolean isLoadPlanValid(int resultId) {
+ return mTaskChangeId == resultId;
}
public RecentsTaskLoadPlan getLastLoadPlan() {
diff --git a/quickstep/src/com/android/quickstep/RecentsView.java b/quickstep/src/com/android/quickstep/RecentsView.java
index 09003b2..9c757bf 100644
--- a/quickstep/src/com/android/quickstep/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/RecentsView.java
@@ -16,8 +16,6 @@
package com.android.quickstep;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-
import android.animation.LayoutTransition;
import android.content.Context;
import android.graphics.Rect;
@@ -27,10 +25,9 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
import com.android.launcher3.PagedView;
import com.android.launcher3.R;
-import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.Utilities;
import com.android.launcher3.uioverrides.OverviewState;
import com.android.launcher3.uioverrides.RecentsViewStateController;
import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan;
@@ -44,11 +41,17 @@
import java.util.ArrayList;
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.quickstep.TaskView.CURVE_FACTOR;
+import static com.android.quickstep.TaskView.CURVE_INTERPOLATOR;
+
/**
* A list of recent tasks.
*/
public class RecentsView extends PagedView {
+ private static final Rect sTempStableInsets = new Rect();
+
public static final int SCROLL_TYPE_NONE = 0;
public static final int SCROLL_TYPE_TASK = 1;
public static final int SCROLL_TYPE_WORKSPACE = 2;
@@ -60,6 +63,9 @@
private boolean mTaskStackListenerRegistered;
private LayoutTransition mLayoutTransition;
+ /**
+ * TODO: Call reloadIdNeeded in onTaskStackChanged.
+ */
private TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
@Override
public void onTaskSnapshotChanged(int taskId, ThumbnailData snapshot) {
@@ -76,6 +82,11 @@
private RecentsViewStateController mStateController;
private int mFirstTaskIndex;
+ private final RecentsModel mModel;
+ private int mLoadPlanId = -1;
+
+ private Task mFirstTask;
+
public RecentsView(Context context) {
this(context, null);
}
@@ -93,6 +104,7 @@
mLauncher = Launcher.getLauncher(context);
mQuickScrubController = new QuickScrubController(mLauncher);
+ mModel = RecentsModel.getInstance(context);
mScrollState.isRtl = mIsRtl;
}
@@ -115,7 +127,6 @@
Rect padding =
getPadding(Launcher.getLauncher(getContext()).getDeviceProfile(), getContext());
setPadding(padding.left, padding.top, padding.right, padding.bottom);
-
mFirstTaskIndex = getPageCount();
}
@@ -154,9 +165,8 @@
updateTaskStackListenerState();
}
- public void update(RecentsTaskLoadPlan loadPlan) {
- final RecentsTaskLoader loader = RecentsModel.getInstance(getContext())
- .getRecentsTaskLoader();
+ private void applyLoadPlan(RecentsTaskLoadPlan loadPlan) {
+ final RecentsTaskLoader loader = mModel.getRecentsTaskLoader();
TaskStack stack = loadPlan != null ? loadPlan.getTaskStack() : null;
if (stack == null) {
removeAllViews();
@@ -166,10 +176,17 @@
// Ensure there are as many views as there are tasks in the stack (adding and trimming as
// necessary)
final LayoutInflater inflater = LayoutInflater.from(getContext());
- final ArrayList<Task> tasks = stack.getTasks();
+ final ArrayList<Task> tasks = new ArrayList<>(stack.getTasks());
setLayoutTransition(null);
- int requiredChildCount = tasks.size() + mFirstTaskIndex;
+ if (mFirstTask != null) {
+ // TODO: Handle this case here once we have a valid implementation for mFirstTask
+ if (tasks.isEmpty() || !keysEquals(tasks.get(tasks.size() - 1), mFirstTask)) {
+ // tasks.add(mFirstTask);
+ }
+ }
+
+ final int requiredChildCount = tasks.size() + mFirstTaskIndex;
for (int i = getChildCount(); i < requiredChildCount; i++) {
final TaskView taskView = (TaskView) inflater.inflate(R.layout.task, this, false);
addView(taskView);
@@ -190,50 +207,35 @@
}
}
- public void initToPage(int pageNo) {
- setCurrentPage(pageNo);
- if (getPageAt(mCurrentPage) instanceof TaskView) {
- ((TaskView) getPageAt(mCurrentPage)).setIconScale(0);
- }
- }
-
- public void launchTaskWithId(int taskId) {
- for (int i = mFirstTaskIndex; i < getChildCount(); i++) {
- final TaskView taskView = (TaskView) getChildAt(i);
- if (taskView.getTask().key.id == taskId) {
- taskView.launchTask(false /* animate */);
- return;
- }
- }
- }
-
private void updateTaskStackListenerState() {
boolean registerStackListener = mOverviewStateEnabled && isAttachedToWindow()
&& getWindowVisibility() == VISIBLE;
if (registerStackListener != mTaskStackListenerRegistered) {
if (registerStackListener) {
- ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
+ ActivityManagerWrapper.getInstance()
+ .registerTaskStackListener(mTaskStackListener);
+ reloadIfNeeded();
} else {
- ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mTaskStackListener);
+ ActivityManagerWrapper.getInstance()
+ .unregisterTaskStackListener(mTaskStackListener);
}
mTaskStackListenerRegistered = registerStackListener;
}
}
private static Rect getPadding(DeviceProfile profile, Context context) {
- Rect stableInsets = new Rect();
- WindowManagerWrapper.getInstance().getStableInsets(stableInsets);
+ WindowManagerWrapper.getInstance().getStableInsets(sTempStableInsets);
Rect padding = new Rect(profile.workspacePadding);
- float taskWidth = profile.widthPx - stableInsets.left - stableInsets.right;
- float taskHeight = profile.heightPx - stableInsets.top - stableInsets.bottom;
+ float taskWidth = profile.widthPx - sTempStableInsets.left - sTempStableInsets.right;
+ float taskHeight = profile.heightPx - sTempStableInsets.top - sTempStableInsets.bottom;
float overviewHeight, overviewWidth;
if (profile.isVerticalBarLayout()) {
// Use the same padding on both sides for symmetry.
float availableWidth = taskWidth - 2 * Math.max(padding.left, padding.right);
float availableHeight = profile.availableHeightPx - padding.top - padding.bottom
- - stableInsets.top
+ - sTempStableInsets.top
- profile.heightPx * (1 - OverviewState.getVerticalProgress(profile, context));
float scaledRatio = Math.min(availableWidth / taskWidth, availableHeight / taskHeight);
@@ -242,18 +244,41 @@
} else {
overviewHeight = profile.availableHeightPx - padding.top - padding.bottom
- - stableInsets.top;
+ - sTempStableInsets.top;
overviewWidth = taskWidth * overviewHeight / taskHeight;
}
- padding.bottom = profile.availableHeightPx - padding.top - stableInsets.top
+ padding.bottom = profile.availableHeightPx - padding.top - sTempStableInsets.top
- Math.round(overviewHeight);
padding.left = padding.right = (int) ((profile.availableWidthPx - overviewWidth) / 2);
return padding;
}
- public static void getPageRect(Launcher launcher, Rect outRect) {
- getPageRect(launcher.getDeviceProfile(), launcher, outRect);
+ /**
+ * Sets the {@param outRect} to match the position of the first tile such that it is scaled
+ * down to match the 2nd taskView.
+ * @return returns the factor which determines the scaling factor for the second task.
+ */
+ public static float getScaledDownPageRect(DeviceProfile dp, Context context, Rect outRect) {
+ getPageRect(dp, context, outRect);
+
+ int pageSpacing = context.getResources()
+ .getDimensionPixelSize(R.dimen.recents_page_spacing);
+ float halfScreenWidth = dp.widthPx * 0.5f;
+ float halfPageWidth = outRect.width() * 0.5f;
+ float pageCenter = outRect.right + pageSpacing + halfPageWidth;
+ float distanceFromCenter = Math.abs(halfScreenWidth - pageCenter);
+ float distanceToReachEdge = halfScreenWidth + halfPageWidth + pageSpacing;
+ float linearInterpolation = Math.min(1, distanceFromCenter / distanceToReachEdge);
+
+ float scale = 1 - CURVE_INTERPOLATOR.getInterpolation(linearInterpolation) * CURVE_FACTOR;
+
+ int topMargin = context.getResources()
+ .getDimensionPixelSize(R.dimen.task_thumbnail_top_margin);
+ outRect.top -= topMargin;
+ Utilities.scaleRectAboutCenter(outRect, scale);
+ outRect.top += (int) (scale * topMargin);
+ return linearInterpolation;
}
public static void getPageRect(DeviceProfile grid, Context context, Rect outRect) {
@@ -302,11 +327,74 @@
public void onTaskDismissed(TaskView taskView) {
ActivityManagerWrapper.getInstance().removeTask(taskView.getTask().key.id);
removeView(taskView);
- if (getChildCount() == mFirstTaskIndex) {
- mLauncher.getStateManager().goToState(LauncherState.NORMAL);
+ if (getTaskCount() == 0) {
+ mLauncher.getStateManager().goToState(NORMAL);
}
}
+ public void reset() {
+ mFirstTask = null;
+ setCurrentPage(0);
+ }
+
+ public int getTaskCount() {
+ return getChildCount() - mFirstTaskIndex;
+ }
+
+ /**
+ * Reloads the view if anything in recents changed.
+ */
+ public void reloadIfNeeded() {
+ if (!mModel.isLoadPlanValid(mLoadPlanId)) {
+ int taskId = -1;
+ if (mFirstTask != null) {
+ taskId = mFirstTask.key.id;
+ }
+ mLoadPlanId = mModel.loadTasks(taskId, this::applyLoadPlan);
+ }
+ }
+
+ /**
+ * Ensures that the first task in the view represents {@param task} and reloads the view
+ * if needed. This allows the swipe-up gesture to assume that the first tile always
+ * corresponds to the correct task.
+ * All subsequent calls to reload will keep the task as the first item until {@link #reset()}
+ * is called.
+ * Also scrolls the view to this task
+ */
+ public void showTask(Task task) {
+ boolean needsReload = false;
+ boolean inflateFirstChild = true;
+ if (getTaskCount() > 0) {
+ TaskView tv = (TaskView) getChildAt(mFirstTaskIndex);
+ inflateFirstChild = !keysEquals(tv.getTask(), task);
+ }
+ if (inflateFirstChild) {
+ needsReload = true;
+ setLayoutTransition(null);
+ // Add an empty view for now
+ final TaskView taskView = (TaskView) LayoutInflater.from(getContext())
+ .inflate(R.layout.task, this, false);
+ addView(taskView, mFirstTaskIndex);
+ taskView.bind(task);
+ setLayoutTransition(mLayoutTransition);
+ }
+ if (!needsReload) {
+ needsReload = !mModel.isLoadPlanValid(mLoadPlanId);
+ }
+ if (needsReload) {
+ mLoadPlanId = mModel.loadTasks(task.key.id, this::applyLoadPlan);
+ }
+ mFirstTask = task;
+ setCurrentPage(mFirstTaskIndex);
+ ((TaskView) getPageAt(mCurrentPage)).setIconScale(0);
+ }
+
+ private static boolean keysEquals(Task t1, Task t2) {
+ // TODO: Match the keys directly
+ return t1.key.id == t2.key.id;
+ }
+
public QuickScrubController getQuickScrubController() {
return mQuickScrubController;
}
@@ -332,5 +420,7 @@
public int halfPageWidth;
public float distanceFromScreenCenter;
public float linearInterpolation;
+
+ public float prevPageExtraWidth;
}
}
diff --git a/quickstep/src/com/android/quickstep/TaskView.java b/quickstep/src/com/android/quickstep/TaskView.java
index 94d85ee..5c15a76 100644
--- a/quickstep/src/com/android/quickstep/TaskView.java
+++ b/quickstep/src/com/android/quickstep/TaskView.java
@@ -16,29 +16,17 @@
package com.android.quickstep;
-import static com.android.quickstep.RecentsView.SCROLL_TYPE_TASK;
-import static com.android.quickstep.RecentsView.SCROLL_TYPE_WORKSPACE;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator;
import android.app.ActivityOptions;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Property;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import android.widget.ImageView;
import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.touch.SwipeDetector;
import com.android.quickstep.RecentsView.PageCallbacks;
import com.android.quickstep.RecentsView.ScrollState;
import com.android.systemui.shared.recents.model.Task;
@@ -52,16 +40,18 @@
import java.util.ArrayList;
import java.util.List;
+import static com.android.quickstep.RecentsView.SCROLL_TYPE_TASK;
+import static com.android.quickstep.RecentsView.SCROLL_TYPE_WORKSPACE;
+
/**
* A task in the Recents view.
*/
-public class TaskView extends FrameLayout implements TaskCallbacks, SwipeDetector.Listener,
- PageCallbacks {
+public class TaskView extends FrameLayout implements TaskCallbacks, PageCallbacks {
/** Designates how "curvy" the carousel is from 0 to 1, where 0 is a straight line. */
- private static final float CURVE_FACTOR = 0.25f;
+ public static final float CURVE_FACTOR = 0.25f;
/** A circular curve of x from 0 to 1, where 0 is the center of the screen and 1 is the edge. */
- private static final TimeInterpolator CURVE_INTERPOLATOR
+ public static final TimeInterpolator CURVE_INTERPOLATOR
= x -> (float) (1 - Math.sqrt(1 - Math.pow(x, 2)));
/**
@@ -70,30 +60,8 @@
*/
private static final float MAX_PAGE_SCRIM_ALPHA = 0.8f;
- private static final int SWIPE_DIRECTIONS = SwipeDetector.DIRECTION_POSITIVE;
-
- /**
- * The task will appear fully dismissed when the distance swiped
- * reaches this percentage of the card height.
- */
- private static final float SWIPE_DISTANCE_HEIGHT_PERCENTAGE = 0.38f;
-
private static final long SCALE_ICON_DURATION = 120;
- private static final Property<TaskView, Float> PROPERTY_SWIPE_PROGRESS =
- new Property<TaskView, Float>(Float.class, "swipe_progress") {
-
- @Override
- public Float get(TaskView taskView) {
- return taskView.mSwipeProgress;
- }
-
- @Override
- public void set(TaskView taskView, Float progress) {
- taskView.setSwipeProgress(progress);
- }
- };
-
private static final Property<TaskView, Float> SCALE_ICON_PROPERTY =
new Property<TaskView, Float>(Float.TYPE, "scale_icon") {
@Override
@@ -110,11 +78,6 @@
private Task mTask;
private TaskThumbnailView mSnapshotView;
private ImageView mIconView;
- private SwipeDetector mSwipeDetector;
- private float mSwipeDistance;
- private float mSwipeProgress;
- private Interpolator mAlphaInterpolator;
- private Interpolator mSwipeAnimInterpolator;
private float mIconScale = 1f;
public TaskView(Context context) {
@@ -130,11 +93,6 @@
setOnClickListener((view) -> {
launchTask(true /* animate */);
});
-
- mSwipeDetector = new SwipeDetector(getContext(), this, SwipeDetector.VERTICAL);
- mSwipeDetector.setDetectableScrollConditions(SWIPE_DIRECTIONS, false);
- mAlphaInterpolator = Interpolators.ACCEL_1_5;
- mSwipeAnimInterpolator = Interpolators.SCROLL_CUBIC;
}
@Override
@@ -144,15 +102,6 @@
mIconView = findViewById(R.id.icon);
}
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
- View p = (View) getParent();
- mSwipeDistance = (getMeasuredHeight() - p.getPaddingTop() - p.getPaddingBottom())
- * SWIPE_DISTANCE_HEIGHT_PERCENTAGE;
- }
-
/**
* Updates this task view to the given {@param task}.
*/
@@ -223,80 +172,6 @@
// Do nothing
}
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- mSwipeDetector.onTouchEvent(ev);
- return super.onInterceptTouchEvent(ev);
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- mSwipeDetector.onTouchEvent(event);
- return mSwipeDetector.isDraggingOrSettling() || super.onTouchEvent(event);
- }
-
- // Swipe detector methods
-
- @Override
- public void onDragStart(boolean start) {
- getParent().requestDisallowInterceptTouchEvent(true);
- }
-
- @Override
- public boolean onDrag(float displacement, float velocity) {
- setSwipeProgress(Utilities.boundToRange(displacement / mSwipeDistance,
- allowsSwipeUp() ? -1 : 0, allowsSwipeDown() ? 1 : 0));
- return true;
- }
-
- /**
- * Indicates the page is being removed.
- * @param progress Ranges from -1 (fading upwards) to 1 (fading downwards).
- */
- private void setSwipeProgress(float progress) {
- mSwipeProgress = progress;
- float translationY = mSwipeProgress * mSwipeDistance;
- float alpha = 1f - mAlphaInterpolator.getInterpolation(Math.abs(mSwipeProgress));
- // Only change children to avoid changing our properties while dragging.
- mIconView.setTranslationY(translationY);
- mSnapshotView.setTranslationY(translationY);
- mIconView.setAlpha(alpha);
- mSnapshotView.setAlpha(alpha);
- }
-
- private boolean allowsSwipeUp() {
- return (SWIPE_DIRECTIONS & SwipeDetector.DIRECTION_POSITIVE) != 0;
- }
-
- private boolean allowsSwipeDown() {
- return (SWIPE_DIRECTIONS & SwipeDetector.DIRECTION_NEGATIVE) != 0;
- }
-
- @Override
- public void onDragEnd(float velocity, boolean fling) {
- boolean movingAwayFromCenter = velocity < 0 == mSwipeProgress < 0;
- boolean flingAway = fling && movingAwayFromCenter
- && (allowsSwipeUp() && velocity < 0 || allowsSwipeDown() && velocity > 0);
- final boolean shouldRemove = flingAway || (!fling && Math.abs(mSwipeProgress) > 0.5f);
- float fromProgress = mSwipeProgress;
- float toProgress = !shouldRemove ? 0f : mSwipeProgress < 0 ? -1f : 1f;
- ValueAnimator swipeAnimator = ObjectAnimator.ofFloat(this, PROPERTY_SWIPE_PROGRESS,
- fromProgress, toProgress);
- swipeAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- if (shouldRemove) {
- ((RecentsView) getParent()).onTaskDismissed(TaskView.this);
- }
- mSwipeDetector.finishedScrolling();
- }
- });
- swipeAnimator.setDuration(SwipeDetector.calculateDuration(velocity,
- Math.abs(toProgress - fromProgress)));
- swipeAnimator.setInterpolator(mSwipeAnimInterpolator);
- swipeAnimator.start();
- }
-
public void animateIconToScale(float scale) {
ObjectAnimator.ofFloat(this, SCALE_ICON_PROPERTY, scale)
.setDuration(SCALE_ICON_DURATION).start();
@@ -331,13 +206,14 @@
// Make sure that the task cards do not overlap with the workspace card
float min = scrollState.halfPageWidth * (1 - scale);
if (scrollState.isRtl) {
- setTranslationX(Math.min(translation, min));
+ setTranslationX(Math.min(translation, min) - scrollState.prevPageExtraWidth);
} else {
- setTranslationX(Math.max(translation, -min));
+ setTranslationX(Math.max(translation, -min) + scrollState.prevPageExtraWidth);
}
} else {
setTranslationX(translation);
}
+ scrollState.prevPageExtraWidth = 0;
return SCROLL_TYPE_TASK;
}
}
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 56f0e89..c35ffee 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -31,6 +31,8 @@
import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.Color;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
@@ -161,10 +163,10 @@
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
ResolveInfo info = getPackageManager().resolveActivity(mHomeIntent, 0);
mLauncher = new ComponentName(getPackageName(), info.activityInfo.name);
- mHomeIntent.setComponent(mLauncher);
+ // Clear the packageName as system can fail to dedupe it b/64108432
+ mHomeIntent.setComponent(mLauncher).setPackage(null);
mEventQueue = new MotionEventQueue(Choreographer.getInstance(), this::handleMotionEvent);
- mRecentsModel.loadTasks(-1, null);
sConnected = true;
}
@@ -307,7 +309,7 @@
});
// Preload the plan
- mRecentsModel.loadTasks(mRunningTask.id, handler::setRecentsTaskLoadPlan);
+ mRecentsModel.loadTasks(mRunningTask.id, null);
mInteractionHandler = handler;
}
@@ -327,11 +329,6 @@
}
private Bitmap getCurrentTaskSnapshot() {
- if (mISystemUiProxy == null) {
- Log.e(TAG, "Never received systemUIProxy");
- return null;
- }
-
TraceHelper.beginSection("TaskSnapshot");
// TODO: We are using some hardcoded layers for now, to best approximate the activity layers
Point displaySize = new Point();
@@ -347,9 +344,13 @@
try {
return mISystemUiProxy.screenshot(new Rect(), displaySize.x, displaySize.y, 0, 100000,
false, rotation).toBitmap();
- } catch (RemoteException e) {
+ } catch (Exception e) {
Log.e(TAG, "Error capturing snapshot", e);
- return null;
+
+ // Return a dummy bitmap
+ Bitmap bitmap = Bitmap.createBitmap(displaySize.x, displaySize.y, Config.RGB_565);
+ bitmap.eraseColor(Color.WHITE);
+ return bitmap;
} finally {
TraceHelper.endSection("TaskSnapshot");
}
diff --git a/res/layout/work_tab_footer.xml b/res/layout/work_tab_footer.xml
index e3416ac..dc0fdd4 100644
--- a/res/layout/work_tab_footer.xml
+++ b/res/layout/work_tab_footer.xml
@@ -73,7 +73,6 @@
android:lines="1"
android:minHeight="24dp"
android:paddingStart="12dp"
- android:text="@string/managed_by_your_organisation"
android:textColor="?android:attr/textColorHint"
android:textSize="13sp"/>
diff --git a/res/values/config.xml b/res/values/config.xml
index f48d474..2096200 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -101,6 +101,9 @@
<!-- Package name of the default wallpaper picker. -->
<string name="wallpaper_picker_package" translatable="false"></string>
+ <!-- Whitelisted package to retrieve packagename for badge. Can be empty. -->
+ <string name="shortcutinfocompat_badgepkg_whitelist" translatable="false"></string>
+
<!-- View ID to use for QSB widget -->
<item type="id" name="qsb_widget" />
diff --git a/res/values/strings.xml b/res/values/strings.xml
index d77065c..ee09946 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -324,13 +324,16 @@
<!-- Label of tab to indicate work apps -->
<string name="all_apps_work_tab">Work</string>
- <!-- Label of the work mode toggle -->
+ <!-- This string is in the work profile tab when a user has All Apps open on their phone. This is a label for a toggle to turn the work profile on and off. "Work profile" means a separate profile on a user's phone that's specifically for their work apps and managed by their company. "Work" is used as an adjective.-->
<string name="work_profile_toggle_label">Work profile</string>
- <!-- Title in bottom user education view in work tab -->
+ <!-- Title of an overlay in All Apps. This overlay is letting a user know about their work profile, which is managed by their employer. "Work apps" are apps in a user's work profile.-->
<string name="bottom_work_tab_user_education_title">Find work apps here</string>
- <!-- Body text in bottom user education view in work tab -->
- <string name="bottom_work_tab_user_education_body">Each work app has an orange badge, which means it\'s kept secure by your organization. Work apps can be moved to your Home Screen for easier access.</string>
- <!-- Label in work tab to tell users that work profile is managed by their organisation. -->
- <string name="managed_by_your_organisation">Managed by your organisation</string>
+ <!-- Text in an overlay in All Apps. This overlay is letting a user know about their work profile, which is managed by their employer.-->
+ <string name="bottom_work_tab_user_education_body">Each work app has an orange badge and is kept secure by your organization. Move apps to your Home screen for easier access.</string>
+ <!-- This string is in the work profile tab when a user has All Apps open on their phone. It describes the label of a toggle, "Work profile," as being managed by the user's employer.
+ "Organization" is used to represent a variety of businesses, non-profits, and educational institutions).-->
+ <string name="work_mode_on_label">Managed by your organization</string>
+ <!-- This string appears under a the label of a toggle in the work profile tab on a user's phone. It describes the status of the toggle, "Work profile," when it's turned off. "Work profile" means a separate profile on a user's phone that's speficially for their work apps and is managed by their company.-->
+ <string name="work_mode_off_label">Notifications and apps are off</string>
</resources>
diff --git a/src/com/android/launcher3/BaseRecyclerView.java b/src/com/android/launcher3/BaseRecyclerView.java
index 76c7845..cc13263 100644
--- a/src/com/android/launcher3/BaseRecyclerView.java
+++ b/src/com/android/launcher3/BaseRecyclerView.java
@@ -91,7 +91,7 @@
// Move to mScrollbar's coordinate system.
// We need to take parent into account (view pager's location)
ViewGroup parent = (ViewGroup) getParent();
- int left = parent.getLeft() + getLeft() - mScrollbar.getLeft();
+ int left = parent.getLeft() - mScrollbar.getLeft();
int top = parent.getTop() + getTop() - mScrollbar.getTop() - getScrollBarTop();
ev.offsetLocation(left, top);
try {
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 7a6a244..164efe5 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -102,8 +102,6 @@
// All apps
public int allAppsCellHeightPx;
- public int allAppsNumCols;
- public int allAppsNumPredictiveCols;
public int allAppsIconSizePx;
public int allAppsIconDrawablePaddingPx;
public float allAppsIconTextSizePx;
@@ -385,10 +383,6 @@
return mInsets;
}
- public void updateAppsViewNumCols() {
- allAppsNumCols = allAppsNumPredictiveCols = inv.numColumns;
- }
-
public Point getCellSize() {
Point result = new Point();
// Since we are only concerned with the overall padding, layout direction does
@@ -462,9 +456,9 @@
mInsets.top + availableHeightPx);
} else {
// Folders should only appear below the drop target bar and above the hotseat
- return new Rect(mInsets.left,
+ return new Rect(mInsets.left + edgeMarginPx,
mInsets.top + dropTargetBarSizePx + edgeMarginPx,
- mInsets.left + availableWidthPx,
+ mInsets.left + availableWidthPx - edgeMarginPx,
mInsets.top + availableHeightPx - hotseatBarSizePx
- pageIndicatorSizePx - edgeMarginPx);
}
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 1f5aa13..25eacb5 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -46,7 +46,7 @@
private CellLayout mContent;
@ViewDebug.ExportedProperty(category = "launcher")
- private final boolean mHasVerticalHotseat;
+ private boolean mHasVerticalHotseat;
public Hotseat(Context context) {
this(context, null);
@@ -59,7 +59,6 @@
public Hotseat(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mLauncher = Launcher.getLauncher(context);
- mHasVerticalHotseat = mLauncher.getDeviceProfile().isVerticalBarLayout();
}
public CellLayout getLayout() {
@@ -91,13 +90,7 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- DeviceProfile grid = mLauncher.getDeviceProfile();
- mContent = (CellLayout) findViewById(R.id.layout);
- if (grid.isVerticalBarLayout()) {
- mContent.setGridSize(1, grid.inv.numHotseatIcons);
- } else {
- mContent.setGridSize(grid.inv.numHotseatIcons, 1);
- }
+ mContent = findViewById(R.id.layout);
resetLayout();
}
@@ -165,7 +158,11 @@
public void setInsets(Rect insets) {
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
DeviceProfile grid = mLauncher.getDeviceProfile();
+ mHasVerticalHotseat = mLauncher.getDeviceProfile().isVerticalBarLayout();
+
if (mHasVerticalHotseat) {
+ mContent.setGridSize(1, grid.inv.numHotseatIcons);
+
lp.height = ViewGroup.LayoutParams.MATCH_PARENT;
if (insets.left > insets.right) {
lp.gravity = Gravity.LEFT;
@@ -180,6 +177,8 @@
grid.hotseatBarSidePaddingPx, insets.top, insets.right, insets.bottom);
}
} else {
+ mContent.setGridSize(grid.inv.numHotseatIcons, 1);
+
lp.gravity = Gravity.BOTTOM;
lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
lp.height = grid.hotseatBarSizePx + insets.bottom;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 2a5f453..e3682b4 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -2552,10 +2552,6 @@
return bounceAnim;
}
- public boolean useVerticalBarLayout() {
- return mDeviceProfile.isVerticalBarLayout();
- }
-
/**
* Add the icons for all apps.
*
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 670f579..472a5a9 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -27,6 +27,7 @@
import com.android.launcher3.uioverrides.AllAppsState;
import com.android.launcher3.states.SpringLoadedState;
import com.android.launcher3.uioverrides.OverviewState;
+import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import java.util.Arrays;
@@ -54,6 +55,9 @@
private static final LauncherState[] sAllStates = new LauncherState[4];
+ /**
+ * TODO: Create a separate class for NORMAL state.
+ */
public static final LauncherState NORMAL = new LauncherState(0, ContainerType.WORKSPACE,
0, FLAG_DISABLE_RESTORE | FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED);
@@ -175,6 +179,15 @@
return NORMAL;
}
+ /**
+ * Called when the start transition ends and the user settles on this particular state.
+ */
+ public void onStateTransitionEnd(Launcher launcher) {
+ if (this == NORMAL) {
+ UiFactory.resetOverview(launcher);
+ }
+ }
+
protected static void dispatchWindowStateChanged(Launcher launcher) {
launcher.getWindow().getDecorView().sendAccessibilityEvent(TYPE_WINDOW_STATE_CHANGED);
}
diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java
index 3a660dc..bcb6252 100644
--- a/src/com/android/launcher3/LauncherStateManager.java
+++ b/src/com/android/launcher3/LauncherStateManager.java
@@ -270,6 +270,7 @@
mCurrentStableState = state;
}
+ state.onStateTransitionEnd(mLauncher);
mLauncher.getWorkspace().setClipChildren(!state.disablePageClipping);
mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
mLauncher.finishAutoCancelActionMode();
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 893d820..de3b09a 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -207,7 +207,7 @@
private boolean mStripScreensOnPageStopMoving = false;
private DragPreviewProvider mOutlineProvider = null;
- private final boolean mWorkspaceFadeInAdjacentScreens;
+ private boolean mWorkspaceFadeInAdjacentScreens;
final WallpaperOffsetInterpolator mWallpaperOffset;
private boolean mUnlockWallpaperFromDefaultPageOnLayout;
@@ -292,8 +292,6 @@
mLauncher = Launcher.getLauncher(context);
mStateTransitionAnimation = new WorkspaceStateTransitionAnimation(mLauncher, this);
- DeviceProfile grid = mLauncher.getDeviceProfile();
- mWorkspaceFadeInAdjacentScreens = grid.shouldFadeAdjacentWorkspaceScreens();
mWallpaperManager = WallpaperManager.getInstance(context);
mWallpaperOffset = new WallpaperOffsetInterpolator(this);
@@ -312,6 +310,9 @@
mInsets.set(insets);
DeviceProfile grid = mLauncher.getDeviceProfile();
+ mMaxDistanceForFolderCreation = (0.55f * grid.iconSizePx);
+ mWorkspaceFadeInAdjacentScreens = grid.shouldFadeAdjacentWorkspaceScreens();
+
Rect padding = grid.workspacePadding;
setPadding(padding.left, padding.top, padding.right, padding.bottom);
@@ -324,6 +325,13 @@
// We assume symmetrical padding in portrait mode.
setPageSpacing(Math.max(grid.defaultPageSpacingPx, padding.left + 1));
}
+
+ int paddingLeftRight = grid.cellLayoutPaddingLeftRightPx;
+ int paddingBottom = grid.cellLayoutBottomPaddingPx;
+ for (int i = mWorkspaceScreens.size() - 1; i >= 0; i--) {
+ mWorkspaceScreens.valueAt(i)
+ .setPadding(paddingLeftRight, 0, paddingLeftRight, paddingBottom);
+ }
}
/**
@@ -445,12 +453,9 @@
*/
protected void initWorkspace() {
mCurrentPage = DEFAULT_PAGE;
- DeviceProfile grid = mLauncher.getDeviceProfile();
- setWillNotDraw(false);
setClipToPadding(false);
setupLayoutTransition();
- mMaxDistanceForFolderCreation = (0.55f * grid.iconSizePx);
// Set the wallpaper dimensions when Launcher starts up
setWallpaperDimension();
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index d05b556..af7ca51 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -276,8 +276,6 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
DeviceProfile grid = mLauncher.getDeviceProfile();
- // Update the number of items in the grid before we measure the view
- grid.updateAppsViewNumCols();
if (mNumAppsPerRow != grid.inv.numColumns ||
mNumPredictedAppsPerRow != grid.inv.numColumns) {
@@ -365,18 +363,30 @@
public void updateIconBadges(Set<PackageUserKey> updatedBadges) {
final PackageUserKey packageUserKey = new PackageUserKey(null, null);
for (int j = 0; j < mAH.length; j++) {
- if (mAH[j].recyclerView != null) {
- final int n = mAH[j].recyclerView.getChildCount();
- for (int i = 0; i < n; i++) {
- View child = mAH[j].recyclerView.getChildAt(i);
- if (!(child instanceof BubbleTextView) || !(child.getTag() instanceof ItemInfo)) {
- continue;
- }
- ItemInfo info = (ItemInfo) child.getTag();
- if (packageUserKey.updateFromItemInfo(info) && updatedBadges.contains(packageUserKey)) {
- ((BubbleTextView) child).applyBadgeState(info, true /* animate */);
- }
- }
+ updateIconBadges(updatedBadges, packageUserKey, mAH[j].recyclerView);
+ }
+ if (mHeader != null) {
+ updateIconBadges(updatedBadges, packageUserKey, mHeader.getPredictionRow());
+ }
+ }
+
+ private void updateIconBadges(Set<PackageUserKey> updatedBadges, PackageUserKey packageUserKey,
+ ViewGroup parent) {
+ if (parent == null) {
+ return;
+ }
+ final int n = parent.getChildCount();
+ for (int i = 0; i < n; i++) {
+ View child = parent.getChildAt(i);
+ if (child instanceof PredictionRowView) {
+ updateIconBadges(updatedBadges, packageUserKey, (PredictionRowView) child);
+ }
+ if (!(child instanceof BubbleTextView) || !(child.getTag() instanceof ItemInfo)) {
+ continue;
+ }
+ ItemInfo info = (ItemInfo) child.getTag();
+ if (packageUserKey.updateFromItemInfo(info) && updatedBadges.contains(packageUserKey)) {
+ ((BubbleTextView) child).applyBadgeState(info, true /* animate */);
}
}
}
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 234eb81..769f9ba 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -41,6 +41,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.AlphabeticalAppsList.AdapterItem;
import com.android.launcher3.anim.SpringAnimationHandler;
+import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.discovery.AppDiscoveryAppInfo;
import com.android.launcher3.discovery.AppDiscoveryItemView;
@@ -377,6 +378,11 @@
case VIEW_TYPE_WORK_TAB_FOOTER:
WorkModeSwitch workModeToggle = holder.itemView.findViewById(R.id.work_mode_toggle);
workModeToggle.refresh();
+ TextView managedByLabel = holder.itemView.findViewById(R.id.managed_by_label);
+ boolean anyProfileQuietModeEnabled = UserManagerCompat.getInstance(
+ managedByLabel.getContext()).isAnyProfileQuietModeEnabled();
+ managedByLabel.setText(anyProfileQuietModeEnabled
+ ? R.string.work_mode_off_label : R.string.work_mode_on_label);
break;
}
if (mBindViewCallback != null) {
diff --git a/src/com/android/launcher3/allapps/PredictionRowView.java b/src/com/android/launcher3/allapps/PredictionRowView.java
index bc934dd..267ef3c 100644
--- a/src/com/android/launcher3/allapps/PredictionRowView.java
+++ b/src/com/android/launcher3/allapps/PredictionRowView.java
@@ -28,9 +28,12 @@
import com.android.launcher3.AppInfo;
import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.logging.UserEventDispatcher;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.ComponentKeyMapper;
import com.android.launcher3.util.Themes;
@@ -40,7 +43,8 @@
import java.util.HashMap;
import java.util.List;
-public class PredictionRowView extends LinearLayout {
+public class PredictionRowView extends LinearLayout implements
+ UserEventDispatcher.LogContainerProvider {
private static final String TAG = "PredictionRowView";
@@ -49,7 +53,7 @@
// The set of predicted app component names
private final List<ComponentKeyMapper<AppInfo>> mPredictedAppComponents = new ArrayList<>();
// The set of predicted apps resolved from the component names and the current set of apps
- private final List<AppInfo> mPredictedApps = new ArrayList<>();
+ private final ArrayList<AppInfo> mPredictedApps = new ArrayList<>();
private final Paint mPaint;
// This adapter is only used to create an identical item w/ same behavior as in the all apps RV
private AllAppsGridAdapter mAdapter;
@@ -225,4 +229,17 @@
canvas.drawLine(x1, y, x2, y, mPaint);
}
}
+
+ @Override
+ public void fillInLogContainerData(View v, ItemInfo info, LauncherLogProto.Target target,
+ LauncherLogProto.Target targetParent) {
+ for (int i = 0; i < mPredictedApps.size(); i++) {
+ AppInfo appInfo = mPredictedApps.get(i);
+ if (appInfo == info) {
+ targetParent.containerType = LauncherLogProto.ContainerType.PREDICTION;
+ target.predictedRank = i;
+ break;
+ }
+ }
+ }
}
diff --git a/src/com/android/launcher3/allapps/WorkModeSwitch.java b/src/com/android/launcher3/allapps/WorkModeSwitch.java
index 05d5e53..e7cf092 100644
--- a/src/com/android/launcher3/allapps/WorkModeSwitch.java
+++ b/src/com/android/launcher3/allapps/WorkModeSwitch.java
@@ -55,22 +55,9 @@
}
public void refresh() {
- setCheckedInternal(!isAnyProfileQuietModeEnabled());
- setEnabled(true);
- }
-
- private boolean isAnyProfileQuietModeEnabled() {
UserManagerCompat userManager = UserManagerCompat.getInstance(getContext());
- List<UserHandle> userProfiles = userManager.getUserProfiles();
- for (UserHandle userProfile : userProfiles) {
- if (Process.myUserHandle().equals(userProfile)) {
- continue;
- }
- if (userManager.isQuietModeEnabled(userProfile)) {
- return true;
- }
- }
- return false;
+ setCheckedInternal(!userManager.isAnyProfileQuietModeEnabled());
+ setEnabled(true);
}
private void trySetQuietModeEnabledToAllProfilesAsync(boolean enabled) {
diff --git a/src/com/android/launcher3/anim/AnimatorPlaybackController.java b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
index 6819386..68e9847 100644
--- a/src/com/android/launcher3/anim/AnimatorPlaybackController.java
+++ b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
@@ -33,6 +33,12 @@
*/
public abstract class AnimatorPlaybackController implements ValueAnimator.AnimatorUpdateListener {
+ /**
+ * Creates an animation controller for the provided animation.
+ * The actual duration does not matter as the animation is manually controlled. It just
+ * needs to be larger than the total number of pixels so that we don't have jittering due
+ * to float (animation-fraction * total duration) to int conversion.
+ */
public static AnimatorPlaybackController wrap(AnimatorSet anim, long duration) {
/**
diff --git a/src/com/android/launcher3/compat/UserManagerCompat.java b/src/com/android/launcher3/compat/UserManagerCompat.java
index ddb244a..62055dc 100644
--- a/src/com/android/launcher3/compat/UserManagerCompat.java
+++ b/src/com/android/launcher3/compat/UserManagerCompat.java
@@ -64,4 +64,5 @@
public abstract boolean isDemoUser();
public abstract boolean requestQuietModeEnabled(boolean enableQuietMode, UserHandle user);
+ public abstract boolean isAnyProfileQuietModeEnabled();
}
diff --git a/src/com/android/launcher3/compat/UserManagerCompatVL.java b/src/com/android/launcher3/compat/UserManagerCompatVL.java
index 5585fe4..e57786d 100644
--- a/src/com/android/launcher3/compat/UserManagerCompatVL.java
+++ b/src/com/android/launcher3/compat/UserManagerCompatVL.java
@@ -88,6 +88,11 @@
}
@Override
+ public boolean isAnyProfileQuietModeEnabled() {
+ return false;
+ }
+
+ @Override
public void enableAndResetCache() {
synchronized (this) {
mUsers = new LongArrayMap<>();
diff --git a/src/com/android/launcher3/compat/UserManagerCompatVN.java b/src/com/android/launcher3/compat/UserManagerCompatVN.java
index 50a0217..3733565 100644
--- a/src/com/android/launcher3/compat/UserManagerCompatVN.java
+++ b/src/com/android/launcher3/compat/UserManagerCompatVN.java
@@ -19,8 +19,11 @@
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
+import android.os.Process;
import android.os.UserHandle;
+import java.util.List;
+
@TargetApi(Build.VERSION_CODES.N)
public class UserManagerCompatVN extends UserManagerCompatVM {
@@ -37,5 +40,19 @@
public boolean isUserUnlocked(UserHandle user) {
return mUserManager.isUserUnlocked(user);
}
+
+ @Override
+ public boolean isAnyProfileQuietModeEnabled() {
+ List<UserHandle> userProfiles = getUserProfiles();
+ for (UserHandle userProfile : userProfiles) {
+ if (Process.myUserHandle().equals(userProfile)) {
+ continue;
+ }
+ if (isQuietModeEnabled(userProfile)) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index 9732261..a59b899 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -369,9 +369,9 @@
return new FixedSizeEmptyDrawable(iconSize);
}
ShortcutInfoCompat si = (ShortcutInfoCompat) obj;
- Bitmap badge = LauncherIcons.getShortcutInfoBadge(si, appState.getIconCache())
- .iconBitmap;
-
+ LauncherIcons li = LauncherIcons.obtain(appState.getContext());
+ Bitmap badge = li.getShortcutInfoBadge(si, appState.getIconCache()).iconBitmap;
+ li.recycle();
float badgeSize = mLauncher.getResources().getDimension(R.dimen.profile_badge_size);
float insetFraction = (iconSize - badgeSize) / iconSize;
return new InsetDrawable(new FastBitmapDrawable(badge),
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 64f96d5..8abafb0 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -18,6 +18,7 @@
import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
import android.animation.Animator;
@@ -929,7 +930,12 @@
int centeredTop = centerY - height / 2;
// We need to bound the folder to the currently visible workspace area
- mLauncher.getWorkspace().getPageAreaRelativeToDragLayer(sTempRect);
+ if (mLauncher.isInState(OVERVIEW)) {
+ mLauncher.getDragLayer().getDescendantRectRelativeToSelf(mLauncher.getOverviewPanel(),
+ sTempRect);
+ } else {
+ mLauncher.getWorkspace().getPageAreaRelativeToDragLayer(sTempRect);
+ }
int left = Math.min(Math.max(sTempRect.left, centeredLeft),
sTempRect.right- width);
int top = Math.min(Math.max(sTempRect.top, centeredTop),
diff --git a/src/com/android/launcher3/graphics/LauncherIcons.java b/src/com/android/launcher3/graphics/LauncherIcons.java
index 0c9f4d9..34fc921 100644
--- a/src/com/android/launcher3/graphics/LauncherIcons.java
+++ b/src/com/android/launcher3/graphics/LauncherIcons.java
@@ -356,10 +356,11 @@
return result;
}
- public static ItemInfoWithIcon getShortcutInfoBadge(
- ShortcutInfoCompat shortcutInfo, IconCache cache) {
+ public ItemInfoWithIcon getShortcutInfoBadge(ShortcutInfoCompat shortcutInfo, IconCache cache) {
ComponentName cn = shortcutInfo.getActivity();
- if (cn != null) {
+ String badgePkg = shortcutInfo.getBadgePackage(mContext);
+ boolean hasBadgePkgSet = !badgePkg.equals(shortcutInfo.getPackage());
+ if (cn != null && !hasBadgePkgSet) {
// Get the app info for the source activity.
AppInfo appInfo = new AppInfo();
appInfo.user = shortcutInfo.getUserHandle();
@@ -370,7 +371,7 @@
cache.getTitleAndIcon(appInfo, false);
return appInfo;
} else {
- PackageItemInfo pkgInfo = new PackageItemInfo(shortcutInfo.getPackage());
+ PackageItemInfo pkgInfo = new PackageItemInfo(badgePkg);
cache.getTitleAndIconForApp(pkgInfo, false);
return pkgInfo;
}
diff --git a/src/com/android/launcher3/notification/NotificationListener.java b/src/com/android/launcher3/notification/NotificationListener.java
index 7b70df7..114b2b8 100644
--- a/src/com/android/launcher3/notification/NotificationListener.java
+++ b/src/com/android/launcher3/notification/NotificationListener.java
@@ -62,6 +62,7 @@
private static NotificationListener sNotificationListenerInstance = null;
private static NotificationsChangedListener sNotificationsChangedListener;
+ private static StatusBarNotificationsChangedListener sStatusBarNotificationsChangedListener;
private static boolean sIsConnected;
private static boolean sIsCreated;
@@ -180,10 +181,19 @@
}
}
+ public static void setStatusBarNotificationsChangedListener
+ (StatusBarNotificationsChangedListener listener) {
+ sStatusBarNotificationsChangedListener = listener;
+ }
+
public static void removeNotificationsChangedListener() {
sNotificationsChangedListener = null;
}
+ public static void removeStatusBarNotificationsChangedListener() {
+ sStatusBarNotificationsChangedListener = null;
+ }
+
@Override
public void onListenerConnected() {
super.onListenerConnected();
@@ -205,7 +215,10 @@
public void onNotificationPosted(final StatusBarNotification sbn) {
super.onNotificationPosted(sbn);
mWorkerHandler.obtainMessage(MSG_NOTIFICATION_POSTED, new NotificationPostedMsg(sbn))
- .sendToTarget();
+ .sendToTarget();
+ if (sStatusBarNotificationsChangedListener != null) {
+ sStatusBarNotificationsChangedListener.onNotificationPosted(sbn);
+ }
}
/**
@@ -227,10 +240,13 @@
public void onNotificationRemoved(final StatusBarNotification sbn) {
super.onNotificationRemoved(sbn);
Pair<PackageUserKey, NotificationKeyData> packageUserKeyAndNotificationKey
- = new Pair<>(PackageUserKey.fromNotification(sbn),
- NotificationKeyData.fromNotification(sbn));
+ = new Pair<>(PackageUserKey.fromNotification(sbn),
+ NotificationKeyData.fromNotification(sbn));
mWorkerHandler.obtainMessage(MSG_NOTIFICATION_REMOVED, packageUserKeyAndNotificationKey)
- .sendToTarget();
+ .sendToTarget();
+ if (sStatusBarNotificationsChangedListener != null) {
+ sStatusBarNotificationsChangedListener.onNotificationRemoved(sbn);
+ }
NotificationGroup notificationGroup = mNotificationGroupMap.get(sbn.getGroupKey());
if (notificationGroup != null) {
@@ -318,4 +334,9 @@
NotificationKeyData notificationKey);
void onNotificationFullRefresh(List<StatusBarNotification> activeNotifications);
}
+
+ public interface StatusBarNotificationsChangedListener {
+ void onNotificationPosted(StatusBarNotification sbn);
+ void onNotificationRemoved(StatusBarNotification sbn);
+ }
}
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index cedf291..f90abb4 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -68,7 +68,6 @@
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DragView;
-import com.android.launcher3.graphics.IconPalette;
import com.android.launcher3.graphics.TriangleShape;
import com.android.launcher3.logging.LoggerUtils;
import com.android.launcher3.notification.NotificationInfo;
@@ -650,7 +649,7 @@
// reopen the container to ensure measurements etc. all work out. While this could
// be quite janky, in practice the user would typically see a small flicker as the
// animation restarts partway through, and this is a very rare edge case anyway.
- ((PopupContainerWithArrow) getParent()).close(false);
+ close(false);
PopupContainerWithArrow.showForIcon(mOriginalIcon);
}
} else if (onClickListener == null && widgetsView != null) {
@@ -658,7 +657,7 @@
if (mSystemShortcutContainer != this) {
mSystemShortcutContainer.removeView(widgetsView);
} else {
- ((PopupContainerWithArrow) getParent()).close(false);
+ close(false);
PopupContainerWithArrow.showForIcon(mOriginalIcon);
}
}
diff --git a/src/com/android/launcher3/shortcuts/ShortcutInfoCompat.java b/src/com/android/launcher3/shortcuts/ShortcutInfoCompat.java
index 9c91c87..325777d 100644
--- a/src/com/android/launcher3/shortcuts/ShortcutInfoCompat.java
+++ b/src/com/android/launcher3/shortcuts/ShortcutInfoCompat.java
@@ -18,11 +18,14 @@
import android.annotation.TargetApi;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.ShortcutInfo;
import android.os.Build;
import android.os.UserHandle;
+import com.android.launcher3.R;
+
/**
* Wrapper class for {@link android.content.pm.ShortcutInfo}, representing deep shortcuts into apps.
*
@@ -31,8 +34,8 @@
@TargetApi(Build.VERSION_CODES.N)
public class ShortcutInfoCompat {
private static final String INTENT_CATEGORY = "com.android.launcher3.DEEP_SHORTCUT";
+ private static final String EXTRA_BADGEPKG = "badge_package";
public static final String EXTRA_SHORTCUT_ID = "shortcut_id";
-
private ShortcutInfo mShortcutInfo;
public ShortcutInfoCompat(ShortcutInfo shortcutInfo) {
@@ -57,6 +60,15 @@
return mShortcutInfo.getPackage();
}
+ public String getBadgePackage(Context context) {
+ String whitelistedPkg = context.getString(R.string.shortcutinfocompat_badgepkg_whitelist);
+ if (whitelistedPkg.equals(getPackage())
+ && mShortcutInfo.getExtras().containsKey(EXTRA_BADGEPKG)) {
+ return mShortcutInfo.getExtras().getString(EXTRA_BADGEPKG);
+ }
+ return getPackage();
+ }
+
public String getId() {
return mShortcutInfo.getId();
}
diff --git a/src/com/android/launcher3/touch/SwipeDetector.java b/src/com/android/launcher3/touch/SwipeDetector.java
index ff5f64c..df34885 100644
--- a/src/com/android/launcher3/touch/SwipeDetector.java
+++ b/src/com/android/launcher3/touch/SwipeDetector.java
@@ -286,6 +286,16 @@
}
}
+ /**
+ * Returns if the start drag was towards the positive direction or negative.
+ *
+ * @see #setDetectableScrollConditions(int, boolean)
+ * @see #DIRECTION_BOTH
+ */
+ public boolean wasInitialTouchPositive() {
+ return mSubtractDisplacement < 0;
+ }
+
private boolean reportDragging() {
if (mDisplacement != mLastDisplacement) {
if (DBG) {
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
index fc81e80..2ea10c2 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
@@ -56,4 +56,6 @@
renderer.render(new Canvas(result));
return result;
}
+
+ public static void resetOverview(Launcher launcher) { }
}