From 594cacb13e95be223edcef38553f9cd92a09b0ba Mon Sep 17 00:00:00 2001 From: flameshikari Date: Mon, 23 Feb 2026 13:57:13 +0500 Subject: [PATCH 01/12] new + reworked icons and compinfos --- app/src/main/assets/drawable.xml | 1 - app/src/main/res/drawable-nodpi/gosuslugi.png | Bin 4963 -> 9574 bytes .../res/drawable-nodpi/gosuslugi_auto.png | Bin 6356 -> 10847 bytes .../res/drawable-nodpi/gosuslugi_culture.png | Bin 7945 -> 11516 bytes .../main/res/drawable-nodpi/gosuslugi_dom.png | Bin 7040 -> 10769 bytes .../main/res/drawable-nodpi/gosuslugi_key.png | Bin 3582 -> 9717 bytes .../main/res/drawable-nodpi/gosuslugi_pos.png | Bin 7230 -> 10357 bytes app/src/main/res/xml/drawable.xml | 1 - contribs/icons.yml | 21 ++++++++++++++++++ contribs/icons/abr_direct.png | Bin 0 -> 4202 bytes contribs/icons/abr_direct.svg | 1 + contribs/icons/gosuslugi_biometry.png | Bin 0 -> 11841 bytes contribs/icons/gosuslugi_biometry.svg | 1 + contribs/icons/gosuslugi_school.png | Bin 0 -> 11337 bytes contribs/icons/gosuslugi_school.svg | 1 + contribs/icons/uralsib.png | Bin 0 -> 4173 bytes contribs/icons/uralsib.svg | 1 + contribs/icons/yoxo.png | Bin 0 -> 4399 bytes contribs/icons/yoxo.svg | 1 + resources/vectors/gosuslugi.svg | 2 +- resources/vectors/gosuslugi_alt_1.svg | 1 - resources/vectors/gosuslugi_auto.svg | 2 +- resources/vectors/gosuslugi_culture.svg | 2 +- resources/vectors/gosuslugi_dom.svg | 2 +- resources/vectors/gosuslugi_key.svg | 2 +- resources/vectors/gosuslugi_pos.svg | 2 +- 26 files changed, 32 insertions(+), 9 deletions(-) create mode 100644 contribs/icons/abr_direct.png create mode 100644 contribs/icons/abr_direct.svg create mode 100644 contribs/icons/gosuslugi_biometry.png create mode 100644 contribs/icons/gosuslugi_biometry.svg create mode 100644 contribs/icons/gosuslugi_school.png create mode 100644 contribs/icons/gosuslugi_school.svg create mode 100644 contribs/icons/uralsib.png create mode 100644 contribs/icons/uralsib.svg create mode 100644 contribs/icons/yoxo.png create mode 100644 contribs/icons/yoxo.svg delete mode 100644 resources/vectors/gosuslugi_alt_1.svg diff --git a/app/src/main/assets/drawable.xml b/app/src/main/assets/drawable.xml index 1e1b1d2b6e..3de1064071 100644 --- a/app/src/main/assets/drawable.xml +++ b/app/src/main/assets/drawable.xml @@ -649,7 +649,6 @@ - diff --git a/app/src/main/res/drawable-nodpi/gosuslugi.png b/app/src/main/res/drawable-nodpi/gosuslugi.png index 610b9476d9645f4cbe84a6c49a9391b5c897f8fd..50565ec18f2382579f57691437cbfc59399c96dc 100644 GIT binary patch literal 9574 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 QN&o-=07*qoM6N<$f@TZM6aWAK literal 4963 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ⅈqYe}6FUWH2@t9}RIdz{<)*^x8-gBLLG- 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 iI5;>sI5;>0jsFA1Pzv8uU{K`%0000^BIW-&Hk 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 diff --git a/app/src/main/res/drawable-nodpi/gosuslugi_dom.png b/app/src/main/res/drawable-nodpi/gosuslugi_dom.png index 1632122e270ad243e9ef488e558483b2ca9cd516..728f98851030e4a02f9734f73e3b96dff4320876 100644 GIT binary patch literal 10769 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 P00000NkvXXu0mjf`Y!~a literal 7040 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 uLkfcmo4`>)SXb6+_* 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 DgxnDY literal 3582 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 TG8t!<00000NkvXXu0mjfoa2w= delta 7221 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 Df - diff --git a/contribs/icons.yml b/contribs/icons.yml index b6df8684e2..2599d58323 100644 --- a/contribs/icons.yml +++ b/contribs/icons.yml @@ -12,3 +12,24 @@ # category: Google # compinfos: # - com.google.app/com.google.app.MainActivity + +uralsib: + - ru.bankuralsib.mb.android/ru.bankuralsib.mb.start.presentation.ui.activity.StartActivity + +yoxo: + - ro.orange.yoxo/ro.orange.yoxo.onboarding.ui.activity.SplashScreenActivity + +abr_direct: + - ru.artsofte.russiafl/ru.artsofte.russiafl.MainActivity + +gosuslugi_school: + - ru.gosuslugi.school/ru.gosuslugi.school.MainActivity + +gosuslugi_biometry: + - ru.rtlabs.mobile.ebs.gosuslugi.android/ru.rtlabs.mobile.ebs.ui.main.MainActivity + +maadhaar: + - in.gov.uidai.pehchaan/in.gov.uidai.pehchaan.onboarding.SplashActivity + +documents: + - andes.oplus.documentsreader/andes.oplus.documentsreader.launcher.activity.MainActivity diff --git a/contribs/icons/abr_direct.png b/contribs/icons/abr_direct.png new file mode 100644 index 0000000000000000000000000000000000000000..f53ac15cf5a1e0ca2077e630e675d5ca776cd285 GIT binary patch literal 4202 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 E1O10|+5i9m literal 0 HcmV?d00001 diff --git a/contribs/icons/abr_direct.svg b/contribs/icons/abr_direct.svg new file mode 100644 index 0000000000..3f5b8f64a5 --- /dev/null +++ b/contribs/icons/abr_direct.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/contribs/icons/gosuslugi_biometry.png b/contribs/icons/gosuslugi_biometry.png new file mode 100644 index 0000000000000000000000000000000000000000..d1e57e6f410d54d44956f03eb3f829c29266af66 GIT binary patch literal 11841 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 literal 0 HcmV?d00001 diff --git a/contribs/icons/gosuslugi_biometry.svg b/contribs/icons/gosuslugi_biometry.svg new file mode 100644 index 0000000000..eb9fe41d86 --- /dev/null +++ b/contribs/icons/gosuslugi_biometry.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/contribs/icons/gosuslugi_school.png b/contribs/icons/gosuslugi_school.png new file mode 100644 index 0000000000000000000000000000000000000000..856c800d5ed96e9eb1721499a22d4480310d6b9a GIT binary patch literal 11337 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 LNkvXXu0mjf2)3i? literal 0 HcmV?d00001 diff --git a/contribs/icons/gosuslugi_school.svg b/contribs/icons/gosuslugi_school.svg new file mode 100644 index 0000000000..37220eab0b --- /dev/null +++ b/contribs/icons/gosuslugi_school.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/contribs/icons/uralsib.png b/contribs/icons/uralsib.png new file mode 100644 index 0000000000000000000000000000000000000000..39d88d3dc523805054c7130105d3ca4a6ab4d0cf GIT binary patch literal 4173 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 literal 0 HcmV?d00001 diff --git a/contribs/icons/uralsib.svg b/contribs/icons/uralsib.svg new file mode 100644 index 0000000000..b62f969e3e --- /dev/null +++ b/contribs/icons/uralsib.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/contribs/icons/yoxo.png b/contribs/icons/yoxo.png new file mode 100644 index 0000000000000000000000000000000000000000..31abf6c5e4ebfa3c8af39df7e463a9d67c1b83cd GIT binary patch literal 4399 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 G{Qm)W$pKja literal 0 HcmV?d00001 diff --git a/contribs/icons/yoxo.svg b/contribs/icons/yoxo.svg new file mode 100644 index 0000000000..556f89cd0a --- /dev/null +++ b/contribs/icons/yoxo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/vectors/gosuslugi.svg b/resources/vectors/gosuslugi.svg index 545ded2985..24094107e2 100644 --- a/resources/vectors/gosuslugi.svg +++ b/resources/vectors/gosuslugi.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/resources/vectors/gosuslugi_alt_1.svg b/resources/vectors/gosuslugi_alt_1.svg deleted file mode 100644 index e35ee5362b..0000000000 --- a/resources/vectors/gosuslugi_alt_1.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/resources/vectors/gosuslugi_auto.svg b/resources/vectors/gosuslugi_auto.svg index 0163d94fdd..7c327cc7e2 100644 --- a/resources/vectors/gosuslugi_auto.svg +++ b/resources/vectors/gosuslugi_auto.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/resources/vectors/gosuslugi_culture.svg b/resources/vectors/gosuslugi_culture.svg index c6a486259e..ff30914ec7 100644 --- a/resources/vectors/gosuslugi_culture.svg +++ b/resources/vectors/gosuslugi_culture.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/resources/vectors/gosuslugi_dom.svg b/resources/vectors/gosuslugi_dom.svg index a482893d24..de3fee90fd 100644 --- a/resources/vectors/gosuslugi_dom.svg +++ b/resources/vectors/gosuslugi_dom.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/resources/vectors/gosuslugi_key.svg b/resources/vectors/gosuslugi_key.svg index 262cd2085c..e3f6565842 100644 --- a/resources/vectors/gosuslugi_key.svg +++ b/resources/vectors/gosuslugi_key.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/resources/vectors/gosuslugi_pos.svg b/resources/vectors/gosuslugi_pos.svg index a6c5e4b20e..f003e94f53 100644 --- a/resources/vectors/gosuslugi_pos.svg +++ b/resources/vectors/gosuslugi_pos.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file From de280d2c2eeaebd6f0c590941fd8309b8de9fade Mon Sep 17 00:00:00 2001 From: flameshikari Date: Mon, 23 Feb 2026 17:57:25 +0500 Subject: [PATCH 02/12] rework scripts, add new features --- .github/workflows/build_all.yml | 12 +- .github/workflows/build_foss.yml | 4 +- resources/scripts/add_icons.py | 352 -------------------- resources/scripts/add_icons_wrapper.py | 124 -------- resources/scripts/bump_version.py | 197 ++++++------ resources/scripts/count_icons.py | 153 +++++---- resources/scripts/create_changelog.py | 85 ++--- resources/scripts/email_dumper.py | 73 ----- resources/scripts/email_parser.py | 98 ------ resources/scripts/process_emails.py | 218 +++++++++++++ resources/scripts/process_icons.py | 424 +++++++++++++++++++++++++ resources/scripts/process_requests.py | 199 ++++++++++++ resources/scripts/requests_parser.py | 149 --------- resources/scripts/requirements.txt | 10 +- resources/scripts/resolve_paths.py | 67 ---- resources/scripts/shared.py | 98 ++++++ resources/scripts/sort_appfilter.py | 78 ----- resources/scripts/sort_drawable.py | 88 ----- 18 files changed, 1174 insertions(+), 1255 deletions(-) delete mode 100755 resources/scripts/add_icons.py delete mode 100755 resources/scripts/add_icons_wrapper.py delete mode 100755 resources/scripts/email_dumper.py delete mode 100755 resources/scripts/email_parser.py create mode 100755 resources/scripts/process_emails.py create mode 100755 resources/scripts/process_icons.py create mode 100755 resources/scripts/process_requests.py delete mode 100755 resources/scripts/requests_parser.py delete mode 100755 resources/scripts/resolve_paths.py create mode 100755 resources/scripts/shared.py delete mode 100755 resources/scripts/sort_appfilter.py delete mode 100755 resources/scripts/sort_drawable.py diff --git a/.github/workflows/build_all.yml b/.github/workflows/build_all.yml index 1557f6bb56..8eaef7fb6c 100644 --- a/.github/workflows/build_all.yml +++ b/.github/workflows/build_all.yml @@ -66,7 +66,7 @@ jobs: - name: Export vars run: | - python -u resources/scripts/resolve_paths.py -p >> $GITHUB_ENV + python -u resources/scripts/shared.py -p >> $GITHUB_ENV due_on=$(TZ=UTC date --iso-8601=seconds -d "$(date +%Y-%m-01) +1 month") >> $GITHUB_ENV - name: Install Python deps @@ -95,7 +95,7 @@ jobs: - name: Add icons run: | - python -u ${{ env.sd }}/add_icons_wrapper.py + python -u ${{ env.sd }}/process_icons.py - name: Set icons count run: | @@ -112,14 +112,6 @@ jobs: echo 'No release tag or changed files found, skip optimizing' fi - - name: Sort XMLs - run: | - cd ${{ env.sd }} - python sort_appfilter.py -o - python sort_drawable.py -o - cp -fv ${{ env.a1 }} ${{ env.a2 }} - cp -fv ${{ env.d1 }} ${{ env.d2 }} - - name: Create changelog run: | mkdir -v changelog diff --git a/.github/workflows/build_foss.yml b/.github/workflows/build_foss.yml index 985b990f9d..56f2f91fcc 100644 --- a/.github/workflows/build_foss.yml +++ b/.github/workflows/build_foss.yml @@ -47,7 +47,7 @@ jobs: - name: Export vars run: | - python -u resources/scripts/resolve_paths.py -p >> $GITHUB_ENV + python -u resources/scripts/shared.py -p >> $GITHUB_ENV - name: Install Python deps run: | @@ -55,7 +55,7 @@ jobs: - name: Add icons run: | - python -u ${{ env.sd }}/add_icons_wrapper.py + python -u ${{ env.sd }}/process_icons.py - name: Set version run: | diff --git a/resources/scripts/add_icons.py b/resources/scripts/add_icons.py deleted file mode 100755 index 9d1a1c6484..0000000000 --- a/resources/scripts/add_icons.py +++ /dev/null @@ -1,352 +0,0 @@ -#!/usr/bin/env python3 - -import argparse -import re - -from filecmp import cmp as compare -from itertools import chain -from os import name as platform -from os import system as execute -from os.path import abspath, basename, dirname, exists, realpath -from shutil import copyfile as copy -from shutil import move -from subprocess import check_output as get_output -from sys import argv as args - -import requests_parser -from resolve_paths import paths - -parser = argparse.ArgumentParser() -parser.add_argument('-n', '--name', metavar='NAME', help='the name of a drawable entry') -parser.add_argument('-c', '--compinfos', metavar='NAME', action='append', default=[], nargs='+', help='include one or multiple compinfos') -parser.add_argument('-a', '--include-appfilter', action='store_true', help='process the appfilter.xml file') -parser.add_argument('-d', '--include-drawable', action='store_true', help='process the drawable.xml file') -parser.add_argument('-C', '--category', metavar='NAME', help='add to a specific category instead of the named one') -parser.add_argument('-r', '--include-requests', action='store_true', help='process the requests.txt file') -parser.add_argument('-i', '--include-icons', action='store_true', help='move icons to the target folders') -parser.add_argument('-o', '--copy-icons', action='store_true', help='copy icons instead of moving') -parser.add_argument('-f', '--force', action='store_true', help='overwrite icons in target folder') -parser.add_argument('-g', '--git-commit', metavar='MESSAGE', help='commit local changes with a specific message') -parser.add_argument('-D', '--dry-run', action='store_true', help='do things without changing any files') -parser.add_argument('-I', '--ignore-errors', action='store_true', help='ignore errors') -parser.add_argument('-P', '--path', metavar='PATH', help='set custom path to delta folder') -parser.add_argument('-p', '--plain-text', action='store_true', help='disable colorized output') -parser.add_argument('-v', '--verbose', action='store_true', help='more debug output') -options = parser.parse_args() - -# ----------- -# definitions -# ----------- - -include_appfilter = options.include_appfilter -include_drawable = options.include_drawable -include_requests = options.include_requests -include_icons = options.include_icons -ignore_errors = options.ignore_errors -copy_icons = options.copy_icons -git_commit = options.git_commit -plain_text = options.plain_text -git_commit = options.git_commit -compinfos = list(chain.from_iterable(options.compinfos)) # flatten array -category = options.category -dry_run = options.dry_run -verbose = options.verbose -force = options.force -name = options.name -path = options.path - -if path: delta_dir = abspath(path) -else: delta_dir = dirname(realpath(__file__ + '/../..')) # if the script placed in utility_scripts - -work_dir = paths['scripts'] -icons_dir = paths['src']['dir'] -appfilter_files = paths['appfilter'] -drawable_files = paths['drawable'] -requests_file = paths['requests'] -icons_src_files = [ - paths['src']['png'].format(name), - paths['src']['svg'].format(name) -] -icons_dst_files = [ - paths['dst']['png'].format(name), - paths['dst']['svg'].format(name) -] - -git_arguments = appfilter_files + drawable_files + [requests_file] - -blank = '' -changes = False -dry_run_prefix = 'will be ' if dry_run else blank - -execute(blank) # enable coloring in PowerShell and cmd (weird, I know) - - -def write(file, content): - # used to avoid repeating code - if not dry_run: - file.seek(0) - file.write(content) - file.truncate() - - -class out(): - # reject stdlib logging, embrace custom logging without stderr - reset = '\033[0m' - def color(text=False, code=blank): - # 0 gray / 1 red / 2 green / 3 yellow - # 4 blue / 5 purple / 6 cyan / 7 white - reset = '\033[0m' - if plain_text: return text - if code == blank: code = out.reset - if code in range(0, 8): code = f'\033[9{code}m' - else: code = out.reset - if text: code = f'{code}{text}{reset}' - return code - def base(text, level='info', code=4): - print(f'[{out.color(code=code, text=level)}] {text}') - if level == 'fail': exit(1) - info = lambda text: out.base(text) - fail = lambda text: out.base(text + '', level='fail', code=1) - done = lambda text: out.base(text, level='done', code=2) - warn = lambda text: out.base(text, level='warn', code=3) - def verb(text=False, code=0): - if verbose: - if not text: text = '...' - out.base(out.color(code=code, text=text), level='verb', code=5) - rem = color(code=1, text=f'- {{0}}') - add = color(code=2, text=f'+ {{0}}') - -# ------------ -# testing zone -# ------------ - -test_files = appfilter_files + drawable_files + [requests_file] -include_all = include_appfilter or include_drawable or include_icons - -if len(args) < 2: - parser.print_usage() - exit() - -for test_file in test_files: - if not exists(test_file): out.fail(f"file '{test_file}' not found, maybe you forgot to set -P option (simply move the script to utility_scripts dir)") - -if not name: - if include_all: out.fail('use -n option options -a/-d/-i') - if category: - out.fail('use -C option together with -n and -d options') - if not include_drawable: out.warn('-C option is useless without -d option') - if include_drawable: out.warn('-C option is useless without -d option') - -if name: - if not category: - category = name[0].upper() - if category[0] == '_': category = '#' - if name[0].isdigit(): out.fail(f"drawable name that starts with a number must be prefixed with '_'") - if name[0] == '_' and name[1].isalpha(): out.fail(f"drawable name must have a leading number after '_'") - -if not exists(icons_dir): out.fail(f"dir '{icons_dir}' not found, create it next to the script") - -if include_icons and not ignore_errors: - for test_file in icons_src_files: - if not exists(test_file): out.fail(f"icon '{test_file}' not found") - if not force: - for test_file in icons_dst_files: - if exists(test_file): out.fail(f"icon '{test_file}' exists in target folder, use -f option to overwrite it") - -compinfos = [*{*compinfos}] # https://stackoverflow.com/a/60518033 - -for compinfo in compinfos: - match = re.search(r'^(.*?)\/[\w+.$]+', compinfo) - if not match: - out.fail(f"compinfo '{compinfo}' not looks valid") - -include_all += include_requests - -if not dry_run: out.info('started') -else: out.info('started in dry mode') - -# ----- -# main? -# ----- - -if include_drawable: - filename = 'drawable.xml' - with open(drawable_files[0], 'r+', encoding='utf-8', newline=blank) as file: - content = file.read() - drawable_entry = f'' - categories = ['', f''] - if category == 'New': out.fail(f"{filename}: category '{category}' can't be used") - if not re.search(categories[1], content, re.IGNORECASE): out.fail(f"{filename}: category '{category}' not found") - if re.search(drawable_entry, content, re.IGNORECASE): out.warn(f"{filename}: drawable '{name}' exists") - else: - # file.seek(0) - # content_list = file.readlines() - # drawables_count = 0 - # entries = [] - # category_name = category - # for occurence, category in enumerate(categories): - # index = False - # scroll = False - # stop = False - # category_sorted = [] - # category_unsorted = [] - # for line in content_list: - # search = re.search(re.compile(category, re.IGNORECASE), line) - # if search: - # categories[occurence] = search.group(0) - # scroll = True - # continue - # if re.search('^\t?$', line): scroll = False - # if scroll: category_unsorted.append(line.strip()) - # category_sorted = sorted(category_unsorted + [drawable_entry]) - # for category in category_unsorted: - # if re.search(drawable_entry, category): stop = True - # if stop: continue - # for line in category_sorted: - # if re.search(drawable_entry, line): - # index = category_sorted.index(line) - # break - # if index: - # pattern = category_sorted[index-1] - # entries += [pattern] - # replace = fr'{pattern}\n\t{drawable_entry}' - # content = re.sub(pattern, replace, content, occurence + 1) - # drawables_count += 1 - file.seek(0) - content_list = file.readlines() - drawables_count = 0 - category_name = category - entries = [] - - for category in categories: - replace = fr'{category}\n\t{drawable_entry}' - entries += [category] - content = re.sub(category, replace, content) - drawables_count += 1 - - if drawables_count > 0: - write(file, content) - out.done(f'{filename}: {dry_run_prefix + "added"} 2 entries in \'New\' and \'{category_name}\'') - out.verb() - for id, entry in enumerate(entries): - if verbose: - out.verb(categories[id]) - out.verb(out.add.format(drawable_entry)) - out.verb() - changes = True - else: out.warn(f'{filename}: no new drawables entries to add') - - -if include_appfilter: - filename = 'appfilter.xml' - if not compinfos: out.warn(f'{filename}: no compinfos passed') - else: - with open(appfilter_files[0], 'r+', encoding='utf-8', newline=blank) as file: - content = file.read() - pattern = '' - replace = blank - appfilter_entries = [] - for compinfo in compinfos: - appfilter_entry = f'' - compinfo_pattern = appfilter_entry.replace('$', '\\$') - if not re.search(compinfo_pattern, content): - appfilter_entries.append(appfilter_entry) - replace += f'\t{appfilter_entry}\n' - if appfilter_entries: - content = re.sub(pattern, replace + pattern, content) - write(file, content) - entry = 'entry' if len(compinfos) == 1 else 'entries' - out.done(f'{filename}: {dry_run_prefix + "added"} {len(compinfos)} {entry}') - if verbose: - out.verb() - for entry in appfilter_entries: out.verb(out.add.format(entry)) - out.verb(pattern) - out.verb() - changes = True - else: out.warn(f"{filename}: existing entries found") - - -if include_requests: - filename = 'requests.yml' - if not compinfos: out.warn(f'{filename}: no compinfos passed') - else: - requests = requests_parser.read(requests_file) - lines = [] - for compinfo in compinfos: - if compinfo in requests: - lines.append(compinfo) - requests.pop(compinfo) - - lines_count = len(lines) - - if not dry_run: requests_parser.write(requests_file, requests) - - if lines_count > 0: - entry = 'entry' if lines_count == 1 else 'entries' - out.done(f'{filename}: {dry_run_prefix + "removed"} {lines_count} {entry}') - if verbose: - out.verb() - for group in lines: - for entry in group: out.verb(out.rem.format(entry)) - out.verb() - changes = True - else: - out.warn(f'{filename}: no entries found to delete') - - -try: - if not dry_run: - if include_appfilter: - copy(appfilter_files[0], appfilter_files[1]) - if include_drawable: - copy(drawable_files[0], drawable_files[1]) - if include_icons: - message = dry_run_prefix - message += 'copied' if copy_icons else 'moved' - if force: message += ' with overwrite' - formats = ['png', 'svg'] - for format in formats: - index = formats.index(format) - filename = f'{name}.{format}' - source = icons_src_files[index] - target = icons_dst_files[index] - folder = 'vectors' if format == 'svg' else 'drawable-nodpi' - if not exists(source): out.warn(f"{filename}: not found in '{basename(icons_dir)}'") - elif exists(target): out.fail(f"{filename}: found in '{folder}'") - else: - git_arguments += [target] - diff = compare(source, target) if exists(target) else False - if diff: - out.warn(f'{filename}: source and target icons are the same') - else: - if not dry_run: - if copy_icons: copy(source, target) - else: move(source, target) - out.done(f"{filename}: '{source}' {message} to '{target}'" if verbose else f'{filename}: {message} to {folder}') - changes = True - - - if not dry_run and git_commit: - git_command = f'cd {delta_dir} && git' - git_arguments = ' '.join(git_arguments) - null = '>NUL 2>NUL' if platform == 'nt' else '>/dev/null 2>&1' - if execute(f'{git_command} add --dry-run {git_arguments} {null}') == 0: execute(f'cd {delta_dir} && git add {git_arguments}') - if execute(f'{git_command} commit --dry-run -m "{git_commit}" {null}') == 0: - execute(f'{git_command} commit -m "{git_commit}" {null}') - message = ['git: commited'] - commit = get_output(f'{git_command} log -1 --pretty=format:%h', shell=True, encoding='utf-8') - message += [out.color(code=6, text=commit)] - if not verbose: message += ['with', get_output(f'{git_command} diff --staged --shortstat HEAD~1', shell=True, encoding='utf-8').strip()] - out.done(' '.join(message)) - if verbose: - diff = filter(None, get_output(f'{git_command} diff {"--color=always" if not plain_text else blank} --staged --stat HEAD~1', - shell=True, encoding='utf-8').split('\n')) - for line in diff: out.verb(text=line.strip(), code=out.reset) - changes = True - else: out.warn('git: no changes to commit') - if not changes: out.fail('nothing to do') - else: - if dry_run: out.info(f'yay, I can do something!') - else: out.info(f'yay, I did something!') -except Exception as message: - out.fail(message) diff --git a/resources/scripts/add_icons_wrapper.py b/resources/scripts/add_icons_wrapper.py deleted file mode 100755 index 363ec95dcb..0000000000 --- a/resources/scripts/add_icons_wrapper.py +++ /dev/null @@ -1,124 +0,0 @@ -#!/usr/bin/env python3 - -import argparse -import re - -from os import system as execute -from os import name as platform -from os.path import basename, join -from shlex import quote - -from requests_parser import read, write -from resolve_paths import paths - - -work_dir = paths['scripts'] -delta_dir = paths['root'] - -add_icons = join(work_dir, 'add_icons.py') - -icons_file = paths['icons'] -requests_file = paths['requests'] - - -header = '''\ -# -# new_icon_1: -# - com.example/com.example.MainActivity -# -# new_icon_2: -# - com.example.1/com.example.MainActivity -# - com.example.2/com.example.MainActivity -# -# new_icon_2_alt_1: {} -# -# new_icon_3: -# category: Google -# compinfos: -# - com.google.app/com.google.app.MainActivity -''' - - -parser = argparse.ArgumentParser() -parser.add_argument('-D', '--dry-run', action='store_true', help='do things without changing any files') -options = parser.parse_args() - - -icons_yml = read(icons_file) - -if not icons_yml: - print(f'{basename(icons_file)} is empty') - exit(0) - -requests = read(requests_file) - -errors = [] - -icons = [(key, value) for key, value in icons_yml.items()] - - -command_base = f"python '{add_icons}' -P '{delta_dir}' -aidI" - -if options.dry_run: command_base += ' -D' - - -for icon in icons: - - name = icon[0] - data = icon[1] - - category = None - compinfos = None - - - if isinstance(data, dict): - if len(data) == 0: - print() - else: - category = data.get('category', None) - compinfos = data.get('compinfos', None) - - elif isinstance(data, list): - compinfos = icon[1] - else: - print('isinstance error') - exit(2) - - - command = f"{command_base} -n '{name}'" - - if compinfos: - for compinfo in compinfos: - command += f" -c '{compinfo}'" - if compinfo in requests: - requests.pop(compinfo) - else: - if re.match('^.*_alt_[0-9]+$', name): - category = 'Alts' - - if category: command += f" -C '{quote(category)}'" - print(command) - - print(f'[icon] {name}') - status = execute(f'{command}') - print() - - - if status != 0: - errors.append(name) - - if status == 0: - icons_yml.pop(name) - - -if errors: - errors.sort() - print(f'Issues with the next icons: {", ".join(errors)}') - exit(1) - -if options.dry_run: exit(0) - -with open(icons_file, 'w', newline='') as file: - file.write(header) - -write(requests_file, requests) diff --git a/resources/scripts/bump_version.py b/resources/scripts/bump_version.py index 740177fe21..deb75f464f 100755 --- a/resources/scripts/bump_version.py +++ b/resources/scripts/bump_version.py @@ -1,95 +1,106 @@ -#! /usr/bin/env python - -import argparse, re - -from os.path import join - -import semver - -from resolve_paths import paths - - -argparser = argparse.ArgumentParser(description='Bump release version') - -argparser.add_argument('-c', '--custom', - dest='custom', - help='custom version name and code (format: \'name|code\')') -argparser.add_argument('-r', '--release', - dest='release', - help='bump specific position', - choices=['beta', 'promote', 'patch', 'minor', 'major'], - default='beta') -argparser.add_argument('-e', '--env', - dest='env', - help='print variables for shell exporting', - default=False, - action=argparse.BooleanOptionalAction) -argparser.add_argument('-w', '--write', - dest='write', - help='write to file', - default=False, - action=argparse.BooleanOptionalAction) -args = argparser.parse_args() - - -target = join(paths['root'], 'app/build.gradle') - -regexp_version_code = re.compile(r'versionCode (\d+)') -regexp_version_name = re.compile(r'versionName "((\d+\.\d+\.\d+)(-beta\.?(\d+))?)"') -is_beta = 'true' if args.release == 'beta' else 'false' - - -def build_version_code(version): - major = str(version.major) - minor = str(version.minor).zfill(2) - patch = str(version.patch) - beta = '00' if version.prerelease is None else \ - ''.join(filter(str.isdigit, version_name.prerelease)).zfill(2) - return int(major + minor + patch + beta) - - -with open(target, 'r+') as file: - content = file.read() - - if args.custom: - version_name, version_code = args.custom.split('|') - else: - version_code = re.search(regexp_version_code, content).group(1) - version_name = semver.VersionInfo.parse(re.search(regexp_version_name, content).group(1)) - - if args.release == 'promote': - if version_name.prerelease: - version_name = version_name.bump_prerelease(token='beta') - version_code = build_version_code(version_name) - version_name = version_name.finalize_version() - +#!/usr/bin/env python + +from re import compile, sub, search +from argparse import ArgumentParser, BooleanOptionalAction + +from os.path import basename, join, relpath + +from semver import VersionInfo + +from shared import paths + + +PATH = join(paths['root'], 'app/build.gradle') + +def create_parser(): + parser = ArgumentParser() + + parser.add_argument('-c', '--custom', + dest='custom', + help='custom version name and code (format: \'name|code\')') + parser.add_argument('-r', '--release', + dest='release', + help='bump specific position', + choices=['beta', 'promote', 'patch', 'minor', 'major'], + default='beta') + parser.add_argument('-e', '--env', + dest='env', + help='print variables for shell exporting', + default=False, + action=BooleanOptionalAction) + parser.add_argument('-i', + dest='input', + metavar='PATH', + help=f"path to {basename(PATH)} to process (default: '{relpath(PATH)}')", + default=PATH) + parser.add_argument('-w', '--write', + dest='write', + help='write to file', + default=False, + action=BooleanOptionalAction) + return parser + + +def bump_version(): + + regexp_version_code = compile(r'versionCode (\d+)') + regexp_version_name = compile(r'versionName "((\d+\.\d+\.\d+)(-beta\.?(\d+))?)"') + is_beta = 'true' if args.release == 'beta' else 'false' + + def build_version_code(version): + major = str(version.major) + minor = str(version.minor).zfill(2) + patch = str(version.patch) + beta = '00' if version.prerelease is None else \ + ''.join(filter(str.isdigit, version_name.prerelease)).zfill(2) + return int(major + minor + patch + beta) + + with open(args.input, 'r+') as file: + content = file.read() + + if args.custom: + version_name, version_code = args.custom.split('|') else: - match args.release: - case 'major': version_name = version_name.bump_major() - case 'minor': version_name = version_name.bump_minor() - case 'patch': version_name = version_name.bump_patch() - case 'beta': - if not version_name.prerelease: - version_name = version_name.bump_minor() + version_code = search(regexp_version_code, content).group(1) + version_name = VersionInfo.parse(search(regexp_version_name, content).group(1)) + + if args.release == 'promote': + if version_name.prerelease: version_name = version_name.bump_prerelease(token='beta') - - version_code = build_version_code(version_name) - - if not args.env: - print(f'Name: {version_name}') - print(f'Code: {version_code}') - - if args.write: - content = re.sub(regexp_version_code, f'versionCode {version_code}', content) - content = re.sub(regexp_version_name, f'versionName "{version_name}"', content) - file.seek(0) - file.write(content) - file.truncate() - - -if args.env: - print(f'is_beta={is_beta}') - print(f'version=v{version_name}') - print(f'version_code={version_code}') - print(f'version_name={version_name}') - print(f'version_next={version_name.bump_minor()}') + version_code = build_version_code(version_name) + version_name = version_name.finalize_version() + + else: + match args.release: + case 'major': version_name = version_name.bump_major() + case 'minor': version_name = version_name.bump_minor() + case 'patch': version_name = version_name.bump_patch() + case 'beta': + if not version_name.prerelease: + version_name = version_name.bump_minor() + version_name = version_name.bump_prerelease(token='beta') + + version_code = build_version_code(version_name) + + if args.env: + print(f'is_beta={is_beta}') + print(f'version=v{version_name}') + print(f'version_code={version_code}') + print(f'version_name={version_name}') + print(f'version_next={version_name.bump_minor()}') + else: + print(f'Name: {version_name}') + print(f'Code: {version_code}') + + if args.write: + content = sub(regexp_version_code, f'versionCode {version_code}', content) + content = sub(regexp_version_name, f'versionName "{version_name}"', content) + file.seek(0) + file.write(content) + file.truncate() + + +if __name__ == '__main__': + parser = create_parser() + args = parser.parse_args() + bump_version() diff --git a/resources/scripts/count_icons.py b/resources/scripts/count_icons.py index 346b2debb7..778c0a197f 100755 --- a/resources/scripts/count_icons.py +++ b/resources/scripts/count_icons.py @@ -1,92 +1,91 @@ -#! /usr/bin/env python +#!/usr/bin/env python -import argparse, re import xml.etree.ElementTree as ET -from os.path import join +from argparse import ArgumentParser +from re import sub +from os.path import basename, join, relpath -from resolve_paths import paths +from shared import paths, transform_xml, list_categories -target = paths['drawable'][0] +path = paths['d1'] +cat_all = 'All' -parser = argparse.ArgumentParser(description=f'count icons') +def create_parser(): + parser = ArgumentParser() -parser.add_argument('-i', '--input', - dest='input', - help=f'path to drawable.xml', - default=target) -parser.add_argument('-c', '--category', - dest='category', - help=f'count icons in category', - default=None) -parser.add_argument('-w', '--write', - dest='write', - help='write to files', - default=False, - action=argparse.BooleanOptionalAction) -parser.add_argument('-p', '--print', - dest='print', - help='output to console', - default=False, - action=argparse.BooleanOptionalAction) + parser.add_argument('-i', + dest='input', + metavar='PATH', + help=f"path to {basename(path)} (default: '{relpath(path)}')", + default=path) + parser.add_argument('-c', + dest='category', + metavar='NAME', + help=f"count number of icons in specific category (default: '{cat_all}')", + default=cat_all) + parser.add_argument('-l', + dest='list', + help=f"list available categories excluding category '{cat_all}'", + action='store_true') + parser.add_argument('-w', + dest='write', + help=f"write number of icons to CandyBar.java (only works with category '{cat_all}')", + action='store_true') + return parser -def transform(xml): - if '' in xml: - xml = re.sub('\t', '', xml) # remove tags - xml = re.sub(r'\n()', r'\1\n', xml) # remove \n before and add it after - xml = re.sub(r'(', r'\1 />', xml) # -> - else: - xml = re.sub(r'/>\n(\n\t<)', r'/>\1/category>\1', xml) # add after latest - xml = re.sub(r'()', r'\t\n\1', xml) # add before - xml = re.sub(r'(', r'\1>', xml) # remove slash from - return xml - - -def main(category = None): - - try: - category = args.category - except: - pass - - with open(target) as file: - xml = file.read().rstrip() # read drawable.xml to string - - root = ET.fromstring(transform(xml)) # transform to true XML format and convert it from string to ET - - if category: - try: - category = len(root.find(f'.//category[@title="{category}"]')) - except: - print(f'{category} does not exist') - exit(1) - return category - else: - count = 0 - for category in root.findall('category'): - count += len(category) - count -= len(root.find(f'.//category[@title="New"]')) - - try: - if args.write: - for type in ['play', 'foss']: - path = join(paths['root'], f'app/src/{type}/java/website/leifs/delta/applications/CandyBar.java') - with open(path, 'r+') as file: - content = file.read() - content = re.sub(r'setCustomIconsCount\(.*\)', f'setCustomIconsCount({count})', content) - file.seek(0) - file.write(content) - file.truncate() - except: - pass +def count_icons(input=path, category=cat_all, write=False, list_cats=False): + + categories = list_categories(input) + + if list_cats: + print('\n'.join(['- ' + x for x in categories])) + exit() + + with open(input) as file: + xml = transform_xml(file.read().rstrip()) + root = ET.fromstring(xml) + + elements = root.findall('category') + category = category.capitalize() + categories = [cat_all] + categories + + count = 0 + + if category not in categories: return count - + + if category == cat_all: + for element in elements: + if element.get('title') in [cat_all, 'New']: + continue + count += len(element) + + if write: + for type in ['play', 'foss']: + java_path = join(paths['root'], f'app/src/{type}/java/website/leifs/delta/applications/CandyBar.java') + with open(java_path, 'r+') as file: + content = file.read().rstrip() + content = sub(r'setCustomIconsCount\(.*\)', f'setCustomIconsCount({count})', content) + file.seek(0) + file.write(content) + file.truncate() + else: + count = len(root.find(f'category[@title="{category}"]')) + + return count + if __name__ == '__main__': + parser = create_parser() args = parser.parse_args() - count = main() - if args.print: - print(count) \ No newline at end of file + count = count_icons( + input=args.input, + category=args.category, + write=args.write, + list_cats=args.list, + ) + print(count) \ No newline at end of file diff --git a/resources/scripts/create_changelog.py b/resources/scripts/create_changelog.py index b06ea20daa..eb2c9472e7 100755 --- a/resources/scripts/create_changelog.py +++ b/resources/scripts/create_changelog.py @@ -1,50 +1,53 @@ -#! /usr/bin/env python +#!/usr/bin/env python -import html, re -import base64 import xml.etree.ElementTree as ET -import argparse -from count_icons import main as count_icons - - -parser = argparse.ArgumentParser(description=f'create changelog') - -parser.add_argument('-d', '--data', - dest='data', - help='changelog b64 encoded') -parser.add_argument('-r', '--release-type', - dest='release_type', - help='type of release', - default='beta') -parser.add_argument('-p', '--print', - dest='print', - help='output to console', - default=False, - action=argparse.BooleanOptionalAction) -parser.add_argument('-w', '--write', - dest='write', - help='write to files', - default=False, - action=argparse.BooleanOptionalAction) -parser.add_argument('-t', '--txt', - dest='txt', - help='path to txt changelog', - default='changelog.txt') -parser.add_argument('-x', '--xml', - dest='xml', - help='path to xml changelog', - default='changelog.xml') -args = parser.parse_args() + +from argparse import ArgumentParser +from base64 import b64decode +from html import unescape +from re import sub + +from count_icons import count_icons + + +def create_parser(): + parser = ArgumentParser(description=f'create changelog') + + parser.add_argument('-d', '--data', + dest='data', + help='changelog b64 encoded') + parser.add_argument('-r', '--release-type', + dest='release_type', + help='type of release', + choices=['prod', 'beta', 'foss'], + default='beta') + parser.add_argument('-p', '--print', + dest='print', + help='output to console', + action='store_true') + parser.add_argument('-w', '--write', + dest='write', + help='write to files', + action='store_true') + parser.add_argument('-t', '--txt', + dest='txt', + help='path to txt changelog', + default='changelog.txt') + parser.add_argument('-x', '--xml', + dest='xml', + help='path to xml changelog', + default='changelog.xml') + return parser def main(): icons_total = count_icons() - icons_new = count_icons('New') + icons_new = count_icons(category='New') txt = [] try: - data = base64.b64decode(args.data).decode() + data = b64decode(args.data).decode() except: data = '' @@ -75,19 +78,19 @@ def main(): for line in data.splitlines(): line = line.strip() if not line: continue - line = re.sub('^-+', '', line).strip() + line = sub('^-+', '', line).strip() ET.SubElement(changelog, 'item').text = line txt.append('- ' + line) tree = ET.ElementTree(resources) - ET.indent(tree, space="\t", level=0) + ET.indent(tree, space='\t', level=0) tree = tree.getroot() xml = ET.tostring(tree, encoding='unicode', xml_declaration=True, short_empty_elements=False) - xml = html.unescape(re.sub(r"(\w+)='(.*?)'", r'\1="\2"', xml)) + xml = unescape(sub(r"(\w+)='(.*?)'", r'\1="\2"', xml)) if args.print: print(xml) @@ -99,4 +102,6 @@ def main(): file.write('\n'.join(txt)) if __name__ == '__main__': + parser = create_parser() + args = parser.parse_args() main() \ No newline at end of file diff --git a/resources/scripts/email_dumper.py b/resources/scripts/email_dumper.py deleted file mode 100755 index 4271ea8ecd..0000000000 --- a/resources/scripts/email_dumper.py +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env python3 - -import argparse, re - -from datetime import datetime -from os import mkdir -from os.path import exists, join - -from redbox import EmailBox as redbox - -from resolve_paths import paths - -parser = argparse.ArgumentParser(description='Dump a IMAP folder into .eml files') -parser.add_argument('-s', '--host', - dest='host', - help='IMAP host', - default='imap.gmail.com') -parser.add_argument('-P', '--port', - dest='port', - help='IMAP port', - default=993) -parser.add_argument('-u', '--username', - dest='username', - help='IMAP username', - required=True) -parser.add_argument('-p', '--password', - dest='password', - help='IMAP password', - required=True) -parser.add_argument('-r', '--remote', - dest='remote', - help='Remote folder to download', - default='INBOX') -parser.add_argument('-l', '--local', - dest='local', - help='Local folder where to save .eml files', - default=join(paths['scripts'], 'emails')) -parser.add_argument('-U', '--unread', - dest='unread', - help='Keep emails unread in the inbox', - default=False, - action=argparse.BooleanOptionalAction) - -args = parser.parse_args() - -mail = redbox(host=args.host, - port=args.port, - username=args.username, - password=args.password) - -messages = mail[args.remote].search(unseen=True) -date_now = datetime.today().strftime('%Y-%m-%d %H:%M:%S') - -print(f'[{date_now}] New {len(messages)} requests!') - -if not exists(args.local): - mkdir(args.local) - - -for message in messages: - try: - index = messages.index(message) + 1 - output = f'{args.local}/{index}.eml' - date = message.date.strftime('%Y-%m-%d %H:%M:%S') - compinfo = re.search('(.*)\nhttp', message.text_body).group(1).strip() - print(f'[{date}] [{index}] {compinfo}') - with open(output, 'w', newline='') as file: - file.write(message.content) - except: - continue - finally: - if args.unread: - message.unread() \ No newline at end of file diff --git a/resources/scripts/email_parser.py b/resources/scripts/email_parser.py deleted file mode 100755 index 9ad0d1e2db..0000000000 --- a/resources/scripts/email_parser.py +++ /dev/null @@ -1,98 +0,0 @@ -#!/usr/bin/env python3 - -import base64 -import copy -import io -import re -import os - -from datetime import datetime -from hashlib import sha1 -from zipfile import ZipFile - -import mailparser -import xmltodict - -from requests_parser import read, write -from resolve_paths import paths - -DATE_NOW = datetime.now() - -EMAILS_DIR = os.path.join(paths['scripts'], 'emails') -REQUESTS_FILE = paths['requests'] - -GREEDY_COUNTER = 1 -REQUEST_LIFETIME = 90 # days - -STORES = [ - 'https://play.google.com/store/apps/details?id={}', - 'https://f-droid.org/en/packages/{}/', - # 'https://apkpure.com/delta/{}/', - 'https://google.com/search?q={}', -] - -greedy_users = {} - -requests_diff = read(REQUESTS_FILE) -requests = copy.deepcopy(requests_diff) - - -def decode_zip(data): - decoded = base64.urlsafe_b64decode(data.encode('UTF-8')) - return ZipFile(io.BytesIO(decoded)) - - -for key, value in requests_diff.items(): - insertion_date = value['reql'] - delta = DATE_NOW - insertion_date - if delta.days > REQUEST_LIFETIME: - requests.pop(key) - print(f'[DEL] [{insertion_date}] {key}') - - -for file in os.listdir(EMAILS_DIR): - file = os.path.join(EMAILS_DIR, file) - - if not file.endswith('.eml'): continue - if not os.path.isfile(file): continue - - try: - with open(file, 'rb') as file: - mail = mailparser.parse_from_bytes(file.read()) - - sender = mail.from_[0][1] - - if sender in greedy_users: - if greedy_users[sender] > GREEDY_COUNTER: continue - - date = mail.date - - attachments = decode_zip(mail.attachments[0]['payload']) - xml = xmltodict.parse(attachments.read('appfilter.xml'), process_comments=True)['resources'] - - name = xml['#comment'] - compinfo = re.search('ComponentInfo{(.*)}', xml['item']['@component'], re.IGNORECASE).group(1) - id = compinfo.split('/')[0] - - if sender not in greedy_users: greedy_users[sender] = 1 - - if compinfo not in requests: - requests[compinfo] = { - 'name': name, - 'reqt': 1, - 'reql': date, - 'hash': sha1(compinfo.encode()).hexdigest(), - 'urls': [url.format(id) for url in STORES] - } - print(f'[NEW] [{date}] {compinfo}') - else: - greedy_users[sender] += 1 - requests[compinfo]['reqt'] += 1 - if requests[compinfo]['reql'] < date: - requests[compinfo]['reql'] = date - print(f'[N++] [{date}] {compinfo}') - except: - continue - - -write(REQUESTS_FILE, requests) diff --git a/resources/scripts/process_emails.py b/resources/scripts/process_emails.py new file mode 100755 index 0000000000..bb161abf5d --- /dev/null +++ b/resources/scripts/process_emails.py @@ -0,0 +1,218 @@ +#!/usr/bin/env python + +from argparse import ArgumentParser +from re import compile, search, IGNORECASE + +from base64 import urlsafe_b64decode +from copy import deepcopy +from datetime import datetime +from hashlib import sha1 +from io import BytesIO +from os import listdir, mkdir +from os.path import exists, isfile, join, relpath +from sys import argv +from zipfile import ZipFile +from xmltodict import parse as xmlparse + +from html2text import HTML2Text +from mailparser import parse_from_bytes +from redbox import EmailBox as redbox + +from shared import paths, stores +from process_requests import read, write + + +GREEDY_COUNTER = 1 +REQUEST_LIFETIME = 90 # days + +HOST = 'imap.gmail.com' +PORT = 993 +LOCAL = paths['emails'] +REMOTE = 'Requests' + + +def create_parser(): + parser = ArgumentParser() + + parser.add_argument('-d', + dest='dump', + help='dump emails', + action='store_true') + parser.add_argument('--host', + dest='host', + help=f"IMAP host (default: '{HOST}')", + default=HOST) + parser.add_argument('--port', + dest='port', + help=f"IMAP port (default: '{PORT}')", + default=PORT) + parser.add_argument('--user', + dest='username', + help='IMAP username', + required='-d' in argv) + parser.add_argument('--pass', + dest='password', + help='IMAP password', + required='-d' in argv) + parser.add_argument('--remote', + dest='remote', + help=f"remote email folder to parse (default: '{REMOTE}')", + default='Requests') + parser.add_argument('--unread', + dest='unread', + help='keep emails unread in remote folder', + action='store_true') + parser.add_argument('--local', + dest='local', + help=f"local folder where to dump/parse emails (default: '{relpath(LOCAL)}')", + default=paths['emails']) + parser.add_argument('-p', + dest='parse', + help='parse dumped emails', + action='store_true') + return parser + +def dump_emails( + host=HOST, + port=PORT, + username=None, + password=None, + local=LOCAL, + remote=REMOTE, + ): + + if not username or not password: + print('username/password cannot be None') + exit(1) + + mail = redbox( + host=host, + port=port, + username=username, + password=password, + ) + + messages = mail[args.remote].search(unseen=True) + date_now = datetime.today().strftime('%Y-%m-%d %H:%M:%S') + + if len(messages) > 0: + print(f'[{date_now}] Found {len(messages)} emails') + else: + print(f'[{date_now}] No new requests!') + exit(0) + + if not exists(args.local): + mkdir(args.local) + + for index, message in enumerate(messages, 1): + try: + output = join(args.local, f'{index}.eml') + date = message.date.strftime('%Y-%m-%d %H:%M:%S') + with open(output, 'w', newline='') as file: + file.write(message.content) + print(f'[{date}] Saved to {index}.eml') + except Exception: + continue + finally: + if args.unread: + message.unread() + + +def parse_emails(local=LOCAL): + + date_now = datetime.now() + + requests_file = paths['requests'] + + greedy_users = {} + + requests_diff = read(requests_file) + requests = deepcopy(requests_diff) + + html2text = HTML2Text() + html2text.ignore_links = True + pattern_ci = compile(r'^[a-zA-Z0-9._]+/[a-zA-Z0-9._$]') + + def decode_zip(data): + decoded = urlsafe_b64decode(data.encode('UTF-8')) + return ZipFile(BytesIO(decoded)) + + + for key, value in requests_diff.items(): + insertion_date = value['reql'] + delta = date_now - insertion_date + if delta.days > REQUEST_LIFETIME: + requests.pop(key) + print(f'[DEL] [{insertion_date}] {key}') + + + for file in listdir(local): + file = join(local, file) + + if not file.endswith('.eml'): continue + if not isfile(file): continue + + try: + with open(file, 'rb') as file: + mail = parse_from_bytes(file.read()) + + sender = mail.from_[0][1] + + if sender in greedy_users: + if greedy_users[sender] > GREEDY_COUNTER: continue + + date = mail.date.replace(tzinfo=None) + + if mail.attachments: + attachments = decode_zip(mail.attachments[0]['payload']) + xml = xmlparse(attachments.read('appfilter.xml'), process_comments=True)['resources'] + name = xml['#comment'] + compinfo = search('ComponentInfo{(.*)}', xml['item']['@component'], IGNORECASE).group(1) + else: + body_plain = html2text.handle(mail.body).strip().split() + compinfo = [*{*list(filter(lambda s: pattern_ci.search(s), body_plain))}][0] + + id = compinfo.split('/')[0] + + if sender not in greedy_users: greedy_users[sender] = 1 + + if compinfo not in requests: + requests[compinfo] = { + 'name': name, + 'reqt': 1, + 'reql': date, + 'hash': sha1(compinfo.encode()).hexdigest(), + 'urls': [url.format(id) for url in stores] + } + print(f'[NEW] [{date}] {compinfo}') + else: + greedy_users[sender] += 1 + requests[compinfo]['reqt'] += 1 + if requests[compinfo]['reql'] < date: + requests[compinfo]['reql'] = date + print(f'[N++] [{date}] {compinfo}') + except Exception as error: + # print(file, error) + continue + + write(requests_file, requests) + + +if __name__ == '__main__': + parser = create_parser() + args = parser.parse_args() + + if args.dump: + dump_emails( + host=args.host, + port=args.port, + username=args.username, + password=args.password, + local=args.local, + remote=args.remote, + ) + + if args.parse: + parse_emails( + local=args.local + ) \ No newline at end of file diff --git a/resources/scripts/process_icons.py b/resources/scripts/process_icons.py new file mode 100755 index 0000000000..a8cffba290 --- /dev/null +++ b/resources/scripts/process_icons.py @@ -0,0 +1,424 @@ +#!/usr/bin/env python + +import re +import logging as log +import xml.etree.ElementTree as ET + +from argparse import ArgumentParser +from copy import deepcopy +from os import environ, unlink +from os.path import basename, isfile, relpath +from shutil import copy, move + +from natsort import natsorted as sorted +from termcolor import colored as color + +from process_requests import read +from shared import paths, transform_xml + +ACTIONS = ('none', 'replace', 'rebrand', 'remove') + +PATTERN_ALT = re.compile(r'^.*_alt_\d+$') +PATTERN_CI = re.compile(r'^[a-zA-Z0-9._]+/[a-zA-Z0-9._$]+$') +PATTERN_DRAWABLE = re.compile(r'^[a-z0-9_]+$') + +ICONS_HEADER = '''\ +# # action: none|replace|rebrand|remove|rename > new_name +# # none - default, new icon +# # replace - overwrite existing images, no xml changes +# # rebrand - existing icon -> alt_x, new icon -> main +# # remove - remove existing icon (files + xml) +# # rename - rename existing icon (files + xml) +# +# new_icon_1: com.example.icon/com.example.icon.MainActivity +# +# new_icon_2: +# - com.example.icon/com.example.icon.MainActivity +# - com.example.icon/com.example.icon.SplashActivity +# +# new_icon_3_alt_1: {} +# +# new_icon_4: {} +# +# signal: +# action: rebrand +# category: Google +# compinfo: +# - com.google.app/com.google.app.MainActivity +# +''' + +args = ArgumentParser() +args.add_argument('-d', '--dry-run', action='store_true', help='do things without changing any files') +args.add_argument('-v', '--verbose', action='store_true', help='verbose') +args.add_argument('-n', '--no-color', action='store_true', help='disable colors') +args = args.parse_args() + +if args.no_color: + environ['NO_COLOR'] = '1' + +log.addLevelName(log.DEBUG, color('d', 'magenta')) +log.addLevelName(log.INFO, color('i', 'blue')) +log.addLevelName(log.WARNING, color('w', 'yellow')) +log.addLevelName(log.ERROR, color('e', 'red')) +log.addLevelName(log.CRITICAL, color('c', 'red')) +log.basicConfig( + level=log.DEBUG if args.verbose else log.INFO, + datefmt='%H:%M:%S', + format='%(levelname)s %(message)s', +) + + +def log_sep(name): + print(color(f'\n<------- {name} ------->', 'dark_grey')) + + +def xml_parser(): + return ET.XMLParser(target=ET.TreeBuilder(insert_comments=True)) + + +def write_file(target, content): + with open(target, 'w') as file: + file.write(content) + + +def parse_action(raw): + match raw: + case True: + return 'rebrand', None, None + case False | None: + return 'none', None, None + case str(): + if '>' in raw: + action, _, rename = raw.partition('>') + action, rename = action.strip(), rename.strip() + if action != 'rename': + return 'none', None, f"action {color(action, 'cyan')} doesn't support {color('>', 'cyan')} syntax" + if not rename: + return 'none', None, f"rename requires a target, e.g. {color('rename > new_name', 'cyan')}" + return 'rename', rename, None + if raw == 'rename': + return 'none', None, f"rename requires a target, e.g. {color('rename > new_name', 'cyan')}" + if raw in ACTIONS: + return raw, None, None + return 'none', None, f"action {color(raw, 'cyan')} is invalid, expected: {color(', '.join((*ACTIONS, 'rename > name')), 'cyan')}" + case _: + return 'none', None, f"action {color(str(raw), 'cyan')} is invalid, expected: {color(', '.join((*ACTIONS, 'rename > name')), 'cyan')}" + + +def parse_compinfos(value): + result = [] + for key in ('compinfo', 'compinfos'): + entries = value.get(key, []) + if isinstance(entries, list): + result.extend(entries) + else: + result.append(entries) + return result + + +def check_source_files(key, extensions=('png', 'svg')): + missing = [] + for ext in extensions: + if not isfile(paths['src'][ext].format(key)): + missing.append(ext) + return missing + + +def move_images(src_name, dst_name=None, from_dst=False, header=True): + if dst_name is None: + dst_name = src_name + src_paths = paths['dst'] if from_dst else paths['src'] + if header: + found = any(isfile(src_paths[fmt].format(src_name)) for fmt in ('png', 'svg')) + if found: + log.info(f"images{color(':', 'dark_grey')}") + for fmt in ('png', 'svg'): + src = src_paths[fmt].format(src_name) + dst = paths['dst'][fmt].format(dst_name) + if isfile(src): + if not args.dry_run: + move(src, dst) + print(f" {color(f'~ {relpath(src)} -> {relpath(dst)}', 'blue')}") + + +def remove_images(name): + found = any(isfile(paths['dst'][fmt].format(name)) for fmt in ('png', 'svg')) + if found: + log.info(f"images{color(':', 'dark_grey')}") + for fmt in ('png', 'svg'): + path = paths['dst'][fmt].format(name) + if isfile(path): + if not args.dry_run: + unlink(path) + print(f" {color(f'– {relpath(path)}', 'red')}") + + +def parse_icons(icons_yml, root_drawable): + entries = [] + errors = dict() + + categories = [ + x.get('title') + for x in root_drawable.findall('category') + if x.get('title') != 'New' + ] + + for key, value in icons_yml.items(): + entry_errors = [] + + if not PATTERN_DRAWABLE.fullmatch(key): + entry_errors.append(f"name {color(key, 'cyan')} looks invalid") + errors[key] = True + + action = 'none' + rename = None + compinfos = [] + + category = key[0].upper() + if key.startswith('_'): + category = '#' + if PATTERN_ALT.fullmatch(key): + category = 'Alts' + + match value: + case dict(): + compinfos = parse_compinfos(value) + category = value.get('category', category).capitalize() + action, rename, action_error = parse_action(value.get('action', 'none')) + if action_error: + entry_errors.append(action_error) + errors[key] = True + if action == 'remove': + if not any(isfile(paths['dst'][fmt].format(key)) for fmt in ('png', 'svg')): + entry_errors.append(f"action is {color('remove', 'cyan')}, but {key} doesn't exist in destination") + errors[key] = True + if rename: + if not PATTERN_DRAWABLE.fullmatch(rename): + entry_errors.append(f"rename target {color(rename, 'cyan')} looks invalid") + errors[key] = True + elif not any(isfile(paths['dst'][fmt].format(key)) for fmt in ('png', 'svg')): + entry_errors.append(f"action is {color('rename', 'cyan')}, but {key} doesn't exist in destination") + errors[key] = True + elif 'category' not in value: + category = rename[0].upper() + if rename.startswith('_'): + category = '#' + if PATTERN_ALT.fullmatch(rename): + category = 'Alts' + case list(): + compinfos = list(dict.fromkeys(value)) + case str(): + compinfos = [value] + + if category not in categories and action != 'remove': + entry_errors.append(f"category {color(category.lower(), 'cyan')} doesn't exist") + errors[key] = True + + if action in ('replace', 'rebrand'): + for ext in check_source_files(key): + entry_errors.append(f"action is {color(action, 'cyan')}, but {key}.{ext} not found") + errors[key] = True + + compinfos = list(dict.fromkeys(x for x in compinfos if isinstance(x, str) and x)) + + if not compinfos and category == 'Alts' and action not in ('rename', 'remove'): + for ext in check_source_files(key): + entry_errors.append(f"category is Alts but {key}.{ext} not found") + errors[key] = True + + for compinfo in compinfos: + if not PATTERN_CI.fullmatch(compinfo): + entry_errors.append(f"compinfo {color(compinfo, 'cyan')} looks invalid") + errors[key] = True + + if entry_errors: + log_sep(key) + for error in entry_errors: + log.error(error) + + entries.append((key, { + 'category': category, + 'compinfos': compinfos, + 'action': action, + 'rename': rename, + })) + + if errors: + log_sep('result') + log.critical('fix issues with the next icons:\n' + '\n'.join( + ' - ' + color(x, 'magenta') for x in errors + )) + print() + exit(1) + + return entries + + +def main(): + icons_yml = read(paths['icons']) + + if not icons_yml: + log.warning(f'{basename(paths["icons"])} is empty') + + with open(paths['d1']) as file: + xml_drawable = file.read().rstrip() + + with open(paths['a1']) as file: + xml_appfilter = file.read().rstrip() + + root_drawable = ET.fromstring(transform_xml(xml_drawable), parser=xml_parser()) + root_appfilter = ET.fromstring(xml_appfilter, parser=xml_parser()) + + scale = root_appfilter[0] + root_appfilter.remove(scale) + + category_new = root_drawable.find("category[@title='New']") + category_alt = root_drawable.find("category[@title='Alts']") + + for drawable, values in parse_icons(icons_yml, root_drawable): + log_sep(drawable) + + renamed_drawable = [] + renamed_appfilter = [] + added_drawable = [] + old_drawable = drawable + + if values['rename']: + new_name = values['rename'] + log.info(f"action {color('=', 'dark_grey')} {color('rename', 'magenta')}{color(':', 'dark_grey')} {color(drawable, 'cyan')} {color('->', 'dark_grey')} {color(new_name, 'cyan')}") + + for item in root_appfilter.findall('item'): + if item.get('drawable') == drawable: + renamed_appfilter.append((ET.tostring(item).decode().strip(), new_name)) + item.set('drawable', new_name) + + for cat in root_drawable.findall('category'): + for item in list(cat.findall('item')): + if item.get('drawable') == drawable: + renamed_drawable.append(ET.tostring(item).decode().strip()) + cat.remove(item) + + drawable = new_name + elif values['action'] != 'none': + log.info(f"action {color('=', 'dark_grey')} {color(values['action'], 'magenta')}") + + if values['action'] != 'remove': + log.info(f"category {color('=', 'dark_grey')} {color(values['category'].lower(), 'magenta')}") + + match values['action']: + case 'rename': + move_images(old_drawable, drawable, from_dst=True) + case 'remove': + remove_images(drawable) + removed_drawable = [] + for cat in root_drawable.findall('category'): + for item in list(cat.findall('item')): + if item.get('drawable') == drawable: + removed_drawable.append(ET.tostring(item).decode().strip()) + cat.remove(item) + if removed_drawable: + log.info(f"drawable.xml{color(':', 'dark_grey')}") + for entry in removed_drawable: + print(f" {color(f'– {entry}', 'red')}") + removed_appfilter = [] + for item in list(root_appfilter.findall('item')): + if item.get('drawable') == drawable: + removed_appfilter.append(ET.tostring(item).decode().strip()) + root_appfilter.remove(item) + if removed_appfilter: + log.info(f"appfilter.xml{color(':', 'dark_grey')}") + for entry in removed_appfilter: + print(f" {color(f'– {entry}', 'red')}") + case 'rebrand': + counter = 1 + targets = [] + for item in root_appfilter.findall('item'): + if item.get('drawable') == drawable: + targets.append(item) + if item.get('drawable').startswith(drawable + '_alt_'): + alt_num = int(re.split(r'_', item.get('drawable'))[-1]) + 1 + if alt_num > counter: + counter = alt_num + new_name = f'{drawable}_alt_{counter}' + if values['compinfos']: + for target in targets: + renamed_appfilter.append((ET.tostring(target).decode().strip(), new_name)) + target.set('drawable', new_name) + item = ET.Element('item') + item.set('drawable', new_name) + item.tail = '\n\t' + category_alt.append(item) + move_images(drawable, new_name, from_dst=True) + move_images(drawable, header=False) + added_drawable.append(f' {color(f'(alts)', 'dark_grey')}') + case 'replace': + move_images(drawable) + case _: + move_images(drawable) + + if values['action'] not in ('replace', 'remove'): + category = root_drawable.find(f"category[@title='{values['category']}']") + add_drawable = drawable not in [item.get('drawable') for item in category] + + if renamed_drawable or added_drawable or add_drawable: + log.info(f"drawable.xml{color(':', 'dark_grey')}") + for entry in renamed_drawable: + print(f" {color(f'– {entry}', 'red')}") + for entry in added_drawable: + print(f" {color(f'+ {entry}', 'green')}") + if add_drawable: + item = ET.Element('item') + item.set('drawable', drawable) + cat_name = values['category'].lower() + print(f" {color(f'+ {ET.tostring(item).decode()}', 'green')} {color(f'(new, {cat_name})', 'dark_grey')}") + item.tail = '\n\t' + category.append(item) + category_new.append(deepcopy(item)) + + if renamed_appfilter or values['compinfos']: + log.info(f"appfilter.xml{color(':', 'dark_grey')}") + for old_entry, new_drawable in renamed_appfilter: + print(f" {color(f'– {old_entry}', 'red')}") + new_entry = old_entry.replace(f'drawable="{old_drawable}"', f'drawable="{new_drawable}"') + print(f" {color(f'+ {new_entry}', 'green')}") + + for compinfo in values['compinfos']: + component = f'ComponentInfo{{{compinfo}}}' + if any(item.get('drawable') == drawable and item.get('component') == component for item in root_appfilter): + log.warning(f"duplicate ci: {color(compinfo, 'cyan')}") + continue + item = ET.Element('item') + item.set('component', component) + item.set('drawable', drawable) + print(f" {color(f'+ {ET.tostring(item).decode()}', 'green')}") + item.tail = '\n\t' + root_appfilter.append(item) + + for category in root_drawable.findall('category'): + category[:] = sorted(category.findall('item'), key=lambda item: item.get('drawable')) + + xml_drawable = ET.tostring(root_drawable, encoding='unicode', short_empty_elements=True, xml_declaration=True) + xml_drawable = re.sub("'", '"', transform_xml(xml_drawable)) + + root_appfilter[:] = sorted(root_appfilter, key=lambda item: (item.tag, item.get('component'))) + root_appfilter.insert(0, scale) + + ET.indent(root_appfilter, space='\t') + xml_appfilter = ET.tostring(root_appfilter, encoding='unicode', xml_declaration=True) + xml_appfilter = re.sub("'", '"', xml_appfilter) + + if not args.dry_run: + write_file(paths['a1'], xml_appfilter) + copy(paths['a1'], paths['a2']) + + write_file(paths['d1'], xml_drawable) + copy(paths['d1'], paths['d2']) + + write_file(paths['icons'], ICONS_HEADER) + + print() + + +if __name__ == '__main__': + main() diff --git a/resources/scripts/process_requests.py b/resources/scripts/process_requests.py new file mode 100755 index 0000000000..f6a9527553 --- /dev/null +++ b/resources/scripts/process_requests.py @@ -0,0 +1,199 @@ +#!/usr/bin/env python + +from argparse import ArgumentParser +import argparse, re, sys +import xml.etree.ElementTree as ET +from datetime import datetime +from difflib import SequenceMatcher +from copy import deepcopy +from os.path import basename, relpath +import yaml +from natsort import natsorted as sorted + +from shared import paths, stores + + +class YamlNewLines(yaml.SafeDumper): + # https://github.com/yaml/pyyaml/issues/127#issuecomment-525800484 + def write_line_break(self, data=None): + super().write_line_break(data) + if len(self.indents) == 1: + super().write_line_break() + + def increase_indent(self, flow=False, indentless=False): + return super().increase_indent(flow, False) + + +PATH = paths['requests'] + +FORMATS = ['txt', 'xml', 'yml'] +FORMATS_DEFAULT = FORMATS[2] + +SORTS = ['name', 'ratio', 'request'] +SORTS_DEFAULT = SORTS[1] + +RATIO_RANGE = {'min': 0.25, 'default': 0.75, 'max': 1.0} + + +def create_parser(): + parser = ArgumentParser() + + parser.add_argument('-i', + dest='input', + metavar='PATH', + help=f"path to {basename(PATH)} to process (default: '{relpath(PATH)}')", + default=PATH) + parser.add_argument('-f', + dest='format', + metavar=f"[{'|'.join(FORMATS)}]", + choices=FORMATS, + help=f"output in specific format (default: '{FORMATS_DEFAULT}')", + default=FORMATS_DEFAULT) + parser.add_argument('-r', + dest='ratio', + metavar='VALUE', + help=f"custom ratio value from {RATIO_RANGE['min']} to {RATIO_RANGE['max']} (default: {RATIO_RANGE['default']})", + default=RATIO_RANGE['default']) + parser.add_argument('-H', + dest='hide_ratios', + help='hide ratios in output', + action='store_true') + parser.add_argument('-s', + dest='sort', + metavar=f"[{'|'.join(SORTS)}]", + help=f"sort by specific value (default: '{SORTS_DEFAULT}')", + choices=SORTS, + default=SORTS_DEFAULT) + parser.add_argument('-u', '--update', + dest='update', + help=f'update and remove entries in {basename(PATH)}', + action='store_true') + parser.add_argument('--urls', + dest='urls', + help=f'update store urls (use with -u)', + action='store_true') + parser.add_argument('-q', + dest='quiet', + help='do not print anything', + action='store_true') + return parser + +def read(path): + with open(path, 'r+') as file: + loaded = yaml.safe_load(file) + return loaded if loaded is not None else {} + + +def write(path, data): + with open(path, 'r+') as file: + data = dict(sorted(data.items(), + # sort by number of requests, then by last request time + key=lambda k: (k[1]['reqt'], k[1]['reql']), + # reverse array to set new requests at top of the list + reverse=True)) + # header message with total number of requested icons and last time update + header = (f"# {len(data)} requested apps pending \n" + f"# updated {datetime.today().strftime('%Y-%m-%d %H:%M:%S')}\n\n") + dump = yaml.dump(data, Dumper=YamlNewLines, allow_unicode=True, indent=4, sort_keys=False) + file.seek(0) + file.write(header + dump) + file.truncate() + + +def parse_requests( + input=PATH, + format=FORMATS_DEFAULT, + ratio=RATIO_RANGE['default'], + sort=SORTS_DEFAULT, + hide_ratios=False, + update=False, + urls=False, + quiet=False, + ): + + requests = read(input) + requests_copy = deepcopy(requests) + + updatable = {} + + RATIO = float(ratio) if RATIO_RANGE['min'] <= float(ratio) <= RATIO_RANGE['max'] else RATIO_RANGE['default'] + + with open(paths['a1'], 'r') as file: + appfilter = ET.ElementTree(ET.fromstring(file.read().rstrip())).getroot() + + if urls: + for request in requests_copy: + id, activity = request.split('/') + requests[request]['urls'] = [url.format(id) for url in stores] + + for item in appfilter: + try: + compinfo = re.search('ComponentInfo{(.*)}', item.attrib['component']).group(1) + id, activity = compinfo.split('/') + name = item.attrib['drawable'] + + for request in requests_copy: + if id not in request: continue + + ratio = 0.0 + + if request.startswith(id + '/'): + ratio = 1.0 + else: + diff = SequenceMatcher(None, request, compinfo).ratio() + ratio = round(diff, 2) + + if ratio >= RATIO: + + if ratio == 1.0: + requests.pop(request) + + if request in updatable: + if updatable[request]['ratio'] < ratio: + ratio = updatable[request]['ratio'] + + updatable[request] = { + 'name': name, + 'ratio': ratio, + 'request': request + } + except: + continue + + updatable = dict(sorted(updatable.items(), key=lambda x: x[1][sort])) + + if update: + if requests != requests_copy: + write(input, requests) + + if not quiet: + for [key, value] in updatable.items(): + name = value['name'] + ratio = int(value['ratio'] * 100) + + match format: + case 'xml': + ratios = f' ' if not hide_ratios else '' + print(ratios + f'') + case 'yml': + ratios = f' # {ratio}%' if not hide_ratios else '' + print(f'{name}:'+ ratios) + print(f' - {key}\n') + case 'txt': + ratios = f'[{ratio}%] ' if not hide_ratios else '' + print(ratios + f'{name} -> {key}') + + +if __name__ == '__main__': + parser = create_parser() + args = parser.parse_args() + parse_requests( + input=args.input, + format=args.format, + ratio=args.ratio, + sort=args.sort, + hide_ratios=args.hide_ratios, + update=args.update, + urls=args.urls, + quiet=args.quiet, + ) \ No newline at end of file diff --git a/resources/scripts/requests_parser.py b/resources/scripts/requests_parser.py deleted file mode 100755 index 81871c6eeb..0000000000 --- a/resources/scripts/requests_parser.py +++ /dev/null @@ -1,149 +0,0 @@ -#!/usr/bin/env python3 - -import argparse, re, sys -import xml.etree.ElementTree as ET -from datetime import datetime -from difflib import SequenceMatcher -from copy import deepcopy as copy - -import yaml -from natsort import natsorted as sorted - -from resolve_paths import paths - - -class YamlNewLines(yaml.SafeDumper): - # https://github.com/yaml/pyyaml/issues/127#issuecomment-525800484 - def write_line_break(self, data=None): - super().write_line_break(data) - if len(self.indents) == 1: - super().write_line_break() - - def increase_indent(self, flow=False, indentless=False): - return super().increase_indent(flow, False) - -filename = 'requests.yml' - - -parser = argparse.ArgumentParser(description=f'parse {filename}') - -parser.add_argument('-f', '--format', - dest='format', - choices=['xml', 'yml'], - help='output in specific format') -parser.add_argument('-r', '--remove', - dest='remove', - help=f'remove existing compinfos from {filename}', - default=False, - action=argparse.BooleanOptionalAction) -parser.add_argument('-R', '--ratio', - dest='ratio', - help=f'custom ratio from 0.5 to 1.0', - default=0.75) -parser.add_argument('-s', '--sort', - dest='sort', - help='sort by specific value', - choices=['name', 'ratio', 'request'], - default='ratio') -parser.add_argument('-H', '--hide-ratios', - dest='hide_ratios', - help='hide ratios in output', - default=False, - action=argparse.BooleanOptionalAction) - - -def read(path): - with open(path, 'r+') as file: - loaded = yaml.safe_load(file) - return loaded if loaded is not None else {} - - -def write(path, data): - with open(path, 'r+') as file: - data = dict(sorted(data.items(), - # sort by number of requests, then by last request time - key=lambda k: (k[1]['reqt'], k[1]['reql']), - # reverse array to set new requests at top of the list - reverse=True)) - # header message with total number of requested icons and last time update - header = (f"# {len(data)} requested apps pending \n" - f"# updated {datetime.today().strftime('%Y-%m-%d %H:%M:%S')}\n\n") - dump = yaml.dump(data, Dumper=YamlNewLines, allow_unicode=True, indent=4, sort_keys=False) - file.seek(0) - file.write(header + dump) - file.truncate() - -def main(): - requests = read(paths['requests']) - requests_copy = copy(requests) - - updatable = {} - - RATIO = float(args.ratio) if 0.5 <= float(args.ratio) <= 1.0 else 0.75 - - with open(paths['appfilter'][0], 'r') as file: - appfilter = ET.ElementTree(ET.fromstring(file.read())).getroot() - - for item in appfilter: - try: - compinfo = re.search('ComponentInfo{(.*)}', item.attrib['component']).group(1) - id, activity = compinfo.split('/') - name = item.attrib['drawable'] - - for request in requests_copy: - if id not in request: continue - - ratio = 0.0 - - if request.startswith(id + '/'): - ratio = 1.0 - else: - diff = SequenceMatcher(None, request, compinfo).ratio() - ratio = round(diff, 2) - - if ratio >= RATIO: - - if ratio == 1.0: - requests.pop(request) - - if request in updatable: - if updatable[request]['ratio'] < ratio: - ratio = updatable[request]['ratio'] - - updatable[request] = { - 'name': name, - 'ratio': ratio, - 'request': request - } - - except: - continue - - updatable = dict(sorted(updatable.items(), key=lambda x: x[1][args.sort])) - - if args.remove: - write(paths['requests'], requests) - - for [key, value] in updatable.items(): - name = value['name'] - ratio = int(value['ratio'] * 100) - - match args.format: - case 'xml': - ratios = f' ' if not args.hide_ratios else '' - print(ratios + f'') - case 'yml': - ratios = f' # {ratio}%' if not args.hide_ratios else '' - print(f'{name}:'+ ratios) - print(f' - {key}\n') - case _: - ratios = f'[{ratio}%] ' if not args.hide_ratios else '' - print(ratios + f'{name} -> {key}') - -if __name__ == '__main__': - if len(sys.argv) > 1: - args = parser.parse_args() - else: - parser.print_help() - sys.exit(1) - main() \ No newline at end of file diff --git a/resources/scripts/requirements.txt b/resources/scripts/requirements.txt index 75d52f8925..e3984482de 100644 --- a/resources/scripts/requirements.txt +++ b/resources/scripts/requirements.txt @@ -1,6 +1,8 @@ -mail-parser==4.0.0 +html2text==2025.4.15 +mail-parser==4.1.4 natsort==8.4.0 -pyyaml==6.0.2 -semver==3.0.4 +pyyaml==6.0.3 redbox==0.2.1 -xmltodict==0.14.2 +semver==3.0.4 +termcolor==3.2.0 +xmltodict==1.0.2 \ No newline at end of file diff --git a/resources/scripts/resolve_paths.py b/resources/scripts/resolve_paths.py deleted file mode 100755 index be9428556e..0000000000 --- a/resources/scripts/resolve_paths.py +++ /dev/null @@ -1,67 +0,0 @@ -#! /usr/bin/env python - -import argparse, sys - -from os.path import abspath, dirname, realpath, join - - -parser = argparse.ArgumentParser(description='Process drawable.xml file') - -parser.add_argument('-p', '--print', - dest='print', - help='Print paths for shell exporting', - default=False, - action=argparse.BooleanOptionalAction) - - -def format_paths(): - root = abspath(f'{dirname(realpath(__file__))}/../..') - scripts = abspath(dirname(realpath(__file__))) - contribs = join(root, 'contribs') - icons = join(contribs, 'icons') - return { - 'root': root, - 'scripts': scripts, - 'contribs': contribs, - 'icons': join(contribs, 'icons.yml'), - 'requests': join(contribs, 'requests.yml'), - 'vectors': join(root, 'resources/vectors'), - 'src': { - 'dir': icons, - 'svg': join(icons, f'{{}}.svg'), - 'png': join(icons, f'{{}}.png') - }, - 'dst': { - 'svg': join(root, f'resources/vectors/{{}}.svg'), - 'png': join(root, f'app/src/main/res/drawable-nodpi/{{}}.png') - }, - 'appfilter': [ - join(root, 'app/src/main/res/xml/appfilter.xml'), - join(root, 'app/src/main/assets/appfilter.xml') - ], - 'drawable': [ - join(root, 'app/src/main/res/xml/drawable.xml'), - join(root, 'app/src/main/assets/drawable.xml') - ] - } - -paths = format_paths() - - -if __name__ == '__main__': - args = parser.parse_args() - if len(sys.argv) > 1: - args = parser.parse_args() - else: - parser.print_help() - sys.exit(1) - if args.print: - print(f"wd={paths['root']}") - print(f"sd={paths['scripts']}") - print(f"id={paths['src']['dir']}") - print(f"vd={paths['vectors']}") - print(f"rq={paths['requests']}") - print(f"a1={paths['appfilter'][0]}") - print(f"a2={paths['appfilter'][1]}") - print(f"d1={paths['drawable'][0]}") - print(f"d2={paths['drawable'][1]}") diff --git a/resources/scripts/shared.py b/resources/scripts/shared.py new file mode 100755 index 0000000000..3cdb8887e3 --- /dev/null +++ b/resources/scripts/shared.py @@ -0,0 +1,98 @@ +#! /usr/bin/env python + +import xml.etree.ElementTree as ET + +from argparse import ArgumentParser +from re import sub +from sys import argv +from os.path import abspath, dirname, realpath, join + + +stores = [ + 'https://play.google.com/store/apps/details?id={}', + 'https://f-droid.org/en/packages/{}/', + 'https://apkpure.com/delta/{}/', + 'https://google.com/search?q={}', +] + + +def create_parser(): + parser = ArgumentParser() + + parser.add_argument('-p', '--paths', + dest='paths', + help='output paths as variables for shell exporting', + action='store_true') + return parser + + +def format_paths(): + root = abspath(f'{dirname(realpath(__file__))}/../..') + scripts = abspath(dirname(realpath(__file__))) + contribs = join(root, 'contribs') + icons = join(contribs, 'icons') + return { + 'root': root, + 'scripts': scripts, + 'contribs': contribs, + 'icons': join(contribs, 'icons.yml'), + 'emails': join(scripts, 'emails'), + 'requests': join(contribs, 'requests.yml'), + 'vectors': join(root, 'resources/vectors'), + 'src': { + 'dir': icons, + 'svg': join(icons, f'{{}}.svg'), + 'png': join(icons, f'{{}}.png') + }, + 'dst': { + 'svg': join(root, f'resources/vectors/{{}}.svg'), + 'png': join(root, f'app/src/main/res/drawable-nodpi/{{}}.png') + }, + 'a1': join(root, 'app/src/main/res/xml/appfilter.xml'), + 'a2': join(root, 'app/src/main/assets/appfilter.xml'), + 'd1': join(root, 'app/src/main/res/xml/drawable.xml'), + 'd2': join(root, 'app/src/main/assets/drawable.xml'), + } + + +paths = format_paths() + + +def transform_xml(xml): + if '' in xml: + xml = sub('\t', '', xml) # remove tags + xml = sub(r'\n()', r'\1\n', xml) # remove \n before and add it after + xml = sub(r'(', r'\1 />', xml) # -> + else: + xml = sub(r'/>\n(\n\t<)', r'/>\1/category>\1', xml) # add after latest + xml = sub(r'()', r'\t\n\1', xml) # add before + xml = sub(r'(', r'\1>', xml) # remove slash from + return xml + + +def list_categories(input=paths['d1']): + with open(input) as file: + xml = transform_xml(file.read().rstrip()) + root = ET.fromstring(xml) + elements = root.findall('category') + return [x.get('title') for x in elements] + + +if __name__ == '__main__': + parser = create_parser() + if len(argv) > 1: + args = parser.parse_args() + else: + parser.print_help() + exit(1) + + if args.paths: + print(f"wd={paths['root']}") + print(f"sd={paths['scripts']}") + print(f"id={paths['src']['dir']}") + print(f"vd={paths['vectors']}") + print(f"rq={paths['requests']}") + print(f"a1={paths['a1']}") + print(f"a2={paths['a2']}") + print(f"d1={paths['d1']}") + print(f"d2={paths['d2']}") diff --git a/resources/scripts/sort_appfilter.py b/resources/scripts/sort_appfilter.py deleted file mode 100755 index 0ebdf967e1..0000000000 --- a/resources/scripts/sort_appfilter.py +++ /dev/null @@ -1,78 +0,0 @@ -#! /usr/bin/env python - -import argparse, re, sys -import xml.etree.ElementTree as ET - -from natsort import natsorted as sorted - -from resolve_paths import paths - - -class XmlKeepComments(ET.TreeBuilder): - # https://gist.github.com/jamiejackson/a37e8d3dacb33b2dcbc1 - def __init__(self, *args, **kwargs): - super(XmlKeepComments, self).__init__(*args, **kwargs) - - def comment(self, data): - self.start(ET.Comment, {}) - self.data(data) - self.end(ET.Comment) - -xml_parser = ET.XMLParser(target=XmlKeepComments()) - - -filename = 'appfilter.xml' - - -parser = argparse.ArgumentParser(description=f'sort {filename}') - -parser.add_argument('-i', '--input', - dest='input', - help=f'path to {filename}', - default=paths['appfilter'][0]) -parser.add_argument('-o', '--output', - dest='output', - help=f'output to file (default: app/src/main/res/xml/{filename})', - nargs='?', - const=paths['appfilter'][0]) -parser.add_argument('-p', '--print', - dest='print', - help='output to console', - default=False, - action=argparse.BooleanOptionalAction) - - -def main(): - with open(args.input) as file: - xml = file.read().rstrip() # read appfilter.xml to string - - root = ET.fromstring(xml, parser=xml_parser) # convert string to ET - - scale = root[0] # save tag - root.remove(scale) # remove tag - - root[:] = sorted(root, key=lambda item: (item.tag, item.get('component'))) # sort items by tag name then my component attribute - root.insert(0, scale) # restore tag - - xml = ET.tostring(root, encoding='unicode', xml_declaration=True) # convert ET to string - xml = re.sub("'", '"', xml) # convert ' to " and transform to default format - xml = re.sub(r'\t()', r'\1\n', xml) # remove \t before and \n after - - if args.output: # if -o passed - try: - with open(args.output, 'w', encoding='utf-8') as file: - file.write(xml) - except IsADirectoryError: - print("Must be a file!") - - if args.print: # if -p passed - print(xml) - - -if __name__ == '__main__': - if len(sys.argv) > 1: - args = parser.parse_args() - else: - parser.print_help() - sys.exit(1) - main() diff --git a/resources/scripts/sort_drawable.py b/resources/scripts/sort_drawable.py deleted file mode 100755 index c142f80457..0000000000 --- a/resources/scripts/sort_drawable.py +++ /dev/null @@ -1,88 +0,0 @@ -#! /usr/bin/env python - -import argparse, re, sys -import xml.etree.ElementTree as ET - -from natsort import natsorted as sorted - -from resolve_paths import paths - - -class XmlKeepComments(ET.TreeBuilder): - # https://gist.github.com/jamiejackson/a37e8d3dacb33b2dcbc1 - def __init__(self, *args, **kwargs): - super(XmlKeepComments, self).__init__(*args, **kwargs) - - def comment(self, data): - self.start(ET.Comment, {}) - self.data(data) - self.end(ET.Comment) - -xml_parser = ET.XMLParser(target=XmlKeepComments()) - - -filename = 'drawable.xml' - - -parser = argparse.ArgumentParser(description=f'sort {filename}') - -parser.add_argument('-i', '--input', - dest='input', - help=f'path to {filename}', - default=paths['drawable'][0]) -parser.add_argument('-o', '--output', - dest='output', - help=f'output to file (default: app/src/main/res/xml/{filename})', - nargs='?', - const=paths['drawable'][0]) -parser.add_argument('-p', '--print', - dest='print', - help='output to console', - default=False, - action=argparse.BooleanOptionalAction) - - -def transform(xml): - if '' in xml: - xml = re.sub('\t', '', xml) # remove tags - xml = re.sub(r'\n()', r'\1\n', xml) # remove \n before and add it after - xml = re.sub(r'(', r'\1 />', xml) # -> - else: - xml = re.sub(r'/>\n(\n\t<)', r'/>\1/category>\1', xml) # add after latest - xml = re.sub(r'()', r'\t\n\1', xml) # add before - xml = re.sub(r'(', r'\1>', xml) # remove slash from - return xml - - -def main(): - with open(args.input) as file: - xml = file.read().rstrip() # read drawable.xml to string - - root = ET.fromstring(transform(xml), parser=xml_parser) # transform to true XML format and convert it from string to ET - - for category in root.findall('category'): - items = category.findall('item') # get all items from category - children = sorted(items, key=lambda item: item.get('drawable')) # sort category items by drawable name - category[:] = children # rewrite category items - - xml = ET.tostring(root, encoding='unicode', xml_declaration=True) # convert ET to string - xml = re.sub("'", '"', transform(xml)) # convert ' to " and transform to default format - - if args.output: # if -o passed - try: - with open(args.output, 'w', encoding='utf-8') as file: - file.write(xml) - except IsADirectoryError: - print("Path must be a file!") - - if args.print: # if -p passed - print(xml) - - -if __name__ == '__main__': - if len(sys.argv) > 1: - args = parser.parse_args() - else: - parser.print_help() - sys.exit(1) - main() \ No newline at end of file From 3e511ba1eef75ac5718dd7623e2104723ed8501b Mon Sep 17 00:00:00 2001 From: flameshikari Date: Mon, 23 Feb 2026 17:57:59 +0500 Subject: [PATCH 03/12] update colonist icon --- contribs/icons.yml | 6 ++++-- contribs/icons/colonist.png | Bin 0 -> 6296 bytes contribs/icons/colonist.svg | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 contribs/icons/colonist.png create mode 100644 contribs/icons/colonist.svg diff --git a/contribs/icons.yml b/contribs/icons.yml index 2599d58323..7297dcc490 100644 --- a/contribs/icons.yml +++ b/contribs/icons.yml @@ -25,11 +25,13 @@ abr_direct: gosuslugi_school: - ru.gosuslugi.school/ru.gosuslugi.school.MainActivity -gosuslugi_biometry: - - ru.rtlabs.mobile.ebs.gosuslugi.android/ru.rtlabs.mobile.ebs.ui.main.MainActivity +gosuslugi_biometry: ru.rtlabs.mobile.ebs.gosuslugi.android/ru.rtlabs.mobile.ebs.ui.main.MainActivity maadhaar: - in.gov.uidai.pehchaan/in.gov.uidai.pehchaan.onboarding.SplashActivity documents: - andes.oplus.documentsreader/andes.oplus.documentsreader.launcher.activity.MainActivity + +colonist: + action: rebrand diff --git a/contribs/icons/colonist.png b/contribs/icons/colonist.png new file mode 100644 index 0000000000000000000000000000000000000000..72998f4e2550c27b25ff926253547a0e51380e5a GIT binary patch literal 6296 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 literal 0 HcmV?d00001 diff --git a/contribs/icons/colonist.svg b/contribs/icons/colonist.svg new file mode 100644 index 0000000000..13f8761ca6 --- /dev/null +++ b/contribs/icons/colonist.svg @@ -0,0 +1,2 @@ + +C From 73b7309ac3b97edd512996a6beb9062f8b010c65 Mon Sep 17 00:00:00 2001 From: flameshikari Date: Mon, 23 Feb 2026 19:20:39 +0500 Subject: [PATCH 04/12] add sorting mode and rename "replace" to "remake" --- .github/workflows/build_all.yml | 5 +--- contribs/icons.yml | 2 +- resources/scripts/process_icons.py | 40 ++++++++++++++++++++---------- 3 files changed, 29 insertions(+), 18 deletions(-) diff --git a/.github/workflows/build_all.yml b/.github/workflows/build_all.yml index 8eaef7fb6c..50a68253b5 100644 --- a/.github/workflows/build_all.yml +++ b/.github/workflows/build_all.yml @@ -180,10 +180,7 @@ jobs: EOF - python ${{ env.sd }}/sort_appfilter.py -o - python ${{ env.sd }}/sort_drawable.py -o - cp -fv ${{ env.a1 }} ${{ env.a2 }} - cp -fv ${{ env.d1 }} ${{ env.d2 }} + python -u ${{ env.sd }}/process_icons.py -s bash gradlew assembleFossdc git restore ${{ env.d1 }} ${{ env.d2 }} diff --git a/contribs/icons.yml b/contribs/icons.yml index 7297dcc490..9877e925b3 100644 --- a/contribs/icons.yml +++ b/contribs/icons.yml @@ -34,4 +34,4 @@ documents: - andes.oplus.documentsreader/andes.oplus.documentsreader.launcher.activity.MainActivity colonist: - action: rebrand + action: remake diff --git a/resources/scripts/process_icons.py b/resources/scripts/process_icons.py index a8cffba290..243075046c 100755 --- a/resources/scripts/process_icons.py +++ b/resources/scripts/process_icons.py @@ -16,16 +16,16 @@ from process_requests import read from shared import paths, transform_xml -ACTIONS = ('none', 'replace', 'rebrand', 'remove') +ACTIONS = ('none', 'remake', 'rebrand', 'remove') PATTERN_ALT = re.compile(r'^.*_alt_\d+$') PATTERN_CI = re.compile(r'^[a-zA-Z0-9._]+/[a-zA-Z0-9._$]+$') PATTERN_DRAWABLE = re.compile(r'^[a-z0-9_]+$') ICONS_HEADER = '''\ -# # action: none|replace|rebrand|remove|rename > new_name +# # action: none|remake|rebrand|remove|rename > new_name # # none - default, new icon -# # replace - overwrite existing images, no xml changes +# # remake - overwrite existing images, add to new category # # rebrand - existing icon -> alt_x, new icon -> main # # remove - remove existing icon (files + xml) # # rename - rename existing icon (files + xml) @@ -52,6 +52,7 @@ args.add_argument('-d', '--dry-run', action='store_true', help='do things without changing any files') args.add_argument('-v', '--verbose', action='store_true', help='verbose') args.add_argument('-n', '--no-color', action='store_true', help='disable colors') +args.add_argument('-s', '--sort', action='store_true', help='sort xml files only, skip icons.yml') args = args.parse_args() if args.no_color: @@ -215,7 +216,7 @@ def parse_icons(icons_yml, root_drawable): entry_errors.append(f"category {color(category.lower(), 'cyan')} doesn't exist") errors[key] = True - if action in ('replace', 'rebrand'): + if action in ('remake', 'rebrand'): for ext in check_source_files(key): entry_errors.append(f"action is {color(action, 'cyan')}, but {key}.{ext} not found") errors[key] = True @@ -256,10 +257,11 @@ def parse_icons(icons_yml, root_drawable): def main(): - icons_yml = read(paths['icons']) + if not args.sort: + icons_yml = read(paths['icons']) - if not icons_yml: - log.warning(f'{basename(paths["icons"])} is empty') + if not icons_yml: + log.warning(f'{basename(paths["icons"])} is empty') with open(paths['d1']) as file: xml_drawable = file.read().rstrip() @@ -276,7 +278,10 @@ def main(): category_new = root_drawable.find("category[@title='New']") category_alt = root_drawable.find("category[@title='Alts']") - for drawable, values in parse_icons(icons_yml, root_drawable): + if args.sort: + log.info('sorting xml files') + + for drawable, values in (parse_icons(icons_yml, root_drawable) if not args.sort else []): log_sep(drawable) renamed_drawable = [] @@ -352,16 +357,18 @@ def main(): move_images(drawable, new_name, from_dst=True) move_images(drawable, header=False) added_drawable.append(f' {color(f'(alts)', 'dark_grey')}') - case 'replace': + case 'remake': move_images(drawable) case _: move_images(drawable) - if values['action'] not in ('replace', 'remove'): + if values['action'] not in ('remove',): category = root_drawable.find(f"category[@title='{values['category']}']") add_drawable = drawable not in [item.get('drawable') for item in category] - if renamed_drawable or added_drawable or add_drawable: + add_new = drawable not in [item.get('drawable') for item in category_new] + + if renamed_drawable or added_drawable or add_drawable or (values['action'] == 'remake' and add_new): log.info(f"drawable.xml{color(':', 'dark_grey')}") for entry in renamed_drawable: print(f" {color(f'– {entry}', 'red')}") @@ -371,10 +378,16 @@ def main(): item = ET.Element('item') item.set('drawable', drawable) cat_name = values['category'].lower() - print(f" {color(f'+ {ET.tostring(item).decode()}', 'green')} {color(f'(new, {cat_name})', 'dark_grey')}") item.tail = '\n\t' category.append(item) category_new.append(deepcopy(item)) + print(f" {color(f'+ {ET.tostring(item).decode()}', 'green')} {color(f'(new, {cat_name})', 'dark_grey')}") + elif values['action'] == 'remake' and add_new: + item = ET.Element('item') + item.set('drawable', drawable) + item.tail = '\n\t' + category_new.append(item) + print(f" {color(f'+ {ET.tostring(item).decode()}', 'green')} {color('(new)', 'dark_grey')}") if renamed_appfilter or values['compinfos']: log.info(f"appfilter.xml{color(':', 'dark_grey')}") @@ -415,7 +428,8 @@ def main(): write_file(paths['d1'], xml_drawable) copy(paths['d1'], paths['d2']) - write_file(paths['icons'], ICONS_HEADER) + if not args.sort: + write_file(paths['icons'], ICONS_HEADER) print() From d8caa0dbe042cd499994a4f553f1a4f8c68faa66 Mon Sep 17 00:00:00 2001 From: flameshikari Date: Mon, 2 Mar 2026 04:43:52 +0500 Subject: [PATCH 05/12] update a lot of things --- CONTRIBUTING.md | 605 +++++++++++++++++------------ README.md | 6 +- contribs/icons.yml | 2 +- resources/scripts/process_icons.py | 179 ++++++--- 4 files changed, 473 insertions(+), 319 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7723aaa194..d42140e435 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,344 +1,441 @@ -# Introduction +# ℹ️ Introduction -## Requirements +> Feel free to ask for help on [the Discord server](https://discord.gg/F9RFqHN) if something is unclear -In case you wanna contribute to Delta you need: +Hello! I see you've decided to contribute. Well, there are a few ways to go: -- basic knowledge of [`git`](https://git-scm.com/) -- a fork of this repo -- an SVG icon -- an 192x192px PNG icon -- [ComponentInfo](#gathering-componentinfo)(s) of the target app +If you know how to make cool icons but don't know how to work with [git](https://git-scm.com/), [GitHub](https://github.com), and other tech stuff, just read [Design Guidelines](#-design-guidelines) carefully, then make some icons and submit them on Discord. -## Info +If you don't know how to make cool icons but you are tech-savvy, you can help improve our CI/CD, add missing ComponentInfos, and do other things. Read [Contributing](#-contributing) for more info. -We have two methods of adding icons: - - [Auto method](#auto-method) is a new method of adding icons. You only need to add PNG and SVG to a specific folder and append the icon name with ComponentInfo(s) to a specific YAML file. These files will be automatically handled by our CI/CD every release. - - [Manual method](#manual-method) implies editing four XML files and adding icons to two specific folders. +If you can handle tech and design stuff, then read this guide completely. -We also have such a thing as alternative icons β€” we mainly use it to move an existing icon to an alternative one after rebranding, but there's nothing stopping you to make alternative icons for any app in different shapes as you wish. You can select an alternative icon for the target app via your icon launcher if it supports that feature. +If you don't know any of that but want to try to make icons... well, we can only help with advice on Discord. There are so many tools, instructions, and guides for them that it’s impossible to describe everything, so the only thing you can do is search for information on the Internet and try it out. -Description of categories and what they are for: +In short, the general requirements are: + +- Basic knowledge of [git](https://git-scm.com/) and/or [GitHub](https://github.com). +- Experience with vector editors. +- A little bit of design sense. πŸ˜‚ + +Good luck! + +# πŸ“ Design Guidelines + +## πŸͺ„ General Tips + +- Keep it simple: fewer details and small elements, because they may not be visible on the screen. +- Your icon doesn't have to be a 1:1 copy of the original; improve and simplify it in every way possible but at the same time try to maintain a recognizable appearance. +- Avoid outlining, otherwise the icon will stand out from the general style. +- Make the icon free-form if possible. +- You can search for app logos online (they're often found on official websites; avoid icons with non-free licenses) and adapt them. If the original icon is too complex, you can use another recognizable element (an item, a faction icon, etc.) β€” this applies to any complex icon, not just games. +- Be sure to double-check that icons are centered and aligned, sized and exported correctly. + +## πŸ–ΌοΈ Icon Template + +### πŸ“ Rules + +- Canvas size must be 192x192px. The template from [Resources](#resources) is correctly configured, just download and work with it. +- The icon size does not exceed template dimensions. Check out [the visual explanation](./resources/templates/template_tutorial.svg). +- If the original logo is simple and doesn't fill most of the template as a shape (circle, square, etc.), keep the logo size between 73–80px. +- The rounded corners of squares and rectangles have a corner radius of 10px. +- The template must be properly centered on the canvas. + +### πŸͺ„ Tips + +- If you're having trouble deciding whether to use geometric or optical centering, you can discuss it on Discord. Mostly it depends on the icon, but optical centering is usually your choice. +- If you have any doubts about the design of your icon, you can also discuss it on Discord. + +### 🧰 Resources + +> You can also check [Figma icon template](https://www.figma.com/design/02aiFRSLkikcw8mpBAnoDA/Delta-Icon-Template?m=auto&t=qyLH05AMDzZwAI2s-1). + + + +## 🌈 Colors + +### πŸ“ Rules + +- $\textcolor{#56595B}{\textsf{⬀}}$ #56595B Davy's grey as default black. + +- $\textcolor{#FF837D}{\textsf{⬀}}$ #FF837D Coral pink as default red and $\textcolor{#BA6561}{\textsf{⬀}}$ #BA6561 Fuzzy Wuzzy as default dark red. Shades of red are specifically for shading purposes. + +- Transparencies can be used as an overlay for additional shading. We rarely use them, so don't use them unnecessarily β€” try to get by with basic colors and greys as much as possible. If you do use transparency, it should be an overlay, never a background (i.e. no transparency in the raster version). + +- Gradients are more acceptable than transparencies, but still try to avoid using them (as noted above). + +### 🎨 Palette + + + + + + + + + + + + + + + + + + +
GreysBasicRedsSkintones
+

$\textcolor{#FFFFFF}{\textsf{⬀}}$ #FFFFFF White

+

$\textcolor{#ECECEC}{\textsf{⬀}}$ #ECECEC Isabelline

+

$\textcolor{#D8D8D8}{\textsf{⬀}}$ #D8D8D8 Timberwolf

+

$\textcolor{#D2D2D2}{\textsf{⬀}}$ #D2D2D2 Light gray

+

$\textcolor{#CCCCCC}{\textsf{⬀}}$ #CCCCCC Pastel gray

+

$\textcolor{#B1B5BD}{\textsf{⬀}}$ #B1B5BD Ash grey

+

$\textcolor{#A0A5AF}{\textsf{⬀}}$ #A0A5AF Dark gray

+

$\textcolor{#979797}{\textsf{⬀}}$ #979797 Manatee

+

$\textcolor{#83868C}{\textsf{⬀}}$ #83868C Taupe gray

+

$\textcolor{#56595B}{\textsf{⬀}}$ #56595B Davy's grey

+

$\textcolor{#4A4A4A}{\textsf{⬀}}$ #4A4A4A Quartz

+

$\textcolor{#000000}{\textsf{⬀}}$ #000000 Black

+
+

$\textcolor{#FFD6D4}{\textsf{⬀}}$ #FFD6D4 Pastel pink

+

$\textcolor{#FF837D}{\textsf{⬀}}$ #FF837D Coral pink

+

$\textcolor{#BA6561}{\textsf{⬀}}$ #BA6561 Fuzzy Wuzzy

+

$\textcolor{#D3B69A}{\textsf{⬀}}$ #D3B69A Tan

+

$\textcolor{#8E6F60}{\textsf{⬀}}$ #8E6F60 Shadow

+

$\textcolor{#FCECDC}{\textsf{⬀}}$ #FCECDC Antique white

+

$\textcolor{#F8C18C}{\textsf{⬀}}$ #F8C18C Pale gold

+

$\textcolor{#FDF5D9}{\textsf{⬀}}$ #FDF5D9 Cornsilk

+

$\textcolor{#F9DE81}{\textsf{⬀}}$ #F9DE81 Jasmine

+

$\textcolor{#C39A54}{\textsf{⬀}}$ #C39A54 Camel

+

$\textcolor{#E0F4E0}{\textsf{⬀}}$ #E0F4E0 Platinum

+

$\textcolor{#98DC9A}{\textsf{⬀}}$ #98DC9A Granny Smith Apple

+

$\textcolor{#71A372}{\textsf{⬀}}$ #71A372 Asparagus

+

$\textcolor{#96DFD3}{\textsf{⬀}}$ #96DFD3 Pale robin egg blue

+

$\textcolor{#73ADA4}{\textsf{⬀}}$ #73ADA4 Cadet blue

+

$\textcolor{#9ABEFF}{\textsf{⬀}}$ #9ABEFF Baby blue eyes

+

$\textcolor{#728DBE}{\textsf{⬀}}$ #728DBE Dark pastel blue

+

$\textcolor{#54688C}{\textsf{⬀}}$ #54688C UCLA Blue

+

$\textcolor{#ABABFF}{\textsf{⬀}}$ #ABABFF Baby blue eyes

+

$\textcolor{#BD9AFF}{\textsf{⬀}}$ #BD9AFF Bright lavender

+

$\textcolor{#8C72BD}{\textsf{⬀}}$ #8C72BD Ube

+
+

$\textcolor{#FFB0AC}{\textsf{⬀}}$ #FFB0AC Melon

+

$\textcolor{#F58F8A}{\textsf{⬀}}$ #F58F8A Light coral

+

$\textcolor{#F4806D}{\textsf{⬀}}$ #F4806D Coral pink

+

$\textcolor{#E85E5C}{\textsf{⬀}}$ #E85E5C Terra cotta

+

$\textcolor{#DC505E}{\textsf{⬀}}$ #DC505E Dark terra cotta

+

$\textcolor{#B02A3C}{\textsf{⬀}}$ #B02A3C Deep carmine

+

$\textcolor{#7A1B1C}{\textsf{⬀}}$ #7A1B1C Falu red

+

$\textcolor{#511119}{\textsf{⬀}}$ #511119 Dark scarlet

+
+

$\textcolor{#F1E9E0}{\textsf{⬀}}$ #F1E9E0 Eggshell

+

$\textcolor{#D6C8BA}{\textsf{⬀}}$ #D6C8BA Pastel gray

+

$\textcolor{#D4C6B8}{\textsf{⬀}}$ #D4C6B8 Pale silver

+

$\textcolor{#D7D0B8}{\textsf{⬀}}$ #D7D0B8 Pastel gray

+

$\textcolor{#E2C9B0}{\textsf{⬀}}$ #E2C9B0 Desert sand

+

$\textcolor{#D4B79A}{\textsf{⬀}}$ #D4B79A Tan

+

$\textcolor{#BF9E73}{\textsf{⬀}}$ #BF9E73 Camel

+
+ +### 🧰 Resources + +#### Vector Palettes + +-
+ Simplified Palette +
+
+
-- `New`: for new icons obviously (new icons always must be duplicated there) -- `Alts`: for alternative icons -- `Calendar`: for calendar icons -- `Folders`: for folder icons -- `Google`: for Google apps (Chrome, YouTube, etc.) -- `System`: for system icons (Camera, Settings, etc.) -- `#`: icons whose name starts with a number (or, to be more clear, with an underscore followed by a number, e.g. `_2048`) -- `A-Z`: icons which don't fit in previous categories must be placed in a category based on the first letter of its name +-
+ Full Palette +
+ +
-## Rules +#### Vector Editors -- Keep `LF` line endings in files (`CLRF` line endings make GitHub think the entire file has been modified, and they may break our CI/CD) -- SVG, PNG and drawable names must be the same -- Keep filenames in alphanumeric lowercase with underscores -- If the icon name starts with a number, it must have a leading underscore (e.g. `_9gag`) and be placed in `#` category -- Keep the next naming format of alternative icons: `new_icon_alt_x`, where `x` is a number of current alternative version (yes, we have multiple of them, e.g. `old_icon_alt_1`, `old_icon_alt_2`, etc. +- [Adobe Swatch Exchange Palette](./resources/palettes/palette.ase) (Illustrator, Photoshop) +- [GPL Palette](./resources/palettes/palette.gpl) (Inkscape, Karbon) -# Contributing +## πŸ—š Font -> _`new_icon` will be used as the icon name_
-> _`new_icon_alt_1` will be used as the alternative icon for `new_icon`_
-> _`com.example/com.example.MainActivity` will be used as the 1st ComponentInfo for `new_icon`_
-> _`com.example/com.example.StartActivity` will be used as the 2nd ComponentInfo for `new_icon`_ +### πŸͺ„ Tips -Don't forget to give yourself an entry at the bottom of [`app/src/main/res/xml/contributors.xml`](./app/src/main/res/xml/contributors.xml) if this is your first contribution! +- If the original icon consists of just one or two letters, you may trace that letter instead of using these fonts. +- Fonts can be a little tricky to align/center in different vector editors, so read about it on the Internet. +- You can use a custom font if it matches the font from the original icon, for the rest use fonts from [Resources](#resources-3). -## Auto Method +### 🧰 Resources -> **This method is only for adding new icons or linking ComponentInfo(s) with existing icons!** +- [Now](https://www.1001fonts.com/now-font.html?text=Delta%20Icons) β€” main Sans-serif font; use Now Alt from the same family for alternate lowercase 'a' letter. +- [Aleo](https://www.1001fonts.com/aleo-font.html?text=Delta%20Icons) β€” use it only when Serif is needed. -1. Add `new_icon.svg` and `new_icon.png` to [`contribs/icons`](./contribs/icons) directory +# πŸ“₯ Contributing -2. Append the icon name with ComponentInfo(s) to [`contribs/icons.yml`](./contribs/icons.yml) with any of the next formats: +## πŸͺŸ Overview - 2.1. The new icon with the ComponentInfo. +### ❔ Key Terms - > Can be used for linking the ComponentInfo with the existing icon +- **Icon images** β€” your exported PNG/SVG icons. +- **ComponentInfo** β€” an identifier (e.g. `com.example/com.example.MainActivity`) that launchers use to match an installed app to its icon in the icon pack. You can use these tools to get ComponentInfos from your installed apps: - ```yaml - # lines omitted for example + - [Icon Pusher](https://iconpusher.com/) by [V01D](https://v01d.uk) + - [Icon Request](https://github.com/Kaiserdragon2/IconRequest/releases) by [Kaiserdragon2](https://github.com/Kaiserdragon2) - new_icon: - - com.example/com.example.MainActivity - ``` +- **Drawable name** β€” an internal name (e.g. `new_icon`) that links an icon image to its ComponentInfo. The drawable name must be in alphanumeric lowercase with underscores only and icon image names must match the drawable name exactly (e.g. `new_icon.png` and `new_icon.svg`). +- **Standalone icon** β€” an icon that isn't linked to any app. They can be non-app icons like `adobe` and be used as web shortcut icons, etc. Users can select them via their launcher if it supports that feature. +- **Alternative icon** β€” an alternative version of an app icon. Mainly used when the app rebrands: the old icon becomes an alternative (e.g. `new_icon_alt_1`), and a new icon takes its place. However, you can create alternative icons for any app without passing any ComponentInfo, and they will be standalone. Users can select them via their launcher if it supports that feature. +- **Categories** β€” categories within [`app/src/main/assets/drawable.xml`](./app/src/main/assets/drawable.xml) to organize icons. Description of categories: - 2.2. The new icon with multiple ComponentInfos: - > Can be used for linking ComponentInfos with the existing icon - ```yaml - # lines omitted for example + - `New` β€” new icons for the current release. If [manually](#️-manual) adding icons, you must also add the entry to this category. + - `Alts` β€” alternative icons. + - `Calendar` β€” calendar icons. + - `Folders` β€” folder icons. + - `Google` β€” Google apps (Chrome, YouTube, etc.). + - `System` β€” system icons (Camera, Contacts, Settings, etc.). + - `#` β€” icons whose name starts with a number (e.g. `_2048`). If [manually](#️-manual) adding icons, drawable names that begin with a number must have a leading underscore and be placed in this category. + - `A–Z` β€” everything else, sorted by the first letter of the drawable name. - new_icon: - - com.example/com.example.MainActivity - - com.example/com.example.StartActivity - ``` +### πŸ“ Rules - 2.3. The new icon with a custom category: - > For example, if your icon named as `pixel_buds` but you want it to go in `Google` category. - - > Note that you don't need to specify `New` category, it will be automatically copied to it, and you don't need to specify category if this is a named icon, _e.g._ `N` category is redundant for `new_icon`, the script will place it to `N` category automatically based on the first letter of drawable name. - - ```yaml - new_icon: - category: 'Google' - compinfos: - - com.example/com.example.MainActivity - - com.example/com.example.StartActivity - ``` +- Keep [LF](https://en.wikipedia.org/wiki/Newline) line endings in edited files. - 2.4. The icon without the ComponentInfo (the alternative icon): +### πŸ—’οΈ Notes for Contributors - ```yaml - # lines omitted for example +Want to help close user requests? Check [`contribs/requests.yml`](./contribs/requests.yml) β€” it contains all pending icon requests and is updated periodically. - new_icon_alt_1: {} - ``` +If you wish, you can add yourself to [`app/src/main/res/xml/contributors.xml`](./app/src/main/res/xml/contributors.xml) to shine in the app's contributors section! -And we're done! Repeat the process for adding new icons. +## πŸ“š Managing Icons -## Manual Method +> `new_icon` (and derivatives of it) will be used as a drawable name
+> `com.example/com.example.MainActivity` (and derivatives of it) will be used as a ComponentInfo -1. Add `new_icon.svg` to [`resources/vectors`](./resources/vectors) directory +So, you made an icon then exported it as `new_icon.png` and `new_icon.svg`. Now you need to select which way to manage icons. Here are two ways: -2. Add `new_icon.png` to [`app/src/main/res/drawable-nodpi`](./app/src/main/res/drawable-nodpi) +- [**Auto**](#-auto) β€” an automatic and declarative way of managing icons via [`contribs/icons.yml`](./contribs/icons.yml), processed by scripts and GitHub Actions. This is the recommended approach. +- [**Manual**](#️-manual) β€” this is how icons were managed before [**Auto**](#-auto) was implemented. Directly editing XMLs and placing icon images into the appropriate directories. More control, but inconvenient. Try to avoid it unless [**Auto**](#-auto) can't handle what you need. -3. Append the line `` in `New` and named categories (the named category is based on the first letter of the icon name; `N` in our case) to [`app/src/main/assets/drawable.xml`](./app/src/main/assets/drawable.xml) and [`app/src/main/res/xml/drawable.xml`](./app/src/main/res/xml/drawable.xml). How it should look: +### πŸ€– Auto - ```xml - - - - - - - - +This is an automatic and declarative method of managing icons via [`contribs/icons.yml`](./contribs/icons.yml), driven by scripts and GitHub Actions. - - - - - - - - ``` - > You can edit one file and overwrite another with it to keep them identical. +Place your icon images in the [`contribs/icons`](./contribs/icons) directory and add the following entry to [`contribs/icons.yml`](./contribs/icons.yml): -4. Append the line `` to [`app/src/main/assets/appfilter.xml`](./app/src/main/assets/appfilter.xml) and [`app/src/main/res/xml/appfilter.xml`](./app/src/main/res/xml/appfilter.xml). How it should look: +```yaml +new_icon: com.example/com.example.MainActivity +``` - ```xml - - - - - ``` - > You can edit one file and overwrite another with it to keep them identical. +And that's all. This is the easiest and most common method for adding a new icon and linking it to an app. [`contribs/icons.yml`](./contribs/icons.yml) will be processed by scripts and cleared automatically every release. -The end. More complicated than Auto method, but it's a base method, you can modify/fix current icons by this method. +That entry can be extended with more options: -## Other Cases +```yaml +new_icon: + action: rewrite + category: google + compinfo: + - com.example/com.example.MainActivity + - com.example/com.example.SplashActivity +``` -### Alternative Icons +Check [Options](#options) to get more explanation of each option and [Examples](#examples) for more examples. -If the existing icon rebranded, don't overwrite it with a new one, do the following: +#### Options -> `old_icon` will be used as an existing icon name +- `action` β€” describes what to do with the icon. It can take one of the following values: -> `old_icon_alt_1` will be used as an alternative icon name for the existing icon name + - `add` β€” add a new icon. This is the default action if the option is not explicitly set. + - `rewrite` β€” overwrite icon images of an existing icon with new ones from [`contribs/icons`](./contribs/icons). + - `rebrand` β€” move an existing icon to `alt_x` (`x` will be automatically calculated), add a new icon and use it as the main one. If you pass any ComponentInfo, existing ComponentInfos will be attached to `alt_x` (for backward compatibility with older versions of the app), otherwise `alt_x` will be a standalone alternative icon. + - `remove` β€” remove an existing icon. + - `rename > name` β€” rename an existing icon (where `name` is a new name of the existing icon). + - `move > category` β€” move an existing icon to a different category (where `category` is the category name). -1. Determine if alternative icons exist for the target app by checking `Alts` category in [`app/src/main/res/xml/drawable.xml`](./app/src/main/res/xml/drawable.xml). If no alternative icons then start numbering from `1` (e.g. `old_icon_alt_1`), otherwise continue numbering based on latest alternative icon number (e.g. `old_icon_alt_2`) +- `category` β€” overrides the automatic category assignment, e.g. if you want to assign a Google app icon to the Google category. If not set, the category is assigned based on the following logic: -2. Rename `old_icon.svg` to `old_icon_alt_1.svg` in [`resources/vectors`](./resources/vectors) directory (if SVG not found there just skip this step) + - if the drawable name starts with `_[0-9]` (e.g. `_2048`), the category will be `#` + - if the drawable name ends with `_alt_[0-9]+` (e.g. `telegram_alt_23`), the category will be `Alts` + - else the category will be `A–Z` based on the first letter of the drawable name -3. Rename `old_icon.png` to `old_icon_alt_1.png` in [`app/src/main/res/drawable-nodpi`](./app/src/main/res/drawable-nodpi) directory +- `compinfo` β€” a list of ComponentInfos to link to the current icon. It can be a string or a list (see more in [Examples](#examples)). -4. Add `old_icon_alt_1` to `Alts` category and `old_icon` to `New` category in [`app/src/main/assets/drawable.xml`](./app/src/main/assets/drawable.xml) and [`app/src/main/res/xml/drawable.xml`](./app/src/main/res/xml/drawable.xml) +#### Examples +```yaml +# a new icon with a single ComponentInfo +# without adding icon images it will only link a ComponentInfo with new_icon +new_icon: com.example/com.example.MainActivity +# or with multiple ComponentInfos +new_icon: + - com.example/com.example.MainActivity + - com.example/com.example.SplashActivity -5. If the ComponentInfo also changed after rebranding, replace `old_icon` with `old_icon_alt_1` in [`app/src/main/assets/appfilter.xml`](./app/src/main/assets/appfilter.xml) and [`app/src/main/res/xml/appfilter.xml`](./app/src/main/res/xml/appfilter.xml) (the alternative icon will be linked with the old ComponentInfos for back compability) +# new alternative icons +# will be automatically assigned to Alts category +new_icon_alt_1: com.example/com.example.MainActivity +new_icon_alt_2: com.example/com.example.SplashActivity +# or new standalone alternative icons +new_icon_alt_1: {} +new_icon_alt_2: {} +# if you're not sure if there's no alts with these names, you can use this: +# _alt_x will be automatically resolved to the next available alt number, e.g.: +# new_icon_alt_x1 > new_icon_alt_4 (if _alt_1, _alt_2, _alt_3 exist) +# use different numbers after x to add multiple alts in one file +new_icon_alt_x1: com.example/com.example.MainActivity +new_icon_alt_x2: com.example/com.example.SplashActivity +new_icon_alt_x3: {} +new_icon_alt_x4: {} -# Resources +# a new standalone icon +new_icon: {} -## Font +# add a new Google app icon to Google category +google_app: + category: google -> If the original icon consists of just one or two letters, you may trace that letter instead of using these fonts +# rewrite the icons of an existing icons without touching XMLs +# existing_icons.png and existing_icon.svg must be in contribs/icons +existing_icon: + rebrand: rewrite -- [Now](https://www.1001fonts.com/now-font.html?text=Delta%20Icons) (Sans-serif) β€” main font; use Now Alt from the same family for alternate lowercase 'a' letter -- [Aleo](https://www.1001fonts.com/aleo-font.html?text=Delta%20Icons) (Serif) β€” use it only when Serif is needed +# rebrand an existing icon and make the previous icon a standalone alternative icon \ +# as existing_icon_alt_x (x will be automatically calculated) +existing_icon: + rebrand: rebrand -## Requests +# rebrand an existing icon with attaching previous ComponentInfos \ +# to a existing_icon_alt_x (x will be automatically calculated) +existing_icon: + rebrand: rebrand + compinfos: + - com.example/com.example.MainActivity -If you wanna help close icon requests from users, you can take a look at [`contribs/requests.yml`](./contribs/requests.yml) where all requests are stored. The file updates periodically. +# rename an existing icon +# will be automatically moved to the appropriate category +existing_icon: + action: rename > new_icon -## Gathering ComponentInfo +# move an existing icon to a different category, e.g. google +existing_icon: + action: move > google -ComponentInfo is what your launcher uses to know which apps get which icons in our icon pack. +# remove an existing icon from XMLs and image directories +existing_icon: + action: remove -You may use these tools to find each app's Π‘omponentInfo(s): -- [Icon Pusher](https://iconpusher.com/) by [V01D](https://v01d.uk) -- [Icon Request](https://github.com/Kaiserdragon2/IconRequest/releases) by [Kaiserdragon2](https://github.com/Kaiserdragon2) +``` -## Icon Template +### ✍️ Manual -### Rules +This is how icons were managed before [**Auto**](#-auto) was implemented. Directly editing XMLs and placing exported icons into the appropriate directories. More control, but inconvenient. Try to avoid it unless [**Auto**](#-auto) can't handle what you need. -- Canvas size must be 192x192px, the icon size according to the template below -- If the original logo doesn't contain small details or doesn't make up most of the background layer (circle/square/etc.) as designed, keep the logo size between 73-80px -- The rounded corners of squares and rectangles have a corner radius of 10px +There are two `drawable.xml` and two `appfilter.xml` files to edit (stored in [`app/src/main/assets`](./app/src/main/assets) and [`app/src/main/res/xml`](./app/src/main/res/xml)). It's better to edit the XMLs in [`app/src/main/assets`](./app/src/main/assets), then copy them to [`app/src/main/res/xml`](./app/src/main/res/xml) to keep all files identical. You can do it however you want (e.g. editing all files at the same time), just keep them identical. + +#### Adding a new icon -||| -|---|---| +1. Add `new_icon.svg` to [`resources/vectors`](./resources/vectors) directory. -Or you can check [Figma icon template](https://www.figma.com/design/02aiFRSLkikcw8mpBAnoDA/Delta-Icon-Template?m=auto&t=qyLH05AMDzZwAI2s-1). +2. Add `new_icon.png` to [`app/src/main/res/drawable-nodpi`](./app/src/main/res/drawable-nodpi) directory. -## Colors +3. Append the line `` to both the `New` and the appropriate letter category in [`app/src/main/assets/drawable.xml`](./app/src/main/assets/drawable.xml). Here's how it should look: -### Rules + ```xml + + + + + + + + + + + + + + + + + ``` -- [ $\textcolor{#56595B}{\textsf{⬀}}$ #56595B Davy's grey ] as default Black +4. Append the line `` to [`app/src/main/assets/appfilter.xml`](./app/src/main/assets/appfilter.xml). Here's how it should look: -- [ $\textcolor{#FF837D}{\textsf{⬀}}$ #FF837D Coral pink ] as default Red and [ $\textcolor{#BA6561}{\textsf{⬀}}$ #BA6561 Fuzzy Wuzzy ] as default Dark Red. Shades of Red are specifically for shading purposes + ```xml + + + + + + ``` +5. Copy edited XMLs from [`app/src/main/assets`](./app/src/main/assets) to [`app/src/main/res/xml`](./app/src/main/res/xml). -- Transparencies β€” White (25%, 50%, 70%) and Black (15%, 25%) can be used as overlay for additional shading +6. Repeat the process for more icons. -### Palette +#### Alternative Icons -> Palette variants are hidden under spoilers below +If an existing icon has been rebranded, don't overwrite it with a new one β€” do the following: -
- Full -
- -
+> `old_icon` will be used as an existing drawable name.
+> `old_icon_alt_1` will be used as an alternative icon name for the existing drawable name. -
- Simple -
-
-
+1. Determine if alternative icons exist for the target app by checking `Alts` category in [`app/src/main/res/xml/drawable.xml`](./app/src/main/res/xml/drawable.xml). If no alternative icons exist, start numbering from `1` (e.g. `old_icon_alt_1`), otherwise continue numbering based on the latest alternative icon number (e.g. `old_icon_alt_2`). -
-HTML -
- - - - - - - - - - - - - - - - - -
GreysBasicRedsSkintones
-

$\textcolor{#FFFFFF}{\textsf{⬀}}$ #FFFFFF White

-

$\textcolor{#ECECEC}{\textsf{⬀}}$ #ECECEC Isabelline

-

$\textcolor{#D8D8D8}{\textsf{⬀}}$ #D8D8D8 Timberwolf

-

$\textcolor{#D2D2D2}{\textsf{⬀}}$ #D2D2D2 Light gray

-

$\textcolor{#CCCCCC}{\textsf{⬀}}$ #CCCCCC Pastel gray

-

$\textcolor{#B1B5BD}{\textsf{⬀}}$ #B1B5BD Ash grey

-

$\textcolor{#A0A5AF}{\textsf{⬀}}$ #A0A5AF Dark gray

-

$\textcolor{#979797}{\textsf{⬀}}$ #979797 Manatee

-

$\textcolor{#83868C}{\textsf{⬀}}$ #83868C Taupe gray

-

$\textcolor{#56595B}{\textsf{⬀}}$ #56595B Davy's grey

-

$\textcolor{#4A4A4A}{\textsf{⬀}}$ #4A4A4A Quartz

-

$\textcolor{#000000}{\textsf{⬀}}$ #000000 Black

-
-

$\textcolor{#FFD6D4}{\textsf{⬀}}$ #FFD6D4 Pastel pink

-

$\textcolor{#FF837D}{\textsf{⬀}}$ #FF837D Coral pink

-

$\textcolor{#BA6561}{\textsf{⬀}}$ #BA6561 Fuzzy Wuzzy

-

$\textcolor{#D3B69A}{\textsf{⬀}}$ #D3B69A Tan

-

$\textcolor{#8E6F60}{\textsf{⬀}}$ #8E6F60 Shadow

-

$\textcolor{#FCECDC}{\textsf{⬀}}$ #FCECDC Antique white

-

$\textcolor{#F8C18C}{\textsf{⬀}}$ #F8C18C Pale gold

-

$\textcolor{#FDF5D9}{\textsf{⬀}}$ #FDF5D9 Cornsilk

-

$\textcolor{#F9DE81}{\textsf{⬀}}$ #F9DE81 Jasmine

-

$\textcolor{#C39A54}{\textsf{⬀}}$ #C39A54 Camel

-

$\textcolor{#E0F4E0}{\textsf{⬀}}$ #E0F4E0 Platinum

-

$\textcolor{#98DC9A}{\textsf{⬀}}$ #98DC9A Granny Smith Apple

-

$\textcolor{#71A372}{\textsf{⬀}}$ #71A372 Asparagus

-

$\textcolor{#96DFD3}{\textsf{⬀}}$ #96DFD3 Pale robin egg blue

-

$\textcolor{#73ADA4}{\textsf{⬀}}$ #73ADA4 Cadet blue

-

$\textcolor{#9ABEFF}{\textsf{⬀}}$ #9ABEFF Baby blue eyes

-

$\textcolor{#728DBE}{\textsf{⬀}}$ #728DBE Dark pastel blue

-

$\textcolor{#54688C}{\textsf{⬀}}$ #54688C UCLA Blue

-

$\textcolor{#ABABFF}{\textsf{⬀}}$ #ABABFF Baby blue eyes

-

$\textcolor{#BD9AFF}{\textsf{⬀}}$ #BD9AFF Bright lavender

-

$\textcolor{#8C72BD}{\textsf{⬀}}$ #8C72BD Ube

-
-

$\textcolor{#FFB0AC}{\textsf{⬀}}$ #FFB0AC Melon

-

$\textcolor{#F58F8A}{\textsf{⬀}}$ #F58F8A Light coral

-

$\textcolor{#F4806D}{\textsf{⬀}}$ #F4806D Coral pink

-

$\textcolor{#E85E5C}{\textsf{⬀}}$ #E85E5C Terra cotta

-

$\textcolor{#DC505E}{\textsf{⬀}}$ #DC505E Dark terra cotta

-

$\textcolor{#B02A3C}{\textsf{⬀}}$ #B02A3C Deep carmine

-

$\textcolor{#7A1B1C}{\textsf{⬀}}$ #7A1B1C Falu red

-

$\textcolor{#511119}{\textsf{⬀}}$ #511119 Dark scarlet

-
-

$\textcolor{#F1E9E0}{\textsf{⬀}}$ #F1E9E0 Eggshell

-

$\textcolor{#D6C8BA}{\textsf{⬀}}$ #D6C8BA Pastel gray

-

$\textcolor{#D4C6B8}{\textsf{⬀}}$ #D4C6B8 Pale silver

-

$\textcolor{#D7D0B8}{\textsf{⬀}}$ #D7D0B8 Pastel gray

-

$\textcolor{#E2C9B0}{\textsf{⬀}}$ #E2C9B0 Desert sand

-

$\textcolor{#D4B79A}{\textsf{⬀}}$ #D4B79A Tan

-

$\textcolor{#BF9E73}{\textsf{⬀}}$ #BF9E73 Camel

-
-
+2. Rename `old_icon.svg` to `old_icon_alt_1.svg` in [`resources/vectors`](./resources/vectors) directory (if SVG is not found there, just skip this step). -#### Graphic Editors +3. Rename `old_icon.png` to `old_icon_alt_1.png` in [`app/src/main/res/drawable-nodpi`](./app/src/main/res/drawable-nodpi) directory. -- [Adobe Swatch Exchange Palette](./resources/palettes/palette.ase) (Illustrator, Photoshop) +4. Add `old_icon_alt_1` to `Alts` category and `old_icon` to `New` category in [`app/src/main/assets/drawable.xml`](./app/src/main/assets/drawable.xml). -- [GPL Pallete](./resources/palettes/palette.gpl) (Inkscape, Karbon) +5. If the ComponentInfo also changed after rebranding, replace `old_icon` with `old_icon_alt_1` in [`app/src/main/assets/appfilter.xml`](./app/src/main/assets/appfilter.xml) (the alternative icon will be linked with the old ComponentInfos for backward compatibility). -# Building via GitHub Actions +6. Copy edited XMLs from [`app/src/main/assets`](./app/src/main/assets) to [`app/src/main/res/xml`](./app/src/main/res/xml). -> Everything described here must be done in your fork +#### Other Manipulations -## [OPTIONAL] Creating Secrets +The rest of the things are more or less obvious like moving drawable names between categories, renaming, etc. Just ask for help in Discord if something isn't clear. -Go to `Settings β†’ Secrets and Variables β†’ Actions` and create the following repository secrets: +# πŸ—οΈ Build - - `KEYSTORE_BASE64` +## πŸˆβ€β¬› GitHub Actions - ``` - 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 - ``` - - `KEYSTORE_PASSWORD` +> Everything described here must be done in your fork. - ``` - android - ``` +### 🏁 Run Workflow - - `KEYSTORE_KEY_ALIAS` +1. Go to [Actions β†’ Build FOSS](../../actions/workflows/build_foss.yml) +2. Click on **Run workflow**, optionally mark preferred checkboxes, then click on **Run workflow** +3. Wait for the build, it takes approximately 5-10 minutes. The zipped APK will be attached to the workflow run. Go to [Actions](../../actions), click on the latest workflow run and download it from **Artifacts** down below - ``` - android - ``` +### 🀐 Creating Secrets - - `KEYSTORE_KEY_PASSWORD` +> This is optional since the workflow contains hardcoded values, but you can do this to use your own keystore. The following values of variables and options match the hardcoded workflow values. - ``` - android - ``` +1. Generate a personal keystore with `keytool -genkeypair -alias android -keypass android -keystore android.keystore -storepass android -keyalg RSA -dname "CN=Android,O=Android,C=US" -validity 9999` + +2. Encode the keystore with `cat android.keystore | base64 | tr -d '\n' > android.keystore.base64` or do it with any online tool. + +3. Go to `Settings β†’ Secrets and Variables β†’ Actions` and create the following repository secrets (key-value pairs): + + - `KEYSTORE_BASE64` (contents of `android.keystore.base64`) + + ``` + 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 + ``` + - `KEYSTORE_PASSWORD` (the value of `-storepass` option) -You can generate a new keystore with `keytool -genkeypair -alias android -keypass android -keystore android.keystore -storepass android -keyalg RSA -dname "CN=Android,O=Android,C=US" -validity 9999` and fill secrets with your values. + ``` + android + ``` -To encode the keystore, use `cat android.keystore | base64 | tr -d '\n' > android.keystore.base64` or do it with any online tool. + - `KEYSTORE_KEY_ALIAS` (the value of `-alias` option) -## Run Workflow + ``` + android + ``` -1. Go to **Actions** -2. Select **Build FOSS** -3. Select **Run workflow** then click to green **Run workflow** button -4. Wait for the build, it takes approximately 5 minutes + - `KEYSTORE_KEY_PASSWORD` (the value of `-keypass` option) -The zipped APK will be attached to the workflow run. Go to **Actions**, click on the latest workflow run and download it from **Artifacts** down below + ``` + android + ``` diff --git a/README.md b/README.md index 5a31bd9ac4..e98d48c397 100644 --- a/README.md +++ b/README.md @@ -48,9 +48,9 @@ ### Flavors -- `gplay` [`website.leifs.delta`] β€” Google Play version -- `foss` [`website.leifs.delta.foss`] β€” FOSS version (Google-free) -- `fossdc` [`website.leifs.delta.fossdc`] β€” FOSS version with a dynamic clock +- `gplay` β€” Google Play version +- `foss` β€” FOSS version (Google-free) +- `fossdc` β€” FOSS version with a dynamic clock > `fossdc` is only available via [Releases](./releases/latest) section on GitHub. It's the same as `foss` flavor but the static clock is replaced with a dynamic one, and it has a different app ID suffix. This is a separate app because dynamic clocks require a higher level of Android API and are not widely supported in launchers. We don't wanna sacriface users with old phones but wanna support cool features, that's why there's such a compromise. This may change in the future. At the moment, you can use this version by installing it via
Obtanium. diff --git a/contribs/icons.yml b/contribs/icons.yml index 9877e925b3..7297dcc490 100644 --- a/contribs/icons.yml +++ b/contribs/icons.yml @@ -34,4 +34,4 @@ documents: - andes.oplus.documentsreader/andes.oplus.documentsreader.launcher.activity.MainActivity colonist: - action: remake + action: rebrand diff --git a/resources/scripts/process_icons.py b/resources/scripts/process_icons.py index 243075046c..3e0c45ea61 100755 --- a/resources/scripts/process_icons.py +++ b/resources/scripts/process_icons.py @@ -16,35 +16,20 @@ from process_requests import read from shared import paths, transform_xml -ACTIONS = ('none', 'remake', 'rebrand', 'remove') +ACTIONS = ('add', 'rewrite', 'rebrand', 'remove') PATTERN_ALT = re.compile(r'^.*_alt_\d+$') +PATTERN_ALT_X = re.compile(r'^(.+)_alt_x\d+$') PATTERN_CI = re.compile(r'^[a-zA-Z0-9._]+/[a-zA-Z0-9._$]+$') PATTERN_DRAWABLE = re.compile(r'^[a-z0-9_]+$') ICONS_HEADER = '''\ -# # action: none|remake|rebrand|remove|rename > new_name -# # none - default, new icon -# # remake - overwrite existing images, add to new category -# # rebrand - existing icon -> alt_x, new icon -> main -# # remove - remove existing icon (files + xml) -# # rename - rename existing icon (files + xml) # -# new_icon_1: com.example.icon/com.example.icon.MainActivity +# options: +# https://github.com/Delta-Icons/android/blob/master/CONTRIBUTING.md#options # -# new_icon_2: -# - com.example.icon/com.example.icon.MainActivity -# - com.example.icon/com.example.icon.SplashActivity -# -# new_icon_3_alt_1: {} -# -# new_icon_4: {} -# -# signal: -# action: rebrand -# category: Google -# compinfo: -# - com.google.app/com.google.app.MainActivity +# examples: +# https://github.com/Delta-Icons/android/blob/master/CONTRIBUTING.md#examples # ''' @@ -88,23 +73,31 @@ def parse_action(raw): case True: return 'rebrand', None, None case False | None: - return 'none', None, None + return 'add', None, None case str(): if '>' in raw: - action, _, rename = raw.partition('>') - action, rename = action.strip(), rename.strip() - if action != 'rename': - return 'none', None, f"action {color(action, 'cyan')} doesn't support {color('>', 'cyan')} syntax" - if not rename: - return 'none', None, f"rename requires a target, e.g. {color('rename > new_name', 'cyan')}" - return 'rename', rename, None + action, _, target = raw.partition('>') + action, target = action.strip(), target.strip() + if action == 'rename': + if not target: + return 'add', None, f"rename requires a target, e.g. {color('rename > new_name', 'cyan')}" + return 'rename', target, None + if action == 'move': + if not target: + return 'add', None, f"move requires a target category, e.g. {color('move > google', 'cyan')}" + return 'move', target, None + return 'add', None, f"action {color(action, 'cyan')} doesn't support {color('>', 'cyan')} syntax" if raw == 'rename': - return 'none', None, f"rename requires a target, e.g. {color('rename > new_name', 'cyan')}" + return 'add', None, f"rename requires a target, e.g. {color('rename > new_name', 'cyan')}" + if raw == 'move': + return 'add', None, f"move requires a target category, e.g. {color('move > google', 'cyan')}" if raw in ACTIONS: return raw, None, None - return 'none', None, f"action {color(raw, 'cyan')} is invalid, expected: {color(', '.join((*ACTIONS, 'rename > name')), 'cyan')}" + expected = ', '.join((*ACTIONS, 'rename > name', 'move > category')) + return 'add', None, f"action {color(raw, 'cyan')} is invalid, expected: {color(expected, 'cyan')}" case _: - return 'none', None, f"action {color(str(raw), 'cyan')} is invalid, expected: {color(', '.join((*ACTIONS, 'rename > name')), 'cyan')}" + expected = ', '.join((*ACTIONS, 'rename > name', 'move > category')) + return 'add', None, f"action {color(str(raw), 'cyan')} is invalid, expected: {color(expected, 'cyan')}" def parse_compinfos(value): @@ -126,6 +119,10 @@ def check_source_files(key, extensions=('png', 'svg')): return missing +def exists_in_dst(key): + return any(isfile(paths['dst'][fmt].format(key)) for fmt in ('png', 'svg')) + + def move_images(src_name, dst_name=None, from_dst=False, header=True): if dst_name is None: dst_name = src_name @@ -144,8 +141,7 @@ def move_images(src_name, dst_name=None, from_dst=False, header=True): def remove_images(name): - found = any(isfile(paths['dst'][fmt].format(name)) for fmt in ('png', 'svg')) - if found: + if exists_in_dst(name): log.info(f"images{color(':', 'dark_grey')}") for fmt in ('png', 'svg'): path = paths['dst'][fmt].format(name) @@ -158,6 +154,7 @@ def remove_images(name): def parse_icons(icons_yml, root_drawable): entries = [] errors = dict() + resolved_alts = {} categories = [ x.get('title') @@ -172,7 +169,26 @@ def parse_icons(icons_yml, root_drawable): entry_errors.append(f"name {color(key, 'cyan')} looks invalid") errors[key] = True - action = 'none' + original_key = None + alt_x_match = PATTERN_ALT_X.fullmatch(key) + if alt_x_match: + base_name = alt_x_match.group(1) + if base_name not in resolved_alts: + max_alt = 0 + for cat in root_drawable.findall('category'): + for item in cat.findall('item'): + d = item.get('drawable', '') + if d.startswith(base_name + '_alt_'): + try: + max_alt = max(max_alt, int(d.rsplit('_', 1)[1])) + except ValueError: + pass + resolved_alts[base_name] = max_alt + resolved_alts[base_name] += 1 + original_key = key + key = f'{base_name}_alt_{resolved_alts[base_name]}' + + action = 'add' rename = None compinfos = [] @@ -186,19 +202,22 @@ def parse_icons(icons_yml, root_drawable): case dict(): compinfos = parse_compinfos(value) category = value.get('category', category).capitalize() - action, rename, action_error = parse_action(value.get('action', 'none')) + action, rename, action_error = parse_action(value.get('action', 'add')) if action_error: entry_errors.append(action_error) errors[key] = True if action == 'remove': - if not any(isfile(paths['dst'][fmt].format(key)) for fmt in ('png', 'svg')): + if not exists_in_dst(key): entry_errors.append(f"action is {color('remove', 'cyan')}, but {key} doesn't exist in destination") errors[key] = True - if rename: + if action == 'move': + category = rename.capitalize() + rename = None + elif rename: if not PATTERN_DRAWABLE.fullmatch(rename): entry_errors.append(f"rename target {color(rename, 'cyan')} looks invalid") errors[key] = True - elif not any(isfile(paths['dst'][fmt].format(key)) for fmt in ('png', 'svg')): + elif not exists_in_dst(key): entry_errors.append(f"action is {color('rename', 'cyan')}, but {key} doesn't exist in destination") errors[key] = True elif 'category' not in value: @@ -207,6 +226,15 @@ def parse_icons(icons_yml, root_drawable): category = '#' if PATTERN_ALT.fullmatch(rename): category = 'Alts' + if action in ('remove', 'move') and compinfos: + entry_errors.append(f"action {color(action, 'cyan')} doesn't support compinfos") + errors[key] = True + if action in ('remove', 'move') and 'category' in value: + entry_errors.append(f"action {color(action, 'cyan')} doesn't support explicit category") + errors[key] = True + if original_key and action != 'add': + entry_errors.append(f"_alt_x naming requires action {color('add', 'cyan')}, got {color(action, 'cyan')}") + errors[key] = True case list(): compinfos = list(dict.fromkeys(value)) case str(): @@ -216,16 +244,17 @@ def parse_icons(icons_yml, root_drawable): entry_errors.append(f"category {color(category.lower(), 'cyan')} doesn't exist") errors[key] = True - if action in ('remake', 'rebrand'): - for ext in check_source_files(key): - entry_errors.append(f"action is {color(action, 'cyan')}, but {key}.{ext} not found") + source_key = original_key or key + if action in ('rewrite', 'rebrand'): + for ext in check_source_files(source_key): + entry_errors.append(f"action is {color(action, 'cyan')}, but {source_key}.{ext} not found") errors[key] = True compinfos = list(dict.fromkeys(x for x in compinfos if isinstance(x, str) and x)) - if not compinfos and category == 'Alts' and action not in ('rename', 'remove'): - for ext in check_source_files(key): - entry_errors.append(f"category is Alts but {key}.{ext} not found") + if not compinfos and category == 'Alts' and action not in ('rename', 'remove', 'move'): + for ext in check_source_files(source_key): + entry_errors.append(f"category is Alts but {source_key}.{ext} not found") errors[key] = True for compinfo in compinfos: @@ -243,6 +272,7 @@ def parse_icons(icons_yml, root_drawable): 'compinfos': compinfos, 'action': action, 'rename': rename, + 'original': original_key, })) if errors: @@ -284,10 +314,14 @@ def main(): for drawable, values in (parse_icons(icons_yml, root_drawable) if not args.sort else []): log_sep(drawable) + if values['original']: + log.info(f"action {color('=', 'dark_grey')} {color('rename', 'magenta')}{color(':', 'dark_grey')} {color(values['original'], 'cyan')} {color('->', 'dark_grey')} {color(drawable, 'cyan')}") + renamed_drawable = [] renamed_appfilter = [] added_drawable = [] old_drawable = drawable + move_from = None if values['rename']: new_name = values['rename'] @@ -305,15 +339,35 @@ def main(): cat.remove(item) drawable = new_name - elif values['action'] != 'none': + elif values['action'] == 'move': + move_from = next( + (cat.get('title').lower() for cat in root_drawable.findall('category') + for item in cat.findall('item') if item.get('drawable') == drawable), + '?' + ) + log.info(f"action {color('=', 'dark_grey')} {color('move', 'magenta')}{color(':', 'dark_grey')} {color(move_from, 'cyan')} {color('->', 'dark_grey')} {color(values['category'].lower(), 'cyan')}") + else: log.info(f"action {color('=', 'dark_grey')} {color(values['action'], 'magenta')}") - if values['action'] != 'remove': - log.info(f"category {color('=', 'dark_grey')} {color(values['category'].lower(), 'magenta')}") + cat_name = values['category'].lower() + add_new = drawable not in [item.get('drawable') for item in category_new] + + if values['action'] not in ('remove', 'move'): + adds_to_new = values['action'] in ('add', 'rename') or (values['action'] == 'rewrite' and add_new) + if adds_to_new: + log.info(f"categories {color('=', 'dark_grey')} {color(f'new, {cat_name}', 'magenta')}") + else: + log.info(f"category {color('=', 'dark_grey')} {color(cat_name, 'magenta')}") match values['action']: case 'rename': move_images(old_drawable, drawable, from_dst=True) + case 'move': + for cat in root_drawable.findall('category'): + for item in list(cat.findall('item')): + if item.get('drawable') == drawable: + renamed_drawable.append(ET.tostring(item).decode().strip()) + cat.remove(item) case 'remove': remove_images(drawable) removed_drawable = [] @@ -356,33 +410,36 @@ def main(): category_alt.append(item) move_images(drawable, new_name, from_dst=True) move_images(drawable, header=False) - added_drawable.append(f' {color(f'(alts)', 'dark_grey')}') - case 'remake': - move_images(drawable) - case _: - move_images(drawable) + added_drawable.append(f' {color("(alts)", "dark_grey")}') + case 'add' | 'rewrite': + move_images(values['original'] or drawable, drawable) if values['action'] not in ('remove',): category = root_drawable.find(f"category[@title='{values['category']}']") add_drawable = drawable not in [item.get('drawable') for item in category] - add_new = drawable not in [item.get('drawable') for item in category_new] - - if renamed_drawable or added_drawable or add_drawable or (values['action'] == 'remake' and add_new): + if renamed_drawable or added_drawable or add_drawable or (values['action'] == 'rewrite' and add_new): log.info(f"drawable.xml{color(':', 'dark_grey')}") for entry in renamed_drawable: - print(f" {color(f'– {entry}', 'red')}") + if values['action'] == 'move': + print(f" {color(f'– {entry}', 'red')} {color(f'({move_from})', 'dark_grey')}") + else: + print(f" {color(f'– {entry}', 'red')}") for entry in added_drawable: print(f" {color(f'+ {entry}', 'green')}") if add_drawable: item = ET.Element('item') item.set('drawable', drawable) - cat_name = values['category'].lower() item.tail = '\n\t' category.append(item) - category_new.append(deepcopy(item)) - print(f" {color(f'+ {ET.tostring(item).decode()}', 'green')} {color(f'(new, {cat_name})', 'dark_grey')}") - elif values['action'] == 'remake' and add_new: + item_str = ET.tostring(item).decode().strip() + if values['action'] == 'move': + print(f" {color(f'+ {item_str}', 'green')} {color(f'({cat_name})', 'dark_grey')}") + else: + category_new.append(deepcopy(item)) + print(f" {color(f'+ {item_str}', 'green')} {color('(new)', 'dark_grey')}") + print(f" {color(f'+ {item_str}', 'green')} {color(f'({cat_name})', 'dark_grey')}") + elif values['action'] == 'rewrite' and add_new: item = ET.Element('item') item.set('drawable', drawable) item.tail = '\n\t' From 8889efd088192e553eec270a790f6444e46e100b Mon Sep 17 00:00:00 2001 From: flameshikari Date: Mon, 2 Mar 2026 04:44:31 +0500 Subject: [PATCH 06/12] new icon: Battery Alarm --- contribs/icons.yml | 4 ++++ contribs/icons/battery_alarm.png | Bin 0 -> 4075 bytes contribs/icons/battery_alarm.svg | 1 + 3 files changed, 5 insertions(+) create mode 100644 contribs/icons/battery_alarm.png create mode 100644 contribs/icons/battery_alarm.svg diff --git a/contribs/icons.yml b/contribs/icons.yml index 7297dcc490..596bd8ce61 100644 --- a/contribs/icons.yml +++ b/contribs/icons.yml @@ -35,3 +35,7 @@ documents: colonist: action: rebrand + +battery_alarm: + - simple.batttery.alarm/simple.batttery.alarm.main + - simple.battery.alarm/simple.battery.alarm.main diff --git a/contribs/icons/battery_alarm.png b/contribs/icons/battery_alarm.png new file mode 100644 index 0000000000000000000000000000000000000000..cde2fe35fee19eed5544c42512b444543f681800 GIT binary patch literal 4075 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 ipe7FcQ}n=G%y>VE(;E \ No newline at end of file From cce706058e9e67a387be7c2e0ea7aca4725cd691 Mon Sep 17 00:00:00 2001 From: flameshikari Date: Mon, 2 Mar 2026 15:48:42 +0500 Subject: [PATCH 07/12] add new icons --- contribs/icons.yml | 16 ++++++++++++++++ contribs/icons/heyjom.png | Bin 0 -> 4599 bytes contribs/icons/heyjom.svg | 1 + contribs/icons/kreditbee.png | Bin 0 -> 5352 bytes contribs/icons/kreditbee.svg | 1 + contribs/icons/naver_store.png | Bin 0 -> 2360 bytes contribs/icons/naver_store.svg | 1 + contribs/icons/tosla.png | Bin 0 -> 3719 bytes contribs/icons/tosla.svg | 1 + contribs/icons/uni.png | Bin 0 -> 7106 bytes contribs/icons/uni.svg | 1 + contribs/icons/weblibre.png | Bin 0 -> 5123 bytes contribs/icons/weblibre.svg | 1 + contribs/icons/xash3d.png | Bin 0 -> 3085 bytes contribs/icons/xash3d.svg | 1 + 15 files changed, 23 insertions(+) create mode 100644 contribs/icons/heyjom.png create mode 100644 contribs/icons/heyjom.svg create mode 100644 contribs/icons/kreditbee.png create mode 100644 contribs/icons/kreditbee.svg create mode 100644 contribs/icons/naver_store.png create mode 100644 contribs/icons/naver_store.svg create mode 100644 contribs/icons/tosla.png create mode 100644 contribs/icons/tosla.svg create mode 100644 contribs/icons/uni.png create mode 100644 contribs/icons/uni.svg create mode 100644 contribs/icons/weblibre.png create mode 100644 contribs/icons/weblibre.svg create mode 100644 contribs/icons/xash3d.png create mode 100644 contribs/icons/xash3d.svg diff --git a/contribs/icons.yml b/contribs/icons.yml index 596bd8ce61..3b7568a136 100644 --- a/contribs/icons.yml +++ b/contribs/icons.yml @@ -39,3 +39,19 @@ colonist: battery_alarm: - simple.batttery.alarm/simple.batttery.alarm.main - simple.battery.alarm/simple.battery.alarm.main + +weblibre: eu.weblibre.gecko/eu.weblibre.gecko.MainActivity + +umi: app.immersely.immersely/app.immersely.immersely.MainActivity + +kreditbee: com.kreditbee.android/com.kreditbee.android.splash.SplashActivity + +tosla: com.akode.tosla/com.valensas.tosla.modules.auth.landing.splash.SplashActivity + +xash3d: + - su.xash.engine/su.xash.engine.MainActivity + - su.xash.engine.test/su.xash.engine.MainActivity + +heyjom: com.heyjom.android/com.heyjom.android.MainActivity + +naver_plus: com.navercorp.navershopping/com.naver.nspa.app.MainActivity \ No newline at end of file diff --git a/contribs/icons/heyjom.png b/contribs/icons/heyjom.png new file mode 100644 index 0000000000000000000000000000000000000000..4b69e93273308849010849a89be59fb6a5d2de49 GIT binary patch literal 4599 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 z zDvHUL_wQt=GFgM&nj1T;THTlMOtmm|bH1%7yT z(XK9;AK2^?8WtwISadozo=!d8_@pZGy0tlSB_TPq3)=6`~#e zpQ!&It$+-Yzu2G-UGcKChUKqbO#ZaH-UTc$t+-vx5o+JcRtHdWZ~Ws)LBj-UXEp2{ zcmt-oeKhHF;tponGjH$nnl~UKW~wX8>%%(lgHB&1!2bzO_G_SScG#g+fz!^v2D&hP KO|-gg)PDh#t91+j literal 0 HcmV?d00001 diff --git a/contribs/icons/heyjom.svg b/contribs/icons/heyjom.svg new file mode 100644 index 0000000000..474798989a --- /dev/null +++ b/contribs/icons/heyjom.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/contribs/icons/kreditbee.png b/contribs/icons/kreditbee.png new file mode 100644 index 0000000000000000000000000000000000000000..eb47d04258241a05cdff0fd1d749618a3afb2d78 GIT binary patch literal 5352 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 \ No newline at end of file diff --git a/contribs/icons/naver_store.png b/contribs/icons/naver_store.png new file mode 100644 index 0000000000000000000000000000000000000000..d86268c28c440f6dca77b6c8b1b5ef012a43bfaf GIT binary patch literal 2360 zcmZWoc{tST7yk~k*vB$ivQ9#E6=g^vV`ePV!bE9AS;{t%2APCw%xF-?NT!lCJGWn? zaA%|$JGYd{R+g-VqO4iM{BpbZkKglq&vVXs&U2pUeLtV`Id1~N-d0*-y959L(s(06&9B$1HG|4_z9%m~$1H=X4FP%8mAw_mWp7wnQiq^q`*ilQs#bJIUUZer7w!z1@& zf~Bv~~yOXw_H-^_iw@B#MwZ5JxCg?~f9X!{!n zBGL30)h&Dy|9}${O&wWMq4+F`mWYtfqUi#E>j_jd%uq%lWlZSQKZg=oUVgnc#2Se~ zLM0enm@aCRPh-+&?p&Wz%7P78-;+}Va5pvXpHmiAgg=K_51t=e*x-+()-^sK_&A^SbH%rgd0SM| z+5vUvr>t->cmC;01z6Z${D9Qp!@@(Uui>u-zg*!Am;t(p`7zP-`8X-`z-C4n(z;~c zZbP{IKCbfI^8AOcl=aclJ3|SpZTp4-S^|b_AR0gfzKF}&?d}RsIXcnbS9lBcKxNn* z+*b0v*_uww<3Jb7uKRRE=2oAJ&B5vvT(nSqCNoLV7_72bdQ`idk5>URAysF4{r}ib z3*d#hWZWXK_TQA)sRjTgb zD9iZUduuBbB~18khonQlhLaPZ14K|By)~3=A)DWzNk|h9Zv$^@rd(dbqnCirAyx z_RMFxH5VR_p?Zs0Jf2TY$mLuh)OhEas11!|&u%^P>-Ks~ zJK4Fvv!&MemS{(JGVUs*Jc1h&L0wFmDuAN{UCSOAX_-pqnl;n>i}%dMcmSo%fBlD> zST&9bhz@57?w*gI**B9YskRe$`s8WsCy7d%MGh&`@XOgcKY+>kb`=}#_oDPl&z?Qo zj(2hBV1t$wiWPU8;W8Dm1f?85QD6!3?2S4ANJS*M>YBv6w@IR zTBImGs*Box@54$K@DQN6$T{9;=&mk_1eQX3LE&WW*{tDvWbfxNDj>EsCY?nzP*F7o zCirGlgfCOL?4njcrk9%~%=z8y9I1vT%>ezY%-@+1=`Qk&V!+#PO;+Qx3U}{N(A_4m z$sMgk&Jnwtwd)v6Vmy<0ZF=^ehwa`*8-1Ko01lyQf|8d<=CkcuFqH>z?BOhbJhAJz z@3W9^8<8pL_|CKsivtTY7Q7Ij>;LoEui}27agcq)SMN7d<0JWnmlC_9Y*Co8#`YKo zV{-sss_GDj$&8_s3tu#t zi*a$^Gz&guD2o_(eHtw+kA8pT3~RjymS0)OTnEbqcf9{Xv$+sqC2^(-<@@U z{bF539Y^-puJ#vN@+_TYM&8%T+8mwQv@kj-eR##cJosQd1+tkPej@?i-nc6CiUUw= zBD|5SwU0I@Y|?sGC64Y)_>E64j89WJAdXhJc?m*p&*^D0nnxZKZW~?eV7k@n#eaVF zS-)d;W%k+mGnjzRRR(bu|x2egYK# zBjisheL<#qX?7(>S7E~|rFliMR8-g|$)x=;Nerbz^Sb|zs{FuZI \ No newline at end of file diff --git a/contribs/icons/tosla.png b/contribs/icons/tosla.png new file mode 100644 index 0000000000000000000000000000000000000000..319b948352614664142d19c465d541d9fd4258ab GIT binary patch literal 3719 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 literal 0 HcmV?d00001 diff --git a/contribs/icons/tosla.svg b/contribs/icons/tosla.svg new file mode 100644 index 0000000000..ea8c26e656 --- /dev/null +++ b/contribs/icons/tosla.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/contribs/icons/uni.png b/contribs/icons/uni.png new file mode 100644 index 0000000000000000000000000000000000000000..9f3ddbc334d4b8533297c6d88018b335ec8c4009 GIT binary patch literal 7106 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 literal 0 HcmV?d00001 diff --git a/contribs/icons/uni.svg b/contribs/icons/uni.svg new file mode 100644 index 0000000000..b35cca896d --- /dev/null +++ b/contribs/icons/uni.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/contribs/icons/weblibre.png b/contribs/icons/weblibre.png new file mode 100644 index 0000000000000000000000000000000000000000..401663b14b46bda4d3b0f417a15acddd97e0014b GIT binary patch literal 5123 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 \ No newline at end of file diff --git a/contribs/icons/xash3d.png b/contribs/icons/xash3d.png new file mode 100644 index 0000000000000000000000000000000000000000..bdb495a62588f7776c688deb07b4ecf16c342c5b GIT binary patch literal 3085 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 \ No newline at end of file From c4cd7cfb00c49e6b9bc60beadefa1d423f0022ea Mon Sep 17 00:00:00 2001 From: flameshikari Date: Tue, 3 Mar 2026 16:16:28 +0500 Subject: [PATCH 08/12] update CONTRIBUTING.md --- CONTRIBUTING.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d42140e435..d385b9505c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -35,7 +35,7 @@ Good luck! ### πŸ“ Rules -- Canvas size must be 192x192px. The template from [Resources](#resources) is correctly configured, just download and work with it. +- Canvas size must be 192x192px. The template from [Resources](#-resources) is correctly configured, just download and work with it. - The icon size does not exceed template dimensions. Check out [the visual explanation](./resources/templates/template_tutorial.svg). - If the original logo is simple and doesn't fill most of the template as a shape (circle, square, etc.), keep the logo size between 73–80px. - The rounded corners of squares and rectangles have a corner radius of 10px. @@ -164,7 +164,7 @@ Good luck! - If the original icon consists of just one or two letters, you may trace that letter instead of using these fonts. - Fonts can be a little tricky to align/center in different vector editors, so read about it on the Internet. -- You can use a custom font if it matches the font from the original icon, for the rest use fonts from [Resources](#resources-3). +- You can use a custom font if it matches the font from the original icon, for the rest use fonts from [Resources](#-resources-2). ### 🧰 Resources @@ -177,15 +177,17 @@ Good luck! ### ❔ Key Terms +> Some things are described very roughly for better understanding. + - **Icon images** β€” your exported PNG/SVG icons. -- **ComponentInfo** β€” an identifier (e.g. `com.example/com.example.MainActivity`) that launchers use to match an installed app to its icon in the icon pack. You can use these tools to get ComponentInfos from your installed apps: +- **ComponentInfo** β€” an app identifier (e.g. `com.example/com.example.MainActivity`) that launchers use to match an installed app to its icon in the icon pack. You can use these tools to get ComponentInfos from your installed apps: - [Icon Pusher](https://iconpusher.com/) by [V01D](https://v01d.uk) - [Icon Request](https://github.com/Kaiserdragon2/IconRequest/releases) by [Kaiserdragon2](https://github.com/Kaiserdragon2) -- **Drawable name** β€” an internal name (e.g. `new_icon`) that links an icon image to its ComponentInfo. The drawable name must be in alphanumeric lowercase with underscores only and icon image names must match the drawable name exactly (e.g. `new_icon.png` and `new_icon.svg`). +- **Drawable name** β€” an internal name of an icon (e.g. `new_icon`). It's used to include an icon image to the icon pack, and in combo with ComponentInfo(s) it links the icon with the target app. The drawable name must be in alphanumeric lowercase with underscores only and icon image names must match the drawable name exactly (e.g. `new_icon.png` and `new_icon.svg`). - **Standalone icon** β€” an icon that isn't linked to any app. They can be non-app icons like `adobe` and be used as web shortcut icons, etc. Users can select them via their launcher if it supports that feature. -- **Alternative icon** β€” an alternative version of an app icon. Mainly used when the app rebrands: the old icon becomes an alternative (e.g. `new_icon_alt_1`), and a new icon takes its place. However, you can create alternative icons for any app without passing any ComponentInfo, and they will be standalone. Users can select them via their launcher if it supports that feature. +- **Alternative icon** β€” an alternative version of an app icon. Mainly used when the app rebrands: the old icon becomes an alternative (e.g. `new_icon_alt_1`), and a new icon takes its place. However, you can create alternative icons for any app without linking them to any ComponentInfo, and they will be standalone. Users can select them via their launcher if it supports that feature. - **Categories** β€” categories within [`app/src/main/assets/drawable.xml`](./app/src/main/assets/drawable.xml) to organize icons. Description of categories: - `New` β€” new icons for the current release. If [manually](#️-manual) adding icons, you must also add the entry to this category. From 32a4fe3cdba13f104523b0292e78fb4033130a96 Mon Sep 17 00:00:00 2001 From: Leif Niemczik Date: Wed, 11 Mar 2026 00:48:51 +0100 Subject: [PATCH 09/12] Revise wording in CONTRIBUTING.md for clarity Updated language for clarity and consistency in CONTRIBUTING.md. --- CONTRIBUTING.md | 131 +++++++++++++++++++++++++----------------------- 1 file changed, 68 insertions(+), 63 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d385b9505c..058f6b1c95 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,70 +1,70 @@ # ℹ️ Introduction -> Feel free to ask for help on [the Discord server](https://discord.gg/F9RFqHN) if something is unclear +> Feel free to ask for help on [the Discord server](https://discord.gg/F9RFqHN) if anything is unclear -Hello! I see you've decided to contribute. Well, there are a few ways to go: +Hi! I see you've decided to contribute. That's great and there are a few ways to do so: -If you know how to make cool icons but don't know how to work with [git](https://git-scm.com/), [GitHub](https://github.com), and other tech stuff, just read [Design Guidelines](#-design-guidelines) carefully, then make some icons and submit them on Discord. +If you know how to make cool icons but don't know how to work with [git](https://git-scm.com/), [GitHub](https://github.com), and other tech stuff, just read [Design Guidelines](#-design-guidelines) carefully, make some icons and submit them on our Discord in the [icon review channel](https://discord.com/channels/743783969216135198/743784395856412782). -If you don't know how to make cool icons but you are tech-savvy, you can help improve our CI/CD, add missing ComponentInfos, and do other things. Read [Contributing](#-contributing) for more info. +If you don't know how to make icons but you are tech-savvy, you can help improve our CI/CD, add missing ComponentInfos, and do other things. Read [Contributing](#-contributing) for more info. -If you can handle tech and design stuff, then read this guide completely. +If you can handle tech and design stuff, then please read this guide completely. -If you don't know any of that but want to try to make icons... well, we can only help with advice on Discord. There are so many tools, instructions, and guides for them that it’s impossible to describe everything, so the only thing you can do is search for information on the Internet and try it out. +If you don't know any of that but want to try to make icons, we can help with some advice on our Discord. There are many tools, instructions, and guides for them that it’s impossible to describe everything, so the only thing you can do is search for information on the Internet and try it out. -In short, the general requirements are: +In short, the suggested skills are: - Basic knowledge of [git](https://git-scm.com/) and/or [GitHub](https://github.com). -- Experience with vector editors. -- A little bit of design sense. πŸ˜‚ +- Some experience with vector editors. +- A little bit of a design sense -Good luck! +We'll be happy to see you participate! # πŸ“ Design Guidelines -## πŸͺ„ General Tips +## General Tips -- Keep it simple: fewer details and small elements, because they may not be visible on the screen. -- Your icon doesn't have to be a 1:1 copy of the original; improve and simplify it in every way possible but at the same time try to maintain a recognizable appearance. +- Keep it simple: fewer details and small elements, because the end result will only be a small element on the users screens. +- Your icon doesn't have to be a 1:1 copy of the original; improve and simplify where possible but at the same time try to maintain a recognizable appearance. - Avoid outlining, otherwise the icon will stand out from the general style. -- Make the icon free-form if possible. +- Make the icon free-form if you can β€” it helps keep Delta a little more diverse - You can search for app logos online (they're often found on official websites; avoid icons with non-free licenses) and adapt them. If the original icon is too complex, you can use another recognizable element (an item, a faction icon, etc.) β€” this applies to any complex icon, not just games. - Be sure to double-check that icons are centered and aligned, sized and exported correctly. -## πŸ–ΌοΈ Icon Template +## Icon Template -### πŸ“ Rules +### Rules -- Canvas size must be 192x192px. The template from [Resources](#-resources) is correctly configured, just download and work with it. +- Canvas size must be 192x192px. The template from [Resources](#-resources) is correctly configured, just download it and you should be good to go. - The icon size does not exceed template dimensions. Check out [the visual explanation](./resources/templates/template_tutorial.svg). -- If the original logo is simple and doesn't fill most of the template as a shape (circle, square, etc.), keep the logo size between 73–80px. +- If the original logo is simple and doesn't fill most of the template as a shape (circle, square, etc.), keep the logo size between 72–80px. - The rounded corners of squares and rectangles have a corner radius of 10px. - The template must be properly centered on the canvas. -### πŸͺ„ Tips +### Tips - If you're having trouble deciding whether to use geometric or optical centering, you can discuss it on Discord. Mostly it depends on the icon, but optical centering is usually your choice. - If you have any doubts about the design of your icon, you can also discuss it on Discord. -### 🧰 Resources +### Resources -> You can also check [Figma icon template](https://www.figma.com/design/02aiFRSLkikcw8mpBAnoDA/Delta-Icon-Template?m=auto&t=qyLH05AMDzZwAI2s-1). +> You can also check out the [Figma icon template](https://www.figma.com/design/02aiFRSLkikcw8mpBAnoDA/Delta-Icon-Template?m=auto&t=qyLH05AMDzZwAI2s-1), if you're using Figma. -## 🌈 Colors +## Colors -### πŸ“ Rules +### Rules -- $\textcolor{#56595B}{\textsf{⬀}}$ #56595B Davy's grey as default black. +- Use $\textcolor{#56595B}{\textsf{⬀}}$ #56595B Davy's grey as default black. -- $\textcolor{#FF837D}{\textsf{⬀}}$ #FF837D Coral pink as default red and $\textcolor{#BA6561}{\textsf{⬀}}$ #BA6561 Fuzzy Wuzzy as default dark red. Shades of red are specifically for shading purposes. +- Use $\textcolor{#FF837D}{\textsf{⬀}}$ #FF837D Coral pink as default red and $\textcolor{#BA6561}{\textsf{⬀}}$ #BA6561 Fuzzy Wuzzy as default dark red. Shades of red are specifically for shading purposes and complex arrangements (if we're honest it's mostly complex anime / game icons overusing pink). -- Transparencies can be used as an overlay for additional shading. We rarely use them, so don't use them unnecessarily β€” try to get by with basic colors and greys as much as possible. If you do use transparency, it should be an overlay, never a background (i.e. no transparency in the raster version). +- Transparencies can be used as an overlay for additional shading. Please keep the use of them to a minimum. Try to get by with basic colors and greys as much as possible. If you do use transparency, it should be an overlay, never a background (the overall shape of the icon should not contain any semi-transparent areas). -- Gradients are more acceptable than transparencies, but still try to avoid using them (as noted above). +- Gradients are more acceptable than transparencies, but the usage should still be kept to a sensible minimum (as noted above). -### 🎨 Palette +### Palette @@ -137,7 +137,7 @@ Good luck!
-### 🧰 Resources +### Resources #### Vector Palettes @@ -155,29 +155,27 @@ Good luck! #### Vector Editors -- [Adobe Swatch Exchange Palette](./resources/palettes/palette.ase) (Illustrator, Photoshop) +- [Adobe Swatch Exchange Palette](./resources/palettes/palette.ase) (Illustrator) - [GPL Palette](./resources/palettes/palette.gpl) (Inkscape, Karbon) -## πŸ—š Font +## Font -### πŸͺ„ Tips +### Tips - If the original icon consists of just one or two letters, you may trace that letter instead of using these fonts. -- Fonts can be a little tricky to align/center in different vector editors, so read about it on the Internet. -- You can use a custom font if it matches the font from the original icon, for the rest use fonts from [Resources](#-resources-2). +- Fonts can be a little tricky to align/center in different vector editors, which you can mitigate by either converting them to paths or in the case of Figma: the text box trim. +- You can use a custom font if it matches the font from the original icon very closely, for the rest use fonts from [Resources](#-resources-2). -### 🧰 Resources +### Resources -- [Now](https://www.1001fonts.com/now-font.html?text=Delta%20Icons) β€” main Sans-serif font; use Now Alt from the same family for alternate lowercase 'a' letter. -- [Aleo](https://www.1001fonts.com/aleo-font.html?text=Delta%20Icons) β€” use it only when Serif is needed. +- [Now](https://www.1001fonts.com/now-font.html?text=Delta%20Icons) β€” main Sans-serif font; Now Alt from the same family can be used for an alternate lowercase 'a' letter. +- [Aleo](https://www.1001fonts.com/aleo-font.html?text=Delta%20Icons) β€” when Serifs are needed. -# πŸ“₯ Contributing +# Contributing -## πŸͺŸ Overview +## Overview -### ❔ Key Terms - -> Some things are described very roughly for better understanding. +### Key Terms - **Icon images** β€” your exported PNG/SVG icons. - **ComponentInfo** β€” an app identifier (e.g. `com.example/com.example.MainActivity`) that launchers use to match an installed app to its icon in the icon pack. You can use these tools to get ComponentInfos from your installed apps: @@ -185,9 +183,9 @@ Good luck! - [Icon Pusher](https://iconpusher.com/) by [V01D](https://v01d.uk) - [Icon Request](https://github.com/Kaiserdragon2/IconRequest/releases) by [Kaiserdragon2](https://github.com/Kaiserdragon2) -- **Drawable name** β€” an internal name of an icon (e.g. `new_icon`). It's used to include an icon image to the icon pack, and in combo with ComponentInfo(s) it links the icon with the target app. The drawable name must be in alphanumeric lowercase with underscores only and icon image names must match the drawable name exactly (e.g. `new_icon.png` and `new_icon.svg`). -- **Standalone icon** β€” an icon that isn't linked to any app. They can be non-app icons like `adobe` and be used as web shortcut icons, etc. Users can select them via their launcher if it supports that feature. -- **Alternative icon** β€” an alternative version of an app icon. Mainly used when the app rebrands: the old icon becomes an alternative (e.g. `new_icon_alt_1`), and a new icon takes its place. However, you can create alternative icons for any app without linking them to any ComponentInfo, and they will be standalone. Users can select them via their launcher if it supports that feature. +- **Drawable name** β€” an internal name of an icon (e.g. `app_name`). It's used to include an icon image to the icon pack, and in combo with ComponentInfo(s) it links the icon with the target app. The drawable name must be in alphanumeric lowercase with underscores only and icon image names must match the drawable name exactly (e.g. `new_icon.png` and `new_icon.svg`). +- **Standalone icon** β€” an icon that isn't linked to any app. They can be non-app icons like `adobe`, or folder icons and be used for web shortcuts, app folders, etc. Users can select them via their launcher if it supports that feature. +- **Alternative icon** β€” an alternative version of an app icon. Mainly used when the app rebrands: the old icon becomes an alternative (e.g. `app_name_alt_1`), and a new icon takes its place. However, you can create alternative icons for any app without linking them to any ComponentInfo, and they will be standalone. Users can select them via their launcher if it supports that feature. - **Categories** β€” categories within [`app/src/main/assets/drawable.xml`](./app/src/main/assets/drawable.xml) to organize icons. Description of categories: - `New` β€” new icons for the current release. If [manually](#️-manual) adding icons, you must also add the entry to this category. @@ -199,31 +197,38 @@ Good luck! - `#` β€” icons whose name starts with a number (e.g. `_2048`). If [manually](#️-manual) adding icons, drawable names that begin with a number must have a leading underscore and be placed in this category. - `A–Z` β€” everything else, sorted by the first letter of the drawable name. -### πŸ“ Rules +### Rules -- Keep [LF](https://en.wikipedia.org/wiki/Newline) line endings in edited files. +- Keep [LF](https://en.wikipedia.org/wiki/Newline) line endings in edited files. Git has a setting for this. -### πŸ—’οΈ Notes for Contributors +### Notes for Contributors Want to help close user requests? Check [`contribs/requests.yml`](./contribs/requests.yml) β€” it contains all pending icon requests and is updated periodically. If you wish, you can add yourself to [`app/src/main/res/xml/contributors.xml`](./app/src/main/res/xml/contributors.xml) to shine in the app's contributors section! -## πŸ“š Managing Icons +## Adding / managing icons + +Your new app icon will have two important identifiers: -> `new_icon` (and derivatives of it) will be used as a drawable name
-> `com.example/com.example.MainActivity` (and derivatives of it) will be used as a ComponentInfo +- `icon_name` (and derivatives of it) will be used as a drawable name +- `com.example/com.example.MainActivity` (and derivatives of it) will be used as a ComponentInfo + +
+ Example + You have an app called "Delta Icon Delivery". A sensible name for your drawable would be delta_icon_delivery. The component info would be dictated by the app itself and be akin to something like com.delta.delivery/com.delta.delivery.Actvities.MainActivity. +
-So, you made an icon then exported it as `new_icon.png` and `new_icon.svg`. Now you need to select which way to manage icons. Here are two ways: +So, you made an icon then exported it as `new_icon.png` and `new_icon.svg`. Now you needto select which way to manage icons. Here are two ways: -- [**Auto**](#-auto) β€” an automatic and declarative way of managing icons via [`contribs/icons.yml`](./contribs/icons.yml), processed by scripts and GitHub Actions. This is the recommended approach. -- [**Manual**](#️-manual) β€” this is how icons were managed before [**Auto**](#-auto) was implemented. Directly editing XMLs and placing icon images into the appropriate directories. More control, but inconvenient. Try to avoid it unless [**Auto**](#-auto) can't handle what you need. +- [**Automatic**](#-automatic) β€” an automatic and declarative way of managing icons via the file [`contribs/icons.yml`](./contribs/icons.yml) in the repo, processed by scripts and GitHub Actions. This is the recommended approach. +- [**Manual**](#️-manual) β€” this is how icons were managed before [**Auto**](#-auto) was implemented. Directly editing XMLs and placing icon images into the appropriate directories. More control, but also more prone to errors. Try to avoid it unless [**Auto**](#-auto) can't handle what you need. -### πŸ€– Auto +### Automatic This is an automatic and declarative method of managing icons via [`contribs/icons.yml`](./contribs/icons.yml), driven by scripts and GitHub Actions. -Place your icon images in the [`contribs/icons`](./contribs/icons) directory and add the following entry to [`contribs/icons.yml`](./contribs/icons.yml): +Place your icon images (the `new_icon.png` and `new_icon.svg` files both) in the [`contribs/icons`](./contribs/icons) directory and add an entry in the following format to [`contribs/icons.yml`](./contribs/icons.yml): ```yaml new_icon: com.example/com.example.MainActivity @@ -231,7 +236,7 @@ new_icon: com.example/com.example.MainActivity And that's all. This is the easiest and most common method for adding a new icon and linking it to an app. [`contribs/icons.yml`](./contribs/icons.yml) will be processed by scripts and cleared automatically every release. -That entry can be extended with more options: +That entry can be extended with more options, for example: ```yaml new_icon: @@ -242,7 +247,7 @@ new_icon: - com.example/com.example.SplashActivity ``` -Check [Options](#options) to get more explanation of each option and [Examples](#examples) for more examples. +Check [Options](#options) below to get an explanation of each option and [Examples](#examples) for more examples. #### Options @@ -328,9 +333,9 @@ existing_icon: ``` -### ✍️ Manual +### Manual -This is how icons were managed before [**Auto**](#-auto) was implemented. Directly editing XMLs and placing exported icons into the appropriate directories. More control, but inconvenient. Try to avoid it unless [**Auto**](#-auto) can't handle what you need. +This is how icons were managed before [**Automatic**](#-automatic) was implemented. Avoid it unless [**Automatic**](#-automatic) can't handle what you need. There are two `drawable.xml` and two `appfilter.xml` files to edit (stored in [`app/src/main/assets`](./app/src/main/assets) and [`app/src/main/res/xml`](./app/src/main/res/xml)). It's better to edit the XMLs in [`app/src/main/assets`](./app/src/main/assets), then copy them to [`app/src/main/res/xml`](./app/src/main/res/xml) to keep all files identical. You can do it however you want (e.g. editing all files at the same time), just keep them identical. @@ -397,9 +402,9 @@ If an existing icon has been rebranded, don't overwrite it with a new one β€” do The rest of the things are more or less obvious like moving drawable names between categories, renaming, etc. Just ask for help in Discord if something isn't clear. -# πŸ—οΈ Build +# Build -## πŸˆβ€β¬› GitHub Actions +## GitHub Actions > Everything described here must be done in your fork. @@ -409,7 +414,7 @@ The rest of the things are more or less obvious like moving drawable names betwe 2. Click on **Run workflow**, optionally mark preferred checkboxes, then click on **Run workflow** 3. Wait for the build, it takes approximately 5-10 minutes. The zipped APK will be attached to the workflow run. Go to [Actions](../../actions), click on the latest workflow run and download it from **Artifacts** down below -### 🀐 Creating Secrets +### Creating Secrets > This is optional since the workflow contains hardcoded values, but you can do this to use your own keystore. The following values of variables and options match the hardcoded workflow values. From ab40120443a7ecf59817c80e2ea00c14af6fbc8e Mon Sep 17 00:00:00 2001 From: flameshikari Date: Tue, 17 Mar 2026 23:54:04 +0500 Subject: [PATCH 10/12] update CONTRIBUTING.md --- CONTRIBUTING.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 058f6b1c95..96aad33786 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -16,7 +16,7 @@ In short, the suggested skills are: - Basic knowledge of [git](https://git-scm.com/) and/or [GitHub](https://github.com). - Some experience with vector editors. -- A little bit of a design sense +- A little bit of a design sense. We'll be happy to see you participate! @@ -27,7 +27,7 @@ We'll be happy to see you participate! - Keep it simple: fewer details and small elements, because the end result will only be a small element on the users screens. - Your icon doesn't have to be a 1:1 copy of the original; improve and simplify where possible but at the same time try to maintain a recognizable appearance. - Avoid outlining, otherwise the icon will stand out from the general style. -- Make the icon free-form if you can β€” it helps keep Delta a little more diverse +- Make the icon free-form if you can β€” it helps keep Delta a little more diverse. - You can search for app logos online (they're often found on official websites; avoid icons with non-free licenses) and adapt them. If the original icon is too complex, you can use another recognizable element (an item, a faction icon, etc.) β€” this applies to any complex icon, not just games. - Be sure to double-check that icons are centered and aligned, sized and exported correctly. @@ -35,9 +35,9 @@ We'll be happy to see you participate! ### Rules -- Canvas size must be 192x192px. The template from [Resources](#-resources) is correctly configured, just download it and you should be good to go. +- Canvas size must be 192x192px. The template from [Resources](#resources) is correctly configured, just download it and you should be good to go. - The icon size does not exceed template dimensions. Check out [the visual explanation](./resources/templates/template_tutorial.svg). -- If the original logo is simple and doesn't fill most of the template as a shape (circle, square, etc.), keep the logo size between 72–80px. +- If the original logo is simple and doesn't fill most of the template as a shape (circle, square, etc.), keep the logo size between 73–80px. - The rounded corners of squares and rectangles have a corner radius of 10px. - The template must be properly centered on the canvas. @@ -164,14 +164,14 @@ We'll be happy to see you participate! - If the original icon consists of just one or two letters, you may trace that letter instead of using these fonts. - Fonts can be a little tricky to align/center in different vector editors, which you can mitigate by either converting them to paths or in the case of Figma: the text box trim. -- You can use a custom font if it matches the font from the original icon very closely, for the rest use fonts from [Resources](#-resources-2). +- You can use a custom font if it matches the font from the original icon very closely, for the rest use fonts from [Resources](#resources-2). ### Resources - [Now](https://www.1001fonts.com/now-font.html?text=Delta%20Icons) β€” main Sans-serif font; Now Alt from the same family can be used for an alternate lowercase 'a' letter. - [Aleo](https://www.1001fonts.com/aleo-font.html?text=Delta%20Icons) β€” when Serifs are needed. -# Contributing +# πŸ“₯ Contributing ## Overview @@ -188,13 +188,13 @@ We'll be happy to see you participate! - **Alternative icon** β€” an alternative version of an app icon. Mainly used when the app rebrands: the old icon becomes an alternative (e.g. `app_name_alt_1`), and a new icon takes its place. However, you can create alternative icons for any app without linking them to any ComponentInfo, and they will be standalone. Users can select them via their launcher if it supports that feature. - **Categories** β€” categories within [`app/src/main/assets/drawable.xml`](./app/src/main/assets/drawable.xml) to organize icons. Description of categories: - - `New` β€” new icons for the current release. If [manually](#️-manual) adding icons, you must also add the entry to this category. + - `New` β€” new icons for the current release. If [manually](#manual) adding icons, you must also add the entry to this category. - `Alts` β€” alternative icons. - `Calendar` β€” calendar icons. - `Folders` β€” folder icons. - `Google` β€” Google apps (Chrome, YouTube, etc.). - `System` β€” system icons (Camera, Contacts, Settings, etc.). - - `#` β€” icons whose name starts with a number (e.g. `_2048`). If [manually](#️-manual) adding icons, drawable names that begin with a number must have a leading underscore and be placed in this category. + - `#` β€” icons whose name starts with a number (e.g. `_2048`). If [manually](#manual) adding icons, drawable names that begin with a number must have a leading underscore and be placed in this category. - `A–Z` β€” everything else, sorted by the first letter of the drawable name. ### Rules @@ -207,7 +207,7 @@ Want to help close user requests? Check [`contribs/requests.yml`](./contribs/req If you wish, you can add yourself to [`app/src/main/res/xml/contributors.xml`](./app/src/main/res/xml/contributors.xml) to shine in the app's contributors section! -## Adding / managing icons +## Adding / Managing Icons Your new app icon will have two important identifiers: @@ -221,8 +221,8 @@ Your new app icon will have two important identifiers: So, you made an icon then exported it as `new_icon.png` and `new_icon.svg`. Now you needto select which way to manage icons. Here are two ways: -- [**Automatic**](#-automatic) β€” an automatic and declarative way of managing icons via the file [`contribs/icons.yml`](./contribs/icons.yml) in the repo, processed by scripts and GitHub Actions. This is the recommended approach. -- [**Manual**](#️-manual) β€” this is how icons were managed before [**Auto**](#-auto) was implemented. Directly editing XMLs and placing icon images into the appropriate directories. More control, but also more prone to errors. Try to avoid it unless [**Auto**](#-auto) can't handle what you need. +- [**Automatic**](#automatic) β€” an automatic and declarative way of managing icons via the file [`contribs/icons.yml`](./contribs/icons.yml) in the repo, processed by scripts and GitHub Actions. This is the recommended approach. +- [**Manual**](#manual) β€” this is how icons were managed before [**Automatic**](#automatic) was implemented. Directly editing XMLs and placing icon images into the appropriate directories. More control, but also more prone to errors. Try to avoid it unless [**Automatic**](#automatic) can't handle what you need. ### Automatic @@ -335,7 +335,7 @@ existing_icon: ### Manual -This is how icons were managed before [**Automatic**](#-automatic) was implemented. Avoid it unless [**Automatic**](#-automatic) can't handle what you need. +This is how icons were managed before [**Automatic**](#automatic) was implemented. Avoid it unless [**Automatic**](#automatic) can't handle what you need. There are two `drawable.xml` and two `appfilter.xml` files to edit (stored in [`app/src/main/assets`](./app/src/main/assets) and [`app/src/main/res/xml`](./app/src/main/res/xml)). It's better to edit the XMLs in [`app/src/main/assets`](./app/src/main/assets), then copy them to [`app/src/main/res/xml`](./app/src/main/res/xml) to keep all files identical. You can do it however you want (e.g. editing all files at the same time), just keep them identical. @@ -402,13 +402,13 @@ If an existing icon has been rebranded, don't overwrite it with a new one β€” do The rest of the things are more or less obvious like moving drawable names between categories, renaming, etc. Just ask for help in Discord if something isn't clear. -# Build +# πŸ—οΈ Build ## GitHub Actions > Everything described here must be done in your fork. -### 🏁 Run Workflow +### Run Workflow 1. Go to [Actions β†’ Build FOSS](../../actions/workflows/build_foss.yml) 2. Click on **Run workflow**, optionally mark preferred checkboxes, then click on **Run workflow** From 5304fe6b4392b507ed880ce36603799980c6edef Mon Sep 17 00:00:00 2001 From: flameshikari Date: Wed, 18 Mar 2026 03:43:41 +0500 Subject: [PATCH 11/12] update check_conflicts.yml --- .github/workflows/check_conflicts.yml | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/.github/workflows/check_conflicts.yml b/.github/workflows/check_conflicts.yml index ec916e4556..f19366a9ec 100644 --- a/.github/workflows/check_conflicts.yml +++ b/.github/workflows/check_conflicts.yml @@ -5,19 +5,24 @@ on: workflow_dispatch: pull_request: paths: - - contribs/** + - contribs/icons/** + - contribs/icons.yml + +env: + FORCE_COLOR: 1 + PYTHONUNBUFFERED: 1 jobs: build: name: Check for Conflicts - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 defaults: run: working-directory: resources/scripts steps: - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Setup Python uses: actions/setup-python@v6 @@ -29,6 +34,6 @@ jobs: run: | pip install -r requirements.txt - - name: Add icons + - name: Process icons in dry run run: | - python -u add_icons_wrapper.py + python process_icons.py -d 2>&1 From 4521e84081179560b01fc31b0dcaee0e087d3992 Mon Sep 17 00:00:00 2001 From: flameshikari Date: Wed, 18 Mar 2026 03:50:30 +0500 Subject: [PATCH 12/12] update update_requests.yml --- .github/workflows/update_requests.yml | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/.github/workflows/update_requests.yml b/.github/workflows/update_requests.yml index 86dce31116..a50c002297 100644 --- a/.github/workflows/update_requests.yml +++ b/.github/workflows/update_requests.yml @@ -10,17 +10,20 @@ permissions: on: workflow_dispatch: +env: + PYTHONUNBUFFERED: 1 + jobs: update: name: Update Requests - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 defaults: run: working-directory: resources/scripts steps: - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: token: ${{ secrets.PA_TOKEN }} @@ -34,20 +37,12 @@ jobs: run: | pip install -r requirements.txt - - name: Dump emails - run: | - python -u email_dumper.py \ - -u '${{ secrets.EMAIL_ADDRESS }}' \ - -p '${{ secrets.EMAIL_PASSWORD }}' \ - -r '${{ secrets.EMAIL_FOLDER }}' - - - name: Parse emails - run: | - python -u email_parser.py - - - name: Parse requests + - name: Process emails run: | - python -u requests_parser.py -r + python process_emails.py -p -d --unread \ + --user '${{ secrets.EMAIL_ADDRESS }}' \ + --pass '${{ secrets.EMAIL_PASSWORD }}' \ + --remote '${{ secrets.EMAIL_FOLDER }}' \ - name: Commit changes uses: stefanzweifel/git-auto-commit-action@v7