Blog-Archiv

Samstag, 25. Oktober 2025

Do-Not-Ask-Anymore Dialog in Java Swing

There are situations where a dialog can be really nasty. For example, in a critical processing situation the application needs to ask the user for a decision, let's say about the deletion of some data. A normal modal dialog would do that sufficiently, but not when the decision is called from a loop, meaning the dialog appears over and over again, always asking the same question.

There may be a few situations where such repeats are actually necessary, but in general the user would like to answer the question just once for all elements in the loop. A popular example is when copying a backup-directory back to its origin, and the application asks for each there existing file whether it should overwrite it.

For that purpose the "Do-Not-Ask-Anymore Dialog" was invented. The Swing JOptionPane.showConfirmDialog() dialog method does not have such a functionality, but it offers means to implement such. In object-oriented thinking, remembering a decision can be solved easily by a class that contains (1) a field for the once "persisted" answer and (2) a method that either shows a dialog or delivers the answer silently when it was already made "persistent".

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import java.awt.*;
import java.util.Objects;
import javax.swing.*;

/**
 * A modal confirm-dialog that may not show but deliver a persistent
 * answer when the user once activated its "Remember" checkbox.
 */
public class DoNotAskAnymoreConfirmDialog
{
    private final String title;
    private final String message;
    private final String rememberAnswerText;
    
    private Boolean answer;
    
    public DoNotAskAnymoreConfirmDialog(String title, String message) {
        this(title, message, null);
    }
    public DoNotAskAnymoreConfirmDialog(String title, String message, String rememberAnswerText) {
        this.title = Objects.requireNonNull(title);
        this.message = Objects.requireNonNull(message);
        this.rememberAnswerText = (rememberAnswerText != null)
                ? rememberAnswerText
                : "Remember That and Don't Ask Anymore";
    }
    
    public boolean answer(Component parent) {
        if (answer != null) // rememberAnswer checkbox has been selected once
            return answer.booleanValue();
        
        final JLabel messageLabel = new JLabel(message);
        final JCheckBox rememberAnswer = new JCheckBox(rememberAnswerText, false);
        final JPanel panel = new JPanel(new BorderLayout());
        panel.add(messageLabel, BorderLayout.CENTER);
        panel.add(rememberAnswer, BorderLayout.SOUTH);
        
        final boolean dialogAnswer =
            (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(
                parent, 
                panel, 
                title, 
                JOptionPane.YES_NO_OPTION, 
                JOptionPane.WARNING_MESSAGE)
            );
        
        if (rememberAnswer.isSelected())
            answer = Boolean.valueOf(dialogAnswer);
        
        return dialogAnswer;
    }
}

The constructors on lines 17 and 20 require that for each decision-semantic there must be a new constructor call. That ensures that a dialog-object is not reused for a different decision and may silently answer the wrong question. The title and message parameters are required (not allowed to be null) through Objects.requireNonNull(), the rememberAnswerText is optional because the text on the checkbox will be always the same. The instance-fields for the constructor parameters are final, so that the semantic never could be changed.

The answer() method on line 28 first looks at the answer field on line 15 and returns its value when not null. It being null means the user did not yet activate the "Remember" checkbox. In that case the dialog is shown on screen and the user can make a decision, optionally activating the checkbox (line 47) to avoid repeated dialogs.

The answer field MUST NOT be static, because then all dialog-semantics would use the same answer! (This beautifully proves how valueable object-oriented thinking is.)

For sure it may be necessary to provide more answers than just TRUE and FALSE. When you copy a backup-directory back to its origin, you may want to ask whether to (1) overwrite or (2) merge all folders below that directory. Then a Boolean answer may be not enough. Feel free to abstract this class even more!




Samstag, 4. Oktober 2025

Two-Factor Authentication on LINUX with KeePassXC

I had to set up a "2FA" (Two Factor Authentication) or "TOTP" (Timedbased One-Time Password) tool for my github account. I use github on my LINUX 6.8.0-85 / Ubuntu 24.04.3 desktop computer from my web-browser and from my Eclipse IDE. Github now forces their users to use 2FA, with a delay of 45 days. They do not recommend any tool, so I had to search for a LINUX app that provides 2FA (TOTP).

First I installed gnome-authenticator 3.32.2, but this did not work. It could not scan the github QR-code and it gave me no way to manually enter the "setup key" that github offers as alternative for scanning. Error message was "AccessDenied ... SelectArea is not allowed" (I love these messages without fix hint:-). I searched for solutions and found I had to install gstreamer1.0-gtk4, but this did not work, I found no PPA repository for it, moreover I had an installed version gstreamer1.0-gtk3. As it is useless, I removed gnome-authenticator from my system again.

Next I found an "askubuntu" page that recommeded KeePassXC, and this installation worked fine via commandline "sudo apt install keepassxc". The graphical user-interface can be launched afterwards via "keepassxc".

Here are some screenshots of what I did then in that tool. I must say that I am completely new to 2FA and do not know at all how this works and what you have to do for it. I just followed the instructions of the "askubuntu" page:

  • Create a new KeePassXC database - the GUI tool prompts you for parameters and does this for you, no problem
  • Create a new "Entry" with username and password, here I used my github username and password, it did not require any password standards, or maybe my password was good enough
  • Select the "Entry" and go to menu "Entries" - "TOTP" - "Set up TOTP", here I entered the "setup key" from github
  • In the context-menu of the selected "Entry" I chose "TOTP" - "Copy TOTP" and entered the result on the github page
  • github then labeled my 2FA authentication as "Configured"

Here are the screenshots of most things I have done:

Here I already created my user "Entry". The screenshot shows the context menu that leads to "New Entry".

Here is the UI where you create the "New Entry". You need to enter your username and password for that. I also entered the URL of my github project.

This is how the app looks when the "Entry" is created.

The menu to set up TOTP. Don't forget to select your "Entry" first.

Here I entered the "setup key" from github as "Secret Key".

Here is the context menu on the selected "Entry" to copy a generated TOTP.

This is how my github account "Password and authentication" page looked after I entered the generated TOTP in the github input field.

One more evening gone for LINUX! But I don't give up trusting in open-source software, it's simply the better concept.


UPDATE: Today I logged out from github to explore how to log in via 2FA with keepassxc.

  • After confirming the dialog that I really want to log out, I clicked "Log in" on the github main page
  • Until now I used a generated password sequence for accessing github; in the github login dialog, I entered my username and that generated long password, but this didn't work, the password was reported to be invalid
  • So I tried the password I entered yesterday on my keepassxc application, and this worked (I can't say if keepassxc submitted that password to github or if that was my initial old github password, because I used the same for both)
  • After receiving the correct password, github asked me to "Enter the code from your two-factor authentication app ... below"
  • I launched keepassxc, selected my "Entry", used the context menu on it und chose "TOTP" - "Copy TOTP" (see screenshot above)
  • I guess the app copied the code to the system-clipboard, so that I could paste it into the input field ("XXXXXX") on the github page in my browser
  • I didn't even have to press ENTER or click the green "Verify" button, immediately my github account page opened.

So for performing 2FA with github, you always need your 2FA app open to copy a generated TOTP from it. For starting the 2FA app, you always have to enter your password in it. Thus 2FA authentication takes more time than a simple log-in with username and password, and it requires the presence of always the same 2FA app on your device. If you lose your device, you lose access to your github account, which only can be restored using the recovery codes that github provided you while setting up 2FA for your account. If you missed that, or stored the recovery codes on your now damaged device, the github support will not be able to give you back your account! That is why they recommend to store recovery codes in some Internet cloud.

I now tried to push some source-code changes in my Eclipse, and it worked. I had configured Eclipse to also use the github-generated long password sequence, so I wonder if that will break in 45 days, because obviously that sequence became invalid.