Snap for 8591706 from a7c8850a5a to tm-qpr1-release
Change-Id: I5bc8cb8733c5f8749d5345ef7f78cd8fc5c897dc
This commit is contained in:
@@ -57,6 +57,7 @@
|
||||
android:enabled="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<action android:name="android.intent.action.SHOW_WORK_APPS" />
|
||||
<category android:name="android.intent.category.HOME" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.MONKEY"/>
|
||||
|
||||
@@ -92,6 +92,9 @@ import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||
import com.android.systemui.unfold.UnfoldTransitionFactory;
|
||||
import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
|
||||
import com.android.systemui.unfold.config.UnfoldTransitionConfig;
|
||||
import com.android.systemui.unfold.system.ActivityManagerActivityTypeProvider;
|
||||
import com.android.systemui.unfold.system.DeviceStateManagerFoldProvider;
|
||||
import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
@@ -343,15 +346,17 @@ public abstract class BaseQuickstepLauncher extends Launcher {
|
||||
}
|
||||
|
||||
private void initUnfoldTransitionProgressProvider() {
|
||||
final UnfoldTransitionConfig config = UnfoldTransitionFactory.createConfig(this);
|
||||
final UnfoldTransitionConfig config = new ResourceUnfoldTransitionConfig();
|
||||
if (config.isEnabled()) {
|
||||
mUnfoldTransitionProgressProvider =
|
||||
UnfoldTransitionFactory.createUnfoldTransitionProgressProvider(
|
||||
this,
|
||||
/* context= */ this,
|
||||
config,
|
||||
ProxyScreenStatusProvider.INSTANCE,
|
||||
getSystemService(DeviceStateManager.class),
|
||||
getSystemService(ActivityManager.class),
|
||||
new DeviceStateManagerFoldProvider(
|
||||
getSystemService(DeviceStateManager.class), /* context */this),
|
||||
new ActivityManagerActivityTypeProvider(
|
||||
getSystemService(ActivityManager.class)),
|
||||
getSystemService(SensorManager.class),
|
||||
getMainThreadHandler(),
|
||||
getMainExecutor(),
|
||||
|
||||
@@ -19,6 +19,7 @@ import static com.android.launcher3.taskbar.TaskbarLauncherStateController.FLAG_
|
||||
import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_EXTRA_NAVIGATION_BAR;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.annotation.ColorInt;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
@@ -28,6 +29,7 @@ import android.view.TaskTransitionSpec;
|
||||
import android.view.WindowManagerGlobal;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.launcher3.BaseQuickstepLauncher;
|
||||
@@ -139,6 +141,24 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
|
||||
mControllers.taskbarStashController.onLongPressToUnstashTaskbar();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the Launcher resume animator to the given animator set.
|
||||
*
|
||||
* This should be used to run a Launcher resume animation whose progress matches a
|
||||
* swipe progress.
|
||||
*
|
||||
* @param placeholderDuration a placeholder duration to be used to ensure all full-length
|
||||
* sub-animations are properly coordinated. This duration should not
|
||||
* actually be used since this animation tracks a swipe progress.
|
||||
*/
|
||||
protected void addLauncherResumeAnimation(AnimatorSet animation, int placeholderDuration) {
|
||||
animation.play(onLauncherResumedOrPaused(
|
||||
/* isResumed= */ true,
|
||||
/* fromInit= */ false,
|
||||
/* startAnimation= */ false,
|
||||
placeholderDuration));
|
||||
}
|
||||
|
||||
/**
|
||||
* Should be called from onResume() and onPause(), and animates the Taskbar accordingly.
|
||||
*/
|
||||
@@ -147,9 +167,19 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
|
||||
}
|
||||
|
||||
private void onLauncherResumedOrPaused(boolean isResumed, boolean fromInit) {
|
||||
onLauncherResumedOrPaused(
|
||||
isResumed,
|
||||
fromInit,
|
||||
/* startAnimation= */ true,
|
||||
QuickstepTransitionManager.CONTENT_ALPHA_DURATION);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Animator onLauncherResumedOrPaused(
|
||||
boolean isResumed, boolean fromInit, boolean startAnimation, int duration) {
|
||||
if (mKeyguardController.isScreenOff()) {
|
||||
if (!isResumed) {
|
||||
return;
|
||||
return null;
|
||||
} else {
|
||||
// Resuming implicitly means device unlocked
|
||||
mKeyguardController.setScreenOn();
|
||||
@@ -157,8 +187,7 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
|
||||
}
|
||||
|
||||
mTaskbarLauncherStateController.updateStateForFlag(FLAG_RESUMED, isResumed);
|
||||
mTaskbarLauncherStateController.applyState(
|
||||
fromInit ? 0 : QuickstepTransitionManager.CONTENT_ALPHA_DURATION);
|
||||
return mTaskbarLauncherStateController.applyState(fromInit ? 0 : duration, startAnimation);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -29,6 +29,7 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_N
|
||||
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
|
||||
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.app.ActivityOptions;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Context;
|
||||
@@ -60,6 +61,8 @@ import com.android.launcher3.BubbleTextView;
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.LauncherSettings.Favorites;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.dot.DotInfo;
|
||||
import com.android.launcher3.folder.Folder;
|
||||
import com.android.launcher3.folder.FolderIcon;
|
||||
@@ -734,6 +737,45 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
|
||||
return mIsNavBarForceVisible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a single frame of the Launcher start from SUW animation.
|
||||
*
|
||||
* This animation is a combination of the Launcher resume animation, which animates the hotseat
|
||||
* icons into position, the Taskbar unstash to hotseat animation, which animates the Taskbar
|
||||
* stash bar into the hotseat icons, and an override to prevent showing the Taskbar all apps
|
||||
* button.
|
||||
*
|
||||
* This should be used to run a Taskbar unstash to hotseat animation whose progress matches a
|
||||
* swipe progress.
|
||||
*
|
||||
* @param duration a placeholder duration to be used to ensure all full-length
|
||||
* sub-animations are properly coordinated. This duration should not actually
|
||||
* be used since this animation tracks a swipe progress.
|
||||
*/
|
||||
protected AnimatorPlaybackController createLauncherStartFromSuwAnim(int duration) {
|
||||
AnimatorSet fullAnimation = new AnimatorSet();
|
||||
fullAnimation.setDuration(duration);
|
||||
|
||||
TaskbarUIController uiController = mControllers.uiController;
|
||||
if (uiController instanceof LauncherTaskbarUIController) {
|
||||
((LauncherTaskbarUIController) uiController).addLauncherResumeAnimation(
|
||||
fullAnimation, duration);
|
||||
}
|
||||
mControllers.taskbarStashController.addUnstashToHotseatAnimation(fullAnimation, duration);
|
||||
|
||||
if (!FeatureFlags.ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT.get()) {
|
||||
ValueAnimator alphaOverride = ValueAnimator.ofFloat(0, 1);
|
||||
alphaOverride.setDuration(duration);
|
||||
alphaOverride.addUpdateListener(a -> {
|
||||
// Override the alpha updates in the icon alignment animation.
|
||||
mControllers.taskbarViewController.getAllAppsButtonView().setAlpha(0);
|
||||
});
|
||||
fullAnimation.play(alphaOverride);
|
||||
}
|
||||
|
||||
return AnimatorPlaybackController.wrap(fullAnimation, duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when we determine the touchable region.
|
||||
*
|
||||
|
||||
@@ -39,6 +39,7 @@ import androidx.annotation.Nullable;
|
||||
import com.android.launcher3.BaseQuickstepLauncher;
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||
import com.android.launcher3.statemanager.StatefulActivity;
|
||||
import com.android.launcher3.taskbar.unfold.NonDestroyableScopedUnfoldTransitionProgressProvider;
|
||||
import com.android.launcher3.util.DisplayController;
|
||||
@@ -182,6 +183,17 @@ public class TaskbarManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a frame of the first Launcher reveal animation.
|
||||
*
|
||||
* This should be used to run a first Launcher reveal animation whose progress matches a swipe
|
||||
* progress.
|
||||
*/
|
||||
public AnimatorPlaybackController createLauncherStartFromSuwAnim(int duration) {
|
||||
return mTaskbarActivityContext == null
|
||||
? null : mTaskbarActivityContext.createLauncherStartFromSuwAnim(duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the user is unlocked
|
||||
*/
|
||||
|
||||
@@ -35,6 +35,8 @@ import android.view.WindowInsets;
|
||||
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.anim.AnimatorListeners;
|
||||
import com.android.launcher3.taskbar.allapps.TaskbarAllAppsSlideInView;
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
|
||||
import com.android.quickstep.AnimatedFloat;
|
||||
@@ -367,13 +369,34 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the Taskbar unstash to Hotseat animator to the animator set.
|
||||
*
|
||||
* This should be used to run a Taskbar unstash to Hotseat animation whose progress matches a
|
||||
* swipe progress.
|
||||
*
|
||||
* @param placeholderDuration a placeholder duration to be used to ensure all full-length
|
||||
* sub-animations are properly coordinated. This duration should not
|
||||
* actually be used since this animation tracks a swipe progress.
|
||||
*/
|
||||
protected void addUnstashToHotseatAnimation(AnimatorSet animation, int placeholderDuration) {
|
||||
createAnimToIsStashed(
|
||||
/* isStashed= */ false,
|
||||
placeholderDuration,
|
||||
/* startDelay= */ 0,
|
||||
/* animateBg= */ false);
|
||||
animation.play(mAnimator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a stash animation and save to {@link #mAnimator}.
|
||||
* @param isStashed whether it's a stash animation or an unstash animation
|
||||
* @param duration duration of the animation
|
||||
* @param startDelay how many milliseconds to delay the animation after starting it.
|
||||
* @param animateBg whether the taskbar's background should be animated
|
||||
*/
|
||||
private void createAnimToIsStashed(boolean isStashed, long duration, long startDelay) {
|
||||
private void createAnimToIsStashed(
|
||||
boolean isStashed, long duration, long startDelay, boolean animateBg) {
|
||||
if (mAnimator != null) {
|
||||
mAnimator.cancel();
|
||||
}
|
||||
@@ -408,10 +431,14 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
|
||||
secondHalfDurationScale = 0.5f;
|
||||
final float stashTranslation = (mUnstashedHeight - mStashedHeight) / 2f;
|
||||
|
||||
fullLengthAnimatorSet.playTogether(
|
||||
mTaskbarBackgroundOffset.animateToValue(1),
|
||||
mIconTranslationYForStash.animateToValue(stashTranslation)
|
||||
);
|
||||
fullLengthAnimatorSet.play(mIconTranslationYForStash.animateToValue(stashTranslation));
|
||||
if (animateBg) {
|
||||
fullLengthAnimatorSet.play(mTaskbarBackgroundOffset.animateToValue(1));
|
||||
} else {
|
||||
fullLengthAnimatorSet.addListener(AnimatorListeners.forEndCallback(
|
||||
() -> mTaskbarBackgroundOffset.updateValue(1)));
|
||||
}
|
||||
|
||||
firstHalfAnimatorSet.playTogether(
|
||||
mIconAlphaForStash.animateToValue(0),
|
||||
mIconScaleForStash.animateToValue(STASHED_TASKBAR_SCALE)
|
||||
@@ -424,10 +451,15 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
|
||||
secondHalfDurationScale = 0.75f;
|
||||
|
||||
fullLengthAnimatorSet.playTogether(
|
||||
mTaskbarBackgroundOffset.animateToValue(0),
|
||||
mIconScaleForStash.animateToValue(1),
|
||||
mIconTranslationYForStash.animateToValue(0)
|
||||
);
|
||||
mIconTranslationYForStash.animateToValue(0));
|
||||
if (animateBg) {
|
||||
fullLengthAnimatorSet.play(mTaskbarBackgroundOffset.animateToValue(0));
|
||||
} else {
|
||||
fullLengthAnimatorSet.addListener(AnimatorListeners.forEndCallback(
|
||||
() -> mTaskbarBackgroundOffset.updateValue(0)));
|
||||
}
|
||||
|
||||
firstHalfAnimatorSet.playTogether(
|
||||
mTaskbarStashedHandleAlpha.animateToValue(0)
|
||||
);
|
||||
@@ -728,7 +760,7 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
|
||||
mIsStashed = isStashed;
|
||||
|
||||
// This sets mAnimator.
|
||||
createAnimToIsStashed(mIsStashed, duration, startDelay);
|
||||
createAnimToIsStashed(mIsStashed, duration, startDelay, /* animateBg= */ true);
|
||||
if (start) {
|
||||
mAnimator.start();
|
||||
}
|
||||
|
||||
@@ -258,21 +258,21 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
|
||||
setter.addOnFrameListener(anim -> mActivity.setTaskbarWindowHeight(
|
||||
anim.getAnimatedFraction() > 0 ? expandedHeight : collapsedHeight));
|
||||
|
||||
int count = mTaskbarView.getChildCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
for (int i = 0; i < mTaskbarView.getChildCount(); i++) {
|
||||
View child = mTaskbarView.getChildAt(i);
|
||||
|
||||
int positionInHotseat = -1;
|
||||
boolean isRtl = Utilities.isRtl(child.getResources());
|
||||
int positionInHotseat;
|
||||
if (FeatureFlags.ENABLE_ALL_APPS_IN_TASKBAR.get()
|
||||
&& ((isRtl && i == 0) || (!isRtl && i == count - 1))) {
|
||||
&& child == mTaskbarView.getAllAppsButtonView()) {
|
||||
// Note that there is no All Apps button in the hotseat, this position is only used
|
||||
// as its convenient for animation purposes.
|
||||
positionInHotseat = isRtl
|
||||
positionInHotseat = Utilities.isRtl(child.getResources())
|
||||
? -1
|
||||
: mActivity.getDeviceProfile().numShownHotseatIcons;
|
||||
|
||||
setter.setViewAlpha(child, 0, LINEAR);
|
||||
if (!FeatureFlags.ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT.get()) {
|
||||
setter.setViewAlpha(child, 0, LINEAR);
|
||||
}
|
||||
} else if (child.getTag() instanceof ItemInfo) {
|
||||
positionInHotseat = ((ItemInfo) child.getTag()).screenId;
|
||||
} else {
|
||||
|
||||
@@ -51,6 +51,7 @@ import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.SystemClock;
|
||||
import android.os.SystemProperties;
|
||||
import android.util.Log;
|
||||
import android.view.Choreographer;
|
||||
import android.view.InputEvent;
|
||||
@@ -127,6 +128,9 @@ public class TouchInteractionService extends Service
|
||||
|
||||
private static final String TAG = "TouchInteractionService";
|
||||
|
||||
private static final boolean BUBBLES_HOME_GESTURE_ENABLED =
|
||||
SystemProperties.getBoolean("persist.wm.debug.bubbles_home_gesture", false);
|
||||
|
||||
private static final String KEY_BACK_NOTIFICATION_COUNT = "backNotificationCount";
|
||||
private static final String NOTIFY_ACTION_BACK = "com.android.quickstep.action.BACK_GESTURE";
|
||||
private static final String HAS_ENABLED_QUICKSTEP_ONCE = "launcher.has_enabled_quickstep_once";
|
||||
@@ -698,16 +702,30 @@ public class TouchInteractionService extends Service
|
||||
base = new TaskbarStashInputConsumer(this, base, mInputMonitorCompat, tac);
|
||||
}
|
||||
|
||||
// If Bubbles is expanded, use the overlay input consumer, which will close Bubbles
|
||||
// instead of going all the way home when a swipe up is detected.
|
||||
// Notification panel can be expanded on top of expanded bubbles. Bubbles remain
|
||||
// expanded in the back. Make sure swipe up is not passed to bubbles in this case.
|
||||
if ((mDeviceState.isBubblesExpanded() && !mDeviceState.isNotificationPanelExpanded())
|
||||
|| mDeviceState.isSystemUiDialogShowing()) {
|
||||
if (mDeviceState.isBubblesExpanded()) {
|
||||
if (BUBBLES_HOME_GESTURE_ENABLED) {
|
||||
// Bubbles can handle home gesture itself.
|
||||
base = getDefaultInputConsumer();
|
||||
} else {
|
||||
// If Bubbles is expanded, use the overlay input consumer, which will close
|
||||
// Bubbles instead of going all the way home when a swipe up is detected.
|
||||
// Notification panel can be expanded on top of expanded bubbles. Bubbles remain
|
||||
// expanded in the back. Make sure swipe up is not passed to bubbles in this
|
||||
// case.
|
||||
if (!mDeviceState.isNotificationPanelExpanded()) {
|
||||
base = new SysUiOverlayInputConsumer(
|
||||
getBaseContext(), mDeviceState, mInputMonitorCompat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mDeviceState.isSystemUiDialogShowing()) {
|
||||
base = new SysUiOverlayInputConsumer(
|
||||
getBaseContext(), mDeviceState, mInputMonitorCompat);
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (mDeviceState.isScreenPinningActive()) {
|
||||
// Note: we only allow accessibility to wrap this, and it replaces the previous
|
||||
// base input consumer (which should be NO_OP anyway since topTaskLocked == true).
|
||||
|
||||
@@ -17,6 +17,7 @@ package com.android.quickstep.interaction;
|
||||
|
||||
import static com.android.launcher3.Utilities.mapBoundToRange;
|
||||
import static com.android.launcher3.Utilities.mapRange;
|
||||
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
|
||||
import static com.android.launcher3.anim.Interpolators.LINEAR;
|
||||
|
||||
import android.animation.Animator;
|
||||
@@ -54,6 +55,8 @@ import androidx.core.graphics.ColorUtils;
|
||||
import com.android.launcher3.InvariantDeviceProfile;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||
import com.android.launcher3.util.Executors;
|
||||
import com.android.quickstep.AnimatedFloat;
|
||||
import com.android.quickstep.GestureState;
|
||||
import com.android.quickstep.TouchInteractionService.TISBinder;
|
||||
@@ -77,6 +80,8 @@ public class AllSetActivity extends Activity {
|
||||
|
||||
private static final float HINT_BOTTOM_FACTOR = 1 - .94f;
|
||||
|
||||
private static final int MAX_SWIPE_DURATION = 350;
|
||||
|
||||
private TISBindHelper mTISBindHelper;
|
||||
private TISBinder mBinder;
|
||||
|
||||
@@ -89,6 +94,8 @@ public class AllSetActivity extends Activity {
|
||||
private LottieAnimationView mAnimatedBackground;
|
||||
private Animator.AnimatorListener mBackgroundAnimatorListener;
|
||||
|
||||
private AnimatorPlaybackController mLauncherStartAnim = null;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@@ -136,6 +143,10 @@ public class AllSetActivity extends Activity {
|
||||
startBackgroundAnimation();
|
||||
}
|
||||
|
||||
private void runOnUiHelperThread(Runnable runnable) {
|
||||
Executors.UI_HELPER_EXECUTOR.execute(runnable);
|
||||
}
|
||||
|
||||
private void startBackgroundAnimation() {
|
||||
if (Utilities.ATLEAST_S && mVibrator != null && mVibrator.areAllPrimitivesSupported(
|
||||
VibrationEffect.Composition.PRIMITIVE_THUD)) {
|
||||
@@ -144,22 +155,22 @@ public class AllSetActivity extends Activity {
|
||||
new Animator.AnimatorListener() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
mVibrator.vibrate(getVibrationEffect());
|
||||
runOnUiHelperThread(() -> mVibrator.vibrate(getVibrationEffect()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animator animation) {
|
||||
mVibrator.vibrate(getVibrationEffect());
|
||||
runOnUiHelperThread(() -> mVibrator.vibrate(getVibrationEffect()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
mVibrator.cancel();
|
||||
runOnUiHelperThread(mVibrator::cancel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
mVibrator.cancel();
|
||||
runOnUiHelperThread(mVibrator::cancel);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -232,11 +243,20 @@ public class AllSetActivity extends Activity {
|
||||
|
||||
private void onSwipeProgressUpdate() {
|
||||
mBackground.setProgress(mSwipeProgress.value);
|
||||
float alpha = Utilities.mapBoundToRange(mSwipeProgress.value, 0, HINT_BOTTOM_FACTOR,
|
||||
1, 0, LINEAR);
|
||||
float alpha = Utilities.mapBoundToRange(
|
||||
mSwipeProgress.value, 0, HINT_BOTTOM_FACTOR, 1, 0, LINEAR);
|
||||
mContentView.setAlpha(alpha);
|
||||
mContentView.setTranslationY((alpha - 1) * mSwipeUpShift);
|
||||
|
||||
if (mLauncherStartAnim == null) {
|
||||
mLauncherStartAnim = mBinder.getTaskbarManager().createLauncherStartFromSuwAnim(
|
||||
MAX_SWIPE_DURATION);
|
||||
}
|
||||
if (mLauncherStartAnim != null) {
|
||||
mLauncherStartAnim.setPlayFraction(Utilities.mapBoundToRange(
|
||||
mSwipeProgress.value, 0, 1, 0, 1, FAST_OUT_SLOW_IN));
|
||||
}
|
||||
|
||||
if (alpha == 0f) {
|
||||
mAnimatedBackground.pauseAnimation();
|
||||
} else if (!mAnimatedBackground.isAnimating()) {
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<!-- PagedView -->
|
||||
<dimen name="min_page_snap_velocity">3600dp</dimen>
|
||||
|
||||
<!-- Hotseat -->
|
||||
<dimen name="spring_loaded_hotseat_top_margin">44dp</dimen>
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2022 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>
|
||||
|
||||
<!-- The duration of the PagedView page snap animation -->
|
||||
<integer name="config_pageSnapAnimationDuration">550</integer>
|
||||
|
||||
</resources>
|
||||
@@ -15,6 +15,9 @@
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<!-- PagedView-->
|
||||
<dimen name="min_page_snap_velocity">3000dp</dimen>
|
||||
|
||||
<!-- DragController -->
|
||||
<dimen name="drag_flingToDeleteMinVelocity">-1000dp</dimen>
|
||||
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<!-- PagedView -->
|
||||
<dimen name="min_page_snap_velocity">5300dp</dimen>
|
||||
|
||||
<!-- Dynamic grid -->
|
||||
<dimen name="dynamic_grid_edge_margin">21.93dp</dimen>
|
||||
<dimen name="cell_layout_padding">29.33dp</dimen>
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<!-- PagedView -->
|
||||
<dimen name="min_page_snap_velocity">3400dp</dimen>
|
||||
|
||||
<!-- AllApps -->
|
||||
<dimen name="all_apps_bottom_sheet_horizontal_padding">28dp</dimen>
|
||||
|
||||
@@ -28,6 +31,7 @@
|
||||
<dimen name="drop_target_button_drawable_horizontal_padding">24dp</dimen>
|
||||
<dimen name="drop_target_button_drawable_vertical_padding">20dp</dimen>
|
||||
<dimen name="drop_target_button_gap">32dp</dimen>
|
||||
<dimen name="drop_target_button_workspace_edge_gap">32dp</dimen>
|
||||
<dimen name="drop_target_top_margin">110dp</dimen>
|
||||
<dimen name="drop_target_bottom_margin">48dp</dimen>
|
||||
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2022 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>
|
||||
<!-- Miscellaneous -->
|
||||
<bool name="config_largeHeap">false</bool>
|
||||
@@ -25,6 +40,9 @@
|
||||
<!-- The duration of the animation from search hint to text entry -->
|
||||
<integer name="config_searchHintAnimationDuration">50</integer>
|
||||
|
||||
<!-- The duration of the PagedView page snap animation -->
|
||||
<integer name="config_pageSnapAnimationDuration">750</integer>
|
||||
|
||||
<!-- View tag key used to store SpringAnimation data. -->
|
||||
<item type="id" name="spring_animation_tag" />
|
||||
|
||||
|
||||
+13
-6
@@ -24,6 +24,8 @@
|
||||
<dimen name="dynamic_grid_icon_drawable_padding">7dp</dimen>
|
||||
<!-- Minimum space between workspace and hotseat in spring loaded mode -->
|
||||
<dimen name="dynamic_grid_min_spring_loaded_space">8dp</dimen>
|
||||
<!-- Minimum amount of next page visible in spring loaded mode -->
|
||||
<dimen name="dynamic_grid_spring_loaded_min_next_space_visible">24dp</dimen>
|
||||
|
||||
<dimen name="dynamic_grid_cell_border_spacing">16dp</dimen>
|
||||
<dimen name="cell_layout_padding">10.77dp</dimen>
|
||||
@@ -58,13 +60,9 @@
|
||||
<!-- Drop target bar -->
|
||||
<dimen name="dynamic_grid_drop_target_size">56dp</dimen>
|
||||
<dimen name="drop_target_vertical_gap">20dp</dimen>
|
||||
<dimen name="drop_target_top_margin">36dp</dimen>
|
||||
<dimen name="drop_target_top_margin">32dp</dimen>
|
||||
<dimen name="drop_target_bottom_margin">16dp</dimen>
|
||||
|
||||
<!-- Button drop target bar -->
|
||||
<dimen name="button_drop_target_min_text_size">10sp</dimen>
|
||||
<dimen name="button_drop_target_resize_text_increment">1sp</dimen>
|
||||
|
||||
<!-- App Widget resize frame -->
|
||||
<dimen name="widget_handle_margin">13dp</dimen>
|
||||
<dimen name="resize_frame_background_padding">24dp</dimen>
|
||||
@@ -99,6 +97,13 @@
|
||||
<dimen name="fastscroll_width">58dp</dimen>
|
||||
<dimen name="fastscroll_end_margin">-26dp</dimen>
|
||||
|
||||
<!-- PagedView -->
|
||||
<dimen name="fling_threshold_velocity">500dp</dimen>
|
||||
<dimen name="easy_fling_threshold_velocity">400dp</dimen>
|
||||
<dimen name="min_fling_velocity">250dp</dimen>
|
||||
<!-- The minimum velocity of a page snap after a fling gesture -->
|
||||
<dimen name="min_page_snap_velocity">1500dp</dimen>
|
||||
|
||||
<!-- All Apps -->
|
||||
<dimen name="all_apps_starting_vertical_translate">300dp</dimen>
|
||||
<dimen name="all_apps_search_bar_field_height">48dp</dimen>
|
||||
@@ -229,7 +234,9 @@
|
||||
<dimen name="drop_target_button_drawable_padding">8dp</dimen>
|
||||
<dimen name="drop_target_button_drawable_horizontal_padding">16dp</dimen>
|
||||
<dimen name="drop_target_button_drawable_vertical_padding">8dp</dimen>
|
||||
<dimen name="drop_target_button_gap">22dp</dimen>
|
||||
<dimen name="drop_target_button_gap">28dp</dimen>
|
||||
<dimen name="drop_target_button_workspace_edge_gap">0dp</dimen>
|
||||
<dimen name="drop_target_button_screen_edge_gap">28dp</dimen>
|
||||
|
||||
<!-- the distance an icon must be dragged before button drop targets accept it -->
|
||||
<dimen name="drag_distanceThreshold">30dp</dimen>
|
||||
|
||||
@@ -24,6 +24,7 @@ import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.text.InputType;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
@@ -49,6 +50,8 @@ public abstract class ButtonDropTarget extends TextView
|
||||
private static final int[] sTempCords = new int[2];
|
||||
private static final int DRAG_VIEW_DROP_DURATION = 285;
|
||||
private static final float DRAG_VIEW_HOVER_OVER_OPACITY = 0.65f;
|
||||
private static final int MAX_LINES_TEXT_MULTI_LINE = 2;
|
||||
private static final int MAX_LINES_TEXT_SINGLE_LINE = 1;
|
||||
|
||||
public static final int TOOLTIP_DEFAULT = 0;
|
||||
public static final int TOOLTIP_LEFT = 1;
|
||||
@@ -72,6 +75,8 @@ public abstract class ButtonDropTarget extends TextView
|
||||
protected CharSequence mText;
|
||||
protected Drawable mDrawable;
|
||||
private boolean mTextVisible = true;
|
||||
private boolean mIconVisible = true;
|
||||
private boolean mTextMultiLine = true;
|
||||
|
||||
private PopupWindow mToolTip;
|
||||
private int mToolTipLocation;
|
||||
@@ -109,8 +114,7 @@ public abstract class ButtonDropTarget extends TextView
|
||||
// drawableLeft and drawableStart.
|
||||
mDrawable = getContext().getDrawable(resId).mutate();
|
||||
mDrawable.setTintList(getTextColors());
|
||||
centerIcon();
|
||||
setCompoundDrawablesRelative(mDrawable, null, null, null);
|
||||
updateIconVisibility();
|
||||
}
|
||||
|
||||
public void setDropTargetBar(DropTargetBar dropTargetBar) {
|
||||
@@ -306,13 +310,49 @@ public abstract class ButtonDropTarget extends TextView
|
||||
if (mTextVisible != isVisible || !TextUtils.equals(newText, getText())) {
|
||||
mTextVisible = isVisible;
|
||||
setText(newText);
|
||||
centerIcon();
|
||||
setCompoundDrawablesRelative(mDrawable, null, null, null);
|
||||
int drawablePadding = mTextVisible ? mDrawablePadding : 0;
|
||||
setCompoundDrawablePadding(drawablePadding);
|
||||
updateIconVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display button text over multiple lines when isMultiLine is true, single line otherwise.
|
||||
*/
|
||||
public void setTextMultiLine(boolean isMultiLine) {
|
||||
if (mTextMultiLine != isMultiLine) {
|
||||
mTextMultiLine = isMultiLine;
|
||||
setSingleLine(!isMultiLine);
|
||||
setMaxLines(isMultiLine ? MAX_LINES_TEXT_MULTI_LINE : MAX_LINES_TEXT_SINGLE_LINE);
|
||||
int inputType = InputType.TYPE_CLASS_TEXT;
|
||||
if (isMultiLine) {
|
||||
inputType |= InputType.TYPE_TEXT_FLAG_MULTI_LINE;
|
||||
|
||||
}
|
||||
setInputType(inputType);
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean isTextMultiLine() {
|
||||
return mTextMultiLine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the button icon visible when isVisible is true, hides it otherwise.
|
||||
*/
|
||||
public void setIconVisible(boolean isVisible) {
|
||||
if (mIconVisible != isVisible) {
|
||||
mIconVisible = isVisible;
|
||||
updateIconVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateIconVisibility() {
|
||||
if (mIconVisible) {
|
||||
centerIcon();
|
||||
}
|
||||
setCompoundDrawablesRelative(mIconVisible ? mDrawable : null, null, null, null);
|
||||
setCompoundDrawablePadding(mIconVisible && mTextVisible ? mDrawablePadding : 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||
super.onSizeChanged(w, h, oldw, oldh);
|
||||
@@ -324,40 +364,6 @@ public abstract class ButtonDropTarget extends TextView
|
||||
hideTooltip();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reduce the size of the text until it fits or reaches a minimum.
|
||||
*
|
||||
* The minimum size is defined by {@code R.dimen.button_drop_target_min_text_size} and
|
||||
* it diminishes by intervals defined by
|
||||
* {@code R.dimen.button_drop_target_resize_text_increment}
|
||||
* This functionality is very similar to the option
|
||||
* {@link TextView#setAutoSizeTextTypeWithDefaults(int)} but can't be used in this view because
|
||||
* the layout width is {@code WRAP_CONTENT}.
|
||||
*
|
||||
* @param availableWidth Available width in the button to fit the text, used in
|
||||
* {@code ButtonDropTarget#isTextTruncated(int)}
|
||||
* @return The biggest text size in SP that makes the text fit or if the text can't fit returns
|
||||
* the min available value
|
||||
*/
|
||||
public float resizeTextToFit(int availableWidth) {
|
||||
float minSize = Utilities.pxToSp(getResources()
|
||||
.getDimensionPixelSize(R.dimen.button_drop_target_min_text_size));
|
||||
float step = Utilities.pxToSp(getResources()
|
||||
.getDimensionPixelSize(R.dimen.button_drop_target_resize_text_increment));
|
||||
float textSize = Utilities.pxToSp(getTextSize());
|
||||
|
||||
while (textSize > minSize) {
|
||||
if (isTextTruncated(availableWidth)) {
|
||||
textSize -= step;
|
||||
setTextSize(textSize);
|
||||
} else {
|
||||
return textSize;
|
||||
}
|
||||
}
|
||||
return minSize;
|
||||
}
|
||||
|
||||
public boolean isTextTruncated(int availableWidth) {
|
||||
availableWidth -= (getPaddingLeft() + getPaddingRight() + mDrawable.getIntrinsicWidth()
|
||||
+ getCompoundDrawablePadding());
|
||||
|
||||
@@ -108,6 +108,7 @@ public class DeviceProfile {
|
||||
public float workspaceSpringLoadShrunkTop;
|
||||
public float workspaceSpringLoadShrunkBottom;
|
||||
public final int workspaceSpringLoadedBottomSpace;
|
||||
public final int workspaceSpringLoadedMinNextPageVisiblePx;
|
||||
|
||||
private final int extraSpace;
|
||||
public int workspaceTopPadding;
|
||||
@@ -214,6 +215,8 @@ public class DeviceProfile {
|
||||
public int dropTargetHorizontalPaddingPx;
|
||||
public int dropTargetVerticalPaddingPx;
|
||||
public int dropTargetGapPx;
|
||||
public int dropTargetButtonWorkspaceEdgeGapPx;
|
||||
public int dropTargetButtonScreenEdgeGapPx;
|
||||
|
||||
// Insets
|
||||
private final Rect mInsets = new Rect();
|
||||
@@ -343,9 +346,15 @@ public class DeviceProfile {
|
||||
dropTargetVerticalPaddingPx = res.getDimensionPixelSize(
|
||||
R.dimen.drop_target_button_drawable_vertical_padding);
|
||||
dropTargetGapPx = res.getDimensionPixelSize(R.dimen.drop_target_button_gap);
|
||||
dropTargetButtonWorkspaceEdgeGapPx = res.getDimensionPixelSize(
|
||||
R.dimen.drop_target_button_workspace_edge_gap);
|
||||
dropTargetButtonScreenEdgeGapPx = res.getDimensionPixelSize(
|
||||
R.dimen.drop_target_button_screen_edge_gap);
|
||||
|
||||
workspaceSpringLoadedBottomSpace =
|
||||
res.getDimensionPixelSize(R.dimen.dynamic_grid_min_spring_loaded_space);
|
||||
workspaceSpringLoadedMinNextPageVisiblePx = res.getDimensionPixelSize(
|
||||
R.dimen.dynamic_grid_spring_loaded_min_next_space_visible);
|
||||
|
||||
workspaceCellPaddingXPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_padding_x);
|
||||
|
||||
@@ -501,7 +510,7 @@ public class DeviceProfile {
|
||||
*/
|
||||
private int calculateQsbWidth() {
|
||||
if (isQsbInline) {
|
||||
int columns = isTwoPanels ? inv.numColumns * 2 : inv.numColumns;
|
||||
int columns = getPanelCount() * inv.numColumns;
|
||||
return getIconToIconWidthForColumns(columns)
|
||||
- iconSizePx * numShownHotseatIcons
|
||||
- hotseatBorderSpace * numShownHotseatIcons;
|
||||
@@ -954,13 +963,6 @@ public class DeviceProfile {
|
||||
return workspaceSpringLoadShrunkBottom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the minimum visible amount of the next workspace page when in the spring-loaded state.
|
||||
*/
|
||||
private float getWorkspaceSpringLoadedMinimumNextPageVisible() {
|
||||
return getCellSize().x / 2f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the scale of the workspace for the spring-loaded edit state.
|
||||
*/
|
||||
@@ -972,8 +974,7 @@ public class DeviceProfile {
|
||||
// Reduce scale if next pages would not be visible after scaling the workspace
|
||||
int workspaceWidth = availableWidthPx;
|
||||
float scaledWorkspaceWidth = workspaceWidth * scale;
|
||||
float maxAvailableWidth =
|
||||
workspaceWidth - (2 * getWorkspaceSpringLoadedMinimumNextPageVisible());
|
||||
float maxAvailableWidth = workspaceWidth - (2 * workspaceSpringLoadedMinNextPageVisiblePx);
|
||||
if (scaledWorkspaceWidth > maxAvailableWidth) {
|
||||
scale *= maxAvailableWidth / scaledWorkspaceWidth;
|
||||
}
|
||||
@@ -1412,11 +1413,19 @@ public class DeviceProfile {
|
||||
writer.println(prefix + pxToDpStr("dropTargetBarSizePx", dropTargetBarSizePx));
|
||||
writer.println(
|
||||
prefix + pxToDpStr("dropTargetBarBottomMarginPx", dropTargetBarBottomMarginPx));
|
||||
writer.println(prefix + pxToDpStr("dropTargetButtonWorkspaceEdgeGapPx",
|
||||
dropTargetButtonWorkspaceEdgeGapPx));
|
||||
writer.println(prefix + pxToDpStr("dropTargetButtonScreenEdgeGapPx",
|
||||
dropTargetButtonScreenEdgeGapPx));
|
||||
|
||||
writer.println(
|
||||
prefix + pxToDpStr("workspaceSpringLoadShrunkTop", workspaceSpringLoadShrunkTop));
|
||||
writer.println(prefix + pxToDpStr("workspaceSpringLoadShrunkBottom",
|
||||
workspaceSpringLoadShrunkBottom));
|
||||
writer.println(prefix + pxToDpStr("workspaceSpringLoadedBottomSpace",
|
||||
workspaceSpringLoadedBottomSpace));
|
||||
writer.println(prefix + pxToDpStr("workspaceSpringLoadedMinNextPageVisiblePx",
|
||||
workspaceSpringLoadedMinNextPageVisiblePx));
|
||||
writer.println(
|
||||
prefix + pxToDpStr("getWorkspaceSpringLoadScale()", getWorkspaceSpringLoadScale()));
|
||||
}
|
||||
|
||||
@@ -39,8 +39,6 @@ import com.android.launcher3.dragndrop.DragController.DragListener;
|
||||
import com.android.launcher3.dragndrop.DragOptions;
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/*
|
||||
* The top bar containing various drop targets: Delete/App Info/Uninstall.
|
||||
*/
|
||||
@@ -53,6 +51,8 @@ public class DropTargetBar extends FrameLayout
|
||||
private final Runnable mFadeAnimationEndRunnable =
|
||||
() -> updateVisibility(DropTargetBar.this);
|
||||
|
||||
private final Launcher mLauncher;
|
||||
|
||||
@ViewDebug.ExportedProperty(category = "launcher")
|
||||
protected boolean mDeferOnDragEnd;
|
||||
|
||||
@@ -60,16 +60,19 @@ public class DropTargetBar extends FrameLayout
|
||||
protected boolean mVisible = false;
|
||||
|
||||
private ButtonDropTarget[] mDropTargets;
|
||||
private ButtonDropTarget[] mTempTargets;
|
||||
private ViewPropertyAnimator mCurrentAnimation;
|
||||
|
||||
private boolean mIsVertical = true;
|
||||
|
||||
public DropTargetBar(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mLauncher = Launcher.getLauncher(context);
|
||||
}
|
||||
|
||||
public DropTargetBar(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
mLauncher = Launcher.getLauncher(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -80,12 +83,13 @@ public class DropTargetBar extends FrameLayout
|
||||
mDropTargets[i] = (ButtonDropTarget) getChildAt(i);
|
||||
mDropTargets[i].setDropTargetBar(this);
|
||||
}
|
||||
mTempTargets = new ButtonDropTarget[getChildCount()];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInsets(Rect insets) {
|
||||
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
|
||||
DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile();
|
||||
DeviceProfile grid = mLauncher.getDeviceProfile();
|
||||
mIsVertical = grid.isVerticalBarLayout();
|
||||
|
||||
lp.leftMargin = insets.left;
|
||||
@@ -116,10 +120,15 @@ public class DropTargetBar extends FrameLayout
|
||||
lp.height = grid.dropTargetBarSizePx;
|
||||
lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
|
||||
|
||||
DeviceProfile dp = mLauncher.getDeviceProfile();
|
||||
int horizontalPadding = dp.dropTargetHorizontalPaddingPx;
|
||||
int verticalPadding = dp.dropTargetVerticalPaddingPx;
|
||||
setLayoutParams(lp);
|
||||
for (ButtonDropTarget button : mDropTargets) {
|
||||
button.setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.dropTargetTextSizePx);
|
||||
button.setToolTipLocation(tooltipLocation);
|
||||
button.setPadding(horizontalPadding, verticalPadding, horizontalPadding,
|
||||
verticalPadding);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,36 +144,83 @@ public class DropTargetBar extends FrameLayout
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
int width = MeasureSpec.getSize(widthMeasureSpec);
|
||||
int height = MeasureSpec.getSize(heightMeasureSpec);
|
||||
int heightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
|
||||
|
||||
int visibleCount = getVisibleButtonsCount();
|
||||
if (visibleCount > 0) {
|
||||
int availableWidth = width / visibleCount;
|
||||
boolean textVisible = true;
|
||||
boolean textResized = false;
|
||||
float textSize = mDropTargets[0].getTextSize();
|
||||
for (ButtonDropTarget button : mDropTargets) {
|
||||
if (button.getVisibility() == GONE) {
|
||||
continue;
|
||||
}
|
||||
if (button.isTextTruncated(availableWidth)) {
|
||||
textSize = Math.min(textSize, button.resizeTextToFit(availableWidth));
|
||||
textResized = true;
|
||||
}
|
||||
textVisible = textVisible && !button.isTextTruncated(availableWidth);
|
||||
}
|
||||
int visibleCount = getVisibleButtons(mTempTargets);
|
||||
if (visibleCount == 1) {
|
||||
int widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST);
|
||||
|
||||
if (textResized) {
|
||||
for (ButtonDropTarget button : mDropTargets) {
|
||||
button.setTextSize(textSize);
|
||||
}
|
||||
}
|
||||
ButtonDropTarget firstButton = mTempTargets[0];
|
||||
firstButton.setTextVisible(true);
|
||||
firstButton.setIconVisible(true);
|
||||
firstButton.measure(widthSpec, heightSpec);
|
||||
} else if (visibleCount == 2) {
|
||||
DeviceProfile dp = mLauncher.getDeviceProfile();
|
||||
int verticalPadding = dp.dropTargetVerticalPaddingPx;
|
||||
int horizontalPadding = dp.dropTargetHorizontalPaddingPx;
|
||||
|
||||
int widthSpec = MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST);
|
||||
int heightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
|
||||
for (ButtonDropTarget button : mDropTargets) {
|
||||
if (button.getVisibility() != GONE) {
|
||||
button.setTextVisible(textVisible);
|
||||
button.measure(widthSpec, heightSpec);
|
||||
ButtonDropTarget firstButton = mTempTargets[0];
|
||||
firstButton.setTextVisible(true);
|
||||
firstButton.setIconVisible(true);
|
||||
|
||||
ButtonDropTarget secondButton = mTempTargets[1];
|
||||
secondButton.setTextVisible(true);
|
||||
secondButton.setIconVisible(true);
|
||||
secondButton.setTextMultiLine(false);
|
||||
// Reset second button padding in case it was previously changed to multi-line text.
|
||||
secondButton.setPadding(horizontalPadding, verticalPadding, horizontalPadding,
|
||||
verticalPadding);
|
||||
|
||||
if (dp.isTwoPanels) {
|
||||
// Both buttons for two panel fit to the width of one Cell Layout (less
|
||||
// half of the center gap between the buttons).
|
||||
float scale = dp.getWorkspaceSpringLoadScale();
|
||||
int scaledPanelWidth = (int) (dp.getCellLayoutWidth() * scale);
|
||||
int halfButtonGap = dp.dropTargetGapPx / 2;
|
||||
scaledPanelWidth -= halfButtonGap / 2;
|
||||
|
||||
int widthSpec = MeasureSpec.makeMeasureSpec(scaledPanelWidth, MeasureSpec.AT_MOST);
|
||||
firstButton.measure(widthSpec, heightSpec);
|
||||
secondButton.measure(widthSpec, heightSpec);
|
||||
} else {
|
||||
int availableWidth;
|
||||
int buttonGap = dp.dropTargetGapPx;
|
||||
if (mIsVertical) {
|
||||
// Both buttons plus the button gap do not display past the edge of the
|
||||
// scaled workspace, less a pre-defined gap from the edge of the workspace.
|
||||
float scale = dp.getWorkspaceSpringLoadScale();
|
||||
int panelWidth = (int) (dp.getCellLayoutWidth() * scale);
|
||||
availableWidth = Math.min(
|
||||
panelWidth - (2 * dp.dropTargetButtonWorkspaceEdgeGapPx), width);
|
||||
} else {
|
||||
// Both buttons plus the button gap display up to a pre-defined margin of
|
||||
// the unscaled workspace edge.
|
||||
availableWidth = Math.min(
|
||||
dp.availableWidthPx - (2 * dp.dropTargetButtonScreenEdgeGapPx),
|
||||
width);
|
||||
}
|
||||
int widthSpec = MeasureSpec.makeMeasureSpec(availableWidth - buttonGap,
|
||||
MeasureSpec.AT_MOST);
|
||||
|
||||
// First button's width is at most the drop target bar's total width less the button
|
||||
// gap.
|
||||
firstButton.measure(widthSpec, heightSpec);
|
||||
|
||||
int usedWidth = firstButton.getMeasuredWidth() + buttonGap;
|
||||
int remainingWidth = availableWidth - usedWidth;
|
||||
widthSpec = MeasureSpec.makeMeasureSpec(remainingWidth, MeasureSpec.AT_MOST);
|
||||
secondButton.measure(widthSpec, heightSpec);
|
||||
|
||||
// Remove both icons and put the second button's text on two lines if text is
|
||||
// truncated on phones. We assume first button's text is never truncated, so it
|
||||
// remains single-line.
|
||||
if (secondButton.isTextTruncated(remainingWidth) && !mIsVertical) {
|
||||
firstButton.setIconVisible(false);
|
||||
secondButton.setIconVisible(false);
|
||||
secondButton.setTextMultiLine(true);
|
||||
secondButton.setPadding(secondButton.getPaddingLeft(),
|
||||
secondButton.getPaddingTop() / 2, secondButton.getPaddingRight(),
|
||||
secondButton.getPaddingBottom() / 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -173,98 +229,79 @@ public class DropTargetBar extends FrameLayout
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
int visibleCount = getVisibleButtonsCount();
|
||||
int visibleCount = getVisibleButtons(mTempTargets);
|
||||
if (visibleCount == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Launcher launcher = Launcher.getLauncher(getContext());
|
||||
Workspace<?> workspace = launcher.getWorkspace();
|
||||
DeviceProfile dp = launcher.getDeviceProfile();
|
||||
int buttonHorizontalPadding = dp.dropTargetHorizontalPaddingPx;
|
||||
int buttonVerticalPadding = dp.dropTargetVerticalPaddingPx;
|
||||
DeviceProfile dp = mLauncher.getDeviceProfile();
|
||||
int barCenter = (right - left) / 2;
|
||||
|
||||
ButtonDropTarget[] visibleButtons = Arrays.stream(mDropTargets)
|
||||
.filter(b -> b.getVisibility() != GONE)
|
||||
.toArray(ButtonDropTarget[]::new);
|
||||
Arrays.stream(visibleButtons).forEach(
|
||||
b -> b.setPadding(buttonHorizontalPadding, buttonVerticalPadding,
|
||||
buttonHorizontalPadding, buttonVerticalPadding));
|
||||
if (mIsVertical) {
|
||||
// Center vertical bar over scaled workspace, accounting for hotseat offset.
|
||||
float scale = dp.getWorkspaceSpringLoadScale();
|
||||
Workspace<?> ws = mLauncher.getWorkspace();
|
||||
int workspaceCenter = (ws.getLeft() + ws.getRight()) / 2;
|
||||
int cellLayoutCenter = ((dp.getInsets().left + dp.workspacePadding.left) + (dp.widthPx
|
||||
- dp.getInsets().right - dp.workspacePadding.right)) / 2;
|
||||
int cellLayoutCenterOffset = (int) ((cellLayoutCenter - workspaceCenter) * scale);
|
||||
barCenter = workspaceCenter + cellLayoutCenterOffset;
|
||||
}
|
||||
|
||||
if (visibleCount == 1) {
|
||||
ButtonDropTarget button = visibleButtons[0];
|
||||
ButtonDropTarget button = mTempTargets[0];
|
||||
button.layout(barCenter - (button.getMeasuredWidth() / 2), 0,
|
||||
barCenter + (button.getMeasuredWidth() / 2), button.getMeasuredHeight());
|
||||
} else if (visibleCount == 2) {
|
||||
int buttonGap = dp.dropTargetGapPx;
|
||||
|
||||
if (dp.isTwoPanels) {
|
||||
ButtonDropTarget leftButton = visibleButtons[0];
|
||||
ButtonDropTarget leftButton = mTempTargets[0];
|
||||
leftButton.layout(barCenter - leftButton.getMeasuredWidth() - (buttonGap / 2), 0,
|
||||
barCenter - (buttonGap / 2), leftButton.getMeasuredHeight());
|
||||
|
||||
ButtonDropTarget rightButton = visibleButtons[1];
|
||||
ButtonDropTarget rightButton = mTempTargets[1];
|
||||
rightButton.layout(barCenter + (buttonGap / 2), 0,
|
||||
barCenter + rightButton.getMeasuredWidth() + (buttonGap / 2),
|
||||
barCenter + (buttonGap / 2) + rightButton.getMeasuredWidth(),
|
||||
rightButton.getMeasuredHeight());
|
||||
} else if (dp.isTablet) {
|
||||
int numberOfMargins = visibleCount - 1;
|
||||
int buttonWidths = Arrays.stream(mDropTargets)
|
||||
.filter(b -> b.getVisibility() != GONE)
|
||||
.mapToInt(ButtonDropTarget::getMeasuredWidth)
|
||||
.sum();
|
||||
int totalWidth = buttonWidths + (numberOfMargins * buttonGap);
|
||||
int buttonsStartMargin = barCenter - (totalWidth / 2);
|
||||
|
||||
int start = buttonsStartMargin;
|
||||
for (ButtonDropTarget button : visibleButtons) {
|
||||
int margin = (start != buttonsStartMargin) ? buttonGap : 0;
|
||||
button.layout(start + margin, 0, start + margin + button.getMeasuredWidth(),
|
||||
button.getMeasuredHeight());
|
||||
start += button.getMeasuredWidth() + margin;
|
||||
}
|
||||
} else if (mIsVertical) {
|
||||
// Center buttons over workspace, not screen.
|
||||
int verticalCenter = (workspace.getRight() - workspace.getLeft()) / 2;
|
||||
ButtonDropTarget leftButton = visibleButtons[0];
|
||||
leftButton.layout(verticalCenter - leftButton.getMeasuredWidth() - (buttonGap / 2),
|
||||
0, verticalCenter - (buttonGap / 2), leftButton.getMeasuredHeight());
|
||||
|
||||
ButtonDropTarget rightButton = visibleButtons[1];
|
||||
rightButton.layout(verticalCenter + (buttonGap / 2), 0,
|
||||
verticalCenter + rightButton.getMeasuredWidth() + (buttonGap / 2),
|
||||
rightButton.getMeasuredHeight());
|
||||
} else if (dp.isPhone) {
|
||||
// Buttons aligned to outer edges of scaled workspace.
|
||||
float scale = dp.getWorkspaceSpringLoadScale();
|
||||
|
||||
int workspaceWidth = (int) (launcher.getWorkspace().getNormalChildWidth() * scale);
|
||||
int start = barCenter - (workspaceWidth / 2);
|
||||
int end = barCenter + (workspaceWidth / 2);
|
||||
|
||||
ButtonDropTarget leftButton = visibleButtons[0];
|
||||
ButtonDropTarget rightButton = visibleButtons[1];
|
||||
|
||||
// If the text within the buttons is too long, the buttons can overlap
|
||||
int overlap = start + leftButton.getMeasuredWidth() + rightButton.getMeasuredWidth()
|
||||
- end;
|
||||
if (overlap > 0) {
|
||||
end += overlap;
|
||||
} else {
|
||||
int start;
|
||||
int end;
|
||||
if (mIsVertical) {
|
||||
// Scaled CellLayout width is assumed to not exceed the bounds of left/right.
|
||||
float scale = dp.getWorkspaceSpringLoadScale();
|
||||
int panelWidth = (int) (dp.getCellLayoutWidth() * scale);
|
||||
start = barCenter - (panelWidth / 2) + dp.dropTargetButtonWorkspaceEdgeGapPx;
|
||||
end = barCenter + (panelWidth / 2) - dp.dropTargetButtonWorkspaceEdgeGapPx;
|
||||
} else {
|
||||
start = Math.max(dp.dropTargetButtonScreenEdgeGapPx, left);
|
||||
end = Math.min(dp.availableWidthPx - dp.dropTargetButtonScreenEdgeGapPx, right);
|
||||
}
|
||||
|
||||
leftButton.layout(start, 0, start + leftButton.getMeasuredWidth(),
|
||||
ButtonDropTarget leftButton = mTempTargets[0];
|
||||
ButtonDropTarget rightButton = mTempTargets[1];
|
||||
|
||||
int leftButtonWidth = leftButton.getMeasuredWidth();
|
||||
int rightButtonWidth = rightButton.getMeasuredWidth();
|
||||
int buttonPlusGapWidth = leftButtonWidth + buttonGap + rightButtonWidth;
|
||||
|
||||
int extraSpace = end - start - buttonPlusGapWidth;
|
||||
start = (start - left) + (extraSpace / 2);
|
||||
|
||||
leftButton.layout(start, 0, start + leftButtonWidth,
|
||||
leftButton.getMeasuredHeight());
|
||||
rightButton.layout(end - rightButton.getMeasuredWidth(), 0, end,
|
||||
|
||||
int rightButtonStart = start + leftButtonWidth + buttonGap;
|
||||
rightButton.layout(rightButtonStart, 0, rightButtonStart + rightButtonWidth,
|
||||
rightButton.getMeasuredHeight());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int getVisibleButtonsCount() {
|
||||
private int getVisibleButtons(ButtonDropTarget[] outVisibleButtons) {
|
||||
int visibleCount = 0;
|
||||
for (ButtonDropTarget buttons : mDropTargets) {
|
||||
if (buttons.getVisibility() != GONE) {
|
||||
for (ButtonDropTarget button : mDropTargets) {
|
||||
if (button.getVisibility() != GONE) {
|
||||
outVisibleButtons[visibleCount] = button;
|
||||
visibleCount++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@ import static com.android.launcher3.touch.PagedOrientationHandler.VIEW_SCROLL_TO
|
||||
import android.animation.LayoutTransition;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Rect;
|
||||
@@ -78,27 +80,19 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
|
||||
public static final int INVALID_PAGE = -1;
|
||||
protected static final ComputePageScrollsLogic SIMPLE_SCROLL_LOGIC = (v) -> v.getVisibility() != GONE;
|
||||
|
||||
public static final int PAGE_SNAP_ANIMATION_DURATION = 750;
|
||||
|
||||
private static final float RETURN_TO_ORIGINAL_PAGE_THRESHOLD = 0.33f;
|
||||
// The page is moved more than halfway, automatically move to the next page on touch up.
|
||||
private static final float SIGNIFICANT_MOVE_THRESHOLD = 0.4f;
|
||||
|
||||
private static final float MAX_SCROLL_PROGRESS = 1.0f;
|
||||
|
||||
// The following constants need to be scaled based on density. The scaled versions will be
|
||||
// assigned to the corresponding member variables below.
|
||||
private static final int FLING_THRESHOLD_VELOCITY = 500;
|
||||
private static final int EASY_FLING_THRESHOLD_VELOCITY = 400;
|
||||
private static final int MIN_SNAP_VELOCITY = 1500;
|
||||
private static final int MIN_FLING_VELOCITY = 250;
|
||||
|
||||
private boolean mFreeScroll = false;
|
||||
|
||||
protected final int mFlingThresholdVelocity;
|
||||
protected final int mEasyFlingThresholdVelocity;
|
||||
protected final int mMinFlingVelocity;
|
||||
protected final int mMinSnapVelocity;
|
||||
private int mFlingThresholdVelocity;
|
||||
private int mEasyFlingThresholdVelocity;
|
||||
private int mMinFlingVelocity;
|
||||
private int mMinSnapVelocity;
|
||||
private int mPageSnapAnimationDuration;
|
||||
|
||||
protected boolean mFirstLayout = true;
|
||||
|
||||
@@ -192,11 +186,7 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
|
||||
mPageSlop = configuration.getScaledPagingTouchSlop();
|
||||
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
|
||||
|
||||
float density = getResources().getDisplayMetrics().density;
|
||||
mFlingThresholdVelocity = (int) (FLING_THRESHOLD_VELOCITY * density);
|
||||
mEasyFlingThresholdVelocity = (int) (EASY_FLING_THRESHOLD_VELOCITY * density);
|
||||
mMinFlingVelocity = (int) (MIN_FLING_VELOCITY * density);
|
||||
mMinSnapVelocity = (int) (MIN_SNAP_VELOCITY * density);
|
||||
updateVelocityValues();
|
||||
|
||||
initEdgeEffect();
|
||||
setDefaultFocusHighlightEnabled(false);
|
||||
@@ -628,6 +618,22 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
|
||||
- mInsets.left - mInsets.right;
|
||||
}
|
||||
|
||||
private void updateVelocityValues() {
|
||||
Resources res = getResources();
|
||||
mFlingThresholdVelocity = res.getDimensionPixelSize(R.dimen.fling_threshold_velocity);
|
||||
mEasyFlingThresholdVelocity =
|
||||
res.getDimensionPixelSize(R.dimen.easy_fling_threshold_velocity);
|
||||
mMinFlingVelocity = res.getDimensionPixelSize(R.dimen.min_fling_velocity);
|
||||
mMinSnapVelocity = res.getDimensionPixelSize(R.dimen.min_page_snap_velocity);
|
||||
mPageSnapAnimationDuration = res.getInteger(R.integer.config_pageSnapAnimationDuration);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
updateVelocityValues();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestLayout() {
|
||||
mIsLayoutValid = false;
|
||||
@@ -1616,7 +1622,7 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
|
||||
}
|
||||
|
||||
protected void snapToDestination() {
|
||||
snapToPage(getDestinationPage(), PAGE_SNAP_ANIMATION_DURATION);
|
||||
snapToPage(getDestinationPage(), mPageSnapAnimationDuration);
|
||||
}
|
||||
|
||||
// We want the duration of the page snap animation to be influenced by the distance that
|
||||
@@ -1640,7 +1646,7 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
|
||||
if (Math.abs(velocity) < mMinFlingVelocity) {
|
||||
// If the velocity is low enough, then treat this more as an automatic page advance
|
||||
// as opposed to an apparent physical response to flinging
|
||||
return snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION);
|
||||
return snapToPage(whichPage, mPageSnapAnimationDuration);
|
||||
}
|
||||
|
||||
// Here we compute a "distance" that will be used in the computation of the overall
|
||||
@@ -1663,11 +1669,11 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
|
||||
}
|
||||
|
||||
public boolean snapToPage(int whichPage) {
|
||||
return snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION);
|
||||
return snapToPage(whichPage, mPageSnapAnimationDuration);
|
||||
}
|
||||
|
||||
public boolean snapToPageImmediately(int whichPage) {
|
||||
return snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION, true);
|
||||
return snapToPage(whichPage, mPageSnapAnimationDuration, true);
|
||||
}
|
||||
|
||||
public boolean snapToPage(int whichPage, int duration) {
|
||||
|
||||
@@ -326,7 +326,7 @@ public abstract class BaseAllAppsContainerView<T extends Context & ActivityConte
|
||||
}
|
||||
|
||||
protected boolean isPersonalTab() {
|
||||
return mViewPager.getNextPage() == 0;
|
||||
return mViewPager == null || mViewPager.getNextPage() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -388,34 +388,34 @@ public abstract class BaseAllAppsContainerView<T extends Context & ActivityConte
|
||||
mInsets.set(insets);
|
||||
DeviceProfile grid = mActivityContext.getDeviceProfile();
|
||||
|
||||
for (int i = 0; i < mAH.size(); i++) {
|
||||
mAH.get(i).mPadding.bottom = insets.bottom;
|
||||
mAH.get(i).mPadding.left = mAH.get(i).mPadding.right = grid.allAppsLeftRightPadding;
|
||||
mAH.get(i).applyPadding();
|
||||
}
|
||||
applyAdapterPaddings(grid);
|
||||
|
||||
MarginLayoutParams mlp = (MarginLayoutParams) getLayoutParams();
|
||||
int leftRightMargin = grid.allAppsLeftRightMargin;
|
||||
mlp.leftMargin = insets.left + leftRightMargin;
|
||||
mlp.rightMargin = insets.right + leftRightMargin;
|
||||
mlp.leftMargin = insets.left;
|
||||
mlp.rightMargin = insets.right;
|
||||
setLayoutParams(mlp);
|
||||
|
||||
if (grid.isVerticalBarLayout()) {
|
||||
setPadding(grid.workspacePadding.left, 0, grid.workspacePadding.right, 0);
|
||||
} else {
|
||||
setPadding(0, grid.allAppsTopPadding, 0, 0);
|
||||
setPadding(grid.allAppsLeftRightMargin, grid.allAppsTopPadding,
|
||||
grid.allAppsLeftRightMargin, 0);
|
||||
}
|
||||
|
||||
InsettableFrameLayout.dispatchInsets(this, insets);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a padding in case a scrim is shown on the bottom of the view and a padding is needed.
|
||||
*/
|
||||
protected int getNavBarScrimHeight(WindowInsets insets) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) {
|
||||
if (Utilities.ATLEAST_Q) {
|
||||
mNavBarScrimHeight = insets.getTappableElementInsets().bottom;
|
||||
} else {
|
||||
mNavBarScrimHeight = insets.getStableInsetBottom();
|
||||
}
|
||||
mNavBarScrimHeight = getNavBarScrimHeight(insets);
|
||||
applyAdapterPaddings(mActivityContext.getDeviceProfile());
|
||||
return super.dispatchApplyWindowInsets(insets);
|
||||
}
|
||||
|
||||
@@ -483,6 +483,15 @@ public abstract class BaseAllAppsContainerView<T extends Context & ActivityConte
|
||||
mAllAppsStore.registerIconContainer(mAH.get(AdapterHolder.WORK).mRecyclerView);
|
||||
}
|
||||
|
||||
private void applyAdapterPaddings(DeviceProfile grid) {
|
||||
int bottomPadding = Math.max(mInsets.bottom, mNavBarScrimHeight);
|
||||
for (int i = 0; i < mAH.size(); i++) {
|
||||
mAH.get(i).mPadding.bottom = bottomPadding;
|
||||
mAH.get(i).mPadding.left = mAH.get(i).mPadding.right = grid.allAppsLeftRightPadding;
|
||||
mAH.get(i).applyPadding();
|
||||
}
|
||||
}
|
||||
|
||||
private void setDeviceManagementResources() {
|
||||
if (mActivityContext.getStringCache() != null) {
|
||||
Button personalTab = findViewById(R.id.tab_personal);
|
||||
|
||||
@@ -18,9 +18,11 @@ package com.android.launcher3.allapps;
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.WindowInsets;
|
||||
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherState;
|
||||
import com.android.launcher3.Utilities;
|
||||
|
||||
/**
|
||||
* AllAppsContainerView with launcher specific callbacks
|
||||
@@ -58,4 +60,13 @@ public class LauncherAllAppsContainerView extends ActivityAllAppsContainerView<L
|
||||
}
|
||||
return super.onTouchEvent(ev);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getNavBarScrimHeight(WindowInsets insets) {
|
||||
if (Utilities.ATLEAST_Q) {
|
||||
return insets.getTappableElementInsets().bottom;
|
||||
} else {
|
||||
return insets.getStableInsetBottom();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,6 +246,10 @@ public final class FeatureFlags {
|
||||
"ENABLE_ALL_APPS_IN_TASKBAR", true,
|
||||
"Enables accessing All Apps from the system Taskbar.");
|
||||
|
||||
public static final BooleanFlag ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT = getDebugFlag(
|
||||
"ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT", false,
|
||||
"Enables displaying the all apps button in the hotseat.");
|
||||
|
||||
public static final BooleanFlag ENABLE_ALL_APPS_ONE_SEARCH_IN_TASKBAR = getDebugFlag(
|
||||
"ENABLE_ALL_APPS_ONE_SEARCH_IN_TASKBAR", false,
|
||||
"Enables One Search box in Taskbar All Apps.");
|
||||
|
||||
@@ -72,7 +72,6 @@ import com.android.launcher3.ExtendedEditText;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherSettings;
|
||||
import com.android.launcher3.OnAlarmListener;
|
||||
import com.android.launcher3.PagedView;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.ShortcutAndWidgetContainer;
|
||||
import com.android.launcher3.Utilities;
|
||||
@@ -145,7 +144,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
||||
* Time for which the scroll hint is shown before automatically changing page.
|
||||
*/
|
||||
public static final int SCROLL_HINT_DURATION = 500;
|
||||
public static final int RESCROLL_DELAY = PagedView.PAGE_SNAP_ANIMATION_DURATION + 150;
|
||||
private static final int RESCROLL_EXTRA_DELAY = 150;
|
||||
|
||||
public static final int SCROLL_NONE = -1;
|
||||
public static final int SCROLL_LEFT = 0;
|
||||
@@ -1523,7 +1522,9 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
||||
|
||||
// Pause drag event until the scrolling is finished
|
||||
mScrollPauseAlarm.setOnAlarmListener(new OnScrollFinishedListener(mDragObject));
|
||||
mScrollPauseAlarm.setAlarm(RESCROLL_DELAY);
|
||||
int rescrollDelay = getResources().getInteger(
|
||||
R.integer.config_pageSnapAnimationDuration) + RESCROLL_EXTRA_DELAY;
|
||||
mScrollPauseAlarm.setAlarm(rescrollDelay);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ import com.android.launcher3.util.RunnableList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
@@ -203,7 +204,9 @@ public abstract class BaseLoaderResults {
|
||||
}
|
||||
|
||||
private void bind() {
|
||||
IntSet currentScreenIds = mCallbacks.getPagesToBindSynchronously(mOrderedScreenIds);
|
||||
final IntSet currentScreenIds =
|
||||
mCallbacks.getPagesToBindSynchronously(mOrderedScreenIds);
|
||||
Objects.requireNonNull(currentScreenIds, "Null screen ids provided by " + mCallbacks);
|
||||
|
||||
// Separate the items that are on the current screen, and all the other remaining items
|
||||
ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<>();
|
||||
|
||||
@@ -31,6 +31,7 @@ import android.text.TextUtils;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.LauncherSettings;
|
||||
@@ -469,6 +470,7 @@ public class BgDataModel {
|
||||
* or an empty IntSet
|
||||
* @param orderedScreenIds All the page ids to be bound
|
||||
*/
|
||||
@NonNull
|
||||
default IntSet getPagesToBindSynchronously(IntArray orderedScreenIds) {
|
||||
return new IntSet();
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ public class ModelUtils {
|
||||
* specified screen.
|
||||
*/
|
||||
public static <T extends ItemInfo> void filterCurrentWorkspaceItems(
|
||||
IntSet currentScreenIds,
|
||||
final IntSet currentScreenIds,
|
||||
ArrayList<T> allWorkspaceItems,
|
||||
ArrayList<T> currentScreenItems,
|
||||
ArrayList<T> otherScreenItems) {
|
||||
|
||||
@@ -120,23 +120,20 @@ public class SecondaryDragLayer extends BaseDragLayer<SecondaryDisplayLauncher>
|
||||
|
||||
int maxWidth =
|
||||
grid.allAppsCellWidthPx * grid.numShownAllAppsColumns + horizontalPadding;
|
||||
int appsWidth = Math.min(width, maxWidth);
|
||||
int appsWidth = Math.min(width - getPaddingLeft() - getPaddingRight(), maxWidth);
|
||||
|
||||
int maxHeight =
|
||||
grid.allAppsCellHeightPx * grid.numShownAllAppsColumns + verticalPadding;
|
||||
int appsHeight = Math.min(height, maxHeight);
|
||||
int appsHeight = Math.min(height - getPaddingTop() - getPaddingBottom(), maxHeight);
|
||||
|
||||
mAppsView.measure(
|
||||
makeMeasureSpec(appsWidth, EXACTLY), makeMeasureSpec(appsHeight, EXACTLY));
|
||||
|
||||
} else if (child == mAllAppsButton) {
|
||||
int appsButtonSpec = makeMeasureSpec(grid.iconSizePx, EXACTLY);
|
||||
mAllAppsButton.measure(appsButtonSpec, appsButtonSpec);
|
||||
|
||||
} else if (child == mWorkspace) {
|
||||
measureChildWithMargins(mWorkspace, widthMeasureSpec, 0, heightMeasureSpec,
|
||||
grid.iconSizePx + grid.edgeMarginPx);
|
||||
|
||||
} else {
|
||||
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ public class RotationHelper implements OnSharedPreferenceChangeListener,
|
||||
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) {
|
||||
if (mDestroyed) return;
|
||||
if (mDestroyed || mIgnoreAutoRotateSettings) return;
|
||||
boolean wasRotationEnabled = mHomeRotationEnabled;
|
||||
mHomeRotationEnabled = mSharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY,
|
||||
getAllowRotationDefaultValue(mActivity.getDeviceProfile()));
|
||||
|
||||
@@ -18,7 +18,6 @@ package com.android.launcher3.widget;
|
||||
|
||||
import static com.android.launcher3.Utilities.ATLEAST_R;
|
||||
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
|
||||
import static com.android.launcher3.widget.BaseWidgetSheet.MAX_WIDTH_SCALE_FOR_LARGER_SCREEN;
|
||||
|
||||
import android.animation.PropertyValuesHolder;
|
||||
import android.annotation.SuppressLint;
|
||||
@@ -106,7 +105,10 @@ public class AddItemWidgetsBottomSheet extends AbstractSlideInView<AddItemActivi
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
DeviceProfile deviceProfile = mActivityContext.getDeviceProfile();
|
||||
int widthUsed;
|
||||
if (mInsets.bottom > 0) {
|
||||
if (deviceProfile.isTablet) {
|
||||
int margin = deviceProfile.allAppsLeftRightMargin;
|
||||
widthUsed = Math.max(2 * margin, 2 * (mInsets.left + mInsets.right));
|
||||
} else if (mInsets.bottom > 0) {
|
||||
widthUsed = mInsets.left + mInsets.right;
|
||||
} else {
|
||||
Rect padding = deviceProfile.workspacePadding;
|
||||
@@ -114,18 +116,8 @@ public class AddItemWidgetsBottomSheet extends AbstractSlideInView<AddItemActivi
|
||||
2 * (mInsets.left + mInsets.right));
|
||||
}
|
||||
|
||||
if (deviceProfile.isTablet || deviceProfile.isTwoPanels) {
|
||||
// In large screen devices, we restrict the width of the widgets picker to show part of
|
||||
// the home screen. Let's ensure the minimum width used is at least the minimum width
|
||||
// that isn't taken by the widgets picker.
|
||||
int minUsedWidth = (int) (deviceProfile.availableWidthPx
|
||||
* (1 - MAX_WIDTH_SCALE_FOR_LARGER_SCREEN));
|
||||
widthUsed = Math.max(widthUsed, minUsedWidth);
|
||||
}
|
||||
|
||||
int heightUsed = mInsets.top + deviceProfile.edgeMarginPx;
|
||||
measureChildWithMargins(mContent, widthMeasureSpec,
|
||||
widthUsed, heightMeasureSpec, heightUsed);
|
||||
widthUsed, heightMeasureSpec, deviceProfile.bottomSheetTopPadding);
|
||||
setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),
|
||||
MeasureSpec.getSize(heightMeasureSpec));
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
package com.android.launcher3.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.util.AttributeSet;
|
||||
@@ -23,6 +25,7 @@ import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.View.OnLongClickListener;
|
||||
import android.view.WindowInsets;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.GuardedBy;
|
||||
@@ -43,6 +46,7 @@ import com.android.launcher3.testing.TestProtocol;
|
||||
import com.android.launcher3.touch.ItemLongClickListener;
|
||||
import com.android.launcher3.util.SystemUiController;
|
||||
import com.android.launcher3.util.Themes;
|
||||
import com.android.launcher3.util.window.WindowManagerProxy;
|
||||
import com.android.launcher3.views.AbstractSlideInView;
|
||||
import com.android.launcher3.views.ActivityContext;
|
||||
import com.android.launcher3.views.ArrowTipView;
|
||||
@@ -55,11 +59,6 @@ public abstract class BaseWidgetSheet extends AbstractSlideInView<Launcher>
|
||||
PopupDataProvider.PopupDataChangeListener, Insettable {
|
||||
/** The default number of cells that can fit horizontally in a widget sheet. */
|
||||
protected static final int DEFAULT_MAX_HORIZONTAL_SPANS = 4;
|
||||
/**
|
||||
* The maximum scale, [0, 1], of the device screen width that the widgets picker can consume
|
||||
* on large screen devices.
|
||||
*/
|
||||
protected static final float MAX_WIDTH_SCALE_FOR_LARGER_SCREEN = 0.89f;
|
||||
|
||||
protected static final String KEY_WIDGETS_EDUCATION_TIP_SEEN =
|
||||
"launcher.widgets_education_tip_seen";
|
||||
@@ -70,10 +69,15 @@ public abstract class BaseWidgetSheet extends AbstractSlideInView<Launcher>
|
||||
|
||||
private int mContentHorizontalMarginInPx;
|
||||
|
||||
protected int mNavBarScrimHeight;
|
||||
private final Paint mNavBarScrimPaint;
|
||||
|
||||
public BaseWidgetSheet(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
mContentHorizontalMarginInPx = getResources().getDimensionPixelSize(
|
||||
R.dimen.widget_list_horizontal_margin);
|
||||
mNavBarScrimPaint = new Paint();
|
||||
mNavBarScrimPaint.setColor(Themes.getAttrColor(context, R.attr.allAppsNavBarScrimColor));
|
||||
}
|
||||
|
||||
protected int getScrimColor(Context context) {
|
||||
@@ -83,6 +87,9 @@ public abstract class BaseWidgetSheet extends AbstractSlideInView<Launcher>
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
WindowInsets windowInsets = WindowManagerProxy.INSTANCE.get(getContext())
|
||||
.normalizeWindowInsets(getContext(), getRootWindowInsets(), new Rect());
|
||||
mNavBarScrimHeight = getNavBarScrimHeight(windowInsets);
|
||||
mActivityContext.getPopupDataProvider().setChangeListener(this);
|
||||
}
|
||||
|
||||
@@ -136,6 +143,30 @@ public abstract class BaseWidgetSheet extends AbstractSlideInView<Launcher>
|
||||
}
|
||||
}
|
||||
|
||||
private int getNavBarScrimHeight(WindowInsets insets) {
|
||||
if (Utilities.ATLEAST_Q) {
|
||||
return insets.getTappableElementInsets().bottom;
|
||||
} else {
|
||||
return insets.getStableInsetBottom();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
|
||||
mNavBarScrimHeight = getNavBarScrimHeight(insets);
|
||||
return super.onApplyWindowInsets(insets);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dispatchDraw(Canvas canvas) {
|
||||
super.dispatchDraw(canvas);
|
||||
|
||||
if (mNavBarScrimHeight > 0) {
|
||||
canvas.drawRect(0, getHeight() - mNavBarScrimHeight, getWidth(), getHeight(),
|
||||
mNavBarScrimPaint);
|
||||
}
|
||||
}
|
||||
|
||||
/** Called when the horizontal margin of the content view has changed. */
|
||||
protected abstract void onContentHorizontalMarginChanged(int contentHorizontalMarginInPx);
|
||||
|
||||
@@ -147,7 +178,10 @@ public abstract class BaseWidgetSheet extends AbstractSlideInView<Launcher>
|
||||
protected void doMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
DeviceProfile deviceProfile = mActivityContext.getDeviceProfile();
|
||||
int widthUsed;
|
||||
if (mInsets.bottom > 0) {
|
||||
if (deviceProfile.isTablet) {
|
||||
int margin = deviceProfile.allAppsLeftRightMargin;
|
||||
widthUsed = Math.max(2 * margin, 2 * (mInsets.left + mInsets.right));
|
||||
} else if (mInsets.bottom > 0) {
|
||||
widthUsed = mInsets.left + mInsets.right;
|
||||
} else {
|
||||
Rect padding = deviceProfile.workspacePadding;
|
||||
@@ -155,15 +189,6 @@ public abstract class BaseWidgetSheet extends AbstractSlideInView<Launcher>
|
||||
2 * (mInsets.left + mInsets.right));
|
||||
}
|
||||
|
||||
if (deviceProfile.isTablet || deviceProfile.isTwoPanels) {
|
||||
// In large screen devices, we restrict the width of the widgets picker to show part of
|
||||
// the home screen. Let's ensure the minimum width used is at least the minimum width
|
||||
// that isn't taken by the widgets picker.
|
||||
int minUsedWidth = (int) (deviceProfile.availableWidthPx
|
||||
* (1 - MAX_WIDTH_SCALE_FOR_LARGER_SCREEN));
|
||||
widthUsed = Math.max(widthUsed, minUsedWidth);
|
||||
}
|
||||
|
||||
measureChildWithMargins(mContent, widthMeasureSpec,
|
||||
widthUsed, heightMeasureSpec, deviceProfile.bottomSheetTopPadding);
|
||||
setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),
|
||||
|
||||
@@ -247,10 +247,12 @@ public class WidgetsBottomSheet extends BaseWidgetSheet {
|
||||
@Override
|
||||
public void setInsets(Rect insets) {
|
||||
super.setInsets(insets);
|
||||
int bottomPadding = Math.max(insets.bottom, mNavBarScrimHeight);
|
||||
|
||||
mContent.setPadding(mContent.getPaddingStart(),
|
||||
mContent.getPaddingTop(), mContent.getPaddingEnd(), insets.bottom);
|
||||
if (insets.bottom > 0) {
|
||||
mContent.getPaddingTop(), mContent.getPaddingEnd(),
|
||||
bottomPadding);
|
||||
if (bottomPadding > 0) {
|
||||
setupNavBarColor();
|
||||
} else {
|
||||
clearNavBarColor();
|
||||
|
||||
@@ -328,15 +328,15 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
||||
@Override
|
||||
public void setInsets(Rect insets) {
|
||||
super.setInsets(insets);
|
||||
|
||||
setBottomPadding(mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView, insets.bottom);
|
||||
setBottomPadding(mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView, insets.bottom);
|
||||
int bottomPadding = Math.max(insets.bottom, mNavBarScrimHeight);
|
||||
setBottomPadding(mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView, bottomPadding);
|
||||
setBottomPadding(mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView, bottomPadding);
|
||||
if (mHasWorkProfile) {
|
||||
setBottomPadding(mAdapters.get(AdapterHolder.WORK).mWidgetsRecyclerView, insets.bottom);
|
||||
setBottomPadding(mAdapters.get(AdapterHolder.WORK).mWidgetsRecyclerView, bottomPadding);
|
||||
}
|
||||
((MarginLayoutParams) mNoWidgetsView.getLayoutParams()).bottomMargin = insets.bottom;
|
||||
((MarginLayoutParams) mNoWidgetsView.getLayoutParams()).bottomMargin = bottomPadding;
|
||||
|
||||
if (insets.bottom > 0) {
|
||||
if (bottomPadding > 0) {
|
||||
setupNavBarColor();
|
||||
} else {
|
||||
clearNavBarColor();
|
||||
|
||||
@@ -48,11 +48,13 @@ import com.android.launcher3.tapl.HomeAppIconMenuItem;
|
||||
import com.android.launcher3.tapl.Widgets;
|
||||
import com.android.launcher3.tapl.Workspace;
|
||||
import com.android.launcher3.util.TestUtil;
|
||||
import com.android.launcher3.util.rule.ScreenRecordRule;
|
||||
import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
|
||||
import com.android.launcher3.widget.picker.WidgetsFullSheet;
|
||||
import com.android.launcher3.widget.picker.WidgetsRecyclerView;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@@ -66,6 +68,10 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
|
||||
private static final String DUMMY_APP_NAME = "Aardwolf";
|
||||
private static final String MAPS_APP_NAME = "Maps";
|
||||
private static final String STORE_APP_NAME = "Play Store";
|
||||
private static final String GMAIL_APP_NAME = "Gmail";
|
||||
|
||||
@Rule
|
||||
public ScreenRecordRule mScreenRecordRule = new ScreenRecordRule();
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
@@ -374,28 +380,23 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
|
||||
|
||||
@Test
|
||||
@PortraitLandscape
|
||||
public void testDragToFolder() throws Exception {
|
||||
final HomeAppIcon playStoreIcon = createShortcutIfNotExist("Play Store", 0, 1);
|
||||
final HomeAppIcon gmailIcon = createShortcutIfNotExist("Gmail", 1, 1);
|
||||
@ScreenRecord
|
||||
public void testDragToFolder() {
|
||||
// TODO: add the use case to drag an icon to an existing folder. Currently it either fails
|
||||
// on tablets or phones due to difference in resolution.
|
||||
final HomeAppIcon playStoreIcon = createShortcutIfNotExist(STORE_APP_NAME, 0, 1);
|
||||
final HomeAppIcon gmailIcon = createShortcutInCenterIfNotExist(GMAIL_APP_NAME);
|
||||
|
||||
FolderIcon folderIcon = gmailIcon.dragToIcon(playStoreIcon);
|
||||
|
||||
Folder folder = folderIcon.open();
|
||||
folder.getAppIcon("Play Store");
|
||||
folder.getAppIcon("Gmail");
|
||||
folder.getAppIcon(STORE_APP_NAME);
|
||||
folder.getAppIcon(GMAIL_APP_NAME);
|
||||
Workspace workspace = folder.close();
|
||||
|
||||
assertNull("Gmail should be moved to a folder.",
|
||||
workspace.tryGetWorkspaceAppIcon("Gmail"));
|
||||
assertNull("Play Store should be moved to a folder.",
|
||||
workspace.tryGetWorkspaceAppIcon("Play Store"));
|
||||
|
||||
final HomeAppIcon youTubeIcon = createShortcutInCenterIfNotExist("YouTube");
|
||||
|
||||
folderIcon = youTubeIcon.dragToIcon(folderIcon);
|
||||
folder = folderIcon.open();
|
||||
folder.getAppIcon("YouTube");
|
||||
folder.close();
|
||||
assertNull(STORE_APP_NAME + " should be moved to a folder.",
|
||||
workspace.tryGetWorkspaceAppIcon(STORE_APP_NAME));
|
||||
assertNull(GMAIL_APP_NAME + " should be moved to a folder.",
|
||||
workspace.tryGetWorkspaceAppIcon(GMAIL_APP_NAME));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -105,7 +105,6 @@ public abstract class Launchable {
|
||||
expectLongClickEvents);
|
||||
}
|
||||
|
||||
|
||||
return dragStartCenter;
|
||||
}
|
||||
|
||||
|
||||
@@ -1552,11 +1552,11 @@ public final class LauncherInstrumentation {
|
||||
|
||||
// vx0: initial speed at the x-dimension, set as twice the avg speed
|
||||
// dx: the constant deceleration at the x-dimension
|
||||
double vx0 = 2 * (to.x - from.x) / duration;
|
||||
double vx0 = 2.0 * (to.x - from.x) / duration;
|
||||
double dx = vx0 / duration;
|
||||
// vy0: initial speed at the y-dimension, set as twice the avg speed
|
||||
// dy: the constant deceleration at the y-dimension
|
||||
double vy0 = 2 * (to.y - from.y) / duration;
|
||||
double vy0 = 2.0 * (to.y - from.y) / duration;
|
||||
double dy = vy0 / duration;
|
||||
|
||||
for (long i = 0; i < steps; ++i) {
|
||||
|
||||
Reference in New Issue
Block a user