Merge "Import translations. DO NOT MERGE ANYWHERE" into sc-v2-dev
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index 38e8e72..4269f2d 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -320,6 +320,11 @@
mOverviewCommandHelper = binder.getOverviewCommandHelper();
}
+ @Override
+ public void runOnBindToTouchInteractionService(Runnable r) {
+ mTISBindHelper.runOnBindToTouchInteractionService(r);
+ }
+
private void initUnfoldTransitionProgressProvider() {
final UnfoldTransitionConfig config = UnfoldTransitionFactory.createConfig(this);
if (config.isEnabled()) {
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 0123c4f..a8a085b 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -660,10 +660,10 @@
AnimOpenProperties prop = new AnimOpenProperties(mLauncher.getResources(), mDeviceProfile,
windowTargetBounds, launcherIconBounds, v, dragLayerBounds[0], dragLayerBounds[1],
hasSplashScreen, floatingView.isDifferentFromAppIcon());
- int left = (int) (prop.cropCenterXStart - prop.cropWidthStart / 2);
- int top = (int) (prop.cropCenterYStart - prop.cropHeightStart / 2);
- int right = (int) (left + prop.cropWidthStart);
- int bottom = (int) (top + prop.cropHeightStart);
+ int left = prop.cropCenterXStart - prop.cropWidthStart / 2;
+ int top = prop.cropCenterYStart - prop.cropHeightStart / 2;
+ int right = left + prop.cropWidthStart;
+ int bottom = top + prop.cropHeightStart;
// Set the crop here so we can calculate the corner radius below.
crop.set(left, top, right, bottom);
@@ -1329,6 +1329,8 @@
mDeviceProfile);
// Hook up floating views to the closing window animators.
+ final int rotationChange = getRotationChange(targets);
+ Rect windowTargetBounds = getWindowTargetBounds(targets, rotationChange);
if (floatingIconView != null) {
anim.addAnimatorListener(floatingIconView);
floatingIconView.setOnTargetChangeListener(anim::onTargetPositionChanged);
@@ -1339,7 +1341,8 @@
// FolderIconView can be seen morphing into the icon shape.
final float windowAlphaThreshold = 1f - SHAPE_PROGRESS_DURATION;
- RectFSpringAnim.OnUpdateListener runner = new SpringAnimRunner(targets, targetRect) {
+ RectFSpringAnim.OnUpdateListener runner = new SpringAnimRunner(targets, targetRect,
+ windowTargetBounds) {
@Override
public void onUpdate(RectF currentRectF, float progress) {
finalFloatingIconView.update(1f, 255 /* fgAlpha */, currentRectF, progress,
@@ -1356,7 +1359,8 @@
final float floatingWidgetAlpha = isTransluscent ? 0 : 1;
FloatingWidgetView finalFloatingWidget = floatingWidget;
- RectFSpringAnim.OnUpdateListener runner = new SpringAnimRunner(targets, targetRect) {
+ RectFSpringAnim.OnUpdateListener runner = new SpringAnimRunner(targets, targetRect,
+ windowTargetBounds) {
@Override
public void onUpdate(RectF currentRectF, float progress) {
final float fallbackBackgroundAlpha =
@@ -1767,12 +1771,17 @@
private final float mStartRadius;
private final float mEndRadius;
private final SurfaceTransactionApplier mSurfaceApplier;
+ private final Rect mWindowTargetBounds = new Rect();
- SpringAnimRunner(RemoteAnimationTargetCompat[] appTargets, RectF targetRect) {
+ private final Rect mTmpRect = new Rect();
+
+ SpringAnimRunner(RemoteAnimationTargetCompat[] appTargets, RectF targetRect,
+ Rect windowTargetBounds) {
mAppTargets = appTargets;
mStartRadius = QuickStepContract.getWindowCornerRadius(mLauncher);
mEndRadius = Math.max(1, targetRect.width()) / 2f;
mSurfaceApplier = new SurfaceTransactionApplier(mDragLayer);
+ mWindowTargetBounds.set(windowTargetBounds);
}
public float getCornerRadius(float progress) {
@@ -1793,13 +1802,36 @@
}
if (target.mode == MODE_CLOSING) {
- float alpha = getWindowAlpha(progress);
currentRectF.round(mCurrentRect);
+ // Scale the target window to match the currentRectF.
+ final float scale;
+
+ // We need to infer the crop (we crop the window to match the currentRectF).
+ if (mWindowTargetBounds.height() > mWindowTargetBounds.width()) {
+ scale = Math.min(1f, currentRectF.width() / mWindowTargetBounds.width());
+
+ int unscaledHeight = (int) (mCurrentRect.height() * (1f / scale));
+ int croppedHeight = mWindowTargetBounds.height() - unscaledHeight;
+ mTmpRect.set(0, 0, mWindowTargetBounds.width(),
+ mWindowTargetBounds.height() - croppedHeight);
+ } else {
+ scale = Math.min(1f, currentRectF.height() / mWindowTargetBounds.height());
+
+ int unscaledWidth = (int) (mCurrentRect.width() * (1f / scale));
+ int croppedWidth = mWindowTargetBounds.width() - unscaledWidth;
+ mTmpRect.set(0, 0, mWindowTargetBounds.width() - croppedWidth,
+ mWindowTargetBounds.height());
+ }
+
+ // Match size and position of currentRect.
+ mMatrix.setScale(scale, scale);
+ mMatrix.postTranslate(mCurrentRect.left, mCurrentRect.top);
+
builder.withMatrix(mMatrix)
- .withWindowCrop(mCurrentRect)
- .withAlpha(alpha)
- .withCornerRadius(getCornerRadius(progress));
+ .withWindowCrop(mTmpRect)
+ .withAlpha(getWindowAlpha(progress))
+ .withCornerRadius(getCornerRadius(progress) / scale);
} else if (target.mode == MODE_OPENING) {
mMatrix.setTranslate(mTmpPos.x, mTmpPos.y);
builder.withMatrix(mMatrix)
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index 48b0f6d..6de876c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -40,6 +40,7 @@
import android.annotation.LayoutRes;
import android.content.pm.ActivityInfo.Config;
import android.content.res.ColorStateList;
+import android.content.res.Configuration;
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.Region.Op;
@@ -60,7 +61,6 @@
import com.android.launcher3.anim.AlphaUpdateListener;
import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarButton;
import com.android.launcher3.util.MultiValueAlpha;
-import com.android.launcher3.util.Themes;
import com.android.quickstep.AnimatedFloat;
import com.android.systemui.shared.rotation.FloatingRotationButton;
import com.android.systemui.shared.rotation.RotationButton;
@@ -198,15 +198,12 @@
navButtonsLayoutParams.gravity = Gravity.START;
mNavButtonContainer.requestLayout();
- if (!isThreeButtonNav) {
- // Tint all the nav buttons since there's no taskbar background in SUW.
- for (int i = 0; i < mNavButtonContainer.getChildCount(); i++) {
- if (!(mNavButtonContainer.getChildAt(i) instanceof ImageView)) continue;
- ImageView button = (ImageView) mNavButtonContainer.getChildAt(i);
- button.setImageTintList(ColorStateList.valueOf(Themes.getAttrColor(
- button.getContext(), android.R.attr.textColorPrimary)));
- }
- }
+ // TODO(b/210906568) Dark intensity is currently not propagated during setup, so set
+ // it based on dark theme for now.
+ int mode = mContext.getResources().getConfiguration().uiMode
+ & Configuration.UI_MODE_NIGHT_MASK;
+ boolean isDarkTheme = mode == Configuration.UI_MODE_NIGHT_YES;
+ mTaskbarNavButtonDarkIntensity.updateValue(isDarkTheme ? 0 : 1);
}
// Animate taskbar background when any of these flags are enabled
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 0e5282a..5c266bb 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -180,45 +180,51 @@
getFlagForIndex(0, "STATE_LAUNCHER_PRESENT");
protected static final int STATE_LAUNCHER_STARTED =
getFlagForIndex(1, "STATE_LAUNCHER_STARTED");
- protected static final int STATE_LAUNCHER_DRAWN = getFlagForIndex(2, "STATE_LAUNCHER_DRAWN");
+ protected static final int STATE_LAUNCHER_DRAWN =
+ getFlagForIndex(2, "STATE_LAUNCHER_DRAWN");
+ // Called when the Launcher has connected to the touch interaction service (and the taskbar
+ // ui controller is initialized)
+ protected static final int STATE_LAUNCHER_BIND_TO_SERVICE =
+ getFlagForIndex(3, "STATE_LAUNCHER_BIND_TO_SERVICE");
// Internal initialization states
private static final int STATE_APP_CONTROLLER_RECEIVED =
- getFlagForIndex(3, "STATE_APP_CONTROLLER_RECEIVED");
+ getFlagForIndex(4, "STATE_APP_CONTROLLER_RECEIVED");
// Interaction finish states
private static final int STATE_SCALED_CONTROLLER_HOME =
- getFlagForIndex(4, "STATE_SCALED_CONTROLLER_HOME");
+ getFlagForIndex(5, "STATE_SCALED_CONTROLLER_HOME");
private static final int STATE_SCALED_CONTROLLER_RECENTS =
- getFlagForIndex(5, "STATE_SCALED_CONTROLLER_RECENTS");
+ getFlagForIndex(6, "STATE_SCALED_CONTROLLER_RECENTS");
protected static final int STATE_HANDLER_INVALIDATED =
- getFlagForIndex(6, "STATE_HANDLER_INVALIDATED");
+ getFlagForIndex(7, "STATE_HANDLER_INVALIDATED");
private static final int STATE_GESTURE_STARTED =
- getFlagForIndex(7, "STATE_GESTURE_STARTED");
+ getFlagForIndex(8, "STATE_GESTURE_STARTED");
private static final int STATE_GESTURE_CANCELLED =
- getFlagForIndex(8, "STATE_GESTURE_CANCELLED");
+ getFlagForIndex(9, "STATE_GESTURE_CANCELLED");
private static final int STATE_GESTURE_COMPLETED =
- getFlagForIndex(9, "STATE_GESTURE_COMPLETED");
+ getFlagForIndex(10, "STATE_GESTURE_COMPLETED");
private static final int STATE_CAPTURE_SCREENSHOT =
- getFlagForIndex(10, "STATE_CAPTURE_SCREENSHOT");
+ getFlagForIndex(11, "STATE_CAPTURE_SCREENSHOT");
protected static final int STATE_SCREENSHOT_CAPTURED =
- getFlagForIndex(11, "STATE_SCREENSHOT_CAPTURED");
+ getFlagForIndex(12, "STATE_SCREENSHOT_CAPTURED");
private static final int STATE_SCREENSHOT_VIEW_SHOWN =
- getFlagForIndex(12, "STATE_SCREENSHOT_VIEW_SHOWN");
+ getFlagForIndex(13, "STATE_SCREENSHOT_VIEW_SHOWN");
private static final int STATE_RESUME_LAST_TASK =
- getFlagForIndex(13, "STATE_RESUME_LAST_TASK");
+ getFlagForIndex(14, "STATE_RESUME_LAST_TASK");
private static final int STATE_START_NEW_TASK =
- getFlagForIndex(14, "STATE_START_NEW_TASK");
+ getFlagForIndex(15, "STATE_START_NEW_TASK");
private static final int STATE_CURRENT_TASK_FINISHED =
- getFlagForIndex(15, "STATE_CURRENT_TASK_FINISHED");
+ getFlagForIndex(16, "STATE_CURRENT_TASK_FINISHED");
private static final int STATE_FINISH_WITH_NO_END =
- getFlagForIndex(16, "STATE_FINISH_WITH_NO_END");
+ getFlagForIndex(17, "STATE_FINISH_WITH_NO_END");
private static final int LAUNCHER_UI_STATES =
- STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN | STATE_LAUNCHER_STARTED;
+ STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN | STATE_LAUNCHER_STARTED |
+ STATE_LAUNCHER_BIND_TO_SERVICE;
public static final long MAX_SWIPE_DURATION = 350;
public static final long HOME_DURATION = StaggeredWorkspaceAnim.DURATION_MS;
@@ -429,6 +435,7 @@
setupRecentsViewUi();
linkRecentsViewScroll();
+ activity.runOnBindToTouchInteractionService(this::onLauncherBindToService);
mActivity.registerActivityLifecycleCallbacks(mLifecycleCallbacks);
return true;
@@ -510,6 +517,11 @@
mStateCallback.setState(STATE_LAUNCHER_STARTED);
}
+ private void onLauncherBindToService() {
+ mStateCallback.setState(STATE_LAUNCHER_BIND_TO_SERVICE);
+ flushOnRecentsAnimationAndLauncherBound();
+ }
+
private void onLauncherPresentAndGestureStarted() {
// Re-setup the recents UI when gesture starts, as the state could have been changed during
// that time by a previous window transition.
@@ -557,9 +569,14 @@
runningTasks = new ActivityManager.RunningTaskInfo[splitTaskIds.length];
for (int i = 0; i < splitTaskIds.length; i++) {
int taskId = splitTaskIds[i];
- ActivityManager.RunningTaskInfo rti = new ActivityManager.RunningTaskInfo();
- rti.taskId = taskId;
- runningTasks[i] = rti;
+ // Order matters here, we want first indexed RunningTaskInfo to be leftTop task
+ for (ActivityManager.RunningTaskInfo rti : mGestureState.getRunningTasks()) {
+ if (taskId == rti.taskId) {
+ runningTasks[i] = rti;
+ break;
+ }
+
+ }
}
} else {
runningTasks = new ActivityManager.RunningTaskInfo[]{mGestureState.getRunningTask()};
@@ -812,12 +829,7 @@
}
// Notify when the animation starts
- if (!mRecentsAnimationStartCallbacks.isEmpty()) {
- for (Runnable action : new ArrayList<>(mRecentsAnimationStartCallbacks)) {
- action.run();
- }
- mRecentsAnimationStartCallbacks.clear();
- }
+ flushOnRecentsAnimationAndLauncherBound();
TaskViewUtils.setSplitAuxiliarySurfacesShown(mRecentsAnimationTargets.nonApps, false);
@@ -1195,7 +1207,7 @@
@UiThread
private void animateToProgress(float start, float end, long duration, Interpolator interpolator,
GestureEndTarget target, PointF velocityPxPerMs) {
- runOnRecentsAnimationStart(() -> animateToProgressInternal(start, end, duration,
+ runOnRecentsAnimationAndLauncherBound(() -> animateToProgressInternal(start, end, duration,
interpolator, target, velocityPxPerMs));
}
@@ -1820,12 +1832,12 @@
SurfaceTransactionApplier.create(mRecentsView, applier -> {
runActionOnRemoteHandles(remoteTargetHandle -> remoteTargetHandle.getTransformParams()
.setSyncTransactionApplier(applier));
- runOnRecentsAnimationStart(() ->
+ runOnRecentsAnimationAndLauncherBound(() ->
mRecentsAnimationTargets.addReleaseCheck(applier));
});
mRecentsView.addOnScrollChangedListener(mOnRecentsScrollListener);
- runOnRecentsAnimationStart(() ->
+ runOnRecentsAnimationAndLauncherBound(() ->
mRecentsView.setRecentsAnimationTargets(mRecentsAnimationController,
mRecentsAnimationTargets));
mRecentsViewScrollLinked = true;
@@ -1871,14 +1883,26 @@
}
/**
- * Runs the given {@param action} if the recents animation has already started, or queues it to
- * be run when it is next started.
+ * Runs the given {@param action} if the recents animation has already started and Launcher has
+ * been created and bound to the TouchInteractionService, or queues it to be run when it this
+ * next happens.
*/
- protected void runOnRecentsAnimationStart(Runnable action) {
- if (mRecentsAnimationTargets == null) {
- mRecentsAnimationStartCallbacks.add(action);
- } else {
- action.run();
+ private void runOnRecentsAnimationAndLauncherBound(Runnable action) {
+ mRecentsAnimationStartCallbacks.add(action);
+ flushOnRecentsAnimationAndLauncherBound();
+ }
+
+ private void flushOnRecentsAnimationAndLauncherBound() {
+ if (mRecentsAnimationTargets == null ||
+ !mStateCallback.hasStates(STATE_LAUNCHER_BIND_TO_SERVICE)) {
+ return;
+ }
+
+ if (!mRecentsAnimationStartCallbacks.isEmpty()) {
+ for (Runnable action : new ArrayList<>(mRecentsAnimationStartCallbacks)) {
+ action.run();
+ }
+ mRecentsAnimationStartCallbacks.clear();
}
}
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index 9bc98d6..01f6e23 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -135,6 +135,7 @@
private final int mGestureId;
private ActivityManager.RunningTaskInfo mRunningTask;
+ private ActivityManager.RunningTaskInfo[] mRunningTasks;
private GestureEndTarget mEndTarget;
private RemoteAnimationTargetCompat mLastAppearedTaskTarget;
private Set<Integer> mPreviouslyAppearedTaskIds = new HashSet<>();
@@ -235,6 +236,14 @@
}
/**
+ * This will array will contain the task returned by {@link #getRunningTask()}
+ * @return the running tasks for this gesture.
+ */
+ public ActivityManager.RunningTaskInfo[] getRunningTasks() {
+ return mRunningTasks;
+ }
+
+ /**
* @return the running task id for this gesture.
*/
public int getRunningTaskId() {
@@ -249,6 +258,15 @@
}
/**
+ * TODO(b/210903248) refactor to consolidate w/ method above
+ * Updates the running task for the gesture to be the given {@param runningTask}.
+ */
+ public void updateRunningTasks(ActivityManager.RunningTaskInfo[] runningTasks) {
+ mRunningTasks = runningTasks;
+ updateRunningTask(runningTasks[0]);
+ }
+
+ /**
* Updates the last task that appeared during this gesture.
*/
public void updateLastAppearedTaskTarget(RemoteAnimationTargetCompat lastAppearedTaskTarget) {
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index d6efc71..103f350 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -141,6 +141,11 @@
mTaskbarManager.setActivity(this);
}
+ @Override
+ public void runOnBindToTouchInteractionService(Runnable r) {
+ mTISBindHelper.runOnBindToTouchInteractionService(r);
+ }
+
public void setTaskbarUIController(FallbackTaskbarUIController taskbarUIController) {
mTaskbarUIController = taskbarUIController;
}
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index f6f2cf9..38d6448 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -665,8 +665,8 @@
gestureState.updatePreviouslyAppearedTaskIds(
previousGestureState.getPreviouslyAppearedTaskIds());
} else {
- gestureState.updateRunningTask(TraceHelper.allowIpcs("getRunningTask.0",
- () -> mAM.getRunningTask(false /* filterOnlyVisibleRecents */)));
+ gestureState.updateRunningTasks(TraceHelper.allowIpcs("getRunningTask.0",
+ () -> mAM.getRunningTasks(false /* filterOnlyVisibleRecents */)));
}
return gestureState;
}
diff --git a/quickstep/src/com/android/quickstep/util/TISBindHelper.java b/quickstep/src/com/android/quickstep/util/TISBindHelper.java
index 92c60c8..7b122c6 100644
--- a/quickstep/src/com/android/quickstep/util/TISBindHelper.java
+++ b/quickstep/src/com/android/quickstep/util/TISBindHelper.java
@@ -26,6 +26,7 @@
import com.android.quickstep.TouchInteractionService;
import com.android.quickstep.TouchInteractionService.TISBinder;
+import java.util.ArrayList;
import java.util.function.Consumer;
/**
@@ -44,9 +45,11 @@
private final Runnable mConnectionRunnable = this::internalBindToTIS;
private final Context mContext;
private final Consumer<TISBinder> mConnectionCallback;
+ private final ArrayList<Runnable> mPendingConnectedCallbacks = new ArrayList<>();
private short mConnectionAttempts;
private boolean mTisServiceBound;
+ private boolean mIsConnected;
public TISBindHelper(Context context, Consumer<TISBinder> connectionCallback) {
mContext = context;
@@ -66,7 +69,13 @@
}
Log.d(TAG, "TIS service connected");
+ mIsConnected = true;
mConnectionCallback.accept((TISBinder) iBinder);
+ // Flush the pending callbacks
+ for (Runnable r : mPendingConnectedCallbacks) {
+ r.run();
+ }
+ mPendingConnectedCallbacks.clear();
resetServiceBindRetryState();
}
@@ -79,6 +88,16 @@
internalBindToTIS();
}
+ /**
+ * Runs the given {@param r} runnable when the service is connected.
+ */
+ public void runOnBindToTouchInteractionService(Runnable r) {
+ if (mIsConnected) {
+ r.run();
+ } else {
+ mPendingConnectedCallbacks.add(r);
+ }
+ }
/**
* Binds to {@link TouchInteractionService}. If the binding fails, attempts to retry via
@@ -120,5 +139,7 @@
public void onDestroy() {
internalUnbindToTIS();
resetServiceBindRetryState();
+ mIsConnected = false;
+ mPendingConnectedCallbacks.clear();
}
}
diff --git a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
index dbdcf19..e5664c6 100644
--- a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
+++ b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
@@ -19,6 +19,7 @@
import static android.provider.Settings.ACTION_APP_USAGE_SETTINGS;
import static android.view.Gravity.BOTTOM;
import static android.view.Gravity.CENTER_HORIZONTAL;
+import static android.view.Gravity.START;
import static com.android.launcher3.Utilities.prefixTextWithIcon;
import static com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR;
@@ -38,26 +39,51 @@
import android.os.Build;
import android.os.UserHandle;
import android.util.Log;
+import android.util.Pair;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import android.widget.FrameLayout;
import android.widget.TextView;
+import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.touch.PagedOrientationHandler;
+import com.android.launcher3.util.SplitConfigurationOptions.StagedSplitBounds;
import com.android.systemui.shared.recents.model.Task;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.time.Duration;
import java.util.Locale;
@TargetApi(Build.VERSION_CODES.Q)
public final class DigitalWellBeingToast {
+
+ private static final float THRESHOLD_LEFT_ICON_ONLY = 0.4f;
+ private static final float THRESHOLD_RIGHT_ICON_ONLY = 0.6f;
+
+ /** Will span entire width of taskView with full text */
+ private static final int SPLIT_BANNER_FULLSCREEN = 0;
+ /** Used for grid task view, only showing icon and time */
+ private static final int SPLIT_GRID_BANNER_LARGE = 1;
+ /** Used for grid task view, only showing icon */
+ private static final int SPLIT_GRID_BANNER_SMALL = 2;
+ @IntDef(value = {
+ SPLIT_BANNER_FULLSCREEN,
+ SPLIT_GRID_BANNER_LARGE,
+ SPLIT_GRID_BANNER_SMALL,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface SPLIT_BANNER_CONFIG{}
+
static final Intent OPEN_APP_USAGE_SETTINGS_TEMPLATE = new Intent(ACTION_APP_USAGE_SETTINGS);
static final int MINUTE_MS = 60000;
@@ -74,7 +100,16 @@
private View mBanner;
private ViewOutlineProvider mOldBannerOutlineProvider;
private float mBannerOffsetPercentage;
- private float mVerticalOffset = 0f;
+ /**
+ * Clips rect provided by {@link #mOldBannerOutlineProvider} when in the model state to
+ * hide this banner as the taskView scales up and down
+ */
+ private float mModalOffset = 0f;
+ @Nullable
+ private StagedSplitBounds mStagedSplitBounds;
+ private int mSplitBannerConfig = SPLIT_BANNER_FULLSCREEN;
+ private float mSplitOffsetTranslationY;
+ private float mSplitOffsetTranslationX;
public DigitalWellBeingToast(BaseDraggingActivity activity, TaskView taskView) {
mActivity = activity;
@@ -103,7 +138,7 @@
}
public String getText() {
- return getText(mAppRemainingTimeMs);
+ return getText(mAppRemainingTimeMs, false /* forContentDesc */);
}
public boolean hasLimit() {
@@ -138,6 +173,31 @@
});
}
+ public void setSplitConfiguration(StagedSplitBounds stagedSplitBounds) {
+ mStagedSplitBounds = stagedSplitBounds;
+ if (mStagedSplitBounds == null ||
+ !mActivity.getDeviceProfile().overviewShowAsGrid ||
+ mTaskView.isFocusedTask()) {
+ mSplitBannerConfig = SPLIT_BANNER_FULLSCREEN;
+ return;
+ }
+
+ // For portrait grid only height of task changes, not width. So we keep the text the same
+ if (!mActivity.getDeviceProfile().isLandscape) {
+ mSplitBannerConfig = SPLIT_GRID_BANNER_LARGE;
+ return;
+ }
+
+ // For landscape grid, for 30% width we only show icon, otherwise show icon and time
+ if (mTask.key.id == mStagedSplitBounds.leftTopTaskId) {
+ mSplitBannerConfig = mStagedSplitBounds.leftTaskPercent < THRESHOLD_LEFT_ICON_ONLY ?
+ SPLIT_GRID_BANNER_SMALL : SPLIT_GRID_BANNER_LARGE;
+ } else {
+ mSplitBannerConfig = mStagedSplitBounds.leftTaskPercent > THRESHOLD_RIGHT_ICON_ONLY ?
+ SPLIT_GRID_BANNER_SMALL : SPLIT_GRID_BANNER_LARGE;
+ }
+ }
+
private String getReadableDuration(
Duration duration,
FormatWidth formatWidthHourAndMinute,
@@ -181,30 +241,33 @@
.formatMeasures(new Measure(0, MeasureUnit.MINUTE));
}
- private String getReadableDuration(
- Duration duration,
- FormatWidth formatWidthHourAndMinute,
- @StringRes int durationLessThanOneMinuteStringId) {
- return getReadableDuration(
- duration,
- formatWidthHourAndMinute,
- durationLessThanOneMinuteStringId,
- /* forceFormatWidth= */ false);
- }
-
- private String getRoundedUpToMinuteReadableDuration(long remainingTime) {
+ /**
+ * Returns text to show for the banner depending on {@link #mSplitBannerConfig}
+ * If {@param forContentDesc} is {@code true}, this will always return the full
+ * string corresponding to {@link #SPLIT_BANNER_FULLSCREEN}
+ */
+ private String getText(long remainingTime, boolean forContentDesc) {
final Duration duration = Duration.ofMillis(
remainingTime > MINUTE_MS ?
(remainingTime + MINUTE_MS - 1) / MINUTE_MS * MINUTE_MS :
remainingTime);
- return getReadableDuration(
- duration, FormatWidth.NARROW, R.string.shorter_duration_less_than_one_minute);
- }
+ String readableDuration = getReadableDuration(duration,
+ FormatWidth.NARROW,
+ R.string.shorter_duration_less_than_one_minute,
+ false /* forceFormatWidth */);
+ if (forContentDesc || mSplitBannerConfig == SPLIT_BANNER_FULLSCREEN) {
+ return mActivity.getString(
+ R.string.time_left_for_app,
+ readableDuration);
+ }
- private String getText(long remainingTime) {
- return mActivity.getString(
- R.string.time_left_for_app,
- getRoundedUpToMinuteReadableDuration(remainingTime));
+ if (mSplitBannerConfig == SPLIT_GRID_BANNER_SMALL) {
+ // show no text
+ return "";
+ } else { // SPLIT_GRID_BANNER_LARGE
+ // only show time
+ return readableDuration;
+ }
}
public void openAppUsageSettings(View view) {
@@ -232,7 +295,7 @@
mActivity.getString(
R.string.task_contents_description_with_remaining_time,
task.titleDescription,
- getText(appRemainingTimeMs)) :
+ getText(appRemainingTimeMs, true /* forContentDesc */)) :
task.titleDescription;
}
@@ -261,10 +324,18 @@
private void setupAndAddBanner() {
FrameLayout.LayoutParams layoutParams =
(FrameLayout.LayoutParams) mBanner.getLayoutParams();
- layoutParams.gravity = BOTTOM | CENTER_HORIZONTAL;
+ DeviceProfile deviceProfile = mActivity.getDeviceProfile();
layoutParams.bottomMargin = ((ViewGroup.MarginLayoutParams)
mTaskView.getThumbnail().getLayoutParams()).bottomMargin;
- mBanner.setTranslationY(mBannerOffsetPercentage * mBanner.getHeight());
+ PagedOrientationHandler orientationHandler = mTaskView.getPagedOrientationHandler();
+ Pair<Float, Float> translations = orientationHandler
+ .setDwbLayoutParamsAndGetTranslations(mTaskView.getMeasuredWidth(),
+ mTaskView.getMeasuredHeight(), mStagedSplitBounds, deviceProfile,
+ mTaskView.getThumbnails(), mTask.key.id, mBanner);
+ mSplitOffsetTranslationX = translations.first;
+ mSplitOffsetTranslationY = translations.second;
+ updateTranslationY();
+ updateTranslationX();
mTaskView.addView(mBanner);
}
@@ -274,7 +345,9 @@
@Override
public void getOutline(View view, Outline outline) {
mOldBannerOutlineProvider.getOutline(view, outline);
- outline.offset(0, Math.round(-view.getTranslationY() + mVerticalOffset));
+ float verticalTranslation = -view.getTranslationY() + mModalOffset
+ + mSplitOffsetTranslationY;
+ outline.offset(0, Math.round(verticalTranslation));
}
});
mBanner.setClipToOutline(true);
@@ -282,13 +355,33 @@
void updateBannerOffset(float offsetPercentage, float verticalOffset) {
if (mBanner != null && mBannerOffsetPercentage != offsetPercentage) {
- mVerticalOffset = verticalOffset;
+ mModalOffset = verticalOffset;
mBannerOffsetPercentage = offsetPercentage;
- mBanner.setTranslationY(offsetPercentage * mBanner.getHeight() + mVerticalOffset);
+ updateTranslationY();
mBanner.invalidateOutline();
}
}
+ private void updateTranslationY() {
+ if (mBanner == null) {
+ return;
+ }
+
+ mBanner.setTranslationY(
+ (mBannerOffsetPercentage * mBanner.getHeight()) +
+ mModalOffset +
+ mSplitOffsetTranslationY
+ );
+ }
+
+ private void updateTranslationX() {
+ if (mBanner == null) {
+ return;
+ }
+
+ mBanner.setTranslationX(mSplitOffsetTranslationX);
+ }
+
void setBannerColorTint(int color, float amount) {
if (mBanner == null) {
return;
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
index b215ef1..7e4f9d0 100644
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
@@ -50,17 +50,20 @@
private final float[] mIcon2CenterCoords = new float[2];
private TransformingTouchDelegate mIcon2TouchDelegate;
@Nullable private StagedSplitBounds mSplitBoundsConfig;
+ private final DigitalWellBeingToast mDigitalWellBeingToast2;
+
public GroupedTaskView(Context context) {
- super(context);
+ this(context, null);
}
public GroupedTaskView(Context context, AttributeSet attrs) {
- super(context, attrs);
+ this(context, attrs, 0);
}
public GroupedTaskView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
+ mDigitalWellBeingToast2 = new DigitalWellBeingToast(mActivity, this);
}
@Override
@@ -102,7 +105,9 @@
mIconLoadRequest2 = iconCache.updateIconInBackground(mSecondaryTask,
(task) -> {
setIcon(mIconView2, task.icon);
- // TODO(199936292) Digital Wellbeing for individual tasks?
+ mDigitalWellBeingToast2.initialize(mSecondaryTask);
+ mDigitalWellBeingToast2.setSplitConfiguration(mSplitBoundsConfig);
+ mDigitalWellBeingToast.setSplitConfiguration(mSplitBoundsConfig);
});
}
} else {
@@ -262,6 +267,19 @@
@Override
protected void setIconAndDimTransitionProgress(float progress, boolean invert) {
super.setIconAndDimTransitionProgress(progress, invert);
- mIconView2.setAlpha(mIconView.getAlpha());
+ // Value set by super call
+ float scale = mIconView.getAlpha();
+ mIconView2.setAlpha(scale);
+ mDigitalWellBeingToast2.updateBannerOffset(1f - scale,
+ mCurrentFullscreenParams.mCurrentDrawnInsets.top
+ + mCurrentFullscreenParams.mCurrentDrawnInsets.bottom);
+ }
+
+ @Override
+ public void setColorTint(float amount, int tintColor) {
+ super.setColorTint(amount, tintColor);
+ mIconView2.setIconColorTint(tintColor, amount);
+ mSnapshotView2.setDimAlpha(amount);
+ mDigitalWellBeingToast2.setBannerColorTint(tintColor, amount);
}
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index e33d650..a71e558 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -366,7 +366,7 @@
protected Task mTask;
protected TaskThumbnailView mSnapshotView;
protected IconView mIconView;
- private final DigitalWellBeingToast mDigitalWellBeingToast;
+ protected final DigitalWellBeingToast mDigitalWellBeingToast;
private float mFullscreenProgress;
private float mGridProgress;
private float mNonGridScale = 1;
diff --git a/src/com/android/launcher3/statemanager/StatefulActivity.java b/src/com/android/launcher3/statemanager/StatefulActivity.java
index 7a23caa..e036943 100644
--- a/src/com/android/launcher3/statemanager/StatefulActivity.java
+++ b/src/com/android/launcher3/statemanager/StatefulActivity.java
@@ -185,4 +185,11 @@
@RequiresApi(api = Build.VERSION_CODES.R)
public void updateWindowInsets(WindowInsets.Builder updatedInsetsBuilder,
WindowInsets oldInsets) { }
+
+ /**
+ * Runs the given {@param r} runnable when this activity binds to the touch interaction service.
+ */
+ public void runOnBindToTouchInteractionService(Runnable r) {
+ r.run();
+ }
}
diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index 9a2d6d8..a94ad7c 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -309,6 +309,46 @@
return new PointF(margin, 0);
}
+ @Override
+ public Pair<Float, Float> setDwbLayoutParamsAndGetTranslations(int taskViewWidth,
+ int taskViewHeight, StagedSplitBounds splitBounds, DeviceProfile deviceProfile,
+ View[] thumbnailViews, int desiredTaskId, View banner) {
+ float translationX = 0;
+ float translationY = 0;
+ FrameLayout.LayoutParams bannerParams = (FrameLayout.LayoutParams) banner.getLayoutParams();
+ banner.setPivotX(0);
+ banner.setPivotY(0);
+ banner.setRotation(getDegreesRotated());
+ translationX = banner.getHeight();
+ FrameLayout.LayoutParams snapshotParams =
+ (FrameLayout.LayoutParams) thumbnailViews[0]
+ .getLayoutParams();
+ bannerParams.gravity = TOP | START;
+ if (splitBounds == null) {
+ // Single, fullscreen case
+ bannerParams.width = taskViewHeight - snapshotParams.topMargin;
+ return new Pair<>(translationX, Integer.valueOf(snapshotParams.topMargin).floatValue());
+ }
+
+ // Set correct width
+ if (desiredTaskId == splitBounds.leftTopTaskId) {
+ bannerParams.width = thumbnailViews[0].getMeasuredHeight();
+ } else {
+ bannerParams.width = thumbnailViews[1].getMeasuredHeight();
+ }
+
+ // Set translations
+ if (desiredTaskId == splitBounds.rightBottomTaskId) {
+ translationY = (snapshotParams.topMargin + taskViewHeight)
+ * (splitBounds.leftTaskPercent) +
+ (taskViewHeight * splitBounds.dividerWidthPercent);
+ }
+ if (desiredTaskId == splitBounds.leftTopTaskId) {
+ translationY = snapshotParams.topMargin;
+ }
+ return new Pair<>(translationX, translationY);
+ }
+
/* ---------- The following are only used by TaskViewTouchHandler. ---------- */
@Override
diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java
index 95168fb..19c4639 100644
--- a/src/com/android/launcher3/touch/PagedOrientationHandler.java
+++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java
@@ -191,6 +191,10 @@
*/
PointF getAdditionalInsetForTaskMenu(float margin);
+ Pair<Float, Float> setDwbLayoutParamsAndGetTranslations(int taskViewWidth,
+ int taskViewHeight, StagedSplitBounds splitBounds, DeviceProfile deviceProfile,
+ View[] thumbnailViews, int desiredTaskId, View banner);
+
// The following are only used by TaskViewTouchHandler.
/** @return Either VERTICAL or HORIZONTAL. */
SingleAxisSwipeDetector.Direction getUpDownSwipeDirection();
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index 7d70f77..ad9f95c 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -16,9 +16,11 @@
package com.android.launcher3.touch;
+import static android.view.Gravity.BOTTOM;
import static android.view.Gravity.CENTER_HORIZONTAL;
import static android.view.Gravity.START;
import static android.view.Gravity.TOP;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
@@ -321,6 +323,50 @@
return new PointF(0, 0);
}
+ @Override
+ public Pair<Float, Float> setDwbLayoutParamsAndGetTranslations(int taskViewWidth,
+ int taskViewHeight, StagedSplitBounds splitBounds, DeviceProfile deviceProfile,
+ View[] thumbnailViews, int desiredTaskId, View banner) {
+ float translationX = 0;
+ float translationY = 0;
+ FrameLayout.LayoutParams bannerParams = (FrameLayout.LayoutParams) banner.getLayoutParams();
+ banner.setPivotX(0);
+ banner.setPivotY(0);
+ banner.setRotation(getDegreesRotated());
+ if (splitBounds == null) {
+ // Single, fullscreen case
+ bannerParams.width = MATCH_PARENT;
+ bannerParams.gravity = BOTTOM | CENTER_HORIZONTAL;
+ return new Pair<>(translationX, translationY);
+ }
+
+ bannerParams.gravity = BOTTOM | ((deviceProfile.isLandscape) ? START : CENTER_HORIZONTAL);
+
+ // Set correct width
+ if (desiredTaskId == splitBounds.leftTopTaskId) {
+ bannerParams.width = thumbnailViews[0].getMeasuredWidth();
+ } else {
+ bannerParams.width = thumbnailViews[1].getMeasuredWidth();
+ }
+
+ // Set translations
+ if (deviceProfile.isLandscape) {
+ if (desiredTaskId == splitBounds.rightBottomTaskId) {
+ translationX = ((taskViewWidth * splitBounds.leftTaskPercent)
+ + (taskViewWidth * splitBounds.dividerWidthPercent));
+ }
+ } else {
+ if (desiredTaskId == splitBounds.leftTopTaskId) {
+ FrameLayout.LayoutParams snapshotParams =
+ (FrameLayout.LayoutParams) thumbnailViews[0]
+ .getLayoutParams();
+ translationY = -((taskViewHeight - snapshotParams.topMargin)
+ * (1f - splitBounds.topTaskPercent));
+ }
+ }
+ return new Pair<>(translationX, translationY);
+ }
+
/* ---------- The following are only used by TaskViewTouchHandler. ---------- */
@Override
diff --git a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
index a921f94..de5f99c 100644
--- a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
@@ -16,6 +16,7 @@
package com.android.launcher3.touch;
+import static android.view.Gravity.BOTTOM;
import static android.view.Gravity.CENTER_VERTICAL;
import static android.view.Gravity.END;
import static android.view.Gravity.START;
@@ -29,6 +30,7 @@
import android.content.res.Resources;
import android.graphics.PointF;
import android.graphics.Rect;
+import android.util.Pair;
import android.view.Surface;
import android.view.View;
import android.widget.FrameLayout;
@@ -106,6 +108,47 @@
}
@Override
+ public Pair<Float, Float> setDwbLayoutParamsAndGetTranslations(int taskViewWidth,
+ int taskViewHeight, StagedSplitBounds splitBounds, DeviceProfile deviceProfile,
+ View[] thumbnailViews, int desiredTaskId, View banner) {
+ float translationX = 0;
+ float translationY = 0;
+ FrameLayout.LayoutParams bannerParams = (FrameLayout.LayoutParams) banner.getLayoutParams();
+ banner.setPivotX(0);
+ banner.setPivotY(0);
+ banner.setRotation(getDegreesRotated());
+ FrameLayout.LayoutParams snapshotParams =
+ (FrameLayout.LayoutParams) thumbnailViews[0]
+ .getLayoutParams();
+ bannerParams.gravity = BOTTOM | END;
+ translationX = taskViewWidth - banner.getHeight();
+ if (splitBounds == null) {
+ // Single, fullscreen case
+ bannerParams.width = taskViewHeight - snapshotParams.topMargin;
+ translationY = banner.getHeight();
+ return new Pair<>(translationX, translationY);
+ }
+
+ // Set correct width
+ if (desiredTaskId == splitBounds.leftTopTaskId) {
+ bannerParams.width = thumbnailViews[1].getMeasuredHeight();
+ } else {
+ bannerParams.width = thumbnailViews[0].getMeasuredHeight();
+ }
+
+ // Set translations
+ if (desiredTaskId == splitBounds.rightBottomTaskId) {
+ translationY = -(taskViewHeight - snapshotParams.topMargin)
+ * (1f - splitBounds.leftTaskPercent)
+ + banner.getHeight();
+ }
+ if (desiredTaskId == splitBounds.leftTopTaskId) {
+ translationY = banner.getHeight();
+ }
+ return new Pair<>(translationX, translationY);
+ }
+
+ @Override
public int getDistanceToBottomOfRect(DeviceProfile dp, Rect rect) {
return dp.widthPx - rect.right;
}