Slide 1

Slide 1 text

Building for Wear OS “Workshop” - Introduction & Part1- Kenichi Kambara (@korodroid) 17 November, 2022 2022

Slide 2

Slide 2 text

Agenda •Part1 (Development) by Kenichi Kambara •Short Session: Developing Apps for Wear OS •Workshop: Developing Wear OS Apps •Part2 (Test) by Josh Murigi •Short Session: Writing tests for Wear OS Apps •Workshop: Testing Wear OS Compose Codelab App •Q&A

Slide 3

Slide 3 text

3 About me Kenichi Kambara (@korodroid) •Mobile App Development •Speeches (e.g. 12 International confs.) •Writings (e.g. 7 Dev Books) •[Of fi cial] Evangelist at NTT TechnoCross •[Private] iplatform.org

Slide 4

Slide 4 text

About my product (Private works) Sekaiphone Pro(Android/iOS/Wear OS) Wear OS version: currently under development

Slide 5

Slide 5 text

Part1. Workshop Material http://bit.ly/3UWME7M

Slide 6

Slide 6 text

Wear OS Features  Overlay Noti fi cation Complication Based on Android & optimized for the wrist Tile 

Slide 7

Slide 7 text

 Compose for Wear OS is now 1.0: time to build wearable apps with Compose! (27 July, 2022) ref: https://android-developers.googleblog.com/2022/07/compose-for-wear-os-10-stable.html Now Stable!!

Slide 8

Slide 8 text

Wear OS Apps Development 8

Slide 9

Slide 9 text

 @Composable fun ChipExample(){ Chip( modifier = Modifier .fillMaxWidth(), icon = { Image( painter = painterResource(id = R.drawable.flag_jp), contentDescription = "Japan", ) }, label = {Text(text = “Japan")}, secondaryLabel = {Text(text = "Tokyo")}, onClick = { /* do anything */ }, ) } Implementation Example (Chip)

Slide 10

Slide 10 text

Very similar to mobile. What’s the difference? 10

Slide 11

Slide 11 text

Optimized for the wrist: UI & UX Input components Dialogs Page Indicator Progress Indicator  Navigation

Slide 12

Slide 12 text

Characteristics of Wear OS 

Slide 13

Slide 13 text

Differences on Wear OS & Mobile  Wear OS 
 (androidx.wear.*) Mobile 
 (androidx.*) androidx.wear.compose:compose-material androidx.compose.foundation:foundation androidx.wear.compose:compose-foundation androidx.compose.material:material androidx.navigation:navigation-compose androidx.compose.foundation:foundation ref: https://developer.android.com/training/wearables/compose Material androidx.wear.compose:compose-navigation Navigation Foundation

Slide 14

Slide 14 text

14 Compose for Wear OS - Get Started

Slide 15

Slide 15 text

15 Wear OS Development Environment  Just my opinion

Slide 16

Slide 16 text

16 Wear OS Development Environment 

Slide 17

Slide 17 text

17 Create a Wear OS app project  for Jetpack Compose

Slide 18

Slide 18 text

After creating the project 

Slide 19

Slide 19 text

19 Live Demo💪 

Slide 20

Slide 20 text

20 Preview 

Slide 21

Slide 21 text

21 More Previews 

Slide 22

Slide 22 text

22 Compose for Wear OS - Workshop

Slide 23

Slide 23 text

Contact App (First Version)  @Composable fun ContactListScreenV0() { LazyColumn( horizontalAlignment = Alignment.CenterHorizontally ) { item { ListHeader { Text(text = "Contacts") } } items(model.data.size) { Chip( modifier = Modifier .fillMaxSize(), icon = { Icon( Icons.Rounded.Face, contentDescription = "faceIcon", ) }, label = { Text(model.data[it]) }, colors = ChipDefaults.primaryChipColors(), onClick = { }, ) } } }

Slide 24

Slide 24 text

not BAD, but not GOOD… How to make better UI for Wear OS? 24

Slide 25

Slide 25 text

Part1. Workshop (Development)  Start Goal

Slide 26

Slide 26 text

5 Steps for improving this App  •Step1. Replacing with ScalingLazyColumn •Step2. Using a Scaffold •Step3. Adding a new Screen •Step4. Implementing Navigation •Step5. Replacing with Wear Navigation 


Slide 27

Slide 27 text

Let’s get started together!!💪 27

Slide 28

Slide 28 text

 Compose for Wear OS - Get Started Summary

Slide 29

Slide 29 text

 •Go future with Wear OS & Jetpack Compose🚀 
 •Important to optimize for Wearable Device⌚ •Let’s expand Wear OS World with making apps💪

Slide 30

Slide 30 text

Please let me know if you have any requests 
 such as technical speeches, technical writings and so on. http://www.linkedin.com/in/korodroid Thank you so much http://fb.com/kanbara.kenichi @korodroid

Slide 31

Slide 31 text

Let’s move on to Part2! 

Slide 32

Slide 32 text

32 Bonus Slides: for your study

Slide 33

Slide 33 text

Step1. Replacing with ScalingLazyColumn  @Composable fun ContactListScreenV1() { ScalingLazyColumn( horizontalAlignment = Alignment.CenterHorizontally ) { item { ListHeader { Text(text = "Contacts") } } items(model.data.size) { Chip( modifier = Modifier .fillMaxSize(), icon = { Icon( Icons.Rounded.Face, contentDescription = "faceIcon", ) }, label = { Text(model.data[it]) }, colors = ChipDefaults.primaryChipColors(), onClick = { }, ) } } }

Slide 34

Slide 34 text

How should we provide better UI/UX?  TimeText Vignette PositionIndicator

Slide 35

Slide 35 text

How to implement them? 35

Slide 36

Slide 36 text

Step2. Using a Scaffold  @Composable fun ContactListScreenV2() { val listState = rememberScalingLazyListState() Scaffold( timeText = { if (!listState.isScrollInProgress) { TimeText() } }, vignette = { Vignette( vignettePosition = VignettePosition.TopAndBottom ) }, positionIndicator = { PositionIndicator( scalingLazyListState = listState, ) } ) { ScalingLazyColumn(state = listState) { // … }

Slide 37

Slide 37 text

Step2-1. TimeText  @Composable fun ContactListScreenV2() { val listState = rememberScalingLazyListState() Scaffold( timeText = { if (!listState.isScrollInProgress) { TimeText() } }, vignette = { Vignette( vignettePosition = VignettePosition.TopAndBottom ) }, positionIndicator = { PositionIndicator( scalingLazyListState = listState, ) } ) { ScalingLazyColumn(state = listState) { // … }

Slide 38

Slide 38 text

Step2-2. Vignette  @Composable fun ContactListScreenV2() { val listState = rememberScalingLazyListState() Scaffold( timeText = { if (!listState.isScrollInProgress) { TimeText() } }, vignette = { Vignette( vignettePosition = VignettePosition.TopAndBottom ) }, positionIndicator = { PositionIndicator( scalingLazyListState = listState, ) } ) { ScalingLazyColumn(state = listState) { // … }

Slide 39

Slide 39 text

Step2-3. PositionIndicator  @Composable fun ContactListScreenV2() { val listState = rememberScalingLazyListState() Scaffold( timeText = { if (!listState.isScrollInProgress) { TimeText() } }, vignette = { Vignette( vignettePosition = VignettePosition.TopAndBottom ) }, positionIndicator = { PositionIndicator( scalingLazyListState = listState, ) } ) { ScalingLazyColumn(state = listState) { // … }

Slide 40

Slide 40 text

How to support multiple screens? 40 

Slide 41

Slide 41 text

Step3. Adding a new Screen  @Composable fun ContactDetailScreen(id: Int) { Chip( modifier = Modifier .fillMaxSize(), icon = { Icon( Icons.Rounded.Face, contentDescription = "faceIcon", ) }, label = { Column( ) { Text(text = model.data[id].name,
 fontWeight = FontWeight.Bold) Text(text = model.data[id].phone) Text(text = model.data[id].nation) } }, colors = ChipDefaults.primaryChipColors(), onClick = { /* do anything */ }, ) }

Slide 42

Slide 42 text

How to support navigation? 42 

Slide 43

Slide 43 text

Step4. Implementing Navigation  @Composable fun ScreenNavigation_BadImpl( navController: NavHostController = rememberNavController() ) { NavHost( navController = navController, startDestination = "contact_list" ) { composable("contact_list") { ContactListScreenV3(navController = navController) } composable("contact_detail/{id}") { val id = it.arguments?.getString("id")!! ContactDetailScreen(id = id.toInt()) } } }

Slide 44

Slide 44 text

Step4. Implementing Navigation  @Composable fun ContactListScreenV3(navController: NavHostController) { Scaffold( // just same ) { ScalingLazyColumn(state = listState) { item { // just same } items(model.data.size) { Chip( modifier = Modifier.fillMaxSize(), icon = { // just same }, label = { Text(model.data[it].name) }, colors = ChipDefaults.primaryChipColors(), onClick = { navController.navigate("contact_detail/$it") }, ) } } } }

Slide 45

Slide 45 text

Looks perfect, but one problem remains… 45 

Slide 46

Slide 46 text

Implementing with Wear Navigation  Wear OS 
 (androidx.wear.*) Mobile 
 (androidx.*) androidx.navigation:navigation-compose ref: https://developer.android.com/training/wearables/compose/navigation androidx.wear.compose:compose-navigation Navigation /build.gradle dependencies { def wear_compose_version = "1.0.2" implementation "androidx.wear.compose:compose-navigation:$wear_compose_version" }

Slide 47

Slide 47 text

Step5. Replacing with Wear Navigation  @Composable fun ScreenNavigation_GoodImpl( navController: NavHostController = rememberSwipeDismissableNavController() ) { SwipeDismissableNavHost( navController = navController, startDestination = "contact_list" ) { composable("contact_list") { ContactListScreenV3(navController = navController) } composable("contact_detail/{id}") { val id = it.arguments?.getString("id")!! ContactDetailScreen(id = id.toInt()) } } }