Compare commits
3 Commits
b447adec0c
...
4e3e6a455e
| Author | SHA1 | Date | |
|---|---|---|---|
| 4e3e6a455e | |||
| ca545ab1d1 | |||
| b0842d9fbd |
@@ -95,7 +95,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.gallery-zone:hover .gallery-img {
|
.gallery-zone:hover .gallery-img {
|
||||||
filter: brightness(40%) saturate(60%);
|
filter: brightness(50%) saturate(80%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.gallery-text-container {
|
.gallery-text-container {
|
||||||
@@ -117,7 +117,7 @@
|
|||||||
height: fit-content;
|
height: fit-content;
|
||||||
font-size: 1.0rem;
|
font-size: 1.0rem;
|
||||||
line-height: 1.4rem;
|
line-height: 1.4rem;
|
||||||
text-shadow: 0 0 6px black;
|
text-shadow: 0 0 6px black, 0 0 9px black;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gallery-desc-container {
|
.gallery-desc-container {
|
||||||
|
|||||||
@@ -155,10 +155,6 @@
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-highlight-marker {
|
|
||||||
color: aqua;
|
|
||||||
}
|
|
||||||
|
|
||||||
.project-title {
|
.project-title {
|
||||||
font-family: var(--font-mono);
|
font-family: var(--font-mono);
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
|
|||||||
@@ -1,7 +1,126 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import ImageGallery from "$lib/lists/image-gallery.svelte";
|
||||||
|
</script>
|
||||||
|
|
||||||
**AvH-Vertretungsplan** was an app I developed back in 2019 to view the substitution plan of my school more easily, since I disliked the disorganised design of the school's website. The substitution app allowed me to be more quickly informed about my courses, as it notified me about any cancellations and changes in my schedule. It also allowed me to view the canteen offers, even though I'd never eaten at the school's canteen.
|
**AvH-Vertretungsplan** was an app I developed back in 2019 to view the substitution plan of my school more easily, since I disliked the disorganised design of the school's website. The substitution app allowed me to be more quickly informed about my courses, as it notified me about any cancellations and changes in my schedule. It also allowed me to view the canteen offers, even though I'd never eaten at the school's canteen.
|
||||||
|
|
||||||
The app was initially developed in Java and later converted into Kotlin as part of my Kotlin learning effort. The backend of the app, since the school website required a login to view the substitution plan, ran on a Raspberry Pi 3B using a Python script that scraped the website using Selenium and sent out notifications via Firebase. The Raspberry Pi crashed frequently and the script was not perfect, requiring me to frequently remote into the Pi using VNC, often while I was at school, to edit the script!
|
The app was initially developed in Java and later converted into Kotlin as part of my Kotlin learning effort. The backend of the app – which I needed as the school website required a login to view the substitution plan – ran on a Raspberry Pi 3B using a Python script that scraped the website using Selenium and sent out notifications via Firebase. The Raspberry Pi crashed often and the script was not perfect, requiring me to frequently remote into the Pi using VNC, often while I was at school, to edit the script!
|
||||||
|
|
||||||
Since the app was written for native Android but a few of my classmates had iPhones, I had an iOS version in the works, which was developed entirely on a MacOS VM running on my laptop and debugged using an iPhone simulator in Xcode. The app achieved feature parity with the Android version at one point, but since I lacked funding to pay the $99-a-year fee Apple charges developers, it was never published and thus abandoned.
|
Since the app was written for native Android but a few of my classmates had iPhones, I had an iOS version in the works, which was developed entirely on a MacOS VM running on my laptop and debugged using an iPhone simulator in Xcode. The app achieved feature parity with the Android version at one point, but since I lacked funding to pay the $99-a-year fee Apple charges developers, it was never published and thus abandoned.
|
||||||
|
|
||||||
This app was genuinely one of my most fun projects, not only serving me as a handy tool, but also being a great playground for my programming practice, since it taught me to build UIs, use databases, and more. The app had been downloaded by 250 other students at my school, which I was – and still am – very proud of.
|
This app was genuinely one of my most fun projects, not only serving me as a handy tool, but also being a great playground for my programming practice, since it taught me to build UIs, use databases, and more. The app had been downloaded by 250 other students at my school, which I was – and still am – very proud of.
|
||||||
|
|
||||||
|
<!-- reaction here -->
|
||||||
|
<!-- one of my favourite memories regarding this app is the time I first presented the app to my classmates. I had someone from my class help me in presenting; he did the intro, hyping up the crowd, and I followed up by showing and explaining the app. I'm still very grateful he decided to help me with this! Thanks, Leon! -->
|
||||||
|
|
||||||
|
<ImageGallery
|
||||||
|
images={[
|
||||||
|
{
|
||||||
|
src: "html_plan.webp",
|
||||||
|
alt: "A website showing a table of substitution plan entries. The website is not styled at all, showing only black text on a white background. The page is running on the domain djd4rkn355.github.io, which is my old website.",
|
||||||
|
desc: [
|
||||||
|
"In order to start development on the app, I had to find a way to get the substitution plan info from my school's website into a format my app could understand. As the plan was behind a login, I couldn't straight-up scrape the website from mobile, so I used a Raspberry Pi using a webdriver to log in with my account and then scrape the info, then put it into a HTML document that was uploaded online – on the predecessor to this very website! This did mean that this information was now publicly accessible, but that didn't become a concern until later.",
|
||||||
|
"This did mean that I was essentially scraping <i>twice</i> – once on the Pi, and once in the app, but it worked so I wasn't concerned back then. Looking back, I should REALLY have used JSON instead.",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: "app_0.webp",
|
||||||
|
alt: "A table of substitution plan entries in an Android app with a pink header. The entries are listed in rows.",
|
||||||
|
desc: [
|
||||||
|
"Initially, I was unsure whether I could create the kind of app I had in mind, but once my app successfully fetched information online and displayed it for the first time, I knew I could do it.",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: "app_1.webp",
|
||||||
|
alt: "The substitution plan app with a card-based design, which visually separates entries.",
|
||||||
|
desc: [
|
||||||
|
"I quickly moved to a card-based design, which I stuck with for the entire lifetime of the app.",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: "plan.webp",
|
||||||
|
alt: "The substitution plan app on the 'personal' tab greeting the user and listing three entries for courses with information on them.",
|
||||||
|
desc: [
|
||||||
|
"This is what the app eventually looked like. I added user-configurable colours (because we could never reach a consensus on which colours the courses should be assigned).",
|
||||||
|
"The bottom row shows the screens the app later gained, from left to right: the full substitution plan, the personalised plan (where only your own courses are displayed), the information box for supplementary notes, the canteen plan, and settings.",
|
||||||
|
"I also added an optional greeting at the top of the screen because I thought it would be nice.",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: "darkmode.webp",
|
||||||
|
alt: "The substitution plan app in dark mode.",
|
||||||
|
desc: [
|
||||||
|
"Of course, a dark mode existed too.",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: "notifs.webp",
|
||||||
|
alt: "A screenshot of the notification centre of a Motorola moto g with two notifications from the substitution plan app. One is listing course cancellations, while the other is notifying of a successful push to GitHub.",
|
||||||
|
desc: [
|
||||||
|
"The app sent out notifications any time there was information on your own courses. This was accomplished by sending a 'new info available' message to the app, which would then fetch the plan and check whether the user's courses were affected. I used Google's Firebase for this.",
|
||||||
|
"This, <b>THIS</b> is the sole reason I made this app. I wanted an automatic way of knowing whether my courses were cancelled. I didn't want to manually check the substitution plan every day, especially since the website wasn't mobile-friendly and thus hard to visually parse on a small screen.",
|
||||||
|
"I, of course, also used the opportunity to implement a dev notification which only I received that notified me if the Raspberry Pi detected changes in the substitution plan and had uploaded them. Sometimes, I would get multiple within a few minutes. When this happened, I knew something was wrong with my script and I had to fix it.",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: "email.webp",
|
||||||
|
alt: "An email notifying of a server issue.",
|
||||||
|
desc: [
|
||||||
|
"If something REALLY went wrong, the Pi would send an email to me. If this happened, I knew I had to fix the script right away. I did this by logging into the Pi via VNC and fixing the script in an IDE running on the Pi. No, I didn't know what SSH was at the time.",
|
||||||
|
"Sometimes, I had to fix the script in the middle of my classes, which teachers never minded. I think it's because people didn't bring their laptops/tablets to school back then usually, so they didn't think I was browsing the internet to distract myself or something.",
|
||||||
|
"Taking care of the server gave me a limited sense of responsibility, which I liked. People depended on me, on my app, for the important but ultimately not critical task of knowing whether they could have a lazy morning.",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: "diagnostics.webp",
|
||||||
|
alt: "A diagnostics screen within the substitution plan app listing device and app data as well as the launch and notification count.",
|
||||||
|
desc: [
|
||||||
|
"I put in a little diagnostics menu hidden behind a dev code. This proved not only useful, but also very fun.",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: "intro.webp",
|
||||||
|
alt: "A setup screen welcoming the user and asking for details on their courses.",
|
||||||
|
desc: [
|
||||||
|
"Later on, once more people (and especially ones I didn't even know) started using the app, I built this welcome screen to get the app set up and guide users in inputting their course data. It wasn't extremely intuitive but it was efficient.",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: "loggedin.webp",
|
||||||
|
alt: "An unlocked lock with the text 'You have been logged in!' displayed underneath.",
|
||||||
|
desc: [
|
||||||
|
"Some months after publishing the app, my computer science teacher approached me and told me the school head caught wind of my app (woah!!) and asked whether I could put in a login screen, as the data should only be accessible to students.",
|
||||||
|
"I eventually found a way of doing this! The app opened a WebView with the school website's login screen, waited for the user to log in, and then looked for a cookie that notified the website that the user was logged in. The data within the cookie didn't matter – as long as it was present, the app could know the user had a valid account. The check was only performed once when setting up the app.",
|
||||||
|
"By virtue of hosting the data on my publicly-accessible website, the data was still available online without login... but no one minded.",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: "ios_plan.webp",
|
||||||
|
alt: "The substitution plan app running on an iOS simulator. It looks almost exactly like the Android version.",
|
||||||
|
desc: [
|
||||||
|
"The app also eventually existed on iOS! I developed this exclusively on a MacOS VM and an iOS simulator, as I had no Apple devices available to me. Surprisingly, I managed to recreate my app fully for iOS, but I only ever saw it running on a real device once or twice, as I couldn't get it published on the App Store, largely for financial reasons.",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: "ios_config.webp",
|
||||||
|
alt: "The settings screen on the iOS version of the app. It is displayed on an iOS-native modal.",
|
||||||
|
desc: [
|
||||||
|
"I made sure to design a UI that felt iOS-native.",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: "macvm.webp",
|
||||||
|
alt: "A preferences window on a MacOS VM displaying information on Xcode. It lists 'Mojave 10.14.3 by Techsviewer' as its location.",
|
||||||
|
desc: [
|
||||||
|
"I have no idea how I managed to set up this VM. It ran quite poorly on my Surface Laptop, but it worked, and I was determined enough to power through.",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: "psa.webp",
|
||||||
|
alt: "The substitution plan app displaying an end-of-year message.",
|
||||||
|
desc: [
|
||||||
|
"I also used my admin powers to send messages to users, like this one: 'Happy holidays and a good 2020!'",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
BIN
static/projects/apps/avhplan/app_0.webp
Normal file
|
After Width: | Height: | Size: 261 KiB |
BIN
static/projects/apps/avhplan/app_1.webp
Normal file
|
After Width: | Height: | Size: 89 KiB |
BIN
static/projects/apps/avhplan/darkmode.webp
Normal file
|
After Width: | Height: | Size: 90 KiB |
BIN
static/projects/apps/avhplan/diagnostics.webp
Normal file
|
After Width: | Height: | Size: 103 KiB |
BIN
static/projects/apps/avhplan/email.webp
Normal file
|
After Width: | Height: | Size: 120 KiB |
BIN
static/projects/apps/avhplan/html_plan.webp
Normal file
|
After Width: | Height: | Size: 232 KiB |
BIN
static/projects/apps/avhplan/intro.webp
Normal file
|
After Width: | Height: | Size: 102 KiB |
BIN
static/projects/apps/avhplan/ios_config.webp
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
static/projects/apps/avhplan/ios_plan.webp
Normal file
|
After Width: | Height: | Size: 47 KiB |
BIN
static/projects/apps/avhplan/loggedin.webp
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
static/projects/apps/avhplan/macvm.webp
Normal file
|
After Width: | Height: | Size: 266 KiB |
BIN
static/projects/apps/avhplan/notifs.webp
Normal file
|
After Width: | Height: | Size: 49 KiB |
BIN
static/projects/apps/avhplan/plan.webp
Normal file
|
After Width: | Height: | Size: 74 KiB |
BIN
static/projects/apps/avhplan/psa.webp
Normal file
|
After Width: | Height: | Size: 52 KiB |