1 /*
2  * Copyright 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.window.layout
18 
19 import android.app.Activity
20 import android.content.Context
21 import android.inputmethodservice.InputMethodService
22 import android.os.Build
23 import android.util.DisplayMetrics
24 import android.view.Display
25 import android.view.WindowMetrics as AndroidWindowMetrics
26 import androidx.annotation.RequiresApi
27 import androidx.annotation.RestrictTo
28 import androidx.annotation.UiContext
29 import androidx.window.core.Bounds
30 import androidx.window.layout.util.WindowMetricsCompatHelper
31 
32 /** An interface to calculate the [WindowMetrics] for an [Activity] or a [UiContext]. */
33 interface WindowMetricsCalculator {
34 
35     /**
36      * Computes the size and position of the area the window would occupy with
37      * [MATCH_PARENT][android.view.WindowManager.LayoutParams.MATCH_PARENT] width and height and any
38      * combination of flags that would allow the window to extend behind display cutouts.
39      *
40      * For example, [android.view.WindowManager.LayoutParams.layoutInDisplayCutoutMode] set to
41      * [android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS] or the
42      * [android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS] flag set.
43      *
44      * The value returned from this method may be different from platform API(s) used to determine
45      * the size and position of the visible area a given context occupies. For example:
46      * * [Display.getSize] can be used to determine the size of the visible area a window occupies,
47      *   but may be subtracted to exclude certain system decorations that always appear on screen,
48      *   notably the navigation bar.
49      * * The decor view's [android.view.View#getWidth] and [android.view.View@getHeight] can be used
50      *   to determine the size of the top level view in the view hierarchy, but this size is
51      *   determined through a combination of [android.view.WindowManager.LayoutParams] flags and may
52      *   not represent the true window size. For example, a window that does not indicate it can be
53      *   displayed behind a display cutout will have the size of the decor view offset to exclude
54      *   this region unless this region overlaps with the status bar, while the value returned from
55      *   this method will include this region.
56      *
57      * The value returned from this method is guaranteed to be correct on platforms
58      * [Q][Build.VERSION_CODES.Q] and above. For older platforms the value may be invalid if the
59      * activity is in multi-window mode or if the navigation bar offset can not be accounted for,
60      * though a best effort is made to ensure the returned value is as close as possible to the true
61      * value. See [.computeWindowBoundsP] and [.computeWindowBoundsN].
62      *
63      * Note: The value of this is based on the last windowing state reported to the client.
64      *
65      * @see android.view.WindowManager.getCurrentWindowMetrics
66      * @see android.view.WindowMetrics.getBounds
67      */
computeCurrentWindowMetricsnull68     fun computeCurrentWindowMetrics(activity: Activity): WindowMetrics
69 
70     /**
71      * Computes the size and position of the area the window would occupy with
72      * [MATCH_PARENT][android.view.WindowManager.LayoutParams.MATCH_PARENT] width and height and any
73      * combination of flags that would allow the window to extend behind display cutouts.
74      *
75      * On [Build.VERSION_CODES.Q] and older, a [UiContext] is either an [Activity] or an
76      * [InputMethodService]. On [Build.VERSION_CODES.R] and newer, a [UiContext] can also be one
77      * created via the [Context.createWindowContext] APIs.
78      *
79      * @throws NotImplementedError if not implemented. The default implementation from [getOrCreate]
80      *   is guaranteed to implement this method.
81      * @see [computeCurrentWindowMetrics]
82      */
83     fun computeCurrentWindowMetrics(@UiContext context: Context): WindowMetrics {
84         throw NotImplementedError(
85             "Must override computeCurrentWindowMetrics(context) and" + " provide an implementation."
86         )
87     }
88 
89     /**
90      * Computes the maximum size and position of the area the window can expect with
91      * [MATCH_PARENT][android.view.WindowManager.LayoutParams.MATCH_PARENT] width and height and any
92      * combination of flags that would allow the window to extend behind display cutouts.
93      *
94      * The value returned from this method will always match [Display.getRealSize] on
95      * [Android 10][Build.VERSION_CODES.Q] and below.
96      *
97      * @see android.view.WindowManager.getMaximumWindowMetrics
98      */
computeMaximumWindowMetricsnull99     fun computeMaximumWindowMetrics(activity: Activity): WindowMetrics
100 
101     /**
102      * Computes the maximum size and position of the area the window can expect with
103      * [MATCH_PARENT][android.view.WindowManager.LayoutParams.MATCH_PARENT] width and height and any
104      * combination of flags that would allow the window to extend behind display cutouts.
105      *
106      * The value returned from this method will always match [Display.getRealSize] on
107      * [Android 10][Build.VERSION_CODES.Q] and below.
108      *
109      * On [Build.VERSION_CODES.Q] and older, a [UiContext] is either an [Activity] or an
110      * [InputMethodService]. On [Build.VERSION_CODES.R] and newer, a [UiContext] can also be one
111      * created via the [Context.createWindowContext] APIs.
112      *
113      * @throws NotImplementedError if not implemented. The default implementation from [getOrCreate]
114      *   is guaranteed to implement this method.
115      * @see [computeMaximumWindowMetrics]
116      */
117     fun computeMaximumWindowMetrics(@UiContext context: Context): WindowMetrics {
118         throw NotImplementedError(
119             "Must override computeMaximumWindowMetrics(context) and" + " provide an implementation."
120         )
121     }
122 
123     companion object {
124 
<lambda>null125         private var decorator: (WindowMetricsCalculator) -> WindowMetricsCalculator = { it }
126         private val windowMetricsCalculatorCompat = WindowMetricsCalculatorCompat()
127 
128         @JvmStatic
getOrCreatenull129         fun getOrCreate(): WindowMetricsCalculator {
130             return decorator(windowMetricsCalculatorCompat)
131         }
132 
133         @JvmStatic
134         @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
overrideDecoratornull135         fun overrideDecorator(overridingDecorator: WindowMetricsCalculatorDecorator) {
136             decorator = overridingDecorator::decorate
137         }
138 
139         @JvmStatic
140         @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
resetnull141         fun reset() {
142             decorator = { it }
143         }
144 
145         /**
146          * Converts [Android API WindowMetrics][AndroidWindowMetrics] to
147          * [Jetpack version WindowMetrics][WindowMetrics]
148          */
149         @RequiresApi(Build.VERSION_CODES.R)
translateWindowMetricsnull150         internal fun translateWindowMetrics(
151             windowMetrics: AndroidWindowMetrics,
152             density: Float
153         ): WindowMetrics {
154             return WindowMetricsCompatHelper.getInstance()
155                 .translateWindowMetrics(windowMetrics, density)
156         }
157 
fromDisplayMetricsnull158         internal fun fromDisplayMetrics(displayMetrics: DisplayMetrics): WindowMetrics {
159             return WindowMetrics(
160                 Bounds(0, 0, displayMetrics.widthPixels, displayMetrics.heightPixels),
161                 displayMetrics.density
162             )
163         }
164     }
165 }
166 
167 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
168 interface WindowMetricsCalculatorDecorator {
169 
170     /** Returns an instance of [WindowMetricsCalculator] */
decoratenull171     fun decorate(calculator: WindowMetricsCalculator): WindowMetricsCalculator
172 }
173