Slide 1

Slide 1 text

Android 2023.4.26 Android Google LLC

Slide 2

Slide 2 text

©MIXI 2 2017 Vantage KARASTA 2015 Vantage M

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

©MIXI 4 Android Android

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

©MIXI 6 1 Android 2 3 UI 4 5 6 7 8 9 10

Slide 7

Slide 7 text

Android 1

Slide 8

Slide 8 text

©MIXI Android 8 Android Auto Android TV PC ChromeOS Wear OS ,

Slide 9

Slide 9 text

©MIXI Android 9 API (SDK ) 2008.09 1.0 1 Base ... 2017.08 8.0 26 Oreo 2017.12 8.1 27 Oreo MR1 2018.08 9 28 Pie 2019.09 10 29 Q 2020.09 11 30 R 2021.10 12 31 S 2022.03 12L 32 S v2 2022.08 13 33 Tiramisu - 14 34 Upside Down Cake API ( )

Slide 10

Slide 10 text

©MIXI Android Studio IntelliJ IDEA ⾒ https://developer.android.com/studio 10

Slide 11

Slide 11 text

©MIXI 11 Android Studio APK (Android Application Package) AVD (Android Virtual Device)

Slide 12

Slide 12 text

©MIXI 12 Android Studio AAB (Android App Bundle) Google Play

Slide 13

Slide 13 text

©MIXI 13 Android Studio AAB (Android App Bundle) Google Play : : xxhdpi CPU: arm : : hdpi CPU: x86 APK

Slide 14

Slide 14 text

©MIXI 14 Android Studio AAB (Android App Bundle) Google Play l l : : xxhdpi CPU: arm : : hdpi CPU: x86

Slide 15

Slide 15 text

©MIXI Kotlin Android (2017 ) Java 15 Kotlin l : l Null : l Java : Kotlin Java Java Kotlin

Slide 16

Slide 16 text

©MIXI Kotlin Android (2017 ) Java 16 Kotlin l : l Null : l Java : Kotlin Java Java Kotlin NullPointerException var count: Int // null var count: Int? // null

Slide 17

Slide 17 text

©MIXI Kotlin Android, iOS, Web https://kotlinlang.org/docs/multiplatform.html 17 Kotlin Multiplatform Kotlin UI ( ) https://developer.android.com/jetpack/compose?hl=ja Jetpack Compose

Slide 18

Slide 18 text

©MIXI Wear OS, ChromeOS, Android TV, Android Auto API : Android 13 API 33, Tiramisu APK : AAB : Google Play APK : , Null , Java Kotlin : Kotlin Multiplatform, Jetpack Compose 18 Android Kotlin

Slide 19

Slide 19 text

2

Slide 20

Slide 20 text

©MIXI Android Studio New Project 20

Slide 21

Slide 21 text

©MIXI Empty Activity (Activity ) Jetpack Compose 21

Slide 22

Slide 22 text

©MIXI Name Package name ID { }.{ } : jp.co.mixi.karasta Minimum SDK 22

Slide 23

Slide 23 text

©MIXI AVD ( ) ⾒ https://developer.android.com/studio/run/managing-avds?hl=ja 23 AVD

Slide 24

Slide 24 text

©MIXI 24 , , Kotlin ,

Slide 25

Slide 25 text

©MIXI ( ) 1. https://developer.android.com/studio/debug/dev-options?hl=ja#enable 2. USB ON 3. USB Android Studio 25 1. USB

Slide 26

Slide 26 text

©MIXI ( ) 1. Android 11 PC 2. https://developer.android.com/studio/debug/dev-options?hl=ja#enable 3. Android Studio Pair Devices Using Wi-Fi 4. ON 5. Android Studio 6. Android Studio 26 2. Wi-Fi

Slide 27

Slide 27 text

UI 3

Slide 28

Slide 28 text

©MIXI : 1. Android Studio Get from VCS 28 2. URL [email protected]:mixigroup/2023AndroidTraining.git git clone OK 3. app.ui 1

Slide 29

Slide 29 text

©MIXI Jetpack l l 29 app Android 13 Android 12 Android 11 if else if else ... app Android 13 Android 12 Android 11 Jetpack

Slide 30

Slide 30 text

©MIXI Jetpack Compose Jetpack Kotlin UI 2021 UI ( ) https://developer.android.com/jetpack/compose?hl=ja 30

Slide 31

Slide 31 text

©MIXI 31 View : UI Compose : UI val textView = TextView(context) textView.setText("Hello") textView.setTextColor(Color.BLUE) Text( text = "Hello", color = Color.Blue ) View UI View Composable UI Composable

Slide 32

Slide 32 text

©MIXI 32 View : UI Compose : UI val textView = TextView(context) textView.setText("Hello") textView.setTextColor(Color.BLUE) Text( text = "Hello", color = Color.Blue ) Composable UI Composable UI Recompose

Slide 33

Slide 33 text

©MIXI Composable 33 @Composable fun RedText( text: String, modifier: Modifier = Modifier ) { Text( text = text, modifier = modifier, color = Color.Red ) } RedText( text = "Red text" ) RedText( text = "Red text with blue background", modifier = Modifier.background(Color.Blue) )

Slide 34

Slide 34 text

©MIXI Composable 34 @Composable fun RedText( text: String, modifier: Modifier = Modifier ) { Text( text = text, modifier = modifier, color = Color.Red ) } Composable Compose Composable l ProfileScreen l LoginButton l HeaderImage

Slide 35

Slide 35 text

©MIXI Composable 35 // modifier RedText( text = "Red text" ) // modifier RedText( text = "Red text with blue background", modifier = Modifier.background(Color.Blue) ) text = modifier API

Slide 36

Slide 36 text

©MIXI Composable Composable Composable Composable API ( ) 36 Button { Text(text = "Hello") } Composable

Slide 37

Slide 37 text

©MIXI Composable 37 @Composable fun Button( ... content: @Composable () -> Unit ) Button(content = { Text(text = "Hello") }) Button() { Text(text = "Hello") } Button { Text(text = "Hello") } ( ) -> () -> Unit { -> } () ()

Slide 38

Slide 38 text

©MIXI Modifier.padding(all = 8.dp) Modifier.padding(horizontal = 8.dp, vertical = 4.dp) Modifier.padding(start = 8.dp, top = 4.dp, end = 8.dp, bottom = 16.dp) Modifier 38 // 96 x 48 dp Modifier.width(96.dp).height(48.dp) // 48 x 48 dp Modifier.size(48.dp) // Modifier.fillMaxWidth().wrapContentHeight() // Modifier.fillMaxSize() dp

Slide 39

Slide 39 text

©MIXI DP Px 39 DP : density-independent pixels : : : :

Slide 40

Slide 40 text

©MIXI 40 SP : scalable pixels

Slide 41

Slide 41 text

©MIXI Button, Checkbox, TopAppBar Composable 41 API https://developer.android.com/reference/kotlin/androidx/compose/material3/package-summary https://m3.material.io/components

Slide 42

Slide 42 text

©MIXI Composable Composable 42 Column Row

Slide 43

Slide 43 text

©MIXI Composable 43 Column( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(8.dp) ) { Text(text = "Top") Text(text = "Center") Text(text = "Bottom") } Composable (36 )

Slide 44

Slide 44 text

©MIXI Composable Spacer 44 Column(horizontalAlignment = Alignment.CenterHorizontally) { Text(text = "Top") Spacer(modifier = Modifier.height(8.dp)) Text(text = "Center") Spacer(modifier = Modifier.height(8.dp)) Text(text = "Bottom") }

Slide 45

Slide 45 text

©MIXI 45 Column(modifier = modifier.height(160.dp)) { Text(text = "Top", modifier = Modifier.weight(1f)) Text(text = "Center") Text(text = "Bottom") } Column(modifier = modifier.height(160.dp)) { Text(text = "Top", modifier = Modifier.weight(1f)) Text(text = "Center", modifier = Modifier.weight(2f)) Text(text = "Bottom", modifier = Modifier.weight(3f)) } 1:2:3 ⾒

Slide 46

Slide 46 text

©MIXI Composable 46 Row( horizontalArrangement = Arrangement.spacedBy(8.dp), verticalAlignment = Alignment.CenterVertically ) { Text(text = "Left") Text(text = "Center") Text(text = "Right") } Row : Column

Slide 47

Slide 47 text

©MIXI : LazyColumn Compose 47 Column LazyColumn Compose Item 1 Item 2 Item 3 Item 4 Item 5 Item 6 Item 7 Item 8 Item 1 Item 2 Item 3 Item 4 Item 5 Item 6 Item 7 Item 8 Compose

Slide 48

Slide 48 text

©MIXI LazyColumn LazyRow 48 LazyColumn( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(8.dp) ) { // 1 item { Text(text = "Header") } // items(listOf("1", "2", "3")) { text -> Text(text = text) } }

Slide 49

Slide 49 text

©MIXI Composable @Preview 築 49

Slide 50

Slide 50 text

©MIXI 3.1 app.ui BookItem Android Studio Tips 50 16 dp 8 dp 8 dp 4 dp 18 sp, 16 sp 16 dp title author

Slide 51

Slide 51 text

©MIXI Android Studio Tips Option + Enter import 51 Unresolved reference Command + B Command +

Slide 52

Slide 52 text

©MIXI 3.2 ( ) API https://developer.android.com/reference/kotlin/androidx/compose/material3/package-summary 52

Slide 53

Slide 53 text

©MIXI UI 53 Jetpack UI Kotlin Jetpack Compose Composable UI Modifier Composable Column , Row , LazyColumn / LazyRow DP , SP

Slide 54

Slide 54 text

4

Slide 55

Slide 55 text

©MIXI Android 55 ( ) l String resource l Drawable resource l Raw resource src/main/res/

Slide 56

Slide 56 text

©MIXI res/values/strings.xml 56 ... Text(text = stringResource(id = R.string.login)) Text(text = stringResource(id = R.string.settings)) UI

Slide 57

Slide 57 text

©MIXI 57 strings.xml strings.xml 1 2

Slide 58

Slide 58 text

©MIXI 58 strings.xml Settings res/values/strings.xml res/values-ja/strings.xml

Slide 59

Slide 59 text

©MIXI 59 : JPG, PNG, WebP : SVG

Slide 60

Slide 60 text

©MIXI 60 res/drawable-mdpi/ res/drawable-hdpi/ res/drawable-xhdpi/ res/drawable-xxhdpi/ res/drawable-xxxhdpi/ res/drawable/ Adobe XD Sketch Android Studio VectorDrawable ( )

Slide 61

Slide 61 text

©MIXI VectorDrawable 61 SVG or

Slide 62

Slide 62 text

©MIXI Icon( painter = painterResource(id = R.drawable.ramen), contentDescription = stringResource(R.string.ramen), tint = Color.Red ) 62 Image( painter = painterResource(id = R.drawable.ramen), contentDescription = stringResource(R.string.ramen) ) Icon

Slide 63

Slide 63 text

©MIXI Modifier 63 Image( ... modifier = Modifier.clip(CircleShape) ) Image( ... modifier = Modifier.clip(RoundedCornerShape(size = 8.dp)) )

Slide 64

Slide 64 text

©MIXI 4.1 3.1 BookItem 3.1 ⾒ answer/3.1

Slide 65

Slide 65 text

©MIXI 8dp padding 4.2 BookItem 65 16 dp 16 dp 48 x 48 dp l Android Studio l Modifier (33 ) l Modifier : https://developer.android.com/jetpack/compose/modifiers?hl=ja#order-modifier-matters

Slide 66

Slide 66 text

©MIXI 4.3 ( ) 4.1 https://developer.android.com/training/basics/supporting-devices/languages?hl=ja 66 l l

Slide 67

Slide 67 text

©MIXI 67 res/values/strings.xml l l Android Studio VectorDrawable

Slide 68

Slide 68 text

5

Slide 69

Slide 69 text

©MIXI Text(text = " ") Composable (Recompose) UI 69 Text(text = " ")

Slide 70

Slide 70 text

©MIXI 70 0 1 1 2

Slide 71

Slide 71 text

©MIXI 1 : 71 var count = 0 Button(onClick = { count++ }) { Text(text = count.toString()) } count Recompose val : var : 0 0

Slide 72

Slide 72 text

©MIXI 1 : 72 var count by remember { mutableStateOf(0) } Button(onClick = { count++ }) { Text(text = count.toString()) } MutableState Compose Recompose (UI )

Slide 73

Slide 73 text

©MIXI 1 : 73 var count by remember { mutableStateOf(0) } Button(onClick = { count++ }) { Text(text = count.toString()) } Delegated property MutableState Int remember Recompose API : https://developer.android.com/jetpack/compose/state#state-in-composables

Slide 74

Slide 74 text

©MIXI 1 : 74 var count by remember { mutableStateOf(0) } Button(onClick = { count++ }) { Text(text = count.toString()) } 0 1

Slide 75

Slide 75 text

©MIXI var text by remember { mutableStateOf("") } TextField( value = text, onValueChange = { value -> text = value } ) 2 : 75

Slide 76

Slide 76 text

©MIXI 5.1 app.login LoginScreen l l ID 4 8 ( ) 76

Slide 77

Slide 77 text

©MIXI 5.2 ( ) UI https://developer.android.com/reference/kotlin/androidx/compose/material3/package-summary 77 l Switch l Checkbox l Slider

Slide 78

Slide 78 text

©MIXI 78 Composable (Recompose) UI MutableState Recompose var state by remember { mutableStateOf( ) } UI Composable onClick onValueChange e.g. Button, TextField, Switch UI UI

Slide 79

Slide 79 text

6

Slide 80

Slide 80 text

©MIXI 80

Slide 81

Slide 81 text

©MIXI 81

Slide 82

Slide 82 text

©MIXI 82 1 2 3

Slide 83

Slide 83 text

©MIXI 83 :

Slide 84

Slide 84 text

©MIXI 84 Log.v(TAG, " ") Log.d(TAG, " ") Log.i(TAG, " ") Log.w(TAG, " ") Log.e(TAG, " ") ( )

Slide 85

Slide 85 text

©MIXI package:mine 85 package: tag: Log level:

Slide 86

Slide 86 text

©MIXI 86 l l l l

Slide 87

Slide 87 text

7

Slide 88

Slide 88 text

©MIXI ( ) 88 A B A B l l UI l

Slide 89

Slide 89 text

©MIXI https://developer.android.com/topic/architecture?hl=ja 89 UI l UI l l UI l l UI l ( )

Slide 90

Slide 90 text

©MIXI UI 90 UI UI Compose l UI l UI ViewModel l UI l UI UI

Slide 91

Slide 91 text

©MIXI 91 UI UI ( )

Slide 92

Slide 92 text

©MIXI UI 92 Jetpack Room SQLite Jetpack DataStore Key-Value Retrofit HTTP Ktor Kotlin Kotlin HTTP

Slide 93

Slide 93 text

©MIXI : ver. 93 Column(...) { var result by remember { mutableStateOf("") } Text(text = result) Button(onClick = { result = listOf(" ", " ", ...).random() }) { Text(text = stringResource(R.string.pick_omikuji)) } }

Slide 94

Slide 94 text

©MIXI 94 UI l l ( ) l UI l

Slide 95

Slide 95 text

©MIXI ( ) 95 class OmikujiRepository { fun getResult(): String { return listOf(" 規", "規", ...).random() } }

Slide 96

Slide 96 text

©MIXI UI 96 data class OmikujiUiState( val result: String = "" ) equals() copy() OK

Slide 97

Slide 97 text

©MIXI Jetpack 97 class OmikujiViewModel() : ViewModel() { ... } UI ViewModel ViewModel

Slide 98

Slide 98 text

©MIXI ViewModel 98 class OmikujiViewModel( private val repository: OmikujiRepository = OmikujiRepository() ) : ViewModel() { ... } (DI) Hilt DI

Slide 99

Slide 99 text

©MIXI UiState MutableState 99 class OmikujiViewModel(...) : ViewModel() { var uiState by mutableStateOf(OmikujiUiState()) private set } MutableStateFlow l MutableState Compose API l MutableStateFlow Kotlin API

Slide 100

Slide 100 text

©MIXI UiState ViewModel 100 class OmikujiViewModel(...) : ViewModel() { ... fun pick() { val result = repository.getResult() uiState = uiState.copy(result = result) } } UiState UI

Slide 101

Slide 101 text

©MIXI UI ViewModel 101 val viewModel: OmikujiViewModel = viewModel() val uiState = viewModel.uiState Text(text = uiState.result) var result by remember { mutableStateOf("") } Text(text = result) Before After Compose ViewModel

Slide 102

Slide 102 text

©MIXI Button(onClick = viewModel::pick) UI ViewModel 102 Button(onClick = { viewModel.pick() }) Button(onClick = { result = listOf(" ", " ", ...).random() }) Before After OK

Slide 103

Slide 103 text

©MIXI 103 UI UI ViewModel app.omikuji

Slide 104

Slide 104 text

©MIXI 7.1 : ToDo 104

Slide 105

Slide 105 text

©MIXI 7.1 : ToDo app.todo 105 ToDoScreen ToDoViewModel uiState l l UI l UI l l l ToDoUiState

Slide 106

Slide 106 text

©MIXI 7.2 ( ) 5.1 LoginScreen 106 LoginScreen LoginViewModel uiState l l UI l UI l LoginUiState

Slide 107

Slide 107 text

©MIXI 107 , , Android l UI l UI UI l (DB ) ( )

Slide 108

Slide 108 text

8

Slide 109

Slide 109 text

©MIXI 109 UI ViewModel ( ) UI

Slide 110

Slide 110 text

©MIXI src/main/java/jp/co/mixi/androidtraining/omikuji/ui/OmikujiViewModel.kt 110 src/test/java/jp/co/mixi/androidtraining/omikuji/ui/OmikujiViewModelTest.kt ViewModel ViewModel Android Studio "Android" "Project"

Slide 111

Slide 111 text

©MIXI ViewModel 111 class OmikujiViewModelTest { private val viewModel = OmikujiViewModel() @Test fun testPick() { assertEquals("", viewModel.uiState.result) viewModel.pick() assertNotEquals("", viewModel.uiState.result) } }

Slide 112

Slide 112 text

©MIXI ViewModel 112 class OmikujiViewModelTest { private val viewModel = OmikujiViewModel() @Test fun testPick() { assertEquals("", viewModel.uiState.result) viewModel.pick() assertNotEquals("", viewModel.uiState.result) } } JUnit 4 (Java ) API

Slide 113

Slide 113 text

©MIXI ViewModel 113 class OmikujiViewModelTest { private val viewModel = OmikujiViewModel() @Test fun testPick() { assertEquals("", viewModel.uiState.result) viewModel.pick() assertNotEquals("", viewModel.uiState.result) } } uiState

Slide 114

Slide 114 text

©MIXI ViewModel 114 class OmikujiViewModelTest { private val viewModel = OmikujiViewModel() @Test fun testPick() { assertEquals("", viewModel.uiState.result) viewModel.pick() assertEquals(" ", viewModel.uiState.result) } }

Slide 115

Slide 115 text

©MIXI 115 class OmikujiViewModel( private val repository: OmikujiRepository ) : ViewModel() DefaultOmikujiRepository FakeOmikujiRepository

Slide 116

Slide 116 text

©MIXI 116 interface OmikujiRepository { fun getResult(): String } class DefaultOmikujiRepository : OmikujiRepository { override fun getResult(): String { return listOf(" ", " ", ...).random() } } class FakeOmikujiRepository : OmikujiRepository { override fun getResult(): String { return " " } }

Slide 117

Slide 117 text

©MIXI 117 class OmikujiViewModelTest { private val viewModel = OmikujiViewModel(FakeOmikujiRepository()) @Test fun testPick() { assertEquals("", viewModel.uiState.result) viewModel.pick() assertEquals(" ", viewModel.uiState.result) } }

Slide 118

Slide 118 text

©MIXI 118

Slide 119

Slide 119 text

©MIXI 8.1 7.1 ToDoViewModel 119 l l l l

Slide 120

Slide 120 text

©MIXI 8.2 ( ) 7.2 LoginViewModel 120 l ID 3 7 l ID 4 7 l ID 4 8

Slide 121

Slide 121 text

©MIXI 121 (Android ) UI : ViewModel, , : src/test/java/ : JUnit 4

Slide 122

Slide 122 text

9

Slide 123

Slide 123 text

©MIXI : 123

Slide 124

Slide 124 text

©MIXI 124 data class WeatherUiState( val weather: String = "", val isLoading: Boolean = false ) fun getWeather() { // uiState = uiState.copy(isLoading = true) // val weather = repository.getWeather() // uiState = uiState.copy(weather = weather, isLoading = false) } ViewModel

Slide 125

Slide 125 text

©MIXI 125 data class WeatherUiState( val weather: String = "", val isLoading: Boolean = false ) fun getWeather() { // uiState = uiState.copy(isLoading = true) // val weather = repository.getWeather() // uiState = uiState.copy(weather = weather, isLoading = false) } ViewModel (UI )

Slide 126

Slide 126 text

©MIXI Kotlin 126 viewModelScope.launch { uiState = uiState.copy(isLoading = true) val weather = repository.getWeather() uiState = uiState.copy(weather = weather, isLoading = false) } uiState = uiState.copy(isLoading = true) val weather = repository.getWeather() uiState = uiState.copy(weather = weather, isLoading = false)

Slide 127

Slide 127 text

©MIXI Kotlin 127 viewModelScope.launch { uiState = uiState.copy(isLoading = true) val weather = repository.getWeather() uiState = uiState.copy(weather = weather, isLoading = false) } getWeather()

Slide 128

Slide 128 text

©MIXI 128 getWeather()

Slide 129

Slide 129 text

©MIXI 129 class WeatherRepository { suspend fun getWeather(): String { // } } suspend l suspend l suspend suspend

Slide 130

Slide 130 text

©MIXI suspend 130 Kotlin API delay(timeMillis = 2000) 2 Ktor (HTTP ) API val response = client.request(urlString = "https://...") DB Jetpack Room (SQLite ) API val user = dao.getUser(userId = ...)

Slide 131

Slide 131 text

©MIXI 131 viewModelScope.launch { val weather = repository.getWeather() } ViewModel viewModelScope ( )

Slide 132

Slide 132 text

©MIXI 132 UI l l suspend l suspend l suspend l suspend suspend e.g. , DB

Slide 133

Slide 133 text

10

Slide 134

Slide 134 text

©MIXI : 134

Slide 135

Slide 135 text

©MIXI 135 data class WeatherUiState( val weather: String = "", val isLoading: Boolean = false, val isError: Boolean = false ) sealed class WeatherUiState { data class Success(val weather: String) : WeatherUiState() object Loading : WeatherUiState() object Error : WeatherUiState() } OK UiState

Slide 136

Slide 136 text

©MIXI 136 sealed class WeatherUiState { data class Success(val weather: String) : WeatherUiState() object Loading : WeatherUiState() object Error : WeatherUiState() } sealed class WeatherUiState WeatherUiState

Slide 137

Slide 137 text

©MIXI try-catch 137 fun getWeather() { viewModelScope.launch { uiState = WeatherUiState.Loading try { val weather = repository.getWeather() uiState = WeatherUiState.Success(weather = weather) } catch (error: SomeException) { uiState = WeatherUiState.Error } } }

Slide 138

Slide 138 text

©MIXI try-catch 138 fun getWeather() { viewModelScope.launch { uiState = WeatherUiState.Loading try { val weather = repository.getWeather() uiState = WeatherUiState.Success(weather = weather) } catch (error: SomeException) { uiState = WeatherUiState.Error } } } catch Exception catch throw ( )

Slide 139

Slide 139 text

©MIXI UI when app.weather 139 @Composable fun WeatherScreen(...) { val uiState = viewModel.uiState when (uiState) { is WeatherUiState.Success -> SuccessScreen(...) is WeatherUiState.Loading -> LoadingScreen(...) is WeatherUiState.Error -> ErrorScreen(...) } }

Slide 140

Slide 140 text

©MIXI 140 try-catch sealed class / /

Slide 141

Slide 141 text

7 8 9 10

Slide 142

Slide 142 text

©MIXI 10.1 : SNS app.sns app.weather 142 TimelineScreen TimelineViewModel TimelineUiState TimelineRepository List l l UiState l suspend

Slide 143

Slide 143 text

©MIXI 10.2 : / / UI app.sns / app.weather IOException throw 143

Slide 144

Slide 144 text

©MIXI 10.3 : SNS 10.1 ViewModel app.weather : https://developer.android.com/kotlin/coroutines/test?hl=ja 144 l l UiState

Slide 145

Slide 145 text

No content

Slide 146

Slide 146 text

©MIXI Compose, , , , https://github.com/android/nowinandroid 146 https://github.com/android/architecture-samples Compose UI https://github.com/android/compose-samples Now in Android App Android Architecture Samples Jetpack Compose Samples

Slide 147

Slide 147 text

©MIXI https://android-developers.googleblog.com/ 147 https://medium.com/androiddevelopers https://material.io/blog/ Android Developers Blog Medium Material Design Blog https://blog.jetbrains.com/kotlin/ Kotlin Blog