Upstream version 9.38.205.0
[platform/framework/web/crosswalk.git] / src / xwalk / app / tools / android / customize.py
1 #!/usr/bin/env python
2
3 # Copyright (c) 2013 Intel Corporation. All rights reserved.
4 # Use of this source code is governed by a BSD-style license that can be
5 # found in the LICENSE file.
6
7 import compress_js_and_css
8 import fnmatch
9 import json
10 import optparse
11 import os
12 import re
13 import shutil
14 import stat
15 import sys
16
17 # get xwalk absolute path so we can run this script from any location
18 xwalk_dir = os.path.dirname(os.path.abspath(__file__))
19 sys.path.append(xwalk_dir)
20
21 from app_info import AppInfo
22 from customize_launch_screen import CustomizeLaunchScreen
23 from handle_xml import AddElementAttribute
24 from handle_xml import AddElementAttributeAndText
25 from handle_xml import EditElementAttribute
26 from handle_xml import EditElementValueByNodeName
27 from handle_permissions import HandlePermissions
28 from xml.dom import minidom
29
30 TEMPLATE_DIR_NAME = 'template'
31
32 def VerifyPackageName(value):
33   regex = r'^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)+$'
34   descrpt = 'Each part of package'
35   sample = 'org.xwalk.example, org.xwalk.example_'
36
37   if len(value) >= 128:
38     print('To be safe, the length of package name or app name '
39           'should be less than 128.')
40     sys.exit(6)
41
42   if not re.match(regex, value):
43     print('Error: %s name should be started with letters and should not '
44           'contain invalid characters.\n'
45           'It may contain lowercase letters, numbers, blank spaces and '
46           'underscores\n'
47           'Sample: %s' % (descrpt, sample))
48     sys.exit(6)
49
50
51 def ReplaceSpaceWithUnderscore(value):
52   return value.replace(' ', '_')
53
54
55 def ReplaceInvalidChars(value, mode='default'):
56   """ Replace the invalid chars with '_' for input string.
57   Args:
58     value: the original string.
59     mode: the target usage mode of original string.
60   """
61   if mode == 'default':
62     invalid_chars = '\/:*?"<>|- '
63   elif mode == 'apkname':
64     invalid_chars = '\/:.*?"<>|- '
65   for c in invalid_chars:
66     if mode == 'apkname' and c in value:
67       print("Illegal character: '%s' is replaced with '_'" % c)
68     value = value.replace(c, '_')
69   return value
70
71
72 def GetFilesByExt(path, ext, sub_dir=True):
73   if os.path.exists(path):
74     file_list = []
75     for name in os.listdir(path):
76       full_name = os.path.join(path, name)
77       st = os.lstat(full_name)
78       if stat.S_ISDIR(st.st_mode) and sub_dir:
79         file_list += GetFilesByExt(full_name, ext)
80       elif os.path.isfile(full_name):
81         if fnmatch.fnmatch(full_name, ext):
82           file_list.append(full_name)
83     return file_list
84   else:
85     return []
86
87
88 def ParseParameterForCompressor(option, value, values, parser):
89   if ((not values or values.startswith('-'))
90       and value.find('--compressor') != -1):
91     values = 'all'
92   val = values
93   if parser.rargs and not parser.rargs[0].startswith('-'):
94     val = parser.rargs[0]
95     parser.rargs.pop(0)
96   setattr(parser.values, option.dest, val)
97
98
99 def CompressSourceFiles(app_root, compressor):
100   js_list = []
101   css_list = []
102   js_ext = '*.js'
103   css_ext = '*.css'
104
105   if compressor == 'all' or compressor == 'js':
106     js_list = GetFilesByExt(app_root, js_ext)
107     compress_js_and_css.CompressJavaScript(js_list)
108
109   if compressor == 'all' or compressor == 'css':
110     css_list = GetFilesByExt(app_root, css_ext)
111     compress_js_and_css.CompressCss(css_list)
112
113
114 def Prepare(app_info, compressor):
115   """Copy the Android template project to a new app project
116      named app_info.app_name
117   """
118   # create new app_dir in xwalk_dir
119   app_name = app_info.android_name
120   app_dir = os.path.join(xwalk_dir, app_name)
121   app_package = app_info.package
122   app_root = app_info.app_root
123   template_app_dir = os.path.join(xwalk_dir, TEMPLATE_DIR_NAME)
124
125   # 1) copy template project to app_dir
126   if os.path.exists(app_dir):
127     shutil.rmtree(app_dir)
128   shutil.copytree(template_app_dir, app_dir)
129
130   # 2) replace app_dir 'src' dir with template 'src' dir
131   shutil.rmtree(os.path.join(app_dir, 'src'))
132   template_src_root = os.path.join(template_app_dir, 'src', 'org', 'xwalk',
133                                    'app', 'template')
134
135   # 3) Create directory tree from app package (org.xyz.foo -> src/org/xyz/foo)
136   #    and copy AppTemplateActivity.java to <app_name>Activity.java 
137   template_activity_file = os.path.join(template_src_root,
138                                         'AppTemplateActivity.java')
139   if not os.path.isfile(template_activity_file):
140     print ('Error: The template file %s was not found. '
141            'Please make sure this file exists.' % template_activity_file)
142     sys.exit(7)
143   app_pkg_dir = os.path.join(app_dir, 'src',
144                              app_package.replace('.', os.path.sep))
145   if not os.path.exists(app_pkg_dir):
146     os.makedirs(app_pkg_dir)
147   app_activity_file = app_name + 'Activity.java'
148   shutil.copyfile(template_activity_file,
149                   os.path.join(app_pkg_dir, app_activity_file))
150
151   # 4) Copy all HTML source from app_root to app_dir
152   if app_root:
153     app_assets_dir = os.path.join(app_dir, 'assets', 'www')
154     if os.path.isdir(app_assets_dir):
155       shutil.rmtree(app_assets_dir)
156     shutil.copytree(app_root, app_assets_dir)
157     if compressor:
158       CompressSourceFiles(app_assets_dir, compressor)
159
160
161 def EncodingUnicodeValue(value):
162   try:
163     if isinstance(value, unicode):
164       value = value.encode("utf-8")
165   except NameError:
166     pass
167   return value
168
169
170 def CustomizeStringXML(name, description):
171   strings_path = os.path.join(xwalk_dir, name, 'res', 'values', 'strings.xml')
172   if not os.path.isfile(strings_path):
173     print ('Please make sure strings_xml'
174            ' exists under template folder.')
175     sys.exit(6)
176
177   if description:
178     description = EncodingUnicodeValue(description)
179     xmldoc = minidom.parse(strings_path)
180     AddElementAttributeAndText(xmldoc, 'string', 'name', 'description',
181                                description)
182     strings_file = open(strings_path, 'w')
183     xmldoc.writexml(strings_file, encoding='utf-8')
184     strings_file.close()
185
186
187 def CustomizeThemeXML(name, fullscreen, manifest):
188   theme_path = os.path.join(xwalk_dir, name, 'res', 'values-v14', 'theme.xml')
189   if not os.path.isfile(theme_path):
190     print('Error: theme.xml is missing in the build tool.')
191     sys.exit(6)
192
193   theme_xmldoc = minidom.parse(theme_path)
194   if fullscreen:
195     EditElementValueByNodeName(theme_xmldoc, 'item',
196                                'android:windowFullscreen', 'true')
197   has_background = CustomizeLaunchScreen(manifest, name)
198   if has_background:
199     EditElementValueByNodeName(theme_xmldoc, 'item',
200                                'android:windowBackground',
201                                '@drawable/launchscreen_bg')
202   theme_file = open(theme_path, 'w')
203   theme_xmldoc.writexml(theme_file, encoding='utf-8')
204   theme_file.close()
205
206
207 def CustomizeXML(app_info, description, icon_dict, manifest, permissions):
208   app_version = app_info.app_version
209   app_versionCode = app_info.app_versionCode
210   name = app_info.android_name
211   orientation = app_info.orientation
212   package = app_info.package
213   app_name = app_info.app_name
214   # Chinese character with unicode get from 'manifest.json' will cause
215   # 'UnicodeEncodeError' when finally wrote to 'AndroidManifest.xml'.
216   app_name = EncodingUnicodeValue(app_name)
217   # If string start with '@' or '?', it will be treated as Android resource,
218   # which will cause 'No resource found' error,
219   # append a space before '@' or '?' to fix that.
220   if app_name.startswith('@') or app_name.startswith('?'):
221     app_name = ' ' + app_name
222   manifest_path = os.path.join(xwalk_dir, name, 'AndroidManifest.xml')
223   if not os.path.isfile(manifest_path):
224     print ('Please make sure AndroidManifest.xml'
225            ' exists under template folder.')
226     sys.exit(6)
227
228   CustomizeStringXML(name, description)
229   CustomizeThemeXML(name, app_info.fullscreen_flag, manifest)
230   xmldoc = minidom.parse(manifest_path)
231   EditElementAttribute(xmldoc, 'manifest', 'package', package)
232   if app_versionCode:
233     EditElementAttribute(xmldoc, 'manifest', 'android:versionCode',
234                          str(app_versionCode))
235   if app_version:
236     EditElementAttribute(xmldoc, 'manifest', 'android:versionName',
237                          app_version)
238   if description:
239     EditElementAttribute(xmldoc, 'manifest', 'android:description',
240                          "@string/description")
241   HandlePermissions(permissions, xmldoc)
242   EditElementAttribute(xmldoc, 'application', 'android:label', app_name)
243   activity_name = package + '.' + name + 'Activity'
244   EditElementAttribute(xmldoc, 'activity', 'android:name', activity_name)
245   EditElementAttribute(xmldoc, 'activity', 'android:label', app_name)
246   if orientation:
247     EditElementAttribute(xmldoc, 'activity', 'android:screenOrientation',
248                          orientation)
249   icon_name = CustomizeIcon(name, app_info.app_root, app_info.icon, icon_dict)
250   if icon_name:
251     EditElementAttribute(xmldoc, 'application', 'android:icon',
252                          '@drawable/%s' % icon_name)
253
254   file_handle = open(os.path.join(xwalk_dir, name, 'AndroidManifest.xml'), 'w')
255   xmldoc.writexml(file_handle, encoding='utf-8')
256   file_handle.close()
257
258
259 def ReplaceString(file_path, src, dest):
260   file_handle = open(file_path, 'r')
261   src_content = file_handle.read()
262   file_handle.close()
263   file_handle = open(file_path, 'w')
264   dest_content = src_content.replace(src, dest)
265   file_handle.write(dest_content)
266   file_handle.close()
267
268
269 def SetVariable(file_path, string_line, variable, value):
270   function_string = ('%sset%s(%s);\n' %
271                     ('        ', variable, value))
272   temp_file_path = file_path + '.backup'
273   file_handle = open(temp_file_path, 'w+')
274   for line in open(file_path):
275     file_handle.write(line)
276     if (line.find(string_line) >= 0):
277       file_handle.write(function_string)
278   file_handle.close()
279   shutil.move(temp_file_path, file_path)
280
281
282 def CustomizeJava(app_info, app_url, app_local_path, keep_screen_on):
283   name = app_info.android_name
284   package = app_info.package
285   app_pkg_dir = os.path.join(xwalk_dir, name, 'src',
286                              package.replace('.', os.path.sep))
287   dest_activity = os.path.join(app_pkg_dir, name + 'Activity.java')
288   ReplaceString(dest_activity, 'org.xwalk.app.template', package)
289   ReplaceString(dest_activity, 'AppTemplate', name)
290   manifest_file = os.path.join(xwalk_dir, name, 'assets/www', 'manifest.json')
291   if os.path.isfile(manifest_file):
292     ReplaceString(
293         dest_activity,
294         'loadAppFromUrl("file:///android_asset/www/index.html")',
295         'loadAppFromManifest("file:///android_asset/www/manifest.json")')
296   else:
297     if app_url:
298       if re.search(r'^http(|s)', app_url):
299         ReplaceString(dest_activity, 'file:///android_asset/www/index.html',
300                       app_url)
301     elif app_local_path:
302       if os.path.isfile(os.path.join(xwalk_dir, name, 'assets/www',
303                                      app_local_path)):
304         ReplaceString(dest_activity, 'file:///android_asset/www/index.html',
305                       'app://' + package + '/' + app_local_path)
306       else:
307         print ('Please make sure that the relative path of entry file'
308                ' is correct.')
309         sys.exit(8)
310
311   if app_info.remote_debugging:
312     SetVariable(dest_activity,
313                 'public void onCreate(Bundle savedInstanceState)',
314                 'RemoteDebugging', 'true')
315   if app_info.use_animatable_view:
316     SetVariable(dest_activity,
317                 'public void onCreate(Bundle savedInstanceState)',
318                 'UseAnimatableView', 'true')
319   if app_info.fullscreen_flag:
320     SetVariable(dest_activity,
321                 'super.onCreate(savedInstanceState)',
322                 'IsFullscreen', 'true')
323   if keep_screen_on:
324     ReplaceString(
325         dest_activity,
326         'super.onCreate(savedInstanceState);',
327         'super.onCreate(savedInstanceState);\n        ' +
328         'getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);')
329
330
331 def CopyExtensionFile(extension_name, suffix, src_path, dest_path):
332   # Copy the file from src_path into dest_path.
333   dest_extension_path = os.path.join(dest_path, extension_name)
334   if os.path.exists(dest_extension_path):
335     # TODO: Refine it by renaming it internally.
336     print('Error: duplicated extension names "%s" are found. Please rename it.'
337           % extension_name)
338     sys.exit(9)
339   else:
340     os.mkdir(dest_extension_path)
341
342   file_name = extension_name + suffix
343   src_file = os.path.join(src_path, file_name)
344   dest_file = os.path.join(dest_extension_path, file_name)
345   if not os.path.isfile(src_file):
346     print('Error: %s was not found in %s.' % (file_name, src_path))
347     sys.exit(9)
348   else:
349     shutil.copyfile(src_file, dest_file)
350
351
352 def CustomizeExtensions(app_info, extensions):
353   """Copy the files from external extensions and merge them into APK.
354
355   The directory of one external extension should be like:
356     myextension/
357       myextension.jar
358       myextension.js
359       myextension.json
360   That means the name of the internal files should be the same as the
361   directory name.
362   For .jar files, they'll be copied to xwalk-extensions/ and then
363   built into classes.dex in make_apk.py.
364   For .js files, they'll be copied into assets/xwalk-extensions/.
365   For .json files, the'll be merged into one file called
366   extensions-config.json and copied into assets/.
367   """
368   if not extensions:
369     return
370   name = app_info.android_name
371   apk_path = os.path.join(xwalk_dir, name)
372   apk_assets_path = os.path.join(apk_path, 'assets')
373   extensions_string = 'xwalk-extensions'
374
375   # Set up the target directories and files.
376   dest_jar_path = os.path.join(apk_path, extensions_string)
377   os.mkdir(dest_jar_path)
378   dest_js_path = os.path.join(apk_assets_path, extensions_string)
379   os.mkdir(dest_js_path)
380   apk_extensions_json_path = os.path.join(apk_assets_path,
381                                           'extensions-config.json')
382
383   # Split the paths into a list.
384   extension_paths = extensions.split(os.pathsep)
385   extension_json_list = []
386   for source_path in extension_paths:
387     if not os.path.exists(source_path):
388       print('Error: can\'t find the extension directory \'%s\'.' % source_path)
389       sys.exit(9)
390     # Remove redundant separators to avoid empty basename.
391     source_path = os.path.normpath(source_path)
392     extension_name = os.path.basename(source_path)
393
394     # Copy .jar file into xwalk-extensions.
395     CopyExtensionFile(extension_name, '.jar', source_path, dest_jar_path)
396
397     # Copy .js file into assets/xwalk-extensions.
398     CopyExtensionFile(extension_name, '.js', source_path, dest_js_path)
399
400     # Merge .json file into assets/xwalk-extensions.
401     file_name = extension_name + '.json'
402     src_file = os.path.join(source_path, file_name)
403     if not os.path.isfile(src_file):
404       print('Error: %s is not found in %s.' % (file_name, source_path))
405       sys.exit(9)
406     else:
407       src_file_handle = open(src_file)
408       src_file_content = src_file_handle.read()
409       json_output = json.JSONDecoder().decode(src_file_content)
410       # Below 3 properties are used by runtime. See extension manager.
411       # And 'permissions' will be merged.
412       if not ('name' in json_output and
413               'class' in json_output and
414               'jsapi' in json_output):
415         print ('Error: properties \'name\', \'class\' and \'jsapi\' in a json '
416                'file are mandatory.')
417         sys.exit(9)
418       # Reset the path for JavaScript.
419       js_path_prefix = extensions_string + '/' + extension_name + '/'
420       json_output['jsapi'] = js_path_prefix + json_output['jsapi']
421       extension_json_list.append(json_output)
422       # Merge the permissions of extensions into AndroidManifest.xml.
423       manifest_path = os.path.join(xwalk_dir, name, 'AndroidManifest.xml')
424       xmldoc = minidom.parse(manifest_path)
425       if ('permissions' in json_output):
426         # Get used permission list to avoid repetition as "--permissions"
427         # option can also be used to declare used permissions.
428         existingList = []
429         usedPermissions = xmldoc.getElementsByTagName("uses-permission")
430         for used in usedPermissions:
431           existingList.append(used.getAttribute("android:name"))
432
433         # Add the permissions to manifest file if not used yet.
434         for p in json_output['permissions']:
435           if p in existingList:
436             continue
437           AddElementAttribute(xmldoc, 'uses-permission', 'android:name', p)
438           existingList.append(p)
439
440         # Write to the manifest file to save the update.
441         file_handle = open(manifest_path, 'w')
442         xmldoc.writexml(file_handle, encoding='utf-8')
443         file_handle.close()
444
445   # Write configuration of extensions into the target extensions-config.json.
446   if extension_json_list:
447     extensions_string = json.JSONEncoder().encode(extension_json_list)
448     extension_json_file = open(apk_extensions_json_path, 'w')
449     extension_json_file.write(extensions_string)
450     extension_json_file.close()
451
452
453 def GenerateCommandLineFile(app_info, xwalk_command_line):
454   if xwalk_command_line == '':
455     return
456   assets_path = os.path.join(xwalk_dir, app_info.android_name, 'assets')
457   file_path = os.path.join(assets_path, 'xwalk-command-line')
458   command_line_file = open(file_path, 'w')
459   command_line_file.write('xwalk ' + xwalk_command_line)
460
461
462 def CustomizeIconByDict(name, app_root, icon_dict):
463   icon_name = None
464   drawable_dict = {'ldpi': [1, 37], 'mdpi': [37, 72], 'hdpi': [72, 96],
465                    'xhdpi': [96, 120], 'xxhdpi': [120, 144],
466                    'xxxhdpi': [144, 168]}
467   if not icon_dict:
468     return icon_name
469
470   try:
471     icon_dict = dict((int(k), v) for k, v in icon_dict.items())
472   except ValueError:
473     print('The key of icon in the manifest file should be a number.')
474
475   if len(icon_dict) > 0:
476     icon_list = sorted(icon_dict.items(), key=lambda d: d[0])
477     for kd, vd in drawable_dict.items():
478       for item in icon_list:
479         if item[0] >= vd[0] and item[0] < vd[1]:
480           drawable_path = os.path.join(xwalk_dir, name, 'res', 'drawable-' + kd)
481           if not os.path.exists(drawable_path):
482             os.makedirs(drawable_path)
483           icon = os.path.join(app_root, item[1])
484           if icon and os.path.isfile(icon):
485             icon_name = os.path.basename(icon)
486             icon_suffix = icon_name.split('.')[-1]
487             shutil.copyfile(icon, os.path.join(drawable_path,
488                                                'icon.' + icon_suffix))
489             icon_name = 'icon'
490           elif icon and (not os.path.isfile(icon)):
491             print('Error: "%s" does not exist.' % icon)
492             sys.exit(6)
493           break
494   return icon_name
495
496
497 def CustomizeIconByOption(name, icon):
498   if os.path.isfile(icon):
499     drawable_path = os.path.join(xwalk_dir, name, 'res', 'drawable')
500     if not os.path.exists(drawable_path):
501       os.makedirs(drawable_path)
502     icon_file = os.path.basename(icon)
503     icon_file = ReplaceInvalidChars(icon_file)
504     shutil.copyfile(icon, os.path.join(drawable_path, icon_file))
505     icon_name = os.path.splitext(icon_file)[0]
506     return icon_name
507   else:
508     print('Error: "%s" does not exist.')
509     sys.exit(6)
510
511
512 def CustomizeIcon(name, app_root, icon, icon_dict):
513   icon_name = None
514   if icon:
515     icon_name = CustomizeIconByOption(name, icon)
516   else:
517     icon_name = CustomizeIconByDict(name, app_root, icon_dict)
518   return icon_name
519
520
521 def CustomizeAll(app_info, description, icon_dict, permissions, app_url,
522                  app_local_path, keep_screen_on, extensions, manifest,
523                  xwalk_command_line='', compressor=None):
524   try:
525     Prepare(app_info, compressor)
526     CustomizeXML(app_info, description, icon_dict, manifest, permissions)
527     CustomizeJava(app_info, app_url, app_local_path, keep_screen_on)
528     CustomizeExtensions(app_info, extensions)
529     GenerateCommandLineFile(app_info, xwalk_command_line)
530   except SystemExit as ec:
531     print('Exiting with error code: %d' % ec.code)
532     sys.exit(ec.code)
533
534
535 def main():
536   parser = optparse.OptionParser()
537   info = ('The package name. Such as: '
538           '--package=com.example.YourPackage')
539   parser.add_option('--package', help=info)
540   info = ('The apk name. Such as: --name="Your Application Name"')
541   parser.add_option('--name', help=info)
542   info = ('The version of the app. Such as: --app-version=TheVersionNumber')
543   parser.add_option('--app-version', help=info)
544   info = ('The versionCode of the app. Such as: --app-versionCode=24')
545   parser.add_option('--app-versionCode', type='int', help=info)
546   info = ('The application description. Such as:'
547           '--description=YourApplicationdDescription')
548   parser.add_option('--description', help=info)
549   info = ('The permission list. Such as: --permissions="geolocation"'
550           'For more permissions, such as:'
551           '--permissions="geolocation:permission2"')
552   parser.add_option('--permissions', help=info)
553   info = ('The url of application. '
554           'This flag allows to package website as apk. Such as: '
555           '--app-url=http://www.intel.com')
556   parser.add_option('--app-url', help=info)
557   info = ('The root path of the web app. '
558           'This flag allows to package local web app as apk. Such as: '
559           '--app-root=/root/path/of/the/web/app')
560   parser.add_option('--app-root', help=info)
561   info = ('The reletive path of entry file based on |app_root|. '
562           'This flag should work with "--app-root" together. '
563           'Such as: --app-local-path=/reletive/path/of/entry/file')
564   parser.add_option('--app-local-path', help=info)
565   parser.add_option('--enable-remote-debugging', action='store_true',
566                     dest='enable_remote_debugging', default=False,
567                     help='Enable remote debugging.')
568   parser.add_option('--use-animatable-view', action='store_true',
569                     dest='use_animatable_view', default=False,
570                     help='Enable using animatable view (TextureView).')
571   parser.add_option('-f', '--fullscreen', action='store_true',
572                     dest='fullscreen', default=False,
573                     help='Make application fullscreen.')
574   parser.add_option('--keep-screen-on', action='store_true', default=False,
575                     help='Support keeping screen on')
576   info = ('The path list for external extensions separated by os separator.'
577           'On Linux and Mac, the separator is ":". On Windows, it is ";".'
578           'Such as: --extensions="/path/to/extension1:/path/to/extension2"')
579   parser.add_option('--extensions', help=info)
580   info = ('The orientation of the web app\'s display on the device. '
581           'Such as: --orientation=landscape. The default value is "unspecified"'
582           'The value options are the same as those on the Android: '
583           'http://developer.android.com/guide/topics/manifest/'
584           'activity-element.html#screen')
585   parser.add_option('--orientation', help=info)
586   parser.add_option('--manifest', help='The manifest path')
587   info = ('Use command lines.'
588           'Crosswalk is powered by Chromium and supports Chromium command line.'
589           'For example, '
590           '--xwalk-command-line=\'--chromium-command-1 --xwalk-command-2\'')
591   parser.add_option('--xwalk-command-line', default='', help=info)
592   info = ('Minify and obfuscate javascript and css.'
593           '--compressor: compress javascript and css.'
594           '--compressor=js: compress javascript.'
595           '--compressor=css: compress css.')
596   parser.add_option('--compressor', dest='compressor', action='callback',
597                     callback=ParseParameterForCompressor,
598                     type='string', nargs=0, help=info)
599   options, _ = parser.parse_args()
600   try:
601     icon_dict = {144: 'icons/icon_144.png',
602                  72: 'icons/icon_72.png',
603                  96: 'icons/icon_96.png',
604                  48: 'icons/icon_48.png'}
605     app_info = AppInfo()
606     if options.name is not None:
607       app_info.android_name = options.name
608     if options.app_root is None:
609       app_info.app_root = os.path.join(xwalk_dir, 'test_data', 'manifest')
610     else:
611       app_info.app_root = options.app_root
612     if options.package is not None:
613       app_info.package = options.package
614     if options.orientation is not None:
615       app_info.orientation = options.orientation
616     if options.app_version is not None:
617       app_info.app_version = options.app_version
618     if options.enable_remote_debugging is not None:
619       app_info.remote_debugging = options.enable_remote_debugging
620     if options.fullscreen is not None:
621       app_info.fullscreen_flag = options.fullscreen
622     app_info.icon = os.path.join('test_data', 'manifest', 'icons',
623                                  'icon_96.png')
624     CustomizeAll(app_info, options.description, icon_dict,
625                  options.permissions, options.app_url, options.app_local_path,
626                  options.keep_screen_on, options.extensions, None,
627                  options.xwalk_command_line, options.compressor)
628   except SystemExit as ec:
629     print('Exiting with error code: %d' % ec.code)
630     return ec.code
631   return 0
632
633
634 if __name__ == '__main__':
635   sys.exit(main())