SystemUIOverlayWindow 관리 시스템

SystemUIOverlayWindow 관리 시스템은 SystemUIOverlayWindow에서 뷰를 표시하고 관리하는 방법을 제공합니다. 현재 이 창은 전체 화면 사용자 전환기와 알림 패널, 키가드를 비롯하여 뷰에 사용됩니다. 이 페이지에서 하지 않는 작업:

  • OEM이 창에 추가할 수 있는 항목에 관한 제한사항을 만듭니다.
  • 이 페이지에 설명된 추상화를 사용하도록 강제합니다.

개요

SystemUIOverlayWindow 관리 시스템을 사용하여 법적 고지, 전체 화면 사용자 전환기, 후방 카메라, HVAC 컨트롤, 키가드와 같은 뷰를 표시할 수 있습니다. 이 창은 앱 공간 외부에 있으며 이 창을 통해 뷰의 Z-순서, 표시/숨기기 트리거, 전반적인 맞춤설정(뷰 배치, 크기, 투명도, 색상 등)을 제어할 수 있습니다. 동시에, 각 뷰가 숨겨지거나 표시될 때 숨겨지거나 표시되어야 하는 시스템 표시줄이나 기타 시스템 UI 객체의 상태를 걱정하지 않아도 됩니다.

SystemUIOverlayWindow를 활용하려면 뷰 미디에이터를 위한 뷰 컨트롤러를 만듭니다. 미디에이터는 창의 전역 상태 컨트롤러에 전달됩니다. 이러한 뷰 미디에이터에 관한 내용은 다음과 같습니다.

  • 뷰 컨트롤러 간에 조정합니다.
  • 뷰 컨트롤러의 비즈니스 로직을 보관합니다.

다음은 뷰 컨트롤러(뷰 미디에이터에서 조정됨)에 관한 내용입니다.

  • 뷰를 소유합니다.
  • OverlayViewsMediator가 비즈니스 로직을 연결할 수 있는 setter를 만듭니다.
  • 뷰의 표시 및 숨기기 애니메이션을 만듭니다.

SystemUI 구성요소인 SystemUIOverlayWindowManager는 전역 상태 컨트롤러로 미디에이터를 초기화하고 등록하는 진입점 역할을 하지만 전역 상태 컨트롤러는 미디에이터가 직접 뷰 컨트롤러를 호출하여 창에서 뷰를 표시하거나 숨길 수 있는 방식으로 뷰 컨트롤러에 연결됩니다.

OverlayViewController

OverlayViewControllerSystemUIOverlayWindow에 표시되는 뷰를 담당하고 뷰가 표시되고 숨겨지는 방식을 제어합니다. 비즈니스 로직에 연결될 수 있도록 필수 리스너를 연결할 수도 있습니다.

중요한 메서드 서명

/**
 * Owns a {@link View} that is present in SystemUIOverlayWindow.
 */
public class OverlayViewController {

    /**
     * Shows content of {@link OverlayViewController}.
     *
     * Should be used to show view externally and in particular by {@link OverlayViewMediator}.
     */
    public final void start();

    /**
     * Hides content of {@link OverlayViewController}.
     *
     * Should be used to hide view externally and in particular by {@link OverlayViewMediator}.
     */
    public final void stop();

    /**
     * Inflate layout owned by controller.
     */
    public final void inflate(ViewGroup baseLayout);

    /**
     * Called once inflate finishes.
     */
    protected void onFinishInflate();

    /**
     * Returns {@code true} if layout owned by controller has been inflated.
     */
    public final boolean isInflated();

    /**
     * Subclasses should override this method to implement reveal animations and implement logic
     * specific to when the layout owned by the controller is shown.
     *
     * Should only be overridden by Superclass but not called by any {@link OverlayViewMediator}.
     */
    protected void showInternal();

    /**
     * Subclasses should override this method to implement conceal animations and implement logic
     * specific to when the layout owned by the controller is hidden.
     *
     * Should only be overridden by Superclass but not called by any {@link OverlayViewMediator}.
     */
    protected void hideInternal();

    /**
     * Provides access to layout owned by controller.
     */
    protected final View getLayout();

    /** Returns the {@link OverlayViewGlobalStateController}. */
    protected final OverlayViewGlobalStateController getOverlayViewGlobalStateController();

    /** Returns whether the view controlled by this controller is visible. */
    public final boolean isVisible();

    /**
     * Returns the ID of the focus area that should receive focus when this view is the
     * topmost view or {@link View#NO_ID} if there is no focus area.
     */
    @IdRes
    protected int getFocusAreaViewId();

    /** Returns whether the view controlled by this controller has rotary focus. */
    protected final boolean hasRotaryFocus();

    /**
     * Sets whether this view allows rotary focus. This should be set to {@code true} for the
     * topmost layer in the overlay window and {@code false} for the others.
     */
    public void setAllowRotaryFocus(boolean allowRotaryFocus);

    /**
     * Refreshes the rotary focus in this view if we are in rotary mode. If the view already has
     * rotary focus, it leaves the focus alone. Returns {@code true} if a new view was focused.
     */
    public boolean refreshRotaryFocusIfNeeded();

    /**
     * Returns {@code true} if heads up notifications should be displayed over this view.
     */
    protected boolean shouldShowHUN();

    /**
     * Returns {@code true} if navigation bar insets should be displayed over this view. Has no
     * effect if {@link #shouldFocusWindow} returns {@code false}.
     */
    protected boolean shouldShowNavigationBarInsets();

    /**
     * Returns {@code true} if status bar insets should be displayed over this view. Has no
     * effect if {@link #shouldFocusWindow} returns {@code false}.
     */
    protected boolean shouldShowStatusBarInsets();

    /**
     * Returns {@code true} if this view should be hidden during the occluded state.
     */
    protected boolean shouldShowWhenOccluded();

    /**
     * Returns {@code true} if the window should be focued when this view is visible. Note that
     * returning {@code false} here means that {@link #shouldShowStatusBarInsets} and
     * {@link #shouldShowNavigationBarInsets} will have no effect.
     */
    protected boolean shouldFocusWindow();

    /**
     * Returns {@code true} if the window should use stable insets. Using stable insets means that
     * even when system bars are temporarily not visible, inset from the system bars will still be
     * applied.
     *
     * NOTE: When system bars are hidden in transient mode, insets from them will not be applied
     * even when the system bars become visible. Setting the return value to {@true} here can
     * prevent the OverlayView from overlapping with the system bars when that happens.
     */
    protected boolean shouldUseStableInsets();

    /**
     * Returns the insets types to fit to the sysui overlay window when this
     * {@link OverlayViewController} is in the foreground.
     */
    @WindowInsets.Type.InsetsType
    protected int getInsetTypesToFit();

    /**
     * Optionally returns the sides of enabled system bar insets to fit to the sysui overlay window
     * when this {@link OverlayViewController} is in the foreground.
     *
     * For example, if the bottom and left system bars are enabled and this method returns
     * WindowInsets.Side.LEFT, then the inset from the bottom system bar will be ignored.
     *
     * NOTE: By default, this method returns {@link #INVALID_INSET_SIDE}, so insets to fit are
     * defined by {@link #getInsetTypesToFit()}, and not by this method, unless it is overridden
     * by subclasses.
     *
     * NOTE: {@link #NO_INSET_SIDE} signifies no insets from any system bars will be honored. Each
     * {@link OverlayViewController} can first take this value and add sides of the system bar
     * insets to honor to it.
     *
     * NOTE: If getInsetSidesToFit is overridden to return {@link WindowInsets.Side}, it always
     * takes precedence over {@link #getInsetTypesToFit()}. That is, the return value of {@link
     * #getInsetTypesToFit()} will be ignored.
     */
    @WindowInsets.Side.InsetsSide
    protected int getInsetSidesToFit();
}

OverlayPanelViewController

OverlayPanelViewController 컨트롤러는 OverlayViewController를 확장하고 슈퍼클래스에 추가 드래그 애니메이션 기능을 제공합니다.

OverlayViewMediator

OverlayViewMediator에는 여러 OverlayViewController 인스턴스를 표시하거나 숨기는 비즈니스 로직이 포함되어 있으므로 뷰 컨트롤러 간의 조정을 관리할 수도 있습니다.

/**
 * Controls when to show and hide {@link OverlayViewController}(s).
 */
public interface OverlayViewMediator {

    /**
     * Register listeners that could use ContentVisibilityAdjuster to show/hide content.
     *
     * Note that we do not unregister listeners because SystemUI components are expected to live
     * for the lifecycle of the device.
     */
    void registerListeners();

    /**
     * Allows for post-inflation callbacks and listeners to be set inside required {@link
     * OverlayViewController}(s).
     */
    void setupOverlayContentViewControllers();
}

SystemUIOverlayWindowManager

SystemUIOverlayWindowManagerSystemUIOverlayWindow 관리 시스템이 OverlayViewGlobalStateControllerOverlayViewMediator 인스턴스를 초기화하고 등록하는 진입점으로 기능하는 SystemUI 객체 역할을 담당합니다.

뷰 표시 흐름
그림 1. SystemUIOverlayWindowManager

OverlayViewGlobalStateController

OverlayViewGlobalStateControllerOverlayViewController 인스턴스에서 호출을 수신하여 자신을 표시하거나 숨깁니다. 따라서 SystemUIOverlayWindow에 표시되거나 숨겨지는 대상의 상태도 유지합니다.

뷰 표시 흐름은 다음과 같습니다.

뷰 표시 흐름
그림 2. 뷰 표시 흐름

뷰 숨기기 흐름

뷰 숨기기 흐름은 다음과 같습니다.

뷰 숨기기 흐름
그림 3. 뷰 숨기기 흐름

공개 메서드 서명

공개 메서드 서명은 다음과 같이 인코딩됩니다.

​/**
 * 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>
 */
@SysUISingleton
public class OverlayViewGlobalStateController {
    /**
     * Register {@link OverlayViewMediator} to use in SystemUIOverlayWindow.
     */
    public void registerMediator(OverlayViewMediator overlayViewMediator);

    /**
     * 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);

    /**
     * Show content in Overlay Window using {@link OverlayViewController}.
     */
    public void showView(OverlayViewController viewController, @Nullable Runnable show);

    /**
     * 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);

    /**
     * Hide content in Overlay Window using {@link OverlayViewController}.
     */
    public void hideView(OverlayViewController viewController, @Nullable Runnable hide);

    /** Returns {@code true} is the window is visible. */
    public boolean isWindowVisible();

    /**
     * Sets the {@link android.view.WindowManager.LayoutParams#FLAG_ALT_FOCUSABLE_IM} flag of the
     * sysui overlay window.
     */
    public void setWindowNeedsInput(boolean needsInput);

    /** Returns {@code true} if the window is focusable. */
    public boolean isWindowFocusable();

    /** Sets the focusable flag of the sysui overlawy window. */
    public void setWindowFocusable(boolean focusable);

    /** Inflates the view controlled by the given view controller. */
    public void inflateView(OverlayViewController viewController);

    /**
     * Return {@code true} if OverlayWindow is in a state where HUNs should be displayed above it.
     */
    public boolean shouldShowHUN();

    /**
     * Set the OverlayViewWindow to be in occluded or unoccluded state. When OverlayViewWindow is
     * occluded, all views mounted to it that are not configured to be shown during occlusion will
     * be hidden.
     */
    public void setOccluded(boolean occluded);
}

SysUIOverlayWindow에 뷰를 추가하는 방법

자세한 내용은 Codelab을 참고하세요.

1단계: SysUIOverlayWindow에 ViewStub 추가

ViewStub창 레이아웃에 추가합니다.

2단계: OverlayViewController 만들기

ViewStub을 사용하여 삽입할 수 있는 새 OverlayViewController를 만듭니다.

3단계: OverlayViewMediator

삽입할 수 있는 새 OverlayViewMediator를 만들거나 기존 OverlayViewMediator를 사용하고(4단계 건너뛰기) 리스너를 등록하여 새 OverlayViewController를 숨기거나 표시합니다.

4단계: 새 OverlayViewMediator 구성

OverlayViewMediator OverlayWindowModuleconfig_carSystemUIOverlayViewsMediator에 추가합니다.

주의사항

SysUIPrimaryWindow가 전체 화면을 덮으면 창 아래의 모든 요소는 터치 이벤트를 등록하지 않습니다. 따라서 창이 전체 화면을 차지하지만 콘텐츠가 네거티브 스페이스를 남기면 네거티브 스페이스를 흐리게 처리하고 네거티브 스페이스에 리스너를 연결하여 창의 콘텐츠를 닫을 수 있습니다.