Revert^2 "Merging from ub-launcher3-master @ build 6294827"
70a76a61bf95741f76b17fc78a8d784ae57dd481
Bug: 151611270
Change-Id: I16e0afcda998d2b1293ff47a66d5562fa4da77dc
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 217a41c..814b728 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -40,7 +40,6 @@
import com.android.launcher3.logging.StatsLogUtils;
import com.android.launcher3.logging.StatsLogUtils.LogStateProvider;
import com.android.launcher3.logging.UserEventDispatcher;
-import com.android.launcher3.logging.UserEventDispatcher.UserEventDelegate;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.userevent.nano.LauncherLogProto;
@@ -52,8 +51,10 @@
import java.lang.annotation.Retention;
import java.util.ArrayList;
-public abstract class BaseActivity extends Activity
- implements UserEventDelegate, LogStateProvider, ActivityContext {
+/**
+ * Launcher BaseActivity
+ */
+public abstract class BaseActivity extends Activity implements LogStateProvider, ActivityContext {
private static final String TAG = "BaseActivity";
@@ -155,7 +156,7 @@
public final UserEventDispatcher getUserEventDispatcher() {
if (mUserEventDispatcher == null) {
- mUserEventDispatcher = UserEventDispatcher.newInstance(this, this);
+ mUserEventDispatcher = UserEventDispatcher.newInstance(this);
}
return mUserEventDispatcher;
}
diff --git a/src/com/android/launcher3/BaseRecyclerView.java b/src/com/android/launcher3/BaseRecyclerView.java
index 864fa6e..38e1201 100644
--- a/src/com/android/launcher3/BaseRecyclerView.java
+++ b/src/com/android/launcher3/BaseRecyclerView.java
@@ -22,11 +22,11 @@
import android.view.View;
import android.view.ViewGroup;
+import androidx.recyclerview.widget.RecyclerView;
+
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.views.RecyclerViewFastScroller;
-import androidx.recyclerview.widget.RecyclerView;
-
/**
* A base {@link RecyclerView}, which does the following:
@@ -138,7 +138,7 @@
if (getCurrentScrollY() == 0) {
return true;
}
- return false;
+ return getAdapter() == null || getAdapter().getItemCount() == 0;
}
/**
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index b89e727..76cfe1c 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -16,12 +16,13 @@
package com.android.launcher3;
+import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
+
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
-import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
import android.widget.FrameLayout;
@@ -32,6 +33,8 @@
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.views.Transposable;
+import java.util.ArrayList;
+
public class Hotseat extends CellLayout implements LogContainerProvider, Insettable, Transposable {
@ViewDebug.ExportedProperty(category = "launcher")
@@ -75,10 +78,12 @@
}
@Override
- public void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent) {
- target.gridX = info.cellX;
- target.gridY = info.cellY;
- targetParent.containerType = LauncherLogProto.ContainerType.HOTSEAT;
+ public void fillInLogContainerData(ItemInfo childInfo, Target child,
+ ArrayList<Target> parents) {
+ child.rank = childInfo.rank;
+ child.gridX = childInfo.cellX;
+ child.gridY = childInfo.cellY;
+ parents.add(newContainerTarget(LauncherLogProto.ContainerType.HOTSEAT));
}
@Override
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 965b87b..1413a5c 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -30,7 +30,6 @@
import static com.android.launcher3.Utilities.postAsyncCallback;
import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_LAUNCHER_LOAD;
import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
-import static com.android.launcher3.logging.LoggerUtils.newTarget;
import static com.android.launcher3.popup.SystemShortcut.APP_INFO;
import static com.android.launcher3.popup.SystemShortcut.INSTALL;
import static com.android.launcher3.popup.SystemShortcut.WIDGETS;
@@ -78,6 +77,7 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.OvershootInterpolator;
import android.widget.Toast;
@@ -110,7 +110,6 @@
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.logging.StatsLogUtils;
import com.android.launcher3.logging.UserEventDispatcher;
-import com.android.launcher3.logging.UserEventDispatcher.UserEventDelegate;
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.model.ModelWriter;
@@ -125,8 +124,8 @@
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.AllAppsSwipeController;
import com.android.launcher3.touch.ItemClickHandler;
+import com.android.launcher3.uioverrides.BackgroundBlurController;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
@@ -183,8 +182,7 @@
* Default launcher application.
*/
public class Launcher extends BaseDraggingActivity implements LauncherExterns,
- Callbacks, UserEventDelegate,
- InvariantDeviceProfile.OnIDPChangeListener, PluginListener<OverlayPlugin> {
+ Callbacks, InvariantDeviceProfile.OnIDPChangeListener, PluginListener<OverlayPlugin> {
public static final String TAG = "Launcher";
public static final ActivityTracker<Launcher> ACTIVITY_TRACKER = new ActivityTracker<>();
@@ -328,6 +326,19 @@
private boolean mDeferOverlayCallbacks;
private final Runnable mDeferredOverlayCallbacks = this::checkIfOverlayStillDeferred;
+ private BackgroundBlurController mBackgroundBlurController =
+ new BackgroundBlurController(this);
+
+ private final ViewTreeObserver.OnDrawListener mOnDrawListener =
+ new ViewTreeObserver.OnDrawListener() {
+ @Override
+ public void onDraw() {
+ getBackgroundBlurController().setSurfaceToLauncher(mDragLayer);
+ mDragLayer.post(() -> mDragLayer.getViewTreeObserver().removeOnDrawListener(
+ this));
+ }
+ };
+
@Override
protected void onCreate(Bundle savedInstanceState) {
Object traceToken = TraceHelper.INSTANCE.beginSection(ON_CREATE_EVT,
@@ -929,6 +940,8 @@
final int origDragLayerChildCount = mDragLayer.getChildCount();
super.onStop();
+ mDragLayer.getViewTreeObserver().removeOnDrawListener(mOnDrawListener);
+
if (mDeferOverlayCallbacks) {
checkIfOverlayStillDeferred();
} else {
@@ -941,6 +954,7 @@
NotificationListener.removeNotificationsChangedListener();
getStateManager().moveToRestState();
+ getBackgroundBlurController().setSurfaceToLauncher(null);
// Workaround for b/78520668, explicitly trim memory once UI is hidden
onTrimMemory(TRIM_MEMORY_UI_HIDDEN);
@@ -968,6 +982,7 @@
if (!mDeferOverlayCallbacks) {
mOverlayManager.onActivityStarted(this);
}
+ mDragLayer.getViewTreeObserver().addOnDrawListener(mOnDrawListener);
mAppWidgetHost.setListenIfResumed(true);
TraceHelper.INSTANCE.endSection(traceToken);
@@ -1890,24 +1905,6 @@
}
@Override
- public void modifyUserEvent(LauncherLogProto.LauncherEvent event) {
- if (event.srcTarget != null && event.srcTarget.length > 0 &&
- event.srcTarget[1].containerType == ContainerType.PREDICTION) {
- Target[] targets = new Target[3];
- targets[0] = event.srcTarget[0];
- targets[1] = event.srcTarget[1];
- targets[2] = newTarget(Target.Type.CONTAINER);
- event.srcTarget = targets;
- LauncherState state = mStateManager.getState();
- if (state == LauncherState.ALL_APPS) {
- event.srcTarget[2].containerType = ContainerType.ALLAPPS;
- } else if (state == OVERVIEW) {
- event.srcTarget[2].containerType = ContainerType.TASKSWITCHER;
- }
- }
- }
-
- @Override
public boolean startActivitySafely(View v, Intent intent, ItemInfo item,
@Nullable String sourceContainer) {
if (!hasBeenResumed()) {
@@ -2710,7 +2707,8 @@
}
protected StateHandler[] createStateHandlers() {
- return new StateHandler[] { getAllAppsController(), getWorkspace() };
+ return new StateHandler[] { getAllAppsController(), getWorkspace(),
+ getBackgroundBlurController() };
}
public TouchController[] createTouchControllers() {
@@ -2747,8 +2745,12 @@
return Stream.of(APP_INFO, WIDGETS, INSTALL);
}
+ public BackgroundBlurController getBackgroundBlurController() {
+ return mBackgroundBlurController;
+ }
+
public static Launcher getLauncher(Context context) {
- return (Launcher) fromContext(context);
+ return fromContext(context);
}
/**
diff --git a/src/com/android/launcher3/LauncherAnimUtils.java b/src/com/android/launcher3/LauncherAnimUtils.java
index 32e9c14..d9cf7f1 100644
--- a/src/com/android/launcher3/LauncherAnimUtils.java
+++ b/src/com/android/launcher3/LauncherAnimUtils.java
@@ -18,7 +18,7 @@
import android.graphics.drawable.Drawable;
import android.util.FloatProperty;
-import android.util.Property;
+import android.util.IntProperty;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
@@ -32,15 +32,15 @@
// The progress of an animation to all apps must be at least this far along to snap to all apps.
public static final float MIN_PROGRESS_TO_ALL_APPS = 0.5f;
- public static final Property<Drawable, Integer> DRAWABLE_ALPHA =
- new Property<Drawable, Integer>(Integer.TYPE, "drawableAlpha") {
+ public static final IntProperty<Drawable> DRAWABLE_ALPHA =
+ new IntProperty<Drawable>("drawableAlpha") {
@Override
public Integer get(Drawable drawable) {
return drawable.getAlpha();
}
@Override
- public void set(Drawable drawable, Integer alpha) {
+ public void setValue(Drawable drawable, int alpha) {
drawable.setAlpha(alpha);
}
};
@@ -64,28 +64,28 @@
return (int) Utilities.boundToRange(Math.abs(velocity) / 2, 2f, 6f);
}
- public static final Property<LayoutParams, Integer> LAYOUT_WIDTH =
- new Property<LayoutParams, Integer>(Integer.TYPE, "width") {
+ public static final IntProperty<LayoutParams> LAYOUT_WIDTH =
+ new IntProperty<LayoutParams>("width") {
@Override
public Integer get(LayoutParams lp) {
return lp.width;
}
@Override
- public void set(LayoutParams lp, Integer width) {
+ public void setValue(LayoutParams lp, int width) {
lp.width = width;
}
};
- public static final Property<LayoutParams, Integer> LAYOUT_HEIGHT =
- new Property<LayoutParams, Integer>(Integer.TYPE, "height") {
+ public static final IntProperty<LayoutParams> LAYOUT_HEIGHT =
+ new IntProperty<LayoutParams>("height") {
@Override
public Integer get(LayoutParams lp) {
return lp.height;
}
@Override
- public void set(LayoutParams lp, Integer height) {
+ public void setValue(LayoutParams lp, int height) {
lp.height = height;
}
};
@@ -117,4 +117,18 @@
return view.getTranslationY();
}
};
+
+ public static final FloatProperty<View> VIEW_ALPHA =
+ View.ALPHA instanceof FloatProperty ? (FloatProperty) View.ALPHA
+ : new FloatProperty<View>("alpha") {
+ @Override
+ public void setValue(View view, float v) {
+ view.setAlpha(v);
+ }
+
+ @Override
+ public Float get(View view) {
+ return view.getAlpha();
+ }
+ };
}
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 36440c9..62b8927 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -41,6 +41,7 @@
import static com.android.launcher3.testing.TestProtocol.QUICK_SWITCH_STATE_ORDINAL;
import static com.android.launcher3.testing.TestProtocol.SPRING_LOADED_STATE_ORDINAL;
+import android.content.Context;
import android.view.View;
import android.view.animation.Interpolator;
@@ -269,6 +270,14 @@
return 0;
}
+ /**
+ * The amount of blur to apply to the background of either the app or Launcher surface in this
+ * state.
+ */
+ public int getBackgroundBlurRadius(Context context) {
+ return 0;
+ }
+
public String getDescription(Launcher launcher) {
return launcher.getWorkspace().getCurrentPageDescription();
}
diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java
index 9f25729..04134f2 100644
--- a/src/com/android/launcher3/LauncherStateManager.java
+++ b/src/com/android/launcher3/LauncherStateManager.java
@@ -88,20 +88,22 @@
// components may be run atomically - that is, all at once, instead of user-controlled. However,
// atomic components are not restricted to this purpose; they can be user-controlled alongside
// non atomic components as well. Note that each gesture model has exactly one atomic component,
- // ATOMIC_OVERVIEW_SCALE_COMPONENT *or* ATOMIC_OVERVIEW_PEEK_COMPONENT.
+ // PLAY_ATOMIC_OVERVIEW_SCALE *or* PLAY_ATOMIC_OVERVIEW_PEEK.
@IntDef(flag = true, value = {
- NON_ATOMIC_COMPONENT,
- ATOMIC_OVERVIEW_SCALE_COMPONENT,
- ATOMIC_OVERVIEW_PEEK_COMPONENT,
+ PLAY_NON_ATOMIC,
+ PLAY_ATOMIC_OVERVIEW_SCALE,
+ PLAY_ATOMIC_OVERVIEW_PEEK,
+ SKIP_OVERVIEW,
})
@Retention(RetentionPolicy.SOURCE)
- public @interface AnimationComponents {}
- public static final int NON_ATOMIC_COMPONENT = 1 << 0;
- public static final int ATOMIC_OVERVIEW_SCALE_COMPONENT = 1 << 1;
- public static final int ATOMIC_OVERVIEW_PEEK_COMPONENT = 1 << 2;
+ public @interface AnimationFlags {}
+ public static final int PLAY_NON_ATOMIC = 1 << 0;
+ public static final int PLAY_ATOMIC_OVERVIEW_SCALE = 1 << 1;
+ public static final int PLAY_ATOMIC_OVERVIEW_PEEK = 1 << 2;
+ public static final int SKIP_OVERVIEW = 1 << 3;
- public static final int ANIM_ALL = NON_ATOMIC_COMPONENT | ATOMIC_OVERVIEW_SCALE_COMPONENT
- | ATOMIC_OVERVIEW_PEEK_COMPONENT;
+ public static final int ANIM_ALL_COMPONENTS = PLAY_NON_ATOMIC | PLAY_ATOMIC_OVERVIEW_SCALE
+ | PLAY_ATOMIC_OVERVIEW_PEEK;
private final AnimationConfig mConfig = new AnimationConfig();
private final Handler mUiHandler;
@@ -244,12 +246,8 @@
} else if (!mConfig.userControlled && animated && mConfig.mTargetState == state) {
// We are running the same animation as requested
if (onCompleteRunnable != null) {
- mConfig.mCurrentAnimation.addListener(new AnimationSuccessListener() {
- @Override
- public void onAnimationSuccess(Animator animator) {
- onCompleteRunnable.run();
- }
- });
+ mConfig.mCurrentAnimation.addListener(
+ AnimationSuccessListener.forRunnable(onCompleteRunnable));
}
return;
}
@@ -315,10 +313,10 @@
}
public AnimatorSet createAtomicAnimation(LauncherState fromState, LauncherState toState,
- AnimatorSetBuilder builder, @AnimationComponents int atomicComponent, long duration) {
+ AnimatorSetBuilder builder, @AnimationFlags int animFlags, long duration) {
prepareForAtomicAnimation(fromState, toState, builder);
AnimationConfig config = new AnimationConfig();
- config.animComponents = atomicComponent;
+ config.mAnimFlags = animFlags;
config.duration = duration;
for (StateHandler handler : mLauncher.getStateManager().getStateHandlers()) {
handler.setStateWithAnimation(toState, builder, config);
@@ -359,25 +357,25 @@
*/
public AnimatorPlaybackController createAnimationToNewWorkspace(
LauncherState state, long duration) {
- return createAnimationToNewWorkspace(state, duration, LauncherStateManager.ANIM_ALL);
+ return createAnimationToNewWorkspace(state, duration, ANIM_ALL_COMPONENTS);
}
public AnimatorPlaybackController createAnimationToNewWorkspace(
- LauncherState state, long duration, @AnimationComponents int animComponents) {
+ LauncherState state, long duration, @AnimationFlags int animComponents) {
return createAnimationToNewWorkspace(state, new AnimatorSetBuilder(), duration, null,
animComponents);
}
public AnimatorPlaybackController createAnimationToNewWorkspace(LauncherState state,
AnimatorSetBuilder builder, long duration, Runnable onCancelRunnable,
- @AnimationComponents int animComponents) {
+ @AnimationFlags int animComponents) {
mConfig.reset();
mConfig.userControlled = true;
- mConfig.animComponents = animComponents;
+ mConfig.mAnimFlags = animComponents;
mConfig.duration = duration;
mConfig.playbackController = AnimatorPlaybackController.wrap(
- createAnimationToNewWorkspaceInternal(state, builder, null), duration,
- onCancelRunnable);
+ createAnimationToNewWorkspaceInternal(state, builder, null), duration)
+ .setOnCancelRunnable(onCancelRunnable);
return mConfig.playbackController;
}
@@ -587,7 +585,7 @@
public long duration;
public boolean userControlled;
public AnimatorPlaybackController playbackController;
- public @AnimationComponents int animComponents = ANIM_ALL;
+ private @AnimationFlags int mAnimFlags = ANIM_ALL_COMPONENTS;
private PropertySetter mPropertySetter;
private AnimatorSet mCurrentAnimation;
@@ -601,7 +599,7 @@
public void reset() {
duration = 0;
userControlled = false;
- animComponents = ANIM_ALL;
+ mAnimFlags = ANIM_ALL_COMPONENTS;
mPropertySetter = null;
mTargetState = null;
@@ -642,16 +640,39 @@
mCurrentAnimation.addListener(this);
}
+ /**
+ * @return Whether Overview is scaling as part of this animation. If this is the only
+ * component (i.e. NON_ATOMIC_COMPONENT isn't included), then this scaling is happening
+ * atomically, rather than being part of a normal state animation. StateHandlers can use
+ * this to designate part of their animation that should scale with Overview.
+ */
public boolean playAtomicOverviewScaleComponent() {
- return (animComponents & ATOMIC_OVERVIEW_SCALE_COMPONENT) != 0;
+ return hasAnimationFlag(PLAY_ATOMIC_OVERVIEW_SCALE);
}
- public boolean playAtomicOverviewPeekComponent() {
- return (animComponents & ATOMIC_OVERVIEW_PEEK_COMPONENT) != 0;
+ /**
+ * @return Whether this animation will play atomically at the same time as a different,
+ * user-controlled state transition. StateHandlers, which contribute to both animations, can
+ * use this to avoid animating the same properties in both animations, since they'd conflict
+ * with one another.
+ */
+ public boolean onlyPlayAtomicComponent() {
+ return getAnimComponents() == PLAY_ATOMIC_OVERVIEW_SCALE
+ || getAnimComponents() == PLAY_ATOMIC_OVERVIEW_PEEK;
}
- public boolean playNonAtomicComponent() {
- return (animComponents & NON_ATOMIC_COMPONENT) != 0;
+ /**
+ * Returns true if the config and any of the provided component flags
+ */
+ public boolean hasAnimationFlag(@AnimationFlags int a) {
+ return (mAnimFlags & a) != 0;
+ }
+
+ /**
+ * @return Only the flags that determine which animation components to play.
+ */
+ public @AnimationFlags int getAnimComponents() {
+ return mAnimFlags & ANIM_ALL_COMPONENTS;
}
}
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index a6180a6..e38631d 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -42,21 +42,6 @@
import android.view.animation.Interpolator;
import android.widget.ScrollView;
-import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.compat.AccessibilityManagerCompat;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.model.PagedViewOrientedState;
-import com.android.launcher3.pageindicators.PageIndicator;
-import com.android.launcher3.states.RotationHelper;
-import com.android.launcher3.touch.PortraitPagedViewHandler;
-import com.android.launcher3.touch.OverScroll;
-import com.android.launcher3.touch.PagedOrientationHandler;
-import com.android.launcher3.touch.PagedOrientationHandler.ChildBounds;
-import com.android.launcher3.util.OverScroller;
-import com.android.launcher3.util.Thunk;
-
-import java.util.ArrayList;
-
import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled;
import static com.android.launcher3.compat.AccessibilityManagerCompat.isObservedEventType;
import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
@@ -65,6 +50,20 @@
import static com.android.launcher3.touch.PagedOrientationHandler.VIEW_SCROLL_BY;
import static com.android.launcher3.touch.PagedOrientationHandler.VIEW_SCROLL_TO;
+import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.compat.AccessibilityManagerCompat;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.model.PagedViewOrientedState;
+import com.android.launcher3.pageindicators.PageIndicator;
+import com.android.launcher3.touch.OverScroll;
+import com.android.launcher3.touch.PagedOrientationHandler;
+import com.android.launcher3.touch.PagedOrientationHandler.ChildBounds;
+import com.android.launcher3.touch.PortraitPagedViewHandler;
+import com.android.launcher3.util.OverScroller;
+import com.android.launcher3.util.Thunk;
+
+import java.util.ArrayList;
+
/**
* An abstraction of the original Workspace which supports browsing through a
* sequential list of "pages"
@@ -73,8 +72,6 @@
private static final String TAG = "PagedView";
private static final boolean DEBUG = false;
- public static boolean sFlagForcedRotation = false;
-
public static final int INVALID_PAGE = -1;
protected static final ComputePageScrollsLogic SIMPLE_SCROLL_LOGIC = (v) -> v.getVisibility() != GONE;
@@ -200,8 +197,6 @@
if (Utilities.ATLEAST_OREO) {
setDefaultFocusHighlightEnabled(false);
}
-
- sFlagForcedRotation = Utilities.isForcedRotation(context);
}
protected void setDefaultInterpolator(Interpolator interpolator) {
@@ -1515,7 +1510,7 @@
// interpolator at zero, ie. 5. We use 4 to make it a little slower.
duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
- if (QUICKSTEP_SPRINGS.get()) {
+ if (QUICKSTEP_SPRINGS.get() && mCurrentPage != whichPage) {
return snapToPage(whichPage, delta, duration, false, null,
velocity * Math.signum(delta), true);
} else {
diff --git a/src/com/android/launcher3/SecondaryDropTarget.java b/src/com/android/launcher3/SecondaryDropTarget.java
index 1841134..2430d5e 100644
--- a/src/com/android/launcher3/SecondaryDropTarget.java
+++ b/src/com/android/launcher3/SecondaryDropTarget.java
@@ -41,6 +41,7 @@
import com.android.launcher3.util.Themes;
import java.net.URISyntaxException;
+import java.util.ArrayList;
/**
* Drop target which provides a secondary option for an item.
@@ -322,9 +323,9 @@
}
@Override
- public void fillInLogContainerData(View v, ItemInfo info, Target target,
- Target targetParent) {
- mOriginal.fillInLogContainerData(v, info, target, targetParent);
+ public void fillInLogContainerData(ItemInfo childInfo, Target child,
+ ArrayList<Target> parents) {
+ mOriginal.fillInLogContainerData(childInfo, child, parents);
}
@Override
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 9780630..122b393 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -17,7 +17,6 @@
package com.android.launcher3;
import static com.android.launcher3.ItemInfoWithIcon.FLAG_ICON_BADGED;
-import static com.android.launcher3.states.RotationHelper.FIXED_ROTATION_TRANSFORM_SETTING_NAME;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
@@ -61,6 +60,7 @@
import android.view.ViewConfiguration;
import android.view.animation.Interpolator;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.graphics.TintedDrawableSpan;
@@ -128,11 +128,6 @@
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0;
}
- public static boolean isForcedRotation(Context context) {
- return Settings.Global.getInt(context.getContentResolver(),
- FIXED_ROTATION_TRANSFORM_SETTING_NAME, 0) != 0;
- }
-
// An intent extra to indicate the horizontal scroll of the wallpaper.
public static final String EXTRA_WALLPAPER_OFFSET = "com.android.launcher3.WALLPAPER_OFFSET";
public static final String EXTRA_WALLPAPER_FLAVOR = "com.android.launcher3.WALLPAPER_FLAVOR";
@@ -481,6 +476,15 @@
LauncherFiles.DEVICE_PREFERENCES_KEY, Context.MODE_PRIVATE);
}
+ /**
+ * @return {@link SharedPreferences} that backs {@link FeatureFlags}
+ */
+ public static SharedPreferences getFeatureFlagsPrefs(Context context) {
+ // Use application context for shared preferences, so that we use a single cached instance
+ return context.getApplicationContext().getSharedPreferences(
+ FeatureFlags.FLAGS_PREF_NAME, Context.MODE_PRIVATE);
+ }
+
public static boolean areAnimationsEnabled(Context context) {
return ATLEAST_OREO
? ValueAnimator.areAnimatorsEnabled()
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 590c620..fc1a074 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -24,6 +24,7 @@
import static com.android.launcher3.LauncherState.SPRING_LOADED;
import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM;
import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_OVERLAY;
+import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -3309,17 +3310,21 @@
}
@Override
- public void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent) {
- target.gridX = info.cellX;
- target.gridY = info.cellY;
- target.pageIndex = getCurrentPage();
- targetParent.containerType = ContainerType.WORKSPACE;
- if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
- target.rank = info.rank;
- targetParent.containerType = ContainerType.HOTSEAT;
- } else if (info.container >= 0) {
- targetParent.containerType = ContainerType.FOLDER;
+ public void fillInLogContainerData(ItemInfo childInfo, Target child,
+ ArrayList<Target> parents) {
+ if (childInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT
+ || childInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) {
+ getHotseat().fillInLogContainerData(childInfo, child, parents);
+ return;
+ } else if (childInfo.container >= 0) {
+ FolderIcon icon = (FolderIcon) getHomescreenIconByItemId(childInfo.container);
+ icon.getFolder().fillInLogContainerData(childInfo, child, parents);
+ return;
}
+ child.gridX = childInfo.cellX;
+ child.gridY = childInfo.cellY;
+ child.pageIndex = getCurrentPage();
+ parents.add(newContainerTarget(ContainerType.WORKSPACE));
}
/**
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index c33392d..6653426 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -18,6 +18,9 @@
import static com.android.launcher3.LauncherAnimUtils.DRAWABLE_ALPHA;
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
+import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
+import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
+import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_HOTSEAT_SCALE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_HOTSEAT_TRANSLATE;
@@ -117,7 +120,7 @@
hotseatIconsAlpha, fadeInterpolator);
}
- if (!config.playNonAtomicComponent()) {
+ if (config.onlyPlayAtomicComponent()) {
// Only the alpha and scale, handled above, are included in the atomic animation.
return;
}
@@ -125,18 +128,18 @@
Interpolator translationInterpolator = !playAtomicComponent
? LINEAR
: builder.getInterpolator(ANIM_WORKSPACE_TRANSLATE, ZOOM_OUT);
- propertySetter.setFloat(mWorkspace, View.TRANSLATION_X,
+ propertySetter.setFloat(mWorkspace, VIEW_TRANSLATE_X,
scaleAndTranslation.translationX, translationInterpolator);
- propertySetter.setFloat(mWorkspace, View.TRANSLATION_Y,
+ propertySetter.setFloat(mWorkspace, VIEW_TRANSLATE_Y,
scaleAndTranslation.translationY, translationInterpolator);
Interpolator hotseatTranslationInterpolator = builder.getInterpolator(
ANIM_HOTSEAT_TRANSLATE, translationInterpolator);
- propertySetter.setFloat(hotseat, View.TRANSLATION_Y,
+ propertySetter.setFloat(hotseat, VIEW_TRANSLATE_Y,
hotseatScaleAndTranslation.translationY, hotseatTranslationInterpolator);
- propertySetter.setFloat(mWorkspace.getPageIndicator(), View.TRANSLATION_Y,
+ propertySetter.setFloat(mWorkspace.getPageIndicator(), VIEW_TRANSLATE_Y,
hotseatScaleAndTranslation.translationY, hotseatTranslationInterpolator);
- propertySetter.setFloat(qsbView, View.TRANSLATION_Y,
+ propertySetter.setFloat(qsbView, VIEW_TRANSLATE_Y,
qsbScaleAndTranslation.translationY, hotseatTranslationInterpolator);
setScrim(propertySetter, state);
@@ -172,14 +175,15 @@
float pageAlpha = pageAlphaProvider.getPageAlpha(childIndex);
int drawableAlpha = Math.round(pageAlpha * (state.hasWorkspacePageBackground ? 255 : 0));
- if (config.playNonAtomicComponent()) {
+ if (!config.onlyPlayAtomicComponent()) {
+ // Don't update the scrim during the atomic animation.
propertySetter.setInt(cl.getScrimBackground(),
DRAWABLE_ALPHA, drawableAlpha, ZOOM_OUT);
}
if (config.playAtomicOverviewScaleComponent()) {
Interpolator fadeInterpolator = builder.getInterpolator(ANIM_WORKSPACE_FADE,
pageAlphaProvider.interpolator);
- propertySetter.setFloat(cl.getShortcutsAndWidgets(), View.ALPHA,
+ propertySetter.setFloat(cl.getShortcutsAndWidgets(), VIEW_ALPHA,
pageAlpha, fadeInterpolator);
}
}
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index afb7217..e085ff0 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -15,8 +15,7 @@
*/
package com.android.launcher3.allapps;
-import static android.view.View.MeasureSpec.EXACTLY;
-import static android.view.View.MeasureSpec.makeMeasureSpec;
+import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
import android.animation.ValueAnimator;
import android.content.Context;
@@ -65,6 +64,8 @@
import com.android.launcher3.views.SpringRelativeLayout;
import com.android.launcher3.views.WorkFooterContainer;
+import java.util.ArrayList;
+
/**
* The all apps view container.
*/
@@ -327,12 +328,10 @@
}
@Override
- public void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent) {
- if (getApps().hasFilter()) {
- targetParent.containerType = ContainerType.SEARCHRESULT;
- } else {
- targetParent.containerType = ContainerType.ALLAPPS;
- }
+ public void fillInLogContainerData(ItemInfo childInfo, Target child,
+ ArrayList<Target> parents) {
+ parents.add(newContainerTarget(
+ getApps().hasFilter() ? ContainerType.SEARCHRESULT : ContainerType.ALLAPPS));
}
@Override
@@ -393,7 +392,7 @@
rebindAdapters(showTabs, false /* force */);
}
- private void rebindAdapters(boolean showTabs, boolean force) {
+ protected void rebindAdapters(boolean showTabs, boolean force) {
if (showTabs == mUsingTabs && !force) {
return;
}
@@ -461,6 +460,7 @@
public void onTabChanged(int pos) {
mHeader.setMainActive(pos == 0);
reset(true /* animate */);
+ mViewPager.getPageIndicator().updateTabTextColor(pos);
if (mAH[pos].recyclerView != null) {
mAH[pos].recyclerView.bindFastScrollbar();
@@ -606,6 +606,7 @@
public static final int MAIN = 0;
public static final int WORK = 1;
+ private ItemInfoMatcher mInfoMatcher;
private final boolean mIsWork;
public final AllAppsGridAdapter adapter;
final LinearLayoutManager layoutManager;
@@ -625,6 +626,7 @@
}
void setup(@NonNull View rv, @Nullable ItemInfoMatcher matcher) {
+ mInfoMatcher = matcher;
appsList.updateItemFilter(matcher);
recyclerView = (AllAppsRecyclerView) rv;
recyclerView.setEdgeEffectFactory(createEdgeEffectFactory());
@@ -645,19 +647,14 @@
void setupOverlay() {
if (!mIsWork || recyclerView == null) return;
boolean workDisabled = UserCache.INSTANCE.get(mLauncher).isAnyProfileQuietModeEnabled();
- recyclerView.getOverlay().clear();
+ if (mWorkDisabled == workDisabled) return;
if (workDisabled) {
- View pausedOverlay = mLauncher.getLayoutInflater().inflate(
- R.layout.work_apps_paused, null);
- recyclerView.post(() -> {
- int width = recyclerView.getWidth();
- int height = recyclerView.getHeight() - mWorkFooterContainer.getHeight();
- pausedOverlay.measure(makeMeasureSpec(recyclerView.getWidth(), EXACTLY),
- makeMeasureSpec(recyclerView.getHeight(), EXACTLY));
- pausedOverlay.layout(0, 0, width, height);
- applyPadding();
- });
- recyclerView.getOverlay().add(pausedOverlay);
+ appsList.updateItemFilter((info, cn) -> false);
+ recyclerView.addAutoSizedOverlay(
+ mLauncher.getLayoutInflater().inflate(R.layout.work_apps_paused, null));
+ } else if (mInfoMatcher != null) {
+ appsList.updateItemFilter(mInfoMatcher);
+ recyclerView.clearAutoSizedOverlays();
}
mWorkDisabled = workDisabled;
}
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index b6744cf..8fe4633 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -15,7 +15,11 @@
*/
package com.android.launcher3.allapps;
+import static android.view.View.MeasureSpec.EXACTLY;
import static android.view.View.MeasureSpec.UNSPECIFIED;
+import static android.view.View.MeasureSpec.makeMeasureSpec;
+
+import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
import android.content.Context;
import android.content.res.Resources;
@@ -40,6 +44,7 @@
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.views.RecyclerViewFastScroller;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -59,6 +64,8 @@
private AllAppsBackgroundDrawable mEmptySearchBackground;
private int mEmptySearchBackgroundTopOffset;
+ private ArrayList<View> mAutoSizedOverlays = new ArrayList<>();
+
public AllAppsRecyclerView(Context context) {
this(context, null);
}
@@ -142,15 +149,37 @@
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
updateEmptySearchBackgroundBounds();
updatePoolSize();
+ for (int i = 0; i < mAutoSizedOverlays.size(); i++) {
+ View overlay = mAutoSizedOverlays.get(i);
+ overlay.measure(makeMeasureSpec(w, EXACTLY), makeMeasureSpec(w, EXACTLY));
+ overlay.layout(0, 0, w, h);
+ }
+ }
+
+ /**
+ * Adds an overlay that automatically rescales with the recyclerview.
+ */
+ public void addAutoSizedOverlay(View overlay) {
+ mAutoSizedOverlays.add(overlay);
+ getOverlay().add(overlay);
+ onSizeChanged(getWidth(), getHeight(), getWidth(), getHeight());
+ }
+
+ /**
+ * Clears auto scaling overlay views added by #addAutoSizedOverlay
+ */
+ public void clearAutoSizedOverlays() {
+ for (View v : mAutoSizedOverlays) {
+ getOverlay().remove(v);
+ }
+ mAutoSizedOverlays.clear();
}
@Override
- public void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent) {
- if (mApps.hasFilter()) {
- targetParent.containerType = ContainerType.SEARCHRESULT;
- } else {
- targetParent.containerType = ContainerType.ALLAPPS;
- }
+ public void fillInLogContainerData(ItemInfo childInfo, Target child,
+ ArrayList<Target> parents) {
+ parents.add(newContainerTarget(
+ getApps().hasFilter() ? ContainerType.SEARCHRESULT : ContainerType.ALLAPPS));
}
public void onSearchResultsChanged() {
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 2179162..744f4eb 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -12,7 +12,6 @@
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
-import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
import static com.android.launcher3.util.SystemUiController.UI_STATE_ALL_APPS;
import android.animation.Animator;
@@ -31,11 +30,8 @@
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.anim.PropertySetter;
-import com.android.launcher3.anim.SpringObjectAnimator;
-import com.android.launcher3.util.DynamicResource;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ScrimView;
-import com.android.systemui.plugins.ResourceProvider;
/**
* Handles AllApps view transition.
@@ -166,7 +162,7 @@
return;
}
- if (!config.playNonAtomicComponent()) {
+ if (config.onlyPlayAtomicComponent()) {
// There is no atomic component for the all apps transition, so just return early.
return;
}
@@ -185,14 +181,6 @@
}
public Animator createSpringAnimation(float... progressValues) {
- if (UNSTABLE_SPRINGS.get()) {
- ResourceProvider rp = DynamicResource.provider(mLauncher);
- float damping = rp.getFloat(R.dimen.all_apps_spring_damping_ratio);
- float stiffness = rp.getFloat(R.dimen.all_apps_spring_stiffness);
-
- return new SpringObjectAnimator<>(this, ALL_APPS_PROGRESS, 1f / mShiftRange,
- damping, stiffness, progressValues);
- }
return ObjectAnimator.ofFloat(this, ALL_APPS_PROGRESS, progressValues);
}
@@ -224,12 +212,7 @@
}
public AnimatorListenerAdapter getProgressAnimatorListener() {
- return new AnimationSuccessListener() {
- @Override
- public void onAnimationSuccess(Animator animator) {
- onProgressAnimationEnd();
- }
- };
+ return AnimationSuccessListener.forRunnable(this::onProgressAnimationEnd);
}
public void setupViews(AllAppsContainerView appsView) {
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderView.java b/src/com/android/launcher3/allapps/FloatingHeaderView.java
index cc33af9..81e1b94 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderView.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.allapps;
+import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
+
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Point;
@@ -370,7 +372,7 @@
}
allowTouchForwarding(hasAllAppsContent);
- setter.setFloat(mTabLayout, ALPHA, hasAllAppsContent ? 1 : 0, headerFade);
+ setter.setFloat(mTabLayout, VIEW_ALPHA, hasAllAppsContent ? 1 : 0, headerFade);
}
protected void allowTouchForwarding(boolean allow) {
diff --git a/src/com/android/launcher3/allapps/PersonalWorkSlidingTabStrip.java b/src/com/android/launcher3/allapps/PersonalWorkSlidingTabStrip.java
index 0e39bbe..3e40392 100644
--- a/src/com/android/launcher3/allapps/PersonalWorkSlidingTabStrip.java
+++ b/src/com/android/launcher3/allapps/PersonalWorkSlidingTabStrip.java
@@ -71,12 +71,10 @@
mIsRtl = Utilities.isRtl(getResources());
}
- private void updateIndicatorPosition(float scrollOffset) {
- mScrollOffset = scrollOffset;
- updateIndicatorPosition();
- }
-
- private void updateTabTextColor(int pos) {
+ /**
+ * Highlights tab with index pos
+ */
+ public void updateTabTextColor(int pos) {
mSelectedPosition = pos;
for (int i = 0; i < getChildCount(); i++) {
Button tab = (Button) getChildAt(i);
@@ -84,6 +82,11 @@
}
}
+ private void updateIndicatorPosition(float scrollOffset) {
+ mScrollOffset = scrollOffset;
+ updateIndicatorPosition();
+ }
+
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
diff --git a/src/com/android/launcher3/allapps/PluginHeaderRow.java b/src/com/android/launcher3/allapps/PluginHeaderRow.java
index 535ef54..3089b18 100644
--- a/src/com/android/launcher3/allapps/PluginHeaderRow.java
+++ b/src/com/android/launcher3/allapps/PluginHeaderRow.java
@@ -15,10 +15,11 @@
*/
package com.android.launcher3.allapps;
-import static android.view.View.ALPHA;
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
+import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
+
import android.graphics.Rect;
import android.view.View;
import android.view.animation.Interpolator;
@@ -67,7 +68,7 @@
public void setContentVisibility(boolean hasHeaderExtra, boolean hasAllAppsContent,
PropertySetter setter, Interpolator headerFade, Interpolator allAppsFade) {
// Don't use setViewAlpha as we want to control the visibility ourselves.
- setter.setFloat(mView, ALPHA, hasAllAppsContent ? 1 : 0, headerFade);
+ setter.setFloat(mView, VIEW_ALPHA, hasAllAppsContent ? 1 : 0, headerFade);
}
@Override
diff --git a/src/com/android/launcher3/anim/AnimationSuccessListener.java b/src/com/android/launcher3/anim/AnimationSuccessListener.java
index 9448632..9905e81 100644
--- a/src/com/android/launcher3/anim/AnimationSuccessListener.java
+++ b/src/com/android/launcher3/anim/AnimationSuccessListener.java
@@ -39,4 +39,25 @@
}
public abstract void onAnimationSuccess(Animator animator);
+
+ /**
+ * Returns an AnimationSuccessListener which runs the provided action on success
+ */
+ public static AnimationSuccessListener forRunnable(Runnable r) {
+ return new RunnableSuccessListener(r);
+ }
+
+ private static class RunnableSuccessListener extends AnimationSuccessListener {
+
+ private final Runnable mRunnable;
+
+ private RunnableSuccessListener(Runnable r) {
+ mRunnable = r;
+ }
+
+ @Override
+ public void onAnimationSuccess(Animator animator) {
+ mRunnable.run();
+ }
+ }
}
diff --git a/src/com/android/launcher3/anim/AnimatorPlaybackController.java b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
index 1c277ab..1fc21fd 100644
--- a/src/com/android/launcher3/anim/AnimatorPlaybackController.java
+++ b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
@@ -16,7 +16,9 @@
package com.android.launcher3.anim;
import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
+import static com.android.launcher3.anim.Interpolators.clampToProgress;
+import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
+import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
@@ -24,17 +26,18 @@
import android.animation.AnimatorSet;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
-import android.util.Log;
+import android.content.Context;
+import android.util.FloatProperty;
import androidx.annotation.Nullable;
-import androidx.dynamicanimation.animation.DynamicAnimation;
-import androidx.dynamicanimation.animation.SpringAnimation;
+
+import com.android.launcher3.Utilities;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashSet;
import java.util.List;
-import java.util.Set;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
/**
* Helper class to control the playback of an {@link AnimatorSet}, with custom interpolators
@@ -43,14 +46,7 @@
* Note: The implementation does not support start delays on child animations or
* sequential playbacks.
*/
-public abstract class AnimatorPlaybackController implements ValueAnimator.AnimatorUpdateListener {
-
- private static final String TAG = "AnimatorPlaybackCtrler";
- private static boolean DEBUG = false;
-
- public static AnimatorPlaybackController wrap(AnimatorSet anim, long duration) {
- return wrap(anim, duration, null);
- }
+public class AnimatorPlaybackController implements ValueAnimator.AnimatorUpdateListener {
/**
* Creates an animation controller for the provided animation.
@@ -58,20 +54,41 @@
* needs to be larger than the total number of pixels so that we don't have jittering due
* to float (animation-fraction * total duration) to int conversion.
*/
- public static AnimatorPlaybackController wrap(AnimatorSet anim, long duration,
- Runnable onCancelRunnable) {
-
+ public static AnimatorPlaybackController wrap(AnimatorSet anim, long duration) {
/**
* TODO: use {@link AnimatorSet#setCurrentPlayTime(long)} once b/68382377 is fixed.
*/
- return new AnimatorPlaybackControllerVL(anim, duration, onCancelRunnable);
+ ArrayList<Holder> childAnims = new ArrayList<>();
+ addAnimationHoldersRecur(anim, SpringProperty.DEFAULT, childAnims);
+
+ return new AnimatorPlaybackController(anim, duration, childAnims);
}
+ public static AnimatorPlaybackController wrap(PendingAnimation anim, long duration) {
+ /**
+ * TODO: use {@link AnimatorSet#setCurrentPlayTime(long)} once b/68382377 is fixed.
+ */
+ return new AnimatorPlaybackController(anim.anim, duration, anim.animHolders);
+ }
+
+ private static final FloatProperty<ValueAnimator> CURRENT_PLAY_TIME =
+ new FloatProperty<ValueAnimator>("current-play-time") {
+ @Override
+ public void setValue(ValueAnimator animator, float v) {
+ animator.setCurrentPlayTime((long) v);
+ }
+
+ @Override
+ public Float get(ValueAnimator animator) {
+ return (float) animator.getCurrentPlayTime();
+ }
+ };
+
private final ValueAnimator mAnimationPlayer;
private final long mDuration;
- protected final AnimatorSet mAnim;
- private Set<SpringAnimation> mSprings;
+ private final AnimatorSet mAnim;
+ private final Holder[] mChildAnimations;
protected float mCurrentFraction;
private Runnable mEndAction;
@@ -79,22 +96,14 @@
protected boolean mTargetCancelled = false;
protected Runnable mOnCancelRunnable;
- private OnAnimationEndDispatcher mEndListener;
- private DynamicAnimation.OnAnimationEndListener mSpringEndListener;
- // We need this variable to ensure the end listener is called immediately, otherwise we run into
- // issues where the callback interferes with the states of the swipe detector.
- private boolean mSkipToEnd = false;
-
- protected AnimatorPlaybackController(AnimatorSet anim, long duration,
- Runnable onCancelRunnable) {
+ private AnimatorPlaybackController(
+ AnimatorSet anim, long duration, ArrayList<Holder> childAnims) {
mAnim = anim;
mDuration = duration;
- mOnCancelRunnable = onCancelRunnable;
mAnimationPlayer = ValueAnimator.ofFloat(0, 1);
mAnimationPlayer.setInterpolator(LINEAR);
- mEndListener = new OnAnimationEndDispatcher();
- mAnimationPlayer.addListener(mEndListener);
+ mAnimationPlayer.addListener(new OnAnimationEndDispatcher());
mAnimationPlayer.addUpdateListener(this);
mAnim.addListener(new AnimatorListenerAdapter() {
@@ -119,14 +128,7 @@
}
});
- mSprings = new HashSet<>();
- mSpringEndListener = (animation, canceled, value, velocity1) -> {
- if (canceled) {
- mEndListener.onAnimationCancel(mAnimationPlayer);
- } else {
- mEndListener.onAnimationEnd(mAnimationPlayer);
- }
- };
+ mChildAnimations = childAnims.toArray(new Holder[childAnims.size()]);
}
public AnimatorSet getTarget() {
@@ -160,9 +162,68 @@
}
/**
+ * Starts playing the animation with the provided velocity optionally playing any
+ * physics based animations
+ */
+ public void startWithVelocity(Context context, boolean goingToEnd,
+ float velocity, float scale, long animationDuration) {
+ float scaleInverse = 1 / Math.abs(scale);
+ float scaledVelocity = velocity * scaleInverse;
+
+ float nextFrameProgress = Utilities.boundToRange(getProgressFraction()
+ + scaledVelocity * getSingleFrameMs(context), 0f, 1f);
+
+ // Update setters for spring
+ int springFlag = goingToEnd
+ ? SpringProperty.FLAG_CAN_SPRING_ON_END
+ : SpringProperty.FLAG_CAN_SPRING_ON_START;
+
+ long springDuration = animationDuration;
+ for (Holder h : mChildAnimations) {
+ if ((h.springProperty.flags & springFlag) != 0) {
+ SpringAnimationBuilder s = new SpringAnimationBuilder(h.anim, CURRENT_PLAY_TIME)
+ .setStartValue(clampDuration(mCurrentFraction))
+ .setEndValue(goingToEnd ? h.anim.getDuration() : 0)
+ .setStartVelocity(scaledVelocity * h.anim.getDuration())
+ .setMinimumVisibleChange(scaleInverse)
+ .setDampingRatio(h.springProperty.mDampingRatio)
+ .setStiffness(h.springProperty.mStiffness);
+
+ long expectedDurationL = s.build(context).getDuration();
+ springDuration = Math.max(expectedDurationL, springDuration);
+
+ float expectedDuration = expectedDurationL;
+ h.setter = (a, l) ->
+ s.setValue(a, mAnimationPlayer.getCurrentPlayTime() / expectedDuration);
+ h.anim.setInterpolator(LINEAR);
+ }
+ }
+
+ mAnimationPlayer.setFloatValues(nextFrameProgress, goingToEnd ? 1f : 0f);
+
+ if (springDuration <= animationDuration) {
+ mAnimationPlayer.setDuration(animationDuration);
+ mAnimationPlayer.setInterpolator(scrollInterpolatorForVelocity(velocity));
+ } else {
+ // Since spring requires more time to run, we let the other animations play with
+ // current time and interpolation and by clamping the duration.
+ mAnimationPlayer.setDuration(springDuration);
+
+ float cutOff = animationDuration / (float) springDuration;
+ mAnimationPlayer.setInterpolator(
+ clampToProgress(scrollInterpolatorForVelocity(velocity), 0, cutOff));
+ }
+ mAnimationPlayer.start();
+ }
+
+ /**
* Pauses the currently playing animation.
*/
public void pause() {
+ // Reset property setters
+ for (Holder h : mChildAnimations) {
+ h.reset();
+ }
mAnimationPlayer.cancel();
}
@@ -176,7 +237,18 @@
/**
* Sets the current animation position and updates all the child animators accordingly.
*/
- public abstract void setPlayFraction(float fraction);
+ public void setPlayFraction(float fraction) {
+ mCurrentFraction = fraction;
+ // Let the animator report the progress but don't apply the progress to child
+ // animations if it has been cancelled.
+ if (mTargetCancelled) {
+ return;
+ }
+ long playPos = clampDuration(fraction);
+ for (Holder holder : mChildAnimations) {
+ holder.setter.set(holder.anim, playPos);
+ }
+ }
public float getProgressFraction() {
return mCurrentFraction;
@@ -208,49 +280,6 @@
}
}
- /**
- * Starts playback and sets the spring.
- */
- public void dispatchOnStartWithVelocity(float end, float velocity) {
- if (!QUICKSTEP_SPRINGS.get()) {
- dispatchOnStart();
- return;
- }
-
- if (DEBUG) Log.d(TAG, "dispatchOnStartWithVelocity#end=" + end + ", velocity=" + velocity);
-
- for (Animator a : mAnim.getChildAnimations()) {
- if (a instanceof SpringObjectAnimator) {
- if (DEBUG) Log.d(TAG, "Found springAnimator=" + a);
- SpringObjectAnimator springAnimator = (SpringObjectAnimator) a;
- mSprings.add(springAnimator.getSpring());
- springAnimator.startSpring(end, velocity, mSpringEndListener);
- }
- }
-
- dispatchOnStart();
- }
-
- public void dispatchOnStart() {
- dispatchOnStartRecursively(mAnim);
- }
-
- private void dispatchOnStartRecursively(Animator animator) {
- List<AnimatorListener> listeners = animator instanceof SpringObjectAnimator
- ? nonNullList(((SpringObjectAnimator) animator).getObjectAnimatorListeners())
- : nonNullList(animator.getListeners());
-
- for (AnimatorListener l : listeners) {
- l.onAnimationStart(animator);
- }
-
- if (animator instanceof AnimatorSet) {
- for (Animator anim : nonNullList(((AnimatorSet) animator).getChildAnimations())) {
- dispatchOnStartRecursively(anim);
- }
- }
- }
-
/** @see #dispatchOnCancelWithoutCancelRunnable(Runnable) */
public void dispatchOnCancelWithoutCancelRunnable() {
dispatchOnCancelWithoutCancelRunnable(null);
@@ -272,115 +301,47 @@
setOnCancelRunnable(onCancel);
}
- public void dispatchOnCancel() {
- dispatchOnCancelRecursively(mAnim);
+
+ public AnimatorPlaybackController setOnCancelRunnable(Runnable runnable) {
+ mOnCancelRunnable = runnable;
+ return this;
}
- private void dispatchOnCancelRecursively(Animator animator) {
- for (AnimatorListener l : nonNullList(animator.getListeners())) {
- l.onAnimationCancel(animator);
- }
+ public void dispatchOnStart() {
+ callListenerCommandRecursively(mAnim, AnimatorListener::onAnimationStart);
+ }
- if (animator instanceof AnimatorSet) {
- for (Animator anim : nonNullList(((AnimatorSet) animator).getChildAnimations())) {
- dispatchOnCancelRecursively(anim);
- }
- }
+ public void dispatchOnCancel() {
+ callListenerCommandRecursively(mAnim, AnimatorListener::onAnimationCancel);
}
public void dispatchSetInterpolator(TimeInterpolator interpolator) {
- dispatchSetInterpolatorRecursively(mAnim, interpolator);
+ callAnimatorCommandRecursively(mAnim, a -> a.setInterpolator(interpolator));
}
- private void dispatchSetInterpolatorRecursively(Animator anim, TimeInterpolator interpolator) {
- anim.setInterpolator(interpolator);
+ private static void callListenerCommandRecursively(
+ Animator anim, BiConsumer<AnimatorListener, Animator> command) {
+ callAnimatorCommandRecursively(anim, a-> {
+ for (AnimatorListener l : nonNullList(a.getListeners())) {
+ command.accept(l, a);
+ }
+ });
+ }
+
+ private static void callAnimatorCommandRecursively(Animator anim, Consumer<Animator> command) {
+ command.accept(anim);
if (anim instanceof AnimatorSet) {
for (Animator child : nonNullList(((AnimatorSet) anim).getChildAnimations())) {
- dispatchSetInterpolatorRecursively(child, interpolator);
+ callAnimatorCommandRecursively(child, command);
}
}
}
- public void setOnCancelRunnable(Runnable runnable) {
- mOnCancelRunnable = runnable;
- }
-
- public void skipToEnd() {
- mSkipToEnd = true;
- for (SpringAnimation spring : mSprings) {
- if (spring.canSkipToEnd()) {
- spring.skipToEnd();
- }
- }
- mAnimationPlayer.end();
- mSkipToEnd = false;
- }
-
- public static class AnimatorPlaybackControllerVL extends AnimatorPlaybackController {
-
- private final ValueAnimator[] mChildAnimations;
-
- private AnimatorPlaybackControllerVL(AnimatorSet anim, long duration,
- Runnable onCancelRunnable) {
- super(anim, duration, onCancelRunnable);
-
- // Build animation list
- ArrayList<ValueAnimator> childAnims = new ArrayList<>();
- getAnimationsRecur(mAnim, childAnims);
- mChildAnimations = childAnims.toArray(new ValueAnimator[childAnims.size()]);
- }
-
- private void getAnimationsRecur(AnimatorSet anim, ArrayList<ValueAnimator> out) {
- long forceDuration = anim.getDuration();
- TimeInterpolator forceInterpolator = anim.getInterpolator();
- for (Animator child : anim.getChildAnimations()) {
- if (forceDuration > 0) {
- child.setDuration(forceDuration);
- }
- if (forceInterpolator != null) {
- child.setInterpolator(forceInterpolator);
- }
- if (child instanceof ValueAnimator) {
- out.add((ValueAnimator) child);
- } else if (child instanceof AnimatorSet) {
- getAnimationsRecur((AnimatorSet) child, out);
- } else {
- throw new RuntimeException("Unknown animation type " + child);
- }
- }
- }
-
- @Override
- public void setPlayFraction(float fraction) {
- mCurrentFraction = fraction;
- // Let the animator report the progress but don't apply the progress to child
- // animations if it has been cancelled.
- if (mTargetCancelled) {
- return;
- }
- long playPos = clampDuration(fraction);
- for (ValueAnimator anim : mChildAnimations) {
- anim.setCurrentPlayTime(Math.min(playPos, anim.getDuration()));
- }
- }
- }
-
- private boolean isAnySpringRunning() {
- for (SpringAnimation spring : mSprings) {
- if (spring.isRunning()) {
- return true;
- }
- }
- return false;
- }
-
/**
* Only dispatches the on end actions once the animator and all springs have completed running.
*/
private class OnAnimationEndDispatcher extends AnimationSuccessListener {
- boolean mAnimatorDone = false;
- boolean mSpringsDone = false;
boolean mDispatched = false;
@Override
@@ -391,39 +352,76 @@
@Override
public void onAnimationSuccess(Animator animator) {
- if (mSprings.isEmpty()) {
- mSpringsDone = mAnimatorDone = true;
- }
- if (isAnySpringRunning()) {
- mAnimatorDone = true;
- } else {
- mSpringsDone = true;
- }
-
// We wait for the spring (if any) to finish running before completing the end callback.
- if (!mDispatched && (mSkipToEnd || (mAnimatorDone && mSpringsDone))) {
- dispatchOnEndRecursively(mAnim);
+ if (!mDispatched) {
+ callListenerCommandRecursively(mAnim, AnimatorListener::onAnimationEnd);
if (mEndAction != null) {
mEndAction.run();
}
mDispatched = true;
}
}
-
- private void dispatchOnEndRecursively(Animator animator) {
- for (AnimatorListener l : nonNullList(animator.getListeners())) {
- l.onAnimationEnd(animator);
- }
-
- if (animator instanceof AnimatorSet) {
- for (Animator anim : nonNullList(((AnimatorSet) animator).getChildAnimations())) {
- dispatchOnEndRecursively(anim);
- }
- }
- }
}
private static <T> List<T> nonNullList(ArrayList<T> list) {
return list == null ? Collections.emptyList() : list;
}
+
+ /**
+ * Interface for setting position of value animator
+ */
+ private interface PositionSetter {
+
+ PositionSetter DEFAULT = (anim, playPos) ->
+ anim.setCurrentPlayTime(Math.min(playPos, anim.getDuration()));
+
+ void set(ValueAnimator anim, long position);
+ }
+
+ /**
+ * Holder class for various child animations
+ */
+ static class Holder {
+
+ public final ValueAnimator anim;
+
+ public final SpringProperty springProperty;
+
+ public final TimeInterpolator interpolator;
+
+ public PositionSetter setter;
+
+ Holder(Animator anim, SpringProperty springProperty) {
+ this.anim = (ValueAnimator) anim;
+ this.springProperty = springProperty;
+ this.interpolator = this.anim.getInterpolator();
+ this.setter = PositionSetter.DEFAULT;
+ }
+
+ public void reset() {
+ anim.setInterpolator(interpolator);
+ setter = PositionSetter.DEFAULT;
+ }
+ }
+
+ static void addAnimationHoldersRecur(
+ Animator anim, SpringProperty springProperty, ArrayList<Holder> out) {
+ long forceDuration = anim.getDuration();
+ TimeInterpolator forceInterpolator = anim.getInterpolator();
+ if (anim instanceof ValueAnimator) {
+ out.add(new Holder(anim, springProperty));
+ } else if (anim instanceof AnimatorSet) {
+ for (Animator child : ((AnimatorSet) anim).getChildAnimations()) {
+ if (forceDuration > 0) {
+ child.setDuration(forceDuration);
+ }
+ if (forceInterpolator != null) {
+ child.setInterpolator(forceInterpolator);
+ }
+ addAnimationHoldersRecur(child, springProperty, out);
+ }
+ } else {
+ throw new RuntimeException("Unknown animation type " + anim);
+ }
+ }
}
diff --git a/src/com/android/launcher3/anim/AnimatorSetBuilder.java b/src/com/android/launcher3/anim/AnimatorSetBuilder.java
index cd30dea..d814b19 100644
--- a/src/com/android/launcher3/anim/AnimatorSetBuilder.java
+++ b/src/com/android/launcher3/anim/AnimatorSetBuilder.java
@@ -21,7 +21,6 @@
import android.view.animation.Interpolator;
import java.util.ArrayList;
-import java.util.List;
/**
* Utility class for building animator set
@@ -42,36 +41,17 @@
public static final int ANIM_OVERVIEW_SCRIM_FADE = 11;
public static final int ANIM_ALL_APPS_HEADER_FADE = 12; // e.g. predictions
- public static final int FLAG_DONT_ANIMATE_OVERVIEW = 1 << 0;
-
protected final ArrayList<Animator> mAnims = new ArrayList<>();
private final SparseArray<Interpolator> mInterpolators = new SparseArray<>();
- private List<Runnable> mOnFinishRunnables = new ArrayList<>();
- private int mFlags = 0;
public void play(Animator anim) {
mAnims.add(anim);
}
- public void addOnFinishRunnable(Runnable onFinishRunnable) {
- mOnFinishRunnables.add(onFinishRunnable);
- }
-
public AnimatorSet build() {
AnimatorSet anim = new AnimatorSet();
anim.playTogether(mAnims);
- if (!mOnFinishRunnables.isEmpty()) {
- anim.addListener(new AnimationSuccessListener() {
- @Override
- public void onAnimationSuccess(Animator animation) {
- for (Runnable onFinishRunnable : mOnFinishRunnables) {
- onFinishRunnable.run();
- }
- mOnFinishRunnables.clear();
- }
- });
- }
return anim;
}
@@ -82,12 +62,4 @@
public void setInterpolator(int animId, Interpolator interpolator) {
mInterpolators.put(animId, interpolator);
}
-
- public void addFlag(int flag) {
- mFlags |= flag;
- }
-
- public boolean hasFlag(int flag) {
- return (mFlags & flag) != 0;
- }
}
diff --git a/src/com/android/launcher3/anim/PendingAnimation.java b/src/com/android/launcher3/anim/PendingAnimation.java
new file mode 100644
index 0000000..562d160
--- /dev/null
+++ b/src/com/android/launcher3/anim/PendingAnimation.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.anim;
+
+import static com.android.launcher3.anim.AnimatorPlaybackController.addAnimationHoldersRecur;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.TimeInterpolator;
+import android.annotation.TargetApi;
+import android.os.Build;
+
+import com.android.launcher3.anim.AnimatorPlaybackController.Holder;
+
+import java.util.ArrayList;
+import java.util.function.Consumer;
+
+/**
+ * Utility class to keep track of a running animation.
+ *
+ * This class allows attaching end callbacks to an animation is intended to be used with
+ * {@link com.android.launcher3.anim.AnimatorPlaybackController}, since in that case
+ * AnimationListeners are not properly dispatched.
+ *
+ * TODO: Find a better name
+ */
+@TargetApi(Build.VERSION_CODES.O)
+public class PendingAnimation {
+
+ private final ArrayList<Consumer<EndState>> mEndListeners = new ArrayList<>();
+
+ /** package private **/
+ final AnimatorSet anim = new AnimatorSet();
+ final ArrayList<Holder> animHolders = new ArrayList<>();
+
+ /**
+ * Utility method to sent an interpolator on an animation and add it to the list
+ */
+ public void add(Animator anim, TimeInterpolator interpolator) {
+ add(anim, interpolator, SpringProperty.DEFAULT);
+ }
+
+ public void add(Animator anim, TimeInterpolator interpolator, SpringProperty springProperty) {
+ anim.setInterpolator(interpolator);
+ add(anim, springProperty);
+ }
+
+ public void add(Animator anim) {
+ add(anim, SpringProperty.DEFAULT);
+ }
+
+ public void add(Animator a, SpringProperty springProperty) {
+ anim.play(a);
+ addAnimationHoldersRecur(a, springProperty, animHolders);
+ }
+
+ public void finish(boolean isSuccess, int logAction) {
+ for (Consumer<EndState> listeners : mEndListeners) {
+ listeners.accept(new EndState(isSuccess, logAction));
+ }
+ mEndListeners.clear();
+ }
+
+ public void addEndListener(Consumer<EndState> listener) {
+ mEndListeners.add(listener);
+ }
+
+ public static class EndState {
+ public boolean isSuccess;
+ public int logAction;
+
+ public EndState(boolean isSuccess, int logAction) {
+ this.isSuccess = isSuccess;
+ this.logAction = logAction;
+ }
+ }
+}
diff --git a/src/com/android/launcher3/anim/PropertySetter.java b/src/com/android/launcher3/anim/PropertySetter.java
index 757edff..0b2eb48 100644
--- a/src/com/android/launcher3/anim/PropertySetter.java
+++ b/src/com/android/launcher3/anim/PropertySetter.java
@@ -19,7 +19,8 @@
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
-import android.util.Property;
+import android.util.FloatProperty;
+import android.util.IntProperty;
import android.view.View;
/**
@@ -36,14 +37,14 @@
}
}
- public <T> void setFloat(T target, Property<T, Float> property, float value,
+ public <T> void setFloat(T target, FloatProperty<T> property, float value,
TimeInterpolator interpolator) {
- property.set(target, value);
+ property.setValue(target, value);
}
- public <T> void setInt(T target, Property<T, Integer> property, int value,
+ public <T> void setInt(T target, IntProperty<T> property, int value,
TimeInterpolator interpolator) {
- property.set(target, value);
+ property.setValue(target, value);
}
public static class AnimatedPropertySetter extends PropertySetter {
@@ -68,7 +69,7 @@
}
@Override
- public <T> void setFloat(T target, Property<T, Float> property, float value,
+ public <T> void setFloat(T target, FloatProperty<T> property, float value,
TimeInterpolator interpolator) {
if (property.get(target) == value) {
return;
@@ -79,7 +80,7 @@
}
@Override
- public <T> void setInt(T target, Property<T, Integer> property, int value,
+ public <T> void setInt(T target, IntProperty<T> property, int value,
TimeInterpolator interpolator) {
if (property.get(target) == value) {
return;
diff --git a/src/com/android/launcher3/anim/SpringAnimationBuilder.java b/src/com/android/launcher3/anim/SpringAnimationBuilder.java
index 0f34c1e..f22a9f0 100644
--- a/src/com/android/launcher3/anim/SpringAnimationBuilder.java
+++ b/src/com/android/launcher3/anim/SpringAnimationBuilder.java
@@ -15,16 +15,15 @@
*/
package com.android.launcher3.anim;
-import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.util.FloatProperty;
-import com.android.launcher3.util.DefaultDisplay;
-
import androidx.annotation.FloatRange;
import androidx.dynamicanimation.animation.SpringForce;
+import com.android.launcher3.util.DefaultDisplay;
+
/**
* Utility class to build an object animator which follows the same path as a spring animation for
* an underdamped spring.
@@ -192,12 +191,8 @@
long durationMs = (long) (1000.0 * duration);
ObjectAnimator animator = ObjectAnimator.ofFloat(mTarget, this, 0, (float) duration);
animator.setDuration(durationMs).setInterpolator(Interpolators.LINEAR);
- animator.addListener(new AnimationSuccessListener() {
- @Override
- public void onAnimationSuccess(Animator animator) {
- mProperty.setValue(mTarget, mEndValue);
- }
- });
+ animator.addListener(AnimationSuccessListener.forRunnable(
+ () -> mProperty.setValue(mTarget, mEndValue)));
return animator;
}
diff --git a/src/com/android/launcher3/anim/SpringObjectAnimator.java b/src/com/android/launcher3/anim/SpringObjectAnimator.java
deleted file mode 100644
index 27b9c18..0000000
--- a/src/com/android/launcher3/anim/SpringObjectAnimator.java
+++ /dev/null
@@ -1,308 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.anim;
-
-import static androidx.dynamicanimation.animation.FloatPropertyCompat.createFloatPropertyCompat;
-
-import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator;
-import android.os.Handler;
-import android.os.Looper;
-import android.util.FloatProperty;
-import android.util.Log;
-
-import androidx.dynamicanimation.animation.DynamicAnimation.OnAnimationEndListener;
-import androidx.dynamicanimation.animation.SpringAnimation;
-import androidx.dynamicanimation.animation.SpringForce;
-
-import java.util.ArrayList;
-
-
-/**
- * This animator allows for an object's property to be be controlled by an {@link ObjectAnimator} or
- * a {@link SpringAnimation}. It extends ValueAnimator so it can be used in an AnimatorSet.
- */
-public class SpringObjectAnimator<T> extends ValueAnimator {
-
- private static final String TAG = "SpringObjectAnimator";
- private static boolean DEBUG = false;
-
- private ObjectAnimator mObjectAnimator;
- private float[] mValues;
-
- private SpringAnimation mSpring;
- private SpringProperty<T> mProperty;
-
- private ArrayList<AnimatorListener> mListeners;
- private boolean mSpringEnded = true;
- private boolean mAnimatorEnded = true;
- private boolean mEnded = true;
-
- public SpringObjectAnimator(T object, FloatProperty<T> property, float minimumVisibleChange,
- float damping, float stiffness, float... values) {
- mSpring = new SpringAnimation(object, createFloatPropertyCompat(property));
- mSpring.setMinimumVisibleChange(minimumVisibleChange);
- mSpring.setSpring(new SpringForce(0)
- .setDampingRatio(damping)
- .setStiffness(stiffness));
- mSpring.setStartVelocity(0.01f);
- mProperty = new SpringProperty<>(property, mSpring);
- mObjectAnimator = ObjectAnimator.ofFloat(object, mProperty, values);
- mValues = values;
- mListeners = new ArrayList<>();
- setFloatValues(values);
-
- // We use this listener and track mListeners so that we can sync the animator and spring
- // listeners.
- mObjectAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- mAnimatorEnded = false;
- mEnded = false;
- for (AnimatorListener l : mListeners) {
- l.onAnimationStart(animation);
- }
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- mAnimatorEnded = true;
- tryEnding();
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- for (AnimatorListener l : mListeners) {
- l.onAnimationCancel(animation);
- }
- mSpring.cancel();
- }
- });
-
- mSpring.addUpdateListener((animation, value, velocity) -> {
- mSpringEnded = false;
- mEnded = false;
- });
- mSpring.addEndListener((animation, canceled, value, velocity) -> {
- mSpringEnded = true;
- tryEnding();
- });
- }
-
- private void tryEnding() {
- if (DEBUG) {
- Log.d(TAG, "tryEnding#mAnimatorEnded=" + mAnimatorEnded + ", mSpringEnded="
- + mSpringEnded + ", mEnded=" + mEnded);
- }
-
- // If springs are disabled, ignore value of mSpringEnded
- if (mAnimatorEnded && (mSpringEnded || !QUICKSTEP_SPRINGS.get()) && !mEnded) {
- for (AnimatorListener l : mListeners) {
- l.onAnimationEnd(this);
- }
- mEnded = true;
- }
- }
-
- public SpringAnimation getSpring() {
- return mSpring;
- }
-
- /**
- * Initializes and sets up the spring to take over controlling the object.
- */
- public void startSpring(float end, float velocity, OnAnimationEndListener endListener) {
- // Cancel the spring so we can set new start velocity and final position. We need to remove
- // the listener since the spring is not actually ending.
- mSpring.removeEndListener(endListener);
- mSpring.cancel();
- mSpring.addEndListener(endListener);
-
- mProperty.switchToSpring();
-
- float startValue = end == 0 ? mValues[1] : mValues[0];
- float endValue = end == 0 ? mValues[0] : mValues[1];
-
- // Ensures that the velocity matches the direction of the values.
- velocity = Math.signum(endValue - startValue) * Math.abs(velocity);
- mSpring.setStartVelocity(velocity);
-
- new Handler(Looper.getMainLooper()).postDelayed(() -> {
- mSpring.animateToFinalPosition(endValue);
- }, getStartDelay());
- }
-
- @Override
- public void addListener(AnimatorListener listener) {
- mListeners.add(listener);
- }
-
- public ArrayList<AnimatorListener> getObjectAnimatorListeners() {
- return mObjectAnimator.getListeners();
- }
-
- @Override
- public ArrayList<AnimatorListener> getListeners() {
- return mListeners;
- }
-
- @Override
- public void removeAllListeners() {
- mListeners.clear();
- }
-
- @Override
- public void removeListener(AnimatorListener listener) {
- mListeners.remove(listener);
- }
-
- @Override
- public void addPauseListener(AnimatorPauseListener listener) {
- mObjectAnimator.addPauseListener(listener);
- }
-
- @Override
- public void cancel() {
- mObjectAnimator.cancel();
- mSpring.cancel();
- }
-
- @Override
- public void end() {
- mObjectAnimator.end();
- }
-
- @Override
- public long getDuration() {
- return mObjectAnimator.getDuration();
- }
-
- @Override
- public TimeInterpolator getInterpolator() {
- return mObjectAnimator.getInterpolator();
- }
-
- @Override
- public long getStartDelay() {
- return mObjectAnimator.getStartDelay();
- }
-
- @Override
- public long getTotalDuration() {
- return mObjectAnimator.getTotalDuration();
- }
-
- @Override
- public boolean isPaused() {
- return mObjectAnimator.isPaused();
- }
-
- @Override
- public boolean isRunning() {
- return mObjectAnimator.isRunning();
- }
-
- @Override
- public boolean isStarted() {
- return mObjectAnimator.isStarted();
- }
-
- @Override
- public void pause() {
- mObjectAnimator.pause();
- }
-
- @Override
- public void removePauseListener(AnimatorPauseListener listener) {
- mObjectAnimator.removePauseListener(listener);
- }
-
- @Override
- public void resume() {
- mObjectAnimator.resume();
- }
-
- @Override
- public ValueAnimator setDuration(long duration) {
- return mObjectAnimator.setDuration(duration);
- }
-
- @Override
- public void setInterpolator(TimeInterpolator value) {
- mObjectAnimator.setInterpolator(value);
- }
-
- @Override
- public void setStartDelay(long startDelay) {
- mObjectAnimator.setStartDelay(startDelay);
- }
-
- @Override
- public void setTarget(Object target) {
- mObjectAnimator.setTarget(target);
- }
-
- @Override
- public void start() {
- mObjectAnimator.start();
- }
-
- @Override
- public void setCurrentFraction(float fraction) {
- mObjectAnimator.setCurrentFraction(fraction);
- }
-
- @Override
- public void setCurrentPlayTime(long playTime) {
- mObjectAnimator.setCurrentPlayTime(playTime);
- }
-
- public static class SpringProperty<T> extends FloatProperty<T> {
-
- boolean useSpring = false;
- final FloatProperty<T> mProperty;
- final SpringAnimation mSpring;
-
- public SpringProperty(FloatProperty<T> property, SpringAnimation spring) {
- super(property.getName());
- mProperty = property;
- mSpring = spring;
- }
-
- public void switchToSpring() {
- useSpring = true;
- }
-
- @Override
- public Float get(T object) {
- return mProperty.get(object);
- }
-
- @Override
- public void setValue(T object, float progress) {
- if (useSpring) {
- mSpring.animateToFinalPosition(progress);
- } else {
- mProperty.setValue(object, progress);
- }
- }
- }
-}
diff --git a/src/com/android/launcher3/anim/SpringProperty.java b/src/com/android/launcher3/anim/SpringProperty.java
new file mode 100644
index 0000000..caedd6c
--- /dev/null
+++ b/src/com/android/launcher3/anim/SpringProperty.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.anim;
+
+import androidx.dynamicanimation.animation.SpringForce;
+
+/**
+ * Utility class to store configurations for spring animation
+ */
+public class SpringProperty {
+
+ public static final SpringProperty DEFAULT = new SpringProperty();
+
+ // Play spring when the animation is going towards the end
+ public static final int FLAG_CAN_SPRING_ON_END = 1 << 0;
+ // Play spring when animation is going towards the start (in reverse direction)
+ public static final int FLAG_CAN_SPRING_ON_START = 1 << 1;
+
+ public final int flags;
+
+ float mDampingRatio = SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY;
+ float mStiffness = SpringForce.STIFFNESS_MEDIUM;
+
+ public SpringProperty() {
+ this(0);
+ }
+
+ public SpringProperty(int flags) {
+ this.flags = flags;
+ }
+
+ public SpringProperty setDampingRatio(float dampingRatio) {
+ mDampingRatio = dampingRatio;
+ return this;
+ }
+
+ public SpringProperty setStiffness(float stiffness) {
+ mStiffness = stiffness;
+ return this;
+ }
+}
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index ed28df0..471a743 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -36,6 +36,8 @@
private static final List<DebugFlag> sDebugFlags = new ArrayList<>();
public static final String FLAGS_PREF_NAME = "featureFlags";
+ public static final String FLAG_ENABLE_FIXED_ROTATION_TRANSFORM =
+ "ENABLE_FIXED_ROTATION_TRANSFORM";
private FeatureFlags() { }
@@ -93,8 +95,9 @@
public static final BooleanFlag FAKE_LANDSCAPE_UI = getDebugFlag(
"FAKE_LANDSCAPE_UI", false, "Rotate launcher UI instead of using transposed layout");
- public static final BooleanFlag FOLDER_NAME_SUGGEST = getDebugFlag(
- "FOLDER_NAME_SUGGEST", true, "Suggests folder names instead of blank text.");
+ public static final BooleanFlag FOLDER_NAME_SUGGEST = new DeviceFlag(
+ "FOLDER_NAME_SUGGEST", true,
+ "Suggests folder names instead of blank text.");
public static final BooleanFlag APP_SEARCH_IMPROVEMENTS = new DeviceFlag(
"APP_SEARCH_IMPROVEMENTS", false,
@@ -152,6 +155,10 @@
"ALWAYS_USE_HARDWARE_OPTIMIZATION_FOR_FOLDER_ANIMATIONS", false,
"Always use hardware optimization for folder animations.");
+ public static final BooleanFlag ENABLE_FIXED_ROTATION_TRANSFORM = getDebugFlag(
+ FLAG_ENABLE_FIXED_ROTATION_TRANSFORM, true,
+ "Launch/close apps without rotation animation. Fix Launcher to portrait");
+
public static void initialize(Context context) {
synchronized (sDebugFlags) {
for (DebugFlag flag : sDebugFlags) {
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index 92f35e2..369bf28 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -119,6 +119,7 @@
recreateControllers();
}
+ @Override
public void recreateControllers() {
mControllers = mActivity.createTouchControllers();
}
diff --git a/src/com/android/launcher3/dragndrop/PinItemDragListener.java b/src/com/android/launcher3/dragndrop/PinItemDragListener.java
index 869dd94..77c6306 100644
--- a/src/com/android/launcher3/dragndrop/PinItemDragListener.java
+++ b/src/com/android/launcher3/dragndrop/PinItemDragListener.java
@@ -16,6 +16,8 @@
package com.android.launcher3.dragndrop;
+import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
+
import android.annotation.TargetApi;
import android.appwidget.AppWidgetManager;
import android.content.pm.LauncherApps.PinItemRequest;
@@ -38,6 +40,8 @@
import com.android.launcher3.widget.PendingItemDragHelper;
import com.android.launcher3.widget.WidgetAddFlowHandler;
+import java.util.ArrayList;
+
/**
* {@link DragSource} for handling drop from a different window. This object is initialized
* in the source window and is passed on to the Launcher activity as an Intent extra.
@@ -103,9 +107,9 @@
}
@Override
- public void fillInLogContainerData(View v, ItemInfo info, LauncherLogProto.Target target,
- LauncherLogProto.Target targetParent) {
- targetParent.containerType = LauncherLogProto.ContainerType.PINITEM;
+ public void fillInLogContainerData(ItemInfo childInfo, LauncherLogProto.Target child,
+ ArrayList<LauncherLogProto.Target> parents) {
+ parents.add(newContainerTarget(LauncherLogProto.ContainerType.PINITEM));
}
@Override
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 69f93de..2be8ff4 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -27,6 +27,7 @@
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
import static com.android.launcher3.config.FeatureFlags.ALWAYS_USE_HARDWARE_OPTIMIZATION_FOR_FOLDER_ANIMATIONS;
+import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_CUSTOM;
import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_EMPTY;
import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_FOLDER_LABEL_STATE_UNSPECIFIED;
@@ -89,7 +90,6 @@
import com.android.launcher3.dragndrop.DragController.DragListener;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragOptions;
-import com.android.launcher3.logging.LoggerUtils;
import com.android.launcher3.pageindicators.PageIndicatorDots;
import com.android.launcher3.userevent.LauncherLogProto.Action;
import com.android.launcher3.userevent.LauncherLogProto.ContainerType;
@@ -1459,12 +1459,24 @@
}
@Override
- public void fillInLogContainerData(View v, ItemInfo info, LauncherLogProto.Target target,
- LauncherLogProto.Target targetParent) {
- target.gridX = info.cellX;
- target.gridY = info.cellY;
- target.pageIndex = mContent.getCurrentPage();
- targetParent.containerType = LauncherLogProto.ContainerType.FOLDER;
+ public void fillInLogContainerData(ItemInfo childInfo, LauncherLogProto.Target child,
+ ArrayList<LauncherLogProto.Target> targets) {
+ child.gridX = childInfo.cellX;
+ child.gridY = childInfo.cellY;
+ child.pageIndex = mContent.getCurrentPage();
+
+ LauncherLogProto.Target target = newContainerTarget(LauncherLogProto.ContainerType.FOLDER);
+ target.pageIndex = mInfo.screenId;
+ target.gridX = mInfo.cellX;
+ target.gridY = mInfo.cellY;
+ targets.add(target);
+
+ // continue to parent
+ if (mInfo.container == CONTAINER_HOTSEAT) {
+ mLauncher.getHotseat().fillInLogContainerData(mInfo, target, targets);
+ } else {
+ mLauncher.getWorkspace().fillInLogContainerData(mInfo, target, targets);
+ }
}
private class OnScrollHintListener implements OnAlarmListener {
@@ -1597,7 +1609,7 @@
}
} else {
mLauncher.getUserEventDispatcher().logActionTapOutside(
- LoggerUtils.newContainerTarget(LauncherLogProto.ContainerType.FOLDER));
+ newContainerTarget(LauncherLogProto.ContainerType.FOLDER));
close(true);
return true;
}
diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java
index 1310d37..f72e674 100644
--- a/src/com/android/launcher3/folder/FolderAnimationManager.java
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -21,6 +21,7 @@
import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
import static com.android.launcher3.graphics.IconShape.getShape;
import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
+import static com.android.launcher3.uioverrides.BackgroundBlurController.BACKGROUND_BLUR;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -46,6 +47,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.PropertyResetListener;
import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.uioverrides.BackgroundBlurController;
import com.android.launcher3.util.Themes;
import java.util.List;
@@ -220,6 +222,13 @@
Animator z = getAnimator(mFolder, View.TRANSLATION_Z, -mFolder.getElevation(), 0);
play(a, z, mIsOpening ? midDuration : 0, midDuration);
+ BackgroundBlurController blurController = mLauncher.getBackgroundBlurController();
+ int stateBackgroundBlur = mLauncher.getStateManager().getState()
+ .getBackgroundBlurRadius(mLauncher);
+ int folderBackgroundBlurAdjustment = blurController.getFolderBackgroundBlurAdjustment();
+ play(a, ObjectAnimator.ofInt(blurController, BACKGROUND_BLUR, mIsOpening
+ ? stateBackgroundBlur + folderBackgroundBlurAdjustment
+ : stateBackgroundBlur));
// Store clip variables
CellLayout cellLayout = mContent.getCurrentCellLayout();
diff --git a/src/com/android/launcher3/folder/FolderNameProvider.java b/src/com/android/launcher3/folder/FolderNameProvider.java
index 184dbb9..07161da 100644
--- a/src/com/android/launcher3/folder/FolderNameProvider.java
+++ b/src/com/android/launcher3/folder/FolderNameProvider.java
@@ -15,8 +15,10 @@
*/
package com.android.launcher3.folder;
+import android.content.ComponentName;
import android.content.Context;
import android.os.Process;
+import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
@@ -34,12 +36,9 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-import java.util.Map;
import java.util.Objects;
import java.util.Optional;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.function.Function;
-import java.util.function.Predicate;
+import java.util.Set;
import java.util.stream.Collectors;
/**
@@ -101,27 +100,23 @@
}
// If all the icons are from work profile,
// Then, suggest "Work" as the folder name
- List<WorkspaceItemInfo> distinctItemInfos = workspaceItemInfos.stream()
- .filter(distinctByKey(p -> p.user))
- .collect(Collectors.toList());
-
- if (distinctItemInfos.size() == 1
- && !distinctItemInfos.get(0).user.equals(Process.myUserHandle())) {
- // Place it as last viable suggestion
+ Set<UserHandle> users = workspaceItemInfos.stream().map(w -> w.user)
+ .collect(Collectors.toSet());
+ if (users.size() == 1 && !users.contains(Process.myUserHandle())) {
setAsLastSuggestion(nameInfos,
context.getResources().getString(R.string.work_folder_name));
}
// If all the icons are from same package (e.g., main icon, shortcut, shortcut)
// Then, suggest the package's title as the folder name
- distinctItemInfos = workspaceItemInfos.stream()
- .filter(distinctByKey(p -> p.getTargetComponent() != null
- ? p.getTargetComponent().getPackageName() : ""))
- .collect(Collectors.toList());
+ Set<String> packageNames = workspaceItemInfos.stream()
+ .map(WorkspaceItemInfo::getTargetComponent)
+ .filter(Objects::nonNull)
+ .map(ComponentName::getPackageName)
+ .collect(Collectors.toSet());
- if (distinctItemInfos.size() == 1) {
- Optional<AppInfo> info = getAppInfoByPackageName(
- distinctItemInfos.get(0).getTargetComponent().getPackageName());
+ if (packageNames.size() == 1) {
+ Optional<AppInfo> info = getAppInfoByPackageName(packageNames.iterator().next());
// Place it as first viable suggestion and shift everything else
info.ifPresent(i -> setAsFirstSuggestion(nameInfos, i.title.toString()));
}
@@ -135,6 +130,7 @@
return Optional.empty();
}
return mAppInfos.stream()
+ .filter(info -> info.componentName != null)
.filter(info -> info.componentName.getPackageName().equals(packageName))
.findAny();
}
@@ -174,12 +170,6 @@
label.toString()));
}
- // This method can be moved to some Utility class location.
- private static <T> Predicate<T> distinctByKey(Function<? super T, Object> keyExtractor) {
- Map<Object, Boolean> map = new ConcurrentHashMap<>();
- return t -> map.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
- }
-
private class FolderNameWorker extends BaseModelUpdateTask {
@Override
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
diff --git a/src/com/android/launcher3/graphics/Scrim.java b/src/com/android/launcher3/graphics/Scrim.java
index 67b2b6d..f90962d 100644
--- a/src/com/android/launcher3/graphics/Scrim.java
+++ b/src/com/android/launcher3/graphics/Scrim.java
@@ -19,7 +19,7 @@
import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
import android.graphics.Canvas;
-import android.util.Property;
+import android.util.FloatProperty;
import android.view.View;
import com.android.launcher3.Launcher;
@@ -31,16 +31,16 @@
public class Scrim implements View.OnAttachStateChangeListener,
WallpaperColorInfo.OnChangeListener {
- public static final Property<Scrim, Float> SCRIM_PROGRESS =
- new Property<Scrim, Float>(Float.TYPE, "scrimProgress") {
+ public static final FloatProperty<Scrim> SCRIM_PROGRESS =
+ new FloatProperty<Scrim>("scrimProgress") {
@Override
public Float get(Scrim scrim) {
return scrim.mScrimProgress;
}
@Override
- public void set(Scrim scrim, Float value) {
- scrim.setScrimProgress(value);
+ public void setValue(Scrim scrim, float v) {
+ scrim.setScrimProgress(v);
}
};
diff --git a/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java b/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java
index 5a1dcab..83349bc 100644
--- a/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java
+++ b/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java
@@ -37,7 +37,7 @@
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import android.util.DisplayMetrics;
-import android.util.Property;
+import android.util.FloatProperty;
import android.view.View;
import androidx.core.graphics.ColorUtils;
@@ -54,28 +54,28 @@
*/
public class WorkspaceAndHotseatScrim extends Scrim {
- public static final Property<WorkspaceAndHotseatScrim, Float> SYSUI_PROGRESS =
- new Property<WorkspaceAndHotseatScrim, Float>(Float.TYPE, "sysUiProgress") {
+ public static final FloatProperty<WorkspaceAndHotseatScrim> SYSUI_PROGRESS =
+ new FloatProperty<WorkspaceAndHotseatScrim>("sysUiProgress") {
@Override
public Float get(WorkspaceAndHotseatScrim scrim) {
return scrim.mSysUiProgress;
}
@Override
- public void set(WorkspaceAndHotseatScrim scrim, Float value) {
+ public void setValue(WorkspaceAndHotseatScrim scrim, float value) {
scrim.setSysUiProgress(value);
}
};
- private static final Property<WorkspaceAndHotseatScrim, Float> SYSUI_ANIM_MULTIPLIER =
- new Property<WorkspaceAndHotseatScrim, Float>(Float.TYPE, "sysUiAnimMultiplier") {
+ private static final FloatProperty<WorkspaceAndHotseatScrim> SYSUI_ANIM_MULTIPLIER =
+ new FloatProperty<WorkspaceAndHotseatScrim>("sysUiAnimMultiplier") {
@Override
public Float get(WorkspaceAndHotseatScrim scrim) {
return scrim.mSysUiAnimMultiplier;
}
@Override
- public void set(WorkspaceAndHotseatScrim scrim, Float value) {
+ public void setValue(WorkspaceAndHotseatScrim scrim, float value) {
scrim.mSysUiAnimMultiplier = value;
scrim.reapplySysUiAlpha();
}
diff --git a/src/com/android/launcher3/logging/LoggerUtils.java b/src/com/android/launcher3/logging/LoggerUtils.java
index b004edf..a9d10d7 100644
--- a/src/com/android/launcher3/logging/LoggerUtils.java
+++ b/src/com/android/launcher3/logging/LoggerUtils.java
@@ -37,6 +37,7 @@
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
+import java.util.ArrayList;
/**
* Helper methods for logging.
@@ -255,4 +256,13 @@
event.action = action;
return event;
}
+
+ /**
+ * Creates LauncherEvent using Action and ArrayList of Targets
+ */
+ public static LauncherEvent newLauncherEvent(Action action, ArrayList<Target> targets) {
+ Target[] targetsArray = new Target[targets.size()];
+ targets.toArray(targetsArray);
+ return newLauncherEvent(action, targetsArray);
+ }
}
diff --git a/src/com/android/launcher3/logging/StatsLogUtils.java b/src/com/android/launcher3/logging/StatsLogUtils.java
index b02a050..8449612 100644
--- a/src/com/android/launcher3/logging/StatsLogUtils.java
+++ b/src/com/android/launcher3/logging/StatsLogUtils.java
@@ -5,11 +5,13 @@
import android.view.View;
import android.view.ViewParent;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.ItemInfo;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
-import androidx.annotation.Nullable;
+import java.util.ArrayList;
public class StatsLogUtils {
@@ -35,14 +37,9 @@
public interface LogContainerProvider {
/**
- * Copies data from the source to the destination proto.
- *
- * @param v source of the data
- * @param info source of the data
- * @param target dest of the data
- * @param targetParent dest of the data
+ * Populates parent container targets for an item
*/
- void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent);
+ void fillInLogContainerData(ItemInfo childInfo, Target child, ArrayList<Target> parents);
}
/**
diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java
index afa3f6d..3770d17 100644
--- a/src/com/android/launcher3/logging/UserEventDispatcher.java
+++ b/src/com/android/launcher3/logging/UserEventDispatcher.java
@@ -25,6 +25,9 @@
import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent;
import static com.android.launcher3.logging.LoggerUtils.newTarget;
import static com.android.launcher3.logging.LoggerUtils.newTouchAction;
+import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
+import static com.android.launcher3.userevent.nano.LauncherLogProto.ItemType;
+import static com.android.launcher3.userevent.nano.LauncherLogProto.TipType;
import static java.util.Optional.ofNullable;
@@ -48,7 +51,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.userevent.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.LauncherEvent;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
@@ -57,7 +60,11 @@
import com.android.launcher3.util.LogConfig;
import com.android.launcher3.util.ResourceBasedOverride;
-import java.util.Locale;
+import com.google.protobuf.InvalidProtocolBufferException;
+import com.google.protobuf.nano.InvalidProtocolBufferNanoException;
+import com.google.protobuf.nano.MessageNano;
+
+import java.util.ArrayList;
import java.util.UUID;
/**
@@ -72,8 +79,10 @@
private static final boolean IS_VERBOSE = Utilities.isPropertyEnabled(LogConfig.USEREVENT);
private static final String UUID_STORAGE = "uuid";
- public static UserEventDispatcher newInstance(Context context,
- UserEventDelegate delegate) {
+ /**
+ * A factory method for UserEventDispatcher
+ */
+ public static UserEventDispatcher newInstance(Context context) {
SharedPreferences sharedPrefs = Utilities.getDevicePrefs(context);
String uuidStr = sharedPrefs.getString(UUID_STORAGE, null);
if (uuidStr == null) {
@@ -82,41 +91,31 @@
}
UserEventDispatcher ued = Overrides.getObject(UserEventDispatcher.class,
context.getApplicationContext(), R.string.user_event_dispatcher_class);
- ued.mDelegate = delegate;
ued.mUuidStr = uuidStr;
ued.mInstantAppResolver = InstantAppResolver.newInstance(context);
return ued;
}
- public static UserEventDispatcher newInstance(Context context) {
- return newInstance(context, null);
- }
-
- public interface UserEventDelegate {
- void modifyUserEvent(LauncherEvent event);
- }
/**
* Fills in the container data on the given event if the given view is not null.
*
* @return whether container data was added.
*/
- public boolean fillInLogContainerData(LauncherLogProto.LauncherEvent event, @Nullable View v) {
- // Fill in grid(x,y), pageIndex of the child and container type of the parent
- LogContainerProvider provider = StatsLogUtils.getLaunchProviderRecursive(v);
- if (v == null || !(v.getTag() instanceof ItemInfo) || provider == null) {
+ public boolean fillLogContainer(@Nullable View v, Target child,
+ @Nullable ArrayList<Target> targets) {
+ LogContainerProvider firstParent = StatsLogUtils.getLaunchProviderRecursive(v);
+ if (v == null || !(v.getTag() instanceof ItemInfo) || firstParent == null) {
return false;
}
final ItemInfo itemInfo = (ItemInfo) v.getTag();
- final Target target = event.srcTarget[0];
- final Target targetParent = event.srcTarget[1];
- onFillInLogContainerData(itemInfo, target, targetParent);
- provider.fillInLogContainerData(v, itemInfo, target, targetParent);
+ firstParent.fillInLogContainerData(itemInfo, child, targets);
return true;
}
- protected void onFillInLogContainerData(
- @NonNull ItemInfo itemInfo, @NonNull Target target, @NonNull Target targetParent) { }
+ protected void onFillInLogContainerData(@NonNull ItemInfo itemInfo, @NonNull Target target,
+ @NonNull ArrayList<Target> targets) {
+ }
private boolean mSessionStarted;
private long mElapsedContainerMillis;
@@ -125,7 +124,6 @@
private String mUuidStr;
protected InstantAppResolver mInstantAppResolver;
private boolean mAppOrTaskLaunch;
- private UserEventDelegate mDelegate;
private boolean mPreviousHomeGesture;
// APP_ICON SHORTCUT WIDGET
@@ -136,16 +134,15 @@
// --------------------------------------------------------------
@Deprecated
- public void logAppLaunch(View v, Intent intent, @Nullable UserHandle userHandle) {
- LauncherEvent event = newLauncherEvent(newTouchAction(Action.Touch.TAP),
- newItemTarget(v, mInstantAppResolver), newTarget(Target.Type.CONTAINER));
-
- if (fillInLogContainerData(event, v)) {
- if (mDelegate != null) {
- mDelegate.modifyUserEvent(event);
- }
- fillIntentInfo(event.srcTarget[0], intent, userHandle);
+ public void logAppLaunch(View v, Intent intent, @Nullable UserHandle userHandle) {
+ Target itemTarget = newItemTarget(v, mInstantAppResolver);
+ Action action = newTouchAction(Action.Touch.TAP);
+ ArrayList<Target> targets = makeTargetsList(itemTarget);
+ if (fillLogContainer(v, itemTarget, targets)) {
+ onFillInLogContainerData((ItemInfo) v.getTag(), itemTarget, targets);
+ fillIntentInfo(itemTarget, intent, userHandle);
}
+ LauncherEvent event = newLauncherEvent(action, targets);
ItemInfo info = (ItemInfo) v.getTag();
if (info != null && Utilities.IS_DEBUG_DEVICE && FeatureFlags.ENABLE_HYBRID_HOTSEAT.get()) {
FileLog.d(TAG, "appLaunch: packageName:" + info.getTargetComponent().getPackageName()
@@ -171,7 +168,7 @@
// Direction DOWN means the task was launched, UP means it was dismissed.
event.action.dir = direction;
}
- event.srcTarget[0].itemType = LauncherLogProto.ItemType.TASK;
+ event.srcTarget[0].itemType = ItemType.TASK;
event.srcTarget[0].pageIndex = taskIndex;
fillComponentInfo(event.srcTarget[0], componentKey.componentName);
dispatchUserEvent(event, null);
@@ -194,8 +191,11 @@
public void logNotificationLaunch(View v, PendingIntent intent) {
LauncherEvent event = newLauncherEvent(newTouchAction(Action.Touch.TAP),
newItemTarget(v, mInstantAppResolver), newTarget(Target.Type.CONTAINER));
- if (fillInLogContainerData(event, v)) {
- event.srcTarget[0].packageNameHash = (mUuidStr + intent.getCreatorPackage()).hashCode();
+ Target itemTarget = newItemTarget(v, mInstantAppResolver);
+ ArrayList<Target> targets = makeTargetsList(itemTarget);
+
+ if (fillLogContainer(v, itemTarget, targets)) {
+ itemTarget.packageNameHash = (mUuidStr + intent.getCreatorPackage()).hashCode();
}
dispatchUserEvent(event, null);
}
@@ -241,50 +241,45 @@
LauncherEvent event = newLauncherEvent(newCommandAction(command),
newItemTarget(itemView, mInstantAppResolver), newTarget(Target.Type.CONTAINER));
- if (fillInLogContainerData(event, itemView)) {
+ Target itemTarget = newItemTarget(itemView, mInstantAppResolver);
+ ArrayList<Target> targets = makeTargetsList(itemTarget);
+
+ if (fillLogContainer(itemView, itemTarget, targets)) {
// TODO: Remove the following two lines once fillInLogContainerData can take in a
// container view.
- event.srcTarget[0].type = Target.Type.CONTAINER;
- event.srcTarget[0].containerType = srcContainerType;
+ itemTarget.type = Target.Type.CONTAINER;
+ itemTarget.containerType = srcContainerType;
}
dispatchUserEvent(event, null);
}
public void logActionOnControl(int action, int controlType) {
- logActionOnControl(action, controlType, null, -1);
+ logActionOnControl(action, controlType, null);
}
public void logActionOnControl(int action, int controlType, int parentContainerType) {
logActionOnControl(action, controlType, null, parentContainerType);
}
- public void logActionOnControl(int action, int controlType, @Nullable View controlInContainer) {
- logActionOnControl(action, controlType, controlInContainer, -1);
- }
+ /**
+ * Logs control action with proper parent hierarchy
+ */
+ public void logActionOnControl(int actionType, int controlType,
+ @Nullable View controlInContainer, int... parentTypes) {
+ Target control = newTarget(Target.Type.CONTROL);
+ control.controlType = controlType;
+ Action action = newAction(actionType);
- public void logActionOnControl(int action, int controlType, int parentContainer,
- int grandParentContainer) {
- LauncherEvent event = newLauncherEvent(newTouchAction(action),
- newControlTarget(controlType),
- newContainerTarget(parentContainer),
- newContainerTarget(grandParentContainer));
- dispatchUserEvent(event, null);
- }
-
- public void logActionOnControl(int action, int controlType, @Nullable View controlInContainer,
- int parentContainerType) {
- final LauncherEvent event = (controlInContainer == null && parentContainerType < 0)
- ? newLauncherEvent(newTouchAction(action), newTarget(Target.Type.CONTROL))
- : newLauncherEvent(newTouchAction(action), newTarget(Target.Type.CONTROL),
- newTarget(Target.Type.CONTAINER));
- event.srcTarget[0].controlType = controlType;
+ ArrayList<Target> targets = makeTargetsList(control);
if (controlInContainer != null) {
- fillInLogContainerData(event, controlInContainer);
+ fillLogContainer(controlInContainer, control, targets);
}
- if (parentContainerType >= 0) {
- event.srcTarget[1].containerType = parentContainerType;
+ for (int parentContainerType : parentTypes) {
+ if (parentContainerType < 0) continue;
+ targets.add(newContainerTarget(parentContainerType));
}
- if (action == Action.Touch.DRAGDROP) {
+ LauncherEvent event = newLauncherEvent(action, targets);
+ if (actionType == Action.Touch.DRAGDROP) {
event.actionDurationMillis = SystemClock.uptimeMillis() - mActionDurationMillis;
}
dispatchUserEvent(event, null);
@@ -300,7 +295,7 @@
public void logActionBounceTip(int containerType) {
LauncherEvent event = newLauncherEvent(newAction(Action.Type.TIP),
newContainerTarget(containerType));
- event.srcTarget[0].tipType = LauncherLogProto.TipType.BOUNCE;
+ event.srcTarget[0].tipType = TipType.BOUNCE;
dispatchUserEvent(event, null);
}
@@ -327,7 +322,7 @@
int srcChildTargetType, int srcParentContainerType, int dstContainerType,
int pageIndex) {
LauncherEvent event;
- if (srcChildTargetType == LauncherLogProto.ItemType.TASK) {
+ if (srcChildTargetType == ItemType.TASK) {
event = newLauncherEvent(newTouchAction(action),
newItemTarget(srcChildTargetType),
newContainerTarget(srcParentContainerType));
@@ -375,52 +370,53 @@
* Logs proto lite version of LauncherEvent object to clearcut.
*/
public void logLauncherEvent(
- com.android.launcher3.userevent.LauncherLogProto.LauncherEvent launcherEvent) {
+ com.android.launcher3.userevent.LauncherLogProto.LauncherEvent launcherEvent) {
if (mPreviousHomeGesture) {
mPreviousHomeGesture = false;
}
mAppOrTaskLaunch = false;
launcherEvent.toBuilder()
- .setElapsedContainerMillis(SystemClock.uptimeMillis() - mElapsedContainerMillis)
- .setElapsedSessionMillis(SystemClock.uptimeMillis() - mElapsedSessionMillis).build();
- if (!IS_VERBOSE) {
- return;
+ .setElapsedContainerMillis(SystemClock.uptimeMillis() - mElapsedContainerMillis)
+ .setElapsedSessionMillis(
+ SystemClock.uptimeMillis() - mElapsedSessionMillis).build();
+ try {
+ dispatchUserEvent(LauncherEvent.parseFrom(launcherEvent.toByteArray()), null);
+ } catch (InvalidProtocolBufferNanoException e) {
+ throw new RuntimeException("Cannot convert LauncherEvent from Lite to Nano version.");
}
- Log.d(TAG, launcherEvent.toString());
}
public void logDeepShortcutsOpen(View icon) {
- LogContainerProvider provider = StatsLogUtils.getLaunchProviderRecursive(icon);
- if (icon == null || !(icon.getTag() instanceof ItemInfo || provider == null)) {
- return;
- }
ItemInfo info = (ItemInfo) icon.getTag();
- LauncherEvent event = newLauncherEvent(newTouchAction(Action.Touch.LONGPRESS),
- newItemTarget(info, mInstantAppResolver), newTarget(Target.Type.CONTAINER));
- provider.fillInLogContainerData(icon, info, event.srcTarget[0], event.srcTarget[1]);
- dispatchUserEvent(event, null);
-
+ Target child = newItemTarget(info, mInstantAppResolver);
+ ArrayList<Target> targets = makeTargetsList(child);
+ fillLogContainer(icon, child, targets);
+ dispatchUserEvent(newLauncherEvent(newTouchAction(Action.Touch.TAP), targets), null);
resetElapsedContainerMillis("deep shortcut open");
}
public void logDragNDrop(DropTarget.DragObject dragObj, View dropTargetAsView) {
- LauncherEvent event = newLauncherEvent(newTouchAction(Action.Touch.DRAGDROP),
- newItemTarget(dragObj.originalDragInfo, mInstantAppResolver),
- newTarget(Target.Type.CONTAINER));
- event.destTarget = new Target[]{
- newItemTarget(dragObj.originalDragInfo, mInstantAppResolver),
- newDropTarget(dropTargetAsView)
- };
+ Target srcChild = newItemTarget(dragObj.originalDragInfo, mInstantAppResolver);
+ ArrayList<Target> srcTargets = makeTargetsList(srcChild);
- dragObj.dragSource.fillInLogContainerData(null, dragObj.originalDragInfo,
- event.srcTarget[0], event.srcTarget[1]);
+ Target destChild = newItemTarget(dragObj.originalDragInfo, mInstantAppResolver);
+ ArrayList<Target> destTargets = makeTargetsList(destChild);
+
+ dragObj.dragSource.fillInLogContainerData(dragObj.originalDragInfo, srcChild, srcTargets);
if (dropTargetAsView instanceof LogContainerProvider) {
- ((LogContainerProvider) dropTargetAsView).fillInLogContainerData(null,
- dragObj.dragInfo, event.destTarget[0], event.destTarget[1]);
-
+ ((LogContainerProvider) dropTargetAsView).fillInLogContainerData(dragObj.dragInfo,
+ destChild, destTargets);
}
+ else {
+ destTargets.add(newDropTarget(dropTargetAsView));
+ }
+ LauncherEvent event = newLauncherEvent(newTouchAction(Action.Touch.DRAGDROP), srcTargets);
+ Target[] destTargetsArray = new Target[destTargets.size()];
+ destTargets.toArray(destTargetsArray);
+ event.destTarget = destTargetsArray;
+
event.actionDurationMillis = SystemClock.uptimeMillis() - mActionDurationMillis;
dispatchUserEvent(event, null);
}
@@ -432,8 +428,8 @@
action.command = Action.Command.BACK;
action.dir = isButton ? Action.Direction.NONE :
gestureSwipeLeft ? Action.Direction.LEFT : Action.Direction.RIGHT;
- Target target = newControlTarget(isButton ? LauncherLogProto.ControlType.BACK_BUTTON :
- LauncherLogProto.ControlType.BACK_GESTURE);
+ Target target = newControlTarget(isButton ? ControlType.BACK_BUTTON :
+ ControlType.BACK_GESTURE);
target.spanX = downX;
target.spanY = downY;
target.cardinality = completed ? 1 : 0;
@@ -444,8 +440,6 @@
/**
* Currently logs following containers: workspace, allapps, widget tray.
- *
- * @param reason
*/
public final void resetElapsedContainerMillis(String reason) {
mElapsedContainerMillis = SystemClock.uptimeMillis();
@@ -484,34 +478,23 @@
if (!IS_VERBOSE) {
return;
}
- Log.d(TAG, generateLog(ev));
+ LauncherLogProto.LauncherEvent liteLauncherEvent;
+ try {
+ liteLauncherEvent =
+ LauncherLogProto.LauncherEvent.parseFrom(MessageNano.toByteArray(ev));
+ } catch (InvalidProtocolBufferException e) {
+ throw new RuntimeException("Cannot parse LauncherEvent from Nano to Lite version");
+ }
+ Log.d(TAG, liteLauncherEvent.toString());
}
/**
- * Returns a human-readable log for given user event.
+ * Constructs an ArrayList with targets
*/
- public static String generateLog(LauncherEvent ev) {
- String log = "\n-----------------------------------------------------"
- + "\naction:" + LoggerUtils.getActionStr(ev.action);
- if (ev.srcTarget != null && ev.srcTarget.length > 0) {
- log += "\n Source " + getTargetsStr(ev.srcTarget);
- }
- if (ev.destTarget != null && ev.destTarget.length > 0) {
- log += "\n Destination " + getTargetsStr(ev.destTarget);
- }
- log += String.format(Locale.US,
- "\n Elapsed container %d ms, session %d ms, action %d ms",
- ev.elapsedContainerMillis,
- ev.elapsedSessionMillis,
- ev.actionDurationMillis);
- log += "\n\n";
- return log;
- }
-
- private static String getTargetsStr(Target[] targets) {
- String result = "child:" + LoggerUtils.getTargetStr(targets[0]);
- for (int i = 1; i < targets.length; i++) {
- result += "\tparent:" + LoggerUtils.getTargetStr(targets[i]);
+ public static ArrayList<Target> makeTargetsList(Target... targets) {
+ ArrayList<Target> result = new ArrayList<>();
+ for (Target target : targets) {
+ result.add(target);
}
return result;
}
diff --git a/src/com/android/launcher3/model/PagedViewOrientedState.java b/src/com/android/launcher3/model/PagedViewOrientedState.java
index 1349eff..e48b8c1 100644
--- a/src/com/android/launcher3/model/PagedViewOrientedState.java
+++ b/src/com/android/launcher3/model/PagedViewOrientedState.java
@@ -50,7 +50,16 @@
*/
private boolean mDisableMultipleOrientations;
+ /**
+ * Sets the appropriate {@link PagedOrientationHandler} for {@link #mOrientationHandler}
+ * @param touchRotation The rotation the nav bar region that is touched is in
+ * @param displayRotation Rotation of the display/device
+ */
public void update(int touchRotation, int displayRotation) {
+ if (mDisableMultipleOrientations) {
+ return;
+ }
+
mDisplayRotation = displayRotation;
mTouchRotation = touchRotation;
if (mTouchRotation == Surface.ROTATION_90) {
@@ -62,20 +71,13 @@
}
}
- /**
- * @return {@code true} if the area where the user touched the nav bar is the expected
- * location for the given display rotation. Ex. bottom of phone in portrait, or left side of
- * phone in landscape, right side in seascape, etc.
- * False otherwise
- */
- public boolean isTouchRegionNaturalForDisplay() {
- return mTouchRotation == mDisplayRotation;
- }
-
public boolean areMultipleLayoutOrientationsDisabled() {
return mDisableMultipleOrientations;
}
+ /**
+ * Setting this preference will render future calls to {@link #update(int, int)} as a no-op.
+ */
public void disableMultipleOrientations(boolean disable) {
mDisableMultipleOrientations = disable;
if (disable) {
diff --git a/src/com/android/launcher3/popup/ArrowPopup.java b/src/com/android/launcher3/popup/ArrowPopup.java
index 1c2acfd..18bc55a 100644
--- a/src/com/android/launcher3/popup/ArrowPopup.java
+++ b/src/com/android/launcher3/popup/ArrowPopup.java
@@ -240,6 +240,17 @@
* and align above if there is enough vertical space.
*/
protected void orientAboutObject() {
+ orientAboutObject(true /* allowAlignLeft */, true /* allowAlignRight */);
+ }
+
+ /**
+ * @see #orientAboutObject()
+ *
+ * @param allowAlignLeft Set to false if we already tried aligning left and didn't have room.
+ * @param allowAlignRight Set to false if we already tried aligning right and didn't have room.
+ * TODO: Can we test this with all permutations of widths/heights and icon locations + RTL?
+ */
+ private void orientAboutObject(boolean allowAlignLeft, boolean allowAlignRight) {
measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
int width = getMeasuredWidth();
int extraVerticalSpace = mArrow.getLayoutParams().height + mArrowOffset
@@ -253,14 +264,8 @@
// Align left (right in RTL) if there is room.
int leftAlignedX = mTempRect.left;
int rightAlignedX = mTempRect.right - width;
- int x = leftAlignedX;
- boolean canBeLeftAligned = leftAlignedX + width + insets.left
- < dragLayer.getRight() - insets.right;
- boolean canBeRightAligned = rightAlignedX > dragLayer.getLeft() + insets.left;
- if (!canBeLeftAligned || (mIsRtl && canBeRightAligned)) {
- x = rightAlignedX;
- }
- mIsLeftAligned = x == leftAlignedX;
+ mIsLeftAligned = !mIsRtl ? allowAlignLeft : !allowAlignRight;
+ int x = mIsLeftAligned ? leftAlignedX : rightAlignedX;
// Offset x so that the arrow and shortcut icons are center-aligned with the original icon.
int iconWidth = mTempRect.width();
@@ -282,6 +287,24 @@
}
x += mIsLeftAligned ? xOffset : -xOffset;
+ // Check whether we can still align as we originally wanted, now that we've calculated x.
+ if (!allowAlignLeft && !allowAlignRight) {
+ // We've already tried both ways and couldn't make it fit. onLayout() will set the
+ // gravity to CENTER_HORIZONTAL, but continue below to update y.
+ } else {
+ boolean canBeLeftAligned = x + width + insets.left
+ < dragLayer.getRight() - insets.right;
+ boolean canBeRightAligned = x > dragLayer.getLeft() + insets.left;
+ boolean alignmentStillValid = mIsLeftAligned && canBeLeftAligned
+ || !mIsLeftAligned && canBeRightAligned;
+ if (!alignmentStillValid) {
+ // Try again, but don't allow this alignment we already know won't work.
+ orientAboutObject(allowAlignLeft && !mIsLeftAligned /* allowAlignLeft */,
+ allowAlignRight && mIsLeftAligned /* allowAlignRight */);
+ return;
+ }
+ }
+
// Open above icon if there is room.
int iconHeight = mTempRect.height();
int y = mTempRect.top - height;
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index 445acca..5af5ebb 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -18,6 +18,7 @@
import static com.android.launcher3.Utilities.squaredHypot;
import static com.android.launcher3.Utilities.squaredTouchSlop;
+import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
import static com.android.launcher3.notification.NotificationMainView.NOTIFICATION_ITEM_INFO;
import static com.android.launcher3.popup.PopupPopulator.MAX_SHORTCUTS;
import static com.android.launcher3.popup.PopupPopulator.MAX_SHORTCUTS_IF_NOTIFICATIONS;
@@ -58,7 +59,6 @@
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DragView;
-import com.android.launcher3.logging.LoggerUtils;
import com.android.launcher3.notification.NotificationInfo;
import com.android.launcher3.notification.NotificationItemView;
import com.android.launcher3.notification.NotificationKeyData;
@@ -172,7 +172,7 @@
BaseDragLayer dl = getPopupContainer();
if (!dl.isEventOverView(this, ev)) {
mLauncher.getUserEventDispatcher().logActionTapOutside(
- LoggerUtils.newContainerTarget(ContainerType.DEEPSHORTCUTS));
+ newContainerTarget(ContainerType.DEEPSHORTCUTS));
close(true);
// We let touches on the original icon go through so that users can launch
@@ -485,14 +485,15 @@
}
@Override
- public void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent) {
- if (info == NOTIFICATION_ITEM_INFO) {
- target.itemType = ItemType.NOTIFICATION;
+ public void fillInLogContainerData(ItemInfo childInfo, Target child,
+ ArrayList<Target> parents) {
+ if (childInfo == NOTIFICATION_ITEM_INFO) {
+ child.itemType = ItemType.NOTIFICATION;
} else {
- target.itemType = ItemType.DEEPSHORTCUT;
- target.rank = info.rank;
+ child.itemType = ItemType.DEEPSHORTCUT;
+ child.rank = childInfo.rank;
}
- targetParent.containerType = ContainerType.DEEPSHORTCUTS;
+ parents.add(newContainerTarget(ContainerType.DEEPSHORTCUTS));
}
@Override
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
index 8fffee8..936d377 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
@@ -54,6 +54,11 @@
public SecondaryDragLayer(Context context, AttributeSet attrs) {
super(context, attrs, 1 /* alphaChannelCount */);
+ recreateControllers();
+ }
+
+ @Override
+ public void recreateControllers() {
mControllers = new TouchController[] {new CloseAllAppsTouchController()};
}
diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java
index 43d54eb..fae0fe2 100644
--- a/src/com/android/launcher3/states/RotationHelper.java
+++ b/src/com/android/launcher3/states/RotationHelper.java
@@ -21,13 +21,13 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE;
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.config.FeatureFlags.FLAG_ENABLE_FIXED_ROTATION_TRANSFORM;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import android.content.ContentResolver;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.res.Resources;
-import android.database.ContentObserver;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -72,26 +72,13 @@
return originalSmallestWidth >= 600;
}
-
- private final ContentObserver mContentObserver =
- new ContentObserver(MAIN_EXECUTOR.getHandler()) {
- @Override
- public void onChange(boolean selfChange) {
- boolean forcedRotation = Utilities.isForcedRotation(mLauncher);
- PagedView.sFlagForcedRotation = forcedRotation;
- updateForcedRotation();
- for (ForcedRotationChangedListener listener : mForcedRotationChangedListeners) {
- listener.onForcedRotationChanged(forcedRotation);
- }
- }
- };
-
public static final int REQUEST_NONE = 0;
public static final int REQUEST_ROTATE = 1;
public static final int REQUEST_LOCK = 2;
private final Launcher mLauncher;
- private final SharedPreferences mPrefs;
+ private final SharedPreferences mSharedPrefs;
+ private final SharedPreferences mFeatureFlagsPrefs;
private boolean mIgnoreAutoRotateSettings;
private boolean mAutoRotateEnabled;
@@ -125,24 +112,42 @@
// On large devices we do not handle auto-rotate differently.
mIgnoreAutoRotateSettings = mLauncher.getResources().getBoolean(R.bool.allow_rotation);
- updateForcedRotation();
if (!mIgnoreAutoRotateSettings) {
- mPrefs = Utilities.getPrefs(mLauncher);
- mPrefs.registerOnSharedPreferenceChangeListener(this);
- mAutoRotateEnabled = mPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY,
+ mSharedPrefs = Utilities.getPrefs(mLauncher);
+ mSharedPrefs.registerOnSharedPreferenceChangeListener(this);
+ mAutoRotateEnabled = mSharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY,
getAllowRotationDefaultValue());
} else {
- mPrefs = null;
+ mSharedPrefs = null;
}
- // TODO(b/150260456) Add this in home settings as well
mContentResolver = launcher.getContentResolver();
- mContentResolver.registerContentObserver(Settings.Global.getUriFor(
- FIXED_ROTATION_TRANSFORM_SETTING_NAME), false, mContentObserver);
+ mFeatureFlagsPrefs = Utilities.getFeatureFlagsPrefs(mLauncher);
+ mFeatureFlagsPrefs.registerOnSharedPreferenceChangeListener(this);
+ updateForcedRotation(true);
}
- private void updateForcedRotation() {
- mForcedRotation = !getAllowRotationDefaultValue() && Utilities.isForcedRotation(mLauncher);
+ /**
+ * @param setValueFromPrefs If true, then {@link #mForcedRotation} will get set to the value
+ * from the home developer settings. Otherwise it will not.
+ * This is primarily to allow tests to set their own conditions.
+ */
+ private void updateForcedRotation(boolean setValueFromPrefs) {
+ boolean isForcedRotation = mFeatureFlagsPrefs
+ .getBoolean(FLAG_ENABLE_FIXED_ROTATION_TRANSFORM, true)
+ && !getAllowRotationDefaultValue();
+ if (mForcedRotation == isForcedRotation) {
+ return;
+ }
+ if (setValueFromPrefs) {
+ mForcedRotation = isForcedRotation;
+ }
+ UI_HELPER_EXECUTOR.execute(
+ () -> Settings.Global.putInt(mContentResolver, FIXED_ROTATION_TRANSFORM_SETTING_NAME,
+ mForcedRotation ? 1 : 0));
+ for (ForcedRotationChangedListener listener : mForcedRotationChangedListeners) {
+ listener.onForcedRotationChanged(mForcedRotation);
+ }
}
/**
@@ -181,8 +186,13 @@
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) {
+ if (FLAG_ENABLE_FIXED_ROTATION_TRANSFORM.equals(s)) {
+ updateForcedRotation(true);
+ return;
+ }
+
boolean wasRotationEnabled = mAutoRotateEnabled;
- mAutoRotateEnabled = mPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY,
+ mAutoRotateEnabled = mSharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY,
getAllowRotationDefaultValue());
if (mAutoRotateEnabled != wasRotationEnabled) {
@@ -218,6 +228,10 @@
public void forceAllowRotationForTesting(boolean allowRotation) {
mIgnoreAutoRotateSettings =
allowRotation || mLauncher.getResources().getBoolean(R.bool.allow_rotation);
+ // TODO(b/150214193) Tests currently expect launcher to be able to be rotated
+ // Modify tests for this new behavior
+ mForcedRotation = !allowRotation;
+ updateForcedRotation(false);
notifyChange();
}
@@ -232,13 +246,11 @@
public void destroy() {
if (!mDestroyed) {
mDestroyed = true;
- if (mPrefs != null) {
- mPrefs.unregisterOnSharedPreferenceChangeListener(this);
- }
- if (mContentResolver != null) {
- mContentResolver.unregisterContentObserver(mContentObserver);
+ if (mSharedPrefs != null) {
+ mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this);
}
mForcedRotationChangedListeners.clear();
+ mFeatureFlagsPrefs.unregisterOnSharedPreferenceChangeListener(this);
}
}
@@ -293,7 +305,7 @@
return degrees;
}
- public static int getRotationFromDegrees(int degrees) {
+ public static int getRotationFromDegrees(float degrees) {
int threshold = 70;
if (degrees >= (360 - threshold) || degrees < (threshold)) {
return Surface.ROTATION_0;
@@ -318,11 +330,30 @@
}
/**
+ * For landscape, since the navbar is already in a vertical position, we don't have to do any
+ * rotations as the change in Y coordinate is what is read. We only flip the sign of the
+ * y coordinate to make it match existing behavior of swipe to the top to go previous
+ */
+ public static void transformEventForNavBar(MotionEvent ev, boolean inverse) {
+ // TODO(b/151269990): Use a temp matrix
+ Matrix m = new Matrix();
+ m.setScale(1, -1);
+ if (inverse) {
+ Matrix inv = new Matrix();
+ m.invert(inv);
+ ev.transform(inv);
+ } else {
+ ev.transform(m);
+ }
+ }
+
+ /**
* Creates a matrix to transform the given motion event specified by degrees.
* If {@param inverse} is {@code true}, the inverse of that matrix will be applied
*/
public static void transformEvent(float degrees, MotionEvent ev, boolean inverse) {
Matrix transform = new Matrix();
+ // TODO(b/151269990): Use a temp matrix
transform.setRotate(degrees);
if (inverse) {
Matrix inv = new Matrix();
@@ -344,6 +375,7 @@
*/
public static Matrix getRotationMatrix(int screenWidth, int screenHeight, int displayRotation) {
Matrix m = new Matrix();
+ // TODO(b/151269990): Use a temp matrix
switch (displayRotation) {
case Surface.ROTATION_0:
return m;
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index 34d69e9..c3664c3 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -19,9 +19,9 @@
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.LauncherStateManager.ANIM_ALL;
-import static com.android.launcher3.LauncherStateManager.ATOMIC_OVERVIEW_SCALE_COMPONENT;
-import static com.android.launcher3.LauncherStateManager.NON_ATOMIC_COMPONENT;
+import static com.android.launcher3.LauncherStateManager.ANIM_ALL_COMPONENTS;
+import static com.android.launcher3.LauncherStateManager.PLAY_ATOMIC_OVERVIEW_SCALE;
+import static com.android.launcher3.LauncherStateManager.PLAY_NON_ATOMIC;
import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
@@ -37,16 +37,16 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager.AnimationComponents;
+import com.android.launcher3.LauncherStateManager.AnimationFlags;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.AnimatorSetBuilder;
+import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.util.FlingBlockCheck;
-import com.android.launcher3.util.PendingAnimation;
import com.android.launcher3.util.TouchController;
/**
@@ -176,7 +176,7 @@
protected abstract LauncherState getTargetState(LauncherState fromState,
boolean isDragTowardPositive);
- protected abstract float initCurrentAnimation(@AnimationComponents int animComponents);
+ protected abstract float initCurrentAnimation(@AnimationFlags int animComponents);
/**
* Returns the container that the touch started from when leaving NORMAL state.
@@ -201,10 +201,10 @@
mCurrentAnimation.setOnCancelRunnable(null);
}
int animComponents = goingBetweenNormalAndOverview(mFromState, mToState)
- ? NON_ATOMIC_COMPONENT : ANIM_ALL;
+ ? PLAY_NON_ATOMIC : ANIM_ALL_COMPONENTS;
mScheduleResumeAtomicComponent = false;
if (mAtomicAnim != null) {
- animComponents = NON_ATOMIC_COMPONENT;
+ animComponents = PLAY_NON_ATOMIC;
// Control the non-atomic components until the atomic animation finishes, then control
// the atomic components as well.
mScheduleResumeAtomicComponent = true;
@@ -215,7 +215,7 @@
}
if (mAtomicComponentsController != null) {
- animComponents &= ~ATOMIC_OVERVIEW_SCALE_COMPONENT;
+ animComponents &= ~PLAY_ATOMIC_OVERVIEW_SCALE;
}
mProgressMultiplier = initCurrentAnimation(animComponents);
mCurrentAnimation.dispatchOnStart();
@@ -360,7 +360,7 @@
long duration) {
AnimatorSetBuilder builder = getAnimatorSetBuilderForStates(fromState, targetState);
return mLauncher.getStateManager().createAtomicAnimation(fromState, targetState, builder,
- ATOMIC_OVERVIEW_SCALE_COMPONENT, duration);
+ PLAY_ATOMIC_OVERVIEW_SCALE, duration);
}
protected AnimatorSetBuilder getAnimatorSetBuilderForStates(LauncherState fromState,
@@ -434,7 +434,7 @@
maybeUpdateAtomicAnim(mFromState, targetState, targetState == mToState ? 1f : 0f);
updateSwipeCompleteAnimation(anim, Math.max(duration, getRemainingAtomicDuration()),
targetState, velocity, fling);
- mCurrentAnimation.dispatchOnStartWithVelocity(endProgress, progressVelocity);
+ mCurrentAnimation.dispatchOnStart();
if (fling && targetState == LauncherState.ALL_APPS && !UNSTABLE_SPRINGS.get()) {
mLauncher.getAppsView().addSpringFromFlingUpdateListener(anim, velocity);
}
diff --git a/src/com/android/launcher3/touch/AllAppsSwipeController.java b/src/com/android/launcher3/touch/AllAppsSwipeController.java
index 31a5d79..8d5f33d 100644
--- a/src/com/android/launcher3/touch/AllAppsSwipeController.java
+++ b/src/com/android/launcher3/touch/AllAppsSwipeController.java
@@ -23,7 +23,7 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager.AnimationComponents;
+import com.android.launcher3.LauncherStateManager.AnimationFlags;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
/**
@@ -76,7 +76,7 @@
}
@Override
- protected float initCurrentAnimation(@AnimationComponents int animComponents) {
+ protected float initCurrentAnimation(@AnimationFlags int animComponents) {
float range = getShiftRange();
long maxAccuracy = (long) (2 * range);
mCurrentAnimation = mLauncher.getStateManager()
diff --git a/src/com/android/launcher3/util/PendingAnimation.java b/src/com/android/launcher3/util/PendingAnimation.java
deleted file mode 100644
index 617a38b..0000000
--- a/src/com/android/launcher3/util/PendingAnimation.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.util;
-
-import android.animation.AnimatorSet;
-import android.annotation.TargetApi;
-import android.os.Build;
-
-import java.util.ArrayList;
-import java.util.function.Consumer;
-
-/**
- * Utility class to keep track of a running animation.
- *
- * This class allows attaching end callbacks to an animation is intended to be used with
- * {@link com.android.launcher3.anim.AnimatorPlaybackController}, since in that case
- * AnimationListeners are not properly dispatched.
- */
-@TargetApi(Build.VERSION_CODES.O)
-public class PendingAnimation {
-
- private final ArrayList<Consumer<OnEndListener>> mEndListeners = new ArrayList<>();
-
- public final AnimatorSet anim;
-
- public PendingAnimation(AnimatorSet anim) {
- this.anim = anim;
- }
-
- public void finish(boolean isSuccess, int logAction) {
- for (Consumer<OnEndListener> listeners : mEndListeners) {
- listeners.accept(new OnEndListener(isSuccess, logAction));
- }
- mEndListeners.clear();
- }
-
- public void addEndListener(Consumer<OnEndListener> listener) {
- mEndListeners.add(listener);
- }
-
- public static class OnEndListener {
- public boolean isSuccess;
- public int logAction;
-
- public OnEndListener(boolean isSuccess, int logAction) {
- this.isSuccess = isSuccess;
- this.logAction = logAction;
- }
- }
-}
diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java
index 254655c..25748ae 100644
--- a/src/com/android/launcher3/views/BaseDragLayer.java
+++ b/src/com/android/launcher3/views/BaseDragLayer.java
@@ -40,7 +40,6 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.InsettableFrameLayout;
-import com.android.launcher3.Launcher;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
@@ -116,6 +115,11 @@
}
/**
+ * Called to reinitialize touch controllers.
+ */
+ public abstract void recreateControllers();
+
+ /**
* Same as {@link #isEventOverView(View, MotionEvent, View)} where evView == this drag layer.
*/
public boolean isEventOverView(View view, MotionEvent ev) {
diff --git a/src/com/android/launcher3/views/ScrimView.java b/src/com/android/launcher3/views/ScrimView.java
index 8ce98f2..6d204f6 100644
--- a/src/com/android/launcher3/views/ScrimView.java
+++ b/src/com/android/launcher3/views/ScrimView.java
@@ -40,7 +40,7 @@
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.AttributeSet;
-import android.util.Property;
+import android.util.IntProperty;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
@@ -80,8 +80,8 @@
public class ScrimView<T extends Launcher> extends View implements Insettable, OnChangeListener,
AccessibilityStateChangeListener, StateListener {
- public static final Property<ScrimView, Integer> DRAG_HANDLE_ALPHA =
- new Property<ScrimView, Integer>(Integer.TYPE, "dragHandleAlpha") {
+ public static final IntProperty<ScrimView> DRAG_HANDLE_ALPHA =
+ new IntProperty<ScrimView>("dragHandleAlpha") {
@Override
public Integer get(ScrimView scrimView) {
@@ -89,7 +89,7 @@
}
@Override
- public void set(ScrimView scrimView, Integer value) {
+ public void setValue(ScrimView scrimView, int value) {
scrimView.setDragHandleAlpha(value);
}
};
@@ -336,7 +336,7 @@
}
private void updateDragHandleVisibility(Drawable recycle) {
- boolean visible = mLauncher.getDeviceProfile().isVerticalBarLayout() || mAM.isEnabled();
+ boolean visible = shouldDragHandleBeVisible();
boolean wasVisible = mDragHandle != null;
if (visible != wasVisible) {
if (visible) {
@@ -352,6 +352,10 @@
}
}
+ protected boolean shouldDragHandleBeVisible() {
+ return mLauncher.getDeviceProfile().isVerticalBarLayout() || mAM.isEnabled();
+ }
+
@Override
public boolean dispatchHoverEvent(MotionEvent event) {
return mAccessibilityHelper.dispatchHoverEvent(event) || super.dispatchHoverEvent(event);
diff --git a/src/com/android/launcher3/views/WorkEduView.java b/src/com/android/launcher3/views/WorkEduView.java
index 81f8327..d849138 100644
--- a/src/com/android/launcher3/views/WorkEduView.java
+++ b/src/com/android/launcher3/views/WorkEduView.java
@@ -46,7 +46,8 @@
public class WorkEduView extends AbstractSlideInView implements Insettable {
private static final int DEFAULT_CLOSE_DURATION = 200;
- private static final String KEY_WORK_EDU_STEP = "showed_work_profile_edu";
+ public static final String KEY_WORK_EDU_STEP = "showed_work_profile_edu";
+ public static final String KEY_LEGACY_WORK_EDU_SEEN = "showed_bottom_user_education";
private static final int WORK_EDU_NOT_STARTED = 0;
private static final int WORK_EDU_PERSONAL_APPS = 1;
@@ -102,6 +103,8 @@
mProceedButton = findViewById(R.id.proceed);
mContentText = findViewById(R.id.content_text);
+ // make sure layout does not shrink when we change the text
+ mContentText.post(() -> mContentText.setMinLines(mContentText.getLineCount()));
if (mLauncher.getAppsView().getContentView() instanceof AllAppsPagedView) {
mAllAppsPagedView = (AllAppsPagedView) mLauncher.getAppsView().getContentView();
}
@@ -179,8 +182,8 @@
if (oldListener != null) {
launcher.getStateManager().removeStateListener(oldListener);
}
- if (launcher.getSharedPrefs().getInt(KEY_WORK_EDU_STEP, WORK_EDU_NOT_STARTED)
- != WORK_EDU_NOT_STARTED) {
+ if (hasSeenLegacyEdu(launcher) || launcher.getSharedPrefs().getInt(KEY_WORK_EDU_STEP,
+ WORK_EDU_NOT_STARTED) != WORK_EDU_NOT_STARTED) {
return null;
}
@@ -210,8 +213,8 @@
* Shows work apps edu if user had dismissed full edu flow
*/
public static void showWorkEduIfNeeded(Launcher launcher) {
- if (launcher.getSharedPrefs().getInt(KEY_WORK_EDU_STEP, WORK_EDU_NOT_STARTED)
- != WORK_EDU_PERSONAL_APPS) {
+ if (hasSeenLegacyEdu(launcher) || launcher.getSharedPrefs().getInt(KEY_WORK_EDU_STEP,
+ WORK_EDU_NOT_STARTED) != WORK_EDU_PERSONAL_APPS) {
return;
}
LayoutInflater layoutInflater = LayoutInflater.from(launcher);
@@ -220,4 +223,8 @@
v.show();
v.goToWorkTab(false);
}
+
+ private static boolean hasSeenLegacyEdu(Launcher launcher) {
+ return launcher.getSharedPrefs().getBoolean(KEY_LEGACY_WORK_EDU_SEEN, false);
+ }
}
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index df1a469..73a0615 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -41,6 +41,8 @@
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.AbstractSlideInView;
+import java.util.ArrayList;
+
/**
* Base class for various widgets popup
*/
@@ -144,9 +146,11 @@
}
@Override
- public void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent) {
- targetParent.containerType = ContainerType.WIDGETS;
- targetParent.cardinality = getElementsRowCount();
+ public void fillInLogContainerData(ItemInfo childInfo, Target child,
+ ArrayList<Target> parents) {
+ Target target = newContainerTarget(ContainerType.WIDGETS);
+ target.cardinality = getElementsRowCount();
+ parents.add(target);
}
@Override