Shift+Ctrl+X option screen: allow persisting settings

This commit is contained in:
Floris Bos 2021-01-20 13:04:18 +01:00
parent a6150f7bc5
commit abbed47f97
6 changed files with 355 additions and 34 deletions

View file

@ -19,6 +19,7 @@ Popup {
padding: 0
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
property bool initialized: false
property bool hasSavedSettings: false
property string config
property string cmdline
property string firstrun
@ -87,8 +88,21 @@ Popup {
ColumnLayout {
GroupBox {
title: qsTr("Image customization options (for this session only)")
Layout.fillWidth: true
title: qsTr("Image customization options")
label: RowLayout {
Label {
text: parent.parent.title
}
ComboBox {
id: comboSaveSettings
model: {
[qsTr("for this session only"),
qsTr("to always use")]
}
Layout.minimumWidth: 250
Layout.maximumHeight: 40
}
}
ColumnLayout {
spacing: -10
@ -97,26 +111,24 @@ Popup {
id: chkOverscan
text: qsTr("Disable overscan")
}
CheckBox {
id: chkHostname
text: qsTr("Set hostname")
onCheckedChanged: {
if (checked) {
fieldHostname.forceActiveFocus()
RowLayout {
CheckBox {
id: chkHostname
text: qsTr("Set hostname:")
onCheckedChanged: {
if (checked) {
fieldHostname.forceActiveFocus()
}
}
}
}
RowLayout {
enabled: chkHostname.checked
Layout.leftMargin: 40
TextField {
id: fieldHostname
enabled: chkHostname.checked
text: "raspberrypi"
}
Text {
text : ".local"
color: parent.enabled ? "black" : "grey"
color: chkHostname.checked ? "black" : "grey"
}
}
CheckBox {
@ -163,6 +175,16 @@ Popup {
id: fieldUserPassword
echoMode: TextInput.Password
Layout.minimumWidth: 200
property bool alreadyCrypted: false
onTextEdited: {
if (alreadyCrypted) {
/* User is trying to edit saved
(crypted) password, clear field */
alreadyCrypted = false
clear()
}
}
}
}
@ -326,6 +348,7 @@ Popup {
}
applySettings()
saveSettings()
popup.close()
}
Material.foreground: "#ffffff"
@ -338,14 +361,47 @@ Popup {
}
}
function openPopup() {
if (!initialized) {
chkBeep.checked = imageWriter.getBoolSetting("beep")
chkTelemtry.checked = imageWriter.getBoolSetting("telemetry")
chkEject.checked = imageWriter.getBoolSetting("eject")
fieldTimezone.model = imageWriter.getTimezoneList()
fieldPublicKey.text = imageWriter.getDefaultPubKey()
fieldWifiCountry.model = imageWriter.getCountryList()
function initialize() {
chkBeep.checked = imageWriter.getBoolSetting("beep")
chkTelemtry.checked = imageWriter.getBoolSetting("telemetry")
chkEject.checked = imageWriter.getBoolSetting("eject")
var settings = imageWriter.getSavedCustomizationSettings()
fieldTimezone.model = imageWriter.getTimezoneList()
fieldPublicKey.text = imageWriter.getDefaultPubKey()
fieldWifiCountry.model = imageWriter.getCountryList()
if (Object.keys(settings).length) {
comboSaveSettings.currentIndex = 1
hasSavedSettings = true
}
if ('disableOverscan' in settings) {
chkOverscan.checked = true
}
if ('hostname' in settings) {
fieldHostname.text = settings.hostname
chkHostname.checked = true
}
if ('sshUserPassword' in settings) {
fieldUserPassword.text = settings.sshUserPassword
fieldUserPassword.alreadyCrypted = true
chkSSH.checked = true
radioPasswordAuthentication.checked = true
}
if ('sshAuthorizedKeys' in settings) {
fieldPublicKey.text = settings.sshAuthorizedKeys
chkSSH.checked = true
radioPubKeyAuthentication.checked = true
}
if ('wifiSSID' in settings) {
fieldWifiSSID.text = settings.wifiSSID
chkShowPassword.checked = false
fieldWifiPassword.text = settings.wifiPassword
fieldWifiCountry.currentIndex = fieldWifiCountry.find(settings.wifiCountry)
if (fieldWifiCountry.currentIndex == -1) {
fieldWifiCountry.editText = settings.wifiCountry
}
chkWifi.checked = true
} else {
fieldWifiCountry.currentIndex = fieldWifiCountry.find("GB")
fieldWifiSSID.text = imageWriter.getSSID()
if (fieldWifiSSID.text.length) {
@ -354,21 +410,41 @@ Popup {
chkShowPassword.checked = false
}
}
var tz = imageWriter.getTimezone()
var tzidx = fieldTimezone.find(tz)
if (tzidx === -1) {
fieldTimezone.editText = tz
} else {
fieldTimezone.currentIndex = tzidx
}
}
var tz;
if ('timezone' in settings) {
chkLocale.checked = true
tz = settings.timezone
} else {
tz = imageWriter.getTimezone()
}
var tzidx = fieldTimezone.find(tz)
if (tzidx === -1) {
fieldTimezone.editText = tz
} else {
fieldTimezone.currentIndex = tzidx
}
if ('keyboardLayout' in settings) {
fieldKeyboardLayout.text = settings.keyboardLayout
} else {
/* Lacking an easy cross-platform to fetch keyboard layout
from host system, just default to "gb" for people in
UK time zone for now, and "us" for everyone else */
if (tz == "Europe/London") {
fieldKeyboardLayout.text = "gb"
}
}
if ('skipFirstUse' in settings) {
chkSkipFirstUse.checked = true
}
initialized = true
initialized = true
}
function openPopup() {
if (!initialized) {
initialize()
}
open()
@ -407,7 +483,8 @@ Popup {
addFirstRun("FIRSTUSERHOME=`getent passwd 1000 | cut -d: -f6`")
if (radioPasswordAuthentication.checked) {
addFirstRun("echo \"$FIRSTUSER:\""+escapeshellarg(imageWriter.crypt(fieldUserPassword.text))+" | chpasswd -e")
var cryptedPassword = fieldUserPassword.alreadyCrypted ? fieldUserPassword.text : imageWriter.crypt(fieldUserPassword.text)
addFirstRun("echo \"$FIRSTUSER:\""+escapeshellarg(cryptedPassword)+" | chpasswd -e")
}
if (radioPubKeyAuthentication.checked) {
var pubkey = fieldPublicKey.text.replace(/\n/g, "")
@ -466,6 +543,47 @@ Popup {
}
imageWriter.setImageCustomization(config, cmdline, firstrun)
}
function saveSettings()
{
if (comboSaveSettings.currentIndex == 1) {
hasSavedSettings = true
var settings = { };
if (chkOverscan.checked) {
settings.disableOverscan = true
}
if (chkHostname.checked && fieldHostname.length) {
settings.hostname = fieldHostname.text
}
if (chkSSH.checked) {
if (radioPasswordAuthentication.checked) {
settings.sshUserPassword = fieldUserPassword.alreadyCrypted ? fieldUserPassword.text : imageWriter.crypt(fieldUserPassword.text)
}
if (radioPubKeyAuthentication.checked) {
settings.sshAuthorizedKeys = fieldPublicKey.text
}
}
if (chkWifi.checked) {
settings.wifiSSID = fieldWifiSSID.text
settings.wifiPassword = fieldWifiPassword.text
settings.wifiCountry = fieldWifiCountry.editText
}
if (chkLocale.checked) {
settings.timezone = fieldTimezone.editText
settings.keyboardLayout = fieldKeyboardLayout.text
if (chkSkipFirstUse.checked) {
settings.skipFirstUse = true
}
}
imageWriter.setSavedCustomizationSettings(settings)
} else if (hasSavedSettings) {
imageWriter.clearSavedCustomizationSettings()
hasSavedSettings = false
}
imageWriter.setSetting("beep", chkBeep.checked)
imageWriter.setSetting("eject", chkEject.checked)
imageWriter.setSetting("telemetry", chkTelemtry.checked)

138
UseSavedSettingsPopup.qml Normal file
View file

@ -0,0 +1,138 @@
/*
* SPDX-License-Identifier: Apache-2.0
* Copyright (C) 2021 Raspberry Pi (Trading) Limited
*/
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.0
import QtQuick.Controls.Material 2.2
Popup {
id: msgpopup
x: 75
y: (parent.height-height)/2
width: parent.width-150
height: msgpopupbody.implicitHeight+150
padding: 0
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
signal yes()
signal no()
signal editSettings()
// background of title
Rectangle {
color: "#f5f5f5"
anchors.right: parent.right
anchors.top: parent.top
height: 35
width: parent.width
}
// line under title
Rectangle {
color: "#afafaf"
width: parent.width
y: 35
implicitHeight: 1
}
Text {
id: msgx
text: "X"
anchors.right: parent.right
anchors.top: parent.top
anchors.rightMargin: 25
anchors.topMargin: 10
font.family: roboto.name
font.bold: true
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
msgpopup.close()
}
}
}
ColumnLayout {
spacing: 20
anchors.fill: parent
Text {
id: msgpopupheader
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
Layout.fillWidth: true
Layout.topMargin: 10
font.family: roboto.name
font.bold: true
text: qsTr("Warning: advanced settings set")
}
Text {
id: msgpopupbody
font.pointSize: 12
wrapMode: Text.Wrap
textFormat: Text.StyledText
font.family: roboto.name
Layout.maximumWidth: msgpopup.width-50
Layout.fillHeight: true
Layout.leftMargin: 25
Layout.topMargin: 25
Accessible.name: text.replace(/<\/?[^>]+(>|$)/g, "")
text: qsTr("Would you like to apply the image customization settings saved earlier?")
}
RowLayout {
Layout.alignment: Qt.AlignCenter | Qt.AlignBottom
Layout.bottomMargin: 10
spacing: 20
Button {
text: qsTr("NO, CLEAR SETTINGS")
onClicked: {
msgpopup.close()
msgpopup.no()
}
Material.foreground: "#ffffff"
Material.background: "#c51a4a"
font.family: roboto.name
Accessible.onPressAction: clicked()
}
Button {
text: qsTr("YES")
onClicked: {
msgpopup.close()
msgpopup.yes()
}
Material.foreground: "#ffffff"
Material.background: "#c51a4a"
font.family: roboto.name
Accessible.onPressAction: clicked()
}
Button {
text: qsTr("EDIT SETTINGS")
onClicked: {
msgpopup.close()
msgpopup.editSettings()
}
Material.foreground: "#ffffff"
Material.background: "#c51a4a"
font.family: roboto.name
Accessible.onPressAction: clicked()
}
Text { text: " " }
}
}
function openPopup() {
open()
// trigger screen reader to speak out message
msgpopupbody.forceActiveFocus()
}
}

View file

@ -926,6 +926,45 @@ QString ImageWriter::crypt(const QByteArray &password)
return sha256_crypt(password.constData(), salt.constData());
}
void ImageWriter::setSavedCustomizationSettings(const QVariantMap &map)
{
_settings.beginGroup("imagecustomization");
_settings.remove("");
for (QString key : map.keys()) {
_settings.setValue(key, map.value(key));
}
_settings.endGroup();
}
QVariantMap ImageWriter::getSavedCustomizationSettings()
{
QVariantMap result;
_settings.beginGroup("imagecustomization");
for (QString key : _settings.childKeys()) {
result.insert(key, _settings.value(key));
}
_settings.endGroup();
return result;
}
void ImageWriter::clearSavedCustomizationSettings()
{
_settings.beginGroup("imagecustomization");
_settings.remove("");
_settings.endGroup();
}
bool ImageWriter::hasSavedCustomizationSettings()
{
_settings.beginGroup("imagecustomization");
bool result = !_settings.childKeys().isEmpty();
_settings.endGroup();
return result;
}
void MountUtilsLog(std::string msg) {
qDebug() << "mountutils:" << msg.c_str();
}

View file

@ -103,6 +103,10 @@ public:
Q_INVOKABLE bool getBoolSetting(const QString &key);
Q_INVOKABLE void setSetting(const QString &key, const QVariant &value);
Q_INVOKABLE void setImageCustomization(const QByteArray &config, const QByteArray &cmdline, const QByteArray &firstrun);
Q_INVOKABLE void setSavedCustomizationSettings(const QVariantMap &map);
Q_INVOKABLE QVariantMap getSavedCustomizationSettings();
Q_INVOKABLE void clearSavedCustomizationSettings();
Q_INVOKABLE bool hasSavedCustomizationSettings();
Q_INVOKABLE QString crypt(const QByteArray &password);

View file

@ -188,10 +188,15 @@ ApplicationWindow {
Material.background: "#ffffff"
Material.foreground: "#c51a4a"
onClicked: {
if (!imageWriter.readyToWrite())
return;
if (!imageWriter.readyToWrite()) {
return
}
confirmwritepopup.askForConfirmation()
if (!optionspopup.initialized && imageWriter.hasSavedCustomizationSettings()) {
usesavedsettingspopup.openPopup()
} else {
confirmwritepopup.askForConfirmation()
}
}
Accessible.onPressAction: clicked()
}
@ -790,6 +795,22 @@ ApplicationWindow {
id: optionspopup
}
UseSavedSettingsPopup {
id: usesavedsettingspopup
onYes: {
optionspopup.initialize()
optionspopup.applySettings()
confirmwritepopup.askForConfirmation()
}
onNo: {
imageWriter.clearSavedCustomizationSettings()
confirmwritepopup.askForConfirmation()
}
onEditSettings: {
optionspopup.openPopup()
}
}
/* Utility functions */
function httpRequest(url, callback) {
var xhr = new XMLHttpRequest();

View file

@ -25,5 +25,6 @@
<file>OptionsPopup.qml</file>
<file>countries.txt</file>
<file>timezones.txt</file>
<file>UseSavedSettingsPopup.qml</file>
</qresource>
</RCC>