1#!/usr/bin/env python
2#
3# Copyright (C) 2022 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#      http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17import xml.etree.ElementTree as ET
18import datetime
19import os
20from os import listdir
21from os.path import isfile, join
22
23
24# This script generates and updates the public.xml file for sample ota app automatically.
25# Must be run after adding new strings to sample ota app.
26
27class AdServicesOTAUtil:
28    ADSERVICES_PUBLIC_RES_FILE = '../apk/publicres/values/public.xml'
29    OTA_PUBLIC_RES_FILE = '../samples/ota/res/values/public.xml'
30    OTA_RES_VALUES_DIR = '../samples/ota/res/values/'
31    OTA_LAYOUT_DIR = "../samples/ota/res/layout"
32    OTA_DRAWABLE_DIR = "../samples/ota/res/drawable"
33    OTA_COLOR_DIR = "../samples/ota/res/color"
34
35    COPYRIGHT_TEXT = f'''<?xml version="1.0" encoding="utf-8"?>
36    <!-- Copyright (C) {datetime.date.today().year} The Android Open Source Project
37
38        Licensed under the Apache License, Version 2.0 (the "License");
39        you may not use this file except in compliance with the License.
40        You may obtain a copy of the License at
41
42        http://www.apache.org/licenses/LICENSE-2.0
43
44        Unless required by applicable law or agreed to in writing, software
45        distributed under the License is distributed on an "AS IS" BASIS,
46        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
47        See the License for the specific language governing permissions and
48        limitations under the License.
49    -->
50    '''
51
52    def _add_value_res(self, adservices_dict, public_xml, ota_res_values_dir):
53        if (not os.path.isdir(ota_res_values_dir)):
54            return
55        for file in os.scandir(ota_res_values_dir):
56            if (file.name == 'public.xml'):
57                continue
58            root_element = ET.parse(file.path).getroot()
59            elem_type = file.name.split(".")[0][:-1]
60            for x in root_element:
61                cur_element = ET.SubElement(public_xml, 'public')
62                cur_element.set('type', elem_type)
63                cur_element.set('name', x.attrib['name'])
64                if (elem_type, x.attrib['name']) not in adservices_dict:
65                    print(f"ERROR: ({elem_type},{x.attrib['name']}) is not in adservices ")
66                    exit(0)
67                cur_element.set('id', adservices_dict[(elem_type, x.attrib['name'])])
68
69    def _add_file_res(self, adservices_dict, public_xml, res_dir, res_type):
70        if (not os.path.isdir(res_dir)):
71            return
72        res_files = [f for f in listdir(res_dir) if isfile(join(res_dir, f))]
73        for file in res_files:
74            cur_element = ET.SubElement(public_xml, 'public')
75            cur_element.set('type', res_type)
76            res_name = file.split('.')[0]
77            cur_element.set('name', res_name)
78            if (res_type, res_name) not in adservices_dict:
79                print(f"ERROR: ({res_type},{res_name}) is not in adservices ")
80                exit(0)
81            cur_element.set('id', adservices_dict[(res_type, res_name)])
82
83    def _add_id_res(self, adservices_dict, public_xml, res_dir):
84        if (not os.path.isdir(res_dir)):
85            return
86        res_ids = set()
87        for file in os.scandir(res_dir):
88            ns = '{http://schemas.android.com/apk/res/android}'
89            for child in ET.parse(file.path).getroot().iter():
90                if (ns + 'id' in child.attrib):
91                    res_ids.add(child.attrib[ns + 'id'].split('/')[1])
92        res_ids = list(res_ids)
93        for res_id in res_ids:
94            cur_element = ET.SubElement(public_xml, 'public')
95            cur_element.set('type', 'id')
96            cur_element.set('name', res_id)
97            if ('id', res_id) not in adservices_dict:
98                print(f"ERROR: ('id',{res_id}) is not in adservices ")
99                exit(0)
100            cur_element.set('id', adservices_dict[('id', res_id)])
101
102    def update_ota_public_xml(self, adservices_public_res=ADSERVICES_PUBLIC_RES_FILE,
103                              ota_public_res=OTA_PUBLIC_RES_FILE,
104                              ota_res_values_dir=OTA_RES_VALUES_DIR,
105                              ota_layout_dir=OTA_LAYOUT_DIR,
106                              ota_drawable_dir=OTA_DRAWABLE_DIR,
107                              ota_color_dir=OTA_COLOR_DIR):
108        adservices_dict = {}
109        adservices_public_xml = ET.parse(adservices_public_res).getroot()
110        for x in adservices_public_xml:
111            adservices_dict[(x.attrib['type'], x.attrib['name'])] = x.attrib['id']
112
113        public_xml = ET.Element('resources')
114
115        self._add_value_res(adservices_dict, public_xml, ota_res_values_dir)
116        self._add_file_res(adservices_dict, public_xml, ota_drawable_dir, 'drawable')
117        self._add_file_res(adservices_dict, public_xml, ota_color_dir, 'color')
118        self._add_file_res(adservices_dict, public_xml, ota_layout_dir, 'layout')
119        self._add_id_res(adservices_dict, public_xml, ota_layout_dir)
120
121        ET.indent(public_xml, space='    ')
122        if os.path.exists(ota_public_res):
123            os.remove(ota_public_res)
124        with open(ota_public_res, 'w') as f:
125            f.write(self.COPYRIGHT_TEXT)
126            f.write(ET.tostring(public_xml, encoding="unicode"))
127
128
129if __name__ == "__main__":
130    util = AdServicesOTAUtil()
131    util.update_ota_public_xml()
132