WindowManager.java
/*
* Copyright 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 androidx.window;
import android.app.Activity;
import android.content.Context;
import android.content.ContextWrapper;
import android.graphics.Rect;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.util.Consumer;
import androidx.window.extensions.ExtensionInterface;
import java.util.concurrent.Executor;
/**
* Main interaction point with the WindowManager library. An instance of this class allows
* polling the current state of the device and display, and registering callbacks for changes in
* the corresponding states.
*/
public final class WindowManager {
/**
* Activity that was registered with this instance of {@link WindowManager} at creation.
* This is used to find the token identifier of the window when requesting layout information
* from the {@link androidx.window.sidecar.SidecarInterface} or is passed directly to the
* {@link ExtensionInterface}.
*/
private Activity mActivity;
/**
* The backend that supplies the information through this class.
*/
private WindowBackend mWindowBackend;
/**
* Gets an instance of the class initialized with and connected to the provided {@link Context}.
* All methods of this class will return information that is associated with this visual
* context.
*
* @param context A visual context, such as an {@link Activity} or a {@link ContextWrapper}
* around one, to use for initialization.
*/
public WindowManager(@NonNull Context context) {
this(context, ExtensionWindowBackend.getInstance(context));
}
/**
* Gets an instance of the class initialized with and connected to the provided {@link Context}.
* All methods of this class will return information that is associated with this visual
* context.
*
* @param context A visual context, such as an {@link Activity} or a
* {@link ContextWrapper}
* around one, to use for initialization.
* @param windowBackend Backing server class that will provide information for this instance.
* Pass a custom {@link WindowBackend} implementation for testing.
* @deprecated WindowBackend will be a required argument in the next implementation.
*/
@Deprecated
public WindowManager(@NonNull Context context, @Nullable WindowBackend windowBackend) {
Activity activity = getActivityFromContext(context);
if (activity == null) {
throw new IllegalArgumentException("Used non-visual Context to obtain an instance of "
+ "WindowManager. Please use an Activity or a ContextWrapper around one "
+ "instead.");
}
mActivity = activity;
mWindowBackend = windowBackend == null ? ExtensionWindowBackend.getInstance(context)
: windowBackend;
}
/**
* Registers a callback for layout changes of the window of the current visual {@link Context}.
* Must be called only after the it is attached to the window.
*
* @see Activity#onAttachedToWindow()
*/
public void registerLayoutChangeCallback(@NonNull Executor executor,
@NonNull Consumer<WindowLayoutInfo> callback) {
mWindowBackend.registerLayoutChangeCallback(mActivity, executor, callback);
}
/**
* Unregisters a callback for window layout changes of the window.
*/
public void unregisterLayoutChangeCallback(@NonNull Consumer<WindowLayoutInfo> callback) {
mWindowBackend.unregisterLayoutChangeCallback(callback);
}
/**
* @deprecated will be removed in the next alpha
* @return the current {@link DeviceState} if Sidecar is present and an empty info otherwise
*/
@Deprecated
@NonNull
public DeviceState getDeviceState() {
return mWindowBackend.getDeviceState();
}
/**
* @deprecated will be removed in the next alpha
* @return the current {@link WindowLayoutInfo} when Sidecar is present and an empty info
* otherwise
*/
@Deprecated
@NonNull
public WindowLayoutInfo getWindowLayoutInfo() {
return mWindowBackend.getWindowLayoutInfo(mActivity);
}
/**
* @deprecated {@link DeviceState} information has been merged into {@link WindowLayoutInfo}
* Registers a callback for device state changes.
*/
@Deprecated
public void registerDeviceStateChangeCallback(@NonNull Executor executor,
@NonNull Consumer<DeviceState> callback) {
mWindowBackend.registerDeviceStateChangeCallback(executor, callback);
}
/**
* @deprecated {@link DeviceState} information has been merged into {@link WindowLayoutInfo}
* Unregisters a callback for device state changes.
*/
@Deprecated
public void unregisterDeviceStateChangeCallback(@NonNull Consumer<DeviceState> callback) {
mWindowBackend.unregisterDeviceStateChangeCallback(callback);
}
/**
* Returns the {@link WindowMetrics} according to the current system state.
* <p>
* The metrics describe the size of the area the window would occupy with
* {@link android.view.WindowManager.LayoutParams#MATCH_PARENT MATCH_PARENT} width and height
* and any combination of flags that would allow the window to extend behind display cutouts.
* <p>
* The value of this is based on the <b>current</b> windowing state of the system. For
* example, for activities in multi-window mode, the metrics returned are based on the
* current bounds that the user has selected for the {@link android.app.Activity Activity}'s
* window.
*
* @see #getMaximumWindowMetrics()
* @see android.view.WindowManager#getCurrentWindowMetrics()
*/
@NonNull
public WindowMetrics getCurrentWindowMetrics() {
Rect currentBounds = WindowBoundsHelper.getInstance().computeCurrentWindowBounds(mActivity);
return new WindowMetrics(currentBounds);
}
/**
* Returns the largest {@link WindowMetrics} an app may expect in the current system state.
* <p>
* The metrics describe the size of the largest potential area the window might occupy with
* {@link android.view.WindowManager.LayoutParams#MATCH_PARENT MATCH_PARENT} width and height
* and any combination of flags that would allow the window to extend behind display cutouts.
* <p>
* The value of this is based on the largest <b>potential</b> windowing state of the system.
* For example, for activities in multi-window mode the metrics returned are based on what the
* bounds would be if the user expanded the window to cover the entire screen.
* <p>
* Note that this might still be smaller than the size of the physical display if certain
* areas of the display are not available to windows created for the associated {@link Context}.
* For example, devices with foldable displays that wrap around the enclosure may split the
* physical display into different regions, one for the front and one for the back, each acting
* as different logical displays. In this case {@link #getMaximumWindowMetrics()} would return
* the region describing the side of the device the associated {@link Context context's}
* window is placed.
*
* @see #getCurrentWindowMetrics()
* @see android.view.WindowManager#getMaximumWindowMetrics()
*/
@NonNull
public WindowMetrics getMaximumWindowMetrics() {
Rect maxBounds = WindowBoundsHelper.getInstance().computeMaximumWindowBounds(mActivity);
return new WindowMetrics(maxBounds);
}
/**
* Unwraps the hierarchy of {@link ContextWrapper}-s until {@link Activity} is reached.
*
* @return Base {@link Activity} context or {@code null} if not available.
*/
@Nullable
private static Activity getActivityFromContext(Context context) {
while (context instanceof ContextWrapper) {
if (context instanceof Activity) {
return (Activity) context;
}
context = ((ContextWrapper) context).getBaseContext();
}
return null;
}
}