1 /*
<lambda>null2  * Copyright (C) 2023 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 com.android.compose.animation.scene.demo
18 
19 import android.os.Bundle
20 import android.view.WindowInsets
21 import androidx.activity.ComponentActivity
22 import androidx.activity.compose.ReportDrawn
23 import androidx.activity.compose.setContent
24 import androidx.compose.foundation.IndicationNodeFactory
25 import androidx.compose.foundation.LocalIndication
26 import androidx.compose.foundation.interaction.InteractionSource
27 import androidx.compose.foundation.isSystemInDarkTheme
28 import androidx.compose.foundation.layout.safeDrawingPadding
29 import androidx.compose.material3.MaterialTheme
30 import androidx.compose.material3.Typography
31 import androidx.compose.material3.dynamicDarkColorScheme
32 import androidx.compose.material3.dynamicLightColorScheme
33 import androidx.compose.runtime.Composable
34 import androidx.compose.runtime.CompositionLocalProvider
35 import androidx.compose.runtime.SideEffect
36 import androidx.compose.runtime.getValue
37 import androidx.compose.runtime.mutableStateOf
38 import androidx.compose.runtime.remember
39 import androidx.compose.runtime.saveable.rememberSaveable
40 import androidx.compose.runtime.setValue
41 import androidx.compose.ui.Modifier
42 import androidx.compose.ui.graphics.drawscope.ContentDrawScope
43 import androidx.compose.ui.node.DelegatableNode
44 import androidx.compose.ui.node.DrawModifierNode
45 import androidx.compose.ui.platform.LocalContext
46 import androidx.compose.ui.text.font.DeviceFontFamilyName
47 import androidx.compose.ui.text.font.Font
48 import androidx.compose.ui.text.font.FontFamily
49 import androidx.compose.ui.text.font.FontWeight
50 import com.android.compose.modifiers.thenIf
51 
52 class DemoActivity : ComponentActivity() {
53     private companion object {
54         const val INITIAL_SCENE_EXTRA = "initial_scene"
55         const val FULLSCREEN_EXTRA = "fullscreen"
56         const val DISABLE_RIPPLE_EXTRA = "disable_ripple"
57     }
58 
59     override fun onCreate(savedInstanceState: Bundle?) {
60         super.onCreate(savedInstanceState)
61         window.setDecorFitsSystemWindows(false)
62 
63         val initialScene =
64             intent.extras?.getString(INITIAL_SCENE_EXTRA)?.let { scene ->
65                 Scenes.AllScenes[scene] ?: error("Scene $scene does not exist")
66             }
67 
68         val isFullscreen = intent.extras?.getBoolean(FULLSCREEN_EXTRA) ?: false
69         val disableRipple = intent.extras?.getBoolean(DISABLE_RIPPLE_EXTRA) ?: false
70 
71         setContent {
72             DemoTheme {
73                 var configuration by
74                     rememberSaveable(stateSaver = DemoConfiguration.Saver) {
75                         mutableStateOf(DemoConfiguration(isFullscreen = isFullscreen))
76                     }
77 
78                 SideEffect {
79                     if (configuration.isFullscreen) {
80                         window.insetsController?.hide(WindowInsets.Type.statusBars())
81                     } else {
82                         window.insetsController?.show(WindowInsets.Type.statusBars())
83                     }
84                 }
85 
86                 val indication = if (disableRipple) NoIndication else LocalIndication.current
87                 CompositionLocalProvider(LocalIndication provides indication) {
88                     SystemUi(
89                         configuration,
90                         { configuration = it },
91                         Modifier.thenIf(!configuration.isFullscreen) {
92                             Modifier.safeDrawingPadding()
93                         },
94                         initialScene = initialScene,
95                     )
96                 }
97 
98                 ReportDrawn()
99             }
100         }
101     }
102 }
103 
104 @Composable
DemoThemenull105 private fun DemoTheme(content: @Composable () -> Unit) {
106     val context = LocalContext.current
107     val colorScheme =
108         if (isSystemInDarkTheme()) dynamicDarkColorScheme(context)
109         else dynamicLightColorScheme(context)
110     val typography = MaterialTheme.typography
111     val typographyWithGoogleSans = remember(typography) { typography.withGoogleSans() }
112 
113     MaterialTheme(
114         colorScheme = colorScheme,
115         typography = typographyWithGoogleSans,
116         content = content,
117     )
118 }
119 
withGoogleSansnull120 private fun Typography.withGoogleSans(): Typography {
121     val brandFont = DeviceFontFamilyName("google-sans-text")
122     val plainFont = DeviceFontFamilyName("google-sans")
123 
124     val brand =
125         FontFamily(
126             Font(brandFont, weight = FontWeight.Medium),
127             Font(brandFont, weight = FontWeight.Normal),
128         )
129 
130     val plain =
131         FontFamily(
132             Font(plainFont, weight = FontWeight.Medium),
133             Font(plainFont, weight = FontWeight.Normal),
134         )
135 
136     return this.copy(
137         displayLarge = this.displayLarge.copy(fontFamily = brand),
138         displayMedium = this.displayMedium.copy(fontFamily = brand),
139         displaySmall = this.displaySmall.copy(fontFamily = brand),
140         headlineLarge = this.headlineLarge.copy(fontFamily = brand),
141         headlineMedium = this.headlineMedium.copy(fontFamily = brand),
142         headlineSmall = this.headlineSmall.copy(fontFamily = brand),
143         titleLarge = this.titleLarge.copy(fontFamily = brand),
144         titleMedium = this.titleMedium.copy(fontFamily = plain),
145         titleSmall = this.titleSmall.copy(fontFamily = plain),
146         bodyLarge = this.bodyLarge.copy(fontFamily = plain),
147         bodyMedium = this.bodyMedium.copy(fontFamily = plain),
148         bodySmall = this.bodySmall.copy(fontFamily = plain),
149         labelLarge = this.labelLarge.copy(fontFamily = plain),
150         labelMedium = this.labelMedium.copy(fontFamily = plain),
151         labelSmall = this.labelSmall.copy(fontFamily = plain),
152     )
153 }
154 
155 private data object NoIndication : IndicationNodeFactory {
createnull156     override fun create(interactionSource: InteractionSource): DelegatableNode = NoIndicationNode()
157 }
158 
159 private class NoIndicationNode : Modifier.Node(), DrawModifierNode {
160     override fun ContentDrawScope.draw() {
161         drawContent()
162     }
163 }
164