Skip to content

Instantly share code, notes, and snippets.

@nift4
Last active January 13, 2025 16:56
Show Gist options
  • Save nift4/baa9f9d0d044692b28f05d2e4787aec4 to your computer and use it in GitHub Desktop.
Save nift4/baa9f9d0d044692b28f05d2e4787aec4 to your computer and use it in GitHub Desktop.
Overlay generator

Overlay generator

$ java -jar ~/Downloads/apktool_2.10.0.jar d $DUMP/vendor/overlay/framework-res__auto_generated_rro_vendor.apk --keep-broken-res
I: Using Apktool 2.10.0 on framework-res__auto_generated_rro_vendor.apk with 16 thread(s).
I: Loading resource table...
W: Duplicate Resource Detected. Ignoring duplicate: bool-mcc234-mnc10/config_moto_nr_enabled
W: Duplicate Resource Detected. Ignoring duplicate: bool-mcc234-mnc10/config_moto_nr_enabled
W: Duplicate Resource Detected. Ignoring duplicate: bool-mcc234-mnc10/config_moto_nr_enabled
W: Duplicate Resource Detected. Ignoring duplicate: bool-mcc234-mnc10/config_moto_nr_enabled
W: Duplicate Resource Detected. Ignoring duplicate: bool-mcc234-mnc10/config_moto_nr_enabled
W: Duplicate Resource Detected. Ignoring duplicate: bool-mcc234-mnc10/config_moto_nr_enabled
W: Duplicate Resource Detected. Ignoring duplicate: bool-mcc310-mnc240/config_moto_nr_enabled
W: Duplicate Resource Detected. Ignoring duplicate: bool-mcc310-mnc240/config_moto_nr_enabled
W: Duplicate Resource Detected. Ignoring duplicate: bool-mcc310-mnc260/config_moto_nr_enabled
W: Duplicate Resource Detected. Ignoring duplicate: bool-mcc310-mnc260/config_moto_nr_enabled
W: Duplicate Resource Detected. Ignoring duplicate: bool-mcc310-mnc260/config_moto_nr_enabled
W: Duplicate Resource Detected. Ignoring duplicate: bool-mcc310-mnc260/config_moto_nr_enabled
I: Decoding file-resources...
I: Decoding values */* XMLs...
I: Loading resource table from file: /home/nick/.local/share/apktool/framework/1.apk
I: Decoding AndroidManifest.xml with resources...
I: Regular manifest package...
I: Copying assets and libs...
I: Copying unknown files...
I: Copying original files...
$ python3 sort.py framework-res__auto_generated_rro_vendor/res/values/ $(gettop)/frameworks/base/core/res/res/values/ config.xml
WARNING: Discarding element 'config_defaultFixedOnPeakRefreshRate' not present in base config.xml
Element: b'<integer name="config_defaultFixedOnPeakRefreshRate">1</integer>'

WARNING: Discarding element 'config_customFirstBootspeedList' not present in base config.xml
Element: b'<string-array name="config_customFirstBootspeedList"><item>com.android.systemui</item></string-array>'

WARNING: Discarding element 'config_customInstallspeedList' not present in base config.xml
Element: b'<string-array name="config_customInstallspeedList"><item>com.google.android.dialer</item></string-array>'

WARNING: Discarding element 'config_mapping_color_mode_to_qcom_or_mtk_solution' not present in base config.xml
Element: b'<bool name="config_mapping_color_mode_to_qcom_or_mtk_solution">false</bool>'

WARNING: Discarding element 'config_wifi_dual_band_support' not present in base config.xml
Element: b'<bool name="config_wifi_dual_band_support">true</bool>'

WARNING: Discarding element 'zz_moto_config_enable_side_fps' not present in base config.xml
Element: b'<bool name="zz_moto_config_enable_side_fps">true</bool>'

WARNING: Discarding element 'status_bar_padding_top_portrait' not present in base config.xml
Element: b'<dimen name="status_bar_padding_top_portrait">15.0px</dimen>'

WARNING: Discarding element 'config_motoCameraAppPackageName' not present in base config.xml
Element: b'<string name="config_motoCameraAppPackageName">com.motorola.camera3</string>'
$ cat config.xml
<?xml version='1.0' encoding='UTF-8'?>
<resources>

    <!-- IWLAN data service package name to bind to by default. If none is specified in an overlay,
         an empty string is passed in -->
    <string name="config_wlan_data_service_package" translatable="false">com.mediatek.telephony</string>

    <!-- IWLAN data service class name to bind to by default. If none is specified in an overlay, an
         empty string is passed in -->
    <string name="config_wlan_data_service_class" translatable="false">com.mediatek.telephony.IwlanDataService</string>

    <!-- IWLAN network service package name to bind to by default. If none is specified in an
         overlay, an empty string is passed in -->
    <string name="config_wlan_network_service_package" translatable="false">com.mediatek.telephony</string>
    
[...]

    <!-- The list of packages to automatically opt out of refresh rates higher than 60hz because
         of known compatibility issues. -->
    <string-array name="config_highRefreshRateBlacklist">
        <item>com.maxgames.stickwarlegacy</item>
    </string-array>

    <!-- Whether this device is supporting the camera toggle -->
    <bool name="config_supportsCamToggle">false</bool>

</resources>
# SPDX-License-Identifier: Apache-2.0
#
# based on broken code generated by ChatGPT
import argparse
import os
from lxml import etree
whitespace = " "
def parse_with_comments(xml_file_path):
"""Parses the XML and associates comments directly with elements."""
with open(xml_file_path, 'rb') as f:
xml_string = f.read()
parser = etree.XMLParser(remove_blank_text=True)
root = etree.fromstring(xml_string, parser=parser)
elements_and_associated_comments = []
previous_comment = None
for child in root:
if isinstance(child, etree._Comment):
previous_comment = child
else:
elements_and_associated_comments.append((child, previous_comment))
previous_comment = None
return root.xpath("/comment()"), elements_and_associated_comments
def parse_with_comments_maybe_folder(xml_file_path):
if os.path.isdir(xml_file_path):
elements_and_comments_1 = []
top_level_comments_1 = []
for file_name in os.listdir(xml_file_path):
if file_name.endswith('.xml') and file_name != "public.xml" and file_name != "symbols.xml":
file_path = os.path.join(xml_file_path, file_name)
top_level_comments, elements_and_comments = parse_with_comments(file_path)
top_level_comments_1.extend(top_level_comments)
elements_and_comments_1.extend(elements_and_comments)
elif os.path.isfile(xml_file_path):
top_level_comments_1, elements_and_comments_1 = parse_with_comments(xml_file_path)
else:
raise FileNotFoundError(f"The specified XML input '{xml_file_path}' does not exist.")
return top_level_comments_1, elements_and_comments_1
def sort_and_copy_comments(xml_input_1, xml_file_2, output_file):
# Extract elements and comments from XML 2
_, elements_and_comments_2 = parse_with_comments_maybe_folder(xml_file_2)
# Create a mapping of element names to their original order in XML 2
order_in_2 = {
elem.attrib['name']: idx
for idx, (elem, _) in enumerate(elements_and_comments_2)
if isinstance(elem, etree._Element) and 'name' in elem.attrib
}
# Combine elements and comments from XML 1 files
top_level_comments_1, elements_and_comments_1 = parse_with_comments_maybe_folder(xml_input_1)
# Sort the elements from XML 1 based on the order in XML 2
sorted_elements = sorted(
[(e, c) for e, c in elements_and_comments_1 if isinstance(e, etree._Element)],
key=lambda x: order_in_2.get(x[0].attrib.get('name', ''), float('inf'))
)
# Identify elements in XML 1 that are not present in XML 2
unsorted_elements = [
(e, c) for e, c in elements_and_comments_1
if isinstance(e, etree._Element) and e.attrib.get('name') not in order_in_2
]
# Create a new XML structure with sorted elements and comments from XML 2
root = etree.Element('resources')
top_level_comments_1.reverse()
for comment in top_level_comments_1:
comment.tail = "\n"
root.addprevious(comment)
for elem, xml2_comment in elements_and_comments_2:
if isinstance(elem, etree._Element) and elem.attrib.get('name') in order_in_2:
# Add sorted elements to the output
for sorted_elem, xml1_comment in sorted_elements:
if sorted_elem.attrib.get('name') == elem.attrib['name']:
if xml1_comment is not None and len(xml1_comment.text) > 0:
if xml2_comment is not None and len(xml2_comment.text) > 0:
if xml1_comment.text != xml2_comment.text:
print("WARNING: discarding input file comment for element that exists in base "
"config.xml and has different comment there\n"
f"Input file comment: {xml1_comment}\n"
f"Base config file comment: {xml2_comment}\n")
xml2_comment.tail = f"\n{whitespace}"
root.append(xml2_comment)
else:
print(f"WARNING: copying over comment for element {elem.attrib['name']} that exists in base"
f" config.xml but has no comment there\n")
xml1_comment.tail = f"\n{whitespace}"
root.append(xml1_comment)
elif xml2_comment is not None and len(xml2_comment.text) > 0:
xml2_comment.tail = f"\n{whitespace}"
root.append(xml2_comment)
# Copy the "translatable" attribute if present in XML 2
if 'translatable' in elem.attrib:
if 'translatable' in sorted_elem.attrib and sorted_elem.attrib['translatable'] != elem.attrib['translatable']:
print(f"WARNING: copying over translatable={elem.attrib['translatable']} from base "
f"config.xml for element {elem.attrib['name']} that has set "
f"translatable=\"{elem.attrib['translatable']}\" in input file\n")
sorted_elem.attrib['translatable'] = elem.attrib['translatable']
etree.indent(sorted_elem, space=whitespace, level=1)
sorted_elem.tail = f"\n\n{whitespace}"
root.append(sorted_elem)
for elem, xml1_comment in unsorted_elements:
print(f"WARNING: Discarding element '{elem.attrib.get('name', '')}' not present in base config.xml")
if xml1_comment is not None and len(xml1_comment.text) > 0:
print(f"Comment: {xml1_comment}")
print(f"Element: {etree.tostring(elem)}")
print()
if len(root) > 1:
root.text = f"\n\n{whitespace}"
root[-1].tail = "\n\n"
tree = etree.ElementTree(root)
tree.write(output_file, encoding='utf-8', xml_declaration=True)
def main():
# Set up the argument parser
parser = argparse.ArgumentParser(description="Sort the elements of XML files in a folder or a single XML file based on another XML file and copy comments.")
parser.add_argument("xml_input_1", help="The folder or XML file containing XML data to sort.")
parser.add_argument("xml_file_2", help="The second XML file that defines the sort order and provides comments.")
parser.add_argument("output_file", help="The output file where the sorted XML will be saved.")
# Parse arguments
args = parser.parse_args()
# Call the function with the provided arguments
sort_and_copy_comments(args.xml_input_1, args.xml_file_2, args.output_file)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment