Sunday, December 07, 2025

Android Log View Hierarchy

While I was trying to implement (or rather suppoer) Dark/Light mode in one of my android apps, I needed to debug the view hierarchy , and the Layout Inspector in Android Studio only gives you the view id  names, like R.id.xxxx, but not the numeric value.

So I created this fine log function for the view hierarchy - notice the recursion for view groups ;)
private void logViewHierarchy(@NonNull View view, int depth)
{
String indent = " ".repeat(depth);

String resourceId;
int id = view.getId();
if (id != View.NO_ID) {
try {
resourceId = getResources().getResourceEntryName(id);
} catch (Exception e) {
// Handles case where ID might be dynamic or not found in resources
resourceId = String.valueOf(id);
}
} else {
resourceId = "NO_ID";
}

String simpleName = view.getClass().getSimpleName();
Log.d(TAG, "%s[%s] ID: %s = 0x%08x".formatted(indent, simpleName, resourceId, id));

if (view instanceof ViewGroup viewGroup)
{
for (int i = 0; i < viewGroup.getChildCount(); i++) {
View child = viewGroup.getChildAt(i);
logViewHierarchy(child, depth + 1); // Increase indent depth for children
}
}
}
When I then added this at the end of the onCreate() in my Activity, it (or course??) did not show any of the childer of the main RecyclerView I was using, so let's do it, once layouting is done.
Best with this little trick:
binding.recyclerview.getViewTreeObserver().
   addOnGlobalLayoutListener(() -> logViewHierarchy(binding.getRoot(), 0));
In essence, you register an observer for when the layout for the RecyclerView is done, and blog then (notice the lamdba there calling my log function). 

Et voilà - full recycler being logged.

Friday, December 05, 2025

My PiThon Project

Pardon the really terrible pun - but I'm working on a small side project on a Raspberry Pi in Python.

Ok, now that we got the Pi jokes done, here's what I'm doing:

I'm a building an station display (board) for nearby train and other public transprt stations with real-time data and more importantly potential delays. Idea is to have a brief glance at it before leaving the house, so you know whether to rush or leisurely stroll to the station.

For that I'm re-using and old PI (3B+ to be precise, not really the fastest one) with a Raspberry Touch Display 2 and a bit of Python code.

So easy do develop (in PyCharm) and test (on my PC), then "stage" it to a Raspberry OS within a virtualbox, and then deploy to a real bare metal RaspPi.

I get the data from the ÖBB online timetable (as json) and the Wiener Linien open real time data (german description here). I chose those 2 because that's really all I need in my neighborhood. ÖBB (the Austrian federal railways) timetable covers all train journeys within Austria, and as I'm living in Vienna, I only need the Vienna public transport timetable. *

For the GUI I started the tkinter but started to regret this almost immediately. It is a terrible API without proper control for tables for example - and yes, showing an online timetable needs a proper table - it's sort of in the name.

When I then first deployed this to a real Raspberry the performance was abysmal - and I'm talking about fetching the data from their soures, but presenting that data (once loaded) in a tkinter window/table.

Think of old terminals connected via a serial V.32bis modem... where you can watch the lines being popuplated - almost that bad. You could see the table being drawn... really.

So, terrible API, hardly control over table formatting, terrible performance on the target HW, lets look for a different GUI library.

PyQt to the rescue. I found the API and the constructs more familiar (from Swing, Android, even back OS/2 and Windows ;)) and it is definitely more performant.

So PyQt it is. Rewrite was done in less then an hour. No visible rendering/drawing artifacts.

Right now, the configuration of the stations is right still in the source code (yuck), but that's the next step then - one needs to fill their spare time, right?

More to follow.

---

* yes, my code is modular enough to plug-in any other transport provider as long as they have machine readable online timetable data via whatever over HTTP without logins ;)

Thursday, December 04, 2025

Note to self - SSH keys

Whenever I need to setup a new ssh client device (laptop / chromebook / PC) or target (like a raspberry, ...) I need to google around how to do my SSH key setup. For Putty, that's easy, for ssh on the chromebook the google restults are less useful (YMMV).

So here's my reminder / note for next time on a chromebook:

On the new Chromebook, (create the linux environment and) open the Linux terminal.

Generate a new SSH key pair by running the command ssh-keygen.

Copy the public key to the server by running the command: ssh-copy-id user@hostname.

Enter the password for the user on the remote server when prompted. 

Then go to the terminal app and select the ssh configuration for that hostname.

Click on import and (re-) import the id_rsa file (you might need to make sure you see hidden files first via the ⋮ menu in the upper right corner)



Edit for Windows: WinSCP has an option to just copy the key to the server:


Wednesday, December 03, 2025

New Chromebook

Today, I upgraded my three-year-old Acer Chromebook 514 with a new model. The original served me incredibly well, but i got annoyed with serious performance issues, like login taking ages - just to enter the PIN. That with some other symptoms to me looked like not enough memory (4GB). One other thing that I regretted immediately after buying it about 3 years ago was the lack of a keyboard light.


So I got myself a newer model with 8GB and a backlit keyboard.

Installation was smooth and easy - switch it on, scan the QR code with your Android phone and it will setup everything from there. It couldn’t be more easy.

Especially when compared to setting up a brand new Windows 11 (which I did a week before for my mother) - but that’s another story which I will probably NOT write ;)

All apps (incl Android apps) are being re-installed on the new Chromebook as well. Chrome settings are synched as usual. Really nice.

Login, opening apps - even android apps - is lightning fast as expected.

Only need to clone/re-create my ssh keys - but more on that later.

So, trying it out now with writing a blog post from the couch….

Monday, December 01, 2025

On the em-dash

I guess — I’m pretty convinced actually —  that these days more 20somethings know about the em-dash than any other generation. Not because they are actively using them, but because AI uses it for them, and then have been called out on it.

Probably calls for a paper with a fancy title like “Generational Aspects of the em-dash” or “Typography – A Generational Approach”

Btw: I've been using the em-dash for years, but mainly in presentations and when it makes typographically or optically. 

Wednesday, November 26, 2025

Read the effing Docs

Yeah, not really news, but just fell into one of those traps, where you not only forget/fail to fully read the documentation, but also fall prey to premature optimization.

It probably dates back to my 8086-Assembler-4.77MHz programming times, that I want to avoid an API call whenever possible - so if their result appears invariant, I happily store if to avoid a second call.

40 years later, when working with Android RecylcerViews, this led to a terrible bug that cost me 2 days to identify and fix.

As the docs say for onBindViewHolder (for the RecyclerView.Adpater)

Note that unlike android.widget.ListView, RecyclerView will not call this method again if the position of the item changes in the data set unless the item itself is invalidated or the new position cannot be determined. For this reason, you should only use the position parameter while acquiring the related data item inside this method and should not keep a copy of it. If you need the position of an item later on (e.g. in a click listener), use getBindingAdapterPosition which will have the updated adapter position.

In order to pass the adapter position down to an onClick lambda, I even — specifically — made it final.

No wonder, that the code didn't work correctly in some — albeit very rare — circumstances, e.g. when an item further up in the list got removed from the view / adapter.

Again, reading the above would make it clear that this was not a time for premature optimization.

I think I should avoid those optimizations at all, the compiler and jvm is quite good at it anyway — especially identifying invariant function calls. Moreover, Android phones no longer run on 4.77MHz CPUs ;)

Lesson learned.

Wednesday, October 08, 2025

Maven deploy to Tomcat

 So, I'm finally moving off of NetBeans and into IntelliJ IDEA

The two main reasons for that are:

  1. NetBeans is really getting old any hardly any relevant updates
  2. All my other IDEs (PyCharm, Android Studio) are built on it
I long ago moved my projects from ant based to maven, not I struggled to get the tomcat plugin to properly deploy to my local tomcat.
Well, the re-deploy didn't work to be honest.
Reason was, that the plugin does not add the "upgrade=true" to the upload URL, and I failed to manually tweak it.

Then I found that the plugin will honor a property called maven.tomcat.update and a quick test from a command line showed that this really works.
But then, how to get it into the IDE itself. 

Took me a while, and the best way is to add it to your POM under properties, like below:

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<jakartaee>10.0.0</jakartaee>
<maven.tomcat.update>true</maven.tomcat.update>
</properties>

And voila, all uploads from the deploy target will now end with "&update=true".