You are browsing as a guest. Sign up (or log in) to start making projects!

bali0531

@bali0531

Joined June 1st, 2026

  • 23Devlogs
  • 2Projects
  • 3Ships
  • 54Votes
Ship

Last ship was about optimizing stuff and fixing small bugs, while this ship is a bit more focused on new features, a new OOBE page, dev mode for advanced statistics about the application, and another batch of bug and UI fixes.

So the main goal was to make the app look better and provide a better experience while using it overall + making it easier to diagnose possible problems through the devmode.

UI/UX fixes include a new OLED theme, fixed overlapping elements, fixed contrast of the pages. For the UX part a new Out Of Box Experience, its basically a new login/info flow on first start, so now instead of separate pages for TOS/PP login, institution search etc, everything is on one seemless experience.

It had some bug fixes too, like the .ics calendar exporting and auto-updating has been fixed now. Also fixed the broken widgets too so now it actually shows what classes you have that day.

In terms of New Functions this ship introduces a new Dev mode by clicking the version number fast multiple times, it has a lot of informations about the startup, resource usage etc.

For the future plans, I don’t have any new idea for now, the ones I had turned out to be not possible because of the api I have to use, I will probably fix the findings from the reviews of this ship + google playstore closed testing findings too. If you have any good idea make sure to write it down in the reviews / on the github page. (or on discord at @ bali411)

  • 11 devlogs
  • 85h
  • 19.62x multiplier
  • 1664 Stardust
Try project → See source code →
Open comments for this post

5h 30m 29s logged

Added an OLED theme, picked it on my phone, and most of the screen was still grey. Rather than argue with my own eyes I took a screenshot and read the actual pixel values out of it, which took about a minute and saved me a lot of guessing. The background was 121212 when I had asked for pure black.

Turned out the theme was never reaching the screen. The app builds its colour scheme without setting a surface colour, so every page fell back to Material’s own default grey and quietly ignored the palette it had been handed. One line, and the background went to 000000 and the cards to 080808.

The bit I liked was the leftovers. A few tiles stayed light and I was about to go and edit the seventy something places that paint card backgrounds by hand, until I noticed the colour was 3B4548. That is not grey. Grey has equal channels. It was the teal accent bleeding in, because Material tints raised surfaces with your primary colour, so that was one setting too rather than an afternoon of find and replace.

Also checked the contrast while I was in there and every accent clears AA on black between 9.37 and 18.80 to one, and found that choosing OLED did not survive the phone flipping to light mode and back, because the theme listener was overwriting whatever you picked. Not finished yet, the tile fix still needs a look on a real device.

0
0
22
Open comments for this post

9h 33m 55s logged

The old setup opened with a page of legal text and then asked whether you wanted to pick your school from a list or type in a Neptun URL, which is a strange thing to ask someone who has not been told what the app does yet. One of the reviews said it was a bit hard to set up and that was the honest version of it. It is four steps now with a bar across the top so you can see where the end is. Terms first, then a screen that actually says what NHNK is, then your school, then the login. The school search used to live behind that how do you want to log in question and now it is just the third step, so there is one less screen to get through. While testing it I typed obuda and got nothing back, because the school is called Óbudai Egyetem and I had not thought about accents. Nobody types those into a search box. It folds them now and there are tests so it stays that way. The screen I deleted was also quietly running the update check and the language prompt, which I only noticed on the last read through before removing it. Testing any of this used to mean wiping my own login, so debug builds install separately now.

0
0
40
Open comments for this post

5h 55m 45s logged

The next release replaces the login flow, so before touching anything I sat down and read the one I already have. It is four screens before anyone sees a timetable, all of them living in a single file of 2148 lines with five page classes in it. Disclosure, then pick how you want to log in, then find your university, then your Neptun code and password. The university list is 54 entries that I typed in by hand, and the app openly admits in its own description that your school might not be there. That is the part I keep coming back to, because someone whose university is missing has to understand what a Neptun URL is before they can get in. I also found out I had a note wrong. I had written down that the app forces Hungarian and needs country detection added. It does not. It already reads the phone language and falls back to English for anything it does not have. Good thing I checked before building a feature that exists. The genuinely awkward bit is the disclaimer. Translations get downloaded from GitHub while the app runs, which is lovely for adding languages and completely wrong for legal text, since nothing downloaded should be able to rewrite what the disclaimer says. So it is compiled in, and the one screen that most needs to be understood is the one screen stuck in two languages. I do not have a clean answer for that yet. Smaller things I wrote down. A class called SetupPageInstitudeSelection, spelled that way in eleven places. One university with its URL fixed up by hand inside the login handler. The Linux desktop build does not compile at all, which I only discovered because I tried to run the first-run flow there rather than wipe my own account off my phone. Four screens is not really the problem. Not knowing which of them people give up on is.

0
0
28
Open comments for this post

6h 37m 42s logged

Someone sent me a screenshot of the home screen widget saying they had no classes, last updated 00:00. They had a class at nine. The 00:00 was not a rounding problem or a timezone problem, it was every day, for everyone, since the widget shipped. When the app caches your week it stores the date as midnight on purpose, because the check for whether the cache is a day old compares whole days. The widget read that same value and printed the clock part of it, so it was showing you the definition of midnight rather than when anything happened. Reading the fetch path to fix it turned up a worse one. If a refresh fails the app puts the old rows back so you still have something to look at, which is right, but then it saves them again with today’s date on them. A week of failed refreshes and the cache still swears it is current. Restored rows keep their original timestamp now. The widget cannot fetch anything by itself, so past a day it says to open the app instead of telling you your day is empty. I nearly shipped that badly too, because the new timestamp only gets written on a real fetch, so every existing user would have been told to refresh the moment they updated. It falls back to the old date for them until their next fetch. An empty widget is fine. An empty widget that looks confident is not.

0
0
26
Open comments for this post

2h 17m 30s logged

The statistics page asks you to type how many credits your degree is, which has always annoyed me because Neptun obviously knows. So I went looking, and it will not tell me. There is an endpoint for it sitting in my own code that I have never called, except it only exists on the old API and the modern one has no equivalent among the ten endpoints I use. The one call that touches your programme at all already runs on every startup, and it returns three fields: whether the training is current, its id, and its name. Not a credit count anywhere. I even tried working it out from the programme name and that failed too, because it is a plain string with no BSc or MSc in it. So the box where you type 180 stays, and the release that was going to remove it does not exist. An evening to find that out is a lot cheaper than half a feature.

0
0
17
Open comments for this post

10h 3m 32s logged

Tap the version seven times and the app now has a developer tab: startup timings, every request it makes, how stale each cache is, and how many frames it dropped. The interesting part was not building it, it was working out what it is allowed to say, because this thing is holding somebody’s Neptun password. So the list of stored keys is an allowlist rather than a blocklist, meaning a key I add next year is hidden until I say otherwise, and the cached grades and messages only ever show their size. The redaction lives in the recorder instead of the screen, which matters more than it sounds, because one of my own URLs turned out to be a credential and a log that printed URLs would have published it.

0
0
21
Open comments for this post

10h 5m 36s logged

Next release I want a developer tab that shows what the app is storing and every request it makes, which sounds harmless right up until you remember my app is holding somebody’s Neptun password. So before writing any of it I went through all 77 things it keeps on disk, and only 46 can have their value put on screen. The trap was sorting by key name, because CachedMarkbook_3 sounds like nothing and is actually somebody’s grades, so that whole group gets to show its size and age and nothing else. The one that nearly got me is the calendar export link, which keeps working with no session at all, meaning the URL is the credential, and a network log that prints URLs would have published it in plain text.

0
0
19
Open comments for this post

5h 19m 23s logged

Someone reviewing the last ship said my contrast was bad, the corners did not match and things needed more padding, so instead of squinting at it I went and measured all three. Every accent colour in the light theme failed WCAG AA, and the worst was the one I use for links and section headers, sitting at 1.43 to 1, which is pale blue text on pale grey. Then I counted the corner radii and found fifteen different values across 88 places, which is exactly the kind of thing you stop seeing in your own app. The padding one turned out to be a single number, the search box above the message list had zero bottom padding, and while I was looking at spacing I finally noticed the week switcher on the timetable has always been two separate boxes of different widths with mismatched corners, pretending to be one panel.

0
0
27
Open comments for this post

10h 0m 32s logged

I added screen reader labels to the payments, messages and periods cards, then
dumped the accessibility tree off the phone to actually hear what came out.
Good thing, because the order was backwards: every card read its details first
and the name of the thing last, so a subject announced itself as “6 credits, not
completed, Introduction to Programming”. That was true of the markbook labels I
shipped months ago as well, which nobody had ever mentioned to me. Also finished
the calendar export fix from last time, so exporting your timetable no longer
tells you the file type is unsupported.

0
0
32
Open comments for this post

9h 52m 8s logged

Spent this one adding screen reader labels to the cards that never had any, and then dumping the accessibility tree off the phone to check what actually comes out. Two things fell out of that: the order was backwards everywhere, so cards read their details before the name of the thing, and that was true of labels I shipped months ago too. The honest scoreboard is three of seven card types verified, payments is impossible for me to check because my account has no bills on it, and I have not run an actual screen reader yet, only read the tree, which tells me nothing about focus order or whether you can even swipe to them. So this one is going into closed testing before it gets a tag.

0
0
44
Open comments for this post

9h 43m 14s logged

Someone told me the timetable export says “unsupported file type” on their phone, and then mine did it too, which at least made it easy to chase. Turns out the plugin I use picks the file type from a big list of extensions and there is no entry for ics, so it falls through to / and Android hands that to whatever generic file previewer the manufacturer ships, which obviously cannot read a calendar. The annoying part is the phone had the right app the whole time, because Honor’s calendar registers itself for text/calendar and I just never asked for it. One extra argument and the export now offers the calendar first, which ships in the next update.

0
0
187
Ship

The previous ship was about fixing up the basics and making everyting work as intended. If you are interested in the main points of this project etc make sure to read the previous ship description too.

In this ship I focused on optimizing the app, making the load time shorter, fixing some bugs like haptics etc, also logged the readme and website rework too. This ship is mainly for ending this stage before I start working on new features and more fixes. I optimized almost everything I can, made loading parallel where it was possible etc.

Next ship will be packed with new features, I plan to add a dev mode to help find and debug edge cases or loading issues / resource leaks, also planning to add an “OOBE” to make the onboarding more seamless and modern.

(No new images for this ship, since most of the changes are code related and not cosmetical. )

  • 6 devlogs
  • 59h
  • 18.22x multiplier
  • 934 Stardust
Try project → See source code →
Open comments for this post

2h 19m 57s logged

Last time I said the real problem was that startup waits for the keystore at all, and that fixing it meant making six getters async across a pile of call sites. Did it, and it went from 239ms to 144ms because the timetable comes out of the cache and never needed a password in the first place. What I did not expect was that the compiler only caught 22 of the 27 places that broke, because the last five build the old API request body by gluing strings together, and a string is happy to take a Future and turn it into “Instance of ‘Future<String?>’”. So those five would have quietly posted that to the university as my password instead of failing, which I only found because I went looking after the analyser had already gone green.

0
0
38
Open comments for this post

9h 47m 20s logged

Last release I added timers to the app. This one I finally pointed them at startup, and the answer was annoying: 153ms of a 251ms cold start was one function, and 128ms of that was six separate trips to the Android keystore to read six saved values one at a time. None of which the timetable needs, because it draws from cache. I have a fix half done that reads them once and starts before anything asks, but while measuring I noticed the bigger problem is that startup waits for those values at all, and unpicking that means changing six getters used in 46 places, so that is the next few evenings.

0
0
88
Open comments for this post

9h 8m 37s logged

I have been shipping features for weeks without ever measuring any of them. So I plugged the phone in and actually timed things, and the Upcoming screen I added last week turned out to fire eight calendar requests one after another. One slow week took 3.2 seconds and the other seven just sat in the queue behind it. Sending them four at a time took the whole thing from 3987ms to 297ms. Then while I was reading the log I noticed every haptic in the app has been throwing an exception instead of vibrating, because I gave Android an amplitude list of the wrong length in all seven places, so vibration has never once worked.

0
0
176
Open comments for this post

10h 55m 14s logged

My app ships a 22.9 MB emoji font to draw 28 emoji. It’s bundled on purpose, so the icons look identical on every phone instead of Samsung doing its own thing. Fine goal. But it’s the whole font, 1485 codepoints, sitting in the base module so every single user downloads all of it. Subset it to the ones I actually use and it’s 1.93 MB.Most of what was left after subsetting turned out to be the SVG table. Google ships every glyph twice, once as COLR and once as SVG, and Skia only draws the COLR one, so 6.1 MB of it was duplicate artwork nothing would ever read.Also learned to measure the thing that matters. Uncompressed it’s a 21 MB saving, which sounds great, but the font compresses inside the APK so the real number is 7.30 MB to 1.01 MB, and the APK went 67.6 to 61.0. Still good, just not the number I wanted to write. Separately, my last release failed twice for reasons that had nothing to do with me: CI tracked Flutter stable, stable moved to 3.47, and it raised the minimum Gradle and then the minimum AGP. Pinned CI to the version I actually build with.

0
0
63
Open comments for this post

10h 36m logged

Last ship’s feedback was blunt: five people said they couldn’t tell what the project was because they don’t read Hungarian. The UI switched to English fine, but the demo data never did, so you’d open it and still get “Analízis I. (előadás)” and “bejelentkezési időszak”. So every bit of fake data the demo makes up now follows the app language. Timetable, exams, deadlines, grades, four semesters of history, payments, periods, mail. Two things had to stay Hungarian underneath though: the period icons are picked by string-matching the period name (Neptun only ever replies in Hungarian), and the “is this payment cancelled” check keys off the word “törölt”. Translating those naively would’ve silently killed every period icon and made cancelled payments look paid. Kept the raw values for the logic, translate only what’s on screen.Found a bug doing it too. Switched to Hungarian and half the demo stayed English, because markbook/payments/periods cache their data for 24h and nobody had exempted demo mode. The calendar already did. Now they all do.Also killed 9 languages. German, Romanian, Russian, Turkish, Ukrainian, Arabic, Spanish, Chinese and Pirate were all downloadable and all mostly half-translated, so picking one left you in a mix of two languages, which is worse than just being in one. Magyar and English only now.

1
0
63
Open comments for this post

16h 38m 14s logged

Spent the last stretch on the parts of the project that aren’t the app itself. Rewrote the README three times. First pass just bolted an English version onto the Hungarian one and it ballooned to 351 lines. Second pass I tried to make it “proper” and somehow ended up at 3,185 words, which is longer than most of my essays and nobody was ever going to read it. Third pass I threw that out and wrote it like I’d explain the thing to a friend. Ended up at 184 lines, shorter than what I started with, in two languages instead of one.Then rewrote the whole website. The old one had a big slogan hero, “Neptun, the way it should work on your phone”, which sounds nice but doesn’t tell you what the thing actually is. New one just says it: an unofficial Neptun client for Android, with a little spec row underneath (platform, licence, tracking, price) so you get the whole picture in about two seconds. 13 files, ~5k lines. Also finally wired the site into the app repo as a submodule so they stop drifting apart. Not the most exciting work but the feedback from last ship was that people couldn’t tell what the project was if they didn’t speak Hungarian, so this was the fix.

0
0
69
Ship

This project’s (NHNK = “Nem Hivatalos Neptun Kliens”, or in english “Unofficial Neptun Client” so you can say unc) goal is to make the life of university students easier, by creating a mobile app that lets them view their schedule, grades, statistics, upcoming events, messages and a lot more in one place, without the need to login every 30 minutes. The main problem was figuring out how the official university system “Neptun” works, and finding their API routes, but after figuring that out, the app works perfectly fine.

This project makes major improvements over the official websites,

  • First of all, every university has their own URL for the management site, that the students have to find / memorize, my app only needs the name of the university, and it automatically uses the saved address(es) from our list.
  • Most universities have multiple urls/servers for “Neptun”, so if one is down, there is another. This again requires the users to figure out which server is up and usable, my app auto selects a server from the fallback list, if the default website is not available, and makes the switching seamless without even the users notice.
  • The “Neptun” website also require users to login after 30min inactivity again, while in the same time most universities enabled the function to require 2fa for all accounts. While this is good for security, it makes checking your schedule grades etc a hassle. My app only needs users to enter their details once, and it wont ask for them again (except if the auth fails because of invalid credentials.), I know it might seems a security issue, that the app wont log users out, but the app is fully read only, as its only meant to check your data.
    these are the main improvements, but there are a lot more fun and usable features in the app, like detailed statistics etc.

You can try the app out on https://nhnk.bali0531.hu via the live demo, or you can download it from github releases (and google play store too soon), but it requires a hungarian university student status to log in. (For non hungarian people who are interested in the app, a DEMO mode is also available in the app itself, but at that point you can just check it out on the website without downloading anything.

Try project → See source code →
Loading more…

Followers

Loading…