TL;DR – Boring and monotonous, find & replace process. If someone has some tips to reduce the manual nature of the below process, please shout!


As part of an effort to add backwards compatibility to a project, (reducing the minimum Android version from 21 to 16), one of the hurdles was adding support for the old marginLeft, marginRight, paddingLeft, and paddingRight.

After resolving all of the lint warnings due to the old margins (left & right) that were used by other developers, I was now kicking myself. However, this increase in backwards compatibility could not be foreseen and until this point, supporting only one type of margin was the right thing to do; it was a light kick.

Where do I begin sorting all of these margins? ConstraintLayout is heavily used in the app and margins play a big part. Regex; the thing I forget immediately after using it for what I need it for and have to relearn every time.

via GIPHY

So I tested my little regex snippet in a file of my on-boarding flow (the first I reach):

  1. Cmd + R in my layout .xml file.
  2. In the top box (find), entered (.*)android:layout_marginStart="(.*)"
  3. In the bottom box (replace), entered $1android:layout_marginStart="$2"\n$1android:layout_marginLeft="$2"
  4. Make sure the Regex checkbox is selected and hit Replace.
Find and replace Android Studio component

I won’t go into detail on the workings of Regex, but suffice it to say, I am capturing the amount of space at the beginning of the original line and also the value of the marginStart property. These captured properties are then referenced in the replace as $1 and $2 respectively.

Lo and behold, it worked. I then got cocky and used the Replace all button for that file and carried on even further, modifying my inputs for marginEnd, paddingStart and paddingEnd. With one file down, I was starting to get optimistic.

Before doing something drastic, which I already had planned, I decided I should probably commit the change to fix that build issue just in case something goes wrong with this. I’ve been stung by things like this before and not easily been able to recover!

Without further ado, Find & Replace All

  1. Cmd + Shift + R
  2. Follow exactly the same approach as above with the separate input boxes.
  3. Hit Replace if you’re feeling boring, Replace All if you like to live dangerously. 
Scary window warning that Android Studio knows best and you’re about to do something stupid

I of course had some errors, just like this one:

Execution failed for task ‘:app:mergeAppDevelopDebugResources’.
> java.util.concurrent.ExecutionException: com.android.builder.internal.aapt.v2.Aapt2Exception: Android resource compilation failed
 /Users/stustirling/Projects/app-android/app/src/main/res/layout/item_pay_activity_header.xml:10: error: duplicate attribute.

This was, of course, due to there already being an android:layout_marginLeft in the layout file which I expected there to be some occurrences of. Just removing one of the attributes did the trick but was monotonous removing them manually. This led me to looking into conditionally matching marginStart only if marginLeft wasn’t already present in the XML object.

The Tangent

I am not a Regex wizard and I struggled with this, so I turned to trusty StackOverflow and asked the question. Whilst I was initially met some very unhelpful close requests, which I did not agree with, a nice chap went to the effort to explain his solution and gave me an online demo. Armed with this new regex, I discarded all my changes so far and tried again with the following Find.

(android:layout_marginStart="(.*?)")(?:(?!layout_marginLeft).)*(?=/>)

This is great for finding if the marginLeft occurs after the marginStart however it could be present before. Therefore I went back to the original poster to ask for some additional assistance when I couldn’t work it out. Very helpfully he gave me the following.

<(?:(?!layout_marginLeft|>).)*\K(android:layout_marginStart="([^\s]*?)")(?:(?!layout_marginLeft|>).)*(?=/>)

The problem was \K is not a valid character in Java regex. I was stumped and again, after looking around for the solution and not finding it I decided to move forward with just the forward check. Something is better than nothing and at this point I had wasted enough time!

When trying to implement the first regex given, I noticed that it didn’t cater for marginStart attributes on a different line to the closing tag />. In all honesty, I’d had enough, couldn’t work it out and in the grand scheme of things, spent more time than if I’d just stuck with the original method. Regex, you win again! Scrap that.

via GIPHY

Back on Track

So back to the initial method. I tried to find how to run through the entire module in one go and for it to report all of the errors but for some reason I couldn’t nail it down. So, it was the manual, monotonous approach.

To attempt to reduce the amount of jumping between files, I decided to run each of the Find & Replaces at once and fix each of them as I jumped into a file.

Then it was a case of the following steps, over and over again. To be honest it only took 20 minutes 🤦‍♂️

  1. CMD + F9 or Build > Make Project.
  2. Find the .xml file listed as the cause of the compilation failure. This is listed in the Build tab.
  3. Search for the file – CMD + Shift + O
  4. Delete any duplicate attributes.
  5. Repeat

Retrospective

If my regex knowledge was up to scratch, or if I had time to learn it myself I could have probably avoided the process of removing all of the duplicate attributes manually. However, as it was, without the tangent of trying to find the complicated find regex, the process probably would have taken around 30 minutes.

Hope this helps someone. If you have some suggestions or know how to do some of the things I didn’t please comment below.

Join the Conversation

3 Comments

  1. I have faced the similar situations where I had to add RTL attributes to the XML layouts. I used lint tool. It lets you process the XML files. Once you have the file, you look for eg. `paddingLeft`, if you find it, you add `paddingStart` to that element with the same value. The code does this for a lot of such properties. You just need to run it once.

    This is one of my blogpost which explains how to use lint for a complicated use-case. https://jayrambhia.com/blog/android-lint-ref
    It does not have the code for how to replace certain attributes. I can help you with that.

Leave a comment

Your email address will not be published. Required fields are marked *