Tuesday, October 4, 2022

Empty Fridge

Never the most fun day when apps need to be unpublished, but such is the price of progress.

FridgeFriend is unpublished from the Google Play Store from this point onwards. The GitHub repository will be archived for historical purposes, but will not receive any bugfixes or new features.

FridgeFriend was my first application that really captured modern Android development. It was the first time I used Room, the first time using Kotlin, Coroutines, Flow, a complex Dagger setup, the whole kitchen sink. As a result of this, the app is always the largest one I work on and the one that takes the most time.

It is also, painfully, the one that is least useful to me in my day to day. Managing the contents of a physical fridge is a difficult problem to solve with technology. The solution must be smart, update in real-time, and not be more of a hassle than simply remembering or checking the fridge periodically. While a program may seem like a decent solution on the surface, issues arise in practice.

Because the app was not integrated with the fridge itself, whenever items in the fridge were eaten or moved or frozen or spoiled, I needed to remember to open the app and check off boxes and swipe away cards. This task (which was inconvenient at best) quickly became a chore.

Because there was no good way to really determine the expiration date of opened packages or produce, I would constantly ignore the notifications from the app and check the fridge myself.

Since there was no way for the app to know when I was in a grocery store (thanks Android Location policy restrictions), I would frequently forget to make a shopping list, or forget to consult the list I made once I was in the store. This led to me coming home with more things than I needed, which defeated the point of the app.

Essentially, when so much of the rest of my life is smart and kept in sync, having one app that adopted a piece of my day-to-day work that was not integrated properly became a huge burden. Since the app is no longer useful to me and I have no interest to maintain things I do not use, I will have to retire FridgeFriend. Moving forward, the only supported pyamsoft Android applications are:

Home Button, TetherFi, Trickle, and (the newly updated) SplatTrak. I hope to add TickerTape, a new stock portfolio application to this list soon.

So long and thanks for all the fish, FridgeFriend.

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, October 3, 2022

SplatTrak and the wonderful world of Compose

Been a little bit.

Lots of developer related updates, PYDroid is preparing for version 25 which has been actually deployed into pyamsoft applications on the Google Play Store now for the past couple of months, but it's not fully API stable yet so tagging the release will have to wait. Lots of work has gone into making the API surface as easy and small as possible, so you can just drop it in your app and get building. Make something cool.

As for app updates, all supported apps continue to receive regular enough updates, I am close to getting everything prepared for Android API 33. Specifically, I just released SplatTrak into Open Testing, which brings support for the new Splatoon 3 game in app version 13.

Since I no longer play Splatoon 2, SplatTrak will no longer support 2 and it's map rotation. All updates going forward will be Splatoon 3 specific.

The new SplatTrak version brings support for the Splatoon 3 lobbies (open, anarchy series, anarchy open), the new coop rotation and weapons, and info about current and past Splatfests!

In the future, I hope to support map and coop rotation alerts and festival notifications, but I will talk about that more at a later date.

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, July 25, 2022

Jetpack Compose Weird Bugs

Jetpack Compose is very cool and also very buggy.

One bug I just recently squashed had to do with random builds failing with no explainable error message, or a build succeeding which will randomly throw an error looking something like

"Class cast Exception, cannot case LamdaImpl to kotlin.jvm.Function0" or something to that effect.

The error would only happen at runtime and would only happen as a result of a ComposeView calling setContent (in my case from a Fragment class).

Fixes usually are some flavor of clean and rebuild, but what helped me especially was also double checking that the gradle configuration property "parallel" was off in my gradle.properties file, and that "compile in parallel" was unchecked in the Android Studio settings.

That was a fun two days.

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 4, 2022

TetherFi and modern rootless Internet sharing

Recently a new pyamsoft application called TetherFi was released.

In short, this app allows you to create Wi-Fi networks hosted by your Android device that can share your mobile data or Wi-Fi powered Internet connection with other devices, like desktops, laptops, and game consoles.

The application works by creating a local Wi-Fi Direct Group and sending Internet requests from your other devices like a game console through your Android device via an HTTP Proxy running on your phone. As a result, Internet traffic from your device looks like it's all coming from the phone, which prevents carriers from seeing your network traffic. It doesn't require root, or installing a program on your computer, or any kind of complex setup. All it takes is a relatively modern Android phone and some reading comprehension.

TetherFi is most useful in situations where your phone has Internet connection but nothing else does (say you are in the middle of nowhere with no Wi-Fi, but you do get cell signal). Maybe you have a hotspot plan that is capped at a small amount of data or throttled after a certain amount is used. While TetherFi will never be as fast as a direct Internet connection (due to extra time it takes to process proxy traffic), it should be at least faster than a speed capped connection and does not have a hotspot data cap.

The only caveats for usage at the moment and that it can only generally support "normal" Internet browsing, like websites and emails. You are unable to do things like play complex online games, due to the fact that you may end up being limited by things like CGNAT and port forwarding (though a port forwarding VPN may be able to fix this in theory).

It's free, it's open, and as always I would love it you could try it out on the Play Store and test the application! Let me know what you think and how it can be improved!


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, March 28, 2022

Fun New Things

Fun new project! Over the past couple of days I've been hard at work on a small project that I've temporarily dubbed "WideFi", which is an Android application that you can use to share your Android device's internet connection with other devices like a laptop or game console.

This came about after a weekend in an area with no Internet. I had connection to the Internet on my Android phone, but my Nintendo Switch could not get online to download updates from the eShop. I thought to myself there must be a better way, and thus after a week of frustration, WideFi was born.

WideFi is a play on the word WiFi, and the idea of opening up the "single" internet connection to a wider audience - your other devices.

WideFi is currently a work in progress but I hope it will get to a state of "generally working well" soon.

WideFi works by creating a WiFi network using your Android device via the Wi-Fi Direct technology. WideFi creates a Wi-Fi Direct legacy group with a network name and password that any device can connect to. Now normally, this Wi-Fi Direct group does not give access to the outside internet, but WideFi solves this problem by also running a very small HTTP proxy server on the Android device. A connecting client device can connect to the Wi-Fi Direct group, and set their network proxy settings to WideFi's proxy server at 192.168.49.1 and port 8228.

By doing so, any device can use the internet connected Android device to proxy it's connections. You can use this to connect to the web for general internet browsing and "normal" usage. Due to limitations with Android, it is currently not possible to use WideFi to get your game console an open NAT type to play multiplayer games, though you should be able to browse most game stores (note that the connection to the Playstation Network fails currently, so the PS store can't be accessed). This can allow you to do things like connect to and download games from the eShop when your Switch does not have access to Wi-Fi.

I hope to one day better understand what causes an open NAT type on a game console so that WideFi can be used as a full proxy to allow you to play multiplayer online games as well. But first thing's first, I will need to finish up the starting version and release it onto the store.

Until then, you can find WideFi's source code here on GitHub, though it's heavily a work in progress.

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, December 28, 2021

Dealing with Depression in a Post Video Game Era

Wow that's a lot of words, what does it all mean?

I want to invite you for a moment to glimpse into my gamer life. Over the course of the last 2 months I have bought the following video games for myself. Numbers are loose estimates and not the actual price:


Ori and the Blind Forest ($15)

Ori and the Will of the Wisps ($15)

Baba is You ($10)

Deponia (4 games) ($2 each)

Lost in Random ($30)

GrindStone ($10)

Loop Hero ($20)

Yoku's Island Express ($10)

Figment ($3)

Moving Out ($7)

Darkest Dungeon ($20)

Tetris Effect: Connected ($40)

Mario Party Superstars ($60)

Nidhogg 2 ($6)

Castle Crashers ($10)

Risk of Rain 2 ($7) 

Chicory ($20)

Night in the Woods ($5)

Oxenfree ($2)


Some quick calculator math later, that's 22 games and $298 dollars spent. Not bad given that some of those games were on major sale. Of those games purchased, I've put about 70 hours into Tetris Effect, and have greatly enjoyed both Mario Party and Nidhogg.

All those other games though, I've played only a little. Most I've played only once for maybe an hour or two at most, like Baba is You, Deponia, Grindstone, Lost in Random, Loop Hero, Figment, or Darkest Dungeon.

There are actually 2 games (I'm not counting Castle Crashes or Risk of Rain having bought them just yesterday) on this list I've bought and just never ever played - both Ori games. They're great games (or so I've heard) and yet I've never even started them.

Now every gamer has a large backlog, and not getting around to games is not an unheard of thing. But let's examine this slightly more in depth.

Of these games listed above I've actually purchased multiple copies for other people (presents and such):

 

Tetris Effect Connected (4 more times)

Castle Crashers (2 more times)

Nidhogg 2 (1 more time)

Risk of Rain 2 (2 more times)

Moving Out (2 more times)

Figment (1 more time)

Mario Party (1 more time)

 

Which brings my total count up to 35 games and around $575 dollars spent on myself and others for strictly Nintendo Switch video games in the last two months. That's a lot of games.


Why buy so many?


If you want my self reflective diagnoses, its a way to deal with depression I suppose. The constant feeling of wanting something new - yet always being ultimately unsatisfied with what I have, even when I acquire the thing that I once thought would bring excitement into my barren gamer life. Now, it's not all bad, 70 hours in a Tetris video game is not exactly a waste of money. A fun time in a game like Mario Party or Nidhogg is something I wouldn't trade away for any amount of money - but what about all the rest of those games?

Did I really need to buy Baba is You only to just play it once and never touch it again? Did I need to buy Loop Hero even though I know I'm not usually a fan of roguelike or roguelite games? Why buy Risk of Rain if I know I'm not a fan of that style of game at all? Why spend 40 dollars on Tetris - furthermore, why spend 40 dollars on Tetris so many times for so many different people?

Did I really need to spend 300 dollars to purchase a PlayStation 4, and then buy PS Plus on an annual subscription for 70 dollars and Kingdom Hearts 3 for 60 dollars, only to not really enjoy the game? Why spend so much money, and take so much time, playing video games that I'm either not going to enjoy, or only enjoy briefly?

What I believe is that the cost is outweighed by the fun I would have playing the game with other people. This I believe is the one thing that helps me step away from the steep precipice of overwhelming depression, is knowing that, even if only for a brief moment, purchasing and playing a video game can help me and other people have fun - genuine good fun. It is unfortunate that it is so expensive, but such is the price of medication I suppose.


Code stuff later.


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
=========================

Full Circle

I began my career as an Android developer by creating a power management application for old Android phones that would turn off Wifi and Data when the screen was off. This was back in the olden days, before AppCompat and support libraries - where we needed to use Eclipse to manually download JAR files and used ActionBarSherlock to support older devices because nobody actually had any modern version of Android.

Life has changed and wow - how we have grown.

I feel it is only fitting then that I come to you today, glad to announce that things have come full circle. I am now an Android developer 10 years older.

 

 

Developing a power management application for Android devices that saves battery when the screen turns off.

 

 

Built in two days as a simple pet project to do a simple task. Now hold on, before you complain about shitty power manager applications that don't actually do anything - this is not snake oil. There is no custom power managing logic happening here, since this application is very simple. It does exactly one thing.

When you turn the phone screen off, this application enables the Android system built-in power-saver mode. When you turn the screen back on, your phone is set back to normal mode. That's it.

By relying on the built in system power-saving mode, we can ensure that a bunch of things "actually work." Battery is actually saved because the system has the ability to shut off or throttle a bunch of things. Whitelists are correctly handed because this is just the same system-wide battery optimization whitelist that you already have on your phone. Nothing actually changes about your device, this application just helps to do one additional convenient thing for you.

As always, full open source with no tracking or analytics or advertisement. I hope to have it up on the store 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
=========================

Saturday, December 4, 2021

Compose, ViewModels, and Configuration

Let's talk.

First is Jetpack Compose UI. It's cool, it's new. I've migrated all supported pyamsoft Android applications to use it. It comes with some new ways of doing things. This is largely a nerd corner post.

First, the move to Jetpack Compose UI sees the deprecation of a number of PYDroid things. The entire UiView system is deprecated, along with UiViewEvents and UiControllerEvents. The UiViewModel is deprecated. The only thing that survives the migration without deprecation is the UiViewState. Let me tell you why.

First, the UiViewEvent and UiControllerEvent and UiView existed as a hacky way to create a React-like render loop, where a ViewModel would control and constantly push an entire view state. In Compose UI, this is all done for us, so all of this ceremony around binding to the normal Android view system is gone. Write Composables as you normally would, pass then props and callbacks as you normally would, and have those callbacks call ViewModel methods as you normally would. This effectively migrates PYDroid-Arch from an MVI to a better MVVM architecture.

Second is ViewModels. I am going to put forward the controversial claim: You no longer need the Android Jetpack ViewModel. PYDroid uses completely independent simple ViewModels called ViewModelers instead of Jetpack ViewModel and it's weird ViewModelStore and other initialization quirks. Why do this? Well, lets examine what exactly we need from a ViewModel that we do not otherwise already have.

ViewModel comes with 2 very important things for us - it survives configuration changes, and with SavedStateHandle can also persist data across process death. As a trade off, we receive the ViewModel class which must be injected by a ViewModelStore and a Factory, and is difficult to configure with things like scope and injection. What if we could remove all this extra baggage, and be left with a ViewModel that still survives configuration changes and process persistence? What if we could manage scoping and injection simply through Dagger as we do for every other class in our project? Well lets see if Compose UI can't help us change things around.

Let's examine configuration changes first. What if I told you that Compose UI can handle all configuration changes on it's own due to the new system? Yes, thats right. You can finally add the configChanges line to your manifest as follows:

android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"

Also, be sure to override in your Fragments or Activities

override fun onConfigurationChanged(newConfig: Configuration) {
  super.onConfigurationChanged(newConfig)  
  mySyncDarkThemeFunction()
  myDialogSizingFunction()
  myComposeView.apply {
    if (isAttachedToWindow) {
      disposeComposition()
      createComposition()
    }
  }
}


This way your components will guarantee that Compose UI is kept in sync with the rest of the system like dark theme and dialog sizes and window sizes and whatnot.

This is massively important because now 1 of the two benefits of ViewModels has been effectively replaced. Now we just have to look as data persistence across process death, which can easily be achieved using our old friend onSaveInstanceState.

As you can see, we have all of the tools provided to us now to control the configChanges in our own application. No more die-recreate Activity lifecycle callbacks when a user simply rotates the phone, or opens the keyboard. Applications can be smooth and responsive, thanks to Compose UI.

Finally, let's talk about handling view state in a ViewModel. Many people like to use Kotlin data classes with val properties to represent their entire view state, and then calling copy() with individual fields changing to handle updates. This is, in fact, how PYDroid itself used to handle state internally. I'm writing today to recommend against this process, for reasons I'll detail now.

Compose runs a re-compose loop as often as it needs to keep things consistent. Let's say you've got a view like this psuedocode:

data class MyViewStateState(val name: String) : UiViewState

var myState = MyViewState(name = "")

fun onNameChanged(name: String) {
  myState = myState.copy(name = name)
}

@Composable
fun MyComposable(
  state: MyViewState,
  onNameChanged: (String) -> Unit,
) {
  val name = state.name
  TextField(
    value = name,
    onValueChange = onNameChanged,
  )
}



Each time you type a character into the TextField, it will re-compose. This will cause onNameChanged to be called which copies the myState object, allocating more memory, and then stores the variable. It's a ton of allocation and memory, which is slow, and bad.

Let's fix this, by replacing the data class State with an interface

interface MyViewState : UiViewState {
  val name: String
}

@ActivityScope
internal class MutableMyViewState @Inject internal constructor() : MyViewState {
  override var name by mutableStateOf("")
}
var myState = MutableMyViewState(name = "")

fun onNameChanged(name: String) {
  myState.name = name
}

Look at the differences in code now.

Notice how the Composable has not changed because we have correctly separated concerns of our View from our ViewModel, yay!

Now, whenever the name changes, the string will simply be assigned to an already allocated string in the MutableMyViewState, which is private to our ViewModel and to the Composable, MyViewState looks like it has not changed at all because it is now a simple interface. By doing ViewModel state this way we can avoid constantly re-allocating new objects whenever a recomposition happens.

These changes and more will be arriving in PYDroid 24.3.0, which deprecates basically anything touching the old Android View system and makes way for a Compose future.

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, November 10, 2021

End of More Ages

Seems like only yesterday I was writing about this.

Sad to say it but it must be said - Pasterino, ZapTorch, and WordWiz have all been deprecated today and are henceforce unpublished and wholly unsupported. They will receive no further updates or bugfixes.

Long story short, Google Play Store and the Android ecosystem have evolved once again to a point where applications like these that serve as simple utility single-function applications are no longer needed nor encouraged. Rather than fill out disclosure forms regarding data I don't collect and having to comply with a continuing restrictive process, I've decided I would simply rather not invest additional work and time into hobby projects I already struggle to maintain.

They are, as of this moment, dead. Any existing versions should continue to work until they don't with any new Android versions, and if you've already installed the application you will continue to be able to use them as always. I just will not be working on them in the future any further unless the landscape once again changes.

That leaves FridgeFriend, HomeButton, and the mystery stock application alive for now - who knows what the future holds?

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, November 5, 2021

A New Age

Lets talk Android.

 

Jetpack Compose UI is the new hotness, and last time we spoke I mentioned how I would begin bringing Compose support over to all pyamsoft Android applications.

First, for users.

Almost all pyamsoft applications have been converted to Jetpack Compose. This includes Home Button, Pasterino, ZapTorch, WordWiz, and SplatTrak. FridgeFriend is larger and thus is not complete yet, but support will be coming soon. Please look forward to seeing these new versions on the store within the next couple of months as bugs and testing need to still iron out any kinks. There will be no new features and the applications should keep 100% feature parity with the current version.

-----

This is a nerd corner post.

100 commits into PYDroid later and I'm happy to report that the first-version-pass support for Jetpack Compose is live in PYDroid 24.0.0!

 Jetpack Compose is, for comparison's sake, a better version of React.js but built for native Android applications using Kotlin. It's awesome.

 All PYDroid internal screens and dialogs have replaced the old XML view system with Compose views, and bridging support for themes is handled per application. Let's go over the main user facing changes with PYDroid first.

1. The PYDroid.init function takes a couple new parameters now.

googlePlayLicenseVerificationKey: String

imageLoader: () -> Unit

theme: ComposeThemeProvider?

logger: PYDroidLogger?

First, the license verification key. This is a new required parameter which should resolve to your Google Play LVL base64 string for your application. Internally, PYDroid will check the Google License verification service to make sure that your application has not been tampered with whenever you make a release build. License verification requires that you extend from PYDroid-UI ActivityBase. To disable this feature, set ActivityBase.disableProtection = true.

Next, the required imageLoader callback. PYDroid now internally uses Coil as an image loader. To allow a user to configure the image loader, this lazy resolver accepts an ImageLoader instance from the application, and uses it in the library on demand. Your application must pull in Coil as a dependency.

The theme is an optional parameter, which allows you to pass your applications Compose MaterialTheme and is provided with a ThemeProvider and a composable content callback. This will allow your Compose theme to be applied to PYDroid internal components for application consistency. Your application must implement its own Compose theme, or provide an instance of MDCTheme, otherwise this will fall back to Compose defaults.

Finally, the optional logger parameter is a new logger interface which is used in the library internally. If you do not wish for the library to show logs, you do not have to pass an implementation. As a result of this interface, PYDroid no longer depends on the Timber library, but your applications can still use Timber as a logger by implementing the DebugTree in a PYDroidLogger interface.

Now let's talk progress - specifically, let's talk deprecations.

From PYDroid-Arch, the entire UiView, BaseUiView, UiRender, ViewBinder, StateSaver, and UiViewEvent system has been deprecated. The UiView system was a way for XML Android Views to fit into PYDroid's component style architecture, which worked well to separate large screens into small independent visual components. With Jetpack Compose, this separation can be achieved inside of a Composable itself, which means that our own independent UiView system is no longer needed. The UiViewModel will continue to exist and operate with a UiController and handle a UiViewState in the MVI style, but any methods revolving around UiComponents and UiViews have been deprecated.

All PYDroid-UI which revolves around the Android View system and XML views have been deprecated. This includes XML Preference support in the SettingsFragment. Preferences should now be handled in the Compose friendly PYDroid-Ui PreferenceScreen composable, which provides a bridge between the old XML based views and the new Compose UI. Applications still using the old AppSettingsPreferenceFragment based settings screen can continue with no changes, but any new applications will be able to use SettingsFragment for Compose compatible preference UI.

All PYDroid-Util functions revolving around Views have been deprecated. As well as these, the Context.asDp() and Drawable.mutate related functions have been deprecated in favor of the Compose built-in counterparts.

The entire PYDroid-Loader module has been deprecated in favor of Coil and it's ImageLoader. Glide is large and not Kotlin-first and Coil is a smaller project and already has first party support for Compose. PYDroid-Loader was built around the old View system, and is both less feature filled than Coil and lacks Compose support.


To migrate to the new PYDroid 24.0.0, you should break up your migration into the following

1. Migrate to PYDroid 24
2. Migrate your application to Compose

To migrate to 24 is (hopefully) very simple. Provide the 2 new required parameters to your application level PYDroid.init() call (you may need to get your Base64 LVL key from Google Play Developer Console), and remove the deprecated fields from your MainActivity like the fragmentContainerId and snackbarOwner. You should be able to then build and run your application. You will notice at this point that some PYDroid UI components are not themed correctly. To resolve this you should either migrate to a Compose theme, or pull in the MDC Theme adapter project to convert your XML theme. Once you have done this, you can now begin the full migration to compose on your own time.


========================
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
=========================