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

Commit 9c795042 authored by Craig Mautner's avatar Craig Mautner
Browse files

Add enter-animation-done callback for system windows

Existing hidden methods allow activities to be notified when their
windows have completed animating in. This change adds that capability
to system windows using a ViewTreeObserver callback since system
windows lack an activity token.

The first subsystem to use this is the UserSwitchingDialog which was
previously using a 250 msec timeout to dismiss the dialog. That
deadline was often missed leaving the user with no dialog on the
screen during the transition.

Fixes bug 16661752.

Change-Id: I70789e0d9c07112f275e76fb82850926305f290d
parent 74fe6ce7
Loading
Loading
Loading
Loading
+5 −0
Original line number Original line Diff line number Diff line
@@ -85,4 +85,9 @@ oneway interface IWindow {
     * is done.
     * is done.
     */
     */
    void doneAnimating();
    void doneAnimating();

    /**
     * Called for non-application windows when the enter animation has completed.
     */
    void dispatchWindowShown();
}
}
+22 −0
Original line number Original line Diff line number Diff line
@@ -3014,6 +3014,7 @@ public final class ViewRootImpl implements ViewParent,
    private final static int MSG_INVALIDATE_WORLD = 23;
    private final static int MSG_INVALIDATE_WORLD = 23;
    private final static int MSG_WINDOW_MOVED = 24;
    private final static int MSG_WINDOW_MOVED = 24;
    private final static int MSG_SYNTHESIZE_INPUT_EVENT = 25;
    private final static int MSG_SYNTHESIZE_INPUT_EVENT = 25;
    private final static int MSG_DISPATCH_WINDOW_SHOWN = 26;


    final class ViewRootHandler extends Handler {
    final class ViewRootHandler extends Handler {
        @Override
        @Override
@@ -3063,6 +3064,8 @@ public final class ViewRootImpl implements ViewParent,
                    return "MSG_WINDOW_MOVED";
                    return "MSG_WINDOW_MOVED";
                case MSG_SYNTHESIZE_INPUT_EVENT:
                case MSG_SYNTHESIZE_INPUT_EVENT:
                    return "MSG_SYNTHESIZE_INPUT_EVENT";
                    return "MSG_SYNTHESIZE_INPUT_EVENT";
                case MSG_DISPATCH_WINDOW_SHOWN:
                    return "MSG_DISPATCH_WINDOW_SHOWN";
            }
            }
            return super.getMessageName(message);
            return super.getMessageName(message);
        }
        }
@@ -3291,6 +3294,9 @@ public final class ViewRootImpl implements ViewParent,
                    invalidateWorld(mView);
                    invalidateWorld(mView);
                }
                }
            } break;
            } break;
            case MSG_DISPATCH_WINDOW_SHOWN: {
                handleDispatchWindowShown();
            }
            }
            }
        }
        }
    }
    }
@@ -5137,6 +5143,10 @@ public final class ViewRootImpl implements ViewParent,
        }
        }
    }
    }


    public void handleDispatchWindowShown() {
        mAttachInfo.mTreeObserver.dispatchOnWindowShown();
    }

    public void getLastTouchPoint(Point outLocation) {
    public void getLastTouchPoint(Point outLocation) {
        outLocation.x = (int) mLastTouchPoint.x;
        outLocation.x = (int) mLastTouchPoint.x;
        outLocation.y = (int) mLastTouchPoint.y;
        outLocation.y = (int) mLastTouchPoint.y;
@@ -5997,6 +6007,10 @@ public final class ViewRootImpl implements ViewParent,
        mHandler.sendMessage(msg);
        mHandler.sendMessage(msg);
    }
    }


    public void dispatchWindowShown() {
        mHandler.sendEmptyMessage(MSG_DISPATCH_WINDOW_SHOWN);
    }

    public void dispatchCloseSystemDialogs(String reason) {
    public void dispatchCloseSystemDialogs(String reason) {
        Message msg = Message.obtain();
        Message msg = Message.obtain();
        msg.what = MSG_CLOSE_SYSTEM_DIALOGS;
        msg.what = MSG_CLOSE_SYSTEM_DIALOGS;
@@ -6507,6 +6521,14 @@ public final class ViewRootImpl implements ViewParent,
                viewAncestor.dispatchDoneAnimating();
                viewAncestor.dispatchDoneAnimating();
            }
            }
        }
        }

        @Override
        public void dispatchWindowShown() {
            final ViewRootImpl viewAncestor = mViewAncestor.get();
            if (viewAncestor != null) {
                viewAncestor.dispatchWindowShown();
            }
        }
    }
    }


    public static final class CalledFromWrongThreadException extends AndroidRuntimeException {
    public static final class CalledFromWrongThreadException extends AndroidRuntimeException {
+86 −0
Original line number Original line Diff line number Diff line
@@ -44,10 +44,15 @@ public final class ViewTreeObserver {
    private CopyOnWriteArray<OnComputeInternalInsetsListener> mOnComputeInternalInsetsListeners;
    private CopyOnWriteArray<OnComputeInternalInsetsListener> mOnComputeInternalInsetsListeners;
    private CopyOnWriteArray<OnScrollChangedListener> mOnScrollChangedListeners;
    private CopyOnWriteArray<OnScrollChangedListener> mOnScrollChangedListeners;
    private CopyOnWriteArray<OnPreDrawListener> mOnPreDrawListeners;
    private CopyOnWriteArray<OnPreDrawListener> mOnPreDrawListeners;
    private CopyOnWriteArray<OnWindowShownListener> mOnWindowShownListeners;


    // These listeners cannot be mutated during dispatch
    // These listeners cannot be mutated during dispatch
    private ArrayList<OnDrawListener> mOnDrawListeners;
    private ArrayList<OnDrawListener> mOnDrawListeners;


    /** Remains false until #dispatchOnWindowShown() is called. If a listener registers after
     * that the listener will be immediately called. */
    private boolean mWindowShown;

    private boolean mAlive = true;
    private boolean mAlive = true;


    /**
    /**
@@ -173,6 +178,19 @@ public final class ViewTreeObserver {
        public void onScrollChanged();
        public void onScrollChanged();
    }
    }


    /**
     * Interface definition for a callback noting when a system window has been displayed.
     * This is only used for non-Activity windows. Activity windows can use
     * Activity.onEnterAnimationComplete() to get the same signal.
     * @hide
     */
    public interface OnWindowShownListener {
        /**
         * Callback method to be invoked when a non-activity window is fully shown.
         */
        void onWindowShown();
    }

    /**
    /**
     * Parameters used with OnComputeInternalInsetsListener.
     * Parameters used with OnComputeInternalInsetsListener.
     * 
     * 
@@ -375,6 +393,14 @@ public final class ViewTreeObserver {
            }
            }
        }
        }


        if (observer.mOnWindowShownListeners != null) {
            if (mOnWindowShownListeners != null) {
                mOnWindowShownListeners.addAll(observer.mOnWindowShownListeners);
            } else {
                mOnWindowShownListeners = observer.mOnWindowShownListeners;
            }
        }

        observer.kill();
        observer.kill();
    }
    }


@@ -567,6 +593,45 @@ public final class ViewTreeObserver {
        mOnPreDrawListeners.remove(victim);
        mOnPreDrawListeners.remove(victim);
    }
    }


    /**
     * Register a callback to be invoked when the view tree window has been shown
     *
     * @param listener The callback to add
     *
     * @throws IllegalStateException If {@link #isAlive()} returns false
     * @hide
     */
    public void addOnWindowShownListener(OnWindowShownListener listener) {
        checkIsAlive();

        if (mOnWindowShownListeners == null) {
            mOnWindowShownListeners = new CopyOnWriteArray<OnWindowShownListener>();
        }

        mOnWindowShownListeners.add(listener);
        if (mWindowShown) {
            listener.onWindowShown();
        }
    }

    /**
     * Remove a previously installed window shown callback
     *
     * @param victim The callback to remove
     *
     * @throws IllegalStateException If {@link #isAlive()} returns false
     *
     * @see #addOnWindowShownListener(OnWindowShownListener)
     * @hide
     */
    public void removeOnWindowShownListener(OnWindowShownListener victim) {
        checkIsAlive();
        if (mOnWindowShownListeners == null) {
            return;
        }
        mOnWindowShownListeners.remove(victim);
    }

    /**
    /**
     * <p>Register a callback to be invoked when the view tree is about to be drawn.</p>
     * <p>Register a callback to be invoked when the view tree is about to be drawn.</p>
     * <p><strong>Note:</strong> this method <strong>cannot</strong> be invoked from
     * <p><strong>Note:</strong> this method <strong>cannot</strong> be invoked from
@@ -853,6 +918,27 @@ public final class ViewTreeObserver {
        return cancelDraw;
        return cancelDraw;
    }
    }


    /**
     * Notifies registered listeners that the window is now shown
     * @hide
     */
    @SuppressWarnings("unchecked")
    public final void dispatchOnWindowShown() {
        mWindowShown = true;
        final CopyOnWriteArray<OnWindowShownListener> listeners = mOnWindowShownListeners;
        if (listeners != null && listeners.size() > 0) {
            CopyOnWriteArray.Access<OnWindowShownListener> access = listeners.start();
            try {
                int count = access.size();
                for (int i = 0; i < count; i++) {
                    access.get(i).onWindowShown();
                }
            } finally {
                listeners.end();
            }
        }
    }

    /**
    /**
     * Notifies registered listeners that the drawing pass is about to start.
     * Notifies registered listeners that the drawing pass is about to start.
     */
     */
+4 −0
Original line number Original line Diff line number Diff line
@@ -102,4 +102,8 @@ public class BaseIWindow extends IWindow.Stub {
    @Override
    @Override
    public void doneAnimating() {
    public void doneAnimating() {
    }
    }

    @Override
    public void dispatchWindowShown() {
    }
}
}
+16 −21
Original line number Original line Diff line number Diff line
@@ -17,17 +17,11 @@
package com.android.server.am;
package com.android.server.am;


import android.app.AlertDialog;
import android.app.AlertDialog;
import android.app.Service;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Resources;
import android.content.res.Resources;
import android.os.Handler;
import android.os.Message;
import android.util.Slog;
import android.view.LayoutInflater;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.view.WindowManager;
import android.widget.TextView;
import android.widget.TextView;


@@ -39,11 +33,10 @@ import com.android.internal.R;
 * in the background rather than just freeze the screen and not know if the user-switch affordance
 * in the background rather than just freeze the screen and not know if the user-switch affordance
 * was being handled.
 * was being handled.
 */
 */
final class UserSwitchingDialog extends AlertDialog {
final class UserSwitchingDialog extends AlertDialog
        implements ViewTreeObserver.OnWindowShownListener {
    private static final String TAG = "ActivityManagerUserSwitchingDialog";
    private static final String TAG = "ActivityManagerUserSwitchingDialog";


    private static final int MSG_START_USER = 1;

    private final ActivityManagerService mService;
    private final ActivityManagerService mService;
    private final int mUserId;
    private final int mUserId;


@@ -74,19 +67,21 @@ final class UserSwitchingDialog extends AlertDialog {


    @Override
    @Override
    public void show() {
    public void show() {
        // Slog.v(TAG, "show called");
        super.show();
        super.show();
        // TODO: Instead of just an arbitrary delay, wait for a signal that the window was fully
        final View decorView = getWindow().getDecorView();
        // displayed by the window manager
        if (decorView != null) {
        mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_START_USER), 250);
            decorView.getViewTreeObserver().addOnWindowShownListener(this);
        }
    }
    }


    private final Handler mHandler = new Handler() {
    @Override
        public void handleMessage(Message msg) {
    public void onWindowShown() {
            switch (msg.what) {
        // Slog.v(TAG, "onWindowShown called");
                case MSG_START_USER:
        mService.startUserInForeground(mUserId, this);
                    mService.startUserInForeground(mUserId, UserSwitchingDialog.this);
        final View decorView = getWindow().getDecorView();
                    break;
        if (decorView != null) {
            decorView.getViewTreeObserver().removeOnWindowShownListener(this);
        }
        }
    }
    }
    };
}
}
Loading