1
0
mirror of https://gitlab.com/fdroid/fdroidserver.git synced 2024-07-04 16:30:12 +02:00

define "string map" type for new Anti-Features explanations

closes #683
This commit is contained in:
Hans-Christoph Steiner 2023-04-21 10:00:40 +02:00
parent 6e62ea3614
commit 061ca38afd
27 changed files with 1188 additions and 194 deletions

View File

@ -597,7 +597,14 @@ include tests/main.TestCase
include tests/metadata/apk/info.guardianproject.urzip.yaml include tests/metadata/apk/info.guardianproject.urzip.yaml
include tests/metadata/apk/org.dyndns.fules.ck.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.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/com.politedroid.yml
include tests/metadata/dump/app.with.special.build.params.yaml
include tests/metadata/dump/com.politedroid.yaml include tests/metadata/dump/com.politedroid.yaml
include tests/metadata/dump/org.adaway.yaml include tests/metadata/dump/org.adaway.yaml
include tests/metadata/dump/org.smssecure.smssecure.yaml include tests/metadata/dump/org.smssecure.smssecure.yaml

View File

@ -546,6 +546,13 @@ def package_metadata(app, repodir):
def convert_version(version, 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 = {} ver = {}
if "added" in version: if "added" in version:
ver["added"] = convert_datetime(version["added"]) ver["added"] = convert_datetime(version["added"])
@ -555,7 +562,7 @@ def convert_version(version, app, repodir):
ver["file"] = { ver["file"] = {
"name": "/{}".format(version["apkName"]), "name": "/{}".format(version["apkName"]),
version["hashType"]: version["hash"], version["hashType"]: version["hash"],
"size": version["size"] "size": version["size"],
} }
ipfsCIDv1 = version.get("ipfsCIDv1") ipfsCIDv1 = version.get("ipfsCIDv1")
@ -619,24 +626,14 @@ def convert_version(version, app, repodir):
else: else:
manifest[en].append({"name": perm[0]}) manifest[en].append({"name": perm[0]})
antiFeatures = dict() # index-v2 has only per-version antifeatures, not per package.
if "AntiFeatures" in app and app["AntiFeatures"]: antiFeatures = app.get('AntiFeatures', {})
for antif in app["AntiFeatures"]: for name, descdict in version.get('antiFeatures', dict()).items():
# TODO: get reasons from fdroiddata antiFeatures[name] = descdict
# 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()
if antiFeatures: 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 "versionCode" in version:
if version["versionCode"] > app["CurrentVersionCode"]: 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()): for ikey, iname in sorted(lvalue.items()):
lordered[lkey][ikey] = iname lordered[lkey][ikey] = iname
app_dict['localized'] = lordered app_dict['localized'] = lordered
antiFeatures = app_dict.get('antiFeatures', []) # v1 uses a list of keys for Anti-Features
if apps[app_dict["packageName"]].get("NoSourceSince"): antiFeatures = app_dict.get('antiFeatures', dict()).keys()
antiFeatures.append("NoSourceSince")
if antiFeatures: if antiFeatures:
app_dict['antiFeatures'] = sorted(set(antiFeatures)) app_dict['antiFeatures'] = sorted(set(antiFeatures))
@ -915,6 +911,9 @@ def make_v1(apps, packages, repodir, repodict, requestsdict, fdroid_signing_key_
continue continue
if k in ('icon', 'icons', 'icons_src', 'ipfsCIDv1', 'name'): if k in ('icon', 'icons', 'icons_src', 'ipfsCIDv1', 'name'):
continue continue
if k == 'antiFeatures':
d[k] = sorted(v.keys())
continue
d[k] = v d[k] = v
json_name = 'index-v1.json' 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) antiFeatures = list(app.AntiFeatures)
if 'antiFeatures' in apklist[0]: if 'antiFeatures' in apklist[0]:
antiFeatures.extend(apklist[0]['antiFeatures']) antiFeatures.extend(apklist[0]['antiFeatures'])
if app.get("NoSourceSince"):
antiFeatures.append("NoSourceSince")
if antiFeatures: if antiFeatures:
afout = sorted(set(antiFeatures)) afout = sorted(set(antiFeatures))
addElementNonEmpty('antifeatures', ','.join(afout), doc, apel) addElementNonEmpty('antifeatures', ','.join(afout), doc, apel)

View File

@ -624,6 +624,17 @@ def check_app_field_types(app):
fieldtype=v.__class__.__name__, 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): def check_antiFeatures(app):

View File

@ -114,7 +114,7 @@ class App(dict):
super().__init__() super().__init__()
self.Disabled = None self.Disabled = None
self.AntiFeatures = [] self.AntiFeatures = dict()
self.Provides = None self.Provides = None
self.Categories = [] self.Categories = []
self.License = 'Unknown' self.License = 'Unknown'
@ -182,12 +182,13 @@ TYPE_SCRIPT = 5
TYPE_MULTILINE = 6 TYPE_MULTILINE = 6
TYPE_BUILD = 7 TYPE_BUILD = 7
TYPE_INT = 8 TYPE_INT = 8
TYPE_STRINGMAP = 9
fieldtypes = { fieldtypes = {
'Description': TYPE_MULTILINE, 'Description': TYPE_MULTILINE,
'MaintainerNotes': TYPE_MULTILINE, 'MaintainerNotes': TYPE_MULTILINE,
'Categories': TYPE_LIST, 'Categories': TYPE_LIST,
'AntiFeatures': TYPE_LIST, 'AntiFeatures': TYPE_STRINGMAP,
'AllowedAPKSigningKeys': TYPE_LIST, 'AllowedAPKSigningKeys': TYPE_LIST,
'Builds': TYPE_BUILD, 'Builds': TYPE_BUILD,
'VercodeOperation': TYPE_LIST, 'VercodeOperation': TYPE_LIST,
@ -277,7 +278,7 @@ class Build(dict):
self.antcommands = [] self.antcommands = []
self.postbuild = '' self.postbuild = ''
self.novcheck = False self.novcheck = False
self.antifeatures = [] self.antifeatures = dict()
if copydict: if copydict:
super().__init__(copydict) super().__init__(copydict)
return return
@ -358,7 +359,7 @@ flagtypes = {
'forceversion': TYPE_BOOL, 'forceversion': TYPE_BOOL,
'forcevercode': TYPE_BOOL, 'forcevercode': TYPE_BOOL,
'novcheck': TYPE_BOOL, 'novcheck': TYPE_BOOL,
'antifeatures': TYPE_LIST, 'antifeatures': TYPE_STRINGMAP,
'timeout': TYPE_INT, 'timeout': TYPE_INT,
} }
@ -649,8 +650,6 @@ def parse_metadata(metadatapath):
metadatapath = Path(metadatapath) metadatapath = Path(metadatapath)
app = App() app = App()
app.metadatapath = metadatapath.as_posix() app.metadatapath = metadatapath.as_posix()
if metadatapath.stem != '.fdroid':
app.id = metadatapath.stem
if metadatapath.suffix == '.yml': if metadatapath.suffix == '.yml':
with metadatapath.open('r', encoding='utf-8') as mf: with metadatapath.open('r', encoding='utf-8') as mf:
app.update(parse_yaml_metadata(mf)) app.update(parse_yaml_metadata(mf))
@ -659,6 +658,10 @@ def parse_metadata(metadatapath):
_('Unknown metadata format: {path} (use: *.yml)').format(path=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: if metadatapath.name != '.fdroid.yml' and app.Repo:
build_dir = common.get_build_dir(app) build_dir = common.get_build_dir(app)
metadata_in_repo = build_dir / '.fdroid.yml' metadata_in_repo = build_dir / '.fdroid.yml'
@ -770,6 +773,116 @@ def parse_yaml_metadata(mf):
return yamldata 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): def _normalize_type_string(v):
"""Normalize any data to TYPE_STRING. """Normalize any data to TYPE_STRING.
@ -787,6 +900,61 @@ def _normalize_type_string(v):
return str(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): def post_parse_yaml_metadata(yamldata):
"""Convert human-readable metadata data structures into consistent data structures. """Convert human-readable metadata data structures into consistent data structures.
@ -808,6 +976,9 @@ def post_parse_yaml_metadata(yamldata):
elif _fieldtype == TYPE_STRING: elif _fieldtype == TYPE_STRING:
if v or v == 0: if v or v == 0:
yamldata[k] = _normalize_type_string(v) 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: else:
if type(v) in (float, int): if type(v) in (float, int):
yamldata[k] = str(v) yamldata[k] = str(v)
@ -843,12 +1014,24 @@ def post_parse_yaml_metadata(yamldata):
build_flag=k, value=v build_flag=k, value=v
) )
) )
elif _flagtype == TYPE_STRINGMAP:
if v or v == 0:
build[k] = _normalize_type_stringmap(k, v)
builds.append(build) builds.append(build)
if builds: if builds:
yamldata['Builds'] = sorted(builds, key=lambda build: build['versionCode']) 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): def write_yaml(mf, app):
"""Write metadata in yaml format. """Write metadata in yaml format.

View File

@ -52,8 +52,9 @@ def remove_blank_flags_from_builds(builds):
for build in builds: for build in builds:
new = dict() new = dict()
for k in metadata.build_flags: for k in metadata.build_flags:
v = build[k] v = build.get(k)
if v is None or v is False or v == [] or v == '': # 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 continue
new[k] = v new[k] = v
newbuilds.append(new) newbuilds.append(new)

View File

@ -294,7 +294,7 @@ class ExodusSignatureDataController(SignatureDataController):
"warn_code_signatures": [tracker["code_signature"]], "warn_code_signatures": [tracker["code_signature"]],
# exodus also provides network signatures, unused atm. # exodus also provides network signatures, unused atm.
# "network_signatures": [tracker["network_signature"]], # "network_signatures": [tracker["network_signature"]],
"AntiFeatures": ["Tracking"], "AntiFeatures": ["Tracking"], # TODO
"license": "NonFree" # We assume all trackers in exodus "license": "NonFree" # We assume all trackers in exodus
# are non-free, although free # are non-free, although free
# trackers like piwik, acra, # trackers like piwik, acra,

View File

@ -158,7 +158,7 @@ def status_update_json(apps, apks):
for appid in apps: for appid in apps:
app = apps[appid] app = apps[appid]
for af in app.get('AntiFeatures', []): for af in app.get('AntiFeatures', dict()):
antiFeatures = output['antiFeatures'] # JSON camelCase antiFeatures = output['antiFeatures'] # JSON camelCase
if af not in antiFeatures: if af not in antiFeatures:
antiFeatures[af] = dict() antiFeatures[af] = dict()
@ -351,7 +351,8 @@ def get_cache():
if not isinstance(v, dict): if not isinstance(v, dict):
continue continue
if 'antiFeatures' in v: 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: if 'added' in v:
v['added'] = datetime.fromtimestamp(v['added']) 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. 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 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 # statically load this pattern
if not hasattr(has_known_vulnerability, "pattern"): if not hasattr(has_known_vulnerability, "pattern"):
@ -431,15 +432,23 @@ def has_known_vulnerability(filename):
logging.debug(_('"{path}" contains recent {name} ({version})') logging.debug(_('"{path}" contains recent {name} ({version})')
.format(path=filename, name=name, version=version)) .format(path=filename, name=name, version=version))
else: else:
logging.warning(_('"{path}" contains outdated {name} ({version})') msg = '"{path}" contains outdated {name} ({version})'
.format(path=filename, name=name, version=version)) logging.warning(
found_vuln = True _(msg).format(path=filename, name=name, version=version)
)
found_vuln += msg.format(
path=filename, name=name, version=version
)
found_vuln += '\n'
break break
elif name == 'AndroidManifest.xml' or name == 'classes.dex' or name.endswith('.so'): elif name == 'AndroidManifest.xml' or name == 'classes.dex' or name.endswith('.so'):
if name in files_in_apk: if name in files_in_apk:
logging.warning(_('{apkfilename} has multiple {name} files, looks like Master Key exploit!') msg = '{apkfilename} has multiple {name} files, looks like Master Key exploit!'
.format(apkfilename=filename, name=name)) logging.warning(
found_vuln = True _(msg).format(apkfilename=filename, name=name)
)
found_vuln += msg.format(apkfilename=filename, name=name)
found_vuln += '\n'
files_in_apk.add(name) files_in_apk.add(name)
return found_vuln return found_vuln
@ -545,7 +554,7 @@ def translate_per_build_anti_features(apps, apks):
if d: if d:
afl = d.get(apk['versionCode']) afl = d.get(apk['versionCode'])
if afl: if afl:
apk['antiFeatures'].update(afl) apk['antiFeatures'].update(afl) # TODO
def _get_localized_dict(app, locale): def _get_localized_dict(app, locale):
@ -1228,7 +1237,7 @@ def scan_apk(apk_file, require_signature=True):
'features': [], 'features': [],
'icons_src': {}, 'icons_src': {},
'icons': {}, 'icons': {},
'antiFeatures': set(), 'antiFeatures': {},
} }
ipfsCIDv1 = common.calculate_IPFS_cid(apk_file) ipfsCIDv1 = common.calculate_IPFS_cid(apk_file)
if ipfsCIDv1: if ipfsCIDv1:
@ -1263,8 +1272,9 @@ def scan_apk(apk_file, require_signature=True):
apk['minSdkVersion'] = 3 # aapt defaults to 3 as the min apk['minSdkVersion'] = 3 # aapt defaults to 3 as the min
# Check for known vulnerabilities # Check for known vulnerabilities
if has_known_vulnerability(apk_file): hkv = has_known_vulnerability(apk_file)
apk['antiFeatures'].add('KnownVuln') if hkv:
apk['antiFeatures']['KnownVuln'] = {DEFAULT_LOCALE: hkv}
return apk 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: if repodir == 'archive' or allow_disabled_algorithms:
try: try:
common.verify_deprecated_jar_signature(apkfile) common.verify_deprecated_jar_signature(apkfile)
apk['antiFeatures'].update(['KnownVuln', 'DisabledAlgorithm']) apk['antiFeatures'].update(['KnownVuln', 'DisabledAlgorithm']) # TODO
except VerificationException: except VerificationException:
skipapk = True skipapk = True
else: else:
@ -1885,7 +1895,7 @@ def archive_old_apks(apps, apks, archapks, repodir, archivedir, defaultkeepversi
for apk in all_app_apks: for apk in all_app_apks:
if len(keep) == keepversions: if len(keep) == keepversions:
break break
if 'antiFeatures' not in apk: if 'antiFeatures' not in apk: # TODO
keep.append(apk) keep.append(apk)
elif 'DisabledAlgorithm' not in apk['antiFeatures'] or disabled_algorithms_allowed(): elif 'DisabledAlgorithm' not in apk['antiFeatures'] or disabled_algorithms_allowed():
keep.append(apk) keep.append(apk)

View File

@ -1,5 +1,5 @@
AntiFeatures: AntiFeatures:
- UpstreamNonFree UpstreamNonFree: {}
Categories: Categories:
- System - System
License: GPL-3.0-only License: GPL-3.0-only
@ -77,6 +77,9 @@ Builds:
maven: yes@.. maven: yes@..
srclibs: srclibs:
- FacebookSDK@sdk-version-3.0.2 - FacebookSDK@sdk-version-3.0.2
antifeatures:
Tracking:
en-US: Uses the Facebook SDK.
- versionName: 2.1.1-c - versionName: 2.1.1-c
versionCode: 50 versionCode: 50

View File

@ -26,6 +26,7 @@ if localmodule not in sys.path:
import fdroidserver import fdroidserver
from fdroidserver import metadata from fdroidserver import metadata
from fdroidserver.exception import MetaDataException from fdroidserver.exception import MetaDataException
from fdroidserver.common import DEFAULT_LOCALE
def _get_mock_mf(s): def _get_mock_mf(s):
@ -216,6 +217,7 @@ class MetadataTest(unittest.TestCase):
yaml = ruamel.yaml.YAML(typ='safe') yaml = ruamel.yaml.YAML(typ='safe')
apps = fdroidserver.metadata.read_metadata() apps = fdroidserver.metadata.read_metadata()
for appid in ( for appid in (
'app.with.special.build.params',
'org.smssecure.smssecure', 'org.smssecure.smssecure',
'org.adaway', 'org.adaway',
'org.videolan.vlc', 'org.videolan.vlc',
@ -300,24 +302,24 @@ class MetadataTest(unittest.TestCase):
@mock.patch('git.Repo') @mock.patch('git.Repo')
def test_rewrite_yaml_special_build_params(self, git_Repo): def test_rewrite_yaml_special_build_params(self, git_Repo):
with tempfile.TemporaryDirectory() as testdir: """Test rewriting a plain YAML metadata file without localized files."""
testdir = Path(testdir) 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 # rewrite metadata
allapps = fdroidserver.metadata.read_metadata() allapps = fdroidserver.metadata.read_metadata({appid: -1})
for appid, app in allapps.items(): for appid, app in allapps.items():
if appid == 'app.with.special.build.params': metadata.write_metadata(file_name, app)
fdroidserver.metadata.write_metadata(
testdir / (appid + '.yml'), app
)
# assert rewrite result # assert rewrite result
self.maxDiff = None self.maxDiff = None
file_name = 'app.with.special.build.params.yml' self.assertEqual(
self.assertEqual( file_name.read_text(),
(testdir / file_name).read_text(encoding='utf-8'), (self.basedir / 'metadata-rewrite-yml' / file_name.name).read_text(),
(Path('metadata-rewrite-yml') / file_name).read_text(encoding='utf-8'), )
)
def test_normalize_type_string(self): def test_normalize_type_string(self):
"""TYPE_STRING currently has some quirky behavior.""" """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('false', metadata._normalize_type_string(False))
self.assertEqual('true', metadata._normalize_type_string(True)) 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): def test_post_parse_yaml_metadata(self):
yamldata = dict() yamldata = dict()
metadata.post_parse_yaml_metadata(yamldata) metadata.post_parse_yaml_metadata(yamldata)
@ -507,6 +521,96 @@ class MetadataTest(unittest.TestCase):
_warning.assert_called_once() _warning.assert_called_once()
_error.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): def test_parse_yaml_srclib_corrupt_file(self):
with tempfile.TemporaryDirectory() as testdir: with tempfile.TemporaryDirectory() as testdir:
testdir = Path(testdir) testdir = Path(testdir)
@ -741,6 +845,257 @@ class MetadataTest(unittest.TestCase):
self.assertNotIn('Provides', result) self.assertNotIn('Provides', result)
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): def test_write_yaml_1_line_scripts_as_string(self):
mf = io.StringIO() mf = io.StringIO()
app = fdroidserver.metadata.App() app = fdroidserver.metadata.App()
@ -1263,9 +1618,9 @@ class PostMetadataParseTest(unittest.TestCase):
fdroidserver.metadata.warnings_action = 'error' fdroidserver.metadata.warnings_action = 'error'
def _post_metadata_parse_app_list(self, from_yaml, expected): def _post_metadata_parse_app_list(self, from_yaml, expected):
app = {'AntiFeatures': from_yaml} app = {'AllowedAPKSigningKeys': from_yaml}
metadata.post_parse_yaml_metadata(app) metadata.post_parse_yaml_metadata(app)
return {'AntiFeatures': expected}, app return {'AllowedAPKSigningKeys': expected}, app
def _post_metadata_parse_app_string(self, from_yaml, expected): def _post_metadata_parse_app_string(self, from_yaml, expected):
app = {'Repo': from_yaml} app = {'Repo': from_yaml}

View File

@ -1,4 +1,4 @@
antiFeatures: !!set {} antiFeatures: {}
features: [] features: []
hash: abfb3adb7496611749e7abfb014c5c789e3a02489e48a5c3665110d1b1acd931 hash: abfb3adb7496611749e7abfb014c5c789e3a02489e48a5c3665110d1b1acd931
hashType: sha256 hashType: sha256

View File

@ -1,4 +1,4 @@
antiFeatures: !!set {} antiFeatures: {}
features: [] features: []
hash: 897486e1f857c6c0ee32ccbad0e1b8cd82f6d0e65a44a23f13f852d2b63a18c8 hash: 897486e1f857c6c0ee32ccbad0e1b8cd82f6d0e65a44a23f13f852d2b63a18c8
hashType: sha256 hashType: sha256

View File

@ -77,6 +77,8 @@ Builds:
maven: yes@.. maven: yes@..
srclibs: srclibs:
- FacebookSDK@sdk-version-3.0.2 - FacebookSDK@sdk-version-3.0.2
antifeatures:
Tracking: Uses the Facebook SDK.
- versionName: 2.1.1-c - versionName: 2.1.1-c
versionCode: 50 versionCode: 50

View File

@ -0,0 +1 @@
includes ad lib

View File

@ -0,0 +1 @@
standard suspects

View File

@ -0,0 +1 @@
please no

View File

@ -0,0 +1 @@
Text from zh-CN/49_Tracking.txt

View File

@ -0,0 +1 @@
包括广告图书馆

View 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

View File

@ -1,6 +1,8 @@
AllowedAPKSigningKeys: [] AllowedAPKSigningKeys: []
AntiFeatures: AntiFeatures:
- NonFreeNet NoSourceSince:
en-US: '1.5'
NonFreeNet: {}
ArchivePolicy: 4 versions ArchivePolicy: 4 versions
AuthorEmail: null AuthorEmail: null
AuthorName: null AuthorName: null
@ -13,9 +15,9 @@ Builds:
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: antifeatures:
- KnownVuln KnownVuln: {}
- UpstreamNonFree NonFreeAssets: {}
- NonFreeAssets UpstreamNonFree: {}
binary: null binary: null
build: '' build: ''
buildjni: [] buildjni: []
@ -50,7 +52,7 @@ Builds:
versionName: '1.2' versionName: '1.2'
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: '' build: ''
buildjni: [] buildjni: []
@ -85,7 +87,7 @@ Builds:
versionName: '1.3' versionName: '1.3'
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: '' build: ''
buildjni: [] buildjni: []
@ -120,7 +122,7 @@ Builds:
versionName: '1.4' versionName: '1.4'
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: '' build: ''
buildjni: [] buildjni: []

View File

@ -1,5 +1,5 @@
AllowedAPKSigningKeys: [] AllowedAPKSigningKeys: []
AntiFeatures: [] AntiFeatures: {}
ArchivePolicy: null ArchivePolicy: null
AuthorEmail: null AuthorEmail: null
AuthorName: null AuthorName: null
@ -11,7 +11,7 @@ Bitcoin: null
Builds: Builds:
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: '' build: ''
buildjni: buildjni:
@ -47,7 +47,7 @@ Builds:
versionName: '1.12' versionName: '1.12'
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: '' build: ''
buildjni: buildjni:
@ -84,7 +84,7 @@ Builds:
versionName: '1.15' versionName: '1.15'
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: '' build: ''
buildjni: buildjni:
@ -121,7 +121,7 @@ Builds:
versionName: '1.18' versionName: '1.18'
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: '' build: ''
buildjni: buildjni:
@ -158,7 +158,7 @@ Builds:
versionName: '1.19' versionName: '1.19'
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: '' build: ''
buildjni: buildjni:
@ -195,7 +195,7 @@ Builds:
versionName: '1.20' versionName: '1.20'
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: '' build: ''
buildjni: buildjni:
@ -232,7 +232,7 @@ Builds:
versionName: '1.21' versionName: '1.21'
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: '' build: ''
buildjni: [] buildjni: []
@ -267,7 +267,7 @@ Builds:
versionName: '1.23' versionName: '1.23'
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: '' build: ''
buildjni: buildjni:
@ -304,7 +304,7 @@ Builds:
versionName: '1.24' versionName: '1.24'
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: '' build: ''
buildjni: buildjni:
@ -341,7 +341,7 @@ Builds:
versionName: '1.25' versionName: '1.25'
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: '' build: ''
buildjni: buildjni:
@ -378,7 +378,7 @@ Builds:
versionName: '1.26' versionName: '1.26'
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: '' build: ''
buildjni: buildjni:
@ -415,7 +415,7 @@ Builds:
versionName: '1.27' versionName: '1.27'
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: '' build: ''
buildjni: buildjni:
@ -453,7 +453,7 @@ Builds:
versionName: '1.29' versionName: '1.29'
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: '' build: ''
buildjni: [] buildjni: []
@ -491,7 +491,7 @@ Builds:
versionName: '1.32' versionName: '1.32'
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: '' build: ''
buildjni: [] buildjni: []
@ -528,7 +528,7 @@ Builds:
versionName: '1.33' versionName: '1.33'
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: '' build: ''
buildjni: [] buildjni: []
@ -566,7 +566,7 @@ Builds:
versionName: '1.34' versionName: '1.34'
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: '' build: ''
buildjni: [] buildjni: []
@ -604,7 +604,7 @@ Builds:
versionName: '1.35' versionName: '1.35'
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: '' build: ''
buildjni: [] buildjni: []
@ -642,7 +642,7 @@ Builds:
versionName: '1.36' versionName: '1.36'
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: '' build: ''
buildjni: [] buildjni: []
@ -684,7 +684,7 @@ Builds:
- android-libs/ActionBarSherlock - android-libs/ActionBarSherlock
- android-libs/HtmlSpanner/htmlspanner - android-libs/HtmlSpanner/htmlspanner
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: '' build: ''
buildjni: buildjni:
@ -734,7 +734,7 @@ Builds:
- android-libs/ActionBarSherlock - android-libs/ActionBarSherlock
- android-libs/HtmlSpanner/htmlspanner - android-libs/HtmlSpanner/htmlspanner
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: '' build: ''
buildjni: buildjni:
@ -780,7 +780,7 @@ Builds:
versionName: '2.3' versionName: '2.3'
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: '' build: ''
buildjni: buildjni:
@ -818,7 +818,7 @@ Builds:
versionName: '2.6' versionName: '2.6'
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: '' build: ''
buildjni: buildjni:
@ -856,7 +856,7 @@ Builds:
versionName: '2.7' versionName: '2.7'
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: '' build: ''
buildjni: buildjni:
@ -894,7 +894,7 @@ Builds:
versionName: '2.8' versionName: '2.8'
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: '' build: ''
buildjni: buildjni:
@ -932,7 +932,7 @@ Builds:
versionName: 2.8.1 versionName: 2.8.1
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: '' build: ''
buildjni: buildjni:
@ -970,7 +970,7 @@ Builds:
versionName: '2.9' versionName: '2.9'
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: '' build: ''
buildjni: buildjni:
@ -1008,7 +1008,7 @@ Builds:
versionName: 2.9.1 versionName: 2.9.1
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: '' build: ''
buildjni: buildjni:
@ -1046,7 +1046,7 @@ Builds:
versionName: 2.9.2 versionName: 2.9.2
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: '' build: ''
buildjni: buildjni:

View File

@ -1,5 +1,5 @@
AllowedAPKSigningKeys: [] AllowedAPKSigningKeys: []
AntiFeatures: [] AntiFeatures: {}
ArchivePolicy: null ArchivePolicy: null
AuthorEmail: null AuthorEmail: null
AuthorName: null AuthorName: null
@ -11,7 +11,7 @@ Bitcoin: null
Builds: Builds:
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: '' build: ''
buildjni: [] buildjni: []
@ -72,7 +72,7 @@ Builds:
versionName: 0.3.3 versionName: 0.3.3
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: '' build: ''
buildjni: [] buildjni: []
@ -115,7 +115,7 @@ Builds:
versionName: 0.3.3 versionName: 0.3.3
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: '' build: ''
buildjni: [] buildjni: []
@ -156,7 +156,7 @@ Builds:
versionName: 0.4.2 versionName: 0.4.2
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: '' build: ''
buildjni: [] buildjni: []
@ -197,7 +197,7 @@ Builds:
versionName: 0.5.1 versionName: 0.5.1
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: '' build: ''
buildjni: [] buildjni: []
@ -237,7 +237,7 @@ Builds:
versionName: 0.5.2 versionName: 0.5.2
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: '' build: ''
buildjni: [] buildjni: []
@ -277,7 +277,7 @@ Builds:
versionName: 0.5.3 versionName: 0.5.3
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: '' build: ''
buildjni: [] buildjni: []
@ -317,7 +317,7 @@ Builds:
versionName: 0.5.4 versionName: 0.5.4
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: '' build: ''
buildjni: [] buildjni: []

View File

@ -1,5 +1,5 @@
AllowedAPKSigningKeys: [] AllowedAPKSigningKeys: []
AntiFeatures: [] AntiFeatures: {}
ArchivePolicy: 9 versions ArchivePolicy: 9 versions
AuthorEmail: null AuthorEmail: null
AuthorName: null AuthorName: null
@ -14,7 +14,7 @@ Builds:
- ../java-libs/SlidingMenu - ../java-libs/SlidingMenu
- ../java-libs/ActionBarSherlock - ../java-libs/ActionBarSherlock
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ANDROID_ABI=armeabi-v7a ./compile.sh release - cd ../ && ANDROID_ABI=armeabi-v7a ./compile.sh release
@ -54,7 +54,7 @@ Builds:
- ../java-libs/SlidingMenu - ../java-libs/SlidingMenu
- ../java-libs/ActionBarSherlock - ../java-libs/ActionBarSherlock
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ANDROID_ABI=armeabi ./compile.sh release - cd ../ && ANDROID_ABI=armeabi ./compile.sh release
@ -94,7 +94,7 @@ Builds:
- ../java-libs/SlidingMenu - ../java-libs/SlidingMenu
- ../java-libs/ActionBarSherlock - ../java-libs/ActionBarSherlock
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ANDROID_ABI=x86 ./compile.sh release - cd ../ && ANDROID_ABI=x86 ./compile.sh release
@ -134,7 +134,7 @@ Builds:
- ../java-libs/SlidingMenu - ../java-libs/SlidingMenu
- ../java-libs/ActionBarSherlock - ../java-libs/ActionBarSherlock
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ANDROID_ABI=mips ./compile.sh release - cd ../ && ANDROID_ABI=mips ./compile.sh release
@ -171,7 +171,7 @@ Builds:
versionName: 0.0.11-mips versionName: 0.0.11-mips
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ANDROID_ABI=mips ./compile.sh release - cd ../ && ANDROID_ABI=mips ./compile.sh release
@ -210,7 +210,7 @@ Builds:
versionName: 0.1.3-MIPS versionName: 0.1.3-MIPS
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ANDROID_ABI=x86 ./compile.sh release - cd ../ && ANDROID_ABI=x86 ./compile.sh release
@ -249,7 +249,7 @@ Builds:
versionName: 0.1.3-x86 versionName: 0.1.3-x86
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ANDROID_ABI=armeabi ./compile.sh release - cd ../ && ANDROID_ABI=armeabi ./compile.sh release
@ -288,7 +288,7 @@ Builds:
versionName: 0.1.3-ARM versionName: 0.1.3-ARM
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ANDROID_ABI=armeabi-v7a ./compile.sh release - cd ../ && ANDROID_ABI=armeabi-v7a ./compile.sh release
@ -327,7 +327,7 @@ Builds:
versionName: 0.1.3-ARMv7 versionName: 0.1.3-ARMv7
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ANDROID_ABI=x86 ./compile.sh release - cd ../ && ANDROID_ABI=x86 ./compile.sh release
@ -365,7 +365,7 @@ Builds:
versionName: 0.9.0 versionName: 0.9.0
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ANDROID_ABI=armeabi-v7a ./compile.sh release - cd ../ && ANDROID_ABI=armeabi-v7a ./compile.sh release
@ -403,7 +403,7 @@ Builds:
versionName: 0.9.0 versionName: 0.9.0
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ANDROID_ABI=x86 ./compile.sh release - cd ../ && ANDROID_ABI=x86 ./compile.sh release
@ -441,7 +441,7 @@ Builds:
versionName: 0.9.1 versionName: 0.9.1
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ANDROID_ABI=armeabi-v7a ./compile.sh release - cd ../ && ANDROID_ABI=armeabi-v7a ./compile.sh release
@ -479,7 +479,7 @@ Builds:
versionName: 0.9.1 versionName: 0.9.1
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ANDROID_ABI=x86 ./compile.sh release - cd ../ && ANDROID_ABI=x86 ./compile.sh release
@ -517,7 +517,7 @@ Builds:
versionName: 0.9.5 versionName: 0.9.5
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ANDROID_ABI=armeabi-v7a ./compile.sh release - cd ../ && ANDROID_ABI=armeabi-v7a ./compile.sh release
@ -555,7 +555,7 @@ Builds:
versionName: 0.9.5 versionName: 0.9.5
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ANDROID_ABI=x86 ./compile.sh release - cd ../ && ANDROID_ABI=x86 ./compile.sh release
@ -593,7 +593,7 @@ Builds:
versionName: 0.9.6 versionName: 0.9.6
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ANDROID_ABI=armeabi-v7a ./compile.sh release - cd ../ && ANDROID_ABI=armeabi-v7a ./compile.sh release
@ -631,7 +631,7 @@ Builds:
versionName: 0.9.6 versionName: 0.9.6
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ANDROID_ABI=x86 ./compile.sh release - cd ../ && ANDROID_ABI=x86 ./compile.sh release
@ -669,7 +669,7 @@ Builds:
versionName: 0.9.7 versionName: 0.9.7
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ANDROID_ABI=armeabi-v7a ./compile.sh release - cd ../ && ANDROID_ABI=armeabi-v7a ./compile.sh release
@ -707,7 +707,7 @@ Builds:
versionName: 0.9.7 versionName: 0.9.7
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ANDROID_ABI=mips ./compile.sh release - cd ../ && ANDROID_ABI=mips ./compile.sh release
@ -745,7 +745,7 @@ Builds:
versionName: 0.9.7.1 versionName: 0.9.7.1
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ANDROID_ABI=x86 ./compile.sh release - cd ../ && ANDROID_ABI=x86 ./compile.sh release
@ -783,7 +783,7 @@ Builds:
versionName: 0.9.7.1 versionName: 0.9.7.1
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ANDROID_ABI=armeabi-v7a ./compile.sh release - cd ../ && ANDROID_ABI=armeabi-v7a ./compile.sh release
@ -821,7 +821,7 @@ Builds:
versionName: 0.9.7.1 versionName: 0.9.7.1
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ANDROID_ABI=x86 ./compile.sh release - cd ../ && ANDROID_ABI=x86 ./compile.sh release
@ -859,7 +859,7 @@ Builds:
versionName: 0.9.8 versionName: 0.9.8
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ANDROID_ABI=armeabi ./compile.sh release - cd ../ && ANDROID_ABI=armeabi ./compile.sh release
@ -897,7 +897,7 @@ Builds:
versionName: 0.9.8 versionName: 0.9.8
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ANDROID_ABI=armeabi-v7a ./compile.sh release - cd ../ && ANDROID_ABI=armeabi-v7a ./compile.sh release
@ -935,7 +935,7 @@ Builds:
versionName: 0.9.8 versionName: 0.9.8
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ANDROID_ABI=x86 ./compile.sh release - cd ../ && ANDROID_ABI=x86 ./compile.sh release
@ -973,7 +973,7 @@ Builds:
versionName: 0.9.9 versionName: 0.9.9
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ANDROID_ABI=armeabi ./compile.sh release - cd ../ && ANDROID_ABI=armeabi ./compile.sh release
@ -1011,7 +1011,7 @@ Builds:
versionName: 0.9.9 versionName: 0.9.9
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ANDROID_ABI=armeabi-v7a ./compile.sh release - cd ../ && ANDROID_ABI=armeabi-v7a ./compile.sh release
@ -1049,7 +1049,7 @@ Builds:
versionName: 0.9.9 versionName: 0.9.9
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ANDROID_ABI=x86 ./compile.sh release - cd ../ && ANDROID_ABI=x86 ./compile.sh release
@ -1087,7 +1087,7 @@ Builds:
versionName: 0.9.10 versionName: 0.9.10
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ANDROID_ABI=armeabi ./compile.sh release - cd ../ && ANDROID_ABI=armeabi ./compile.sh release
@ -1125,7 +1125,7 @@ Builds:
versionName: 0.9.10 versionName: 0.9.10
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ANDROID_ABI=armeabi-v7a ./compile.sh release - cd ../ && ANDROID_ABI=armeabi-v7a ./compile.sh release
@ -1163,7 +1163,7 @@ Builds:
versionName: 0.9.10 versionName: 0.9.10
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ANDROID_ABI=x86 ./compile.sh release - cd ../ && ANDROID_ABI=x86 ./compile.sh release
@ -1201,7 +1201,7 @@ Builds:
versionName: 1.0.0 versionName: 1.0.0
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ANDROID_ABI=armeabi ./compile.sh release - cd ../ && ANDROID_ABI=armeabi ./compile.sh release
@ -1239,7 +1239,7 @@ Builds:
versionName: 1.0.0 versionName: 1.0.0
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ANDROID_ABI=armeabi-v7a ./compile.sh release - cd ../ && ANDROID_ABI=armeabi-v7a ./compile.sh release
@ -1277,7 +1277,7 @@ Builds:
versionName: 1.0.0 versionName: 1.0.0
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ANDROID_ABI=x86 ./compile.sh release - cd ../ && ANDROID_ABI=x86 ./compile.sh release
@ -1315,7 +1315,7 @@ Builds:
versionName: 1.0.1 versionName: 1.0.1
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ANDROID_ABI=armeabi ./compile.sh release - cd ../ && ANDROID_ABI=armeabi ./compile.sh release
@ -1353,7 +1353,7 @@ Builds:
versionName: 1.0.1 versionName: 1.0.1
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ANDROID_ABI=armeabi-v7a ./compile.sh release - cd ../ && ANDROID_ABI=armeabi-v7a ./compile.sh release
@ -1391,7 +1391,7 @@ Builds:
versionName: 1.0.1 versionName: 1.0.1
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ./compile.sh -a "armeabi" --release - cd ../ && ./compile.sh -a "armeabi" --release
@ -1431,7 +1431,7 @@ Builds:
versionName: 1.1.3 versionName: 1.1.3
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ./compile.sh -a "armeabi-v7a" --release - cd ../ && ./compile.sh -a "armeabi-v7a" --release
@ -1471,7 +1471,7 @@ Builds:
versionName: 1.1.3 versionName: 1.1.3
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ./compile.sh -a "x86" --release - cd ../ && ./compile.sh -a "x86" --release
@ -1511,7 +1511,7 @@ Builds:
versionName: 1.1.3 versionName: 1.1.3
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ./compile.sh -a "armeabi" --release - cd ../ && ./compile.sh -a "armeabi" --release
@ -1551,7 +1551,7 @@ Builds:
versionName: 1.1.5 versionName: 1.1.5
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ./compile.sh -a "armeabi-v7a" --release - cd ../ && ./compile.sh -a "armeabi-v7a" --release
@ -1591,7 +1591,7 @@ Builds:
versionName: 1.1.5 versionName: 1.1.5
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ./compile.sh -a "x86" --release - cd ../ && ./compile.sh -a "x86" --release
@ -1631,7 +1631,7 @@ Builds:
versionName: 1.1.5 versionName: 1.1.5
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ./compile.sh -a "armeabi" --release - cd ../ && ./compile.sh -a "armeabi" --release
@ -1671,7 +1671,7 @@ Builds:
versionName: 1.1.6 versionName: 1.1.6
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ./compile.sh -a "armeabi-v7a" --release - cd ../ && ./compile.sh -a "armeabi-v7a" --release
@ -1711,7 +1711,7 @@ Builds:
versionName: 1.1.6 versionName: 1.1.6
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ./compile.sh -a "x86" --release - cd ../ && ./compile.sh -a "x86" --release
@ -1751,7 +1751,7 @@ Builds:
versionName: 1.1.6 versionName: 1.1.6
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ./compile.sh -a "armeabi" --release - cd ../ && ./compile.sh -a "armeabi" --release
@ -1791,7 +1791,7 @@ Builds:
versionName: 1.2.0 versionName: 1.2.0
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ./compile.sh -a "armeabi-v7a" --release - cd ../ && ./compile.sh -a "armeabi-v7a" --release
@ -1831,7 +1831,7 @@ Builds:
versionName: 1.2.0 versionName: 1.2.0
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ./compile.sh -a "x86" --release - cd ../ && ./compile.sh -a "x86" --release
@ -1871,7 +1871,7 @@ Builds:
versionName: 1.2.0 versionName: 1.2.0
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ./compile.sh -a "armeabi" --release - cd ../ && ./compile.sh -a "armeabi" --release
@ -1911,7 +1911,7 @@ Builds:
versionName: 1.2.1 versionName: 1.2.1
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ./compile.sh -a "armeabi-v7a" --release - cd ../ && ./compile.sh -a "armeabi-v7a" --release
@ -1951,7 +1951,7 @@ Builds:
versionName: 1.2.1 versionName: 1.2.1
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ./compile.sh -a "x86" --release - cd ../ && ./compile.sh -a "x86" --release
@ -1991,7 +1991,7 @@ Builds:
versionName: 1.2.1 versionName: 1.2.1
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ./compile.sh -a "armeabi" --release - cd ../ && ./compile.sh -a "armeabi" --release
@ -2031,7 +2031,7 @@ Builds:
versionName: 1.2.2 versionName: 1.2.2
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ./compile.sh -a "armeabi-v7a" --release - cd ../ && ./compile.sh -a "armeabi-v7a" --release
@ -2071,7 +2071,7 @@ Builds:
versionName: 1.2.2 versionName: 1.2.2
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ./compile.sh -a "x86" --release - cd ../ && ./compile.sh -a "x86" --release
@ -2111,7 +2111,7 @@ Builds:
versionName: 1.2.2 versionName: 1.2.2
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ./compile.sh -a "armeabi" --release - cd ../ && ./compile.sh -a "armeabi" --release
@ -2151,7 +2151,7 @@ Builds:
versionName: 1.2.3 versionName: 1.2.3
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ./compile.sh -a "armeabi-v7a" --release - cd ../ && ./compile.sh -a "armeabi-v7a" --release
@ -2191,7 +2191,7 @@ Builds:
versionName: 1.2.3 versionName: 1.2.3
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ./compile.sh -a "x86" --release - cd ../ && ./compile.sh -a "x86" --release
@ -2231,7 +2231,7 @@ Builds:
versionName: 1.2.3 versionName: 1.2.3
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ./compile.sh -a "armeabi" --release - cd ../ && ./compile.sh -a "armeabi" --release
@ -2271,7 +2271,7 @@ Builds:
versionName: 1.2.4 versionName: 1.2.4
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ./compile.sh -a "armeabi-v7a" --release - cd ../ && ./compile.sh -a "armeabi-v7a" --release
@ -2311,7 +2311,7 @@ Builds:
versionName: 1.2.4 versionName: 1.2.4
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ./compile.sh -a "x86" --release - cd ../ && ./compile.sh -a "x86" --release
@ -2351,7 +2351,7 @@ Builds:
versionName: 1.2.4 versionName: 1.2.4
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ./compile.sh -a "armeabi" --release - cd ../ && ./compile.sh -a "armeabi" --release
@ -2391,7 +2391,7 @@ Builds:
versionName: 1.2.5 versionName: 1.2.5
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ./compile.sh -a "armeabi-v7a" --release - cd ../ && ./compile.sh -a "armeabi-v7a" --release
@ -2431,7 +2431,7 @@ Builds:
versionName: 1.2.5 versionName: 1.2.5
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ./compile.sh -a "x86" --release - cd ../ && ./compile.sh -a "x86" --release
@ -2471,7 +2471,7 @@ Builds:
versionName: 1.2.5 versionName: 1.2.5
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ./compile.sh -a "armeabi" --release - cd ../ && ./compile.sh -a "armeabi" --release
@ -2511,7 +2511,7 @@ Builds:
versionName: 1.2.6 versionName: 1.2.6
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ./compile.sh -a "armeabi-v7a" --release - cd ../ && ./compile.sh -a "armeabi-v7a" --release
@ -2551,7 +2551,7 @@ Builds:
versionName: 1.2.6 versionName: 1.2.6
- androidupdate: [] - androidupdate: []
antcommands: [] antcommands: []
antifeatures: [] antifeatures: {}
binary: null binary: null
build: build:
- cd ../ && ./compile.sh -a "x86" --release - cd ../ && ./compile.sh -a "x86" --release

View File

@ -3,8 +3,8 @@
"version": 20002, "version": 20002,
"index": { "index": {
"name": "/index-v2.json", "name": "/index-v2.json",
"sha256": "a3c7e88a522a7228937e5c3d760fc239e3578e292035d88478d32fec9ff5eb54", "sha256": "f4979b9db840cb51a99e80c20da676ba42b13133dbaa4819673bc43ed2ffc3f3",
"size": 52314, "size": 52481,
"numPackages": 10 "numPackages": 10
}, },
"diffs": {} "diffs": {}

View File

@ -568,7 +568,9 @@
] ]
}, },
"antiFeatures": { "antiFeatures": {
"NoSourceSince": {}, "NoSourceSince": {
"en-US": "1.5"
},
"NonFreeNet": {} "NonFreeNet": {}
} }
}, },
@ -602,7 +604,9 @@
] ]
}, },
"antiFeatures": { "antiFeatures": {
"NoSourceSince": {}, "NoSourceSince": {
"en-US": "1.5"
},
"NonFreeNet": {} "NonFreeNet": {}
} }
}, },
@ -645,7 +649,9 @@
] ]
}, },
"antiFeatures": { "antiFeatures": {
"NoSourceSince": {}, "NoSourceSince": {
"en-US": "1.5"
},
"NonFreeNet": {} "NonFreeNet": {}
} }
}, },
@ -689,7 +695,9 @@
}, },
"antiFeatures": { "antiFeatures": {
"KnownVuln": {}, "KnownVuln": {},
"NoSourceSince": {}, "NoSourceSince": {
"en-US": "1.5"
},
"NonFreeAssets": {}, "NonFreeAssets": {},
"NonFreeNet": {}, "NonFreeNet": {},
"UpstreamNonFree": {} "UpstreamNonFree": {}
@ -1395,4 +1403,4 @@
} }
} }
} }
} }

View File

@ -45,11 +45,11 @@ class RewriteMetaTest(unittest.TestCase):
'versionCode': 3, 'versionCode': 3,
'commit': '6a548e4b19', 'commit': '6a548e4b19',
'target': 'android-10', 'target': 'android-10',
'antifeatures': [ 'antifeatures': {
'KnownVuln', 'KnownVuln': {},
'UpstreamNonFree', 'UpstreamNonFree': {},
'NonFreeAssets', '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): def test_remove_blank_flags_from_builds_no_builds(self):
"""Unset fields in Builds: entries should be removed.""" """Unset fields in Builds: entries should be removed."""
self.assertEqual( self.assertEqual(
@ -78,6 +96,24 @@ class RewriteMetaTest(unittest.TestCase):
rewritemeta.remove_blank_flags_from_builds(dict()), rewritemeta.remove_blank_flags_from_builds(dict()),
list(), 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): def test_rewrite_no_builds(self):
os.chdir(self.testdir) os.chdir(self.testdir)

View File

@ -768,7 +768,7 @@ class UpdateTest(unittest.TestCase):
'-1': 'res/drawable-mdpi-v4/icon_launcher.png'}) '-1': 'res/drawable-mdpi-v4/icon_launcher.png'})
self.assertEqual(apk_info['icons'], {}) self.assertEqual(apk_info['icons'], {})
self.assertEqual(apk_info['features'], []) 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['versionName'], 'v1.6pre2')
self.assertEqual(apk_info['hash'], self.assertEqual(apk_info['hash'],
'897486e1f857c6c0ee32ccbad0e1b8cd82f6d0e65a44a23f13f852d2b63a18c8') '897486e1f857c6c0ee32ccbad0e1b8cd82f6d0e65a44a23f13f852d2b63a18c8')
@ -819,7 +819,7 @@ class UpdateTest(unittest.TestCase):
'hashType': 'sha256', 'hashType': 'sha256',
'packageName': 'no.min.target.sdk', 'packageName': 'no.min.target.sdk',
'features': [], 'features': [],
'antiFeatures': set(), 'antiFeatures': dict(),
'size': 14102, 'size': 14102,
'sig': 'b4964fd759edaa54e65bb476d0276880', 'sig': 'b4964fd759edaa54e65bb476d0276880',
'versionName': '1.2-fake', 'versionName': '1.2-fake',