Compare commits

..

No commits in common. "v4.7.1" and "v4.7-rc.1" have entirely different histories.

46 changed files with 191 additions and 1761 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

View file

@ -1,2 +0,0 @@
.env
__pycache__/

View file

@ -1,57 +0,0 @@
import base64
import secrets
from hashlib import sha256
from typing import Tuple
import mysql.connector as mysql
from Crypto.Cipher import AES
def get_password(
version_name: str,
version_code: int,
db_host: str,
db_user: str,
db_pass: str,
db_name: str,
) -> Tuple[str, bytes]:
db = mysql.connect(
host=db_host,
user=db_user,
password=db_pass,
database=db_name,
auth_plugin="mysql_native_password",
)
print(f"Generating passwords for version {version_name} ({version_code})")
password = base64.b64encode(secrets.token_bytes(16)).decode()
iv = secrets.token_bytes(16)
key = f"{version_name}.{password}.{version_code}"
key = sha256(key.encode()).digest()
data = "ThisIsOurHardWorkPleaseDoNotCopyOrSteal(c)2019.KubaSz"
data = sha256(data.encode()).digest()
data = data + (chr(16) * 16).encode()
aes = AES.new(key=key, mode=AES.MODE_CBC, iv=iv)
app_password = base64.b64encode(aes.encrypt(data)).decode()
c = db.cursor()
c.execute(
"INSERT IGNORE INTO _appPasswords (versionCode, appPassword, password, iv) VALUES (%s, %s, %s, %s);",
(version_code, app_password, password, iv),
)
db.commit()
c = db.cursor()
c.execute(
"SELECT password, iv FROM _appPasswords WHERE versionCode = %s;",
(version_code,),
)
row = c.fetchone()
db.close()
return (row[0], row[1])

View file

@ -1,142 +0,0 @@
import re
import subprocess
import sys
from datetime import datetime
from typing import Tuple
VERSION_NAME_REGEX = r'versionName: "(.+?)"'
VERSION_CODE_REGEX = r"versionCode: ([0-9]+)"
VERSION_NAME_FORMAT = 'versionName: "{}"'
VERSION_CODE_FORMAT = "versionCode: {}"
def get_project_dir() -> str:
project_dir = sys.argv[1]
if project_dir[-1:] == "/" or project_dir[-1:] == "\\":
project_dir = project_dir[:-1]
return project_dir
def read_gradle_version(project_dir: str) -> Tuple[int, str]:
GRADLE_PATH = f"{project_dir}/build.gradle"
with open(GRADLE_PATH, "r") as f:
gradle = f.read()
version_name = re.search(VERSION_NAME_REGEX, gradle).group(1)
version_code = int(re.search(VERSION_CODE_REGEX, gradle).group(1))
return (version_code, version_name)
def write_gradle_version(project_dir: str, version_code: int, version_name: str):
GRADLE_PATH = f"{project_dir}/build.gradle"
with open(GRADLE_PATH, "r") as f:
gradle = f.read()
gradle = re.sub(
VERSION_NAME_REGEX, VERSION_NAME_FORMAT.format(version_name), gradle
)
gradle = re.sub(
VERSION_CODE_REGEX, VERSION_CODE_FORMAT.format(version_code), gradle
)
with open(GRADLE_PATH, "w") as f:
f.write(gradle)
def build_version_code(version_name: str) -> int:
version = version_name.split("+")[0].split("-")
version_base = version[0]
version_suffix = version[1] if len(version) == 2 else ""
base_parts = version_base.split(".")
major = int(base_parts[0]) or 0
minor = int(base_parts[1]) if len(base_parts) > 1 else 0
patch = int(base_parts[2]) if len(base_parts) > 2 else 0
beta = 9
rc = 9
if "dev" in version_suffix:
beta = 0
rc = 0
elif "beta." in version_suffix:
beta = int(version_suffix.split(".")[1])
rc = 0
elif "rc." in version_suffix:
beta = 0
rc = int(version_suffix.split(".")[1])
version_code = beta + rc * 10 + patch * 100 + minor * 10000 + major * 1000000
return version_code
def get_changelog(project_dir: str, format: str) -> Tuple[str, str]:
with open(
f"{project_dir}/app/src/main/assets/pl-changelog.html", "r", encoding="utf-8"
) as f:
changelog = f.read()
title = re.search(r"<h3>(.+?)</h3>", changelog).group(1)
content = re.search(r"(?s)<ul>(.+)</ul>", changelog).group(1).strip()
content = "\n".join(line.strip() for line in content.split("\n"))
if format != "html":
content = content.replace("<li>", "- ")
content = content.replace("<br>", "\n")
if format == "markdown":
content = re.sub(r"<u>(.+?)</u>", "__\\1__", content)
content = re.sub(r"<i>(.+?)</i>", "*\\1*", content)
content = re.sub(r"<b>(.+?)</b>", "**\\1**", content)
content = re.sub(r"</?.+?>", "", content)
return (title, content)
def get_commit_log(project_dir: str, format: str, max_lines: int = None) -> str:
last_tag = (
subprocess.check_output("git describe --tags --abbrev=0".split(" "))
.decode()
.strip()
)
log = subprocess.run(
args=f"git log {last_tag}..HEAD --format=%an%x00%at%x00%h%x00%s%x00%D".split(" "),
cwd=project_dir,
stdout=subprocess.PIPE,
)
log = log.stdout.strip().decode()
commits = [line.split("\x00") for line in log.split("\n")]
if max_lines:
commits = commits[:max_lines]
output = []
valid = False
for commit in commits:
if not commit[0]:
continue
if "origin/" in commit[4]:
valid = True
if not valid:
continue
date = datetime.fromtimestamp(float(commit[1]))
date = date.strftime("%Y-%m-%d %H:%M:%S")
if format == "html":
output.append(f"<li>{commit[3]} <i> - {commit[0]}</i></li>")
elif format == "markdown":
output.append(f"[{date}] {commit[0]}\n {commit[3]}")
elif format == "markdown_full":
output.append(
f"_[{date}] {commit[0]}_\n` `__`{commit[2]}`__ **{commit[3]}**"
)
elif format == "plain":
output.append(f"- {commit[3]}")
if format == "markdown":
output.insert(0, "```")
output.append("```")
return "\n".join(output)

View file

@ -1,69 +0,0 @@
import json
import os
import re
import sys
from datetime import datetime, timedelta
import requests
from _utils import (
get_commit_log,
get_project_dir,
read_gradle_version,
write_gradle_version,
)
if __name__ == "__main__":
if len(sys.argv) < 2:
print("usage: bump_nightly.py <project dir>")
exit(-1)
repo = os.getenv("GITHUB_REPOSITORY")
sha = os.getenv("GITHUB_SHA")
if not repo or not sha:
print("Missing GitHub environment variables.")
exit(-1)
with requests.get(
f"https://api.github.com/repos/{repo}/actions/runs?per_page=5&status=success"
) as r:
data = json.loads(r.text)
runs = [run for run in data["workflow_runs"] if run["head_sha"] == sha]
if runs:
print("::set-output name=hasNewChanges::false")
exit(0)
print("::set-output name=hasNewChanges::true")
project_dir = get_project_dir()
(version_code, version_name) = read_gradle_version(project_dir)
version_name = version_name.split("+")[0]
date = datetime.now()
if date.hour > 6:
version_name += "+daily." + date.strftime("%Y%m%d-%H%M")
else:
date -= timedelta(days=1)
version_name += "+nightly." + date.strftime("%Y%m%d")
print("::set-output name=appVersionName::" + version_name)
print("::set-output name=appVersionCode::" + str(version_code))
write_gradle_version(project_dir, version_code, version_name)
commit_log = get_commit_log(project_dir, format="html", max_lines=10)
with open(
f"{project_dir}/app/src/main/assets/pl-changelog.html", "r", encoding="utf-8"
) as f:
changelog = f.read()
changelog = re.sub(r"<h3>(.+?)</h3>", f"<h3>{version_name}</h3>", changelog)
changelog = re.sub(r"(?s)<ul>(.+)</ul>", f"<ul>\n{commit_log}\n</ul>", changelog)
with open(
f"{project_dir}/app/src/main/assets/pl-changelog.html", "w", encoding="utf-8"
) as f:
f.write(changelog)

View file

@ -1,41 +0,0 @@
import os
from dotenv import load_dotenv
from _get_password import get_password
from _utils import build_version_code, write_gradle_version
from sign import sign
if __name__ == "__main__":
version_name = input("Enter version name: ")
version_code = build_version_code(version_name)
print(f"Bumping version to {version_name} ({version_code})")
project_dir = "../.."
load_dotenv()
DB_HOST = os.getenv("DB_HOST")
DB_USER = os.getenv("DB_USER")
DB_PASS = os.getenv("DB_PASS")
DB_NAME = os.getenv("DB_NAME")
write_gradle_version(project_dir, version_code, version_name)
(password, iv) = get_password(
version_name, version_code, DB_HOST, DB_USER, DB_PASS, DB_NAME
)
sign(project_dir, version_name, version_code, password, iv, commit=False)
print("Writing mock passwords")
os.chdir(project_dir)
os.system(
"sed -i -E 's/\/\*([0-9a-f]{2} ?){16}\*\//\/*secret password - removed for source code publication*\//g' app/src/main/cpp/szkolny-signing.cpp"
)
os.system(
"sed -i -E 's/\\t0x.., 0x(.)., 0x.(.), 0x.(.), 0x.., 0x.., 0x.., 0x.(.), 0x.., 0x.(.), 0x(.)., 0x(.)., 0x.., 0x.., 0x.., 0x.(.)/\\t0x\\3\\6, 0x\\7\\4, 0x\\1\\8, 0x\\2\\5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff /g' app/src/main/cpp/szkolny-signing.cpp"
)
os.system(
"sed -i -E 's/param1\..(.).(.).(.).(.)..(.)..(.)..(.)..(.).../param1.MTIzNDU2Nzg5MD\\5\\2\\7\\6\\1\\3\\4\8==/g' app/src/main/java/pl/szczodrzynski/edziennik/data/api/szkolny/interceptor/Signing.kt"
)
input("Press any key to finish")

View file

@ -1,72 +0,0 @@
import os
import sys
from _utils import get_changelog, get_commit_log, get_project_dir, read_gradle_version
if __name__ == "__main__":
if len(sys.argv) < 2:
print("usage: extract_changelogs.py <project dir>")
exit(-1)
project_dir = get_project_dir()
(version_code, version_name) = read_gradle_version(project_dir)
print("::set-output name=appVersionName::" + version_name)
print("::set-output name=appVersionCode::" + str(version_code))
dir = f"{project_dir}/app/release/whatsnew-{version_name}/"
os.makedirs(dir, exist_ok=True)
print("::set-output name=changelogDir::" + dir)
(title, changelog) = get_changelog(project_dir, format="plain")
# plain text changelog - Firebase App Distribution
with open(dir + "whatsnew-titled.txt", "w", encoding="utf-8") as f:
f.write(title)
f.write("\n")
f.write(changelog)
print("::set-output name=changelogPlainTitledFile::" + dir + "whatsnew-titled.txt")
print("::set-output name=changelogTitle::" + title)
# plain text changelog, max 500 chars - Google Play
with open(dir + "whatsnew-pl-PL", "w", encoding="utf-8") as f:
changelog_lines = changelog.split("\n")
changelog = ""
for line in changelog_lines:
if len(changelog) + len(line) < 500:
changelog += "\n" + line
changelog = changelog.strip()
f.write(changelog)
print("::set-output name=changelogPlainFile::" + dir + "whatsnew-pl-PL")
# markdown changelog - Discord webhook
(_, changelog) = get_changelog(project_dir, format="markdown")
with open(dir + "whatsnew.md", "w", encoding="utf-8") as f:
f.write(changelog)
print("::set-output name=changelogMarkdownFile::" + dir + "whatsnew.md")
# html changelog - version info in DB
(_, changelog) = get_changelog(project_dir, format="html")
with open(dir + "whatsnew.html", "w", encoding="utf-8") as f:
f.write(changelog)
print("::set-output name=changelogHtmlFile::" + dir + "whatsnew.html")
changelog = get_commit_log(project_dir, format="plain", max_lines=10)
with open(dir + "commit_log.txt", "w", encoding="utf-8") as f:
f.write(changelog)
print("::set-output name=commitLogPlainFile::" + dir + "commit_log.txt")
changelog = get_commit_log(project_dir, format="markdown", max_lines=10)
with open(dir + "commit_log.md", "w", encoding="utf-8") as f:
f.write(changelog)
print("::set-output name=commitLogMarkdownFile::" + dir + "commit_log.md")
changelog = get_commit_log(project_dir, format="html", max_lines=10)
with open(dir + "commit_log.html", "w", encoding="utf-8") as f:
f.write(changelog)
print("::set-output name=commitLogHtmlFile::" + dir + "commit_log.html")

View file

@ -1,26 +0,0 @@
import glob
import os
import sys
from _utils import get_project_dir
if __name__ == "__main__":
if len(sys.argv) < 2:
print("usage: rename_artifacts.py <project dir>")
exit(-1)
project_dir = get_project_dir()
files = glob.glob(f"{project_dir}/app/release/*.*")
for file in files:
file_relative = file.replace(os.getenv("GITHUB_WORKSPACE") + "/", "")
if "-aligned.apk" in file:
os.unlink(file)
elif "-signed.apk" in file:
new_file = file.replace("-signed.apk", ".apk")
if os.path.isfile(new_file):
os.unlink(new_file)
os.rename(file, new_file)
elif ".apk" in file or ".aab" in file:
print("::set-output name=signedReleaseFile::" + file)
print("::set-output name=signedReleaseFileRelative::" + file_relative)

View file

@ -1,122 +0,0 @@
import glob
import os
import sys
from datetime import datetime
from time import time
import mysql.connector as mysql
from dotenv import load_dotenv
from _utils import get_changelog, get_commit_log, get_project_dir, read_gradle_version
def save_version(
project_dir: str,
db_host: str,
db_user: str,
db_pass: str,
db_name: str,
apk_server_release: str,
apk_server_nightly: str,
):
db = mysql.connect(
host=db_host,
user=db_user,
password=db_pass,
database=db_name,
auth_plugin="mysql_native_password",
)
(version_code, version_name) = read_gradle_version(project_dir)
(_, changelog) = get_changelog(project_dir, format="html")
types = ["dev", "beta", "nightly", "daily", "rc", "release"]
build_type = [x for x in types if x in version_name]
build_type = build_type[0] if build_type else "release"
if "+nightly." in version_name or "+daily." in version_name:
changelog = get_commit_log(project_dir, format="html")
build_type = "nightly"
elif "-dev" in version_name:
build_type = "dev"
elif "-beta." in version_name:
build_type = "beta"
elif "-rc." in version_name:
build_type = "rc"
build_date = int(time())
apk_name = None
bundle_name_play = None
files = glob.glob(f"{project_dir}/app/release/*.*")
output_apk = f"Edziennik_{version_name}_official.apk"
output_aab_play = f"Edziennik_{version_name}_play.aab"
for file in files:
if output_apk in file:
build_date = int(os.stat(file).st_mtime)
apk_name = output_apk
if output_aab_play in file:
build_date = int(os.stat(file).st_mtime)
bundle_name_play = output_aab_play
build_date = datetime.fromtimestamp(build_date).strftime("%Y-%m-%d %H:%M:%S")
if build_type in ["nightly", "daily"]:
download_url = apk_server_nightly + apk_name if apk_name else None
else:
download_url = apk_server_release + apk_name if apk_name else None
cols = [
"versionCode",
"versionName",
"releaseDate",
"releaseNotes",
"releaseType",
"downloadUrl",
"apkName",
"bundleNamePlay",
]
updated = {
"versionCode": version_code,
"downloadUrl": download_url,
"apkName": apk_name,
"bundleNamePlay": bundle_name_play,
}
values = [
version_code,
version_name,
build_date,
changelog,
build_type,
download_url,
apk_name,
bundle_name_play,
]
values.extend(val for val in updated.values() if val)
updated = ", ".join(f"{col} = %s" for (col, val) in updated.items() if val)
sql = f"INSERT INTO updates ({', '.join(cols)}) VALUES ({'%s, ' * (len(cols) - 1)}%s) ON DUPLICATE KEY UPDATE {updated};"
c = db.cursor()
c.execute(sql, tuple(values))
db.commit()
if __name__ == "__main__":
if len(sys.argv) < 2:
print("usage: save_version.py <project dir>")
exit(-1)
project_dir = get_project_dir()
load_dotenv()
DB_HOST = os.getenv("DB_HOST")
DB_USER = os.getenv("DB_USER")
DB_PASS = os.getenv("DB_PASS")
DB_NAME = os.getenv("DB_NAME")
APK_SERVER_RELEASE = os.getenv("APK_SERVER_RELEASE")
APK_SERVER_NIGHTLY = os.getenv("APK_SERVER_NIGHTLY")
save_version(project_dir, DB_HOST, DB_USER, DB_PASS, DB_NAME, APK_SERVER_RELEASE, APK_SERVER_NIGHTLY)

84
.github/utils/sign.py vendored
View file

@ -1,84 +0,0 @@
import os
import re
import sys
from dotenv import load_dotenv
from _get_password import get_password
from _utils import get_project_dir, read_gradle_version
def sign(
project_dir: str,
version_name: str,
version_code: int,
password: str,
iv: bytes,
commit: bool = False,
):
SIGNING_PATH = f"{project_dir}/app/src/main/java/pl/szczodrzynski/edziennik/data/api/szkolny/interceptor/Signing.kt"
CPP_PATH = f"{project_dir}/app/src/main/cpp/szkolny-signing.cpp"
with open(SIGNING_PATH, "r") as f:
signing = f.read()
with open(CPP_PATH, "r") as f:
cpp = f.read()
SIGNING_REGEX = r"\$param1\..*\.\$param2"
CPP_REGEX = r"(?s)/\*.+?toys AES_IV\[16\] = {.+?};"
SIGNING_FORMAT = "$param1.{}.$param2"
CPP_FORMAT = "/*{}*/\nstatic toys AES_IV[16] = {{\n\t{} }};"
print(f"Writing passwords for version {version_name} ({version_code})")
iv_hex = " ".join(["{:02x}".format(x) for x in iv])
iv_cpp = ", ".join(["0x{:02x}".format(x) for x in iv])
signing = re.sub(SIGNING_REGEX, SIGNING_FORMAT.format(password), signing)
cpp = re.sub(CPP_REGEX, CPP_FORMAT.format(iv_hex, iv_cpp), cpp)
with open(SIGNING_PATH, "w") as f:
f.write(signing)
with open(CPP_PATH, "w") as f:
f.write(cpp)
if commit:
os.chdir(project_dir)
os.system("git add .")
os.system(
f'git commit -m "[{version_name}] Update build.gradle, signing and changelog."'
)
if __name__ == "__main__":
if len(sys.argv) < 2:
print("usage: sign.py <project dir> [commit]")
exit(-1)
project_dir = get_project_dir()
load_dotenv()
DB_HOST = os.getenv("DB_HOST")
DB_USER = os.getenv("DB_USER")
DB_PASS = os.getenv("DB_PASS")
DB_NAME = os.getenv("DB_NAME")
(version_code, version_name) = read_gradle_version(project_dir)
(password, iv) = get_password(
version_name, version_code, DB_HOST, DB_USER, DB_PASS, DB_NAME
)
print("::set-output name=appVersionName::" + version_name)
print("::set-output name=appVersionCode::" + str(version_code))
sign(
project_dir,
version_name,
version_code,
password,
iv,
commit="commit" in sys.argv,
)

View file

@ -1,118 +0,0 @@
import os
import sys
from datetime import datetime
import requests
from dotenv import load_dotenv
from _utils import get_changelog, get_commit_log, get_project_dir, read_gradle_version
def post_webhook(
project_dir: str,
apk_file: str,
apk_server_release: str,
apk_server_nightly: str,
webhook_release: str,
webhook_testing: str,
):
(_, version_name) = read_gradle_version(project_dir)
types = ["dev", "beta", "nightly", "daily", "rc", "release"]
build_type = [x for x in types if x in version_name]
build_type = build_type[0] if build_type else None
testing = ["dev", "beta", "nightly", "daily"]
testing = build_type in testing
apk_name = os.path.basename(apk_file)
if build_type in ["nightly", "daily"]:
download_url = apk_server_nightly + apk_name
else:
download_url = apk_server_release + apk_name
if testing:
build_date = int(os.stat(apk_file).st_mtime)
if build_date:
build_date = datetime.fromtimestamp(build_date).strftime("%Y-%m-%d %H:%M")
# untagged release, get commit log
if build_type in ["nightly", "daily"]:
changelog = get_commit_log(project_dir, format="markdown", max_lines=5)
else:
changelog = get_changelog(project_dir, format="markdown")
webhook = get_webhook_testing(
version_name, build_type, changelog, download_url, build_date
)
requests.post(url=webhook_testing, json=webhook)
else:
changelog = get_changelog(project_dir, format="markdown")
webhook = get_webhook_release(changelog, download_url)
requests.post(url=webhook_release, json=webhook)
def get_webhook_release(changelog: str, download_url: str):
(title, content) = changelog
return {"content": f"__**{title}**__\n{content}\n{download_url}"}
def get_webhook_testing(
version_name: str,
build_type: str,
changelog: str,
download_url: str,
build_date: str,
):
return {
"embeds": [
{
"title": f"Nowa wersja {build_type} aplikacji Szkolny.eu",
"description": f"Dostępna jest nowa wersja testowa **{build_type}**.",
"color": 2201331,
"fields": [
{
"name": f"Wersja `{version_name}`",
"value": f"[Pobierz .APK]({download_url})"
if download_url
else "*Pobieranie niedostępne*",
"inline": False,
},
{
"name": "Data kompilacji",
"value": build_date or "-",
"inline": False,
},
{
"name": "Ostatnie zmiany",
"value": changelog or "-",
"inline": False,
},
],
}
]
}
if __name__ == "__main__":
if len(sys.argv) < 2:
print("usage: webhook_discord.py <project dir>")
exit(-1)
project_dir = get_project_dir()
load_dotenv()
APK_FILE = os.getenv("APK_FILE")
APK_SERVER_RELEASE = os.getenv("APK_SERVER_RELEASE")
APK_SERVER_NIGHTLY = os.getenv("APK_SERVER_NIGHTLY")
WEBHOOK_RELEASE = os.getenv("WEBHOOK_RELEASE")
WEBHOOK_TESTING = os.getenv("WEBHOOK_TESTING")
post_webhook(
project_dir,
APK_FILE,
APK_SERVER_RELEASE,
APK_SERVER_NIGHTLY,
WEBHOOK_RELEASE,
WEBHOOK_TESTING,
)

View file

@ -1,151 +0,0 @@
name: Nightly build
on:
schedule:
# 23:30 UTC, 0:30 or 1:30 CET/CEST
- cron: "30 23 * * *"
workflow_dispatch:
jobs:
prepare:
name: Prepare build environment
runs-on: self-hosted
outputs:
hasNewChanges: ${{ steps.nightly.outputs.hasNewChanges }}
steps:
- name: Checkout repository
uses: actions/checkout@v2
with:
fetch-depth: 0
clean: false
- name: Set executable permissions to gradlew
run: chmod +x ./gradlew
- name: Setup Python
uses: actions/setup-python@v2
- name: Install packages
uses: BSFishy/pip-action@v1
with:
packages: |
python-dotenv
pycryptodome
mysql-connector-python
requests
- name: Bump nightly version
id: nightly
run: python $GITHUB_WORKSPACE/.github/utils/bump_nightly.py $GITHUB_WORKSPACE
- name: Write signing passwords
if: steps.nightly.outputs.hasNewChanges
env:
DB_HOST: ${{ secrets.DB_HOST }}
DB_USER: ${{ secrets.DB_USER }}
DB_PASS: ${{ secrets.DB_PASS }}
DB_NAME: ${{ secrets.DB_NAME }}
run: python $GITHUB_WORKSPACE/.github/utils/sign.py $GITHUB_WORKSPACE commit
build:
name: Build APK
runs-on: self-hosted
needs:
- prepare
if: ${{ needs.prepare.outputs.hasNewChanges == 'true' }}
outputs:
androidHome: ${{ env.ANDROID_HOME }}
androidSdkRoot: ${{ env.ANDROID_SDK_ROOT }}
steps:
- name: Setup JDK 1.8
uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Setup Android SDK
uses: android-actions/setup-android@v2
- name: Clean build artifacts
run: |
rm -rf app/release/*
rm -rf app/build/outputs/apk/*
rm -rf app/build/outputs/bundle/*
- name: Assemble official release with Gradle
run: ./gradlew assembleOfficialRelease
sign:
name: Sign APK
runs-on: self-hosted
needs:
- build
outputs:
signedReleaseFile: ${{ steps.artifacts.outputs.signedReleaseFile }}
signedReleaseFileRelative: ${{ steps.artifacts.outputs.signedReleaseFileRelative }}
steps:
- name: Sign build artifacts
id: sign_app
uses: r0adkll/sign-android-release@v1
with:
releaseDirectory: app/release
signingKeyBase64: ${{ secrets.KEY_STORE }}
alias: ${{ secrets.KEY_ALIAS }}
keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }}
keyPassword: ${{ secrets.KEY_ALIAS_PASSWORD }}
env:
ANDROID_HOME: ${{ needs.build.outputs.androidHome }}
ANDROID_SDK_ROOT: ${{ needs.build.outputs.androidSdkRoot }}
BUILD_TOOLS_VERSION: "30.0.2"
- name: Rename signed artifacts
id: artifacts
run: python $GITHUB_WORKSPACE/.github/utils/rename_artifacts.py $GITHUB_WORKSPACE
publish:
name: Publish APK
runs-on: self-hosted
needs:
- sign
steps:
- name: Setup Python
uses: actions/setup-python@v2
- name: Extract changelogs
id: changelog
run: python $GITHUB_WORKSPACE/.github/utils/extract_changelogs.py $GITHUB_WORKSPACE
- name: Upload APK to SFTP
uses: easingthemes/ssh-deploy@v2.1.6
env:
REMOTE_HOST: ${{ secrets.SSH_IP }}
REMOTE_USER: ${{ secrets.SSH_USERNAME }}
SSH_PRIVATE_KEY: ${{ secrets.SSH_KEY }}
SOURCE: ${{ needs.sign.outputs.signedReleaseFileRelative }}
TARGET: ${{ secrets.SSH_PATH_NIGHTLY }}
- name: Save version metadata
env:
DB_HOST: ${{ secrets.DB_HOST }}
DB_USER: ${{ secrets.DB_USER }}
DB_PASS: ${{ secrets.DB_PASS }}
DB_NAME: ${{ secrets.DB_NAME }}
APK_SERVER_RELEASE: ${{ secrets.APK_SERVER_RELEASE }}
APK_SERVER_NIGHTLY: ${{ secrets.APK_SERVER_NIGHTLY }}
run: python $GITHUB_WORKSPACE/.github/utils/save_version.py $GITHUB_WORKSPACE
- name: Distribute to App Distribution
uses: wzieba/Firebase-Distribution-Github-Action@v1
with:
appId: ${{ secrets.FIREBASE_APP_ID }}
token: ${{ secrets.FIREBASE_TOKEN }}
groups: ${{ secrets.FIREBASE_GROUPS_NIGHTLY }}
file: ${{ needs.sign.outputs.signedReleaseFile }}
releaseNotesFile: ${{ steps.changelog.outputs.commitLogPlainFile }}
- name: Post Discord webhook
env:
APK_FILE: ${{ needs.sign.outputs.signedReleaseFile }}
APK_SERVER_RELEASE: ${{ secrets.APK_SERVER_RELEASE }}
APK_SERVER_NIGHTLY: ${{ secrets.APK_SERVER_NIGHTLY }}
WEBHOOK_RELEASE: ${{ secrets.WEBHOOK_RELEASE }}
WEBHOOK_TESTING: ${{ secrets.WEBHOOK_TESTING }}
run: python $GITHUB_WORKSPACE/.github/utils/webhook_discord.py $GITHUB_WORKSPACE
- name: Upload workflow artifact
uses: actions/upload-artifact@v2
if: true
with:
name: ${{ steps.changelog.outputs.appVersionName }}
path: |
app/release/whatsnew*/
app/release/*.apk
app/release/*.aab
app/release/*.json
app/release/*.txt

View file

@ -1,128 +0,0 @@
name: Release build - Google Play [AAB]
on:
push:
branches:
- "master"
jobs:
prepare:
name: Prepare build environment
runs-on: self-hosted
steps:
- name: Checkout repository
uses: actions/checkout@v2
with:
fetch-depth: 0
clean: false
- name: Set executable permissions to gradlew
run: chmod +x ./gradlew
- name: Setup Python
uses: actions/setup-python@v2
- name: Install packages
uses: BSFishy/pip-action@v1
with:
packages: |
python-dotenv
pycryptodome
mysql-connector-python
requests
- name: Write signing passwords
env:
DB_HOST: ${{ secrets.DB_HOST }}
DB_USER: ${{ secrets.DB_USER }}
DB_PASS: ${{ secrets.DB_PASS }}
DB_NAME: ${{ secrets.DB_NAME }}
run: python $GITHUB_WORKSPACE/.github/utils/sign.py $GITHUB_WORKSPACE commit
build:
name: Build App Bundle
runs-on: self-hosted
needs:
- prepare
outputs:
androidHome: ${{ env.ANDROID_HOME }}
androidSdkRoot: ${{ env.ANDROID_SDK_ROOT }}
steps:
- name: Setup JDK 1.8
uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Setup Android SDK
uses: android-actions/setup-android@v2
- name: Clean build artifacts
run: |
rm -rf app/release/*
rm -rf app/build/outputs/apk/*
rm -rf app/build/outputs/bundle/*
- name: Bundle play release with Gradle
run: ./gradlew bundlePlayRelease
sign:
name: Sign App Bundle
runs-on: self-hosted
needs:
- build
outputs:
signedReleaseFile: ${{ steps.artifacts.outputs.signedReleaseFile }}
signedReleaseFileRelative: ${{ steps.artifacts.outputs.signedReleaseFileRelative }}
steps:
- name: Sign build artifacts
id: sign_app
uses: r0adkll/sign-android-release@v1
with:
releaseDirectory: app/release
signingKeyBase64: ${{ secrets.KEY_STORE }}
alias: ${{ secrets.KEY_ALIAS }}
keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }}
keyPassword: ${{ secrets.KEY_ALIAS_PASSWORD }}
env:
ANDROID_HOME: ${{ needs.build.outputs.androidHome }}
ANDROID_SDK_ROOT: ${{ needs.build.outputs.androidSdkRoot }}
BUILD_TOOLS_VERSION: "30.0.2"
- name: Rename signed artifacts
id: artifacts
run: python $GITHUB_WORKSPACE/.github/utils/rename_artifacts.py $GITHUB_WORKSPACE
publish:
name: Publish App Bundle
runs-on: self-hosted
needs:
- sign
steps:
- name: Setup Python
uses: actions/setup-python@v2
- name: Extract changelogs
id: changelog
run: python $GITHUB_WORKSPACE/.github/utils/extract_changelogs.py $GITHUB_WORKSPACE
- name: Save version metadata
env:
DB_HOST: ${{ secrets.DB_HOST }}
DB_USER: ${{ secrets.DB_USER }}
DB_PASS: ${{ secrets.DB_PASS }}
DB_NAME: ${{ secrets.DB_NAME }}
APK_SERVER_RELEASE: ${{ secrets.APK_SERVER_RELEASE }}
APK_SERVER_NIGHTLY: ${{ secrets.APK_SERVER_NIGHTLY }}
run: python $GITHUB_WORKSPACE/.github/utils/save_version.py $GITHUB_WORKSPACE
- name: Publish AAB to Google Play
uses: r0adkll/upload-google-play@v1
if: ${{ endsWith(needs.sign.outputs.signedReleaseFile, '.aab') }}
with:
serviceAccountJsonPlainText: ${{ secrets.PLAY_SERVICE_ACCOUNT_JSON }}
packageName: pl.szczodrzynski.edziennik
releaseFile: ${{ needs.sign.outputs.signedReleaseFile }}
releaseName: ${{ steps.changelog.outputs.appVersionName }}
track: ${{ secrets.PLAY_RELEASE_TRACK }}
whatsNewDirectory: ${{ steps.changelog.outputs.changelogDir }}
- name: Upload workflow artifact
uses: actions/upload-artifact@v2
if: always()
with:
name: ${{ steps.changelog.outputs.appVersionName }}
path: |
app/release/whatsnew*/
app/release/*.apk
app/release/*.aab
app/release/*.json
app/release/*.txt

View file

@ -1,151 +0,0 @@
name: Release build - official
on:
push:
tags:
- "*"
jobs:
prepare:
name: Prepare build environment
runs-on: self-hosted
steps:
- name: Checkout repository
uses: actions/checkout@v2
with:
fetch-depth: 0
clean: false
- name: Set executable permissions to gradlew
run: chmod +x ./gradlew
- name: Setup Python
uses: actions/setup-python@v2
- name: Install packages
uses: BSFishy/pip-action@v1
with:
packages: |
python-dotenv
pycryptodome
mysql-connector-python
requests
- name: Write signing passwords
env:
DB_HOST: ${{ secrets.DB_HOST }}
DB_USER: ${{ secrets.DB_USER }}
DB_PASS: ${{ secrets.DB_PASS }}
DB_NAME: ${{ secrets.DB_NAME }}
run: python $GITHUB_WORKSPACE/.github/utils/sign.py $GITHUB_WORKSPACE commit
build:
name: Build APK
runs-on: self-hosted
needs:
- prepare
outputs:
androidHome: ${{ env.ANDROID_HOME }}
androidSdkRoot: ${{ env.ANDROID_SDK_ROOT }}
steps:
- name: Setup JDK 1.8
uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Setup Android SDK
uses: android-actions/setup-android@v2
- name: Clean build artifacts
run: |
rm -rf app/release/*
rm -rf app/build/outputs/apk/*
rm -rf app/build/outputs/bundle/*
- name: Assemble official release with Gradle
run: ./gradlew assembleOfficialRelease
sign:
name: Sign APK
runs-on: self-hosted
needs:
- build
outputs:
signedReleaseFile: ${{ steps.artifacts.outputs.signedReleaseFile }}
signedReleaseFileRelative: ${{ steps.artifacts.outputs.signedReleaseFileRelative }}
steps:
- name: Sign build artifacts
id: sign_app
uses: r0adkll/sign-android-release@v1
with:
releaseDirectory: app/release
signingKeyBase64: ${{ secrets.KEY_STORE }}
alias: ${{ secrets.KEY_ALIAS }}
keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }}
keyPassword: ${{ secrets.KEY_ALIAS_PASSWORD }}
env:
ANDROID_HOME: ${{ needs.build.outputs.androidHome }}
ANDROID_SDK_ROOT: ${{ needs.build.outputs.androidSdkRoot }}
BUILD_TOOLS_VERSION: "30.0.2"
- name: Rename signed artifacts
id: artifacts
run: python $GITHUB_WORKSPACE/.github/utils/rename_artifacts.py $GITHUB_WORKSPACE
publish:
name: Publish APK
runs-on: self-hosted
needs:
- sign
steps:
- name: Setup Python
uses: actions/setup-python@v2
- name: Extract changelogs
id: changelog
run: python $GITHUB_WORKSPACE/.github/utils/extract_changelogs.py $GITHUB_WORKSPACE
- name: Upload APK to SFTP
uses: easingthemes/ssh-deploy@v2.1.6
env:
REMOTE_HOST: ${{ secrets.SSH_IP }}
REMOTE_USER: ${{ secrets.SSH_USERNAME }}
SSH_PRIVATE_KEY: ${{ secrets.SSH_KEY }}
SOURCE: ${{ needs.sign.outputs.signedReleaseFileRelative }}
TARGET: ${{ secrets.SSH_PATH_RELEASE }}
- name: Save version metadata
env:
DB_HOST: ${{ secrets.DB_HOST }}
DB_USER: ${{ secrets.DB_USER }}
DB_PASS: ${{ secrets.DB_PASS }}
DB_NAME: ${{ secrets.DB_NAME }}
APK_SERVER_RELEASE: ${{ secrets.APK_SERVER_RELEASE }}
APK_SERVER_NIGHTLY: ${{ secrets.APK_SERVER_NIGHTLY }}
run: python $GITHUB_WORKSPACE/.github/utils/save_version.py $GITHUB_WORKSPACE
- name: Distribute to App Distribution
uses: wzieba/Firebase-Distribution-Github-Action@v1
with:
appId: ${{ secrets.FIREBASE_APP_ID }}
token: ${{ secrets.FIREBASE_TOKEN }}
groups: ${{ secrets.FIREBASE_GROUPS_RELEASE }}
file: ${{ needs.sign.outputs.signedReleaseFile }}
releaseNotesFile: ${{ steps.changelog.outputs.changelogPlainTitledFile }}
- name: Release on GitHub
uses: softprops/action-gh-release@v1
with:
name: ${{ steps.changelog.outputs.changelogTitle }}
body_path: ${{ steps.changelog.outputs.changelogMarkdownFile }}
files: ${{ needs.sign.outputs.signedReleaseFile }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Post Discord webhook
env:
APK_FILE: ${{ needs.sign.outputs.signedReleaseFile }}
APK_SERVER_RELEASE: ${{ secrets.APK_SERVER_RELEASE }}
APK_SERVER_NIGHTLY: ${{ secrets.APK_SERVER_NIGHTLY }}
WEBHOOK_RELEASE: ${{ secrets.WEBHOOK_RELEASE }}
WEBHOOK_TESTING: ${{ secrets.WEBHOOK_TESTING }}
run: python $GITHUB_WORKSPACE/.github/utils/webhook_discord.py $GITHUB_WORKSPACE
- name: Upload workflow artifact
uses: actions/upload-artifact@v2
if: true
with:
name: ${{ steps.changelog.outputs.appVersionName }}
path: |
app/release/whatsnew*/
app/release/*.apk
app/release/*.aab
app/release/*.json
app/release/*.txt

View file

@ -15,7 +15,6 @@
<match> <match>
<AND> <AND>
<NAME>xmlns:android</NAME> <NAME>xmlns:android</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE> <XML_NAMESPACE>^$</XML_NAMESPACE>
</AND> </AND>
</match> </match>
@ -26,7 +25,6 @@
<match> <match>
<AND> <AND>
<NAME>xmlns:.*</NAME> <NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE> <XML_NAMESPACE>^$</XML_NAMESPACE>
</AND> </AND>
</match> </match>
@ -38,7 +36,6 @@
<match> <match>
<AND> <AND>
<NAME>.*:id</NAME> <NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE> <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND> </AND>
</match> </match>
@ -49,7 +46,6 @@
<match> <match>
<AND> <AND>
<NAME>.*:name</NAME> <NAME>.*:name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE> <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND> </AND>
</match> </match>
@ -60,7 +56,6 @@
<match> <match>
<AND> <AND>
<NAME>name</NAME> <NAME>name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE> <XML_NAMESPACE>^$</XML_NAMESPACE>
</AND> </AND>
</match> </match>
@ -71,7 +66,6 @@
<match> <match>
<AND> <AND>
<NAME>style</NAME> <NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE> <XML_NAMESPACE>^$</XML_NAMESPACE>
</AND> </AND>
</match> </match>
@ -82,7 +76,6 @@
<match> <match>
<AND> <AND>
<NAME>.*</NAME> <NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE> <XML_NAMESPACE>^$</XML_NAMESPACE>
</AND> </AND>
</match> </match>
@ -94,7 +87,6 @@
<match> <match>
<AND> <AND>
<NAME>.*</NAME> <NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE> <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND> </AND>
</match> </match>
@ -106,7 +98,6 @@
<match> <match>
<AND> <AND>
<NAME>.*</NAME> <NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE> <XML_NAMESPACE>.*</XML_NAMESPACE>
</AND> </AND>
</match> </match>

View file

@ -1,6 +1,8 @@
<div align="center"> # Szkolny.eu
![Readme Banner](.github/readme-banner.png) Nieoficjalna aplikacja do obsługi najpopularniejszych dzienników elektronicznych w Polsce.
<div align="center">
[![Discord](https://img.shields.io/discord/619178050562686988?color=%237289DA&logo=discord&logoColor=white&style=for-the-badge)](https://szkolny.eu/discord) [![Discord](https://img.shields.io/discord/619178050562686988?color=%237289DA&logo=discord&logoColor=white&style=for-the-badge)](https://szkolny.eu/discord)
[![Oficjalna strona](https://img.shields.io/badge/-website-orange?style=for-the-badge&logo=internet-explorer&logoColor=white)](https://szkolny.eu/) [![Oficjalna strona](https://img.shields.io/badge/-website-orange?style=for-the-badge&logo=internet-explorer&logoColor=white)](https://szkolny.eu/)
@ -10,15 +12,11 @@
[![Najnowsza wersja](https://img.shields.io/github/v/release/szkolny-eu/szkolny-android?color=%2344CC11&include_prereleases&logo=github&logoColor=white&style=for-the-badge)](https://github.com/szkolny-eu/szkolny-android/releases/latest) [![Najnowsza wersja](https://img.shields.io/github/v/release/szkolny-eu/szkolny-android?color=%2344CC11&include_prereleases&logo=github&logoColor=white&style=for-the-badge)](https://github.com/szkolny-eu/szkolny-android/releases/latest)
![Licencja](https://img.shields.io/github/license/szkolny-eu/szkolny-android?color=blue&logo=github&logoColor=white&style=for-the-badge) ![Licencja](https://img.shields.io/github/license/szkolny-eu/szkolny-android?color=blue&logo=github&logoColor=white&style=for-the-badge)
[![Release build](https://img.shields.io/github/workflow/status/szkolny-eu/szkolny-android/Release%20build%20-%20official?label=Release&logo=github-actions&logoColor=white&style=for-the-badge)](https://github.com/szkolny-eu/szkolny-android/actions/workflows/build-release-apk.yml)
[![Play build](https://img.shields.io/github/workflow/status/szkolny-eu/szkolny-android/Release%20build%20-%20Google%20Play%20%5BAAB%5D?label=Play&logo=google-play&logoColor=white&style=for-the-badge)](https://github.com/szkolny-eu/szkolny-android/actions/workflows/build-release-aab-play.yml)
[![Nightly build](https://img.shields.io/github/workflow/status/szkolny-eu/szkolny-android/Nightly%20build?label=Nightly&logo=github-actions&logoColor=white&style=for-the-badge)](https://github.com/szkolny-eu/szkolny-android/actions/workflows/build-nightly-apk.yml)
</div> </div>
## Ważna informacja ## Ważna informacja
Jak zapewne już wiecie, we wrześniu 2020 r. **firma Librus zabroniła nam** publikowania w sklepie Google Play naszej aplikacji z obsługą dziennika Librus&reg; Synergia. Prowadziliśmy rozmowy, aby **umożliwić Wam wygodny, bezpłatny dostęp do Waszych ocen, wiadomości, zadań domowych**, jednak oczekiwania firmy Librus zdecydowanie przekroczyły wszelkie nasze możliwości finansowe. Mając na uwadze powyższe względy, zdecydowaliśmy się opublikować kod źródłowy aplikacji Szkolny.eu. Liczymy, że dzięki temu aplikacja będzie mogła dalej funkcjonować, być rozwijana, pomagając Wam w czasie zdalnego nauczania i przez kolejne lata nauki. Jak zapewne już wiecie, we wrześniu 2020r. **firma Librus zabroniła nam** publikowania w sklepie Google Play naszej aplikacji z obsługą dziennika Librus&reg; Synergia. Prowadziliśmy rozmowy, aby **umożliwić Wam wygodny, bezpłatny dostęp do Waszych ocen, wiadomości, zadań domowych**, jednak oczekiwania firmy Librus zdecydowanie przekroczyły wszelkie nasze możliwości finansowe. Mając na uwadze powyższe względy, zdecydowaliśmy się opublikować kod źródłowy aplikacji Szkolny.eu. Liczymy, że dzięki temu aplikacja będzie mogła dalej funkcjonować, być rozwijana, pomagając Wam w czasie zdalnego nauczania i przez kolejne lata nauki.
__Zachęcamy do [przeczytania całej informacji](https://szkolny.eu/informacja) na naszej stronie.__ __Zachęcamy do [przeczytania całej informacji](https://szkolny.eu/informacja) na naszej stronie.__
@ -32,17 +30,17 @@ Szkolny.eu jest nieoficjalną aplikacją, umożliwiającą rodzicom i uczniom do
- plan lekcji, terminarz, oceny, wiadomości, zadania domowe, uwagi, frekwencja - plan lekcji, terminarz, oceny, wiadomości, zadania domowe, uwagi, frekwencja
- wygodne **widgety** na ekran główny - wygodne **widgety** na ekran główny
- łatwa komunikacja z nauczycielami **odbieranie, wyszukiwanie i wysyłanie wiadomości** - łatwa komunikacja z nauczycielami - **odbieranie, wyszukiwanie i wysyłanie wiadomości**
- pobieranie **załączników wiadomości i zadań domowych** - pobieranie **załączników wiadomości i zadań domowych**
- **powiadomienia** o nowych informacjach na telefonie lub na komputerze - **powiadomienia** o nowych informacjach na telefonie lub na komputerze
- organizacja zadań domowych i sprawdzianów łatwe oznaczanie jako wykonane - organizacja zadań domowych i sprawdzianów - łatwe oznaczanie jako wykonane
- obliczanie **średniej ocen** ze wszystkich przedmiotów, oceny proponowane i końcowe - obliczanie **średniej ocen** ze wszystkich przedmiotów, oceny proponowane i końcowe
- Symulator edycji ocen obliczanie średniej z przedmiotu po zmianie dowolnych jego ocen - Symulator edycji ocen - obliczanie średniej z przedmiotu po zmianie dowolnych jego ocen
- **dodawanie własnych wydarzeń** i zadań do terminarza - **dodawanie własnych wydarzeń** i zadań do terminarza
- nowoczesny i intuicyjny interfejs użytkownika - nowoczesny i intuicyjny interfejs użytkownika
- **obsługa wielu profili** uczniów jeżeli jesteś Rodzicem, możesz skonfigurować wszystkie swoje konta uczniowskie i łatwo między nimi przełączać - **obsługa wielu profili** uczniów - jeżeli jesteś Rodzicem, możesz skonfigurować wszystkie swoje konta uczniowskie i łatwo między nimi przełączać
- opcja **automatycznej synchronizacji** z E-dziennikiem - opcja **automatycznej synchronizacji** z E-dziennikiem
- opcja Ciszy nocnej nigdy więcej budzących Cię dźwięków z telefonu - opcja Ciszy nocnej - nigdy więcej budzących Cię dźwięków z telefonu
[Zobacz porównanie funkcji z innymi aplikacjami](https://szkolny.eu/funkcje) [Zobacz porównanie funkcji z innymi aplikacjami](https://szkolny.eu/funkcje)
@ -55,7 +53,7 @@ Najnowsze wersje możesz pobrać z Google Play lub bezpośrednio z naszej strony
### Kompilacja ### Kompilacja
Aby uruchomić aplikację „ze źródeł” należy użyć Android Studio w wersji co najmniej 4.2 Beta 6. Wersja `debug` może wtedy zostać zainstalowana np. na emulatorze Androida. Aby uruchomić aplikację "ze źródeł" należy użyć Android Studio w wersji co najmniej 4.2 Beta 6. Wersja `debug` może wtedy zostać zainstalowana np. na emulatorze Androida.
Aby zbudować wersję produkcyjną, tzn. `release` należy użyć wariantu `mainRelease` oraz podpisać wyjściowy plik .APK sygnaturą w wersji V1 i V2. Aby zbudować wersję produkcyjną, tzn. `release` należy użyć wariantu `mainRelease` oraz podpisać wyjściowy plik .APK sygnaturą w wersji V1 i V2.
@ -70,15 +68,15 @@ __Jeśli masz jakieś pytania, zapraszamy na [nasz serwer Discord](https://szkol
## Licencja ## Licencja
Szkolny.eu publikowany jest na licencji [GNU GPLv3](LICENSE). W szczególności, deweloper: Szkolny.eu publikowany jest na licencji [GNU GPLv3](LICENSE). W szczególności, deweloper:
- Może modyfikować oraz usprawniać kod aplikacji - może modyfikować oraz usprawniać kod aplikacji
- Może dystrybuować wersje produkcyjne - może dystrybuować wersje produkcyjne
- Musi opublikować wszelkie wprowadzone zmiany, tzn. publiczny fork tego repozytorium - musi opublikować wszelkie wprowadzone zmiany, tzn. publiczny fork tego repozytorium
- Nie może zmieniać licencji ani copyrightu aplikacji - nie może zmieniać licencji ani copyrightu aplikacji
Dodatkowo: Dodatkowo:
- Zabronione jest modyfikowanie lub usuwanie kodu odpowiedzialnego za zgodność wersji produkcyjnych z licencją. - zabronione jest modyfikowanie lub usuwanie kodu odpowiedzialnego za zgodność wersji produkcyjnych z licencją
- **Wersje skompilowane nie mogą być dystrybuowane za pomocą Google Play oraz żadnej platformy, na której istnieje oficjalna wersja aplikacji**. - **wersje skompilowane nie mogą być dystrybuowane za pomocą Google Play oraz żadnej platformy, na której istnieje oficjalna wersja aplikacji**
**Autorzy aplikacji nie biorą odpowiedzialności za używanie aplikacji, modyfikowanie oraz dystrybuowanie.** **Autorzy aplikacji nie biorą odpowiedzialności za używanie aplikacji, modyfikowanie oraz dystrybuowanie.**

View file

@ -18,10 +18,8 @@ android {
versionName release.versionName versionName release.versionName
buildConfigField "java.util.Map<String, String>", "GIT_INFO", gitInfoMap buildConfigField "java.util.Map<String, String>", "GIT_INFO", gitInfoMap
buildConfigField "long", "BUILD_TIMESTAMP", String.valueOf(System.currentTimeMillis())
buildConfigField "String", "VERSION_BASE", "\"${release.versionName}\"" buildConfigField "String", "VERSION_BASE", "\"${release.versionName}\""
manifestPlaceholders = [
buildTimestamp: String.valueOf(System.currentTimeMillis())
]
multiDexEnabled = true multiDexEnabled = true
@ -100,10 +98,7 @@ tasks.whenTaskAdded { task ->
if (flavor != "") { if (flavor != "") {
tasks.create(renameTaskName, Copy) { tasks.create(renameTaskName, Copy) {
from file("${projectDir}/${flavor}/release/"), from file("${projectDir}/${flavor}/release/"), file("${buildDir}/outputs/mapping/${flavor}Release/")
file("${buildDir}/outputs/mapping/${flavor}Release/"),
file("${buildDir}/outputs/apk/${flavor}/release/"),
file("${buildDir}/outputs/bundle/${flavor}Release/")
include "*.aab", "*.apk", "mapping.txt", "output-metadata.json" include "*.aab", "*.apk", "mapping.txt", "output-metadata.json"
destinationDir file("${projectDir}/release/") destinationDir file("${projectDir}/release/")
rename ".+?\\.(.+)", "Edziennik_${android.defaultConfig.versionName}_${flavor}." + '$1' rename ".+?\\.(.+)", "Edziennik_${android.defaultConfig.versionName}_${flavor}." + '$1'

View file

@ -84,7 +84,7 @@ private def buildGitInfo() {
.stream() .stream()
.map { .map {
it.name + "(" + it.URIs.stream() it.name + "(" + it.URIs.stream()
.map { it.rawPath.stripMargin('/').replace(".git", "") } .map { it.rawPath }
.toArray() .toArray()
.join(", ") + ")" .join(", ") + ")"
} }

View file

@ -32,9 +32,8 @@
-keepnames class pl.szczodrzynski.edziennik.ui.widgets.luckynumber.WidgetLuckyNumberProvider -keepnames class pl.szczodrzynski.edziennik.ui.widgets.luckynumber.WidgetLuckyNumberProvider
-keepnames class androidx.appcompat.view.menu.MenuBuilder { setHeaderTitleInt(java.lang.CharSequence); } -keepnames class androidx.appcompat.view.menu.MenuBuilder { setHeaderTitleInt(java.lang.CharSequence); }
-keepnames class androidx.appcompat.view.menu.MenuPopupHelper { showPopup(int, int, boolean, boolean); }
-keepclassmembernames class androidx.appcompat.view.menu.StandardMenuPopup { private *; } -keepclassmembernames class androidx.appcompat.view.menu.StandardMenuPopup { private *; }
-keepclassmembernames class androidx.appcompat.view.menu.MenuItemImpl { private *; } -keepnames class androidx.appcompat.view.menu.MenuPopupHelper { showPopup(int, int, boolean, boolean); }
-keepclassmembernames class com.mikepenz.materialdrawer.widget.MiniDrawerSliderView { private *; } -keepclassmembernames class com.mikepenz.materialdrawer.widget.MiniDrawerSliderView { private *; }

View file

@ -29,8 +29,6 @@
android:usesCleartextTraffic="true" android:usesCleartextTraffic="true"
tools:ignore="UnusedAttribute"> tools:ignore="UnusedAttribute">
<meta-data android:name="buildTimestamp" android:value="${buildTimestamp}" />
<!-- __ __ _ _ _ _ _ <!-- __ __ _ _ _ _ _
| \/ | (_) /\ | | (_) (_) | | \/ | (_) /\ | | (_) (_) |
| \ / | __ _ _ _ __ / \ ___| |_ ___ ___| |_ _ _ | \ / | __ _ _ _ __ / \ ___| |_ ___ ___| |_ _ _

View file

@ -1,9 +1,14 @@
<h3>Wersja 4.7.1, 2021-04-12</h3> <h3>Wersja 4.7-rc.1, 2021-04-01</h3>
<ul> <ul>
<li>Poprawiono sprawdzanie dostępności e-dziennika.</li> <li><u>Szkolny.eu jest teraz open source!</u> Zapraszamy na stronę https://szkolny.eu/ po więcej ważnych informacji.</li>
<li>Zmieniono datę w informacjach o aplikacji. @Luncenok</li> <li>Poprawiono wybieranie obrazków (tła nagłówka, tła aplikacji oraz profilu) z dowolnego źródła.</li>
<li>Naprawiono zatrzymanie aplikacji na Androidzie 4.4 i starszych.</li>
<li>Naprawiono problemy z połączeniem internetowym na Androidzie 4.4 i starszych.</li>
<li>Dodano ekran informacji o kompilacji w Ustawieniach.</li>
<li>Zaktualizowano ekran licencji open source.</li>
<li>Zoptymalizowano wielkość aplikacji.</li>
</ul> </ul>
<br> <br>
<br> <br>
Dzięki za korzystanie ze Szkolnego!<br> Dzięki za korzystanie ze Szkolnego!<br>
<i>&copy; [Kuba Szczodrzyński](@kuba2k2), [Kacper Ziubryniewicz](@kapi2289) 2021</i> <i>&copy; Kuba Szczodrzyński, Kacper Ziubryniewicz 2021</i>

View file

@ -9,7 +9,7 @@
/*secret password - removed for source code publication*/ /*secret password - removed for source code publication*/
static toys AES_IV[16] = { static toys AES_IV[16] = {
0xcc, 0x64, 0xdb, 0x3a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; 0xdd, 0x0a, 0x72, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
unsigned char *agony(unsigned int laugh, unsigned char *box, unsigned char *heat); unsigned char *agony(unsigned int laugh, unsigned char *box, unsigned char *heat);

View file

@ -6,7 +6,6 @@ package pl.szczodrzynski.edziennik.config
import com.google.gson.Gson import com.google.gson.Gson
import com.google.gson.reflect.TypeToken import com.google.gson.reflect.TypeToken
import pl.szczodrzynski.edziennik.BuildConfig
import pl.szczodrzynski.edziennik.config.utils.get import pl.szczodrzynski.edziennik.config.utils.get
import pl.szczodrzynski.edziennik.config.utils.getIntList import pl.szczodrzynski.edziennik.config.utils.getIntList
import pl.szczodrzynski.edziennik.config.utils.set import pl.szczodrzynski.edziennik.config.utils.set
@ -124,19 +123,6 @@ class ConfigSync(private val config: Config) {
private var mRegisterAvailability: Map<String, RegisterAvailabilityStatus>? = null private var mRegisterAvailability: Map<String, RegisterAvailabilityStatus>? = null
var registerAvailability: Map<String, RegisterAvailabilityStatus> var registerAvailability: Map<String, RegisterAvailabilityStatus>
get() { get() { mRegisterAvailability = mRegisterAvailability ?: config.values.get("registerAvailability", null as String?)?.let { it -> gson.fromJson<Map<String, RegisterAvailabilityStatus>>(it, object: TypeToken<Map<String, RegisterAvailabilityStatus>>(){}.type) }; return mRegisterAvailability ?: mapOf() }
val flavor = config.values.get("registerAvailabilityFlavor", null as String?) set(value) { config.setMap("registerAvailability", value); mRegisterAvailability = value }
if (BuildConfig.FLAVOR != flavor)
return mapOf()
mRegisterAvailability = mRegisterAvailability ?: config.values.get("registerAvailability", null as String?)?.let { it ->
gson.fromJson(it, object: TypeToken<Map<String, RegisterAvailabilityStatus>>(){}.type)
}
return mRegisterAvailability ?: mapOf()
}
set(value) {
config.setMap("registerAvailability", value)
config.set("registerAvailabilityFlavor", BuildConfig.FLAVOR)
mRegisterAvailability = value
}
} }

View file

@ -15,7 +15,6 @@ class ApiCacheInterceptor(val app: App) : Interceptor {
val request = chain.request() val request = chain.request()
if (request.url().host() == "api.szkolny.eu" if (request.url().host() == "api.szkolny.eu"
&& Signing.appCertificate.md5() == app.config.apiInvalidCert && Signing.appCertificate.md5() == app.config.apiInvalidCert
&& !app.buildManager.isSigned
) { ) {
val response = ApiResponse<Unit>( val response = ApiResponse<Unit>(
success = false, success = false,

View file

@ -46,6 +46,6 @@ object Signing {
/*fun provideKey(param1: String, param2: Long): ByteArray {*/ /*fun provideKey(param1: String, param2: Long): ByteArray {*/
fun pleaseStopRightNow(param1: String, param2: Long): ByteArray { fun pleaseStopRightNow(param1: String, param2: Long): ByteArray {
return "$param1.MTIzNDU2Nzg5MDXHhAtZBW===.$param2".sha256() return "$param1.MTIzNDU2Nzg5MDy+5jm3L0===.$param2".sha256()
} }
} }

View file

@ -17,7 +17,6 @@ import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.dp import pl.szczodrzynski.edziennik.dp
import pl.szczodrzynski.edziennik.utils.BetterLinkMovementMethod
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
class ChangelogDialog( class ChangelogDialog(
@ -44,14 +43,9 @@ class ChangelogDialog(
val textView = TextView(activity) val textView = TextView(activity)
textView.setPadding(24.dp, 24.dp, 24.dp, 0) textView.setPadding(24.dp, 24.dp, 24.dp, 0)
var text = app.assets.open("pl-changelog.html").bufferedReader().use { val text = app.assets.open("pl-changelog.html").bufferedReader().use {
it.readText() it.readText()
} }
val commitsUrlPrefix = "https://github.com/szkolny-eu/szkolny-android/commits?author="
text = text.replace("""\[(.+?)]\(@([A-z0-9-]+)\)""".toRegex(), "<a href=\"$commitsUrlPrefix$2\">$1</a>")
text = text.replace("""\s@([A-z0-9-]+)""".toRegex(), " <a href=\"$commitsUrlPrefix$1\">@$1</a>")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
textView.text = Html.fromHtml(text) textView.text = Html.fromHtml(text)
} }
@ -59,8 +53,6 @@ class ChangelogDialog(
textView.text = Html.fromHtml(text.replace("<li>", "<br><li> - ")) textView.text = Html.fromHtml(text.replace("<li>", "<br><li> - "))
} }
textView.movementMethod = BetterLinkMovementMethod.getInstance()
val scrollView = ScrollView(activity) val scrollView = ScrollView(activity)
scrollView.addView(textView) scrollView.addView(textView)
@ -75,4 +67,4 @@ class ChangelogDialog(
} }
.show() .show()
}} }}
} }

View file

@ -199,14 +199,10 @@ class EventDetailsDialog(
} }
b.downloadButton.attachToastHint(R.string.hint_download_again) b.downloadButton.attachToastHint(R.string.hint_download_again)
BetterLink.attach(b.topic, onActionSelected = dialog::dismiss)
event.teacherName?.let { name -> b.topic.text = event.topic
BetterLink.attach( BetterLink.attach(b.topic) {
b.teacherName, dialog.dismiss()
teachers = mapOf(event.teacherId to name),
onActionSelected = dialog::dismiss
)
} }
if (event.homeworkBody == null && !event.addedManually && event.type == Event.TYPE_HOMEWORK) { if (event.homeworkBody == null && !event.addedManually && event.type == Event.TYPE_HOMEWORK) {
@ -224,7 +220,10 @@ class EventDetailsDialog(
b.bodyTitle.isVisible = true b.bodyTitle.isVisible = true
b.bodyProgressBar.isVisible = false b.bodyProgressBar.isVisible = false
b.body.isVisible = true b.body.isVisible = true
BetterLink.attach(b.body, onActionSelected = dialog::dismiss) b.body.text = event.homeworkBody
BetterLink.attach(b.body) {
dialog.dismiss()
}
} }
if (event.attachmentIds.isNullOrEmpty() || event.attachmentNames.isNullOrEmpty()) { if (event.attachmentIds.isNullOrEmpty() || event.attachmentNames.isNullOrEmpty()) {

View file

@ -14,7 +14,6 @@ import pl.szczodrzynski.edziennik.databinding.DialogGradeDetailsBinding
import pl.szczodrzynski.edziennik.onClick import pl.szczodrzynski.edziennik.onClick
import pl.szczodrzynski.edziennik.setTintColor import pl.szczodrzynski.edziennik.setTintColor
import pl.szczodrzynski.edziennik.ui.modules.grades.GradesAdapter import pl.szczodrzynski.edziennik.ui.modules.grades.GradesAdapter
import pl.szczodrzynski.edziennik.utils.BetterLink
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
@ -69,14 +68,6 @@ class GradeDetailsDialog(
GradesConfigDialog(activity, reloadOnDismiss = true) GradesConfigDialog(activity, reloadOnDismiss = true)
} }
grade.teacherName?.let { name ->
BetterLink.attach(
b.teacherName,
teachers = mapOf(grade.teacherId to name),
onActionSelected = dialog::dismiss
)
}
launch { launch {
val historyList = withContext(Dispatchers.Default) { val historyList = withContext(Dispatchers.Default) {
app.db.gradeDao().getByParentIdNow(App.profileId, grade.id) app.db.gradeDao().getByParentIdNow(App.profileId, grade.id)

View file

@ -25,7 +25,6 @@ import pl.szczodrzynski.edziennik.ui.dialogs.event.EventDetailsDialog
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventListAdapter import pl.szczodrzynski.edziennik.ui.dialogs.event.EventListAdapter
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventManualDialog import pl.szczodrzynski.edziennik.ui.dialogs.event.EventManualDialog
import pl.szczodrzynski.edziennik.ui.modules.timetable.TimetableFragment import pl.szczodrzynski.edziennik.ui.modules.timetable.TimetableFragment
import pl.szczodrzynski.edziennik.utils.BetterLink
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Week import pl.szczodrzynski.edziennik.utils.models.Week
@ -217,19 +216,5 @@ class LessonDetailsDialog(
b.eventsNoData.visibility = View.VISIBLE b.eventsNoData.visibility = View.VISIBLE
} }
}) })
lesson.displayTeacherName?.let { name ->
lesson.displayTeacherId ?: return@let
BetterLink.attach(
b.teacherNameView,
teachers = mapOf(lesson.displayTeacherId!! to name),
onActionSelected = dialog::dismiss
)
BetterLink.attach(
b.oldTeacherNameView,
teachers = mapOf(lesson.displayTeacherId!! to name),
onActionSelected = dialog::dismiss
)
}
} }
} }

View file

@ -16,7 +16,6 @@ import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull
import pl.szczodrzynski.edziennik.databinding.AttendanceDetailsDialogBinding import pl.szczodrzynski.edziennik.databinding.AttendanceDetailsDialogBinding
import pl.szczodrzynski.edziennik.setTintColor import pl.szczodrzynski.edziennik.setTintColor
import pl.szczodrzynski.edziennik.utils.BetterLink
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
class AttendanceDetailsDialog( class AttendanceDetailsDialog(
@ -61,13 +60,5 @@ class AttendanceDetailsDialog(
b.attendanceName.background.setTintColor(attendanceColor) b.attendanceName.background.setTintColor(attendanceColor)
b.attendanceIsCounted.setText(if (attendance.isCounted) R.string.yes else R.string.no) b.attendanceIsCounted.setText(if (attendance.isCounted) R.string.yes else R.string.no)
attendance.teacherName?.let { name ->
BetterLink.attach(
b.teacherName,
teachers = mapOf(attendance.teacherId to name),
onActionSelected = dialog::dismiss
)
}
}} }}
} }

View file

@ -21,7 +21,6 @@ import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_MOBIDZIENNIK import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_MOBIDZIENNIK
import pl.szczodrzynski.edziennik.data.db.entity.Notice import pl.szczodrzynski.edziennik.data.db.entity.Notice
import pl.szczodrzynski.edziennik.data.db.full.NoticeFull import pl.szczodrzynski.edziennik.data.db.full.NoticeFull
import pl.szczodrzynski.edziennik.utils.BetterLink
import pl.szczodrzynski.edziennik.utils.Utils.bs import pl.szczodrzynski.edziennik.utils.Utils.bs
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
@ -84,14 +83,6 @@ class NoticesAdapter//getting the context and product list with constructor
} else { } else {
holder.noticesItemReason.background = null holder.noticesItemReason.background = null
} }
BetterLink.attach(holder.noticesItemReason)
notice.teacherName?.let { name ->
BetterLink.attach(holder.noticesItemTeacherName, teachers = mapOf(
notice.teacherId to name
))
}
} }
override fun getItemCount(): Int { override fun getItemCount(): Int {

View file

@ -10,14 +10,12 @@ import android.app.Activity
import android.content.Intent import android.content.Intent
import android.graphics.Color import android.graphics.Color
import android.os.Bundle import android.os.Bundle
import android.text.Html
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.animation.AccelerateDecelerateInterpolator import android.view.animation.AccelerateDecelerateInterpolator
import android.view.animation.Animation import android.view.animation.Animation
import android.view.animation.RotateAnimation import android.view.animation.RotateAnimation
import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
@ -31,9 +29,7 @@ import pl.szczodrzynski.edziennik.data.api.szkolny.response.RegisterAvailability
import pl.szczodrzynski.edziennik.databinding.LoginChooserFragmentBinding import pl.szczodrzynski.edziennik.databinding.LoginChooserFragmentBinding
import pl.szczodrzynski.edziennik.ui.dialogs.RegisterUnavailableDialog import pl.szczodrzynski.edziennik.ui.dialogs.RegisterUnavailableDialog
import pl.szczodrzynski.edziennik.ui.modules.feedback.FeedbackActivity import pl.szczodrzynski.edziennik.ui.modules.feedback.FeedbackActivity
import pl.szczodrzynski.edziennik.utils.BetterLinkMovementMethod
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
import pl.szczodrzynski.edziennik.utils.models.Date
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
class LoginChooserFragment : Fragment(), CoroutineScope { class LoginChooserFragment : Fragment(), CoroutineScope {
@ -66,15 +62,6 @@ class LoginChooserFragment : Fragment(), CoroutineScope {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
if (!isAdded) return if (!isAdded) return
b.versionText.setText(
R.string.login_chooser_version_format,
app.buildManager.versionName,
Date.fromMillis(app.buildManager.buildTimestamp).stringY_m_d
)
b.versionText.onClick {
app.buildManager.showVersionDialog(activity)
}
val adapter = LoginChooserAdapter(activity, this::onLoginModeClicked) val adapter = LoginChooserAdapter(activity, this::onLoginModeClicked)
LoginInfo.chooserList = LoginInfo.chooserList LoginInfo.chooserList = LoginInfo.chooserList
@ -216,23 +203,6 @@ class LoginChooserFragment : Fragment(), CoroutineScope {
return return
} }
if (!app.config.privacyPolicyAccepted) {
MaterialAlertDialogBuilder(activity)
.setTitle(R.string.privacy_policy)
.setMessage(Html.fromHtml(activity.getString(R.string.privacy_policy_dialog_html)))
.setPositiveButton(R.string.i_agree) { _, _ ->
app.config.privacyPolicyAccepted = true
onLoginModeClicked(loginType, loginMode)
}
.setNegativeButton(R.string.i_disagree, null)
.show()
.also { dialog ->
dialog.findViewById<TextView>(android.R.id.message)?.movementMethod =
BetterLinkMovementMethod.getInstance()
}
return
}
launch { launch {
if (!checkAvailability(loginType.loginType)) if (!checkAvailability(loginType.loginType))
return@launch return@launch

View file

@ -5,6 +5,7 @@
package pl.szczodrzynski.edziennik.ui.modules.login package pl.szczodrzynski.edziennik.ui.modules.login
import android.os.Bundle import android.os.Bundle
import android.text.Html
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@ -74,6 +75,19 @@ class LoginSummaryFragment : Fragment(), CoroutineScope {
} }
b.finishButton.onClick { b.finishButton.onClick {
if (!app.config.privacyPolicyAccepted) {
MaterialAlertDialogBuilder(activity)
.setTitle(R.string.privacy_policy)
.setMessage(Html.fromHtml("Korzystając z aplikacji potwierdzasz <a href=\"http://szkolny.eu/privacy-policy\">przeczytanie Polityki prywatności</a> i akceptujesz jej postanowienia."))
.setPositiveButton(R.string.i_agree) { _, _ ->
app.config.privacyPolicyAccepted = true
b.finishButton.performClick()
}
.setNegativeButton(R.string.i_disagree, null)
.show()
return@onClick
}
val args = Bundle( val args = Bundle(
"registrationAllowed" to b.registerMeSwitch.isChecked "registrationAllowed" to b.registerMeSwitch.isChecked
) )

View file

@ -345,7 +345,6 @@ class MessagesComposeFragment : Fragment(), CoroutineScope {
b.recipients.setAdapter(adapter) b.recipients.setAdapter(adapter)
handleReplyMessage() handleReplyMessage()
handleMailToIntent()
}} }}
private fun handleReplyMessage() { launch { private fun handleReplyMessage() { launch {
@ -403,22 +402,6 @@ class MessagesComposeFragment : Fragment(), CoroutineScope {
} }
}} }}
private fun handleMailToIntent() {
val teacherId = arguments?.getLong("messageRecipientId")
if (teacherId == 0L)
return
val chipList = mutableListOf<ChipInfo>()
teachers.firstOrNull { it.id == teacherId }?.let { teacher ->
teacher.image = getProfileImage(48, 24, 16, 12, 1, teacher.fullName)
chipList += ChipInfo(teacher.fullName, teacher)
}
b.recipients.addTextWithChips(chipList)
val subject = arguments?.getString("messageSubject")
b.subject.setText(subject ?: return)
}
private fun sendMessage() { private fun sendMessage() {
b.recipientsLayout.error = null b.recipientsLayout.error = null
b.subjectLayout.error = null b.subjectLayout.error = null

View file

@ -7,7 +7,6 @@ package pl.szczodrzynski.edziennik.ui.modules.settings.cards
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import eu.szkolny.font.SzkolnyFont import eu.szkolny.font.SzkolnyFont
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.after import pl.szczodrzynski.edziennik.after
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_LIBRUS import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_LIBRUS
@ -142,15 +141,12 @@ class SettingsRegisterCard(util: SettingsUtil) : SettingsCard(util) {
else else
null, null,
if (App.devMode) util.createPropertyItem(
util.createPropertyItem( text = R.string.settings_register_hide_sticks_from_old,
text = R.string.settings_register_hide_sticks_from_old, icon = CommunityMaterial.Icon3.cmd_numeric_1_box_outline,
icon = CommunityMaterial.Icon3.cmd_numeric_1_box_outline, value = configProfile.grades.hideSticksFromOld
value = configProfile.grades.hideSticksFromOld ) { _, it ->
) { _, it -> configProfile.grades.hideSticksFromOld = it
configProfile.grades.hideSticksFromOld = it }
}
else
null
) )
} }

View file

@ -7,8 +7,6 @@
package pl.szczodrzynski.edziennik.utils package pl.szczodrzynski.edziennik.utils
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.graphics.Color import android.graphics.Color
import android.text.Spannable import android.text.Spannable
import android.text.SpannableString import android.text.SpannableString
@ -21,268 +19,141 @@ import android.view.ViewGroup
import android.widget.TextView import android.widget.TextView
import androidx.appcompat.view.menu.MenuBuilder import androidx.appcompat.view.menu.MenuBuilder
import androidx.appcompat.view.menu.MenuPopupHelper import androidx.appcompat.view.menu.MenuPopupHelper
import androidx.core.widget.addTextChangedListener import pl.szczodrzynski.edziennik.Intent
import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.copyToClipboard
import pl.szczodrzynski.edziennik.data.api.Regexes import pl.szczodrzynski.edziennik.data.api.Regexes
import pl.szczodrzynski.edziennik.get
import pl.szczodrzynski.edziennik.getTextPosition
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
@SuppressLint("RestrictedApi")
object BetterLink { object BetterLink {
/** @SuppressLint("RestrictedApi")
* Used in conjunction with the item's ID to execute the fun attach(textView: TextView, onActionSelected: (() -> Unit)? = null) {
* [attach]'s onActionSelected listener when the item is textView.autoLinkMask = Linkify.WEB_URLS or Linkify.EMAIL_ADDRESSES
* clicked. BetterLinkMovementMethod.linkify(textView.autoLinkMask, textView).setOnLinkClickListener { v, span: BetterLinkMovementMethod.ClickableSpanWithText ->
*/ val url = span.text()
private const val FLAG_ACTION = 0x8000 val c = v.context
private fun MenuBuilder.setTitle(title: CharSequence): MenuBuilder { val s = v.text as Spanned
this::class.java.getDeclaredMethod("setHeaderTitleInt", CharSequence::class.java).let { val start = s.getSpanStart(span.span())
it.isAccessible = true val end = s.getSpanEnd(span.span())
it.invoke(this, title)
}
return this
}
private fun MenuItem.addListener(listener: (item: MenuItem) -> Boolean): MenuItem { val parent = v.rootView.findViewById<ViewGroup>(android.R.id.content)
this::class.java.getDeclaredField("mClickListener").let { val parentLocation = intArrayOf(0, 0)
it.isAccessible = true parent.getLocationOnScreen(parentLocation)
val oldListener = it.get(this) as? MenuItem.OnMenuItemClickListener
it.set(this, object : MenuItem.OnMenuItemClickListener { val rect = textView.getTextPosition(start..end)
override fun onMenuItemClick(item: MenuItem): Boolean {
oldListener?.onMenuItemClick(item) val view = View(c)
return listener(item) view.layoutParams = ViewGroup.LayoutParams(rect.width(), rect.height())
view.setBackgroundColor(Color.TRANSPARENT)
parent.addView(view)
view.x = rect.left.toFloat() - parentLocation[0]
view.y = rect.top.toFloat() - parentLocation[1]
val menu = MenuBuilder(c)
val helper = MenuPopupHelper(c, menu, view)
val popup = helper.popup
var menuTitle = url.substringAfter(":")
var date: Date? = null
var urlItem: MenuItem? = null
var createEventItem: MenuItem? = null
//var goToTimetableItem: MenuItem? = null // TODO 2020-03-19: implement this
var mailItem: MenuItem? = null
var copyItem: MenuItem? = null
when {
url.startsWith("mailto:") -> {
mailItem = menu.add(1, 20, 2, "Napisz e-mail")
} }
}) url.startsWith("dateYmd:") -> {
} createEventItem = menu.add(1, 10, 2, "Utwórz wydarzenie")
return this //goToTimetableItem = menu.add(1, 11, 3, "Idź do planu lekcji")
} date = parseDateYmd(menuTitle)
}
url.startsWith("dateDmy:") -> {
createEventItem = menu.add(1, 10, 2, "Utwórz wydarzenie")
//goToTimetableItem = menu.add(1, 11, 3, "Idź do planu lekcji")
date = parseDateDmy(menuTitle)
}
url.startsWith("dateAbs:") -> {
createEventItem = menu.add(1, 10, 2, "Utwórz wydarzenie")
//goToTimetableItem = menu.add(1, 11, 3, "Idź do planu lekcji")
date = parseDateAbs(menuTitle)
}
url.startsWith("dateRel:") -> {
createEventItem = menu.add(1, 10, 2, "Utwórz wydarzenie")
//goToTimetableItem = menu.add(1, 11, 3, "Idź do planu lekcji")
date = parseDateRel(menuTitle)
}
else -> {
urlItem = menu.add(1, 1, 2, "Otwórz w przeglądarce")
menuTitle = url
}
}
copyItem = menu.add(1, 1000, 1000, "Kopiuj tekst")
private fun createUrlItems(menu: MenuBuilder, context: Context, url: String) { helper.setOnDismissListener { parent.removeView(view) }
menu.setTitle(url)
menu.add(
1,
2,
2,
"Otwórz w przeglądarce"
).setOnMenuItemClickListener {
Utils.openUrl(context, url)
true
}
}
private fun createMailtoItems(menu: MenuBuilder, context: Context, url: String) { urlItem?.setOnMenuItemClickListener { Utils.openUrl(c, url); true }
menu.add( mailItem?.setOnMenuItemClickListener { Utils.openUrl(c, url); true }
1, copyItem?.setOnMenuItemClickListener { menuTitle.copyToClipboard(c); true }
3, createEventItem?.setOnMenuItemClickListener {
3,
"Napisz e-mail"
).setOnMenuItemClickListener {
Utils.openUrl(context, url)
true
}
}
private fun createDateItems(menu: MenuBuilder, context: Context, date: Date?) {
date ?: return
menu.setTitle(date.formattedString)
menu.add(
1,
4 or FLAG_ACTION,
4,
"Utwórz wydarzenie"
).setOnMenuItemClickListener {
val intent = Intent(
Intent.ACTION_MAIN,
"action" to "createManualEvent",
"eventDate" to date.stringY_m_d
)
context.sendBroadcast(intent)
true
}
}
private fun createTeacherItems(menu: MenuBuilder, context: Context, teacherId: Long, fullName: String) {
menu.setTitle(fullName)
menu.add(
1,
5 or FLAG_ACTION,
5,
"Napisz wiadomość"
).setOnMenuItemClickListener {
val intent = Intent(
Intent.ACTION_MAIN,
"fragmentId" to MainActivity.TARGET_MESSAGES_COMPOSE,
"messageRecipientId" to teacherId
)
context.sendBroadcast(intent)
true
}
}
private fun onClickListener(
view: TextView,
span: BetterLinkMovementMethod.ClickableSpanWithText,
onActionSelected: (() -> Unit)?
): Boolean {
val context = view.context
val spanned = view.text as Spanned
val start = spanned.getSpanStart(span.span())
val end = spanned.getSpanEnd(span.span())
val parent = view.rootView.findViewById<ViewGroup>(android.R.id.content)
val parentLocation = intArrayOf(0, 0)
parent.getLocationOnScreen(parentLocation)
val rect = view.getTextPosition(start..end)
val popupView = View(context)
popupView.layoutParams = ViewGroup.LayoutParams(rect.width(), rect.height())
popupView.setBackgroundColor(Color.TRANSPARENT)
parent.addView(popupView)
popupView.x = rect.left.toFloat() - parentLocation[0]
popupView.y = rect.top.toFloat() - parentLocation[1]
val menu = MenuBuilder(context)
val helper = MenuPopupHelper(context, menu, popupView)
val popup = helper.popup
val spanUrl = span.text()
val spanParameter = spanUrl.substringAfter(":")
val spanText = spanned.substring(start, end)
//goToTimetableItem = menu.add(1, 11, 3, "Idź do planu lekcji")
// create appropriate items for spans
when {
spanUrl.startsWith("mailto:") -> createMailtoItems(menu, context, spanUrl)
spanUrl.startsWith("dateYmd:") -> createDateItems(menu, context, parseDateYmd(spanParameter))
spanUrl.startsWith("dateDmy:") -> createDateItems(menu, context, parseDateDmy(spanParameter))
spanUrl.startsWith("dateAbs:") -> createDateItems(menu, context, parseDateAbs(spanParameter))
spanUrl.startsWith("dateRel:") -> createDateItems(menu, context, parseDateRel(spanParameter))
spanUrl.startsWith("teacher:") -> createTeacherItems(
menu,
context,
teacherId = spanParameter.toLongOrNull() ?: -1,
fullName = spanText
)
else -> createUrlItems(menu, context, spanUrl)
}
menu.add(1, 1000, 1000, "Kopiuj tekst").setOnMenuItemClickListener {
spanParameter.copyToClipboard(context)
true
}
helper.setOnDismissListener { parent.removeView(popupView) }
menu.visibleItems.forEach { item ->
if ((item.itemId and FLAG_ACTION) != FLAG_ACTION)
return@forEach
item.addListener {
onActionSelected?.invoke() onActionSelected?.invoke()
val intent = Intent(
android.content.Intent.ACTION_MAIN,
"action" to "createManualEvent",
"eventDate" to date?.stringY_m_d
)
c.sendBroadcast(intent)
true true
} }
}
popup::class.java.getDeclaredField("mShowTitle").let { menu::class.java.getDeclaredMethod("setHeaderTitleInt", CharSequence::class.java).let {
it.isAccessible = true it.isAccessible = true
it.set(popup, true) it.invoke(menu, menuTitle)
}
helper::class.java.getDeclaredMethod(
"showPopup",
Int::class.java,
Int::class.java,
Boolean::class.java,
Boolean::class.java
).let {
it.isAccessible = true
it.invoke(helper, 0, 0, false, true)
}
return true
}
fun attach(
textView: TextView,
teachers: Map<Long, String>? = null,
onActionSelected: (() -> Unit)? = null
) {
textView.autoLinkMask = Linkify.WEB_URLS or Linkify.EMAIL_ADDRESSES
BetterLinkMovementMethod
.linkify(textView.autoLinkMask, textView)
.setOnLinkClickListener { view, span ->
onClickListener(view, span, onActionSelected)
} }
popup::class.java.getDeclaredField("mShowTitle").let {
textView.addTextChangedListener { it.isAccessible = true
attachSpan(textView, teachers) it.set(popup, true)
}
helper::class.java.getDeclaredMethod("showPopup", Int::class.java, Int::class.java, Boolean::class.java, Boolean::class.java).let {
it.isAccessible = true
it.invoke(helper, 0, 0, false, true)
}
true
} }
attachSpan(textView, teachers) val spanned = textView.text as? Spannable ?: {
} SpannableString(textView.text)
}()
private fun attachSpan(
textView: TextView,
teachers: Map<Long, String>? = null
) {
val spanned = textView.text as? Spannable ?: SpannableString(textView.text)
teachers?.forEach { (id, fullName) ->
val index = textView.text.indexOf(fullName)
if (index == -1)
return@forEach
val span = URLSpan("teacher:$id")
spanned.setSpan(
span,
index,
index + fullName.length,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)
}
Regexes.LINKIFY_DATE_YMD.findAll(textView.text).forEach { match -> Regexes.LINKIFY_DATE_YMD.findAll(textView.text).forEach { match ->
val span = URLSpan("dateYmd:" + match.value) val span = URLSpan("dateYmd:" + match.value)
spanned.setSpan( spanned.setSpan(span, match.range.first, match.range.last + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
span,
match.range.first,
match.range.last + 1,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)
} }
Regexes.LINKIFY_DATE_DMY.findAll(textView.text).forEach { match -> Regexes.LINKIFY_DATE_DMY.findAll(textView.text).forEach { match ->
val span = URLSpan("dateDmy:" + match.value) val span = URLSpan("dateDmy:" + match.value)
spanned.setSpan( spanned.setSpan(span, match.range.first, match.range.last + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
span,
match.range.first,
match.range.last + 1,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)
} }
Regexes.LINKIFY_DATE_ABSOLUTE.findAll(textView.text).forEach { match -> Regexes.LINKIFY_DATE_ABSOLUTE.findAll(textView.text).forEach { match ->
val span = URLSpan("dateAbs:" + match.value) val span = URLSpan("dateAbs:" + match.value)
spanned.setSpan( spanned.setSpan(span, match.range.first, match.range.last + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
span,
match.range.first,
match.range.last + 1,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)
} }
Regexes.LINKIFY_DATE_RELATIVE.findAll(textView.text).forEach { match -> Regexes.LINKIFY_DATE_RELATIVE.findAll(textView.text).forEach { match ->
val span = URLSpan("dateRel:" + match.value) val span = URLSpan("dateRel:" + match.value)
spanned.setSpan( spanned.setSpan(span, match.range.first, match.range.last + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
span,
match.range.first,
match.range.last + 1,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)
} }
//Linkify.addLinks(textView, LINKIFY_DATE_ABSOLUTE.toPattern(), "dateAbs:")
//Linkify.addLinks(textView, LINKIFY_DATE_RELATIVE.toPattern(), "dateRel:")
} }
private val monthNames = private val monthNames = listOf("sty", "lut", "mar", "kwi", "maj", "cze", "lip", "sie", "wrz", "paź", "lis", "gru")
listOf("sty", "lut", "mar", "kwi", "maj", "cze", "lip", "sie", "wrz", "paź", "lis", "gru")
private fun parseDateYmd(text: String): Date? { private fun parseDateYmd(text: String): Date? {
return Regexes.LINKIFY_DATE_YMD.find(text)?.let { return Regexes.LINKIFY_DATE_YMD.find(text)?.let {
@ -292,7 +163,6 @@ object BetterLink {
Date(year, month, day) Date(year, month, day)
} }
} }
private fun parseDateDmy(text: String): Date? { private fun parseDateDmy(text: String): Date? {
return Regexes.LINKIFY_DATE_DMY.find(text)?.let { return Regexes.LINKIFY_DATE_DMY.find(text)?.let {
val day = it[1].toIntOrNull() ?: 1 val day = it[1].toIntOrNull() ?: 1
@ -324,7 +194,7 @@ object BetterLink {
else -> 1 else -> 1
} }
date.stepForward(0, 0, amount * unitInDays) date.stepForward(0, 0, amount*unitInDays)
} }
} }
} }

View file

@ -4,7 +4,6 @@
package pl.szczodrzynski.edziennik.utils.managers package pl.szczodrzynski.edziennik.utils.managers
import android.content.pm.PackageManager
import android.text.TextUtils import android.text.TextUtils
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
@ -27,18 +26,12 @@ class BuildManager(val app: App) : CoroutineScope {
val buildFlavor = BuildConfig.FLAVOR val buildFlavor = BuildConfig.FLAVOR
val buildType = BuildConfig.BUILD_TYPE val buildType = BuildConfig.BUILD_TYPE
val buildTimestamp = BuildConfig.BUILD_TIMESTAMP
val isRelease = !BuildConfig.DEBUG val isRelease = !BuildConfig.DEBUG
val isDebug = BuildConfig.DEBUG val isDebug = BuildConfig.DEBUG
val isNightly = BuildConfig.VERSION_NAME.contains("nightly") val isNightly = BuildConfig.VERSION_NAME.contains("nightly")
val isDaily = BuildConfig.VERSION_NAME.contains("daily") val isDaily = BuildConfig.VERSION_NAME.contains("daily")
val buildTimestamp: Long
get() {
val info = app.packageManager.getApplicationInfo(app.packageName, PackageManager.GET_META_DATA)
val metadata = info.metaData
return metadata?.getFloat("buildTimestamp")?.toLong() ?: 0
}
val gitHash = BuildConfig.GIT_INFO["hash"] val gitHash = BuildConfig.GIT_INFO["hash"]
val gitVersion = BuildConfig.GIT_INFO["version"] val gitVersion = BuildConfig.GIT_INFO["version"]
val gitBranch = BuildConfig.GIT_INFO["branch"] val gitBranch = BuildConfig.GIT_INFO["branch"]
@ -63,9 +56,9 @@ class BuildManager(val app: App) : CoroutineScope {
} }
val versionBadge = when { val versionBadge = when {
isSigned && isNightly -> isOfficial && isNightly ->
"Nightly\n" + BuildConfig.VERSION_NAME.substringAfterLast('.') "Nightly\n" + BuildConfig.VERSION_NAME.substringAfterLast('.')
isSigned && isDaily -> isOfficial && isDaily ->
"Daily\n" + BuildConfig.VERSION_NAME.substringAfterLast('.') "Daily\n" + BuildConfig.VERSION_NAME.substringAfterLast('.')
isDebug -> isDebug ->
"Debug\n" + BuildConfig.VERSION_BASE "Debug\n" + BuildConfig.VERSION_BASE
@ -85,22 +78,10 @@ class BuildManager(val app: App) : CoroutineScope {
val fields = mapOf( val fields = mapOf(
R.string.build_version to BuildConfig.VERSION_BASE, R.string.build_version to BuildConfig.VERSION_BASE,
R.string.build_official to when { R.string.build_official to if (isOfficial)
isOfficial -> yes.asColoredSpannable(mtrlGreen) yes.asColoredSpannable(mtrlGreen)
isSigned -> TextUtils.concat( else
yes.asColoredSpannable(mtrlYellow), no.asColoredSpannable(mtrlRed),
when {
isNightly -> " (nightly build)"
isDaily -> " (daily build)"
else -> no.asColoredSpannable(mtrlYellow)
}
)
isDebug -> no
else -> TextUtils.concat(
no.asColoredSpannable(mtrlRed),
if (gitAuthor.isNotNullNorBlank()) " ($gitAuthor)" else ""
)
},
R.string.build_platform to when { R.string.build_platform to when {
isPlayRelease -> activity.getString(R.string.build_platform_play) isPlayRelease -> activity.getString(R.string.build_platform_play)
isApkRelease -> activity.getString(R.string.build_platform_apk) isApkRelease -> activity.getString(R.string.build_platform_apk)
@ -248,10 +229,8 @@ class BuildManager(val app: App) : CoroutineScope {
val validation = Signing.appCertificate + gitHash + gitRemotes?.join(";") val validation = Signing.appCertificate + gitHash + gitRemotes?.join(";")
// app already validated // app already validated
if (app.config.validation?.substringBefore(":") == validation.md5()){ if (app.config.validation == validation.md5())
gitAuthor = app.config.validation?.substringAfter(":")
return@launch return@launch
}
val dialog = MaterialAlertDialogBuilder(activity) val dialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.please_wait) .setTitle(R.string.please_wait)
@ -272,7 +251,7 @@ class BuildManager(val app: App) : CoroutineScope {
} }
// release, unofficial, published build // release, unofficial, published build
app.config.validation = validation.md5() + ":" + gitAuthor app.config.validation = validation.md5()
invalidateBuild(activity, dialog, InvalidBuildReason.VALID) invalidateBuild(activity, dialog, InvalidBuildReason.VALID)
} }
} }

View file

@ -94,7 +94,6 @@
android:textAppearance="@style/NavView.TextView.Helper" /> android:textAppearance="@style/NavView.TextView.Helper" />
<TextView <TextView
android:id="@+id/teacherName"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="0dp" android:layout_marginTop="0dp"

View file

@ -112,7 +112,6 @@
android:text="@string/dialog_event_details_teacher" android:text="@string/dialog_event_details_teacher"
android:visibility="@{event.teacherName != null ? View.VISIBLE : View.GONE}"/> android:visibility="@{event.teacherName != null ? View.VISIBLE : View.GONE}"/>
<TextView <TextView
android:id="@+id/teacherName"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@{event.teacherName}" android:text="@{event.teacherName}"

View file

@ -121,7 +121,6 @@
android:textAppearance="@style/NavView.TextView.Helper" /> android:textAppearance="@style/NavView.TextView.Helper" />
<TextView <TextView
android:id="@+id/teacherName"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="0dp" android:layout_marginTop="0dp"

View file

@ -176,7 +176,6 @@
android:textAppearance="@style/NavView.TextView.Helper" android:textAppearance="@style/NavView.TextView.Helper"
android:text="@string/dialog_lesson_details_teacher" /> android:text="@string/dialog_lesson_details_teacher" />
<TextView <TextView
android:id="@+id/oldTeacherNameView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textAppearance="@style/NavView.TextView.Helper" android:textAppearance="@style/NavView.TextView.Helper"
@ -187,7 +186,6 @@
app:strikeThrough="@{true}" app:strikeThrough="@{true}"
tools:text="Janósz Kowalski" /> tools:text="Janósz Kowalski" />
<TextView <TextView
android:id="@+id/teacherNameView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@{teacherName}" android:text="@{teacherName}"

View file

@ -72,16 +72,10 @@
android:text="@string/cancel" android:text="@string/cancel"
android:textAllCaps="false" /> android:textAllCaps="false" />
<TextView <Space
android:id="@+id/versionText"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="0dp"
android:layout_weight="1" android:layout_weight="1" />
android:background="?selectableItemBackgroundBorderless"
android:gravity="center"
android:textAppearance="@style/NavView.TextView.Small"
android:textSize="12sp"
tools:text="Szkolny.eu v4.7-rc.2\n2021-04-06" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/helpButton" android:id="@+id/helpButton"

View file

@ -856,7 +856,7 @@
<string name="settings_about_licenses_text">Open-Source-Lizenzen</string> <string name="settings_about_licenses_text">Open-Source-Lizenzen</string>
<string name="settings_about_privacy_policy_text">Datenschutzrichtlinie</string> <string name="settings_about_privacy_policy_text">Datenschutzrichtlinie</string>
<string name="settings_card_register_title">E-Klassenbuch</string> <string name="settings_card_register_title">E-Klassenbuch</string>
<string name="settings_about_title_subtext">© Kuba Szczodrzyński &amp;&amp; Kacper Ziubryniewicz\nSeptember 2018 - April 2021</string> <string name="settings_about_title_subtext">© Kuba Szczodrzyński &amp;&amp; Kacper Ziubryniewicz\nSeptember 2018 - Februar 2021</string>
<string name="settings_about_update_subtext">Klicken Sie hier, um nach Aktualisierungen zu suchen</string> <string name="settings_about_update_subtext">Klicken Sie hier, um nach Aktualisierungen zu suchen</string>
<string name="settings_about_update_text">Aktualisierung</string> <string name="settings_about_update_text">Aktualisierung</string>
<string name="settings_about_version_text">Version</string> <string name="settings_about_version_text">Version</string>

View file

@ -858,7 +858,7 @@
<string name="settings_about_licenses_text">Open-source licenses</string> <string name="settings_about_licenses_text">Open-source licenses</string>
<string name="settings_about_privacy_policy_text">Privacy policy</string> <string name="settings_about_privacy_policy_text">Privacy policy</string>
<string name="settings_card_register_title">E-register</string> <string name="settings_card_register_title">E-register</string>
<string name="settings_about_title_subtext">© Kuba Szczodrzyński &amp;&amp; Kacper Ziubryniewicz\nSeptember 2018 - April 2021</string> <string name="settings_about_title_subtext">© Kuba Szczodrzyński &amp;&amp; Kacper Ziubryniewicz\nSeptember 2018 - February 2021</string>
<string name="settings_about_update_subtext">Click to check for updates</string> <string name="settings_about_update_subtext">Click to check for updates</string>
<string name="settings_about_update_text">Update</string> <string name="settings_about_update_text">Update</string>
<string name="settings_about_version_text">Version</string> <string name="settings_about_version_text">Version</string>
@ -1235,135 +1235,5 @@
<string name="permissions_attachment">In order to download the file, you have to grant file storage permission for the application.\n\nClick OK to grant the permission.</string> <string name="permissions_attachment">In order to download the file, you have to grant file storage permission for the application.\n\nClick OK to grant the permission.</string>
<string name="permissions_denied">You denied the required permissions for the application.\n\nIn order to grant the permission, open the Permissions screen for Szkolny.eu in phone settings.\n\nClick OK to open app settings now.</string> <string name="permissions_denied">You denied the required permissions for the application.\n\nIn order to grant the permission, open the Permissions screen for Szkolny.eu in phone settings.\n\nClick OK to open app settings now.</string>
<string name="permissions_required">Required permissions</string> <string name="permissions_required">Required permissions</string>
<string name="settings_register_hide_sticks_from_old">Hide sticks from old</string> <string name="settings_register_hide_sticks_from_old">Your mother won\'t see your F grades</string>
<string name="build_official">Official build</string>
<string name="build_platform_play">Google Play</string>
<string name="login_chooser_testing_title">Testing version</string>
<string name="login_chooser_mode_recommended">{cmd-information-outline} Recommended</string>
<string name="login_chooser_mode_dev_only">{cmd-android-studio} Developer version</string>
<string name="login_chooser_mode_testing">{cmd-alert-circle-outline} Testing version</string>
<string name="eggs">\???</string>
<string name="see_also">See also</string>
<string name="settings_about_github_text">Source code</string>
<string name="build_branch">Git branch</string>
<string name="build_platform_apk">.APK</string>
<string name="build_platform_unofficial">Unofficial (.APK)</string>
<string name="build_commit">Commit hash</string>
<string name="build_dirty">Unsaved changes</string>
<string name="build_tag">Last tag</string>
<string name="build_rev_count">Commits since last tag</string>
<string name="build_remote">Remote repository</string>
<string name="build_details">Build details</string>
<string name="build_dialog_open_repo">Check code</string>
<string name="profile_config_name_hint">Profile name</string>
<string name="profile_config_logout">Logout</string>
<string name="profile_config_sync_enabled">Synchronize this profile</string>
<string name="home_availability_update">Update</string>
<string name="update_available_later">Not now</string>
<string name="update_available_button">Update</string>
<string name="login_type_edudziennik">EduDziennik</string>
<string name="login_form_title_format">Log in - %s</string>
<string name="attendance_details_date">Date</string>
<string name="attendance_details_teacher">Teacher</string>
<string name="attendance_tab_days">By days</string>
<string name="attendance_tab_list">List</string>
<string name="attendance_lesson_number_format">lesson %d</string>
<string name="attendance_tab_types">By type</string>
<string name="settings_add_student_text">Add new student</string>
<string name="attendance_details_lesson_topic">Lesson subject</string>
<string name="register_unavailable_read_more">Read more</string>
<string name="build_version">App version</string>
<string name="settings_about_version_details_text">Version details</string>
<string name="build_date">Build date</string>
<string name="error_no_api_access">No API access</string>
<string name="attendance_details_time">Time</string>
<string name="settings_about_github_subtext">Help with app development on GitHub</string>
<string name="build_platform">Distribution</string>
<string name="build_validate_progress">Build verification in progress...</string>
<string name="attendance_tab_months">By months</string>
<string name="attendance_tab_summary">Summary</string>
<string name="attendance_details_type">Type</string>
<string name="home_availability_info">See more</string>
<string name="home_availability_title">Update available</string>
<string name="settings_about_version_details_subtext">Build details</string>
<string name="home_availability_text">Update app to the latest version - %s.</string>
<string name="update_available_title">App update available</string>
<string name="home_archive_close">Close archive</string>
<string name="home_archive_title">Archived profile</string>
<string name="profile_archived_title">Profile is archived</string>
<string name="profile_year_not_started_title">Holiday ;)</string>
<string name="profile_archiving_title">The end of the school year</string>
<string name="login_podlasie_logout_devices">Logout from other devices</string>
<string name="event_manual_no_profile">Student profile not found.</string>
<string name="settings_about_homepage_text">Go to application website</string>
<string name="settings_about_homepage_subtext">Get help or support authors</string>
<string name="build_invalid_title">Information about application version</string>
<string name="home_archive_close_no_target_title">No current profile</string>
<string name="login_platform_list_loading">Loading e-registers list...</string>
<string name="login_mode_podlasie_api">Log in using token</string>
<string name="login_mode_podlasie_api_guide">Provide mobile app token.</string>
<string name="attendance_config_title">Attendance configuration</string>
<string name="attendance_config_use_symbols">Show symbols and colors from e-register config</string>
<string name="attendance_empty_text">There are no absences here.</string>
<string name="grades_empty_text">There are no grades in this semester</string>
<string name="menu_attendance_config">Attendance settings</string>
<string name="login_chooser_title">What is your e-register at school?</string>
<string name="login_mode_librus_synergia">Log in using login and password</string>
<string name="login_mode_librus_synergia_hint">Use a login in form of \"9874123u\"</string>
<string name="login_mode_vulcan_api">Use token, symbol and PIN code</string>
<string name="login_mode_vulcan_api_hint">Register device on journal VULCAN® page</string>
<string name="login_mode_vulcan_web">Use e-mail/username and password</string>
<string name="edziennik_progress_login_podlasie_api">Logging in to PPE...</string>
<string name="login_type_podlasie">Podlaska Platforma Edukacyjna</string>
<string name="login_mode_edudziennik_web">Log in using e-mail and password</string>
<string name="edziennik_progress_login_vulcan_web_main">Logging in to VULCAN® register...</string>
<string name="login_mode_librus_jst">Login via VULCAN® platform</string>
<string name="login_mode_librus_email">Log in using e-mail</string>
<string name="attendance_details_id">Attendance ID</string>
<string name="login_mode_mobidziennik_web">Log in with the server name, login and password</string>
<string name="login_mode_vulcan_web_hint">Log in with the data that you provide on VULCAN® e-register website</string>
<string name="login_mode_mobidziennik_web_hint">Provide data, that you use on e-register website</string>
<string name="attendance_details_type_id">Base type ID</string>
<string name="attendance_details_is_counted">Counted to the stats?</string>
<string name="login_chooser_subtitle">Choose which e-register your school uses. If you have several accounts in different e-registers, you will be able to add them later.</string>
<string name="login_mode_librus_email_hint">You must have a LIBRUS® Rodzina account</string>
<string name="login_mode_librus_jst_hint">Only Oświata w Radomiu and Innowacyjny Tarnobrzeg</string>
<string name="login_platform_list_title">How do you log into the e-register?</string>
<string name="attendance_config_group_consecutive_days">Group consecutive days on the list</string>
<string name="attendance_config_use_symbols_hint">Visible when the list is expanded</string>
<string name="attendance_config_show_presence_in_month">Display attendance in months view</string>
<string name="attendance_percentage_format">%.2f%%</string>
<string name="attendance_period_summary_format">Attendance during this period: %.2f%%</string>
<string name="settings_add_student_subtext">Log in child/parent account in app</string>
<string name="login_mode_edudziennik_web_guide">Enter the e-mail address and password that you use to log in to the browser on the EduDziennik website.</string>
<string name="profile_year_not_started_format">Probably the school year for this student has not yet started (will start %s). Please try to sync later.</string>
<string name="profile_archiving_format">The school year ended on %s. Student data from the previous year will be moved to the archive for later review.</string>
<string name="login_copyright_notice">Trademarks featured in this application belong to their rightful owners and are used for informational purposes only.</string>
<string name="home_archive_text">You are viewing a student\'s data from the school year %d/%d.</string>
<string name="login_mode_edudziennik_web_hint">Use data, that you enter on the e-register website</string>
<string name="permissions_qr_scanner">To be able to scan the QR code, you need to grant access to the camera.\n\nClick OK to grant permissions.</string>
<string name="rate_snackbar_negative_message">It\'s a pity, the opinions of others help me develop the application.</string>
<string name="login_chooser_testing_text">The selected login method is still being tested and may not work properly. If you have problems with the app, please choose the recommended login method.</string>
<string name="update_available_fallback">You have an outdated version of the Szkolny.eu application. You need to update the app to continue to sync data.</string>
<string name="update_available_format">You are using an old version of the Szkolny.eu application (%s). To use the app and ensure the best performance, please upgrade to %s. Change log: %s</string>
<string name="profile_archived_text">You are viewing a student\'s data from the previous school year (%d /%d). Syncing and downloading of messages and some homework have been disabled. To open a student\'s profile for the current year, select Close Archive on the home page.</string>
<string name="login_mode_librus_jst_guide">Log in to LIBRUS® Synergia on your computer, select the Mobile Applications tab, then enter the received Token and PIN below.</string>
<string name="login_mode_librus_synergia_guide">Enter the login received from the school with which you log into LIBRUS® Synergia (purple form).\n\nIt is recommended to log in with the LIBRUS® Family account (using e-mail) in the previous step.</string>
<string name="login_mode_librus_email_guide">Log in with your LIBRUS® Rodzina account, which works in the official LIBRUS® application and on the website portal.librus.pl, in the blue form. \n\nIf you do not have a LIBRUS® Rodzina account, you can create one at https://portal.librus.pl/rodzina/register.</string>
<string name="login_platform_list_loading_timeout">If it takes too long, please check your internet connection and restart the application.</string>
<string name="login_platform_list_subtitle">Select which image corresponds to the one you see when logging in to your log website. If your school doesn\'t use any of these city platforms, choose the first option.</string>
<string name="build_invalid_remote_no_commit">You have an application build with unpublished changes. The build is in the repository:\n%1$s (%2$s)\nwhich is private or does not contain the latest changes.\n\nFor security reasons and compliance with the license, the use of the application has been blocked.</string>
<string name="login_mode_vulcan_api_guide">Log in to the VULCAN® log on your computer, select the Mobile Access tab, click the Register mobile device button. Enter the received Token, Symbol and PIN in the fields below.</string>
<string name="login_mode_vulcan_web_guide">Enter the data that you log in to the VULCAN® log website or the city platform.</string>
<string name="build_invalid_official_unsigned">You cannot modify this type of compilation of the Szkolny.eu application.\n\nTo make your own changes, please use the source code available on GitHub and see the README and license information.\n\nhttps://szkolny.eu/github/android</string>
<string name="build_invalid_unstaged_changes">This build contains changes not committed to any revision. Save and publish all changes before \"release\".\n\nFor security reasons and for compliance with the license, the use of the application has been blocked.</string>
<string name="build_invalid_debug">You are using a \"debug\" build. This information will only be displayed once for the current device.</string>
<string name="build_valid_unofficial">You are using an unofficial compilation of the Szkolny.eu application. We recommend that you use only the official versions of the application.\n\nLast changes in this version were made by:\n%3$s\nin the repository:\n%1$s (%2$s).\n\nThis window will not reappear.</string>
<string name="build_invalid_no_commit_hash">The hash of the current commit was not found. Check Gradle configuration.</string>
<string name="home_archive_close_no_target_text">Child %s does not have a profile on this account in the current school year. Probably this profile has been deleted or the student no longer attends this class.\n\nTo go to the current profile, select a student from the list or log in to their account with the Add student button.</string>
<string name="build_invalid_no_remote_repo">A reference to a remote repository was not found. Make sure you are using the official repository fork and verify your Gradle configuration.</string>
<string name="login_mode_mobidziennik_web_guide">"Enter the data you use to log in to the MobiDziennik website. As the server address, you can enter the address of the website where you have MobiDziennik. "</string>
<string name="permissions_generate_timetable">In order to be able to save the generated timetable, you must grant access rights to the device\'s memory.\n\nClick OK to grant permissions.</string>
<string name="login_summary_account_child">(Child)</string>
<string name="login_summary_account_parent">(Parent)</string>
</resources> </resources>

View file

@ -921,7 +921,7 @@
<string name="settings_about_licenses_text">Licencje open-source</string> <string name="settings_about_licenses_text">Licencje open-source</string>
<string name="settings_about_privacy_policy_text">Polityka prywatności</string> <string name="settings_about_privacy_policy_text">Polityka prywatności</string>
<string name="settings_card_register_title">E-dziennik</string> <string name="settings_card_register_title">E-dziennik</string>
<string name="settings_about_title_subtext">© Kuba Szczodrzyński &amp;&amp; Kacper Ziubryniewicz\nwrzesień 2018 - kwiecień 2021</string> <string name="settings_about_title_subtext">© Kuba Szczodrzyński &amp;&amp; Kacper Ziubryniewicz\nwrzesień 2018 - luty 2021</string>
<string name="settings_about_update_subtext">Kliknij, aby sprawdzić aktualizacje</string> <string name="settings_about_update_subtext">Kliknij, aby sprawdzić aktualizacje</string>
<string name="settings_about_update_text">Aktualizacja</string> <string name="settings_about_update_text">Aktualizacja</string>
<string name="settings_about_version_text">Wersja</string> <string name="settings_about_version_text">Wersja</string>
@ -1346,6 +1346,10 @@
<string name="login_mode_vulcan_web_guide">Podaj dane, którymi logujesz się na stronie internetowej dziennika VULCAN® lub na miejskiej platformie.</string> <string name="login_mode_vulcan_web_guide">Podaj dane, którymi logujesz się na stronie internetowej dziennika VULCAN® lub na miejskiej platformie.</string>
<string name="login_mode_mobidziennik_web_guide">Podaj dane, których używasz do logowania na stronie MobiDziennika. Jako adres serwera możesz wpisać adres strony internetowej, na której masz MobiDziennik.</string> <string name="login_mode_mobidziennik_web_guide">Podaj dane, których używasz do logowania na stronie MobiDziennika. Jako adres serwera możesz wpisać adres strony internetowej, na której masz MobiDziennik.</string>
<string name="edziennik_progress_login_vulcan_web_main">Logowanie do dziennika VULCAN®...</string> <string name="edziennik_progress_login_vulcan_web_main">Logowanie do dziennika VULCAN®...</string>
<string name="login_type_idziennik">iDziennik Progman / iUczniowie</string>
<string name="login_mode_idziennik_web">Zaloguj używając nazwy użytkownika i hasła</string>
<string name="login_mode_idziennik_web_hint">Podaj dane, których używasz na stronie internetowej e-dziennika</string>
<string name="login_mode_idziennik_web_guide">Użyj danych, które wpisujesz w formularz na stronie iDziennika. Jeśli nie pamiętasz hasła, wejdź na http://iuczniowie.progman.pl/ i kliknij przycisk \"Zapomniałem hasła\".</string>
<string name="login_type_edudziennik">EduDziennik</string> <string name="login_type_edudziennik">EduDziennik</string>
<string name="login_mode_edudziennik_web">Zaloguj używając e-maila i hasła</string> <string name="login_mode_edudziennik_web">Zaloguj używając e-maila i hasła</string>
<string name="login_mode_edudziennik_web_hint">Użyj danych, które podajesz na stronie internetowej e-dziennika</string> <string name="login_mode_edudziennik_web_hint">Użyj danych, które podajesz na stronie internetowej e-dziennika</string>
@ -1415,7 +1419,7 @@
<string name="build_invalid_official_unsigned">Nie możesz modyfikować tego rodzaju kompilacji aplikacji Szkolny.eu.\n\nAby wprowadzić własne zmiany, skorzystaj z kodu źródłowego dostępnego na GitHubie oraz zapoznaj się z README i informacją o licencji.\n\nhttps://szkolny.eu/github/android</string> <string name="build_invalid_official_unsigned">Nie możesz modyfikować tego rodzaju kompilacji aplikacji Szkolny.eu.\n\nAby wprowadzić własne zmiany, skorzystaj z kodu źródłowego dostępnego na GitHubie oraz zapoznaj się z README i informacją o licencji.\n\nhttps://szkolny.eu/github/android</string>
<string name="build_invalid_unstaged_changes">Ta kompilacja zawiera zmiany niezatwierdzone do żadnej rewizji. Zapisz oraz opublikuj wszystkie zmiany przed wydaniem wersji \"release\".\n\nDla bezpieczeństwa oraz ze względów zgodności z licencją, korzystanie z aplikacji zostało zablokowane.</string> <string name="build_invalid_unstaged_changes">Ta kompilacja zawiera zmiany niezatwierdzone do żadnej rewizji. Zapisz oraz opublikuj wszystkie zmiany przed wydaniem wersji \"release\".\n\nDla bezpieczeństwa oraz ze względów zgodności z licencją, korzystanie z aplikacji zostało zablokowane.</string>
<string name="build_invalid_debug">Korzystasz z kompilacji typu \"debug\". Ta informacja zostanie wyświetlona tylko jeden raz dla aktualnego urządzenia.</string> <string name="build_invalid_debug">Korzystasz z kompilacji typu \"debug\". Ta informacja zostanie wyświetlona tylko jeden raz dla aktualnego urządzenia.</string>
<string name="build_valid_unofficial">Korzystasz z nieoficjalnej kompilacji aplikacji Szkolny.eu. Zalecamy używanie wyłącznie oficjalnych wersji aplikacji.\n\nOstatnie zmiany w tej wersji zostały wprowadzone przez:\n%3$s\nw repozytorium:\n%1$s (%2$s).\n\nTo okno nie wyświetli się ponownie.</string> <string name="build_valid_unofficial">Korzystasz z nieoficjalnej kompilacji aplikacji Szkolny.eu. Zalecamy używanie wyłącznie oficjalnych wersji aplikacji.\n\nOstatnie zmiany w tej wersji zostały wprowadzone przez %3$s w repozytorium %2$s (%1$s).\n\nTo okno nie wyświetli się ponownie.</string>
<string name="build_invalid_title">Informacja dotycząca wersji aplikacji</string> <string name="build_invalid_title">Informacja dotycząca wersji aplikacji</string>
<string name="settings_about_version_details_text">Szczegóły wersji</string> <string name="settings_about_version_details_text">Szczegóły wersji</string>
<string name="settings_about_version_details_subtext">Informacje o kompilacji</string> <string name="settings_about_version_details_subtext">Informacje o kompilacji</string>
@ -1423,6 +1427,4 @@
<string name="error_no_api_access">Brak dostępu do API</string> <string name="error_no_api_access">Brak dostępu do API</string>
<string name="build_date">Data kompilacji</string> <string name="build_date">Data kompilacji</string>
<string name="permissions_generate_timetable">Aby móc zapisać wygenerowany plan lekcji musisz przyznać uprawnienia dostępu do pamięci urządzenia.\n\nKliknij OK, aby przyznać uprawnienia.</string> <string name="permissions_generate_timetable">Aby móc zapisać wygenerowany plan lekcji musisz przyznać uprawnienia dostępu do pamięci urządzenia.\n\nKliknij OK, aby przyznać uprawnienia.</string>
<string name="privacy_policy_dialog_html"><![CDATA[Korzystając z aplikacji potwierdzasz <a href="https://szkolny.eu/privacy-policy">przeczytanie Polityki prywatności</a> i akceptujesz jej postanowienia.<br /><br />Autorzy aplikacji nie biorą odpowiedzialności za korzystanie z aplikacji Szkolny.eu.]]></string>
<string name="login_chooser_version_format">Szkolny.eu v%s\n%s</string>
</resources> </resources>

View file

@ -5,8 +5,8 @@ buildscript {
kotlin_version = '1.4.31' kotlin_version = '1.4.31'
release = [ release = [
versionName: "4.7.1", versionName: "4.7-rc.1",
versionCode: 4070199 versionCode: 4070010
] ]
setup = [ setup = [