| /* |
| * Copyright (C) 2020 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.WindowManager.LayoutParams.TYPE_INPUT_METHOD; |
| import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; |
| import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER; |
| import static android.window.DisplayAreaOrganizer.FEATURE_IME_PLACEHOLDER; |
| |
| import static com.android.server.wm.DisplayAreaPolicyBuilder.Feature; |
| import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; |
| |
| import android.annotation.Nullable; |
| import android.util.Slog; |
| |
| import com.android.server.policy.WindowManagerPolicy; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Map; |
| |
| /** |
| * Root of a {@link DisplayArea} hierarchy. It can be either the {@link DisplayContent} as the root |
| * of the whole logical display, or a {@link DisplayAreaGroup} as the root of a partition of the |
| * logical display. |
| */ |
| class RootDisplayArea extends DisplayArea.Dimmable { |
| |
| /** {@link Feature} that are supported in this {@link DisplayArea} hierarchy. */ |
| List<DisplayAreaPolicyBuilder.Feature> mFeatures; |
| |
| /** |
| * Mapping from policy supported {@link Feature} to list of {@link DisplayArea} created to cover |
| * all the window types that the {@link Feature} will be applied to. |
| */ |
| Map<Feature, List<DisplayArea<WindowContainer>>> mFeatureToDisplayAreas; |
| |
| /** Mapping from window layer to {@link DisplayArea.Tokens} that holds windows on that layer. */ |
| private DisplayArea.Tokens[] mAreaForLayer; |
| |
| /** Whether the hierarchy has been built. */ |
| private boolean mHasBuiltHierarchy; |
| |
| RootDisplayArea(WindowManagerService wms, String name, int featureId) { |
| super(wms, Type.ANY, name, featureId); |
| } |
| |
| @Override |
| RootDisplayArea getRootDisplayArea() { |
| return this; |
| } |
| |
| @Override |
| RootDisplayArea asRootDisplayArea() { |
| return this; |
| } |
| |
| /** Whether the orientation (based on dimensions) of this root is different from the Display. */ |
| boolean isOrientationDifferentFromDisplay() { |
| return false; |
| } |
| |
| /** |
| * Places the IME container below this root, so that it's bounds and config will be updated to |
| * match the root. |
| * |
| * @return {@code true} if the IME container is reparented to this root. |
| */ |
| boolean placeImeContainer(DisplayArea.Tokens imeContainer) { |
| final RootDisplayArea previousRoot = imeContainer.getRootDisplayArea(); |
| |
| List<Feature> features = mFeatures; |
| for (int i = 0; i < features.size(); i++) { |
| Feature feature = features.get(i); |
| if (feature.getId() == FEATURE_IME_PLACEHOLDER) { |
| List<DisplayArea<WindowContainer>> imeDisplayAreas = |
| mFeatureToDisplayAreas.get(feature); |
| if (imeDisplayAreas.size() != 1) { |
| throw new IllegalStateException("There must be exactly one DisplayArea for the " |
| + "FEATURE_IME_PLACEHOLDER"); |
| } |
| |
| previousRoot.updateImeContainerForLayers(null /* imeContainer */); |
| imeContainer.reparent(imeDisplayAreas.get(0), POSITION_TOP); |
| updateImeContainerForLayers(imeContainer); |
| return true; |
| } |
| } |
| |
| // Some device UX may not have the need to update the IME bounds and position for IME target |
| // in a child DisplayArea, so instead of throwing exception, we just allow the IME container |
| // to remain in its previous root. |
| if (!isDescendantOf(previousRoot)) { |
| // When this RootDisplayArea is a descendant of the current RootDisplayArea, it will be |
| // at the APPLICATION_LAYER, and the IME container will always be on top and have bounds |
| // equal or larger than the input target. |
| // If it is not a descendant, the DisplayAreaPolicy owner should make sure the IME is |
| // working correctly. Print a warning in case they are not. |
| Slog.w(TAG_WM, "The IME target is not in the same root as the IME container, but there " |
| + "is no DisplayArea of FEATURE_IME_PLACEHOLDER in the target RootDisplayArea"); |
| } |
| return false; |
| } |
| |
| /** |
| * Finds the {@link DisplayArea.Tokens} in {@code mAreaForLayer} that this type of window |
| * should be attached to. |
| * <p> |
| * Note that in most cases, users are expected to call |
| * {@link DisplayContent#findAreaForToken(WindowToken)} to find a {@link DisplayArea} in |
| * {@link DisplayContent} level instead of calling this inner method. |
| * </p> |
| */ |
| @Nullable |
| DisplayArea.Tokens findAreaForTokenInLayer(WindowToken token) { |
| return findAreaForWindowTypeInLayer(token.windowType, token.mOwnerCanManageAppTokens, |
| token.mRoundedCornerOverlay); |
| } |
| |
| /** @see #findAreaForTokenInLayer(WindowToken) */ |
| @Nullable |
| DisplayArea.Tokens findAreaForWindowTypeInLayer(int windowType, boolean ownerCanManageAppTokens, |
| boolean roundedCornerOverlay) { |
| int windowLayerFromType = mWmService.mPolicy.getWindowLayerFromTypeLw(windowType, |
| ownerCanManageAppTokens, roundedCornerOverlay); |
| if (windowLayerFromType == APPLICATION_LAYER) { |
| throw new IllegalArgumentException( |
| "There shouldn't be WindowToken on APPLICATION_LAYER"); |
| } |
| return mAreaForLayer[windowLayerFromType]; |
| } |
| |
| /** Callback after {@link DisplayArea} hierarchy has been built. */ |
| void onHierarchyBuilt(ArrayList<Feature> features, DisplayArea.Tokens[] areaForLayer, |
| Map<Feature, List<DisplayArea<WindowContainer>>> featureToDisplayAreas) { |
| if (mHasBuiltHierarchy) { |
| throw new IllegalStateException("Root should only build the hierarchy once"); |
| } |
| mHasBuiltHierarchy = true; |
| mFeatures = Collections.unmodifiableList(features); |
| mAreaForLayer = areaForLayer; |
| mFeatureToDisplayAreas = featureToDisplayAreas; |
| } |
| |
| private void updateImeContainerForLayers(@Nullable DisplayArea.Tokens imeContainer) { |
| final WindowManagerPolicy policy = mWmService.mPolicy; |
| mAreaForLayer[policy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD)] = imeContainer; |
| mAreaForLayer[policy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD_DIALOG)] = imeContainer; |
| } |
| } |