diff options
| author | 2020-06-17 21:37:40 +0000 | |
|---|---|---|
| committer | 2020-06-17 21:37:40 +0000 | |
| commit | 5fc81f9566721a35df006d455776984c1566ee5e (patch) | |
| tree | 34894ba8eff05365e7360910bcc4925d9a7f8c00 | |
| parent | 825ea90e8390eff24aae6bb2ffd46bc9bd097267 (diff) | |
| parent | f3c5e1e8999617fddcfab37834c2f330affc67e0 (diff) | |
Merge "Scale down PIP when it's dragged into the dismiss target." into rvc-dev am: 7a1cd3f757 am: bbd804df0a am: f3c5e1e899
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/11886098
Change-Id: I555ccb1a0ba87958ccc16f376918c06a5644bbab
5 files changed, 228 insertions, 90 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java index b93e07e65c73..9daa876038d5 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java @@ -708,6 +708,12 @@ public class PipTaskOrganizer extends TaskOrganizer implements Log.w(TAG, "Abort animation, invalid leash"); return; } + + if (startBounds.isEmpty() || destinationBounds.isEmpty()) { + Log.w(TAG, "Attempted to user resize PIP to or from empty bounds, aborting."); + return; + } + final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); mSurfaceTransactionHelper.scale(tx, mLeash, startBounds, destinationBounds); tx.apply(); diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java index 856c19290af6..06c98d00cca7 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ComponentName; import android.content.Context; +import android.graphics.PointF; import android.graphics.Rect; import android.os.Debug; import android.util.Log; @@ -38,6 +39,9 @@ import com.android.systemui.util.magnetictarget.MagnetizedObject; import java.io.PrintWriter; import java.util.function.Consumer; +import kotlin.Unit; +import kotlin.jvm.functions.Function0; + /** * A helper to animate and manipulate the PiP. */ @@ -74,9 +78,15 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, new SfVsyncFrameCallbackProvider(); /** - * Bounds that are animated using the physics animator. + * Temporary bounds used when PIP is being dragged or animated. These bounds are applied to PIP + * using {@link PipTaskOrganizer#scheduleUserResizePip}, so that we can animate shrinking into + * and expanding out of the magnetic dismiss target. + * + * Once PIP is done being dragged or animated, we set {@link #mBounds} equal to these temporary + * bounds, and call {@link PipTaskOrganizer#scheduleFinishResizePip} to 'officially' move PIP to + * its new bounds. */ - private final Rect mAnimatedBounds = new Rect(); + private final Rect mTemporaryBounds = new Rect(); /** The destination bounds to which PIP is animating. */ private final Rect mAnimatingToBounds = new Rect(); @@ -85,20 +95,20 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, private FloatingContentCoordinator mFloatingContentCoordinator; /** Callback that re-sizes PIP to the animated bounds. */ - private final Choreographer.FrameCallback mResizePipVsyncCallback = - l -> resizePipUnchecked(mAnimatedBounds); + private final Choreographer.FrameCallback mResizePipVsyncCallback; /** - * PhysicsAnimator instance for animating {@link #mAnimatedBounds} using physics animations. + * PhysicsAnimator instance for animating {@link #mTemporaryBounds} using physics animations. */ - private PhysicsAnimator<Rect> mAnimatedBoundsPhysicsAnimator = PhysicsAnimator.getInstance( - mAnimatedBounds); + private PhysicsAnimator<Rect> mTemporaryBoundsPhysicsAnimator = PhysicsAnimator.getInstance( + mTemporaryBounds); + + private MagnetizedObject<Rect> mMagnetizedPip; /** - * Update listener that resizes the PIP to {@link #mAnimatedBounds}. + * Update listener that resizes the PIP to {@link #mTemporaryBounds}. */ - final PhysicsAnimator.UpdateListener<Rect> mResizePipUpdateListener = - (target, values) -> mSfVsyncFrameProvider.postFrameCallback(mResizePipVsyncCallback); + private final PhysicsAnimator.UpdateListener<Rect> mResizePipUpdateListener; /** FlingConfig instances provided to PhysicsAnimator for fling gestures. */ private PhysicsAnimator.FlingConfig mFlingConfigX; @@ -124,6 +134,12 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, private boolean mSpringingToTouch = false; /** + * Whether PIP was released in the dismiss target, and will be animated out and dismissed + * shortly. + */ + private boolean mDismissalPending = false; + + /** * Gets set in {@link #animateToExpandedState(Rect, Rect, Rect, Runnable)}, this callback is * used to show menu activity when the expand animation is completed. */ @@ -155,6 +171,16 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, mSnapAlgorithm = snapAlgorithm; mFloatingContentCoordinator = floatingContentCoordinator; mPipTaskOrganizer.registerPipTransitionCallback(mPipTransitionCallback); + + mResizePipVsyncCallback = l -> { + if (!mTemporaryBounds.isEmpty()) { + mPipTaskOrganizer.scheduleUserResizePip( + mBounds, mTemporaryBounds, null); + } + }; + + mResizePipUpdateListener = (target, values) -> + mSfVsyncFrameProvider.postFrameCallback(mResizePipVsyncCallback); } @NonNull @@ -186,19 +212,8 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, } } - /** - * Synchronizes the current bounds with either the pinned stack, or the ongoing animation. This - * is done to prepare for a touch gesture. - */ - void synchronizePinnedStackBoundsForTouchGesture() { - if (mAnimatingToBounds.isEmpty()) { - // If we're not animating anywhere, sync normally. - synchronizePinnedStackBounds(); - } else { - // If we're animating, set the current bounds to the animated bounds. That way, the - // touch gesture will begin at the most recent animated location of the bounds. - mBounds.set(mAnimatedBounds); - } + boolean isAnimating() { + return mTemporaryBoundsPhysicsAnimator.isRunning(); } /** @@ -224,32 +239,54 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, // If we are moving PIP directly to the touch event locations, cancel any animations and // move PIP to the given bounds. cancelAnimations(); - resizePipUnchecked(toBounds); - mBounds.set(toBounds); + + if (!isDragging) { + resizePipUnchecked(toBounds); + mBounds.set(toBounds); + } else { + mTemporaryBounds.set(toBounds); + mPipTaskOrganizer.scheduleUserResizePip(mBounds, mTemporaryBounds, null); + } } else { // If PIP is 'catching up' after being stuck in the dismiss target, update the animation // to spring towards the new touch location. - mAnimatedBoundsPhysicsAnimator + mTemporaryBoundsPhysicsAnimator + .spring(FloatProperties.RECT_WIDTH, mBounds.width(), mSpringConfig) + .spring(FloatProperties.RECT_HEIGHT, mBounds.height(), mSpringConfig) .spring(FloatProperties.RECT_X, toBounds.left, mSpringConfig) - .spring(FloatProperties.RECT_Y, toBounds.top, mSpringConfig) - .withEndActions(() -> mSpringingToTouch = false); + .spring(FloatProperties.RECT_Y, toBounds.top, mSpringConfig); startBoundsAnimator(toBounds.left /* toX */, toBounds.top /* toY */, false /* dismiss */); } } - /** Set whether we're springing-to-touch to catch up after being stuck in the dismiss target. */ - void setSpringingToTouch(boolean springingToTouch) { - if (springingToTouch) { - mAnimatedBounds.set(mBounds); - } + /** Animates the PIP into the dismiss target, scaling it down. */ + void animateIntoDismissTarget( + MagnetizedObject.MagneticTarget target, + float velX, float velY, + boolean flung, Function0<Unit> after) { + final PointF targetCenter = target.getCenterOnScreen(); - mSpringingToTouch = springingToTouch; + final float desiredWidth = mBounds.width() / 2; + final float desiredHeight = mBounds.height() / 2; + + final float destinationX = targetCenter.x - (desiredWidth / 2f); + final float destinationY = targetCenter.y - (desiredHeight / 2f); + + mTemporaryBoundsPhysicsAnimator + .spring(FloatProperties.RECT_X, destinationX, velX, mSpringConfig) + .spring(FloatProperties.RECT_Y, destinationY, velY, mSpringConfig) + .spring(FloatProperties.RECT_WIDTH, desiredWidth, mSpringConfig) + .spring(FloatProperties.RECT_HEIGHT, desiredHeight, mSpringConfig) + .withEndActions(after); + + startBoundsAnimator(destinationX, destinationY, false); } - void prepareForAnimation() { - mAnimatedBounds.set(mBounds); + /** Set whether we're springing-to-touch to catch up after being stuck in the dismiss target. */ + void setSpringingToTouch(boolean springingToTouch) { + mSpringingToTouch = springingToTouch; } /** @@ -309,13 +346,22 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, } /** + * Returns the PIP bounds if we're not animating, or the current, temporary animating bounds + * otherwise. + */ + Rect getPossiblyAnimatingBounds() { + return mTemporaryBounds.isEmpty() ? mBounds : mTemporaryBounds; + } + + /** * Flings the PiP to the closest snap target. */ void flingToSnapTarget( float velocityX, float velocityY, @Nullable Runnable updateAction, @Nullable Runnable endAction) { - mAnimatedBounds.set(mBounds); - mAnimatedBoundsPhysicsAnimator + mTemporaryBoundsPhysicsAnimator + .spring(FloatProperties.RECT_WIDTH, mBounds.width(), mSpringConfig) + .spring(FloatProperties.RECT_HEIGHT, mBounds.height(), mSpringConfig) .flingThenSpring( FloatProperties.RECT_X, velocityX, mFlingConfigX, mSpringConfig, true /* flingMustReachMinOrMax */) @@ -324,13 +370,14 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, .withEndActions(endAction); if (updateAction != null) { - mAnimatedBoundsPhysicsAnimator.addUpdateListener( + mTemporaryBoundsPhysicsAnimator.addUpdateListener( (target, values) -> updateAction.run()); } final float xEndValue = velocityX < 0 ? mMovementBounds.left : mMovementBounds.right; final float estimatedFlingYEndValue = - PhysicsAnimator.estimateFlingEndValue(mBounds.top, velocityY, mFlingConfigY); + PhysicsAnimator.estimateFlingEndValue( + mTemporaryBounds.top, velocityY, mFlingConfigY); startBoundsAnimator(xEndValue /* toX */, estimatedFlingYEndValue /* toY */, false /* dismiss */); @@ -341,8 +388,12 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, * configuration */ void animateToBounds(Rect bounds, PhysicsAnimator.SpringConfig springConfig) { - mAnimatedBounds.set(mBounds); - mAnimatedBoundsPhysicsAnimator + if (!mTemporaryBoundsPhysicsAnimator.isRunning()) { + // Animate from the current bounds if we're not already animating. + mTemporaryBounds.set(mBounds); + } + + mTemporaryBoundsPhysicsAnimator .spring(FloatProperties.RECT_X, bounds.left, springConfig) .spring(FloatProperties.RECT_Y, bounds.top, springConfig); startBoundsAnimator(bounds.left /* toX */, bounds.top /* toY */, @@ -353,18 +404,19 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, * Animates the dismissal of the PiP off the edge of the screen. */ void animateDismiss() { - mAnimatedBounds.set(mBounds); - // Animate off the bottom of the screen, then dismiss PIP. - mAnimatedBoundsPhysicsAnimator + mTemporaryBoundsPhysicsAnimator .spring(FloatProperties.RECT_Y, - mBounds.bottom + mBounds.height(), + mMovementBounds.bottom + mBounds.height() * 2, 0, mSpringConfig) .withEndActions(this::dismissPip); - startBoundsAnimator(mBounds.left /* toX */, mBounds.bottom + mBounds.height() /* toY */, + startBoundsAnimator( + mBounds.left /* toX */, mBounds.bottom + mBounds.height() /* toY */, true /* dismiss */); + + mDismissalPending = false; } /** @@ -415,7 +467,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, * Cancels all existing animations. */ private void cancelAnimations() { - mAnimatedBoundsPhysicsAnimator.cancel(); + mTemporaryBoundsPhysicsAnimator.cancel(); mAnimatingToBounds.setEmpty(); mSpringingToTouch = false; } @@ -449,15 +501,36 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, (int) toY + mBounds.height()); setAnimatingToBounds(mAnimatingToBounds); - mAnimatedBoundsPhysicsAnimator - .withEndActions(() -> { - if (!dismiss) { - mPipTaskOrganizer.scheduleFinishResizePip(mAnimatedBounds); - } - mAnimatingToBounds.setEmpty(); - }) - .addUpdateListener(mResizePipUpdateListener) - .start(); + if (!mTemporaryBoundsPhysicsAnimator.isRunning()) { + mTemporaryBoundsPhysicsAnimator + .addUpdateListener(mResizePipUpdateListener) + .withEndActions(this::onBoundsAnimationEnd); + } + + mTemporaryBoundsPhysicsAnimator.start(); + } + + /** + * Notify that PIP was released in the dismiss target and will be animated out and dismissed + * shortly. + */ + void notifyDismissalPending() { + mDismissalPending = true; + } + + private void onBoundsAnimationEnd() { + if (!mDismissalPending + && !mSpringingToTouch + && !mMagnetizedPip.getObjectStuckToTarget()) { + mBounds.set(mTemporaryBounds); + mPipTaskOrganizer.scheduleFinishResizePip(mBounds); + + mTemporaryBounds.setEmpty(); + } + + mAnimatingToBounds.setEmpty(); + mSpringingToTouch = false; + mDismissalPending = false; } /** @@ -503,25 +576,29 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, * magnetic dismiss target so it can calculate PIP's size and position. */ MagnetizedObject<Rect> getMagnetizedPip() { - return new MagnetizedObject<Rect>( - mContext, mAnimatedBounds, FloatProperties.RECT_X, FloatProperties.RECT_Y) { - @Override - public float getWidth(@NonNull Rect animatedPipBounds) { - return animatedPipBounds.width(); - } - - @Override - public float getHeight(@NonNull Rect animatedPipBounds) { - return animatedPipBounds.height(); - } + if (mMagnetizedPip == null) { + mMagnetizedPip = new MagnetizedObject<Rect>( + mContext, mTemporaryBounds, FloatProperties.RECT_X, FloatProperties.RECT_Y) { + @Override + public float getWidth(@NonNull Rect animatedPipBounds) { + return animatedPipBounds.width(); + } + + @Override + public float getHeight(@NonNull Rect animatedPipBounds) { + return animatedPipBounds.height(); + } + + @Override + public void getLocationOnScreen( + @NonNull Rect animatedPipBounds, @NonNull int[] loc) { + loc[0] = animatedPipBounds.left; + loc[1] = animatedPipBounds.top; + } + }; + } - @Override - public void getLocationOnScreen( - @NonNull Rect animatedPipBounds, @NonNull int[] loc) { - loc[0] = animatedPipBounds.left; - loc[1] = animatedPipBounds.top; - } - }; + return mMagnetizedPip; } public void dump(PrintWriter pw, String prefix) { diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java index 74f0d9741fbc..2f9b29d13744 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java @@ -70,6 +70,8 @@ import com.android.systemui.util.magnetictarget.MagnetizedObject; import java.io.PrintWriter; +import kotlin.Unit; + /** * Manages all the touch handling for PIP on the Phone, including moving, dismissing and expanding * the PIP. @@ -262,12 +264,14 @@ public class PipTouchHandler { mMagneticTarget = mMagnetizedPip.addTarget(mTargetView, 0); updateMagneticTargetSize(); - mMagnetizedPip.setPhysicsAnimatorUpdateListener(mMotionHelper.mResizePipUpdateListener); + mMagnetizedPip.setAnimateStuckToTarget( + (target, velX, velY, flung, after) -> { + mMotionHelper.animateIntoDismissTarget(target, velX, velY, flung, after); + return Unit.INSTANCE; + }); mMagnetizedPip.setMagnetListener(new MagnetizedObject.MagnetListener() { @Override public void onStuckToTarget(@NonNull MagnetizedObject.MagneticTarget target) { - mMotionHelper.prepareForAnimation(); - // Show the dismiss target, in case the initial touch event occurred within the // magnetic field radius. showDismissTargetMaybe(); @@ -286,12 +290,13 @@ public class PipTouchHandler { @Override public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) { + mMotionHelper.notifyDismissalPending(); + mHandler.post(() -> { mMotionHelper.animateDismiss(); hideDismissTarget(); }); - MetricsLoggerWrapper.logPictureInPictureDismissByDrag(mContext, PipUtils.getTopPipActivity(mContext, mActivityManager)); } @@ -617,11 +622,16 @@ public class PipTouchHandler { } MotionEvent ev = (MotionEvent) inputEvent; - - if (mPipResizeGestureHandler.isWithinTouchRegion((int) ev.getRawX(), (int) ev.getRawY())) { + if (!mTouchState.isDragging() + && !mMagnetizedPip.getObjectStuckToTarget() + && !mMotionHelper.isAnimating() + && mPipResizeGestureHandler.isWithinTouchRegion( + (int) ev.getRawX(), (int) ev.getRawY())) { return true; } - if (mMagnetizedPip.maybeConsumeMotionEvent(ev)) { + + if ((ev.getAction() == MotionEvent.ACTION_DOWN || mTouchState.isUserInteracting()) + && mMagnetizedPip.maybeConsumeMotionEvent(ev)) { // If the first touch event occurs within the magnetic field, pass the ACTION_DOWN event // to the touch state. Touch state needs a DOWN event in order to later process MOVE // events it'll receive if the object is dragged out of the magnetic field. @@ -643,7 +653,6 @@ public class PipTouchHandler { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: { - mMotionHelper.synchronizePinnedStackBoundsForTouchGesture(); mGesture.onDown(mTouchState); break; } @@ -872,7 +881,7 @@ public class PipTouchHandler { return; } - Rect bounds = mMotionHelper.getBounds(); + Rect bounds = mMotionHelper.getPossiblyAnimatingBounds(); mDelta.set(0f, 0f); mStartPosition.set(bounds.left, bounds.top); mMovementWithinDismiss = touchState.getDownTouchPosition().y >= mMovementBounds.bottom; @@ -914,7 +923,7 @@ public class PipTouchHandler { mDelta.x += left - lastX; mDelta.y += top - lastY; - mTmpBounds.set(mMotionHelper.getBounds()); + mTmpBounds.set(mMotionHelper.getPossiblyAnimatingBounds()); mTmpBounds.offsetTo((int) left, (int) top); mMotionHelper.movePip(mTmpBounds, true /* isDragging */); diff --git a/packages/SystemUI/src/com/android/systemui/util/animation/FloatProperties.kt b/packages/SystemUI/src/com/android/systemui/util/animation/FloatProperties.kt index ecd3afd687b3..a284a747da21 100644 --- a/packages/SystemUI/src/com/android/systemui/util/animation/FloatProperties.kt +++ b/packages/SystemUI/src/com/android/systemui/util/animation/FloatProperties.kt @@ -67,6 +67,40 @@ class FloatProperties { } /** + * Represents the width of a [Rect]. Typically used to animate resizing a Rect horizontally. + * + * This property's getter returns [Rect.width], and its setter changes the value of + * [Rect.right] by adding the animated width value to [Rect.left]. + */ + @JvmField + val RECT_WIDTH = object : FloatPropertyCompat<Rect>("RectWidth") { + override fun getValue(rect: Rect): Float { + return rect.width().toFloat() + } + + override fun setValue(rect: Rect, value: Float) { + rect.right = rect.left + value.toInt() + } + } + + /** + * Represents the height of a [Rect]. Typically used to animate resizing a Rect vertically. + * + * This property's getter returns [Rect.height], and its setter changes the value of + * [Rect.bottom] by adding the animated height value to [Rect.top]. + */ + @JvmField + val RECT_HEIGHT = object : FloatPropertyCompat<Rect>("RectHeight") { + override fun getValue(rect: Rect): Float { + return rect.height().toFloat() + } + + override fun setValue(rect: Rect, value: Float) { + rect.bottom = rect.top + value.toInt() + } + } + + /** * Represents the x-coordinate of a [RectF]. Typically used to animate moving a RectF * horizontally. * diff --git a/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt b/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt index 5a2b064c5389..47b607fc6285 100644 --- a/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt +++ b/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt @@ -178,6 +178,18 @@ abstract class MagnetizedObject<T : Any>( var physicsAnimatorEndListener: PhysicsAnimator.EndListener<T>? = null /** + * Method that is called when the object should be animated stuck to the target. The default + * implementation uses the object's x and y properties to animate the object centered inside the + * target. You can override this if you need custom animation. + * + * The method is invoked with the MagneticTarget that the object is sticking to, the X and Y + * velocities of the gesture that brought the object into the magnetic radius, whether or not it + * was flung, and a callback you must call after your animation completes. + */ + var animateStuckToTarget: (MagneticTarget, Float, Float, Boolean, (() -> Unit)?) -> Unit = + ::animateStuckToTargetInternal + + /** * Sets whether forcefully flinging the object vertically towards a target causes it to be * attracted to the target and then released immediately, despite never being dragged within the * magnetic field. @@ -373,7 +385,7 @@ abstract class MagnetizedObject<T : Any>( targetObjectIsStuckTo = targetObjectIsInMagneticFieldOf cancelAnimations() magnetListener.onStuckToTarget(targetObjectIsInMagneticFieldOf!!) - animateStuckToTarget(targetObjectIsInMagneticFieldOf, velX, velY, false) + animateStuckToTarget(targetObjectIsInMagneticFieldOf, velX, velY, false, null) vibrateIfEnabled(VibrationEffect.EFFECT_HEAVY_CLICK) } else if (targetObjectIsInMagneticFieldOf == null && objectStuckToTarget) { @@ -430,8 +442,8 @@ abstract class MagnetizedObject<T : Any>( targetObjectIsStuckTo = flungToTarget animateStuckToTarget(flungToTarget, velX, velY, true) { - targetObjectIsStuckTo = null magnetListener.onReleasedInTarget(flungToTarget) + targetObjectIsStuckTo = null vibrateIfEnabled(VibrationEffect.EFFECT_HEAVY_CLICK) } @@ -465,7 +477,7 @@ abstract class MagnetizedObject<T : Any>( } /** Animates sticking the object to the provided target with the given start velocities. */ - private fun animateStuckToTarget( + private fun animateStuckToTargetInternal( target: MagneticTarget, velX: Float, velY: Float, @@ -581,10 +593,10 @@ abstract class MagnetizedObject<T : Any>( * multiple objects. */ class MagneticTarget( - internal val targetView: View, + val targetView: View, var magneticFieldRadiusPx: Int ) { - internal val centerOnScreen = PointF() + val centerOnScreen = PointF() private val tempLoc = IntArray(2) |