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

Commit ba41f4b9 authored by Jorim Jaggi's avatar Jorim Jaggi
Browse files

Clean up starting window to prepare for saved surfaces

- Move all starting window logic to AppWindowContainerController
- Use startingView to hold any kind of contents for startingWindow
- Remove some conflicting code which looks very old and doesn't
apply anymore.

Test: Make sure starting window still works.

Bug: 31339431
Change-Id: I018dd013ab7e64a44932b6d54ae9bb4a47f315d3
parent b5d57e10
Loading
Loading
Loading
Loading
+18 −9
Original line number Diff line number Diff line
@@ -436,6 +436,15 @@ public interface WindowManagerPolicy {
        void dismiss();
    }

    /**
     * Holds the contents of a starting window. {@link #addSplashScreen} needs to wrap the
     * contents of the starting window into an class implementing this interface, which then will be
     * held by WM and passed into {@link #removeSplashScreen} when the starting window is no
     * longer needed.
     */
    interface StartingSurface {
    }

    /**
     * Interface for calling back in to the window manager that is private
     * between it and the policy.
@@ -738,17 +747,17 @@ public interface WindowManagerPolicy {
     *        context to for resources.
     *
     * @return Optionally you can return the View that was used to create the
     *         window, for easy removal in removeStartingWindow.
     *         window, for easy removal in removeSplashScreen.
     *
     * @see #removeStartingWindow
     * @see #removeSplashScreen
     */
    public View addStartingWindow(IBinder appToken, String packageName,
            int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel,
            int labelRes, int icon, int logo, int windowFlags, Configuration overrideConfig);
    public StartingSurface addSplashScreen(IBinder appToken, String packageName, int theme,
            CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon,
            int logo, int windowFlags, Configuration overrideConfig);

    /**
     * Called when the first window of an application has been displayed, while
     * {@link #addStartingWindow} has created a temporary initial window for
     * {@link #addSplashScreen} has created a temporary initial window for
     * that application.  You should at this point remove the window from the
     * window manager.  This is called without the window manager locked so
     * that you can call back into it.
@@ -759,11 +768,11 @@ public interface WindowManagerPolicy {
     * even if you previously returned one.
     *
     * @param appToken Token of the application that has started.
     * @param window Window View that was returned by createStartingWindow.
     * @param surface Surface that was returned by {@link #addSplashScreen}.
     *
     * @see #addStartingWindow
     * @see #addSplashScreen
     */
    public void removeStartingWindow(IBinder appToken, View window);
    public void removeSplashScreen(IBinder appToken, StartingSurface surface);

    /**
     * Prepare for a window being added to the window manager.  You can throw an
+18 −18
Original line number Diff line number Diff line
@@ -251,9 +251,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
    static final boolean DEBUG_INPUT = false;
    static final boolean DEBUG_KEYGUARD = false;
    static final boolean DEBUG_LAYOUT = false;
    static final boolean DEBUG_STARTING_WINDOW = false;
    static final boolean DEBUG_SPLASH_SCREEN = false;
    static final boolean DEBUG_WAKEUP = false;
    static final boolean SHOW_STARTING_ANIMATIONS = true;
    static final boolean SHOW_SPLASH_SCREENS = true;

    // Whether to allow dock apps with METADATA_DOCK_HOME to temporarily take over the Home key.
    // No longer recommended for desk docks;
@@ -2794,10 +2794,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {

    /** {@inheritDoc} */
    @Override
    public View addStartingWindow(IBinder appToken, String packageName, int theme,
            CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
            int icon, int logo, int windowFlags, Configuration overrideConfig) {
        if (!SHOW_STARTING_ANIMATIONS) {
    public StartingSurface addSplashScreen(IBinder appToken, String packageName, int theme,
            CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon,
            int logo, int windowFlags, Configuration overrideConfig) {
        if (!SHOW_SPLASH_SCREENS) {
            return null;
        }
        if (packageName == null) {
@@ -2809,7 +2809,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {

        try {
            Context context = mContext;
            if (DEBUG_STARTING_WINDOW) Slog.d(TAG, "addStartingWindow " + packageName
            if (DEBUG_SPLASH_SCREEN) Slog.d(TAG, "addSplashScreen " + packageName
                    + ": nonLocalizedLabel=" + nonLocalizedLabel + " theme="
                    + Integer.toHexString(theme));
            if (theme != context.getThemeResId() || labelRes != 0) {
@@ -2822,8 +2822,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
            }

            if (overrideConfig != null && !overrideConfig.equals(EMPTY)) {
                if (DEBUG_STARTING_WINDOW) Slog.d(TAG, "addStartingWindow: creating context based"
                        + " on overrideConfig" + overrideConfig + " for starting window");
                if (DEBUG_SPLASH_SCREEN) Slog.d(TAG, "addSplashScreen: creating context based"
                        + " on overrideConfig" + overrideConfig + " for splash screen");
                final Context overrideContext = context.createConfigurationContext(overrideConfig);
                overrideContext.setTheme(theme);
                final TypedArray typedArray = overrideContext.obtainStyledAttributes(
@@ -2833,7 +2833,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                    // We want to use the windowBackground for the override context if it is
                    // available, otherwise we use the default one to make sure a themed starting
                    // window is displayed for the app.
                    if (DEBUG_STARTING_WINDOW) Slog.d(TAG, "addStartingWindow: apply overrideConfig"
                    if (DEBUG_SPLASH_SCREEN) Slog.d(TAG, "addSplashScreen: apply overrideConfig"
                            + overrideConfig + " to starting window resId=" + resId);
                    context = overrideContext;
                }
@@ -2895,19 +2895,19 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
            }

            params.setTitle("Starting " + packageName);
            params.setTitle("Splash Screen " + packageName);

            wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
            view = win.getDecorView();

            if (DEBUG_STARTING_WINDOW) Slog.d(TAG, "Adding starting window for "
            if (DEBUG_SPLASH_SCREEN) Slog.d(TAG, "Adding splash screen window for "
                + packageName + " / " + appToken + ": " + (view.getParent() != null ? view : null));

            wm.addView(view, params);

            // Only return the view if it was successfully added to the
            // window manager... which we can tell by it having a parent.
            return view.getParent() != null ? view : null;
            return view.getParent() != null ? new SplashScreenSurface(view) : null;
        } catch (WindowManager.BadTokenException e) {
            // ignore
            Log.w(TAG, appToken + " already running, starting window not displayed. " +
@@ -2929,13 +2929,13 @@ public class PhoneWindowManager implements WindowManagerPolicy {

    /** {@inheritDoc} */
    @Override
    public void removeStartingWindow(IBinder appToken, View window) {
        if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Removing starting window for " + appToken + ": "
                + window + " Callers=" + Debug.getCallers(4));
    public void removeSplashScreen(IBinder appToken, StartingSurface surface) {
        if (DEBUG_SPLASH_SCREEN) Slog.v(TAG, "Removing splash screen window for " + appToken + ": "
                + surface + " Callers=" + Debug.getCallers(4));

        if (window != null) {
        if (surface != null) {
            WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
            wm.removeView(window);
            wm.removeView(((SplashScreenSurface) surface).view);
        }
    }

+38 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 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.server.policy;

import android.view.View;
import android.view.WindowManagerPolicy;
import android.view.WindowManagerPolicy.StartingSurface;

import com.android.internal.policy.DecorView;
import com.android.internal.policy.PhoneWindow;

/**
 * Holds the contents of a splash screen starting window, i.e. the {@link DecorView} of a
 * {@link PhoneWindow}. This is just a wrapper such that we can return it from
 * {@link WindowManagerPolicy#addSplashScreen}.
 */
class SplashScreenSurface implements StartingSurface {

    final View view;

    SplashScreenSurface(View view) {
        this.view = view;
    }
}
+128 −16
Original line number Diff line number Diff line
@@ -27,21 +27,21 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WIND
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.H.ADD_STARTING;

import android.graphics.Bitmap;
import android.os.Trace;
import com.android.server.AttributeCache;

import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.os.Binder;
import android.os.Debug;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Looper;
import android.os.Trace;
import android.util.Slog;
import android.view.IApplicationToken;
import android.view.WindowManagerPolicy.StartingSurface;

import com.android.server.AttributeCache;
/**
 * Controller for the app window token container. This is created by activity manager to link
 * activity records to the app window token container they use in window manager.
@@ -52,6 +52,7 @@ public class AppWindowContainerController
        extends WindowContainerController<AppWindowToken, AppWindowContainerListener> {

    private final IApplicationToken mToken;
    private final Handler mHandler = new Handler(Looper.getMainLooper());

    private final Runnable mOnWindowsDrawn = () -> {
        if (mListener == null) {
@@ -80,6 +81,94 @@ public class AppWindowContainerController
        mListener.onWindowsGone();
    };

    private final Runnable mAddStartingWindow = () -> {
        final StartingData startingData;
        final Configuration mergedOverrideConfiguration;

        synchronized (mWindowMap) {
            startingData = mContainer.startingData;
            mergedOverrideConfiguration = mContainer.getMergedOverrideConfiguration();
        }

        if (startingData == null) {
            // Animation has been canceled... do nothing.
            return;
        }

        if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Add starting "
                + this + ": pkg=" + mContainer.startingData.pkg);

        StartingSurface contents = null;
        try {
            contents = mService.mPolicy.addSplashScreen(mContainer.token, startingData.pkg,
                    startingData.theme, startingData.compatInfo, startingData.nonLocalizedLabel,
                    startingData.labelRes, startingData.icon, startingData.logo,
                    startingData.windowFlags, mergedOverrideConfiguration);
        } catch (Exception e) {
            Slog.w(TAG_WM, "Exception when adding starting window", e);
        }
        if (contents != null) {
            boolean abort = false;

            synchronized(mWindowMap) {
                if (mContainer.removed || mContainer.startingData == null) {
                    // If the window was successfully added, then
                    // we need to remove it.
                    if (mContainer.startingWindow != null) {
                        if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
                                "Aborted starting " + mContainer
                                        + ": removed=" + mContainer.removed
                                        + " startingData=" + mContainer.startingData);
                        mContainer.startingWindow = null;
                        mContainer.startingData = null;
                        abort = true;
                    }
                } else {
                    mContainer.startingSurface = contents;
                }
                if (DEBUG_STARTING_WINDOW && !abort) Slog.v(TAG_WM,
                        "Added starting " + mContainer
                                + ": startingWindow="
                                + mContainer.startingWindow + " startingView="
                                + mContainer.startingSurface);
            }

            if (abort) {
                try {
                    mService.mPolicy.removeSplashScreen(mContainer.token, contents);
                } catch (Exception e) {
                    Slog.w(TAG_WM, "Exception when removing starting window", e);
                }
            }
        }
    };

    private final Runnable mRemoveStartingWindow = () -> {
        IBinder token = null;
        StartingSurface contents = null;
        synchronized (mWindowMap) {
            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Remove starting "
                    + mContainer + ": startingWindow="
                    + mContainer.startingWindow + " startingView="
                    + mContainer.startingSurface);
            if (mContainer.startingWindow != null) {
                contents = mContainer.startingSurface;
                token = mContainer.token;
                mContainer.startingData = null;
                mContainer.startingSurface = null;
                mContainer.startingWindow = null;
                mContainer.startingDisplayed = false;
            }
        }
        if (contents != null) {
            try {
                mService.mPolicy.removeSplashScreen(token, contents);
            } catch (Exception e) {
                Slog.w(TAG_WM, "Exception when removing starting window", e);
            }
        }
    };

    public AppWindowContainerController(IApplicationToken token,
            AppWindowContainerListener listener, int taskId, int index, int requestedOrientation,
            boolean fullscreen, boolean showForAllUsers, int configChanges,
@@ -393,19 +482,42 @@ public class AppWindowContainerController
            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating StartingData");
            mContainer.startingData = new StartingData(pkg, theme, compatInfo, nonLocalizedLabel,
                    labelRes, icon, logo, windowFlags);
            final Message m = mService.mH.obtainMessage(ADD_STARTING, mContainer);
            scheduleAddStartingWindow();
        }
        return true;
    }

    void scheduleAddStartingWindow() {

        // Note: we really want to do sendMessageAtFrontOfQueue() because we
        // want to process the message ASAP, before any other queued
        // messages.
        if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Enqueueing ADD_STARTING");
            mService.mH.sendMessageAtFrontOfQueue(m);
        }
        return true;
        mHandler.postAtFrontOfQueue(mAddStartingWindow);
    }

    public void removeStartingWindow() {
        synchronized (mWindowMap) {
            mService.scheduleRemoveStartingWindowLocked(mContainer);
            if (mHandler.hasCallbacks(mRemoveStartingWindow)) {
                // Already scheduled.
                return;
            }

            if (mContainer.startingWindow == null) {
                if (mContainer.startingData != null) {
                    // Starting window has not been added yet, but it is scheduled to be added.
                    // Go ahead and cancel the request.
                    if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
                            "Clearing startingData for token=" + mContainer);
                    mContainer.startingData = null;
                }
                return;
            }

            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, Debug.getCallers(1)
                    + ": Schedule remove starting " + mContainer
                    + " startingWindow=" + mContainer.startingWindow);
            mHandler.post(mRemoveStartingWindow);
        }
    }

@@ -508,15 +620,15 @@ public class AppWindowContainerController


    void reportWindowsDrawn() {
        mService.mH.post(mOnWindowsDrawn);
        mHandler.post(mOnWindowsDrawn);
    }

    void reportWindowsVisible() {
        mService.mH.post(mOnWindowsVisible);
        mHandler.post(mOnWindowsVisible);
    }

    void reportWindowsGone() {
        mService.mH.post(mOnWindowsGone);
        mHandler.post(mOnWindowsGone);
    }

    /** Calls directly into activity manager so window manager lock shouldn't held. */
+33 −32
Original line number Diff line number Diff line
@@ -29,9 +29,9 @@ import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.wm.AppTransition.TRANSIT_UNSET;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
@@ -47,22 +47,21 @@ import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
import static com.android.server.wm.WindowManagerService.logWithStack;

import android.os.Debug;
import com.android.internal.util.ToBooleanFunction;
import com.android.server.input.InputApplicationHandle;
import com.android.server.wm.WindowManagerService.H;

import android.annotation.NonNull;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Binder;
import android.os.Debug;
import android.os.IBinder;
import android.os.Message;
import android.os.SystemClock;
import android.util.Slog;
import android.view.IApplicationToken;
import android.view.View;
import android.view.WindowManager;
import android.view.WindowManagerPolicy.StartingSurface;

import com.android.internal.util.ToBooleanFunction;
import com.android.server.input.InputApplicationHandle;
import com.android.server.wm.WindowManagerService.H;

import java.io.PrintWriter;
import java.util.ArrayDeque;
@@ -138,7 +137,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
    // Information about an application starting window if displayed.
    StartingData startingData;
    WindowState startingWindow;
    View startingView;
    StartingSurface startingSurface;
    boolean startingDisplayed;
    boolean startingMoved;
    boolean firstWindowDrawn;
@@ -213,8 +212,9 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
            // it from behind the starting window, so there is no need for it to also be doing its
            // own stuff.
            winAnimator.clearAnimation();
            winAnimator.mService.mFinishedStarting.add(this);
            winAnimator.mService.mH.sendEmptyMessage(H.FINISHED_STARTING);
            if (getController() != null) {
                getController().removeStartingWindow();
            }
        }
        updateReportedVisibilityLocked();
    }
@@ -439,8 +439,6 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
    }

    void onRemovedFromDisplay() {
        AppWindowToken startingToken = null;

        if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, "Removing app token: " + this);

        boolean delayed = setVisibility(null, false, TRANSIT_UNSET, true, mVoiceInteraction);
@@ -461,6 +459,10 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
        if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG_WM, "removeAppToken: "
                + this + " delayed=" + delayed + " Callers=" + Debug.getCallers(4));

        if (startingData != null && getController() != null) {
            getController().removeStartingWindow();
        }

        final TaskStack stack = mTask.mStack;
        if (delayed && !isEmpty()) {
            // set the token aside because it has an active animation to be finished
@@ -477,9 +479,6 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
        }

        removed = true;
        if (startingData != null) {
            startingToken = this;
        }
        stopFreezingScreen(true, true);
        if (mService.mFocusedApp == this) {
            if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "Removing focused app token:" + this);
@@ -491,9 +490,6 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
        if (!delayed) {
            updateReportedVisibilityLocked();
        }

        // Will only remove if startingToken non null.
        mService.scheduleRemoveStartingWindowLocked(startingToken);
    }

    void clearAnimatingFlags() {
@@ -557,7 +553,9 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
        mAppStopped = true;
        destroySurfaces();
        // Remove any starting window that was added for this app if they are still around.
        mTask.mService.scheduleRemoveStartingWindowLocked(this);
        if (getController() != null) {
            getController().removeStartingWindow();
        }
    }

    /**
@@ -667,16 +665,20 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
        // TODO: Something smells about the code below...Is there a better way?
        if (startingWindow == win) {
            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Notify removed startingWindow " + win);
            mService.scheduleRemoveStartingWindowLocked(this);
            if (getController() != null) {
                getController().removeStartingWindow();
            }
        } else if (mChildren.size() == 0 && startingData != null) {
            // If this is the last window and we had requested a starting transition window,
            // well there is no point now.
            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Nulling last startingWindow");
            startingData = null;
        } else if (mChildren.size() == 1 && startingView != null) {
        } else if (mChildren.size() == 1 && startingSurface != null) {
            // If this is the last window except for a starting transition window,
            // we need to get rid of the starting transition.
            mService.scheduleRemoveStartingWindowLocked(this);
            if (getController() != null) {
                getController().removeStartingWindow();
            }
        }
    }

@@ -1015,7 +1017,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
        }

        final WindowState tStartingWindow = fromToken.startingWindow;
        if (tStartingWindow != null && fromToken.startingView != null) {
        if (tStartingWindow != null && fromToken.startingSurface != null) {
            // In this case, the starting icon has already been displayed, so start
            // letting windows get shown immediately without any more transitions.
            mService.mSkipAppTransitionAnimation = true;
@@ -1027,13 +1029,13 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree

            // Transfer the starting window over to the new token.
            startingData = fromToken.startingData;
            startingView = fromToken.startingView;
            startingSurface = fromToken.startingSurface;
            startingDisplayed = fromToken.startingDisplayed;
            fromToken.startingDisplayed = false;
            startingWindow = tStartingWindow;
            reportedVisible = fromToken.reportedVisible;
            fromToken.startingData = null;
            fromToken.startingView = null;
            fromToken.startingSurface = null;
            fromToken.startingWindow = null;
            fromToken.startingMoved = true;
            tStartingWindow.mToken = this;
@@ -1080,10 +1082,9 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
            startingData = fromToken.startingData;
            fromToken.startingData = null;
            fromToken.startingMoved = true;
            final Message m = mService.mH.obtainMessage(H.ADD_STARTING, this);
            // Note: we really want to do sendMessageAtFrontOfQueue() because we want to process the
            // message ASAP, before any other queued messages.
            mService.mH.sendMessageAtFrontOfQueue(m);
            if (getController() != null) {
                getController().scheduleAddStartingWindow();
            }
            return true;
        }

@@ -1421,10 +1422,10 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
                    pw.print(" firstWindowDrawn="); pw.print(firstWindowDrawn);
                    pw.print(" mIsExiting="); pw.println(mIsExiting);
        }
        if (startingWindow != null || startingView != null
        if (startingWindow != null || startingSurface != null
                || startingDisplayed || startingMoved) {
            pw.print(prefix); pw.print("startingWindow="); pw.print(startingWindow);
                    pw.print(" startingView="); pw.print(startingView);
                    pw.print(" startingSurface="); pw.print(startingSurface);
                    pw.print(" startingDisplayed="); pw.print(startingDisplayed);
                    pw.print(" startingMoved="); pw.println(startingMoved);
        }
Loading