#!/usr/bin/env python
-# Copyright (c) 2013 Intel Corporation. All rights reserved.
+# Copyright (c) 2013,2014 Intel Corporation. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import stat
import sys
+# get xwalk absolute path so we can run this script from any location
+xwalk_dir = os.path.dirname(os.path.abspath(__file__))
+sys.path.append(xwalk_dir)
+
+from app_info import AppInfo
from customize_launch_screen import CustomizeLaunchScreen
from handle_xml import AddElementAttribute
from handle_xml import AddElementAttributeAndText
from handle_xml import EditElementAttribute
from handle_xml import EditElementValueByNodeName
from handle_permissions import HandlePermissions
+from util import CleanDir, CreateAndCopyDir, GetBuildDir
from xml.dom import minidom
-def VerifyAppName(value, mode='default'):
- descrpt = 'The app'
- sample = 'helloworld, hello world, hello_world, hello_world1'
- regex = r'[a-zA-Z][\w ]*$'
+TEMPLATE_DIR_NAME = 'template'
+
+
+def VerifyPackageName(value):
+ regex = r'^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)+$'
+ descrpt = 'Each part of package'
+ sample = 'org.xwalk.example, org.xwalk.example_'
if len(value) >= 128:
print('To be safe, the length of package name or app name '
'should be less than 128.')
sys.exit(6)
- if mode == 'packagename':
- regex = r'^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)+$'
- descrpt = 'Each part of package'
- sample = 'org.xwalk.example, org.xwalk.example_'
if not re.match(regex, value):
print('Error: %s name should be started with letters and should not '
'contain invalid characters.\n'
- 'It may contain letters, numbers, blank spaces and underscores\n'
+ 'It may contain lowercase letters, numbers, blank spaces and '
+ 'underscores\n'
'Sample: %s' % (descrpt, sample))
sys.exit(6)
invalid_chars = '\/:.*?"<>|- '
for c in invalid_chars:
if mode == 'apkname' and c in value:
- print("Illegal character: '%s' is replaced with '_'" % c)
+ print('Illegal character: "%s" replaced with "_"' % c)
value = value.replace(c, '_')
return value
compress_js_and_css.CompressCss(css_list)
-def Prepare(name, package, app_root, compressor):
- if os.path.exists(name):
- shutil.rmtree(name)
- shutil.copytree('app_src', name)
- shutil.rmtree(os.path.join(name, 'src'))
- src_root = os.path.join('app_src', 'src', 'org', 'xwalk', 'app', 'template')
- src_activity = os.path.join(src_root, 'AppTemplateActivity.java')
- if not os.path.isfile(src_activity):
- print ('Please make sure that the java file'
- ' of activity does exist.')
+def Prepare(app_info, compressor):
+ """Copy the Android template project to a new app project
+ named app_info.app_name
+ """
+ # create new app_dir in temp dir
+ app_name = app_info.android_name
+ app_dir = GetBuildDir(app_name)
+ app_package = app_info.package
+ app_root = app_info.app_root
+ template_app_dir = os.path.join(xwalk_dir, TEMPLATE_DIR_NAME)
+
+ # 1) copy template project to app_dir
+ CleanDir(app_dir)
+ if not os.path.isdir(template_app_dir):
+ print('Error: The template directory could not be found (%s).' %
+ template_app_dir)
sys.exit(7)
- root_path = os.path.join(name, 'src', package.replace('.', os.path.sep))
- if not os.path.exists(root_path):
- os.makedirs(root_path)
- dest_activity = name + 'Activity.java'
- shutil.copyfile(src_activity, os.path.join(root_path, dest_activity))
+ shutil.copytree(template_app_dir, app_dir)
+
+ # 2) replace app_dir 'src' dir with template 'src' dir
+ CleanDir(os.path.join(app_dir, 'src'))
+ template_src_root = os.path.join(template_app_dir, 'src', 'org', 'xwalk',
+ 'app', 'template')
+
+ # 3) Create directory tree from app package (org.xyz.foo -> src/org/xyz/foo)
+ # and copy AppTemplateActivity.java to <app_name>Activity.java
+ template_activity_file = os.path.join(template_src_root,
+ 'AppTemplateActivity.java')
+ if not os.path.isfile(template_activity_file):
+ print ('Error: The template file %s was not found. '
+ 'Please make sure this file exists.' % template_activity_file)
+ sys.exit(7)
+ app_pkg_dir = os.path.join(app_dir, 'src',
+ app_package.replace('.', os.path.sep))
+ if not os.path.exists(app_pkg_dir):
+ os.makedirs(app_pkg_dir)
+ app_activity_file = app_name + 'Activity.java'
+ shutil.copyfile(template_activity_file,
+ os.path.join(app_pkg_dir, app_activity_file))
+
+ # 4) Copy all HTML source from app_root to app_dir
if app_root:
- assets_path = os.path.join(name, 'assets')
- shutil.rmtree(assets_path)
- os.makedirs(assets_path)
- app_src_path = os.path.join(assets_path, 'www')
- shutil.copytree(app_root, app_src_path)
+ app_assets_dir = os.path.join(app_dir, 'assets', 'www')
+ CleanDir(app_assets_dir)
+ shutil.copytree(app_root, app_assets_dir)
if compressor:
- CompressSourceFiles(app_src_path, compressor)
+ CompressSourceFiles(app_assets_dir, compressor)
+
+
+def EncodingUnicodeValue(value):
+ try:
+ if isinstance(value, unicode):
+ value = value.encode("utf-8")
+ except NameError:
+ pass
+ return value
def CustomizeStringXML(name, description):
- strings_path = os.path.join(name, 'res', 'values', 'strings.xml')
+ strings_path = os.path.join(GetBuildDir(name), 'res', 'values',
+ 'strings.xml')
if not os.path.isfile(strings_path):
print ('Please make sure strings_xml'
- ' exists under app_src folder.')
+ ' exists under template folder.')
sys.exit(6)
if description:
+ description = EncodingUnicodeValue(description)
xmldoc = minidom.parse(strings_path)
AddElementAttributeAndText(xmldoc, 'string', 'name', 'description',
description)
strings_file.close()
-def CustomizeThemeXML(name, fullscreen, app_manifest):
- theme_path = os.path.join(name, 'res', 'values', 'theme.xml')
+def CustomizeThemeXML(name, fullscreen, manifest):
+ theme_path = os.path.join(GetBuildDir(name), 'res', 'values-v14', 'theme.xml')
if not os.path.isfile(theme_path):
print('Error: theme.xml is missing in the build tool.')
sys.exit(6)
if fullscreen:
EditElementValueByNodeName(theme_xmldoc, 'item',
'android:windowFullscreen', 'true')
- has_background = CustomizeLaunchScreen(app_manifest, name)
+ has_background = CustomizeLaunchScreen(manifest, name)
if has_background:
EditElementValueByNodeName(theme_xmldoc, 'item',
'android:windowBackground',
theme_file.close()
-def CustomizeXML(package, app_versionCode, app_version, description, name,
- orientation, icon_dict, fullscreen, icon, app_manifest,
- permissions, app_root):
- manifest_path = os.path.join(name, 'AndroidManifest.xml')
+def CustomizeXML(app_info, description, icon_dict, manifest, permissions):
+ app_version = app_info.app_version
+ app_versionCode = app_info.app_versionCode
+ name = app_info.android_name
+ orientation = app_info.orientation
+ package = app_info.package
+ app_name = app_info.app_name
+ app_dir = GetBuildDir(name)
+ # Chinese character with unicode get from 'manifest.json' will cause
+ # 'UnicodeEncodeError' when finally wrote to 'AndroidManifest.xml'.
+ app_name = EncodingUnicodeValue(app_name)
+ # If string start with '@' or '?', it will be treated as Android resource,
+ # which will cause 'No resource found' error,
+ # append a space before '@' or '?' to fix that.
+ if app_name.startswith('@') or app_name.startswith('?'):
+ app_name = ' ' + app_name
+ manifest_path = os.path.join(app_dir, 'AndroidManifest.xml')
if not os.path.isfile(manifest_path):
print ('Please make sure AndroidManifest.xml'
- ' exists under app_src folder.')
+ ' exists under template folder.')
sys.exit(6)
CustomizeStringXML(name, description)
- CustomizeThemeXML(name, fullscreen, app_manifest)
+ CustomizeThemeXML(name, app_info.fullscreen_flag, manifest)
xmldoc = minidom.parse(manifest_path)
EditElementAttribute(xmldoc, 'manifest', 'package', package)
if app_versionCode:
EditElementAttribute(xmldoc, 'manifest', 'android:description',
"@string/description")
HandlePermissions(permissions, xmldoc)
- EditElementAttribute(xmldoc, 'application', 'android:label', name)
+ EditElementAttribute(xmldoc, 'application', 'android:label', app_name)
activity_name = package + '.' + name + 'Activity'
EditElementAttribute(xmldoc, 'activity', 'android:name', activity_name)
- EditElementAttribute(xmldoc, 'activity', 'android:label', name)
+ EditElementAttribute(xmldoc, 'activity', 'android:label', app_name)
if orientation:
EditElementAttribute(xmldoc, 'activity', 'android:screenOrientation',
orientation)
- icon_name = CustomizeIcon(name, app_root, icon, icon_dict)
+ icon_name = CustomizeIcon(name, app_info.app_root, app_info.icon, icon_dict)
if icon_name:
EditElementAttribute(xmldoc, 'application', 'android:icon',
'@drawable/%s' % icon_name)
- file_handle = open(os.path.join(name, 'AndroidManifest.xml'), 'w')
+ file_handle = open(os.path.join(app_dir, 'AndroidManifest.xml'), 'w')
xmldoc.writexml(file_handle, encoding='utf-8')
file_handle.close()
def SetVariable(file_path, string_line, variable, value):
function_string = ('%sset%s(%s);\n' %
- (' ', variable, value))
+ (' ', variable, value))
temp_file_path = file_path + '.backup'
file_handle = open(temp_file_path, 'w+')
for line in open(file_path):
shutil.move(temp_file_path, file_path)
-def CustomizeJava(name, package, app_url, app_local_path,
- enable_remote_debugging, display_as_fullscreen,
- keep_screen_on):
- root_path = os.path.join(name, 'src', package.replace('.', os.path.sep))
- dest_activity = os.path.join(root_path, name + 'Activity.java')
+def CustomizeJava(app_info, app_url, app_local_path, keep_screen_on):
+ name = app_info.android_name
+ package = app_info.package
+ app_dir = GetBuildDir(name)
+ app_pkg_dir = os.path.join(app_dir, 'src', package.replace('.', os.path.sep))
+ dest_activity = os.path.join(app_pkg_dir, name + 'Activity.java')
ReplaceString(dest_activity, 'org.xwalk.app.template', package)
ReplaceString(dest_activity, 'AppTemplate', name)
- manifest_file = os.path.join(name, 'assets/www', 'manifest.json')
+ manifest_file = os.path.join(app_dir, 'assets', 'www', 'manifest.json')
if os.path.isfile(manifest_file):
ReplaceString(
dest_activity,
ReplaceString(dest_activity, 'file:///android_asset/www/index.html',
app_url)
elif app_local_path:
- if os.path.isfile(os.path.join(name, 'assets/www', app_local_path)):
+ if os.path.isfile(os.path.join(app_dir, 'assets', 'www', app_local_path)):
ReplaceString(dest_activity, 'file:///android_asset/www/index.html',
'app://' + package + '/' + app_local_path)
else:
' is correct.')
sys.exit(8)
- if enable_remote_debugging:
+ if app_info.remote_debugging:
SetVariable(dest_activity,
'public void onCreate(Bundle savedInstanceState)',
'RemoteDebugging', 'true')
- if display_as_fullscreen:
+ if app_info.use_animatable_view:
+ SetVariable(dest_activity,
+ 'public void onCreate(Bundle savedInstanceState)',
+ 'UseAnimatableView', 'true')
+ if app_info.fullscreen_flag:
SetVariable(dest_activity,
'super.onCreate(savedInstanceState)',
'IsFullscreen', 'true')
dest_extension_path = os.path.join(dest_path, extension_name)
if os.path.exists(dest_extension_path):
# TODO: Refine it by renaming it internally.
- print('Error: duplicated extension names "%s" are found. Please rename it.'
+ print('Error: duplicate extension names were found (%s). Please rename it.'
% extension_name)
sys.exit(9)
else:
src_file = os.path.join(src_path, file_name)
dest_file = os.path.join(dest_extension_path, file_name)
if not os.path.isfile(src_file):
+ print('Error: %s was not found in %s.' % (file_name, src_path))
sys.exit(9)
- print('Error: %s is not found in %s.' % (file_name, src_path))
else:
shutil.copyfile(src_file, dest_file)
-def CustomizeExtensions(name, extensions):
+def CustomizeExtensions(app_info, extensions):
"""Copy the files from external extensions and merge them into APK.
The directory of one external extension should be like:
"""
if not extensions:
return
- apk_path = name
- apk_assets_path = os.path.join(apk_path, 'assets')
+ name = app_info.android_name
+ app_dir = GetBuildDir(name)
+ apk_assets_path = os.path.join(app_dir, 'assets')
extensions_string = 'xwalk-extensions'
# Set up the target directories and files.
- dest_jar_path = os.path.join(apk_path, extensions_string)
+ dest_jar_path = os.path.join(app_dir, extensions_string)
os.mkdir(dest_jar_path)
dest_js_path = os.path.join(apk_assets_path, extensions_string)
os.mkdir(dest_js_path)
extension_json_list = []
for source_path in extension_paths:
if not os.path.exists(source_path):
- print('Error: can\'t find the extension directory \'%s\'.' % source_path)
+ print('Error: can not find the extension directory \'%s\'.' % source_path)
sys.exit(9)
# Remove redundant separators to avoid empty basename.
source_path = os.path.normpath(source_path)
file_name = extension_name + '.json'
src_file = os.path.join(source_path, file_name)
if not os.path.isfile(src_file):
- print('Error: %s is not found in %s.' % (file_name, source_path))
+ print('Error: %s was not found in %s.' % (file_name, source_path))
sys.exit(9)
else:
- src_file_handle = file(src_file)
+ src_file_handle = open(src_file)
src_file_content = src_file_handle.read()
json_output = json.JSONDecoder().decode(src_file_content)
# Below 3 properties are used by runtime. See extension manager.
json_output['jsapi'] = js_path_prefix + json_output['jsapi']
extension_json_list.append(json_output)
# Merge the permissions of extensions into AndroidManifest.xml.
- manifest_path = os.path.join(name, 'AndroidManifest.xml')
+ manifest_path = os.path.join(app_dir, 'AndroidManifest.xml')
xmldoc = minidom.parse(manifest_path)
if ('permissions' in json_output):
# Get used permission list to avoid repetition as "--permissions"
extension_json_file.close()
-def GenerateCommandLineFile(name, xwalk_command_line):
+def GenerateCommandLineFile(app_info, xwalk_command_line):
if xwalk_command_line == '':
return
- assets_path = os.path.join(name, 'assets')
+ assets_path = os.path.join(GetBuildDir(app_info.android_name), 'assets')
file_path = os.path.join(assets_path, 'xwalk-command-line')
command_line_file = open(file_path, 'w')
command_line_file.write('xwalk ' + xwalk_command_line)
def CustomizeIconByDict(name, app_root, icon_dict):
+ app_dir = GetBuildDir(name)
icon_name = None
drawable_dict = {'ldpi': [1, 37], 'mdpi': [37, 72], 'hdpi': [72, 96],
'xhdpi': [96, 120], 'xxhdpi': [120, 144],
print('The key of icon in the manifest file should be a number.')
if len(icon_dict) > 0:
- icon_list = sorted(icon_dict.iteritems(), key=lambda d: d[0])
- for kd, vd in drawable_dict.iteritems():
+ icon_list = sorted(icon_dict.items(), key=lambda d: d[0])
+ for kd, vd in drawable_dict.items():
for item in icon_list:
if item[0] >= vd[0] and item[0] < vd[1]:
- drawable_path = os.path.join(name, 'res', 'drawable-' + kd)
+ drawable_path = os.path.join(app_dir, 'res', 'drawable-' + kd)
if not os.path.exists(drawable_path):
os.makedirs(drawable_path)
icon = os.path.join(app_root, item[1])
'icon.' + icon_suffix))
icon_name = 'icon'
elif icon and (not os.path.isfile(icon)):
- print ('Error: Please make sure \"' + icon + '\" does exist!')
+ print('Error: "%s" does not exist.' % icon)
sys.exit(6)
break
return icon_name
def CustomizeIconByOption(name, icon):
if os.path.isfile(icon):
- drawable_path = os.path.join(name, 'res', 'drawable')
+ drawable_path = os.path.join(GetBuildDir(name), 'res', 'drawable')
if not os.path.exists(drawable_path):
os.makedirs(drawable_path)
icon_file = os.path.basename(icon)
icon_name = os.path.splitext(icon_file)[0]
return icon_name
else:
- print ('Error: Please make sure \"' + icon + '\" is a file!')
+ print('Error: "%s" does not exist.' % icon)
sys.exit(6)
return icon_name
-def CustomizeAll(app_versionCode, description, icon_dict, permissions, app_url,
- app_root, app_local_path, enable_remote_debugging,
- display_as_fullscreen, keep_screen_on, extensions,
- app_manifest, icon, package='org.xwalk.app.template',
- name='AppTemplate', app_version='1.0.0',
- orientation='unspecified', xwalk_command_line='',
- compressor=None):
+def CustomizeAll(app_info, description, icon_dict, permissions, app_url,
+ app_local_path, keep_screen_on, extensions, manifest,
+ xwalk_command_line='', compressor=None):
try:
- Prepare(name, package, app_root, compressor)
- CustomizeXML(package, app_versionCode, app_version, description, name,
- orientation, icon_dict, display_as_fullscreen, icon,
- app_manifest, permissions, app_root)
- CustomizeJava(name, package, app_url, app_local_path,
- enable_remote_debugging, display_as_fullscreen,
- keep_screen_on)
- CustomizeExtensions(name, extensions)
- GenerateCommandLineFile(name, xwalk_command_line)
+ Prepare(app_info, compressor)
+ CustomizeXML(app_info, description, icon_dict, manifest, permissions)
+ CustomizeJava(app_info, app_url, app_local_path, keep_screen_on)
+ CustomizeExtensions(app_info, extensions)
+ GenerateCommandLineFile(app_info, xwalk_command_line)
except SystemExit as ec:
print('Exiting with error code: %d' % ec.code)
sys.exit(ec.code)
parser.add_option('--enable-remote-debugging', action='store_true',
dest='enable_remote_debugging', default=False,
help='Enable remote debugging.')
+ parser.add_option('--use-animatable-view', action='store_true',
+ dest='use_animatable_view', default=False,
+ help='Enable using animatable view (TextureView).')
parser.add_option('-f', '--fullscreen', action='store_true',
dest='fullscreen', default=False,
help='Make application fullscreen.')
'Crosswalk is powered by Chromium and supports Chromium command line.'
'For example, '
'--xwalk-command-line=\'--chromium-command-1 --xwalk-command-2\'')
+ info = ('Create an Android project directory at this location. ')
+ parser.add_option('--project-dir', help=info)
parser.add_option('--xwalk-command-line', default='', help=info)
info = ('Minify and obfuscate javascript and css.'
'--compressor: compress javascript and css.'
72: 'icons/icon_72.png',
96: 'icons/icon_96.png',
48: 'icons/icon_48.png'}
- if options.name is None:
- options.name = 'Example'
+ app_info = AppInfo()
+ if options.name is not None:
+ app_info.android_name = options.name
if options.app_root is None:
- options.app_root = os.path.join('test_data', 'manifest')
- if options.package is None:
- options.package = 'org.xwalk.app.template'
- if options.orientation is None:
- options.orientation = 'unspecified'
- if options.app_version is None:
- options.app_version = '1.0.0'
- icon = os.path.join('test_data', 'manifest', 'icons', 'icon_96.png')
- CustomizeAll(options.app_versionCode, options.description, icon_dict,
- options.permissions, options.app_url, options.app_root,
- options.app_local_path, options.enable_remote_debugging,
- options.fullscreen, options.keep_screen_on,
- options.extensions, options.manifest,
- icon, options.package, options.name,
- options.app_version, options.orientation,
+ app_info.app_root = os.path.join(xwalk_dir, 'test_data', 'manifest')
+ else:
+ app_info.app_root = options.app_root
+ if options.package is not None:
+ app_info.package = options.package
+ if options.orientation is not None:
+ app_info.orientation = options.orientation
+ if options.app_version is not None:
+ app_info.app_version = options.app_version
+ if options.enable_remote_debugging is not None:
+ app_info.remote_debugging = options.enable_remote_debugging
+ if options.fullscreen is not None:
+ app_info.fullscreen_flag = options.fullscreen
+ app_info.icon = os.path.join('test_data', 'manifest', 'icons',
+ 'icon_96.png')
+ CustomizeAll(app_info, options.description, icon_dict,
+ options.permissions, options.app_url, options.app_local_path,
+ options.keep_screen_on, options.extensions, None,
options.xwalk_command_line, options.compressor)
+
+ # build project is now in /tmp/<name>. Copy to project_dir
+ if options.project_dir:
+ src_dir = GetBuildDir(app_info.android_name)
+ dest_dir = os.path.join(options.project_dir, app_info.android_name)
+ CreateAndCopyDir(src_dir, dest_dir, True)
+
except SystemExit as ec:
print('Exiting with error code: %d' % ec.code)
return ec.code
+ finally:
+ CleanDir(GetBuildDir(app_info.android_name))
return 0