All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.com.android.server.wm.InsetsStateController Maven / Gradle / Ivy

/*
 * Copyright (C) 2018 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.wm;

import static android.view.InsetsState.TYPE_IME;
import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
import static android.view.InsetsState.TYPE_TOP_BAR;
import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
import static android.view.ViewRootImpl.sNewInsetsMode;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.SparseArray;
import android.view.InsetsSource;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.ViewRootImpl;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.function.Consumer;

/**
 * Manages global window inset state in the system represented by {@link InsetsState}.
 */
class InsetsStateController {

    private final InsetsState mLastState = new InsetsState();
    private final InsetsState mState = new InsetsState();
    private final DisplayContent mDisplayContent;

    private final ArrayMap mControllers = new ArrayMap<>();
    private final ArrayMap> mWinControlTypeMap = new ArrayMap<>();
    private final SparseArray mTypeWinControlMap = new SparseArray<>();
    private final ArraySet mPendingControlChanged = new ArraySet<>();

    private final Consumer mDispatchInsetsChanged = w -> {
        if (w.isVisible()) {
            w.notifyInsetsChanged();
        }
    };

    InsetsStateController(DisplayContent displayContent) {
        mDisplayContent = displayContent;
    }

    /**
     * When dispatching window state to the client, we'll need to exclude the source that represents
     * the window that is being dispatched.
     *
     * @param target The client we dispatch the state to.
     * @return The state stripped of the necessary information.
     */
    InsetsState getInsetsForDispatch(WindowState target) {
        final InsetsSourceProvider provider = target.getInsetProvider();
        if (provider == null) {
            return mState;
        }

        final InsetsState state = new InsetsState();
        state.set(mState);
        final int type = provider.getSource().getType();
        state.removeSource(type);

        // Navigation bar doesn't get influenced by anything else
        if (type == TYPE_NAVIGATION_BAR) {
            state.removeSource(TYPE_IME);
            state.removeSource(TYPE_TOP_BAR);
        }
        return state;
    }

    @Nullable InsetsSourceControl[] getControlsForDispatch(WindowState target) {
        ArrayList controlled = mWinControlTypeMap.get(target);
        if (controlled == null) {
            return null;
        }
        final int size = controlled.size();
        final InsetsSourceControl[] result = new InsetsSourceControl[size];
        for (int i = 0; i < size; i++) {
            result[i] = mControllers.get(controlled.get(i)).getControl();
        }
        return result;
    }

    /**
     * @return The provider of a specific type.
     */
    InsetsSourceProvider getSourceProvider(int type) {
        return mControllers.computeIfAbsent(type,
                key -> new InsetsSourceProvider(mState.getSource(key), this, mDisplayContent));
    }

    /**
     * Called when a layout pass has occurred.
     */
    void onPostLayout() {
        mState.setDisplayFrame(mDisplayContent.getBounds());
        for (int i = mControllers.size() - 1; i>= 0; i--) {
            mControllers.valueAt(i).onPostLayout();
        }
        if (!mLastState.equals(mState)) {
            mLastState.set(mState, true /* copySources */);
            notifyInsetsChanged();
        }
    }

    void onInsetsModified(WindowState windowState, InsetsState state) {
        boolean changed = false;
        for (int i = state.getSourcesCount() - 1; i >= 0; i--) {
            final InsetsSource source = state.sourceAt(i);
            final InsetsSourceProvider provider = mControllers.get(source.getType());
            if (provider == null) {
                continue;
            }
            changed |= provider.onInsetsModified(windowState, source);
        }
        if (changed) {
            notifyInsetsChanged();
        }
    }

    void onImeTargetChanged(@Nullable WindowState imeTarget) {
        onControlChanged(TYPE_IME, imeTarget);
        notifyPendingInsetsControlChanged();
    }

    /**
     * Called when the top opaque fullscreen window that is able to control the system bars changes.
     *
     * @param controllingWindow The window that is now able to control the system bars appearance
     *                          and visibility.
     */
    void onBarControllingWindowChanged(@Nullable WindowState controllingWindow) {
        // TODO: Apply policy that determines whether controllingWindow is able to control system
        // bars

        // TODO: Depending on the form factor, mapping is different
        onControlChanged(TYPE_TOP_BAR, controllingWindow);
        onControlChanged(TYPE_NAVIGATION_BAR, controllingWindow);
        notifyPendingInsetsControlChanged();
    }

    void notifyControlRevoked(@NonNull WindowState previousControllingWin,
            InsetsSourceProvider provider) {
        removeFromControlMaps(previousControllingWin, provider.getSource().getType());
    }

    private void onControlChanged(int type, @Nullable WindowState win) {
        final WindowState previous = mTypeWinControlMap.get(type);
        if (win == previous) {
            return;
        }
        final InsetsSourceProvider controller = getSourceProvider(type);
        if (controller == null) {
            return;
        }
        if (!controller.isControllable()) {
            return;
        }
        controller.updateControlForTarget(win, false /* force */);
        if (previous != null) {
            removeFromControlMaps(previous, type);
            mPendingControlChanged.add(previous);
        }
        if (win != null) {
            addToControlMaps(win, type);
            mPendingControlChanged.add(win);
        }
    }

    private void removeFromControlMaps(@NonNull WindowState win, int type) {
        final ArrayList array = mWinControlTypeMap.get(win);
        if (array == null) {
            return;
        }
        array.remove((Integer) type);
        if (array.isEmpty()) {
            mWinControlTypeMap.remove(win);
        }
        mTypeWinControlMap.remove(type);
    }

    private void addToControlMaps(@NonNull WindowState win, int type) {
        final ArrayList array = mWinControlTypeMap.computeIfAbsent(win,
                key -> new ArrayList<>());
        array.add(type);
        mTypeWinControlMap.put(type, win);
    }

    void notifyControlChanged(WindowState target) {
        mPendingControlChanged.add(target);
        notifyPendingInsetsControlChanged();
    }

    private void notifyPendingInsetsControlChanged() {
        if (mPendingControlChanged.isEmpty()) {
            return;
        }
        mDisplayContent.mWmService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
            for (int i = mPendingControlChanged.size() - 1; i >= 0; i--) {
                final WindowState controllingWin = mPendingControlChanged.valueAt(i);
                controllingWin.notifyInsetsControlChanged();
            }
            mPendingControlChanged.clear();
        });
    }

    private void notifyInsetsChanged() {
        mDisplayContent.forAllWindows(mDispatchInsetsChanged, true /* traverseTopToBottom */);
    }

    void dump(String prefix, PrintWriter pw) {
        pw.println(prefix + "WindowInsetsStateController");
        mState.dump(prefix + "  ", pw);
        pw.println(prefix + "  " + "Control map:");
        for (int i = mTypeWinControlMap.size() - 1; i >= 0; i--) {
            pw.print(prefix + "  ");
            pw.println(InsetsState.typeToString(mTypeWinControlMap.keyAt(i)) + " -> "
                    + mTypeWinControlMap.valueAt(i));
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy