Posted by Rebecca Gutteridge, Developer Relations Engineer on Android
With the release of Android 12 and Material You, we provided documentation and guidance on dynamic color foundations, how to implement dynamic color in Jetpack Compose and a getting started codelab. But creating a scalable, personalized, and accessible app with dynamic color can feel like a daunting task. We talked to designers and developers on Google Chrome, and they offered to share some tips on how they approached it at scale for their Android app. Here’s what they suggest if you are considering adopting dynamic color in your app.
Start by reviewing all your current screens in your app and identify your current colors, themes and surfaces. Chrome kicked off a design review and evaluated their color scheme. Material 3 encourages designers and developers to use color tokens which enable flexibility and consistency across an app by allowing designers to assign an element's color role in a UI, rather than a set value. This is particularly powerful when considering designing for light and dark themes and dynamic color.
Figure 1 : An example surface for Chrome, the Tab Switcher, identifying the color token for each element
Your app may already have a color token system, so reviewing how the new Material You dynamic color enabled color scheme matches your previous naming convention is an important exercise. Engineering should align with UX to review the new color token system with your mocks. This is also a good opportunity to review your current colors.xml, themes.xml and styles.xml.In particular check that your app correctly differentiates between Styles and Themes as well as correctly extending from base themes. It is also worth reviewing if there are redundant colors in your existing scheme or an opportunity to make a more consistent color scheme throughout your app. Dynamic color implementation with Compose is also available.
colors.xml, themes.xml
styles.xml.
Ensuring your app’s color system is accessible is critical for designing for everyone and creating products that are inclusive to the widest possible audience. Dynamic color is committed to guaranteeing that the color selection model has accessibility requirements built in. Material 3 color schemes are defined by tonality rather than hue or hex value, this system of tonal palettes is central to making any color system accessible by default. Using a minimum 60 luminance spread in color pairings provides enough contrast to ensure accessibility standards.
Figure 2 : Combining color based on tonality, rather than hex value or hue, is one of the key systems that make any color output accessible.
When looking at implementation, consider this upgrade as a phased approach if needed, targeting the primary surfaces first and leveraging that dynamic color can be applied at a per activity level. This was how Chrome was able to update their app and used it as an opportunity to migrate some of their older UI app compat components to the modern Material 3 components, such as Top app bar.
Your app may have custom colors or brand colors that you do not want to change with the user’s preference. These can simply be added additionally as you are building out your color scheme. Alternatively you can import additional colors to extend your color scheme using the Material Theme Builder to create a unified color system. The theme builder includes a color harmonization feature that shifts the tone of a custom color to ensure that visual balance and accessible contrast is achieved when combined with user-generated colors.
Figure 3: Understand how to harmonize custom colors with the Material guidance.
For Chrome, here is a deep dive into two examples of where protected colors are important for them and how they approached it.
It is important that Chrome allows for brands to keep their known colors and not impact that functionality when adopting dynamic color.
Publishers have the ability to set a publisher color using a metadata element in their html. The top toolbar is controlled using a decision tree to programmatically determine the toolbar color and icon color based on a series of cascading rules:
In Incognito mode, the dark gray color scheme has a semantic importance and reassurance for users. Chrome decided to preserve and leverage their existing color system and not change it dynamically.
Figure 4: Incognito mode remains the same
To achieve this, Chrome defined non adaptive colors that map to hex values and adaptive colors that map to different non adaptive colors for day/night mode. For incognito mode, Chrome uses the dark non adaptive colors as they are easily recognized by the users as incognito. With these adaptive colors, Chrome created a baseline theme.
The table below shows what their background colors look like after applying dynamic colors:
One thing to consider for adhering to theme best practices, is to leverage Theme Overlays properly. The Chrome team used this opportunity to refactor their themes and leveraged the power of Theme Overlays for a given activity. At times Chrome saw that full themes were being used where a ThemeOverlay would be more appropriate. Dynamic color and Material3 encourages better code hygiene.
Take a look at this example, previously the theme for full screen dialogs inherited from a full theme. This overrode all the attributes from the activity theme, undoing the dynamic colors or any overrides that are applied at the activity level. With the dynamic color work, the team became more deliberate in how they define and use their theming.
Previously:
<style name="Base.Theme.Chromium.Fullscreen" parent="Theme.BrowserUI.DayNight"> <item name="windowActionBar">true</item> <item name="colorPrimary">...</item> <item name="colorAccent">...</item> </style>
Now:
<style name="Base.ThemeOverlay.BrowserUI.Fullscreen" parent=""> <item name="android:windowContentTransitions">false</item> </style>
This section shares some key lessons that Chrome’s designers applied to successfully create an intentional and unified theme
This section shares some key lessons that Chrome’s developers applied to successfully migrate
@color/default_text_color_light: Color used for primary text
→ @color/default_text_color_dark/@color/default_text_color_light (adaptive to night mode)
→ @color/modern_grey_900/@color/modern_white
→ #1F1F1F / #FFFFFF
Your app may already have a semantic color system and so migrating adds additional considerations. For Chrome they wanted to preserve their semantic colors. In collaboration with UX, they translated the existing color palette to the Material color roles/attributes. Their first idea was to point to these attributes from the existing semantic colors. For example, @color/default_text_color from the example above would look like this: <color name="default_text_color">?attr/colorOnSurface</color>. However, the @color resource cannot point to an ?attr. The next idea was to convert all semantic `@color`s to `?attr`s with the same names. This approach also caused issues as they needed to add all the attributes to their themes and there are many activities, themes and entry points to Chrome, so it would be challenging to maintain. Finally, they adopted the newly introduced <macro> tag. Macros are much like C/C++ macros but for Android resources: they are replaced with whatever they point to at build time. So semantic colors became semantic macros, for example, <macro name="default_text_color">?attr/colorOnSurface</macro>. This made it possible to implement dynamic colors at bulk. One limitation of macros is that they cannot be accessed programmatically, but Chrome added static utility methods to work around this. The macro tag is now available in Android Studio Canary.
@color/default_text_colo
<color name="default_text_color">?attr/colorOnSurface</color>
@color
<macro>
<macro name="default_text_color">?attr/colorOnSurface</macro>
Dynamic color is coming to more Android 12 phones globally, including devices by Samsung, OnePlus, Oppo, Vivo, realme, Xiaomi, Tecno, and more! As you work with dynamic color in your app, we’d love to get your feedback via the Material Android issue tracker. Happy coloring!
Posted by Nick Butcher, Developer Relations Engineer
Our goal is to make developing beautiful and engaging Android apps as fast and easy as possible. We want to take on the complex parts of building apps so that you can focus on your app’s features and deliver high quality experiences to your users.
We call this approach Modern Android Development (or MAD for short!) and deliver it through a suite of tools, libraries and guidance. At Google I/O we announced a number of updates and additions to our MAD offerings; here’s a recap of the three largest announcements.
Jetpack Compose 1.2 reaches the first Beta, which means the API is stable. We continue to build out our roadmap, bringing the APIs you need to support more advanced use cases like downloadable fonts, LazyGrids, window insets, nested scrolling interop, and more tooling support with features like LiveEdit, Recomposition counts in the Layout Inspector and Animation Preview. Learn more about how developers like Airbnb are improving their productivity with Jetpack Compose, and check out what else is new in Compose.
Baseline profiles allow you to embed a profile to guide the Android Runtime about which code paths should be pre-compiled rather than interpreted, which could dramatically impact critical user journeys like app startup. This is especially significant when using unbundled libraries like Jetpack Compose which don’t benefit from optimizations in platform code.
Many Jetpack libraries (including Jetpack Compose) already ship baseline profiles, but you can learn how to add them to your own apps and libraries to boost their performance. We've seen up to 40% faster app startup times thanks to adding baseline profiles alone, no other code changes required!
With Live Edit you can edit composables and view those changes in real time, on the Compose Preview or on physical devices or emulators, enabling rapid iteration. Live Edit is an opt-in experimental feature in Android Studio Electric Eel, with a number of limitations. Please try it out and provide your feedback.
Those were the top three announcements about Modern Android Development at Google I/O. To learn more, check out the full playlist of talks and workshops.
Posted by Oscar Rodriguez, Developer Relations Engineer
With the recent launch of the Play Integrity API, more developers are now taking action to protect their games and apps from potentially risky and fraudulent interactions.
In addition to useful signals on the integrity of the app, the integrity of the device, and licensing information, the Play Integrity API features a simple, yet very useful feature called “nonce” that, when correctly used, can further strengthen the existing protections the Play Integrity API offers, as well as mitigate certain types of attacks, such as person-in-the-middle (PITM) tampering attacks, and replay attacks.
In this blog post, we will take a deeper look at what the nonce is, how it works, and how it can be used to further protect your app.
In cryptography and security engineering, a nonce (number once) is a number that is used only once in a secure communication. There are many applications for nonces, such as in authentication, encryption and hashing.
In the Play Integrity API, the nonce is an opaque base-64 encoded binary blob that you set before invoking the API integrity check, and it will be returned as-is inside the signed response of the API. Depending on how you create and validate the nonce, it is possible to leverage it to further strengthen the existing protections the Play Integrity API offers, as well as mitigate certain types of attacks, such as person-in-the-middle (PITM) tampering attacks, and replay attacks.
Apart from returning the nonce as-is in the signed response, the Play Integrity API doesn’t perform any processing of the actual nonce data, so as long as it is a valid base-64 value, you can set any arbitrary value. That said, in order to digitally sign the response, the nonce is sent to Google’s servers, so it is very important not to set the nonce to any type of personally identifiable information (PII), such as the user’s name, phone or email address.
After having set up your app to use the Play Integrity API, you set the nonce with the setNonce() method, or its appropriate variant, available in the Kotlin, Java, Unity, and Native versions of the API.
setNonce()
Kotlin:
val nonce: String = ... // Create an instance of a manager. val integrityManager = IntegrityManagerFactory.create(applicationContext) // Request the integrity token by providing a nonce. val integrityTokenResponse: Task<IntegrityTokenResponse> = integrityManager.requestIntegrityToken( IntegrityTokenRequest.builder() .setNonce(nonce) // Set the nonce .build())
Java:
String nonce = ... // Create an instance of a manager. IntegrityManager integrityManager = IntegrityManagerFactory.create(getApplicationContext()); // Request the integrity token by providing a nonce. Task<IntegrityTokenResponse> integrityTokenResponse = integrityManager .requestIntegrityToken( IntegrityTokenRequest.builder() .setNonce(nonce) // Set the nonce .build());
Unity:
string nonce = ... // Create an instance of a manager. var integrityManager = new IntegrityManager(); // Request the integrity token by providing a nonce. var tokenRequest = new IntegrityTokenRequest(nonce); var requestIntegrityTokenOperation = integrityManager.RequestIntegrityToken(tokenRequest);
Native:
/// Create an IntegrityTokenRequest object. const char* nonce = ... IntegrityTokenRequest* request; IntegrityTokenRequest_create(&request); IntegrityTokenRequest_setNonce(request, nonce); // Set the nonce IntegrityTokenResponse* response; IntegrityErrorCode error_code = IntegrityManager_requestIntegrityToken(request, &response);
The response of the Play Integrity API is returned in the form of a JSON Web Token (JWT), whose payload is a plain-text JSON text, in the following format:
{ requestDetails: { ... } appIntegrity: { ... } deviceIntegrity: { ... } accountDetails: { ... } }
The nonce can be found inside the requestDetails structure, which is formatted in the following manner:
requestDetails
requestDetails: { requestPackageName: "...", nonce: "...", timestampMillis: ... }
The value of the nonce field should exactly match the one you previously passed to the API. Furthermore, since the nonce is inside the cryptographically signed response of the Play Integrity API, it is not feasible to alter its value after the response is received. It is by leveraging these properties that it is possible to use the nonce to further protect your app.
nonce
Let us consider the scenario in which a malicious user is interacting with an online game that reports the player score to the game server. In this case, the device is not compromised, but the user can view and modify the network data flow between the game and the server with the help of a proxy server or a VPN, so the malicious user can report a higher score, while the real score is much lower.
Simply calling the Play Integrity API is not sufficient to protect the app in this case: the device is not compromised, and the app is legitimate, so all the checks done by the Play Integrity API will pass.
However, it is possible to leverage the nonce of the Play Integrity API to protect this particular high-value operation of reporting the game score, by encoding the value of the operation inside the nonce. The implementation is as follows:
The following sequence diagram illustrates these steps:
As long as the original message to protect is sent along with the signed result, and both the server and client use the exact same mechanism for calculating the nonce, this offers a strong guarantee that the message has not been tampered with.
Notice that in this scenario, the security model works under the assumption that the attack is happening in the network, not the device or the app, so it is particularly important to also verify the device and app integrity signals that the Play Integrity API offers as well.
Let us consider another scenario in which a malicious user is trying to interact with a server-client app protected by the Play Integrity API, but wants to do so with a compromised device, in a way so the server doesn’t detect this.
To do so, the attacker first uses the app with a legitimate device, and gathers the signed response of the Play Integrity API. The attacker then uses the app with the compromised device, intercepts the Play Integrity API call, and instead of performing the integrity checks, it simply returns the previously recorded signed response.
Since the signed response has not been altered in any way, the digital signature will look okay, and the app server may be fooled into thinking it is communicating with a legitimate device. This is called a replay attack.
The first line of defense against such an attack is to verify the timestampMillis field in the signed response. This field contains the timestamp when the response was created, and can be useful in detecting suspiciously old responses, even when the digital signature is verified as authentic.
timestampMillis
That said, it is also possible to leverage the nonce in the Play Integrity API, to assign a unique value to each response, and verifying that the response matches the previously set unique value. The implementation is as follows:
With this implementation, each time the server asks the app to call the Play Integrity API, it does so with a different globally unique value, so as long as this value cannot be predicted by the attacker, it is not possible to reuse a previous response, as the nonce won’t match the expected value.
While the two mechanisms described above work in very different ways, if an app requires both protections at the same time, it is possible to combine them in a single Play Integrity API call, for example, by appending the results of both protections into a larger base-64 nonce. An implementation that combines both approaches is as follows:
These are some examples of ways you can use the nonce to further protect your app against malicious users. If your app handles sensitive data, or is vulnerable against abuse, we hope you consider taking action to mitigate these threats with the help of the Play Integrity API.
To learn more about using the Play Integrity API and to get started, visit the documentation at g.co/play/integrityapi.
Since 2007, Airbnb has grown to connect more than 4 million hosts with more than 1 billion guests across the globe. One of the reasons behind the app’s success is that its developers aim to achieve engineering excellence by focusing on two main principles: using technology that sparks innovative development and empowering the engineers behind the work.
Jetpack Compose, Android’s modern UI-building toolkit, directly supports both of Airbnb’s development principles. Compose provided a solid foundation for adaptable, quality engineering and reduced boilerplate code, so developers could focus on delivering a great user experience — and advance their two-fold pursuit of engineering excellence.
Airbnb started testing Compose in 2020 when it was in developer preview. As an early adopter, the Airbnb team was eager use the various new features and simplify their workflow. Now, having gained confidence using Compose in production, Airbnb engineers continue to be satisfied with how it improved their development process.
Compose’s deterministic testing helped ensure Airbnb’s engineers had tight control over the UI tests they ran and eliminated common flakiness, thereby strengthening their confidence in the quality of every part of their app and the user experiences they were creating. Engineers can now also use Compose to test animations they previously couldn't.
Similarly, Airbnb developers used Compose to add automated screenshot tests to their codebase. Because they didn’t need to write the code for screenshot testing, engineers could go straight into using it to catch bugs and regressions. This gave them more time to review and guarantee feature functionality and UI appearance across a variety of devices.
Compose is great to use alongside Views. This interoperability made it easy for Airbnb engineers to onboard and test the new UI toolkit at their own pace, so they were able to experience the benefits of Compose without having to migrate entire features.
These engineering improvements gave them the solid technical foundations they needed to serve users in fresh and improved ways.
Airbnb keeps hosts and guests at the heart of their decisions. The engineering team was excited to adopt Compose when they learned about how it would enable them to more easily and efficiently produce UI, resulting in better experiences for their end users.
Because Compose made Airbnb’s features require significantly less code to write and manage, the Airbnb team boosted their efficiency. All of this meant the team could focus its energy on executing the complex tasks involved in developing the innovative features that could best serve users.
Because their features now require less code, the Airbnb team will be able to slow the growth of their app size in the long run. Providing a smaller app is important to Airbnb as an organization with users across the globe that looks to ensure all hosts and guests can easily download and access their app — especially those with older devices or logging on from countries with high data costs.
Using Compose’s engineering enhancements, the Airbnb team was able to put user needs first.
Compose simplified UI development to allow Airbnb engineers the freedom to focus on more dynamic and innovative features that benefit the app’s hosts and guests.
Learn how you can improve your team’s productivity with Jetpack Compose.
Posted by Paris Hsu, Product & Design, Android and Don Turner, Developer Relations Engineer, Android
The Now in Android app is now on GitHub!
For two years, 'Now in Android' has been a popular blog and YouTube series, providing you with the latest and greatest developer news from the Android team. Starting today, you can check out the alpha version of the Now in Android app on GitHub! 🎉
The app has two goals:
Firstly, it showcases best practices, opinionated designs, and solutions to complex real-world problems which other sample apps don’t handle. It does so with an open source implementation of a real world app.
Secondly, it helps you (the developer) keep up to date with the areas of Android development which interest you most. It is a working app planned for publication on the Play Store.
Now in Android app screen designs
For this first alpha release, the Now in Android app includes:
As well as these features, we are also documenting the learning journeys we took to certain decisions with the app's design and implementation. Check out our first journey on the app's Architecture here.
The Now in Android screens adapt based on device screen size
Since this is an alpha release, we expect that there will be bugs and missing features, and we would greatly appreciate your feedback. We have some exciting features planned, such as user authentication and loading data from a real backend. We can’t wait for you to check out the app and let us know what you think!
Finally, if you want to learn about the tools we used to build the app and how we target multiple screen sizes, check out these talks from this year's Google I/O:
Shobana Radhakrishnan, Senior Director of Engineering - Google TV
Paul Lammertsma, Developer Relations Engineer
Today, there is more entertainment content available than ever before. In fact, our research shows a third of U.S. households now watch more than 25 hours of TV every week. As the role of TV continues to evolve, it’s our goal to build a tailored TV experience that gives users easy access to the entertainment they love.
We’re excited about the future of Android TV OS, now with over 110 million monthly active devices, including millions of Google TVs. Android TV and Google TV are available on over 300 partners worldwide, including 7 of the 10 largest smart TV OEMs and over 170 pay TV operators. And thanks to the hard work of our developer community, there are more than 10,000 apps available on TV, with more being added everyday.
Since last year’s I/O, we’ve continued our commitment to enable you to build better and more engaging experiences on Android TV OS. In addition to platform updates, new features, like expanded integrations with the Live tab, offer opportunities for users to better engage with your content. And if you haven’t begun using WatchNext API, take a moment to learn how to add it to your app to make your content more discoverable and accessible.
Today, we are introducing new features and tools on Android 13 that focus on overall performance & quality, improve accessibility, and enable multitasking.
Android 13 Beta for TV is available now, allowing you to test your apps and provide feedback on the latest release. Thank you for your continued support of Android TV OS. We can’t wait to see what amazing and innovative things you continue to build for the big screen.
Posted by Jennifer Chui, Technical Program Manager and Rod Lopez, Product Manager
At Google, our work in cars has always been guided by our vision of creating safe and seamless connected experiences. This work would not be possible without developers like you. We’re excited to share some of our combined accomplishments from this past year, and introduce new updates that will make it easier for you to provide users with an even better experience in the car.
Android Auto continues to grow and scale, with compatible vehicles now numbering over 150 million worldwide. An increasing number are also wirelessly compatible, and with the newly introduced Motorola MA1 adapter, even more drivers now have access to a wireless experience. In addition, our new design for Android Auto brings split-screen functionality to every screen, keeping navigation and media front and center while also providing room for prominent notification widgets.
Android Automotive OS with Google built-in also has exciting updates. Beyond the continued expansion of carmakers that are bringing more car models to the market, we’ve also been hard at work enabling more parked experiences to take advantage of the large screens that many AAOS cars offer. From more video streaming apps like Epix Now and Tubi to future features like browsing and cast, there’s much to look forward to, and given minimal effort is required to translate your large screen tablet apps into a parked car experience, it’s now easier than ever to reach users in the car.
We know that developing for cars can be complex, which is why we’re focused on making developing across Android for Cars as easy as possible. We’ve seen strong momentum with our Car App Library with over 200 apps published to date, and beyond enriching the navigation feature set with version 1.3, we’re also excited to share that all developers can now publish apps in supported categories directly to production for both Android Auto and Android Automotive OS. We’ve also created new templates and expanded our supported app categories, adding driver apps like Lyft to the navigation category, and replacing the parking and charging categories with a comprehensive point of interest (POI) category to include apps like MochiMochi and Fuelio.
We’re also introducing several new features to help you build more powerful media apps on Android Auto. Media recommendations working side by side with Google Assistant helps users easily discover and quickly play relevant content based on their preferred music provider at the click of a button. To surface recommendations from your app, integrate with this API.
For long form content such as podcasts and audiobooks, you can now introduce a progress bar that shows how much of the content the user has previously listened to, and with our new single item styling API, you can now assign content items individually as either list or grid as opposed to categorically, to easily combine them in the same content space.
We’re grateful to have you on the journey with us as we seek to create safer, more seamless connected experiences in cars. Be sure to check out our Google I/O technical session above, and as always, you can get help from the developer community at Stack Overflow using the android-automotive and android-auto tags. We can’t wait to see what you build next, and where the road takes you.