mirror of
				https://github.com/f-droid/fdroidserver.git
				synced 2025-11-03 22:20:28 +03:00 
			
		
		
		
	define "string map" type for new Anti-Features explanations
closes #683
This commit is contained in:
		
							parent
							
								
									6e62ea3614
								
							
						
					
					
						commit
						061ca38afd
					
				
					 27 changed files with 1188 additions and 194 deletions
				
			
		| 
						 | 
				
			
			@ -597,7 +597,14 @@ include tests/main.TestCase
 | 
			
		|||
include tests/metadata/apk/info.guardianproject.urzip.yaml
 | 
			
		||||
include tests/metadata/apk/org.dyndns.fules.ck.yaml
 | 
			
		||||
include tests/metadata/app.with.special.build.params.yml
 | 
			
		||||
include tests/metadata/app.with.special.build.params/en-US/antifeatures/50_Ads.txt
 | 
			
		||||
include tests/metadata/app.with.special.build.params/en-US/antifeatures/50_Tracking.txt
 | 
			
		||||
include tests/metadata/app.with.special.build.params/en-US/antifeatures/Ads.txt
 | 
			
		||||
include tests/metadata/app.with.special.build.params/en-US/antifeatures/NoSourceSince.txt
 | 
			
		||||
include tests/metadata/app.with.special.build.params/zh-CN/antifeatures/49_Tracking.txt
 | 
			
		||||
include tests/metadata/app.with.special.build.params/zh-CN/antifeatures/50_Ads.txt
 | 
			
		||||
include tests/metadata/com.politedroid.yml
 | 
			
		||||
include tests/metadata/dump/app.with.special.build.params.yaml
 | 
			
		||||
include tests/metadata/dump/com.politedroid.yaml
 | 
			
		||||
include tests/metadata/dump/org.adaway.yaml
 | 
			
		||||
include tests/metadata/dump/org.smssecure.smssecure.yaml
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -546,6 +546,13 @@ def package_metadata(app, repodir):
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
def convert_version(version, app, repodir):
 | 
			
		||||
    """Convert the internal representation of Builds: into index-v2 versions.
 | 
			
		||||
 | 
			
		||||
    The diff algorithm of index-v2 uses null/None to mean a field to
 | 
			
		||||
    be removed, so this function handles any Nones that are in the
 | 
			
		||||
    metadata file.
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    ver = {}
 | 
			
		||||
    if "added" in version:
 | 
			
		||||
        ver["added"] = convert_datetime(version["added"])
 | 
			
		||||
| 
						 | 
				
			
			@ -555,7 +562,7 @@ def convert_version(version, app, repodir):
 | 
			
		|||
    ver["file"] = {
 | 
			
		||||
        "name": "/{}".format(version["apkName"]),
 | 
			
		||||
        version["hashType"]: version["hash"],
 | 
			
		||||
        "size": version["size"]
 | 
			
		||||
        "size": version["size"],
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ipfsCIDv1 = version.get("ipfsCIDv1")
 | 
			
		||||
| 
						 | 
				
			
			@ -619,24 +626,14 @@ def convert_version(version, app, repodir):
 | 
			
		|||
                else:
 | 
			
		||||
                    manifest[en].append({"name": perm[0]})
 | 
			
		||||
 | 
			
		||||
    antiFeatures = dict()
 | 
			
		||||
    if "AntiFeatures" in app and app["AntiFeatures"]:
 | 
			
		||||
        for antif in app["AntiFeatures"]:
 | 
			
		||||
            # TODO: get reasons from fdroiddata
 | 
			
		||||
            # ver["antiFeatures"][antif] = {"en-US": "reason"}
 | 
			
		||||
            antiFeatures[antif] = dict()
 | 
			
		||||
 | 
			
		||||
    if "antiFeatures" in version and version["antiFeatures"]:
 | 
			
		||||
        for antif in version["antiFeatures"]:
 | 
			
		||||
            # TODO: get reasons from fdroiddata
 | 
			
		||||
            # ver["antiFeatures"][antif] = {"en-US": "reason"}
 | 
			
		||||
            antiFeatures[antif] = dict()
 | 
			
		||||
 | 
			
		||||
    if app.get("NoSourceSince"):
 | 
			
		||||
        antiFeatures["NoSourceSince"] = dict()
 | 
			
		||||
 | 
			
		||||
    # index-v2 has only per-version antifeatures, not per package.
 | 
			
		||||
    antiFeatures = app.get('AntiFeatures', {})
 | 
			
		||||
    for name, descdict in version.get('antiFeatures', dict()).items():
 | 
			
		||||
        antiFeatures[name] = descdict
 | 
			
		||||
    if antiFeatures:
 | 
			
		||||
        ver["antiFeatures"] = dict(sorted(antiFeatures.items()))
 | 
			
		||||
        ver['antiFeatures'] = {
 | 
			
		||||
            k: dict(sorted(antiFeatures[k].items())) for k in sorted(antiFeatures)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    if "versionCode" in version:
 | 
			
		||||
        if version["versionCode"] > app["CurrentVersionCode"]:
 | 
			
		||||
| 
						 | 
				
			
			@ -881,9 +878,8 @@ def make_v1(apps, packages, repodir, repodict, requestsdict, fdroid_signing_key_
 | 
			
		|||
                for ikey, iname in sorted(lvalue.items()):
 | 
			
		||||
                    lordered[lkey][ikey] = iname
 | 
			
		||||
            app_dict['localized'] = lordered
 | 
			
		||||
        antiFeatures = app_dict.get('antiFeatures', [])
 | 
			
		||||
        if apps[app_dict["packageName"]].get("NoSourceSince"):
 | 
			
		||||
            antiFeatures.append("NoSourceSince")
 | 
			
		||||
        # v1 uses a list of keys for Anti-Features
 | 
			
		||||
        antiFeatures = app_dict.get('antiFeatures', dict()).keys()
 | 
			
		||||
        if antiFeatures:
 | 
			
		||||
            app_dict['antiFeatures'] = sorted(set(antiFeatures))
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -915,6 +911,9 @@ def make_v1(apps, packages, repodir, repodict, requestsdict, fdroid_signing_key_
 | 
			
		|||
                continue
 | 
			
		||||
            if k in ('icon', 'icons', 'icons_src', 'ipfsCIDv1', 'name'):
 | 
			
		||||
                continue
 | 
			
		||||
            if k == 'antiFeatures':
 | 
			
		||||
                d[k] = sorted(v.keys())
 | 
			
		||||
                continue
 | 
			
		||||
            d[k] = v
 | 
			
		||||
 | 
			
		||||
    json_name = 'index-v1.json'
 | 
			
		||||
| 
						 | 
				
			
			@ -1160,8 +1159,6 @@ def make_v0(apps, apks, repodir, repodict, requestsdict, fdroid_signing_key_fing
 | 
			
		|||
        antiFeatures = list(app.AntiFeatures)
 | 
			
		||||
        if 'antiFeatures' in apklist[0]:
 | 
			
		||||
            antiFeatures.extend(apklist[0]['antiFeatures'])
 | 
			
		||||
        if app.get("NoSourceSince"):
 | 
			
		||||
            antiFeatures.append("NoSourceSince")
 | 
			
		||||
        if antiFeatures:
 | 
			
		||||
            afout = sorted(set(antiFeatures))
 | 
			
		||||
            addElementNonEmpty('antifeatures', ','.join(afout), doc, apel)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -624,6 +624,17 @@ def check_app_field_types(app):
 | 
			
		|||
                    fieldtype=v.__class__.__name__,
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
        elif t == metadata.TYPE_STRINGMAP and not isinstance(v, dict):
 | 
			
		||||
            yield (
 | 
			
		||||
                _(
 | 
			
		||||
                    "{appid}: {field} must be a '{type}', but it is a '{fieldtype}'!"
 | 
			
		||||
                ).format(
 | 
			
		||||
                    appid=app.id,
 | 
			
		||||
                    field=field,
 | 
			
		||||
                    type='dict',
 | 
			
		||||
                    fieldtype=v.__class__.__name__,
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def check_antiFeatures(app):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -114,7 +114,7 @@ class App(dict):
 | 
			
		|||
        super().__init__()
 | 
			
		||||
 | 
			
		||||
        self.Disabled = None
 | 
			
		||||
        self.AntiFeatures = []
 | 
			
		||||
        self.AntiFeatures = dict()
 | 
			
		||||
        self.Provides = None
 | 
			
		||||
        self.Categories = []
 | 
			
		||||
        self.License = 'Unknown'
 | 
			
		||||
| 
						 | 
				
			
			@ -182,12 +182,13 @@ TYPE_SCRIPT = 5
 | 
			
		|||
TYPE_MULTILINE = 6
 | 
			
		||||
TYPE_BUILD = 7
 | 
			
		||||
TYPE_INT = 8
 | 
			
		||||
TYPE_STRINGMAP = 9
 | 
			
		||||
 | 
			
		||||
fieldtypes = {
 | 
			
		||||
    'Description': TYPE_MULTILINE,
 | 
			
		||||
    'MaintainerNotes': TYPE_MULTILINE,
 | 
			
		||||
    'Categories': TYPE_LIST,
 | 
			
		||||
    'AntiFeatures': TYPE_LIST,
 | 
			
		||||
    'AntiFeatures': TYPE_STRINGMAP,
 | 
			
		||||
    'AllowedAPKSigningKeys': TYPE_LIST,
 | 
			
		||||
    'Builds': TYPE_BUILD,
 | 
			
		||||
    'VercodeOperation': TYPE_LIST,
 | 
			
		||||
| 
						 | 
				
			
			@ -277,7 +278,7 @@ class Build(dict):
 | 
			
		|||
        self.antcommands = []
 | 
			
		||||
        self.postbuild = ''
 | 
			
		||||
        self.novcheck = False
 | 
			
		||||
        self.antifeatures = []
 | 
			
		||||
        self.antifeatures = dict()
 | 
			
		||||
        if copydict:
 | 
			
		||||
            super().__init__(copydict)
 | 
			
		||||
            return
 | 
			
		||||
| 
						 | 
				
			
			@ -358,7 +359,7 @@ flagtypes = {
 | 
			
		|||
    'forceversion': TYPE_BOOL,
 | 
			
		||||
    'forcevercode': TYPE_BOOL,
 | 
			
		||||
    'novcheck': TYPE_BOOL,
 | 
			
		||||
    'antifeatures': TYPE_LIST,
 | 
			
		||||
    'antifeatures': TYPE_STRINGMAP,
 | 
			
		||||
    'timeout': TYPE_INT,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -649,8 +650,6 @@ def parse_metadata(metadatapath):
 | 
			
		|||
    metadatapath = Path(metadatapath)
 | 
			
		||||
    app = App()
 | 
			
		||||
    app.metadatapath = metadatapath.as_posix()
 | 
			
		||||
    if metadatapath.stem != '.fdroid':
 | 
			
		||||
        app.id = metadatapath.stem
 | 
			
		||||
    if metadatapath.suffix == '.yml':
 | 
			
		||||
        with metadatapath.open('r', encoding='utf-8') as mf:
 | 
			
		||||
            app.update(parse_yaml_metadata(mf))
 | 
			
		||||
| 
						 | 
				
			
			@ -659,6 +658,10 @@ def parse_metadata(metadatapath):
 | 
			
		|||
            _('Unknown metadata format: {path} (use: *.yml)').format(path=metadatapath)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    if metadatapath.stem != '.fdroid':
 | 
			
		||||
        app.id = metadatapath.stem
 | 
			
		||||
        parse_localized_antifeatures(app)
 | 
			
		||||
 | 
			
		||||
    if metadatapath.name != '.fdroid.yml' and app.Repo:
 | 
			
		||||
        build_dir = common.get_build_dir(app)
 | 
			
		||||
        metadata_in_repo = build_dir / '.fdroid.yml'
 | 
			
		||||
| 
						 | 
				
			
			@ -770,6 +773,116 @@ def parse_yaml_metadata(mf):
 | 
			
		|||
    return yamldata
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def parse_localized_antifeatures(app):
 | 
			
		||||
    """Read in localized Anti-Features files from the filesystem.
 | 
			
		||||
 | 
			
		||||
    To support easy integration with Weblate and other translation
 | 
			
		||||
    systems, there is a special type of metadata that can be
 | 
			
		||||
    maintained in a Fastlane-style directory layout, where each field
 | 
			
		||||
    is represented by a text file on directories that specified which
 | 
			
		||||
    app it belongs to, which locale, etc.  This function reads those
 | 
			
		||||
    in and puts them into the internal dict, to be merged with any
 | 
			
		||||
    related data that came from the metadata.yml file.
 | 
			
		||||
 | 
			
		||||
    This needs to be run after parse_yaml_metadata() since that
 | 
			
		||||
    normalizes the data structure.  Also, these values are lower
 | 
			
		||||
    priority than what comes from the metadata file. So this should
 | 
			
		||||
    not overwrite anything parse_yaml_metadata() puts into the App
 | 
			
		||||
    instance.
 | 
			
		||||
 | 
			
		||||
    metadata/<Application ID>/<locale>/antifeatures/<Version Code>_<Anti-Feature>.txt
 | 
			
		||||
    metadata/<Application ID>/<locale>/antifeatures/<Anti-Feature>.txt
 | 
			
		||||
 | 
			
		||||
    └── metadata/
 | 
			
		||||
        └── <Application ID>/
 | 
			
		||||
            ├── en-US/
 | 
			
		||||
            │   └── antifeatures/
 | 
			
		||||
            │       ├── 123_Ads.txt       -> "includes ad lib"
 | 
			
		||||
            │       ├── 123_Tracking.txt  -> "standard suspects"
 | 
			
		||||
            │       └── NoSourceSince.txt -> "it vanished"
 | 
			
		||||
            │
 | 
			
		||||
            └── zh-CN/
 | 
			
		||||
                └── antifeatures/
 | 
			
		||||
                    └── 123_Ads.txt       -> "包括广告图书馆"
 | 
			
		||||
 | 
			
		||||
    Gets parsed into the metadata data structure:
 | 
			
		||||
 | 
			
		||||
    AntiFeatures:
 | 
			
		||||
      NoSourceSince:
 | 
			
		||||
        en-US: it vanished
 | 
			
		||||
    Builds:
 | 
			
		||||
      - versionCode: 123
 | 
			
		||||
        antifeatures:
 | 
			
		||||
          Ads:
 | 
			
		||||
            en-US: includes ad lib
 | 
			
		||||
            zh-CN: 包括广告图书馆
 | 
			
		||||
          Tracking:
 | 
			
		||||
            en-US: standard suspects
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    app_dir = Path('metadata', app['id'])
 | 
			
		||||
    if not app_dir.is_dir():
 | 
			
		||||
        return
 | 
			
		||||
    af_dup_msg = _('Duplicate Anti-Feature declaration at {path} was ignored!')
 | 
			
		||||
 | 
			
		||||
    if app.get('AntiFeatures'):
 | 
			
		||||
        app_has_AntiFeatures = True
 | 
			
		||||
    else:
 | 
			
		||||
        app_has_AntiFeatures = False
 | 
			
		||||
 | 
			
		||||
    has_versionCode = re.compile(r'^-?[0-9]+_.*')
 | 
			
		||||
    has_antifeatures_from_app = set()
 | 
			
		||||
    for build in app.get('Builds', []):
 | 
			
		||||
        antifeatures = build.get('antifeatures')
 | 
			
		||||
        if antifeatures:
 | 
			
		||||
            has_antifeatures_from_app.add(build['versionCode'])
 | 
			
		||||
 | 
			
		||||
    for f in sorted(app_dir.glob('*/antifeatures/*.txt')):
 | 
			
		||||
        path = f.as_posix()
 | 
			
		||||
        left = path.index('/', 9)  # 9 is length of "metadata/"
 | 
			
		||||
        right = path.index('/', left + 1)
 | 
			
		||||
        locale = path[left + 1 : right]
 | 
			
		||||
        description = f.read_text()
 | 
			
		||||
        if has_versionCode.match(f.stem):
 | 
			
		||||
            i = f.stem.index('_')
 | 
			
		||||
            versionCode = int(f.stem[:i])
 | 
			
		||||
            antifeature = f.stem[i + 1 :]
 | 
			
		||||
            if versionCode in has_antifeatures_from_app:
 | 
			
		||||
                logging.error(af_dup_msg.format(path=f))
 | 
			
		||||
                continue
 | 
			
		||||
            if 'Builds' not in app:
 | 
			
		||||
                app['Builds'] = []
 | 
			
		||||
            found = False
 | 
			
		||||
            for build in app['Builds']:
 | 
			
		||||
                # loop though builds again, there might be duplicate versionCodes
 | 
			
		||||
                if versionCode == build['versionCode']:
 | 
			
		||||
                    found = True
 | 
			
		||||
                    if 'antifeatures' not in build:
 | 
			
		||||
                        build['antifeatures'] = dict()
 | 
			
		||||
                    if antifeature not in build['antifeatures']:
 | 
			
		||||
                        build['antifeatures'][antifeature] = dict()
 | 
			
		||||
                    build['antifeatures'][antifeature][locale] = description
 | 
			
		||||
            if not found:
 | 
			
		||||
                app['Builds'].append(
 | 
			
		||||
                    {
 | 
			
		||||
                        'versionCode': versionCode,
 | 
			
		||||
                        'antifeatures': {
 | 
			
		||||
                            antifeature: {locale: description},
 | 
			
		||||
                        },
 | 
			
		||||
                    }
 | 
			
		||||
                )
 | 
			
		||||
        elif app_has_AntiFeatures:
 | 
			
		||||
            logging.error(af_dup_msg.format(path=f))
 | 
			
		||||
            continue
 | 
			
		||||
        else:
 | 
			
		||||
            if 'AntiFeatures' not in app:
 | 
			
		||||
                app['AntiFeatures'] = dict()
 | 
			
		||||
            if f.stem not in app['AntiFeatures']:
 | 
			
		||||
                app['AntiFeatures'][f.stem] = dict()
 | 
			
		||||
            with f.open() as fp:
 | 
			
		||||
                app['AntiFeatures'][f.stem][locale] = fp.read()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _normalize_type_string(v):
 | 
			
		||||
    """Normalize any data to TYPE_STRING.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -787,6 +900,61 @@ def _normalize_type_string(v):
 | 
			
		|||
    return str(v)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _normalize_type_stringmap(k, v):
 | 
			
		||||
    """Normalize any data to TYPE_STRINGMAP.
 | 
			
		||||
 | 
			
		||||
    The internal representation of this format is a dict of dicts,
 | 
			
		||||
    where the outer dict's keys are things like tag names of
 | 
			
		||||
    Anti-Features, the inner dict's keys are locales, and the ultimate
 | 
			
		||||
    values are human readable text.
 | 
			
		||||
 | 
			
		||||
    Metadata entries like AntiFeatures: can be written in many
 | 
			
		||||
    forms, including a simple one-entry string, a list of strings,
 | 
			
		||||
    a dict with keys and descriptions as values, or a dict with
 | 
			
		||||
    localization.
 | 
			
		||||
 | 
			
		||||
    Returns
 | 
			
		||||
    -------
 | 
			
		||||
    A dictionary with string keys, where each value is either a string
 | 
			
		||||
    message or a dict with locale keys and string message values.
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    if v is None:
 | 
			
		||||
        return dict()
 | 
			
		||||
    if isinstance(v, str) or isinstance(v, int) or isinstance(v, float):
 | 
			
		||||
        return {_normalize_type_string(v): dict()}
 | 
			
		||||
    if isinstance(v, list) or isinstance(v, tuple) or isinstance(v, set):
 | 
			
		||||
        retdict = dict()
 | 
			
		||||
        for i in v:
 | 
			
		||||
            if isinstance(i, dict):
 | 
			
		||||
                # transitional format
 | 
			
		||||
                if len(i) != 1:
 | 
			
		||||
                    _warn_or_exception(
 | 
			
		||||
                        _(
 | 
			
		||||
                            "'{value}' is not a valid {field}, should be {pattern}"
 | 
			
		||||
                        ).format(field=k, value=v, pattern='key: value')
 | 
			
		||||
                    )
 | 
			
		||||
                afname = _normalize_type_string(next(iter(i)))
 | 
			
		||||
                desc = _normalize_type_string(next(iter(i.values())))
 | 
			
		||||
                retdict[afname] = {common.DEFAULT_LOCALE: desc}
 | 
			
		||||
            else:
 | 
			
		||||
                retdict[_normalize_type_string(i)] = {}
 | 
			
		||||
        return retdict
 | 
			
		||||
 | 
			
		||||
    retdict = dict()
 | 
			
		||||
    for af, afdict in v.items():
 | 
			
		||||
        key = _normalize_type_string(af)
 | 
			
		||||
        if afdict:
 | 
			
		||||
            if isinstance(afdict, dict):
 | 
			
		||||
                retdict[key] = afdict
 | 
			
		||||
            else:
 | 
			
		||||
                retdict[key] = {common.DEFAULT_LOCALE: _normalize_type_string(afdict)}
 | 
			
		||||
        else:
 | 
			
		||||
            retdict[key] = dict()
 | 
			
		||||
 | 
			
		||||
    return retdict
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def post_parse_yaml_metadata(yamldata):
 | 
			
		||||
    """Convert human-readable metadata data structures into consistent data structures.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -808,6 +976,9 @@ def post_parse_yaml_metadata(yamldata):
 | 
			
		|||
        elif _fieldtype == TYPE_STRING:
 | 
			
		||||
            if v or v == 0:
 | 
			
		||||
                yamldata[k] = _normalize_type_string(v)
 | 
			
		||||
        elif _fieldtype == TYPE_STRINGMAP:
 | 
			
		||||
            if v or v == 0:  # TODO probably want just `if v:`
 | 
			
		||||
                yamldata[k] = _normalize_type_stringmap(k, v)
 | 
			
		||||
        else:
 | 
			
		||||
            if type(v) in (float, int):
 | 
			
		||||
                yamldata[k] = str(v)
 | 
			
		||||
| 
						 | 
				
			
			@ -843,12 +1014,24 @@ def post_parse_yaml_metadata(yamldata):
 | 
			
		|||
                            build_flag=k, value=v
 | 
			
		||||
                        )
 | 
			
		||||
                    )
 | 
			
		||||
            elif _flagtype == TYPE_STRINGMAP:
 | 
			
		||||
                if v or v == 0:
 | 
			
		||||
                    build[k] = _normalize_type_stringmap(k, v)
 | 
			
		||||
 | 
			
		||||
        builds.append(build)
 | 
			
		||||
 | 
			
		||||
    if builds:
 | 
			
		||||
        yamldata['Builds'] = sorted(builds, key=lambda build: build['versionCode'])
 | 
			
		||||
 | 
			
		||||
    no_source_since = yamldata.get("NoSourceSince")
 | 
			
		||||
    # do not overwrite the description if it is there
 | 
			
		||||
    if no_source_since and not yamldata.get('AntiFeatures', {}).get('NoSourceSince'):
 | 
			
		||||
        if 'AntiFeatures' not in yamldata:
 | 
			
		||||
            yamldata['AntiFeatures'] = dict()
 | 
			
		||||
        yamldata['AntiFeatures']['NoSourceSince'] = {
 | 
			
		||||
            common.DEFAULT_LOCALE: no_source_since
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def write_yaml(mf, app):
 | 
			
		||||
    """Write metadata in yaml format.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -52,8 +52,9 @@ def remove_blank_flags_from_builds(builds):
 | 
			
		|||
    for build in builds:
 | 
			
		||||
        new = dict()
 | 
			
		||||
        for k in metadata.build_flags:
 | 
			
		||||
            v = build[k]
 | 
			
		||||
            if v is None or v is False or v == [] or v == '':
 | 
			
		||||
            v = build.get(k)
 | 
			
		||||
            # 0 is valid value, it should not be stripped
 | 
			
		||||
            if v is None or v is False or v == '' or v == dict() or v == list():
 | 
			
		||||
                continue
 | 
			
		||||
            new[k] = v
 | 
			
		||||
        newbuilds.append(new)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -294,7 +294,7 @@ class ExodusSignatureDataController(SignatureDataController):
 | 
			
		|||
                        "warn_code_signatures": [tracker["code_signature"]],
 | 
			
		||||
                        # exodus also provides network signatures, unused atm.
 | 
			
		||||
                        # "network_signatures": [tracker["network_signature"]],
 | 
			
		||||
                        "AntiFeatures": ["Tracking"],
 | 
			
		||||
                        "AntiFeatures": ["Tracking"],  # TODO
 | 
			
		||||
                        "license": "NonFree"  # We assume all trackers in exodus
 | 
			
		||||
                                              # are non-free, although free
 | 
			
		||||
                                              # trackers like piwik, acra,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -158,7 +158,7 @@ def status_update_json(apps, apks):
 | 
			
		|||
 | 
			
		||||
    for appid in apps:
 | 
			
		||||
        app = apps[appid]
 | 
			
		||||
        for af in app.get('AntiFeatures', []):
 | 
			
		||||
        for af in app.get('AntiFeatures', dict()):
 | 
			
		||||
            antiFeatures = output['antiFeatures']  # JSON camelCase
 | 
			
		||||
            if af not in antiFeatures:
 | 
			
		||||
                antiFeatures[af] = dict()
 | 
			
		||||
| 
						 | 
				
			
			@ -351,7 +351,8 @@ def get_cache():
 | 
			
		|||
        if not isinstance(v, dict):
 | 
			
		||||
            continue
 | 
			
		||||
        if 'antiFeatures' in v:
 | 
			
		||||
            v['antiFeatures'] = set(v['antiFeatures'])
 | 
			
		||||
            if not isinstance(v['antiFeatures'], dict):
 | 
			
		||||
                v['antiFeatures'] = {k: {} for k in sorted(v['antiFeatures'])}
 | 
			
		||||
        if 'added' in v:
 | 
			
		||||
            v['added'] = datetime.fromtimestamp(v['added'])
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -400,7 +401,7 @@ def has_known_vulnerability(filename):
 | 
			
		|||
    Janus is similar to Master Key but is perhaps easier to scan for.
 | 
			
		||||
    https://www.guardsquare.com/en/blog/new-android-vulnerability-allows-attackers-modify-apps-without-affecting-their-signatures
 | 
			
		||||
    """
 | 
			
		||||
    found_vuln = False
 | 
			
		||||
    found_vuln = ''
 | 
			
		||||
 | 
			
		||||
    # statically load this pattern
 | 
			
		||||
    if not hasattr(has_known_vulnerability, "pattern"):
 | 
			
		||||
| 
						 | 
				
			
			@ -431,15 +432,23 @@ def has_known_vulnerability(filename):
 | 
			
		|||
                            logging.debug(_('"{path}" contains recent {name} ({version})')
 | 
			
		||||
                                          .format(path=filename, name=name, version=version))
 | 
			
		||||
                        else:
 | 
			
		||||
                            logging.warning(_('"{path}" contains outdated {name} ({version})')
 | 
			
		||||
                                            .format(path=filename, name=name, version=version))
 | 
			
		||||
                            found_vuln = True
 | 
			
		||||
                            msg = '"{path}" contains outdated {name} ({version})'
 | 
			
		||||
                            logging.warning(
 | 
			
		||||
                                _(msg).format(path=filename, name=name, version=version)
 | 
			
		||||
                            )
 | 
			
		||||
                            found_vuln += msg.format(
 | 
			
		||||
                                path=filename, name=name, version=version
 | 
			
		||||
                            )
 | 
			
		||||
                            found_vuln += '\n'
 | 
			
		||||
                        break
 | 
			
		||||
            elif name == 'AndroidManifest.xml' or name == 'classes.dex' or name.endswith('.so'):
 | 
			
		||||
                if name in files_in_apk:
 | 
			
		||||
                    logging.warning(_('{apkfilename} has multiple {name} files, looks like Master Key exploit!')
 | 
			
		||||
                                    .format(apkfilename=filename, name=name))
 | 
			
		||||
                    found_vuln = True
 | 
			
		||||
                    msg = '{apkfilename} has multiple {name} files, looks like Master Key exploit!'
 | 
			
		||||
                    logging.warning(
 | 
			
		||||
                        _(msg).format(apkfilename=filename, name=name)
 | 
			
		||||
                    )
 | 
			
		||||
                    found_vuln += msg.format(apkfilename=filename, name=name)
 | 
			
		||||
                    found_vuln += '\n'
 | 
			
		||||
                files_in_apk.add(name)
 | 
			
		||||
    return found_vuln
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -545,7 +554,7 @@ def translate_per_build_anti_features(apps, apks):
 | 
			
		|||
        if d:
 | 
			
		||||
            afl = d.get(apk['versionCode'])
 | 
			
		||||
            if afl:
 | 
			
		||||
                apk['antiFeatures'].update(afl)
 | 
			
		||||
                apk['antiFeatures'].update(afl)  # TODO
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _get_localized_dict(app, locale):
 | 
			
		||||
| 
						 | 
				
			
			@ -1228,7 +1237,7 @@ def scan_apk(apk_file, require_signature=True):
 | 
			
		|||
        'features': [],
 | 
			
		||||
        'icons_src': {},
 | 
			
		||||
        'icons': {},
 | 
			
		||||
        'antiFeatures': set(),
 | 
			
		||||
        'antiFeatures': {},
 | 
			
		||||
    }
 | 
			
		||||
    ipfsCIDv1 = common.calculate_IPFS_cid(apk_file)
 | 
			
		||||
    if ipfsCIDv1:
 | 
			
		||||
| 
						 | 
				
			
			@ -1263,8 +1272,9 @@ def scan_apk(apk_file, require_signature=True):
 | 
			
		|||
        apk['minSdkVersion'] = 3  # aapt defaults to 3 as the min
 | 
			
		||||
 | 
			
		||||
    # Check for known vulnerabilities
 | 
			
		||||
    if has_known_vulnerability(apk_file):
 | 
			
		||||
        apk['antiFeatures'].add('KnownVuln')
 | 
			
		||||
    hkv = has_known_vulnerability(apk_file)
 | 
			
		||||
    if hkv:
 | 
			
		||||
        apk['antiFeatures']['KnownVuln'] = {DEFAULT_LOCALE: hkv}
 | 
			
		||||
 | 
			
		||||
    return apk
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1545,7 +1555,7 @@ def process_apk(apkcache, apkfilename, repodir, knownapks, use_date_from_apk=Fal
 | 
			
		|||
            if repodir == 'archive' or allow_disabled_algorithms:
 | 
			
		||||
                try:
 | 
			
		||||
                    common.verify_deprecated_jar_signature(apkfile)
 | 
			
		||||
                    apk['antiFeatures'].update(['KnownVuln', 'DisabledAlgorithm'])
 | 
			
		||||
                    apk['antiFeatures'].update(['KnownVuln', 'DisabledAlgorithm'])  # TODO
 | 
			
		||||
                except VerificationException:
 | 
			
		||||
                    skipapk = True
 | 
			
		||||
            else:
 | 
			
		||||
| 
						 | 
				
			
			@ -1885,7 +1895,7 @@ def archive_old_apks(apps, apks, archapks, repodir, archivedir, defaultkeepversi
 | 
			
		|||
        for apk in all_app_apks:
 | 
			
		||||
            if len(keep) == keepversions:
 | 
			
		||||
                break
 | 
			
		||||
            if 'antiFeatures' not in apk:
 | 
			
		||||
            if 'antiFeatures' not in apk:  # TODO
 | 
			
		||||
                keep.append(apk)
 | 
			
		||||
            elif 'DisabledAlgorithm' not in apk['antiFeatures'] or disabled_algorithms_allowed():
 | 
			
		||||
                keep.append(apk)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
AntiFeatures:
 | 
			
		||||
  - UpstreamNonFree
 | 
			
		||||
    UpstreamNonFree: {}
 | 
			
		||||
Categories:
 | 
			
		||||
  - System
 | 
			
		||||
License: GPL-3.0-only
 | 
			
		||||
| 
						 | 
				
			
			@ -77,6 +77,9 @@ Builds:
 | 
			
		|||
    maven: yes@..
 | 
			
		||||
    srclibs:
 | 
			
		||||
      - FacebookSDK@sdk-version-3.0.2
 | 
			
		||||
    antifeatures:
 | 
			
		||||
        Tracking:
 | 
			
		||||
            en-US: Uses the Facebook SDK.
 | 
			
		||||
 | 
			
		||||
  - versionName: 2.1.1-c
 | 
			
		||||
    versionCode: 50
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,6 +26,7 @@ if localmodule not in sys.path:
 | 
			
		|||
import fdroidserver
 | 
			
		||||
from fdroidserver import metadata
 | 
			
		||||
from fdroidserver.exception import MetaDataException
 | 
			
		||||
from fdroidserver.common import DEFAULT_LOCALE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _get_mock_mf(s):
 | 
			
		||||
| 
						 | 
				
			
			@ -216,6 +217,7 @@ class MetadataTest(unittest.TestCase):
 | 
			
		|||
        yaml = ruamel.yaml.YAML(typ='safe')
 | 
			
		||||
        apps = fdroidserver.metadata.read_metadata()
 | 
			
		||||
        for appid in (
 | 
			
		||||
            'app.with.special.build.params',
 | 
			
		||||
            'org.smssecure.smssecure',
 | 
			
		||||
            'org.adaway',
 | 
			
		||||
            'org.videolan.vlc',
 | 
			
		||||
| 
						 | 
				
			
			@ -300,24 +302,24 @@ class MetadataTest(unittest.TestCase):
 | 
			
		|||
 | 
			
		||||
    @mock.patch('git.Repo')
 | 
			
		||||
    def test_rewrite_yaml_special_build_params(self, git_Repo):
 | 
			
		||||
        with tempfile.TemporaryDirectory() as testdir:
 | 
			
		||||
            testdir = Path(testdir)
 | 
			
		||||
        """Test rewriting a plain YAML metadata file without localized files."""
 | 
			
		||||
        os.chdir(self.testdir)
 | 
			
		||||
        os.mkdir('metadata')
 | 
			
		||||
        appid = 'app.with.special.build.params'
 | 
			
		||||
        file_name = Path('metadata/%s.yml' % appid)
 | 
			
		||||
        shutil.copy(self.basedir / file_name, file_name)
 | 
			
		||||
 | 
			
		||||
            # rewrite metadata
 | 
			
		||||
            allapps = fdroidserver.metadata.read_metadata()
 | 
			
		||||
            for appid, app in allapps.items():
 | 
			
		||||
                if appid == 'app.with.special.build.params':
 | 
			
		||||
                    fdroidserver.metadata.write_metadata(
 | 
			
		||||
                        testdir / (appid + '.yml'), app
 | 
			
		||||
                    )
 | 
			
		||||
        # rewrite metadata
 | 
			
		||||
        allapps = fdroidserver.metadata.read_metadata({appid: -1})
 | 
			
		||||
        for appid, app in allapps.items():
 | 
			
		||||
            metadata.write_metadata(file_name, app)
 | 
			
		||||
 | 
			
		||||
            # assert rewrite result
 | 
			
		||||
            self.maxDiff = None
 | 
			
		||||
            file_name = 'app.with.special.build.params.yml'
 | 
			
		||||
            self.assertEqual(
 | 
			
		||||
                (testdir / file_name).read_text(encoding='utf-8'),
 | 
			
		||||
                (Path('metadata-rewrite-yml') / file_name).read_text(encoding='utf-8'),
 | 
			
		||||
            )
 | 
			
		||||
        # assert rewrite result
 | 
			
		||||
        self.maxDiff = None
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            file_name.read_text(),
 | 
			
		||||
            (self.basedir / 'metadata-rewrite-yml' / file_name.name).read_text(),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_normalize_type_string(self):
 | 
			
		||||
        """TYPE_STRING currently has some quirky behavior."""
 | 
			
		||||
| 
						 | 
				
			
			@ -331,6 +333,18 @@ class MetadataTest(unittest.TestCase):
 | 
			
		|||
        self.assertEqual('false', metadata._normalize_type_string(False))
 | 
			
		||||
        self.assertEqual('true', metadata._normalize_type_string(True))
 | 
			
		||||
 | 
			
		||||
    def test_normalize_type_stringmap_none(self):
 | 
			
		||||
        self.assertEqual(dict(), metadata._normalize_type_stringmap('key', None))
 | 
			
		||||
 | 
			
		||||
    def test_normalize_type_stringmap_empty_list(self):
 | 
			
		||||
        self.assertEqual(dict(), metadata._normalize_type_stringmap('AntiFeatures', []))
 | 
			
		||||
 | 
			
		||||
    def test_normalize_type_stringmap_simple_list_format(self):
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            {'Ads': {}, 'Tracking': {}},
 | 
			
		||||
            metadata._normalize_type_stringmap('AntiFeatures', ['Ads', 'Tracking']),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_post_parse_yaml_metadata(self):
 | 
			
		||||
        yamldata = dict()
 | 
			
		||||
        metadata.post_parse_yaml_metadata(yamldata)
 | 
			
		||||
| 
						 | 
				
			
			@ -507,6 +521,96 @@ class MetadataTest(unittest.TestCase):
 | 
			
		|||
        _warning.assert_called_once()
 | 
			
		||||
        _error.assert_called_once()
 | 
			
		||||
 | 
			
		||||
    def test_parse_localized_antifeatures(self):
 | 
			
		||||
        """Unit test based on reading files included in the test repo."""
 | 
			
		||||
        app = dict()
 | 
			
		||||
        app['id'] = 'app.with.special.build.params'
 | 
			
		||||
        metadata.parse_localized_antifeatures(app)
 | 
			
		||||
        self.maxDiff = None
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            app,
 | 
			
		||||
            {
 | 
			
		||||
                'AntiFeatures': {
 | 
			
		||||
                    'Ads': {'en-US': 'please no'},
 | 
			
		||||
                    'NoSourceSince': {'en-US': 'no activity\n'},
 | 
			
		||||
                },
 | 
			
		||||
                'Builds': [
 | 
			
		||||
                    {
 | 
			
		||||
                        'versionCode': 50,
 | 
			
		||||
                        'antifeatures': {
 | 
			
		||||
                            'Ads': {
 | 
			
		||||
                                'en-US': 'includes ad lib\n',
 | 
			
		||||
                                'zh-CN': '包括广告图书馆\n',
 | 
			
		||||
                            },
 | 
			
		||||
                            'Tracking': {'en-US': 'standard suspects\n'},
 | 
			
		||||
                        },
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        'versionCode': 49,
 | 
			
		||||
                        'antifeatures': {
 | 
			
		||||
                            'Tracking': {'zh-CN': 'Text from zh-CN/49_Tracking.txt'},
 | 
			
		||||
                        },
 | 
			
		||||
                    },
 | 
			
		||||
                ],
 | 
			
		||||
                'id': app['id'],
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_parse_localized_antifeatures_passthrough(self):
 | 
			
		||||
        """Test app values are cleanly passed through if no localized files."""
 | 
			
		||||
        before = {
 | 
			
		||||
            'id': 'placeholder',
 | 
			
		||||
            'AntiFeatures': {'NonFreeDep': {}},
 | 
			
		||||
            'Builds': [{'versionCode': 999, 'antifeatures': {'zero': {}, 'one': {}}}],
 | 
			
		||||
        }
 | 
			
		||||
        after = copy.deepcopy(before)
 | 
			
		||||
        with tempfile.TemporaryDirectory() as testdir:
 | 
			
		||||
            os.chdir(testdir)
 | 
			
		||||
            os.mkdir('metadata')
 | 
			
		||||
            os.mkdir(os.path.join('metadata', after['id']))
 | 
			
		||||
            metadata.parse_localized_antifeatures(after)
 | 
			
		||||
        self.assertEqual(before, after)
 | 
			
		||||
 | 
			
		||||
    def test_parse_metadata_antifeatures_NoSourceSince(self):
 | 
			
		||||
        """Test that NoSourceSince gets added as an Anti-Feature."""
 | 
			
		||||
        os.chdir(self.testdir)
 | 
			
		||||
        yml = Path('metadata/test.yml')
 | 
			
		||||
        yml.parent.mkdir()
 | 
			
		||||
        with yml.open('w') as fp:
 | 
			
		||||
            fp.write('AntiFeatures: Ads\nNoSourceSince: gone\n')
 | 
			
		||||
        app = metadata.parse_metadata(yml)
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            app['AntiFeatures'], {'Ads': {}, 'NoSourceSince': {DEFAULT_LOCALE: 'gone'}}
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    @mock.patch('logging.error')
 | 
			
		||||
    def test_yml_overrides_localized_antifeatures(self, logging_error):
 | 
			
		||||
        """Definitions in .yml files should override the localized versions."""
 | 
			
		||||
        app = metadata.parse_metadata('metadata/app.with.special.build.params.yml')
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(app['AntiFeatures'], {'UpstreamNonFree': {}})
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(49, app['Builds'][-3]['versionCode'])
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            app['Builds'][-3]['antifeatures'],
 | 
			
		||||
            {'Tracking': {DEFAULT_LOCALE: 'Uses the Facebook SDK.'}},
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(50, app['Builds'][-2]['versionCode'])
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            app['Builds'][-2]['antifeatures'],
 | 
			
		||||
            {
 | 
			
		||||
                'Ads': {
 | 
			
		||||
                    'en-US': 'includes ad lib\n',
 | 
			
		||||
                    'zh-CN': '包括广告图书馆\n',
 | 
			
		||||
                },
 | 
			
		||||
                'Tracking': {'en-US': 'standard suspects\n'},
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
        # errors are printed when .yml overrides localized
 | 
			
		||||
        logging_error.assert_called()
 | 
			
		||||
        self.assertEqual(3, len(logging_error.call_args_list))
 | 
			
		||||
 | 
			
		||||
    def test_parse_yaml_srclib_corrupt_file(self):
 | 
			
		||||
        with tempfile.TemporaryDirectory() as testdir:
 | 
			
		||||
            testdir = Path(testdir)
 | 
			
		||||
| 
						 | 
				
			
			@ -741,6 +845,257 @@ class MetadataTest(unittest.TestCase):
 | 
			
		|||
        self.assertNotIn('Provides', result)
 | 
			
		||||
        self.assertNotIn('provides', result)
 | 
			
		||||
 | 
			
		||||
    def test_parse_yaml_app_antifeatures_dict(self):
 | 
			
		||||
        nonfreenet = 'free it!'
 | 
			
		||||
        tracking = 'so many'
 | 
			
		||||
        mf = io.StringIO(
 | 
			
		||||
            textwrap.dedent(
 | 
			
		||||
                f"""
 | 
			
		||||
                AntiFeatures:
 | 
			
		||||
                  Tracking: {tracking}
 | 
			
		||||
                  NonFreeNet: {nonfreenet}
 | 
			
		||||
                """
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            metadata.parse_yaml_metadata(mf),
 | 
			
		||||
            {
 | 
			
		||||
                'AntiFeatures': {
 | 
			
		||||
                    'NonFreeNet': {DEFAULT_LOCALE: nonfreenet},
 | 
			
		||||
                    'Tracking': {DEFAULT_LOCALE: tracking},
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_parse_yaml_metadata_build_antifeatures_old_style(self):
 | 
			
		||||
        mf = _get_mock_mf(
 | 
			
		||||
            textwrap.dedent(
 | 
			
		||||
                """
 | 
			
		||||
                AntiFeatures:
 | 
			
		||||
                  - Ads
 | 
			
		||||
                Builds:
 | 
			
		||||
                  - versionCode: 123
 | 
			
		||||
                    antifeatures:
 | 
			
		||||
                      - KnownVuln
 | 
			
		||||
                      - UpstreamNonFree
 | 
			
		||||
                      - NonFreeAssets
 | 
			
		||||
                """
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            metadata.parse_yaml_metadata(mf),
 | 
			
		||||
            {
 | 
			
		||||
                'AntiFeatures': {'Ads': {}},
 | 
			
		||||
                'Builds': [
 | 
			
		||||
                    {
 | 
			
		||||
                        'antifeatures': {
 | 
			
		||||
                            'KnownVuln': {},
 | 
			
		||||
                            'NonFreeAssets': {},
 | 
			
		||||
                            'UpstreamNonFree': {},
 | 
			
		||||
                        },
 | 
			
		||||
                        'versionCode': 123,
 | 
			
		||||
                    }
 | 
			
		||||
                ],
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_parse_yaml_metadata_antifeatures_sort(self):
 | 
			
		||||
        """All data should end up sorted, to minimize diffs in the index files."""
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            metadata.parse_yaml_metadata(
 | 
			
		||||
                _get_mock_mf(
 | 
			
		||||
                    textwrap.dedent(
 | 
			
		||||
                        """
 | 
			
		||||
                Builds:
 | 
			
		||||
                  - versionCode: 123
 | 
			
		||||
                    antifeatures:
 | 
			
		||||
                      KnownVuln:
 | 
			
		||||
                        es: 2nd
 | 
			
		||||
                        az: zero
 | 
			
		||||
                        en-US: first
 | 
			
		||||
                      UpstreamNonFree:
 | 
			
		||||
                      NonFreeAssets:
 | 
			
		||||
                AntiFeatures:
 | 
			
		||||
                  NonFreeDep:
 | 
			
		||||
                  Ads:
 | 
			
		||||
                    sw: 2nd
 | 
			
		||||
                    zh-CN: 3rd
 | 
			
		||||
                    de: 1st
 | 
			
		||||
                """
 | 
			
		||||
                    )
 | 
			
		||||
                )
 | 
			
		||||
            ),
 | 
			
		||||
            {
 | 
			
		||||
                'AntiFeatures': {
 | 
			
		||||
                    'Ads': {'de': '1st', 'sw': '2nd', 'zh-CN': '3rd'},
 | 
			
		||||
                    'NonFreeDep': {},
 | 
			
		||||
                },
 | 
			
		||||
                'Builds': [
 | 
			
		||||
                    {
 | 
			
		||||
                        'antifeatures': {
 | 
			
		||||
                            'KnownVuln': {'az': 'zero', 'en-US': 'first', 'es': '2nd'},
 | 
			
		||||
                            'NonFreeAssets': {},
 | 
			
		||||
                            'UpstreamNonFree': {},
 | 
			
		||||
                        },
 | 
			
		||||
                        'versionCode': 123,
 | 
			
		||||
                    }
 | 
			
		||||
                ],
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_parse_yaml_app_antifeatures_str(self):
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            metadata.parse_yaml_metadata(io.StringIO('AntiFeatures: Tracking')),
 | 
			
		||||
            {'AntiFeatures': {'Tracking': {}}},
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_parse_yaml_app_antifeatures_bool(self):
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            metadata.parse_yaml_metadata(io.StringIO('AntiFeatures: true')),
 | 
			
		||||
            {'AntiFeatures': {'true': {}}},
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_parse_yaml_app_antifeatures_int(self):
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            metadata.parse_yaml_metadata(io.StringIO('AntiFeatures: 1')),
 | 
			
		||||
            {'AntiFeatures': {'1': {}}},
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_parse_yaml_app_antifeatures_float(self):
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            metadata.parse_yaml_metadata(io.StringIO('AntiFeatures: 1.0')),
 | 
			
		||||
            {'AntiFeatures': {'1.0': {}}},
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_parse_yaml_app_antifeatures_list_float(self):
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            metadata.parse_yaml_metadata(io.StringIO('AntiFeatures:\n  - 1.0\n')),
 | 
			
		||||
            {'AntiFeatures': {'1.0': {}}},
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_parse_yaml_app_antifeatures_dict_float(self):
 | 
			
		||||
        mf = io.StringIO('AntiFeatures:\n  0.0: too early\n')
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            metadata.parse_yaml_metadata(mf),
 | 
			
		||||
            {'AntiFeatures': {'0.0': {'en-US': 'too early'}}},
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_parse_yaml_app_antifeatures_dict_float_fail_value(self):
 | 
			
		||||
        mf = io.StringIO('AntiFeatures:\n  NoSourceSince: 1.0\n')
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            metadata.parse_yaml_metadata(mf),
 | 
			
		||||
            {'AntiFeatures': {'NoSourceSince': {'en-US': '1.0'}}},
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_parse_yaml_metadata_type_stringmap_old_list(self):
 | 
			
		||||
        mf = _get_mock_mf(
 | 
			
		||||
            textwrap.dedent(
 | 
			
		||||
                """
 | 
			
		||||
                    AntiFeatures:
 | 
			
		||||
                      - Ads
 | 
			
		||||
                      - Tracking
 | 
			
		||||
                """
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            {'AntiFeatures': {'Ads': {}, 'Tracking': {}}},
 | 
			
		||||
            metadata.parse_yaml_metadata(mf),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_parse_yaml_app_antifeatures_dict_no_value(self):
 | 
			
		||||
        mf = io.StringIO(
 | 
			
		||||
            textwrap.dedent(
 | 
			
		||||
                """\
 | 
			
		||||
                AntiFeatures:
 | 
			
		||||
                  Tracking:
 | 
			
		||||
                  NonFreeNet:
 | 
			
		||||
                """
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            metadata.parse_yaml_metadata(mf),
 | 
			
		||||
            {'AntiFeatures': {'NonFreeNet': {}, 'Tracking': {}}},
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_parse_yaml_metadata_type_stringmap_transitional(self):
 | 
			
		||||
        """Support a transitional format, where users just append a text"""
 | 
			
		||||
        ads = 'Has ad lib in it.'
 | 
			
		||||
        tracking = 'opt-out reports with ACRA'
 | 
			
		||||
        mf = _get_mock_mf(
 | 
			
		||||
            textwrap.dedent(
 | 
			
		||||
                f"""
 | 
			
		||||
                    AntiFeatures:
 | 
			
		||||
                      - Ads: {ads}
 | 
			
		||||
                      - Tracking: {tracking}
 | 
			
		||||
                """
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            metadata.parse_yaml_metadata(mf),
 | 
			
		||||
            {
 | 
			
		||||
                'AntiFeatures': {
 | 
			
		||||
                    'Ads': {DEFAULT_LOCALE: ads},
 | 
			
		||||
                    'Tracking': {DEFAULT_LOCALE: tracking},
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_parse_yaml_app_antifeatures_dict_mixed_values(self):
 | 
			
		||||
        ads = 'true'
 | 
			
		||||
        tracking = 'many'
 | 
			
		||||
        nonfreenet = '1'
 | 
			
		||||
        mf = io.StringIO(
 | 
			
		||||
            textwrap.dedent(
 | 
			
		||||
                f"""
 | 
			
		||||
                AntiFeatures:
 | 
			
		||||
                  Ads: {ads}
 | 
			
		||||
                  Tracking: {tracking}
 | 
			
		||||
                  NonFreeNet: {nonfreenet}
 | 
			
		||||
                """
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            metadata.parse_yaml_metadata(mf),
 | 
			
		||||
            {
 | 
			
		||||
                'AntiFeatures': {
 | 
			
		||||
                    'Ads': {DEFAULT_LOCALE: ads},
 | 
			
		||||
                    'NonFreeNet': {DEFAULT_LOCALE: nonfreenet},
 | 
			
		||||
                    'Tracking': {DEFAULT_LOCALE: tracking},
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_parse_yaml_app_antifeatures_stringmap_full(self):
 | 
			
		||||
        ads = 'watching'
 | 
			
		||||
        tracking = 'many'
 | 
			
		||||
        nonfreenet = 'pipes'
 | 
			
		||||
        nonfreenet_zh = '非免费网络'
 | 
			
		||||
        self.maxDiff = None
 | 
			
		||||
        mf = io.StringIO(
 | 
			
		||||
            textwrap.dedent(
 | 
			
		||||
                f"""
 | 
			
		||||
                AntiFeatures:
 | 
			
		||||
                  Ads:
 | 
			
		||||
                    {DEFAULT_LOCALE}: {ads}
 | 
			
		||||
                  Tracking:
 | 
			
		||||
                    {DEFAULT_LOCALE}: {tracking}
 | 
			
		||||
                  NonFreeNet:
 | 
			
		||||
                    {DEFAULT_LOCALE}: {nonfreenet}
 | 
			
		||||
                    zh-CN: {nonfreenet_zh}
 | 
			
		||||
                """
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            metadata.parse_yaml_metadata(mf),
 | 
			
		||||
            {
 | 
			
		||||
                'AntiFeatures': {
 | 
			
		||||
                    'Ads': {DEFAULT_LOCALE: ads},
 | 
			
		||||
                    'NonFreeNet': {DEFAULT_LOCALE: nonfreenet, 'zh-CN': nonfreenet_zh},
 | 
			
		||||
                    'Tracking': {DEFAULT_LOCALE: tracking},
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_write_yaml_1_line_scripts_as_string(self):
 | 
			
		||||
        mf = io.StringIO()
 | 
			
		||||
        app = fdroidserver.metadata.App()
 | 
			
		||||
| 
						 | 
				
			
			@ -1263,9 +1618,9 @@ class PostMetadataParseTest(unittest.TestCase):
 | 
			
		|||
        fdroidserver.metadata.warnings_action = 'error'
 | 
			
		||||
 | 
			
		||||
    def _post_metadata_parse_app_list(self, from_yaml, expected):
 | 
			
		||||
        app = {'AntiFeatures': from_yaml}
 | 
			
		||||
        app = {'AllowedAPKSigningKeys': from_yaml}
 | 
			
		||||
        metadata.post_parse_yaml_metadata(app)
 | 
			
		||||
        return {'AntiFeatures': expected}, app
 | 
			
		||||
        return {'AllowedAPKSigningKeys': expected}, app
 | 
			
		||||
 | 
			
		||||
    def _post_metadata_parse_app_string(self, from_yaml, expected):
 | 
			
		||||
        app = {'Repo': from_yaml}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
antiFeatures: !!set {}
 | 
			
		||||
antiFeatures: {}
 | 
			
		||||
features: []
 | 
			
		||||
hash: abfb3adb7496611749e7abfb014c5c789e3a02489e48a5c3665110d1b1acd931
 | 
			
		||||
hashType: sha256
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
antiFeatures: !!set {}
 | 
			
		||||
antiFeatures: {}
 | 
			
		||||
features: []
 | 
			
		||||
hash: 897486e1f857c6c0ee32ccbad0e1b8cd82f6d0e65a44a23f13f852d2b63a18c8
 | 
			
		||||
hashType: sha256
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -77,6 +77,8 @@ Builds:
 | 
			
		|||
    maven: yes@..
 | 
			
		||||
    srclibs:
 | 
			
		||||
      - FacebookSDK@sdk-version-3.0.2
 | 
			
		||||
    antifeatures:
 | 
			
		||||
      Tracking: Uses the Facebook SDK.
 | 
			
		||||
 | 
			
		||||
  - versionName: 2.1.1-c
 | 
			
		||||
    versionCode: 50
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
includes ad lib
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
standard suspects
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
please no
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
no activity
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
Text from zh-CN/49_Tracking.txt
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
包括广告图书馆
 | 
			
		||||
							
								
								
									
										373
									
								
								tests/metadata/dump/app.with.special.build.params.yaml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										373
									
								
								tests/metadata/dump/app.with.special.build.params.yaml
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,373 @@
 | 
			
		|||
AllowedAPKSigningKeys: []
 | 
			
		||||
AntiFeatures:
 | 
			
		||||
  UpstreamNonFree: {}
 | 
			
		||||
ArchivePolicy: 0 versions
 | 
			
		||||
AuthorEmail: null
 | 
			
		||||
AuthorName: null
 | 
			
		||||
AuthorWebSite: null
 | 
			
		||||
AutoName: UberSync for Facebook
 | 
			
		||||
AutoUpdateMode: None
 | 
			
		||||
Binaries: null
 | 
			
		||||
Bitcoin: null
 | 
			
		||||
Builds:
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni: []
 | 
			
		||||
  commit: b3879c973e7cac3a3319
 | 
			
		||||
  disable: ''
 | 
			
		||||
  encoding: null
 | 
			
		||||
  extlibs: []
 | 
			
		||||
  forcevercode: false
 | 
			
		||||
  forceversion: false
 | 
			
		||||
  gradle: []
 | 
			
		||||
  gradleprops: []
 | 
			
		||||
  init: ''
 | 
			
		||||
  maven: null
 | 
			
		||||
  ndk: null
 | 
			
		||||
  novcheck: false
 | 
			
		||||
  oldsdkloc: false
 | 
			
		||||
  output: null
 | 
			
		||||
  patch: []
 | 
			
		||||
  postbuild: ''
 | 
			
		||||
  preassemble: []
 | 
			
		||||
  prebuild: ''
 | 
			
		||||
  rm: []
 | 
			
		||||
  scandelete: []
 | 
			
		||||
  scanignore: []
 | 
			
		||||
  srclibs: []
 | 
			
		||||
  subdir: null
 | 
			
		||||
  submodules: false
 | 
			
		||||
  sudo: ''
 | 
			
		||||
  target: null
 | 
			
		||||
  timeout: null
 | 
			
		||||
  versionCode: 32
 | 
			
		||||
  versionName: 1.0.0
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni: []
 | 
			
		||||
  commit: 252c8dd4c9
 | 
			
		||||
  disable: ''
 | 
			
		||||
  encoding: null
 | 
			
		||||
  extlibs: []
 | 
			
		||||
  forcevercode: false
 | 
			
		||||
  forceversion: false
 | 
			
		||||
  gradle: []
 | 
			
		||||
  gradleprops: []
 | 
			
		||||
  init: ''
 | 
			
		||||
  maven: null
 | 
			
		||||
  ndk: null
 | 
			
		||||
  novcheck: false
 | 
			
		||||
  oldsdkloc: false
 | 
			
		||||
  output: null
 | 
			
		||||
  patch: []
 | 
			
		||||
  postbuild: ''
 | 
			
		||||
  preassemble: []
 | 
			
		||||
  prebuild: ''
 | 
			
		||||
  rm: []
 | 
			
		||||
  scandelete: []
 | 
			
		||||
  scanignore: []
 | 
			
		||||
  srclibs: []
 | 
			
		||||
  subdir: null
 | 
			
		||||
  submodules: false
 | 
			
		||||
  sudo: ''
 | 
			
		||||
  target: null
 | 
			
		||||
  timeout: null
 | 
			
		||||
  versionCode: 33
 | 
			
		||||
  versionName: 1.0.1
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni: []
 | 
			
		||||
  commit: v1.2.0
 | 
			
		||||
  disable: ''
 | 
			
		||||
  encoding: null
 | 
			
		||||
  extlibs: []
 | 
			
		||||
  forcevercode: false
 | 
			
		||||
  forceversion: false
 | 
			
		||||
  gradle: []
 | 
			
		||||
  gradleprops: []
 | 
			
		||||
  init: ''
 | 
			
		||||
  maven: null
 | 
			
		||||
  ndk: null
 | 
			
		||||
  novcheck: false
 | 
			
		||||
  oldsdkloc: false
 | 
			
		||||
  output: null
 | 
			
		||||
  patch:
 | 
			
		||||
  - appbrain.patch
 | 
			
		||||
  postbuild: ''
 | 
			
		||||
  preassemble: []
 | 
			
		||||
  prebuild:
 | 
			
		||||
  - sed -i 's@\(reference.1=\).*@\1$$FacebookSDK$$@' project.properties
 | 
			
		||||
  - sed -i 's/Class\[\]/Class\<?\>\[\]/g' $$FacebookSDK$$/src/com/facebook/model/GraphObject.java
 | 
			
		||||
  rm:
 | 
			
		||||
  - libs/appbrain-sdk-android.jar
 | 
			
		||||
  scandelete: []
 | 
			
		||||
  scanignore: []
 | 
			
		||||
  srclibs:
 | 
			
		||||
  - FacebookSDK@sdk-version-3.0.1
 | 
			
		||||
  subdir: null
 | 
			
		||||
  submodules: false
 | 
			
		||||
  sudo: ''
 | 
			
		||||
  target: null
 | 
			
		||||
  timeout: null
 | 
			
		||||
  versionCode: 39
 | 
			
		||||
  versionName: 1.2.0
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni: []
 | 
			
		||||
  commit: v1.2.2
 | 
			
		||||
  disable: ''
 | 
			
		||||
  encoding: null
 | 
			
		||||
  extlibs:
 | 
			
		||||
  - android/android-support-v4.jar
 | 
			
		||||
  forcevercode: false
 | 
			
		||||
  forceversion: false
 | 
			
		||||
  gradle: []
 | 
			
		||||
  gradleprops: []
 | 
			
		||||
  init: ''
 | 
			
		||||
  maven: null
 | 
			
		||||
  ndk: null
 | 
			
		||||
  novcheck: false
 | 
			
		||||
  oldsdkloc: false
 | 
			
		||||
  output: null
 | 
			
		||||
  patch:
 | 
			
		||||
  - appbrain.patch
 | 
			
		||||
  postbuild: ''
 | 
			
		||||
  preassemble: []
 | 
			
		||||
  prebuild:
 | 
			
		||||
  - mv libs/android-support-v4.jar $$FacebookSDK$$/libs/
 | 
			
		||||
  - sed -i 's@\(reference.1=\).*@\1$$FacebookSDK$$@' project.properties
 | 
			
		||||
  - sed -i 's/Class\[\]/Class\<?\>\[\]/g'          $$FacebookSDK$$/src/com/facebook/model/GraphObject.java
 | 
			
		||||
  rm:
 | 
			
		||||
  - libs/appbrain-sdk-android.jar
 | 
			
		||||
  scandelete: []
 | 
			
		||||
  scanignore: []
 | 
			
		||||
  srclibs:
 | 
			
		||||
  - FacebookSDK@sdk-version-3.0.2
 | 
			
		||||
  subdir: null
 | 
			
		||||
  submodules: false
 | 
			
		||||
  sudo: ''
 | 
			
		||||
  target: null
 | 
			
		||||
  timeout: null
 | 
			
		||||
  versionCode: 42
 | 
			
		||||
  versionName: 1.2.2
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni: []
 | 
			
		||||
  commit: 2.1.1
 | 
			
		||||
  disable: ''
 | 
			
		||||
  encoding: null
 | 
			
		||||
  extlibs: []
 | 
			
		||||
  forcevercode: false
 | 
			
		||||
  forceversion: false
 | 
			
		||||
  gradle: []
 | 
			
		||||
  gradleprops: []
 | 
			
		||||
  init: ''
 | 
			
		||||
  maven: yes
 | 
			
		||||
  ndk: null
 | 
			
		||||
  novcheck: false
 | 
			
		||||
  oldsdkloc: false
 | 
			
		||||
  output: null
 | 
			
		||||
  patch:
 | 
			
		||||
  - manifest-ads.patch
 | 
			
		||||
  - mobilecore.patch
 | 
			
		||||
  postbuild: ''
 | 
			
		||||
  preassemble: []
 | 
			
		||||
  prebuild: ''
 | 
			
		||||
  rm: []
 | 
			
		||||
  scandelete: []
 | 
			
		||||
  scanignore: []
 | 
			
		||||
  srclibs:
 | 
			
		||||
  - FacebookSDK@sdk-version-3.0.2
 | 
			
		||||
  subdir: null
 | 
			
		||||
  submodules: false
 | 
			
		||||
  sudo: ''
 | 
			
		||||
  target: null
 | 
			
		||||
  timeout: null
 | 
			
		||||
  versionCode: 48
 | 
			
		||||
  versionName: 2.1.1
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures:
 | 
			
		||||
    Tracking:
 | 
			
		||||
      en-US: Uses the Facebook SDK.
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni: []
 | 
			
		||||
  commit: 2.1.1
 | 
			
		||||
  disable: ''
 | 
			
		||||
  encoding: null
 | 
			
		||||
  extlibs: []
 | 
			
		||||
  forcevercode: false
 | 
			
		||||
  forceversion: false
 | 
			
		||||
  gradle: []
 | 
			
		||||
  gradleprops: []
 | 
			
		||||
  init: ''
 | 
			
		||||
  maven: yes@..
 | 
			
		||||
  ndk: null
 | 
			
		||||
  novcheck: false
 | 
			
		||||
  oldsdkloc: false
 | 
			
		||||
  output: null
 | 
			
		||||
  patch:
 | 
			
		||||
  - manifest-ads.patch
 | 
			
		||||
  - mobilecore.patch
 | 
			
		||||
  postbuild: ''
 | 
			
		||||
  preassemble: []
 | 
			
		||||
  prebuild: ''
 | 
			
		||||
  rm: []
 | 
			
		||||
  scandelete: []
 | 
			
		||||
  scanignore: []
 | 
			
		||||
  srclibs:
 | 
			
		||||
  - FacebookSDK@sdk-version-3.0.2
 | 
			
		||||
  subdir: null
 | 
			
		||||
  submodules: false
 | 
			
		||||
  sudo: ''
 | 
			
		||||
  target: null
 | 
			
		||||
  timeout: null
 | 
			
		||||
  versionCode: 49
 | 
			
		||||
  versionName: 2.1.1-b
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures:
 | 
			
		||||
    Ads:
 | 
			
		||||
      en-US: 'includes ad lib
 | 
			
		||||
 | 
			
		||||
        '
 | 
			
		||||
      zh-CN: '包括广告图书馆
 | 
			
		||||
 | 
			
		||||
        '
 | 
			
		||||
    Tracking:
 | 
			
		||||
      en-US: 'standard suspects
 | 
			
		||||
 | 
			
		||||
        '
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni: []
 | 
			
		||||
  commit: 2.1.1
 | 
			
		||||
  disable: ''
 | 
			
		||||
  encoding: null
 | 
			
		||||
  extlibs: []
 | 
			
		||||
  forcevercode: false
 | 
			
		||||
  forceversion: false
 | 
			
		||||
  gradle: []
 | 
			
		||||
  gradleprops: []
 | 
			
		||||
  init: ''
 | 
			
		||||
  maven: '2'
 | 
			
		||||
  ndk: null
 | 
			
		||||
  novcheck: false
 | 
			
		||||
  oldsdkloc: false
 | 
			
		||||
  output: null
 | 
			
		||||
  patch:
 | 
			
		||||
  - manifest-ads.patch
 | 
			
		||||
  - mobilecore.patch
 | 
			
		||||
  postbuild: ''
 | 
			
		||||
  preassemble: []
 | 
			
		||||
  prebuild: ''
 | 
			
		||||
  rm: []
 | 
			
		||||
  scandelete: []
 | 
			
		||||
  scanignore: []
 | 
			
		||||
  srclibs:
 | 
			
		||||
  - FacebookSDK@sdk-version-3.0.2
 | 
			
		||||
  subdir: null
 | 
			
		||||
  submodules: false
 | 
			
		||||
  sudo: ''
 | 
			
		||||
  target: null
 | 
			
		||||
  timeout: null
 | 
			
		||||
  versionCode: 50
 | 
			
		||||
  versionName: 2.1.1-c
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni: []
 | 
			
		||||
  commit: null
 | 
			
		||||
  disable: Labelled as pre-release, so skipped
 | 
			
		||||
  encoding: null
 | 
			
		||||
  extlibs: []
 | 
			
		||||
  forcevercode: false
 | 
			
		||||
  forceversion: false
 | 
			
		||||
  gradle: []
 | 
			
		||||
  gradleprops: []
 | 
			
		||||
  init: ''
 | 
			
		||||
  maven: null
 | 
			
		||||
  ndk: null
 | 
			
		||||
  novcheck: false
 | 
			
		||||
  oldsdkloc: false
 | 
			
		||||
  output: null
 | 
			
		||||
  patch: []
 | 
			
		||||
  postbuild: ''
 | 
			
		||||
  preassemble: []
 | 
			
		||||
  prebuild: ''
 | 
			
		||||
  rm: []
 | 
			
		||||
  scandelete: []
 | 
			
		||||
  scanignore: []
 | 
			
		||||
  srclibs: []
 | 
			
		||||
  subdir: null
 | 
			
		||||
  submodules: false
 | 
			
		||||
  sudo: ''
 | 
			
		||||
  target: null
 | 
			
		||||
  timeout: null
 | 
			
		||||
  versionCode: 51
 | 
			
		||||
  versionName: 2.1.2
 | 
			
		||||
Categories:
 | 
			
		||||
- System
 | 
			
		||||
Changelog: ''
 | 
			
		||||
CurrentVersion: 2.1.2
 | 
			
		||||
CurrentVersionCode: 49
 | 
			
		||||
Description: 'To configure, go to "Settings => Accounts & Sync => Add Account". Depending
 | 
			
		||||
  on
 | 
			
		||||
 | 
			
		||||
  how many friends you have, the first import might take a while, so be patient.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  * Facebook does not allow to export phone numbers or emails: only names, pictures
 | 
			
		||||
  and statuses are synced.
 | 
			
		||||
 | 
			
		||||
  * Facebook users have the option to block one or all apps: if they opt for that,
 | 
			
		||||
  they will be EXCLUDED from your friends list.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  Appbrain SDK was removed before building.'
 | 
			
		||||
Disabled: null
 | 
			
		||||
Donate: null
 | 
			
		||||
FlattrID: null
 | 
			
		||||
IssueTracker: https://github.com/loadrunner/Facebook-Contact-Sync/issues
 | 
			
		||||
Liberapay: null
 | 
			
		||||
License: GPL-3.0-only
 | 
			
		||||
Litecoin: null
 | 
			
		||||
MaintainerNotes: ''
 | 
			
		||||
Name: null
 | 
			
		||||
NoSourceSince: ''
 | 
			
		||||
OpenCollective: null
 | 
			
		||||
Provides: null
 | 
			
		||||
Repo: https://github.com/loadrunner/Facebook-Contact-Sync.git
 | 
			
		||||
RepoType: git
 | 
			
		||||
RequiresRoot: false
 | 
			
		||||
SourceCode: https://github.com/loadrunner/Facebook-Contact-Sync
 | 
			
		||||
Summary: Sync your Facebook Contacts
 | 
			
		||||
Translation: ''
 | 
			
		||||
UpdateCheckData: null
 | 
			
		||||
UpdateCheckIgnore: null
 | 
			
		||||
UpdateCheckMode: None
 | 
			
		||||
UpdateCheckName: null
 | 
			
		||||
VercodeOperation: []
 | 
			
		||||
WebSite: ''
 | 
			
		||||
added: null
 | 
			
		||||
id: app.with.special.build.params
 | 
			
		||||
lastUpdated: null
 | 
			
		||||
metadatapath: metadata/app.with.special.build.params.yml
 | 
			
		||||
| 
						 | 
				
			
			@ -1,6 +1,8 @@
 | 
			
		|||
AllowedAPKSigningKeys: []
 | 
			
		||||
AntiFeatures:
 | 
			
		||||
- NonFreeNet
 | 
			
		||||
  NoSourceSince:
 | 
			
		||||
    en-US: '1.5'
 | 
			
		||||
  NonFreeNet: {}
 | 
			
		||||
ArchivePolicy: 4 versions
 | 
			
		||||
AuthorEmail: null
 | 
			
		||||
AuthorName: null
 | 
			
		||||
| 
						 | 
				
			
			@ -13,9 +15,9 @@ Builds:
 | 
			
		|||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures:
 | 
			
		||||
  - KnownVuln
 | 
			
		||||
  - UpstreamNonFree
 | 
			
		||||
  - NonFreeAssets
 | 
			
		||||
    KnownVuln: {}
 | 
			
		||||
    NonFreeAssets: {}
 | 
			
		||||
    UpstreamNonFree: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni: []
 | 
			
		||||
| 
						 | 
				
			
			@ -50,7 +52,7 @@ Builds:
 | 
			
		|||
  versionName: '1.2'
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni: []
 | 
			
		||||
| 
						 | 
				
			
			@ -85,7 +87,7 @@ Builds:
 | 
			
		|||
  versionName: '1.3'
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni: []
 | 
			
		||||
| 
						 | 
				
			
			@ -120,7 +122,7 @@ Builds:
 | 
			
		|||
  versionName: '1.4'
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni: []
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
AllowedAPKSigningKeys: []
 | 
			
		||||
AntiFeatures: []
 | 
			
		||||
AntiFeatures: {}
 | 
			
		||||
ArchivePolicy: null
 | 
			
		||||
AuthorEmail: null
 | 
			
		||||
AuthorName: null
 | 
			
		||||
| 
						 | 
				
			
			@ -11,7 +11,7 @@ Bitcoin: null
 | 
			
		|||
Builds:
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni:
 | 
			
		||||
| 
						 | 
				
			
			@ -47,7 +47,7 @@ Builds:
 | 
			
		|||
  versionName: '1.12'
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni:
 | 
			
		||||
| 
						 | 
				
			
			@ -84,7 +84,7 @@ Builds:
 | 
			
		|||
  versionName: '1.15'
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni:
 | 
			
		||||
| 
						 | 
				
			
			@ -121,7 +121,7 @@ Builds:
 | 
			
		|||
  versionName: '1.18'
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni:
 | 
			
		||||
| 
						 | 
				
			
			@ -158,7 +158,7 @@ Builds:
 | 
			
		|||
  versionName: '1.19'
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni:
 | 
			
		||||
| 
						 | 
				
			
			@ -195,7 +195,7 @@ Builds:
 | 
			
		|||
  versionName: '1.20'
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni:
 | 
			
		||||
| 
						 | 
				
			
			@ -232,7 +232,7 @@ Builds:
 | 
			
		|||
  versionName: '1.21'
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni: []
 | 
			
		||||
| 
						 | 
				
			
			@ -267,7 +267,7 @@ Builds:
 | 
			
		|||
  versionName: '1.23'
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni:
 | 
			
		||||
| 
						 | 
				
			
			@ -304,7 +304,7 @@ Builds:
 | 
			
		|||
  versionName: '1.24'
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni:
 | 
			
		||||
| 
						 | 
				
			
			@ -341,7 +341,7 @@ Builds:
 | 
			
		|||
  versionName: '1.25'
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni:
 | 
			
		||||
| 
						 | 
				
			
			@ -378,7 +378,7 @@ Builds:
 | 
			
		|||
  versionName: '1.26'
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni:
 | 
			
		||||
| 
						 | 
				
			
			@ -415,7 +415,7 @@ Builds:
 | 
			
		|||
  versionName: '1.27'
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni:
 | 
			
		||||
| 
						 | 
				
			
			@ -453,7 +453,7 @@ Builds:
 | 
			
		|||
  versionName: '1.29'
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni: []
 | 
			
		||||
| 
						 | 
				
			
			@ -491,7 +491,7 @@ Builds:
 | 
			
		|||
  versionName: '1.32'
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni: []
 | 
			
		||||
| 
						 | 
				
			
			@ -528,7 +528,7 @@ Builds:
 | 
			
		|||
  versionName: '1.33'
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni: []
 | 
			
		||||
| 
						 | 
				
			
			@ -566,7 +566,7 @@ Builds:
 | 
			
		|||
  versionName: '1.34'
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni: []
 | 
			
		||||
| 
						 | 
				
			
			@ -604,7 +604,7 @@ Builds:
 | 
			
		|||
  versionName: '1.35'
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni: []
 | 
			
		||||
| 
						 | 
				
			
			@ -642,7 +642,7 @@ Builds:
 | 
			
		|||
  versionName: '1.36'
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni: []
 | 
			
		||||
| 
						 | 
				
			
			@ -684,7 +684,7 @@ Builds:
 | 
			
		|||
  - android-libs/ActionBarSherlock
 | 
			
		||||
  - android-libs/HtmlSpanner/htmlspanner
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni:
 | 
			
		||||
| 
						 | 
				
			
			@ -734,7 +734,7 @@ Builds:
 | 
			
		|||
  - android-libs/ActionBarSherlock
 | 
			
		||||
  - android-libs/HtmlSpanner/htmlspanner
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni:
 | 
			
		||||
| 
						 | 
				
			
			@ -780,7 +780,7 @@ Builds:
 | 
			
		|||
  versionName: '2.3'
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni:
 | 
			
		||||
| 
						 | 
				
			
			@ -818,7 +818,7 @@ Builds:
 | 
			
		|||
  versionName: '2.6'
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni:
 | 
			
		||||
| 
						 | 
				
			
			@ -856,7 +856,7 @@ Builds:
 | 
			
		|||
  versionName: '2.7'
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni:
 | 
			
		||||
| 
						 | 
				
			
			@ -894,7 +894,7 @@ Builds:
 | 
			
		|||
  versionName: '2.8'
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni:
 | 
			
		||||
| 
						 | 
				
			
			@ -932,7 +932,7 @@ Builds:
 | 
			
		|||
  versionName: 2.8.1
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni:
 | 
			
		||||
| 
						 | 
				
			
			@ -970,7 +970,7 @@ Builds:
 | 
			
		|||
  versionName: '2.9'
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni:
 | 
			
		||||
| 
						 | 
				
			
			@ -1008,7 +1008,7 @@ Builds:
 | 
			
		|||
  versionName: 2.9.1
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni:
 | 
			
		||||
| 
						 | 
				
			
			@ -1046,7 +1046,7 @@ Builds:
 | 
			
		|||
  versionName: 2.9.2
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
AllowedAPKSigningKeys: []
 | 
			
		||||
AntiFeatures: []
 | 
			
		||||
AntiFeatures: {}
 | 
			
		||||
ArchivePolicy: null
 | 
			
		||||
AuthorEmail: null
 | 
			
		||||
AuthorName: null
 | 
			
		||||
| 
						 | 
				
			
			@ -11,7 +11,7 @@ Bitcoin: null
 | 
			
		|||
Builds:
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni: []
 | 
			
		||||
| 
						 | 
				
			
			@ -72,7 +72,7 @@ Builds:
 | 
			
		|||
  versionName: 0.3.3
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni: []
 | 
			
		||||
| 
						 | 
				
			
			@ -115,7 +115,7 @@ Builds:
 | 
			
		|||
  versionName: 0.3.3
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni: []
 | 
			
		||||
| 
						 | 
				
			
			@ -156,7 +156,7 @@ Builds:
 | 
			
		|||
  versionName: 0.4.2
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni: []
 | 
			
		||||
| 
						 | 
				
			
			@ -197,7 +197,7 @@ Builds:
 | 
			
		|||
  versionName: 0.5.1
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni: []
 | 
			
		||||
| 
						 | 
				
			
			@ -237,7 +237,7 @@ Builds:
 | 
			
		|||
  versionName: 0.5.2
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni: []
 | 
			
		||||
| 
						 | 
				
			
			@ -277,7 +277,7 @@ Builds:
 | 
			
		|||
  versionName: 0.5.3
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni: []
 | 
			
		||||
| 
						 | 
				
			
			@ -317,7 +317,7 @@ Builds:
 | 
			
		|||
  versionName: 0.5.4
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build: ''
 | 
			
		||||
  buildjni: []
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
AllowedAPKSigningKeys: []
 | 
			
		||||
AntiFeatures: []
 | 
			
		||||
AntiFeatures: {}
 | 
			
		||||
ArchivePolicy: 9 versions
 | 
			
		||||
AuthorEmail: null
 | 
			
		||||
AuthorName: null
 | 
			
		||||
| 
						 | 
				
			
			@ -14,7 +14,7 @@ Builds:
 | 
			
		|||
  - ../java-libs/SlidingMenu
 | 
			
		||||
  - ../java-libs/ActionBarSherlock
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ANDROID_ABI=armeabi-v7a ./compile.sh release
 | 
			
		||||
| 
						 | 
				
			
			@ -54,7 +54,7 @@ Builds:
 | 
			
		|||
  - ../java-libs/SlidingMenu
 | 
			
		||||
  - ../java-libs/ActionBarSherlock
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ANDROID_ABI=armeabi ./compile.sh release
 | 
			
		||||
| 
						 | 
				
			
			@ -94,7 +94,7 @@ Builds:
 | 
			
		|||
  - ../java-libs/SlidingMenu
 | 
			
		||||
  - ../java-libs/ActionBarSherlock
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ANDROID_ABI=x86 ./compile.sh release
 | 
			
		||||
| 
						 | 
				
			
			@ -134,7 +134,7 @@ Builds:
 | 
			
		|||
  - ../java-libs/SlidingMenu
 | 
			
		||||
  - ../java-libs/ActionBarSherlock
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ANDROID_ABI=mips ./compile.sh release
 | 
			
		||||
| 
						 | 
				
			
			@ -171,7 +171,7 @@ Builds:
 | 
			
		|||
  versionName: 0.0.11-mips
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ANDROID_ABI=mips ./compile.sh release
 | 
			
		||||
| 
						 | 
				
			
			@ -210,7 +210,7 @@ Builds:
 | 
			
		|||
  versionName: 0.1.3-MIPS
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ANDROID_ABI=x86 ./compile.sh release
 | 
			
		||||
| 
						 | 
				
			
			@ -249,7 +249,7 @@ Builds:
 | 
			
		|||
  versionName: 0.1.3-x86
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ANDROID_ABI=armeabi ./compile.sh release
 | 
			
		||||
| 
						 | 
				
			
			@ -288,7 +288,7 @@ Builds:
 | 
			
		|||
  versionName: 0.1.3-ARM
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ANDROID_ABI=armeabi-v7a ./compile.sh release
 | 
			
		||||
| 
						 | 
				
			
			@ -327,7 +327,7 @@ Builds:
 | 
			
		|||
  versionName: 0.1.3-ARMv7
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ANDROID_ABI=x86 ./compile.sh release
 | 
			
		||||
| 
						 | 
				
			
			@ -365,7 +365,7 @@ Builds:
 | 
			
		|||
  versionName: 0.9.0
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ANDROID_ABI=armeabi-v7a ./compile.sh release
 | 
			
		||||
| 
						 | 
				
			
			@ -403,7 +403,7 @@ Builds:
 | 
			
		|||
  versionName: 0.9.0
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ANDROID_ABI=x86 ./compile.sh release
 | 
			
		||||
| 
						 | 
				
			
			@ -441,7 +441,7 @@ Builds:
 | 
			
		|||
  versionName: 0.9.1
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ANDROID_ABI=armeabi-v7a ./compile.sh release
 | 
			
		||||
| 
						 | 
				
			
			@ -479,7 +479,7 @@ Builds:
 | 
			
		|||
  versionName: 0.9.1
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ANDROID_ABI=x86 ./compile.sh release
 | 
			
		||||
| 
						 | 
				
			
			@ -517,7 +517,7 @@ Builds:
 | 
			
		|||
  versionName: 0.9.5
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ANDROID_ABI=armeabi-v7a ./compile.sh release
 | 
			
		||||
| 
						 | 
				
			
			@ -555,7 +555,7 @@ Builds:
 | 
			
		|||
  versionName: 0.9.5
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ANDROID_ABI=x86 ./compile.sh release
 | 
			
		||||
| 
						 | 
				
			
			@ -593,7 +593,7 @@ Builds:
 | 
			
		|||
  versionName: 0.9.6
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ANDROID_ABI=armeabi-v7a ./compile.sh release
 | 
			
		||||
| 
						 | 
				
			
			@ -631,7 +631,7 @@ Builds:
 | 
			
		|||
  versionName: 0.9.6
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ANDROID_ABI=x86 ./compile.sh release
 | 
			
		||||
| 
						 | 
				
			
			@ -669,7 +669,7 @@ Builds:
 | 
			
		|||
  versionName: 0.9.7
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ANDROID_ABI=armeabi-v7a ./compile.sh release
 | 
			
		||||
| 
						 | 
				
			
			@ -707,7 +707,7 @@ Builds:
 | 
			
		|||
  versionName: 0.9.7
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ANDROID_ABI=mips ./compile.sh release
 | 
			
		||||
| 
						 | 
				
			
			@ -745,7 +745,7 @@ Builds:
 | 
			
		|||
  versionName: 0.9.7.1
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ANDROID_ABI=x86 ./compile.sh release
 | 
			
		||||
| 
						 | 
				
			
			@ -783,7 +783,7 @@ Builds:
 | 
			
		|||
  versionName: 0.9.7.1
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ANDROID_ABI=armeabi-v7a ./compile.sh release
 | 
			
		||||
| 
						 | 
				
			
			@ -821,7 +821,7 @@ Builds:
 | 
			
		|||
  versionName: 0.9.7.1
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ANDROID_ABI=x86 ./compile.sh release
 | 
			
		||||
| 
						 | 
				
			
			@ -859,7 +859,7 @@ Builds:
 | 
			
		|||
  versionName: 0.9.8
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ANDROID_ABI=armeabi ./compile.sh release
 | 
			
		||||
| 
						 | 
				
			
			@ -897,7 +897,7 @@ Builds:
 | 
			
		|||
  versionName: 0.9.8
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ANDROID_ABI=armeabi-v7a ./compile.sh release
 | 
			
		||||
| 
						 | 
				
			
			@ -935,7 +935,7 @@ Builds:
 | 
			
		|||
  versionName: 0.9.8
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ANDROID_ABI=x86 ./compile.sh release
 | 
			
		||||
| 
						 | 
				
			
			@ -973,7 +973,7 @@ Builds:
 | 
			
		|||
  versionName: 0.9.9
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ANDROID_ABI=armeabi ./compile.sh release
 | 
			
		||||
| 
						 | 
				
			
			@ -1011,7 +1011,7 @@ Builds:
 | 
			
		|||
  versionName: 0.9.9
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ANDROID_ABI=armeabi-v7a ./compile.sh release
 | 
			
		||||
| 
						 | 
				
			
			@ -1049,7 +1049,7 @@ Builds:
 | 
			
		|||
  versionName: 0.9.9
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ANDROID_ABI=x86 ./compile.sh release
 | 
			
		||||
| 
						 | 
				
			
			@ -1087,7 +1087,7 @@ Builds:
 | 
			
		|||
  versionName: 0.9.10
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ANDROID_ABI=armeabi ./compile.sh release
 | 
			
		||||
| 
						 | 
				
			
			@ -1125,7 +1125,7 @@ Builds:
 | 
			
		|||
  versionName: 0.9.10
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ANDROID_ABI=armeabi-v7a ./compile.sh release
 | 
			
		||||
| 
						 | 
				
			
			@ -1163,7 +1163,7 @@ Builds:
 | 
			
		|||
  versionName: 0.9.10
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ANDROID_ABI=x86 ./compile.sh release
 | 
			
		||||
| 
						 | 
				
			
			@ -1201,7 +1201,7 @@ Builds:
 | 
			
		|||
  versionName: 1.0.0
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ANDROID_ABI=armeabi ./compile.sh release
 | 
			
		||||
| 
						 | 
				
			
			@ -1239,7 +1239,7 @@ Builds:
 | 
			
		|||
  versionName: 1.0.0
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ANDROID_ABI=armeabi-v7a ./compile.sh release
 | 
			
		||||
| 
						 | 
				
			
			@ -1277,7 +1277,7 @@ Builds:
 | 
			
		|||
  versionName: 1.0.0
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ANDROID_ABI=x86 ./compile.sh release
 | 
			
		||||
| 
						 | 
				
			
			@ -1315,7 +1315,7 @@ Builds:
 | 
			
		|||
  versionName: 1.0.1
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ANDROID_ABI=armeabi ./compile.sh release
 | 
			
		||||
| 
						 | 
				
			
			@ -1353,7 +1353,7 @@ Builds:
 | 
			
		|||
  versionName: 1.0.1
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ANDROID_ABI=armeabi-v7a ./compile.sh release
 | 
			
		||||
| 
						 | 
				
			
			@ -1391,7 +1391,7 @@ Builds:
 | 
			
		|||
  versionName: 1.0.1
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ./compile.sh -a "armeabi" --release
 | 
			
		||||
| 
						 | 
				
			
			@ -1431,7 +1431,7 @@ Builds:
 | 
			
		|||
  versionName: 1.1.3
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ./compile.sh -a "armeabi-v7a" --release
 | 
			
		||||
| 
						 | 
				
			
			@ -1471,7 +1471,7 @@ Builds:
 | 
			
		|||
  versionName: 1.1.3
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ./compile.sh -a "x86" --release
 | 
			
		||||
| 
						 | 
				
			
			@ -1511,7 +1511,7 @@ Builds:
 | 
			
		|||
  versionName: 1.1.3
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ./compile.sh -a "armeabi" --release
 | 
			
		||||
| 
						 | 
				
			
			@ -1551,7 +1551,7 @@ Builds:
 | 
			
		|||
  versionName: 1.1.5
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ./compile.sh -a "armeabi-v7a" --release
 | 
			
		||||
| 
						 | 
				
			
			@ -1591,7 +1591,7 @@ Builds:
 | 
			
		|||
  versionName: 1.1.5
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ./compile.sh -a "x86" --release
 | 
			
		||||
| 
						 | 
				
			
			@ -1631,7 +1631,7 @@ Builds:
 | 
			
		|||
  versionName: 1.1.5
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ./compile.sh -a "armeabi" --release
 | 
			
		||||
| 
						 | 
				
			
			@ -1671,7 +1671,7 @@ Builds:
 | 
			
		|||
  versionName: 1.1.6
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ./compile.sh -a "armeabi-v7a" --release
 | 
			
		||||
| 
						 | 
				
			
			@ -1711,7 +1711,7 @@ Builds:
 | 
			
		|||
  versionName: 1.1.6
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ./compile.sh -a "x86" --release
 | 
			
		||||
| 
						 | 
				
			
			@ -1751,7 +1751,7 @@ Builds:
 | 
			
		|||
  versionName: 1.1.6
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ./compile.sh -a "armeabi" --release
 | 
			
		||||
| 
						 | 
				
			
			@ -1791,7 +1791,7 @@ Builds:
 | 
			
		|||
  versionName: 1.2.0
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ./compile.sh -a "armeabi-v7a" --release
 | 
			
		||||
| 
						 | 
				
			
			@ -1831,7 +1831,7 @@ Builds:
 | 
			
		|||
  versionName: 1.2.0
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ./compile.sh -a "x86" --release
 | 
			
		||||
| 
						 | 
				
			
			@ -1871,7 +1871,7 @@ Builds:
 | 
			
		|||
  versionName: 1.2.0
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ./compile.sh -a "armeabi" --release
 | 
			
		||||
| 
						 | 
				
			
			@ -1911,7 +1911,7 @@ Builds:
 | 
			
		|||
  versionName: 1.2.1
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ./compile.sh -a "armeabi-v7a" --release
 | 
			
		||||
| 
						 | 
				
			
			@ -1951,7 +1951,7 @@ Builds:
 | 
			
		|||
  versionName: 1.2.1
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ./compile.sh -a "x86" --release
 | 
			
		||||
| 
						 | 
				
			
			@ -1991,7 +1991,7 @@ Builds:
 | 
			
		|||
  versionName: 1.2.1
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ./compile.sh -a "armeabi" --release
 | 
			
		||||
| 
						 | 
				
			
			@ -2031,7 +2031,7 @@ Builds:
 | 
			
		|||
  versionName: 1.2.2
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ./compile.sh -a "armeabi-v7a" --release
 | 
			
		||||
| 
						 | 
				
			
			@ -2071,7 +2071,7 @@ Builds:
 | 
			
		|||
  versionName: 1.2.2
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ./compile.sh -a "x86" --release
 | 
			
		||||
| 
						 | 
				
			
			@ -2111,7 +2111,7 @@ Builds:
 | 
			
		|||
  versionName: 1.2.2
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ./compile.sh -a "armeabi" --release
 | 
			
		||||
| 
						 | 
				
			
			@ -2151,7 +2151,7 @@ Builds:
 | 
			
		|||
  versionName: 1.2.3
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ./compile.sh -a "armeabi-v7a" --release
 | 
			
		||||
| 
						 | 
				
			
			@ -2191,7 +2191,7 @@ Builds:
 | 
			
		|||
  versionName: 1.2.3
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ./compile.sh -a "x86" --release
 | 
			
		||||
| 
						 | 
				
			
			@ -2231,7 +2231,7 @@ Builds:
 | 
			
		|||
  versionName: 1.2.3
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ./compile.sh -a "armeabi" --release
 | 
			
		||||
| 
						 | 
				
			
			@ -2271,7 +2271,7 @@ Builds:
 | 
			
		|||
  versionName: 1.2.4
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ./compile.sh -a "armeabi-v7a" --release
 | 
			
		||||
| 
						 | 
				
			
			@ -2311,7 +2311,7 @@ Builds:
 | 
			
		|||
  versionName: 1.2.4
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ./compile.sh -a "x86" --release
 | 
			
		||||
| 
						 | 
				
			
			@ -2351,7 +2351,7 @@ Builds:
 | 
			
		|||
  versionName: 1.2.4
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ./compile.sh -a "armeabi" --release
 | 
			
		||||
| 
						 | 
				
			
			@ -2391,7 +2391,7 @@ Builds:
 | 
			
		|||
  versionName: 1.2.5
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ./compile.sh -a "armeabi-v7a" --release
 | 
			
		||||
| 
						 | 
				
			
			@ -2431,7 +2431,7 @@ Builds:
 | 
			
		|||
  versionName: 1.2.5
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ./compile.sh -a "x86" --release
 | 
			
		||||
| 
						 | 
				
			
			@ -2471,7 +2471,7 @@ Builds:
 | 
			
		|||
  versionName: 1.2.5
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ./compile.sh -a "armeabi" --release
 | 
			
		||||
| 
						 | 
				
			
			@ -2511,7 +2511,7 @@ Builds:
 | 
			
		|||
  versionName: 1.2.6
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ./compile.sh -a "armeabi-v7a" --release
 | 
			
		||||
| 
						 | 
				
			
			@ -2551,7 +2551,7 @@ Builds:
 | 
			
		|||
  versionName: 1.2.6
 | 
			
		||||
- androidupdate: []
 | 
			
		||||
  antcommands: []
 | 
			
		||||
  antifeatures: []
 | 
			
		||||
  antifeatures: {}
 | 
			
		||||
  binary: null
 | 
			
		||||
  build:
 | 
			
		||||
  - cd ../ && ./compile.sh -a "x86" --release
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,8 +3,8 @@
 | 
			
		|||
  "version": 20002,
 | 
			
		||||
  "index": {
 | 
			
		||||
    "name": "/index-v2.json",
 | 
			
		||||
    "sha256": "a3c7e88a522a7228937e5c3d760fc239e3578e292035d88478d32fec9ff5eb54",
 | 
			
		||||
    "size": 52314,
 | 
			
		||||
    "sha256": "f4979b9db840cb51a99e80c20da676ba42b13133dbaa4819673bc43ed2ffc3f3",
 | 
			
		||||
    "size": 52481,
 | 
			
		||||
    "numPackages": 10
 | 
			
		||||
  },
 | 
			
		||||
  "diffs": {}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -568,7 +568,9 @@
 | 
			
		|||
            ]
 | 
			
		||||
          },
 | 
			
		||||
          "antiFeatures": {
 | 
			
		||||
            "NoSourceSince": {},
 | 
			
		||||
            "NoSourceSince": {
 | 
			
		||||
              "en-US": "1.5"
 | 
			
		||||
            },
 | 
			
		||||
            "NonFreeNet": {}
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
| 
						 | 
				
			
			@ -602,7 +604,9 @@
 | 
			
		|||
            ]
 | 
			
		||||
          },
 | 
			
		||||
          "antiFeatures": {
 | 
			
		||||
            "NoSourceSince": {},
 | 
			
		||||
            "NoSourceSince": {
 | 
			
		||||
              "en-US": "1.5"
 | 
			
		||||
            },
 | 
			
		||||
            "NonFreeNet": {}
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
| 
						 | 
				
			
			@ -645,7 +649,9 @@
 | 
			
		|||
            ]
 | 
			
		||||
          },
 | 
			
		||||
          "antiFeatures": {
 | 
			
		||||
            "NoSourceSince": {},
 | 
			
		||||
            "NoSourceSince": {
 | 
			
		||||
              "en-US": "1.5"
 | 
			
		||||
            },
 | 
			
		||||
            "NonFreeNet": {}
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
| 
						 | 
				
			
			@ -689,7 +695,9 @@
 | 
			
		|||
          },
 | 
			
		||||
          "antiFeatures": {
 | 
			
		||||
            "KnownVuln": {},
 | 
			
		||||
            "NoSourceSince": {},
 | 
			
		||||
            "NoSourceSince": {
 | 
			
		||||
              "en-US": "1.5"
 | 
			
		||||
            },
 | 
			
		||||
            "NonFreeAssets": {},
 | 
			
		||||
            "NonFreeNet": {},
 | 
			
		||||
            "UpstreamNonFree": {}
 | 
			
		||||
| 
						 | 
				
			
			@ -1395,4 +1403,4 @@
 | 
			
		|||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -45,11 +45,11 @@ class RewriteMetaTest(unittest.TestCase):
 | 
			
		|||
                'versionCode': 3,
 | 
			
		||||
                'commit': '6a548e4b19',
 | 
			
		||||
                'target': 'android-10',
 | 
			
		||||
                'antifeatures': [
 | 
			
		||||
                    'KnownVuln',
 | 
			
		||||
                    'UpstreamNonFree',
 | 
			
		||||
                    'NonFreeAssets',
 | 
			
		||||
                ],
 | 
			
		||||
                'antifeatures': {
 | 
			
		||||
                    'KnownVuln': {},
 | 
			
		||||
                    'UpstreamNonFree': {},
 | 
			
		||||
                    'NonFreeAssets': {},
 | 
			
		||||
                },
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -68,6 +68,24 @@ class RewriteMetaTest(unittest.TestCase):
 | 
			
		|||
            },
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_remove_blank_flags_from_builds_org_adaway_52(self):
 | 
			
		||||
        """Unset fields in Builds: entries should be removed."""
 | 
			
		||||
        appid = 'org.adaway'
 | 
			
		||||
        app = metadata.read_metadata({appid: -1})[appid]
 | 
			
		||||
        builds = rewritemeta.remove_blank_flags_from_builds(app.get('Builds'))
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            builds[-1],
 | 
			
		||||
            {
 | 
			
		||||
                'buildjni': ['yes'],
 | 
			
		||||
                'commit': 'v3.0',
 | 
			
		||||
                'gradle': ['yes'],
 | 
			
		||||
                'preassemble': ['renameExecutables'],
 | 
			
		||||
                'subdir': 'AdAway',
 | 
			
		||||
                'versionCode': 52,
 | 
			
		||||
                'versionName': '3.0',
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_remove_blank_flags_from_builds_no_builds(self):
 | 
			
		||||
        """Unset fields in Builds: entries should be removed."""
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
| 
						 | 
				
			
			@ -78,6 +96,24 @@ class RewriteMetaTest(unittest.TestCase):
 | 
			
		|||
            rewritemeta.remove_blank_flags_from_builds(dict()),
 | 
			
		||||
            list(),
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            rewritemeta.remove_blank_flags_from_builds(list()),
 | 
			
		||||
            list(),
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            rewritemeta.remove_blank_flags_from_builds(set()),
 | 
			
		||||
            list(),
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            rewritemeta.remove_blank_flags_from_builds(tuple()),
 | 
			
		||||
            list(),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_remove_blank_flags_from_builds_0_is_a_value(self):
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            rewritemeta.remove_blank_flags_from_builds([{'versionCode': 0}]),
 | 
			
		||||
            [{'versionCode': 0}],
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_rewrite_no_builds(self):
 | 
			
		||||
        os.chdir(self.testdir)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -768,7 +768,7 @@ class UpdateTest(unittest.TestCase):
 | 
			
		|||
                                                 '-1': 'res/drawable-mdpi-v4/icon_launcher.png'})
 | 
			
		||||
        self.assertEqual(apk_info['icons'], {})
 | 
			
		||||
        self.assertEqual(apk_info['features'], [])
 | 
			
		||||
        self.assertEqual(apk_info['antiFeatures'], set())
 | 
			
		||||
        self.assertEqual(apk_info['antiFeatures'], dict())
 | 
			
		||||
        self.assertEqual(apk_info['versionName'], 'v1.6pre2')
 | 
			
		||||
        self.assertEqual(apk_info['hash'],
 | 
			
		||||
                         '897486e1f857c6c0ee32ccbad0e1b8cd82f6d0e65a44a23f13f852d2b63a18c8')
 | 
			
		||||
| 
						 | 
				
			
			@ -819,7 +819,7 @@ class UpdateTest(unittest.TestCase):
 | 
			
		|||
            'hashType': 'sha256',
 | 
			
		||||
            'packageName': 'no.min.target.sdk',
 | 
			
		||||
            'features': [],
 | 
			
		||||
            'antiFeatures': set(),
 | 
			
		||||
            'antiFeatures': dict(),
 | 
			
		||||
            'size': 14102,
 | 
			
		||||
            'sig': 'b4964fd759edaa54e65bb476d0276880',
 | 
			
		||||
            'versionName': '1.2-fake',
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue