Pas de fichiers Word svp!

Et au lieu de fichiers Word, veuillez envoyer des fichiers PDF afin que tout le monde puisse les ouvrir facilement:

Avant Windows 10: https://www.linternaute.fr/hightech/guide-high-tech/1412973-comment-convertir-un-document-word-en-fichier-pdf/

Avec Windows 10: https://pdf.wondershare.com/fr/pdf-knowledge/print-to-pdf-on-windows-10.html

Avec MacOS: https://www.futura-sciences.com/tech/questions-reponses/mac-mac-creer-pdf-depuis-nimporte-document-482/

Avec Linux: http://thorpora.fr/imprimer-en-pdf-sous-linux/

Atomare Updates mit Android LiveData

Android’s LiveData is a valuable asset in wrinting easy to maintain software. It introduces a life-cycle for data updates that help to reduce boilerplate code and make user interfaces more flexible. Unfortunately LiveData only covers one data point. If you need more data in your UI, you’ll face the problem that you see each update separately and therefore have unnecessary updates of your UI. Here I’ll show you how to get rid of those by updating the UI with atomic updates.

Introduction: LiveData basics

LiveData follows the observer pattern which was described over 25 years ago: you have a mutable object and you get informed when this object changes. What makes this flexible is that there can be zero or many observers on the object. This allows loose coupling of the components of an application.

When it comes to data updates, concurrency is always something to pay attention to. On Android there is a special thread called the main or UI thread. All UI updates happen on it as well as the handling of all UI events like button presses. It is therefore imperative that you don’t block this thread for too long and it is prohibited to do network IO on it. LiveData notifies its observers on the main thread as well. It therefore offers two methods that are important for multi-threading:

  • setValue(x)
  • postValue(x)

The difference is that setValue may only be called on the main thread but changes the value immediately while postValue may be called on any thread and puts the update to the value into the queue on the main thread. This means that any changes via postValue do not take effect immediately but delayed. If you run this code:

liveData.setValue(1);
liveData.getValue();  // returns 1
liveData.postValue(2);
liveData.getValue();  // still returns 1!

then both calls to getValue will return 1 as the change to 2 has not happened yet on the second call.

If you run this code

liveData.postValue(2);
liveData.setValue(1);

then every observer will first get the value 1 and then the value 2.

What happens if you update it several times? This code (which can only run on the main thread)

liveData.setValue(1);
liveData.setValue(2);

will call each observer twice (immediately), first with 1 and then with 2, while this code (which can run on any thread)

liveData.postValue(1);
liveData.postValue(2);

will call the observers only once with the value 2 after everything is done on this call from the main thread.

The problem

Imagine you have a UI that shows two different items. Let’s say it is a map that has a location and a zoom level. Both of these values can change independently. They are also unrelated in the sense, that each can live without the other. You could model them into one object LocationAndZoom, but this would violate the single responsibility principle. So you would model them with two independent LiveData:

LiveData<Location> liveLocation;
LiveData<Zoom> liveZoom;

You want your UI to update whenever one of them changes, but you need both values to paint the map.

liveLocation.observe(this, t -> updateMap());
liveZoom.observe(this, s -> updateMap());

Now picture that after a search you want you to update both location and zoom level. So you would do

liveLocation.postValue(searchResult.getLocation());
liveZoom.postValue(ZOOM_CITY);

This results in two calls to updateMap(), one with stale and one with current data. As map redraws are expensive, we should optimize it!

The solution

So you want to get only one update no matter how many of your UI variables change at the same time. For this you need another LiveData with an object that consolidates all data that you need:

private static class LocationAndZoom {
    private final Location location;
    private final Zoom zoom;

    private LocationAndZoom(Location location, Zoom zoom) {
        this.location = location;
        this.zoom = zoom;
    }
}

This is not a violation of the single responsibility principle as a change to it is driven by the same stake holder („and now we need humidity as well in the UI“). Now we can define a LiveData that holds this LocationAndZoom. But how can we achieve that it only notifies its observers once even when there are multiple updates? We will use the fact that postValue postpones updates to the next run of the main thread. This is the idea:

1st run of the main thread

Both LiveData will be updated:

liveLocation.postValue(searchResult.getLocation());
liveZoom.postValue(ZOOM_CITY);

2nd run of the main thread

This results in two calls to the observeable which will update the consolidated LiveData:

// from liveLocation observer:
liveLocationAndZoom.postValue(new LocationAndZoom(...)); // <= here the zoom is stale

// from liveZoom observer
liveLocationAndZoom.postValue(new LocationAndZoom(...)); // <= here both are recent

Certainly not like this in the code, but you should get the point that there are two updates.

3rd run of the main thread

As both calls are postValue, only the last one wins and resulting in just one call to the observables of liveLocationAndZoomd with the recent value.

Implementation

The MediatorLiveData allows to change LiveData objects on the fly. This is used to change from the source changes to the consolidated change. To simplify the code use this class:

import androidx.annotation.NonNull;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MediatorLiveData;
import androidx.lifecycle.Observer;

public abstract class Merger<T> {
    private MediatorLiveData<T> liveMerged;

    private T currentMerged;

    protected abstract boolean isValid(final T merged);

    public synchronized LiveData<T> getMerged() {
        if (null == liveMerged) {
             liveMerged = new MediatorLiveData<>();
             init();
        }
        return liveMerged;
    }

    protected abstract void init();

    protected <S> void addSource(
            @NonNull LiveData<S> source, @NonNull Observer<? super S> observer) {
        liveMerged.addSource(source, observer);
    }

    protected T getCurrentMerged() {
        return currentMerged;
    }

    protected void updateCurrent(T newMerged) {
        currentMerged = newMerged;
        if (isValid(currentMerged)) {
            liveMerged.postValue(currentMerged);
        }
    }
}

now you can implement the consolidates LiveData like this:

private static class LocationAndZoomMerger extends Merger<LocationAndZoom> {
    private final LiveData<Location> liveLocation;
    private final LiveData<Zoom> liveZoom;

    private LocationAndZoomMerger(final LiveData<Location> liveLocation,
                                  final LiveData<Zoom> liveZoom) {
        this.liveLocation = liveLocation;
        this.liveZoom = liveZoom;
    }

    @Override
    protected void init() {
        updateCurrent(new LocationAndZoom(null, null));
        addSource(
            liveLocation,
            l -> updateCurrent(new LocationAndZoom(l, getCurrentMerged().zoom)));
        addSource(
            liveZoom,
            z -> updateCurrent(new LocationAndZoom(getCurrentMerged().location, z)));
    }

    @Override
    protected boolean isValid(final LocationAndZoom locationAndZoom) {
        return null != locationAndZoom.location && null != locationAndZoom.zoom;
    }
}

Finally observe the merged LiveData and enjoy atomic updates:

new LocationAndZoomMerger(liveLocation, liveZoom)
        .getMerged()
        .observe(this, m -> Log.e("Atomic", "update"));

 

Zuschuss-Karte bei Ölmagnat

Zuschuss-Karte von Öl-MagnatAls Kind haben wir immer Ölmagnat gespielt. Schon damals hat uns geärgert, dass in der Anleitung die „Zuschuss $ 500“-Karte nicht erklärt war. In der Tat findet man in der Anleitung lediglich den Halbsatz, dass die Karte bedeutet, dass „eine Öl-Ausschöpfungsgenehmigung erteilt wird“. Jedoch wird nirgendwo erklärt, was dies bedeutet.

Glücklicherweise gibt es die Original-Anleitung von KingOil von Hasbro. Hier heißt es: „The oil depletion allowance card of $500 is a payment of $500 without regard to the number of derricks a player has erected.“ auf Deutsch: „Die Öl-Ausschöpfungsgenehmigungskarte über $500 ist eine Auszahlung von $500, umabhängig davon, wie viele Bohrtürme ein Spieler errichtet hat.“

Dies bedeutet, dass der Spieler der diese Karte zieht, immer $500 erhält, egal wie viele Bohrtürme er hat.

Damit ist auch klar, dass wir das damals immer falsch gespielt hatten, denn bei uns musste der Spieler die $500 bezahlen.

Bonus:

Bohrturm von Öl-Magnat Wisst ihr, warum der Bohrturm unterschiedliche Farben hat? In der US-Version wurden die Kosten einer erfolgreichen Bohrung nicht erwürfelt, sondern hingen davon ab, wie tief der Bohrturm in das Spielfeld eindrang:

  • war er ganz drin, so war das Bohrloch trocken, also genau umgekehrt, wie bei der deutschen Variante, wo dies bedeutet, dass man Öl gefunden hat
  • ist er eine Stufe draußen (rot), so muss der Spieler $6.000 bezahlen
  • sind zwei eine Stufe draußen (gelb), so muss der Spieler $4.000 bezahlen
  • sind alle 3 Stufe draußen (blau), so muss der Spieler $2.000 bezahlen

 

Runtastic mit Android Wear OS 1.0

Ich besitze noch die beiden ersten Uhren, die es mit Android Wear OS gab, eine Samsung Gear Live und eine LG G Watch. Seit Jahren verwende ich beim Laufen Runtastic, damit ich Puls, Geschwindigkeit und Strecke automatisch aufzeichnen kann. Mit den Uhren läuft es sich einfacher, da man den Puls einfach am Handgelenk ablesen kann, statt umständlich auf ein Handy zu schauen.

Leider unterstützt Runtastic mittlerweile nur noch Wear OS ab Version 2.0, was es für die Samsung Gear Live gar und für die LG G Watch immerhin bei XDA gibt. Eine Anfrage beim Runtastic Support ergab leider auch nur ein „kauf dir eine neue Uhr“. Aber zum Glück gibt es das Internet, in dem man alte Versionen von Android Apps finden kann. Wenn man nach Runtastic Running App Run Mileage Tracker_v8.11.2_apkpure.com.apk oder Runtastic PRO-v8.11.2_build_201812173.apk sucht, dann findet man die richtige alte Version. Hierbau auf jeden Fall die MD5-Prüfsumme vergleichen, damit man sich keinen Virus einfängt:

e752f2325264be4e965970e98e12b258 Runtastic-v8.11.2.apk
986e5012292bab985c5748a24ca8293c Runtastic PRO-v8.11.2.apk

Runtastic Update deaktivierenDamit die alte Versionauch dauerhaft bestehen bleibt, muss man in der Play Store App die automatischen Updates deaktiveren. Hierzu einfach auf die Runtastic App auswählen, dann oben rechts auf die drei Punkte tippen und den Haken bei „Autom. Update an“ entfernen.

Tesla Autopilot – Abstand einstellen

Jedes neue Tesla-Fahrzeugt wird mit dem Autopiloten ausgeliefert, der das Fahrzeug automatisch lenkt und Abstand zum vorausfahrenden Fahrzeug einhalten kann.

Autopilot

Beim Model 3 kann man mit dem rechten Rädchen am Lenkrad den Abstand in den Stufen 1 bis 7 auswählen (bei S und X druck Drehen am Hebel links vom Lenkrad). Aber was bedeuten diese? Der Abstand ist keine Angabe in Metern – dies wäre gefährlich, denn 20m Abstand sind bei 30 im Ort genug, aber auf der Autobahn mit 130 km/h sehr gefährlich. Der Abstand wird deshalb in Sekunden gerechnet, allerdings hab ich nirgendswo gefunden, was die Stufen bedeuten. Also rein ins Auto und mit der Stoppuhr gemessen, wo groß der Abstand ist. Dies ist sehr einfach: man startet die Stoppuhr, wenn das vorausfahrende Fahrzeug einen markanten Punkt passiert und stoppt sie wieder, wenn man selbst dort ist. Gut eignen sich hierfür Schatten oder Markierungen auf der Fahrbahn. Man muss allerdings darauf achten, dass man weder selbst noch das andere Fahrzeug abbremst oder beschleunigt. Ich habe dies einige Male gemacht und diese Ergebnisse erhalten:

Die Stufe 1 entspricht 1 Sekunde, also dem Minimalabstand, der außerhalb geschlossener Ortschalten vorgeschrieben ist – darunter gibt es ein Bußgeld. Sieht in der Anzeige auch schon recht knapp aus:

Abstand 1

Auf Stufe 7 sind es 2 Sekunden, also der empfohlene Abstand auf Autobahnen:

Abstand 7

Bei Stufe 6 waren es bei mir recht genau die „Tacho-halbe in Metern“.

Also keine Scheu und den Abstand ruhig auf das Maximum stellen – dann kann auch noch jemand einscheren und der Tesla muss auch nicht so scharf bremsen, wenn vor einem abgebremst wird. Ich fahre jedenfalls ab jetzt immer mit Stufe 7 – das entspannt und es ist mehr ein schnelles dahingleiten, als ein hektisches Beschleunigen und Bremsen – ist ja auch besser für die Reichweite und die Umwelt.

Wer sich jetzt gerne einen Tesla zulegen möchte und 1.500km kostenloses Superchargen dazu, der kann meinen Empfehlungscode verwenden:

https://ts.la/kurt59764

Copy & Paste mit der Maus im VIM unter Archlinux / Manjaro

Unter Linux (z.B. Ubuntu) ist man gewohnt, dass man Text mit der Maus einfach markiert und dann mit der mittleren Maustaste einfügen kann, ohne dass man Strg-C oder Strg-V drücken muss. Unter Archlinux / Manjaro geht dies im VI / VIM nicht mehr so einfach, denn die – durchaus praktische – Mausunterstützung des VI / VIM ist standardmäßig aktiviert.

Glücklicherweise gibt es einen Trick, wie man das alte Verhalten bekommt, wenn man es braucht: einfach die Umschalt (SHIFT)-Taste festhalten, während man markieren oder einfügen will. VI / VIM reicht dann alles an das umschließende Terminal weiter und es funktioniert so, wie man es von anderen Linuxen gewohnt ist.