| /* |
| * 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.systemui.car.window; |
| |
| import android.annotation.Nullable; |
| import android.util.Log; |
| |
| import androidx.annotation.VisibleForTesting; |
| |
| import com.android.systemui.car.navigationbar.CarNavigationBarController; |
| |
| import java.util.HashMap; |
| import java.util.Map; |
| import java.util.SortedMap; |
| import java.util.TreeMap; |
| |
| import javax.inject.Inject; |
| import javax.inject.Singleton; |
| |
| /** |
| * This controller is responsible for the following: |
| * <p><ul> |
| * <li>Holds the global state for SystemUIOverlayWindow. |
| * <li>Allows {@link SystemUIOverlayWindowManager} to register {@link OverlayViewMediator}(s). |
| * <li>Enables {@link OverlayViewController)(s) to reveal/conceal themselves while respecting the |
| * global state of SystemUIOverlayWindow. |
| * </ul> |
| */ |
| @Singleton |
| public class OverlayViewGlobalStateController { |
| private static final boolean DEBUG = false; |
| private static final String TAG = OverlayViewGlobalStateController.class.getSimpleName(); |
| private static final int UNKNOWN_Z_ORDER = -1; |
| private final SystemUIOverlayWindowController mSystemUIOverlayWindowController; |
| private final CarNavigationBarController mCarNavigationBarController; |
| @VisibleForTesting |
| Map<OverlayViewController, Integer> mZOrderMap; |
| @VisibleForTesting |
| SortedMap<Integer, OverlayViewController> mZOrderVisibleSortedMap; |
| @VisibleForTesting |
| OverlayViewController mHighestZOrder; |
| |
| @Inject |
| public OverlayViewGlobalStateController( |
| CarNavigationBarController carNavigationBarController, |
| SystemUIOverlayWindowController systemUIOverlayWindowController) { |
| mSystemUIOverlayWindowController = systemUIOverlayWindowController; |
| mSystemUIOverlayWindowController.attach(); |
| mCarNavigationBarController = carNavigationBarController; |
| mZOrderMap = new HashMap<>(); |
| mZOrderVisibleSortedMap = new TreeMap<>(); |
| } |
| |
| /** |
| * Register {@link OverlayViewMediator} to use in SystemUIOverlayWindow. |
| */ |
| public void registerMediator(OverlayViewMediator overlayViewMediator) { |
| Log.d(TAG, "Registering content mediator: " + overlayViewMediator.getClass().getName()); |
| |
| overlayViewMediator.registerListeners(); |
| overlayViewMediator.setupOverlayContentViewControllers(); |
| } |
| |
| /** |
| * Show content in Overlay Window using {@link OverlayPanelViewController}. |
| * |
| * This calls {@link OverlayViewGlobalStateController#showView(OverlayViewController, Runnable)} |
| * where the runnable is nullified since the actual showing of the panel is handled by the |
| * controller itself. |
| */ |
| public void showView(OverlayPanelViewController panelViewController) { |
| showView(panelViewController, /* show= */ null); |
| } |
| |
| /** |
| * Show content in Overlay Window using {@link OverlayViewController}. |
| */ |
| public void showView(OverlayViewController viewController, @Nullable Runnable show) { |
| debugLog(); |
| if (mZOrderVisibleSortedMap.isEmpty()) { |
| setWindowVisible(true); |
| } |
| if (!(viewController instanceof OverlayPanelViewController)) { |
| inflateView(viewController); |
| } |
| |
| if (show != null) { |
| show.run(); |
| } |
| |
| updateInternalsWhenShowingView(viewController); |
| refreshNavigationBarVisibility(); |
| |
| Log.d(TAG, "Content shown: " + viewController.getClass().getName()); |
| debugLog(); |
| } |
| |
| private void updateInternalsWhenShowingView(OverlayViewController viewController) { |
| int zOrder; |
| if (mZOrderMap.containsKey(viewController)) { |
| zOrder = mZOrderMap.get(viewController); |
| } else { |
| zOrder = mSystemUIOverlayWindowController.getBaseLayout().indexOfChild( |
| viewController.getLayout()); |
| mZOrderMap.put(viewController, zOrder); |
| } |
| |
| mZOrderVisibleSortedMap.put(zOrder, viewController); |
| |
| refreshHighestZOrderWhenShowingView(viewController); |
| } |
| |
| private void refreshHighestZOrderWhenShowingView(OverlayViewController viewController) { |
| if (mZOrderMap.getOrDefault(mHighestZOrder, UNKNOWN_Z_ORDER) < mZOrderMap.get( |
| viewController)) { |
| mHighestZOrder = viewController; |
| } |
| } |
| |
| /** |
| * Hide content in Overlay Window using {@link OverlayPanelViewController}. |
| * |
| * This calls {@link OverlayViewGlobalStateController#hideView(OverlayViewController, Runnable)} |
| * where the runnable is nullified since the actual hiding of the panel is handled by the |
| * controller itself. |
| */ |
| public void hideView(OverlayPanelViewController panelViewController) { |
| hideView(panelViewController, /* hide= */ null); |
| } |
| |
| /** |
| * Hide content in Overlay Window using {@link OverlayViewController}. |
| */ |
| public void hideView(OverlayViewController viewController, @Nullable Runnable hide) { |
| debugLog(); |
| if (!viewController.isInflated()) { |
| Log.d(TAG, "Content cannot be hidden since it isn't inflated: " |
| + viewController.getClass().getName()); |
| return; |
| } |
| if (!mZOrderMap.containsKey(viewController)) { |
| Log.d(TAG, "Content cannot be hidden since it has never been shown: " |
| + viewController.getClass().getName()); |
| return; |
| } |
| if (!mZOrderVisibleSortedMap.containsKey(mZOrderMap.get(viewController))) { |
| Log.d(TAG, "Content cannot be hidden since it isn't currently shown: " |
| + viewController.getClass().getName()); |
| return; |
| } |
| |
| if (hide != null) { |
| hide.run(); |
| } |
| |
| mZOrderVisibleSortedMap.remove(mZOrderMap.get(viewController)); |
| refreshHighestZOrderWhenHidingView(viewController); |
| refreshNavigationBarVisibility(); |
| |
| if (mZOrderVisibleSortedMap.isEmpty()) { |
| setWindowVisible(false); |
| } |
| |
| Log.d(TAG, "Content hidden: " + viewController.getClass().getName()); |
| debugLog(); |
| } |
| |
| private void refreshHighestZOrderWhenHidingView(OverlayViewController viewController) { |
| if (mZOrderVisibleSortedMap.isEmpty()) { |
| mHighestZOrder = null; |
| return; |
| } |
| if (!mHighestZOrder.equals(viewController)) { |
| return; |
| } |
| |
| mHighestZOrder = mZOrderVisibleSortedMap.get(mZOrderVisibleSortedMap.lastKey()); |
| } |
| |
| private void refreshNavigationBarVisibility() { |
| if (mZOrderVisibleSortedMap.isEmpty() || mHighestZOrder.shouldShowNavigationBar()) { |
| mCarNavigationBarController.showBars(); |
| } else { |
| mCarNavigationBarController.hideBars(); |
| } |
| } |
| |
| /** Returns {@code true} is the window is visible. */ |
| public boolean isWindowVisible() { |
| return mSystemUIOverlayWindowController.isWindowVisible(); |
| } |
| |
| private void setWindowVisible(boolean visible) { |
| mSystemUIOverlayWindowController.setWindowVisible(visible); |
| } |
| |
| /** |
| * Sets the {@link android.view.WindowManager.LayoutParams#FLAG_ALT_FOCUSABLE_IM} flag of the |
| * sysui overlay window. |
| */ |
| public void setWindowNeedsInput(boolean needsInput) { |
| mSystemUIOverlayWindowController.setWindowNeedsInput(needsInput); |
| } |
| |
| /** Returns {@code true} if the window is focusable. */ |
| public boolean isWindowFocusable() { |
| return mSystemUIOverlayWindowController.isWindowFocusable(); |
| } |
| |
| /** Sets the focusable flag of the sysui overlawy window. */ |
| public void setWindowFocusable(boolean focusable) { |
| mSystemUIOverlayWindowController.setWindowFocusable(focusable); |
| } |
| |
| /** Inflates the view controlled by the given view controller. */ |
| public void inflateView(OverlayViewController viewController) { |
| if (!viewController.isInflated()) { |
| viewController.inflate(mSystemUIOverlayWindowController.getBaseLayout()); |
| } |
| } |
| |
| /** |
| * Return {@code true} if OverlayWindow is in a state where HUNs should be displayed above it. |
| */ |
| public boolean shouldShowHUN() { |
| return mZOrderVisibleSortedMap.isEmpty() || mHighestZOrder.shouldShowHUN(); |
| } |
| |
| private void debugLog() { |
| if (!DEBUG) { |
| return; |
| } |
| |
| Log.d(TAG, "mHighestZOrder: " + mHighestZOrder); |
| Log.d(TAG, "mZOrderVisibleSortedMap.size(): " + mZOrderVisibleSortedMap.size()); |
| Log.d(TAG, "mZOrderVisibleSortedMap: " + mZOrderVisibleSortedMap); |
| Log.d(TAG, "mZOrderMap.size(): " + mZOrderMap.size()); |
| Log.d(TAG, "mZOrderMap: " + mZOrderMap); |
| } |
| } |