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

Commit 71527448 authored by Diego Vela's avatar Diego Vela Committed by Android (Google) Code Review
Browse files

Merge "Add a listener to know when the list of root views changes." into main

parents 9838c829 f8d9ad19
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -58287,7 +58287,9 @@ package android.view.inspector {
  }
  public final class WindowInspector {
    method @FlaggedApi("android.view.flags.root_view_changed_listener") public static void addGlobalWindowViewsListener(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.util.List<android.view.View>>);
    method @NonNull public static java.util.List<android.view.View> getGlobalWindowViews();
    method @FlaggedApi("android.view.flags.root_view_changed_listener") public static void removeGlobalWindowViewsListener(@NonNull java.util.function.Consumer<java.util.List<android.view.View>>);
  }
}
+56 −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.view;

import android.annotation.NonNull;

import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.function.Consumer;

/**
 * A utilty class to bundle a {@link Consumer} and an {@link Executor}
 * @param <T> the type of value to be reported.
 * @hide
 */
public class ListenerWrapper<T> {

    @NonNull
    private final Consumer<T> mConsumer;
    @NonNull
    private final Executor mExecutor;

    public ListenerWrapper(@NonNull Executor executor, @NonNull Consumer<T> consumer) {
        mExecutor = Objects.requireNonNull(executor);
        mConsumer = Objects.requireNonNull(consumer);
    }

    /**
     * Relays the new value to the {@link Consumer} using the {@link  Executor}
     */
    public void accept(@NonNull T value) {
        mExecutor.execute(() -> mConsumer.accept(value));
    }

    /**
     * Returns {@code true} if the consumer matches the one provided in the constructor,
     * {@code false} otherwise.
     */
    public boolean isConsumerSame(@NonNull Consumer<T> consumer) {
        return mConsumer.equals(consumer);
    }
}
+33 −0
Original line number Diff line number Diff line
@@ -44,6 +44,7 @@ import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
import android.view.inputmethod.InputMethodManager;
import android.view.translation.ListenerGroup;
import android.window.ITrustedPresentationListener;
import android.window.InputTransferToken;
import android.window.TrustedPresentationThresholds;
@@ -58,6 +59,7 @@ import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.WeakHashMap;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -147,6 +149,12 @@ public final class WindowManagerGlobal {

    @UnsupportedAppUsage
    private final ArrayList<View> mViews = new ArrayList<View>();
    /**
     * The {@link ListenerGroup} that is associated to {@link #mViews}.
     * @hide
     */
    @GuardedBy("mLock")
    private final ListenerGroup<List<View>> mWindowViewsListenerGroup = new ListenerGroup<>();
    @UnsupportedAppUsage
    private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
    @UnsupportedAppUsage
@@ -319,6 +327,29 @@ public final class WindowManagerGlobal {
        }
    }

    /**
     * Adds a listener that will be notified whenever {@link #getWindowViews()} changes. The
     * current value is provided immediately. If it was registered previously then this is ano op.
     */
    public void addWindowViewsListener(@NonNull Executor executor,
            @NonNull Consumer<List<View>> consumer) {
        synchronized (mLock) {
            mWindowViewsListenerGroup.addListener(executor, consumer);
            mWindowViewsListenerGroup.accept(getWindowViews());
        }
    }

    /**
     * Removes a listener that was registered in
     * {@link #addWindowViewsListener(Executor, Consumer)}. If it was not registered previously,
     * then this is a no op.
     */
    public void removeWindowViewsListener(@NonNull Consumer<List<View>> consumer) {
        synchronized (mLock) {
            mWindowViewsListenerGroup.removeListener(consumer);
        }
    }

    public View getWindowView(IBinder windowToken) {
        synchronized (mLock) {
            final int numViews = mViews.size();
@@ -454,6 +485,7 @@ public final class WindowManagerGlobal {
            // do this last because it fires off messages to start doing things
            try {
                root.setView(view, wparams, panelParentView, userId);
                mWindowViewsListenerGroup.accept(getWindowViews());
            } catch (RuntimeException e) {
                Log.e(TAG, "Couldn't add view: " + view, e);
                final int viewIndex = (index >= 0) ? index : (mViews.size() - 1);
@@ -575,6 +607,7 @@ public final class WindowManagerGlobal {
                mDyingViews.remove(view);
            }
            allViewsRemoved = mRoots.isEmpty();
            mWindowViewsListenerGroup.accept(getWindowViews());
        }

        // If we don't have any views anymore in our process, we no longer need the
+8 −0
Original line number Diff line number Diff line
@@ -159,3 +159,11 @@ flag {
    bug: "364653005"
    is_fixed_read_only: true
}

flag {
    name: "root_view_changed_listener"
    namespace: "windowing_sdk"
    description: "Implement listener pattern for WindowInspector#getGlobalWindowViews."
    bug: "394397033"
    is_fixed_read_only: false
}
+24 −0
Original line number Diff line number Diff line
@@ -16,11 +16,14 @@

package android.view.inspector;

import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.view.View;
import android.view.WindowManagerGlobal;

import java.util.List;
import java.util.concurrent.Executor;
import java.util.function.Consumer;

/**
 * Provides access to window inspection information.
@@ -37,4 +40,25 @@ public final class WindowInspector {
    public static List<View> getGlobalWindowViews() {
        return WindowManagerGlobal.getInstance().getWindowViews();
    }

    /**
     * Adds a listener that is notified whenever the list of global window views changes. If a
     * {@link Consumer} is already registered this method is a no op.
     * @see #getGlobalWindowViews()
     */
    @FlaggedApi(android.view.flags.Flags.FLAG_ROOT_VIEW_CHANGED_LISTENER)
    public static void addGlobalWindowViewsListener(@NonNull Executor executor,
            @NonNull Consumer<List<View>> consumer) {
        WindowManagerGlobal.getInstance().addWindowViewsListener(executor, consumer);
    }

    /**
     * Removes a listener from getting notifications of global window views changes. If the
     * {@link Consumer} is not registered this method is a no op.
     * @see #addGlobalWindowViewsListener(Executor, Consumer)
     */
    @FlaggedApi(android.view.flags.Flags.FLAG_ROOT_VIEW_CHANGED_LISTENER)
    public static void removeGlobalWindowViewsListener(@NonNull Consumer<List<View>> consumer) {
        WindowManagerGlobal.getInstance().removeWindowViewsListener(consumer);
    }
}
Loading