A while ago, F-Droid rolled out a new anti-feature in their repository. It's
called "Tethered Network Services" and informs users of whether an app is
dependent on access to a single centralized web service. E.g., a YouTube app
won't work without access to youtube.com. There are many reasons to consider
this before installing an app. E.g., some users might want to avoid GAFAM;
some may live in countries where, where US based services are censored; etc.
Organic Maps is a FOSS map/navigation app with exceptional user experience.
When the Tethered Network Services anti-feature was rolled out, it happened to
get assigned to Organic Maps, because they provide a custom download service
for map data. Due to the way F-Droid's app is handling new anti-features, it
effectively removed Organic Maps from search results and browsing in F-Droid's
app for the vast majority of users. As you might imagine, I think that's quite
a frustrating situation.
F-Droid app has a feature for hiding apps based on anti-features. At the very
least there are some very vocal users who love this feature and I think hiding
NSFW apps is also broadly accepted default. It's implemented as an allow list.
This means that, when F-Droid gets installed for the first time (or when users
do a full data reset) the allow list will be populated with some default
values. Then when there's a new anti-feature the F-Droid app doesn't handle
that and apps with that new anti-feature will be hidden.
Another important thing to acknowledge here is that anti-features are dynamic.
So every repository can make up their own set of anti-features however they see
fit. So while this is in theory a great feature for informing users, apps with
custom anti-features will not show up. The anti-feature hiding functionality
can be configured in F-Droid app settings. It is based on a hard-coded list of
anti-features and groups all of custom anti-features into the 'other
anti-features' category which are disabled by default.
This made me think and come up with some UX research question:
- Are users aware that some content is hidden when searching/browsing F-Droid?
- Do users understand what exactly is hidden from them when searching/browsing?
- Do users understand how to change what's hidden from them when searching/browsing?
- What do users expect to be hidden and/or visible by default in search results?
- Do users trust F-Droid App to provide default settings for filtering
their search results?
- or would users prefer to be guided to pick their own
preferences?
I don't really have stake in the design process of F-Droid, all I can do really
is raising questions and pushing in certain directions. So I think I'll bring
this up and see what will happen.
However since I think I've got good intuition for UX design, I think it's fun
to do some predictions about these questions. It might be fun to come back in a
couple of years and see what I got right or wrong:
I think the vast majority of users is not aware that some search results are
hidden. So I think the sane approach here is to hide as little as possible.
Since I don't think that users expect their app to censor search results,
they also have no concept of what's hidden or not.
I do think quite a few users are capable of changing Anti-Feature visibility
settings. But again I'd expect that the majority of users don't fully grasp
this feature as presented in F-Droid App right now. I think reworking the
"Include Anti-Features" settings would help maybe a quarter of users but I'd
expect a substantial fraction of users to be overwhelmed buy that feature. In
F-Droid App settings I'd rename the "app compatibility" section to "content".
I'd rename "Include Anti-Features" to "Hide unwanted Anti-Features". I'd also
make "Hide unwanted Anti-Features" displaying Anti-Feature Icons, short
descriptions of the Anti-Features and most importantly short examples of what
those Anti-Features mean in practice.
I think that a substantial fraction of users will either conscious or unconscious
expect that search results are filtered for NSFW content ant nothing else. I'd
also expect that there's a non-negligible fraction of users that'll expect no
filtering by default. An easy fix would be to add a 1 click solution for
getting full search results when filtering is active.
I think the vast majority of users will prefer to get sane defaults for
search filters (NSFW only) instead of having to make a choice there. Overall I
think the right place to make those kind of decisions is a repo inclusion
policy. Users with special need then can setup their own filters, but by
default getting mostly unfiltered repo content is making things easier for
everyone involved.
I need to scan documents ever so often. So I've got a scanner at home, it works
great with Linux. But Linux is a hostile place for users, so of course it does
not work out of the box, although things have improved a lot since I've last
posted about scanners here.
These days all the required drivers for my scanner (and most
scanners really) are already
preinstalled on normal debain desktop installs. There's one one key thing not
working out of the box: making a WiFi connection.
To get that working you'll need to figure out the IP address of your scanner. I
found mine with nmap
. I guess looking it up in my WiFi routers control pannel
would also work.
Then I could configure sane like so:
sudo bash -c 'echo "192.168.0.93" >> /etc/sane.d/net.conf'
After running that command (192.168.0.93
is the scanners IP address, will
vary) my scanner showd up Debians document scanner app.
Here's how I'm using poetry
and twine
to publish python projects to pypi.org.
Start by making sure the tools we'll need are installed.
sudo apt install python3-poetry twine
Create a file called pyproject.toml
in the project root. Here's minimal template that worked for me:
[tool.poetry]
name = "<package-name>"
version = "0.1"
description = "<package-description>"
authors = ["Your Name Here <your@email.com>"]
readme = "README.md"
packages = [{include = "<python-module-name>"}]
# optional: this block tells poetry to expose one of your python functions
# as a cli tool/shell command.
[tool.poetry.scripts]
my-tool = '<python-module-name>.main:main'
[tool.poetry.dependencies]
python = "^3.7"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
You'll need to replace all the values with someting that makes senst for your project.
For publishing your package you'll have to get an account at pypi.org, setup 2-faktor authentication and register an api token.
I'm using twine to uplaod the built project to pypi.org. I guess poetry could do that too, but twine is easier:
python3 -m twine upload dist/*
twine
will promt you for a username and password. Use __token__
as username and your token as password.
username: __token__
password: pypi-HE7yZvs2GBJVHbANguWOjo5CwnTZKlVQKQUTqlswykvIJbOkOofM6pf2eGFxj64jEzFT8i986Dxa6OXtjWj6G9ZAd5J7xp4FenLWDOHepIqRvCbsWvROPL93xtGoTlGGXsmYGiHLvK0DBh0FsrE3lANngkOi5zxC8pLk1m3d47HQSXyN
I've looked a bit further into using micro python on NodeMCU. I've got a tiny
monochromatic SSD1306 OLED display connected to one of my ESP8266 based NodeMCU
boards. It's only 128x64 pixels in size, but well supported by micro python. So
I thought I'll give it a try and implemented a continous WiFI scanner, that's
displaying SSIDs directly on the micro controllers display.
First of all I needed some tooling for uploading my python scripts to my micro
controller.
pip install --break-system-packages microdeploy
Here's the WiFi scanner script I came up with ('main.py'):
from time import sleep
from machine import I2C
from ssd1306 import SSD1306_I2C
from network import WLAN, STA_IF
d = SSD1306_I2C(128, 64, I2C(sda=14, scl=12))
sta_if = WLAN(STA_IF)
while True:
d.fill(0)
d.text("scanning ...", 0, 0)
d.show()
scan_result = sta_if.scan()
for i in range(len(scan_result)):
d.fill(0)
d.text("found WiFi:", 0, 0)
d.text(scan_result[i][0], 0, 16)
d.text("({}/{})".format(i+1, len(scan_result)+1), 0, 48)
d.show()
sleep(3)
Micro Python will automatically start a script called 'main.py' on boot. So all
that's left to do is uploading the script to my micro controller:
microdeploy --port /dev/ttyUSB0 --baud 115200 device put ~/main.py main.py
NodeMCU is a pretty cheap ESP8266 based WiFi capeable micro controller, with
good open source support. I had one stowed away in a box for years. When I
re-discovered it the other day I finally gave it a try. It's also supported by Micro Python so getting software running there is pretty effortless
I started by getting the latest version of micro python firmware for Node MCU. From: https://micropython.org/download/ESP8266_GENERIC/
wget https://micropython.org/resources/firmware/ESP8266_GENERIC-20240105-v1.22.1.bin
Next I needed to setup my Debian user to be allowed to open tty devices.
Because NodeMCU (like many other micro controllers) uses serial.
# you need to re-login for this change to take effect
sudo adduser $USER dialout
Then I've connected the micro controller to my computer with USB. A good tool for inspecting USB devices is lsusb
. The micro controller showed up as /dev/ttyUSB0
for me.
Next I needed to install esptool
a script for flashing firmware to mirco controllers. It's not packaged for Debian, but it can be comfortably installed using pip
. (pip is pythons package manager and is packaged for Debian sudo apt install python3-pip
)
pip install esptool --break-system-packages
Now I we can proceed to flash the previously downloaded pre-compiled micro
python firmware.
esptool.py --port /dev/ttyUSB0 --baud 115200 write_flash --flash_size=detect --flash_mode qio 0 ./ESP8266_GENERIC-20240105-v1.22.1.bin
(Side note: The manufacturer printed "use 9600 baud" on my board. However
esptool runs into a timeout when the baud rate is too low. This was quite
confusing to figure out ...)
Micro Python is installed now. To have some quick fun, we can use screen
to
connect to the micro controller and get an interactive python shell. For
testing I've connected a LED to the pin labeled D1 (and G). Here's how to use
an interactive shell to make it blink:
screen /dev/ttyUSB0 115200
>>> from time import sleep
>>> from machine import Pin
>>> p = Pin(5, Pin.OUT)
>>> while True:
... p.value(1 - p.value())
... sleep(0.5)
...
To interrupt the loop press [Ctrl+c]
, to quit the screen session press [Ctrl+a] [\] [y]
It was also easy to connect to my WiFi in the interactive shell by just
following the instructions of micro pythons built in help()
.
Here are some docs I found helpful:
This command will make gpg
read my.file
and create a detached signature for
it called my.file.asc
. So you can use it to sign pretty much any file. I'm
also explicitly specifying which key I'm using for signing. (1234...
) Just in
case I don't want to use my default signing key here.
gpg --armor --default-key 1234... --detach-sign my.file
This here demonstrates how I'm verifying the signature with gpg
. This command
assumes that my.file
is located in the same directory as my.file.asc
.
gpg --verify my.file.asc
Here's an update to my favorite Gnome exteions. This time around on Debian 12 (bookworm) with Gnome 40.
In Gnome settings:
- Keyboard > View and Customize Settings > Navigation > Switch Applications: disabled
- Keyboard > View and Customize Settings > Navigation > Swtich Windows: Alt+Tab
- Multitasking > Application Switching: Include Applications from the current workspace only
Additional Gnome extensions:
- AppIndicator and KStatusNotifierItem
Support -
IMHO the most important fix all Gnome users need. It adds a status icon
area. Some apps I need to use are only accessible through a status icon.
(nextcloud, nitrokey app, etc.) I've tried a lot of status icon area
extensions over the years. This is the only one which doesn't bug out.
Lucky break.
- Current screen only on windows switcher - does exactly what the name sais. This way alt-tab is less confusing for me.
- Frippery Move
Clock - It's just
easier on my eyes, when the clock is not cluttering up the center
of my top bar.
- gTile - This extension
allows me to do arrange my windows using keyboard shortcuts instead of having
to try hit the edges of windows with the mouse cursor and pull them to the
exactly right size.
- Impatience -
Extension for scaling down animation durations. I'm trying to get some work
done here. Watching pretty windows fly across the desktop is fun sometimes,
but I'd rather navigate quickly.
- Vitals - So far my
favorite system monitor plugin. It's highly customizable and supports
monitoring really a lot of things. You could use it to pin your secondary
casing fan speed or your wifi chip temperature to gnomes top bar. I've only
pinned the most basic things and still numbers it's tracking are 2 clicks
away at most. It has proven itself very useful to me time and again.
- Workspace
Matrix - I
use a lot of workspaces and prefer a vertical layout. This makes it
feasible.
Here's how to retreive the SHA1 and SHA256 fingerprints for debug.keystore. That's what Android Studio / gradle is using to sign APKs for testing and debugging.
./gradlew signingReport
Alternatively, this is how to get the same fingerprints using javas keytool directly:
keytool -list -v \
-keystore ~/.android/debug.keystore \
-alias androiddebugkey \
-storepass android -keypass android
This assumes that the standard debug keystore config wasn't changed.
This is not a complete guide, this just documents how I got things done. I'm
using heimdall
instead of Samsungs official odin
tool. Follow the official
Linage guide for your specific Samsung device from LinageOS wiki. There's what
I ended up doing to get my phone flashed:
sudo apt install adb heimdall
# make sure phone is fully chared
# boot phone into fastboot mode
# connect phone with usb to my computer
heimdall flash --RECOVERY recovery.img
# boot phone into recovery mode
# do a fatory reset on the phone as discribed in the wiki in recovery
# start sideload mode in recovery
adb sideload Downloads/lineage-...-signed.zip
I want to run vagrant up
on Debian. However I don't want to install
VirtualBox and use KVM instead. I kept forgetting which packages I need to
install so here we go:
sudo apt install --no-install-recommends \
bridge-utils \
busybox \
dnsmasq-base \
ebtables \
libguestfs-tools \
libvirt-clients \
libvirt-daemon \
libvirt-daemon-system \
netcat-openbsd \
nfs-kernel-server \
qemu-kvm \
vagrant \
vagrant-libvirt
I also had to add my user to the libvirt
group to make it work smoothly:
sudo adduser $USER libvirt
Then all that's left is to start libvirt and maybe also tell systemd to start it on boot:
sudo systemctl start libvirtd.service
sudo systemctl enable libvirtd.service
That's it vagrant up --provider libvirt
should work now.
On a side note: On rare occasion virt-manager
is a quite handy tool for
working with libvirt VMs. It's also in Debian an can be installed with apt
.