Vaadin FileDownload requires the button be clicked twice to download - java

I'm following the Vaadin sample code but when I do I require a second click to start the file download. Below is my code:
final StreamResource streamResource = new StreamResource(
() -> {
return new ByteArrayInputStream("hello world".getBytes());
}, document.getName() + ".txt");
FileDownloader fileDownloader = new FileDownloader(streamResource);
fileDownloader.extend(getDownloadButton());
There's nothing special about the code to create the button but as requested in the comments here it is:
Button downloadButton = new Button("Download");

To me, I inited FileDownloader in constructor with dummy resource:
if (fileDownloader == null) {
fileDownloader = new FileDownloader(streamResourceDummy);
fileDownloader.extend(button);
}
And set actual resource in button event listener:
buttonClick(ClickEvent event){
if (fileDownloader != null) {
// close previous stream
((StreamResource) fileDownloader.getFileDownloadResource()).getStreamSource().getStream().close();
fileDownloader.setFileDownloadResource(streamResource);
}
}
And It works :)). I am using Vaadin 8.

Although I don't like this solution it works. It has to do with how the download works and some restrictions by the browser. I'm sure there's a better solution but right now I simulate the first click with Javascript. If someone can find the correct answer then please post it and I'll change the selected answer, otherwise this is the only solution I found (posted in the Vaadin forums).
streamResource = createStreamResource();
downloadButton createDownloadButton();
downloadButton.setId("DownloadButtonID");
if(fileDownloader == null)
{
fileDownloader = new FileDownloader(streamResource);
fileDownloader.extend(downloadButton);
// Javascript click so that it works without a second click
Page.getCurrent().getJavaScript().execute(
"document.getElementById('DownloadButtonID').click();");
} else {
fileDownloader.setFileDownloadResource(streamResource);
}

The solution to the second click requirement for downloading the file is the button's listener. Like in my case, i have the click listener of button in which FileDownloader extends button. But it should be without listener only as FileDownloader has its own mechanism to handle the listener actions.
Here, the first button click gets handled by the clickListener and only in that, fileDownloader extends download button which holds all the functionality of downloading the file and this functionality occurs only when click event goes through the FileDownloader. so for the next time click goes through the FileDownloader as now its extending the button.
public static Button getDownloadButton(String fileName, String fileAsString, String caption) {
// caption
Button dwnButton = new Button(caption, VaadinIcons.DOWNLOAD);
dwnButton.addClickListener(listener -> {
StreamResource resource = createResource(fileName, fileAsString);
FileDownloader fileDownloader = new FileDownloader(resource);
fileDownloader.extend(dwnButton);
});
return dwnButton;
}
Here, fileDownloader already extending the button and it has all the stream resources. So on the first click only downloading gets invoked.
public static Button getDownloadButton(String fileName, String fileAsString, String caption) {
// caption
Button dwnButton = new Button(caption, VaadinIcons.DOWNLOAD);
StreamResource resource = createResource(fileName, fileAsString);
FileDownloader fileDownloader = new FileDownloader(resource);
fileDownloader.extend(dwnButton);
return dwnButton;
}

Related

How to open a generated PDF in vaadin?

In my vaadin application I have a Table with an additional column containing a print Button. The Button calls the following util method to create a pdf and open it in a new window (ui parameter is the button):
public static void printPDF(Offer offer, AbstractComponent ui) throws IOException, DocumentException, TemplateException {
// ... create PDF
FileResource resource = new FileResource(pdfFile);
BrowserWindowOpener opener = new BrowserWindowOpener(resource);
opener.setFeatures("");
opener.extend(ui);
}
Now clicking the button the first time does not work. Clicking it the second time works. Clicking it the third time, opens two windows. This increases on every further click.
I also want to open the pdf using the context menu e.g.
table.addActionHandler(new Handler()...
There I don't even have a button to extend. I would prefer to, not use the .extend() part and just open a new window. How can I do that?
EDIT: This blocks the button from opening mulitple instances, still not a nice solution and the first click does not work.
Collection<Extension> extensions = ui.getExtensions();
for (Extension e : extensions) {
if (e instanceof BrowserWindowOpener) {
((BrowserWindowOpener) e).setResource(resource);
return;
}
}
I guess I would need to create a BrowserWindowOpener for every print Button in my Table.
Not a very clean solution, the table may contain lots of rows which would create a lot of BrowserWindowOpener instances which will never be used. The context menu problem would not be solved as well.
EDIT2: This is the other solution I tried:
ResourceReference rr = ResourceReference.create(resource, ui, "print");
Page.getCurrent().open(rr.getURL(), "blank_");
Here I get the following error:
Button (175) did not handle connector request for
print/2016_9090_R_1634500091131558445.pdf
You can use the FileDownloader to achieve what you want.
FileResource resource = new FileResource(pdfFile);
FileDownloader downloader = new FileDownloader(resource);
Button pdf= new Button("Download PDF");
downloader.extend(pdf);
Use this code
Window window = new Window();
((VerticalLayout) window.getContent()).setSizeFull();
window.setResizable(true);
window.setCaption("Exemplo PDF");
window.setWidth("800");
window.setHeight("600");
window.center();
StreamSource s = new StreamResource.StreamSource() {
#Override
public InputStream getStream() {
try {
File f = new File("C:/themes/repy.pdf");
FileInputStream fis = new FileInputStream(f);
return fis;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
};
StreamResource r = new StreamResource(s, "repy.pdf", mainLayout.getApplication());
Embedded e = new Embedded();
e.setSizeFull();
e.setType(Embedded.TYPE_BROWSER);
r.setMIMEType("application/pdf");
e.setSource(r);
window.addComponent(e);
getMainWindow().addWindow(window);

Issues with creating custom events in android

I am writing a custom event and would like some help please. Most of what I am about to talk about is based on the help provided at Custom event listener on Android app
So here is my issue. I am writing an app that needs to download updated images from the web, store the images on the phone, then later display those images. Basically, I download any needed images during a splash screen. Then when the images are downloaded and stored, the splash screen clears and any necessary (newly downloaded) images are displayed on the screen. Here is the problem: the download process is done via an asynctask so the part where the images are loaded on to the screen can't be done inside the asynctask. It has to be done on the main UI thread. I would like to create an event and a custom event listener for the main thread to listen for that basically tells the main UI thread that it is safe to start loading the downloaded images from memory.
According to the discussion from the link above, I came up with this so far... a download listener interace
public interface DataDownloadListener {
void onDownloadStarted();
void onDownloadFinished();
}
an event class...
public class DataDownloadEvent {
ArrayList<DataDownloadListener> listeners = new ArrayList<DataDownloadListener>();
public void setOnDownload(DataDownloadListener listener){
this.listeners.add(listener);
}
}
My problem is that I don't understand where to put the last two steps in those instructions. I thought I would have to put the listener and event inside the class that actually initiates the downloads. But where? Here is my function that initiates the download and saves it to the device:
public String download(String sourceLocation) {
String filename = "";
String path = "";
try {
File externalStorageDirectory = Environment
.getExternalStorageDirectory();
URL urlTmp = new URL(sourceLocation);
filename = urlTmp.getFile()
.substring(filename.lastIndexOf("/") + 1);
path = externalStorageDirectory + PATH;
// check if the path exists
File f = new File(path);
if (!f.exists()) {
f.mkdirs();
}
filename = path + filename;
f = new File(filename);
//only perform the download if the file doesn't already exist
if (!f.exists()) {
Bitmap bitmap = BitmapFactory.decodeStream(urlTmp.openStream());
FileOutputStream fileOutputStream = new FileOutputStream(
filename);
if (bitmap != null) {
bitmap.compress(getFormat(filename), 50, fileOutputStream);
Log.d(TAG, "Saved image " + filename);
return filename;
}
}
else{
Log.d(TAG, "Image already exists: " + filename + " Not re-downloading file.");
}
} catch (MalformedURLException e) {
//bad url
} catch (IOException e) {
//save error
}
return null;
}
And the last step about registering the listener, where do I put that? The instructions say to put that somewhere during initialization. Does that mean in the onCreate method of my main activity? outside the class in the import section of the main activity? Never done a custom event before, so any help would be appreciated.
According to the discussion from the link above, I came up with this so far... a download listener interace
public interface DataDownloadListener {
void onDownloadStarted();
void onDownloadFinished();
}
an event class...
public class DataDownloadEvent {
ArrayList<DataDownloadListener> listeners = new ArrayList<DataDownloadListener>();
public void setOnDownload(DataDownloadListener listener){
this.listeners.add(listener);
}
}
Ok...
Now in your download procedure, at the start of the download, cycle all the elements on the listeners ArrayList and invoke the onDownloadStarted event to inform all your listeners that the download is just started (in this event i presume you'll need to open the splashscreen).
Always in your download procedure, at the and of the download, cycle all the elements on the listeners ArrayList and invoke the onDownloadFinished event to inform all your listeners that the download is finished (now close the splashscreen).
How to cycle listeners on download completed
foreach(DataDownloadListener downloadListener: listeners){
downloadListener.onDownloadFinished();
}
How to cycle listeners on download started
foreach(DataDownloadListener downloadListener: listeners){
downloadListener.onDownloadStarted();
}
Don't make it static if possible... In the class that you'll use to download your files, simply add what you put in your DataDownloadEvent class (listeners arrayList and facility methods for adding and removing). You have no immediate need to use a class in that way (static members I mean).
Example
public class DownloadFileClassExample{
private ArrayList<DataDownloadListener> listeners = new ArrayList<DataDownloadListener>();
public DownloadFileClassExample(){
}
public void addDownloadListener(DataDownloadListener listener){
listeners.add(listener);
}
public void removeDownloadListener(DataDownloadListener listener){
listeners.remove(listener);
}
//this is your download procedure
public void downloadFile(){...}
}
Then access you class in this way
DownloadFileClassExample example = new DownloadFileClassExample();
example.addDownloadListener(this); // if your class is implementing the **DataDownloadListener**
or use
example.addDownloadListener( new DataDownloadListener{...})

How to Subscribe to GUI Events in Other JFrames

What is the best practice for subscribing to events from another JFrame? For example, I have a "settings" form, and when the user presses okay on the settings form, I want the main form to know about this so it can retrieve the settings.
Thanks.
Here is my ideal interface:
public void showSettingsButton_Click() {
frmSettings sForm = new sForm(this._currentSettings);
//sForm.btnOkay.Click = okayButtonClicked; // What to do here?
sForm.setVisible(true);
}
public void okayButtonClicked(frmSettings sForm) {
this._currentSettings = sForm.getSettings();
}
Someone publishes an Event, that something has changed, here the settings. A subscriber that registered for this specifig event, gets notified about it and can do his work, here get the settings. This is called publisher/subscriber.
For this you can use Eventbus or implementing something smaller on your own.
One approach is to have only a single JFrame. All the other 'free floating top level containers' could be modal dialogs. Access the the main GUI will be blocked until the current dialog is dismissed, and the code in the main frame can check the settings of the dialog after it is dismissed.
For anyone interested, here is what I ended up going with. I'm not sure if it's the best way, but it is working for my purposes.
// Method called when the "Show Settings" button is pressed from the main JFrame
private void showSettingsButton_Click() {
// Create new settings form and populate with my settings
frmSettings sForm = new frmSettings(this.mySettings);
// Get the "Save" button and register for its click event...
JButton btnSave = sForm.getSaveButton();
btnSave.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent evt) {
SaveSettings(sForm);
}
});
// Show the settings form
sForm.setVisible(true);
}
// Method called whenever the save button is clicked on the settings form
private void SaveSettings(frmSettings sForm) {
// Get the new settings and assign them to the local member
Settings newSettings = sForm.getSettings();
this.mySettings = newSettings;
}
And if, like me, you are coming from a .NET perspective, here is the C# version:
private void showSettingsButton_Click(object sender, EventArgs e)
{
frmSettings sForm = new frmSettings(this.mySettings);
sForm.btnSave += new EventHandler(SaveSettings);
sForm.Show();
}
private void SaveSettings(object sender, EventArgs e)
{
frmSettings sForm = (frmSettings)sender; // This isn't the exact cast you need..
Settings newSettings = sForm.Settings;
this.mySettings = newSettings;
}

how to write lwuit radio button code

I have my form Welcome on this form i have two radio buttons-Verification and enrollment and a OK button .when user select one of radio buttons and press OK then a form will show but i am not able to do that. Please help.
this is my Statemachine class code:
package userclasses;
import generated.StateMachineBase;
import com.sun.lwuit.*;
import com.sun.lwuit.events.*;
import com.sun.lwuit.RadioButton;
import com.sun.lwuit.Form;
import com.sun.lwuit.util.Resources;
public class StateMachine extends StateMachineBase implements ActionListener {
Resources resources;
RadioButton Verification = new RadioButton("Verification");
RadioButton Enrollment = new RadioButton("Enrollment");
StateMachineBase cl = new StateMachineBase() { };
com.sun.lwuit.ButtonGroup bg=new ButtonGroup();
Form fo, f;
public StateMachine(String resFile) {
super(resFile);
// do not modify, write code in initVars and initialize class members there,
// the constructor might be invoked too late due to race conditions that might occur
}
/**
* this method should be used to initialize variables instead of
* the constructor/class scope to avoid race conditions
*/
StateMachine()
{
try {
resources = Resources.open("/NEW AADHAR.res");
}
catch(java.io.IOException err) {
err.printStackTrace();
}
cl.setHomeForm("Welcome");
fo = (Form)cl.startApp(resources,null,true);
f = cl.findWelcome(fo);
//fo.addCommandListener(this);
Verification = cl.findRadioButton1(f);
Enrollment = cl.findRadioButton(f);
f.addComponent(Verification);
f.addComponent(Enrollment);
//fo.addComponent(bg,null);
bg.add(Enrollment);
bg.add(Verification);
Verification.addActionListener(this);
Enrollment.addActionListener(this);
}
protected void initVars() { }
protected void onWelcome_OKAction(Component c, ActionEvent event) { }
public void actionPerformed(ActionEvent ae) {
throw new UnsupportedOperationException("Not supported yet.");
}
protected boolean onWelcomeEXIT() {
// If the resource file changes the names of components this call will break notifying you that you should fix the code
boolean val = super.onWelcomeEXIT();
return val;
}
protected void onWelcome_ButtonAction(Component c, ActionEvent event) {
// If the resource file changes the names of components this call will break notifying you that you should fix the code
super.onWelcome_RadioButton1Action(c, event);
super.onWelcome_RadioButtonAction(c, event);
if(Verification.hasFocus()) {
showForm("Login",null);
}
else if(Enrollment.hasFocus()) {
showForm("Authentication",null);
}
else {
Dialog.show("INFORMATION","Please select option","OK","CANCEL");
}
}
|
When you generate a netbeans project from the GUI builder the src folder will now contain the res file you need to work with. Whenever you modify the GUI code that StateMachineBase will be regenerated so you can just rename the components in the GUI builder (you can do this by clicking on the tree node and pressing F2 or by selecting the name attribute in the properties table).
The properties table allows you to assign an event for every component that supports it (e.g. radio button action events) which will generate the appropriate callback method in the StateMachine class (write your code only in the StateMachine class).
Radio buttons can be associated with one group by giving them the same group name.
The easiest way to do it is to use Resource Editor. Simply run it from LWUIT/util directory.
To create project using this tool follow each step from this video: http://www.youtube.com/watch?v=HOfb8qiySd8. Be sure to watch it to the end.
It will create 4 Netbeans projects (ProjectName, ProjectName_Desktop, ProjectName_MIDP, ProjectName_RIM). Fix depedencies (most important for ProjectName and _MIDP one) and you can start coding.
File StateMachineBase.java will be located in 'generated' package, which means that it will be regenerated every time you change something in Resource Editor.
Implement everything in StateMachine class ('userclasses' package), but don't create new methods there, use Resource Editor to create them for you: Resource Editor -> GUI Builder (tab on left side) -> Select component -> Events (tab on the right).
Now, if you want to do something for example, you want to change TextField value, you will write something like this:
protected boolean onUstawieniaKontoZapisz() {
// If the resource file changes the names of components this call will break notifying you that you should fix the code //this comment was generated
boolean val = super.onUstawieniaKontoZapisz(); //generated
Form current = Display.getInstance().getCurrent();
TextField login = findUstawieniaKontoLoginTextField(current); //TextField name in Editor is: 'UstawieniaKontoLoginTextField' - a bit long I know, but it's unique
TextField password = findUstawieniaKontoHasloTextField(current); //same here, 'UstawieniaKontoHasloTextField' is second's TextField name
Configuration.setEmail(login.getText()); //Configuration class is used to store preferences
Configuration.setPassword(password.getText());
return val; //generated
}
You can find all 'find*' methods inside StateMachineBase class. There is one for each Component you have added using Resource Editor (GUI Builder tab).
For grouping radio buttons into groups use Resource Editor too, select each radio button and on Properties tab find 'Group' property. Set it to the same word on every radio button you want to have in the same group.

Is it possible to nest forms in Wicket that are independent of each other?

Is it possible to nest forms in Wicket that are independent of each other? I want to have a form with a submit button and a cancel button. Both buttons should direct the user to the same page (let's call it Foo). The submit button should send some info to the server first; the cancel button should do nothing.
Here's a really simplified version of my existing code:
Form form = new Form() {
public void onSubmit()
{
PageParameters params = new PageParameters();
params.put("DocumentID", docID);
setResponsePage(Foo.class, params);
}
};
DropDownChoice<String> ddc = new DropDownChoice<String>("name", new PropertyModel<String>(this, "nameSelection"), names);
ddc.setRequired(true);
final Button submitButton = new Button("Submit") {
public void onSubmit() { doSubmitStuff(true); }
};
final Button cancelButton = new Button("Cancel") {
public void onSubmit() { doSubmitStuff(false); }
};
form.add(ddc);
form.add(submitButton);
form.add(cancelButton);
form.add(new FeedbackPanel("validationMessages"));
The problem is, I just added a validator, and it fires even if I press the cancel button, since the cancel button is attached to the same form as everything else. This could be avoided if the cancel button were in a separate form. As far as I know, I can't create a separate form because — due to the structure of the HTML — the separate form would be under the existing form in the component hierarchy.
Can I make the forms separate somehow in spite of the hierarchy? Or is there some other solution I can use?
EDIT:
In response to Don Roby's comment, this is a bit closer to what my code looked like back when I was trying setDefaultFormProcessing():
Form<Object> theForm = new Form<Object>("theForm") {
public void onSubmit()
{
PageParameters params = new PageParameters();
params.put("DocumentID", docID);
setResponsePage(Foo.class, params);
}
};
final CheckBox checkbox = new CheckBox("checkbox", new PropertyModel<Boolean>(this, "something"));
checkbox.add(new PermissionsValidator());
theForm.add(checkbox);
final Button saveButton = new Button("Save") {
public void onSubmit()
{ someMethod(true); }
};
final Button cancelButton = new Button("Cancel") {
public void onSubmit()
{ someMethod(false); }
};
cancelButton.setDefaultFormProcessing(false);
theForm.add(saveButton);
theForm.add(cancelButton);
theForm.add(new FeedbackPanel("validationMessages"));
There is an even simpler solution: call the setDefaultFormProcessing method on the cancel button with false as a parameter:
cancelButton.setDefaultFormProcessing(false);
This way, clicking the cancel button will bypass the form validation (and model updating), directly calling the onSubmit function.
It is possible to "nest" forms in wicket.
See this wiki entry
for some notes on how it works and this wiki entry for how it interacts with validation.
But for what you're after, the answer from Jawher should have worked and is much simpler.
Look at this example code for hints on getting that working.
I'm wondering if you've simplified your code too far in this posting. Can you produce a sample small enough to post that definitely has the problem?

Categories

Resources