fix webcam opening/closing issues, display progress bar for qr animation scanning progress

This commit is contained in:
Craig Raw 2021-03-24 12:54:14 +02:00
parent b74741bccb
commit 11a201b3f5
3 changed files with 55 additions and 13 deletions

View file

@ -51,7 +51,7 @@ dependencies {
implementation('com.github.arteam:simple-json-rpc-server:1.0') {
exclude group: 'org.slf4j'
}
implementation('com.sparrowwallet:hummingbird:1.5.2')
implementation('com.sparrowwallet:hummingbird:1.5.3')
implementation('com.nativelibs4java:bridj:0.7-20140918-3') {
exclude group: 'com.google.android.tools', module: 'dx'
}

View file

@ -28,13 +28,16 @@ import com.sparrowwallet.sparrow.AppServices;
import com.sparrowwallet.sparrow.glyphfont.FontAwesome5;
import com.sparrowwallet.sparrow.io.Config;
import javafx.application.Platform;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.*;
import org.controlsfx.glyphfont.Glyph;
import org.controlsfx.tools.Borders;
import org.slf4j.Logger;
@ -59,13 +62,14 @@ public class QRScanDialog extends Dialog<QRScanDialog.Result> {
private final WebcamService webcamService;
private List<String> parts;
private boolean isUr;
private QRScanDialog.Result result;
private static final Pattern PART_PATTERN = Pattern.compile("p(\\d+)of(\\d+) (.+)");
private final ObjectProperty<WebcamResolution> webcamResolutionProperty = new SimpleObjectProperty<>(WebcamResolution.VGA);
private final DoubleProperty percentComplete = new SimpleDoubleProperty(0.0);
public QRScanDialog() {
this.decoder = new URDecoder();
this.legacyDecoder = new LegacyURDecoder();
@ -83,8 +87,23 @@ public class QRScanDialog extends Dialog<QRScanDialog.Result> {
StackPane stackPane = new StackPane();
stackPane.getChildren().add(webcamView.getView());
Node wrappedView = Borders.wrap(stackPane).lineBorder().buildAll();
dialogPane.setContent(Borders.wrap(stackPane).lineBorder().buildAll());
ProgressBar progressBar = new ProgressBar();
progressBar.setMinHeight(20);
progressBar.setPadding(new Insets(0, 10, 0, 10));
progressBar.setPrefWidth(Integer.MAX_VALUE);
progressBar.progressProperty().bind(percentComplete);
webcamService.openingProperty().addListener((observable, oldValue, newValue) -> {
if(percentComplete.get() <= 0.0) {
Platform.runLater(() -> percentComplete.set(newValue ? 0.0 : -1.0));
}
});
VBox vBox = new VBox(20);
vBox.getChildren().addAll(wrappedView, progressBar);
dialogPane.setContent(vBox);
webcamService.resultProperty().addListener(new QRResultListener());
webcamService.setOnFailed(failedEvent -> {
@ -111,7 +130,7 @@ public class QRScanDialog extends Dialog<QRScanDialog.Result> {
final ButtonType hdButtonType = new javafx.scene.control.ButtonType("Use HD Capture", ButtonBar.ButtonData.LEFT);
dialogPane.getButtonTypes().addAll(hdButtonType, cancelButtonType);
dialogPane.setPrefWidth(646);
dialogPane.setPrefHeight(webcamResolutionProperty.get() == WebcamResolution.HD ? 450 : 550);
dialogPane.setPrefHeight(webcamResolutionProperty.get() == WebcamResolution.HD ? 490 : 590);
setResultConverter(dialogButton -> dialogButton != cancelButtonType ? result : null);
}
@ -127,11 +146,10 @@ public class QRScanDialog extends Dialog<QRScanDialog.Result> {
String qrtext = qrResult.getText();
Matcher partMatcher = PART_PATTERN.matcher(qrtext);
if(isUr || qrtext.toLowerCase().startsWith(UR.UR_PREFIX)) {
isUr = true;
if(LegacyURDecoder.isLegacyURFragment(qrtext)) {
if(qrtext.toLowerCase().startsWith(UR.UR_PREFIX)) {
if(LegacyURDecoder.isLegacyURFragment(qrtext.toLowerCase())) {
legacyDecoder.receivePart(qrtext.toLowerCase());
Platform.runLater(() -> percentComplete.setValue(legacyDecoder.getPercentComplete()));
if(legacyDecoder.isComplete()) {
try {
@ -143,6 +161,7 @@ public class QRScanDialog extends Dialog<QRScanDialog.Result> {
}
} else {
decoder.receivePart(qrtext);
Platform.runLater(() -> percentComplete.setValue(decoder.getEstimatedPercentComplete()));
if(decoder.getResult() != null) {
URDecoder.Result urResult = decoder.getResult();
@ -164,6 +183,10 @@ public class QRScanDialog extends Dialog<QRScanDialog.Result> {
}
parts.set(m - 1, payload);
if(n > 0) {
Platform.runLater(() -> percentComplete.setValue((double)parts.stream().filter(Objects::nonNull).count() / n));
}
if(parts.stream().filter(Objects::nonNull).count() == n) {
String complete = String.join("", parts);
try {
@ -484,6 +507,7 @@ public class QRScanDialog extends Dialog<QRScanDialog.Result> {
private class QRScanDialogPane extends DialogPane {
@Override
protected Node createButton(ButtonType buttonType) {
Node button = null;
if(buttonType.getButtonData() == ButtonBar.ButtonData.LEFT) {
ToggleButton hd = new ToggleButton(buttonType.getText());
hd.setSelected(webcamResolutionProperty.get() == WebcamResolution.HD);
@ -497,10 +521,13 @@ public class QRScanDialog extends Dialog<QRScanDialog.Result> {
setHdGraphic(hd, newValue);
});
return hd;
button = hd;
} else {
button = super.createButton(buttonType);
}
return super.createButton(buttonType);
button.disableProperty().bind(webcamService.openingProperty());
return button;
}
private void setHdGraphic(ToggleButton hd, boolean isHd) {

View file

@ -6,7 +6,9 @@ import com.github.sarxos.webcam.WebcamResolution;
import com.google.zxing.*;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.common.HybridBinarizer;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
@ -20,6 +22,7 @@ import java.util.concurrent.TimeUnit;
public class WebcamService extends Service<Image> {
private WebcamResolution resolution;
private final WebcamListener listener;
private BooleanProperty opening = new SimpleBooleanProperty(false);
private final ObjectProperty<Result> resultProperty = new SimpleObjectProperty<>(null);
@ -41,7 +44,9 @@ public class WebcamService extends Service<Image> {
cam.addWebcamListener(listener);
}
opening.set(true);
cam.open();
opening.set(false);
while(!isCancelled()) {
if(cam.isImageNew()) {
BufferedImage bimg = cam.getImage();
@ -49,12 +54,14 @@ public class WebcamService extends Service<Image> {
readQR(bimg);
}
}
cam.close();
return getValue();
} finally {
opening.set(false);
if(!cam.close()) {
cam.close();
}
}
}
};
}
@ -89,4 +96,12 @@ public class WebcamService extends Service<Image> {
public void setResolution(WebcamResolution resolution) {
this.resolution = resolution;
}
public boolean isOpening() {
return opening.get();
}
public BooleanProperty openingProperty() {
return opening;
}
}