1 /*
<lambda>null2  * Copyright 2022 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.compose.foundation.layout.samples
18 
19 import android.os.Bundle
20 import androidx.activity.ComponentActivity
21 import androidx.activity.compose.setContent
22 import androidx.annotation.Sampled
23 import androidx.compose.foundation.background
24 import androidx.compose.foundation.layout.Box
25 import androidx.compose.foundation.layout.Column
26 import androidx.compose.foundation.layout.ExperimentalLayoutApi
27 import androidx.compose.foundation.layout.MutableWindowInsets
28 import androidx.compose.foundation.layout.PaddingValues
29 import androidx.compose.foundation.layout.WindowInsets
30 import androidx.compose.foundation.layout.asPaddingValues
31 import androidx.compose.foundation.layout.captionBarPadding
32 import androidx.compose.foundation.layout.consumeWindowInsets
33 import androidx.compose.foundation.layout.displayCutoutPadding
34 import androidx.compose.foundation.layout.exclude
35 import androidx.compose.foundation.layout.fillMaxSize
36 import androidx.compose.foundation.layout.fillMaxWidth
37 import androidx.compose.foundation.layout.ime
38 import androidx.compose.foundation.layout.imePadding
39 import androidx.compose.foundation.layout.mandatorySystemGesturesPadding
40 import androidx.compose.foundation.layout.navigationBars
41 import androidx.compose.foundation.layout.navigationBarsPadding
42 import androidx.compose.foundation.layout.onConsumedWindowInsetsChanged
43 import androidx.compose.foundation.layout.padding
44 import androidx.compose.foundation.layout.recalculateWindowInsets
45 import androidx.compose.foundation.layout.safeContent
46 import androidx.compose.foundation.layout.safeContentPadding
47 import androidx.compose.foundation.layout.safeDrawingPadding
48 import androidx.compose.foundation.layout.safeGesturesPadding
49 import androidx.compose.foundation.layout.statusBars
50 import androidx.compose.foundation.layout.statusBarsPadding
51 import androidx.compose.foundation.layout.systemBars
52 import androidx.compose.foundation.layout.systemBarsPadding
53 import androidx.compose.foundation.layout.systemGesturesPadding
54 import androidx.compose.foundation.layout.union
55 import androidx.compose.foundation.layout.waterfallPadding
56 import androidx.compose.foundation.layout.windowInsetsPadding
57 import androidx.compose.foundation.lazy.LazyColumn
58 import androidx.compose.material.Button
59 import androidx.compose.material.Text
60 import androidx.compose.runtime.Composable
61 import androidx.compose.runtime.getValue
62 import androidx.compose.runtime.mutableStateOf
63 import androidx.compose.runtime.remember
64 import androidx.compose.runtime.setValue
65 import androidx.compose.ui.Alignment
66 import androidx.compose.ui.Modifier
67 import androidx.compose.ui.graphics.Color
68 import androidx.compose.ui.platform.LocalDensity
69 import androidx.compose.ui.unit.dp
70 import androidx.core.view.WindowCompat
71 
72 @Sampled
73 fun captionBarPaddingSample() {
74     class SampleActivity : ComponentActivity() {
75         override fun onCreate(savedInstanceState: Bundle?) {
76             WindowCompat.setDecorFitsSystemWindows(window, false)
77             super.onCreate(savedInstanceState)
78             setContent {
79                 Box(Modifier.captionBarPadding()) {
80                     // app content
81                 }
82             }
83         }
84     }
85 }
86 
87 @Sampled
systemBarsPaddingSamplenull88 fun systemBarsPaddingSample() {
89     class SampleActivity : ComponentActivity() {
90         override fun onCreate(savedInstanceState: Bundle?) {
91             WindowCompat.setDecorFitsSystemWindows(window, false)
92             super.onCreate(savedInstanceState)
93             setContent {
94                 Box(Modifier.systemBarsPadding()) {
95                     // app content
96                 }
97             }
98         }
99     }
100 }
101 
102 @Sampled
displayCutoutPaddingSamplenull103 fun displayCutoutPaddingSample() {
104     class SampleActivity : ComponentActivity() {
105         override fun onCreate(savedInstanceState: Bundle?) {
106             WindowCompat.setDecorFitsSystemWindows(window, false)
107             super.onCreate(savedInstanceState)
108             setContent {
109                 Box(Modifier.background(Color.Blue).statusBarsPadding()) {
110                     Box(Modifier.background(Color.Yellow).displayCutoutPadding()) {
111                         // app content
112                     }
113                 }
114             }
115         }
116     }
117 }
118 
119 @Sampled
statusBarsAndNavigationBarsPaddingSamplenull120 fun statusBarsAndNavigationBarsPaddingSample() {
121     class SampleActivity : ComponentActivity() {
122         override fun onCreate(savedInstanceState: Bundle?) {
123             WindowCompat.setDecorFitsSystemWindows(window, false)
124             super.onCreate(savedInstanceState)
125             setContent {
126                 Box(Modifier.background(Color.Blue).statusBarsPadding()) {
127                     Box(Modifier.background(Color.Green).navigationBarsPadding()) {
128                         // app content
129                     }
130                 }
131             }
132         }
133     }
134 }
135 
136 @Sampled
imePaddingSamplenull137 fun imePaddingSample() {
138     class SampleActivity : ComponentActivity() {
139         override fun onCreate(savedInstanceState: Bundle?) {
140             WindowCompat.setDecorFitsSystemWindows(window, false)
141             super.onCreate(savedInstanceState)
142             setContent {
143                 Box(Modifier.background(Color.Blue).systemBarsPadding()) {
144                     Box(Modifier.imePadding()) {
145                         // app content
146                     }
147                 }
148             }
149         }
150     }
151 }
152 
153 @Sampled
waterfallPaddingSamplenull154 fun waterfallPaddingSample() {
155     class SampleActivity : ComponentActivity() {
156         override fun onCreate(savedInstanceState: Bundle?) {
157             WindowCompat.setDecorFitsSystemWindows(window, false)
158             super.onCreate(savedInstanceState)
159             setContent {
160                 Box(Modifier.background(Color.Blue).systemBarsPadding()) {
161                     // The app content shouldn't spill over the edges. They will be green.
162                     Box(Modifier.background(Color.Green).waterfallPadding()) {
163                         // app content
164                     }
165                 }
166             }
167         }
168     }
169 }
170 
171 @Sampled
systemGesturesPaddingSamplenull172 fun systemGesturesPaddingSample() {
173     class SampleActivity : ComponentActivity() {
174         override fun onCreate(savedInstanceState: Bundle?) {
175             WindowCompat.setDecorFitsSystemWindows(window, false)
176             super.onCreate(savedInstanceState)
177             setContent {
178                 Box(Modifier.background(Color.Blue).systemBarsPadding()) {
179                     // The app content won't interfere with the system gestures area.
180                     // It will just be white.
181                     Box(Modifier.background(Color.White).systemGesturesPadding()) {
182                         // app content
183                     }
184                 }
185             }
186         }
187     }
188 }
189 
190 @Sampled
mandatorySystemGesturesPaddingSamplenull191 fun mandatorySystemGesturesPaddingSample() {
192     class SampleActivity : ComponentActivity() {
193         override fun onCreate(savedInstanceState: Bundle?) {
194             WindowCompat.setDecorFitsSystemWindows(window, false)
195             super.onCreate(savedInstanceState)
196             setContent {
197                 Box(Modifier.background(Color.Blue).systemBarsPadding()) {
198                     // The app content won't interfere with the mandatory system gestures area.
199                     // It will just be white.
200                     Box(Modifier.background(Color.White).mandatorySystemGesturesPadding()) {
201                         // app content
202                     }
203                 }
204             }
205         }
206     }
207 }
208 
209 @Sampled
safeDrawingPaddingSamplenull210 fun safeDrawingPaddingSample() {
211     class SampleActivity : ComponentActivity() {
212         override fun onCreate(savedInstanceState: Bundle?) {
213             WindowCompat.setDecorFitsSystemWindows(window, false)
214             super.onCreate(savedInstanceState)
215             setContent {
216                 Box(Modifier.background(Color.Black).systemBarsPadding()) {
217                     // The app content won't have anything drawing over it, but all the
218                     // background not in the status bars will be white.
219                     Box(Modifier.background(Color.White).safeDrawingPadding()) {
220                         // app content
221                     }
222                 }
223             }
224         }
225     }
226 }
227 
228 @Sampled
safeGesturesPaddingSamplenull229 fun safeGesturesPaddingSample() {
230     class SampleActivity : ComponentActivity() {
231         override fun onCreate(savedInstanceState: Bundle?) {
232             WindowCompat.setDecorFitsSystemWindows(window, false)
233             super.onCreate(savedInstanceState)
234             setContent {
235                 Box(Modifier.background(Color.Black).systemBarsPadding()) {
236                     // The app content will only be drawn where there is no possible
237                     // gesture confusion. The rest will be plain white
238                     Box(Modifier.background(Color.White).safeGesturesPadding()) {
239                         // app content
240                     }
241                 }
242             }
243         }
244     }
245 }
246 
247 @Sampled
safeContentPaddingSamplenull248 fun safeContentPaddingSample() {
249     class SampleActivity : ComponentActivity() {
250         override fun onCreate(savedInstanceState: Bundle?) {
251             WindowCompat.setDecorFitsSystemWindows(window, false)
252             super.onCreate(savedInstanceState)
253             setContent {
254                 Box(Modifier.background(Color.Black).systemBarsPadding()) {
255                     // The app content will only be drawn where there is no possible
256                     // gesture confusion and content will not be drawn over.
257                     // The rest will be plain white
258                     Box(Modifier.background(Color.White).safeContentPadding()) {
259                         // app content
260                     }
261                 }
262             }
263         }
264     }
265 }
266 
267 @Sampled
insetsPaddingSamplenull268 fun insetsPaddingSample() {
269     class SampleActivity : ComponentActivity() {
270         override fun onCreate(savedInstanceState: Bundle?) {
271             WindowCompat.setDecorFitsSystemWindows(window, false)
272             super.onCreate(savedInstanceState)
273             setContent {
274                 val insets = WindowInsets.systemBars.union(WindowInsets.ime)
275                 Box(Modifier.background(Color.White).fillMaxSize().windowInsetsPadding(insets)) {
276                     // app content
277                 }
278             }
279         }
280     }
281 }
282 
283 @Sampled
consumedInsetsPaddingSamplenull284 fun consumedInsetsPaddingSample() {
285     class SampleActivity : ComponentActivity() {
286         override fun onCreate(savedInstanceState: Bundle?) {
287             WindowCompat.setDecorFitsSystemWindows(window, false)
288             super.onCreate(savedInstanceState)
289             setContent {
290                 with(LocalDensity.current) {
291                     val paddingValues = PaddingValues(horizontal = 20.dp)
292                     Box(Modifier.padding(paddingValues).consumeWindowInsets(paddingValues)) {
293                         // app content
294                     }
295                 }
296             }
297         }
298     }
299 }
300 
301 @Sampled
insetsIntnull302 fun insetsInt() {
303     class SampleActivity : ComponentActivity() {
304         override fun onCreate(savedInstanceState: Bundle?) {
305             WindowCompat.setDecorFitsSystemWindows(window, false)
306             super.onCreate(savedInstanceState)
307             setContent {
308                 // Make sure we are at least 10 pixels away from the top.
309                 val insets = WindowInsets.statusBars.union(WindowInsets(top = 10))
310                 Box(Modifier.windowInsetsPadding(insets)) {
311                     // app content
312                 }
313             }
314         }
315     }
316 }
317 
318 @Sampled
insetsDpnull319 fun insetsDp() {
320     class SampleActivity : ComponentActivity() {
321         override fun onCreate(savedInstanceState: Bundle?) {
322             WindowCompat.setDecorFitsSystemWindows(window, false)
323             super.onCreate(savedInstanceState)
324             setContent {
325                 // Make sure we are at least 10 DP away from the top.
326                 val insets = WindowInsets.statusBars.union(WindowInsets(top = 10.dp))
327                 Box(Modifier.windowInsetsPadding(insets)) {
328                     // app content
329                 }
330             }
331         }
332     }
333 }
334 
335 @Sampled
paddingValuesSamplenull336 fun paddingValuesSample() {
337     class SampleActivity : ComponentActivity() {
338         override fun onCreate(savedInstanceState: Bundle?) {
339             WindowCompat.setDecorFitsSystemWindows(window, false)
340             super.onCreate(savedInstanceState)
341             setContent {
342                 LazyColumn(contentPadding = WindowInsets.navigationBars.asPaddingValues()) {
343                     // items
344                 }
345             }
346         }
347     }
348 }
349 
350 @Sampled
consumedInsetsSamplenull351 fun consumedInsetsSample() {
352     class SampleActivity : ComponentActivity() {
353         override fun onCreate(savedInstanceState: Bundle?) {
354             WindowCompat.setDecorFitsSystemWindows(window, false)
355             super.onCreate(savedInstanceState)
356             setContent {
357                 Box(Modifier.padding(WindowInsets.navigationBars.asPaddingValues())) {
358                     Box(Modifier.consumeWindowInsets(WindowInsets.navigationBars)) {
359                         // app content
360                     }
361                 }
362             }
363         }
364     }
365 }
366 
367 @OptIn(ExperimentalLayoutApi::class)
368 @Sampled
withConsumedInsetsSamplenull369 fun withConsumedInsetsSample() {
370     class SampleActivity : ComponentActivity() {
371         override fun onCreate(savedInstanceState: Bundle?) {
372             WindowCompat.setDecorFitsSystemWindows(window, false)
373             super.onCreate(savedInstanceState)
374             setContent {
375                 val remainingInsets = remember { MutableWindowInsets() }
376                 val safeContent = WindowInsets.safeContent
377                 Box(
378                     Modifier.navigationBarsPadding().onConsumedWindowInsetsChanged {
379                         consumedWindowInsets ->
380                         remainingInsets.insets = safeContent.exclude(consumedWindowInsets)
381                     }
382                 ) {
383                     // padding can be used without recomposition when insets change.
384                     val padding = remainingInsets.asPaddingValues()
385                     Box(Modifier.padding(padding))
386                 }
387             }
388         }
389     }
390 }
391 
392 @Sampled
393 @Composable
recalculateWindowInsetsSamplenull394 fun recalculateWindowInsetsSample() {
395     var hasFirstItem by remember { mutableStateOf(true) }
396     var hasLastItem by remember { mutableStateOf(true) }
397     Column(Modifier.fillMaxSize()) {
398         if (hasFirstItem) {
399             Box(Modifier.weight(1f).fillMaxWidth().background(Color.Magenta))
400         }
401         Box(
402             Modifier.fillMaxWidth() // force a fixed size on the content
403                 .recalculateWindowInsets()
404                 .weight(1f)
405                 .background(Color.Yellow)
406                 .safeDrawingPadding()
407         ) {
408             Button(
409                 onClick = { hasFirstItem = !hasFirstItem },
410                 Modifier.align(Alignment.TopCenter)
411             ) {
412                 val action = if (hasFirstItem) "Remove" else "Add"
413                 Text("$action First Item")
414             }
415             Button(
416                 onClick = { hasLastItem = !hasLastItem },
417                 Modifier.align(Alignment.BottomCenter)
418             ) {
419                 val action = if (hasLastItem) "Remove" else "Add"
420                 Text("$action Last Item")
421             }
422         }
423         if (hasLastItem) {
424             Box(Modifier.weight(1f).fillMaxWidth().background(Color.Cyan))
425         }
426     }
427 }
428 
429 @Sampled
430 @Composable
consumeWindowInsetsWithPaddingSamplenull431 fun consumeWindowInsetsWithPaddingSample() {
432     // The outer Box uses padding and properly compensates for it by using consumeWindowInsets()
433     Box(
434         Modifier.fillMaxSize()
435             .padding(10.dp)
436             .consumeWindowInsets(WindowInsets(10.dp, 10.dp, 10.dp, 10.dp))
437     ) {
438         Box(Modifier.fillMaxSize().safeContentPadding().background(Color.Blue))
439     }
440 }
441 
442 @Sampled
443 @Composable
unconsumedWindowInsetsWithPaddingSamplenull444 fun unconsumedWindowInsetsWithPaddingSample() {
445     // This outer Box is representing a 3rd-party layout that you don't control. It has a
446     // padding, but doesn't properly use consumeWindowInsets()
447     Box(Modifier.padding(10.dp)) {
448         // This is the content that you control. You can make sure that the WindowInsets are correct
449         // so you can pad your content despite the fact that the parent did not
450         // consumeWindowInsets()
451         Box(
452             Modifier.fillMaxSize() // Force a fixed size on the content
453                 .recalculateWindowInsets()
454                 .safeContentPadding()
455                 .background(Color.Blue)
456         )
457     }
458 }
459