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

Commit 86b273f1 authored by Jorim Jaggi's avatar Jorim Jaggi
Browse files

Fix jank in swipe-up to unlock

Some stuff is slow in the swipe-up to unlock transition. Introduce
DejankUtils to execute it when RenderThread is doing its work.

Bug: 22205322
Change-Id: I7089f0a1bdca13ad13a1e305a7bff32276a3cd53
parent f8763581
Loading
Loading
Loading
Loading
+80 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 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.systemui;

import android.os.Handler;
import android.os.Looper;
import android.os.StrictMode;
import android.view.Choreographer;

import java.util.ArrayList;

/**
 * Utility class for methods used to dejank the UI.
 */
public class DejankUtils {

    private static final Choreographer sChoreographer = Choreographer.getInstance();
    private static final Handler sHandler = new Handler();

    private static final ArrayList<Runnable> sPendingRunnables = new ArrayList<>();

    private static final Runnable sAnimationCallbackRunnable = new Runnable() {
        @Override
        public void run() {
            for (int i = 0; i < sPendingRunnables.size(); i++) {
                sHandler.post(sPendingRunnables.get(i));
            }
            sPendingRunnables.clear();
        }
    };

    /**
     * Executes {@code r} after performTraversals. Use this do to CPU heavy work for which the
     * timing is not critical for animation. The work is then scheduled at the same time
     * RenderThread is doing its thing, leading to better parallelization.
     *
     * <p>Needs to be called from the main thread.
     */
    public static void postAfterTraversal(Runnable r) {
        throwIfNotCalledOnMainThread();
        sPendingRunnables.add(r);
        postAnimationCallback();
    }

    /**
     * Removes a previously scheduled runnable.
     *
     * <p>Needs to be called from the main thread.
     */
    public static void removeCallbacks(Runnable r) {
        throwIfNotCalledOnMainThread();
        sPendingRunnables.remove(r);
        sHandler.removeCallbacks(r);
    }

    private static void postAnimationCallback() {
        sChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, sAnimationCallbackRunnable,
                null);
    }

    private static void throwIfNotCalledOnMainThread() {
        if (!Looper.getMainLooper().isCurrentThread()) {
            throw new IllegalStateException("should be called from the main thread.");
        }
    }
}
+4 −6
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import com.android.keyguard.KeyguardHostView;
import com.android.keyguard.KeyguardSecurityView;
import com.android.keyguard.R;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.DejankUtils;

import static com.android.keyguard.KeyguardHostView.OnDismissAction;
import static com.android.keyguard.KeyguardSecurityModel.SecurityMode;
@@ -46,7 +47,6 @@ public class KeyguardBouncer {
    private KeyguardHostView mKeyguardView;
    private ViewGroup mRoot;
    private boolean mShowingSoon;
    private Choreographer mChoreographer = Choreographer.getInstance();
    private int mBouncerPromptReason;

    public KeyguardBouncer(Context context, ViewMediatorCallback callback,
@@ -70,16 +70,13 @@ public class KeyguardBouncer {
            return;
        }

        mBouncerPromptReason = mCallback.getBouncerPromptReason();

        // Try to dismiss the Keyguard. If no security pattern is set, this will dismiss the whole
        // Keyguard. If we need to authenticate, show the bouncer.
        if (!mKeyguardView.dismiss()) {
            mShowingSoon = true;

            // Split up the work over multiple frames.
            mChoreographer.postCallbackDelayed(Choreographer.CALLBACK_ANIMATION, mShowRunnable,
                    null, 16);
            DejankUtils.postAfterTraversal(mShowRunnable);
        }
    }

@@ -107,7 +104,7 @@ public class KeyguardBouncer {
    }

    private void cancelShowRunnable() {
        mChoreographer.removeCallbacks(Choreographer.CALLBACK_ANIMATION, mShowRunnable, null);
        DejankUtils.removeCallbacks(mShowRunnable);
        mShowingSoon = false;
    }

@@ -165,6 +162,7 @@ public class KeyguardBouncer {
        if (wasInitialized) {
            mKeyguardView.showPrimarySecurityScreen();
        }
        mBouncerPromptReason = mCallback.getBouncerPromptReason();
    }

    private void ensureView() {
+3 −2
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;

import com.android.systemui.DejankUtils;
import com.android.systemui.EventLogTags;
import com.android.systemui.R;

@@ -117,12 +118,12 @@ public class PhoneStatusBarView extends PanelBar {
    public void onAllPanelsCollapsed() {
        super.onAllPanelsCollapsed();
        // Close the status bar in the next frame so we can show the end of the animation.
        postOnAnimation(mHideExpandedRunnable);
        DejankUtils.postAfterTraversal(mHideExpandedRunnable);
        mLastFullyOpenedPanel = null;
    }

    public void removePendingHideExpandedRunnables() {
        removeCallbacks(mHideExpandedRunnable);
        DejankUtils.removeCallbacks(mHideExpandedRunnable);
    }

    @Override