applying autosave features with javafx - javafx

I am creating an application for project management which has various features including saving and opening the saved file. My application is running smoothly but i want to add another feature in the application which will allow it to save the data after some time.
Here is my code for the save and save as functions.
#FXML
private void handleSave() {
File userstoryFile = mainApp.getUserStoryFilePath();
if (userstoryFile != null) {
mainApp.saveUserStoryDataToFile(userstoryFile);
} else {
handleSaveAs();
}
}
/**
* Opens a FileChooser to let the user select a file to save to.
*/
#FXML
private void handleSaveAs() {
FileChooser fileChooser = new FileChooser();
// Set extension filter
FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter(
"XML files (*.xml)", "*.xml");
fileChooser.getExtensionFilters().add(extFilter);
// Show save file dialog
File file = fileChooser.showSaveDialog(mainApp.getPrimaryStage());
if (file != null) {
// Make sure it has the correct extension
if (!file.getPath().endsWith(".xml")) {
file = new File(file.getPath() + ".xml");
}
mainApp.saveUserStoryDataToFile(file);
}
}
Is it possible to add autosave feature here ( using timer function )? if yes, How?
click here to get complete application code

Something like this should be easy to implement using ScheduledExecutorService:
ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
executorService.scheduleWithFixedDelay(() -> mainApp.saveUserStoryDataToFile(file), 0, 1, TimeUnit.MINUTES);
For saving the data with a delay of one minute between saves (no initial delay).
Note that using this approach you need to take care of a few things:
Synchronize access to the data.
Prevent concurrent access to the file.
Making sure shutdown is called when (or before) the JavaFX platform exits.

Related

How to run vlcj in a separate thread in background just to calculate the duration of the media file

I am making a media player using Javafx and VLCJ.
The app contains a list view in which all the Media files. i.e. audio and video that the user selected are listed. Its like a playlist.
I want to show the total Play time of the entire playlist.
I also want to display the duration of individual media at the side.
e.g.
1> my_visiting_video.mp4 | 01:21:35
2> avengers_end_game.flv | 02:13:40
TotalDuration: 03:35:15
above is the sample of my playlist that I would like to display in the app.
Following is what I did
DMediaPlayer.java
import uk.co.caprica.vlcj.factory.MediaPlayerFactory;
import uk.co.caprica.vlcj.javafx.fullscreen.JavaFXFullScreenStrategy;
import uk.co.caprica.vlcj.javafx.videosurface.ImageViewVideoSurface;
import uk.co.caprica.vlcj.player.base.MediaPlayer;
import uk.co.caprica.vlcj.player.base.MediaPlayerEventAdapter;
import uk.co.caprica.vlcj.player.embedded.EmbeddedMediaPlayer;
public class DMediaPlayer implements MediaPlayerInterface {
private ImageView mediaView;
private final MediaPlayerFactory mediaPlayerFactory;
private final EmbeddedMediaPlayer embeddedMediaPlayer;
public DMediaPlayer() {
this.mediaPlayerFactory = new MediaPlayerFactory();
this.embeddedMediaPlayer = mediaPlayerFactory.mediaPlayers().newEmbeddedMediaPlayer();
this.mediaView = new ImageView();
this.mediaView.setPreserveRatio(true);
embeddedMediaPlayer.videoSurface().set(new ImageViewVideoSurface(this.mediaView));
}
#Override
public void load(String filePath) throws FileNotFoundException {
embeddedMediaPlayer.media().startPaused(filePath);
embeddedMediaPlayer.controls().setPosition(0f);
}
#Override
public ImageView getMediaView(){
return mediaView;
}
}
The above class returns a ImageView i.e. DMediaPlayer().getMediaView()
and this ImageView is added inside another class that controls all the playbacks, As well as that class has the playlist. For now the playlist only displays the available media files audio/video.
It works fine.
But I want to add a new feature in which the list view not only displays the media files but also some additional information like total play time of the entire playlist and the play time of individual media file.
following is what I did.
File[] mediaList; // it is initialized in the constructor
private void updatePlayList(){
CompletableFuture.runAsync(() -> {
long totalDur = 0;
MediaPlayerFactory factory = new MediaPlayerFactory();
EmbeddedMediaPlayer player = factory.mediaPlayers().newEmbeddedMediaPlayer();
for (int i = 0; i < mediaList.length; i++){
long mediaDur = 0;
player.media().startPaused(mediaList[i].getAbsolutePath());
mediaDur = player.status().length();
totalDur += mediaDur;
drawer.addItem(
(i+1) + "> " +
mediaList[i].getName().substring(0, 25) + "..." +
" | " + millisToDuration(mediaDur)
);
}
drawer.getTotalDuration().setText("Total Duration: " + totalDur);
});
}
drawer is a class that controls ListView.
everything works fine except the vlcj.
It throws the following error.
../include/vlc_xlib.h:46:vlc_xlib_init: Xlib not initialized for threads.
This process is probably using LibVLC incorrectly.
Pass "--no-xlib" to libvlc_new() to fix this.
Exception in thread "ForkJoinPool.commonPool-worker-3" java.lang.IllegalStateException: Not on FX application thread; currentThread = ForkJoinPool.commonPool-worker-3
at javafx.graphics#19/com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:299)
at javafx.graphics#19/com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:458)
at javafx.graphics#19/javafx.scene.Parent$3.onProposedChange(Parent.java:474)
at javafx.base#19/com.sun.javafx.collections.VetoableListDecorator.setAll(VetoableListDecorator.java:113)
at javafx.base#19/com.sun.javafx.collections.VetoableListDecorator.setAll(VetoableListDecorator.java:108)
at javafx.controls#19/javafx.scene.control.skin.LabeledSkinBase.updateChildren(LabeledSkinBase.java:282)
at javafx.controls#19/javafx.scene.control.skin.LabeledSkinBase.lambda$new$11(LabeledSkinBase.java:219)
at javafx.controls#19/com.sun.javafx.scene.control.LambdaMultiplePropertyChangeListenerHandler.lambda$new$1(LambdaMultiplePropertyChangeListenerHandler.java:88)
at javafx.base#19/javafx.beans.value.WeakChangeListener.changed(WeakChangeListener.java:86)
at javafx.base#19/com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:181)
at javafx.base#19/com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:80)
at javafx.base#19/javafx.beans.property.StringPropertyBase.fireValueChangedEvent(StringPropertyBase.java:104)
at javafx.base#19/javafx.beans.property.StringPropertyBase.markInvalid(StringPropertyBase.java:111)
at javafx.base#19/javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:145)
at javafx.base#19/javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:50)
at javafx.base#19/javafx.beans.property.StringProperty.setValue(StringProperty.java:71)
at javafx.controls#19/javafx.scene.control.Labeled.setText(Labeled.java:147)
at com.doruk.dplayer.controllers.PlayerController.lambda$updatePlayList$2(PlayerController.java:108)
at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1804)
at java.base/java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1796)
at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:373)
at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1182)
at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1655)
at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1622)
at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:165)
Now how can I fix this error, or how can I run the vlcj in the background?
I mentioned background because, additional window opens that plays the video when I run the app.
or am I following the wrong approach...? is there better way...?
all I want is to display the playtime of each media in the list.
I went through several pages in this website but all I could find was to get the total time of the file currently being played. So I followed the above approach. i.e. create a separate media player and start it and then calculate the play time.

Allow user to copy data from TableView

I have a simple JavaFX app that allows the user to query a database and see the data in a table.
I'd like to allow the user to be able to click a table cell and copy text from that cell to the clipboard with the standard clipboard key stroke: ctrl-c for Win/Linux or cmd-c for Mac. FYI, the text entry controls support basic copy/paste by default.
I'm using the standard javafx.scene.control.TableView class. Is there a simple way to enable cell copy? I did some searches and I see other people create custom menu commands... I don't want to create a custom menu, I just want basic keyboard copy to work with single cells.
I'm using single selection mode, but I can change to something else if need be:
TableView<Document> tableView = new TableView<Document>();
tableView.getSelectionModel().setCellSelectionEnabled(true);
tableView.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
You just have to create a listener in the scene, something like:
scene.getAccelerators()
.put(new KeyCodeCombination(KeyCode.C, KeyCombination.CONTROL_ANY), new Runnable() {
#Override
public void run() {
int row = table.getSelectionModel().getSelectedIndex();
DataRow tmp = table.getItems().get(row);
final Clipboard clipboard = Clipboard.getSystemClipboard();
final ClipboardContent content = new ClipboardContent();
if(table.getSelectionModel().isSelected(row, numColumn)){
System.out.println(tmp.getNumSlices());
content.putString(tmp.getNumSlices().toString());
}
else{
System.out.println(tmp.getSelected());
content.putString(tmp.getSelected());
}
clipboard.setContent(content);
}
});
For a complete example, you can download it at the gist.
I recommended that you review this post, work for me
http://respostas.guj.com.br/47439-habilitar-copypaste-tableview-funcionando-duvida-editar-funcionalidade
The author use an aditional util java class for enable the cell content copy from a tableView

Flex mobile : how to know it is the very first time running the application

I googled but didn't find a post for Flex mobile..
All I want for now is display an user agreement popup from TabbedViewNavigatorApplication when the user uses the app for the first time
var agreementView: UserAgreement = new UserAgreement();
PopUpManager.addPopUp(agreementView, this,true);
PopUpManager.centerPopUp(agreementView);
but maybe more later.
Please help..
What i did in my desktop air app;
I guess this will work at a mobile app also.
Make sure you have write access;
open yourproject-app.mxml scroll down to the end of the document. In the section, uncomment the following permission:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
Now you can create files like for example an sqlite database.
At the applicationcomplete call the function checkFirstRun:
// functions to check if the application is run for the first time (also after updates)
// so important structural changes can be made here.
public var file:File;
public var currentVersion:Number;
private function checkFirstRun():void
{
//get the current application version.
currentVersion=getApplicationVersion();
//get versionfile
file = File.applicationStorageDirectory;
file= file.resolvePath("Preferences/version.txt");
if(file.exists)
{
checkVersion();
}
else
{
firstRun(); // create the version file.
}
}
public function getApplicationVersion():Number
{
var appXML:XML = NativeApplication.nativeApplication.applicationDescriptor;
var ns:Namespace = appXML.namespace();
var versionnumber:Number =Number(appXML.ns::versionNumber);
return versionnumber;
}
private function checkVersion():void
{
var stream:FileStream= new FileStream();
stream.open(file,FileMode.READ);
var prevVersion:String = stream.readUTFBytes(stream.bytesAvailable);
stream.close();
if(Number(prevVersion)<currentVersion)
{
// if the versionnumber inside the file is older than the current version we go and run important code.
// like alternating the structure of tables inside the sqlite database file.
runImportantCode();
//after running the important code, we set the version to the currentversion.
saveFile(currentVersion);
}
}
private function firstRun():void
{
// at the first time, we set the file version to 0, so the important code will be executed.
var firstVersion:Number=0;
saveFile(firstVersion);
// we also run the checkversion so important code is run right after installing an update
//(and the version file doesn't exist before the update).
checkFirstRun();
}
private function saveFile(currentVersion:Number):void
{
var stream:FileStream=new FileStream();
stream.open(file,FileMode.WRITE);
stream.writeUTFBytes(String(currentVersion));
stream.close();
}
private function runImportantCode():void
{
// here goes important code.
// make sure you check if the important change previously has been made or not, because this code is run after each update.
}
Hope this helps.
Greets, J.
Some you need to store whether the user has agreed to the agreement or not. IF they haven't agreed, then show it.
One way to do this would be to store a value in a shared object. Another way to do this would be to use a remote service and store such data in a central repository. I assume you'll want the second; so you can do some form of tracking against the number of users using your app.

application becomes unresponsive during directory listing in flex 3

I have designed an AIR application that displays a list with all txt files located in C:\ directory. I have used following code:
var videoListsArr:ArrayList = new ArrayList();
var folder:File = new File(driveName+":\\");
folder.getDirectoryListingAsync();
folder.addEventListener( FileListEvent.DIRECTORY_LISTING, handleDirectoryListing );
private function handleDirectoryListing( event:FileListEvent ):void
{
for each(var item:File in event.files)
{
var itemExtn:String = (item.extension != null) ? item.extension.toLocaleLowerCase() : null;
if(item.isDirectory)
{
item.getDirectoryListingAsync();
item.addEventListener( FileListEvent.DIRECTORY_LISTING, handleDirectoryListing );
}
else if(!item.isSymbolicLink && itemExtn != null)
{
if(itemExtn == "txt")
videoListsArr.addItem(txt);
}
}
}
This function works fine but it is being executed the application is hang and become unresponsive. Please tell me how to resolve this problem that it displays the list of txt file without making application unresponsive ?
from what i can see, you're getting a directory, looking at all the files in there, and if one of them is a directory, you look at all the files etc recursively, which could end up being very heavy depending on the start folder. remove the first if check and tell me if it's still unresponsive
I use a timer to break up the processing. My timer subclasses Timer to add a files:Array and a directories : Array.
The initial handler that processed the top-level directory (in my case, loadDirectoryHandler) loads the timer.files with all the files it cannot processes immediately as well as all the sub-directories,
On each cycle through the TIMER handler, it slices out a chunk of the files Array (e.g. 200 files), processes that, and then starts the timer if there are any left so they get processed on the next TIMER event.
For each directory, it takes the File object and
file.addEventListener( FileListEvent.DIRECTORY_LISTING, loadDirectoryHandler );
file.getDirectoryListingAsync();
Cheers

Flex 3 multiple upload progress monitoring

I have a Flex3 application which has to be capable of uploading multiple files and monitoring each files individual progress using a label NOT a progress bar.
My problem is that a generic progress handler for the uploads has no way (that I know of) of indicating WHICH upload it is that is progressing. I know that a file name is available to check but in the case of this app the file name might be the same for multiple uploads.
My question: With a generic progress handler how does one differentiate between 2 multiple uploads with the same file name?
EDIT: answerers may assume that I am a total newb to Flex... because I am.
I use this:
private function _addFileListeners(dispatcher:IEventDispatcher):void {
dispatcher.addEventListener(Event.OPEN, this._handleFileOpen);
dispatcher.addEventListener(Event.SELECT, this._handleFileOpen);
dispatcher.addEventListener(Event.CANCEL, this._handleFileCancel);
dispatcher.addEventListener(ProgressEvent.PROGRESS, this._handleFileProgress);
dispatcher.addEventListener(DataEvent.UPLOAD_COMPLETE_DATA,this._handleFileComplete);
dispatcher.addEventListener(IOErrorEvent.IO_ERROR, this._handleError);
dispatcher.addEventListener(SecurityErrorEvent.SECURITY_ERROR, this._handleError);
}
where "dispatcher" is the file:
for (var i:uint = 0; i < fileList.length; i++) {
file = FileReference(fileList[i]);
this._addFileListeners(file);
this._pendingFiles.push(file);
}
and a sample handler:
private function _handleFileOpen(e:Event):void {
var file:FileReference = FileReference(e.target);
...
}
I'm not sure how you want to differentiate between two files with the same name. In my case, I send the files in a queue. So there's only ever 1 file being uploaded at a time. (pendingFiles).
If you are listening for ProgressEvents, these events have a currentTarget attribute that would have a reference to the object that has registered the event listener.
I'm assuming you know which file-uploading object goes with each object in the first place.
EDIT: Example using FileReference:
import flash.net.FileReference;
import flash.events.ProgressEvent;
import flash.utils.Dictionary;
public var files:Dictionary = new Dictionary(); // This will hold all the FileReference objects
public function loadFile(id:String):void
{
var file:FileReference = new FileReference();
// Listen for the progress event on this FileReference... will call the same function for every progress event
file.addEventListener(ProgressEvent.PROGRESS, onProgress);
// TODO: listen for errors and actually upload a file, etc.
// Add file to the dictionary (as key), with value set to an object containing the id
files[file] = { 'id': id };
}
public function onProgress(event:ProgressEvent):void
{
// Determine which FileReference dispatched thi progress event:
var file:FileReference = FileReference(event.target);
// Get the ID of the FileReference which dispatched this function:
var id:String = files[file].id;
// Determine the current progress for this file (in percent):
var progress:Number = event.bytesLoaded / event.bytesTotal;
trace('File "' + id + '" is ' + progress + '% done uploading');
}
// Load some files:
loadFile('the first file');
loadFile('the second file');
I ended up creating my own class that manages events for each uploading file

Resources