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

Commit 8c657a1b authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Enable WindowContext to set window and override type" into main

parents 48f459e7 bc4b5c76
Loading
Loading
Loading
Loading
+13 −1
Original line number Diff line number Diff line
@@ -2725,7 +2725,9 @@ public interface WindowManager extends ViewManager {
                TYPE_APPLICATION_OVERLAY,
                TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY,
                TYPE_NOTIFICATION_SHADE,
                TYPE_STATUS_BAR_ADDITIONAL
                TYPE_STATUS_BAR_ADDITIONAL,
                // TODO(b/398759994): Rename to TYPE_INVALID
                INVALID_WINDOW_TYPE,
        })
        @Retention(RetentionPolicy.SOURCE)
        public @interface WindowType {}
@@ -2750,6 +2752,16 @@ public interface WindowManager extends ViewManager {
            return false;
        }

        /**
         * Returns {@code true} if the given {@code type} is a sub-window type.
         *
         * @hide
         */
        public static boolean isSubWindowType(@WindowType int type) {
            return (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW)
                    || type == TYPE_STATUS_BAR_SUB_PANEL;
        }

        /** @deprecated this is ignored, this value is set automatically when needed. */
        @Deprecated
        public static final int MEMORY_TYPE_NORMAL = 0;
+22 −0
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@ import android.view.inputmethod.InputMethodManager;
import android.window.ITrustedPresentationListener;
import android.window.InputTransferToken;
import android.window.TrustedPresentationThresholds;
import android.window.WindowContext;

import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
@@ -1107,6 +1108,27 @@ public final class WindowManagerGlobal {
            mInputEventReceiver = inputEventReceiver;
        }
    }

    /**
     * Checks whether {@link WindowContext#getWindowTypeOverride()} can be applied when
     * {@link WindowManager#addView} or {@link WindowManager#updateViewLayout}.
     *
     * @param windowTypeToOverride the window type to override
     * @param view the view that applies the window type
     */
    public boolean canApplyWindowTypeOverride(
            @WindowManager.LayoutParams.WindowType int windowTypeToOverride,
            @NonNull View view) {
        final int index = findViewLocked(view, false /* required */);
        if (index < 0) {
            // The view has not been added yet. Window type can be overridden.
            return true;
        }
        final WindowManager.LayoutParams params = mParams.get(index);
        // If the view has been attached, we should make sure the override type matches the existing
        // one. The window type can't be changed after the view was added.
        return windowTypeToOverride == params.type;
    }
}

final class WindowLeaked extends AndroidRuntimeException {
+42 −11
Original line number Diff line number Diff line
@@ -16,8 +16,8 @@

package android.view;

import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
import static android.view.WindowManager.LayoutParams.isSubWindowType;
import static android.window.WindowProviderService.isWindowProviderService;

import static com.android.window.flags.Flags.screenRecordingCallbacks;
@@ -49,6 +49,7 @@ import android.window.WindowProvider;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.IResultReceiver;
import com.android.window.flags.Flags;

import java.util.ArrayList;
import java.util.Iterator;
@@ -93,7 +94,7 @@ public class WindowManagerImpl implements WindowManager {
    @UiContext
    @VisibleForTesting
    public final Context mContext;
    private final Window mParentWindow;
    private Window mParentWindow;

    /**
     * If {@link LayoutParams#token} is {@code null} and no parent window is specified, the value
@@ -152,8 +153,16 @@ public class WindowManagerImpl implements WindowManager {
        mDefaultToken = token;
    }

    /**
     * Sets the parent window.
     */
    public void setParentWindow(@NonNull Window parentWindow) {
        mParentWindow = parentWindow;
    }

    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyWindowTypeOverrideIfNeeded(params, view);
        applyTokens(params);
        mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
                mContext.getUserId());
@@ -161,6 +170,7 @@ public class WindowManagerImpl implements WindowManager {

    @Override
    public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyWindowTypeOverrideIfNeeded(params, view);
        applyTokens(params);
        mGlobal.updateViewLayout(view, params);
    }
@@ -179,16 +189,10 @@ public class WindowManagerImpl implements WindowManager {
    }

    private void assertWindowContextTypeMatches(@LayoutParams.WindowType int windowType) {
        if (!(mContext instanceof WindowProvider)) {
        if (!(mContext instanceof WindowProvider windowProvider)) {
            return;
        }
        // Don't need to check sub-window type because sub window should be allowed to be attached
        // to the parent window.
        if (windowType >= FIRST_SUB_WINDOW && windowType <= LAST_SUB_WINDOW) {
            return;
        }
        final WindowProvider windowProvider = (WindowProvider) mContext;
        if (windowProvider.getWindowType() == windowType) {
        if (windowProvider.isValidWindowType(windowType)) {
            return;
        }
        IllegalArgumentException exception = new IllegalArgumentException("Window type mismatch."
@@ -206,6 +210,33 @@ public class WindowManagerImpl implements WindowManager {
                + " match type in WindowManager.LayoutParams", exception);
    }

    private void applyWindowTypeOverrideIfNeeded(
            @NonNull ViewGroup.LayoutParams params,
            @NonNull View view) {
        if (!Flags.enableWindowContextOverrideType()) {
            return;
        }
        if (!(params instanceof WindowManager.LayoutParams wparams)) {
            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
        }
        if (!(mContext instanceof WindowProvider windowProvider)) {
            return;
        }
        final int windowTypeOverride = windowProvider.getWindowTypeOverride();
        if (windowTypeOverride == INVALID_WINDOW_TYPE) {
            return;
        }
        if (!mGlobal.canApplyWindowTypeOverride(windowTypeOverride, view)) {
            return;
        }
        if (isSubWindowType(windowTypeOverride) && mParentWindow == null) {
            throw new IllegalArgumentException("Sub-window must be attached to the parent window."
                    + " Please try to obtain WindowManager from a window class, call "
                    + "WindowContext#attachWindow before adding any sub-windows.");
        }
        wparams.type = windowTypeOverride;
    }

    @Override
    public void removeView(View view) {
        mGlobal.removeView(view, false);
+226 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.window;

import android.content.Context;
import android.content.res.Configuration;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.view.InputQueue;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;

import androidx.annotation.Nullable;

/**
 * A helper class that has the minimum implementation of {@link Window}.
 *
 * @hide
 */
public abstract class WindowBase extends Window {

    public WindowBase(Context context) {
        super(context);
    }

    @Override
    public void takeSurface(SurfaceHolder.Callback2 callback) {}

    @Override
    public void takeInputQueue(InputQueue.Callback callback) {}

    @Override
    public boolean isFloating() {
        return false;
    }

    @Override
    public void alwaysReadCloseOnTouchAttr() {}

    @Override
    public void setContentView(int layoutResID) {}

    @Override
    public void setContentView(View view) {}

    @Override
    public void setContentView(View view, ViewGroup.LayoutParams params) {}

    @Override
    public void addContentView(View view, ViewGroup.LayoutParams params) {}

    @Override
    public void clearContentView() {}

    @Nullable
    @Override
    public View getCurrentFocus() {
        return null;
    }

    @Override
    public void setTitle(CharSequence title) {}

    @Override
    public void setTitleColor(int textColor) {}

    @Override
    public void openPanel(int featureId, KeyEvent event) {}

    @Override
    public void closePanel(int featureId) {}

    @Override
    public void togglePanel(int featureId, KeyEvent event) {}

    @Override
    public void invalidatePanelMenu(int featureId) {}

    @Override
    public boolean performPanelShortcut(int featureId, int keyCode, KeyEvent event, int flags) {
        return false;
    }

    @Override
    public boolean performPanelIdentifierAction(int featureId, int id, int flags) {
        return false;
    }

    @Override
    public void closeAllPanels() {}

    @Override
    public boolean performContextMenuIdentifierAction(int id, int flags) {
        return false;
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {}

    @Override
    public void setBackgroundDrawable(Drawable drawable) {}

    @Override
    public void setFeatureDrawableResource(int featureId, int resId) {}

    @Override
    public void setFeatureDrawableUri(int featureId, Uri uri) {}

    @Override
    public void setFeatureDrawable(int featureId, Drawable drawable) {}

    @Override
    public void setFeatureDrawableAlpha(int featureId, int alpha) {}

    @Override
    public void setFeatureInt(int featureId, int value) {}

    @Override
    public void takeKeyEvents(boolean get) {}

    @Override
    public boolean superDispatchKeyEvent(KeyEvent event) {
        return false;
    }

    @Override
    public boolean superDispatchKeyShortcutEvent(KeyEvent event) {
        return false;
    }

    @Override
    public boolean superDispatchTouchEvent(MotionEvent event) {
        return false;
    }

    @Override
    public boolean superDispatchTrackballEvent(MotionEvent event) {
        return false;
    }

    @Override
    public boolean superDispatchGenericMotionEvent(MotionEvent event) {
        return false;
    }

    @Override
    public View peekDecorView() {
        return null;
    }

    @Override
    public Bundle saveHierarchyState() {
        return null;
    }

    @Override
    public void restoreHierarchyState(Bundle savedInstanceState) {}

    @Override
    protected void onActive() {}

    @Override
    public void setChildDrawable(int featureId, Drawable drawable) {}

    @Override
    public void setChildInt(int featureId, int value) {}

    @Override
    public boolean isShortcutKey(int keyCode, KeyEvent event) {
        return false;
    }

    @Override
    public void setVolumeControlStream(int streamType) {}

    @Override
    public int getVolumeControlStream() {
        return 0;
    }

    @Override
    public int getStatusBarColor() {
        return 0;
    }

    @Override
    public void setStatusBarColor(int color) {}

    @Override
    public int getNavigationBarColor() {
        return 0;
    }

    @Override
    public void setNavigationBarColor(int color) {}

    @Override
    public void setDecorCaptionShade(int decorCaptionShade) {}

    @Override
    public void setResizingCaptionDrawable(Drawable drawable) {}

    @Override
    public void onMultiWindowModeChanged() {}

    @Override
    public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {}
}
+120 −8
Original line number Diff line number Diff line
@@ -15,8 +15,11 @@
 */
package android.window;

import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
import static android.view.WindowManagerImpl.createWindowContextWindowManager;

import static java.util.Objects.requireNonNull;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UiContext;
@@ -27,7 +30,12 @@ import android.content.ContextWrapper;
import android.content.res.Configuration;
import android.os.Bundle;
import android.view.Display;
import android.view.LayoutInflater;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams.WindowType;
import android.view.WindowManagerImpl;

import com.android.internal.annotations.VisibleForTesting;
import com.android.window.flags.Flags;
@@ -46,15 +54,18 @@ import java.lang.ref.Reference;
@UiContext
public class WindowContext extends ContextWrapper implements WindowProvider,
        ConfigurationDispatcher {
    private final WindowManager mWindowManager;
    @WindowManager.LayoutParams.WindowType
    @WindowType
    private final int mType;
    @Nullable
    private final Bundle mOptions;
    @WindowType
    private int mWindowTypeOverride = INVALID_WINDOW_TYPE;
    private final ComponentCallbacksController mCallbacksController =
            new ComponentCallbacksController();
    private final WindowContextController mController;

    private WindowManager mWindowManager;

    /**
     * Default implementation of {@link WindowContext}
     * <p>
@@ -73,14 +84,16 @@ public class WindowContext extends ContextWrapper implements WindowProvider,
     * @param options A bundle used to pass window-related options.
     * @see DisplayAreaInfo#rootDisplayAreaId
     */
    public WindowContext(@NonNull Context base, int type, @Nullable Bundle options) {
    public WindowContext(@NonNull Context base,
                         @WindowType int type,
                         @Nullable Bundle options) {
        super(base);

        mType = type;
        mOptions = options;
        mWindowManager = createWindowContextWindowManager(this);
        WindowTokenClient token = (WindowTokenClient) getWindowContextToken();
        mController = new WindowContextController(token);
        mController = new WindowContextController(requireNonNull(token));

        Reference.reachabilityFence(this);
    }
@@ -153,12 +166,58 @@ public class WindowContext extends ContextWrapper implements WindowProvider,
        mCallbacksController.unregisterCallbacks(callback);
    }

    /** Dispatch {@link Configuration} to each {@link ComponentCallbacks}. */
    @Override
    public void dispatchConfigurationChanged(@NonNull Configuration newConfig) {
        mCallbacksController.dispatchConfigurationChanged(newConfig);
    /**
     * If set, this {@code WindowContext} will override the window type when
     * {@link WindowManager#addView} or {@link WindowManager#updateViewLayout}.
     * <p>
     * Allowed window types are {@link #getWindowType()} and
     * any sub-window types. If set to {@link WindowManager.LayoutParams#INVALID_WINDOW_TYPE},
     * this {@code WindowContext} won't override the type.
     * <p>
     * Note:
     * <ol>
     *   <li>If a view is attached, the window type won't be overridden to another window type.</li>
     *   <li>If a sub-window override is requested, a parent window must be prepared. It can
     *   be either by using {@link WindowManager} from a {@link Window} or calling
     *   {@link #attachWindow(View)} before adding any sub-windows, or
     *   {@link IllegalArgumentException} throws when {@link WindowManager#addView}.
     *   </li>
     * </ol>
     *
     * @throws IllegalArgumentException if the passed {@code windowTypeOverride} is not an allowed
     *     window type mentioned above.
     */
    public void setWindowTypeOverride(@WindowType int windowTypeOverride) {
        if (!Flags.enableWindowContextOverrideType()) {
            return;
        }
        if (!isValidWindowType(windowTypeOverride) && windowTypeOverride != INVALID_WINDOW_TYPE) {
            throw new IllegalArgumentException(
                    "The window type override must be either "
                    + mType
                    + " or a sub window type, but it's "
                    + windowTypeOverride
            );
        }
        mWindowTypeOverride = windowTypeOverride;
    }

    /**
     * Associates {@code window} to this {@code WindowContext} and attach {@code window} to
     * associated {@link WindowManager}.
     * <p>
     * Note that this method must be called before {@link WindowManager#addView}.
     */
    public void attachWindow(@NonNull View window) {
        if (!Flags.enableWindowContextOverrideType()) {
            return;
        }
        final Window wrapper = new WindowWrapper(this, window);
        ((WindowManagerImpl) mWindowManager).setParentWindow(wrapper);
    }

/* === WindowProvider APIs === */

    @Override
    public int getWindowType() {
        return mType;
@@ -170,9 +229,62 @@ public class WindowContext extends ContextWrapper implements WindowProvider,
        return mOptions;
    }

    @WindowType
    @Override
    public int getWindowTypeOverride() {
        return mWindowTypeOverride;
    }

/* === ConfigurationDispatcher APIs === */

    @Override
    public boolean shouldReportPrivateChanges() {
        // Always dispatch config changes to WindowContext.
        return true;
    }

    /** Dispatch {@link Configuration} to each {@link ComponentCallbacks}. */
    @Override
    public void dispatchConfigurationChanged(@NonNull Configuration newConfig) {
        mCallbacksController.dispatchConfigurationChanged(newConfig);
    }

    /**
     * A simple {@link Window} wrapper that is used to pass to
     * {@link WindowManagerImpl#createLocalWindowManager(Window)}.
     */
    private static class WindowWrapper extends WindowBase {

        @NonNull
        private final View mDecorView;

        /**
         * The {@link WindowWrapper} constructor.
         *
         * @param context the associated {@link WindowContext}`
         * @param decorView the view to be wrapped as {@link Window}'s decor view.
         */
        WindowWrapper(@NonNull @UiContext Context context, @NonNull View decorView) {
            super(context);

            mDecorView = requireNonNull(decorView);
        }

        @NonNull
        @Override
        public LayoutInflater getLayoutInflater() {
            return LayoutInflater.from(mDecorView.getContext());
        }

        @NonNull
        @Override
        public View getDecorView() {
            return mDecorView;
        }

        @Override
        public View peekDecorView() {
            return mDecorView;
        }
    }
}
Loading