/support/android/builder.py
Python | 2648 lines | 2435 code | 132 blank | 81 comment | 233 complexity | cac4effa378a19aae6a2aed15fafadbf MD5 | raw file
Possible License(s): MIT, JSON, Apache-2.0, 0BSD, CC-BY-SA-3.0, BSD-2-Clause, MPL-2.0-no-copyleft-exception, BSD-3-Clause, CC-BY-3.0, Unlicense
Large files files are truncated, but you can click here to view the full file
- #!/usr/bin/env python
- # -*- coding: utf-8 -*-
- #
- # Appcelerator Titanium Mobile
- # Copyright (c) 2011-2012 by Appcelerator, Inc. All Rights Reserved.
- # Licensed under the terms of the Apache Public License
- # Please see the LICENSE included with this distribution for details.
- #
- # General builder script for staging, packaging, deploying,
- # and debugging Titanium Mobile applications on Android
- #
- import os, sys, subprocess, shutil, time, signal, string, platform, re, glob, hashlib, imp, inspect
- import run, avd, prereq, zipfile, tempfile, fnmatch, codecs, traceback, sgmllib
- from os.path import splitext
- from compiler import Compiler
- from os.path import join, splitext, split, exists
- from shutil import copyfile
- from xml.dom.minidom import parseString
- from tilogger import *
- from datetime import datetime, timedelta
- reload(sys) # this is required to prevent the following error: "AttributeError: 'module' object has no attribute 'setdefaultencoding'"
- sys.setdefaultencoding("utf_8") # Fix umlaut issues
- template_dir = os.path.abspath(os.path.dirname(sys._getframe(0).f_code.co_filename))
- top_support_dir = os.path.dirname(template_dir)
- sys.path.append(top_support_dir)
- sys.path.append(os.path.join(top_support_dir, 'common'))
- sys.path.append(os.path.join(top_support_dir, 'module'))
- import simplejson, java
- from mako.template import Template
- from tiapp import *
- from android import Android
- from androidsdk import AndroidSDK
- from deltafy import Deltafy, Delta
- from css import csscompiler
- from module import ModuleDetector
- import localecompiler
- import fastdev
- import requireIndex
- resourceFiles = ['strings.xml', 'attrs.xml', 'styles.xml', 'bools.xml', 'colors.xml',
- 'dimens.xml', 'ids.xml', 'integers.xml', 'arrays.xml']
- ignoreFiles = ['.gitignore', '.cvsignore', '.DS_Store'];
- ignoreDirs = ['.git','.svn','_svn', 'CVS'];
- android_avd_hw = {'hw.camera': 'yes', 'hw.gps':'yes'}
- res_skips = ['style']
- log = None
- # Copied from frameworks/base/tools/aapt/Package.cpp
- uncompressed_types = [
- ".jpg", ".jpeg", ".png", ".gif",
- ".wav", ".mp2", ".mp3", ".ogg", ".aac",
- ".mpg", ".mpeg", ".mid", ".midi", ".smf", ".jet",
- ".rtttl", ".imy", ".xmf", ".mp4", ".m4a",
- ".m4v", ".3gp", ".3gpp", ".3g2", ".3gpp2",
- ".amr", ".awb", ".wma", ".wmv"
- ]
- # Java keywords to reference in case app id contains java keyword
- java_keywords = [
- "abstract", "continue", "for", "new", "switch",
- "assert", "default", "goto", "package", "synchronized",
- "boolean", "do", "if", "private", "this",
- "break", "double", "implements", "protected", "throw",
- "byte", "else", "import", "public", "throws",
- "case", "enum", "instanceof", "return", "transient",
- "catch", "extends", "int", "short", "try",
- "char", "final", "interface", "static", "void",
- "class", "finally", "long", "strictfp", "volatile",
- "const", "float", "native", "super", "while",
- "true", "false", "null"
- ]
- MIN_API_LEVEL = 10
- HONEYCOMB_MR2_LEVEL = 13
- KNOWN_ABIS = ("armeabi-v7a", "x86")
- # Used only to find <script> tags in HTML files
- # so we can be sure to package referenced JS files
- # even when compiling for production. (See
- # Builder.package_and_deploy later in this file.)
- class HTMLParser(sgmllib.SGMLParser):
- def parse(self, html_source):
- self.feed(html_source)
- self.close()
- def __init__(self, verbose=0):
- sgmllib.SGMLParser.__init__(self, verbose)
- self.referenced_js_files = []
- def start_script(self, attributes):
- for name, value in attributes:
- if value and name.lower() == "src":
- self.referenced_js_files.append(value.lower())
- def get_referenced_js_files(self):
- return self.referenced_js_files
- def launch_logcat():
- valid_device_switches = ('-e', '-d', '-s')
- device_id = None
- android_sdk_location = None
- adb_location = None
- logcat_process = None
- device_switch = None # e.g., -e or -d or -s
- def show_usage():
- print >> sys.stderr, ""
- print >> sys.stderr, "%s devicelog <sdk_dir> <device_switch> [device_serial_number]" % os.path.basename(sys.argv[0])
- print >> sys.stderr, ""
- print >> sys.stderr, "The <device_switch> can be -e, -d -s. If -s, also pass serial number."
- sys.exit(1)
- if len(sys.argv) < 3:
- print >> sys.stderr, "Missing Android SDK location."
- show_usage()
- else:
- android_sdk_location = os.path.abspath(os.path.expanduser(sys.argv[2]))
- adb_location = AndroidSDK(android_sdk_location).get_adb()
- if len(sys.argv) < 4:
- print >> sys.stderr, "Missing device/emulator switch (e.g., -e, -d, -s)."
- show_usage()
- device_switch = sys.argv[3]
- if device_switch not in valid_device_switches:
- print >> sys.stderr, "Unknown device type switch: %s" % device_switch
- show_usage()
- if device_switch == "-s":
- if len(sys.argv) < 5:
- print >> sys.stderr, "Must specify serial number when using -s."
- show_usage()
- else:
- device_id = sys.argv[4]
- # For killing the logcat process if our process gets killed.
- def signal_handler(signum, frame):
- print "[DEBUG] Signal %s received. Terminating the logcat process." % signum
- if logcat_process is not None:
- if platform.system() == "Windows":
- os.system("taskkill /F /T /PID %i" % logcat_process.pid)
- else:
- os.kill(logcat_process.pid, signal.SIGTERM)
- # make sure adb is running on windows, else XP can lockup the python
- # process when adb runs first time
- if platform.system() == "Windows":
- run.run([adb_location, "start-server"], True, ignore_output=True)
- logcat_cmd = [adb_location, device_switch]
- if device_id:
- logcat_cmd.append(device_id)
- logcat_cmd.extend(["logcat", "-s", "*:d,*,TiAPI:V"])
- logcat_process = subprocess.Popen(logcat_cmd)
- if platform.system() != "Windows":
- signal.signal(signal.SIGHUP, signal_handler)
- signal.signal(signal.SIGQUIT, signal_handler)
- signal.signal(signal.SIGINT, signal_handler)
- signal.signal(signal.SIGABRT, signal_handler)
- signal.signal(signal.SIGTERM, signal_handler)
- # In case it's gonna exit early (like if the command line
- # was wrong or something) give it a chance to do so before we start
- # waiting on it.
- time.sleep(1)
- return_code = logcat_process.poll()
- if return_code:
- signal_handler(signal.SIGQUIT, None)
- sys.exit(return_code)
- # Now wait for it.
- try:
- return_code = logcat_process.wait()
- except OSError:
- signal_handler(signal.SIGQUIT, None)
- sys.exit(return_code)
- sys.exit(return_code)
- def render_template_with_tiapp(template_text, tiapp_obj):
- t = Template(template_text)
- return t.render(tiapp=tiapp_obj)
- def remove_ignored_dirs(dirs):
- for d in dirs:
- if d in ignoreDirs:
- dirs.remove(d)
- # ZipFile.extractall introduced in Python 2.6, so this is workaround for earlier
- # versions
- def zip_extractall(zfile, target_dir):
- file_infos = zfile.infolist()
- for info in file_infos:
- if info.file_size > 0:
- file_path = os.path.join(target_dir, os.path.normpath(info.filename))
- parent_path = os.path.dirname(file_path)
- if not os.path.exists(parent_path):
- os.makedirs(parent_path)
- out_file = open(file_path, "wb")
- out_file.write(zfile.read(info.filename))
- out_file.close()
- def dequote(s):
- if s[0:1] == '"':
- return s[1:-1]
- return s
- def pipe(args1,args2):
- p1 = subprocess.Popen(args1, stdout=subprocess.PIPE)
- p2 = subprocess.Popen(args2, stdin=p1.stdout, stdout=subprocess.PIPE)
- return p2.communicate()[0]
- def read_properties(propFile, separator=":= "):
- propDict = dict()
- for propLine in propFile:
- propDef = propLine.strip()
- if len(propDef) == 0:
- continue
- if propDef[0] in ( '!', '#' ):
- continue
- punctuation= [ propDef.find(c) for c in separator ] + [ len(propDef) ]
- found= min( [ pos for pos in punctuation if pos != -1 ] )
- name= propDef[:found].rstrip()
- value= propDef[found:].lstrip(separator).rstrip()
- propDict[name]= value
- propFile.close()
- return propDict
- def info(msg):
- log.info(msg)
- def debug(msg):
- log.debug(msg)
- def warn(msg):
- log.warn(msg)
- def trace(msg):
- log.trace(msg)
- def error(msg):
- log.error(msg)
- def copy_all(source_folder, dest_folder, mergeXMLResources=False, ignore_dirs=[], ignore_files=[], ignore_exts=[], one_time_msg=""):
- msg_shown = False
- for root, dirs, files in os.walk(source_folder, True, None, True):
- for d in dirs:
- if d in ignore_dirs:
- dirs.remove(d)
- for f in files:
- if f in ignore_files:
- continue
- ext = os.path.splitext(f)[1]
- if ext in ignore_exts:
- continue
- if one_time_msg and not msg_shown:
- info(one_time_msg)
- msg_shown = True
- from_ = os.path.join(root, f)
- to_ = from_.replace(source_folder, dest_folder, 1)
- to_directory = os.path.split(to_)[0]
- if not os.path.exists(to_directory):
- os.makedirs(to_directory)
- shutil.copyfile(from_, to_)
- #
- # Merge the xml resource files in res/values/ if there are multiple files with the same name.
- # (TIMOB-12663)
- #
- elif mergeXMLResources and os.path.isfile(to_) and f in resourceFiles:
- sfile = open(from_, 'r')
- dfile = open(to_, 'r')
- scontent = sfile.read()
- dcontent = dfile.read()
- sfile.close()
- dfile.close()
- sindex = scontent.find('</resources>')
- dindex = dcontent.find('>', dcontent.find('<resources')) + 1
- content_to_write = scontent[:sindex] + dcontent[dindex:]
- wfile = open(to_, 'w')
- wfile.write(content_to_write)
- wfile.close()
- else:
- shutil.copyfile(from_, to_)
- def remove_orphaned_files(source_folder, target_folder, ignore=[]):
- is_res = source_folder.endswith('Resources') or source_folder.endswith('Resources' + os.sep)
- for root, dirs, files in os.walk(target_folder):
- for f in files:
- if f in ignore:
- continue
- full = os.path.join(root, f)
- rel = full.replace(target_folder, '')
- if rel[0] == os.sep:
- rel = rel[1:]
- is_orphan = False
- if not os.path.exists(os.path.join(source_folder, rel)):
- is_orphan = True
- # But it could be under android/... too (platform-specific)
- if is_orphan and is_res:
- if os.path.exists(os.path.join(source_folder, 'android', rel)):
- is_orphan = False
- if is_orphan:
- os.remove(full)
- def is_resource_drawable(path):
- if re.search("android/images/(high|medium|low|res-[^/]+)/", path.replace(os.sep, "/")):
- return True
- else:
- return False
- def resource_drawable_folder(path):
- if not is_resource_drawable(path):
- return None
- else:
- pattern = r'/android/images/(high|medium|low|res-[^/]+)/'
- match = re.search(pattern, path.replace(os.sep, "/"))
- if not match.groups():
- return None
- folder = match.groups()[0]
- if re.match('high|medium|low', folder):
- return 'drawable-%sdpi' % folder[0]
- else:
- return 'drawable-%s' % folder.replace('res-', '')
- def remove_duplicate_nodes_in_res_file(full_path, node_names_to_check):
- f = open(full_path, 'r')
- contents = f.read()
- f.close()
- doc = parseString(contents)
- resources_node = doc.getElementsByTagName('resources')[0]
- made_change = False
- for node_name in node_names_to_check:
- nodes = doc.getElementsByTagName(node_name)
- if len(nodes) == 0:
- continue
- name_list = [] #keeps track of the name attribute for the node we are checking
- for node in nodes:
- # Only check for the children of the "resources" node
- if node.parentNode != resources_node:
- continue
- name = node.getAttribute('name')
- # Remove the node with the duplicate names
- if name in name_list:
- resources_node.removeChild(node)
- made_change = True
- debug('Removed duplicate node [%s] from %s' %(name, full_path))
- else:
- name_list.append(name)
- if made_change:
- new_contents = doc.toxml()
- f = codecs.open(full_path, 'w')
- f.write(new_contents)
- f.close()
- class Builder(object):
- def __init__(self, name, sdk, project_dir, support_dir, app_id, is_emulator):
- self.top_dir = project_dir
- self.project_tiappxml = os.path.join(self.top_dir,'tiapp.xml')
- self.project_dir = os.path.join(project_dir,'build','android')
- self.res_dir = os.path.join(self.project_dir,'res')
- self.platform_dir = os.path.join(project_dir, 'platform', 'android')
- self.project_src_dir = os.path.join(self.project_dir, 'src')
- self.project_gen_dir = os.path.join(self.project_dir, 'gen')
- self.name = name
- self.app_id = app_id
- self.support_dir = support_dir
- self.compiled_files = []
- self.force_rebuild = False
- self.debugger_host = None
- self.debugger_port = -1
- self.profiler_host = None
- self.profiler_port = -1
- self.fastdev_port = -1
- self.fastdev = False
- self.compile_js = False
- self.tool_api_level = MIN_API_LEVEL
- self.abis = list(KNOWN_ABIS)
- # don't build if a java keyword in the app id would cause the build to fail
- tok = self.app_id.split('.')
- for token in tok:
- if token in java_keywords:
- error("Do not use java keywords for project app id, such as " + token)
- sys.exit(1)
- tool_api_level_explicit = False
- temp_tiapp = TiAppXML(self.project_tiappxml)
- if temp_tiapp and temp_tiapp.android:
- if 'tool-api-level' in temp_tiapp.android:
- self.tool_api_level = int(temp_tiapp.android['tool-api-level'])
- tool_api_level_explicit = True
- if 'abi' in temp_tiapp.android and temp_tiapp.android['abi'] != 'all':
- tiapp_abis = [abi.strip() for abi in temp_tiapp.android['abi'].split(",")]
- to_remove = [bad_abi for bad_abi in tiapp_abis if bad_abi not in KNOWN_ABIS]
- if to_remove:
- warn("The following ABIs listed in the Android <abi> section of tiapp.xml are unknown and will be ignored: %s." % ", ".join(to_remove))
- tiapp_abis = [abi for abi in tiapp_abis if abi not in to_remove]
- self.abis = tiapp_abis
- if not self.abis:
- warn("Android <abi> tiapp.xml section does not specify any valid ABIs. Defaulting to '%s'." %
- ",".join(KNOWN_ABIS))
- self.abis = list(KNOWN_ABIS)
- self.sdk = AndroidSDK(sdk, self.tool_api_level)
- # If the tool-api-level was not explicitly set in the tiapp.xml, but
- # <uses-sdk android:targetSdkVersion> *is* set, try to match the target version.
- if (not tool_api_level_explicit and temp_tiapp and temp_tiapp.android_manifest
- and "manifest" in temp_tiapp.android_manifest):
- self.check_target_api_version(temp_tiapp.android_manifest["manifest"])
- self.tiappxml = temp_tiapp
- json_contents = open(os.path.join(template_dir,'dependency.json')).read()
- self.depends_map = simplejson.loads(json_contents)
- # favor the ANDROID_SDK_HOME environment variable if used
- if os.environ.has_key('ANDROID_SDK_HOME') and os.path.exists(os.environ['ANDROID_SDK_HOME']):
- self.home_dir = os.path.join(os.environ['ANDROID_SDK_HOME'], '.titanium')
- self.android_home_dir = os.path.join(os.environ['ANDROID_SDK_HOME'], '.android')
- # we place some files in the users home
- elif platform.system() == "Windows":
- self.home_dir = os.path.join(os.environ['USERPROFILE'], '.titanium')
- self.android_home_dir = os.path.join(os.environ['USERPROFILE'], '.android')
- else:
- self.home_dir = os.path.join(os.path.expanduser('~'), '.titanium')
- self.android_home_dir = os.path.join(os.path.expanduser('~'), '.android')
- if not os.path.exists(self.home_dir):
- os.makedirs(self.home_dir)
- self.sdcard = os.path.join(self.home_dir,'android2.sdcard')
- self.classname = Android.strip_classname(self.name)
- if not is_emulator:
- self.set_java_commands()
- # start in 1.4, you no longer need the build/android directory
- # if missing, we'll create it on the fly
- if not os.path.exists(self.project_dir) or not os.path.exists(os.path.join(self.project_dir,'AndroidManifest.xml')):
- android_creator = Android(name, app_id, self.sdk, None, self.java)
- parent_dir = os.path.dirname(self.top_dir)
- if os.path.exists(self.top_dir):
- android_creator.create(parent_dir, project_dir=self.top_dir, build_time=True)
- else:
- android_creator.create(parent_dir)
- self.force_rebuild = True
- sys.stdout.flush()
- def check_target_api_version(self, manifest_elements):
- pattern = r'android:targetSdkVersion=\"(\d+)\"'
- for el in manifest_elements:
- if el.nodeName == "uses-sdk":
- xml = el.toxml()
- matches = re.findall(pattern, xml)
- if matches:
- new_level = self.sdk.try_best_match_api_level(int(matches[0]))
- if new_level != self.tool_api_level:
- self.tool_api_level = new_level
- break
- def set_java_commands(self):
- commands = java.find_java_commands()
- to_check = ("java", "javac", "keytool", "jarsigner")
- found = True
- for check in to_check:
- if not commands[check]:
- found = False
- error("Required Java tool '%s' not located." % check)
- if not found:
- error("One or more required files not found - please check your JAVA_HOME environment variable")
- sys.exit(1)
- self.jarsigner = commands["jarsigner"]
- self.keytool = commands["keytool"]
- self.javac = commands["javac"]
- self.java = commands["java"]
- if not commands["environ_java_home"] and commands["java_home"]:
- os.environ["JAVA_HOME"] = commands["java_home"]
- def wait_for_home(self, type):
- max_wait = 20
- attempts = 0
- while True:
- processes = self.sdk.list_processes(['-%s' % type])
- found_home = False
- for process in processes:
- if process["name"] == "android.process.acore":
- found_home = True
- break
- if found_home:
- break
- attempts += 1
- if attempts == max_wait:
- error("Timed out waiting for android.process.acore")
- return False
- time.sleep(1)
- return True
- def wait_for_device(self, type):
- debug("Waiting for device to be ready ...")
- t = time.time()
- max_wait = 30
- max_zero = 10
- attempts = 0
- zero_attempts = 0
- timed_out = True
- no_devices = False
- while True:
- devices = self.sdk.list_devices()
- trace("adb devices returned %s devices/emulators" % len(devices))
- if len(devices) > 0:
- found = False
- for device in devices:
- if type == "e" and device.is_emulator() and not device.is_offline(): found = True
- elif type == "d" and device.is_device(): found = True
- if found:
- timed_out = False
- break
- else: zero_attempts += 1
- try: time.sleep(5) # for some reason KeyboardInterrupts get caught here from time to time
- except KeyboardInterrupt: pass
- attempts += 1
- if attempts == max_wait:
- break
- elif zero_attempts == max_zero:
- no_devices = True
- break
- if timed_out:
- if type == "e":
- device = "emulator"
- extra_message = "you may need to close the emulator and try again"
- else:
- device = "device"
- extra_message = "you may try reconnecting the USB cable"
- error("Timed out waiting for %s to be ready, %s" % (device, extra_message))
- if no_devices:
- sys.exit(1)
- return False
- debug("Device connected... (waited %d seconds)" % (attempts*5))
- duration = time.time() - t
- debug("waited %f seconds on emulator to get ready" % duration)
- if duration > 1.0:
- info("Waiting for the Android Emulator to become available")
- return self.wait_for_home(type)
- #time.sleep(20) # give it a little more time to get installed
- return True
- def create_avd(self, avd_id, avd_skin, avd_abi):
- # Sanity check the AVD to see if the ABI is available, or
- # necessary.
- available_avds = avd.get_avds(self.sdk)
- multiple_abis = False
- for device in available_avds:
- if device['id'] == avd_id:
- default_abi = device['abis'][0]
- multiple_abis = ( len(device['abis']) != 1 )
- if avd_abi is None:
- avd_abi = default_abi
- elif avd_abi not in device['abis']:
- warn("ABI %s not supported for AVD ID %s: Using default ABI %s" % (avd_abi, avd_id, default_abi))
- avd_abi = default_abi
- break
- if multiple_abis:
- name = "titanium_%s_%s_%s" % (avd_id, avd_skin, avd_abi)
- else:
- name = "titanium_%s_%s" % (avd_id, avd_skin)
- name = name.replace(' ', '_')
- if not os.path.exists(self.home_dir):
- os.makedirs(self.home_dir)
- avd_path = os.path.join(self.android_home_dir, 'avd')
- my_avd = os.path.join(avd_path,"%s.avd" % name)
- own_sdcard = os.path.join(self.home_dir, '%s.sdcard' % name)
- if not os.path.exists(my_avd) or os.path.exists(own_sdcard):
- # starting with 1.7.2, when we create a new avd, give it its own
- # SDCard as well.
- self.sdcard = own_sdcard
- if not os.path.exists(self.sdcard):
- info("Creating 64M SD card for use in Android emulator")
- run.run([self.sdk.get_mksdcard(), '64M', self.sdcard])
- if not os.path.exists(my_avd):
- if multiple_abis:
- info("Creating new Android Virtual Device (%s %s %s)" % (avd_id,avd_skin,avd_abi))
- else:
- info("Creating new Android Virtual Device (%s %s)" % (avd_id,avd_skin))
- inputgen = os.path.join(template_dir,'input.py')
- abi_args = []
- if multiple_abis:
- abi_args = ['-b', avd_abi]
- pipe([sys.executable, inputgen], [self.sdk.get_android(), '--verbose', 'create', 'avd', '--name', name, '--target', avd_id, '-s', avd_skin, '--force', '--sdcard', self.sdcard] + abi_args)
- inifile = os.path.join(my_avd,'config.ini')
- inifilec = open(inifile,'r').read()
- inifiledata = open(inifile,'w')
- inifiledata.write(inifilec)
- # TODO - Document options
- for hw_option in android_avd_hw.keys():
- inifiledata.write("%s=%s\n" % (hw_option, android_avd_hw[hw_option]))
- inifiledata.close()
- return name
- def run_emulator(self, avd_id, avd_skin, avd_name, avd_abi, add_args):
- info("Launching Android emulator...one moment")
- debug("From: " + self.sdk.get_emulator())
- debug("SDCard: " + self.sdcard)
- if avd_name is None:
- debug("AVD ID: " + avd_id)
- debug("AVD Skin: " + avd_skin)
- else:
- debug("AVD Name: " + avd_name)
- if avd_abi is not None:
- debug("AVD ABI: " + avd_abi)
- debug("SDK: " + sdk_dir)
- # make sure adb is running on windows, else XP can lockup the python
- # process when adb runs first time
- if platform.system() == "Windows":
- run.run([self.sdk.get_adb(), "start-server"], True, ignore_output=True)
- devices = self.sdk.list_devices()
- for device in devices:
- if device.is_emulator() and device.get_port() == 5560:
- info("Emulator is running.")
- sys.exit()
- # this will create an AVD on demand or re-use existing one if already created
- if avd_name == None:
- avd_name = self.create_avd(avd_id, avd_skin, avd_abi)
- # start the emulator
- emulator_cmd = [
- self.sdk.get_emulator(),
- '-avd',
- avd_name,
- '-port',
- '5560',
- '-sdcard',
- self.get_sdcard_path(),
- '-logcat',
- '*:d,*,TiAPI:V',
- '-no-boot-anim',
- '-partition-size',
- '128' # in between nexusone and droid
- ]
- if add_args:
- emulator_cmd.extend([arg.strip() for arg in add_args if len(arg.strip()) > 0])
- debug(' '.join(emulator_cmd))
- p = subprocess.Popen(emulator_cmd)
- def handler(signum, frame):
- debug("signal caught: %d" % signum)
- if not p == None:
- debug("calling emulator kill on %d" % p.pid)
- if platform.system() == "Windows":
- os.system("taskkill /F /T /PID %i" % p.pid)
- else:
- os.kill(p.pid, signal.SIGTERM)
- if platform.system() != "Windows":
- signal.signal(signal.SIGHUP, handler)
- signal.signal(signal.SIGQUIT, handler)
- signal.signal(signal.SIGINT, handler)
- signal.signal(signal.SIGABRT, handler)
- signal.signal(signal.SIGTERM, handler)
- # give it some time to exit prematurely
- time.sleep(1)
- rc = p.poll()
- if rc != None:
- handler(3,None)
- sys.exit(rc)
- # wait for the emulator to finish
- try:
- rc = p.wait()
- except OSError:
- handler(3,None)
- info("Android Emulator has exited")
- sys.exit(rc)
- def check_file_exists(self, path):
- output = self.run_adb('shell', 'ls', path)
- if output != None:
- if output.find("No such file or directory") == -1 \
- and output.find("error: device offline") == -1:
- return True
- return False
- def is_app_installed(self):
- return self.check_file_exists('/data/app/%s*.apk' % self.app_id)
- def get_sdcard_path(self):
- # We need to surround the sd card path in quotes for windows to account for spaces in path
- if platform.system() == "Windows":
- return '"' + self.sdcard + '"'
- return self.sdcard
- def are_resources_installed(self):
- return self.check_file_exists(self.sdcard_resources+'/app.js')
- def include_path(self, path, isfile):
- if not isfile and os.path.basename(path) in ignoreDirs: return False
- elif isfile and os.path.basename(path) in ignoreFiles: return False
- return True
- def warn_dupe_drawable_folders(self):
- tocheck = ('high', 'medium', 'low')
- image_parent = os.path.join(self.top_dir, 'Resources', 'android', 'images')
- for check in tocheck:
- if os.path.exists(os.path.join(image_parent, check)) and os.path.exists(os.path.join(image_parent, 'res-%sdpi' % check[0])):
- warn('You have both an android/images/%s folder and an android/images/res-%sdpi folder. Files from both of these folders will end up in res/drawable-%sdpi. If two files are named the same, there is no guarantee which one will be copied last and therefore be the one the application uses. You should use just one of these folders to avoid conflicts.' % (check, check[0], check[0]))
- def copy_module_platform_folders(self):
- for module in self.modules:
- platform_folder = os.path.join(module.path, 'platform', 'android')
- if os.path.exists(platform_folder):
- copy_all(platform_folder, self.project_dir, True, one_time_msg="Copying platform-specific files for '%s' module" % module.manifest.name)
- def copy_commonjs_modules(self):
- info('Copying CommonJS modules...')
- for module in self.modules:
- if module.js is None:
- continue
- module_name = os.path.basename(module.js)
- self.non_orphans.append(module_name)
- shutil.copy(module.js, self.assets_resources_dir)
- def copy_project_platform_folder(self, ignore_dirs=[], ignore_files=[]):
- if not os.path.exists(self.platform_dir):
- return
- copy_all(self.platform_dir, self.project_dir, True, ignore_dirs, ignore_files, one_time_msg="Copying platform-specific files ...")
- def copy_resource_drawables(self):
- debug('Processing Android resource drawables')
- def make_resource_drawable_filename(orig):
- normalized = orig.replace(os.sep, "/")
- matches = re.search("/android/images/(high|medium|low|res-[^/]+)/(?P<chopped>.*$)", normalized)
- if matches and matches.groupdict() and 'chopped' in matches.groupdict():
- chopped = matches.groupdict()['chopped'].lower()
- for_hash = chopped
- if for_hash.endswith('.9.png'):
- for_hash = for_hash[:-6] + '.png'
- extension = ""
- without_extension = chopped
- if re.search("\\..*$", chopped):
- if chopped.endswith('.9.png'):
- extension = '9.png'
- without_extension = chopped[:-6]
- else:
- extension = chopped.split(".")[-1]
- without_extension = chopped[:-(len(extension)+1)]
- cleaned_without_extension = re.sub(r'[^a-z0-9_]', '_', without_extension)
- cleaned_extension = re.sub(r'[^a-z0-9\._]', '_', extension)
- result = cleaned_without_extension[:80] + "_" + hashlib.md5(for_hash).hexdigest()[:10]
- if extension:
- result += "." + extension
- return result
- else:
- trace("Regexp for resource drawable file %s failed" % orig)
- return None
- def delete_resource_drawable(orig):
- folder = resource_drawable_folder(orig)
- res_file = os.path.join(self.res_dir, folder, make_resource_drawable_filename(orig))
- if os.path.exists(res_file):
- try:
- trace("DELETING FILE: %s" % res_file)
- os.remove(res_file)
- except:
- warn('Unable to delete %s: %s. Execution will continue.' % (res_file, sys.exc_info()[0]))
- def copy_resource_drawable(orig):
- partial_folder = resource_drawable_folder(orig)
- if not partial_folder:
- trace("Could not copy %s; resource folder not determined" % orig)
- return
- dest_folder = os.path.join(self.res_dir, partial_folder)
- dest_filename = make_resource_drawable_filename(orig)
- if dest_filename is None:
- return
- dest = os.path.join(dest_folder, dest_filename)
- if not os.path.exists(dest_folder):
- os.makedirs(dest_folder)
- trace("COPYING FILE: %s => %s" % (orig, dest))
- shutil.copy(orig, dest)
- fileset = []
- if self.force_rebuild or self.deploy_type == 'production' or \
- (self.js_changed and not self.fastdev):
- for root, dirs, files in os.walk(os.path.join(self.top_dir, "Resources")):
- remove_ignored_dirs(dirs)
- for f in files:
- if f in ignoreFiles:
- continue
- path = os.path.join(root, f)
- if is_resource_drawable(path) and f != 'default.png':
- fileset.append(path)
- else:
- if self.project_deltas:
- for delta in self.project_deltas:
- path = delta.get_path()
- if is_resource_drawable(path):
- if delta.get_status() == Delta.DELETED:
- delete_resource_drawable(path)
- else:
- fileset.append(path)
- if len(fileset) == 0:
- return False
- for f in fileset:
- copy_resource_drawable(f)
- return True
- def copy_project_resources(self):
- info("Copying project resources..")
- def validate_filenames(topdir):
- for root, dirs, files in os.walk(topdir):
- remove_ignored_dirs(dirs)
- for d in dirs:
- if d == "iphone" or d == "mobileweb":
- dirs.remove(d)
- for filename in files:
- if filename.startswith("_"):
- error("%s is an invalid filename. Android will not package assets whose filenames start with underscores. Fix and rebuild." % os.path.join(root, filename))
- sys.exit(1)
- resources_dir = os.path.join(self.top_dir, 'Resources')
- validate_filenames(resources_dir)
- android_resources_dir = os.path.join(resources_dir, 'android')
- self.project_deltafy = Deltafy(resources_dir, include_callback=self.include_path)
- self.project_deltas = self.project_deltafy.scan()
- self.js_changed = False
- tiapp_delta = self.project_deltafy.scan_single_file(self.project_tiappxml)
- self.tiapp_changed = tiapp_delta is not None
- full_copy = not os.path.exists(self.assets_resources_dir)
- if self.tiapp_changed or self.force_rebuild or full_copy:
- info("Detected change in tiapp.xml, or assets deleted. Forcing full re-build...")
- # force a clean scan/copy when the tiapp.xml has changed
- self.project_deltafy.clear_state()
- self.project_deltas = self.project_deltafy.scan()
- # rescan tiapp.xml so it doesn't show up as created next time around
- self.project_deltafy.scan_single_file(self.project_tiappxml)
- if self.tiapp_changed:
- for root, dirs, files in os.walk(self.project_gen_dir, topdown=False):
- for name in files:
- os.remove(os.path.join(root, name))
- for name in dirs:
- os.rmdir(os.path.join(root, name))
- def strip_slash(s):
- if s[0:1]=='/' or s[0:1]=='\\': return s[1:]
- return s
- def make_relative(path, relative_to, prefix=None):
- relative_path = strip_slash(path[len(relative_to):])
- if prefix is not None:
- return os.path.join(prefix, relative_path)
- return relative_path
- for delta in self.project_deltas:
- path = delta.get_path()
- if re.search("android/images/(high|medium|low|res-[^/]+)/", path.replace(os.sep, "/")):
- continue # density images are handled later
- if delta.get_status() == Delta.DELETED and path.startswith(android_resources_dir):
- shared_path = path.replace(android_resources_dir, resources_dir, 1)
- if os.path.exists(shared_path):
- dest = make_relative(shared_path, resources_dir, self.assets_resources_dir)
- trace("COPYING FILE: %s => %s (platform-specific file was removed)" % (shared_path, dest))
- shutil.copy(shared_path, dest)
- if delta.get_status() != Delta.DELETED:
- if path.startswith(android_resources_dir):
- dest = make_relative(path, android_resources_dir, self.assets_resources_dir)
- else:
- # don't copy it if there is an android-specific file
- if os.path.exists(path.replace(resources_dir, android_resources_dir, 1)):
- continue
- dest = make_relative(path, resources_dir, self.assets_resources_dir)
- if path.startswith(os.path.join(resources_dir, "iphone")) or path.startswith(os.path.join(resources_dir, "mobileweb")) or path.startswith(os.path.join(resources_dir, "blackberry")):
- continue
- parent = os.path.dirname(dest)
- if not os.path.exists(parent):
- os.makedirs(parent)
- trace("COPYING %s FILE: %s => %s" % (delta.get_status_str(), path, dest))
- shutil.copy(path, dest)
- if (path.startswith(resources_dir) or path.startswith(android_resources_dir)) and path.endswith(".js"):
- self.js_changed = True
- # copy to the sdcard in development mode
- if self.sdcard_copy and self.app_installed and (self.deploy_type == 'development' or self.deploy_type == 'test'):
- if path.startswith(android_resources_dir):
- relative_path = make_relative(delta.get_path(), android_resources_dir)
- else:
- relative_path = make_relative(delta.get_path(), resources_dir)
- relative_path = relative_path.replace("\\", "/")
- self.run_adb('push', delta.get_path(), "%s/%s" % (self.sdcard_resources, relative_path))
- if os.environ.has_key('LIVEVIEW'):
- debug("LiveView enabled")
- appjs = os.path.join(self.assets_resources_dir, 'app.js')
- _appjs = os.path.join(self.assets_resources_dir, '_app.js')
- liveviewjs = os.path.join(tempfile.gettempdir(), 'liveview.js')
- self.non_orphans.append('_app.js')
- if not os.path.exists(appjs):
- debug('app.js not found: %s' % appjs)
- if not os.path.exists(liveviewjs):
- debug('liveviewjs.js not found: %s' % liveviewjs)
- if os.path.exists(appjs) and os.path.exists(liveviewjs):
- trace("COPYING %s => %s" % (appjs, _appjs))
- shutil.copy(appjs, _appjs)
- trace("COPYING %s => %s" % (liveviewjs, appjs))
- shutil.copy(liveviewjs, appjs)
- else:
- debug('LiveView not enabled')
- index_json_path = os.path.join(self.assets_dir, "index.json")
- if len(self.project_deltas) > 0 or not os.path.exists(index_json_path):
- requireIndex.generateJSON(self.assets_dir, index_json_path)
- def check_permissions_mapping(self, key, permissions_mapping, permissions_list):
- try:
- perms = permissions_mapping[key]
- if perms:
- for perm in perms:
- try:
- permissions_list.index(perm)
- except:
- permissions_list.append(perm)
- except:
- pass
- def generate_android_manifest(self,compiler):
- self.generate_localizations()
- self.remove_duplicate_res()
- # NOTE: these are built-in permissions we need -- we probably need to refine when these are needed too
- permissions_required = ['INTERNET','ACCESS_WIFI_STATE','ACCESS_NETWORK_STATE', 'WRITE_EXTERNAL_STORAGE']
- GEO_PERMISSION = [ 'ACCESS_COARSE_LOCATION', 'ACCESS_FINE_LOCATION']
- CONTACTS_READ_PERMISSION = ['READ_CONTACTS']
- CONTACTS_PERMISSION = ['READ_CONTACTS', 'WRITE_CONTACTS']
- CALENDAR_PERMISSION = ['READ_CALENDAR', 'WRITE_CALENDAR']
- VIBRATE_PERMISSION = ['VIBRATE']
- CAMERA_PERMISSION = ['CAMERA']
- WALLPAPER_PERMISSION = ['SET_WALLPAPER']
- # Enable mock location if in development or test mode.
- if self.deploy_type == 'development' or self.deploy_type == 'test':
- GEO_PERMISSION.append('ACCESS_MOCK_LOCATION')
- # this is our module to permission(s) trigger - for each module on the left, require the permission(s) on the right
- permissions_module_mapping = {
- # GEO
- 'geolocation' : GEO_PERMISSION
- }
- # this is our module method to permission(s) trigger - for each method on the left, require the permission(s) on the right
- permissions_method_mapping = {
- # MAP
- 'Map.createView' : GEO_PERMISSION,
- # MEDIA
- 'Media.vibrate' : VIBRATE_PERMISSION,
- 'Media.showCamera' : CAMERA_PERMISSION,
- # CONTACTS
- 'Contacts.createPerson' : CONTACTS_PERMISSION,
- 'Contacts.removePerson' : CONTACTS_PERMISSION,
- 'Contacts.getAllContacts' : CONTACTS_READ_PERMISSION,
- 'Contacts.showContactPicker' : CONTACTS_READ_PERMISSION,
- 'Contacts.showContacts' : CONTACTS_READ_PERMISSION,
- 'Contacts.getPersonByID' : CONTACTS_READ_PERMISSION,
- 'Contacts.getPeopleWithName' : CONTACTS_READ_PERMISSION,
- 'Contacts.getAllPeople' : CONTACTS_READ_PERMISSION,
- 'Contacts.getAllGroups' : CONTACTS_READ_PERMISSION,
- 'Contacts.getGroupByID' : CONTACTS_READ_PERMISSION,
- # Old CALENDAR
- 'Android.Calendar.getAllAlerts' : CALENDAR_PERMISSION,
- 'Android.Calendar.getAllCalendars' : CALENDAR_PERMISSION,
- 'Android.Calendar.getCalendarById' : CALENDAR_PERMISSION,
- 'Android.Calendar.getSelectableCalendars' : CALENDAR_PERMISSION,
- # CALENDAR
- 'Calendar.getAllAlerts' : CALENDAR_PERMISSION,
- 'Calendar.getAllCalendars' : CALENDAR_PERMISSION,
- 'Calendar.getCalendarById' : CALENDAR_PERMISSION,
- 'Calendar.getSelectableCalendars' : CALENDAR_PERMISSION,
- # WALLPAPER
- 'Media.Android.setSystemWallpaper' : WALLPAPER_PERMISSION,
- }
- VIDEO_ACTIVITY = """<activity
- android:name="ti.modules.titanium.media.TiVideoActivity"
- android:configChanges="keyboardHidden|orientation"
- android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
- android:launchMode="singleTask"
- />"""
- MAP_ACTIVITY = """<activity
- android:name="ti.modules.titanium.map.TiMapActivity"
- android:configChanges="keyboardHidden|orientation"
- android:launchMode="singleTask"
- />
- <uses-library android:name="com.google.android.maps" />"""
- CAMERA_ACTIVITY = """<activity
- android:name="ti.modules.titanium.media.TiCameraActivity"
- android:configChanges="keyboardHidden|orientation"
- android:theme="@android:style/Theme.Translucent.NoTitleBar.Fullscreen"
- />"""
- activity_mapping = {
- # MEDIA
- 'Media.createVideoPlayer' : VIDEO_ACTIVITY,
- 'Media.showCamera' : CAMERA_ACTIVITY,
- # MAPS
- 'Map.createView' : MAP_ACTIVITY,
- }
- # this is a map of our APIs to ones that require Google APIs to be available on the device
- google_apis = {
- "Map.createView" : True
- }
- activities = []
- # figure out which permissions we need based on the used module
- for mod in compiler.modules:
- self.check_permissions_mapping(mod, permissions_module_mapping, permissions_required)
- # figure out which permissions we need based on the used module methods
- for mn in compiler.module_methods:
- self.check_permissions_mapping(mn, permissions_method_mapping, permissions_required)
- try:
- mappings = activity_mapping[mn]
- try:
- if google_apis[mn] and not self.google_apis_supported:
- warn("Google APIs detected but a device has been selected that doesn't support them. The API call to Titanium.%s will fail using '%s'" % (mn,my_avd['name']))
- continue
- except:
- pass
- try:
- activities.index(mappings)
- except:
- activities.append(mappings)
- except:
- pass
- # Javascript-based activities defined in tiapp.xml
- if self.tiapp and self.tiapp.android and 'activities' in self.tiapp.android:
- tiapp_activities = self.tiapp.android['activities']
- for key in tiapp_activities:
- activity = tiapp_activities[key]
- if not 'url' in activity:
- continue
- activity_name = self.app_id + '.' + activity['classname']
- activity_str = '<activity \n\t\t\tandroid:name="%s"' % activity_name
- for subkey in activity:
- if subkey not in ('nodes', 'name', 'url', 'options', 'classname', 'android:name'):
- activity_str += '\n\t\t\t%s="%s"' % (subkey, activity[subkey])
- if 'android:config' not in activity:
- activity_str += '\n\t\t\tandroid:configChanges="keyboardHidden|orientation"'
- if 'nodes' in activity:
- activity_str += '>'
- for node in activity['nodes']:
- activity_str += '\n\t\t\t\t' + node.toxml()
- activities.append(activity_str + '\n\t\t</activity>\n')
- else:
- activities.append(activity_str + '\n\t\t/>\n')
- activities = set(activities)
- services = []
- # Javascript-based services defined in tiapp.xml
- if self.tiapp and self.tiapp.android and 'services' in self.tiapp.android:
- tiapp_services = self.tiapp.android['services']
- for key in tiapp_services:
- service = tiapp_services[key]
- if not 'url' in service:
- continue
- service_name = self.app_id + '.' + service['classname']
- service_str = '<service \n\t\t\tandroid:name="%s"' % service_name
- for subkey in service:
- if subkey not in ('nodes', 'service_type', 'type', 'name', 'url', 'options', 'classname', 'android:name'):
- service_str += '\n\t\t\t%s="%s"' % (subkey, service[subkey])
- if 'nodes' in service:
- service_str += '>'
- for node in service['nodes']:
- service_str += '\n\t\t\t\t' + node.toxml()
- services.append(service_str + '\n\t\t</service>\n')
- else:
- services.append(service_str + '\n\t\t/>\n')
- self.use_maps = False
- self.res_changed = False
- icon_name = self.tiapp.properties['icon']
- icon_path = os.path.join(self.assets_resources_dir, icon_name)
- icon_ext = os.path.splitext(icon_path)[1]
- res_drawable_dest = os.path.join(self.project_dir, 'res', 'drawable')
- if not os.path.exists(res_drawable_dest):
- os.makedirs(res_drawable_dest)
- default_icon = os.path.join(self.support_resources_dir, 'default.png')
- dest_icon = os.path.join(res_drawable_dest, 'appicon%s' % icon_ext)
- if Deltafy.needs_update(icon_path, dest_icon):
- self.res_changed = True
- debug("copying app icon: %s" % icon_path)
- shutil.copy(icon_path, dest_icon)
- elif Deltafy.needs_update(default_icon, dest_icon):
- self.res_changed = True
- debug("copying default app icon")
- shutil.copy(default_icon, dest_icon)
- # make our Titanium theme for our icon
- res_values_dir = os.path.join(self.project_dir, 'res','values')
- if not os.path.exists(res_values_dir):
- os.makedirs(res_values_dir)
- theme_xml = os.path.join(res_values_dir,'theme.xml')
- if not os.path.exists(theme_xml):
- self.res_changed = True
- debug('generating theme.xml')
- theme_file = open(theme_xml, 'w')
- theme_flags = "Theme"
- # We need to treat the default values for fulscreen and
- # navbar-hidden the same as android.py does -- false for both.
- theme_fullscreen = False
- theme_navbarhidden = False
- if (self.tiapp.properties.get("fullscreen") == "true" or
- self.tiapp.properties.get("statusbar-hidden") == "true"):
- theme_fullscreen = True
- elif self.tiapp.properties.get("navbar-hidden") == "true":
- theme_navbarhidden = True
- if theme_fullscreen:
- theme_flags += ".NoTitleBar.Fullscreen"
- elif theme_navbarhidden:
- theme_flags += ".NoTitleBar"
- # Wait, one exception. If you want the notification area (very
- # top of screen) hidden, but want the title bar in the app,
- # there's no theme for that. So we have to use the default theme (no flags)
- # and when the application code starts running, the adjustments are then made.
- # Only do this when the properties are explicitly set, so as to avoid changing
- # old default behavior.
- if theme_flags.endswith('.Fullscreen') and \
- self.tiapp.properties.get("navbar-hidden") == 'false' and \
- ('fullscreen' in self.tiapp.explicit_properties or \
- 'statusbar-hidden' in self.tiapp.explicit_properties) and \
- 'navbar-hidden' in self.tiapp.explicit_properties:
- theme_flags = 'Theme'
- TITANIUM_THEME="""<?xml version="1.0" encoding="utf-8"?>
- <resources>
- <style name="Theme.Titanium" parent="android:%s">
- <item name="android:windowBackground">@drawable/background</item>
- </style>
- </resources>
- """ % theme_flags
- theme_file.write(TITANIUM_THEME)
- theme_file.close()
- # create our background image which acts as splash screen during load
- resources_dir = os.path.join(self.top_dir, 'Resources')
- android_images_dir = os.path.join(resources_dir, 'android', 'images')
- # look for density-specific default.png's first
- if os.path.exists(android_images_dir):
- pattern = r'/android/images/(high|medium|low|res-[^/]+)/default.png'
- for root, dirs, files in os.walk(android_images_dir):
- remove_ignored_dirs(dirs)
- for f in files:
- if f in ignoreFiles:
- continue
- path = os.path.join(root, f)
- if re.search(pattern, path.replace(os.sep, "/")):
- res_folder = resource_drawable_folder(path)
- debug('found %s splash screen at %s' % (res_folder, path))
- dest_path = os.path.join(self.res_dir, res_folder)
- dest_file = os.path.join(dest_path, 'background.png')
- if not os.path.exists(dest_path):
- os.makedirs(dest_path)
- if Deltafy.needs_update(path, dest_file):
- self.res_changed = True
- debug('copying %s splash screen to %s' % (path, dest_file))
- shutil.copy(path, dest_file)
- default_png = os.path.join(self.assets_resources_dir, 'default.png')
- support_default_png = os.path.join(self.support_resources_dir, 'default.png')
- background_png = os.path.join(self.project_dir, 'res','drawable','background.png')
- if os.path.exists(default_png) and Deltafy.needs_update(default_png, background_png):
- self.res_changed = True
- debug("found splash screen at %s" % os.path.abspath(default_png))
- shutil.copy(default_png, background_png)
- elif Deltafy.needs_update(support_default_png, background_png):
- self.res_changed = True
- debug("copying default splash screen")
- shutil.copy(support_default_png, background_png)
- android_manifest = os.path.join(self.project_dir, 'AndroidManifest.xml')
- android_manifest_to_read = android_manifest
- # NOTE: allow the user to use their own custom AndroidManifest if they put a file named
- # AndroidManifest.xml in platform/android, in which case all bets are off
- is_custom = False
- # Catch people who may have it in project root (un-released 1.4.x android_native_refactor branch users)
- if os.path.exists(os.path.join(self.top_dir, 'AndroidManifest.xml')):
- warn('AndroidManifest.xml file in the project root is ignored. Move it to platform/android if you want it to be your custom manifest.')
- android_custom_manifest = os.path.join(self.project_dir, 'AndroidManifest.custom.xml')
- if not os.path.exists(android_custom_manifest):
- android_custom_manifest = os.path.join(self.platform_dir, 'AndroidManifest.xml')
- else:
- warn('Use of AndroidManifest.custom.xml is deprecated. Please put your custom manifest as "AndroidManifest.xml" in the "platform/android" directory if you do not need to compile for versions < 1.5')
- if os.path.exists(android_custom_manifest):
- android_manifest_to_read = android_custom_manifest
- is_custom = True
- info("Detected custom ApplicationManifest.xml -- no Titanium version migration supported")
- default_manifest_contents = self.android.render_android_manifest()
- if self.sdk.api_level >= HONEYCOMB_MR2_LEVEL:
- # Need to add "screenSize" in our default "configChanges" attribute on
- # <activity> elements, else changes in orientation will cause the app
- # to restart. cf. TIMOB-10863.
- default_manifest_contents = default_manifest_contents.replace('|orientation"', '|orientation|screenSize"')
- debug("Added 'screenSize' to <activity android:configChanges> because targeted api level %s is >= %s" % (self.sdk.api_level, HONEYCOMB_MR2_LEVEL))
- custom_manifest_contents = None
- if is_custom:
- custom_manifest_contents = open(android_manifest_to_read,'r').read()
- manifest_xml = ''
- def get_manifest_xml(tiapp, template_obj=None):
- xml = ''
- if 'manifest' in tiapp.android_manifest:
- for manifest_el in tiapp.android_manifest['manifest']:
- # since we already track permissions in another way, go ahead and us e that
- if manifest_el.nodeName == 'uses-permission' and manifest_el.hasAttribute('android:name'):
- if manifest_el.getAttribute('android:name').split('.')[-1] not in permissions_required:
- perm_val = manifest_el.getAttribute('android:name')
- if template_obj is not None and "${" in perm_val:
- perm_val = render_template_with_tiapp(perm_val, template_obj)
- permissions_required.append(perm_val)
- elif manifest_el.nodeName not in ('supports-screens', 'uses-sdk'):
- this_xml = manifest_el.toprettyxml()
- if template_obj is not None and "${" in this_xml:
- this_xml = render_template_with_tiapp(this_xml, template_obj)
- xml += this_xml
- return xml
- application_xml = ''
- def get_application_xml(tiapp, template_obj=None):
- xml = ''
- if 'application' in tiapp.android_manifest:
- for app_el in tiapp.android_manifest['application']:
- this_xml = app_el.toxml()
- if template_obj is not None and "${" in this_xml:
- this_xml = render_template_with_tiapp(this_xml, template_obj)
- xml += this_xml
- return xml
- # add manifest / application entries from tiapp.xml
- manifest_xml += get_manifest_xml(self.tiapp)
- application_xml += get_application_xml(self.tiapp)
- # add manifest / application entries from modules
- for module in self.modules:
- if module.xml == None: continue
- manifest_xml += get_manifest_xml(module.xml, self.tiapp)
- application_xml += get_application_xml(module.xml, self.tiapp)
- # build the permissions XML based on the permissions detected
- permissions_required = set(permissions_required)
- permissions_required_xml = ""
- for p in permissions_required:
- if '.' not in p:
- permissions_required_xml+="<uses-permission android:name=\"android.permission.%s\"/>\n\t" % p
- else:
- permissions_required_xml+="<uses-permission android:name=\"%s\"/>\n\t" % p
- def fill_manifest(manifest_source):
- ti_activities = '<!-- TI_ACTIVITIES -->'
- ti_permissions = '<!-- TI_PERMISSIONS -->'
- ti_manifest = '<!-- TI_MANIFEST -->'
- ti_application = '<!-- TI_APPLICATION -->'
- ti_services = '<!-- TI_SERVICES -->'
- manifest_source = manifest_source.replace(ti_activities,"\n\n\t\t".join(activities))
- manifest_source = manifest_source.replace(ti_services,"\n\n\t\t".join(services))
- manifest_source = manifest_source.replace(ti_permissions,permissions_required_xml)
- if len(manifest_xml) > 0:
- manifest_source = manifest_source.replace(ti_manifest, manifest_xml)
- if len(application_xml) > 0:
- manifest_source = manifest_source.replace(ti_application, application_xml)
- return manifest_source
- default_manifest_contents = fill_manifest(default_manifest_contents)
- # if a custom uses-sdk or supports-screens has been specified via tiapp.xml
- # <android><manifest>..., we need to replace the ones in the generated
- # default manifest
- supports_screens_node = None
- uses_sdk_node = None
- if 'manifest' in self.tiapp.android_manifest:
- for node in self.tiapp.android_manifest['manifest']:
- if node.nodeName == 'uses-sdk':
- uses_sdk_node = node
- elif node.nodeName == 'supports-screens':
- supports_screens_node = node
- if supports_screens_node or uses_sdk_node or ('manifest-attributes' in self.tiapp.android_manifest and se…
Large files files are truncated, but you can click here to view the full file