Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 96eefda2 authored by Ryan Lin's avatar Ryan Lin Committed by Android (Google) Code Review
Browse files

Merge "Change magnification animation callback (1/2)"

parents 1ea2b651 e4d95433
Loading
Loading
Loading
Loading
+31 −0
Original line number Diff line number Diff line
/*
 * 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 android.view.accessibility;

/**
 * A callback for magnification animation result.
 * @hide
 */
public interface MagnificationAnimationCallback {
    /**
     * Called when the animation is finished or interrupted during animating.
     *
     * @param success {@code true} if animating successfully with given spec or the spec did not
     *                change. Otherwise {@code false}
     */
    void onResult(boolean success);
}
+44 −41
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ import android.util.SparseArray;
import android.view.Display;
import android.view.MagnificationSpec;
import android.view.View;
import android.view.accessibility.MagnificationAnimationCallback;
import android.view.animation.DecelerateInterpolator;

import com.android.internal.R;
@@ -63,7 +64,7 @@ public class FullScreenMagnificationController {
    private static final boolean DEBUG = false;
    private static final String LOG_TAG = "FullScreenMagnificationController";

    private static final Runnable STUB_RUNNABLE = () -> {
    private static final MagnificationAnimationCallback STUB_ANIMATION_CALLBACK = success -> {
    };
    public static final float MIN_SCALE = 1.0f;
    public static final float MAX_SCALE = 8.0f;
@@ -304,18 +305,19 @@ public class FullScreenMagnificationController {
            }
        }

        void sendSpecToAnimation(MagnificationSpec spec, Runnable endCallback) {
        void sendSpecToAnimation(MagnificationSpec spec,
                MagnificationAnimationCallback animationCallback) {
            if (DEBUG) {
                Slog.i(LOG_TAG,
                        "sendSpecToAnimation(spec = " + spec + ", endCallback = " + endCallback
                                + ")");
                        "sendSpecToAnimation(spec = " + spec + ", animationCallback = "
                                + animationCallback + ")");
            }
            if (Thread.currentThread().getId() == mMainThreadId) {
                mSpecAnimationBridge.updateSentSpecMainThread(spec, endCallback);
                mSpecAnimationBridge.updateSentSpecMainThread(spec, animationCallback);
            } else {
                final Message m = PooledLambda.obtainMessage(
                        SpecAnimationBridge::updateSentSpecMainThread,
                        mSpecAnimationBridge, spec, endCallback);
                        mSpecAnimationBridge, spec, animationCallback);
                mControllerCtx.getHandler().sendMessage(m);
            }
        }
@@ -415,11 +417,11 @@ public class FullScreenMagnificationController {

        @GuardedBy("mLock")
        boolean reset(boolean animate) {
            return reset(transformToStubRunnable(animate));
            return reset(transformToStubCallback(animate));
        }

        @GuardedBy("mLock")
        boolean reset(Runnable endCallback) {
        boolean reset(MagnificationAnimationCallback animationCallback) {
            if (!mRegistered) {
                return false;
            }
@@ -430,7 +432,7 @@ public class FullScreenMagnificationController {
                onMagnificationChangedLocked();
            }
            mIdOfLastServiceToMagnify = INVALID_ID;
            sendSpecToAnimation(spec, endCallback);
            sendSpecToAnimation(spec, animationCallback);
            return changed;
        }

@@ -458,24 +460,23 @@ public class FullScreenMagnificationController {
            final float centerX = normPivotX + offsetX;
            final float centerY = normPivotY + offsetY;
            mIdOfLastServiceToMagnify = id;
            return setScaleAndCenter(scale, centerX, centerY, transformToStubRunnable(animate), id);
            return setScaleAndCenter(scale, centerX, centerY, transformToStubCallback(animate), id);
        }

        @GuardedBy("mLock")
        boolean setScaleAndCenter(float scale, float centerX, float centerY,
                Runnable endCallback, int id) {
                MagnificationAnimationCallback animationCallback, int id) {
            if (!mRegistered) {
                return false;
            }
            if (DEBUG) {
                Slog.i(LOG_TAG,
                        "setScaleAndCenterLocked(scale = " + scale + ", centerX = " + centerX
                                + ", centerY = " + centerY + ", endCallback = " + endCallback
                                + ", id = " + id
                                + ")");
                                + ", centerY = " + centerY + ", endCallback = "
                                + animationCallback + ", id = " + id + ")");
            }
            final boolean changed = updateMagnificationSpecLocked(scale, centerX, centerY);
            sendSpecToAnimation(mCurrentMagnificationSpec, endCallback);
            sendSpecToAnimation(mCurrentMagnificationSpec, animationCallback);
            if (isMagnifying() && (id != INVALID_ID)) {
                mIdOfLastServiceToMagnify = id;
            }
@@ -875,7 +876,7 @@ public class FullScreenMagnificationController {
     *         the spec did not change
     */
    public boolean reset(int displayId, boolean animate) {
        return reset(displayId, animate ?  STUB_RUNNABLE : null);
        return reset(displayId, animate ? STUB_ANIMATION_CALLBACK : null);
    }

    /**
@@ -883,18 +884,19 @@ public class FullScreenMagnificationController {
     * transition.
     *
     * @param displayId The logical display id.
     * @param endCallback Called when the animation is ended or the spec did not change.
     * @param animationCallback Called when the animation result is valid.
     *                    {@code null} to transition immediately
     * @return {@code true} if the magnification spec changed, {@code false} if
     *         the spec did not change
     */
    public boolean reset(int displayId, Runnable endCallback) {
    public boolean reset(int displayId,
            MagnificationAnimationCallback animationCallback) {
        synchronized (mLock) {
            final DisplayMagnification display = mDisplays.get(displayId);
            if (display == null) {
                return false;
            }
            return display.reset(endCallback);
            return display.reset(animationCallback);
        }
    }

@@ -946,7 +948,7 @@ public class FullScreenMagnificationController {
                return false;
            }
            return display.setScaleAndCenter(Float.NaN, centerX, centerY,
                    animate ?  STUB_RUNNABLE : null, id);
                    animate ? STUB_ANIMATION_CALLBACK : null, id);
        }
    }

@@ -970,7 +972,7 @@ public class FullScreenMagnificationController {
    public boolean setScaleAndCenter(int displayId, float scale, float centerX, float centerY,
            boolean animate, int id) {
        return setScaleAndCenter(displayId, scale, centerX, centerY,
                transformToStubRunnable(animate), id);
                transformToStubCallback(animate), id);
    }

    /**
@@ -984,20 +986,20 @@ public class FullScreenMagnificationController {
     *                center and scale, or {@link Float#NaN} to leave unchanged
     * @param centerY the screen-relative Y coordinate around which to
     *                center and scale, or {@link Float#NaN} to leave unchanged
     * @param endCallback called when the transition is finished successfully or the spec did not
     *                   change. {@code null} to transition immediately.
     * @param animationCallback Called when the animation result is valid.
     *                           {@code null} to transition immediately
     * @param id the ID of the service requesting the change
     * @return {@code true} if the magnification spec changed, {@code false} if
     *         the spec did not change
     */
    public boolean setScaleAndCenter(int displayId, float scale, float centerX, float centerY,
            Runnable endCallback, int id) {
            MagnificationAnimationCallback animationCallback, int id) {
        synchronized (mLock) {
            final DisplayMagnification display = mDisplays.get(displayId);
            if (display == null) {
                return false;
            }
            return display.setScaleAndCenter(scale, centerX, centerY, endCallback, id);
            return display.setScaleAndCenter(scale, centerX, centerY, animationCallback, id);
        }
    }

@@ -1230,7 +1232,7 @@ public class FullScreenMagnificationController {
        private final ValueAnimator mValueAnimator;

        // Called when the callee wants animating and the sent spec matches the target spec.
        private Runnable mEndCallback;
        private MagnificationAnimationCallback mAnimationCallback;
        private final Object mLock;

        private final int mDisplayId;
@@ -1268,33 +1270,35 @@ public class FullScreenMagnificationController {
            }
        }

        void updateSentSpecMainThread(MagnificationSpec spec, Runnable endCallback) {
        void updateSentSpecMainThread(MagnificationSpec spec,
                MagnificationAnimationCallback animationCallback) {
            if (mValueAnimator.isRunning()) {
                // Avoid AnimationEnd Callback.
                mEndCallback = null;
                mValueAnimator.cancel();
            }

            mEndCallback = endCallback;
            mAnimationCallback = animationCallback;
            // If the current and sent specs don't match, update the sent spec.
            synchronized (mLock) {
                final boolean changed = !mSentMagnificationSpec.equals(spec);
                if (changed) {
                    if (mEndCallback != null) {
                    if (mAnimationCallback != null) {
                        animateMagnificationSpecLocked(spec);
                    } else {
                        setMagnificationSpecLocked(spec);
                    }
                } else {
                    sendEndCallbackMainThread();
                    sendEndCallbackMainThread(true);
                }
            }
        }

        private void sendEndCallbackMainThread() {
            if (mEndCallback != null) {
                mEndCallback.run();
                mEndCallback = null;
        private void sendEndCallbackMainThread(boolean success) {
            if (mAnimationCallback != null) {
                if (DEBUG) {
                    Slog.d(LOG_TAG, "sendEndCallbackMainThread: " + success);
                }
                mAnimationCallback.onResult(success);
                mAnimationCallback = null;
            }
        }

@@ -1337,17 +1341,16 @@ public class FullScreenMagnificationController {

        @Override
        public void onAnimationStart(Animator animation) {

        }

        @Override
        public void onAnimationEnd(Animator animation) {
            sendEndCallbackMainThread();
            sendEndCallbackMainThread(true);
        }

        @Override
        public void onAnimationCancel(Animator animation) {

            sendEndCallbackMainThread(false);
        }

        @Override
@@ -1481,7 +1484,7 @@ public class FullScreenMagnificationController {
    }

    @Nullable
    private static Runnable transformToStubRunnable(boolean animate) {
        return animate ? STUB_RUNNABLE : null;
    private static MagnificationAnimationCallback transformToStubCallback(boolean animate) {
        return animate ? STUB_ANIMATION_CALLBACK : null;
    }
}
+22 −21
Original line number Diff line number Diff line
@@ -44,6 +44,7 @@ import android.graphics.Rect;
import android.graphics.Region;
import android.os.Looper;
import android.view.MagnificationSpec;
import android.view.accessibility.MagnificationAnimationCallback;

import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
@@ -99,12 +100,12 @@ public class FullScreenMagnificationControllerTest {
    ValueAnimator.AnimatorListener mStateListener;

    FullScreenMagnificationController mFullScreenMagnificationController;
    Runnable mEndCallback;
    MagnificationAnimationCallback mAnimationCallback;

    @Before
    public void setUp() {
        Looper looper = InstrumentationRegistry.getContext().getMainLooper();
        mEndCallback = Mockito.mock(Runnable.class);
        mAnimationCallback = Mockito.mock(MagnificationAnimationCallback.class);
        // Pretending ID of the Thread associated with looper as main thread ID in controller
        when(mMockContext.getMainLooper()).thenReturn(looper);
        when(mMockControllerCtx.getContext()).thenReturn(mMockContext);
@@ -323,7 +324,6 @@ public class FullScreenMagnificationControllerTest {
        for (int i = 0; i < DISPLAY_COUNT; i++) {
            setScaleAndCenter_animated_stateChangesAndAnimationHappens(i);
            resetMockWindowManager();
            Mockito.reset(mEndCallback);
        }
    }

@@ -336,7 +336,7 @@ public class FullScreenMagnificationControllerTest {
        MagnificationSpec endSpec = getMagnificationSpec(scale, offsets);

        assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId, scale,
                newCenter.x, newCenter.y, mEndCallback, SERVICE_ID_1));
                newCenter.x, newCenter.y, mAnimationCallback, SERVICE_ID_1));
        mMessageCapturingHandler.sendAllMessages();

        assertEquals(newCenter.x, mFullScreenMagnificationController.getCenterX(displayId), 0.5);
@@ -365,18 +365,17 @@ public class FullScreenMagnificationControllerTest {
        mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator);
        mStateListener.onAnimationEnd(mMockValueAnimator);
        verify(mMockWindowManager).setMagnificationSpec(eq(displayId), argThat(closeTo(endSpec)));
        verify(mEndCallback).run();
        verify(mAnimationCallback).onResult(true);
    }

    @Test
    public void testSetScaleAndCenterWithAnimation_sameSpec_noAnimationButInvokeEndCallback() {
        for (int i = 0; i < DISPLAY_COUNT; i++) {
            setScaleAndCenter_sameSpec_noAnimationButInvokeEndCallback(i);
            Mockito.reset(mEndCallback);
            setScaleAndCenter_sameSpec_noAnimationButInvokeCallbacks(i);
        }
    }

    private void setScaleAndCenter_sameSpec_noAnimationButInvokeEndCallback(int displayId) {
    private void setScaleAndCenter_sameSpec_noAnimationButInvokeCallbacks(int displayId) {
        register(displayId);
        final PointF center = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
        final float targetScale = 2.0f;
@@ -385,11 +384,11 @@ public class FullScreenMagnificationControllerTest {
        mMessageCapturingHandler.sendAllMessages();

        assertFalse(mFullScreenMagnificationController.setScaleAndCenter(displayId,
                targetScale, center.x, center.y, mEndCallback, SERVICE_ID_1));
                targetScale, center.x, center.y, mAnimationCallback, SERVICE_ID_1));
        mMessageCapturingHandler.sendAllMessages();

        verify(mMockValueAnimator, never()).start();
        verify(mEndCallback).run();
        verify(mAnimationCallback).onResult(true);
    }

    @Test
@@ -673,38 +672,38 @@ public class FullScreenMagnificationControllerTest {
    public void testReset_notMagnifying_noStateChangeButInvokeCallback() {
        for (int i = 0; i < DISPLAY_COUNT; i++) {
            reset_notMagnifying_noStateChangeButInvokeCallback(i);
            Mockito.reset(mEndCallback);
        }
    }

    private void reset_notMagnifying_noStateChangeButInvokeCallback(int displayId) {
        register(displayId);

        assertFalse(mFullScreenMagnificationController.reset(displayId, mEndCallback));
        assertFalse(mFullScreenMagnificationController.reset(displayId, mAnimationCallback));
        mMessageCapturingHandler.sendAllMessages();

        verify(mMockAms, never()).notifyMagnificationChanged(eq(displayId),
                any(Region.class), anyFloat(), anyFloat(), anyFloat());
        verify(mEndCallback).run();
        verify(mAnimationCallback).onResult(true);
    }

    @Test
    public void testReset_Magnifying_resetsMagnificationAndInvokeLastEndCallback() {
    public void testReset_Magnifying_resetsMagnificationAndInvokeCallbacks() {
        for (int i = 0; i < DISPLAY_COUNT; i++) {
            reset_Magnifying_resetsMagnificationAndInvokeLastEndCallback(i);
            reset_Magnifying_resetsMagnificationAndInvokeCallbacks(i);
        }
    }

    private void reset_Magnifying_resetsMagnificationAndInvokeLastEndCallback(int displayId) {
    private void reset_Magnifying_resetsMagnificationAndInvokeCallbacks(int displayId) {
        register(displayId);
        float scale = 2.5f;
        PointF firstCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
        assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId,
                scale, firstCenter.x, firstCenter.y, mEndCallback, SERVICE_ID_1));
                scale, firstCenter.x, firstCenter.y, mAnimationCallback, SERVICE_ID_1));
        mMessageCapturingHandler.sendAllMessages();
        Mockito.reset(mMockValueAnimator);
        // Stubs the logic after the animation is started.
        doAnswer(invocation -> {
            mStateListener.onAnimationCancel(mMockValueAnimator);
            mStateListener.onAnimationEnd(mMockValueAnimator);
            return null;
        }).when(mMockValueAnimator).cancel();
@@ -713,13 +712,14 @@ public class FullScreenMagnificationControllerTest {
        float fraction = 0.33f;
        when(mMockValueAnimator.getAnimatedFraction()).thenReturn(fraction);
        mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator);
        Runnable lastEndCallback = Mockito.mock(Runnable.class);
        MagnificationAnimationCallback lastAnimationCallback = Mockito.mock(
                MagnificationAnimationCallback.class);

        assertTrue(mFullScreenMagnificationController.reset(displayId, lastEndCallback));
        assertTrue(mFullScreenMagnificationController.reset(displayId, lastAnimationCallback));
        mMessageCapturingHandler.sendAllMessages();

        // Verify expected actions.
        verify(mEndCallback, never()).run();
        verify(mAnimationCallback).onResult(false);
        verify(mMockValueAnimator).start();
        verify(mMockValueAnimator).cancel();

@@ -729,7 +729,7 @@ public class FullScreenMagnificationControllerTest {
        mStateListener.onAnimationEnd(mMockValueAnimator);

        assertFalse(mFullScreenMagnificationController.isMagnifying(DISPLAY_0));
        verify(lastEndCallback).run();
        verify(lastAnimationCallback).onResult(true);
    }

    @Test
@@ -1142,6 +1142,7 @@ public class FullScreenMagnificationControllerTest {
        verify(mMockValueAnimator).addListener(animatorListenerArgumentCaptor.capture());
        mStateListener = animatorListenerArgumentCaptor.getValue();
        Mockito.reset(mMockValueAnimator); // Ignore other initialization
        Mockito.reset(mAnimationCallback);
    }

    private void zoomIn2xToMiddle(int displayId) {