12/01/2025

[Android] Sprescia app for run activity tracking

No it is not Christmas today, but here is another free, no ads, no trackers, no data collection, minimalistic app to track your run activity!

Built for Android 14+ using help from ChatGPT and icons from SVG Repo this app writes to a local Rooom DB in 1 table: run

Run view allows you to add, edit, delete run details and calculates average speed for each entry, comparing it with the previous one, giving you a quick view of your training progression. You can also export and import data to/from csv for quick backup and restore logic.

Daily stats view allows you to see daily run data and compare it to previous runs using key metrics: steps, average speed, distance, time.

Monthly stats view provides the same capability but averages the data grouping per month instead.

You can find the Sprescia project on my GitHub

[Android] MaiCar app for car expense management (refuels and maintenance)

Finally a free, no ads, no trackers, no data collection, minimalistic app to track your car expenses!

Built for Android 14+ using help from ChatGPT and icons from SVG Repo this app writes to a local Rooom DB in 2 tables: fuel and maintenance

Fuel view allows you to add, edit, delete refuel details (partial and full tank) and calculates efficiency for each entry, comparing it with the previous one, giving you a quick view of your trip efficiency progression.

Maintenance view allows you to add, edit, delete maintenance details for multiple maintenance types, it also allows you to filter the view on a specific maintenance type to quickly find a specific item.

Both views allow you to export and import data to/from csv for quick backup and restore logic.

You can find the MaiCar project on my GitHub

15/12/2024

[Android] Recompile and sign APK

Each new Android version introduces changes that usually require app developers to put some effort into keeping their app compatible with the latest OS releases. As a result, sometimes, your favorite free apps have issues if the developer does not update it.

Depending on the required changes, the effort varies, however you can often adapt the app yourself by simply decompiling it, updating whatever needs to be updated, and repackage it with a new Android version as target. Here are the simple steps to achieve this.

08/11/2024

[Google Sheets] Import data from Investing.com

DISCLAIMER: This is not a stock picking recommendation, the tickers shown are used as example and not provided as financial or investment advice.

DISCLAIMER2: Web scraping can go against usage policies so read them, understand them and abide by them. Also web scraping is notoriously flaky as small changes in the retrieved page can affect the location of the desired element(s).

We have already seen how to import data from Yahoo Finance and Coingecko, now we try to add Investing.com as source which unfortunately does not provide API, so we have to do some HTML scraping instead.

We would like to retrieve the current price of an ETF called JPNA. By looking at that page we can luckily identify a specific locator, which then allows us to do a couple string manipulation operations before getting the desired value:

function getInvestingData(path) {
  var result = UrlFetchApp.fetch("https://www.investing.com/etfs/" + path.toLowerCase());
  var response = result.getContentText();
  var start = response.indexOf(' data-test="instrument-price-last">') + 35;
  var end = response.indexOf('</div>', start)
  var out = response.substring(start, end);
  var strip = out.replace(",", "");
  return Number(strip);
}

19/10/2024

[Google Sheets] Import crypto data from Coingecko

DISCLAIMER: I do not recommend investing in crypto, all tokens you will see are used as sample and not provided as financial or investment advice.

We've already seen how to add custom functions to our Google Sheets projects, importing Yahoo Finance data. Now we expand this to import cryptocurrency data from Coingecko.

I could have picked any of the million sources, but this one has a simple free plan which for light usage works well.

After registering and getting your API key, RTFM to find that they provide a lot of stuff, what we care about is the pricing data in this example, you will need to find the ID of the currency you want (NOT the token) by querying the list and then you can get the data you want by calling (add your API key at the end):

"https://api.coingecko.com/api/v3/simple/price?ids=" + token + "&vs_currencies=usd&x_cg_demo_api_key="

You can put this in a Google Sheet function as well (you can extend input parameters to get quote in different currencies as well):

function getCoingeckoData(path) {
  var result = UrlFetchApp.fetch("https://api.coingecko.com/api/v3/simple/price?ids=" + path + "&vs_currencies=usd&x_cg_demo_api_key=YOUR_API_KEY");
  var response = result.getContentText();
  var json = JSON.parse(response);
  return json[path]['usd'];
}

04/10/2024

[Java] Get type of elements in collection using reflection

Java type erasure logic means that at runtime some information is removed/replaced from generic declarations, which poses a minor challenge when trying to identify the actual parameter types using reflection at runtime.

Here is a way:

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;


//maybe you are looping over fields or have a field of type Field f
ParameterizedType collectionType = (ParameterizedType) f.getGenericType();
Class<?> actualClassOfElementInCollection = (Class<?>) collectionType.getActualTypeArguments()[0];

27/09/2024

[Java] Load entity with lazy children collections

When using JPA and lazy collection loading, at the time the parent entity is retrieved from DB, the lazy children are NOT also loaded in memory, instead, a proxy reference is added, which is used to retrieve the data if that particular field is ever accessed.

In some scenarios you might want to load the whole entity, including the lazy children in memory instead (eg you want to clone/serialize it, whatever).


If you want to load ONE child together with the parent, a JOIN FETCH clause would do the trick: 

SELECT p

FROM Parent p

LEFT JOIN FETCH p.childField c

WHERE p.id = :id


But if you try to load more than one child at the same time, you will get a MultipleBagFetchException. A workaround is to call the load with JOIN FETCH for all entities you need sequentially, for example:

private Parent loadChildViaQuery(ID id, String childClause) {
  return entityManager
    .createQuery(
      "select p " +
      "from Parent p " +
      "left join fetch " +
      childClause +
      " where p.id = :id",
      Parent.class
    )
    .setParameter("id", id)
    .getSingleResult();
}

Where childClause input is the join statement you need, for example:

"p.childField c"

Also remember that if the parent entity is not found, the operation would throw a NoResultException, while if the child is not found, no exception is raised, the child is simply null/empty.