<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:base="https://www.eric-scheibler.de/">
  <id>https://www.eric-scheibler.de/</id>
  <title>Erics Blog</title>
  <updated>2022-04-30T20:52:05Z</updated>
  <link rel="alternate" href="https://www.eric-scheibler.de/" type="text/html"/>
  <link rel="self" href="https://www.eric-scheibler.de/de/blog/feed.xml" type="application/atom+xml"/>
  <author>
    <name>Eric Scheibler</name>
    <uri>https://www.eric-scheibler.de</uri>
  </author>
  <entry>
    <id>tag:www.eric-scheibler.de,2022-04-30:/de/blog/2022/04/starte-den-android-screenreader-talkback-von-der-linux-kommandozeile/</id>
    <title type="html">Starte den Android Screenreader Talkback von der Linux Kommandozeile</title>
    <published>2022-04-30T20:52:05Z</published>
    <updated>2022-04-30T20:52:05Z</updated>
    <link rel="alternate" href="https://www.eric-scheibler.de/de/blog/2022/04/starte-den-android-screenreader-talkback-von-der-linux-kommandozeile/" type="text/html"/>
    <content type="html">&lt;p&gt;Mit dem folgenden Befehl lässt sich Androids Screenreader Talkback
barrierefrei vom Linux PC aus aktivieren:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;adb shell settings put secure enabled_accessibility_services com.google.android.marvin.talkback/com.google.android.marvin.talkback.TalkBackService&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Leider muss man auf dem Android Gerät zuvor einmal das &lt;a
href="https://developer.android.com/studio/debug/dev-options"&gt;USB
Debugging&lt;/a&gt; aktiviert und auf dem PC die &lt;a
href="https://developer.android.com/studio/command-line/adb"&gt;Android
Debug Bridge&lt;/a&gt; installiert haben. Sonst funktioniert es nicht.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>tag:www.eric-scheibler.de,2021-07-03:/de/blog/2021/07/shell-skript-fuer-adf-scanner-fujitsu-sp-1120/</id>
    <title type="html">Shell Skript für ADF Scanner Fujitsu SP-1120</title>
    <published>2021-07-03T21:10:31Z</published>
    <updated>2021-07-03T21:10:31Z</updated>
    <link rel="alternate" href="https://www.eric-scheibler.de/de/blog/2021/07/shell-skript-fuer-adf-scanner-fujitsu-sp-1120/" type="text/html"/>
    <content type="html">&lt;p&gt;Kürzlich habe ich einen &lt;a
href="https://www.fujitsu.com/global/products/computing/peripheral/scanners/business/sp1120/"&gt;Fujitsu
SP-1120&lt;/a&gt; gekauft, um meinen alten und langsamen Flachbettscanner zu
ersetzen. Der Fujitsu ist ein Scanner mit automatischem Dokumenteneinzug
(ADF) und Duplex Unterstützung. Er scannt deutlich schneller, erfasst
Vorer- und Rückseite auf einmal und liefert eine deutlich bessere
Bildqualität und somit erheblich genauere OCR Ergebnisse.&lt;/p&gt;
&lt;p&gt;Dieser Artikel enthält eine Installationsanleitung für Debian Linux
und ein einfaches Shell Skript, welches einen Scan in eine durchsuchbare
PDF Datei konvertiert.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2 id="scanner-installieren"&gt;Scanner installieren&lt;/h2&gt;
&lt;p&gt;Zuerst muss das &lt;a
href="https://imagescanner.fujitsu.com/global/dl/index-sps.html"&gt;Linux
Treiberpaket&lt;/a&gt; heruntergeladen werden. Der Treiber wird offiziell für
Ubuntu angeboten, funktioniert aber mindestens auch unter Debian.&lt;/p&gt;
&lt;p&gt;Anschließend startet man die Installation:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# dpkg -i pfusp-ubuntu18.04_2.1.1_amd64.deb&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;und erzeugt die udev Regel &lt;code&gt;99-fujitsu-sp-1120.rules&lt;/code&gt;
unter &lt;code&gt;/etc/udev/rules.d&lt;/code&gt; mit dem folgenden Inhalt:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Fujitsu ScanSnap SP-1120
ATTRS{idVendor}==&amp;quot;04c5&amp;quot;, ATTRS{idProduct}==&amp;quot;1473&amp;quot;, MODE=&amp;quot;0664&amp;quot;, GROUP=&amp;quot;scanner&amp;quot;, ENV{libsane_matched}=&amp;quot;yes&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Hat man ein anderes Scannermodell wie beispielsweise den SP-1125 oder
den SP-1130 gekauft, muss die Produkt-Id angepasst werden. Anschließend
die udev Regeln neu laden:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# udevadm control --reload
# udevadm trigger&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Schließlich noch sane (Scanner Access Now Easy) installieren und
überprüfen, ob der Scanner korrekt erkannt wird:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# apt install sane
# scanadf --list-devices&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Die Ausgabe sollte etwa wie folgt aussehen:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;device `pfusp:SP1120:001:093&amp;#39; is a FUJITSU SP1120 scanner&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="scanskript"&gt;Scanskript&lt;/h2&gt;
&lt;p&gt;Als nächstes das weiter unten angebotene Shell Skript herunterladen,
ausführbar machen und ggf. ein paar Variablen anpassen. Beispielsweise
habe ich als PDF Betrachter ein anderes Shell Skript von mir angegeben,
welches sich &lt;a
href="/de/blog/2015/04/skript-zum-extrahieren-von-text-aus-bildern-und-gescannten-pdf-dokumenten/"&gt;hier&lt;/a&gt;
findet.&lt;/p&gt;
&lt;p&gt;Einen Scan startet man anschließend wie folgt:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;adf&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Der Parameter &lt;code&gt;-p&lt;/code&gt; zeigt eine Vorschau der ersten Seite,
bevor das gesamte Dokument verarbeitet wird und &lt;code&gt;-h&lt;/code&gt; gibt
einen Überblick über alle Funktionen des Skripts.&lt;/p&gt;
&lt;p&gt;Download: &lt;a href="/downloads/adf"&gt;adf&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/bin/bash

# Script to control an ADF scanner
# - start scanning and create a single pdf file
# - with empty page and orientation detection
# - tested with Fujitsu SP-1120
#
# ... excessively borrowed from https://github.com/rocketraman/sane-scan-pdf
#
# Version: 0.1
# Date:    2021-06-16
# License: GNU General Public License
# Author:  Eric Scheibler
# E-Mail:  email [at] eric-scheibler [dot] de
# URL:     http://eric-scheibler.de/en/blog/2015/04/script-to-extract-text-from-images-and-scanned-pdf-files/
#
# Install:
#   sudo apt install imagemagick poppler-utils sane tesseract-ocr tesseract-ocr-deu tesseract-ocr-eng unpaper

OUTPUT=&amp;quot;scan.pdf&amp;quot;
TEXT_EDITOR=&amp;quot;/usr/bin/vim&amp;quot;
PDF_VIEWER=&amp;quot;$HOME/bin/ocr&amp;quot;
HELP=0
VERBOSE=0

# scanner params
DEVICE=pfusp
RESOLUTION=400
MODE=Lineart

# ocr params
OCR_LANGUAGE=deu
OCR_PREVIEW_FIRST_PAGE=0
OVERWRITE_OUTPUT_FILE=0


#####

TMP_DIR=$(mktemp -d -p &amp;quot;&amp;quot; scan.XXXXXXXXXX)
cleanup() {
    rm -rf &amp;quot;$TMP_DIR&amp;quot;
}
trap cleanup EXIT

function yes_or_no {
    while true; do
        read -p &amp;quot;$* [y/n]: &amp;quot; yn
        case $yn in
            [Yy]*) return 0  ;;
            [Nn]*) echo &amp;quot;Aborted&amp;quot; ; return  1 ;;
        esac
    done
}


# Parse command-line options
while [[ $# &amp;gt; 0 ]]; do
    case &amp;quot;$1&amp;quot; in
    -h|--help) HELP=1 ;;
    -v|--verbose) VERBOSE=1 ;;
    -o|--output) shift; OUTPUT=&amp;quot;$1&amp;quot; ;;
    -x|--device) shift; DEVICE=$1;;
    -m|--mode) shift; MODE=$1 ;;
    -r|--resolution) shift; RESOLUTION=$1 ;;
    -l|--language) shift; OCR_LANGUAGE=$1 ;;
    -p|--preview-first-page) OCR_PREVIEW_FIRST_PAGE=1 ;;
    -w|--overwrite-output-file) OVERWRITE_OUTPUT_FILE=1 ;;
    esac
    shift     # next option
done

if [[ $HELP == 1 ]]; then
    echo &amp;quot;$(basename $0) [OPTIONS]... [OUTPUT]&amp;quot;
    echo &amp;quot;&amp;quot;
    echo &amp;quot;OPTIONS&amp;quot;
    echo &amp;quot; -x, --device&amp;quot;
    echo &amp;quot;    Override scanner device name, defaulting to \&amp;quot;pfusp\&amp;quot;&amp;quot;
    echo &amp;quot; -m, --mode&amp;quot;
    echo &amp;quot;     Mode e.g. Lineart (default), Halftone, Gray, Color, etc.&amp;quot;
    echo &amp;quot; -r, --resolution&amp;quot;
    echo &amp;quot;     Resolution e.g 300 (default)&amp;quot;
    echo &amp;quot; -l, --language &amp;lt;lang&amp;gt;&amp;quot;
    echo &amp;quot;     which language to use for OCR&amp;quot;
    echo &amp;quot; -p, --preview-first-page&amp;quot;
    echo &amp;quot;     OCR first page and preview in $TEXT_EDITOR&amp;quot;
    echo &amp;quot;&amp;quot;
    echo &amp;quot;OUTPUT&amp;quot;
    echo &amp;quot; -o, --output &amp;lt;outputfile&amp;gt;&amp;quot;
    echo &amp;quot;     Output to named file default=scan.pdf&amp;quot;
    echo &amp;quot; -w, --overwrite-output-file&amp;quot;
    echo &amp;quot;     Overwrite the output pdf file, if it already exists&amp;quot;
    echo &amp;quot; -v, --verbose&amp;quot;
    exit 0
fi

if [[ $VERBOSE == 0 ]]; then
    quiet_paran=&amp;quot;--quiet&amp;quot;
    suppress_error_messages=&amp;quot;2&amp;gt; /dev/null&amp;quot;
fi

if [[ &amp;quot;$OUTPUT&amp;quot; == &amp;quot;&amp;quot; ]]; then
    echo &amp;gt;&amp;amp;2 &amp;quot;Output file must be specified. Aborting.&amp;quot;
    exit 1
fi

if [[ -f &amp;quot;$OUTPUT&amp;quot; ]]; then
    if [[ $OVERWRITE_OUTPUT_FILE == 0 ]]; then
        echo &amp;gt;&amp;amp;2 &amp;quot;Output file $OUTPUT already exists. Aborting.&amp;quot;
        exit 1
    else
        rm &amp;quot;$OUTPUT&amp;quot;
    fi
fi


echo &amp;gt;&amp;amp;2 &amp;quot;Scanning...&amp;quot;
scanadf --device-name &amp;quot;$DEVICE&amp;quot; --source Adf-duplex --resolution $RESOLUTION --mode $MODE -o $TMP_DIR/scan-%04d
if [[ $? != 0 ]]; then
    exit 1
fi
echo &amp;quot;&amp;quot;


shopt -s extglob nullglob
image_files=($TMP_DIR/scan-[0-9]*)
num_scans=${#image_files[@]}
if [[ $num_scans &amp;gt; 0 ]]; then

    if [[ $OCR_PREVIEW_FIRST_PAGE == 1 ]]; then
        echo &amp;quot;Creating preview...&amp;quot;
        preview_image_file=&amp;quot;${image_files[0]}&amp;quot;
        preview_text_file=&amp;quot;$TMP_DIR/preview_first_page.txt&amp;quot;
        # ocr
        eval tesseract $preview_image_file ${preview_text_file%.*} -l $OCR_LANGUAGE --psm 12 $suppress_error_messages
        # show
        $TEXT_EDITOR $preview_text_file
        if ! yes_or_no &amp;quot;Proceed?&amp;quot;; then
            exit 0
        fi
        # remove preview text file
        rm $preview_text_file
        echo &amp;quot;&amp;quot;
    fi

    echo &amp;quot;Processing $num_scans pages&amp;quot;
    for image_file in ${image_files[@]}; do
        echo &amp;quot;Process $(basename $image_file)&amp;quot;

        # unpaper
        eval unpaper $quiet_paran --overwrite --dpi $RESOLUTION $image_file $image_file $suppress_error_messages

        # convert to tiff
        convert -density ${RESOLUTION}x${RESOLUTION} -units PixelsPerInch $image_file ${image_file}.tiff
        rm $image_file

        # orientation detection
        orientation_result=$(eval tesseract ${image_file}.tiff - --psm 0 $suppress_error_messages) || orientation_result=
        if [[ $orientation_result == *&amp;quot;Rotate: 180&amp;quot;* ]]; then
            echo &amp;quot;Image orientation is upside down, rotate&amp;quot;
            convert -rotate 180 ${image_file}.tiff ${image_file}.tiff
        fi

        # empty page detection
        percentage_white=$(convert ${image_file}.tiff -fuzz 0% -negate -threshold 0 -negate -format &amp;quot;%[fx:100*mean]&amp;quot; info:) || percentage_white=0
        is_empty_page=$(echo &amp;quot;$percentage_white &amp;gt;= 99.8&amp;quot; | bc -l)
        if [[ $is_empty_page == 1 &amp;amp;&amp;amp; $orientation_result == &amp;quot;&amp;quot; ]]; then
            echo &amp;quot;Empty page removed&amp;quot;

        else
            eval tesseract ${image_file}.tiff $image_file -l $OCR_LANGUAGE pdf $suppress_error_messages
            rm ${image_file}.tiff
        fi

        echo &amp;quot;&amp;quot;
    done

    # rename or unite created pdf(s)
    pdf_files=($TMP_DIR/scan-[0-9]*.pdf)
    num_pdf_files=${#pdf_files[@]}
    if [[ $num_pdf_files == 1 ]]; then
        echo &amp;quot;Renaming...&amp;quot;
        mv $TMP_DIR/scan-0*.pdf &amp;quot;$OUTPUT&amp;quot;
    elif [[ $num_pdf_files &amp;gt; 1 ]]; then
        echo &amp;quot;Concatenating pdfs...&amp;quot;
        pdfunite &amp;quot;${pdf_files[@]}&amp;quot; &amp;quot;$OUTPUT&amp;quot;
    fi
fi

if [[ -f &amp;quot;$OUTPUT&amp;quot; ]]; then
    echo &amp;quot;Done.&amp;quot;
    if [[ $PDF_VIEWER != &amp;quot;&amp;quot; ]]; then
        if yes_or_no &amp;quot;Open ${OUTPUT}?&amp;quot;; then
            $PDF_VIEWER $OUTPUT
        fi
    fi
else
    echo &amp;quot;No scans found.&amp;quot;
fi
&lt;/code&gt;&lt;/pre&gt;
</content>
  </entry>
  <entry>
    <id>tag:www.eric-scheibler.de,2021-06-12:/de/blog/2021/06/einfache-und-barrierefreie-netzwerkverkehranalyse/</id>
    <title type="html">Einfache und barrierefreie Netzwerkverkehranalyse</title>
    <published>2021-06-12T12:56:47Z</published>
    <updated>2021-06-12T12:56:47Z</updated>
    <link rel="alternate" href="https://www.eric-scheibler.de/de/blog/2021/06/einfache-und-barrierefreie-netzwerkverkehranalyse/" type="text/html"/>
    <content type="html">&lt;p&gt;Während der Entwicklung einer Android App musste ich unlängst die
abgesendeten Netzwerkanfragen analysieren. Unter Android selbst kam ich
nicht weiter. Die Analyse gestaltete sich aber unter Zuhilfenahme von &lt;a
href="https://mitmproxy.org"&gt;mitmproxy&lt;/a&gt; erstaunlich einfach.&lt;/p&gt;
&lt;p&gt;Im folgenden Artikel beschreibe ich dessen Installation und die
Verwendung in Android Apps und wget.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;Grundsätzlich funktioniert das wie ein &lt;a
href="https://de.wikipedia.org/wiki/Man-in-the-Middle-Angriff"&gt;mitm
Angriff&lt;/a&gt;. Man installiert einen Proxyserver innerhalb des lokalen
Netzwerks und konfiguriert die Clients, sodass sie ihre Anfragen über
den Proxy verschicken. Ich habe mich für mitmproxy entschieden, da es
für die Linux Konsole verfügbar, einfach zu benutzen und mit
Screenreadern wie &lt;a href="https://brltty.app"&gt;brltty&lt;/a&gt; recht gut
bedienbar ist.&lt;/p&gt;
&lt;p&gt;Installation: &lt;code&gt;sudo apt install mitmproxy&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Start:
&lt;code&gt;mitmproxy --listen-host 192.168.100.100 --listen-port 22222 --save-stream-file +requests.log&lt;/code&gt;&lt;/p&gt;
&lt;h2 id="wget"&gt;Wget&lt;/h2&gt;
&lt;p&gt;Zunächst testweise eine http Anfrage:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;wget -e https_proxy=192.168.100.100:22222 http://example.org&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Diese sollte in der mitmproxy Hauptansicht auftauchen.&lt;/p&gt;
&lt;p&gt;Wer auch verschlüsselte Anfragen analysieren möchte, muss in seinem
Client das SSL Zertifikat angeben, welches mitmproxy beim ersten Start
generiert hat. Man findet es unter
&lt;code&gt;~/.mitmproxy/mitmproxy-ca-cert.pem&lt;/code&gt;. Die Datei muss auf den
Clientrechner kopiert und wie folgt verwendet werden:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;wget -e https_proxy=192.168.100.100:22222 --ca-certificate /path/to/mitmproxy-ca-cert.pem https://example.org&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Wenn alles geklappt hat, enthält die mitmproxy Hauptansicht nun zwei
Einträge. Mit den Pfeiltasten bewegt man sich durch die Liste, mit ENTER
öffnet man die Details.&lt;/p&gt;
&lt;p&gt;Leider unterstützt die UI den Hardware Konsolencursor nicht. D.h.
Screenreader lesen den ausgewählten Listeneintrag beim navigieren nicht
korrekt vor. Wer brltty einsetzt, kann allerdings die autom.
Cursorverfolgung vorübergehend deaktivieren (CAPSLOCK+ENTER) und dem
ausgewählten Eintrag manuell folgen. Selbigen erkennt man an einem
vorangestellten “&amp;gt;&amp;gt;”.&lt;/p&gt;
&lt;p&gt;Die Detailansicht besteht aus drei Tabs: Request, Response und
Details. Man wechselt entweder mit den Pfeiltasten links und rechts oder
via TAB durch die Reiter. Mich interessierten vor allem die Cookie
Header der Anfrage - daher wurde ich bereits im Requests-Tab fündig.&lt;/p&gt;
&lt;h2 id="android-app-development"&gt;Android app development&lt;/h2&gt;
&lt;p&gt;Zunächst muss das SSL Zertifikat von oben zu den Resourcen der App
hinzugefügt werden:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mkdir /path/to/android_project/app/src/main/res/raw/
cp ~/.mitmproxy/mitmproxy-ca-cert.pem /path/to/android_project/app/src/main/res/raw/mitm_proxy.pem&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Anschließend den folgenden Programmcode verwenden, um&lt;/p&gt;
&lt;ol type="1"&gt;
&lt;li&gt;den Proxy zu konfigurieren und&lt;/li&gt;
&lt;li&gt;das SSL Zertifikat für die Anfrage zu setzen.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="sourceCode" id="cb4"&gt;&lt;pre
class="sourceCode java"&gt;&lt;code class="sourceCode java"&gt;&lt;span id="cb4-1"&gt;&lt;a href="#cb4-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;import&lt;/span&gt; &lt;span class="im"&gt;org&lt;/span&gt;&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="im"&gt;example&lt;/span&gt;&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="im"&gt;project&lt;/span&gt;&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="im"&gt;BuildConfig&lt;/span&gt;&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-2"&gt;&lt;a href="#cb4-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;import&lt;/span&gt; &lt;span class="im"&gt;java&lt;/span&gt;&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="im"&gt;net&lt;/span&gt;&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="im"&gt;InetSocketAddress&lt;/span&gt;&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-3"&gt;&lt;a href="#cb4-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;import&lt;/span&gt; &lt;span class="im"&gt;java&lt;/span&gt;&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="im"&gt;net&lt;/span&gt;&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="im"&gt;Proxy&lt;/span&gt;&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-4"&gt;&lt;a href="#cb4-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;import&lt;/span&gt; &lt;span class="im"&gt;java&lt;/span&gt;&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="im"&gt;net&lt;/span&gt;&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="im"&gt;URL&lt;/span&gt;&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-5"&gt;&lt;a href="#cb4-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;import&lt;/span&gt; &lt;span class="im"&gt;java&lt;/span&gt;&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="im"&gt;security&lt;/span&gt;&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="im"&gt;KeyStore&lt;/span&gt;&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-6"&gt;&lt;a href="#cb4-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;import&lt;/span&gt; &lt;span class="im"&gt;java&lt;/span&gt;&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="im"&gt;security&lt;/span&gt;&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="im"&gt;cert&lt;/span&gt;&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="im"&gt;CertificateFactory&lt;/span&gt;&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-7"&gt;&lt;a href="#cb4-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;import&lt;/span&gt; &lt;span class="im"&gt;javax&lt;/span&gt;&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="im"&gt;net&lt;/span&gt;&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="im"&gt;ssl&lt;/span&gt;&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="im"&gt;HttpsURLConnection&lt;/span&gt;&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-8"&gt;&lt;a href="#cb4-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;import&lt;/span&gt; &lt;span class="im"&gt;javax&lt;/span&gt;&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="im"&gt;net&lt;/span&gt;&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="im"&gt;ssl&lt;/span&gt;&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="im"&gt;SSLContext&lt;/span&gt;&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-9"&gt;&lt;a href="#cb4-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;import&lt;/span&gt; &lt;span class="im"&gt;javax&lt;/span&gt;&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="im"&gt;net&lt;/span&gt;&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="im"&gt;ssl&lt;/span&gt;&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="im"&gt;TrustManagerFactory&lt;/span&gt;&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-10"&gt;&lt;a href="#cb4-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb4-11"&gt;&lt;a href="#cb4-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="bu"&gt;URL&lt;/span&gt; url &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="kw"&gt;new&lt;/span&gt; &lt;span class="bu"&gt;URL&lt;/span&gt;&lt;span class="op"&gt;(&lt;/span&gt;&lt;span class="st"&gt;&amp;quot;https://example.org&amp;quot;&lt;/span&gt;&lt;span class="op"&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-12"&gt;&lt;a href="#cb4-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="bu"&gt;String&lt;/span&gt; proxyHost &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="st"&gt;&amp;quot;192.168.100.100&amp;quot;&lt;/span&gt;&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-13"&gt;&lt;a href="#cb4-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="dt"&gt;int&lt;/span&gt; proxyPort &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="dv"&gt;22222&lt;/span&gt;&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-14"&gt;&lt;a href="#cb4-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb4-15"&gt;&lt;a href="#cb4-15" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="bu"&gt;HttpsURLConnection&lt;/span&gt; connection &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="kw"&gt;null&lt;/span&gt;&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-16"&gt;&lt;a href="#cb4-16" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="cf"&gt;if&lt;/span&gt; &lt;span class="op"&gt;(&lt;/span&gt;BuildConfig&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="fu"&gt;DEBUG&lt;/span&gt;&lt;span class="op"&gt;)&lt;/span&gt; &lt;span class="op"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-17"&gt;&lt;a href="#cb4-17" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb4-18"&gt;&lt;a href="#cb4-18" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="bu"&gt;Proxy&lt;/span&gt; proxy &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="kw"&gt;new&lt;/span&gt; &lt;span class="bu"&gt;Proxy&lt;/span&gt;&lt;span class="op"&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-19"&gt;&lt;a href="#cb4-19" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            &lt;span class="bu"&gt;Proxy&lt;/span&gt;&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="fu"&gt;Type&lt;/span&gt;&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="fu"&gt;HTTP&lt;/span&gt;&lt;span class="op"&gt;,&lt;/span&gt; &lt;span class="kw"&gt;new&lt;/span&gt; &lt;span class="bu"&gt;InetSocketAddress&lt;/span&gt;&lt;span class="op"&gt;(&lt;/span&gt;proxyHost&lt;span class="op"&gt;,&lt;/span&gt; proxyPort&lt;span class="op"&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-20"&gt;&lt;a href="#cb4-20" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    connection &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="op"&gt;(&lt;/span&gt;&lt;span class="bu"&gt;HttpsURLConnection&lt;/span&gt;&lt;span class="op"&gt;)&lt;/span&gt; url&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="fu"&gt;openConnection&lt;/span&gt;&lt;span class="op"&gt;(&lt;/span&gt;proxy&lt;span class="op"&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-21"&gt;&lt;a href="#cb4-21" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb4-22"&gt;&lt;a href="#cb4-22" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="cf"&gt;try&lt;/span&gt; &lt;span class="op"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-23"&gt;&lt;a href="#cb4-23" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;span class="bu"&gt;CertificateFactory&lt;/span&gt; cf &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="bu"&gt;CertificateFactory&lt;/span&gt;&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="fu"&gt;getInstance&lt;/span&gt;&lt;span class="op"&gt;(&lt;/span&gt;&lt;span class="st"&gt;&amp;quot;X.509&amp;quot;&lt;/span&gt;&lt;span class="op"&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-24"&gt;&lt;a href="#cb4-24" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;span class="bu"&gt;InputStream&lt;/span&gt; caInput&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-25"&gt;&lt;a href="#cb4-25" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;span class="co"&gt;// Create a KeyStore containing our trusted CAs&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-26"&gt;&lt;a href="#cb4-26" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;span class="bu"&gt;KeyStore&lt;/span&gt; keyStore &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="bu"&gt;KeyStore&lt;/span&gt;&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="fu"&gt;getInstance&lt;/span&gt;&lt;span class="op"&gt;(&lt;/span&gt;&lt;span class="bu"&gt;KeyStore&lt;/span&gt;&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="fu"&gt;getDefaultType&lt;/span&gt;&lt;span class="op"&gt;());&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-27"&gt;&lt;a href="#cb4-27" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        keyStore&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="fu"&gt;load&lt;/span&gt;&lt;span class="op"&gt;(&lt;/span&gt;&lt;span class="kw"&gt;null&lt;/span&gt;&lt;span class="op"&gt;,&lt;/span&gt; &lt;span class="kw"&gt;null&lt;/span&gt;&lt;span class="op"&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-28"&gt;&lt;a href="#cb4-28" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;span class="co"&gt;// load mitm_proxy certificate&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-29"&gt;&lt;a href="#cb4-29" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        caInput &lt;span class="op"&gt;=&lt;/span&gt; ApplicationInstance&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="fu"&gt;getContext&lt;/span&gt;&lt;span class="op"&gt;().&lt;/span&gt;&lt;span class="fu"&gt;getResources&lt;/span&gt;&lt;span class="op"&gt;().&lt;/span&gt;&lt;span class="fu"&gt;openRawResource&lt;/span&gt;&lt;span class="op"&gt;(&lt;/span&gt;R&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="fu"&gt;raw&lt;/span&gt;&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="fu"&gt;mitm_proxy&lt;/span&gt;&lt;span class="op"&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-30"&gt;&lt;a href="#cb4-30" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;span class="cf"&gt;try&lt;/span&gt; &lt;span class="op"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-31"&gt;&lt;a href="#cb4-31" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            keyStore&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="fu"&gt;setCertificateEntry&lt;/span&gt;&lt;span class="op"&gt;(&lt;/span&gt;&lt;span class="st"&gt;&amp;quot;ca1&amp;quot;&lt;/span&gt;&lt;span class="op"&gt;,&lt;/span&gt; cf&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="fu"&gt;generateCertificate&lt;/span&gt;&lt;span class="op"&gt;(&lt;/span&gt;caInput&lt;span class="op"&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-32"&gt;&lt;a href="#cb4-32" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;span class="op"&gt;}&lt;/span&gt; &lt;span class="cf"&gt;finally&lt;/span&gt; &lt;span class="op"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-33"&gt;&lt;a href="#cb4-33" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            caInput&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="fu"&gt;close&lt;/span&gt;&lt;span class="op"&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-34"&gt;&lt;a href="#cb4-34" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;span class="op"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-35"&gt;&lt;a href="#cb4-35" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;span class="co"&gt;// Create a TrustManager that trusts the CAs in our KeyStore&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-36"&gt;&lt;a href="#cb4-36" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;span class="bu"&gt;String&lt;/span&gt; tmfAlgorithm &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="bu"&gt;TrustManagerFactory&lt;/span&gt;&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="fu"&gt;getDefaultAlgorithm&lt;/span&gt;&lt;span class="op"&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-37"&gt;&lt;a href="#cb4-37" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;span class="bu"&gt;TrustManagerFactory&lt;/span&gt; tmf &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="bu"&gt;TrustManagerFactory&lt;/span&gt;&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="fu"&gt;getInstance&lt;/span&gt;&lt;span class="op"&gt;(&lt;/span&gt;tmfAlgorithm&lt;span class="op"&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-38"&gt;&lt;a href="#cb4-38" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        tmf&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="fu"&gt;init&lt;/span&gt;&lt;span class="op"&gt;(&lt;/span&gt;keyStore&lt;span class="op"&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-39"&gt;&lt;a href="#cb4-39" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;span class="co"&gt;// Create an SSLContext that uses our TrustManager&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-40"&gt;&lt;a href="#cb4-40" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;span class="bu"&gt;SSLContext&lt;/span&gt; sslContext &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="bu"&gt;SSLContext&lt;/span&gt;&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="fu"&gt;getInstance&lt;/span&gt;&lt;span class="op"&gt;(&lt;/span&gt;&lt;span class="st"&gt;&amp;quot;TLS&amp;quot;&lt;/span&gt;&lt;span class="op"&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-41"&gt;&lt;a href="#cb4-41" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        sslContext&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="fu"&gt;init&lt;/span&gt;&lt;span class="op"&gt;(&lt;/span&gt;&lt;span class="kw"&gt;null&lt;/span&gt;&lt;span class="op"&gt;,&lt;/span&gt; tmf&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="fu"&gt;getTrustManagers&lt;/span&gt;&lt;span class="op"&gt;(),&lt;/span&gt; &lt;span class="kw"&gt;null&lt;/span&gt;&lt;span class="op"&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-42"&gt;&lt;a href="#cb4-42" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        connection&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="fu"&gt;setSSLSocketFactory&lt;/span&gt;&lt;span class="op"&gt;(&lt;/span&gt;sslContext&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="fu"&gt;getSocketFactory&lt;/span&gt;&lt;span class="op"&gt;());&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-43"&gt;&lt;a href="#cb4-43" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="op"&gt;}&lt;/span&gt; &lt;span class="cf"&gt;catch&lt;/span&gt; &lt;span class="op"&gt;(&lt;/span&gt;&lt;span class="bu"&gt;Exception&lt;/span&gt; e&lt;span class="op"&gt;)&lt;/span&gt; &lt;span class="op"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-44"&gt;&lt;a href="#cb4-44" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;span class="bu"&gt;System&lt;/span&gt;&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="fu"&gt;out&lt;/span&gt;&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="fu"&gt;println&lt;/span&gt;&lt;span class="op"&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-45"&gt;&lt;a href="#cb4-45" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                &lt;span class="bu"&gt;String&lt;/span&gt;&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="fu"&gt;format&lt;/span&gt;&lt;span class="op"&gt;(&lt;/span&gt;&lt;span class="st"&gt;&amp;quot;Cert exception: &lt;/span&gt;&lt;span class="sc"&gt;%1$s&lt;/span&gt;&lt;span class="st"&gt;&amp;quot;&lt;/span&gt;&lt;span class="op"&gt;,&lt;/span&gt; e&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="fu"&gt;getMessage&lt;/span&gt;&lt;span class="op"&gt;()));&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-46"&gt;&lt;a href="#cb4-46" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="op"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-47"&gt;&lt;a href="#cb4-47" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb4-48"&gt;&lt;a href="#cb4-48" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="op"&gt;}&lt;/span&gt; &lt;span class="cf"&gt;else&lt;/span&gt; &lt;span class="op"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-49"&gt;&lt;a href="#cb4-49" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    connection &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="op"&gt;(&lt;/span&gt;&lt;span class="bu"&gt;HttpsURLConnection&lt;/span&gt;&lt;span class="op"&gt;)&lt;/span&gt; url&lt;span class="op"&gt;.&lt;/span&gt;&lt;span class="fu"&gt;openConnection&lt;/span&gt;&lt;span class="op"&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-50"&gt;&lt;a href="#cb4-50" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="op"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Alternativ kann man den Proxy auch systemweit setzen. Dann muss man
das Zertifikat in den Android Einstellungen, Abschnitt Sicherheit
hinzufügen. Leider berücksichtigen nicht alle Apps die systemweite
Proxyeinstellung - z.B. Browser bringen gern ihre eigene Konfiguration
mit.&lt;/p&gt;
&lt;p&gt;Wer nur die Anfragen seiner eigenen App analysieren möchte, sollte
daher den oben beschriebenen, direkten Weg einschlagen - das ist
zumindest meiner Erfahrung nach deutlich unkomplizierter.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>tag:www.eric-scheibler.de,2020-05-02:/de/blog/2020/05/kontoeroeffnung-online-identitaetsnachweis-im-jahr-2020/</id>
    <title type="html">Kontoeröffnung - Online-Identitätsnachweis im Jahr 2020</title>
    <published>2020-05-02T10:40:33Z</published>
    <updated>2020-05-02T10:40:33Z</updated>
    <link rel="alternate" href="https://www.eric-scheibler.de/de/blog/2020/05/kontoeroeffnung-online-identitaetsnachweis-im-jahr-2020/" type="text/html"/>
    <content type="html">&lt;p&gt;Vor kurzem habe ich ein Konto bei einer Bank eröffnet und dabei
unfreiwillig die Methoden zur Online-Identifikation ausprobiert. Im
Folgenden möchte ich meine Erfahrungen kurz zusammenfassen und dabei vor
allem auf Usability und Accessibility eingehen.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2 id="inhaltsverzeichnis"&gt;Inhaltsverzeichnis&lt;/h2&gt;
&lt;ol type="1"&gt;
&lt;li&gt;&lt;a href="#ausgangslage"&gt;Ausgangslage&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#videoident"&gt;VideoIdent&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#eid-des-personalausweises"&gt;eID des
Personalausweises&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#fazit"&gt;Fazit&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="ausgangslage"&gt;Ausgangslage&lt;/h2&gt;
&lt;p&gt;Ich habe Mitte April 2020 ein Konto bei einer sog. Internetbank
eröffnet. Um diesen Prozess abschließen zu können, muss man sich in
Deutschland einer unpersönlichen &lt;a
href="https://de.wikipedia.org/wiki/Legitimationspr%C3%BCfung"&gt;Legitimationsprüfung&lt;/a&gt;
unterziehen.&lt;/p&gt;
&lt;p&gt;Bisher hatte ich dafür das &lt;a
href="https://de.wikipedia.org/wiki/Postident"&gt;PostIdent Verfahren&lt;/a&gt;
eingesetzt - d.h. man geht mit dem Personalausweis zu einer Post und
bestätigt dort seine Identität. Das war zwar etwas unpraktisch, dafür
aber recht barrierearm, da der Postangestellte die meiste Arbeit für
mich getan hat.&lt;/p&gt;
&lt;p&gt;Die Internetbank bot allerdings kein PostIdent in der Filiale an,
sondern ließ mir die Wahl zwischen VideoIdent und Ausweisen mittels eID
des Personalausweises. Wobei an dieser Stelle anzumerken ist, dass beide
Verfahren von mehreren Firmen angeboten werden. Die Post bietet neben
PostIdent in ihren Filialen beispielsweise auch VideoIdent und ausweisen
mittels eID an.&lt;/p&gt;
&lt;p&gt;Die folgenden Abschnitte beschreiben daher vor allem die Umsetzung
der Verfahren durch die, von der Bank ausgewählten Dienstleister.
Einiges lässt sich allerdings durchaus auch verallgemeinern.&lt;/p&gt;
&lt;h2 id="videoident"&gt;VideoIdent&lt;/h2&gt;
&lt;p&gt;Ich habe meinen Personalausweis vor 9 Jahren bekommen, die eID zur
Identifikation mangels Einsatzzweck allerdings nie ausprobiert. Daher
fiel meine Wahl auf VideoIdent. Man benötigt dafür lediglich ein
Smartphone oder ein Notebook bzw. einen PC mit einer Webcam.&lt;/p&gt;
&lt;p&gt;Für das VideoIdent Verfahren wurde die &lt;a
href="https://www.idnow.io"&gt;IDnow GmbH&lt;/a&gt; beauftragt. Ich startete die
Legitimationsprüfung, wurde auf die Seite von IDnow weitergeleitet und
bekam dort den Link zu ihrer Android App und einen Transaktionscode
angezeigt. Nachdem ich die App installiert und den Code eingegeben habe,
wurde ein Video Chat aufgebaut und ich wartete auf einen freien
Mitarbeiter.&lt;/p&gt;
&lt;p&gt;So weit, so gut. Was muss man nun tun?&lt;/p&gt;
&lt;p&gt;Man muss sein Gesicht und den Personalausweis in die Kamera halten
und ein paar Angaben über sich laut aussprechen. Darunter fällt auch die
Personalausweisnummer und hier fingen die Probleme an.&lt;/p&gt;
&lt;p&gt;Dem Gegenüber fiel natürlich sehr schnell auf, dass ich blind bin, da
ich nicht direkt in die Frontkamera geschaut habe. Daraufhin wurde mir,
noch bevor die Legitimationsprüfung so richtig starten konnte, gesagt,
dass ich nicht berechtigt sei, das VideoIdent Verfahren zu nutzen. Als
Grund wurde angeführt, dass man die Ausweisnummer mit den eigenen Augen
ab- und vorlesen können muss.&lt;/p&gt;
&lt;p&gt;Ich hatte die Ausweisnummer wohl gemerkt jederzeit griffbereit und
hätte sie:&lt;/p&gt;
&lt;ol type="1"&gt;
&lt;li&gt;aus dem Gedächtnis rezitieren können&lt;/li&gt;
&lt;li&gt;von einem Stück Papier ablesen können, auf das ich sie mir in
Blindenschrift notiert habe&lt;/li&gt;
&lt;li&gt;von einer Braillezeile ablesen können, die an ein Notebook
angeschlossen ist&lt;/li&gt;
&lt;li&gt;von einer Sprachausgabe vorlesen lassen und sie selbst wiederholen
können&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Auf meine vorgebrachten Lösungsvorschläge ging der Mitarbeiter von
IDnow jedoch gar nicht ein und beendete das Gespräch. Ich probierte es
dann noch einmal, bekam einen anderen Menschen zugelost, bekam aber die
gleiche Antwort. Das lässt darauf schließen, dass IDnow diese
Anforderungen vorgibt und der einzelne Mensch diese penibel umsetzen
muss, wenn er keinen Ärger von seinen Vorgesetzten bekommen möchte.&lt;/p&gt;
&lt;p&gt;Ich beschwerte mich dann per E-Mail beim IDnow Support und bekam die
folgende Antwort:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Sie können versichert sein, dass wir Ihr Feedback sehr ernst nehmen.
Unsere Mitarbeiter werden regelmäßig durch unser internes Trainerteam
geschult zudem führen wir Qualitätskontrollen der Gespräche durch. Sehr
gerne werden wir auch mit den Mitarbeitern ein Coaching bezüglich Ihres
Feedbacks durchführen.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So weit, so üblich, wenn man Beschwerden dieser Art vorbringt. Was
genau geschult werden soll und ob sich an der Praxis der Diskriminierung
Blinder etwas ändern wird, kann man aus diesen Statement nicht ablesen.
Und selbst wenn: Wie überzeuge ich dann das Gegenüber im Video Chat von
dem Richtungswechsel?&lt;/p&gt;
&lt;p&gt;In der Antwort des Supports fanden sich freundlicherweise auch noch
die Anforderungen der &lt;a
href="https://de.wikipedia.org/wiki/Bafin"&gt;BaFin&lt;/a&gt; an Blinde und
VideoIdent, denn die gibt es tatsächlich:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Der User muss die Ausweisnummer selbst vorlesen oder aufs Band
sprechen. Eine zweite Person darf bei diesem Prozessschritt als
Hilfestellung agieren und die Ausweisnummer zur Wiederholung der zu
identifizierenden Person vorsagen. Der zu identifizierende User muss
diese dann wiederholen. Auch bei den Aufnahmen der Sicherheitsmerkmale
oder der Eingabe des Idents-Codes darf Hilfe durch eine weitere Person
angenommen werden&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Nirgendwo wird erwähnt, dass das Vorlesen ausschließlich durch die
eigenen Augen zu erfolgen hat. Die BaFin erlaubt hingegen sogar eine
Hilfsperson, die die Nummer vorspricht und der Blinde wiederholt.&lt;/p&gt;
&lt;p&gt;Meine Methode 4 ist besonders nahe an den BaFin Ausnahmen, nur dass
die Hilfsperson kein Mensch sondern ein Computer ist. 1 und 2
funktionieren ganz ohne zusätzliche Technik. Alle vier Varianten
erfüllen meiner Meinung nach die BaFin Vorgaben.&lt;/p&gt;
&lt;p&gt;Blindheit allein kann also nicht von der BaFin als
Ausschlusskriterium gemeint gewesen sein. Aber wenn die Firma nicht
möchte, dann möchte sie nicht. Da lässt sich kurz- bis mittelfristig
nichts ändern. Da ich die Kontoeröffnung aber ganz gern abschließen
wollte, entschied ich mich, das zweite angebotene Verfahren
auszuprobieren.&lt;/p&gt;
&lt;h2 id="eid-des-personalausweises"&gt;eID des Personalausweises&lt;/h2&gt;
&lt;p&gt;Für die Legitimationsprüfung mittels Personalausweis beauftragte die
Bank die &lt;a href="https://authada.de"&gt;AUTHADA GmbH&lt;/a&gt;. Man benötigt
ebenfalls ein Smartphone und seinen Personalausweis mit freigeschalteter
&lt;a
href="https://de.wikipedia.org/wiki/Personalausweis_%28Deutschland%29#eID-Funktion"&gt;eID&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Ich hatte bereits damals bei der Abholung des Ausweises im Bürgerbüro
die eID freigeschaltet und die PIN gesetzt. Leider konnte ich mich an
den Vorgang nicht und an die PIN erst recht nicht mehr erinnern. Deshalb
musste ich erneut zum Bürgerbüro, um die PIN zurückzusetzen und eine
neue zu vergeben.&lt;/p&gt;
&lt;p&gt;Dort stellte sich heraus, dass das Modul zur PIN-Eingabe
ausschließlich ein Touchscreen ist - ein selbstständiges Setzen der PIN
ist für Blinde also nicht möglich. Da wurde bei der Beschaffung wie
üblich nicht auf Barrierefreiheit geachtet. Immerhin besitzt das
EC-Kartenterminal, mit dem ich die anfallende Gebühr entrichten muss,
noch eine Tastatur - da frage ich mich schon lange, wann man auch dort
auf Touchscreens umstellt.&lt;/p&gt;
&lt;p&gt;Nun konnte der eigentliche Legitimationsprüfungsvorgang starten. Dazu
wurde ich von der Bankwebseite auf die Webseite von AUTHADA
weitergeleitet. Dort folgte zunächst die Aufforderung, ihre App aus dem
Play Store zu laden. Bis dahin dachte ich noch, ich würde den weiteren
Vorgang mit der &lt;a
href="https://de.wikipedia.org/wiki/AusweisApp2"&gt;Ausweisapp2&lt;/a&gt;
durchführen, die zwar auch nicht barrierefrei aber wenigstens benutzbar
ist.&lt;/p&gt;
&lt;p&gt;Statt dessen sollte ich mit der AUTHADA App nun einen QR Code von
meinem Notebookbildschirm scannen. Dieser Vorgang ist für Blinde zwar
nicht übermäßig komfortabel aber durchaus durchführbar. In der
Vergangenheit habe ich schon mehrere QR Codes gescannt und dafür max.
ein paar Sekunden benötigt.&lt;/p&gt;
&lt;p&gt;Mit diesem QR Code war das allerdings nicht zu schaffen. Der Code bot
einen extrem schlechten Kontrast und ihm fehlte eine neutrale Umrandung.
Nach einigem probieren habe ich ihn heruntergeladen, in einem
Bildbetrachterprogramm geöffnet und reingezoomt - gleiches Ergebnis.
Auch Ausdrucken und vom Papier scannen funktionierte nicht.&lt;/p&gt;
&lt;p&gt;An diesem Punkt kam ich nicht weiter und musste auf eine Assistenz
zurückgreifen. Das passiert mir im IT-Alltag sonst recht selten. Auch
die Assistenz brauchte mehrere Minuten und eine ruhige Hand, bis der
Code endlich von der App erkannt worden war. Hier könnte man durch die
Erzeugung eines kontrastreichen Codes inkl. Umrandung viel für die
Usability tun.&lt;/p&gt;
&lt;p&gt;Anschließend folgte der NFC Scan des Ausweises und die PIN Eingabe.
Dazu hält man den Ausweis an die Geräterückseite des Smartphones und
gibt seine sechsstellige PIN ein. Das war der einzige Teil des
Verfahrens, der tatsächlich reibungslos funktioniert hat.&lt;/p&gt;
&lt;p&gt;Als nächstes mussten Vorder- und Rückseite des Ausweises fotografiert
werden. Warum diese Fotos zusätzlich zur bereits erfolgten
elektronischen Verifikation benötigt werden, bleibt unklar. Vielleicht
verlangt die BaFin diese Fotos, vielleicht auch nicht. Es wird
jedenfalls nicht erläutert und die &lt;a
href="https://authada.de/datenschutzerklaerung"&gt;Datenschutzerklärung&lt;/a&gt;
ist so allgemein gehalten, dass sie beinahe zu jedem Unternehmen passen
könnte.&lt;/p&gt;
&lt;p&gt;Unabhängig von der Datenschutzproblematik hätte ich spätestens hier
jemanden benötigt, der mir beim Anfertigen der Fotos hilft.&lt;/p&gt;
&lt;p&gt;Nach dem erfolgreichem Foto-Upload generierte die App eine TAN, die
auf der zu Beginn geöffneten Webseite eingegeben werden muss. Es passte
zum bisherigen Gesamteindruck, dass diese TAN in einem Bild ohne
Bildbeschreibung daher kam und vom Screenreader deshalb nicht vorgelesen
werden konnte.&lt;/p&gt;
&lt;p&gt;Damit war der Legitimationsprozess aber noch nicht abgeschlossen,
denn der “Absenden” Button neben dem TAN Eingabefeld lies sich nicht
klicken. Unklar. Schließlich erkannte meine Assistenz, dass man neben
dem App-aus-Play-Store-laden Hinweis noch einen Haken zur Bestätigung
setzen muss. Diese Checkbox war für den Screenreader gar nicht zu
erkennen und auch visuell offenbar nicht übermäßig markant.&lt;/p&gt;
&lt;p&gt;Nachdem auch das geklärt war, konnte meine Assistenz den “Absenden”
Button anklicken und den Legitimationsprozess erfolgreich abschließen.
Auch der Button war mit der Tastatur nicht erreichbar aber das spielt
nach so vielen Problemen schon keine große Rolle mehr.&lt;/p&gt;
&lt;h2 id="fazit"&gt;Fazit&lt;/h2&gt;
&lt;p&gt;Beide Verfahren waren nicht barrierefrei. Leider, so muss man sagen,
nicht mal ansatzweise.&lt;/p&gt;
&lt;p&gt;IDnow schließt mich grundsätzlich von VideoIdent aus - da hätte nicht
einmal eine Assistenz etwas daran geändert. Ein anderer VideoIdent
Dienst mag das besser lösen aber diesbezüglich hat der Kunde nicht die
Wahl, sondern muss nehmen, was der, bei dem er sich identifizieren
möchte, ausgewählt hat.&lt;/p&gt;
&lt;p&gt;Die AUTHADA eID Umsetzung scheitert gleich an mehreren Stellen.
Bedenkt man, dass sich das legitimieren mit dem Personalausweis
theoretisch an die gesamte Bevölkerung richtet, würde ich hier eine
Verpflichtung zu Usability und Accessibility erwarten. Das zöge im
Idealfall Nutzertests mit einem repräsentativen Teil der Bevölkerung
nach sich. Was am Ende schief gegangen ist, lässt sich von Außen nicht
nachvollziehen - es ist jedenfalls alles andere als praxistauglich und
komfortabel.&lt;/p&gt;
&lt;p&gt;Dass das lokale Bürgerbüro Touchscreens zur PIN-Verwaltung eingekauft
hat, ist dann schon beinahe folgerichtig.&lt;/p&gt;
&lt;p&gt;Meine Legitimationspräferenzen sehen also wie folgt aus:&lt;/p&gt;
&lt;ol type="1"&gt;
&lt;li&gt;PostIdent in der Filiale: Der Weg zur Post ist für mich relativ kurz
und problemlos zurückzulegen. Alles weitere erledigt die Post.&lt;/li&gt;
&lt;li&gt;VideoIdent: Lässt sich von zu Hause mit geringen
Hardwareanforderungen durchführen und wäre noch einen Versuch wert,
solange das Unternehmen nicht IDnow heißt.&lt;/li&gt;
&lt;li&gt;eID: Zumindest die Umsetzung von AUTHADA ist eine absolute Notlösung
und nur mit Assistenz durchzuführen. In meinem Fall war es tatsächlich
die einzige Möglichkeit, sich zu legitimieren. Ich würde sie daher
zumindest nicht komplett ausschließen. Vielleicht haben andere Anbieter
sich bei Usability und Accessibility auch mehr Mühe gegeben.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Von der Bank würde ich mir wünschen, dass sie ein möglichst breites
Spektrum an Legitimationsverfahren anbietet und im Idealfall auch
Barrierefreiheit ein Kriterium für die Anbieterauswahl darstellt. Statt
dessen war die Antwort dort ein Schulterzucken.&lt;/p&gt;
&lt;p&gt;Ich mache mir bezüglich der Online-Funktionen des Personalausweises
für die Zukunft auch kaum Hoffnung auf Besserung. Schon einen einzelnen
Anbieter zur Barrierefreiheit zu verpflichten, erscheint unrealistisch
aber mehrere? Wer sollte das kontrollieren? Was passiert bei
Verstößen?&lt;/p&gt;
&lt;p&gt;Da bleibt nur der Trost, dass man diese Funktionen nicht allzu häufig
in Anspruch nehmen muss.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>tag:www.eric-scheibler.de,2018-12-29:/de/blog/2018/12/mpd-aktuellen-titel-ansagen/</id>
    <title type="html">MPD - Aktuellen Titel ansagen</title>
    <published>2018-12-29T02:14:13Z</published>
    <updated>2018-12-29T02:14:13Z</updated>
    <link rel="alternate" href="https://www.eric-scheibler.de/de/blog/2018/12/mpd-aktuellen-titel-ansagen/" type="text/html"/>
    <content type="html">&lt;p&gt;Auf meinem Server läuft ein &lt;a
href="https://www.musicpd.org"&gt;MPD&lt;/a&gt;, welcher für die Musikwiedergabe
zuständig ist. Das folgende Skript für den Mac sagt den aktuellen Titel
nach jedem Titelwechsel an. Es verwendet die bei Mac OS vorinstallierte
TTS und versucht, die Sprache des Songtitels zu ermitteln.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;Download: &lt;a
href="/downloads/mpd_speak_current_song.py"&gt;mpd_speak_current_song.py&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/Users/eric/.virtualenvs/speak_current_song/bin/python
# -*- coding: utf-8 -*-

# mpd: speak currently playing song after it has changed
#
# This script uses the mpd client mpc to watch for song changes on the running mpd server and speaks them aloud
# Song title language detection with https://github.com/Mimino666/langdetect
# It&amp;#39;s written for Mac OS but could be easily adapted to Linux as well (change tts_command variable)
#
# author: Eric Scheibler &amp;lt;email@eric-scheibler.de&amp;gt;
# Date: 2019-03-29
# Version: 0.2
#
# Installation requirements:
#   brew install mpc
#   pip install langdetect
#
# Usage: Must run in background for example within a screen session
#   ./mpd_speak_current_song

# configuration
#
# adapt the following params to your needs
# leave them blank if not required
#
# mpd host and port
mpd_host = &amp;quot;192.168.2.11&amp;quot;
mpd_port = &amp;quot;6600&amp;quot;
# audio output device
# Mac: get all devices with: say -a &amp;#39;?&amp;#39;
audio_output_device = &amp;quot;&amp;quot;
# voice table
# Mac: get all installed voices with: say -v &amp;#39;?&amp;#39;
voices = {
        &amp;quot;de&amp;quot; : {&amp;quot;name&amp;quot;:&amp;quot;Yannick&amp;quot;, &amp;quot;rate&amp;quot;:280},
        &amp;quot;en&amp;quot; : {&amp;quot;name&amp;quot;:&amp;quot;Samantha&amp;quot;, &amp;quot;rate&amp;quot;:200},
        &amp;quot;fr&amp;quot; : {&amp;quot;name&amp;quot;:&amp;quot;Thomas&amp;quot;, &amp;quot;rate&amp;quot;:200},
        # default: different German voice
        &amp;quot;default&amp;quot; : {&amp;quot;name&amp;quot;:&amp;quot;Anna&amp;quot;, &amp;quot;rate&amp;quot;:240}
        }


import subprocess, sys, threading
from langdetect import detect, DetectorFactory
from langdetect.lang_detect_exception import LangDetectException


# construct commands
#
# control mpd with: mpc ...
mpc_command = [&amp;quot;mpc&amp;quot;, &amp;quot;--quiet&amp;quot;]
if mpd_host:
    mpc_command += [&amp;quot;--host&amp;quot;, mpd_host]
if mpd_port:
    mpc_command += [&amp;quot;--port&amp;quot;, mpd_port]
# speak with: say ...
tts_command = [&amp;quot;say&amp;quot;]
if audio_output_device:
    tts_command += [&amp;quot;-a&amp;quot;, audio_output_device]


class SpeakCurrentSong(threading.Thread):

    def __init__(self, song, voice):
        threading.Thread.__init__(self)
        self.song = song
        self.voice = voice
        # class variables
        self.process = None
        self.finished = False
        self.killed = False

    def run(self):
        # pause playback
        if not self.killed:
            self.execute(mpc_command + [&amp;quot;pause&amp;quot;])
        # announce new song
        if not self.killed:
            self.execute(tts_command + [&amp;quot;-v&amp;quot;, self.voice.get(&amp;quot;name&amp;quot;), &amp;quot;-r&amp;quot;, str(self.voice.get(&amp;quot;rate&amp;quot;)), self.song])
        # resume playback
        if not self.killed:
            self.execute(mpc_command + [&amp;quot;play&amp;quot;])
        self.finished = True

    def execute(self, command):
        self.process = subprocess.Popen(command)
        self.process.wait()

    def kill(self):
        self.process.kill()
        self.killed = True


def main():
    speakCurrentSongThread = None
    # force deterministic language detection results
    DetectorFactory.seed = 0
    while True:
        try:
            # wait for change of current song
            current_song = subprocess.check_output(mpc_command + [&amp;quot;current&amp;quot;, &amp;quot;--wait&amp;quot;], encoding=&amp;#39;UTF-8&amp;#39;).strip()
        except subprocess.CalledProcessError as e:
            # could not get current song
            error_message = &amp;quot;Error: Could not get the current song&amp;quot;
            subprocess.Popen(tts_command + [&amp;quot;-v&amp;quot;, &amp;quot;Alex&amp;quot;, error_message])
            print(&amp;#39;{}: {}&amp;#39;.format(error_message, e))
            sys.exit(1)
        else:
            # detect language and get tts voice
            voice = voices.get(&amp;quot;default&amp;quot;)
            try:
                language = detect(current_song)
            except LangDetectException as e:
                pass
            else:
                if language in voices.keys():
                    voice = voices.get(language)
            # start SpeakCurrentSong thread
            if speakCurrentSongThread:
                # kill old thread if necessary
                if not speakCurrentSongThread.finished:
                    speakCurrentSongThread.kill()
                speakCurrentSongThread.join()
            speakCurrentSongThread = SpeakCurrentSong(current_song, voice)
            speakCurrentSongThread.start()
            # log to stdout
            print(&amp;#39;{}.{}: {}&amp;#39;.format(language, voice.get(&amp;quot;name&amp;quot;), current_song))


if __name__ == &amp;quot;__main__&amp;quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
</content>
  </entry>
  <entry>
    <id>tag:www.eric-scheibler.de,2018-02-10:/de/blog/2018/02/clipboardtospeech-android-app-liest-den-inhalt-der-zwischenablage-vor/</id>
    <title type="html">ClipboardToSpeech - Android App liest den Inhalt der Zwischenablage vor</title>
    <published>2018-02-10T09:43:24Z</published>
    <updated>2018-02-10T09:43:24Z</updated>
    <link rel="alternate" href="https://www.eric-scheibler.de/de/blog/2018/02/clipboardtospeech-android-app-liest-den-inhalt-der-zwischenablage-vor/" type="text/html"/>
    <content type="html">&lt;p&gt;Diese Android App überwacht die Zwischenablage und liest neu
hinzugefügten Text automatisch vor.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;Weitere Features sind:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Verwendet das Standardsprachausgabemodul&lt;/li&gt;
&lt;li&gt;Beinhaltet eine History der letzten Einträge der Zwischenablage&lt;/li&gt;
&lt;li&gt;Startet automatisch nach dem Booten des Geräts&lt;/li&gt;
&lt;li&gt;Erstellt eine Low-Priority Notification in der
Benachrichtigungsleiste wenn der Hintergrunddienst aktiv ist&lt;/li&gt;
&lt;li&gt;Benötigt mindestens Android Version 4.1 und ist kompatibel zu 8.0
und 8.1&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Git Repository klonen:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git clone https://github.com/scheibler/ClipboardToSpeech/&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Die bereits kompilierte Programmversion kann wie folgt installiert
werden:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;adb install app/src/main/misc/releases/ClipboardToSpeech_vx.x.x.apk&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Alternativ von Hand bauen:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;./gradlew build&lt;/code&gt;&lt;/pre&gt;
</content>
  </entry>
  <entry>
    <id>tag:www.eric-scheibler.de,2016-07-03:/de/blog/2016/07/laptop-bildschirm-unter-linux-abschalten/</id>
    <title type="html">Laptop Bildschirm unter Linux abschalten</title>
    <published>2016-07-03T20:29:42Z</published>
    <updated>2016-07-03T20:29:42Z</updated>
    <link rel="alternate" href="https://www.eric-scheibler.de/de/blog/2016/07/laptop-bildschirm-unter-linux-abschalten/" type="text/html"/>
    <content type="html">&lt;p&gt;Dieser Artikel beschreibt, wie der Laptop Bildschirm von Lenovo
Thinkpads unter Linux dauerhaft abgeschaltet werden kann. Dies spart im
Akkubetrieb nicht nur eine Menge Strom sondern schützt auch die
Privatsphäre. Benötigt root Rechte. Getestet mit einem Thinkpad T450
unter Debian Stretch.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;Das folgende Skript unter /usr/local/sbin/monitor abspeichern:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/bin/bash
if [ -f /tmp/screen_stays_active ]; then
    echo 0 &amp;gt; /sys/class/backlight/intel_backlight/brightness
    rm /tmp/screen_stays_active
    echo &amp;quot;Disable screen&amp;quot;
else
    echo 850 &amp;gt; /sys/class/backlight/intel_backlight/brightness
    touch /tmp/screen_stays_active
    echo &amp;quot;Restore screen brightness&amp;quot;
fi&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;und ausführbar machen:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;chmod +x /usr/local/sbin/monitor&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Da sich der Monitor in unregelmäßigen Zeitabständen wieder
einschaltet, muss die Helligkeit alle 20 Sekunden auf 0 gesetzt werden.
Ich habe dies mit drei Cronjobs des root Users gelöst. Ein weiteres
Skript mit einer endlosen While-Loop wäre auch denkbar.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# crontab -e

*  *   *   *   *   if [ ! -f /tmp/screen_stays_active ]; then echo 0 &amp;gt; /sys/class/backlight/intel_backlight/brightness; fi
*  *   *   *   *   sleep 20 &amp;amp;&amp;amp; if [ ! -f /tmp/screen_stays_active ]; then echo 0 &amp;gt; /sys/class/backlight/intel_backlight/brightness; fi
*  *   *   *   *   sleep 40 &amp;amp;&amp;amp; if [ ! -f /tmp/screen_stays_active ]; then echo 0 &amp;gt; /sys/class/backlight/intel_backlight/brightness; fi&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Es ist zu beachten, dass der Bildschirm nach der Installation der
Crontab sofort komplett abgeschaltet wird. Dies gilt natürlich auch noch
nach einem Neustart des Systems.&lt;/p&gt;
&lt;p&gt;Der Bildschirm lässt sich anschließend mittels
&lt;code&gt;sudo monitor&lt;/code&gt; ein- und auch wieder abschalten. Wer dafür
nicht jedes Mal ein Passwort eingeben möchte, kann in der sudoers Datei
eine Ausnahme definieren. Dazu den &lt;code&gt;visudo&lt;/code&gt; Befehl als root
ausführen und die folgende Zeile anhängen:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;MY_USER_NAME ALL = NOPASSWD: /usr/local/sbin/monitor&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Bei der Gelegenheit lässt sich auch gleich unter root noch ein
Cronjob anlegen, der die Tastaturbeleuchtung im Minutentakt
deaktiviert:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# crontab -e

*  *   *   *   *   echo 0 &amp;gt; /sys/class/leds/tpacpi::kbd_backlight/brightness&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Weitere Informationen gibt es unter &lt;a
href="http://www.thinkwiki.org/wiki/ThinkLight"
class="uri"&gt;http://www.thinkwiki.org/wiki/ThinkLight&lt;/a&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>tag:www.eric-scheibler.de,2016-05-06:/de/blog/2016/05/android-app-tactileclock-vibriert-die-aktuelle-uhrzeit/</id>
    <title type="html">Android App Tactile Clock vibriert die aktuelle Uhrzeit</title>
    <published>2016-05-06T21:45:20Z</published>
    <updated>2016-05-06T21:45:20Z</updated>
    <link rel="alternate" href="https://www.eric-scheibler.de/de/blog/2016/05/android-app-tactileclock-vibriert-die-aktuelle-uhrzeit/" type="text/html"/>
    <content type="html">&lt;p&gt;Diese Android App gibt die aktuelle Uhrzeit als Vibrationspattern
aus. Dazu muss das Display abgeschaltet sein und der Power-Button
zweimal kurz hintereinander betätigt werden.&lt;/p&gt;
&lt;p&gt;Die App ist freie Software. Die Links zum Play Store, einem F-Droid
Repository und dem Quellcode finden sich weiter unten auf dieser
Seite.&lt;/p&gt;
&lt;p&gt;Tactile Clock wurde zuletzt am 18.01.2026 aktualisiert.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2 id="funktionsweise"&gt;Funktionsweise&lt;/h2&gt;
&lt;p&gt;Mache einen Doppelklick auf den Power-Button. Das erfordert etwas
Übung, da es nicht zu schnell geschehen darf. Anderenfalls registriert
das Smartphone nur einen einzelnen Klick und schaltet stattdessen den
Bildschirm ein.&lt;/p&gt;
&lt;p&gt;Die Uhrzeitvibration ist wie folgt aufgebaut:&lt;/p&gt;
&lt;p&gt;Eine kurze Vibration steht für die Ziffer 1 und eine lange Vibration
für die Ziffer 5. Die Ziffer 2 wird somit durch zwei aufeinander
folgende kurze Vibrationen, die 6 durch eine lange und eine kurze
Vibration dargestellt usw.. Die 0 bildet mit zwei langen Vibrationen
eine Ausnahme.&lt;/p&gt;
&lt;h3 id="beispiele"&gt;Beispiele:&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;01:18 =  .    .  -...
02:51 =  ..    -  .
10:11 =  .  --    .  .
19:06 =  .  -....    -.&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="erklärung"&gt;Erklärung:&lt;/h3&gt;
&lt;p&gt;Die Uhrzeit wird Ziffer für Ziffer verarbeitet:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;. = kurze Vibration&lt;/li&gt;
&lt;li&gt;- = lange Vibration&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Sowohl bei der Stunden-, als auch bei der Minutenangabe werden
führende Nullen weggelassen.&lt;/p&gt;
&lt;h3 id="pausen"&gt;Pausen:&lt;/h3&gt;
&lt;p&gt;Um die Vibrationen unterscheiden zu können, existieren drei
unterschiedlich lange Pausen:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;[]: Kurz für die Pause zwischen zwei Vibrationen&lt;/li&gt;
&lt;li&gt;[  ]: Mittel für die Trennung zwischen den Ziffern im Stunden- und
Minutenfeld&lt;/li&gt;
&lt;li&gt;[    ]: Lang für die Pause zwischen Stunden und Minuten&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="weitere-funktionen"&gt;Weitere Funktionen&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Behalte die aktuelle Uhrzeit im Blick. Die App kann die aktuelle
Uhrzeit automatisch alle X Minuten oder jede Stunde vibrieren.&lt;/li&gt;
&lt;li&gt;Spiele das &lt;a
href="https://de.wikipedia.org/wiki/Greenwich_Time_Signal"&gt;Greenwich
Time Signal&lt;/a&gt; zu Beginn jeder Stunde ab, ähnlich wie bei einem
Radiosender.&lt;/li&gt;
&lt;li&gt;&lt;a
href="https://play.google.com/store/apps/details?id=net.dinglisch.android.taskerm"&gt;Tasker&lt;/a&gt;-Integration:
Wenn dein Smartphone beispielsweise eine zusätzliche Hardware-Taste
besitzt, die von Tasker unterstützt wird, kannst du diese so
konfigurieren, dass sie die Uhrzeit vibriert. Die zugehörige Aktion
findest du in Tasker in der Kategorie “Plugins”.&lt;/li&gt;
&lt;li&gt;Entscheide, was passieren soll, wenn der Bildschirm beim Doppelklick
eingeschaltet ist. Du kannst zwischen einer Warnvibration und dem
Vibrieren der Uhrzeit wählen.&lt;/li&gt;
&lt;li&gt;Die App startet automatisch nach Dem Neustart des Systems.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="download-und-quellcode"&gt;Download und Quellcode:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a
href="https://play.google.com/store/apps/details?id=de.eric_scheibler.tactileclock&amp;amp;hl=de"&gt;Google
Play Store&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a
href="https://apt.izzysoft.de/packages/de.eric_scheibler.tactileclock"&gt;IzzySoft
Android Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;APK Download: &lt;a
href="/downloads/TactileClock_v2.6.0.apk"&gt;TactileClock_v2.6.0.apk&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/scheibler/TactileClock"&gt;Quellcode auf
GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a
href="https://www.eric-scheibler.de/de/tactile_clock_datenschutz/"&gt;Datenschutzinformationen&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Lizenz: &lt;a href="https://www.gnu.org/licenses/gpl-3.0.de.html"&gt;GPL
V3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="letzte-änderungen"&gt;Letzte Änderungen&lt;/h2&gt;
&lt;h3 id="version-2.6.0-vom-18.01.2026"&gt;Version 2.6.0 vom 18.01.2026&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Tasker-Integration hinzugefügt&lt;/li&gt;
&lt;li&gt;Ukrainische Übersetzung hinzugefügt&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="version-2.5.1-vom-29.12.2025"&gt;Version 2.5.1 vom 29.12.2025&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Uhr-stoppen Aktion zur Servicebenachrichtigung hinzugefügt&lt;/li&gt;
&lt;li&gt;Audioducking beim Abspielen des Greenwich Time Signals&lt;/li&gt;
&lt;li&gt;Tutorialtext aktualisiert&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="version-2.5.0-vom-16.12.2025"&gt;Version 2.5.0 vom 16.12.2025&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Dauer der kurzen und langen Vibration in den Einstellungen
konfigurierbar&lt;/li&gt;
&lt;li&gt;Test der Uhrzeitvibration im Tutorial und den Einstellungen
hinzugefügt&lt;/li&gt;
&lt;li&gt;Wenn der Bildschirm aus- und eingeschaltet wird, kann nun entweder
eine Warnvibration oder ebenfalls die Uhrzeit vibriert werden&lt;/li&gt;
&lt;li&gt;Greenwich Time Signal zu jeder vollen Stunde abspielen&lt;/li&gt;
&lt;li&gt;Tutorialtext überarbeitet&lt;/li&gt;
&lt;li&gt;Spanische Übersetzung hinzugefügt&lt;/li&gt;
&lt;li&gt;Führende Nullen werden nun sowohl bei den Stunden als auch bei den
Minuten weggelassen&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
</feed>
