From 5713b54e0b3aad9d226596a3e37c921baac3fc80 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 14 Feb 2018 12:04:39 +0100 Subject: [PATCH 1/2] update: find alternate PNGs for apps that have an XML app icon Apps can now use an XML icon, but if the app supports older Android versions, it'll also contain PNG versions of the same icon. This finds those PNGs and uses them instead. #344 closes #392 fdroiddata#913 --- fdroidserver/update.py | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/fdroidserver/update.py b/fdroidserver/update.py index 9b97fdb0..c34885c5 100644 --- a/fdroidserver/update.py +++ b/fdroidserver/update.py @@ -1442,10 +1442,12 @@ def process_apks(apkcache, repodir, knownapks, use_date_from_apk=False): def extract_apk_icons(icon_filename, apk, apkzip, repo_dir): - """ - Extracts icons from the given APK zip in various densities, - saves them into given repo directory - and stores their names in the APK metadata dictionary. + """Extracts PNG icons from an APK with the supported pixel densities + + Extracts icons from the given APK zip in various densities, saves + them into given repo directory and stores their names in the APK + metadata dictionary. If the icon is an XML icon, then this tries + to find PNG icon that can replace it. :param icon_filename: A string representing the icon's file name :param apk: A populated dictionary containing APK metadata. @@ -1453,7 +1455,15 @@ def extract_apk_icons(icon_filename, apk, apkzip, repo_dir): :param apkzip: An opened zipfile.ZipFile of the APK file :param repo_dir: The directory of the APK's repository :return: A list of icon densities that are missing + """ + res_name_re = re.compile(r'res/(drawable|mipmap)-(x*[hlm]dpi|anydpi).*/(.*)_[0-9]+dp.(png|xml)') + pngs = dict() + for f in apkzip.namelist(): + m = res_name_re.match(f) + if m and m.group(4) == 'png': + density = screen_resolutions[m.group(2)] + pngs[m.group(3) + '/' + density] = m.group(0) empty_densities = [] for density in screen_densities: if density not in apk['icons_src']: @@ -1465,12 +1475,11 @@ def extract_apk_icons(icon_filename, apk, apkzip, repo_dir): # Extract the icon files per density if icon_src.endswith('.xml'): - png = os.path.basename(icon_src)[:-4] + '.png' - for f in apkzip.namelist(): - if f.endswith(png): - m = re.match(r'res/(drawable|mipmap)-(x*[hlm]dpi).*/', f) - if m and screen_resolutions[m.group(2)] == density: - icon_src = f + m = res_name_re.match(icon_src) + if m: + name = pngs.get(m.group(3) + '/' + str(density)) + if name: + icon_src = name if icon_src.endswith('.xml'): empty_densities.append(density) continue From 40fac10ebcd95095741a243ff621b67335cf9e5b Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 14 Feb 2018 13:44:58 +0100 Subject: [PATCH 2/2] update: extract and store XML icons These can then be used by the client. #344 #392 --- fdroidserver/update.py | 72 ++++++++++-------- tests/repo/info.zwanenburg.caffeinetile_4.apk | Bin 0 -> 11740 bytes tests/update.TestCase | 22 +++++- 3 files changed, 60 insertions(+), 34 deletions(-) create mode 100644 tests/repo/info.zwanenburg.caffeinetile_4.apk diff --git a/fdroidserver/update.py b/fdroidserver/update.py index c34885c5..10705a8f 100644 --- a/fdroidserver/update.py +++ b/fdroidserver/update.py @@ -61,7 +61,7 @@ APK_PERMISSION_PAT = \ re.compile(".*(name='(?P.*?)')(.*maxSdkVersion='(?P.*?)')?.*") APK_FEATURE_PAT = re.compile(".*name='([^']*)'.*") -screen_densities = ['640', '480', '320', '240', '160', '120'] +screen_densities = ['65534', '640', '480', '320', '240', '160', '120'] screen_resolutions = { "xxxhdpi": '640', "xxhdpi": '480', @@ -96,9 +96,10 @@ def px_to_dpi(px): def get_icon_dir(repodir, density): - if density == '0': + if density == '0' or density == '65534': return os.path.join(repodir, "icons") - return os.path.join(repodir, "icons-%s" % density) + else: + return os.path.join(repodir, "icons-%s" % density) def get_icon_dirs(repodir): @@ -1377,7 +1378,7 @@ def process_apk(apkcache, apkfilename, repodir, knownapks, use_date_from_apk=Fal .format(apkfilename=apkfile) + str(e)) # extract icons from APK zip file - iconfilename = "%s.%s.png" % (apk['packageName'], apk['versionCode']) + iconfilename = "%s.%s" % (apk['packageName'], apk['versionCode']) try: empty_densities = extract_apk_icons(iconfilename, apk, apkzip, repodir) finally: @@ -1464,6 +1465,8 @@ def extract_apk_icons(icon_filename, apk, apkzip, repo_dir): if m and m.group(4) == 'png': density = screen_resolutions[m.group(2)] pngs[m.group(3) + '/' + density] = m.group(0) + + icon_type = None empty_densities = [] for density in screen_densities: if density not in apk['icons_src']: @@ -1471,7 +1474,7 @@ def extract_apk_icons(icon_filename, apk, apkzip, repo_dir): continue icon_src = apk['icons_src'][density] icon_dir = get_icon_dir(repo_dir, density) - icon_dest = os.path.join(icon_dir, icon_filename) + icon_type = '.png' # Extract the icon files per density if icon_src.endswith('.xml'): @@ -1482,59 +1485,68 @@ def extract_apk_icons(icon_filename, apk, apkzip, repo_dir): icon_src = name if icon_src.endswith('.xml'): empty_densities.append(density) - continue + icon_type = '.xml' + icon_dest = os.path.join(icon_dir, icon_filename + icon_type) + try: with open(icon_dest, 'wb') as f: f.write(get_icon_bytes(apkzip, icon_src)) - apk['icons'][density] = icon_filename + apk['icons'][density] = icon_filename + icon_type except (zipfile.BadZipFile, ValueError, KeyError) as e: logging.warning("Error retrieving icon file: %s %s", icon_dest, e) del apk['icons_src'][density] empty_densities.append(density) - if '-1' in apk['icons_src'] and not apk['icons_src']['-1'].endswith('.xml'): + # '-1' here is a remnant of the parsing of aapt output, meaning "no DPI specified" + if '-1' in apk['icons_src']: icon_src = apk['icons_src']['-1'] - icon_path = os.path.join(get_icon_dir(repo_dir, '0'), icon_filename) + icon_type = icon_src[-4:] + icon_path = os.path.join(get_icon_dir(repo_dir, '0'), icon_filename + icon_type) with open(icon_path, 'wb') as f: f.write(get_icon_bytes(apkzip, icon_src)) - im = None - try: - im = Image.open(icon_path) - dpi = px_to_dpi(im.size[0]) - for density in screen_densities: - if density in apk['icons']: - break - if density == screen_densities[-1] or dpi >= int(density): - apk['icons'][density] = icon_filename - shutil.move(icon_path, - os.path.join(get_icon_dir(repo_dir, density), icon_filename)) - empty_densities.remove(density) - break - except Exception as e: - logging.warning(_("Failed reading {path}: {error}") - .format(path=icon_path, error=e)) - finally: - if im and hasattr(im, 'close'): - im.close() + if icon_type == '.png': + im = None + try: + im = Image.open(icon_path) + dpi = px_to_dpi(im.size[0]) + for density in screen_densities: + if density in apk['icons']: + break + if density == screen_densities[-1] or dpi >= int(density): + apk['icons'][density] = icon_filename + shutil.move(icon_path, + os.path.join(get_icon_dir(repo_dir, density), icon_filename)) + empty_densities.remove(density) + break + except Exception as e: + logging.warning(_("Failed reading {path}: {error}") + .format(path=icon_path, error=e)) + finally: + if im and hasattr(im, 'close'): + im.close() if apk['icons']: - apk['icon'] = icon_filename + apk['icon'] = icon_filename + icon_type return empty_densities def fill_missing_icon_densities(empty_densities, icon_filename, apk, repo_dir): """ - Resize existing icons for densities missing in the APK to ensure all densities are available + Resize existing PNG icons for densities missing in the APK to ensure all densities are available :param empty_densities: A list of icon densities that are missing :param icon_filename: A string representing the icon's file name :param apk: A populated dictionary containing APK metadata. Needs to have 'icons' key :param repo_dir: The directory of the APK's repository + """ + icon_filename += '.png' # First try resizing down to not lose quality last_density = None for density in screen_densities: + if density == '65534': # not possible to generate 'anydpi' from other densities + continue if density not in empty_densities: last_density = density continue diff --git a/tests/repo/info.zwanenburg.caffeinetile_4.apk b/tests/repo/info.zwanenburg.caffeinetile_4.apk new file mode 100644 index 0000000000000000000000000000000000000000..bc54a73e946219bf23e003e8ff81de954667dc63 GIT binary patch literal 11740 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 literal 0 HcmV?d00001 diff --git a/tests/update.TestCase b/tests/update.TestCase index 94bf507d..2e9b687b 100755 --- a/tests/update.TestCase +++ b/tests/update.TestCase @@ -253,14 +253,14 @@ class UpdateTest(unittest.TestCase): apps = fdroidserver.metadata.read_metadata(xref=True) knownapks = fdroidserver.common.KnownApks() apks, cachechanged = fdroidserver.update.process_apks({}, 'repo', knownapks, False) - self.assertEqual(len(apks), 12) + self.assertEqual(len(apks), 13) apk = apks[0] self.assertEqual(apk['packageName'], 'com.politedroid') self.assertEqual(apk['versionCode'], 3) self.assertEqual(apk['minSdkVersion'], '3') self.assertEqual(apk['targetSdkVersion'], '3') self.assertFalse('maxSdkVersion' in apk) - apk = apks[5] + apk = apks[6] self.assertEqual(apk['packageName'], 'obb.main.oldversion') self.assertEqual(apk['versionCode'], 1444412523) self.assertEqual(apk['minSdkVersion'], '4') @@ -298,7 +298,6 @@ class UpdateTest(unittest.TestCase): raise Exception('This test must be run in the "tests/" subdir') apk_info = fdroidserver.update.scan_apk('org.dyndns.fules.ck_20.apk') - self.assertEqual(apk_info['icons_src'], {'240': 'res/drawable-hdpi-v4/icon_launcher.png', '120': 'res/drawable-ldpi-v4/icon_launcher.png', '160': 'res/drawable-mdpi-v4/icon_launcher.png', @@ -319,6 +318,21 @@ class UpdateTest(unittest.TestCase): self.assertEqual(apk_info['hashType'], 'sha256') self.assertEqual(apk_info['targetSdkVersion'], '8') + apk_info = fdroidserver.update.scan_apk('org.bitbucket.tickytacky.mirrormirror_4.apk') + self.assertEqual(apk_info['icons_src'], {'160': 'res/drawable-mdpi/mirror.png', + '-1': 'res/drawable-mdpi/mirror.png'}) + + apk_info = fdroidserver.update.scan_apk('repo/info.zwanenburg.caffeinetile_4.apk') + self.assertEqual(apk_info['icons_src'], {'160': 'res/drawable/ic_coffee_on.xml', + '-1': 'res/drawable/ic_coffee_on.xml'}) + + apk_info = fdroidserver.update.scan_apk('repo/com.politedroid_6.apk') + self.assertEqual(apk_info['icons_src'], {'120': 'res/drawable-ldpi-v4/icon.png', + '160': 'res/drawable-mdpi-v4/icon.png', + '240': 'res/drawable-hdpi-v4/icon.png', + '320': 'res/drawable-xhdpi-v4/icon.png', + '-1': 'res/drawable-mdpi-v4/icon.png'}) + def test_scan_apk_no_sig(self): config = dict() fdroidserver.common.fill_config_defaults(config) @@ -527,7 +541,7 @@ class UpdateTest(unittest.TestCase): knownapks = fdroidserver.common.KnownApks() apks, cachechanged = fdroidserver.update.process_apks({}, 'repo', knownapks, False) fdroidserver.update.translate_per_build_anti_features(apps, apks) - self.assertEqual(len(apks), 12) + self.assertEqual(len(apks), 13) foundtest = False for apk in apks: if apk['packageName'] == 'com.politedroid' and apk['versionCode'] == 3: