Esempio n. 1
0
def GenerateCommandLineFile(app_info, xwalk_command_line):
    if xwalk_command_line == '':
        return
    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)
Esempio n. 2
0
def MakeApk(options, app_info, manifest):
  CheckSystemRequirements()
  Customize(options, app_info, manifest)
  name = app_info.android_name
  app_dir = GetBuildDir(name)
  packaged_archs = []
  if options.mode == 'shared':
    MakeSharedApk(options, app_info, app_dir)
  else: # default
    MakeEmbeddedApk(options, app_info, app_dir, packaged_archs)

  # if project_dir, save build directory
  if options.project_dir:
    print ('\nCreating project directory')
    save_dir = os.path.join(options.project_dir, name)
    if CreateAndCopyDir(app_dir, save_dir, True):
      print ('  A project directory was created successfully in:\n   %s' %
             os.path.abspath(save_dir))
      print ('  To manually generate an APK, run the following in that '
             'directory:')
      print ('   ant release -f build.xml')
      print ('  For more information, see:\n'
             '   http://developer.android.com/tools/building/'
             'building-cmdline.html')
    else:
      print ('Error: Unable to create a project directory during the build. '
             'Please check the directory passed in --project-dir, '
             'available disk space, and write permission.')

  if not options.project_only:
    PrintPackageInfo(options, name, packaged_archs)
Esempio n. 3
0
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],
                   'xxxhdpi': [144, 168]}
  if not icon_dict:
    return icon_name

  try:
    icon_dict = dict((int(k), v) for k, v in icon_dict.items())
  except ValueError:
    print('The key of icon in the manifest file should be a number.')

  if len(icon_dict) > 0:
    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(app_dir, 'res', 'drawable-' + kd)
          if not os.path.exists(drawable_path):
            os.makedirs(drawable_path)
          icon = os.path.join(app_root, item[1])
          if icon and os.path.isfile(icon):
            icon_name = os.path.basename(icon)
            icon_suffix = icon_name.split('.')[-1]
            shutil.copyfile(icon, os.path.join(drawable_path,
                                               'icon.' + icon_suffix))
            icon_name = 'icon'
          elif icon and (not os.path.isfile(icon)):
            print('Error: "%s" does not exist.' % icon)
            sys.exit(6)
          break
  return icon_name
Esempio n. 4
0
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 template folder.')
        sys.exit(6)

    CustomizeStringXML(name, description)
    CustomizeThemeXML(name, app_info.fullscreen_flag, manifest)
    xmldoc = minidom.parse(manifest_path)
    EditElementAttribute(xmldoc, 'manifest', 'package', package)
    if app_versionCode:
        EditElementAttribute(xmldoc, 'manifest', 'android:versionCode',
                             str(app_versionCode))
    if app_version:
        EditElementAttribute(xmldoc, 'manifest', 'android:versionName',
                             app_version)
    if description:
        EditElementAttribute(xmldoc, 'manifest', 'android:description',
                             "@string/description")
    HandlePermissions(permissions, xmldoc)
    EditElementAttribute(xmldoc, 'application', 'android:label', app_name)
    activity_name = package + '.' + name + 'Activity'
    EditElementAttribute(xmldoc, 'activity', 'android:name', activity_name)
    EditElementAttribute(xmldoc, 'activity', 'android:label', app_name)
    if orientation:
        EditElementAttribute(xmldoc, 'activity', 'android:screenOrientation',
                             orientation)
    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(app_dir, 'AndroidManifest.xml'), 'w')
    xmldoc.writexml(file_handle, encoding='utf-8')
    file_handle.close()
Esempio n. 5
0
def CustomizeIconByOption(name, icon):
    if os.path.isfile(icon):
        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_file = ReplaceInvalidChars(icon_file)
        shutil.copyfile(icon, os.path.join(drawable_path, icon_file))
        icon_name = os.path.splitext(icon_file)[0]
        return icon_name
    else:
        print('Error: "%s" does not exist.' % icon)
        sys.exit(6)
Esempio n. 6
0
def CustomizeJava(app_info, app_url, app_local_path, keep_screen_on, manifest):
    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)
    if manifest:
        manifest_name = os.path.basename(manifest.input_path)
        manifest_in_asset = 'file:///android_asset/www/' + manifest_name
        load_from_manifest = 'loadAppFromManifest("' + manifest_in_asset + '")'
        ReplaceString(
            dest_activity,
            'loadAppFromUrl("file:///android_asset/www/index.html")',
            load_from_manifest)
    else:
        if app_url:
            if re.search(r'^http(|s)', app_url):
                ReplaceString(dest_activity,
                              'file:///android_asset/www/index.html', app_url)
        elif 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:
                print('Please make sure that the relative path of entry file'
                      ' is correct.')
                sys.exit(8)

    if app_info.remote_debugging:
        SetVariable(dest_activity,
                    'public void onCreate(Bundle savedInstanceState)',
                    'RemoteDebugging', 'true')
    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')
    if keep_screen_on:
        ReplaceString(
            dest_activity, 'super.onCreate(savedInstanceState);',
            'super.onCreate(savedInstanceState);\n        ' +
            'getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);'
        )
Esempio n. 7
0
def CustomizeStringXML(name, description):
    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 template folder.')
        sys.exit(6)

    if description:
        description = EncodingUnicodeValue(description)
        xmldoc = minidom.parse(strings_path)
        AddElementAttributeAndText(xmldoc, 'string', 'name', 'description',
                                   description)
        strings_file = open(strings_path, 'w')
        xmldoc.writexml(strings_file, encoding='utf-8')
        strings_file.close()
Esempio n. 8
0
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)
    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:
        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_assets_dir, compressor)
Esempio n. 9
0
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)

  theme_xmldoc = minidom.parse(theme_path)
  if fullscreen:
    EditElementValueByNodeName(theme_xmldoc, 'item',
                               'android:windowFullscreen', 'true')
  has_background = CustomizeLaunchScreen(manifest, name)
  if has_background:
    EditElementValueByNodeName(theme_xmldoc, 'item',
                               'android:windowBackground',
                               '@drawable/launchscreen_bg')
  theme_file = open(theme_path, 'w')
  theme_xmldoc.writexml(theme_file, encoding='utf-8')
  theme_file.close()
Esempio n. 10
0
def CustomizeManifest(app_info):
    app_versionCode = app_info.app_versionCode
    app_dir = GetBuildDir(app_info.android_name)
    app_name = EncodingUnicodeValue(app_info.app_name)
    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 template folder.')
        sys.exit(6)

    xmldoc = minidom.parse(manifest_path)
    if app_versionCode:
        EditElementAttribute(xmldoc, 'manifest', 'android:versionCode',
                             str(app_versionCode))
    EditElementAttribute(xmldoc, 'application', 'android:label', app_name)
    EditElementAttribute(xmldoc, 'activity', 'android:label', app_name)

    file_handle = open(os.path.join(app_dir, 'AndroidManifest.xml'), 'w')
    xmldoc.writexml(file_handle, encoding='utf-8')
    file_handle.close()
Esempio n. 11
0
def Execution(options, app_info):
    # Now we've got correct app_version and correct ABI value,
    # start to generate suitable versionCode
    app_info.app_versionCode = MakeVersionCode(options, app_info.app_version)
    # Write generated versionCode into AndroidManifest.xml.
    # Later if we have other customization,
    # we can put them together into CustomizeManifest func.
    CustomizeManifest(app_info)
    name = app_info.android_name

    arch_string = (' (' + options.arch + ')' if options.arch else '')
    print('\nStarting application build' + arch_string)
    app_dir = GetBuildDir(name)
    android_path = Which('android')
    api_level = GetAndroidApiLevel(android_path)
    target_string = 'android-%d' % api_level

    print(' * Checking keystore for signing')
    if options.keystore_path:
        key_store = os.path.expanduser(options.keystore_path)
        if options.keystore_alias:
            key_alias = options.keystore_alias
        else:
            print('Please provide an alias name of the developer key.')
            sys.exit(6)
        if options.keystore_passcode:
            key_code = options.keystore_passcode
        else:
            key_code = None
        if options.keystore_alias_passcode:
            key_alias_code = options.keystore_alias_passcode
        else:
            key_alias_code = None
    else:
        print('   No keystore provided for signing. Using xwalk\'s keystore '
              'for debugging.\n   Please use a valid keystore when '
              'distributing to the app market.')
        key_store = os.path.join(xwalk_dir, 'xwalk-debug.keystore')
        key_alias = 'xwalkdebugkey'
        key_code = 'xwalkdebug'
        key_alias_code = 'xwalkdebug'

    # Update android project for app and xwalk_core_library.
    update_project_cmd = [
        android_path, 'update', 'project', '--path', app_dir, '--target',
        target_string, '--name', name
    ]
    if options.mode == 'embedded':
        print(' * Updating project with xwalk_core_library')
        RunCommand([
            android_path, 'update', 'lib-project', '--path',
            os.path.join(app_dir, EMBEDDED_LIBRARY), '--target', target_string
        ])
        update_project_cmd.extend(['-l', EMBEDDED_LIBRARY])
    elif options.mode == 'shared':
        print(' * Updating project with xwalk_shared_library')
        RunCommand([
            android_path, 'update', 'lib-project', '--path',
            os.path.join(app_dir, SHARED_LIBRARY), '--target', target_string
        ])
        update_project_cmd.extend(['-l', SHARED_LIBRARY])
    else:
        print(' * Updating project')
    RunCommand(update_project_cmd)

    # Check whether external extensions are included.
    print(' * Checking for external extensions')
    extensions_string = 'xwalk-extensions'
    extensions_dir = os.path.join(app_dir, extensions_string)
    external_extension_jars = FindExtensionJars(extensions_dir)
    for external_extension_jar in external_extension_jars:
        shutil.copyfile(
            external_extension_jar,
            os.path.join(app_dir, 'libs',
                         os.path.basename(external_extension_jar)))

    if options.mode == 'embedded':
        print(' * Copying native libraries for %s' % options.arch)
        # Remove existing native libraries in xwalk_core_library, they are probably
        # for the last execution to make apk for another CPU arch.
        # And then copy the native libraries for the specified arch into
        # xwalk_core_library.
        arch = ConvertArchNameToArchFolder(options.arch)
        if not arch:
            print('Invalid CPU arch: %s.' % arch)
            sys.exit(10)
        library_lib_path = os.path.join(app_dir, EMBEDDED_LIBRARY, 'libs')
        for dir_name in os.listdir(library_lib_path):
            lib_dir = os.path.join(library_lib_path, dir_name)
            if ContainsNativeLibrary(lib_dir):
                shutil.rmtree(lib_dir)
        native_lib_path = os.path.join(app_dir, 'native_libs', arch)
        if ContainsNativeLibrary(native_lib_path):
            shutil.copytree(native_lib_path,
                            os.path.join(library_lib_path, arch))
        else:
            print(
                'No %s native library has been found for creating a Crosswalk '
                'embedded APK.' % arch)
            sys.exit(10)

    if options.project_only:
        print(' (Skipping apk package creation)')
        return

    # Build the APK
    if options.mode == 'embedded':
        print(' * Building Android apk package with Crosswalk embedded' +
              arch_string)
    else:
        print(' * Building Android apk package')
    ant_path = Which('ant')
    ant_cmd = [ant_path, 'release', '-f', os.path.join(app_dir, 'build.xml')]
    ant_cmd.extend(['-Dkey.store=%s' % os.path.abspath(key_store)])
    ant_cmd.extend(['-Dkey.alias=%s' % key_alias])
    if key_code:
        ant_cmd.extend(['-Dkey.store.password=%s' % key_code])
    if key_alias_code:
        ant_cmd.extend(['-Dkey.alias.password=%s' % key_alias_code])

    cmd_display = ' '.join([str(item) for item in ant_cmd])
    if options.verbose:
        print('Executing:\n %s\n' % cmd_display)
    else:
        ant_cmd.extend(['-quiet'])
    ant_result = subprocess.call(ant_cmd)
    if ant_result != 0:
        print('Command "%s" exited with non-zero exit code %d' %
              (cmd_display, ant_result))
        sys.exit(ant_result)

    src_file = os.path.join(app_dir, 'bin', '%s-release.apk' % name)
    package_name = name
    if options.app_version:
        package_name += ('_' + options.app_version)
    if options.mode == 'shared':
        dst_file = os.path.join(options.target_dir, '%s.apk' % package_name)
    elif options.mode == 'embedded':
        dst_file = os.path.join(options.target_dir,
                                '%s_%s.apk' % (package_name, options.arch))
    shutil.copyfile(src_file, dst_file)
    print(' (Location: %s)' % dst_file)
Esempio n. 12
0
def main():
    parser = optparse.OptionParser()
    info = ('The package name. Such as: ' '--package=com.example.YourPackage')
    parser.add_option('--package', help=info)
    info = ('The apk name. Such as: --name="Your Application Name"')
    parser.add_option('--name', help=info)
    info = ('The version of the app. Such as: --app-version=TheVersionNumber')
    parser.add_option('--app-version', help=info)
    info = ('The versionCode of the app. Such as: --app-versionCode=24')
    parser.add_option('--app-versionCode', type='int', help=info)
    info = ('The application description. Such as:'
            '--description=YourApplicationdDescription')
    parser.add_option('--description', help=info)
    info = ('The permission list. Such as: --permissions="geolocation"'
            'For more permissions, such as:'
            '--permissions="geolocation:permission2"')
    parser.add_option('--permissions', help=info)
    info = ('The url of application. '
            'This flag allows to package website as apk. Such as: '
            '--app-url=http://www.intel.com')
    parser.add_option('--app-url', help=info)
    info = ('The root path of the web app. '
            'This flag allows to package local web app as apk. Such as: '
            '--app-root=/root/path/of/the/web/app')
    parser.add_option('--app-root', help=info)
    info = ('The reletive path of entry file based on |app_root|. '
            'This flag should work with "--app-root" together. '
            'Such as: --app-local-path=/reletive/path/of/entry/file')
    parser.add_option('--app-local-path', help=info)
    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.')
    parser.add_option('--keep-screen-on',
                      action='store_true',
                      default=False,
                      help='Support keeping screen on')
    info = ('The path list for external extensions separated by os separator.'
            'On Linux and Mac, the separator is ":". On Windows, it is ";".'
            'Such as: --extensions="/path/to/extension1:/path/to/extension2"')
    parser.add_option('--extensions', help=info)
    info = (
        'The orientation of the web app\'s display on the device. '
        'Such as: --orientation=landscape. The default value is "unspecified"'
        'The value options are the same as those on the Android: '
        'http://developer.android.com/guide/topics/manifest/'
        'activity-element.html#screen')
    parser.add_option('--orientation', help=info)
    parser.add_option('--manifest', help='The manifest path')
    info = (
        'Use command lines.'
        '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.'
            '--compressor=js: compress javascript.'
            '--compressor=css: compress css.')
    parser.add_option('--compressor',
                      dest='compressor',
                      action='callback',
                      callback=ParseParameterForCompressor,
                      type='string',
                      nargs=0,
                      help=info)
    options, _ = parser.parse_args()
    try:
        icon_dict = {
            144: 'icons/icon_144.png',
            72: 'icons/icon_72.png',
            96: 'icons/icon_96.png',
            48: 'icons/icon_48.png'
        }
        app_info = AppInfo()
        if options.name is not None:
            app_info.android_name = options.name
        if options.app_root is None:
            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
Esempio n. 13
0
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:
    myextension/
      myextension.jar
      myextension.js
      myextension.json
  That means the name of the internal files should be the same as the
  directory name.
  For .jar files, they'll be copied to xwalk-extensions/ and then
  built into classes.dex in make_apk.py.
  For .js files, they'll be copied into assets/xwalk-extensions/.
  For .json files, the'll be merged into one file called
  extensions-config.json and copied into assets/.
  """
    if not extensions:
        return
    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(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)
    apk_extensions_json_path = os.path.join(apk_assets_path,
                                            'extensions-config.json')

    # Split the paths into a list.
    extension_paths = extensions.split(os.pathsep)
    extension_json_list = []
    for source_path in extension_paths:
        if not os.path.exists(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)
        extension_name = os.path.basename(source_path)

        # Copy .jar file into xwalk-extensions.
        CopyExtensionFile(extension_name, '.jar', source_path, dest_jar_path)

        # Copy .js file into assets/xwalk-extensions.
        CopyExtensionFile(extension_name, '.js', source_path, dest_js_path)

        # Merge .json file into assets/xwalk-extensions.
        file_name = extension_name + '.json'
        src_file = os.path.join(source_path, file_name)
        if not os.path.isfile(src_file):
            print('Error: %s was not found in %s.' % (file_name, source_path))
            sys.exit(9)
        else:
            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.
            # And 'permissions' will be merged.
            if not ('name' in json_output and 'class' in json_output
                    and 'jsapi' in json_output):
                print(
                    'Error: properties \'name\', \'class\' and \'jsapi\' in a json '
                    'file are mandatory.')
                sys.exit(9)
            # Reset the path for JavaScript.
            js_path_prefix = extensions_string + '/' + extension_name + '/'
            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(app_dir, 'AndroidManifest.xml')
            xmldoc = minidom.parse(manifest_path)
            if ('permissions' in json_output):
                # Get used permission list to avoid repetition as "--permissions"
                # option can also be used to declare used permissions.
                existingList = []
                usedPermissions = xmldoc.getElementsByTagName(
                    "uses-permission")
                for used in usedPermissions:
                    existingList.append(used.getAttribute("android:name"))

                # Add the permissions to manifest file if not used yet.
                for p in json_output['permissions']:
                    if p in existingList:
                        continue
                    AddElementAttribute(xmldoc, 'uses-permission',
                                        'android:name', p)
                    existingList.append(p)

                # Write to the manifest file to save the update.
                file_handle = open(manifest_path, 'w')
                xmldoc.writexml(file_handle, encoding='utf-8')
                file_handle.close()
            if 'manifest' in json_output:
                manifest_merge_path = os.path.join(source_path,
                                                   json_output['manifest'])
                if not os.path.isfile(manifest_merge_path):
                    print('Error: %s specified in the extension\'s JSON '
                          'could not be found.' % manifest_merge_path)
                    sys.exit(9)
                xmldoc_merge = minidom.parse(manifest_merge_path)
                manifest_nodes = xmldoc.getElementsByTagName('manifest')
                manifest_nodes_merge = xmldoc_merge.getElementsByTagName(
                    'manifest')
                if not manifest_nodes:
                    print('Error: %s does not have a <manifest> node.' %
                          manifest_path)
                    sys.exit(9)
                if not manifest_nodes_merge:
                    print('Error: %s does not have a <manifest> node.' %
                          manifest_merge_path)
                    sys.exit(9)
                MergeNodes(manifest_nodes[0], manifest_nodes_merge[0])
                with open(manifest_path, 'w') as file_handle:
                    xmldoc.writexml(file_handle, encoding='utf-8')

    # Write configuration of extensions into the target extensions-config.json.
    if extension_json_list:
        extensions_string = json.JSONEncoder().encode(extension_json_list)
        extension_json_file = open(apk_extensions_json_path, 'w')
        extension_json_file.write(extensions_string)
        extension_json_file.close()
Esempio n. 14
0
def Execution(options, app_info):
  # Now we've got correct app_version and correct ABI value,
  # start to generate suitable versionCode
  app_info.app_versionCode = MakeVersionCode(options, app_info.app_version)
  # Write generated versionCode into AndroidManifest.xml.
  # Later if we have other customization,
  # we can put them together into CustomizeManifest func.
  CustomizeManifest(app_info)
  name = app_info.android_name

  arch_string = (' ('+options.arch+')' if options.arch else '')
  print('\nStarting application build' + arch_string)
  app_dir = GetBuildDir(name)
  android_path = Which('android')
  api_level = GetAndroidApiLevel(android_path)
  target_string = 'android-%d' % api_level

  print (' * Checking keystore for signing')
  if options.keystore_path:
    key_store = os.path.expanduser(options.keystore_path)
    if options.keystore_alias:
      key_alias = options.keystore_alias
    else:
      print('Please provide an alias name of the developer key.')
      sys.exit(6)
    if options.keystore_passcode:
      key_code = options.keystore_passcode
    else:
      key_code = None
    if options.keystore_alias_passcode:
      key_alias_code = options.keystore_alias_passcode
    else:
      key_alias_code = None
  else:
    print('   No keystore provided for signing. Using xwalk\'s keystore '
          'for debugging.\n   Please use a valid keystore when '
          'distributing to the app market.')
    key_store = os.path.join(xwalk_dir, 'xwalk-debug.keystore')
    key_alias = 'xwalkdebugkey'
    key_code = 'xwalkdebug'
    key_alias_code = 'xwalkdebug'

  # Update android project for app and xwalk_core_library.
  update_project_cmd = [android_path, 'update', 'project',
                        '--path', app_dir,
                        '--target', target_string,
                        '--name', name]
  if options.mode == 'embedded':
    print(' * Updating project with xwalk_core_library')
    RunCommand([android_path, 'update', 'lib-project',
                '--path', os.path.join(app_dir, EMBEDDED_LIBRARY),
                '--target', target_string])
    update_project_cmd.extend(['-l', EMBEDDED_LIBRARY])
  elif options.mode == 'shared':
    print(' * Updating project with xwalk_shared_library')
    RunCommand([android_path, 'update', 'lib-project',
                '--path', os.path.join(app_dir, SHARED_LIBRARY),
                '--target', target_string])
    update_project_cmd.extend(['-l', SHARED_LIBRARY])
  else:
    print(' * Updating project')
  RunCommand(update_project_cmd)

  # Enable proguard
  if options.mode == 'embedded' and options.enable_proguard:
    print(' * Enabling proguard config files')
    # Enable proguard in project.properies.
    if not os.path.exists(os.path.join(app_dir, 'project.properties')):
      print('Error, project.properties file not found!')
      sys.exit(14)
    file_prop = file(os.path.join(app_dir, 'project.properties'), 'a')
    file_prop.write('proguard.config=${sdk.dir}/tools/proguard/'
                    'proguard-android.txt:proguard-xwalk.txt')
    file_prop.close()

    # Add proguard cfg file.
    if not os.path.exists(os.path.join(app_dir, 'proguard-xwalk.txt')):
      print('Error, proguard config file for Crosswalk not found!')
      sys.exit(14)

  # Check whether external extensions are included.
  print(' * Checking for external extensions')
  extensions_string = 'xwalk-extensions'
  extensions_dir = os.path.join(app_dir, extensions_string)
  external_extension_jars = FindExtensionJars(extensions_dir)
  for external_extension_jar in external_extension_jars:
    shutil.copyfile(external_extension_jar,
                    os.path.join(app_dir, 'libs',
                                 os.path.basename(external_extension_jar)))

  if options.mode == 'embedded':
    print (' * Copying native libraries for %s' % options.arch)
    # Remove existing native libraries in xwalk_core_library, they are probably
    # for the last execution to make apk for another CPU arch.
    # And then copy the native libraries for the specified arch into
    # xwalk_core_library.
    arch = ConvertArchNameToArchFolder(options.arch)
    if not arch:
      print ('Invalid CPU arch: %s.' % arch)
      sys.exit(10)

    native_path = os.path.join(app_dir, 'native_libs', arch)
    library_path = os.path.join(app_dir, EMBEDDED_LIBRARY, 'libs')
    raw_path = os.path.join(app_dir, EMBEDDED_LIBRARY, 'res', 'raw')

    if options.enable_lzma:
      contains_library = ContainsCompressedLibrary
      clean_library = CleanCompressedLibrary
      copy_library = CopyCompressedLibrary
    else:
      contains_library = ContainsNativeLibrary
      clean_library = CleanNativeLibrary
      copy_library = CopyNativeLibrary

    # cleanup previous build's library first.
    for dir_name in os.listdir(library_path):
      clean_library(library_path, dir_name)

    if options.native_extensions:
      CheckValidationOfExpectedLibraryArch(options.native_extensions,
                                           options.arch)
      CopyNativeExtensionFile('.so', os.path.join(options.native_extensions,
                                                  arch),
                              native_path)

    if contains_library(native_path):
      copy_library(native_path, library_path, raw_path, arch)
    else:
      print('No %s native library has been found for creating a Crosswalk '
            'embedded APK.' % arch)
      sys.exit(10)
  else:
    if options.native_extensions:
      for arch_name in ALL_ARCHITECTURES:
        arch = ConvertArchNameToArchFolder(arch_name)
        extension_path = os.path.join(app_dir, SHARED_LIBRARY, 'libs', arch)
        library_path = os.path.join(options.native_extensions, arch)
        CheckValidationOfExpectedLibraryArch(library_path, arch_name)
        os.mkdir(extension_path)
        CopyNativeExtensionFile('.so', os.path.join(options.native_extensions,
                                                    arch),
                                extension_path)

  if options.project_only:
    print (' (Skipping apk package creation)')
    return

  # Build the APK
  if options.mode == 'embedded':
    print(' * Building Android apk package with Crosswalk embedded' +
          arch_string)
  else:
    print(' * Building Android apk package')
  ant_path = Which('ant')
  ant_cmd = [ant_path, 'release', '-f', os.path.join(app_dir, 'build.xml')]
  ant_cmd.extend(['-Dkey.store=%s' % os.path.abspath(key_store)])
  ant_cmd.extend(['-Dkey.alias=%s' % key_alias])
  if key_code:
    ant_cmd.extend(['-Dkey.store.password=%s' % key_code])
  if key_alias_code:
    ant_cmd.extend(['-Dkey.alias.password=%s' % key_alias_code])
  ignore_properties = "!.svn:!.git:.*:!CVS:!thumbs.db:!picasa.ini:!*.scc:*~"
  ant_cmd.extend(['-Daapt.ignore.assets=%s' % ignore_properties])

  cmd_display = ' '.join([str(item) for item in ant_cmd])
  if options.verbose:
    print('Executing:\n %s\n' % cmd_display)
  else:
    ant_cmd.extend(['-quiet'])
  ant_result = subprocess.call(ant_cmd)
  if ant_result != 0:
    print('Command "%s" exited with non-zero exit code %d'
          % (cmd_display, ant_result))
    sys.exit(ant_result)

  src_file = os.path.join(app_dir, 'bin', '%s-release.apk' % name)
  package_name = name
  if options.app_version:
    package_name += ('_' + options.app_version)
  if options.mode == 'shared':
    dst_file = os.path.join(options.target_dir, '%s.apk' % package_name)
  elif options.mode == 'embedded':
    dst_file = os.path.join(options.target_dir,
                            '%s_%s.apk' % (package_name, options.arch))
  shutil.copyfile(src_file, dst_file)
  print(' (Location: %s)' % dst_file)

  #Copy proguard dumping files
  if options.mode == 'embedded' and options.enable_proguard \
      and not options.project_dir:
    proguard_dir = os.path.join(app_dir, 'bin/proguard/')
    if os.path.exists(proguard_dir):
      for afile in os.listdir(proguard_dir):
        if afile.endswith('.txt'):
          shutil.copy(os.path.join(proguard_dir,afile), xwalk_dir)
    else:
      print('Warning:Cannot find proguard dumping directory!')
Esempio n. 15
0
def MakeApk(options, app_info, manifest):
    CheckSystemRequirements()
    Customize(options, app_info, manifest)
    name = app_info.android_name
    app_dir = GetBuildDir(name)
    packaged_archs = []
    if options.mode == 'shared':
        # Copy xwalk_shared_library into app folder
        target_library_path = os.path.join(app_dir, SHARED_LIBRARY)
        shutil.copytree(os.path.join(xwalk_dir, SHARED_LIBRARY),
                        target_library_path)
        Execution(options, app_info)
    elif options.mode == 'embedded':
        # Copy xwalk_core_library into app folder and move the native libraries
        # out.
        # When making apk for specified CPU arch, will only include the
        # corresponding native library by copying it back into xwalk_core_library.
        target_library_path = os.path.join(app_dir, EMBEDDED_LIBRARY)
        shutil.copytree(os.path.join(xwalk_dir, EMBEDDED_LIBRARY),
                        target_library_path)
        library_lib_path = os.path.join(target_library_path, 'libs')
        native_lib_path = os.path.join(app_dir, 'native_libs')
        os.makedirs(native_lib_path)
        available_archs = []
        for dir_name in os.listdir(library_lib_path):
            lib_dir = os.path.join(library_lib_path, dir_name)
            if ContainsNativeLibrary(lib_dir):
                shutil.move(lib_dir, os.path.join(native_lib_path, dir_name))
                available_archs.append(dir_name)
        if options.arch:
            Execution(options, app_info)
            packaged_archs.append(options.arch)
        else:
            # If the arch option is unspecified, all of available platform APKs
            # will be generated.
            for arch in AllArchitectures():
                if ConvertArchNameToArchFolder(arch) in available_archs:
                    options.arch = arch
                    Execution(options, app_info)
                    packaged_archs.append(options.arch)
                else:
                    print('Warning: failed to create package for arch "%s" '
                          'due to missing native library' % arch)
            if len(packaged_archs) == 0:
                print('No packages created, aborting')
                sys.exit(13)

    # if project_dir, save build directory
    if options.project_dir:
        print('\nCreating project directory')
        save_dir = os.path.join(options.project_dir, name)
        if CreateAndCopyDir(app_dir, save_dir, True):
            print('  A project directory was created successfully in:\n   %s' %
                  os.path.abspath(save_dir))
            print('  To manually generate an APK, run the following in that '
                  'directory:')
            print('   ant release -f build.xml')
            print('  For more information, see:\n'
                  '   http://developer.android.com/tools/building/'
                  'building-cmdline.html')
        else:
            print(
                'Error: Unable to create a project directory during the build. '
                'Please check the directory passed in --project-dir, '
                'available disk space, and write permission.')

    if not options.project_only:
        PrintPackageInfo(options, name, packaged_archs)
Esempio n. 16
0
def main(argv):
    parser = optparse.OptionParser()
    parser.add_option('-v',
                      '--version',
                      action='store_true',
                      dest='version',
                      default=False,
                      help='The version of this python tool.')
    parser.add_option('--verbose',
                      action="store_true",
                      dest='verbose',
                      default=False,
                      help='Print debug messages.')
    info = (
        'The packaging mode of the web application. The value \'shared\' '
        'means that the runtime is shared across multiple application '
        'instances and that the runtime needs to be distributed separately. '
        'The value \'embedded\' means that the runtime is embedded into the '
        'application itself and distributed along with it.'
        'Set the default mode as \'embedded\'. For example: --mode=embedded')
    parser.add_option('--mode',
                      choices=('embedded', 'shared'),
                      default='embedded',
                      help=info)
    info = (
        'The target architecture of the embedded runtime. Supported values: '
        '%s. If not specified, APKs for all available architectures will be '
        'generated.' % ', '.join(AllArchitectures()))
    parser.add_option('--arch', choices=AllArchitectures(), help=info)
    group = optparse.OptionGroup(
        parser, 'Application Source Options',
        'This packaging tool supports 3 kinds of web application source: '
        '1) XPK package; 2) manifest.json; 3) various command line options, '
        'for example, \'--app-url\' for website, \'--app-root\' and '
        '\'--app-local-path\' for local web application.')
    info = (
        'The path of the XPK package. For example, --xpk=/path/to/xpk/file')
    group.add_option('--xpk', help=info)
    info = (
        'The manifest file with the detail description of the application. '
        'For example, --manifest=/path/to/your/manifest/file')
    group.add_option('--manifest', help=info)
    info = ('The url of application. '
            'This flag allows to package website as apk. For example, '
            '--app-url=http://www.intel.com')
    group.add_option('--app-url', help=info)
    info = ('The root path of the web app. '
            'This flag allows to package local web app as apk. For example, '
            '--app-root=/root/path/of/the/web/app')
    group.add_option('--app-root', help=info)
    info = (
        'The relative path of entry file based on the value from '
        '\'app_root\'. This flag should work with \'--app-root\' together. '
        'For example, --app-local-path=/relative/path/of/entry/file')
    group.add_option('--app-local-path', help=info)
    parser.add_option_group(group)
    # Mandatory options group
    group = optparse.OptionGroup(
        parser, 'Mandatory arguments',
        'They are used for describing the APK information through '
        'command line options.')
    info = ('The apk name. For example, --name="Your Application Name"')
    group.add_option('--name', help=info)
    info = ('The package name. For example, '
            '--package=com.example.YourPackage')
    group.add_option('--package', help=info)
    parser.add_option_group(group)
    # Optional options group (alphabetical)
    group = optparse.OptionGroup(
        parser, 'Optional arguments',
        'They are used for various settings for applications through '
        'command line options.')
    info = ('The version name of the application. '
            'For example, --app-version=1.0.0')
    group.add_option('--app-version', help=info)
    info = ('The version code of the application. '
            'For example, --app-versionCode=24')
    group.add_option('--app-versionCode', type='int', help=info)
    info = ('The version code base of the application. Version code will '
            'be made by adding a prefix based on architecture to the version '
            'code base. For example, --app-versionCodeBase=24')
    group.add_option('--app-versionCodeBase', type='int', help=info)
    info = ('The description of the application. For example, '
            '--description=YourApplicationDescription')
    group.add_option('--description', help=info)
    group.add_option('--enable-remote-debugging',
                     action='store_true',
                     dest='enable_remote_debugging',
                     default=False,
                     help='Enable remote debugging.')
    group.add_option('--use-animatable-view',
                     action='store_true',
                     dest='use_animatable_view',
                     default=False,
                     help='Enable using animatable view (TextureView).')
    info = ('The list of external extension paths splitted by OS separators. '
            'The separators are \':\' , \';\' and \':\' on Linux, Windows and '
            'Mac OS respectively. For example, '
            '--extensions=/path/to/extension1:/path/to/extension2.')
    group.add_option('--extensions', help=info)
    group.add_option('-f',
                     '--fullscreen',
                     action='store_true',
                     dest='fullscreen',
                     default=False,
                     help='Make application fullscreen.')
    group.add_option('--keep-screen-on',
                     action='store_true',
                     default=False,
                     help='Support keeping screen on')
    info = ('The path of application icon. '
            'Such as: --icon=/path/to/your/customized/icon')
    group.add_option('--icon', help=info)
    info = ('The orientation of the web app\'s display on the device. '
            'For example, --orientation=landscape. The default value is '
            '\'unspecified\'. The permitted values are from Android: '
            'http://developer.android.com/guide/topics/manifest/'
            'activity-element.html#screen')
    group.add_option('--orientation', help=info)
    info = (
        'The list of permissions to be used by web application. For example, '
        '--permissions=geolocation:webgl')
    group.add_option('--permissions', help=info)
    info = (
        'Create an Android project directory with Crosswalk at this location.'
        ' (See project-only option below)')
    group.add_option('--project-dir', help=info)
    info = ('Must be used with project-dir option. Create an Android project '
            'directory with Crosswalk but do not build the APK package')
    group.add_option('--project-only',
                     action='store_true',
                     default=False,
                     dest='project_only',
                     help=info)
    info = ('Packaging tool will move the output APKs to the target directory')
    group.add_option('--target-dir', default=os.getcwd(), help=info)
    info = (
        'Use command lines.'
        'Crosswalk is powered by Chromium and supports Chromium command line.'
        'For example, '
        '--xwalk-command-line=\'--chromium-command-1 --xwalk-command-2\'')
    group.add_option('--xwalk-command-line', default='', help=info)
    parser.add_option_group(group)
    # Keystore options group
    group = optparse.OptionGroup(
        parser, 'Keystore Options',
        'The keystore is a signature from web developer, it\'s used when '
        'developer wants to distribute the applications.')
    info = ('The path to the developer keystore. For example, '
            '--keystore-path=/path/to/your/developer/keystore')
    group.add_option('--keystore-path', help=info)
    info = ('The alias name of keystore. For example, --keystore-alias=name')
    group.add_option('--keystore-alias', help=info)
    info = ('The passcode of keystore. For example, --keystore-passcode=code')
    group.add_option('--keystore-passcode', help=info)
    info = ('Passcode for alias\'s private key in the keystore, '
            'For example, --keystore-alias-passcode=alias-code')
    group.add_option('--keystore-alias-passcode', help=info)
    info = ('Minify and obfuscate javascript and css.'
            '--compressor: compress javascript and css.'
            '--compressor=js: compress javascript.'
            '--compressor=css: compress css.')
    group.add_option('--compressor',
                     dest='compressor',
                     action='callback',
                     callback=ParseParameterForCompressor,
                     type='string',
                     nargs=0,
                     help=info)
    parser.add_option_group(group)
    options, _ = parser.parse_args()
    if len(argv) == 1:
        parser.print_help()
        return 0

    if options.version:
        if os.path.isfile('VERSION'):
            print(GetVersion('VERSION'))
            return 0
        else:
            parser.error(
                'VERSION was not found, so Crosswalk\'s version could not '
                'be determined.')

    xpk_temp_dir = ''
    if options.xpk:
        xpk_name = os.path.splitext(os.path.basename(options.xpk))[0]
        xpk_temp_dir = tempfile.mkdtemp(prefix="%s-" % xpk_name + '_xpk')
        CleanDir(xpk_temp_dir)
        ParseXPK(options, xpk_temp_dir)

    if options.manifest:
        options.manifest = os.path.abspath(options.manifest)
        if not os.path.isfile(options.manifest):
            print('Error: The manifest file does not exist.')
            sys.exit(8)

    if options.app_root and not options.manifest:
        manifest_path = os.path.join(options.app_root, 'manifest.json')
        if os.path.exists(manifest_path):
            print('Using manifest.json distributed with the application.')
            options.manifest = manifest_path

    app_info = AppInfo()
    manifest = None
    if not options.manifest:
        # The checks here are really convoluted, but at the moment make_apk
        # misbehaves any of the following conditions is true.
        if options.app_url:
            # 1) --app-url must be passed without either --app-local-path or
            #    --app-root.
            if options.app_root or options.app_local_path:
                parser.error(
                    'You must pass either "--app-url" or "--app-local-path" '
                    'with "--app-root", but not all.')
        else:
            # 2) --app-url is not passed but only one of --app-local-path and
            #    --app-root is set.
            if bool(options.app_root) != bool(options.app_local_path):
                parser.error('You must specify both "--app-local-path" and '
                             '"--app-root".')
            # 3) None of --app-url, --app-local-path and --app-root are passed.
            elif not options.app_root and not options.app_local_path:
                parser.error(
                    'You must pass either "--app-url" or "--app-local-path" '
                    'with "--app-root".')

        if options.permissions:
            permission_list = options.permissions.split(':')
        else:
            print(
                'Warning: all supported permissions on Android port are added. '
                'Refer to https://github.com/crosswalk-project/'
                'crosswalk-website/wiki/Crosswalk-manifest')
            permission_list = permission_mapping_table.keys()
        options.permissions = HandlePermissionList(permission_list)
        options.icon_dict = {}
    else:
        try:
            manifest = ParseManifest(options)
        except SystemExit as ec:
            return ec.code

    if not options.name:
        parser.error(
            'An APK name is required. Please use the "--name" option.')

    if not options.package:
        parser.error('A package name is required. Please use the "--package" '
                     'option.')
    VerifyPackageName(options.package)

    if (options.app_root and options.app_local_path and not os.path.isfile(
            os.path.join(options.app_root, options.app_local_path))):
        print('Please make sure that the local path file of launching app '
              'does exist.')
        sys.exit(7)

    if options.target_dir:
        target_dir = os.path.abspath(os.path.expanduser(options.target_dir))
        options.target_dir = target_dir
        if not os.path.isdir(target_dir):
            os.makedirs(target_dir)

    if options.project_only and not options.project_dir:
        print('\nmake_apk.py error: Option --project-only must be used '
              'with --project-dir')
        sys.exit(8)

    try:
        MakeApk(options, app_info, manifest)
    except SystemExit as ec:
        return ec.code
    finally:
        CleanDir(GetBuildDir(app_info.android_name))
        CleanDir(xpk_temp_dir)
    return 0
Esempio n. 17
0
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 template folder.')
        sys.exit(6)

    CustomizeStringXML(name, description)
    CustomizeThemeXML(name, app_info.fullscreen_flag, manifest)
    xmldoc = minidom.parse(manifest_path)
    EditElementAttribute(xmldoc, 'manifest', 'package', package)
    if app_versionCode:
        EditElementAttribute(xmldoc, 'manifest', 'android:versionCode',
                             str(app_versionCode))
    if app_version:
        EditElementAttribute(xmldoc, 'manifest', 'android:versionName',
                             app_version)
    if description:
        EditElementAttribute(xmldoc, 'manifest', 'android:description',
                             "@string/description")
    HandlePermissions(permissions, xmldoc)
    if app_info.mode == 'download':
        AddElementAttribute(
            xmldoc, 'uses-permission', 'android:name',
            'android.permission.DOWNLOAD_WITHOUT_NOTIFICATION')
    EditElementAttribute(xmldoc, 'application', 'android:label', app_name)
    activity_name = package + '.' + name + 'Activity'
    EditElementAttribute(xmldoc, 'activity', 'android:name', activity_name)
    EditElementAttribute(xmldoc, 'activity', 'android:label', app_name)
    if orientation:
        EditElementAttribute(xmldoc, 'activity', 'android:screenOrientation',
                             orientation)
    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)

    if app_info.xwalk_apk_url:
        meta_data = xmldoc.createElement('meta-data')
        meta_data.setAttribute('android:name', 'xwalk_apk_url')
        meta_data.setAttribute('android:value', app_info.xwalk_apk_url)
        app_node = xmldoc.getElementsByTagName('application')[0]
        comment = 'The download URL of Crosswalk runtime library APK. \n\
        Default updater use the Android download manager to fetch the url'

        app_node.appendChild(xmldoc.createComment(comment))
        app_node.appendChild(meta_data)

    if app_info.mode == 'download':
        meta_data = xmldoc.createElement('meta-data')
        meta_data.setAttribute('android:name', 'xwalk_enable_download_mode')
        meta_data.setAttribute('android:value', 'enable')
        comment = 'Make application run in silent download mode.'
        app_node.appendChild(xmldoc.createComment(comment))
        app_node.appendChild(meta_data)

    file_handle = open(os.path.join(app_dir, 'AndroidManifest.xml'), 'w')
    xmldoc.writexml(file_handle, encoding='utf-8')
    file_handle.close()