Merge "Revert "Capture screenshot when animating to home"" into ub-launcher3-qt-dev
diff --git a/go/quickstep/res/layout/task_item_view.xml b/go/quickstep/res/layout/task_item_view.xml
index 699178d..ab2cf28 100644
--- a/go/quickstep/res/layout/task_item_view.xml
+++ b/go/quickstep/res/layout/task_item_view.xml
@@ -18,7 +18,8 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="@dimen/task_item_height"
- android:orientation="horizontal">
+ android:orientation="horizontal"
+ android:clipChildren="false">
<com.android.quickstep.views.TaskThumbnailIconView
android:id="@+id/task_icon_and_thumbnail"
android:layout_width="match_parent"
diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
index bcb6343..87b4d4e 100644
--- a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
+++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
@@ -38,6 +38,7 @@
import android.content.res.Resources;
import android.graphics.Matrix;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.util.ArraySet;
import android.util.AttributeSet;
@@ -768,6 +769,7 @@
Rect endRect = new Rect();
thumbnailView.getGlobalVisibleRect(endRect);
Rect appBounds = appTarget.sourceContainerBounds;
+ RectF currentAppRect = new RectF();
SyncRtSurfaceTransactionApplierCompat surfaceApplier =
new SyncRtSurfaceTransactionApplierCompat(this);
@@ -810,17 +812,30 @@
@Override
public void onUpdate(float percent) {
- appMatrix.preScale(mScaleX.value, mScaleY.value,
+ Matrix m = new Matrix();
+ m.preScale(mScaleX.value, mScaleY.value,
appBounds.width() / 2.0f, appBounds.height() / 2.0f);
- appMatrix.postTranslate(mTranslationX.value, mTranslationY.value);
-
+ m.postTranslate(mTranslationX.value, mTranslationY.value);
+ appMatrix.preConcat(m);
params[1] = new SurfaceParams(appTarget.leash, mAlpha.value, appMatrix,
null /* windowCrop */, getLayer(appTarget, boostedMode),
0 /* cornerRadius */);
surfaceApplier.scheduleApply(params);
+
+ m.mapRect(currentAppRect, new RectF(appBounds));
+ setViewToRect(thumbnailView, new RectF(endRect), currentAppRect);
appMatrix.reset();
}
});
+ remoteAppAnim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ thumbnailView.setTranslationY(0);
+ thumbnailView.setTranslationX(0);
+ thumbnailView.setScaleX(1);
+ thumbnailView.setScaleY(1);
+ }
+ });
anim.play(remoteAppAnim);
}
@@ -886,6 +901,27 @@
}
}
+ /**
+ * Set view properties so that the view fits to the target rect.
+ *
+ * @param view view to set
+ * @param origRect original rect that view was located
+ * @param targetRect rect to set to
+ */
+ private void setViewToRect(View view, RectF origRect, RectF targetRect) {
+ float dX = targetRect.left - origRect.left;
+ float dY = targetRect.top - origRect.top;
+ view.setTranslationX(dX);
+ view.setTranslationY(dY);
+
+ float scaleX = targetRect.width() / origRect.width();
+ float scaleY = targetRect.height() / origRect.height();
+ view.setPivotX(0);
+ view.setPivotY(0);
+ view.setScaleX(scaleX);
+ view.setScaleY(scaleY);
+ }
+
@Override
public void setInsets(Rect insets) {
mInsets = insets;
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index 97fc284..be275e0 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -85,6 +85,13 @@
android:name="com.android.launcher3.uioverrides.dynamicui.WallpaperManagerCompatVL$ColorExtractionService"
tools:node="remove" />
+ <activity
+ android:name="com.android.launcher3.proxy.ProxyActivityStarter"
+ android:theme="@android:style/Theme.Translucent.NoTitleBar.Fullscreen"
+ android:launchMode="singleTask"
+ android:clearTaskOnLaunch="true"
+ android:exported="false" />
+
</application>
</manifest>
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AllAppsTipView.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AllAppsTipView.java
index 948f39e..d3042cf 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AllAppsTipView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AllAppsTipView.java
@@ -19,7 +19,6 @@
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.quickstep.logging.UserEventDispatcherExtension.ALL_APPS_PREDICTION_TIPS;
-import android.app.ActivityManager;
import android.content.Context;
import android.graphics.CornerPathEffect;
import android.graphics.Paint;
@@ -39,6 +38,7 @@
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager;
import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.FloatingHeaderView;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.compat.UserManagerCompat;
@@ -152,7 +152,7 @@
|| !launcher.isInState(ALL_APPS)
|| hasSeenAllAppsTip(launcher)
|| UserManagerCompat.getInstance(launcher).isDemoUser()
- || ActivityManager.isRunningInTestHarness()) {
+ || Utilities.IS_RUNNING_IN_TEST_HARNESS) {
return false;
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java
index fa28106..af67e1b 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java
@@ -27,11 +27,13 @@
import android.content.ComponentName;
import android.content.Context;
import android.os.Build;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.UserHandle;
import android.util.Log;
+import androidx.annotation.Nullable;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
import com.android.launcher3.model.AppLaunchTracker;
@@ -97,6 +99,7 @@
new AppPredictionContext.Builder(mContext)
.setUiSurface(client.id)
.setPredictedTargetCount(count)
+ .setExtras(getAppPredictionContextExtras(client))
.build());
predictor.registerPredictionUpdates(mContext.getMainExecutor(),
PredictionUiStateManager.INSTANCE.get(mContext).appPredictorCallback(client));
@@ -104,6 +107,15 @@
return predictor;
}
+ /**
+ * Override to add custom extras.
+ */
+ @WorkerThread
+ @Nullable
+ public Bundle getAppPredictionContextExtras(Client client){
+ return null;
+ }
+
@WorkerThread
private boolean handleMessage(Message msg) {
switch (msg.what) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
index 3a2958d..ebae1cd 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
@@ -42,6 +42,7 @@
import com.android.launcher3.uioverrides.touchcontrollers.StatusBarTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.QuickSwitchTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController;
+import com.android.launcher3.uioverrides.touchcontrollers.TransposedQuickSwitchTouchController;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.util.UiThreadHelper;
import com.android.launcher3.util.UiThreadHelper.AsyncCommand;
@@ -148,6 +149,9 @@
if (launcher.getDeviceProfile().isVerticalBarLayout()) {
list.add(new OverviewToAllAppsTouchController(launcher));
list.add(new LandscapeEdgeSwipeController(launcher));
+ if (mode.hasGestures) {
+ list.add(new TransposedQuickSwitchTouchController(launcher));
+ }
} else {
list.add(new PortraitStatesTouchController(launcher,
mode.hasGestures /* allowDragToOverview */));
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
index fa07e27..c26a1d0 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
@@ -17,13 +17,10 @@
import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
-import android.graphics.Rect;
-
import com.android.launcher3.Launcher;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.quickstep.util.ClipAnimationHelper;
import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.TaskThumbnailView;
import com.android.quickstep.views.TaskView;
/**
@@ -45,18 +42,9 @@
if (recentsView.getTaskViewCount() == 0) {
return super.getOverviewScaleAndTranslation(launcher);
}
- // Compute scale and translation y such that the most recent task view fills the screen.
- TaskThumbnailView dummyThumbnail = recentsView.getTaskViewAt(0).getThumbnail();
+ TaskView dummyTask = recentsView.getTaskViewAt(0);
ClipAnimationHelper clipAnimationHelper = new ClipAnimationHelper(launcher);
- clipAnimationHelper.fromTaskThumbnailView(dummyThumbnail, recentsView);
- Rect targetRect = new Rect();
- recentsView.getTaskSize(targetRect);
- clipAnimationHelper.updateTargetRect(targetRect);
- float toScale = clipAnimationHelper.getSourceRect().width()
- / clipAnimationHelper.getTargetRect().width();
- float toTranslationY = clipAnimationHelper.getSourceRect().centerY()
- - clipAnimationHelper.getTargetRect().centerY();
- return new ScaleAndTranslation(toScale, 0, toTranslationY);
+ return clipAnimationHelper.getOverviewFullscreenScaleAndTranslation(dummyTask);
}
@Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
index 6358109..7a6cd2d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
@@ -43,9 +43,11 @@
public class FlingAndHoldTouchController extends PortraitStatesTouchController {
private static final long PEEK_ANIM_DURATION = 100;
+ private static final float MAX_DISPLACEMENT_PERCENT = 0.75f;
private final MotionPauseDetector mMotionPauseDetector;
private final float mMotionPauseMinDisplacement;
+ private final float mMotionPauseMaxDisplacement;
private AnimatorSet mPeekAnim;
@@ -53,6 +55,7 @@
super(l, false /* allowDragToOverview */);
mMotionPauseDetector = new MotionPauseDetector(l);
mMotionPauseMinDisplacement = ViewConfiguration.get(l).getScaledTouchSlop();
+ mMotionPauseMaxDisplacement = getShiftRange() * MAX_DISPLACEMENT_PERCENT;
}
@Override
@@ -101,7 +104,9 @@
@Override
public boolean onDrag(float displacement, MotionEvent event) {
- mMotionPauseDetector.setDisallowPause(-displacement < mMotionPauseMinDisplacement);
+ float upDisplacement = -displacement;
+ mMotionPauseDetector.setDisallowPause(upDisplacement < mMotionPauseMinDisplacement
+ || upDisplacement > mMotionPauseMaxDisplacement);
mMotionPauseDetector.addPosition(displacement, event.getEventTime());
return super.onDrag(displacement, event);
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/OverviewToAllAppsTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/OverviewToAllAppsTouchController.java
index 82ab34b..73f328b 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/OverviewToAllAppsTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/OverviewToAllAppsTouchController.java
@@ -24,6 +24,7 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
+import com.android.launcher3.Utilities;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.quickstep.TouchInteractionService;
import com.android.quickstep.views.RecentsView;
@@ -52,7 +53,7 @@
// In all-apps only listen if the container cannot scroll itself
return mLauncher.getAppsView().shouldContainerScroll(ev);
} else if (mLauncher.isInState(NORMAL)) {
- return true;
+ return (ev.getEdgeFlags() & Utilities.EDGE_NAV_BAR) == 0;
} else if (mLauncher.isInState(OVERVIEW)) {
RecentsView rv = mLauncher.getOverviewPanel();
return ev.getY() > (rv.getBottom() - rv.getPaddingBottom());
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
index 3b664b7..e1dd124 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
@@ -57,7 +57,11 @@
private @Nullable TaskView mTaskToLaunch;
public QuickSwitchTouchController(Launcher launcher) {
- super(launcher, SwipeDetector.HORIZONTAL);
+ this(launcher, SwipeDetector.HORIZONTAL);
+ }
+
+ protected QuickSwitchTouchController(Launcher l, SwipeDetector.Direction dir) {
+ super(l, dir);
}
@Override
@@ -124,7 +128,7 @@
@Override
protected void updateProgress(float progress) {
super.updateProgress(progress);
- updateFullscreenProgress(progress);
+ updateFullscreenProgress(Utilities.boundToRange(progress, 0, 1));
}
private void updateFullscreenProgress(float progress) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TransposedQuickSwitchTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TransposedQuickSwitchTouchController.java
new file mode 100644
index 0000000..f1e4041
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TransposedQuickSwitchTouchController.java
@@ -0,0 +1,44 @@
+/*
+ * 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.launcher3.uioverrides.touchcontrollers;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.touch.SwipeDetector;
+
+public class TransposedQuickSwitchTouchController extends QuickSwitchTouchController {
+
+ public TransposedQuickSwitchTouchController(Launcher launcher) {
+ super(launcher, SwipeDetector.VERTICAL);
+ }
+
+ @Override
+ protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) {
+ return super.getTargetState(fromState,
+ isDragTowardPositive ^ mLauncher.getDeviceProfile().isSeascape());
+ }
+
+ @Override
+ protected float initCurrentAnimation(int animComponents) {
+ float multiplier = super.initCurrentAnimation(animComponents);
+ return mLauncher.getDeviceProfile().isSeascape() ? multiplier : -multiplier;
+ }
+
+ @Override
+ protected float getShiftRange() {
+ return mLauncher.getDeviceProfile().heightPx / 2f;
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
index e932452..434353d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
@@ -118,16 +118,15 @@
}
final RectF iconLocation = new RectF();
boolean canUseWorkspaceView = workspaceView != null && workspaceView.isAttachedToWindow();
- final FloatingIconView floatingView = canUseWorkspaceView
- ? FloatingIconView.getFloatingIconView(activity, workspaceView,
- true /* hideOriginal */, iconLocation, false /* isOpening */, null /* recycle */)
+ FloatingIconView floatingIconView = canUseWorkspaceView
+ ? recentsView.getFloatingIconView(activity, workspaceView, iconLocation)
: null;
return new HomeAnimationFactory() {
@Nullable
@Override
public View getFloatingView() {
- return floatingView;
+ return floatingIconView;
}
@NonNull
@@ -301,34 +300,19 @@
return;
}
- // Setup the clip animation helper source/target rects in the final transformed state
- // of the recents view (a scale/translationY may be applied prior to this animation
- // starting to line up the side pages during swipe up)
- float prevRvScale = recentsView.getScaleX();
- float prevRvTransY = recentsView.getTranslationY();
- float targetRvScale = endState.getOverviewScaleAndTranslation(launcher).scale;
- SCALE_PROPERTY.set(recentsView, targetRvScale);
- recentsView.setTranslationY(0);
ClipAnimationHelper clipHelper = new ClipAnimationHelper(launcher);
- float tmpCurveScale = v.getCurveScale();
- v.setCurveScale(1f);
- clipHelper.fromTaskThumbnailView(v.getThumbnail(), (RecentsView) v.getParent(), null);
- v.setCurveScale(tmpCurveScale);
- SCALE_PROPERTY.set(recentsView, prevRvScale);
- recentsView.setTranslationY(prevRvTransY);
+ LauncherState.ScaleAndTranslation fromScaleAndTranslation
+ = clipHelper.getOverviewFullscreenScaleAndTranslation(v);
+ LauncherState.ScaleAndTranslation endScaleAndTranslation
+ = endState.getOverviewScaleAndTranslation(launcher);
- if (!clipHelper.getSourceRect().isEmpty() && !clipHelper.getTargetRect().isEmpty()) {
- float fromScale = clipHelper.getSourceRect().width()
- / clipHelper.getTargetRect().width();
- float fromTranslationY = clipHelper.getSourceRect().centerY()
- - clipHelper.getTargetRect().centerY();
- Animator scale = ObjectAnimator.ofFloat(recentsView, SCALE_PROPERTY, fromScale, 1);
- Animator translateY = ObjectAnimator.ofFloat(recentsView, TRANSLATION_Y,
- fromTranslationY, 0);
- scale.setInterpolator(LINEAR);
- translateY.setInterpolator(LINEAR);
- anim.playTogether(scale, translateY);
- }
+ Animator scale = ObjectAnimator.ofFloat(recentsView, SCALE_PROPERTY,
+ fromScaleAndTranslation.scale, endScaleAndTranslation.scale);
+ Animator translateY = ObjectAnimator.ofFloat(recentsView, TRANSLATION_Y,
+ fromScaleAndTranslation.translationY, endScaleAndTranslation.translationY);
+ scale.setInterpolator(LINEAR);
+ translateY.setInterpolator(LINEAR);
+ anim.playTogether(scale, translateY);
}
@Override
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 404cfe6..f2260d6 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -175,7 +175,8 @@
RECENTS(1, STATE_SCALED_CONTROLLER_RECENTS | STATE_CAPTURE_SCREENSHOT
| STATE_SCREENSHOT_VIEW_SHOWN, true, false, ContainerType.TASKSWITCHER, true),
- NEW_TASK(0, STATE_START_NEW_TASK, false, true, ContainerType.APP, true),
+ NEW_TASK(0, STATE_START_NEW_TASK | STATE_CAPTURE_SCREENSHOT, false, true,
+ ContainerType.APP, true),
LAST_TASK(0, STATE_RESUME_LAST_TASK, false, true, ContainerType.APP, false);
@@ -319,7 +320,7 @@
mStateCallback.addCallback(STATE_RESUME_LAST_TASK | STATE_APP_CONTROLLER_RECEIVED,
this::resumeLastTask);
- mStateCallback.addCallback(STATE_START_NEW_TASK | STATE_APP_CONTROLLER_RECEIVED,
+ mStateCallback.addCallback(STATE_START_NEW_TASK | STATE_SCREENSHOT_CAPTURED,
this::startNewTask);
mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java
index 3109921..a650113 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java
@@ -35,12 +35,14 @@
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.views.BaseDragLayer;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskThumbnailView;
+import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.recents.utilities.RectFEvaluator;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@@ -280,6 +282,21 @@
}
}
+ /**
+ * Compute scale and translation y such that the specified task view fills the screen.
+ */
+ public LauncherState.ScaleAndTranslation getOverviewFullscreenScaleAndTranslation(TaskView v) {
+ TaskThumbnailView thumbnailView = v.getThumbnail();
+ RecentsView recentsView = v.getRecentsView();
+ fromTaskThumbnailView(thumbnailView, recentsView);
+ Rect taskSize = new Rect();
+ recentsView.getTaskSize(taskSize);
+ updateTargetRect(taskSize);
+ float scale = mSourceRect.width() / mTargetRect.width();
+ float translationY = mSourceRect.centerY() - mSourceRect.top - mTargetRect.centerY();
+ return new LauncherState.ScaleAndTranslation(scale, 0, translationY);
+ }
+
private void updateStackBoundsToMultiWindowTaskSize(BaseDraggingActivity activity) {
ISystemUiProxy sysUiProxy = RecentsModel.INSTANCE.get(activity).getSystemUiProxy();
if (sysUiProxy != null) {
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 525ead8..bded5ba 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
@@ -75,6 +75,7 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAnimUtils.ViewProgressProperty;
import com.android.launcher3.PagedView;
import com.android.launcher3.R;
@@ -91,6 +92,7 @@
import com.android.launcher3.util.PendingAnimation;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.ViewPool;
+import com.android.launcher3.views.FloatingIconView;
import com.android.quickstep.RecentsAnimationWrapper;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.RecentsModel.TaskThumbnailChangeListener;
@@ -288,6 +290,8 @@
private Layout mEmptyTextLayout;
private LiveTileOverlay mLiveTileOverlay;
+ private FloatingIconView mFloatingIconView;
+
private BaseActivity.MultiWindowModeChangedListener mMultiWindowModeChangedListener =
(inMultiWindowMode) -> {
if (!inMultiWindowMode && mOverviewStateEnabled) {
@@ -1704,4 +1708,10 @@
return super::onTouchEvent;
}
}
+
+ public FloatingIconView getFloatingIconView(Launcher launcher, View view, RectF iconLocation) {
+ mFloatingIconView = FloatingIconView.getFloatingIconView(launcher, view,
+ true /* hideOriginal */, iconLocation, false /* isOpening */, mFloatingIconView);
+ return mFloatingIconView;
+ }
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
index 7e15d52..a9184ec 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -33,6 +33,7 @@
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.util.FloatProperty;
@@ -59,7 +60,7 @@
private final static ColorMatrix COLOR_MATRIX = new ColorMatrix();
private final static ColorMatrix SATURATION_COLOR_MATRIX = new ColorMatrix();
- private final static Rect EMPTY_RECT = new Rect();
+ private final static RectF EMPTY_RECT_F = new RectF();
public static final Property<TaskThumbnailView, Float> DIM_ALPHA =
new FloatProperty<TaskThumbnailView>("dimAlpha") {
@@ -87,10 +88,9 @@
private final Matrix mMatrix = new Matrix();
private float mClipBottom = -1;
- private Rect mScaledInsets = new Rect();
- private Rect mCurrentDrawnInsets = new Rect();
- private float mCurrentDrawnCornerRadius;
- private boolean mIsRotated;
+ // Contains the portion of the thumbnail that is clipped when fullscreen progress = 0.
+ private RectF mClippedInsets = new RectF();
+ private TaskView.FullscreenDrawParams mFullscreenParams;
private Task mTask;
private ThumbnailData mThumbnailData;
@@ -118,7 +118,7 @@
mDimmingPaintAfterClearing.setColor(Color.BLACK);
mActivity = BaseActivity.fromContext(context);
mIsDarkTextTheme = Themes.getAttrBoolean(mActivity, R.attr.isWorkspaceDarkText);
- setCurrentDrawnInsetsAndRadius(EMPTY_RECT, mCornerRadius);
+ mFullscreenParams = new TaskView.FullscreenDrawParams(mCornerRadius);
}
public void bind(Task task) {
@@ -201,23 +201,27 @@
@Override
protected void onDraw(Canvas canvas) {
+ RectF currentDrawnInsets = mFullscreenParams.mCurrentDrawnInsets;
+ canvas.save();
+ canvas.translate(currentDrawnInsets.left, currentDrawnInsets.top);
+ canvas.scale(mFullscreenParams.mScale, mFullscreenParams.mScale);
// Draw the insets if we're being drawn fullscreen (we do this for quick switch).
drawOnCanvas(canvas,
- -mCurrentDrawnInsets.left,
- -mCurrentDrawnInsets.top,
- getMeasuredWidth() + mCurrentDrawnInsets.right,
- getMeasuredHeight() + mCurrentDrawnInsets.bottom,
- mCurrentDrawnCornerRadius);
+ -currentDrawnInsets.left,
+ -currentDrawnInsets.top,
+ getMeasuredWidth() + currentDrawnInsets.right,
+ getMeasuredHeight() + currentDrawnInsets.bottom,
+ mFullscreenParams.mCurrentDrawnCornerRadius);
+ canvas.restore();
}
- public Rect getInsetsToDrawInFullscreen(boolean isMultiWindowMode) {
- // Don't show insets in the wrong orientation or in multi window mode.
- return mIsRotated || isMultiWindowMode ? EMPTY_RECT : mScaledInsets;
+ public RectF getInsetsToDrawInFullscreen(boolean isMultiWindowMode) {
+ // Don't show insets in multi window mode.
+ return isMultiWindowMode ? EMPTY_RECT_F : mClippedInsets;
}
- public void setCurrentDrawnInsetsAndRadius(Rect insets, float radius) {
- mCurrentDrawnInsets.set(insets);
- mCurrentDrawnCornerRadius = radius;
+ public void setFullscreenParams(TaskView.FullscreenDrawParams fullscreenParams) {
+ mFullscreenParams = fullscreenParams;
invalidate();
}
@@ -275,7 +279,7 @@
}
private void updateThumbnailMatrix() {
- mIsRotated = false;
+ boolean isRotated = false;
mClipBottom = -1;
if (mBitmapShader != null && mThumbnailData != null) {
float scale = mThumbnailData.scale;
@@ -296,30 +300,28 @@
final Configuration configuration =
getContext().getResources().getConfiguration();
// Rotate the screenshot if not in multi-window mode
- mIsRotated = FeatureFlags.OVERVIEW_USE_SCREENSHOT_ORIENTATION &&
+ isRotated = FeatureFlags.OVERVIEW_USE_SCREENSHOT_ORIENTATION &&
configuration.orientation != mThumbnailData.orientation &&
!mActivity.isInMultiWindowMode() &&
mThumbnailData.windowingMode == WINDOWING_MODE_FULLSCREEN;
// Scale the screenshot to always fit the width of the card.
- thumbnailScale = mIsRotated
+ thumbnailScale = isRotated
? getMeasuredWidth() / thumbnailHeight
: getMeasuredWidth() / thumbnailWidth;
}
- mScaledInsets.set(thumbnailInsets);
- Utilities.scaleRect(mScaledInsets, thumbnailScale);
-
- if (mIsRotated) {
+ if (isRotated) {
int rotationDir = profile.isVerticalBarLayout() && !profile.isSeascape() ? -1 : 1;
mMatrix.setRotate(90 * rotationDir);
int newLeftInset = rotationDir == 1 ? thumbnailInsets.bottom : thumbnailInsets.top;
int newTopInset = rotationDir == 1 ? thumbnailInsets.left : thumbnailInsets.right;
- mMatrix.postTranslate(-newLeftInset * scale, -newTopInset * scale);
+ mClippedInsets.offsetTo(newLeftInset * scale, newTopInset * scale);
if (rotationDir == -1) {
// Crop the right/bottom side of the screenshot rather than left/top
float excessHeight = thumbnailWidth * thumbnailScale - getMeasuredHeight();
- mMatrix.postTranslate(0, -excessHeight);
+ mClippedInsets.offset(0, excessHeight);
}
+ mMatrix.postTranslate(-mClippedInsets.left, -mClippedInsets.top);
// Move the screenshot to the thumbnail window (rotation moved it out).
if (rotationDir == 1) {
mMatrix.postTranslate(mThumbnailData.thumbnail.getHeight(), 0);
@@ -327,13 +329,28 @@
mMatrix.postTranslate(0, mThumbnailData.thumbnail.getWidth());
}
} else {
- mMatrix.setTranslate(-mThumbnailData.insets.left * scale,
- -mThumbnailData.insets.top * scale);
+ mClippedInsets.offsetTo(thumbnailInsets.left * scale, thumbnailInsets.top * scale);
+ mMatrix.setTranslate(-mClippedInsets.left, -mClippedInsets.top);
}
+
+ final float widthWithInsets;
+ final float heightWithInsets;
+ if (isRotated) {
+ widthWithInsets = mThumbnailData.thumbnail.getHeight() * thumbnailScale;
+ heightWithInsets = mThumbnailData.thumbnail.getWidth() * thumbnailScale;
+ } else {
+ widthWithInsets = mThumbnailData.thumbnail.getWidth() * thumbnailScale;
+ heightWithInsets = mThumbnailData.thumbnail.getHeight() * thumbnailScale;
+ }
+ mClippedInsets.left *= thumbnailScale;
+ mClippedInsets.top *= thumbnailScale;
+ mClippedInsets.right = widthWithInsets - mClippedInsets.left - getMeasuredWidth();
+ mClippedInsets.bottom = heightWithInsets - mClippedInsets.top - getMeasuredHeight();
+
mMatrix.postScale(thumbnailScale, thumbnailScale);
mBitmapShader.setLocalMatrix(mMatrix);
- float bitmapHeight = Math.max((mIsRotated ? thumbnailWidth : thumbnailHeight)
+ float bitmapHeight = Math.max((isRotated ? thumbnailWidth : thumbnailHeight)
* thumbnailScale, 0);
if (Math.round(bitmapHeight) < getMeasuredHeight()) {
mClipBottom = bitmapHeight;
@@ -341,7 +358,7 @@
mPaint.setShader(mBitmapShader);
}
- if (mIsRotated) {
+ if (isRotated) {
// The overlay doesn't really work when the screenshot is rotated, so don't add it.
mOverlay.reset();
} else {
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 6cd46d9..c67058d 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
@@ -32,6 +32,7 @@
import android.content.res.Resources;
import android.graphics.Outline;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
@@ -166,7 +167,7 @@
private float mCurveScale;
private float mZoomScale;
private float mFullscreenProgress;
- private final Rect mCurrentDrawnInsets = new Rect();
+ private final FullscreenDrawParams mCurrentFullscreenParams;
private final float mCornerRadius;
private final float mWindowCornerRadius;
private final BaseDraggingActivity mActivity;
@@ -214,7 +215,8 @@
});
mCornerRadius = TaskCornerRadius.get(context);
mWindowCornerRadius = QuickStepContract.getWindowCornerRadius(context.getResources());
- mOutlineProvider = new TaskOutlineProvider(getResources(), mCornerRadius);
+ mCurrentFullscreenParams = new FullscreenDrawParams(mCornerRadius);
+ mOutlineProvider = new TaskOutlineProvider(getResources(), mCurrentFullscreenParams);
setOutlineProvider(mOutlineProvider);
}
@@ -473,7 +475,12 @@
@Override
public void onRecycle() {
resetViewTransforms();
- setFullscreenProgress(0);
+ // Clear any references to the thumbnail (it will be re-read either from the cache or the
+ // system on next bind)
+ mSnapshotView.setThumbnail(mTask, null);
+ if (mTask != null) {
+ mTask.thumbnail = null;
+ }
}
@Override
@@ -540,26 +547,26 @@
private static final class TaskOutlineProvider extends ViewOutlineProvider {
private final int mMarginTop;
- private final Rect mInsets = new Rect();
- private float mRadius;
+ private FullscreenDrawParams mFullscreenParams;
- TaskOutlineProvider(Resources res, float radius) {
+ TaskOutlineProvider(Resources res, FullscreenDrawParams fullscreenParams) {
mMarginTop = res.getDimensionPixelSize(R.dimen.task_thumbnail_top_margin);
- mRadius = radius;
+ mFullscreenParams = fullscreenParams;
}
- public void setCurrentDrawnInsetsAndRadius(Rect insets, float radius) {
- mInsets.set(insets);
- mRadius = radius;
+ public void setFullscreenParams(FullscreenDrawParams params) {
+ mFullscreenParams = params;
}
@Override
public void getOutline(View view, Outline outline) {
- outline.setRoundRect(-mInsets.left,
- mMarginTop - mInsets.top,
- view.getWidth() + mInsets.right,
- view.getHeight() + mInsets.bottom,
- mRadius);
+ RectF insets = mFullscreenParams.mCurrentDrawnInsets;
+ float scale = mFullscreenParams.mScale;
+ outline.setRoundRect(0,
+ (int) (mMarginTop * scale),
+ (int) ((insets.left + view.getWidth() + insets.right) * scale),
+ (int) ((insets.top + view.getHeight() + insets.bottom) * scale),
+ mFullscreenParams.mCurrentDrawnCornerRadius);
}
}
@@ -658,17 +665,25 @@
TaskThumbnailView thumbnail = getThumbnail();
boolean isMultiWindowMode = mActivity.getDeviceProfile().isMultiWindowMode;
- Rect insets = thumbnail.getInsetsToDrawInFullscreen(isMultiWindowMode);
- mCurrentDrawnInsets.set((int) (insets.left * mFullscreenProgress),
- (int) (insets.top * mFullscreenProgress),
- (int) (insets.right * mFullscreenProgress),
- (int) (insets.bottom * mFullscreenProgress));
+ RectF insets = thumbnail.getInsetsToDrawInFullscreen(isMultiWindowMode);
+ float currentInsetsLeft = insets.left * mFullscreenProgress;
+ float currentInsetsRight = insets.right * mFullscreenProgress;
+ mCurrentFullscreenParams.setInsets(currentInsetsLeft,
+ insets.top * mFullscreenProgress,
+ currentInsetsRight,
+ insets.bottom * mFullscreenProgress);
float fullscreenCornerRadius = isMultiWindowMode ? 0 : mWindowCornerRadius;
- float cornerRadius = Utilities.mapRange(mFullscreenProgress, mCornerRadius,
- fullscreenCornerRadius) / getRecentsView().getScaleX();
+ mCurrentFullscreenParams.setCornerRadius(Utilities.mapRange(mFullscreenProgress,
+ mCornerRadius, fullscreenCornerRadius) / getRecentsView().getScaleX());
+ // We scaled the thumbnail to fit the content (excluding insets) within task view width.
+ // Now that we are drawing left/right insets again, we need to scale down to fit them.
+ if (getWidth() > 0) {
+ mCurrentFullscreenParams.setScale(getWidth()
+ / (getWidth() + currentInsetsLeft + currentInsetsRight));
+ }
- thumbnail.setCurrentDrawnInsetsAndRadius(mCurrentDrawnInsets, cornerRadius);
- mOutlineProvider.setCurrentDrawnInsetsAndRadius(mCurrentDrawnInsets, cornerRadius);
+ thumbnail.setFullscreenParams(mCurrentFullscreenParams);
+ mOutlineProvider.setFullscreenParams(mCurrentFullscreenParams);
invalidateOutline();
}
@@ -686,4 +701,30 @@
}
return mShowScreenshot;
}
+
+ /**
+ * We update and subsequently draw these in {@link #setFullscreenProgress(float)}.
+ */
+ static class FullscreenDrawParams {
+ RectF mCurrentDrawnInsets = new RectF();
+ float mCurrentDrawnCornerRadius;
+ /** The current scale we apply to the thumbnail to adjust for new left/right insets. */
+ float mScale = 1;
+
+ public FullscreenDrawParams(float cornerRadius) {
+ setCornerRadius(cornerRadius);
+ }
+
+ public void setInsets(float left, float top, float right, float bottom) {
+ mCurrentDrawnInsets.set(left, top, right, bottom);
+ }
+
+ public void setCornerRadius(float cornerRadius) {
+ mCurrentDrawnCornerRadius = cornerRadius;
+ }
+
+ public void setScale(float scale) {
+ mScale = scale;
+ }
+ }
}
diff --git a/quickstep/res/layout/task.xml b/quickstep/res/layout/task.xml
index 1d1c272..ecf1b0a 100644
--- a/quickstep/res/layout/task.xml
+++ b/quickstep/res/layout/task.xml
@@ -35,30 +35,42 @@
android:focusable="false"
android:importantForAccessibility="no" />
- <com.android.quickstep.views.DigitalWellBeingToast
- android:id="@+id/digital_well_being_toast"
+ <LinearLayout
android:layout_width="match_parent"
- android:layout_height="48dp"
- android:importantForAccessibility="noHideDescendants"
- android:background="@drawable/bg_wellbeing_toast"
- android:layout_gravity="bottom"
- android:gravity="center"
- android:visibility="gone">
- <ImageView
- android:id="@+id/digital_well_being_hourglass"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:layout_marginEnd="8dp"
- android:src="@drawable/ic_hourglass_top"
- />
- <TextView
- android:id="@+id/digital_well_being_remaining_time"
- android:layout_width="wrap_content"
- android:layout_height="24dp"
- android:fontFamily="sans-serif"
- android:textSize="14sp"
- android:textColor="@android:color/white"
- android:gravity="center_vertical"
- />
- </com.android.quickstep.views.DigitalWellBeingToast>
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_gravity="bottom|center_horizontal">
+ <FrameLayout
+ android:id="@+id/proactive_suggest_container"
+ android:layout_width="match_parent"
+ android:layout_height="36dp"
+ android:gravity="center"
+ android:visibility="gone"
+ />
+ <com.android.quickstep.views.DigitalWellBeingToast
+ android:id="@+id/digital_well_being_toast"
+ android:layout_width="match_parent"
+ android:layout_height="48dp"
+ android:importantForAccessibility="noHideDescendants"
+ android:background="@drawable/bg_wellbeing_toast"
+ android:gravity="center"
+ android:visibility="gone">
+ <ImageView
+ android:id="@+id/digital_well_being_hourglass"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_marginEnd="8dp"
+ android:src="@drawable/ic_hourglass_top"
+ />
+ <TextView
+ android:id="@+id/digital_well_being_remaining_time"
+ android:layout_width="wrap_content"
+ android:layout_height="24dp"
+ android:fontFamily="sans-serif"
+ android:textSize="14sp"
+ android:textColor="@android:color/white"
+ android:gravity="center_vertical"
+ />
+ </com.android.quickstep.views.DigitalWellBeingToast>
+ </LinearLayout>
</com.android.quickstep.views.TaskView>
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
index 7dd4df7..91c4601 100644
--- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
@@ -476,6 +476,16 @@
float shapeRevealDuration = APP_LAUNCH_DURATION * SHAPE_PROGRESS_DURATION;
+ final float startCrop;
+ final float endCrop;
+ if (mDeviceProfile.isVerticalBarLayout()) {
+ startCrop = windowTargetBounds.height();
+ endCrop = windowTargetBounds.width();
+ } else {
+ startCrop = windowTargetBounds.width();
+ endCrop = windowTargetBounds.height();
+ }
+
final float windowRadius = mDeviceProfile.isMultiWindowMode
? 0 : getWindowCornerRadius(mLauncher.getResources());
appAnimator.addUpdateListener(new MultiValueUpdateListener() {
@@ -485,10 +495,10 @@
EXAGGERATED_EASE);
FloatProp mIconAlpha = new FloatProp(1f, 0f, APP_LAUNCH_ALPHA_START_DELAY,
alphaDuration, LINEAR);
- FloatProp mCropHeight = new FloatProp(windowTargetBounds.width(),
- windowTargetBounds.height(), 0, APP_LAUNCH_DURATION, EXAGGERATED_EASE);
- FloatProp mWindowRadius = new FloatProp(windowTargetBounds.width() / 2f,
- windowRadius, 0, APP_LAUNCH_DURATION, EXAGGERATED_EASE);
+ FloatProp mCroppedSize = new FloatProp(startCrop, endCrop, 0, APP_LAUNCH_DURATION,
+ EXAGGERATED_EASE);
+ FloatProp mWindowRadius = new FloatProp(startCrop / 2f, windowRadius, 0,
+ APP_LAUNCH_DURATION, EXAGGERATED_EASE);
@Override
public void onUpdate(float percent) {
@@ -496,10 +506,16 @@
float iconWidth = bounds.width() * mIconScale.value;
float iconHeight = bounds.height() * mIconScale.value;
- // Animate the window crop so that it starts off as a square, and then reveals
- // horizontally.
- int windowWidth = windowTargetBounds.width();
- int windowHeight = (int) mCropHeight.value;
+ // Animate the window crop so that it starts off as a square.
+ final int windowWidth;
+ final int windowHeight;
+ if (mDeviceProfile.isVerticalBarLayout()) {
+ windowWidth = (int) mCroppedSize.value;
+ windowHeight = windowTargetBounds.height();
+ } else {
+ windowWidth = windowTargetBounds.width();
+ windowHeight = (int) mCroppedSize.value;
+ }
crop.set(0, 0, windowWidth, windowHeight);
// Scale the app window to match the icon size.
@@ -522,6 +538,7 @@
float transY0 = temp.top - offsetY;
float croppedHeight = (windowTargetBounds.height() - crop.height()) * scale;
+ float croppedWidth = (windowTargetBounds.width() - crop.width()) * scale;
SurfaceParams[] params = new SurfaceParams[targets.length];
for (int i = targets.length - 1; i >= 0; i--) {
RemoteAnimationTargetCompat target = targets[i];
@@ -535,7 +552,11 @@
alpha = 1f - mIconAlpha.value;
cornerRadius = mWindowRadius.value;
matrix.mapRect(currentBounds, targetBounds);
- currentBounds.bottom -= croppedHeight;
+ if (mDeviceProfile.isVerticalBarLayout()) {
+ currentBounds.right -= croppedWidth;
+ } else {
+ currentBounds.bottom -= croppedHeight;
+ }
mFloatingView.update(currentBounds, mIconAlpha.value, percent, 0f,
cornerRadius * scale, true /* isOpening */);
} else {
diff --git a/quickstep/src/com/android/launcher3/proxy/ProxyActivityStarter.java b/quickstep/src/com/android/launcher3/proxy/ProxyActivityStarter.java
new file mode 100644
index 0000000..e302b4f
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/proxy/ProxyActivityStarter.java
@@ -0,0 +1,82 @@
+/*
+ * 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.launcher3.proxy;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentSender.SendIntentException;
+import android.os.Bundle;
+import android.util.Log;
+
+public class ProxyActivityStarter extends Activity {
+
+ private static final String TAG = "ProxyActivityStarter";
+
+ public static final String EXTRA_PARAMS = "start-activity-params";
+
+ private StartActivityParams mParams;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setVisible(false);
+
+ mParams = getIntent().getParcelableExtra(EXTRA_PARAMS);
+ if (mParams == null) {
+ Log.d(TAG, "Proxy activity started without params");
+ finishAndRemoveTask();
+ return;
+ }
+
+ if (savedInstanceState != null) {
+ // Already started the activity. Just wait for the result.
+ return;
+ }
+
+ if (mParams.intent != null) {
+ startActivityForResult(mParams.intent, mParams.requestCode, mParams.options);
+ return;
+ } else if (mParams.intentSender != null) {
+ try {
+ startIntentSenderForResult(mParams.intentSender, mParams.requestCode,
+ mParams.fillInIntent, mParams.flagsMask, mParams.flagsValues,
+ mParams.extraFlags,
+ mParams.options);
+ return;
+ } catch (SendIntentException e) {
+ mParams.deliverResult(this, RESULT_CANCELED, null);
+ }
+ }
+ finishAndRemoveTask();
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == mParams.requestCode) {
+ mParams.deliverResult(this, resultCode, data);
+ }
+ finishAndRemoveTask();
+ }
+
+ public static Intent getLaunchIntent(Context context, StartActivityParams params) {
+ return new Intent(context, ProxyActivityStarter.class)
+ .putExtra(EXTRA_PARAMS, params)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
+ | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/proxy/StartActivityParams.java b/quickstep/src/com/android/launcher3/proxy/StartActivityParams.java
new file mode 100644
index 0000000..1e8bd93
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/proxy/StartActivityParams.java
@@ -0,0 +1,103 @@
+/*
+ * 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.launcher3.proxy;
+
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.app.PendingIntent.CanceledException;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+public class StartActivityParams implements Parcelable {
+
+ private static final String TAG = "StartActivityParams";
+
+ private final PendingIntent mCallback;
+ public final int requestCode;
+
+ public Intent intent;
+
+ public IntentSender intentSender;
+ public Intent fillInIntent;
+ public int flagsMask;
+ public int flagsValues;
+ public int extraFlags;
+ public Bundle options;
+
+ public StartActivityParams(Activity activity, int requestCode) {
+ mCallback = activity.createPendingResult(requestCode, new Intent(),
+ PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT);
+ this.requestCode = requestCode;
+ }
+
+ private StartActivityParams(Parcel parcel) {
+ mCallback = parcel.readTypedObject(PendingIntent.CREATOR);
+ requestCode = parcel.readInt();
+ intent = parcel.readTypedObject(Intent.CREATOR);
+
+ intentSender = parcel.readTypedObject(IntentSender.CREATOR);
+ fillInIntent = parcel.readTypedObject(Intent.CREATOR);
+ flagsMask = parcel.readInt();
+ flagsValues = parcel.readInt();
+ extraFlags = parcel.readInt();
+ options = parcel.readBundle();
+ }
+
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeTypedObject(mCallback, flags);
+ parcel.writeInt(requestCode);
+ parcel.writeTypedObject(intent, flags);
+
+ parcel.writeTypedObject(intentSender, flags);
+ parcel.writeTypedObject(fillInIntent, flags);
+ parcel.writeInt(flagsMask);
+ parcel.writeInt(flagsValues);
+ parcel.writeInt(extraFlags);
+ parcel.writeBundle(options);
+ }
+
+ public void deliverResult(Context context, int resultCode, Intent data) {
+ try {
+ mCallback.send(context, resultCode, data);
+ } catch (CanceledException e) {
+ Log.e(TAG, "Unable to send back result", e);
+ }
+ }
+
+ public static final Parcelable.Creator<StartActivityParams> CREATOR =
+ new Parcelable.Creator<StartActivityParams>() {
+ public StartActivityParams createFromParcel(Parcel source) {
+ return new StartActivityParams(source);
+ }
+
+ public StartActivityParams[] newArray(int size) {
+ return new StartActivityParams[size];
+ }
+ };
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
index 77ac35c..4891746 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
@@ -16,6 +16,8 @@
package com.android.launcher3.uioverrides;
+import static android.app.Activity.RESULT_CANCELED;
+
import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
import static com.android.launcher3.AbstractFloatingView.TYPE_HIDE_BACK_BUTTON;
import static com.android.launcher3.LauncherState.ALL_APPS;
@@ -31,6 +33,9 @@
import android.animation.ValueAnimator;
import android.app.Activity;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.os.Bundle;
import android.os.CancellationSignal;
import android.util.Base64;
@@ -43,6 +48,8 @@
import com.android.launcher3.QuickstepAppTransitionManagerImpl;
import com.android.launcher3.Utilities;
import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.proxy.ProxyActivityStarter;
+import com.android.launcher3.proxy.StartActivityParams;
import com.android.quickstep.OverviewInteractionState;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.SysUINavigationMode;
@@ -192,6 +199,40 @@
return true;
}
+ public static boolean startIntentSenderForResult(Activity activity, IntentSender intent,
+ int requestCode, Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags,
+ Bundle options) {
+ StartActivityParams params = new StartActivityParams(activity, requestCode);
+ params.intentSender = intent;
+ params.fillInIntent = fillInIntent;
+ params.flagsMask = flagsMask;
+ params.flagsValues = flagsValues;
+ params.extraFlags = extraFlags;
+ params.options = options;
+ ((Context) activity).startActivity(ProxyActivityStarter.getLaunchIntent(activity, params));
+ return true;
+ }
+
+ public static boolean startActivityForResult(Activity activity, Intent intent, int requestCode,
+ Bundle options) {
+ StartActivityParams params = new StartActivityParams(activity, requestCode);
+ params.intent = intent;
+ params.options = options;
+ activity.startActivity(ProxyActivityStarter.getLaunchIntent(activity, params));
+ return true;
+ }
+
+ /**
+ * Removes any active ProxyActivityStarter task and sends RESULT_CANCELED to Launcher.
+ *
+ * ProxyActivityStarter is started with clear task to reset the task after which it removes the
+ * task itself.
+ */
+ public static void resetPendingActivityResults(Launcher launcher, int requestCode) {
+ launcher.onActivityResult(requestCode, RESULT_CANCELED, null);
+ launcher.startActivity(ProxyActivityStarter.getLaunchIntent(launcher, null));
+ }
+
public static ScaleAndTranslation getOverviewScaleAndTranslationForNormalState(Launcher l) {
if (SysUINavigationMode.getMode(l) == Mode.NO_BUTTON) {
float offscreenTranslationX = l.getDeviceProfile().widthPx
diff --git a/quickstep/src/com/android/launcher3/uioverrides/WallpaperColorInfo.java b/quickstep/src/com/android/launcher3/uioverrides/WallpaperColorInfo.java
index 8218517..711e59a 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/WallpaperColorInfo.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/WallpaperColorInfo.java
@@ -35,6 +35,10 @@
@TargetApi(Build.VERSION_CODES.P)
public class WallpaperColorInfo implements OnColorsChangedListener {
+ private static final int MAIN_COLOR_LIGHT = 0xffdadce0;
+ private static final int MAIN_COLOR_DARK = 0xff202124;
+ private static final int MAIN_COLOR_REGULAR = 0xff000000;
+
private static final Object sInstanceLock = new Object();
private static WallpaperColorInfo sInstance;
@@ -79,6 +83,10 @@
return mExtractionInfo.supportsDarkText;
}
+ public boolean isMainColorDark() {
+ return mExtractionInfo.mainColor == MAIN_COLOR_DARK;
+ }
+
@Override
public void onColorsChanged(WallpaperColors colors, int which) {
if ((which & FLAG_SYSTEM) != 0) {
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index 06a36c9..3538373 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -120,6 +120,16 @@
}
@Override
+ public void onTaskRemoved(int taskId) {
+ for (int i = mTasks.size() - 1; i >= 0; i--) {
+ if (mTasks.get(i).key.id == taskId) {
+ mTasks.remove(i);
+ return;
+ }
+ }
+ }
+
+ @Override
public synchronized void onActivityPinned(String packageName, int userId, int taskId,
int stackId) {
mChangeId++;
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index 675cfe2..9f12484 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -166,6 +166,12 @@
}
}
+ @Override
+ public void onTaskRemoved(int taskId) {
+ Task.TaskKey dummyKey = new Task.TaskKey(taskId, 0, null, null, 0, 0);
+ mThumbnailCache.remove(dummyKey);
+ }
+
public void setSystemUiProxy(ISystemUiProxy systemUiProxy) {
mSystemUiProxy = systemUiProxy;
}
diff --git a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
index d05196b..57c5a27 100644
--- a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
+++ b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
@@ -187,6 +187,13 @@
}
/**
+ * Removes the cached thumbnail for the given task.
+ */
+ public void remove(Task.TaskKey key) {
+ mCache.remove(key);
+ }
+
+ /**
* @return The cache size.
*/
public int getCacheSize() {
diff --git a/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java b/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java
index 47ce44c..5e20e56 100644
--- a/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java
+++ b/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java
@@ -79,6 +79,8 @@
@After
public void tearDown() throws Throwable {
+ AppLaunchTracker.INSTANCE.initializeForTesting(null);
+ PredictionUiStateManager.INSTANCE.initializeForTesting(null);
mDevice.unfreezeRotation();
}
diff --git a/res/drawable-v28/round_rect_folder.xml b/res/drawable-v28/round_rect_folder.xml
new file mode 100644
index 0000000..0403be0
--- /dev/null
+++ b/res/drawable-v28/round_rect_folder.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="?attr/folderFillColor" />
+ <corners android:radius="?android:attr/dialogCornerRadius" />
+</shape>
diff --git a/res/drawable/round_rect_folder.xml b/res/drawable/round_rect_folder.xml
new file mode 100644
index 0000000..8b3d06c
--- /dev/null
+++ b/res/drawable/round_rect_folder.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="?attr/folderFillColor" />
+ <corners android:radius="@dimen/bg_round_rect_radius" />
+</shape>
diff --git a/res/layout/folder_application.xml b/res/layout/folder_application.xml
index de861a0..c156e11 100644
--- a/res/layout/folder_application.xml
+++ b/res/layout/folder_application.xml
@@ -18,5 +18,6 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto"
style="@style/BaseIcon"
+ android:textColor="?attr/folderTextColor"
android:includeFontPadding="false"
launcher:iconDisplay="folder" />
diff --git a/res/layout/user_folder_icon_normalized.xml b/res/layout/user_folder_icon_normalized.xml
index 2e6ce94..835fee2 100644
--- a/res/layout/user_folder_icon_normalized.xml
+++ b/res/layout/user_folder_icon_normalized.xml
@@ -18,7 +18,7 @@
xmlns:launcher="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:background="@drawable/round_rect_primary"
+ android:background="@drawable/round_rect_folder"
android:elevation="5dp"
android:orientation="vertical" >
diff --git a/res/values-v28/styles.xml b/res/values-v28/styles.xml
new file mode 100644
index 0000000..7df9ce5
--- /dev/null
+++ b/res/values-v28/styles.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+* Copyright (C) 2019 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+-->
+<resources>
+ <style name="TextHeadline" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle" >
+ <item name="android:textFontWeight">400</item>
+ </style>
+</resources>
diff --git a/res/values-v29/styles.xml b/res/values-v29/styles.xml
new file mode 100644
index 0000000..f623823
--- /dev/null
+++ b/res/values-v29/styles.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+* Copyright (C) 2019 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+-->
+
+<resources>
+ <!-- Launcher theme -->
+ <style name="BaseLauncherTheme" parent="@android:style/Theme.DeviceDefault.DayNight">
+ <item name="android:colorBackgroundCacheHint">@null</item>
+ <item name="android:colorEdgeEffect">#FF757575</item>
+ <item name="android:windowActionBar">false</item>
+ <item name="android:windowBackground">@android:color/transparent</item>
+ <item name="android:windowNoTitle">true</item>
+ <item name="android:windowShowWallpaper">true</item>
+ <item name="folderTextColor">?attr/workspaceTextColor</item>
+ <item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
+ </style>
+</resources>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 43194d5..69b8c8a 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -36,8 +36,10 @@
<attr name="loadingIconColor" format="color" />
<attr name="folderDotColor" format="color" />
+ <attr name="folderFillColor" format="color" />
<attr name="folderIconRadius" format="float" />
<attr name="folderIconBorderColor" format="color" />
+ <attr name="folderTextColor" format="color" />
<!-- BubbleTextView specific attributes. -->
<declare-styleable name="BubbleTextView">
@@ -55,7 +57,7 @@
<!-- BubbleTextView specific attributes. -->
<declare-styleable name="FolderIconPreview">
- <attr name="android:colorPrimary" />
+ <attr name="folderFillColor" />
<attr name="folderIconBorderColor" />
<attr name="folderDotColor" />
</declare-styleable>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 7932c6d..881f65d 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -19,13 +19,14 @@
<resources>
<!-- Launcher theme -->
- <style name="BaseLauncherTheme" parent="@android:style/Theme.DeviceDefault.DayNight">
+ <style name="BaseLauncherTheme" parent="@android:style/Theme.DeviceDefault.Light">
<item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:colorEdgeEffect">#FF757575</item>
<item name="android:windowActionBar">false</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowShowWallpaper">true</item>
+ <item name="folderTextColor">?attr/workspaceTextColor</item>
</style>
<style name="LauncherTheme" parent="@style/BaseLauncherTheme">
@@ -44,7 +45,9 @@
<item name="workspaceStatusBarScrim">@drawable/workspace_bg</item>
<item name="widgetsTheme">@style/WidgetContainerTheme</item>
<item name="folderDotColor">?android:attr/colorPrimary</item>
+ <item name="folderFillColor">#CDFFFFFF</item>
<item name="folderIconBorderColor">?android:attr/colorPrimary</item>
+ <item name="folderTextColor">#FF212121</item>
<item name="loadingIconColor">#CCFFFFFF</item>
<item name="android:windowTranslucentStatus">false</item>
@@ -54,6 +57,11 @@
<item name="android:navigationBarColor">#00000000</item>
</style>
+ <style name="LauncherTheme.DarkMainColor" parent="@style/LauncherTheme">
+ <item name="folderFillColor">#FF3C4043</item> <!-- 100% GM2 800 -->
+ <item name="folderTextColor">?attr/workspaceTextColor</item>
+ </style>
+
<style name="LauncherTheme.DarkText" parent="@style/LauncherTheme">
<item name="workspaceTextColor">#FF212121</item>
<item name="allAppsInterimScrimAlpha">128</item>
@@ -63,7 +71,9 @@
<item name="isWorkspaceDarkText">true</item>
<item name="workspaceStatusBarScrim">@null</item>
<item name="folderDotColor">#FF464646</item>
+ <item name="folderFillColor">#CDFFFFFF</item>
<item name="folderIconBorderColor">#FF80868B</item>
+ <item name="folderTextColor">?attr/workspaceTextColor</item>
</style>
<style name="LauncherTheme.Dark" parent="@style/LauncherTheme">
@@ -81,13 +91,22 @@
<item name="popupColorTertiary">#757575</item> <!-- Gray 600 -->
<item name="widgetsTheme">@style/WidgetContainerTheme.Dark</item>
<item name="folderDotColor">#FF464646</item>
+ <item name="folderFillColor">#DD3C4043</item> <!-- 87% GM2 800 -->
<item name="folderIconBorderColor">#FF80868B</item>
+ <item name="folderTextColor">@android:color/white</item>
<item name="isMainColorDark">true</item>
<item name="loadingIconColor">#99FFFFFF</item>
</style>
+ <style name="LauncherTheme.Dark.DarkMainColor" parent="@style/LauncherTheme.Dark">
+ <item name="folderFillColor">#FF3C4043</item> <!-- 100% GM2 800 -->
+ <item name="folderTextColor">@android:color/white</item>
+ </style>
+
<style name="LauncherTheme.Dark.DarkText" parent="@style/LauncherTheme.Dark">
<item name="allAppsInterimScrimAlpha">25</item>
+ <item name="folderFillColor">#CDFFFFFF</item>
+ <item name="folderTextColor">?attr/workspaceTextColor</item>
<item name="workspaceTextColor">#FF212121</item>
<item name="workspaceShadowColor">@android:color/transparent</item>
<item name="workspaceAmbientShadowColor">@android:color/transparent</item>
@@ -99,8 +118,11 @@
<!-- A derivative project can extend these themes to customize the application theme without
affecting the base theme -->
<style name="AppTheme" parent="@style/LauncherTheme" />
+ <style name="AppTheme.DarkMainColor" parent="@style/LauncherTheme.DarkMainColor" />
<style name="AppTheme.DarkText" parent="@style/LauncherTheme.DarkText" />
+
<style name="AppTheme.Dark" parent="@style/LauncherTheme.Dark" />
+ <style name="AppTheme.Dark.DarkMainColor" parent="@style/LauncherTheme.Dark.DarkMainColor" />
<style name="AppTheme.Dark.DarkText" parent="@style/LauncherTheme.Dark.DarkText" />
<style name="AppItemActivityTheme" parent="@android:style/Theme.Material.Light.Dialog.Alert">
@@ -157,7 +179,7 @@
<item name="android:shadowRadius">0</item>
</style>
- <!-- Icon displayed on the worksapce -->
+ <!-- Icon displayed on the workspace -->
<style name="BaseIcon.Workspace" >
<item name="android:shadowRadius">2.0</item>
<item name="android:shadowColor">?attr/workspaceShadowColor</item>
diff --git a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
index 0250c36..d949141 100644
--- a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
+++ b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
@@ -8,7 +8,6 @@
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
-import android.os.Handler;
import android.util.Log;
import com.android.launcher3.LauncherSettings.Favorites;
@@ -34,16 +33,8 @@
final int[] oldIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_OLD_IDS);
final int[] newIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS);
- if (oldIds.length == newIds.length) {
- final PendingResult asyncResult = goAsync();
- new Handler(LauncherModel.getWorkerLooper())
- .postAtFrontOfQueue(new Runnable() {
- @Override
- public void run() {
- restoreAppWidgetIds(context, oldIds, newIds);
- asyncResult.finish();
- }
- });
+ if (oldIds != null && newIds != null && oldIds.length == newIds.length) {
+ RestoreDbTask.setRestoredAppWidgetIds(context, oldIds, newIds);
} else {
Log.e(TAG, "Invalid host restored received");
}
@@ -54,7 +45,7 @@
* Updates the app widgets whose id has changed during the restore process.
*/
@WorkerThread
- static void restoreAppWidgetIds(Context context, int[] oldWidgetIds, int[] newWidgetIds) {
+ public static void restoreAppWidgetIds(Context context, int[] oldWidgetIds, int[] newWidgetIds) {
AppWidgetHost appWidgetHost = new LauncherAppWidgetHost(context);
if (FeatureFlags.GO_DISABLE_WIDGETS) {
Log.e(TAG, "Skipping widget ID remap as widgets not supported");
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 3eb01e6..09fb244 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -845,7 +845,8 @@
* width in {@link DeviceProfile#calculateCellWidth(int, int)}.
*/
public int getUnusedHorizontalSpace() {
- return getMeasuredWidth() - getPaddingLeft() - getPaddingRight() - (mCountX * mCellWidth);
+ return (mRotationMode.isTransposed ? getMeasuredHeight() : getMeasuredWidth())
+ - getPaddingLeft() - getPaddingRight() - (mCountX * mCellWidth);
}
public Drawable getScrimBackground() {
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 823fb6b..40eb912 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -72,7 +72,6 @@
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
-import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.OvershootInterpolator;
import android.widget.Toast;
@@ -191,6 +190,8 @@
private static final String RUNTIME_STATE = "launcher.state";
// Type: PendingRequestArgs
private static final String RUNTIME_STATE_PENDING_REQUEST_ARGS = "launcher.request_args";
+ // Type: int
+ private static final String RUNTIME_STATE_PENDING_REQUEST_CODE = "launcher.request_code";
// Type: ActivityResultInfo
private static final String RUNTIME_STATE_PENDING_ACTIVITY_RESULT = "launcher.activity_result";
// Type: SparseArray<Parcelable>
@@ -264,6 +265,8 @@
* {@link #startActivityForResult(Intent, int)} or {@link #requestPermissions(String[], int)}
*/
private PendingRequestArgs mPendingRequestArgs;
+ // Request id for any pending activity result
+ private int mPendingActivityRequestCode = -1;
public ViewGroupFocusHelper mFocusHandler;
@@ -303,6 +306,7 @@
LauncherAppState app = LauncherAppState.getInstance(this);
mOldConfig = new Configuration(getResources().getConfiguration());
mModel = app.setLauncher(this);
+ mRotationHelper = new RotationHelper(this);
InvariantDeviceProfile idp = app.getInvariantDeviceProfile();
initDeviceProfile(idp);
idp.addOnChangeListener(this);
@@ -325,7 +329,6 @@
setupViews();
mPopupDataProvider = new PopupDataProvider(this);
- mRotationHelper = new RotationHelper(this);
mAppTransitionManager = LauncherAppTransitionManager.newInstance(this);
boolean internalStateHandled = InternalStateHandler.handleCreate(this, getIntent());
@@ -396,12 +399,6 @@
}
}
});
-
- if (FeatureFlags.FAKE_LANDSCAPE_UI.get()) {
- WindowManager.LayoutParams lp = getWindow().getAttributes();
- lp.rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
- getWindow().setAttributes(lp);
- }
}
@Override
@@ -428,9 +425,13 @@
super.onConfigurationChanged(newConfig);
}
+ private boolean supportsFakeLandscapeUI() {
+ return FeatureFlags.FAKE_LANDSCAPE_UI.get() && !mRotationHelper.homeScreenCanRotate();
+ }
+
@Override
- protected void reapplyUi() {
- if (FeatureFlags.FAKE_LANDSCAPE_UI.get()) {
+ public void reapplyUi() {
+ if (supportsFakeLandscapeUI()) {
mRotationMode = mStableDeviceProfile == null
? RotationMode.NORMAL : UiFactory.getRotationMode(mDeviceProfile);
}
@@ -486,7 +487,8 @@
mDeviceProfile = mDeviceProfile.getMultiWindowProfile(this, mwSize);
}
- if (FeatureFlags.FAKE_LANDSCAPE_UI.get() && mDeviceProfile.isVerticalBarLayout()
+ if (supportsFakeLandscapeUI()
+ && mDeviceProfile.isVerticalBarLayout()
&& !mDeviceProfile.isMultiWindowMode) {
mStableDeviceProfile = mDeviceProfile.inv.portraitProfile;
mRotationMode = UiFactory.getRotationMode(mDeviceProfile);
@@ -763,6 +765,7 @@
@Override
public void onActivityResult(
final int requestCode, final int resultCode, final Intent data) {
+ mPendingActivityRequestCode = -1;
handleActivityResult(requestCode, resultCode, data);
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onActivityResult(requestCode, resultCode, data);
@@ -891,9 +894,21 @@
UiFactory.onLauncherStateOrResumeChanged(this);
AppLaunchTracker.INSTANCE.get(this).onReturnedToHome();
+ resetPendingActivityResultIfNeeded();
}
}
+ private void resetPendingActivityResultIfNeeded() {
+ if (hasBeenResumed() && mPendingActivityRequestCode != -1 && isInState(NORMAL)) {
+ UiFactory.resetPendingActivityResults(this, mPendingActivityRequestCode);
+ }
+ }
+
+ protected void onStateSet(LauncherState state) {
+ getAppWidgetHost().setResumed(state == LauncherState.NORMAL);
+ resetPendingActivityResultIfNeeded();
+ }
+
@Override
protected void onResume() {
RaceConditionTracker.onEvent(ON_RESUME_EVT, ENTER);
@@ -1010,6 +1025,7 @@
if (requestArgs != null) {
setWaitingForResult(requestArgs);
}
+ mPendingActivityRequestCode = savedState.getInt(RUNTIME_STATE_PENDING_REQUEST_CODE);
mPendingActivityResult = savedState.getParcelable(RUNTIME_STATE_PENDING_ACTIVITY_RESULT);
@@ -1393,6 +1409,8 @@
if (mPendingRequestArgs != null) {
outState.putParcelable(RUNTIME_STATE_PENDING_REQUEST_ARGS, mPendingRequestArgs);
}
+ outState.putInt(RUNTIME_STATE_PENDING_REQUEST_CODE, mPendingActivityRequestCode);
+
if (mPendingActivityResult != null) {
outState.putParcelable(RUNTIME_STATE_PENDING_ACTIVITY_RESULT, mPendingActivityResult);
}
@@ -1449,17 +1467,29 @@
@Override
public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
- super.startActivityForResult(intent, requestCode, options);
+ if (requestCode != -1) {
+ mPendingActivityRequestCode = requestCode;
+ }
+ if (requestCode == -1
+ || !UiFactory.startActivityForResult(this, intent, requestCode, options)) {
+ super.startActivityForResult(intent, requestCode, options);
+ }
}
@Override
- public void startIntentSenderForResult (IntentSender intent, int requestCode,
+ public void startIntentSenderForResult(IntentSender intent, int requestCode,
Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags, Bundle options) {
- try {
- super.startIntentSenderForResult(intent, requestCode,
- fillInIntent, flagsMask, flagsValues, extraFlags, options);
- } catch (IntentSender.SendIntentException e) {
- throw new ActivityNotFoundException();
+ if (requestCode != -1) {
+ mPendingActivityRequestCode = requestCode;
+ }
+ if (requestCode == -1 || !UiFactory.startIntentSenderForResult(this, intent, requestCode,
+ fillInIntent, flagsMask, flagsValues, extraFlags, options)) {
+ try {
+ super.startIntentSenderForResult(intent, requestCode,
+ fillInIntent, flagsMask, flagsValues, extraFlags, options);
+ } catch (IntentSender.SendIntentException e) {
+ throw new ActivityNotFoundException();
+ }
}
}
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index f830301..6ad5c36 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -158,7 +158,8 @@
mOpenHelper = new DatabaseHelper(getContext(), mListenerHandler);
if (RestoreDbTask.isPending(getContext())) {
- if (!RestoreDbTask.performRestore(mOpenHelper, new BackupManager(getContext()))) {
+ if (!RestoreDbTask.performRestore(getContext(), mOpenHelper,
+ new BackupManager(getContext()))) {
mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase());
}
// Set is pending to false irrespective of the result, so that it doesn't get
diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java
index f5040b3..b1a3fc9 100644
--- a/src/com/android/launcher3/LauncherStateManager.java
+++ b/src/com/android/launcher3/LauncherStateManager.java
@@ -452,7 +452,7 @@
}
mState = state;
mState.onStateEnabled(mLauncher);
- mLauncher.getAppWidgetHost().setResumed(state == LauncherState.NORMAL);
+ mLauncher.onStateSet(mState);
if (state.disablePageClipping) {
// Only disable clipping if needed, otherwise leave it as previous value.
diff --git a/src/com/android/launcher3/TestProtocol.java b/src/com/android/launcher3/TestProtocol.java
index ecbaa5b..eefecda 100644
--- a/src/com/android/launcher3/TestProtocol.java
+++ b/src/com/android/launcher3/TestProtocol.java
@@ -24,6 +24,7 @@
public static final String SCROLL_Y_FIELD = "scrollY";
public static final String STATE_FIELD = "state";
public static final String SWITCHED_TO_STATE_MESSAGE = "TAPL_SWITCHED_TO_STATE";
+ public static final String SCROLL_FINISHED_MESSAGE = "TAPL_SCROLL_FINISHED";
public static final String RESPONSE_MESSAGE_POSTFIX = "_RESPONSE";
public static final int NORMAL_STATE_ORDINAL = 0;
public static final int SPRING_LOADED_STATE_ORDINAL = 1;
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 5cfd95c..732aa95 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -17,6 +17,7 @@
package com.android.launcher3;
import android.animation.ValueAnimator;
+import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.app.WallpaperManager;
import android.content.BroadcastReceiver;
@@ -32,12 +33,16 @@
import android.content.pm.ResolveInfo;
import android.content.pm.ShortcutInfo;
import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.InsetDrawable;
import android.os.Build;
import android.os.Bundle;
import android.os.DeadObjectException;
@@ -62,6 +67,7 @@
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
import com.android.launcher3.graphics.RotationMode;
+import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.IntArray;
@@ -74,6 +80,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
+import java.util.StringTokenizer;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
@@ -81,6 +88,8 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import static com.android.launcher3.ItemInfoWithIcon.FLAG_ICON_BADGED;
+
/**
* Various utilities shared amongst the Launcher's classes.
*/
@@ -652,4 +661,77 @@
return null;
}
}
+
+ /**
+ * For apps icons and shortcut icons that have badges, this method creates a drawable that can
+ * later on be rendered on top of the layers for the badges. For app icons, work profile badges
+ * can only be applied. For deep shortcuts, when dragged from the pop up container, there's no
+ * badge. When dragged from workspace or folder, it may contain app AND/OR work profile badge
+ **/
+ @TargetApi(Build.VERSION_CODES.O)
+ public static Drawable getBadge(Launcher launcher, ItemInfo info, Object obj) {
+ LauncherAppState appState = LauncherAppState.getInstance(launcher);
+ int iconSize = appState.getInvariantDeviceProfile().iconBitmapSize;
+ if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+ boolean iconBadged = (info instanceof ItemInfoWithIcon)
+ && (((ItemInfoWithIcon) info).runtimeStatusFlags & FLAG_ICON_BADGED) > 0;
+ if ((info.id == ItemInfo.NO_ID && !iconBadged)
+ || !(obj instanceof ShortcutInfo)) {
+ // The item is not yet added on home screen.
+ return new FixedSizeEmptyDrawable(iconSize);
+ }
+ ShortcutInfo si = (ShortcutInfo) obj;
+ LauncherIcons li = LauncherIcons.obtain(appState.getContext());
+ Bitmap badge = li.getShortcutInfoBadge(si, appState.getIconCache()).iconBitmap;
+ li.recycle();
+ float badgeSize = launcher.getResources().getDimension(R.dimen.profile_badge_size);
+ float insetFraction = (iconSize - badgeSize) / iconSize;
+ return new InsetDrawable(new FastBitmapDrawable(badge),
+ insetFraction, insetFraction, 0, 0);
+ } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
+ return ((FolderAdaptiveIcon) obj).getBadge();
+ } else {
+ return launcher.getPackageManager()
+ .getUserBadgedIcon(new FixedSizeEmptyDrawable(iconSize), info.user);
+ }
+ }
+
+ public static int[] getIntArrayFromString(String tokenized) {
+ StringTokenizer tokenizer = new StringTokenizer(tokenized, ",");
+ int[] array = new int[tokenizer.countTokens()];
+ int count = 0;
+ while (tokenizer.hasMoreTokens()) {
+ array[count] = Integer.parseInt(tokenizer.nextToken());
+ count++;
+ }
+ return array;
+ }
+
+ public static String getStringFromIntArray(int[] array) {
+ StringBuilder str = new StringBuilder();
+ for (int value : array) {
+ str.append(value).append(",");
+ }
+ return str.toString();
+ }
+
+ private static class FixedSizeEmptyDrawable extends ColorDrawable {
+
+ private final int mSize;
+
+ public FixedSizeEmptyDrawable(int size) {
+ super(Color.TRANSPARENT);
+ mSize = size;
+ }
+
+ @Override
+ public int getIntrinsicHeight() {
+ return mSize;
+ }
+
+ @Override
+ public int getIntrinsicWidth() {
+ return mSize;
+ }
+ }
}
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index 180ca48..548d5de 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -32,7 +32,8 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
-import com.android.launcher3.graphics.DrawableFactory;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
@@ -419,4 +420,13 @@
public boolean hasOverlappingRendering() {
return false;
}
+
+ @Override
+ public void onScrollStateChanged(int state) {
+ super.onScrollStateChanged(state);
+
+ if (state == SCROLL_STATE_IDLE && Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ AccessibilityManagerCompat.sendScrollFinishedEventToTest(getContext());
+ }
+ }
}
diff --git a/src/com/android/launcher3/allapps/DiscoveryBounce.java b/src/com/android/launcher3/allapps/DiscoveryBounce.java
index 7467119..1d62b43 100644
--- a/src/com/android/launcher3/allapps/DiscoveryBounce.java
+++ b/src/com/android/launcher3/allapps/DiscoveryBounce.java
@@ -24,7 +24,6 @@
import android.animation.Animator;
import android.animation.AnimatorInflater;
import android.animation.AnimatorListenerAdapter;
-import android.app.ActivityManager;
import android.content.SharedPreferences;
import android.os.Handler;
import android.view.MotionEvent;
@@ -32,6 +31,7 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.states.InternalStateHandler;
@@ -134,7 +134,7 @@
&& !shouldShowForWorkProfile(launcher))
|| AbstractFloatingView.getTopOpenView(launcher) != null
|| UserManagerCompat.getInstance(launcher).isDemoUser()
- || ActivityManager.isRunningInTestHarness()) {
+ || Utilities.IS_RUNNING_IN_TEST_HARNESS) {
return;
}
@@ -159,7 +159,7 @@
|| (launcher.getSharedPrefs().getBoolean(SHELF_BOUNCE_SEEN, false)
&& !shouldShowForWorkProfile(launcher))
|| UserManagerCompat.getInstance(launcher).isDemoUser()
- || ActivityManager.isRunningInTestHarness()) {
+ || Utilities.IS_RUNNING_IN_TEST_HARNESS) {
return;
}
diff --git a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
index b4d0c54..86f773f 100644
--- a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
+++ b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
@@ -62,6 +62,13 @@
sendEventToTest(accessibilityManager, TestProtocol.SWITCHED_TO_STATE_MESSAGE, parcel);
}
+ public static void sendScrollFinishedEventToTest(Context context) {
+ final AccessibilityManager accessibilityManager = getAccessibilityManagerForTest(context);
+ if (accessibilityManager == null) return;
+
+ sendEventToTest(accessibilityManager, TestProtocol.SCROLL_FINISHED_MESSAGE, null);
+ }
+
private static void sendEventToTest(
AccessibilityManager accessibilityManager, String eventTag, Bundle data) {
final AccessibilityEvent e = AccessibilityEvent.obtain(
diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java
index 70df97a..bad8282 100644
--- a/src/com/android/launcher3/config/BaseFlags.java
+++ b/src/com/android/launcher3/config/BaseFlags.java
@@ -111,7 +111,7 @@
"Show chip hints and gleams on the overview screen");
public static final TogglableFlag FAKE_LANDSCAPE_UI = new TogglableFlag(
- "FAKE_LANDSCAPE_UI", false,
+ "FAKE_LANDSCAPE_UI", true,
"Rotate launcher UI instead of using transposed layout");
public static void initialize(Context context) {
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index 3ab97b0..7af12c5 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -16,7 +16,7 @@
package com.android.launcher3.dragndrop;
-import static com.android.launcher3.ItemInfoWithIcon.FLAG_ICON_BADGED;
+import static com.android.launcher3.Utilities.getBadge;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -24,7 +24,6 @@
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.annotation.TargetApi;
-import android.content.pm.ShortcutInfo;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -37,7 +36,6 @@
import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
-import android.graphics.drawable.InsetDrawable;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
@@ -45,11 +43,11 @@
import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.ItemInfo;
-import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.LauncherStateManager;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.FirstFrameAnimatorHelper;
@@ -64,7 +62,7 @@
import androidx.dynamicanimation.animation.SpringAnimation;
import androidx.dynamicanimation.animation.SpringForce;
-public class DragView extends View {
+public class DragView extends View implements LauncherStateManager.StateListener {
private static final ColorMatrix sTempMatrix1 = new ColorMatrix();
private static final ColorMatrix sTempMatrix2 = new ColorMatrix();
@@ -176,6 +174,27 @@
setElevation(getResources().getDimension(R.dimen.drag_elevation));
}
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mLauncher.getStateManager().addStateListener(this);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mLauncher.getStateManager().removeStateListener(this);
+ }
+
+ @Override
+ public void onStateTransitionStart(LauncherState toState) { }
+
+ @Override
+ public void onStateTransitionComplete(LauncherState finalState) {
+ setVisibility((finalState == LauncherState.NORMAL
+ || finalState == LauncherState.SPRING_LOADED) ? VISIBLE : INVISIBLE);
+ }
+
/**
* Initialize {@code #mIconDrawable} if the item can be represented using
* an {@link AdaptiveIconDrawable} or {@link FolderAdaptiveIcon}.
@@ -195,7 +214,6 @@
new Handler(workerLooper).postAtFrontOfQueue(new Runnable() {
@Override
public void run() {
- LauncherAppState appState = LauncherAppState.getInstance(mLauncher);
Object[] outObj = new Object[1];
int w = mBitmap.getWidth();
int h = mBitmap.getHeight();
@@ -211,7 +229,7 @@
// Badge is applied after icon normalization so the bounds for badge should not
// be scaled down due to icon normalization.
Rect badgeBounds = new Rect(bounds);
- mBadge = getBadge(info, appState, outObj[0]);
+ mBadge = getBadge(mLauncher, info, outObj[0]);
mBadge.setBounds(badgeBounds);
// Do not draw the background in case of folder as its translucent
@@ -307,40 +325,6 @@
invalidate();
}
- /**
- * For apps icons and shortcut icons that have badges, this method creates a drawable that can
- * later on be rendered on top of the layers for the badges. For app icons, work profile badges
- * can only be applied. For deep shortcuts, when dragged from the pop up container, there's no
- * badge. When dragged from workspace or folder, it may contain app AND/OR work profile badge
- **/
-
- @TargetApi(Build.VERSION_CODES.O)
- private Drawable getBadge(ItemInfo info, LauncherAppState appState, Object obj) {
- int iconSize = appState.getInvariantDeviceProfile().iconBitmapSize;
- if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
- boolean iconBadged = (info instanceof ItemInfoWithIcon)
- && (((ItemInfoWithIcon) info).runtimeStatusFlags & FLAG_ICON_BADGED) > 0;
- if ((info.id == ItemInfo.NO_ID && !iconBadged)
- || !(obj instanceof ShortcutInfo)) {
- // The item is not yet added on home screen.
- return new FixedSizeEmptyDrawable(iconSize);
- }
- ShortcutInfo si = (ShortcutInfo) obj;
- 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),
- insetFraction, insetFraction, 0, 0);
- } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
- return ((FolderAdaptiveIcon) obj).getBadge();
- } else {
- return mLauncher.getPackageManager()
- .getUserBadgedIcon(new FixedSizeEmptyDrawable(iconSize), info.user);
- }
- }
-
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(mBitmap.getWidth(), mBitmap.getHeight());
@@ -626,24 +610,4 @@
mSpring.animateToFinalPosition(Utilities.boundToRange(value, -mDelta, mDelta));
}
}
-
- private static class FixedSizeEmptyDrawable extends ColorDrawable {
-
- private final int mSize;
-
- public FixedSizeEmptyDrawable(int size) {
- super(Color.TRANSPARENT);
- mSize = size;
- }
-
- @Override
- public int getIntrinsicHeight() {
- return mSize;
- }
-
- @Override
- public int getIntrinsicWidth() {
- return mSize;
- }
- }
}
diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java
index 0e2ddd4..9373976 100644
--- a/src/com/android/launcher3/folder/FolderAnimationManager.java
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -152,7 +152,7 @@
final float yDistance = initialY - lp.y;
// Set up the Folder background.
- final int finalColor = Themes.getAttrColor(mContext, android.R.attr.colorPrimary);
+ final int finalColor = Themes.getAttrColor(mContext, R.attr.folderFillColor);
final int initialColor = setColorAlphaBound(
finalColor, mPreviewBackground.getBackgroundAlpha());
mFolderBackground.mutate();
diff --git a/src/com/android/launcher3/folder/PreviewBackground.java b/src/com/android/launcher3/folder/PreviewBackground.java
index 09e8276..b2c0ca7 100644
--- a/src/com/android/launcher3/folder/PreviewBackground.java
+++ b/src/com/android/launcher3/folder/PreviewBackground.java
@@ -131,7 +131,7 @@
TypedArray ta = context.getTheme().obtainStyledAttributes(R.styleable.FolderIconPreview);
mDotColor = ta.getColor(R.styleable.FolderIconPreview_folderDotColor, 0);
mStrokeColor = ta.getColor(R.styleable.FolderIconPreview_folderIconBorderColor, 0);
- mBgColor = ta.getColor(R.styleable.FolderIconPreview_android_colorPrimary, 0);
+ mBgColor = ta.getColor(R.styleable.FolderIconPreview_folderFillColor, 0);
ta.recycle();
DeviceProfile grid = activity.getWallpaperDeviceProfile();
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
index 053c493..3c0c5fd 100644
--- a/src/com/android/launcher3/provider/RestoreDbTask.java
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -16,18 +16,23 @@
package com.android.launcher3.provider;
+import static com.android.launcher3.Utilities.getIntArrayFromString;
+import static com.android.launcher3.Utilities.getStringFromIntArray;
import static com.android.launcher3.provider.LauncherDbUtils.dropTable;
import android.app.backup.BackupManager;
import android.content.ContentValues;
import android.content.Context;
+import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
-import android.os.Build;
import android.os.UserHandle;
import android.util.LongSparseArray;
import android.util.SparseLongArray;
+import androidx.annotation.NonNull;
+
+import com.android.launcher3.AppWidgetsRestoredReceiver;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherProvider.DatabaseHelper;
import com.android.launcher3.LauncherSettings.Favorites;
@@ -53,10 +58,16 @@
private static final String INFO_COLUMN_NAME = "name";
private static final String INFO_COLUMN_DEFAULT_VALUE = "dflt_value";
- public static boolean performRestore(DatabaseHelper helper, BackupManager backupManager) {
+ private static final String APPWIDGET_OLD_IDS = "appwidget_old_ids";
+ private static final String APPWIDGET_IDS = "appwidget_ids";
+
+ public static boolean performRestore(Context context, DatabaseHelper helper,
+ BackupManager backupManager) {
SQLiteDatabase db = helper.getWritableDatabase();
try (SQLiteTransaction t = new SQLiteTransaction(db)) {
- new RestoreDbTask().sanitizeDB(helper, db, backupManager);
+ RestoreDbTask task = new RestoreDbTask();
+ task.sanitizeDB(helper, db, backupManager);
+ task.restoreAppWidgetIdsIfExists(context);
t.commit();
return true;
} catch (Exception e) {
@@ -230,4 +241,27 @@
FileLog.d(TAG, "Restore data received through full backup " + isPending);
Utilities.getPrefs(context).edit().putBoolean(RESTORE_TASK_PENDING, isPending).commit();
}
+
+ private void restoreAppWidgetIdsIfExists(Context context) {
+ SharedPreferences prefs = Utilities.getPrefs(context);
+ if (prefs.contains(APPWIDGET_OLD_IDS) && prefs.contains(APPWIDGET_IDS)) {
+ AppWidgetsRestoredReceiver.restoreAppWidgetIds(context,
+ getIntArrayFromString(prefs.getString(APPWIDGET_OLD_IDS, "")),
+ getIntArrayFromString(prefs.getString(APPWIDGET_IDS, "")));
+ } else {
+ FileLog.d(TAG, "No app widget ids to restore.");
+ }
+
+ prefs.edit().remove(APPWIDGET_OLD_IDS)
+ .remove(APPWIDGET_IDS).apply();
+ }
+
+ public static void setRestoredAppWidgetIds(Context context, @NonNull int[] oldIds,
+ @NonNull int[] newIds) {
+ Utilities.getPrefs(context).edit()
+ .putString(APPWIDGET_OLD_IDS, getStringFromIntArray(oldIds))
+ .putString(APPWIDGET_IDS, getStringFromIntArray(newIds))
+ .commit();
+ }
+
}
diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java
index fb41ea1..3727fa6 100644
--- a/src/com/android/launcher3/states/RotationHelper.java
+++ b/src/com/android/launcher3/states/RotationHelper.java
@@ -20,13 +20,16 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE;
-import android.app.Activity;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.res.Resources;
+import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
+import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.UiThreadHelper;
/**
@@ -49,7 +52,7 @@
public static final int REQUEST_ROTATE = 1;
public static final int REQUEST_LOCK = 2;
- private final Activity mActivity;
+ private final Launcher mLauncher;
private final SharedPreferences mPrefs;
private boolean mIgnoreAutoRotateSettings;
@@ -70,13 +73,13 @@
private int mLastActivityFlags = -1;
- public RotationHelper(Activity activity) {
- mActivity = activity;
+ public RotationHelper(Launcher launcher) {
+ mLauncher = launcher;
// On large devices we do not handle auto-rotate differently.
- mIgnoreAutoRotateSettings = mActivity.getResources().getBoolean(R.bool.allow_rotation);
+ mIgnoreAutoRotateSettings = mLauncher.getResources().getBoolean(R.bool.allow_rotation);
if (!mIgnoreAutoRotateSettings) {
- mPrefs = Utilities.getPrefs(mActivity);
+ mPrefs = Utilities.getPrefs(mLauncher);
mPrefs.registerOnSharedPreferenceChangeListener(this);
mAutoRotateEnabled = mPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY,
getAllowRotationDefaultValue());
@@ -85,11 +88,32 @@
}
}
+ public boolean homeScreenCanRotate() {
+ return mIgnoreAutoRotateSettings || mAutoRotateEnabled
+ || mStateHandlerRequest != REQUEST_NONE;
+ }
+
+ private void updateRotationAnimation() {
+ if (FeatureFlags.FAKE_LANDSCAPE_UI.get()) {
+ WindowManager.LayoutParams lp = mLauncher.getWindow().getAttributes();
+ lp.rotationAnimation = homeScreenCanRotate()
+ ? WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE
+ : WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
+ mLauncher.getWindow().setAttributes(lp);
+ }
+ }
+
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) {
+ boolean wasRotationEnabled = mAutoRotateEnabled;
mAutoRotateEnabled = mPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY,
getAllowRotationDefaultValue());
- notifyChange();
+ if (mAutoRotateEnabled != wasRotationEnabled) {
+
+ notifyChange();
+ updateRotationAnimation();
+ mLauncher.reapplyUi();
+ }
}
public void setStateHandlerRequest(int request) {
@@ -109,7 +133,7 @@
// Used by tests only.
public void forceAllowRotationForTesting(boolean allowRotation) {
mIgnoreAutoRotateSettings =
- allowRotation || mActivity.getResources().getBoolean(R.bool.allow_rotation);
+ allowRotation || mLauncher.getResources().getBoolean(R.bool.allow_rotation);
notifyChange();
}
@@ -117,6 +141,7 @@
if (!mInitialized) {
mInitialized = true;
notifyChange();
+ updateRotationAnimation();
}
}
@@ -150,7 +175,7 @@
}
if (activityFlags != mLastActivityFlags) {
mLastActivityFlags = activityFlags;
- UiThreadHelper.setOrientationAsync(mActivity, activityFlags);
+ UiThreadHelper.setOrientationAsync(mLauncher, activityFlags);
}
}
diff --git a/src/com/android/launcher3/util/Themes.java b/src/com/android/launcher3/util/Themes.java
index 0c44012..0d02715 100644
--- a/src/com/android/launcher3/util/Themes.java
+++ b/src/com/android/launcher3/util/Themes.java
@@ -48,10 +48,12 @@
if (darkTheme) {
return wallpaperColorInfo.supportsDarkText() ?
- R.style.AppTheme_Dark_DarkText : R.style.AppTheme_Dark;
+ R.style.AppTheme_Dark_DarkText : wallpaperColorInfo.isMainColorDark() ?
+ R.style.AppTheme_Dark_DarkMainColor : R.style.AppTheme_Dark;
} else {
return wallpaperColorInfo.supportsDarkText() ?
- R.style.AppTheme_DarkText : R.style.AppTheme;
+ R.style.AppTheme_DarkText : wallpaperColorInfo.isMainColorDark() ?
+ R.style.AppTheme_DarkMainColor : R.style.AppTheme;
}
}
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index cd0ae3d..cb7bba7 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.views;
+import static com.android.launcher3.LauncherAnimUtils.DRAWABLE_ALPHA;
+import static com.android.launcher3.Utilities.getBadge;
import static com.android.launcher3.Utilities.mapToRange;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM;
@@ -62,6 +64,9 @@
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
+import androidx.dynamicanimation.animation.FloatPropertyCompat;
+import androidx.dynamicanimation.animation.SpringAnimation;
+import androidx.dynamicanimation.animation.SpringForce;
/**
* A view that is created to look like another view with the purpose of creating fluid animations.
@@ -76,6 +81,39 @@
private static final RectF sTmpRectF = new RectF();
private static final Object[] sTmpObjArray = new Object[1];
+ // We spring the foreground drawable relative to the icon's movement in the DragLayer.
+ // We then use these two factor values to scale the movement of the fg within this view.
+ private static final int FG_TRANS_X_FACTOR = 80;
+ private static final int FG_TRANS_Y_FACTOR = 100;
+
+ private static final FloatPropertyCompat<FloatingIconView> mFgTransYProperty
+ = new FloatPropertyCompat<FloatingIconView>("FloatingViewFgTransY") {
+ @Override
+ public float getValue(FloatingIconView view) {
+ return view.mFgTransY;
+ }
+
+ @Override
+ public void setValue(FloatingIconView view, float transY) {
+ view.mFgTransY = transY;
+ view.invalidate();
+ }
+ };
+
+ private static final FloatPropertyCompat<FloatingIconView> mFgTransXProperty
+ = new FloatPropertyCompat<FloatingIconView>("FloatingViewFgTransX") {
+ @Override
+ public float getValue(FloatingIconView view) {
+ return view.mFgTransX;
+ }
+
+ @Override
+ public void setValue(FloatingIconView view, float transX) {
+ view.mFgTransX = transX;
+ view.invalidate();
+ }
+ };
+
private Runnable mEndRunnable;
private CancellationSignal mLoadIconSignal;
@@ -85,6 +123,7 @@
private boolean mIsVerticalBarLayout = false;
private boolean mIsAdaptiveIcon = false;
+ private @Nullable Drawable mBadge;
private @Nullable Drawable mForeground;
private @Nullable Drawable mBackground;
private float mRotation;
@@ -100,17 +139,30 @@
private final Rect mOutline = new Rect();
private final Rect mFinalDrawableBounds = new Rect();
- private final Rect mBgDrawableBounds = new Rect();
private AnimatorSet mFadeAnimatorSet;
private ListenerView mListenerView;
+ private final SpringAnimation mFgSpringY;
+ private float mFgTransY;
+ private final SpringAnimation mFgSpringX;
+ private float mFgTransX;
+
private FloatingIconView(Launcher launcher) {
super(launcher);
mLauncher = launcher;
mBlurSizeOutline = getResources().getDimensionPixelSize(
R.dimen.blur_size_medium_outline);
mListenerView = new ListenerView(launcher, null);
+
+ mFgSpringX = new SpringAnimation(this, mFgTransXProperty)
+ .setSpring(new SpringForce()
+ .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)
+ .setStiffness(SpringForce.STIFFNESS_LOW));
+ mFgSpringY = new SpringAnimation(this, mFgTransYProperty)
+ .setSpring(new SpringForce()
+ .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)
+ .setStiffness(SpringForce.STIFFNESS_LOW));
}
@Override
@@ -158,7 +210,12 @@
Math.max(shapeProgressStart, progress), shapeProgressStart, 1f, 0, toMax,
LINEAR), 0, 1);
- mOutline.bottom = (int) (rect.height() / scale);
+ if (mIsVerticalBarLayout) {
+ mOutline.right = (int) (rect.width() / scale);
+ } else {
+ mOutline.bottom = (int) (rect.height() / scale);
+ }
+
mTaskCornerRadius = cornerRadius / scale;
if (mIsAdaptiveIcon) {
if (!isOpening && shapeRevealProgress >= 0) {
@@ -178,7 +235,32 @@
mRevealAnimator.setCurrentFraction(shapeRevealProgress);
}
- setBackgroundDrawableBounds(mOutline.height() / minSize);
+ float drawableScale = (mIsVerticalBarLayout ? mOutline.width() : mOutline.height())
+ / minSize;
+ setBackgroundDrawableBounds(drawableScale);
+ if (isOpening) {
+ // Center align foreground
+ int height = mFinalDrawableBounds.height();
+ int width = mFinalDrawableBounds.width();
+ int diffY = mIsVerticalBarLayout ? 0
+ : (int) (((height * drawableScale) - height) / 2);
+ int diffX = mIsVerticalBarLayout ? (int) (((width * drawableScale) - width) / 2)
+ : 0;
+ sTmpRect.set(mFinalDrawableBounds);
+ sTmpRect.offset(diffX, diffY);
+ mForeground.setBounds(sTmpRect);
+ } else {
+ // Spring the foreground relative to the icon's movement within the DragLayer.
+ int diffX = (int) (dX / mLauncher.getDeviceProfile().availableWidthPx
+ * FG_TRANS_X_FACTOR);
+ int diffY = (int) (dY / mLauncher.getDeviceProfile().availableHeightPx
+ * FG_TRANS_Y_FACTOR);
+
+ mFgSpringX.animateToFinalPosition(diffX);
+ mFgSpringY.animateToFinalPosition(diffY);
+ }
+
+
}
invalidate();
invalidateOutline();
@@ -289,7 +371,9 @@
if (supportsAdaptiveIcons) {
drawable = Utilities.getFullDrawable(mLauncher, info, lp.width, lp.height,
false, sTmpObjArray);
- if (!(drawable instanceof AdaptiveIconDrawable)) {
+ if ((drawable instanceof AdaptiveIconDrawable)) {
+ mBadge = getBadge(mLauncher, info, sTmpObjArray[0]);
+ } else {
// The drawable we get back is not an adaptive icon, so we need to use the
// BubbleTextView icon that is already legacy treated.
drawable = btvIcon;
@@ -348,6 +432,14 @@
mStartRevealRect.set(0, 0, originalWidth, originalHeight);
+ if (mBadge != null) {
+ mBadge.setBounds(mStartRevealRect);
+ if (!isOpening) {
+ DRAWABLE_ALPHA.set(mBadge, 0);
+ }
+
+ }
+
if (!isFolderIcon) {
mStartRevealRect.inset(mBlurSizeOutline, mBlurSizeOutline);
}
@@ -393,17 +485,15 @@
}
private void setBackgroundDrawableBounds(float scale) {
- mBgDrawableBounds.set(mFinalDrawableBounds);
- Utilities.scaleRectAboutCenter(mBgDrawableBounds, scale);
+ sTmpRect.set(mFinalDrawableBounds);
+ Utilities.scaleRectAboutCenter(sTmpRect, scale);
// Since the drawable is at the top of the view, we need to offset to keep it centered.
if (mIsVerticalBarLayout) {
- mBgDrawableBounds.offsetTo((int) (mFinalDrawableBounds.left * scale),
- mBgDrawableBounds.top);
+ sTmpRect.offsetTo((int) (mFinalDrawableBounds.left * scale), sTmpRect.top);
} else {
- mBgDrawableBounds.offsetTo(mBgDrawableBounds.left,
- (int) (mFinalDrawableBounds.top * scale));
+ sTmpRect.offsetTo(sTmpRect.left, (int) (mFinalDrawableBounds.top * scale));
}
- mBackground.setBounds(mBgDrawableBounds);
+ mBackground.setBounds(sTmpRect);
}
@WorkerThread
@@ -448,7 +538,13 @@
mBackground.draw(canvas);
}
if (mForeground != null) {
+ int count2 = canvas.save();
+ canvas.translate(mFgTransX, mFgTransY);
mForeground.draw(canvas);
+ canvas.restoreToCount(count2);
+ }
+ if (mBadge != null) {
+ mBadge.draw(canvas);
}
canvas.restoreToCount(count);
}
@@ -568,6 +664,12 @@
}
});
+ if (mBadge != null) {
+ ObjectAnimator badgeFade = ObjectAnimator.ofInt(mBadge, DRAWABLE_ALPHA, 255);
+ badgeFade.addUpdateListener(valueAnimator -> invalidate());
+ fade.play(badgeFade);
+ }
+
if (originalView instanceof BubbleTextView) {
BubbleTextView btv = (BubbleTextView) originalView;
btv.forceHideDot(true);
@@ -624,7 +726,6 @@
mBackground = null;
mClipPath = null;
mFinalDrawableBounds.setEmpty();
- mBgDrawableBounds.setEmpty();
if (mRevealAnimator != null) {
mRevealAnimator.cancel();
}
@@ -639,5 +740,10 @@
mOnTargetChangeRunnable = null;
mTaskCornerRadius = 0;
mOutline.setEmpty();
+ mFgTransY = 0;
+ mFgSpringX.cancel();
+ mFgTransX = 0;
+ mFgSpringY.cancel();
+ mBadge = null;
}
}
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
index 6008d14..550327d 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
@@ -18,6 +18,9 @@
import android.app.Activity;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.os.Bundle;
import android.os.CancellationSignal;
import com.android.launcher3.DeviceProfile;
@@ -79,4 +82,18 @@
public static RotationMode getRotationMode(DeviceProfile dp) {
return RotationMode.NORMAL;
}
+
+ public static boolean startIntentSenderForResult(Activity activity, IntentSender intent,
+ int requestCode, Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags,
+ Bundle options) {
+ return false;
+ }
+
+ public static boolean startActivityForResult(Activity activity, Intent intent, int requestCode,
+ Bundle options) {
+ return false;
+ }
+
+ public static void resetPendingActivityResults(Launcher launcher, int requestCode) { }
+
}
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/WallpaperColorInfo.java b/src_ui_overrides/com/android/launcher3/uioverrides/WallpaperColorInfo.java
index 56e3260..b05e125 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/WallpaperColorInfo.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/WallpaperColorInfo.java
@@ -30,6 +30,10 @@
public class WallpaperColorInfo implements WallpaperManagerCompat.OnColorsChangedListenerCompat {
+ private static final int MAIN_COLOR_LIGHT = 0xffdadce0;
+ private static final int MAIN_COLOR_DARK = 0xff202124;
+ private static final int MAIN_COLOR_REGULAR = 0xff000000;
+
private static final int FALLBACK_COLOR = Color.WHITE;
private static final Object sInstanceLock = new Object();
private static WallpaperColorInfo sInstance;
@@ -76,6 +80,10 @@
return mSupportsDarkText;
}
+ public boolean isMainColorDark() {
+ return mMainColor == MAIN_COLOR_DARK;
+ }
+
@Override
public void onColorsChanged(WallpaperColorsCompat colors, int which) {
if ((which & FLAG_SYSTEM) != 0) {
diff --git a/tests/src/com/android/launcher3/ui/TestViewHelpers.java b/tests/src/com/android/launcher3/ui/TestViewHelpers.java
index 2116f5e..a73bde0 100644
--- a/tests/src/com/android/launcher3/ui/TestViewHelpers.java
+++ b/tests/src/com/android/launcher3/ui/TestViewHelpers.java
@@ -27,6 +27,7 @@
import android.os.Process;
import android.os.SystemClock;
import android.util.Log;
+import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
@@ -186,10 +187,8 @@
*/
public static UiObject2 openWidgetsTray() {
final UiDevice device = getDevice();
- device.pressMenu(); // Enter overview mode.
- device.wait(Until.findObject(
- By.text(getTargetContext().getString(R.string.widget_button_text))),
- AbstractLauncherUiTest.DEFAULT_UI_TIMEOUT).click();
+ device.pressKeyCode(KeyEvent.KEYCODE_W, KeyEvent.META_CTRL_ON);
+ device.waitForIdle();
return findViewById(R.id.widgets_list_view);
}
diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java
index 68bdfe3..a296975 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java
@@ -18,6 +18,8 @@
import static com.android.launcher3.tapl.LauncherInstrumentation.NavigationModel.ZERO_BUTTON;
+import android.graphics.Rect;
+
import androidx.annotation.NonNull;
import androidx.test.uiautomator.BySelector;
import androidx.test.uiautomator.Direction;
@@ -32,7 +34,6 @@
public class AllApps extends LauncherInstrumentation.VisibleContainer {
private static final int MAX_SCROLL_ATTEMPTS = 40;
private static final int MIN_INTERACT_SIZE = 100;
- private static final int FLING_SPEED = LauncherInstrumentation.needSlowGestures() ? 1000 : 3000;
private final int mHeight;
@@ -102,7 +103,7 @@
"search_container_all_apps");
int attempts = 0;
- allAppsContainer.setGestureMargins(0, searchBox.getVisibleBounds().bottom + 1, 0, 5);
+ final Rect margins = new Rect(0, searchBox.getVisibleBounds().bottom + 1, 0, 5);
for (int scroll = getScroll(allAppsContainer);
scroll != 0;
@@ -113,7 +114,7 @@
"Exceeded max scroll attempts: " + MAX_SCROLL_ATTEMPTS,
++attempts <= MAX_SCROLL_ATTEMPTS);
- allAppsContainer.scroll(Direction.UP, 1);
+ mLauncher.scroll(allAppsContainer, Direction.UP, 1, margins, 50);
}
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("scrolled up")) {
@@ -133,7 +134,7 @@
// Try to figure out how much percentage of the container needs to be scrolled in order
// to reveal the app icon to have the MIN_INTERACT_SIZE
final float pct = Math.max(((float) (MIN_INTERACT_SIZE - appHeight)) / mHeight, 0.2f);
- allAppsContainer.scroll(Direction.DOWN, pct);
+ mLauncher.scroll(allAppsContainer, Direction.DOWN, pct, null, 10);
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"scrolled an icon in all apps to make it visible - and then")) {
mLauncher.waitForIdle();
@@ -150,9 +151,8 @@
mLauncher.addContextLayer("want to fling forward in all apps")) {
final UiObject2 allAppsContainer = verifyActiveContainer();
// Start the gesture in the center to avoid starting at elements near the top.
- allAppsContainer.setGestureMargins(0, 0, 0, mHeight / 2);
- allAppsContainer.fling(Direction.DOWN,
- (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
+ mLauncher.scroll(
+ allAppsContainer, Direction.DOWN, 1, new Rect(0, 0, 0, mHeight / 2), 10);
verifyActiveContainer();
}
}
@@ -165,9 +165,8 @@
mLauncher.addContextLayer("want to fling backward in all apps")) {
final UiObject2 allAppsContainer = verifyActiveContainer();
// Start the gesture in the center, for symmetry with forward.
- allAppsContainer.setGestureMargins(0, mHeight / 2, 0, 0);
- allAppsContainer.fling(Direction.UP,
- (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
+ mLauncher.scroll(
+ allAppsContainer, Direction.UP, 1, new Rect(0, mHeight / 2, 0, 0), 10);
verifyActiveContainer();
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index a63d468..d3f1460 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -28,6 +28,7 @@
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.Point;
+import android.graphics.Rect;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@@ -47,6 +48,7 @@
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.BySelector;
import androidx.test.uiautomator.Configurator;
+import androidx.test.uiautomator.Direction;
import androidx.test.uiautomator.UiDevice;
import androidx.test.uiautomator.UiObject2;
import androidx.test.uiautomator.Until;
@@ -56,6 +58,7 @@
import org.junit.Assert;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.Deque;
@@ -212,7 +215,22 @@
};
}
+ private void dumpViewHierarchy() {
+ final ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ try {
+ mDevice.dumpWindowHierarchy(stream);
+ stream.flush();
+ stream.close();
+ for (String line : stream.toString().split("\\r?\\n")) {
+ Log.e(TAG, line.trim());
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "error dumping XML to logcat", e);
+ }
+ }
+
private void fail(String message) {
+ dumpViewHierarchy();
Assert.fail("http://go/tapl : " + getContextDescription() + message);
}
@@ -363,10 +381,10 @@
? NORMAL_STATE_ORDINAL : BACKGROUND_APP_STATE_ORDINAL;
final Point displaySize = getRealDisplaySize();
- swipeViaMovePointer(
+ swipeToState(
displaySize.x / 2, displaySize.y - 1,
displaySize.x / 2, 0,
- finalState, ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME);
+ ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME, finalState);
}
} else {
log(action = "clicking home button");
@@ -530,6 +548,14 @@
}
@NonNull
+ UiObject2 waitForLauncherObjectByClass(String clazz) {
+ final BySelector selector = getLauncherObjectSelectorByClass(clazz);
+ final UiObject2 object = mDevice.wait(Until.findObject(selector), WAIT_TIME_MS);
+ assertNotNull("Can't find a launcher object; selector: " + selector, object);
+ return object;
+ }
+
+ @NonNull
UiObject2 waitForFallbackLauncherObject(String resName) {
final BySelector selector = getFallbackLauncherObjectSelector(resName);
final UiObject2 object = mDevice.wait(Until.findObject(selector), WAIT_TIME_MS);
@@ -541,6 +567,10 @@
return By.res(getLauncherPackageName(), resName);
}
+ BySelector getLauncherObjectSelectorByClass(String clazz) {
+ return By.pkg(getLauncherPackageName()).clazz(clazz);
+ }
+
BySelector getFallbackLauncherObjectSelector(String resName) {
return By.res(getOverviewPackageName(), resName);
}
@@ -563,18 +593,63 @@
() -> mDevice.swipe(startX, startY, endX, endY, steps));
}
- void swipeViaMovePointer(
- int startX, int startY, int endX, int endY, int expectedState, int steps) {
- changeStateViaGesture(startX, startY, endX, endY, expectedState, () -> {
- final long downTime = SystemClock.uptimeMillis();
- final Point start = new Point(startX, startY);
- final Point end = new Point(endX, endY);
- sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, start);
- final long endTime = movePointer(downTime, downTime, steps * GESTURE_STEP_MS, start,
- end);
- sendPointer(
- downTime, endTime, MotionEvent.ACTION_UP, end);
- });
+ void swipeToState(int startX, int startY, int endX, int endY, int steps, int expectedState) {
+ changeStateViaGesture(startX, startY, endX, endY, expectedState,
+ () -> linearGesture(startX, startY, endX, endY, steps));
+ }
+
+ void scroll(UiObject2 container, Direction direction, float percent, Rect margins, int steps) {
+ final Rect rect = container.getVisibleBounds();
+ if (margins != null) {
+ rect.left += margins.left;
+ rect.top += margins.top;
+ rect.right -= margins.right;
+ rect.bottom -= margins.bottom;
+ }
+
+ final int startX;
+ final int startY;
+ final int endX;
+ final int endY;
+
+ switch (direction) {
+ case UP: {
+ startX = endX = rect.centerX();
+ final int vertCenter = rect.centerY();
+ final float halfGestureHeight = rect.height() * percent / 2.0f;
+ startY = (int) (vertCenter - halfGestureHeight);
+ endY = (int) (vertCenter + halfGestureHeight);
+ }
+ break;
+ case DOWN: {
+ startX = endX = rect.centerX();
+ final int vertCenter = rect.centerY();
+ final float halfGestureHeight = rect.height() * percent / 2.0f;
+ startY = (int) (vertCenter + halfGestureHeight);
+ endY = (int) (vertCenter - halfGestureHeight);
+ }
+ break;
+ default:
+ fail("Unsupported direction");
+ return;
+ }
+
+ executeAndWaitForEvent(
+ () -> linearGesture(startX, startY, endX, endY, steps),
+ event -> TestProtocol.SCROLL_FINISHED_MESSAGE.equals(event.getClassName()),
+ "Didn't receive a scroll end message: " + startX + ", " + startY
+ + ", " + endX + ", " + endY);
+ }
+
+ // Inject a swipe gesture. Inject exactly 'steps' motion points, incrementing event time by a
+ // fixed interval each time.
+ private void linearGesture(int startX, int startY, int endX, int endY, int steps) {
+ final long downTime = SystemClock.uptimeMillis();
+ final Point start = new Point(startX, startY);
+ final Point end = new Point(endX, endY);
+ sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, start);
+ final long endTime = movePointer(downTime, downTime, steps * GESTURE_STEP_MS, start, end);
+ sendPointer(downTime, endTime, MotionEvent.ACTION_UP, end);
}
private void changeStateViaGesture(int startX, int startY, int endX, int endY,
@@ -688,10 +763,7 @@
}
static void sleep(int duration) {
- try {
- Thread.sleep(duration);
- } catch (InterruptedException e) {
- }
+ SystemClock.sleep(duration);
}
int getEdgeSensitivityWidth() {
diff --git a/tests/tapl/com/android/launcher3/tapl/Overview.java b/tests/tapl/com/android/launcher3/tapl/Overview.java
index e625510..ec99d26 100644
--- a/tests/tapl/com/android/launcher3/tapl/Overview.java
+++ b/tests/tapl/com/android/launcher3/tapl/Overview.java
@@ -49,13 +49,14 @@
"want to switch from overview to all apps")) {
verifyActiveContainer();
- // Swipe from the prediction row to the top.
+ // Swipe from an app icon to the top.
LauncherInstrumentation.log("Overview.switchToAllApps before swipe");
- final UiObject2 predictionRow = mLauncher.waitForLauncherObject("prediction_row");
- mLauncher.swipe(mLauncher.getDevice().getDisplayWidth() / 2,
- predictionRow.getVisibleBounds().centerY(),
+ final UiObject2 appIcon = mLauncher.waitForLauncherObjectByClass(
+ "android.widget.TextView");
+ mLauncher.swipeToState(mLauncher.getDevice().getDisplayWidth() / 2,
+ appIcon.getVisibleBounds().centerY(),
mLauncher.getDevice().getDisplayWidth() / 2,
- 0, ALL_APPS_STATE_ORDINAL);
+ 0, 50, ALL_APPS_STATE_ORDINAL);
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
"swiped all way up from overview")) {
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index c0bafa2..3cab1a1 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -65,13 +65,14 @@
LauncherInstrumentation.log(
"switchToAllApps: swipeHeight = " + swipeHeight + ", slop = "
+ mLauncher.getTouchSlop());
- mLauncher.swipe(
+
+ mLauncher.swipeToState(
start.x,
start.y,
start.x,
start.y - swipeHeight - mLauncher.getTouchSlop(),
- ALL_APPS_STATE_ORDINAL
- );
+ 60,
+ ALL_APPS_STATE_ORDINAL);
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
"swiped to all apps")) {