SlideShare a Scribd company logo
© Instil Software 2020
Compose Part 2
(the practice)
Belfast Google Dev Group
September 2022
@kelvinharron
https://instil.co
– Short history tour of Android UI Development.
– Examples of using Jetpack Compose in across multiple
apps.
– Focus on Navigation, Accessibility & Testing.
– Sneak peak at an upcoming Instil app!
AGENDA
1
A history of
Android UI dev
The classic - findViewById XML layout
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
id used by Activity
The classic - findViewById Activity
private lateinit var textView: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
textView = findViewById<TextView?>(R.id.textView).apply {
text = "Hello GDE"
textSize = 42f
setTextColor(Color.CYAN)
}
}
Compose In Practice
Instil recommendation - Databinding
<layout ...>
<data>
<import type="android.view.View"/>
<variable
name="vm"
type="co.instil.databinding.DemoViewModel"/>
</data>
<TextView
...
android:text="@{vm.text}"/>
</LinearLayout>
</layout>
Databinding XML
layout as root
source of truth
binded field
class DemoActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = DataBindingUtil.setContentView<ActivityDemoBinding>
(this,R.layout.activity_demo)
binding.vm = DemoViewModel()
}
}
Databinding Activity
XML layout file
setting vm on XML
class DemoViewModel {
val text = ObservableField("Data binding works!")
}
ViewModel
The shiny new thing, Jetpack Compose!
UI representing State
… but starting from scratch?
2
Working with
Compose
Compose In Practice
Compose In Practice
Compose In Practice
WIP
Compose In Practice
Sharing Components
Previews that work!
@Composable
@Preview(uiMode = UI_MODE_NIGHT_YES)
fun CarouselScreenDarkPreview() {
EstiMateTheme {
CarouselScreen(
{},
{ CardSelection.Nuclear },
CardSelection.Nuclear
)
}
}
Card(
backgroundColor = instilGreen,
shape = RoundedCornerShape(size = 16.dp),
elevation = 8.dp,
modifier = modifier
.padding(8.dp)
.width(120.dp)
.height(150.dp)
.clickable { onSelected(selection) },
) {
CardContent(
selection = selection,
fontSize = fontSize
)
}
EstiMateCard
default modifier
material component
Box(contentAlignment = Alignment.Center) {
when (selection) {
is CoffeeBreak -> CoffeeImage()
is Nuclear -> NuclearImage()
else -> SelectionText(selection, fontSize)
}
}
CardContent
`Flow` of data
StateFlow<List<Player>>
ViewModel
Composable
Screen
Jetpack
DataStore
Firebase
Flow<List<Player>>
List<Player>
open fun getPlayers(teamName: String): Flow<List<Player>> = callbackFlow {
firebaseService.observePlayersIn(teamName) { players ->
trySend(players)
}
awaitClose { channel.close() }
}
PlayerService calling to Firebase
posting a fresh list of players
val players: StateFlow<List<Player>> = playerService.getPlayers()
.stateIn(
scope = viewModelScope,
started = SharingStarted.Lazily,
initialValue = emptyList()
)
ViewModel called to PlayerService
cancels the work once vm cleared
val votedPlayers: List<Player> by viewModel.players.collectAsState()
...
Composable calling to ViewModel
LazyColumn(modifier = Modifier.fillMaxWidth()) {
items(votedPlayers.size) { index ->
UserVoteItem(votedPlayers[index])
}
}
composable per list item
listOf(
Player(CardSelection.Five, "Kelvin"),
Player(CardSelection.Three, "Garth")
)
Compose In Practice
– Started in 2016.
– MVP pattern through use of interfaces.
– Jetpack Compose introduced as we took ownership of
development.
– Shipped 2 new UI driven features with Jetpack Compose.
Vypr
Android App
Bumping old dependencies
Compose In Practice
Using Jetpack Compose in XML Views
<androidx.compose.ui.platform.ComposeView
android:id="@+id/compose_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
reference id
Using Jetpack Compose in XML Views
private var _binding: FragmentSteersBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentSteersBinding.inflate(inflater, container, false)
val view = binding.root
binding.composeView.apply {
setViewCompositionStrategy(DisposeOnViewTreeLifecycleDestroyed)
setContent {
VyprTheme {
SteersListView()
}
}
}
return view
}
compose_view xml element
our new composable
Using XML views in Jetpack Compose
AndroidView(
modifier = Modifier.fillMaxSize(),
factory = { context ->
AspectRatioImageView(context).apply {
load(steer.previewImageUrl)
setOnClickListener { onSteerClicked(steer) }
}
}
)
legacy view with lots of scary
Compose In Practice
– RecyclerView & adapter complexity removed.
– Jetpack Compose views driven by lifecycle aware ViewModel.
– More testable implementation.
– We fixed the bug!
Steers List Interop example
3
Navigation
Activity to Activity
Activities with Fragments
Single Activity Architecture
Jetpack Compose Navigation!
– NavController - central API for stateful navigation.
– NavHost - links NavController with a navigation graph.
– Each Composable screen is known as a route.
Jetpack Navigation
Now supporting Compose
Compose Navigation!
NavHost(navController = navController, startDestination = "profile") {
composable("profile") { Profile(/*...*/) }
composable("friendslist") { FriendsList(/*...*/) }
/*...*/
}
Navigating to another Route
navController.navigate("friendslist")
Compose Navigation
With Arguments
NavHost(startDestination = "profile/{userId}") {
...
composable(
"profile/{userId}",
arguments = listOf(navArgument("userId") { type = NavType.StringType
})
) {...}
}
...
navController.navigate("profile/user1234")
Compose In Practice
compose-destinations
A KSP library that processes annotations and
generates code that uses Official Jetpack
Compose Navigation under the hood.
It hides the complex, non-type-safe and boilerplate
code you would have to write otherwise.
Rafael
Costa
github.com/raamcosta/compose-destinations
Adding a Destination
@Destination(start = true)
@Composable
fun LoginRoute(
destinationsNavigator: DestinationsNavigator
) {
LoginScreen(
...
)
}
tag composable for generation
provided for nav
Generated NavGraph
object NavGraphs {
val root = NavGraph(
route = "root",
startRoute = LoginRouteDestination,
destinations = listOf(
CarouselRouteDestination,
LoginRouteDestination,
ResultsRouteDestination,
SelectionRouteDestination
)
)
}
each annotated composable
labelled start
Type safe navigation
onLoginClicked = {
destinationsNavigator.navigate(SelectionRouteDestination)
},
Type safe navigation with args
onCardSelected = { cardSelection ->
destinationsNavigator.navigate(
CarouselRouteDestination(selection = cardSelection)
)
})
Custom Serializer
@NavTypeSerializer
class CardSelectionSerializer : DestinationsNavTypeSerializer<CardSelection> {
override fun fromRouteString(routeStr: String): CardSelection {
return CardSelection.fromString(routeStr)
}
override fun toRouteString(value: CardSelection): String {
return value.name
}
}
Under the hood
object LoginRouteDestination : DirectionDestination {
operator fun invoke() = this
@get:RestrictTo(RestrictTo.Scope.SUBCLASSES)
override val baseRoute = "login_route"
override val route = baseRoute
@Composable
override fun DestinationScope<Unit>.Content(
dependenciesContainerBuilder: @Composable DependenciesContainerBuilder<Unit>.() -> Unit
) {
LoginRoute(
destinationsNavigator = destinationsNavigator
)
}
}
our composable
4
Accessibility
What is Accessibility?
“Mobile accessibility” refers to making websites
and applications more accessible to people with
disabilities when they are using mobile phones
and other devices.
Shawn Lawton
Henry
w3.org/WAI/standards-guidelines/mobile/
Android Accessibility
– Switch Access: interact with switches instead of the
touchscreen.
– BrailleBack: refreshable Braille display to an Android device
over Bluetooth.
– Voice Access: control an Android device with spoken
commands.
– TalkBack: spoken feedback for UI interactions.
What options are baked into the OS?
TalkBack
An essential tool for every Android team
Compose In Practice
Play Store Testing
Play Store Testing - Example Issue
Screen Reader issue confirmed
no context of dialog
Fixing the Dialog
val accountDeletionDialogAccessibilityLabel = stringResource(
id = R.string.accessibility_account_deletion_delete_dialog
)
AlertDialog(
...
modifier = Modifier.semantics(mergeDescendants = true) {
contentDescription = accountDeletionDialogAccessibilityLabel
}
)
Fixed!
Accessibility Starts with Design
Screen Sizes & Resolution
UI Mockups
– Discuss what the UI toolkit can do when size is
constrained. Compose is good at scaling text!
– Agree how to handle view scaling.
– Agree copy for accessibility labelling.
– Collaborate with designers & product owners.
5
Testing
Compose Semantics
Semantics, as the name implies, give meaning to a
piece of UI. In this context, a "piece of UI" (or element)
can mean anything from a single composable to a full
screen.
The semantics tree is generated alongside the UI
hierarchy, and describes it.
Example Button
Button(
modifier = Modifier.semantics {
contentDescription = "Add to favorites"
}
)
individual ui elements make up a button
easier to find
Test Setup
@get:Rule
val composeTestRule = createAndroidComposeRule<VyPopsActivity>()
@Before
fun beforeEachTest() {
composeTestRule.setContent {
VyprTheme {
VyPopsLandingScreen(EmptyDestinationsNavigator)
}
}
}
Finders
Select one or more elements (nodes) to assert or act on
composeTestRule
.onNodeWithContentDescription("Close Button")
composeTestRule
.onNodeWithText("What happens next")
Finders - Debug Logging
Node #1 at (l=0.0, t=54.0, r=720.0, b=1436.0)px
|-Node #2 at (l=70.0, t=54.0, r=650.0, b=1436.0)px
ContentDescription = '[VyPops Permissions Page]'
|-Node #3 at (l=70.0, t=75.0, r=112.0, b=117.0)px
| Role = 'Button'
| Focused = 'false'
| ContentDescription = '[Close Button]'
| Actions = [OnClick]
| MergeDescendants = 'true'
|-Node #6 at (l=229.0, t=194.0, r=492.0, b=303.0)px
| ContentDescription = '[Vypr Logo]'
| Role = 'Image'
|-Node #7 at (l=91.0, t=687.0, r=133.0, b=729.0)px
| ContentDescription = '[Record Audio Tick]'
| Role = 'Image'
|-Node #8 at (l=147.0, t=684.0, r=615.0, b=731.0)px
| Text = '[Microphone access granted]'
| Actions = [GetTextLayoutResult]
|-Node #9 at (l=125.0, t=762.0, r=167.0, b=804.0)px
| ContentDescription = '[Camera Tick]'
| Role = 'Image'
|-Node #10 at (l=181.0, t=759.0, r=582.0, b=806.0)px
| Text = '[Camera access granted]'
| Actions = [GetTextLayoutResult]
|-Node #11 at (l=84.0, t=1275.0, r=636.0, b=1366.0)px
Text = '[VyPops needs access to both your camera and microphone.]'
Actions = [GetTextLayoutResult]
Node #1 at (l=0.0, t=54.0, r=720.0, b=1436.0)px
|-Node #2 at (l=70.0, t=54.0, r=650.0, b=1436.0)px
ContentDescription = '[VyPops Permissions Page]'
|-Node #3 at (l=70.0, t=75.0, r=112.0, b=117.0)px
| Role = 'Button'
| Focused = 'false'
| Actions = [OnClick]
| MergeDescendants = 'true'
| |-Node #5 at (l=70.0, t=75.0, r=112.0, b=117.0)px
| ContentDescription = '[Close Button]'
| Role = 'Image'
|-Node #6 at (l=229.0, t=194.0, r=492.0, b=303.0)px
| ContentDescription = '[Vypr Logo]'
| Role = 'Image'
|-Node #7 at (l=91.0, t=687.0, r=133.0, b=729.0)px
| ContentDescription = '[Record Audio Tick]'
| Role = 'Image'
|-Node #8 at (l=147.0, t=684.0, r=615.0, b=731.0)px
| Text = '[Microphone access granted]'
| Actions = [GetTextLayoutResult]
|-Node #9 at (l=125.0, t=762.0, r=167.0, b=804.0)px
| ContentDescription = '[Camera Tick]'
| Role = 'Image'
|-Node #10 at (l=181.0, t=759.0, r=582.0, b=806.0)px
| Text = '[Camera access granted]'
| Actions = [GetTextLayoutResult]
|-Node #11 at (l=84.0, t=1275.0, r=636.0, b=1366.0)px
Text = '[VyPops needs access to both your camera and microphone.]'
Actions = [GetTextLayoutResult]
Assertions
Verify elements exist or have certain attributes
composeTestRule
.onNodeWithContentDescription("Login Button")
.assertIsEnabled()
composeTestRule
.onNodeWithText("What happens next")
.assertIsDisplayed()
Simulate user input or gestures
Actions
composeTestRule
.onNodeWithContentDescription("Close Button")
.performClick()
...
.performTouchInput {
swipeLeft()
}
developer.android.com/jetpack/compose/testing-cheatsheet
Shot is a Gradle plugin and a core android library
thought to run screenshot tests for Android.
Pedro
Gómez
github.com/pedrovgs/Shot
./gradlew executeScreenshotTests -Precord
Example Test
@get:Rule
val composeRule = createAndroidComposeRule<AccountDeletionActivity>()
@Test
fun accountDeletedScreenBodyScreenshot() {
composeRule.setContent { AccountDeletedScreenBody() }
compareScreenshot(composeRule)
}
./gradlew executeScreenshotTests
Example Output
6
Introducing Compose
to your team
Compose In Practice
Compose In Practice
Introducing Compose
Phased approach
– Do you have an existing app with custom UI
components?
– Recreate them in Compose!
– Provide a foundation to educate your team.
– Define standards & best practices.
Introducing Compose
medium.com/tinder/sparking-jetpack-compose-at-tinder-8f15d77eb4da
7
Conclusions
– We’ve adopted Jetpack Compose for all new Android
projects.
– Excellent official documentation & codelabs available.
– Good tooling and a growing list of third-party libraries
available.
– Recommend new starts prioritise Jetpack Compose over
XML.
Conclusions
EstiMate for Android is coming soon™
Questions?
Questions?
Ad

More Related Content

Similar to Compose In Practice (20)

Telerik Kendo UI Overview
Telerik Kendo UI OverviewTelerik Kendo UI Overview
Telerik Kendo UI Overview
Ed Musters
 
Training Session 2 - Day 2
Training Session 2 - Day 2Training Session 2 - Day 2
Training Session 2 - Day 2
Vivek Bhusal
 
Fragments: Why, How, What For?
Fragments: Why, How, What For?Fragments: Why, How, What For?
Fragments: Why, How, What For?
Brenda Cook
 
How to use data binding in android
How to use data binding in androidHow to use data binding in android
How to use data binding in android
InnovationM
 
Infinum Android Talks #14 - Data binding to the rescue... or not (?) by Krist...
Infinum Android Talks #14 - Data binding to the rescue... or not (?) by Krist...Infinum Android Talks #14 - Data binding to the rescue... or not (?) by Krist...
Infinum Android Talks #14 - Data binding to the rescue... or not (?) by Krist...
Infinum
 
Building Modern Apps using Android Architecture Components
Building Modern Apps using Android Architecture ComponentsBuilding Modern Apps using Android Architecture Components
Building Modern Apps using Android Architecture Components
Hassan Abid
 
android level 3
android level 3android level 3
android level 3
DevMix
 
Android accessibility for developers and QA
Android accessibility for developers and QAAndroid accessibility for developers and QA
Android accessibility for developers and QA
Ted Drake
 
Android gui framework
Android gui frameworkAndroid gui framework
Android gui framework
Sri Harsha Pamu
 
Answer1)Responsive design is the idea where all the developed pag.pdf
Answer1)Responsive design is the idea where all the developed pag.pdfAnswer1)Responsive design is the idea where all the developed pag.pdf
Answer1)Responsive design is the idea where all the developed pag.pdf
ankitcomputer11
 
Android Development project
Android Development projectAndroid Development project
Android Development project
Minhaj Kazi
 
Android Workshop
Android WorkshopAndroid Workshop
Android Workshop
Junda Ong
 
Android
AndroidAndroid
Android
Ravi Shankar
 
Android
AndroidAndroid
Android
Pranav Ashok
 
What's new in Android Wear 2.0
What's new in Android Wear 2.0What's new in Android Wear 2.0
What's new in Android Wear 2.0
Peter Friese
 
android design pattern
android design patternandroid design pattern
android design pattern
Lucas Xu
 
Android Bootcamp Tanzania:understanding ui in_android
Android Bootcamp Tanzania:understanding ui in_androidAndroid Bootcamp Tanzania:understanding ui in_android
Android Bootcamp Tanzania:understanding ui in_android
Denis Minja
 
Android Tutorials : Basic widgets
Android Tutorials : Basic widgetsAndroid Tutorials : Basic widgets
Android Tutorials : Basic widgets
Prajyot Mainkar
 
Android Study Jams- Day 2(Hands on Experience)
Android Study Jams- Day 2(Hands on Experience)Android Study Jams- Day 2(Hands on Experience)
Android Study Jams- Day 2(Hands on Experience)
GoogleDSC
 
Stmik bandung
Stmik bandungStmik bandung
Stmik bandung
farid savarudin
 
Telerik Kendo UI Overview
Telerik Kendo UI OverviewTelerik Kendo UI Overview
Telerik Kendo UI Overview
Ed Musters
 
Training Session 2 - Day 2
Training Session 2 - Day 2Training Session 2 - Day 2
Training Session 2 - Day 2
Vivek Bhusal
 
Fragments: Why, How, What For?
Fragments: Why, How, What For?Fragments: Why, How, What For?
Fragments: Why, How, What For?
Brenda Cook
 
How to use data binding in android
How to use data binding in androidHow to use data binding in android
How to use data binding in android
InnovationM
 
Infinum Android Talks #14 - Data binding to the rescue... or not (?) by Krist...
Infinum Android Talks #14 - Data binding to the rescue... or not (?) by Krist...Infinum Android Talks #14 - Data binding to the rescue... or not (?) by Krist...
Infinum Android Talks #14 - Data binding to the rescue... or not (?) by Krist...
Infinum
 
Building Modern Apps using Android Architecture Components
Building Modern Apps using Android Architecture ComponentsBuilding Modern Apps using Android Architecture Components
Building Modern Apps using Android Architecture Components
Hassan Abid
 
android level 3
android level 3android level 3
android level 3
DevMix
 
Android accessibility for developers and QA
Android accessibility for developers and QAAndroid accessibility for developers and QA
Android accessibility for developers and QA
Ted Drake
 
Answer1)Responsive design is the idea where all the developed pag.pdf
Answer1)Responsive design is the idea where all the developed pag.pdfAnswer1)Responsive design is the idea where all the developed pag.pdf
Answer1)Responsive design is the idea where all the developed pag.pdf
ankitcomputer11
 
Android Development project
Android Development projectAndroid Development project
Android Development project
Minhaj Kazi
 
Android Workshop
Android WorkshopAndroid Workshop
Android Workshop
Junda Ong
 
What's new in Android Wear 2.0
What's new in Android Wear 2.0What's new in Android Wear 2.0
What's new in Android Wear 2.0
Peter Friese
 
android design pattern
android design patternandroid design pattern
android design pattern
Lucas Xu
 
Android Bootcamp Tanzania:understanding ui in_android
Android Bootcamp Tanzania:understanding ui in_androidAndroid Bootcamp Tanzania:understanding ui in_android
Android Bootcamp Tanzania:understanding ui in_android
Denis Minja
 
Android Tutorials : Basic widgets
Android Tutorials : Basic widgetsAndroid Tutorials : Basic widgets
Android Tutorials : Basic widgets
Prajyot Mainkar
 
Android Study Jams- Day 2(Hands on Experience)
Android Study Jams- Day 2(Hands on Experience)Android Study Jams- Day 2(Hands on Experience)
Android Study Jams- Day 2(Hands on Experience)
GoogleDSC
 

Recently uploaded (20)

Interactive odoo dashboards for sales, CRM , Inventory, Invoice, Purchase, Pr...
Interactive odoo dashboards for sales, CRM , Inventory, Invoice, Purchase, Pr...Interactive odoo dashboards for sales, CRM , Inventory, Invoice, Purchase, Pr...
Interactive odoo dashboards for sales, CRM , Inventory, Invoice, Purchase, Pr...
AxisTechnolabs
 
Revolutionizing Residential Wi-Fi PPT.pptx
Revolutionizing Residential Wi-Fi PPT.pptxRevolutionizing Residential Wi-Fi PPT.pptx
Revolutionizing Residential Wi-Fi PPT.pptx
nidhisingh691197
 
Landscape of Requirements Engineering for/by AI through Literature Review
Landscape of Requirements Engineering for/by AI through Literature ReviewLandscape of Requirements Engineering for/by AI through Literature Review
Landscape of Requirements Engineering for/by AI through Literature Review
Hironori Washizaki
 
Download YouTube By Click 2025 Free Full Activated
Download YouTube By Click 2025 Free Full ActivatedDownload YouTube By Click 2025 Free Full Activated
Download YouTube By Click 2025 Free Full Activated
saniamalik72555
 
Douwan Crack 2025 new verson+ License code
Douwan Crack 2025 new verson+ License codeDouwan Crack 2025 new verson+ License code
Douwan Crack 2025 new verson+ License code
aneelaramzan63
 
Exceptional Behaviors: How Frequently Are They Tested? (AST 2025)
Exceptional Behaviors: How Frequently Are They Tested? (AST 2025)Exceptional Behaviors: How Frequently Are They Tested? (AST 2025)
Exceptional Behaviors: How Frequently Are They Tested? (AST 2025)
Andre Hora
 
Exploring Code Comprehension in Scientific Programming: Preliminary Insight...
Exploring Code Comprehension  in Scientific Programming:  Preliminary Insight...Exploring Code Comprehension  in Scientific Programming:  Preliminary Insight...
Exploring Code Comprehension in Scientific Programming: Preliminary Insight...
University of Hawai‘i at Mānoa
 
Meet the Agents: How AI Is Learning to Think, Plan, and Collaborate
Meet the Agents: How AI Is Learning to Think, Plan, and CollaborateMeet the Agents: How AI Is Learning to Think, Plan, and Collaborate
Meet the Agents: How AI Is Learning to Think, Plan, and Collaborate
Maxim Salnikov
 
How to Optimize Your AWS Environment for Improved Cloud Performance
How to Optimize Your AWS Environment for Improved Cloud PerformanceHow to Optimize Your AWS Environment for Improved Cloud Performance
How to Optimize Your AWS Environment for Improved Cloud Performance
ThousandEyes
 
Pixologic ZBrush Crack Plus Activation Key [Latest 2025] New Version
Pixologic ZBrush Crack Plus Activation Key [Latest 2025] New VersionPixologic ZBrush Crack Plus Activation Key [Latest 2025] New Version
Pixologic ZBrush Crack Plus Activation Key [Latest 2025] New Version
saimabibi60507
 
Explaining GitHub Actions Failures with Large Language Models Challenges, In...
Explaining GitHub Actions Failures with Large Language Models Challenges, In...Explaining GitHub Actions Failures with Large Language Models Challenges, In...
Explaining GitHub Actions Failures with Large Language Models Challenges, In...
ssuserb14185
 
F-Secure Freedome VPN 2025 Crack Plus Activation New Version
F-Secure Freedome VPN 2025 Crack Plus Activation  New VersionF-Secure Freedome VPN 2025 Crack Plus Activation  New Version
F-Secure Freedome VPN 2025 Crack Plus Activation New Version
saimabibi60507
 
Requirements in Engineering AI- Enabled Systems: Open Problems and Safe AI Sy...
Requirements in Engineering AI- Enabled Systems: Open Problems and Safe AI Sy...Requirements in Engineering AI- Enabled Systems: Open Problems and Safe AI Sy...
Requirements in Engineering AI- Enabled Systems: Open Problems and Safe AI Sy...
Lionel Briand
 
Top 10 Client Portal Software Solutions for 2025.docx
Top 10 Client Portal Software Solutions for 2025.docxTop 10 Client Portal Software Solutions for 2025.docx
Top 10 Client Portal Software Solutions for 2025.docx
Portli
 
TestMigrationsInPy: A Dataset of Test Migrations from Unittest to Pytest (MSR...
TestMigrationsInPy: A Dataset of Test Migrations from Unittest to Pytest (MSR...TestMigrationsInPy: A Dataset of Test Migrations from Unittest to Pytest (MSR...
TestMigrationsInPy: A Dataset of Test Migrations from Unittest to Pytest (MSR...
Andre Hora
 
Automation Techniques in RPA - UiPath Certificate
Automation Techniques in RPA - UiPath CertificateAutomation Techniques in RPA - UiPath Certificate
Automation Techniques in RPA - UiPath Certificate
VICTOR MAESTRE RAMIREZ
 
Salesforce Data Cloud- Hyperscale data platform, built for Salesforce.
Salesforce Data Cloud- Hyperscale data platform, built for Salesforce.Salesforce Data Cloud- Hyperscale data platform, built for Salesforce.
Salesforce Data Cloud- Hyperscale data platform, built for Salesforce.
Dele Amefo
 
Avast Premium Security Crack FREE Latest Version 2025
Avast Premium Security Crack FREE Latest Version 2025Avast Premium Security Crack FREE Latest Version 2025
Avast Premium Security Crack FREE Latest Version 2025
mu394968
 
Adobe After Effects Crack FREE FRESH version 2025
Adobe After Effects Crack FREE FRESH version 2025Adobe After Effects Crack FREE FRESH version 2025
Adobe After Effects Crack FREE FRESH version 2025
kashifyounis067
 
Adobe Marketo Engage Champion Deep Dive - SFDC CRM Synch V2 & Usage Dashboards
Adobe Marketo Engage Champion Deep Dive - SFDC CRM Synch V2 & Usage DashboardsAdobe Marketo Engage Champion Deep Dive - SFDC CRM Synch V2 & Usage Dashboards
Adobe Marketo Engage Champion Deep Dive - SFDC CRM Synch V2 & Usage Dashboards
BradBedford3
 
Interactive odoo dashboards for sales, CRM , Inventory, Invoice, Purchase, Pr...
Interactive odoo dashboards for sales, CRM , Inventory, Invoice, Purchase, Pr...Interactive odoo dashboards for sales, CRM , Inventory, Invoice, Purchase, Pr...
Interactive odoo dashboards for sales, CRM , Inventory, Invoice, Purchase, Pr...
AxisTechnolabs
 
Revolutionizing Residential Wi-Fi PPT.pptx
Revolutionizing Residential Wi-Fi PPT.pptxRevolutionizing Residential Wi-Fi PPT.pptx
Revolutionizing Residential Wi-Fi PPT.pptx
nidhisingh691197
 
Landscape of Requirements Engineering for/by AI through Literature Review
Landscape of Requirements Engineering for/by AI through Literature ReviewLandscape of Requirements Engineering for/by AI through Literature Review
Landscape of Requirements Engineering for/by AI through Literature Review
Hironori Washizaki
 
Download YouTube By Click 2025 Free Full Activated
Download YouTube By Click 2025 Free Full ActivatedDownload YouTube By Click 2025 Free Full Activated
Download YouTube By Click 2025 Free Full Activated
saniamalik72555
 
Douwan Crack 2025 new verson+ License code
Douwan Crack 2025 new verson+ License codeDouwan Crack 2025 new verson+ License code
Douwan Crack 2025 new verson+ License code
aneelaramzan63
 
Exceptional Behaviors: How Frequently Are They Tested? (AST 2025)
Exceptional Behaviors: How Frequently Are They Tested? (AST 2025)Exceptional Behaviors: How Frequently Are They Tested? (AST 2025)
Exceptional Behaviors: How Frequently Are They Tested? (AST 2025)
Andre Hora
 
Exploring Code Comprehension in Scientific Programming: Preliminary Insight...
Exploring Code Comprehension  in Scientific Programming:  Preliminary Insight...Exploring Code Comprehension  in Scientific Programming:  Preliminary Insight...
Exploring Code Comprehension in Scientific Programming: Preliminary Insight...
University of Hawai‘i at Mānoa
 
Meet the Agents: How AI Is Learning to Think, Plan, and Collaborate
Meet the Agents: How AI Is Learning to Think, Plan, and CollaborateMeet the Agents: How AI Is Learning to Think, Plan, and Collaborate
Meet the Agents: How AI Is Learning to Think, Plan, and Collaborate
Maxim Salnikov
 
How to Optimize Your AWS Environment for Improved Cloud Performance
How to Optimize Your AWS Environment for Improved Cloud PerformanceHow to Optimize Your AWS Environment for Improved Cloud Performance
How to Optimize Your AWS Environment for Improved Cloud Performance
ThousandEyes
 
Pixologic ZBrush Crack Plus Activation Key [Latest 2025] New Version
Pixologic ZBrush Crack Plus Activation Key [Latest 2025] New VersionPixologic ZBrush Crack Plus Activation Key [Latest 2025] New Version
Pixologic ZBrush Crack Plus Activation Key [Latest 2025] New Version
saimabibi60507
 
Explaining GitHub Actions Failures with Large Language Models Challenges, In...
Explaining GitHub Actions Failures with Large Language Models Challenges, In...Explaining GitHub Actions Failures with Large Language Models Challenges, In...
Explaining GitHub Actions Failures with Large Language Models Challenges, In...
ssuserb14185
 
F-Secure Freedome VPN 2025 Crack Plus Activation New Version
F-Secure Freedome VPN 2025 Crack Plus Activation  New VersionF-Secure Freedome VPN 2025 Crack Plus Activation  New Version
F-Secure Freedome VPN 2025 Crack Plus Activation New Version
saimabibi60507
 
Requirements in Engineering AI- Enabled Systems: Open Problems and Safe AI Sy...
Requirements in Engineering AI- Enabled Systems: Open Problems and Safe AI Sy...Requirements in Engineering AI- Enabled Systems: Open Problems and Safe AI Sy...
Requirements in Engineering AI- Enabled Systems: Open Problems and Safe AI Sy...
Lionel Briand
 
Top 10 Client Portal Software Solutions for 2025.docx
Top 10 Client Portal Software Solutions for 2025.docxTop 10 Client Portal Software Solutions for 2025.docx
Top 10 Client Portal Software Solutions for 2025.docx
Portli
 
TestMigrationsInPy: A Dataset of Test Migrations from Unittest to Pytest (MSR...
TestMigrationsInPy: A Dataset of Test Migrations from Unittest to Pytest (MSR...TestMigrationsInPy: A Dataset of Test Migrations from Unittest to Pytest (MSR...
TestMigrationsInPy: A Dataset of Test Migrations from Unittest to Pytest (MSR...
Andre Hora
 
Automation Techniques in RPA - UiPath Certificate
Automation Techniques in RPA - UiPath CertificateAutomation Techniques in RPA - UiPath Certificate
Automation Techniques in RPA - UiPath Certificate
VICTOR MAESTRE RAMIREZ
 
Salesforce Data Cloud- Hyperscale data platform, built for Salesforce.
Salesforce Data Cloud- Hyperscale data platform, built for Salesforce.Salesforce Data Cloud- Hyperscale data platform, built for Salesforce.
Salesforce Data Cloud- Hyperscale data platform, built for Salesforce.
Dele Amefo
 
Avast Premium Security Crack FREE Latest Version 2025
Avast Premium Security Crack FREE Latest Version 2025Avast Premium Security Crack FREE Latest Version 2025
Avast Premium Security Crack FREE Latest Version 2025
mu394968
 
Adobe After Effects Crack FREE FRESH version 2025
Adobe After Effects Crack FREE FRESH version 2025Adobe After Effects Crack FREE FRESH version 2025
Adobe After Effects Crack FREE FRESH version 2025
kashifyounis067
 
Adobe Marketo Engage Champion Deep Dive - SFDC CRM Synch V2 & Usage Dashboards
Adobe Marketo Engage Champion Deep Dive - SFDC CRM Synch V2 & Usage DashboardsAdobe Marketo Engage Champion Deep Dive - SFDC CRM Synch V2 & Usage Dashboards
Adobe Marketo Engage Champion Deep Dive - SFDC CRM Synch V2 & Usage Dashboards
BradBedford3
 
Ad

Compose In Practice

  • 1. © Instil Software 2020 Compose Part 2 (the practice) Belfast Google Dev Group September 2022
  • 3. – Short history tour of Android UI Development. – Examples of using Jetpack Compose in across multiple apps. – Focus on Navigation, Accessibility & Testing. – Sneak peak at an upcoming Instil app! AGENDA
  • 5. The classic - findViewById XML layout <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout> id used by Activity
  • 6. The classic - findViewById Activity private lateinit var textView: TextView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) textView = findViewById<TextView?>(R.id.textView).apply { text = "Hello GDE" textSize = 42f setTextColor(Color.CYAN) } }
  • 10. class DemoActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val binding = DataBindingUtil.setContentView<ActivityDemoBinding> (this,R.layout.activity_demo) binding.vm = DemoViewModel() } } Databinding Activity XML layout file setting vm on XML
  • 11. class DemoViewModel { val text = ObservableField("Data binding works!") } ViewModel
  • 12. The shiny new thing, Jetpack Compose!
  • 14. … but starting from scratch?
  • 19. WIP
  • 22. Previews that work! @Composable @Preview(uiMode = UI_MODE_NIGHT_YES) fun CarouselScreenDarkPreview() { EstiMateTheme { CarouselScreen( {}, { CardSelection.Nuclear }, CardSelection.Nuclear ) } }
  • 23. Card( backgroundColor = instilGreen, shape = RoundedCornerShape(size = 16.dp), elevation = 8.dp, modifier = modifier .padding(8.dp) .width(120.dp) .height(150.dp) .clickable { onSelected(selection) }, ) { CardContent( selection = selection, fontSize = fontSize ) } EstiMateCard default modifier material component
  • 24. Box(contentAlignment = Alignment.Center) { when (selection) { is CoffeeBreak -> CoffeeImage() is Nuclear -> NuclearImage() else -> SelectionText(selection, fontSize) } } CardContent
  • 26. open fun getPlayers(teamName: String): Flow<List<Player>> = callbackFlow { firebaseService.observePlayersIn(teamName) { players -> trySend(players) } awaitClose { channel.close() } } PlayerService calling to Firebase posting a fresh list of players
  • 27. val players: StateFlow<List<Player>> = playerService.getPlayers() .stateIn( scope = viewModelScope, started = SharingStarted.Lazily, initialValue = emptyList() ) ViewModel called to PlayerService cancels the work once vm cleared
  • 28. val votedPlayers: List<Player> by viewModel.players.collectAsState() ... Composable calling to ViewModel LazyColumn(modifier = Modifier.fillMaxWidth()) { items(votedPlayers.size) { index -> UserVoteItem(votedPlayers[index]) } } composable per list item listOf( Player(CardSelection.Five, "Kelvin"), Player(CardSelection.Three, "Garth") )
  • 30. – Started in 2016. – MVP pattern through use of interfaces. – Jetpack Compose introduced as we took ownership of development. – Shipped 2 new UI driven features with Jetpack Compose. Vypr Android App
  • 33. Using Jetpack Compose in XML Views <androidx.compose.ui.platform.ComposeView android:id="@+id/compose_view" android:layout_width="match_parent" android:layout_height="match_parent" /> reference id
  • 34. Using Jetpack Compose in XML Views private var _binding: FragmentSteersBinding? = null private val binding get() = _binding!! override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { _binding = FragmentSteersBinding.inflate(inflater, container, false) val view = binding.root binding.composeView.apply { setViewCompositionStrategy(DisposeOnViewTreeLifecycleDestroyed) setContent { VyprTheme { SteersListView() } } } return view } compose_view xml element our new composable
  • 35. Using XML views in Jetpack Compose AndroidView( modifier = Modifier.fillMaxSize(), factory = { context -> AspectRatioImageView(context).apply { load(steer.previewImageUrl) setOnClickListener { onSteerClicked(steer) } } } ) legacy view with lots of scary
  • 37. – RecyclerView & adapter complexity removed. – Jetpack Compose views driven by lifecycle aware ViewModel. – More testable implementation. – We fixed the bug! Steers List Interop example
  • 43. – NavController - central API for stateful navigation. – NavHost - links NavController with a navigation graph. – Each Composable screen is known as a route. Jetpack Navigation Now supporting Compose
  • 44. Compose Navigation! NavHost(navController = navController, startDestination = "profile") { composable("profile") { Profile(/*...*/) } composable("friendslist") { FriendsList(/*...*/) } /*...*/ }
  • 45. Navigating to another Route navController.navigate("friendslist")
  • 46. Compose Navigation With Arguments NavHost(startDestination = "profile/{userId}") { ... composable( "profile/{userId}", arguments = listOf(navArgument("userId") { type = NavType.StringType }) ) {...} } ... navController.navigate("profile/user1234")
  • 49. A KSP library that processes annotations and generates code that uses Official Jetpack Compose Navigation under the hood. It hides the complex, non-type-safe and boilerplate code you would have to write otherwise. Rafael Costa github.com/raamcosta/compose-destinations
  • 50. Adding a Destination @Destination(start = true) @Composable fun LoginRoute( destinationsNavigator: DestinationsNavigator ) { LoginScreen( ... ) } tag composable for generation provided for nav
  • 51. Generated NavGraph object NavGraphs { val root = NavGraph( route = "root", startRoute = LoginRouteDestination, destinations = listOf( CarouselRouteDestination, LoginRouteDestination, ResultsRouteDestination, SelectionRouteDestination ) ) } each annotated composable labelled start
  • 52. Type safe navigation onLoginClicked = { destinationsNavigator.navigate(SelectionRouteDestination) },
  • 53. Type safe navigation with args onCardSelected = { cardSelection -> destinationsNavigator.navigate( CarouselRouteDestination(selection = cardSelection) ) })
  • 54. Custom Serializer @NavTypeSerializer class CardSelectionSerializer : DestinationsNavTypeSerializer<CardSelection> { override fun fromRouteString(routeStr: String): CardSelection { return CardSelection.fromString(routeStr) } override fun toRouteString(value: CardSelection): String { return value.name } }
  • 55. Under the hood object LoginRouteDestination : DirectionDestination { operator fun invoke() = this @get:RestrictTo(RestrictTo.Scope.SUBCLASSES) override val baseRoute = "login_route" override val route = baseRoute @Composable override fun DestinationScope<Unit>.Content( dependenciesContainerBuilder: @Composable DependenciesContainerBuilder<Unit>.() -> Unit ) { LoginRoute( destinationsNavigator = destinationsNavigator ) } } our composable
  • 58. “Mobile accessibility” refers to making websites and applications more accessible to people with disabilities when they are using mobile phones and other devices. Shawn Lawton Henry w3.org/WAI/standards-guidelines/mobile/
  • 59. Android Accessibility – Switch Access: interact with switches instead of the touchscreen. – BrailleBack: refreshable Braille display to an Android device over Bluetooth. – Voice Access: control an Android device with spoken commands. – TalkBack: spoken feedback for UI interactions. What options are baked into the OS?
  • 61. An essential tool for every Android team
  • 64. Play Store Testing - Example Issue
  • 65. Screen Reader issue confirmed no context of dialog
  • 66. Fixing the Dialog val accountDeletionDialogAccessibilityLabel = stringResource( id = R.string.accessibility_account_deletion_delete_dialog ) AlertDialog( ... modifier = Modifier.semantics(mergeDescendants = true) { contentDescription = accountDeletionDialogAccessibilityLabel } )
  • 69. Screen Sizes & Resolution
  • 70. UI Mockups – Discuss what the UI toolkit can do when size is constrained. Compose is good at scaling text! – Agree how to handle view scaling. – Agree copy for accessibility labelling. – Collaborate with designers & product owners.
  • 72. Compose Semantics Semantics, as the name implies, give meaning to a piece of UI. In this context, a "piece of UI" (or element) can mean anything from a single composable to a full screen. The semantics tree is generated alongside the UI hierarchy, and describes it.
  • 73. Example Button Button( modifier = Modifier.semantics { contentDescription = "Add to favorites" } ) individual ui elements make up a button easier to find
  • 74. Test Setup @get:Rule val composeTestRule = createAndroidComposeRule<VyPopsActivity>() @Before fun beforeEachTest() { composeTestRule.setContent { VyprTheme { VyPopsLandingScreen(EmptyDestinationsNavigator) } } }
  • 75. Finders Select one or more elements (nodes) to assert or act on composeTestRule .onNodeWithContentDescription("Close Button") composeTestRule .onNodeWithText("What happens next")
  • 76. Finders - Debug Logging Node #1 at (l=0.0, t=54.0, r=720.0, b=1436.0)px |-Node #2 at (l=70.0, t=54.0, r=650.0, b=1436.0)px ContentDescription = '[VyPops Permissions Page]' |-Node #3 at (l=70.0, t=75.0, r=112.0, b=117.0)px | Role = 'Button' | Focused = 'false' | ContentDescription = '[Close Button]' | Actions = [OnClick] | MergeDescendants = 'true' |-Node #6 at (l=229.0, t=194.0, r=492.0, b=303.0)px | ContentDescription = '[Vypr Logo]' | Role = 'Image' |-Node #7 at (l=91.0, t=687.0, r=133.0, b=729.0)px | ContentDescription = '[Record Audio Tick]' | Role = 'Image' |-Node #8 at (l=147.0, t=684.0, r=615.0, b=731.0)px | Text = '[Microphone access granted]' | Actions = [GetTextLayoutResult] |-Node #9 at (l=125.0, t=762.0, r=167.0, b=804.0)px | ContentDescription = '[Camera Tick]' | Role = 'Image' |-Node #10 at (l=181.0, t=759.0, r=582.0, b=806.0)px | Text = '[Camera access granted]' | Actions = [GetTextLayoutResult] |-Node #11 at (l=84.0, t=1275.0, r=636.0, b=1366.0)px Text = '[VyPops needs access to both your camera and microphone.]' Actions = [GetTextLayoutResult] Node #1 at (l=0.0, t=54.0, r=720.0, b=1436.0)px |-Node #2 at (l=70.0, t=54.0, r=650.0, b=1436.0)px ContentDescription = '[VyPops Permissions Page]' |-Node #3 at (l=70.0, t=75.0, r=112.0, b=117.0)px | Role = 'Button' | Focused = 'false' | Actions = [OnClick] | MergeDescendants = 'true' | |-Node #5 at (l=70.0, t=75.0, r=112.0, b=117.0)px | ContentDescription = '[Close Button]' | Role = 'Image' |-Node #6 at (l=229.0, t=194.0, r=492.0, b=303.0)px | ContentDescription = '[Vypr Logo]' | Role = 'Image' |-Node #7 at (l=91.0, t=687.0, r=133.0, b=729.0)px | ContentDescription = '[Record Audio Tick]' | Role = 'Image' |-Node #8 at (l=147.0, t=684.0, r=615.0, b=731.0)px | Text = '[Microphone access granted]' | Actions = [GetTextLayoutResult] |-Node #9 at (l=125.0, t=762.0, r=167.0, b=804.0)px | ContentDescription = '[Camera Tick]' | Role = 'Image' |-Node #10 at (l=181.0, t=759.0, r=582.0, b=806.0)px | Text = '[Camera access granted]' | Actions = [GetTextLayoutResult] |-Node #11 at (l=84.0, t=1275.0, r=636.0, b=1366.0)px Text = '[VyPops needs access to both your camera and microphone.]' Actions = [GetTextLayoutResult]
  • 77. Assertions Verify elements exist or have certain attributes composeTestRule .onNodeWithContentDescription("Login Button") .assertIsEnabled() composeTestRule .onNodeWithText("What happens next") .assertIsDisplayed()
  • 78. Simulate user input or gestures Actions composeTestRule .onNodeWithContentDescription("Close Button") .performClick() ... .performTouchInput { swipeLeft() }
  • 80. Shot is a Gradle plugin and a core android library thought to run screenshot tests for Android. Pedro Gómez github.com/pedrovgs/Shot
  • 81. ./gradlew executeScreenshotTests -Precord Example Test @get:Rule val composeRule = createAndroidComposeRule<AccountDeletionActivity>() @Test fun accountDeletedScreenBodyScreenshot() { composeRule.setContent { AccountDeletedScreenBody() } compareScreenshot(composeRule) }
  • 86. Introducing Compose Phased approach – Do you have an existing app with custom UI components? – Recreate them in Compose! – Provide a foundation to educate your team. – Define standards & best practices.
  • 89. – We’ve adopted Jetpack Compose for all new Android projects. – Excellent official documentation & codelabs available. – Good tooling and a growing list of third-party libraries available. – Recommend new starts prioritise Jetpack Compose over XML. Conclusions
  • 90. EstiMate for Android is coming soon™