Dynamic Theme

Dynamic Theme

The development story of a text (JSON) based universal theme engine for Android.

Dynamic Theme is a text-based universal theme engine having some predefined properties that can be shared across apps and widgets. It has background-aware functionality which can adjust views according to their background to provide the best visibility. I have been using it in my Android apps since 2019 but the concept and idea were way older than you think and this post is all about the development of that fully dynamic theme engine which has the capability to generate completely unexpected and delightful colors unique to every user.

In a not-so-distant future, within a Cyberpunk city, a QR Code might be changing the ambiance of interconnected things dynamically.

Preview

{
  "backgroundColor": "#455A64",
  "tintBackgroundColor": "auto",
  "surfaceColor": "auto",
  "tintSurfaceColor": "auto",
  "primaryColor": "#FF455A64",
  "tintPrimaryColor": "auto",
  "accentColor": "#FFE6EE9C",
  "tintAccentColor": "auto",
  "errorColor": "auto",
  "tintErrorColor": "auto",
  "textPrimaryColor": "auto",
  "textSecondaryColor": "auto",
  "fontScale": "auto",
  "backgroundAware": "auto",
  "contrast": "auto"
}

ToC

Table of Contents

Background

I used to draw a lot during my adolescence age but rarely filled them with colors as I was not good enough at selecting the palette. When I started developing Android apps in 2013, the same limitation challenged me again while developing the User Interface and then I came up with a solution to give users the control of selecting colors for the important UI elements. Initially, it felt like I was just countering my weakness but later this unexpected decision led to the solution of various accessibility challenges when we work with dynamic colors and multiple (day and night) themes in a software product.

Drawings

Shree Ganesh and Lord Krishn | Effects

In 2021, Google addressed similar challenges with the introduction of Material You in Android 12 but this is something I was visualizing since 2013 and started using in my apps in 2019.


Framework

In 2013, I had a bunch of small apps for Sony Ericsson devices and luckily enough they had a theme engine as well. Custom ROM development was at its peak at that time and Sony was just playing with colors instead of heavily modifying their skin. This approach was favorable enough to visualize my concept even better.

Unfortunately, all of this stuff was part of their NXT lineup of devices and I had an old Xperia Mini Pro released in 2011. I thought that it would never be possible but my dedication and curiosity to learn more about Android led to the development of xNXT custom ROM to port those latest functionalities to older Xperia devices. Soon it became a standard for NXT flavored ROMs and I will always be grateful to those early supporters of my work.

This sudden fame made me even more curious and motivated to achieve the unexpected.

Concept

Sony had their own custom theme engine allowing other developers to create themes for it. They were providing a lot of resources including an SDK to build themes for it and were pushing it really well. But the whole concept was just limited to their (system) apps and I was trying to incorporate those capabilities into my apps as well.

As I started my software development journey by reverse-engineering Android apps and developing custom ROMs, that extra knowledge played a vital role and acted as a cherry on top when figuring out the initial concept. I already had the internal details about the latest system apps of Sony devices and while developing the xNXT ROM, I got the breakthrough and came to know about the process of extracting colors from the framework.

The reason behind this approach was to see the dynamic colors in action by switching the themes on the fly.

Findings

Soon I was able to extract colors from the framework into my own apps and I was delighted to see how my apps were changing colors based on the system theme. That was the first time I saw something hacky approach working like magic and I even published that on Google Play. My apps were the only small apps supporting that functionality just like Sony had implemented into their own apps.

Although it gave further reassurances to move forward, something was still missing to call it a success.

Small Apps

Small Apps Extension for Sony Products

Small App Support

Later, I open-sourced my findings in the form of a library to develop small apps for Sony devices.

Challenges

I tested my apps with the default themes but there were thousands of themes available on various stores for those devices. Although Sony provided the guidelines to follow the various accessibility standards and keep the colors in sync with the appropriate contrast ratio, there were enough themes to ruin the completely dynamic experience by providing dark text on a dark background.

I coded my small apps in such a way that they were just using the colors received from the framework, and hence from the third-party developers. Unfortunately, at that time I had no other choice left and stopped the further development of my initial apps.

I published 12 small apps during the period of 2013-2015 on Google Play and I concluded that to make everything dynamic, the developer must be in control rather than depending on the system framework.


Application

After realizing that relying on the system is not the most appropriate way to be in complete control, I decided to experiment at the application level by trying to build a custom solution for my future Android apps. That was another learning experience for me but the implementation was still not coinciding with my original visualization and quality standards.

Most of the time people talk about the actual product or the final destination, the procedure or the path to reach there should be the point of discussion.

Rotation

In 2014, I started working on my first major app for Android and decided to give control to the user from the beginning by providing a set of dark and light themes with the ability to switch major UI colors on the fly. Luckily, Rotation became popular too just like my previous works due to its unique functionality and customizable interface. However, I received some backlash from the users as well due to its immature implementation of user-selected colors.

Rotation Legacy

Rotation before Dynamic Theme on Android

In other words, the overall aesthetics of the app was appealing but not practical simultaneously.

That was another setback to me as despite providing unique and rare functionalities to the users, the whole UI of the app got messed up, and hence was hindering the practical usage and growth of a newly launched app on Google Play. I tried to find out the root cause and realized that it’s easier to give full control to the user and store their inputs within the app but to build a completely dynamic ambience around their taste is something that Android doesn’t offer by default.

I was well aware of the View system, the original and good old API to build UI on Android, and began thinking about developing a completely customizable set of elements that would respect the user preferences in a better way.

The work associated with building such kind of toolkit or library was a big deal to me at that time, so I decided to be in the coast mode for a while and came back later to give it a final try.

Reboot

Rebuilding Rotation from scratch was already planned but this heartfelt email gave me the additional boost to move towards my endeavour and experience the unexpected while making things accessible for so many people.

Email

Thank you for creating this app. I’m a disabled gamer and this has made many apps that only play in portrait mode very useful to me. Because I have to have the case hold my tablet because I do not have finger movement. So I play everything and do everything in landscape mode because that’s the way the case holds the tablet. There were so many apps that only play in portrait and I couldn’t go near them cuz everything was sideways and now you have fix that. God bless you. I have already paid for the fee if it was more I would have paid that so I thank you. I hope that more people pay because your fee is so modest and what you offer is a very nice thing.

Rotation already had an extensive feature set which I had to rebuild from scratch but this time I tried to do it in a more professional way in the form of various loosely coupled modules which later became the Dynamic Support libraries for Android. I had plans to decouple theming aspects completely from the app but unfortunately, that was not possible in the beginning so I stuck to what Android was offering by default and completely scrapped the user-based color selection during the alpha and initial beta phase of the app.

Please go through the Epilogue of Rotation to know more about the development phase of the dynamic libraries for Android.

The reboot was progressing as planned and I was focusing more on implementing the core functionalities, even to this point I was not sure if I would be able to implement that ambitious theme engine I imagined since the beginning. In the worst case, I had planned to ditch it completely as I was mature enough to identify what’s important to provide a consistent experience and when to move away from the bells and whistles.


Dynamic

Rotation was about to become stable with all the previous functionalities, I convinced myself to give the theme engine a final try and I believe that was the most fruitful effort I ever made to achieve the unexpected.

From my previous works, I concluded that we need to implement two major aspects to develop a dynamic theme engine. The first one was a set of custom Views to let them behave according to our requirements and then devise some kind of mechanism to reload them whenever the theme changes.

I wanted to test the reloading functionality first as that was the driving force behind the dynamic experience I was trying to create. The UI on Android is made up of Views (now we have Compose UI as well) and they are part of an Activity (most of the time) and it has some internal components as well like an app bar or toolbar, menu items, etc. It provides the static way to style them via XML but even to this day, some components are not exposed for the runtime customizations. Due to this limitation, I decided to reload the whole activity whenever a theme change would occur.

System

This approach is officially followed since Android 12 was released in 2021 whenever the system detects a theme change based on the device wallpaper and to support this kind of functionality, an app should handle all the configuration changes as the activity recreation loses the previous unhandled state. I was fortunate enough to have Rotation by my side as it was developed like that since the beginning, after all, it was forcing other apps to support all orientations and I believe that we should change ourselves first.

One more aspect I was thinking about is to reuse the existing framework that Google was already offering to provide maximum compatibility. So, I decided to use the Material Design Components for Android which itself is built on top of the official support libraries for Android and both are provided by Google.

Initially, I was trying to minimize my own burden as much as possible but this approach also helped in providing a drop-in replacement for the existing apps.

Theme

I started with a bunch of custom views having a functionality based on the visible contrast ratio so that they can adjust their color according to the background, hence making them background-aware dynamically. That was the major breakthrough, although the driving code or implementation was slightly different for each types of views like button, radio, checkbox, card, etc. but the basic idea was working as exactly as I imagined few years back.

Soon, I was able to categorize commonly used views (or widgets) based on their theming properties and created various interfaces to implement the dynamic theming capabilities accordingly.

Later, I was able to implement some Dynamic Widgets with a couple of custom layout inflaters to replace the default widgets dynamically at runtime. This approach helped in reusing the older layout (xml) files and providing a drop-in replacement for AppCompat and MDC-Android based apps like Rotation.

Rotation

Rotation with Dynamic Theme on Android

AndroidX

The official support libraries for Android also use a similar approach to replace widgets according to the various API levels. I extended this approach further to provide dynamic widgets instead, built on top of their support library counterpart to provide maximum compatibility. It also helped in fixing (and filing) various internal issues instead of waiting for the official release by Google.

Initially, the whole functionality, including widgets, inflater and the theme were part of a monolith library called Dynamic Support and it was growing rapidly day by day. I already had plans to break it up into smaller modules for better management and compatibility but to decouple theme from it was still a major challenge to be conquered.


Format

I devised a generalized text based format inspired by the Material Design by Google. The basic idea was to store required colors and properties in a human-readable JSON format and then build the dynamic widgets and interfaces around it. Initially, there were a bunch of colors like background, primary and accent ones but it grew over time with some other properties like contrast ratio and font scale to support accessibility standards as well.

It also had a setting since the beginning for background-aware to control the color adjustment on demand.

{
  "backgroundColor": "auto|color",
  "tintBackgroundColor": "auto|color",
  "surfaceColor": "auto|color",
  "tintSurfaceColor": "auto|color",
  "primaryColor": "auto|color",
  "tintPrimaryColor": "auto|color",
  "primaryColorDark": "auto|color",
  "tintPrimaryColorDark": "auto|color",
  "accentColor": "auto|color",
  "tintAccentColor": "auto|color",
  "accentColorDark": "auto|color",
  "tintAccentColorDark": "auto|color",
  "errorColor": "auto|color",
  "tintErrorColor": "auto|color",
  "textPrimaryColor": "auto|color",
  "textPrimaryColorInverse": "auto|color",
  "textSecondaryColor": "auto|color",
  "textSecondaryColorInverse": "auto|color",
  "fontScale": "auto|integer",
  "cornerRadius": "auto|system|integer",
  "backgroundAware": "auto|disable|enable",
  "contrast": "auto|integer",
  "opacity": "auto|integer",
  "elevation": "auto|disable|enable",
  "style": "auto|custom",
  "header": "auto|hide|show"
}

A dynamic theme can be represented as a JSON, URL or a File with .theme extension containing the JSON text. Use the Palettes app for Android to create or share dynamic themes in various formats. However, the most suitable way is to share it via a URL that can be imported easily on various platforms.

Extend

Initially, it was intended to work with the app theme only but to embrace the customization power of Android, the app widget and notification themes (categorized as remote theme) support was added later where we have the UI elements but no Android activity is visible to the user.

Wallpaper

Google introduced dynamic color via Material You in 2021 to provide similar functionality at the system level by extracting colors from the device wallpaper and I incorporated it as well by implementing a color palette selector for the dynamic theme based on the system, app and wallpaper. It even works on the older versions of Android (4.0+) and helped in providing a consistent user experience.

Everyday for Android

I wrote a custom app widget provider supported by the factory to develop the Everyday app and published it in 2019 to feature the dynamic theme with remote views on Android. It was even before Apple innovated widgets on iOS and then Google took lessons to revamp them in almost a decade to support Material You.

URL

The ultimate goal was to make it a universal format but it was difficult to share it as JSON text on various platforms. A URL is the short representation of the theme JSON that can be shared on the platforms having a character limit like X (Twitter). This is the most suitable way to share a dynamic theme that can be imported easily in various apps or platforms.

Mappings

The color and other properties are mapped to numbers and alphabets respectively to generate a unique URL that can be imported via built-in parser bundled with the dynamic theme on Android.

JSONURLJSONURL
{,}""
Split (Key)-Dark | InverseZ
backgroundColor0tintBackgroundColor1
surfaceColor2tintSurfaceColor3
primaryColor4tintPrimaryColor5
primaryColorDark4ZtintPrimaryColorDark7
accentColor8tintAccentColor9
accentColorDark8ZtintAccentColorDark11
errorColor21tintErrorColor22
textPrimaryColor12textPrimaryColorInverse12Z
textSecondaryColor14textSecondaryColorInverse14Z
fontScale16cornerRadius17
backgroundAware18contrast25
opacity20elevation26
style23header19
Values
JSONURLJSONURL
Split (Value)v#X
autoAsystemM
disableDenableE
hideHshowS

Decouple

Based on the above findings and implementations, I created several interfaces to represent different types of themes internally and exposed a couple of abstract classes to implement the app and widget themes on Android. This approach helped in creating a standalone module, hence decoupling it from the support library and was published as a separate maven artifact so that it can provide the required base to empower other UI libraries as well.

It also includes several utility methods to encode and decode the dynamic theme on Android including the QR Code.

Dynamic Support

Since then, I have been developing the dynamic support library as an implementation of the dynamic theme to feature more UI templates to build a dynamic yet connected world.

Zerocros for Android

Zerocros with a spinoff YouTube Channel as well, a game published in 2021 is built by just using the tutorial or onboarding API of the support library, hence unlocking the endless possibilities on Android.


Connection

You must be wondering how things are connected and similar to what Google has been offering officially since Android 12. The major reason behind it is that they are using the same base on which I did my initial testing as Sony has contributed to Android Open Source Project (AOSP) to lay the foundation for the default theme engine via Runtime Resource Overlay (RRO). It’s good to see Google improving upon what Sony did earlier and providing dynamic colors based on the device wallpaper.

Interview

I was even interviewed at Google for a UI/UX Engineer role shortly after I launched the dynamic theme for Android in 2019/2020 and reached straight out to the Hiring Committee just before the COVID-19 pandemic. They might be working on a similar thing at that time as 2 years later, they introduced dynamic color in Android 12.

Forward

Neither did I get the offer letter nor the official rejection (just notified on call) from Google and I think the search is still on. I don’t know how my resume got shortlisted while applying directly from their website but this unexpected interview experience spanning around 5 months taught me a lot about my strengths and weaknesses. I was told that out of 8 (9 including team fit) rounds, only the feedback of just one coding round was not satisfactory although I was praised consistently for the deep understanding of Android APIs.

I used the dynamic libraries and the initial version of this theme engine for the design exercise and passed it with lots of appreciation and positive feedback which further convinced me to move forward and add the missing colors.

The dynamic theme was still not stable and now I was with a broken heart too because I thought that was the most appropriate time to work within Google and implement my UI/UX experience at a much larger scale but we had to move on when things don’t go as planned and I did it in style by implementing the QR Code support to scan and change the aesthetics on the fly.

Barquode for Android

This unexpected setback or a blessing in disguise combined with the zeal to keep working on Android further led to the development of the Barquode app to handle all kinds of matrix codes with a built-in scanner which was published in 2021.

Implement

The dynamic theme works on the product (app) level and a developer must integrate it to support theme presets. There is no official connection with the dynamic color as they are completely different things, and the latter was introduced by Google in 2021/2022. The dynamic color works on the system level (based on the wallpaper) which apps can support to change their appearance accordingly.

However, the dynamic theme has the capability to use system-generated colors but the reverse is not possible.

Please visit the dynamic theme website for more info and check the repository on GitHub if you want to integrate it into your apps.

Palettes

A fairy tale moment for me as a person always struggling with colors and later launching an app to manage them. Palettes is a universal manager for apps supporting the dynamic theme on Android. It provides some default configurations that can be extended to create custom ones.

  • A dynamic theme engine with background-aware functionality to avoid any visibility issues.
  • Change the theme of all the supported apps at once via shortcuts and notification tiles.

Preview

Palettes for Android

Presets

  • A collection of presets to provide various base styles.
  • Extend them to create custom ones according to the requirements.
  • Preview and apply them natively in the supported apps and widgets.
  • Experimental option to enable dark mode in devices that don’t have a system setting.

Universal

The final theme implementation depends on the app developer and some apps might not support all the colors.

The applied theme may be different from the theme preview.


Future

This journey is deeply connected to the Android world as they both evolved simultaneously but it is possible to implement the dynamic theme on other platforms including the web by writing a custom JSON parser but no official support is available yet.

Dynamic Theme has never been a standalone thing but combining it with real-world products can make them accessible and provide the missing X-factor to onboard and delight the customers. It also helped me conquer my weaknesses and understand colors even better.

Generative

The thing that is even more fascinating is it already supports generating a dynamic theme by taking an image as a reference, hence giving rise to the generative UI as the color palette is the most basic and necessary aspect that needs to be generated dynamically.

Dynamic Theme via Images on Android


Motivation

This post itself was a long due not just because it connects all of my works, but the blog was not ready to support both day and night themes to give it the required ambiance.

The dynamic theme was an ambitious project and I was always afraid of its failure or abandoned in between but when we connect live projects to our ideas, it keeps us motivated to give life to our imaginations.

Related Posts