Sunday, October 1, 2023

Security By Obscurity

Came across this post on the socials the other day which described a trojan attack on unix-like systems which stole sensitive tokens like SSH and cryto private keys.



What stuck out to me about the script that harvested information, was that it only looked in hardcoded, default paths. It wouldn't be able to attack your firefox data, for example, if you used a Firefox flatpak which stored it's data in .var/app. It wouldn't be able to extract your .profile or .bashrc, if your profile contained only a single line to source ~/.config/profile in it, instead of real contents. If your ETH tokens were held in ~/.cryptowallet/eth instead of the standard location, nothing could be stolen.

It speaks to how a simple obscure change like "changing the default storage location of a program" can sometimes help you avoid very simple attacks.

Is something as stupid as changing a default storage location the panacea to supply chain attacks or a replacement for proper security sandboxing? No.

But I guess it's better than nothing.

Stay tuned!

========================
Follow pyamsoft around the Web for updates and announcements about the newest applications!
Like what I do?

Send me an email at: pyam.soft@gmail.com
Or find me online at: https://pyamsoft.blogspot.com

Follow my Facebook Page
Check out my code on GitHub
=========================

Tuesday, September 12, 2023

Selfhosting with a Jump Point

Long, nerdy, nerd post alert.

In my spare time I like to program.
But in my other spare time I like to - well - mess around with hosting random media services on a spare computer and broadcasting them out online. I know. So cool. So sexy.

My online-hobby is what's called "selfhosting" which is a fancy way of saying instead of paying 15 dollars a month for a Netflix subscription that I can't share outside of my house, I pay 10 dollars a year for a domain name and 3 dollars a month for a Virtual Private Server (VPS) that I can connect to anywhere.

Now, if you've never dabbled in VPS or cloud hosting or Amazon Web Services (AWS) or any of these hyper-nerd things, the only thing you need to know is that cloud hosting is very very expensive. We're talking every time you type a letter on your computer, Amazon is billing you for the amount of CPU time you are spending. These bills add up fast, when you are using your VPS on AWS to do things like host websites or store media like Big Buck Bunny and Linux ISOs.

To offset these ridiculous costs, if you have a spare machine laying around like I do (oh we privileged few), then you could run all of these media services on this spare machine and just have it do all the work instead of paying Amazon. This is called hosting your own server! Look at you go! You can put media and files on your server and then access it from any other computer at home. And when you go outside and want to access stuff on your phone, you can port-forward on your router and share your server with the Internet - which will get you immediately hacked and DDOSed, and your ISP will shut you down and now you don't have any Internet at home anymore. Fun. Realistically, exposing your machine directly to the world via a port-forward on your router just isn't going to work. It's horribly insecure and one DDOS attack can take down your entire home network. We need something slightly better.

--

Our solution has to meet three minimum constraints.

1, it needs to be accessible to you outside of your physical home (or your virtual home network). What's the point of having all these movies on a computer if I can't actually watch them on anything except the actual computer when I'm sitting next to it at home. I want to be able to login, just like someone would login to Netflix, and watch the media I've curated (so many copies and variations of Big Buck Bunny).

2, exposing the server to the Internet should not immediately make us susceptible to being hacked. Basically, the server running should not expose our home network directly as an attack vector.

3, low cost.

First off, let's actually tackle point number 3, the low cost. To reduce cost, instead of using a platform like Google Cloud or Amazon AWS, I strictly run all of my services and hosting on my spare server machine. I own all the data, I buy hard drives for storage, I directly manage the machine. This does require a little (a lot) of Linux knowledge. While this reduces the cost in raw dollars, it does come at a cost of my time both learning and maintaining stuff. Now since this is a hobby I don't mind, the goal is to reduce dollar cost. But be aware that self hosting can cost significant time and lose you significant points on the "Partner-Approval-Scale". Now, on to my self-hosting odyssey. 


-- Part 1: The Tunnel

I started my self-host journey spending 0 dollars and giving out 0 credit card information, by using Freenom for a domain name and Cloudflare Tunnels for access. Freenom was, at the time, a legit enough website that would give me a free domain name like "hellothere.tk". I was able to switch the domain DNS to Cloudflare for their additional security and global network via Cloudflare Free Tier. Then I used Cloudflare Tunnels so that all of the programs running on my local machine could be shared out over this domain name. It was a fully working, globally accessible setup that cost me 0 dollars to maintain. Cloudflare Tunnels made sure that I was not directly exposing my home network onto the Internet, which kept me relatively secure. The setup met all the requirements  (1, 2, and 3), and was fast enough to stream Big Buck Bunny to a laptop across the ocean with relatively little lag. Great!

The problem with the setup though, was that "technically" Cloudflare Tunnels only let you host normal websites. They don't let you do things like send video streams or host game servers. Just normal, look at my blog, boring old static websites. I say "techincally" because the only thing actually stopping you from streaming media over the tunnel was that it went against the Cloudflare Terms of Service. Also the Tunnel connection did have a maximum bandwidth cap of ~100MB, which while fast enough for streaming content, was significantly slower than my Internet 1Gig plan (thanks Google Fiber). I needed a different solution that wouldn't break any ToS agreements and would allow me to use as much of my network connection as possible.

-- Part 2: The Mesh

I had a working setup, but I needed to find something that was a bit more "above board" to feel comfortable. Constraint (3) was handled by my home server. I needed a replacement for (1) and (2) that didn't involve the Cloudflare Tunnel. I temporarily settled on a mesh network VPN setup. Now, TailScale or NetMaker or NetBird or ZeroTier could do this for you, easily, free of charge, effectively "super secure" with the only requirement being that all of your devices have the respective app installed. These apps create a network between your devices, so no matter where you are in the world, your phone can talk to your server running at home via the mesh network. Ezpz, job done. Worldwide access, free of charge, and effectively complete security. 1, 2, 3. End of post, see you next week.

For many people, a private mesh VPN network actually is the end game solution. It covers all three of our requirements, and is free. But what if you want to share this cool website you made with your friend, but you don't want them on your TailScale network, or you just don't want them to have to install an app to access your stuff? Well then there are tradeoffs. Again, we revisit "The Cloud" and the VPS. Which brings us to my current setup and solution.

-- Part 3: The Jump

Now wait, I thought we were self-hosting our stuff? I thought we have this awesome machine sitting in the living room just chugging away at Big Buck Bunny, why would we want to spend money to purchase a VPS? Well, remember our 3 requirements? Port-forwarding your home network accomplishes 1 and 3, but horribly fails the security requirement in 2. Putting all your stuff on a VPS directly accomplishes 1 and 2 but horribly fails the cost-effective requirement of 3. The "nice" middle ground is hosting all your content on your local machine, which is cheap (3), hosting your domain on a VPS which is globally accessible (1) and then bridging the VPS to your self-hosted server via TailScale or NetMaker or NetBird or ZeroTier, which avoids you directly exposing your home network to the Internet, so it's comparably safer (2). This is, I think, called a "jump host" setup.

The general idea of how this works is that you own a domain name, and you point the domain name at your VPS. Your friend connects to your public VPS using your domain name. Your VPS runs only a single program, a reverse proxy (I like Caddy) that forwards your friend from the VPS to your server at home via a mesh network VPN connection that is only accessible to the VPS and your server. This way, your home network is not publicly accessible, except through the controlled routes that you set up on your VPS. Connections from the public Internet "jump" over to your server and then "jump" back out.

Your VPS can be a shit tier machine that costs pennies per month, because all it's doing is running a single program to jump connections back and forth. All you need is a domain name from a registry like Cloudflare's (which is a yearly payment of like 10 bucks unless you want a very popular name). You can even cut costs even more using things like Oracle's VPS free tier for a 0 cost VPS.

---

I could have stayed fully-free, but I decided I would spend a little bit of money to ensure the providers I ended up using would be reliable and provide me with SLAs and uptime guarantees and all that fun stuff.

My current setup is a Cloudflare domain name - which costed me 80 bucks for 10 years, and an Amazon Lightsail instance which costs me $3.50 a month that I use as a jump host. All of my configuration, except for keys, are on Github in my dotfiles, public for anyone to view and copy. I can take the entire setup, move to any VPS provider I want, and set up a new jump host in minutes and be up and running. $3.50 a month. Lots of fun.

I'm still working on securing the VPS itself though. Since it's open to the world I get a lot of bots looking for exploits. I've had fun learning about honeypots and using CrowdSec and Fail2Ban to figure out how to keep my cloud secure. Hopefully when we chat again it will still be secure. We'll see.

Stay tuned!

========================
Follow pyamsoft around the Web for updates and announcements about the newest applications!
Like what I do?

Send me an email at: pyam.soft@gmail.com
Or find me online at: https://pyamsoft.blogspot.com

Follow my Facebook Page
Check out my code on GitHub
=========================

Monday, August 28, 2023

Rootless Podman Privileged Port Binding

podman is a cool docker-replacement that is right at home on Linux. Among other cool party tricks, it is able to run containers (after some configuration) as a normal non-root user. This is good because a container breakout will not grant a root shell, but rather just a normal user shell (which you can further isolate to a "container-only" user). Great!

Just one problem - ye old "privileged ports", otherwise known as any port number below 1024 on Linux because trust and reasons. Thus, if you wanted to run things like nginx or AdGuardHome which bind to ports like 80 and 53, you have to run your container as root.

This is both not very nice for security, but also, limits you to things like network namespaces (as the root user podman will only know about the root user network namespaces) which is lame. Except for the one part where podman binds to privileged ports, a container like nginx or AdGuardHome can run entirely fine as non-root on the host machine.

You can do a fun little trick by running sudo sysctl net.ipv4.unprivileged_port_start and setting it to 0 which will remove protection from these ports < 1024, but this is also not great since that security does exist for a reason. But you can get the best of both worlds by running the dropping the port range, starting the container and binding the ports, and then resetting the port range back to the original 1024 in the background. I've just updated my container scripts to launch AdGuardHome and Cloudflared as a non-root user using this nice little trick.

It's brittle, since if the startup takes more than ~3 seconds, the port will protect again. I also haven't tested various scenarios in the no-worky path, but the happy path of container launches and services work is performing fine with this fun little shell workaround. Nerdy stuff.

Stay tuned!

========================
Follow pyamsoft around the Web for updates and announcements about the newest applications!
Like what I do?

Send me an email at: pyam.soft@gmail.com
Or find me online at: https://pyamsoft.blogspot.com

Follow my Facebook Page
Check out my code on GitHub
=========================

Friday, August 4, 2023

Tears of the Kingdom is a Good Game, but an OK Zelda

Tears of the Kingdom (TOTK) came out a few months back. I had played it semi-religiously for some 50 or 60 hours before beating the final boss for the first time and honestly, I haven't had the drive to pick it up since. TOTK is a great game, and I enjoyed almost every minute of my playthrough, but its not a great Zelda game - it's just a painful OK Zelda game.

First, let me just say that I played the snot out of Breath of the Wild (BOTW). I clocked 200 hours on the Wii U version when it first came out, beat it, bought a Nintendo Switch, clocked another 100 hours in the game again and beat it again. Then restarted in Master mode and clocked another 100 hours and beat it again. I have played and beaten the game across multiple hundreds of hours across multiple consoles and game modes. I loved BOTW.

I believe I can condense my problem with TOTK into 3 main issues. 1 - It's not Breath of the Wild. 2 - It is Breath of the Wild though if you look at it with your head tilted to the side. 3 - Its confused on where it fits with Breath of the Wild.

1. Tears of the Kingdom took six years to make. It was a labor of love crafted by hundreds of workers over thousands of hours of development - and for what? The new additions to map - the sky and the depths, are sadly empty feeling with little to do. Aside from the Great Sky Island that you start the game on, there is very little reason to explore the sky or depth areas except for the parts of the game which force you into sky dungeons or depth dungeons. Other than these main story requirements, your exploration rewards you with - Zonai part dispensers, Zonai construct mini-boss fights, or copy-pasted take-off platforms that come with 2 or 3 gliders, and maybe if you are lucky some rockets or flame emitters or fairies. There's nothing to do in the sky, and very little opportunity to explore given how sparse the sky platforms are. The sky's most valuable asset is that it is a high jumping-off point so that you can glide over to a point of interest on the ground.

The depths are more interesting than the sky, but your exploration is limited because of the Gloom, which stops you from progressing after you take enough hits that you have no more food to "repair" your hearts and are forced to leave the area. Yes if you discover a lightroot you can restore your lost hearts, but there is no reprieve if you want to re-explore an area that you have lit-up already but have not combed over yet. You are subtly punished for returning to explore an area again as you have no heal that can save you from the Gloom unless you pack a bunch of sundelions.


2. Even though I paid 70 dollars for it, the more I played it, the more I thought that it was just a "big DLC." For all the things they managed to add into the game as mechanics, they didn't actually take any development time to solve the problems the game had. Underneath it all it was still Breath of the Wild. Suffered the same problems that BOTW had, like weapon durability and menu mania, and buffets during combat and all that.

Puzzles in shrines were a big problem of mine in BOTW. Due to it's non-linear nature, you were never able to have puzzles that were introduced in shrine A and then expanded on in shrine B. The closest we got were things like the dueling peaks which had two "paired" shrines. But any puzzle or setup had to be one-and-done in the shrine itself, since there was no practical way to ensure that a player would have discovered shrine A before getting to shrine B - so you couldn't assume the player had been introduced to any game mechanics at any point ever. Every shrine could potentially be the player's first shrine, so puzzles had to be introduced and finished with that idea in mind. This led to both ideas that repeated with effectively the same puzzle in multiple shrines (like the rolling ball with gyro puzzles), and deep ideas that could never be used again or inspected further (like EventTide island being a one-off idea). The non-linear nature honestly hindered the idea of puzzle-boxes in the game, which are by their very nature a linear activity.

The overworld map was also largely the same, so there was no real "reward" for exploring. In BOTW, everything was new, and so exploring an area, even without a treasure chest at the end, was still exciting because the discovery was in itself a rewarding experience. In TOTK, every area is either effectively untouched, been defaced by a magical crop-circle, or the same with with one quirky "change" to it. And the "change" always seemed to hinder progress. Found a new skyview tower, well you can't use it yet because the platform is broken! Went back to the Kakariko fairy, they aren't there anymore but if you find them in their new location you can't just pay them and upgrade you have to go find a trumpet. Or a flute. Or pull a wagon with your horse. Or a number of various other obstacles that just exist to stop you from progressing the way you used to. So many undocumented side-quests stop you in the middle of your current side-quest and tell you to go somewhere else. You can't just visit the goddess statue on the plateau anymore. You can't just go find the heroine statues in the desert. You can't just visit your house in Hateno, you can't just explore Kakariko. Every quest that re-visits a familiar area (where the reward should be the nostalgia hit of seeing the changed and unchanged bits) is stopped by some monotonous side quest. Link is never allowed to just exist and walk around. He always has to be doing something.


3. But seeing as every NPC in the game suffers from selective amnesia, I never know if it's even worth it to do things in the game in the first place. Teba knows me well, and I apparently know his tiny bird son too. Sidon and I are best friends apparently. But nobody seems to know that the Calamity ever existed. Bolson looks at me like I am a stranger. The champions are effectively written out of history except for the statues and rock walls that bear their faces. All the Sheikah tech is just gone, the Sheikah slate never existed, Guardians are nowhere to be seen. People talk to me like it's the first game in Hyrule. What's the point of fixing towers and killing pirates and building schools and rebuilding towns if in the next game everyone will forget all about my actions anyway. When almost no-one in-game acknowledges any of my accomplishments, what's my character motivation for even continuing?

3 Bonus. The game really suffered this time around from the non-linear story telling. In BOTW, Link had amnesia, so it made sense that he would remember bits and pieces out of order. In TOTK, Link is fine, minus 1 hand. So when I discover magical crop circle 11 before magical crop circle 3, it kind of messes up my flow. I learned that Zelda became a dragon before I learned she ever went back in time in the first place. Why was I even allowed to learn the story out of order. It doesn't make any in-game sense this time around. You're saying that within 15 minutes of landing on the ground again, Link learned Zelda was a dragon and told nobody? Just kept up with the charade of looking for her even though she obviously was a dragon? Really?

Also Ultrahand. Was not a fan of "just build a car bro." I'm long past the point where I can be creative enough to care about neat ways to solve problems. I build my 2 fan bike and just go, that's it. The spirit temple took me 3 days because I just could not be bothered anymore to make another random Ultrahand-whatever out of a box and rocket it over a gap for the third or fourth time. That's not a puzzle. That's just strapping a rocket to a shield, or a box, or combining 40 logs together into a long bridge. It's just tedious.


--

Ok that was a bit. Overall, I had a lot of fun with the game but ultimately left disappointed. It was a good game. It pushed the envelope on many game-mechanics, Fuse and Ascend are literal game changers and I can't wait to see how the industry further develops the idea of them. But as a Zelda game it was just "ok" in my eyes. Sad to think after so many years of waiting.


Nerdy code stuff soon maybe
Stay tuned!

========================
Follow pyamsoft around the Web for updates and announcements about the newest applications!
Like what I do?

Send me an email at: pyam.soft@gmail.com
Or find me online at: https://pyamsoft.blogspot.com

Follow my Facebook Page
Check out my code on GitHub
=========================

Saturday, July 15, 2023

Revival of Trickle

Trickle has been revived! Brought back from the dead! Made fully compatible with Android 14!

Trickle is the pyamsoft Android application which puts your device automatically into Low-Power mode when the screen turns off, and turns off Low-Power mode when the screen turns back on. It is a very efficient power manager application, and much better than the other snake-oil applications on the store as it does not do any custom power logic itself - rather it simply calls to the Android OS power mode.

Recent changes have made it compatible with Android 14 and the new store policy around Foreground Service Restrictions. The has been re-published to the store as the new version 22 with support for Android 14! The publication will go live in a few days and then you will be able to download the new version.

Trickle will continue to be supported for the foreseeable future, until Play Store policy kills us again!


Stay tuned!

========================
Follow pyamsoft around the Web for updates and announcements about the newest applications!
Like what I do?

Send me an email at: pyam.soft@gmail.com
Or find me online at: https://pyamsoft.blogspot.com

Follow my Facebook Page
Check out my code on GitHub
=========================

Wednesday, July 12, 2023

Death of the Highlander

The Highlander Android library is deprecated and no longer maintained.

Highlander is a simple pyamsoft Android library which helped ensure that only a single instance of a coroutine was running at any given time.

It was one of the earliest pyamsoft libraries that dealt with Coroutines, and it was over-engineered due to a misunderstanding of what coroutines were. A coroutine can mimic Highlander behavior, by simply assigning the launched Job to a variable and canceling the variable before attempting a new Job launch.

Simple, stupid, and no maintaining required.

Worried about race conditions? You are either in a suspending function already and you can use a Mutex to lock and ensure assignments, or you are not in a suspending function and thus can either safely cancel-and-assign a new job (like on the Main Thread), or you can launch a simple coroutine that then allows you to access the Mutex and launch your real coroutine after you have ensured it was cancelled. Easy peasy! Go forth, and adhere to best practices!

Stay tuned!

========================
Follow pyamsoft around the Web for updates and announcements about the newest applications!
Like what I do?

Send me an email at: pyam.soft@gmail.com
Or find me online at: https://pyamsoft.blogspot.com

Follow my Facebook Page
Check out my code on GitHub
=========================

Always Type Your Sudo Password

Here's a fun one.

You're working on your machine, life is good, everything normal.

You type some command that uses sudo and it asks for your password. You decide you actually don't want to run the command, and you cancel it with Ctrl+C. Maybe you do this a few more times.

Suddenly, you decide you actually do want to run the command, so you type it out again, with sudo and you enter your password and...it rejects you.

You know your password is correct, you're absolutely sure. You even change your password to 12 to make sure you can't mis-type it.

Nope still blocked.

Congratulations, you messed up!

Well sort of. On Linux, sudo works with pam to manage your user-switching attempts. pam is configured by default via /etc/security/faillock.conf which will lock an account if 3 bad passwords are entered in the span of 15 minutes. It seems like a recent change means that even "not entering" a password counts as entering a "bad password" now, so you will be locked and the error message is the same as when the password is wrong so you are none the wiser.

What you need to do, assuming you have normal root access, is switch to root and run faillock --user ${USER} --reset which will clear out the locked sessions for your user if they exist. Then you'll need to re-login to your desktop session. Loads of fun with this one the past hour - thanks sudo!


Stay tuned!

========================
Follow pyamsoft around the Web for updates and announcements about the newest applications!
Like what I do?

Send me an email at: pyam.soft@gmail.com
Or find me online at: https://pyamsoft.blogspot.com

Follow my Facebook Page
Check out my code on GitHub
=========================

Thursday, July 6, 2023

Feedback

Do you use haptic feedback on your phone? I didn't until recently when I turned it on one day on a whim, it's pretty cool. Since I'm so interested now in the pleasing little buzz of haptics when you swipe and scroll and press on important buttons, I'm adding haptics to all pyamsoft apps, and the PYDroid library. Yay buzz buzz!

Work continues on TetherFi specifically around network proxy performance. I continue to look at the ability to proxy UDP traffic - but it does not really seem to be as easy as I was hoping for a non-rooted Android app to be able to do, so this may need to be put on ice for a while as I figure it out more.

I have been daily driving both my stock app "TickerTape" and my finance management app "Sleep for Breakfast" for a while now - but just haven't made the final polish touches to release them as products, which is unfortunate. Sleep for Breakfast is pretty neat - it watches your phone notifications for things like Google Wallet payment notifications and Venmo transactions, and automatically adds Spending or Earning transactions, so you don't need to open the app up every time youre doing stuff to annoyingly fiddle with a virtual keyboard. You don't even need to take a picture of a stupid receipt, because as long as you received a notification about it, its already recorded! Super useful - very not complete though.

Final bits on the coding side, PYDroid has almost completely removed everything related to the old View system, meaning we are almost ready to go 100% Compose and never interact again with Fragments, Views, or XML (except in string/color resources I guess). Some day soon! I also moved the build toolchain to the new KSP instead of kapt (except in the case of Dagger, which needs kapt still), and hope to eventually move all to KSP once Dagger is ready.

Other miscellaneous updates on the non-coding side, my Steam game launching script gametime gained support for Zink OpenGL rendering, inside of a flatpak, inside of a gamescope embedded session. It's proven very useful when playing Team Fortress 2 and other old OpenGL games that are - lets face it - never getting Vulkan support.

I haven't wrote much lately because all of my computer time has either been spent being employed, or coding instead of writing. Sorry about that. But for the foreseeable future, I see myself coding more than writing once again.

Stay tuned!

========================
Follow pyamsoft around the Web for updates and announcements about the newest applications!
Like what I do?

Send me an email at: pyam.soft@gmail.com
Or find me online at: https://pyamsoft.blogspot.com

Follow my Facebook Page
Check out my code on GitHub
=========================

Saturday, June 3, 2023

Been a little while

Hey there,

Been away for a bit but that doesn't mean that nothing has been happening. Nerd stuff first.

TetherFi

Multiple app updates went out, the latest and most stable being version 24. Version 23 has a critical performance bug which will slow down your phone after enough usage, and I recommend you upgrade immediately off of it. A bunch of performance and battery changes have been made so the app should be faster and more efficient!

PYDroid

The library has seen some changes, mainly on the usage of the EventBus class, which is now just an alias for a Flow. We still keep this class because the event bus underneath is a MutableSharedFlow, which for some reason is harder to bind into Dagger than using our own EventBus and EventConsumer. Plus, the interface names being different from "just a Flow" help mentally convey that this flow may travel outside of the class you directly find it in.

Lots of other small changes and fun projects continued and some new ones started. Mostly revolving around money and investments and spending - you know how it is in this economy. Hopefully later on I will have more concrete thoughts to write about, but for now, just know that I'm still here, and still working.

When I'm not playing Zelda, that is.

Stay tuned!



========================
Follow pyamsoft around the Web for updates and announcements about the newest applications!
Like what I do?

Send me an email at: pyam.soft@gmail.com
Or find me online at: https://pyamsoft.blogspot.com

Follow my Facebook Page
Check out my code on GitHub
=========================

Thursday, February 9, 2023

Android 14 and the death of fun things

She's dead, Jim.

Android 14's first developer preview released and with it comes continued changes to make applications that solely run as background utilities useless.

Namely, these changes affect at least Home Button and Trickle. TetherFi fits slightly easier into the use-case mold that Google seems to want Android to evolve towards - as it's usecase is met under the CONNECTED_DEVICES type of foreground long-running service, as it managed connected devices under a WiFi Direct group. Home Button and Trickle both do not fall under any use case specifically, and thus they would be put under the "special use" type which is subject to both approval and deprecation in the future.

Let me be very clear, no third-party app will ever receive the "special use" case type approval from Google unless they are large, multi-million dollar companies. As such, since both Trickle and Home Button require a foreground service to operate even in a somewhat expected way, both of these applications cannot continue on the Play Store in the future.

Trickle was a newer application but was genuinely useful, as it allowed the device to automatically enter the system-level low-power mode - a feature which has not yet been duplicated in the system or other third-party applications and thus there is no replacement for it.

Home Button is pyamsoft's original Android app, built first as a learning opportunity and turned into an online product. It has seen widespread use, but now has official alternatives such as modern software buttons and device-swipe controls. After more than 10 long years of Android development and support - it finally sees its last day.

TetherFi should continue to be supported, assuming that we are allowed as a valid foreground service use-case, and the upcoming stock app should also not be affected by these new restrictions.

Stay tuned!

========================
Follow pyamsoft around the Web for updates and announcements about the newest applications!
Like what I do?

Send me an email at: pyam.soft@gmail.com
Or find me online at: https://pyamsoft.blogspot.com

Follow my Facebook Page
Check out my code on GitHub
=========================

Tuesday, January 24, 2023

New Kung Fu

PYDroid 26.2.0 has been released which brings support for fully Compose layouts. No more view binding, no more XML, no more fragments - all Compose. PYDroid is now a Kotlin first, Compose first library.

And since PYDroid is the backbone of all pyamsoft applications, updates to all pyamsoft applications have been released (Home Button still reviewing) that move them to fully Compose layouts too.

TetherFi has received a plethora of fixes and new features as the community has picked up around it a bit this weekend. The network can be shared with a QR code, the password will be hidden by default, and language changes attempt to clarify features in the application. Future updates will continue to bring more features, and one day UDP proxying will work too - one can hope.

These updates have been pushed to the store and will go live for you soon.

Stay tuned!

========================
Follow pyamsoft around the Web for updates and announcements about the newest applications!
Like what I do?

Send me an email at: pyam.soft@gmail.com
Or find me online at: https://pyamsoft.blogspot.com

Follow my Facebook Page
Check out my code on GitHub
=========================

Wednesday, January 18, 2023

The important of re-reading documentation

PYDroid is being updated to 26.2.0 which brings support for a fully Compose application. PYDroid internally is dropping all Fragments and moving to a Single-Activity, multi-Composable architecture.

With this change comes a lot of learning about Compose. One particular problem that had me super confused for almost a day involves some very innocent code and some very particular Compose behaviors.

@Composable
fun Test() {
  val (show, setShow) = remember { mutableStateOf(false) }

  Scaffold {
    Box(
      modifier = Modifier.fillMaxSize(),
      contentAlignment = Alignment.Center,
    ) {
      Button(
        onClick = { setShow(true) },
      ) {
        Text(
          text = "Click Me",
        )
      }
    }
  }

  if (show) {
    Dialog(
      onDismissRequest = { setShow(false) },
    ) {
      Text(
        text = "Hello!",
      )
    }
  }
}

Looks pretty simple right? A composable with a button in the center, that when clicked, shows a Dialog. This works fine.

This doesn't:

@Composable
fun Test() {
  val (show, setShow) = remember { mutableStateOf(false) }
  val breakItAll by rememberUpdatedState { setShow(true) }

  Scaffold {
    Box(
      modifier = Modifier.fillMaxSize(),
      contentAlignment = Alignment.Center,
    ) {
      Button(
        onClick = breakItAll,
      ) {
        Text(
          text = "Click Me",
        )
      }
    }
  }

  if (show) {
    Dialog(
      onDismissRequest = { setShow(false) },
    ) {
      Text(
        text = "Hello!",
      )
    }
  }
}

See what changed? I've added a breakItAll field which remembers a callback passed to it through future renders.

If you've ever coded in React before, this kind of pattern may look very familiar to you. Instead of passing a lambda function to a callback, you memoize the callback with React.useCallback so it won't continue to be allocated each render pass.

In Compose this breaks horribly. Once you click the button and the Dialog launches, your Button will begin infinitely recomposing, and it will never ever stop, even if you close the Dialog. Like a ticking time bomb, once you set it off there is no going back.  Even worse, your app will probably still continue to work. The button still works, the Dialog still shows. But performance dies in a hole. No crashes, just extreme amounts of lag. What the heck is going on here, why does one simple change, which should optimize the code even, make it blow up so spectacularly?

Compose does not like this, because Compose also cares about the idea of Stability. There are plenty of good articles on the web about Compose and Stable such as this one. Now what is the problem with the above by rememberUpdatedState?

Compose treats lambdas as Stable. The first code works because the onClick is a lambda, so when it fires, the re-render sees that the Button component has not changed at all and skips it. It renders the Dialog and is done, life moves on. This is how things should work.

When we change this to by rememberUpdatedState, our resulting variable (despite being the same Type as the lambda) is no longer stable, since the type of the variable is actually the delegated property. Thus, when the button is clicked and the state is switched, the Button is re-rendered and (even though the onClick is not re-allocated, it is re-assigned to a new lambda) a new onClick is attached. It then goes and re-renders the Dialog as it did before. The issue now though, is the Composition snapshot has treated Button as a dirty change, and tells the engine to once again re-render the Button. The Button is rendered, a re-assigned onClick handler is given to it, and Compose once again marks it dirty. This continues to happen infinitely, causing the problem above.

The reason you may want to memoize something like a callback is for all the reasons Compose provides you with that callback function. But you need to be aware that typewise, the function is not the same as a lambda at all. You should generally only be using this rememberUpdatedState when you are attempting a callback from an Effect hook, as it was designed. This second code block is an anti-pattern and should not be used.

That was a fun day. Thankfully the problem has been fixed, and various Stable issues in PYDroid have been addressed, so performance is significantly improved. With the new library changes will come new versions of pyamsoft applications with shiny new features, so get excited for that!

Stay tuned!

========================
Follow pyamsoft around the Web for updates and announcements about the newest applications!
Like what I do?

Send me an email at: pyam.soft@gmail.com
Or find me online at: https://pyamsoft.blogspot.com

Follow my Facebook Page
Check out my code on GitHub
=========================