How to fix Gradle -P arguments not working in PowerShell

Executing this command in PowerShell:

./gradlew createGplayDebugCoverageReport -Pcoverage=true -Pandroid.testInstrumentationRunnerArguments.class=com.owncloud.android.datamodel.FileDataStorageManagerContentProviderClientIT

gave me the weird error

A problem occurred evaluating project ':app'.
> No such property: applicationVariants for class: java.lang.String

Turns out, PowerShell can interpret the -P as a parameter for the script or command, not as an argument to Gradle, which leads to parsing problems.

Solution: Use –% to Stop PowerShell from Parsing Arguments

./gradlew --% createGplayDebugCoverageReport -Pcoverage=true -Pandroid.testInstrumentationRunnerArguments.class=com.owncloud.android.datamodel.FileDataStorageManagerContentProviderClientIT

Bonus: I used Github Copilot to have this explained to me and it actually hallucinated a reference.

It gave a link to https://stackoverflow.com/questions/36540081/gradle-p-arguments-not-working-in-powershell which actually resolves to an unrelated article https://stackoverflow.com/questions/36540015/how-to-reduce-apk-apk-size-in-android-studio/36540081#36540081. So I figured, let’s make this article a reality!

How to keep your fitness tracker health data to yourself

For a long time I have refrained from using fitness trackers or sport watches because I never found the time to dive into questions like:

  1. Which fitness tracker can be used without a mandatory mobile app?
  2. How can I access my fitness data conveniently but keep it away from “the cloud”?
    • Or: How can I backup fitness data on “my cloud”?
  3. Which fitness tracker works well with the Open Source ecosystem?

Well, I finally did and now this is my setup:

  1. Garmin Instinct 2
  2. The open source Android app Gadgetbridge

Honestly, the watch works well enough on its own. You need to get used to its 90s-Casio-watch-style control using five hardware buttons, but once you learned it, you could access all the relevant data without any additional connection.

But still, for a bit more convenience and more in-depth details of fitness and activity data, Gadgetbridge is quite nice. Inside the app, you can easily connect to the Garmin watch via Bluetooth.

Fetching and updating activity data is by default done manually via button press, since it may take 10-15 seconds. Once the data is downloaded from the watch, you can dive into all the details about your activities, sleep, heart rate, etc…

Screenshots from https://gadgetbridge.org/basics/features/activities/

There is a couple of maintenance steps you’ll need perform manually from time to time, as Gadgetbridge cannot interfere too much with the IP of the original manufacturers.

Firmware updates

Gadgetbridge allows you to upload updated firmware to the device, but it doesn’t tell you where to get these files from, most likely out of fear of retaliation.

So how do you get the firmware files for your Garmin watch? From a non-shady source, preferably? Easy: You can find them on Garmin’s official forum. More specifically: Their beta builds published for Side Loading contain the last official build as well, for an easy roll-back.

As an example, see their announcement Beta Version 17.04 – Side Load (Archive.org link) containing zip archives for various watch models.

  1. Download the zip file for your model
  2. Inside, you find the latest official build under SystemBackdate_XX.XX/GUPDATE.GCD
  3. Open Gadgetbridge, connect to your watch
  4. Click the three dot menu next to your watch, then “File Installer”
  5. Select the .gcd file and upload it to the watch

AGPS updates

AGPS is responsible for speeding up your GPS-based localization and make it more precise. For that, it relies on (pre-)computed satellite orbit and correction data. This must be refreshed from time to time, e.g. every 30 days.

  1. Open Gadgetbridge, connect to your watch
  2. Click on the gear to open the device-specific settings
  3. Click on “Location” and scroll down where it says “Folder”. Set a folder where you will download the AGPS file in a minute.
  4. After folder selection, back on the “Location” screen, see the AGPS 1 URL. Something like https://api.gcs.garmin.com/...
  5. Click on it to copy it to the clipboard. Open the link in a webbrowser to download the file to the folder you set before.
  6. Back on the “Location” screen, directly under the URL, select the “Local file” you’ve just downloaded
  7. The “Status” should switch to “Pending”. Whenever the watch requests an AGPS update, Gadgetbridge will now intercept that call and deliver the file. You’ll see that some days later, the Status will then show “Current”

(Cloud) Backup

The automatic export periodically stores the Gadgetbridge database at a location of your choice, which can also be an online folder, e.g. from Nextcloud if you have the app installed. The important caveat is: This only stores the already processed data from Gadgetbridge, not the raw files from the device (e.g. .fit files in case of Garmin):

  1. In the app’s settings (not the device settings!) go to “Automations”
  2. Toggle the switch for “Auto export enabled” to ON
  3. Under “Export location” select the folder where to export the Gadgetbridge.db to.

If you want to also get the raw files from the device backed up, this needs to be triggered manually:

  1. In the app, open the tab “Data management”
  2. Click “Export zip” and store the file at a location of your choice
  3. The resulting file contains the Gadgetbridge database under database/Gadgetbridge and the raw device files under files/<device ID>/

More about backups, including examples of how to process the data in the Gadgetbridge manual.

How to create a new Unity project without a cloud connection

With their recent Unity Hub 3.13.0 upgrade, they have removed the option for not connecting a project to the Unity Cloud. Because all that yummy data, I guess…

This is how to setup a project without uploading everything to Unity’s servers:

  1. If you want to use a project template you’ve not used before, create a dummy project with that template (which will be uploaded to the cloud). Give it a funny name, maybe
  2. The template will be downloaded to your local app data
    • Windows: %APPDATA%\UnityHub\Templates
  3. In that folder you find .tgz archives for each template. Select the one you need and pull the contents of package/ProjectData~ out.
  4. Place it into a new folder with your desired project name.
  5. In Unity Hub, click “Add” next to the “+New project” button and add a project from disk

I found it funny to illustrate a post about a Microsoft bug with a Microsoft Copilot generated image containing garbled text

Can’t open a Microsoft Outlook protected message? This is how you work around it

Email encryption forever is a pain point in the IT ecosystem. PGP is a great system but hasn’t been widely adopted.
This is how I personally have used PGP in the past, for the <10 PGP emails I have received over my entire lifetime:

https://xkcd.com/1181
(Yes, I know the joke is more about sender verification and less about encryption itself)

Microsoft has rolled out their own solution to the problem: Outlook Protected Messages. A proprietary system on top of an open, wide-spread standard – I don’t really like that but hey, it is better than nothing!
If an Outlook user sends you such a protected email to a non-Outlook and non-GMail address, you will receive an email “Alice has sent you a message that was protected with Microsoft Office 365” and a link to click. You’ll be redirected to a page where you can sign in and receive a single-use code sent to your email address.

But here is the catch: This sign in just doesn’t work! The email I received these messages is not connected to a Microsoft account. So I could not login to request the single-use code. I then tried it with an email which was connected to a Microsoft account – turns out, this also doesn’t work! Even if the protected email was sent to this Microsoft account.

Somehow, the solution to this is to trick Microsoft into your browser being a mobile browser. Then, you are not asked for any sign in but can directly request the single-use code. This is how you do it, using the Browser’s developer tools:

  1. Copy the link from the protected message
  2. Open a new empty browser tab.
  3. Right-click → Inspect
  4. Click the “device icon”
    • On Firefox, it is on the right side of the bar
    • On Chrome, it is on the left side of the bar
  5. Your browser now acts as a mobile browser.
  6. Enter the link into the address bar
  7. Request the single-use code to be sent to your email
  8. Then enter the received code in the browser

Privacy in the Metaverse

Or: How to install any app on the Quest 3 without giving Meta your phone number.

With the long anticipated Apple Vision Pro become available at February 2nd 2024 (unfortunately only in the US), we’ll finally see Apple’s take on a consumer-ready headset for mixed reality – er … I meant to say spatial computing. Seamless video see-through and hand tracking – what a technological marvel.

As of now, the closest alternative to the Vision Pro, those unwilling to spend $3500 or located outside the US, seems to be the Meta Quest 3. And this only at a fraction of the price, at $500. But unlike Apple, Meta is less known for privacy-aware products. After all, it is their core business model to not be.

This post explains how to increase your privacy on the Quest 3, in four easy steps.

Continue reading Privacy in the Metaverse
A tour of C++ Cover Mountains

Eine Tour durch C++

English tl;dr: I helped with the German translation of Bjarne Stroustrup’s “A Tour of C++”. As it is about a German book, this post is in German as well

Nachdem Sie mit dem Buch “C++ Schnelleinstieg” einen praktikablen Einstieg in diese Sprache gewagt haben, können Sie als nächstes Ihr Wissen mit “Eine Tour durch C++” vervollständigen. Bei der Übersetzung dieses Buches vom Englischen ins Deutsche habe ich als Fachkorrektor mitgewirkt.

Dieses Buch deckt zahlreiche Features ab, die mit C++20 neu eingeführt wurden, darunter Module, Konzepte, Koroutinen und Bereiche. Selbst einige schon jetzt verfügbare Komponenten, die nicht vor C++23 in den Standard integriert werden sollen, werden vorgestellt.

Beim Verlag kaufen: Eine Tour durch C++

Über den Autor:

Bjarne Stroustrup hat C++ entwickelt und die erste Implementierung geschrieben. Derzeit ist er Professor an der Columbia University und hat zuvor bei AT&T Bell Labs an der Texas A&M University sowie bei Morgan Stanley gearbeitet.

Step Check Open Beta start

Wow, what a journey 🚀😎 . We are so proud to finally publicly share one of the things we have worked on since ioxp joined Vuforia: The new Vuforia Instruct feature Step Check™, starts its Public Beta now!

Based on the best-in-class Vuforia technology and developed together with many of the brightest people I know:

✴️Assisted quality assurance using #augmentedreality
✴️Integrated AI training
✴️Authoring from CAD

Next steps: learn from the beta feedback, improve and deliver!
Thank you, Vuforia, for how all of you welcomed us and for how we made things possible – together!

#ar #computervision #mixedreality #innovation #service #qualityassurance #acquisition

https://www.linkedin.com/posts/philipphasper_ioxp-augmentedreality-ai-activity-7004503199806013440-KJXV/

Update: Special video for the third Sunday of Advent 😉

https://www.linkedin.com/posts/philipphasper_ai-ar-vuforia-activity-7007691815181025280-6GEl

C++ Weihnachtsspecial

English summary: This is a holiday special for my German C++ beginners book, hence this post is in German.

Dieser Blogpost erweitert Ihr Wissen aus dem Buch C++ Schnelleinstieg: Programmieren lernen in 14 Tagen. Nach dem Kapitel 9 (Fortgeschrittene Konzepte) haben Sie alle nötigen Grundlagen, die Sie als Voraussetzung für das Special benötigen:

  • Grundlagen der Objektorientierung
  • Bibliotheken mittels vcpkg einbinden
  • GUI-Programmierung mit Nana
  • Callbacks und Lambdas
  • Fehler werfen

In diesem Projekt soll ein Weihnachtsbaum programmiert werden, bei dem Sie die Kerzen per Mausklick anzünden können. So sieht das ganze aus, wenn es fertig ist:

Das Codebeispiel beginnt in der main-Funktion des Programms und fügt nach und nach zusätzliche Komponenten oberhalb ein – daher beginnt die Zeilennummerierung so wie sie in der fertigen Datei sein wird (Downloadlink am Ende des Beitrags). Der Code wird durch Erklärungen unterbrochen, wie Sie an den fortlaufenden Zeilennummern erkennen können.

int main()
{
  nana::paint::image tree("tree.png");
  if (tree.empty())
  {
    /* Sie müssen die drei Bilder (tree.png, candleOn.png, candleOff.png) in den
       Build-Ordner kopieren. Sie finden diesen, indem Sie in Visual Studio im
       Projektmappen-Explorer einen Rechtsklick auf das Projekt durchführen und
       "Ordner in Datei-Explorer öffnen" auswählen.
    */
    throwException("Konnte das Baumbild nicht laden!");
  }

Die Funktion throwException wird später noch implementiert. Die für das Programm benötigten drei Bilder des Baums, der erloschenen und der angezündeten Kerze finden Sie zusammen mit der fertigen Codedatei am Ende dieses Beitrags. Nun geht es um die grundlegende Struktur: Das Fenster sowie die Kerzen.

  // Ein Nana-Formular als Basis des Fensters anlegen
  nana::form window(nana::API::make_center(tree.size().width, tree.size().height));
  window.caption("C++ Weihnachtsspecial 2021");

  // Jede Codezeile beim Anlegen der Liste entspricht einem Stockwerk des Baumes
  std::vector<Candle> candles = { Candle(290, 200),
    Candle(200, 300), Candle(300, 330), Candle(400, 315),
    Candle(120, 430), Candle(250, 450), Candle(350, 450), Candle(480, 450),
    Candle(70, 580), Candle(170, 620), Candle(290, 630), Candle(420, 625), Candle(540, 600) };

Die Kerzen werden als Objekte modelliert und mit x- und y-Koordinaten angelegt. Die Klassendefinition folgt gleich, nachdem der Rest der main-Funktion besprochen wurde. Folgen Sie solange dem im Abschnitt 4.3 des Buches erläuterten Prinzip des “Wishful Programmings” und nehmen Sie einfach an, Sie hätten schon eine solche Klasse, die Ihnen alle Wünsche erfüllt. Nun sollen die Grafiken alle gezeichnet werden:

  // Zeichnen des Baumes und der Kerzen
  nana::drawing drawing(window);
  drawing.draw([&](nana::paint::graphics& graphics)
  {
    tree.paste(graphics, nana::point(0, 0));
    for (Candle& candle : candles)
    {
      candle.draw(graphics);
    }
    graphics.string(nana::point(10, static_cast<int>(window.size().height) - 20),
      "C++ Schnelleinstieg: Programmieren lernen in 14 Tagen. Philipp Hasper, 2021");
  });

Das Zeichnen der weihnachtlichen Szenerie übernimmt die Nana-Klasse nana::drawing innerhalb einer Lambdafunktion. Sie sehen hier zwei praktische Methoden in Aktion:

  • tree.paste() kopiert das Bild des Baumes an eine bestimmte Stelle – hier an die Stelle (0,0) was der linken oberen Ecke entspricht. Hierfür muss auch noch das graphics-Object übergeben werden, welches aus dem Parameter der Lambdafunktion kommt.
  • graphics.string() schreibt einen Text an eine bestimmte Stelle. Hier soll es in die linke untere Ecke, daher muss für die y-Koordinate von der Höhe des Fensters genug Platz abgezogen werden, sodass der Text noch hinpasst.

Zum Schluss fehlen noch die Klickereignisse. Da die Kerzen als Grafiken gezeichnet wurden, haben sie kein automatisches Klick-Event wie zum Beispiel eine Schaltfläche. Daher muss jeder Klick ins Fenster abgefangen und darauf überprüft werden, ob seine Koordinaten innerhalb einer Kerze liegen:

  // Jeden Klick überprüfen, ob er eine Kerze trifft
  window.events().click([&](const nana::arg_click& event) {
    if (event.mouse_args == nullptr)
    {
      // Event kann nicht verarbeitet werden
      return;
    }
    for (Candle& candle : candles)
    {
      if (candle.isClicked(event.mouse_args->pos))
      {
        candle.toggle();
        /* Hier könnte man mit einem return die Schleife verlassen.
           Da sich aber bei falscher Platzierung die Kerzen überlagern
           könnten, wird die Schleife noch zu Ende geführt.
        */
      }
    }
    // Zeichnung wiederholen, sodass das Bild aktualisiert wird.
    drawing.update();
  });

  // Fenster anzeigen und Nana starten
  window.show();
  nana::exec();
  return 0;
}

Ob eine Kerze geklickt wurde, wird innerhalb der Candle-Klasse überprüft. Auch die Reaktion auf einen Klick ist in dieser Klasse implementiert. Fügen Sie daher diese Klasse oberhalb der main-Funktion ein:

// Klasse zum Zeichnen einer Kerze in zwei Zuständen (angezündet, ausgeblasen)
class Candle {
private:
  nana::paint::image candleOn;
  nana::paint::image candleOff;
  bool isOn = false;
  nana::rectangle rectangle;
public:
  Candle(int centerX, int centerY)
    : candleOn("candleOn.png"),
      candleOff("candleOff.png")
  {
    if (candleOn.empty() || candleOff.empty())
    {
      throwException("Konnte die Kerzenbilder nicht laden!");
    }
    if (candleOn.size() != candleOff.size())
    {
      throwException("Kerzenbilder haben nicht die gleiche Größe!");
    }
    /* Die an den Konstruktor übergebene x - und y - Koordinate sollen das Zentrum
       der Kerze angeben, daher müssen Sie für das Ziel-Rechteck umgerechnet werden.
     */
    nana::size size = candleOn.size();
    rectangle = nana::rectangle(centerX - size.width/2,
                                centerY - size.height/2,
                                size.width,
                                size.height);
  }

  void draw(nana::paint::graphics& graphics)
  {
    if (isOn)
    {
      candleOn.paste(graphics, rectangle.position());
    }
    else
    {
      candleOff.paste(graphics, rectangle.position());
    }
  }

  bool isClicked(const nana::point& point)
  {
    // Diese Methode testet, ob der Punkt innerhalb des Rechtecks liegt
    return rectangle.is_hit(point);
  }

  void toggle()
  {
    isOn = !isOn;
  }
};

Diese Klasse handhabt zwei verschiedene Bilder, die an einem Punkt zentriert angezeigt werden sollen. Abhängig vom Zustand (isOn), wird das eine oder das andere Bild gezeichnet. Zur einfacheren Platzierung wird beim Erzeugen des Objektes das gewünschte Zentrum der Kerze angegeben, was dann innerhalb des Konstruktors in ein entsprechendes Rechteck umgerechnet werden muss.

Nun fehlt noch eine Hilfsfunktion, die eine Fehlermeldung auf der Konsole ausgibt und dann einen Fehler wirft. Fügen Sie diese am Anfang der Datei, nach den include-Befehlen ein:

// Funktion, einen Fehler auf der Konsole ausgibt und dann einen Fehler wirft
void throwException(const std::string& msg)
{
  std::cout << msg << std::endl;
  throw std::exception(msg.c_str());
}

Und das war auch schon das ganze Programm. Frohe Feiertage und einen guten Rutsch! Den vollständigen Code und die Bilder können Sie hier herunterladen: