Monday, May 11, 2020

PYDroid 21

PYDroid is versioned enough to legally drink.

This new release deprecates the BindingUiView interface and moves its ViewBinding related code into the BaseUiView. Long live ViewBinding!

The UiViewModel state is bused using the brand new StateFlow interface which allows it to be easier to maintain and coroutine and thread safe by default. This change is transparent.

This release also adds support for a page about other pyamsoft apps! If you do not have a network connection, or have not allowed the app to connect to the internet, clicking on the "More apps" button in the settings page will still perform the old behavior and route you to the pyamsoft page on the Play store, but as long as you are connected to the internet, it will now load a new page which provides you links to all pyamsoft apps as well as links to their store pages and source code!

This is the first major release in a long time and it will be followed by updates to the pyamsoft android applications. I know, its been a long long time coming - work and life and the coronavirus have all gotten in the way. FridgeFriend is not dead - neither are any of the pyamsoft android applications. I do want to release soon, but FridgeFriend still needs some general UI cleanup and feature implementation to truly be customer ready. Please be patient as always, you'll hear from me again 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
=========================

Tuesday, March 3, 2020

Android 11

With Android 11 come some new permission changes - in particular - Google is no longer allowing any application to easily request Background Location permission access. If this permission process is anything like SMS was a while back - nobody will get background location permission.

FridgeFriend was reliant on this functionality to use geofencing at the user's request - and notify you if you have come within range of a marked supermarket and need to still purchase some items. Sadly with these new restrictions this is no longer possible.

While this nice feature is gone, as long as the application is open FridgeFriend can notify you about nearby stores and needed items. I am also working to include a feature to notify you in the evening about cleaning out any items from the fridge which have gone bad and polishing up your FridgeFriend list of items so that it is ready for the next day.

Release is - sadly - not yet. Life gets in the way but we keep working. One day - I promise.

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 28, 2020

One state, two state, previous state, new state

With the latest changes to PYDroid in 28, you'll probably notice that when renders occur, the render function is called only with a single State parameter. In fact, throughout the history of the PYDroid Arch library, there has never been an instance where the previous state was also passed through the render. PYDroid doesn't pass the previous state, PYDroid doesn't like the previous state.

Why not?

Ultimately, its a philosophical decision and not a technical one. Before we dive deeper, we should understand why the previousState is generally a wanted thing.

In many cases with MVI frameworks, the render function is responsible for drawing and updating the entire visual part of state. In many cases, the previousState and current state are used in this render function to avoid unneeded renders or re-renders. Sounds like a good thing, so why doesn't PYDroid like it?

I would argue that the point of MVI is to simply how to draw a screen - by giving the consumer a single parameter and saying "everything on screen must come from this state object". By including in the mix a previousState, the idea of rendering on to the screen is now a function where the output is a complex combination of two very specific inputs, instead of just a simple object. Adding previousState makes it just as tedious to track proper state as one would experience in a non MVI setup. It does have the nice benefit of avoiding un-needed drawing and rendering, but at what cost?

So then PYDroid doesn't use previousState, so therefore PYDroid can cause useless rerendering and is slow. Well - not quite. PYDroid debounces on states which are the same - if a state change ultimately results in the exact same state as the one before it, the render is never called. This is different from a framework like React, which will render each time setState is called as long as shouldComponentUpdate is true.

Then, how does PYDroid avoid the situation where a state change occurs, but only one component of the state has actually changed? For example, if your state holds an error Throwable and a loading boolean - how can you avoid re-rendering the view when only one of these changes but not the other? Well, PYDroid does nothing. It just sends the render call to the view layer.

But remember what our View layer is. We are not on the web, where the View is created and rendered as just a DOM - we are on Android. Android views largely handle these duplicate state issues on their own. Passing the same text string to a TextView will not cause a redraw. Passing a drawable to an ImageView will not reload it. Passing a new list to a RecyclerView adapter will be diffed via the submitList function, and only the individually changed ViewHolders will be re-rendered. Basically, PYDroid doesn't care - it delegates the responsibility of avoiding useless rendering to the View layer which does the rendering. In the event that you really need to manage previous states and avoid re-rendering, you can keep track of the specific components of state on your own.

Maybe in the future this may change if decide it is more trouble to not have previousState - but at least for the lifespan of 28, PYDroid will be a single state MVI framework.

========================
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 22, 2020

New droid, my droid, good droid, pydroid

PYDroid changes and incompatible APIs, name a more iconic duo.

The next version of PYDroid will be 20.8.0, which will be generally sane, but will require some API consumer changes to work.

The first big change is that the use of directly referencing Bundle objects in UiViewModels and UiViews is gone! Replacing it are two new abstractions, UiBundleReader and UiBundleWriter. I assume you can guess what they do. You can create a new reader or a writer from a Bundle using the respective Companion create method. They have a similar API to the normal bundle and should be simple to migrate to.

The perhaps larger change is an update to the rendering of UiViews. Gone is the notion of the UiSavedState class, as it was a class which held saved bundle data but was also stateful and sometimes returned data and sometimes did not depending on when the data was last consumed. This posed problems for nested render loops which relied on the same saved state. As a result, all render and onRender methods now only take a single parameter - state.

The init, inflate, teardown, and saveState functions on UiView which come from its interfaces are now final - please use hooks instead of overriding these if you extend from UiView directly. Any other consumer, like those from BaseUiView, PrefUiView, or just consumers of the IView interface should notice no changes.

As a result of these changes, some memory leaks have been plugged in the pyamsoft applications - I will try to get a release out soon. FridgeFriend is ever vigilent and still coming along.

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, January 13, 2020

Suspending SharedPreferences

In an effort to future proof pyamsoft applications for what I assume will be a future announcement of deprecating SharedPreferences, I have updated PYDroid to access all preferences as suspending functions. This will avoid activity on the main thread as SharedPreferences are initialized and should improve read performance.

Initialization hooks on UiViewModels are now stored and replayed from a Set instead of a List, so ordering of these hooks is not guaranteed. While still guaranteed for UiView instances, you should not rely on this ordering as a strict API guarantee. Anything that requires ordering should run in the same block of a given hook.

FridgeFriend has received more improvements on its quest to finally - finally - FINALLY be released... and its not quite there yet but I will release it one day. Since a new Half Life is coming out I suppose my pool of excuses is slowly drying up. After all this time, I'm a bit anxious about releasing what is effectively still an MVP of an idea - but hey that's what you get with hobby time projects.

I've got more to talk about but its a nitty gritty nerd topic - so maybe another time.

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, January 6, 2020

PYDroid Party

Happy new year, happy new PYDroid!

PYDroid 20.7.0 has been released over the holidays! This finishes the deprecation of the old override API for the PYDroid-Arch component, and brings the new hooks API as well as the ability to nest BaseUiView<S, V> objects within each other as long as the state and event types are the same!

This also brings a shiny new convenience API called the StateSaver

  private var stateSaver: StateSaver? = null

  override fun onCreate(savedInstanceState: Bundle?) {
    ...
    stateSaver = createComponent(savedInstanceState, this, viewModel, view1, view2, view3) {
      ...
    }
  }

  override fun onSaveInstanceState(outState: Bundle) {
    stateSaver?.saveState(outState)
  }


StateSaver helps to reduce the boilerplate of saving instance state on all your views and viewmodel by wrapping them all up into a single object and calling save state on everything all at once. By marking the object as nullable and creating it in the onCreate function, you can guarantee cases where onSaveInstanceState is called before construction (like in Fragments when you rely on onViewCreated) will never crash your application - as the StateSaver will only be available once the view is created.

Oh, and speaking of saving state, UiViewModel now has a doOnSaveState { state -> ... } hook which will let you hook directly into the Android state saving lifecycle. If you are correctly modelling state in your application using UiViewModel and UiViewStates, in many cases you can remove code out of your UiView save state hooks and into the view model hook. The UiViewModel init hooks also gained support for accessing the save state bundle in the doOnInit { savedInstanceState -> ... } hook.

BaseUiView<S,V> (one's which inflate and use an Android View or ViewGroup as the parent object) have gained support for nested UiViews. You can use the top level UiView layoutRoot as the container for nested BaseUiView objects - as long as the layoutRoot is a ViewGroup. This is a bit of an API hack - as we override the parent argument passed in with the constructor and use the containing UiView instead. In a major release like 21.X.X, this API will probably be re-worked to be less clunky - but I wanted to make nesting available without breaking existing UiView setups.

Each UiView and all of its nested UiViews will be subscribed to the same UiViewModel and will receive all events in parent -> child order. This includes inflate, teardown, and save state hooks. You can nest BaseUiView objects using the nest(vararg view: BaseUiView<S,V>) function from within a BaseUiView object - usually within the init block. Though you theoretically could dynamically nest views on the fly - it is generally recommended that you always nest views in the correct relations - and then dynamically show or hide the needed views on the fly. Save your sanity.

The PYDroid UI reference library gains some new function as well - all animators in the view animator utility package will now return the animator object instead of throwing it away. This will allow you to pause and cancel animations in progress if needed. The listener override API will now append your listener to the default animation listener instead of overwriting it entirely - this change should not be noticed for too many people - but be aware as it is a minor behavior change.

List level ViewHolder performance has been improved thanks to the new ViewBinder<S> API. The ViewBinder is a lightweight component-like API, which groups a lifecycle owner with an abitrary set of UiView<S> which all share the same UiViewState. Instead of responding to UiController events, the Binder will respond to UiView events - this is because the view holder itself is mainly a light weight display container. All events would generally be forwarded to the list component which will be a part of the controller. The ViewBinder has a single method, bind(state: S) which will fire a render on all of the held view components. This helps to simplify playing with the RecyclerView ViewHolder framework - layouts are inflated, click listeners are set, components are injected or constructed - and the the ViewBinder is created via the top level bindViews(vararg views UiView<S,V>) function inside of the onCreateViewHolder function. The ViewBinder is used in the onBindViewHolder function to bind a model mapped to UiState to the list item. Generally the lifecycle owner is the container lifecycle owner passed into to the onCreate for the view holder - this allows all items to be bound and unbound with the container state.

class ViewHolder(parentLifecycleOwner: LifecycleOwner, itemView: View) : RecyclerView.ViewHolder(itemView) {

  private val viewBinder: ViewBinder<MyListItemState>

  init {
    ...
    viewBinder = bindViews(parentLifecycleOwner, view1, view2, view3) {
      ...
    }
  }

  fun bind(state: MyListItemState) {
    viewBinder.bind(state)
  }
}


You should notice these performance changes in the Open Source Licenses list page in pyamsoft applications.

Finally, though not documented, UiView initialization is now broken up into init(savedInstanceState: Bundle?) and inflate(savedInstanceState: Bundle?) steps. This is used for the new view model APIs, and breaks up the roiot view inflate step and the later inflation hooks into two seperate processes. If you, for some reason, do not use the standard UiViewModel component API, you will need to update your UiView classes to handle this view. It can be a no-op.

All of these big API changes are in prep for FridgeFriend to finally - finally ... finally release ........ someday soon! The holidays were a super lazy time for me - and as a result - I ate and drank and generally did zero work. So, continue to be patient as I continue to work hard and at some point I will deem the app good enough for a first version and that will be that.

But until then,

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 10, 2019

The end of override and long live the hooks

PYDroid 20.6.10-SNAPSHOT is the current version being used in all the in-development pyamsoft applications - and it will be the final version to support the overrides for onInflate and onTeardown.

Going forward with PYDroid 20.7.0, these overrides will be gone and the hook methods will be the only remaining ones.

Not to worry - if you are not a developer this does not concern you - and if you are a developer - thanks! But the overrides have been deprecated since 20.5.0 and its time for them to go.

You know what its not time for? In-app purchases. Ever.

My payment profile on my merchant account appears to have been closed as a result of closing what I thought was only access to the Google Pay app. Oh well, live and learn. As a result I am unable to associate a payment profile and cannot create, manage, or view in-app purchases in the console ever again.

So any form of monetization to keep me able to continue working on these apps will have to come from somewhere else, oh well.

I did however manage to get a full branch on PYDroid up and running called billing which integrates the Google Play Billing Client library into the UI architecture - but its completely untested and will probably forever remain untested. You're welcome to branch off and try it, but I don't recommend it - unless you want to learn about how to make coroutines work with a strictly callback based library and how it fits into an existing MVI architecture. Actually - that is rather interesting - so maybe it would be worth a look at the PYDroid "billing" branch.

FridgeFriend is so so close. I know you've heard this before - in fact you've been hearing this for almost the entire year. Happy procrastination anniversary! It really is gettting closer every day - and once I can convince myself that it will never reach my own personal standards for what a "complete minimum viable product" is, it will be released. To ease your fears about it dying an early death - I've been using it in my own life every day since like May when it first reached a usable state, so far so good. Nothing has spoiled in the fridge since.

I'm debating to continue publishing here versus moving over to a more 2019 platform like Medium - just in time for 2019 to be over. If I do decide that change is coming, rest assured I will publish far in advance about how the move will happen.

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, October 29, 2019

More Library Updates

Cachify and Highlander have received small bugfix updates today. Cachify has bumped to 0.0.4 and Highlander to 0.0.3.

Cachify has changed to allow for more verbose logging when enabled regarding what steps the runner is taking at what point in time. The multi-backend setup is also now backed by a Mutex to guard against concurrent access of an Orchestrator over different coroutines. It also fixes a bug where - if a cache is spam called quickly enough, the runner will attach and await a coroutine which is already completed or cancelled - which would cause the Orchestrator to attach to a runner that returned a value successfully - but tossed it out into the void.

Now the runner will check that an active task is still currently alive, and only attach to it if that is the case. If a task is active - but cancelled or completed, the runner will consider the task no longer active and clear it out - and then it will wait for a new active task, or take over as the active task.

With these library updates, PYDroid 20.6.1 has been released which just pulls in these new library versions.

These small bugs were discovered as part of a testing run on FridgeFriend to make sure that it can correctly handle users spamming buttons, clicking around, and any such other nonsense. Every day I am getting closer and closer to preparing for a release - and then I get to battle the new Play store and its policies -_-

Thanks for your patience, this has been a passion project of mine for the past year or so now and I'm really excited to see it nearing completion!

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, October 23, 2019

Android Studio on Android

Kind of. Sort of. It's not perfect. Temper your expectations - and continue.

Let's set the stage - I have a Samsung Galaxy S10. Its a powerful device - 8GB of ram, 8 cores around 2GHz or so, not too bad for a pocket computer. It should be able to do things that a pocket computer would be expected to do - like run computer applications in my pocket. I like to code, I like to travel - I like to code while I travel and I didn't enjoy carrying around a seperate laptop everywhere. Here is my journey at trying to get Android Studio to run on Android.

What you'll need:

Patience.
UserLand
bVNC
Patience
A phone running Android
Basic Linux knowledge
Patience


What you'll be asked to download

Android Studio
The Android SDK
The Java JDK 8 built for ARM devices
Optionally the Kotlin compiler built for ARM devices


The rest of this assumes that you have some basic Linux knowledge and are insane enough to attempt running Android Studio off your Android phone.

First you should download, install, and setup Userland. I picked from a simple Ubuntu option and chose to connect over SSH. Once the Ubuntu install is finished, I launched into an session from the Userland app, and installed the following components from the package manager apt:

openjdk-8-jdk build-essential ubuntu-make tigervnc-standalone-server tigervnc-xorg-extension adb aapt

You will also need at least one android-platform from the package manager - on Ubuntu its platform 23. It's out of date, but that doesn't matter - we just need it so that Android Studio believes we have a valid SDK install. You'll also need to install Android Studio from the website - NOT the package manager. Head over to the Android Studio website and download Android Studio and the Android SDK for Linux tar files. You need to download the SDK seperately since the one bundled with Android Studio does not work on ARM.
From this point forward, I will refer to the SDK files as the Android SDK, and the stuff you got from the package manager as the repo SDK.

To steel your nerves for what you are about to get yourself into - largely things don't really work. You can expect many issues - know that if anything doesn't work your first instinct should be to try and replace whatever the file in the Android SDK is with the repo SDK.



You'll first need to set up TigerVNC so that you can connect to a graphical desktop session - you'll need to know a little bit about setting up VNC here, but here are the scripts I use every day to connect to the VNC server that I have set up on my phone.

The reason we want TigerVNC over the Userland default TightVNC is that Tiger supports the XRANDR extension - which we need to launch Android Studio. Use the absolute latest TigerVNC release for the best performance.

You can also install the DE or WM of your choice (I opted for XFCE4).

Once everything is downloaded, launch Android Studio and point it to the SDK you just downloaded. You can attempt the first time setup wizard - but it will most likely fail. That's expected - it is usually expecting to run things like adb or mksdcard which don't run on arm. Click on through as far as you can get and mark the wizard to not re-run. Close Android Studio.

Now for the first sync/index/file download. The first thing you'll need to do is STOP using the embedded Android Studio JDK - you'll have to go into both your Project Properties as well as the Default Project Properties and change the JDK target to the ARM JDK 8 you installed from the package manager, usually in /usr/lib/jvm. If you do not do this for both Project and Default settings, Gradle will not run. You will also want to manually copy the aapt, aapt2, and adb binaries you installed from the repo and replace the ones in your Android SDK location. For me, this meant copying /usr/bin/adb over ~/Android/sdk/platform-tools/adb and such. You'll also want to copy the repo-SDK Android platform that you installed (in my case platform-23) over to the correct directory in the Android SDK.

Once you have done this, open Android Studio and open the Settings popup - head over to the Android SDK settings under General - it should warn you about not finding any Android SDK setup because it failed earlier in the setup wizard. Click the SDK path to open the path picker and select your Android SDK location - this time it should treat the path as valid because you have a working adb, aapt, and 1 valid Android platform. It should succeed and now you can manage the Android SDK from Android Studio.

Download all the updates and get the latest platforms you need to support and all that fun stuff. One note - when installing a new Android platform there is a step during the process where Android Studio will attempt to stop the running ADB daemon. This step will fail if you have not put the repo-SDK adb file over the Android SDK one. NOTE that you must install the Build-Tools, SDK-Tools, and Platform-Tools file first on their own - copy over the old adb binary - and then install any new Android platform SDK updates. If you do not do things in this order the update will not go through.

Once you have done this, you should be able to attempt your first build - as long as what you are building is a library project. Normal Android applications will not build successfully because of the resource merge step which requires them to run aapt2 - aapt2 is not built for ARM in the Android SDK and so it will fail to run. Library projects should compile fine because they do not run aapt2, but normal Android applications do. Sadly this is a limitation of ARM at the moment - the only way to compile your normal Android applications is to change the plugin from 'com.android.application' to 'com.android.library', and in doing so you will be able to compile, but not actually build an APK on to your device. It's better than nothing I guess. You can see an example of what I mean here.

If you use kotlin (you should), you'll also need to manually build your own kotlinc so that it will run on ARM instead of using the version shipped with the Kotlin plugin. You can do this using ubuntu-make.

If you want to run platform tools like fastboot, you can install their packages from the package manager repository - but you won't be able to mix and match them with your current Android SDK. Even if you manage to install an ARM version of aapt2, when you go through a build in Android Studio it attempts to access a version of aapt2 from the ~/.gradle/caches directory which will not work - so its a dead end for now.

At the long end of your tiring journey - you have a phone which can adb to itself over VNC, a sort-of Linux distribution running on the same hardware that runs your Youtube app, and a way to - if nothing else - at least code and compile Android apps on the go. It works great in Samsung Dex mode, and can actually provide some decent results. It's not perfect, but its a fun little excercise in trying to get Linux onto everything that has a chip - and hopefully it will only get better in the 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
=========================

Friday, October 18, 2019

Library updates

PYDroid 20.5.0 is released, bringing the new hook style initializer and teardown handlers I mentioned in the last post! As mentioned before, the old function inheritance style is still present - and will continue to work until 21.0.0 at which point it will be unceremoniously slaughtered for the greater good.

Along with PYDroid, Cachify has seen a new release bringing it to version 0.0.3! The new release is backwards compatible with 0.0.2 syntax wise (but not binary compatible - sorry) and lays the groundwork for a full repository pattern. Cachify 0.0.3 now allows you to specify a list of CacheStorage objects, which will be iterated through one after the other when looking for cached data. The first object to present valid cached data will be returned, and only if no CacheStorage objects contain valid data will the upstream be hit. This change is transparent for consumers, and the simple syntax for creating a Cached instance which is backed by a memory cache storage is unchanged.

The one change though - previous releases of Cachify used public inline functions and called through to internal library implementations via the @PublishedApi annotation. With 0.0.3, this annotation has been removed, making the library not binary-compatible with 0.0.2. Unless you have a very strange setup, you should not notice this change at all.

And with the release of Cachify 0.0.3 also comes PYDroid 20.6.0! Wait what? Two releases of PYDroid in the same evening? Why yes! For those of you who do not know, PYDroid is this mishmash library which contains other libraries - like Coroutines, Cachify, and Highlander. The only difference between 20.5.0 and 20.6.0 is that 20.6.0 brings support for the new Cachify 0.0.3! I didn't want to remove the already released version and I figure its nice to know that not too many things are changing at once. For those who want to play it safe, you can use 20.5.0, but if you are adventurous and daring, try the new 20.6.0!

I'm hard at work on actual applications too - FridgeFriend is nearing completion and once it is done it will go out with the other pyamsoft applications - bringing support for Android 10 and all its shiny new things!

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