diff --git a/.github/readme-banner.png b/.github/readme-banner.png
new file mode 100644
index 00000000..03e78742
Binary files /dev/null and b/.github/readme-banner.png differ
diff --git a/.github/utils/.gitignore b/.github/utils/.gitignore
new file mode 100644
index 00000000..d50a09fc
--- /dev/null
+++ b/.github/utils/.gitignore
@@ -0,0 +1,2 @@
+.env
+__pycache__/
diff --git a/.github/utils/_get_password.py b/.github/utils/_get_password.py
new file mode 100644
index 00000000..33071b6c
--- /dev/null
+++ b/.github/utils/_get_password.py
@@ -0,0 +1,57 @@
+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])
diff --git a/.github/utils/_utils.py b/.github/utils/_utils.py
new file mode 100644
index 00000000..09b59f4a
--- /dev/null
+++ b/.github/utils/_utils.py
@@ -0,0 +1,142 @@
+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"
(.+?) ", changelog).group(1)
+ content = re.search(r"(?s)", changelog).group(1).strip()
+ content = "\n".join(line.strip() for line in content.split("\n"))
+
+ if format != "html":
+ content = content.replace("", "- ")
+ content = content.replace(" ", "\n")
+ if format == "markdown":
+ content = re.sub(r"(.+?) ", "__\\1__", content)
+ content = re.sub(r"(.+?) ", "*\\1*", content)
+ content = re.sub(r"(.+?) ", "**\\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" {commit[3]} - {commit[0]} ")
+ 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)
diff --git a/.github/utils/bump_nightly.py b/.github/utils/bump_nightly.py
new file mode 100644
index 00000000..88b4798c
--- /dev/null
+++ b/.github/utils/bump_nightly.py
@@ -0,0 +1,69 @@
+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 ")
+ 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"(.+?) ", f"{version_name} ", changelog)
+ changelog = re.sub(r"(?s)", f"", changelog)
+
+ with open(
+ f"{project_dir}/app/src/main/assets/pl-changelog.html", "w", encoding="utf-8"
+ ) as f:
+ f.write(changelog)
diff --git a/.github/utils/bump_version.py b/.github/utils/bump_version.py
new file mode 100644
index 00000000..80d36519
--- /dev/null
+++ b/.github/utils/bump_version.py
@@ -0,0 +1,41 @@
+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")
diff --git a/.github/utils/extract_changelogs.py b/.github/utils/extract_changelogs.py
new file mode 100644
index 00000000..25d346c1
--- /dev/null
+++ b/.github/utils/extract_changelogs.py
@@ -0,0 +1,72 @@
+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 ")
+ 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")
diff --git a/.github/utils/rename_artifacts.py b/.github/utils/rename_artifacts.py
new file mode 100644
index 00000000..4eeabfaf
--- /dev/null
+++ b/.github/utils/rename_artifacts.py
@@ -0,0 +1,26 @@
+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 ")
+ 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)
diff --git a/.github/utils/save_version.py b/.github/utils/save_version.py
new file mode 100644
index 00000000..01de17a7
--- /dev/null
+++ b/.github/utils/save_version.py
@@ -0,0 +1,122 @@
+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 ")
+ 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)
diff --git a/.github/utils/sign.py b/.github/utils/sign.py
new file mode 100644
index 00000000..026d819b
--- /dev/null
+++ b/.github/utils/sign.py
@@ -0,0 +1,84 @@
+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 [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,
+ )
diff --git a/.github/utils/webhook_discord.py b/.github/utils/webhook_discord.py
new file mode 100644
index 00000000..3c1404ae
--- /dev/null
+++ b/.github/utils/webhook_discord.py
@@ -0,0 +1,118 @@
+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 ")
+ 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,
+ )
diff --git a/.github/workflows/build-nightly-apk.yml b/.github/workflows/build-nightly-apk.yml
new file mode 100644
index 00000000..0951149e
--- /dev/null
+++ b/.github/workflows/build-nightly-apk.yml
@@ -0,0 +1,154 @@
+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 11
+ uses: actions/setup-java@v2
+ with:
+ distribution: 'temurin'
+ java-version: '11'
+ - 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
+ uses: gradle/gradle-build-action@v2
+ with:
+ arguments: 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
diff --git a/.github/workflows/build-release-aab-play.yml b/.github/workflows/build-release-aab-play.yml
new file mode 100644
index 00000000..95fec5cb
--- /dev/null
+++ b/.github/workflows/build-release-aab-play.yml
@@ -0,0 +1,131 @@
+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 11
+ uses: actions/setup-java@v2
+ with:
+ distribution: 'temurin'
+ java-version: '11'
+ - 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
+ uses: gradle/gradle-build-action@v2
+ with:
+ arguments: 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
diff --git a/.github/workflows/build-release-apk.yml b/.github/workflows/build-release-apk.yml
new file mode 100644
index 00000000..5ca055b9
--- /dev/null
+++ b/.github/workflows/build-release-apk.yml
@@ -0,0 +1,154 @@
+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 11
+ uses: actions/setup-java@v2
+ with:
+ distribution: 'temurin'
+ java-version: '11'
+ - 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
+ uses: gradle/gradle-build-action@v2
+ with:
+ arguments: 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
diff --git a/.gitignore b/.gitignore
index 07e9f679..375b15e2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -265,3 +265,4 @@ fabric.properties
# End of https://www.toptal.com/developers/gitignore/api/android,androidstudio,gradle,java,kotlin
signatures/
+.idea/*.xml
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
index 29204aff..ab75be24 100644
--- a/.idea/codeStyles/Project.xml
+++ b/.idea/codeStyles/Project.xml
@@ -1,6 +1,7 @@
+
@@ -15,6 +16,7 @@
xmlns:android
+
^$
@@ -25,6 +27,7 @@
xmlns:.*
+
^$
@@ -36,6 +39,7 @@
.*:id
+
http://schemas.android.com/apk/res/android
@@ -46,6 +50,7 @@
.*:name
+
http://schemas.android.com/apk/res/android
@@ -56,6 +61,7 @@
name
+
^$
@@ -66,6 +72,7 @@
style
+
^$
@@ -76,6 +83,7 @@
.*
+
^$
@@ -87,6 +95,7 @@
.*
+
http://schemas.android.com/apk/res/android
@@ -98,6 +107,7 @@
.*
+
.*
diff --git a/.idea/copyright/Antoni.xml b/.idea/copyright/Antoni.xml
new file mode 100644
index 00000000..438a39d6
--- /dev/null
+++ b/.idea/copyright/Antoni.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/dictionaries/Kuba.xml b/.idea/dictionaries/Kuba.xml
index 7910687c..592a5d5e 100644
--- a/.idea/dictionaries/Kuba.xml
+++ b/.idea/dictionaries/Kuba.xml
@@ -5,6 +5,7 @@
ciasteczko
csrf
edziennik
+ elearning
gson
hebe
idziennik
diff --git a/.idea/discord.xml b/.idea/discord.xml
deleted file mode 100644
index a04e4e5f..00000000
--- a/.idea/discord.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
deleted file mode 100644
index 0dd4b354..00000000
--- a/.idea/kotlinc.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml
deleted file mode 100644
index e497da99..00000000
--- a/.idea/runConfigurations.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/README.md b/README.md
index 0726f161..1646ab6b 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,7 @@
-# Szkolny.eu
-
-Nieoficjalna aplikacja do obsługi najpopularniejszych dzienników elektronicznych w Polsce.
-
+
+
[](https://szkolny.eu/discord)
[](https://szkolny.eu/)
[](https://szkolny.eu/facebook)
@@ -12,11 +10,15 @@ Nieoficjalna aplikacja do obsługi najpopularniejszych dzienników elektroniczny
[](https://github.com/szkolny-eu/szkolny-android/releases/latest)

+[](https://github.com/szkolny-eu/szkolny-android/actions/workflows/build-release-apk.yml)
+[](https://github.com/szkolny-eu/szkolny-android/actions/workflows/build-release-aab-play.yml)
+[](https://github.com/szkolny-eu/szkolny-android/actions/workflows/build-nightly-apk.yml)
+
## Ważna informacja
-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® 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 2020 r. **firma Librus zabroniła nam** publikowania w sklepie Google Play naszej aplikacji z obsługą dziennika Librus® 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.__
@@ -30,17 +32,17 @@ Szkolny.eu jest nieoficjalną aplikacją, umożliwiającą rodzicom i uczniom do
- plan lekcji, terminarz, oceny, wiadomości, zadania domowe, uwagi, frekwencja
- 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**
- **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
-- 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
- 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 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)
@@ -53,7 +55,7 @@ Najnowsze wersje możesz pobrać z Google Play lub bezpośrednio z naszej strony
### 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.
@@ -68,15 +70,15 @@ __Jeśli masz jakieś pytania, zapraszamy na [nasz serwer Discord](https://szkol
## Licencja
Szkolny.eu publikowany jest na licencji [GNU GPLv3](LICENSE). W szczególności, deweloper:
-- może modyfikować oraz usprawniać kod aplikacji
-- może dystrybuować wersje produkcyjne
-- musi opublikować wszelkie wprowadzone zmiany, tzn. publiczny fork tego repozytorium
-- nie może zmieniać licencji ani copyrightu aplikacji
+- Może modyfikować oraz usprawniać kod aplikacji
+- Może dystrybuować wersje produkcyjne
+- Musi opublikować wszelkie wprowadzone zmiany, tzn. publiczny fork tego repozytorium
+- Nie może zmieniać licencji ani copyrightu aplikacji
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.**
diff --git a/app/build.gradle b/app/build.gradle
index 7d22ea41..7635b8c1 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,6 +1,7 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
+apply plugin: 'kotlin-parcelize'
apply plugin: 'com.google.gms.google-services'
apply plugin: 'com.google.firebase.crashlytics'
@@ -18,8 +19,10 @@ android {
versionName release.versionName
buildConfigField "java.util.Map", "GIT_INFO", gitInfoMap
- buildConfigField "long", "BUILD_TIMESTAMP", String.valueOf(System.currentTimeMillis())
buildConfigField "String", "VERSION_BASE", "\"${release.versionName}\""
+ manifestPlaceholders = [
+ buildTimestamp: String.valueOf(System.currentTimeMillis())
+ ]
multiDexEnabled = true
@@ -28,11 +31,21 @@ android {
cppFlags "-std=c++11"
}
}
+
+ kapt {
+ arguments {
+ arg("room.schemaLocation", "$projectDir/schemas")
+ }
+ }
}
buildTypes {
debug {
+ getIsDefault().set(true)
minifyEnabled = false
+ manifestPlaceholders = [
+ buildTimestamp: 0
+ ]
}
release {
minifyEnabled = true
@@ -43,15 +56,27 @@ android {
}
flavorDimensions "platform"
productFlavors {
- main {
- versionName gitInfo.versionHuman
+ unofficial {
+ getIsDefault().set(true)
+ versionName "${release.versionName}-${gitInfo.versionSuffix}"
}
official {}
play {}
}
variantFilter { variant ->
def flavors = variant.flavors*.name
- setIgnore(variant.buildType.name == "debug" && !flavors.contains("main"))
+ setIgnore(variant.buildType.name == "debug" && !flavors.contains("unofficial") || flavors.contains("main"))
+ }
+ sourceSets {
+ unofficial {
+ java.srcDirs = ["src/main/java", "src/play-not/java"]
+ }
+ official {
+ java.srcDirs = ["src/main/java", "src/play-not/java"]
+ }
+ play {
+ java.srcDirs = ["src/main/java", "src/play/java"]
+ }
}
defaultConfig {
@@ -98,7 +123,10 @@ tasks.whenTaskAdded { task ->
if (flavor != "") {
tasks.create(renameTaskName, Copy) {
- from file("${projectDir}/${flavor}/release/"), file("${buildDir}/outputs/mapping/${flavor}Release/")
+ from file("${projectDir}/${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"
destinationDir file("${projectDir}/release/")
rename ".+?\\.(.+)", "Edziennik_${android.defaultConfig.versionName}_${flavor}." + '$1'
@@ -115,25 +143,25 @@ dependencies {
coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:1.1.5"
// Android Jetpack
- implementation "androidx.appcompat:appcompat:1.2.0"
+ implementation "androidx.appcompat:appcompat:1.3.1"
implementation "androidx.cardview:cardview:1.0.0"
- implementation "androidx.constraintlayout:constraintlayout:2.0.4"
- implementation "androidx.core:core-ktx:1.3.2"
- implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.0"
- implementation "androidx.navigation:navigation-fragment-ktx:2.3.4"
- implementation "androidx.recyclerview:recyclerview:1.1.0"
- implementation "androidx.room:room-runtime:2.2.6"
- implementation "androidx.work:work-runtime-ktx:2.5.0"
- kapt "androidx.room:room-compiler:2.2.6"
+ implementation "androidx.constraintlayout:constraintlayout:2.1.1"
+ implementation "androidx.core:core-ktx:1.6.0"
+ implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1"
+ implementation "androidx.navigation:navigation-fragment-ktx:2.3.5"
+ implementation "androidx.recyclerview:recyclerview:1.2.1"
+ implementation "androidx.room:room-runtime:2.3.0"
+ implementation "androidx.work:work-runtime-ktx:2.6.0"
+ kapt "androidx.room:room-compiler:2.3.0"
// Google design libs
- implementation "com.google.android.material:material:1.3.0"
- implementation "com.google.android:flexbox:2.0.1"
+ implementation "com.google.android.material:material:1.4.0"
+ implementation "com.google.android.flexbox:flexbox:3.0.0"
// Play Services/Firebase
- implementation "com.google.android.gms:play-services-wearable:17.0.0"
- implementation "com.google.firebase:firebase-core:18.0.2"
- implementation "com.google.firebase:firebase-crashlytics:17.4.0"
+ implementation "com.google.android.gms:play-services-wearable:17.1.0"
+ implementation "com.google.firebase:firebase-core:19.0.2"
+ implementation "com.google.firebase:firebase-crashlytics:18.2.3"
implementation("com.google.firebase:firebase-messaging") { version { strictly "20.1.3" } }
// OkHttp, Retrofit, Gson, Jsoup
@@ -141,20 +169,22 @@ dependencies {
implementation "com.squareup.retrofit2:retrofit:2.9.0"
implementation "com.squareup.retrofit2:converter-gson:2.9.0"
implementation "com.squareup.retrofit2:converter-scalars:2.9.0"
- implementation 'com.google.code.gson:gson:2.8.6'
- implementation "org.jsoup:jsoup:1.13.1"
+ implementation 'com.google.code.gson:gson:2.8.8'
+ implementation 'org.jsoup:jsoup:1.14.3'
implementation "pl.droidsonroids:jspoon:1.3.2"
implementation "pl.droidsonroids.retrofit2:converter-jspoon:1.3.2"
// Szkolny.eu libraries/forks
- implementation "eu.szkolny:agendacalendarview:1799f8ef47"
+ implementation "eu.szkolny:android-snowfall:1ca9ea2da3"
+ implementation "eu.szkolny:agendacalendarview:1.0.4"
implementation "eu.szkolny:cafebar:5bf0c618de"
implementation "eu.szkolny.fslogin:lib:2.0.0"
implementation "eu.szkolny:material-about-library:1d5ebaf47c"
implementation "eu.szkolny:mhttp:af4b62e6e9"
implementation "eu.szkolny:nachos:0e5dfcaceb"
implementation "eu.szkolny.selective-dao:annotation:27f8f3f194"
- implementation "eu.szkolny:ssl-provider:1.0.0"
+ officialImplementation "eu.szkolny:ssl-provider:1.0.0"
+ unofficialImplementation "eu.szkolny:ssl-provider:1.0.0"
implementation "pl.szczodrzynski:navlib:0.8.0"
implementation "pl.szczodrzynski:numberslidingpicker:2921225f76"
implementation "pl.szczodrzynski:recyclertablayout:700f980584"
@@ -162,34 +192,34 @@ dependencies {
kapt "eu.szkolny.selective-dao:codegen:27f8f3f194"
// Iconics & related
- implementation "com.mikepenz:iconics-core:5.3.0-b01"
- implementation "com.mikepenz:iconics-views:5.3.0-b01"
+ implementation "com.mikepenz:iconics-core:5.3.2"
+ implementation "com.mikepenz:iconics-views:5.3.2"
implementation "com.mikepenz:community-material-typeface:5.8.55.0-kotlin@aar"
- implementation "eu.szkolny:szkolny-font:1.3"
+ implementation "eu.szkolny:szkolny-font:77e33acc2a"
// Other dependencies
implementation "cat.ereza:customactivityoncrash:2.3.0"
- implementation "com.applandeo:material-calendar-view:1.5.0"
+ implementation "com.android.volley:volley:1.2.1"
implementation "com.daimajia.swipelayout:library:1.2.0@aar"
- implementation "com.github.antonKozyriatskyi:CircularProgressIndicator:1.2.2"
- implementation "com.github.bassaer:chatmessageview:2.0.1"
- implementation "com.github.CanHub:Android-Image-Cropper:2.2.2"
- implementation "com.github.ChuckerTeam.Chucker:library:3.0.1"
- implementation "com.github.jetradarmobile:android-snowfall:1.2.0"
- implementation "com.github.wulkanowy.uonet-request-signer:hebe-jvm:a99ca50a31"
- implementation("com.heinrichreimersoftware:material-intro") { version { strictly "1.5.8" } }
- implementation "com.hypertrack:hyperlog:0.0.10"
+ implementation "com.github.Applandeo:Material-Calendar-View:15de569cbc" // https://github.com/Applandeo/Material-Calendar-View
+ implementation "com.github.CanHub:Android-Image-Cropper:2.2.2" // https://github.com/CanHub/Android-Image-Cropper
+ implementation "com.github.ChuckerTeam.Chucker:library:3.0.1" // https://github.com/ChuckerTeam/chucker
+ implementation "com.github.antonKozyriatskyi:CircularProgressIndicator:1.2.2" // https://github.com/antonKozyriatskyi/CircularProgressIndicator
+ implementation "com.github.bassaer:chatmessageview:2.0.1" // https://github.com/bassaer/ChatMessageView
+ implementation "com.github.hypertrack:hyperlog-android:0.0.10" // https://github.com/hypertrack/hyperlog-android
+ implementation "com.github.smuyyh:JsonViewer:V1.0.6" // https://github.com/smuyyh/JsonViewer
+ implementation "com.github.underwindfall.PowerPermission:powerpermission-coroutines:1.4.0" // https://github.com/underwindfall/PowerPermission
+ implementation "com.github.underwindfall.PowerPermission:powerpermission:1.4.0" // https://github.com/underwindfall/PowerPermission
+ implementation "com.github.wulkanowy.uonet-request-signer:hebe-jvm:a99ca50a31" // https://github.com/wulkanowy/uonet-request-signer
implementation "com.jaredrummler:colorpicker:1.1.0"
- implementation "com.qifan.powerpermission:powerpermission-coroutines:1.3.0"
- implementation "com.qifan.powerpermission:powerpermission:1.3.0"
- implementation "com.yuyh.json:jsonviewer:1.0.6"
implementation "io.coil-kt:coil:1.1.1"
implementation "me.dm7.barcodescanner:zxing:1.9.8"
implementation "me.grantland:autofittextview:0.2.1"
implementation "me.leolin:ShortcutBadger:1.1.22@aar"
implementation "org.greenrobot:eventbus:3.2.0"
+ implementation("com.heinrichreimersoftware:material-intro") { version { strictly "1.5.8" } }
implementation("pl.droidsonroids.gif:android-gif-drawable") { version { strictly "1.2.15" } }
// Debug-only dependencies
- debugImplementation "com.amitshekhar.android:debug-db:1.0.5"
+ debugImplementation "com.github.amitshekhariitbhu.Android-Debug-Database:debug-db:v1.0.6"
}
diff --git a/app/git-info.gradle b/app/git-info.gradle
index 929d5307..3577f668 100644
--- a/app/git-info.gradle
+++ b/app/git-info.gradle
@@ -84,7 +84,7 @@ private def buildGitInfo() {
.stream()
.map {
it.name + "(" + it.URIs.stream()
- .map { it.rawPath }
+ .map { it.rawPath.stripMargin('/').replace(".git", "") }
.toArray()
.join(", ") + ")"
}
@@ -97,18 +97,17 @@ private def buildGitInfo() {
def tag = getLastTag(repo, git, head)
def tagName = tag[1]
def tagRevCount = tag[2]
- def versionName = tagName.replace("v", "")
def result = [
- hash : head.objectId.name,
- branch : repo.branch,
- dirty : dirty,
- remotes : remotes,
- unstaged : status.uncommittedChanges.join("; "),
- tag : tagName,
- revCount : tagRevCount,
- version : """$tagName-$tagRevCount-g${head.objectId.name.substring(0, 8)}""" + (dirty ? ".dirty" : ""),
- versionHuman: """$versionName-${repo.branch.replace("/", "_")}""" + (dirty ? ".dirty" : "")
+ hash : head.objectId.name,
+ branch : repo.branch,
+ dirty : dirty,
+ remotes : remotes,
+ unstaged : status.uncommittedChanges.join("; "),
+ tag : tagName,
+ revCount : tagRevCount,
+ version : """$tagName-$tagRevCount-g${head.objectId.name.substring(0, 8)}""" + (dirty ? ".dirty" : ""),
+ versionSuffix : """${repo.branch.replace("/", "_")}""" + (dirty ? ".dirty" : "")
]
return result
}
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
index 4d84d11d..1d72bf89 100644
--- a/app/proguard-rules.pro
+++ b/app/proguard-rules.pro
@@ -25,15 +25,17 @@
-keep class pl.szczodrzynski.edziennik.data.db.entity.Event { *; }
-keep class pl.szczodrzynski.edziennik.data.db.full.EventFull { *; }
-keep class pl.szczodrzynski.edziennik.data.db.entity.FeedbackMessage { *; }
--keep class pl.szczodrzynski.edziennik.ui.modules.home.HomeCardModel { *; }
+-keep class pl.szczodrzynski.edziennik.data.db.entity.Note { *; }
+-keep class pl.szczodrzynski.edziennik.ui.home.HomeCardModel { *; }
-keepclassmembers class pl.szczodrzynski.edziennik.ui.widgets.WidgetConfig { public *; }
-keepnames class pl.szczodrzynski.edziennik.ui.widgets.timetable.WidgetTimetableProvider
-keepnames class pl.szczodrzynski.edziennik.ui.widgets.notifications.WidgetNotificationsProvider
-keepnames class pl.szczodrzynski.edziennik.ui.widgets.luckynumber.WidgetLuckyNumberProvider
-keepnames class androidx.appcompat.view.menu.MenuBuilder { setHeaderTitleInt(java.lang.CharSequence); }
--keepclassmembernames class androidx.appcompat.view.menu.StandardMenuPopup { private *; }
-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.MenuItemImpl { private *; }
-keepclassmembernames class com.mikepenz.materialdrawer.widget.MiniDrawerSliderView { private *; }
@@ -66,7 +68,7 @@
-keepclassmembers class pl.szczodrzynski.edziennik.data.api.szkolny.request.** { *; }
-keepclassmembers class pl.szczodrzynski.edziennik.data.api.szkolny.response.** { *; }
--keepclassmembernames class pl.szczodrzynski.edziennik.ui.modules.login.LoginInfo$Platform { *; }
+-keepclassmembernames class pl.szczodrzynski.edziennik.ui.login.LoginInfo$Platform { *; }
-keepclassmembernames class pl.szczodrzynski.fslogin.realm.RealmData { *; }
-keepclassmembernames class pl.szczodrzynski.fslogin.realm.RealmData$Type { *; }
diff --git a/app/schemas/pl.szczodrzynski.edziennik.data.db.AppDb/97.json b/app/schemas/pl.szczodrzynski.edziennik.data.db.AppDb/97.json
new file mode 100644
index 00000000..37b6d0f4
--- /dev/null
+++ b/app/schemas/pl.szczodrzynski.edziennik.data.db.AppDb/97.json
@@ -0,0 +1,2293 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 97,
+ "identityHash": "08a8998e311e4e62a9e9554132b5c011",
+ "entities": [
+ {
+ "tableName": "grades",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`profileId` INTEGER NOT NULL, `gradeId` INTEGER NOT NULL, `gradeName` TEXT NOT NULL, `gradeType` INTEGER NOT NULL, `gradeValue` REAL NOT NULL, `gradeWeight` REAL NOT NULL, `gradeColor` INTEGER NOT NULL, `gradeCategory` TEXT, `gradeDescription` TEXT, `gradeComment` TEXT, `gradeSemester` INTEGER NOT NULL, `teacherId` INTEGER NOT NULL, `subjectId` INTEGER NOT NULL, `addedDate` INTEGER NOT NULL, `gradeValueMax` REAL, `gradeClassAverage` REAL, `gradeParentId` INTEGER, `gradeIsImprovement` INTEGER NOT NULL, `keep` INTEGER NOT NULL, PRIMARY KEY(`profileId`, `gradeId`))",
+ "fields": [
+ {
+ "fieldPath": "profileId",
+ "columnName": "profileId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "gradeId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "gradeName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "gradeType",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "value",
+ "columnName": "gradeValue",
+ "affinity": "REAL",
+ "notNull": true
+ },
+ {
+ "fieldPath": "weight",
+ "columnName": "gradeWeight",
+ "affinity": "REAL",
+ "notNull": true
+ },
+ {
+ "fieldPath": "color",
+ "columnName": "gradeColor",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "category",
+ "columnName": "gradeCategory",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "description",
+ "columnName": "gradeDescription",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "comment",
+ "columnName": "gradeComment",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "semester",
+ "columnName": "gradeSemester",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherId",
+ "columnName": "teacherId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subjectId",
+ "columnName": "subjectId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "addedDate",
+ "columnName": "addedDate",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "valueMax",
+ "columnName": "gradeValueMax",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "classAverage",
+ "columnName": "gradeClassAverage",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "parentId",
+ "columnName": "gradeParentId",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isImprovement",
+ "columnName": "gradeIsImprovement",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "keep",
+ "columnName": "keep",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "profileId",
+ "gradeId"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [
+ {
+ "name": "index_grades_profileId",
+ "unique": false,
+ "columnNames": [
+ "profileId"
+ ],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_grades_profileId` ON `${TABLE_NAME}` (`profileId`)"
+ }
+ ],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "teachers",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`profileId` INTEGER NOT NULL, `teacherId` INTEGER NOT NULL, `teacherLoginId` TEXT, `teacherName` TEXT, `teacherSurname` TEXT, `teacherType` INTEGER NOT NULL, `teacherTypeDescription` TEXT, `teacherSubjects` TEXT NOT NULL, PRIMARY KEY(`profileId`, `teacherId`))",
+ "fields": [
+ {
+ "fieldPath": "profileId",
+ "columnName": "profileId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "teacherId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "loginId",
+ "columnName": "teacherLoginId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "teacherName",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "surname",
+ "columnName": "teacherSurname",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "teacherType",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "typeDescription",
+ "columnName": "teacherTypeDescription",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "subjects",
+ "columnName": "teacherSubjects",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "profileId",
+ "teacherId"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "teacherAbsence",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`profileId` INTEGER NOT NULL, `teacherAbsenceId` INTEGER NOT NULL, `teacherAbsenceType` INTEGER NOT NULL, `teacherAbsenceName` TEXT, `teacherAbsenceDateFrom` TEXT NOT NULL, `teacherAbsenceDateTo` TEXT NOT NULL, `teacherAbsenceTimeFrom` TEXT, `teacherAbsenceTimeTo` TEXT, `teacherId` INTEGER NOT NULL, `addedDate` INTEGER NOT NULL, `keep` INTEGER NOT NULL, PRIMARY KEY(`profileId`, `teacherAbsenceId`))",
+ "fields": [
+ {
+ "fieldPath": "profileId",
+ "columnName": "profileId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "teacherAbsenceId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "teacherAbsenceType",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "teacherAbsenceName",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "dateFrom",
+ "columnName": "teacherAbsenceDateFrom",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "dateTo",
+ "columnName": "teacherAbsenceDateTo",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timeFrom",
+ "columnName": "teacherAbsenceTimeFrom",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "timeTo",
+ "columnName": "teacherAbsenceTimeTo",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "teacherId",
+ "columnName": "teacherId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "addedDate",
+ "columnName": "addedDate",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "keep",
+ "columnName": "keep",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "profileId",
+ "teacherAbsenceId"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [
+ {
+ "name": "index_teacherAbsence_profileId",
+ "unique": false,
+ "columnNames": [
+ "profileId"
+ ],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_teacherAbsence_profileId` ON `${TABLE_NAME}` (`profileId`)"
+ }
+ ],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "teacherAbsenceTypes",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`profileId` INTEGER NOT NULL, `teacherAbsenceTypeId` INTEGER NOT NULL, `teacherAbsenceTypeName` TEXT NOT NULL, PRIMARY KEY(`profileId`, `teacherAbsenceTypeId`))",
+ "fields": [
+ {
+ "fieldPath": "profileId",
+ "columnName": "profileId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "teacherAbsenceTypeId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "teacherAbsenceTypeName",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "profileId",
+ "teacherAbsenceTypeId"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "subjects",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`profileId` INTEGER NOT NULL, `subjectId` INTEGER NOT NULL, `subjectLongName` TEXT, `subjectShortName` TEXT, `subjectColor` INTEGER NOT NULL, PRIMARY KEY(`profileId`, `subjectId`))",
+ "fields": [
+ {
+ "fieldPath": "profileId",
+ "columnName": "profileId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "subjectId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "longName",
+ "columnName": "subjectLongName",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "shortName",
+ "columnName": "subjectShortName",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "color",
+ "columnName": "subjectColor",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "profileId",
+ "subjectId"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "notices",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`profileId` INTEGER NOT NULL, `noticeId` INTEGER NOT NULL, `noticeType` INTEGER NOT NULL, `noticeSemester` INTEGER NOT NULL, `noticeText` TEXT NOT NULL, `noticeCategory` TEXT, `noticePoints` REAL, `teacherId` INTEGER NOT NULL, `addedDate` INTEGER NOT NULL, `keep` INTEGER NOT NULL, PRIMARY KEY(`profileId`, `noticeId`))",
+ "fields": [
+ {
+ "fieldPath": "profileId",
+ "columnName": "profileId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "noticeId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "noticeType",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semester",
+ "columnName": "noticeSemester",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "text",
+ "columnName": "noticeText",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "category",
+ "columnName": "noticeCategory",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "points",
+ "columnName": "noticePoints",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "teacherId",
+ "columnName": "teacherId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "addedDate",
+ "columnName": "addedDate",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "keep",
+ "columnName": "keep",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "profileId",
+ "noticeId"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [
+ {
+ "name": "index_notices_profileId",
+ "unique": false,
+ "columnNames": [
+ "profileId"
+ ],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_notices_profileId` ON `${TABLE_NAME}` (`profileId`)"
+ }
+ ],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "teams",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`profileId` INTEGER NOT NULL, `teamId` INTEGER NOT NULL, `teamType` INTEGER NOT NULL, `teamName` TEXT, `teamCode` TEXT, `teamTeacherId` INTEGER NOT NULL, PRIMARY KEY(`profileId`, `teamId`))",
+ "fields": [
+ {
+ "fieldPath": "profileId",
+ "columnName": "profileId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "teamId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "teamType",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "teamName",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "code",
+ "columnName": "teamCode",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "teacherId",
+ "columnName": "teamTeacherId",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "profileId",
+ "teamId"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "attendances",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`profileId` INTEGER NOT NULL, `attendanceId` INTEGER NOT NULL, `attendanceBaseType` INTEGER NOT NULL, `attendanceTypeName` TEXT NOT NULL, `attendanceTypeShort` TEXT NOT NULL, `attendanceTypeSymbol` TEXT NOT NULL, `attendanceTypeColor` INTEGER, `attendanceDate` TEXT NOT NULL, `attendanceTime` TEXT, `attendanceSemester` INTEGER NOT NULL, `teacherId` INTEGER NOT NULL, `subjectId` INTEGER NOT NULL, `addedDate` INTEGER NOT NULL, `attendanceLessonTopic` TEXT, `attendanceLessonNumber` INTEGER, `attendanceIsCounted` INTEGER NOT NULL, `keep` INTEGER NOT NULL, PRIMARY KEY(`profileId`, `attendanceId`))",
+ "fields": [
+ {
+ "fieldPath": "profileId",
+ "columnName": "profileId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "attendanceId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "baseType",
+ "columnName": "attendanceBaseType",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "typeName",
+ "columnName": "attendanceTypeName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "typeShort",
+ "columnName": "attendanceTypeShort",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "typeSymbol",
+ "columnName": "attendanceTypeSymbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "typeColor",
+ "columnName": "attendanceTypeColor",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "attendanceDate",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "startTime",
+ "columnName": "attendanceTime",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "semester",
+ "columnName": "attendanceSemester",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherId",
+ "columnName": "teacherId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subjectId",
+ "columnName": "subjectId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "addedDate",
+ "columnName": "addedDate",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lessonTopic",
+ "columnName": "attendanceLessonTopic",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "lessonNumber",
+ "columnName": "attendanceLessonNumber",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isCounted",
+ "columnName": "attendanceIsCounted",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "keep",
+ "columnName": "keep",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "profileId",
+ "attendanceId"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [
+ {
+ "name": "index_attendances_profileId",
+ "unique": false,
+ "columnNames": [
+ "profileId"
+ ],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_attendances_profileId` ON `${TABLE_NAME}` (`profileId`)"
+ }
+ ],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "events",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`profileId` INTEGER NOT NULL, `eventId` INTEGER NOT NULL, `eventDate` TEXT NOT NULL, `eventTime` TEXT, `eventTopic` TEXT NOT NULL, `eventColor` INTEGER, `eventType` INTEGER NOT NULL, `teacherId` INTEGER NOT NULL, `subjectId` INTEGER NOT NULL, `teamId` INTEGER NOT NULL, `addedDate` INTEGER NOT NULL, `eventAddedManually` INTEGER NOT NULL, `eventSharedBy` TEXT, `eventSharedByName` TEXT, `eventBlacklisted` INTEGER NOT NULL, `eventIsDone` INTEGER NOT NULL, `eventIsDownloaded` INTEGER NOT NULL, `homeworkBody` TEXT, `attachmentIds` TEXT, `attachmentNames` TEXT, `keep` INTEGER NOT NULL, PRIMARY KEY(`profileId`, `eventId`))",
+ "fields": [
+ {
+ "fieldPath": "profileId",
+ "columnName": "profileId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "eventId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "eventDate",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "time",
+ "columnName": "eventTime",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "topic",
+ "columnName": "eventTopic",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "color",
+ "columnName": "eventColor",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "eventType",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherId",
+ "columnName": "teacherId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subjectId",
+ "columnName": "subjectId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teamId",
+ "columnName": "teamId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "addedDate",
+ "columnName": "addedDate",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "addedManually",
+ "columnName": "eventAddedManually",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "sharedBy",
+ "columnName": "eventSharedBy",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "sharedByName",
+ "columnName": "eventSharedByName",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "blacklisted",
+ "columnName": "eventBlacklisted",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isDone",
+ "columnName": "eventIsDone",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isDownloaded",
+ "columnName": "eventIsDownloaded",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "homeworkBody",
+ "columnName": "homeworkBody",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "attachmentIds",
+ "columnName": "attachmentIds",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "attachmentNames",
+ "columnName": "attachmentNames",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "keep",
+ "columnName": "keep",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "profileId",
+ "eventId"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [
+ {
+ "name": "index_events_profileId_eventDate_eventTime",
+ "unique": false,
+ "columnNames": [
+ "profileId",
+ "eventDate",
+ "eventTime"
+ ],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_events_profileId_eventDate_eventTime` ON `${TABLE_NAME}` (`profileId`, `eventDate`, `eventTime`)"
+ },
+ {
+ "name": "index_events_profileId_eventType",
+ "unique": false,
+ "columnNames": [
+ "profileId",
+ "eventType"
+ ],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_events_profileId_eventType` ON `${TABLE_NAME}` (`profileId`, `eventType`)"
+ }
+ ],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "eventTypes",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`profileId` INTEGER NOT NULL, `eventType` INTEGER NOT NULL, `eventTypeName` TEXT NOT NULL, `eventTypeColor` INTEGER NOT NULL, `eventTypeOrder` INTEGER NOT NULL, `eventTypeSource` INTEGER NOT NULL, PRIMARY KEY(`profileId`, `eventType`))",
+ "fields": [
+ {
+ "fieldPath": "profileId",
+ "columnName": "profileId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "eventType",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "eventTypeName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "color",
+ "columnName": "eventTypeColor",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "order",
+ "columnName": "eventTypeOrder",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "source",
+ "columnName": "eventTypeSource",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "profileId",
+ "eventType"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "loginStores",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`loginStoreId` INTEGER NOT NULL, `loginStoreType` INTEGER NOT NULL, `loginStoreMode` INTEGER NOT NULL, `loginStoreData` TEXT NOT NULL, PRIMARY KEY(`loginStoreId`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "loginStoreId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "loginStoreType",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "mode",
+ "columnName": "loginStoreMode",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "data",
+ "columnName": "loginStoreData",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "loginStoreId"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "profiles",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`profileId` INTEGER NOT NULL, `loginStoreId` INTEGER NOT NULL, `loginStoreType` INTEGER NOT NULL, `name` TEXT NOT NULL, `subname` TEXT, `studentNameLong` TEXT NOT NULL, `studentNameShort` TEXT NOT NULL, `accountName` TEXT, `studentData` TEXT NOT NULL, `image` TEXT, `empty` INTEGER NOT NULL, `archived` INTEGER NOT NULL, `archiveId` INTEGER, `syncEnabled` INTEGER NOT NULL, `enableSharedEvents` INTEGER NOT NULL, `registration` INTEGER NOT NULL, `userCode` TEXT NOT NULL, `studentNumber` INTEGER NOT NULL, `studentClassName` TEXT, `studentSchoolYearStart` INTEGER NOT NULL, `dateSemester1Start` TEXT NOT NULL, `dateSemester2Start` TEXT NOT NULL, `dateYearEnd` TEXT NOT NULL, `disabledNotifications` TEXT, `lastReceiversSync` INTEGER NOT NULL, PRIMARY KEY(`profileId`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "profileId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "loginStoreId",
+ "columnName": "loginStoreId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "loginStoreType",
+ "columnName": "loginStoreType",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subname",
+ "columnName": "subname",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "studentNameLong",
+ "columnName": "studentNameLong",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentNameShort",
+ "columnName": "studentNameShort",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "accountName",
+ "columnName": "accountName",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "studentData",
+ "columnName": "studentData",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "image",
+ "columnName": "image",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "empty",
+ "columnName": "empty",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "archived",
+ "columnName": "archived",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "archiveId",
+ "columnName": "archiveId",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "syncEnabled",
+ "columnName": "syncEnabled",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "enableSharedEvents",
+ "columnName": "enableSharedEvents",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "registration",
+ "columnName": "registration",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userCode",
+ "columnName": "userCode",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentNumber",
+ "columnName": "studentNumber",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentClassName",
+ "columnName": "studentClassName",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "studentSchoolYearStart",
+ "columnName": "studentSchoolYearStart",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "dateSemester1Start",
+ "columnName": "dateSemester1Start",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "dateSemester2Start",
+ "columnName": "dateSemester2Start",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "dateYearEnd",
+ "columnName": "dateYearEnd",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "disabledNotifications",
+ "columnName": "disabledNotifications",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "lastReceiversSync",
+ "columnName": "lastReceiversSync",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "profileId"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "luckyNumbers",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`profileId` INTEGER NOT NULL, `luckyNumberDate` INTEGER NOT NULL, `luckyNumber` INTEGER NOT NULL, `keep` INTEGER NOT NULL, PRIMARY KEY(`profileId`, `luckyNumberDate`))",
+ "fields": [
+ {
+ "fieldPath": "profileId",
+ "columnName": "profileId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "luckyNumberDate",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "number",
+ "columnName": "luckyNumber",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "keep",
+ "columnName": "keep",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "profileId",
+ "luckyNumberDate"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "announcements",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`profileId` INTEGER NOT NULL, `announcementId` INTEGER NOT NULL, `announcementSubject` TEXT NOT NULL, `announcementText` TEXT, `announcementStartDate` TEXT, `announcementEndDate` TEXT, `teacherId` INTEGER NOT NULL, `addedDate` INTEGER NOT NULL, `announcementIdString` TEXT, `keep` INTEGER NOT NULL, PRIMARY KEY(`profileId`, `announcementId`))",
+ "fields": [
+ {
+ "fieldPath": "profileId",
+ "columnName": "profileId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "announcementId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "announcementSubject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "text",
+ "columnName": "announcementText",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "startDate",
+ "columnName": "announcementStartDate",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "endDate",
+ "columnName": "announcementEndDate",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "teacherId",
+ "columnName": "teacherId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "addedDate",
+ "columnName": "addedDate",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "idString",
+ "columnName": "announcementIdString",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "keep",
+ "columnName": "keep",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "profileId",
+ "announcementId"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [
+ {
+ "name": "index_announcements_profileId",
+ "unique": false,
+ "columnNames": [
+ "profileId"
+ ],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_announcements_profileId` ON `${TABLE_NAME}` (`profileId`)"
+ }
+ ],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "gradeCategories",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`profileId` INTEGER NOT NULL, `categoryId` INTEGER NOT NULL, `weight` REAL NOT NULL, `color` INTEGER NOT NULL, `text` TEXT, `columns` TEXT, `valueFrom` REAL NOT NULL, `valueTo` REAL NOT NULL, `type` INTEGER NOT NULL, PRIMARY KEY(`profileId`, `categoryId`, `type`))",
+ "fields": [
+ {
+ "fieldPath": "profileId",
+ "columnName": "profileId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "categoryId",
+ "columnName": "categoryId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "weight",
+ "columnName": "weight",
+ "affinity": "REAL",
+ "notNull": true
+ },
+ {
+ "fieldPath": "color",
+ "columnName": "color",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "text",
+ "columnName": "text",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "columns",
+ "columnName": "columns",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "valueFrom",
+ "columnName": "valueFrom",
+ "affinity": "REAL",
+ "notNull": true
+ },
+ {
+ "fieldPath": "valueTo",
+ "columnName": "valueTo",
+ "affinity": "REAL",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "profileId",
+ "categoryId",
+ "type"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "feedbackMessages",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`messageId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `received` INTEGER NOT NULL, `text` TEXT NOT NULL, `senderName` TEXT NOT NULL, `deviceId` TEXT, `deviceName` TEXT, `devId` INTEGER, `devImage` TEXT, `sentTime` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "messageId",
+ "columnName": "messageId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "received",
+ "columnName": "received",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "text",
+ "columnName": "text",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "senderName",
+ "columnName": "senderName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "deviceId",
+ "columnName": "deviceId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "deviceName",
+ "columnName": "deviceName",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "devId",
+ "columnName": "devId",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "devImage",
+ "columnName": "devImage",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "sentTime",
+ "columnName": "sentTime",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "messageId"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "messages",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`profileId` INTEGER NOT NULL, `messageId` INTEGER NOT NULL, `messageType` INTEGER NOT NULL, `messageSubject` TEXT NOT NULL, `messageBody` TEXT, `senderId` INTEGER, `addedDate` INTEGER NOT NULL, `messageIsPinned` INTEGER NOT NULL, `hasAttachments` INTEGER NOT NULL, `attachmentIds` TEXT, `attachmentNames` TEXT, `attachmentSizes` TEXT, `keep` INTEGER NOT NULL, PRIMARY KEY(`profileId`, `messageId`))",
+ "fields": [
+ {
+ "fieldPath": "profileId",
+ "columnName": "profileId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "messageId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "messageType",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "messageSubject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "body",
+ "columnName": "messageBody",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "senderId",
+ "columnName": "senderId",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "addedDate",
+ "columnName": "addedDate",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isStarred",
+ "columnName": "messageIsPinned",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "hasAttachments",
+ "columnName": "hasAttachments",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "attachmentIds",
+ "columnName": "attachmentIds",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "attachmentNames",
+ "columnName": "attachmentNames",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "attachmentSizes",
+ "columnName": "attachmentSizes",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "keep",
+ "columnName": "keep",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "profileId",
+ "messageId"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [
+ {
+ "name": "index_messages_profileId_messageType",
+ "unique": false,
+ "columnNames": [
+ "profileId",
+ "messageType"
+ ],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_messages_profileId_messageType` ON `${TABLE_NAME}` (`profileId`, `messageType`)"
+ }
+ ],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "messageRecipients",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`profileId` INTEGER NOT NULL, `messageRecipientId` INTEGER NOT NULL, `messageRecipientReplyId` INTEGER NOT NULL, `messageRecipientReadDate` INTEGER NOT NULL, `messageId` INTEGER NOT NULL, PRIMARY KEY(`profileId`, `messageRecipientId`, `messageId`))",
+ "fields": [
+ {
+ "fieldPath": "profileId",
+ "columnName": "profileId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "messageRecipientId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "replyId",
+ "columnName": "messageRecipientReplyId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "readDate",
+ "columnName": "messageRecipientReadDate",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "messageId",
+ "columnName": "messageId",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "profileId",
+ "messageRecipientId",
+ "messageId"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "debugLogs",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `text` TEXT)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "text",
+ "columnName": "text",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "endpointTimers",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`profileId` INTEGER NOT NULL, `endpointId` INTEGER NOT NULL, `endpointLastSync` INTEGER, `endpointNextSync` INTEGER NOT NULL, `endpointViewId` INTEGER, PRIMARY KEY(`profileId`, `endpointId`))",
+ "fields": [
+ {
+ "fieldPath": "profileId",
+ "columnName": "profileId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "endpointId",
+ "columnName": "endpointId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastSync",
+ "columnName": "endpointLastSync",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "nextSync",
+ "columnName": "endpointNextSync",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "viewId",
+ "columnName": "endpointViewId",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "profileId",
+ "endpointId"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "lessonRanges",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`profileId` INTEGER NOT NULL, `lessonRangeNumber` INTEGER NOT NULL, `lessonRangeStart` TEXT NOT NULL, `lessonRangeEnd` TEXT NOT NULL, PRIMARY KEY(`profileId`, `lessonRangeNumber`))",
+ "fields": [
+ {
+ "fieldPath": "profileId",
+ "columnName": "profileId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lessonNumber",
+ "columnName": "lessonRangeNumber",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "startTime",
+ "columnName": "lessonRangeStart",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "endTime",
+ "columnName": "lessonRangeEnd",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "profileId",
+ "lessonRangeNumber"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "notifications",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT NOT NULL, `text` TEXT NOT NULL, `textLong` TEXT, `type` INTEGER NOT NULL, `profileId` INTEGER, `profileName` TEXT, `posted` INTEGER NOT NULL, `viewId` INTEGER, `extras` TEXT, `addedDate` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "text",
+ "columnName": "text",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "textLong",
+ "columnName": "textLong",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "profileId",
+ "columnName": "profileId",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "profileName",
+ "columnName": "profileName",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "posted",
+ "columnName": "posted",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "viewId",
+ "columnName": "viewId",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "extras",
+ "columnName": "extras",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "addedDate",
+ "columnName": "addedDate",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "classrooms",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`profileId` INTEGER NOT NULL, `id` INTEGER NOT NULL, `name` TEXT NOT NULL, PRIMARY KEY(`profileId`, `id`))",
+ "fields": [
+ {
+ "fieldPath": "profileId",
+ "columnName": "profileId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "profileId",
+ "id"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "noticeTypes",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`profileId` INTEGER NOT NULL, `id` INTEGER NOT NULL, `name` TEXT NOT NULL, PRIMARY KEY(`profileId`, `id`))",
+ "fields": [
+ {
+ "fieldPath": "profileId",
+ "columnName": "profileId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "profileId",
+ "id"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "attendanceTypes",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`profileId` INTEGER NOT NULL, `id` INTEGER NOT NULL, `baseType` INTEGER NOT NULL, `typeName` TEXT NOT NULL, `typeShort` TEXT NOT NULL, `typeSymbol` TEXT NOT NULL, `typeColor` INTEGER, PRIMARY KEY(`profileId`, `id`))",
+ "fields": [
+ {
+ "fieldPath": "profileId",
+ "columnName": "profileId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "baseType",
+ "columnName": "baseType",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "typeName",
+ "columnName": "typeName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "typeShort",
+ "columnName": "typeShort",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "typeSymbol",
+ "columnName": "typeSymbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "typeColor",
+ "columnName": "typeColor",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "profileId",
+ "id"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "timetable",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`profileId` INTEGER NOT NULL, `id` INTEGER NOT NULL, `type` INTEGER NOT NULL, `date` TEXT, `lessonNumber` INTEGER, `startTime` TEXT, `endTime` TEXT, `subjectId` INTEGER, `teacherId` INTEGER, `teamId` INTEGER, `classroom` TEXT, `oldDate` TEXT, `oldLessonNumber` INTEGER, `oldStartTime` TEXT, `oldEndTime` TEXT, `oldSubjectId` INTEGER, `oldTeacherId` INTEGER, `oldTeamId` INTEGER, `oldClassroom` TEXT, `isExtra` INTEGER NOT NULL, `keep` INTEGER NOT NULL, PRIMARY KEY(`profileId`, `id`))",
+ "fields": [
+ {
+ "fieldPath": "profileId",
+ "columnName": "profileId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "lessonNumber",
+ "columnName": "lessonNumber",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "startTime",
+ "columnName": "startTime",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "endTime",
+ "columnName": "endTime",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "subjectId",
+ "columnName": "subjectId",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "teacherId",
+ "columnName": "teacherId",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "teamId",
+ "columnName": "teamId",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "classroom",
+ "columnName": "classroom",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "oldDate",
+ "columnName": "oldDate",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "oldLessonNumber",
+ "columnName": "oldLessonNumber",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "oldStartTime",
+ "columnName": "oldStartTime",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "oldEndTime",
+ "columnName": "oldEndTime",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "oldSubjectId",
+ "columnName": "oldSubjectId",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "oldTeacherId",
+ "columnName": "oldTeacherId",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "oldTeamId",
+ "columnName": "oldTeamId",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "oldClassroom",
+ "columnName": "oldClassroom",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isExtra",
+ "columnName": "isExtra",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "keep",
+ "columnName": "keep",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "profileId",
+ "id"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [
+ {
+ "name": "index_timetable_profileId_type_date",
+ "unique": false,
+ "columnNames": [
+ "profileId",
+ "type",
+ "date"
+ ],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_timetable_profileId_type_date` ON `${TABLE_NAME}` (`profileId`, `type`, `date`)"
+ },
+ {
+ "name": "index_timetable_profileId_type_oldDate",
+ "unique": false,
+ "columnNames": [
+ "profileId",
+ "type",
+ "oldDate"
+ ],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_timetable_profileId_type_oldDate` ON `${TABLE_NAME}` (`profileId`, `type`, `oldDate`)"
+ }
+ ],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "config",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`profileId` INTEGER NOT NULL, `key` TEXT NOT NULL, `value` TEXT, PRIMARY KEY(`profileId`, `key`))",
+ "fields": [
+ {
+ "fieldPath": "profileId",
+ "columnName": "profileId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "key",
+ "columnName": "key",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "value",
+ "columnName": "value",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "profileId",
+ "key"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "librusLessons",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`profileId` INTEGER NOT NULL, `lessonId` INTEGER NOT NULL, `teacherId` INTEGER NOT NULL, `subjectId` INTEGER NOT NULL, `teamId` INTEGER, PRIMARY KEY(`profileId`, `lessonId`))",
+ "fields": [
+ {
+ "fieldPath": "profileId",
+ "columnName": "profileId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lessonId",
+ "columnName": "lessonId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherId",
+ "columnName": "teacherId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subjectId",
+ "columnName": "subjectId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teamId",
+ "columnName": "teamId",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "profileId",
+ "lessonId"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [
+ {
+ "name": "index_librusLessons_profileId",
+ "unique": false,
+ "columnNames": [
+ "profileId"
+ ],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_librusLessons_profileId` ON `${TABLE_NAME}` (`profileId`)"
+ }
+ ],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "timetableManual",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`profileId` INTEGER NOT NULL, `type` INTEGER NOT NULL, `repeatBy` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `date` INTEGER, `weekDay` INTEGER, `lessonNumber` INTEGER, `startTime` TEXT, `endTime` TEXT, `subjectId` INTEGER, `teacherId` INTEGER, `teamId` INTEGER, `classroom` TEXT)",
+ "fields": [
+ {
+ "fieldPath": "profileId",
+ "columnName": "profileId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "repeatBy",
+ "columnName": "repeatBy",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "weekDay",
+ "columnName": "weekDay",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "lessonNumber",
+ "columnName": "lessonNumber",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "startTime",
+ "columnName": "startTime",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "endTime",
+ "columnName": "endTime",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "subjectId",
+ "columnName": "subjectId",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "teacherId",
+ "columnName": "teacherId",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "teamId",
+ "columnName": "teamId",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "classroom",
+ "columnName": "classroom",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [
+ {
+ "name": "index_timetableManual_profileId_date",
+ "unique": false,
+ "columnNames": [
+ "profileId",
+ "date"
+ ],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_timetableManual_profileId_date` ON `${TABLE_NAME}` (`profileId`, `date`)"
+ },
+ {
+ "name": "index_timetableManual_profileId_weekDay",
+ "unique": false,
+ "columnNames": [
+ "profileId",
+ "weekDay"
+ ],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_timetableManual_profileId_weekDay` ON `${TABLE_NAME}` (`profileId`, `weekDay`)"
+ }
+ ],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "notes",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`profileId` INTEGER NOT NULL, `noteId` INTEGER NOT NULL, `noteOwnerType` TEXT, `noteOwnerId` INTEGER, `noteReplacesOriginal` INTEGER NOT NULL, `noteTopic` TEXT, `noteBody` TEXT NOT NULL, `noteColor` INTEGER, `noteSharedBy` TEXT, `noteSharedByName` TEXT, `addedDate` INTEGER NOT NULL, PRIMARY KEY(`noteId`))",
+ "fields": [
+ {
+ "fieldPath": "profileId",
+ "columnName": "profileId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "noteId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "ownerType",
+ "columnName": "noteOwnerType",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "ownerId",
+ "columnName": "noteOwnerId",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "replacesOriginal",
+ "columnName": "noteReplacesOriginal",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "topic",
+ "columnName": "noteTopic",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "body",
+ "columnName": "noteBody",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "color",
+ "columnName": "noteColor",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "sharedBy",
+ "columnName": "noteSharedBy",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "sharedByName",
+ "columnName": "noteSharedByName",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "addedDate",
+ "columnName": "addedDate",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "noteId"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [
+ {
+ "name": "index_notes_profileId_noteOwnerType_noteOwnerId",
+ "unique": false,
+ "columnNames": [
+ "profileId",
+ "noteOwnerType",
+ "noteOwnerId"
+ ],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_notes_profileId_noteOwnerType_noteOwnerId` ON `${TABLE_NAME}` (`profileId`, `noteOwnerType`, `noteOwnerId`)"
+ }
+ ],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "metadata",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`profileId` INTEGER NOT NULL, `metadataId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `thingType` INTEGER NOT NULL, `thingId` INTEGER NOT NULL, `seen` INTEGER NOT NULL, `notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "profileId",
+ "columnName": "profileId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "metadataId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "thingType",
+ "columnName": "thingType",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "thingId",
+ "columnName": "thingId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "seen",
+ "columnName": "seen",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "notified",
+ "columnName": "notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "metadataId"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [
+ {
+ "name": "index_metadata_profileId_thingType_thingId",
+ "unique": true,
+ "columnNames": [
+ "profileId",
+ "thingType",
+ "thingId"
+ ],
+ "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_metadata_profileId_thingType_thingId` ON `${TABLE_NAME}` (`profileId`, `thingType`, `thingId`)"
+ }
+ ],
+ "foreignKeys": []
+ }
+ ],
+ "views": [],
+ "setupQueries": [
+ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '08a8998e311e4e62a9e9554132b5c011')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 5f295533..c710c6b8 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -29,6 +29,8 @@
android:usesCleartextTraffic="true"
tools:ignore="UnusedAttribute">
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/agenda_counter_item.xml b/app/src/main/res/layout/agenda_counter_item.xml
new file mode 100644
index 00000000..334c419c
--- /dev/null
+++ b/app/src/main/res/layout/agenda_counter_item.xml
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/agenda_event_compact_item.xml b/app/src/main/res/layout/agenda_event_compact_item.xml
new file mode 100644
index 00000000..b97ddfa3
--- /dev/null
+++ b/app/src/main/res/layout/agenda_event_compact_item.xml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/agenda_event_item.xml b/app/src/main/res/layout/agenda_event_item.xml
new file mode 100644
index 00000000..acaffa27
--- /dev/null
+++ b/app/src/main/res/layout/agenda_event_item.xml
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/agenda_group_item.xml b/app/src/main/res/layout/agenda_group_item.xml
new file mode 100644
index 00000000..4787a8cf
--- /dev/null
+++ b/app/src/main/res/layout/agenda_group_item.xml
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/agenda_event_lesson_change.xml b/app/src/main/res/layout/agenda_wrapped_counter.xml
similarity index 77%
rename from app/src/main/res/layout/agenda_event_lesson_change.xml
rename to app/src/main/res/layout/agenda_wrapped_counter.xml
index c51f9b35..31864636 100644
--- a/app/src/main/res/layout/agenda_event_lesson_change.xml
+++ b/app/src/main/res/layout/agenda_wrapped_counter.xml
@@ -1,15 +1,17 @@
+
+
-
diff --git a/app/src/main/res/layout/agenda_event_teacher_absence.xml b/app/src/main/res/layout/agenda_wrapped_event.xml
similarity index 79%
rename from app/src/main/res/layout/agenda_event_teacher_absence.xml
rename to app/src/main/res/layout/agenda_wrapped_event.xml
index 785256d0..2ca38528 100644
--- a/app/src/main/res/layout/agenda_event_teacher_absence.xml
+++ b/app/src/main/res/layout/agenda_wrapped_event.xml
@@ -1,13 +1,17 @@
+
+
-
diff --git a/app/src/main/res/layout/agenda_wrapped_event_compact.xml b/app/src/main/res/layout/agenda_wrapped_event_compact.xml
new file mode 100644
index 00000000..997c24db
--- /dev/null
+++ b/app/src/main/res/layout/agenda_wrapped_event_compact.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/agenda_wrapped_group.xml b/app/src/main/res/layout/agenda_wrapped_group.xml
new file mode 100644
index 00000000..6b081d8e
--- /dev/null
+++ b/app/src/main/res/layout/agenda_wrapped_group.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/attendance_details_dialog.xml b/app/src/main/res/layout/attendance_details_dialog.xml
index 34a2dc0d..b3c46095 100644
--- a/app/src/main/res/layout/attendance_details_dialog.xml
+++ b/app/src/main/res/layout/attendance_details_dialog.xml
@@ -86,6 +86,14 @@
+
+
+
+
diff --git a/app/src/main/res/layout/attendance_fragment.xml b/app/src/main/res/layout/attendance_fragment.xml
index d83601f0..48fb1aa5 100644
--- a/app/src/main/res/layout/attendance_fragment.xml
+++ b/app/src/main/res/layout/attendance_fragment.xml
@@ -26,7 +26,7 @@
app:tabSelectedTextColor="?colorPrimary"
app:tabTextColor="?android:textColorPrimary" />
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/card_home_notes.xml b/app/src/main/res/layout/card_home_notes.xml
new file mode 100644
index 00000000..da14da63
--- /dev/null
+++ b/app/src/main/res/layout/card_home_notes.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/card_home_template.xml b/app/src/main/res/layout/card_home_template.xml
index 8cea8889..1570c8b3 100644
--- a/app/src/main/res/layout/card_home_template.xml
+++ b/app/src/main/res/layout/card_home_template.xml
@@ -24,6 +24,7 @@
android:layout_gravity="center_horizontal"
android:layout_margin="16dp"
android:fontFamily="sans-serif-light"
+ android:gravity="center"
android:text="@string/card_grades_no_data"
android:textSize="16sp" />
diff --git a/app/src/main/res/layout/contributors_activity.xml b/app/src/main/res/layout/contributors_activity.xml
new file mode 100644
index 00000000..fa54100d
--- /dev/null
+++ b/app/src/main/res/layout/contributors_activity.xml
@@ -0,0 +1,124 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/contributors_list_fragment.xml b/app/src/main/res/layout/contributors_list_fragment.xml
new file mode 100644
index 00000000..70d3d1c5
--- /dev/null
+++ b/app/src/main/res/layout/contributors_list_fragment.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
diff --git a/app/src/main/res/layout/contributors_list_item.xml b/app/src/main/res/layout/contributors_list_item.xml
new file mode 100644
index 00000000..5029203d
--- /dev/null
+++ b/app/src/main/res/layout/contributors_list_item.xml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/dialog_config_agenda.xml b/app/src/main/res/layout/dialog_config_agenda.xml
new file mode 100644
index 00000000..f17b5989
--- /dev/null
+++ b/app/src/main/res/layout/dialog_config_agenda.xml
@@ -0,0 +1,156 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/dialog_day.xml b/app/src/main/res/layout/dialog_day.xml
index 59fd7f8c..9fd14e1e 100644
--- a/app/src/main/res/layout/dialog_day.xml
+++ b/app/src/main/res/layout/dialog_day.xml
@@ -3,118 +3,128 @@
~ Copyright (c) Kuba Szczodrzyński 2019-12-16.
-->
-
-
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ android:paddingTop="16dp"
+ android:paddingBottom="8dp"
+ android:visibility="gone"
+ tools:visibility="visible">
-
+ android:layout_gravity="center"
+ android:drawablePadding="16dp"
+ android:fontFamily="sans-serif-light"
+ android:gravity="center"
+ android:text="@string/dialog_day_no_events"
+ android:textSize="24sp"
+ app:drawableTopCompat="@drawable/ic_no_events" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ android:layout_gravity="center"
+ android:gravity="center"
+ android:text="@string/dialog_no_events_hint"
+ android:textStyle="italic" />
-
-
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/dialog_event_details.xml b/app/src/main/res/layout/dialog_event_details.xml
index 7a9d8004..af817e1d 100644
--- a/app/src/main/res/layout/dialog_event_details.xml
+++ b/app/src/main/res/layout/dialog_event_details.xml
@@ -105,13 +105,28 @@
+
+
+
+
@@ -149,7 +164,6 @@
android:id="@+id/topic"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="@{event.topic}"
android:textAppearance="@style/NavView.TextView.Medium"
android:textIsSelectable="true"
tools:text="Rozdział II: Panowanie Piastów i Jagiellonów.Przeniesiony z 11 grudnia." />
@@ -158,7 +172,7 @@
android:id="@+id/bodyTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="4dp"
+ android:layout_marginTop="8dp"
android:text="@string/dialog_event_details_body"
android:textAppearance="@style/NavView.TextView.Helper" />
@@ -175,7 +189,6 @@
android:id="@+id/body"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="@{event.homeworkBody}"
android:textAppearance="@style/NavView.TextView.Medium"
android:textIsSelectable="true"
tools:text="Rozdział II: Panowanie Piastów i Jagiellonów.Przeniesiony z 11 grudnia." />
@@ -184,11 +197,11 @@
android:id="@+id/attachmentsTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="4dp"
+ android:layout_marginTop="8dp"
android:text="@string/dialog_event_details_attachments"
android:textAppearance="@style/NavView.TextView.Helper" />
-
@@ -266,6 +279,14 @@
android:text="\uf436"
android:textSize="20sp" />
+
+
diff --git a/app/src/main/res/layout/dialog_event_manual_v2.xml b/app/src/main/res/layout/dialog_event_manual_v2.xml
index 8d2fc267..e8451616 100644
--- a/app/src/main/res/layout/dialog_event_manual_v2.xml
+++ b/app/src/main/res/layout/dialog_event_manual_v2.xml
@@ -25,7 +25,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/dialog_event_manual_date">
-
-
-
-
+ android:hint="@string/dialog_event_manual_topic"
+ app:endIconMode="custom"
+ tools:endIconDrawable="@android:drawable/ic_menu_crop">
-
-
+
+
+
+
diff --git a/app/src/main/res/layout/dialog_lesson_details.xml b/app/src/main/res/layout/dialog_lesson_details.xml
index dcc76e5e..03a3c843 100644
--- a/app/src/main/res/layout/dialog_lesson_details.xml
+++ b/app/src/main/res/layout/dialog_lesson_details.xml
@@ -134,7 +134,8 @@
android:layout_marginTop="8dp"
android:baselineAligned="false"
android:gravity="center_vertical"
- android:orientation="horizontal">
+ android:orientation="horizontal"
+ android:visibility="gone">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -333,6 +399,13 @@
tools:visibility="visible"
tools:listitem="@layout/event_list_item" />
+
diff --git a/app/src/main/res/layout/dialog_register_unavailable.xml b/app/src/main/res/layout/dialog_register_unavailable.xml
index 0880179d..e6c3e16b 100644
--- a/app/src/main/res/layout/dialog_register_unavailable.xml
+++ b/app/src/main/res/layout/dialog_register_unavailable.xml
@@ -8,7 +8,7 @@
-
+
@@ -43,7 +43,7 @@
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="@{HtmlCompat.fromHtml(message.title, HtmlCompat.FROM_HTML_MODE_LEGACY)}"
+ android:text="@{BetterHtml.fromHtml(context, message.title, false)}"
android:textAppearance="@style/NavView.TextView.Title"
tools:text="Dziennik nie działa" />
@@ -52,7 +52,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
- android:text="@{HtmlCompat.fromHtml(message.contentLong, HtmlCompat.FROM_HTML_MODE_LEGACY)}"
+ android:text="@{BetterHtml.fromHtml(context, message.contentLong, false)}"
tools:text="Dziennik się zepsuł i nie działa, szkoda\n\n\nwiele linijek ma ten tekst" />
+
+
+
diff --git a/app/src/main/res/layout/event_list_item.xml b/app/src/main/res/layout/event_list_item.xml
index 24445327..9a13cfdd 100644
--- a/app/src/main/res/layout/event_list_item.xml
+++ b/app/src/main/res/layout/event_list_item.xml
@@ -2,13 +2,14 @@
-
-
+
-
+
+
+
@@ -17,9 +18,9 @@
+ android:padding="8dp">
+
+
-
+ android:visibility="gone"
+ tools:visibility="visible" />
-
-
-
-
-
+ tools:visibility="visible" />
diff --git a/app/src/main/res/layout/fragment_debug.xml b/app/src/main/res/layout/fragment_debug.xml
index bf27e110..f87bd64a 100644
--- a/app/src/main/res/layout/fragment_debug.xml
+++ b/app/src/main/res/layout/fragment_debug.xml
@@ -4,7 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
- tools:context=".ui.modules.debug.DebugFragment">
+ tools:context=".ui.debug.DebugFragment">
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/grades_item_grade.xml b/app/src/main/res/layout/grades_item_grade.xml
index 1ff1f0b0..a87f8cf3 100644
--- a/app/src/main/res/layout/grades_item_grade.xml
+++ b/app/src/main/res/layout/grades_item_grade.xml
@@ -13,7 +13,7 @@
android:orientation="horizontal"
android:paddingVertical="8dp">
-
-
-
-
-
-
-
-
+
+
-->
-
+
+
+
+
+ tools:text="Cookies:\n\nsynergia.librus.pl\n DZIENNIKSID=L01~1234567890abcdef" />
+ android:layout_height="wrap_content" />
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/login_chooser_fragment.xml b/app/src/main/res/layout/login_chooser_fragment.xml
index 6e83c68c..d39ef3da 100644
--- a/app/src/main/res/layout/login_chooser_fragment.xml
+++ b/app/src/main/res/layout/login_chooser_fragment.xml
@@ -72,10 +72,16 @@
android:text="@string/cancel"
android:textAllCaps="false" />
-
+ android:layout_height="match_parent"
+ 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" />
-
+
+
+ android:orientation="horizontal">
+ tools:visibility="gone" />
-
+
+
+
-
+ tools:text="20 lis 2021\n14:26" />
@@ -173,7 +188,7 @@
android:text="Załączniki:"
android:textAppearance="@style/NavView.TextView.Subtitle" />
-
-
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp"
+ android:text="@string/message_reply"
+ android:textAllCaps="false"
+ tools:drawableTop="@android:drawable/sym_action_email" />
-
-
-
-
-
-
-
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp"
+ android:text="@string/message_forward"
+ android:textAllCaps="false"
+ tools:drawableTop="@android:drawable/stat_sys_phone_call_forward" />
-
-
-
-
-
-
-
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp"
+ android:text="@string/message_delete"
+ android:textAllCaps="false"
+ tools:drawableTop="@android:drawable/ic_menu_delete" />
-
-
-
-
-
-
-
-
-
-
-
-
-
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp"
+ android:text="@string/message_download"
+ android:textAllCaps="false"
+ android:visibility="gone"
+ tools:drawableTop="@android:drawable/stat_sys_download" />
+
+
-
+
diff --git a/app/src/main/res/layout/messages_compose_fragment.xml b/app/src/main/res/layout/messages_compose_fragment.xml
index 9363a92f..032550bb 100644
--- a/app/src/main/res/layout/messages_compose_fragment.xml
+++ b/app/src/main/res/layout/messages_compose_fragment.xml
@@ -2,133 +2,106 @@
-
-
-
+
+
-
+
+ app:boxBackgroundColor="@android:color/transparent"
+ app:boxBackgroundMode="filled"
+ app:endIconDrawable="@drawable/dropdown_arrow"
+ app:endIconMode="custom">
-
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:focusedByDefault="true"
+ android:hint="@string/messages_compose_to_hint" />
+
-
+
-
-
-
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:hint="@string/messages_compose_subject_hint"
+ android:inputType="textCapSentences|textAutoCorrect|textShortMessage|textAutoComplete|textEmailSubject"
+ android:singleLine="true"
+ tools:text="kachoomba" />
+
-
+
-
-
-
-
-
+ android:ems="10"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:gravity="start|top"
+ android:hint="@string/messages_compose_text_hint"
+ android:inputType="textMultiLine|textAutoCorrect|textLongMessage|textAutoComplete|textCapSentences"
+ android:minLines="3"
+ tools:text="Witam,\n\nchciałem przekazać bardzo ważną wiadomość.\nJest to cytat znanej osoby.\n\n"To jest tak, ale nie. Pamiętaj żeby oczywiście"\n\nCytat ma bardzo duże przesłanie i ogromny wpływ na dzisiejszą kulturę i rozwój współczesnej cywilizacji.\n\nJako zadanie domowe, należy wypisać 5 pierwszych liczb pierwszych. Uzasadnij decyzję, odwołując się do cytatu i 3 innych przykładów z literatury lub filmu.\n\nPozdrawiam,\nJa." />
+
-
-
-
-
-
-
-
-
+
+
+
diff --git a/app/src/main/res/layout/messages_config_dialog.xml b/app/src/main/res/layout/messages_config_dialog.xml
new file mode 100644
index 00000000..d0205b39
--- /dev/null
+++ b/app/src/main/res/layout/messages_config_dialog.xml
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/messages_fragment.xml b/app/src/main/res/layout/messages_fragment.xml
index b09dc1f5..b7d7cbe8 100644
--- a/app/src/main/res/layout/messages_fragment.xml
+++ b/app/src/main/res/layout/messages_fragment.xml
@@ -26,7 +26,7 @@
app:tabSelectedTextColor="?colorPrimary"
app:tabTextColor="?android:textColorPrimary" />
-
-
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools">
-
+
+
+ tools:background="@tools:sample/avatars[4]" />
diff --git a/app/src/main/res/layout/note_details_dialog.xml b/app/src/main/res/layout/note_details_dialog.xml
new file mode 100644
index 00000000..58c37ed0
--- /dev/null
+++ b/app/src/main/res/layout/note_details_dialog.xml
@@ -0,0 +1,143 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/note_dialog_header.xml b/app/src/main/res/layout/note_dialog_header.xml
new file mode 100644
index 00000000..a6f21049
--- /dev/null
+++ b/app/src/main/res/layout/note_dialog_header.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/note_dialog_subtitle.xml b/app/src/main/res/layout/note_dialog_subtitle.xml
new file mode 100644
index 00000000..4c38dbfa
--- /dev/null
+++ b/app/src/main/res/layout/note_dialog_subtitle.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/note_editor_dialog.xml b/app/src/main/res/layout/note_editor_dialog.xml
new file mode 100644
index 00000000..f52b3bb0
--- /dev/null
+++ b/app/src/main/res/layout/note_editor_dialog.xml
@@ -0,0 +1,138 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/note_list_category_item.xml b/app/src/main/res/layout/note_list_category_item.xml
new file mode 100644
index 00000000..da6bd6b4
--- /dev/null
+++ b/app/src/main/res/layout/note_list_category_item.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/note_list_dialog.xml b/app/src/main/res/layout/note_list_dialog.xml
new file mode 100644
index 00000000..38730de1
--- /dev/null
+++ b/app/src/main/res/layout/note_list_dialog.xml
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/note_list_item.xml b/app/src/main/res/layout/note_list_item.xml
new file mode 100644
index 00000000..910d06ab
--- /dev/null
+++ b/app/src/main/res/layout/note_list_item.xml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/notes_fragment.xml b/app/src/main/res/layout/notes_fragment.xml
new file mode 100644
index 00000000..0234f947
--- /dev/null
+++ b/app/src/main/res/layout/notes_fragment.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/notifications_list_fragment.xml b/app/src/main/res/layout/notifications_list_fragment.xml
index a5c9aa90..f5ca3a28 100644
--- a/app/src/main/res/layout/notifications_list_fragment.xml
+++ b/app/src/main/res/layout/notifications_list_fragment.xml
@@ -24,6 +24,8 @@
android:layout_gravity="center"
android:drawablePadding="16dp"
android:fontFamily="sans-serif-light"
+ android:gravity="center"
+ android:padding="16dp"
android:text="@string/notifications_no_data"
android:textSize="24sp"
android:visibility="gone"
diff --git a/app/src/main/res/layout/notifications_list_item.xml b/app/src/main/res/layout/notifications_list_item.xml
index 498f7e21..13df88d1 100644
--- a/app/src/main/res/layout/notifications_list_item.xml
+++ b/app/src/main/res/layout/notifications_list_item.xml
@@ -9,12 +9,28 @@
android:orientation="vertical"
android:padding="8dp">
-
+ android:gravity="center_vertical"
+ android:orientation="horizontal">
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/row_teacher_absence_item.xml b/app/src/main/res/layout/row_teacher_absence_item.xml
deleted file mode 100644
index da78c625..00000000
--- a/app/src/main/res/layout/row_teacher_absence_item.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/messages_list_item_search.xml b/app/src/main/res/layout/search_item.xml
similarity index 100%
rename from app/src/main/res/layout/messages_list_item_search.xml
rename to app/src/main/res/layout/search_item.xml
diff --git a/app/src/main/res/layout/styled_text_buttons.xml b/app/src/main/res/layout/styled_text_buttons.xml
new file mode 100644
index 00000000..a87e3e41
--- /dev/null
+++ b/app/src/main/res/layout/styled_text_buttons.xml
@@ -0,0 +1,99 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/styled_text_dialog.xml b/app/src/main/res/layout/styled_text_dialog.xml
new file mode 100644
index 00000000..f90a1c2c
--- /dev/null
+++ b/app/src/main/res/layout/styled_text_dialog.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/messages_compose_suggestion_item.xml b/app/src/main/res/layout/teacher_item.xml
similarity index 55%
rename from app/src/main/res/layout/messages_compose_suggestion_item.xml
rename to app/src/main/res/layout/teacher_item.xml
index cbc8591f..ecfa14fb 100644
--- a/app/src/main/res/layout/messages_compose_suggestion_item.xml
+++ b/app/src/main/res/layout/teacher_item.xml
@@ -8,12 +8,12 @@
+ android:layout_marginStart="8dp"
+ android:layout_marginLeft="8dp"
+ tools:srcCompat="@drawable/bg_circle" />
-
\ No newline at end of file
+
+
+
+
diff --git a/app/src/main/res/layout/teachers_list_fragment.xml b/app/src/main/res/layout/teachers_list_fragment.xml
new file mode 100644
index 00000000..9138b401
--- /dev/null
+++ b/app/src/main/res/layout/teachers_list_fragment.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/template_fragment.xml b/app/src/main/res/layout/template_fragment.xml
index 9a015c15..d80df010 100644
--- a/app/src/main/res/layout/template_fragment.xml
+++ b/app/src/main/res/layout/template_fragment.xml
@@ -26,7 +26,7 @@
app:tabSelectedTextColor="?colorPrimary"
app:tabTextColor="?android:textColorPrimary" />
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/timetable_lesson.xml b/app/src/main/res/layout/timetable_lesson.xml
index 3b51a197..491788b5 100644
--- a/app/src/main/res/layout/timetable_lesson.xml
+++ b/app/src/main/res/layout/timetable_lesson.xml
@@ -48,16 +48,16 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
- android:paddingHorizontal="8dp"
+ android:baselineAligned="false"
android:orientation="horizontal"
- android:baselineAligned="false">
+ android:paddingHorizontal="8dp"
+ android:paddingVertical="4dp">
-
+ android:background="@drawable/unread_red_circle"
+ android:visibility="@{unread ? View.VISIBLE : View.GONE}" />
-
-
-
+ tools:text="3" />
@@ -149,7 +138,8 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="end|bottom"
- android:orientation="horizontal">
+ android:orientation="horizontal"
+ android:paddingBottom="2dp">
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 9949f2e2..92471e03 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -656,6 +656,7 @@
Nachricht
Schreiben Sie eine Nachricht
Nachrichten
+ Mehr
Verhalten
Benachrichtigungen
Löschen
@@ -675,7 +676,7 @@
Bestätigen Sie das Senden der Nachricht
Fügen Sie einen Anhang hinzu
Nachricht abbrechen
- Entwurf speichern
+ Entwurf speichern
Senden
Dieser Empfänger wurde bereits ausgewählt
Wählen Sie die Empfänger aus
@@ -856,7 +857,7 @@
Open-Source-Lizenzen
Datenschutzrichtlinie
E-Klassenbuch
- © Kuba Szczodrzyński && Kacper Ziubryniewicz\nSeptember 2018 - Februar 2021
+ © Kuba Szczodrzyński && Kacper Ziubryniewicz\nSeptember 2018 - 2022
Klicken Sie hier, um nach Aktualisierungen zu suchen
Aktualisierung
Version
@@ -967,6 +968,8 @@
Öffnen Sie das Menü mit der Zurück-Taste
Schlittenfahrt im Schnee
Jingle Bells, Jingle Bells
+ Brrrr
+ Bajo jajo, bajo jajo
Rosa
System
Thema
@@ -1106,6 +1109,7 @@
Administrator / Superadministrator
Lehrer
Kategorie durchsuchen
+ Keine Lehrer.
Übermorgen
Vorgestern
Bernstein
@@ -1232,4 +1236,8 @@
Netzwerkverbindung
(Kind)
(Elternteil)
+ Anwendungsentwickler
+ Liste der Szkolny-Entwickler
+ Lehrer
+ Addressbuch herunterladen…
diff --git a/app/src/main/res/values-en/plurals.xml b/app/src/main/res/values-en/plurals.xml
index 1bada092..a600f325 100644
--- a/app/src/main/res/values-en/plurals.xml
+++ b/app/src/main/res/values-en/plurals.xml
@@ -55,4 +55,13 @@
- %1$s - %2$d unread
- %1$s - %2$d unread
-
\ No newline at end of file
+
+
+ - %d contribution
+ - %d contributions
+
+
+ - %d translation
+ - %d translations
+
+
diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml
index 2f5cb1fe..97eaeac1 100644
--- a/app/src/main/res/values-en/strings.xml
+++ b/app/src/main/res/values-en/strings.xml
@@ -348,7 +348,7 @@
information
Added %1$s by %2$s%3$s
Added %1$s by you%3$s
- Adde %1$s%3$s
+ Added %1$s%3$s
{cmd-share-variant} %1$s by %2$s%3$s
{cmd-share-variant} %1$s by you%3$s
Removing event…
@@ -356,7 +356,7 @@
Sharing event…
Removing event from the rest of the class…
Removing shared event…
- Are you sure you want to mark this task as completed?\n\nIt won\'t show up on the main or homework pages, but will still be available in the timetable
+ Are you sure you want to mark this task as completed?\n\nIt won\'t show up on the main or homework pages, but will still be available in the agenda.
Mark as done
other
project
@@ -658,6 +658,7 @@
Message
Compose
Messages
+ More
Behaviour
Notifications
Clear
@@ -677,7 +678,7 @@
Confirm sending the message
Add an attachment
Abort message
- Save draft
+ Save draft
Send
This recipient has already been selected
Select recipients
@@ -858,7 +859,7 @@
Open-source licenses
Privacy policy
E-register
- © Kuba Szczodrzyński && Kacper Ziubryniewicz\nSeptember 2018 - February 2021
+ © Kuba Szczodrzyński && Kacper Ziubryniewicz\nSeptember 2018 - 2022
Click to check for updates
Update
Version
@@ -969,6 +970,8 @@
Back button opens drawer
Jingle all the way
Jingle bells, Jingle bells
+ Brrrr
+ Bajo jajo, bajo jajo
Pink
System
Theme
@@ -1108,6 +1111,7 @@
SuperAdmin
Teacher
Browse category
+ No teachers.
overmorrow
the day before yesterday
Amber
@@ -1235,5 +1239,216 @@
In order to download the file, you have to grant file storage permission for the application.\n\nClick OK to grant the permission.
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.
Required permissions
- Your mother won\'t see your F grades
+ App contributors
+ List of Szkolny.eu contributors
+ Contributors
+ Translators
+ Hide sticks from old
+ Official build
+ Google Play
+ Testing version
+ {cmd-information-outline} Recommended
+ {cmd-android-studio} Developer version
+ {cmd-alert-circle-outline} Testing version
+ \???
+ See also
+ Source code
+ Git branch
+ .APK
+ Unofficial (.APK)
+ Commit hash
+ Unsaved changes
+ Last tag
+ Commits since last tag
+ Remote repository
+ Build details
+ Check code
+ Profile name
+ Logout
+ Synchronize this profile
+ Update
+ Not now
+ Update
+ EduDziennik
+ Log in - %s
+ Date
+ Teacher
+ By days
+ List
+ lesson %d
+ By type
+ Add new student
+ Lesson subject
+ Read more
+ App version
+ Version details
+ Build date
+ No API access
+ Time
+ Help with app development on GitHub
+ Distribution
+ Build verification in progress…
+ By months
+ Summary
+ Type
+ See more
+ Update available
+ Build details
+ Update app to the latest version - %s.
+ App update available
+ Close archive
+ Archived profile
+ Profile is archived
+ Holiday ;)
+ The end of the school year
+ Logout from other devices
+ Student profile not found.
+ Go to application website
+ Get help or support authors
+ Information about application version
+ No current profile
+ Loading e-registers list…
+ Log in using token
+ Provide mobile app token.
+ Attendance configuration
+ Show symbols and colors from e-register config
+ There are no absences here.
+ There are no grades in this semester
+ Attendance settings
+ What is your e-register at school?
+ Log in using login and password
+ Use a login in form of \"9874123u\"
+ Use token, symbol and PIN code
+ Register device on journal VULCAN® page
+ Use e-mail/username and password
+ Logging in to PPE…
+ Podlaska Platforma Edukacyjna
+ Log in using e-mail and password
+ Logging in to VULCAN® register…
+ Login via VULCAN® platform
+ Log in using e-mail
+ Attendance ID
+ Log in with the server name, login and password
+ Log in with the data that you provide on VULCAN® e-register website
+ Provide data, that you use on e-register website
+ Base type ID
+ Counted to the stats?
+ Choose which e-register your school uses. If you have several accounts in different e-registers, you will be able to add them later.
+ You must have a LIBRUS® Rodzina account
+ Only Oświata w Radomiu and Innowacyjny Tarnobrzeg
+ How do you log into the e-register?
+ Group consecutive days on the list
+ Visible when the list is expanded
+ Display attendance in months view
+ %.2f%%
+ Attendance during this period: %.2f%%
+ Log in child/parent account in app
+ Enter the e-mail address and password that you use to log in to the browser on the EduDziennik website.
+ Probably the school year for this student has not yet started (will start %s). Please try to sync later.
+ The school year ended on %s. Student data from the previous year will be moved to the archive for later review.
+ Trademarks featured in this application belong to their rightful owners and are used for informational purposes only.
+ You are viewing a student\'s data from the school year %d/%d.
+ Use data, that you enter on the e-register website
+ To be able to scan the QR code, you need to grant access to the camera.\n\nClick OK to grant permissions.
+ It\'s a pity, the opinions of others help me develop the application.
+ 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.
+ You have an outdated version of the Szkolny.eu application. You need to update the app to continue to sync data.
+ 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
+ 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.
+ Log in to LIBRUS® Synergia on your computer, select the Mobile Applications tab, then enter the received Token and PIN below.
+ 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.
+ 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.
+ If it takes too long, please check your internet connection and restart the application.
+ 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.
+ 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.
+ 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.
+ Enter the data that you log in to the VULCAN® log website or the city platform.
+ 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
+ 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.
+ You are using a \"debug\" build. This information will only be displayed once for the current device.
+ 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.
+ The hash of the current commit was not found. Check Gradle configuration.
+ 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.
+ A reference to a remote repository was not found. Make sure you are using the official repository fork and verify your Gradle configuration.
+ "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. "
+ 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.
+ (Child)
+ (Parent)
+ Teachers
+ Syncing addressbook…
+ Send message
+ Black
+ Gray
+ Notes
+ Notes
+ Edit note
+ Color
+ Brown
+ Pink
+ Dark blue
+ Purple
+ Blue
+ Teal
+ Green
+ Yellow
+ Orange
+ Red
+ Add note
+ Grade
+ read the Privacy Policy and accept its provisions. The authors of the application are not responsible for the use of the Szkolny.eu application.]]>
+ Szkolny.eu v%s\n%s
+ Appearance
+ Online learning
+ online lesson
+ Messages settings
+ Delete
+ Reply
+ Forward
+ Discard
+ Draft message saved
+ Drafts
+ Send a message
+ Notes
+ Notes
+ Added on %1$s
+ Attendance
+ Behavior
+ Day
+ Event
+ Lesson
+ Message
+ Note ID
+ Owner ID
+ Announcement
+ Edit text
+ The QR code doesn\'t seem correct
+ You can add notes and share them with your class using the Add button.
+ {cmd-playlist-edit} notes added
+ {cmd-playlist-edit} notes added\n{cmd-swap-horizontal} text replaced with a note
+ No notes added yet
+ Do you want to discard the saved version of the message?\n\nThis will also cancel your changes and delete your message.
+ Discard draft
+ Draft message deleted
+ Discard draft
+ Note
+ Note title (optional)
+ Note text
+ The classmates will see your note next to this item.
+ The content of the note will be visible instead of the original content of the item.
+ Enter the note text
+ None
+ Note shared
+ Do you want to delete this note?\n\nIt will be deleted on your device and on classmates\' devices.
+ Deleting shared note…
+ Sharing the note…
+ Replace original content
+ Newest notes
+ Save changes
+ Do you want to save your changes as a draft?\n\nIt will be possible to edit and send the message later.
+ The original message could not be loaded.
+ Download again
+ Mark with a star
+ Details
+ Agenda settings
+ Share notes
diff --git a/app/src/main/res/values/errors.xml b/app/src/main/res/values/errors.xml
index d90af9ed..827227c5 100644
--- a/app/src/main/res/values/errors.xml
+++ b/app/src/main/res/values/errors.xml
@@ -165,6 +165,7 @@
ERROR_VULCAN_HEBE_CERTIFICATE_GONE
ERROR_VULCAN_HEBE_SERVER_ERROR
ERROR_VULCAN_HEBE_ENTITY_NOT_FOUND
+ ERROR_VULCAN_HEBE_MISSING_SENDER_ENTRY
ERROR_VULCAN_API_DEPRECATED
ERROR_LOGIN_EDUDZIENNIK_WEB_INVALID_LOGIN
@@ -363,6 +364,7 @@
VULCAN®: urządzenie usunięte. Zaloguj się ponownie do dziennika.
VULCAN®: błąd serwera. Dziennik może być przeciążony.
VULCAN®: nie znaleziono bytu
+ Błąd wysyłania wiadomości - brak informacji o nadawcy.
W związku z wygaszeniem aplikacji Dzienniczek+ przez firmę Vulcan, należy zalogować się ponownie.\n\nAby móc dalej korzystać z aplikacji Szkolny.eu, otwórz Ustawienia i wybierz opcję Dodaj nowego ucznia.\nNastępnie zaloguj się do dziennika Vulcan zgodnie z instrukcją.\n\nPrzepraszamy za niedogodności.
Błędny email lub hasło
diff --git a/app/src/main/res/values/plurals.xml b/app/src/main/res/values/plurals.xml
index bb74ba85..bcd87eca 100644
--- a/app/src/main/res/values/plurals.xml
+++ b/app/src/main/res/values/plurals.xml
@@ -159,4 +159,15 @@
- %d oceny
- %d ocen
+
+
+ - %d commit
+ - %d commity
+ - %d commit\'ów
+
+
+ - %d tłumaczenie
+ - %d tłumaczenia
+ - %d tłumaczeń
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index e9a9befb..ba7f7458 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -301,6 +301,7 @@
Dzięki niej, aplikacja Szkolny.eu może synchronizować dane z e-dziennikiem. Możesz ją zamknąć, ponieważ w tej chwili nic nie robi.
Usługa synchronizacji
Pobieranie szczegółów konta…
+ Pobieranie listy odbiorców…
Pobieranie ogłoszeń szkolnych…
Pobieranie frekwencji ucznia…
Pobieranie kategorii obecności…
@@ -708,6 +709,7 @@
Wiadomość
Napisz wiadomość
Wiadomości
+ Więcej
Zachowanie
Powiadomienia
Usuń wszystkie
@@ -716,6 +718,7 @@
Ustawienia
Synchronizuj
Synchronizuj wszystkie
+ Nauczyciele
Szablon
Plan lekcji
Edytor planu lekcji
@@ -730,7 +733,7 @@
Potwierdź wysłanie wiadomości
Dodaj załącznik
Odrzuć wiadomość
- Zapisz wersję roboczą
+ Zapisz wersję roboczą
Wyślij
Ten odbiorca został już wybrany
Wybierz odbiorców
@@ -744,7 +747,7 @@
Napisz wiadomość…
Napisz wiadomość
Do
- %s, %s
+ %s\n%s
Czy chcesz usunąć wiadomość?
Spowoduje to przeniesienie wiadomości do zakładki \"Usunięte\" w aplikacji. Zmiany nie wpłyną na wiadomość w e-dzienniku (nie zostanie ona tam usunięta).
Błąd pobierania wiadomości
@@ -898,7 +901,9 @@
Pobieranie udostępnionych wydarzeń…
Rejestracja jest automatyczna, jeśli ta opcja jest włączona. Pozwala na tworzenie i odbieranie wydarzeń udostępnionych innym uczniom z Twojej klasy. Dzięki temu, można dodawać do dziennika pozycje nie zapisane przez nauczyciela.\n\nKontynuując, oświadczasz przeczytanie i akceptację postanowień Polityki prywatności .
Aby móc udostępnić wydarzenie, należy włączyć opcję rejestracji na serwerze. Pozwala to na tworzenie i odbieranie wydarzeń udostępnionych w Twojej klasie.\n\nPo kliknięciu OK zostanie ona automatycznie włączona.\n\nKontynuując, oświadczasz przeczytanie i akceptację postanowień Polityki prywatności .
+ Aby móc udostępnić notatkę, należy włączyć opcję rejestracji na serwerze. Pozwala to na tworzenie i odbieranie notatek udostępnionych w Twojej klasie.\n\nPo kliknięciu OK zostanie ona automatycznie włączona.\n\nKontynuując, oświadczasz przeczytanie i akceptację postanowień Polityki prywatności .
Udostępnianie wydarzeń
+ Udostępnianie notatek
Rejestracja aplikacji Szkolny.eu
Usuń
Usunięto
@@ -921,7 +926,7 @@
Licencje open-source
Polityka prywatności
E-dziennik
- © Kuba Szczodrzyński && Kacper Ziubryniewicz\nwrzesień 2018 - luty 2021
+ © Kuba Szczodrzyński && Kacper Ziubryniewicz\nwrzesień 2018 - 2022
Kliknij, aby sprawdzić aktualizacje
Aktualizacja
Wersja
@@ -1034,6 +1039,8 @@
Otwieraj menu przyciskiem wstecz
Dzwonią dzwonki sań
Pada śnieg, pada śnieg
+ Brrrr
+ Bajo jajo, bajo jajo
Różowy
Systemowy
Motyw
@@ -1178,6 +1185,7 @@
Administrator / SuperAdministrator
Nauczyciel
Przeglądaj kategorię
+ Brak nauczycieli.
pojutrze
przedwczoraj
Bursztynowy
@@ -1346,10 +1354,6 @@
Podaj dane, którymi logujesz się na stronie internetowej dziennika VULCAN® lub na miejskiej platformie.
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.
Logowanie do dziennika VULCAN®...
- iDziennik Progman / iUczniowie
- Zaloguj używając nazwy użytkownika i hasła
- Podaj dane, których używasz na stronie internetowej e-dziennika
- 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\".
EduDziennik
Zaloguj używając e-maila i hasła
Użyj danych, które podajesz na stronie internetowej e-dziennika
@@ -1393,6 +1397,10 @@
Zobacz także
Wejdź na stronę aplikacji
Uzyskaj pomoc lub wesprzyj autorów
+ Twórcy aplikacji
+ Lista twórców Szkolnego
+ Współtwórcy
+ Tłumacze
Kod źródłowy
Pomóż w rozwoju aplikacji na GitHubie
Nazwa profilu
@@ -1419,7 +1427,7 @@
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
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.
Korzystasz z kompilacji typu \"debug\". Ta informacja zostanie wyświetlona tylko jeden raz dla aktualnego urządzenia.
- 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.
+ 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.
Informacja dotycząca wersji aplikacji
Szczegóły wersji
Informacje o kompilacji
@@ -1427,4 +1435,118 @@
Brak dostępu do API
Data kompilacji
Aby móc zapisać wygenerowany plan lekcji musisz przyznać uprawnienia dostępu do pamięci urządzenia.\n\nKliknij OK, aby przyznać uprawnienia.
+ przeczytanie Polityki prywatności i akceptujesz jej postanowienia. Autorzy aplikacji nie biorą odpowiedzialności za korzystanie z aplikacji Szkolny.eu.]]>
+ Szkolny.eu v%s\n%s
+ Ustawienia terminarza
+ Wygląd
+ Pokazuj zmiany planu lekcji
+ Pokazuj nieobecności nauczycieli
+ Tryb kompaktowy
+ Mniejszy rozmiar wydarzeń na liście
+ Grupuj wydarzenia tego samego typu
+ Niedostępne w trybie kalendarza
+ Udostępnianie wydarzeń
+ Włącz Udostępnianie wydarzeń
+ Nauczanie zdalne
+ Ustaw wydarzenia jako lekcje on-line
+ Wybierz rodzaj wydarzeń
+ Grupuj lekcje on-line na liście
+ {cmd-clipboard-edit-outline} wydarzenie dodane ręcznie
+ {cmd-check} oznaczono jako wykonane
+ Funkcja jeszcze nie jest dostępna.
+ Tworzenie wiadomości
+ Dodaj podpis przy tworzeniu wiadomości
+ Dodaj podpis przy odpowiadaniu na wiadomość
+ Dodaj podpis przy przekazywaniu wiadomości
+ Treść podpisu
+ Ustawienia wiadomości
+ lekcja online
+ Rodzaj: %s\nData: %s (%s)\nLekcja: %s\nNauczyciel: %s
+ Rodzaj: %s\nPrzedmiot: %s\nTermin: %s (%s), %s\nTreść: %s
+ Dodane przez: %s\nRodzaj: %s\nPrzedmiot: %s\nTermin: %s (%s), %s\nTreść: %s
+ Ocena: %s (waga %s)\nPrzedmiot: %s\nKategoria: %s\nOpis: %s\nNauczyciel: %s
+ Rodzaj: %s\nNauczyciel: %s\nTreść: %s
+ Rodzaj: %s\nTermin: %s, %s\nNr lekcji: %s\nPrzedmiot: %s\nNauczyciel: %s\nTemat lekcji: %s
+ \@%s - %s
+ Najłatwiejszy sposób na korzystanie z e-dziennika.
+ Szczegóły
+ Pogrubienie
+ Pochylenie
+ Podkreślenie
+ Przekreślenie
+ Indeks dolny
+ Indeks górny
+ Wyczyść format
+ Oznacz gwiazdką
+ Przekaż dalej
+ Usuń
+ Odpowiedz
+ Pobierz ponownie
+ Re: %s
+ Fwd: %s
+ Nie udało się wczytać oryginalnej wiadomości.
+ Zapisz zmiany
+ Czy chcesz zapisać zmiany jako wersję roboczą?\n\nBędzie możliwa późniejsza edycja oraz wysłanie wiadomości.
+ Odrzuć
+ Zapisano wersję roboczą
+ Wersje robocze
+ Wyślij wiadomość
+ Usuń wersję roboczą
+ Usunięto wersję roboczą
+ Usuń wersję roboczą
+ Czy chcesz odrzucić zapisaną wersję wiadomości? Spowoduje to również anulowanie wprowadzonych zmian i usunięcie wiadomości.
+ https://
+ .mobidziennik.pl/
+ Edytuj tekst
+ Wyślij wiadomość
+ Kod QR nie wygląda na prawidłowy
+ Notatki
+ Notatki
+ Nie dodano żadnych notatek
+ Możesz dodać notatki oraz udostępnić je w klasie, używając przycisku Dodaj.
+ {cmd-playlist-edit} dodano notatki
+ {cmd-playlist-edit} dodano notatki\n{cmd-swap-horizontal} zastąpiono treść notatką
+ Dodano %1$s
+ Ogłoszenie szkolne
+ Frekwencja
+ Zachowanie
+ Dzień
+ Wydarzenie
+ Ocena
+ Lekcja
+ Wiadomość
+ ID notatki
+ ID właściciela
+ Notatka
+ Edytuj notatkę
+ Tytuł notatki (opcjonalnie)
+ Treść notatki
+ Zastąp oryginalną treść
+ Osoby z klasy zobaczą Twoją notatkę przy tym elemencie.
+ Treść notatki będzie widoczna zamiast oryginalnej treści elementu.
+ Podaj treść notatki
+ Kolor
+ Udostępnianie notatki…
+ Usuwanie udostępnionej notatki…
+ Udostępniono notatkę
+ Czy chcesz usunąć tą notatkę?\n\nZostanie ona usunięta na Twoim urządzeniu, oraz u pozostałych osób z klasy.
+ Notatki
+ Dodaj notatkę
+ Brak
+ Czerwony
+ Pomarańczowy
+ Żółty
+ Zielony
+ Morski
+ Niebieski
+ Ciemnoniebieski
+ Fioletowy
+ Różowy
+ Brązowy
+ Szary
+ Czarny
+ Notatki
+ Najnowsze notatki
+ (uczeń)
+ (rodzic)
diff --git a/app/src/play-not/java/pl/szczodrzynski/edziennik/network/SSLProviderInstaller.kt b/app/src/play-not/java/pl/szczodrzynski/edziennik/network/SSLProviderInstaller.kt
new file mode 100644
index 00000000..a18243b1
--- /dev/null
+++ b/app/src/play-not/java/pl/szczodrzynski/edziennik/network/SSLProviderInstaller.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2022-2-21.
+ */
+
+package pl.szczodrzynski.edziennik.network
+
+import android.content.Context
+import eu.szkolny.sslprovider.SSLProvider
+import eu.szkolny.sslprovider.enableSupportedTls
+import okhttp3.OkHttpClient
+import timber.log.Timber
+
+object SSLProviderInstaller {
+
+ fun install(applicationContext: Context, rebuildCallback: () -> Unit) {
+ SSLProvider.install(
+ applicationContext,
+ downloadIfNeeded = true,
+ supportTls13 = false,
+ onFinish = {
+ rebuildCallback()
+ },
+ onError = {
+ Timber.e("Failed to install SSLProvider: $it")
+ it.printStackTrace()
+ }
+ )
+ }
+
+ fun enableSupportedTls(builder: OkHttpClient.Builder, enableCleartext: Boolean = true) {
+ builder.enableSupportedTls(enableCleartext)
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/sync/UpdateDownloaderService.kt b/app/src/play-not/java/pl/szczodrzynski/edziennik/sync/UpdateDownloaderService.kt
similarity index 100%
rename from app/src/main/java/pl/szczodrzynski/edziennik/sync/UpdateDownloaderService.kt
rename to app/src/play-not/java/pl/szczodrzynski/edziennik/sync/UpdateDownloaderService.kt
diff --git a/app/src/play/java/pl/szczodrzynski/edziennik/network/SSLProviderInstaller.kt b/app/src/play/java/pl/szczodrzynski/edziennik/network/SSLProviderInstaller.kt
new file mode 100644
index 00000000..53b4e769
--- /dev/null
+++ b/app/src/play/java/pl/szczodrzynski/edziennik/network/SSLProviderInstaller.kt
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2022-2-21.
+ */
+
+package pl.szczodrzynski.edziennik.network
+
+import android.content.Context
+import okhttp3.OkHttpClient
+
+object SSLProviderInstaller {
+
+ fun install(applicationContext: Context, rebuildCallback: () -> Unit) {
+
+ }
+
+ fun enableSupportedTls(builder: OkHttpClient.Builder, enableCleartext: Boolean = true) {
+
+ }
+}
diff --git a/app/src/play/java/pl/szczodrzynski/edziennik/sync/UpdateDownloaderService.kt b/app/src/play/java/pl/szczodrzynski/edziennik/sync/UpdateDownloaderService.kt
new file mode 100644
index 00000000..4b7f4342
--- /dev/null
+++ b/app/src/play/java/pl/szczodrzynski/edziennik/sync/UpdateDownloaderService.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2022-2-21.
+ */
+
+package pl.szczodrzynski.edziennik.sync
+
+import android.app.IntentService
+import android.content.Intent
+import android.widget.Toast
+import pl.szczodrzynski.edziennik.utils.Utils
+
+class UpdateDownloaderService : IntentService(UpdateDownloaderService::class.java.simpleName) {
+
+ override fun onHandleIntent(intent: Intent?) {
+ try {
+ Utils.openGooglePlay(this, application.packageName)
+ }
+ catch (e: Exception) {
+ e.printStackTrace()
+ Toast.makeText(this, "Nie znaleziono Google Play. Pobierz aktualizację ręcznie.", Toast.LENGTH_SHORT).show()
+ }
+ }
+}
diff --git a/build.gradle b/build.gradle
index 441d5c9c..ad977b77 100644
--- a/build.gradle
+++ b/build.gradle
@@ -2,11 +2,11 @@
buildscript {
ext {
- kotlin_version = '1.4.31'
+ kotlin_version = '1.5.30'
release = [
- versionName: "4.7-rc.1",
- versionCode: 4070010
+ versionName: "4.11.3",
+ versionCode: 4110399
]
setup = [
@@ -18,13 +18,13 @@ buildscript {
repositories {
google()
- jcenter()
+ mavenCentral()
}
dependencies {
- classpath "com.android.tools.build:gradle:4.2.0-beta06"
+ classpath 'com.android.tools.build:gradle:7.0.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
- classpath 'com.google.gms:google-services:4.3.5'
- classpath 'com.google.firebase:firebase-crashlytics-gradle:2.5.1'
+ classpath 'com.google.gms:google-services:4.3.10'
+ classpath 'com.google.firebase:firebase-crashlytics-gradle:2.7.1'
}
}
@@ -37,11 +37,8 @@ allprojects {
}
repositories {
google()
- jcenter()
+ mavenCentral()
maven { url 'https://jitpack.io' }
- maven { url "https://kotlin.bintray.com/kotlinx/" }
- maven { url "https://dl.bintray.com/wulkanowy/wulkanowy" }
- maven { url "https://dl.bintray.com/undervoid/PowerPermission" }
}
}
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 9bd0ad16..4c24d72f 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
#Wed Feb 17 14:04:38 CET 2021
distributionBase=GRADLE_USER_HOME
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME