Display progress bar while compressing video file - javafx

I have to compress video files. So I used this link http://whaticode.com/tag/audio/ and xuggler for the compression. Now I want to show the progress bar while compressing the video file in javafx.
import java.io.File;
import com.xuggle.mediatool.IMediaReader;
import com.xuggle.mediatool.IMediaWriter;
import com.xuggle.mediatool.MediaToolAdapter;
import com.xuggle.mediatool.ToolFactory;
import com.xuggle.mediatool.event.AudioSamplesEvent;
import com.xuggle.mediatool.event.IAddStreamEvent;
import com.xuggle.mediatool.event.IAudioSamplesEvent;
import com.xuggle.mediatool.event.IVideoPictureEvent;
import com.xuggle.mediatool.event.VideoPictureEvent;
import com.xuggle.xuggler.IAudioResampler;
import com.xuggle.xuggler.IAudioSamples;
import com.xuggle.xuggler.IRational;
import com.xuggle.xuggler.IStreamCoder;
import com.xuggle.xuggler.IVideoPicture;
import com.xuggle.xuggler.IVideoResampler;
import com.xuggle.xuggler.ICodec;
public class ConvertVideo extends MediaToolAdapter implements Runnable{
private int VIDEO_WIDTH = 640;
private int VIDEO_HEIGHT = 360;
private IMediaWriter writer;
private IMediaReader reader;
private File outputFile;
public ConvertVideo(File inputFile, File outputFile) {
this.outputFile = outputFile;
reader = ToolFactory.makeReader(inputFile.getAbsolutePath());
reader.addListener(this);
}
private IVideoResampler videoResampler = null;
private IAudioResampler audioResampler = null;
#Override
public void onAddStream(IAddStreamEvent event) {
int streamIndex = event.getStreamIndex();
IStreamCoder streamCoder = event.getSource().getContainer().getStream(streamIndex).getStreamCoder();
if (streamCoder.getCodecType() == ICodec.Type.CODEC_TYPE_AUDIO) {
writer.addAudioStream(streamIndex, streamIndex, 2, 44100);
} else if (streamCoder.getCodecType() == ICodec.Type.CODEC_TYPE_VIDEO) {
streamCoder.setWidth(VIDEO_WIDTH);
streamCoder.setHeight(VIDEO_HEIGHT);
streamCoder.setBitRate(100);
streamCoder.setBitRateTolerance(100);
writer.addVideoStream(streamIndex, streamIndex, ICodec.ID.CODEC_ID_H264,IRational.make((double)15),VIDEO_WIDTH, VIDEO_HEIGHT);
}
super.onAddStream(event);
}
#Override
public void onVideoPicture(IVideoPictureEvent event) {
IVideoPicture pic = event.getPicture();
if (videoResampler == null) {
videoResampler = IVideoResampler.make(VIDEO_WIDTH, VIDEO_HEIGHT, pic.getPixelType(), pic.getWidth(), pic.getHeight(), pic.getPixelType());
}
IVideoPicture out = IVideoPicture.make(pic.getPixelType(), VIDEO_WIDTH, VIDEO_HEIGHT);
videoResampler.resample(out, pic);
IVideoPictureEvent asc = new VideoPictureEvent(event.getSource(), out, event.getStreamIndex());
super.onVideoPicture(asc);
out.delete();
}
#Override
public void onAudioSamples(IAudioSamplesEvent event) {
IAudioSamples samples = event.getAudioSamples();
if (audioResampler == null) {
audioResampler = IAudioResampler.make(2, samples.getChannels(), 44100, samples.getSampleRate());
}
if (event.getAudioSamples().getNumSamples() > 0) {
IAudioSamples out = IAudioSamples.make(samples.getNumSamples(), samples.getChannels());
audioResampler.resample(out, samples, samples.getNumSamples());
AudioSamplesEvent asc = new AudioSamplesEvent(event.getSource(), out, event.getStreamIndex());
super.onAudioSamples(asc);
out.delete();
}
}
public void run() {
writer = ToolFactory.makeWriter(outputFile.getAbsolutePath(), reader);
this.addListener(writer);
while (reader.readPacket() == null) {
System.out.println("Converting file..");
}
}
public static void main(String[] args) {
try {
System.out.println("Converting process started");
File file = new File("C:\\Development\\1.mp4");
file.createNewFile();
ConvertVideo obj = new ConvertVideo(new File("C:\\Development\\camera 1_record_1417702745727.wmv"),file);
obj.run();
System.out.println("Converting process end");
} catch (Exception e) {
e.printStackTrace();
}
}
}
Thanks for any help!

I would suggest that you read this doc from oracle. It describes the basics of Concurrency in JavaFX.

Use a Task or a Service instead of a Runnable.
Use Task.updateProgress() to inform on the current progress / work done.
Bind the progressProperty of your running Task to a ProgressBar or ProgressIndicator.

Related

Restarting and pausing and resuming clip hangs the gui of music player, while pressing pause and play resumes playing from stopping point

This program is a music player that allows user to pick a .wav file, play, pause, resume, and restart a the music file from a clip object and audioinput stream. The audio input stream loads a file that is determined by user via FileChooser. The program can play, pause, and resume by selecting a file, pressing play, pause, then play again, but does not play using the restart method or the resume method invoked via the respective buttons. Instead, the program hangs until the X button is clicked. I think it has something to do with the resetaudiostream method, but I am unsure what. Maybe something to do with ending the old clip and creating a new clip instance. Please review the logic and let me know what is making it hang and how that could be remedied.
package sample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import javax.sound.sampled.*;
import java.io.File;
import java.io.IOException;
public class Main extends Application {
static File musicfile;
static Long currentFrame;
static Clip clip;
static String status = "play";
static AudioInputStream audioInputStream;
static String filePath;
public void SimpleAudioPlayer()
throws UnsupportedAudioFileException,
IOException, LineUnavailableException
{
// create AudioInputStream object
audioInputStream =
AudioSystem.getAudioInputStream(new File(filePath).getAbsoluteFile());
// create clip reference
clip = AudioSystem.getClip();
// open audioInputStream to the clip
clip.open(audioInputStream);
clip.loop(Clip.LOOP_CONTINUOUSLY);
}
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setTitle("Music Player");
GridPane gp = new GridPane();
Button selectFile = new Button("Select File");
GridPane.setConstraints(selectFile, 0,0);
selectFile.setOnAction(event->{
FileChooser filechooser = new FileChooser();
// create AudioInputStream object
try {
musicfile = filechooser.showOpenDialog(null);
audioInputStream = AudioSystem.getAudioInputStream(musicfile);
clip = AudioSystem.getClip();
// open audioInputStream to the clip
clip.open(audioInputStream);
}catch(IOException | UnsupportedAudioFileException | LineUnavailableException e){
e.printStackTrace();
}
});
Button play = new Button("Play");
GridPane.setConstraints(play, 1,0);
play.setOnAction(event->{
if(status == "play") {
clip.loop(Clip.LOOP_CONTINUOUSLY);
}
play();
});
Button pause = new Button("Pause");
GridPane.setConstraints(pause, 2,0);
pause.setOnAction(event -> pause());
Button restart = new Button("Restart");
GridPane.setConstraints(restart, 0,1);
restart.setOnAction(event -> {
try{
restart();
}
catch(IOException | UnsupportedAudioFileException | LineUnavailableException e){
e.printStackTrace();}
});
Button resume = new Button("Resume");
GridPane.setConstraints(resume, 1,1);
resume.setOnAction(event -> {
try {
resumeAudio();
}catch(IOException | LineUnavailableException | UnsupportedAudioFileException e){
e.printStackTrace();
}
});
gp.getChildren().addAll(play,selectFile, pause, restart, resume);
primaryStage.setScene(new Scene(gp, 300, 275));
primaryStage.show();
}
public void play()
{
//start the clip
clip.start();
status = "play";
}
// Method to pause the audio
public void pause()
{
if (status.equals("paused"))
{
System.out.println("audio is already paused");
return;
}
currentFrame =
clip.getMicrosecondPosition();
clip.stop();
status = "paused";
}
// Method to resume the audio
public void resumeAudio() throws UnsupportedAudioFileException,
IOException, LineUnavailableException
{
if (status.equals("play"))
{
System.out.println("Audio is already "+
"being played");
return;
}
clip.close();
resetAudioStream();
clip.setMicrosecondPosition(currentFrame);
status = "play";
play();
}
// Method to restart the audio
public void restart() throws IOException, LineUnavailableException,
UnsupportedAudioFileException
{
clip.stop();
clip.close();
resetAudioStream();
currentFrame = 0L;
clip.setMicrosecondPosition(0);
status = "play";
play();
}
// Method to stop the audio
public void stop() throws UnsupportedAudioFileException,
IOException, LineUnavailableException
{
currentFrame = 0L;
clip.stop();
clip.close();
}
// Method to jump over a specific part
public void jump(long c) throws UnsupportedAudioFileException, IOException,
LineUnavailableException
{
if (c > 0 && c < clip.getMicrosecondLength())
{
clip.stop();
clip.close();
resetAudioStream();
currentFrame = c;
clip.setMicrosecondPosition(c);
this.play();
}
}
// Method to reset audio stream
public void resetAudioStream() throws UnsupportedAudioFileException, IOException,
LineUnavailableException
{
audioInputStream = AudioSystem.getAudioInputStream(musicfile);
clip = AudioSystem.getClip();
clip.open(audioInputStream);
clip.loop(Clip.LOOP_CONTINUOUSLY);
}
public static void main(String[] args) {
launch(args);
}
}
It is quiet simple to get the required functionality with a MediaPlayer:
import java.net.URI;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.GridPane;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.scene.media.MediaPlayer.Status;
import javafx.stage.Stage;
import javafx.util.Duration;
/*
* If you get "cannot access class com.sun.glass.utils.NativeLibLoader" exception you may need to
* add a VM argument: --add-modules javafx.controls,javafx.media as explained here:
* https://stackoverflow.com/questions/53237287/module-error-when-running-javafx-media-application
*/
public class Main extends Application {
private MediaPlayer player;
private static final long JUMP_BY = 5000;//millis
#Override
public void start(Stage primaryStage) throws Exception{
URI uri = new URI("https://www.soundhelix.com/examples/mp3/SoundHelix-Song-5.mp3");
Media media = new Media(uri.toString());
//OR Media media = new Media("https://www.soundhelix.com/examples/mp3/SoundHelix-Song-5.mp3");
player = new MediaPlayer(media);
player.setOnError(() -> System.out.println(media.getError().toString()));
GridPane gp = new GridPane();
gp.setHgap(10);
Button play = new Button("Play");
GridPane.setConstraints(play, 0,0);
play.setOnAction(event-> playAudio());
Button pause = new Button("Pause");
GridPane.setConstraints(pause, 1,0);
pause.setOnAction(event -> pauseAudio());
Button resume = new Button("Resume");
GridPane.setConstraints(resume, 2,0);
resume.setOnAction(event -> resumeAudio());
Button stop = new Button("Stop");
GridPane.setConstraints(stop, 3,0);
stop.setOnAction(event -> stopAudio());
Button restart = new Button("Restart");
GridPane.setConstraints(restart, 4,0);
restart.setOnAction(event -> restartAudio());
Button jump = new Button("Jump >");
GridPane.setConstraints(jump, 5,0);
jump.setOnAction(event -> jump(JUMP_BY));
Label time = new Label();
GridPane.setConstraints(time, 6,0);
time.textProperty().bind( player.currentTimeProperty().asString("%.4s") );
gp.getChildren().addAll(play, pause, resume, stop, restart, jump, time);
primaryStage.setScene(new Scene(gp, 400, 45));
primaryStage.show();
}
//play audio
public void playAudio()
{
player.play();
}
//pause audio
public void pauseAudio()
{
if (player.getStatus().equals(Status.PAUSED))
{
System.out.println("audio is already paused");
return;
}
player.pause();
}
//resume audio
public void resumeAudio()
{
if (player.getStatus().equals(Status.PLAYING))
{
System.out.println("Audio is already playing");
return;
}
playAudio();
}
//restart audio
public void restartAudio()
{
player.seek(Duration.ZERO);
playAudio();
}
// stop audio
public void stopAudio()
{
player.stop();
}
//jump by c millis
public void jump(long c)
{
player.seek(player.getCurrentTime().add(Duration.millis(c)));
}
public static void main(String[] args) {
launch(args);
}
}

I can't make jar file that is using RXTX

I made the following source code and succeed in communicating via RS232.
Then, I attempted to make jar file the source code.I can make the runnable jar file. But it couldn't execute the program when I execute double click in another PC(In case that I used my PC, The jar file executes properly.).
What should I do to resolve the problem.
I read some involved question, but I do't know what I should do.
So, please let me know the detail procedure how to make jar file.
[I have made jar file of other program before but then it didn't include external jar file. In this time, I used a jar file(RxTxcomm.jar) and two dll file to make the program . so I don't know the way of linking one another.]
My source code
[MyRxTx.java]
package sample;
import javafx.application.Platform;
import gnu.io.CommPortIdentifier;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
public class MyRxTx implements SerialPortEventListener {
SerialPort serialPort;
Main sample_serial;
public byte[] SendBuffer;
/**
* The port we're normally going to use.
*/
private final String PORT_NAMES[] = {
"COM29",
"COM30",
"COM31",
};
private InputStream inputStream;
private OutputStream outputStream;
private static final int TIME_OUT = 2000;
private static final int Baud_Rate = 115200;
public void initialize() throws InterruptedException {
CommPortIdentifier portId = null;
Enumeration portEnum = CommPortIdentifier.getPortIdentifiers();
//First, Find an instance of serial port as set in PORT_NAMES.
while (portEnum.hasMoreElements()) {
CommPortIdentifier currPortId
= (CommPortIdentifier) portEnum.nextElement();
for (String portName : PORT_NAMES) {
if (currPortId.getName().equals(portName)) {
portId = currPortId;
break;
}
}
}
if (portId == null) {
System.out.println("Could not find COM port.");
return;
} else {
System.out.println("Port Name: " + portId.getName() + "\n"
+ "Current Owner: " + portId.getCurrentOwner() + "\n"
+ "Port Type: " + portId.getPortType());
}
try {
// open serial port, and use class name for the appName.
serialPort = (SerialPort) portId.open(this.getClass().getName(),
TIME_OUT);
// set port parameters
serialPort.setSerialPortParams(
Baud_Rate,
SerialPort.DATABITS_8,
SerialPort.STOPBITS_1,
SerialPort.PARITY_NONE);
// open the streams
inputStream = serialPort.getInputStream();
outputStream = serialPort.getOutputStream();
serialPort.addEventListener(this);
serialPort.notifyOnDataAvailable(true);
} catch (Exception e) {
System.err.println(e.toString());
}
SendBuffer=new byte[]{(byte)0x41,(byte)0x54};
write(SendBuffer);
}
#Override
public void serialEvent(SerialPortEvent Serial_event){
int received_data = 0;
StringBuffer buffer = new StringBuffer();
if(Serial_event.getEventType() == SerialPortEvent.DATA_AVAILABLE){
while(true){
try{
received_data = inputStream.read();
if(received_data == -1) break;
if((char)received_data != '\r'){
buffer.append((char)received_data);
}
else{
buffer.append('\n');
break;
}
}
catch(IOException ex){}
}//while()文ここまで。
System.out.println("Receive Data:"+buffer);
Platform.runLater( () ->{
sample_serial.label_receive.setText("Receive Data:"+buffer);
});
}
}
protected void write(byte[] buffer) throws InterruptedException{
try {
outputStream.write(buffer);
outputStream.flush();
} catch (IOException e) {
System.err.println("Cannot write:" + e.getMessage());
}
}
/**
* This should be called when you stop using the port. This will prevent
* port locking on platforms like Linux.
*/
public synchronized void close() {
if (serialPort != null) {
serialPort.removeEventListener();
serialPort.close();
}
}
}
[Main.java]
package sample;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Pos;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.HBox;
import javafx.scene.layout.HBoxBuilder;
import javafx.scene.layout.VBox;
import javafx.scene.layout.VBoxBuilder;
import javafx.scene.text.Font;
import javafx.scene.text.FontPosture;
import javafx.stage.Stage;
import javafx.stage.StageBuilder;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Main extends Application {
MyRxTx myRxTx;
private TextField text_Send;
public static Label label_receive;
#Override
public void start(Stage primaryStage) throws InterruptedException {
Button btn_send = ButtonBuilder.create().text("Send")
.prefWidth(100).alignment(Pos.CENTER)
.id("Send").build();
text_Send = TextFieldBuilder.create().text("Sending message").alignment(Pos.CENTER)
.prefWidth(150).build();
Font Font_receive = Font.font("Arial", FontPosture.REGULAR,20);
label_receive = LabelBuilder.create().text("Recieved message")
.alignment(Pos.TOP_CENTER).prefWidth(400).font(Font_receive).build();
HBox root1 = HBoxBuilder.create().spacing(100).children(text_Send,btn_send).build();
HBox root2 = HBoxBuilder.create().spacing(100).children(label_receive).build();
VBox root3 = VBoxBuilder.create().spacing(15).children(root1,root2).build();
Scene scene = new Scene(root3);
myRxTx = new MyRxTx();
myRxTx.initialize();
scene.addEventHandler(ActionEvent.ACTION,actionHandler);
primaryStage = StageBuilder.create().width(410).height(340).scene(scene).title("Serial Communication Test Tool").build();
primaryStage.show();
}
EventHandler<ActionEvent> actionHandler = new EventHandler<ActionEvent>(){
public void handle (ActionEvent e){
Button src =(Button)e.getTarget();
String text = src.getId();
System.out.println("Select Button:"+text);
String text_send = text_Send.getText();
if(text.equals("Send")){
try {
myRxTx.write(text_send.getBytes());
} catch (InterruptedException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
};
/* main*/
public static void main(String[] args) {
launch(args);
}
}

Correct sizing of Webview embedded in Tabelcell

As mentioned here and here there is no easy way to determine the required height of a webview, until "RT-25005 Automatic preferred sizing of WebView" is implemented.
Are there any workarounds to this issue? I couldn't find a solution in SO or elsewhere. However since i think this is no uncommon problem, there needs to be a workaround. Any idea?
For Webviewsembeded in a stage I found the following solution (see here):
webView.getEngine().executeScript(
"window.getComputedStyle(document.body, null).getPropertyValue('height')"
);
or
Double.parseDouble(webView.getEngine().executeScript("document.height").toString())
However this doesn't seem to work for Webviews embedded in a treecell, like here. In this case I always get too big numbers as a result.
Minimal running example (including recommendation of jewelsea):
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Application;
import javafx.application.Application;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.Region;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import javafx.util.Callback;
import org.w3c.dom.Document;
public class TableViewSampleHTML extends Application {
private final ObservableList<MyData> data = FXCollections.observableArrayList(new MyData(1L), new MyData(3L), new MyData(2L), new MyData(4L), new MyData(1L));
public static void main(final String[] args) {
launch(args);
}
#Override
public void start(final Stage stage) {
final Scene scene = new Scene(new Group());
TableView<MyData> table = new TableView<>();
table.setPrefHeight(700);
final TableColumn<MyData, Long> nameCol = new TableColumn("So So");
nameCol.setMinWidth(200);
nameCol.setCellValueFactory(new PropertyValueFactory<>("i"));
// Allow to display Textflow in Column
nameCol.setCellFactory(new Callback<TableColumn<MyData, Long>, TableCell<MyData, Long>>() {
#Override
public TableCell<MyData, Long> call(TableColumn<MyData, Long> column) {
return new TableCell<MyData, Long>() {
#Override
protected void updateItem(Long item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText(null);
setGraphic(null);
setStyle("");
} else {
WebView webview = new WebView();
webview.setPrefWidth(700.0);
WebEngine engine = webview.getEngine();
String textHTML = new String(new char[item.intValue()]).replace("\0", " <b> bold </b> normal, ");
// textHTML = "<body>"
// + textHTML + "</body>";
engine.loadContent(textHTML);
setGraphic(webview);
engine.documentProperty().addListener((obj, prev, newv) -> {
String heightText = engine.executeScript(
"window.getComputedStyle(document.body, null).getPropertyValue('height')"
).toString();
System.out.println("heighttext: " + heightText);
webview.setPrefHeight(Double.parseDouble(heightText.replace("px", "")));
this.setPrefHeight(Double.parseDouble(heightText.replace("px", "")));
setGraphic(webview);
});
}
}
};
}
});
table.setItems(data);
table.getColumns().addAll(nameCol);
((Group) scene.getRoot()).getChildren().addAll(table);
stage.setScene(scene);
stage.show();
}
public static class MyData {
private Long i;
public MyData(Long i) {
this.i = i;
}
public Long getI() {
return i;
}
}
}
Now the outout is
heighttext: 581px
heighttext: 581px
However these values seem to be too big. See screeenshot:
Some progress has been made and cell heights are now calculated more realisticly. Kindly see the relevant the adapted code below.
Relevant changens:
Seems like it is mandatory to call webview.setPrefHeight(-1); before executing the jevascript.
Javascript has been modified. No big change seen, but maybe the result is more general
Open points:
For some reason i still have to add + 15.0 to the calculated height. This is a hack. Seems like some additional lenght has to be considered somewhere.
Functionality on recalculation after resize of column has is not optimal. Using table.refresh() causes significant delay in rendering.
public class TableViewSampleHTML extends Application {
private final ObservableList<MyData> data = FXCollections.observableArrayList(new MyData(1L), new MyData(14L), new MyData(2L), new MyData(3L), new MyData(15L));
public static void main(final String[] args) {
launch(args);
}
#Override
public void start(final Stage stage) {
final Scene scene = new Scene(new Group(), 400, 800);
TableView<MyData> table = new TableView<>();
table.setPrefWidth(400);
table.setPrefHeight(800);
final TableColumn<MyData, Long> nameCol = new TableColumn("So So");
final TableColumn<MyData, Long> col2 = new TableColumn("la la");
nameCol.setPrefWidth(200);
col2.setCellValueFactory(new PropertyValueFactory<>("i"));
nameCol.setCellValueFactory(new PropertyValueFactory<>("i"));
nameCol.widthProperty().addListener((ob,oldV,newV) -> {table.refresh();} );
// Allow to display Textflow in Column
nameCol.setCellFactory(new Callback<TableColumn<MyData, Long>, TableCell<MyData, Long>>() {
#Override
public TableCell<MyData, Long> call(TableColumn<MyData, Long> column) {
return new TableCell<MyData, Long>() {
#Override
protected void updateItem(Long item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText(null);
setGraphic(null);
setStyle("");
} else {
WebView webview = new WebView();
WebEngine engine = webview.getEngine();
webview.setPrefHeight(-1); // <- Absolute must at this position (before calling the Javascript)
setGraphic(webview);
String textHTML = new String(new char[item.intValue()]).replace("\0", " <b> bold </b> normal, ");
textHTML = "<body>"
+ textHTML + "</body>";
engine.loadContent(textHTML);
engine.documentProperty().addListener((obj, prev, newv) -> {
String heightText = engine.executeScript( // <- Some modification, which gives moreless the same result than the original
"var body = document.body,"
+ "html = document.documentElement;"
+ "Math.max( body.scrollHeight , body.offsetHeight, "
+ "html.clientHeight, html.scrollHeight , html.offsetHeight );"
).toString();
System.out.println("heighttext: " + heightText);
Double height = Double.parseDouble(heightText.replace("px", "")) + 15.0; // <- Why are this 15.0 required??
webview.setPrefHeight(height);
this.setPrefHeight(height);
});
}
}
};
}
});
table.setItems(data);
table.getColumns().addAll(nameCol);
table.getColumns().addAll(col2);
((Group) scene.getRoot()).getChildren().addAll(table);
stage.setScene(scene);
stage.show();
}
public static class MyData {
private Long i;
public MyData(Long i) {
this.i = i;
}
public Long getI() {
return i;
}
}
}
Output now looks like:
From the example you linked (JavaFX webview, get document height) the height of the document is computed in a ChangeListener on the document:
engine.documentProperty().addListener((prop, oldDoc, newDoc) -> {
String heightText = engine.executeScript(
"window.getComputedStyle(document.body, null).getPropertyValue('height')"
).toString();
System.out.println("heighttext: " + heightText);
});
Output:
heighttext: 36px
heighttext: 581px
heighttext: 581px
In the code in your question you are not executing the height check based upon a ChangeListener. So you are querying the height of the WebView document before the document has been loaded (which is why it is returning zero for your code).
Basedon BerndGirt's answer.
public class WebviewCellFactory<S,T> implements Callback<TableColumn<S,T>, TableCell<S,T>> {
#Override
public TableCell<S, T> call(TableColumn<S, T> column) {
return new TableCell<S, T>() {
#Override
protected void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText(null);
setGraphic(null);
setStyle("");
} else {
WebView webview = new WebView();
WebEngine engine = webview.getEngine();
webview.setPrefHeight(-1); // <- Absolute must at this position (before calling the Javascript)
webview.setBlendMode(BlendMode.DARKEN);
setGraphic(webview);
engine.loadContent("<body topmargin=0 leftmargin=0 style=\"background-color: transparent;\">"+item+"</body>");
engine.documentProperty().addListener((obj, prev, newv) -> {
String heightText = engine.executeScript( // <- Some modification, which gives moreless the same result than the original
"var body = document.body,"
+ "html = document.documentElement;"
+ "Math.max( body.scrollHeight , body.offsetHeight, "
+ "html.clientHeight, html.scrollHeight , html.offsetHeight );"
).toString();
Double height = Double.parseDouble(heightText.replace("px", "")) + 10 ; // <- Why are this 15.0 required??
webview.setPrefHeight(height);
this.setPrefHeight(height);
});
}
}
};
}
}
then you just need to set
tableColumn.setCellFactory(new WebviewCellFactory());
if there are any mistakes please tell me.

HTMLEditor subscript and superscript text

I have been trying to show subscript and superscript text in HTMLEditor. there are two buttons for sub and sup mode. the user types the (sub/sup)text in a textfield and press the OK button which allows the textfield text to be rendered as sub or sup in HTMLEditor. The code is as follows:
import java.util.List;
import java.util.regex.Pattern;
import javafx.application.*;
import javafx.collections.FXCollections;
import javafx.event.*;
import javafx.geometry.Orientation;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.image.*;
import javafx.scene.web.HTMLEditor;
import javafx.stage.Stage;
public class HTMLEditorCustomizationSample extends Application {
// limits the fonts a user can select from in the html editor.
private static final List<String> limitedFonts = FXCollections.observableArrayList("Arial", "Times New Roman", "Courier New", "Comic Sans MS");
String sup = " ⁺⁻⁼⁽⁾⁰¹²³⁴⁵⁶⁷⁸⁹ᴬᵃᴭᵆᵄᵅᶛᴮᵇᶜᶝᴰᵈᶞᴱᵉᴲᵊᵋᶟᵌᶠᴳᵍᶢˠʰᴴʱᴵⁱᶦᶤᶧᶥʲᴶᶨᶡᴷᵏˡᴸᶫᶪᶩᴹᵐᶬᴺⁿᶰᶮᶯᵑᴼᵒᵓᵔᵕᶱᴽᴾᵖᶲʳᴿʴʵʶˢᶳᶴᵀᵗᶵᵁᵘᶸᵙᶶᶣᵚᶭᶷᵛⱽᶹᶺʷᵂˣʸᶻᶼᶽᶾꝰᵜᵝᵞᵟᶿᵠᵡᵸჼˤⵯ";
String supchars = " +−=()0123456789AaÆᴂɐɑɒBbcɕDdðEeƎəɛɜɜfGgɡɣhHɦIiɪɨᵻɩjJʝɟKklLʟᶅɭMmɱNnɴɲɳŋOoɔᴖᴗɵȢPpɸrRɹɻʁsʂʃTtƫUuᴜᴝʉɥɯɰʊvVʋʌwWxyzʐʑʒꝯᴥβγδθφχнნʕⵡ";
String subchars=" +−=()0123456789aeəhijklmnoprstuvxβγρφχ";
String sub=" ₊₋₌₍₎₀₁₂₃₄₅₆₇₈₉ₐₑₔₕᵢⱼₖₗₘₙₒₚᵣₛₜᵤᵥₓᵦᵧᵨᵩᵪ";
char[] csup = sup.toCharArray();
char[] characters = supchars.toCharArray();
char[] csub = sub.toCharArray();
char[] character = subchars.toCharArray();
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
// create a new html editor and show it before we start modifying it.
final HTMLEditor htmlEditor = new HTMLEditor();
stage.setScene(new Scene(htmlEditor));
stage.show();
// hide controls we don't need.
hideImageNodesMatching(htmlEditor, Pattern.compile(".*(Cut|Copy|Paste).*"), 0);
Node seperator = htmlEditor.lookup(".separator");
seperator.setVisible(false);
seperator.setManaged(false);
// modify font selections.
int i = 0;
for (Node candidate : (htmlEditor.lookupAll("MenuButton"))) {
// fonts are selected by the second menu in the htmlEditor.
if (candidate instanceof MenuButton && i == 1) {
// limit the font selections to our predefined list.
MenuButton menuButton = (MenuButton) candidate;
List<MenuItem> removalList = FXCollections.observableArrayList();
final List<MenuItem> fontSelections = menuButton.getItems();
for (MenuItem item : fontSelections) {
if (!limitedFonts.contains(item.getText())) {
removalList.add(item);
}
}
fontSelections.removeAll(removalList);
// Select a font from out limited font selection.
// Selection done in Platform.runLater because if you try to do
// the selection immediately, it won't take place.
Platform.runLater(new Runnable() {
#Override
public void run() {
boolean fontSelected = false;
for (final MenuItem item : fontSelections) {
if ("Comic Sans MS".equals(item.getText())) {
if (item instanceof RadioMenuItem) {
((RadioMenuItem) item).setSelected(true);
fontSelected = true;
}
}
}
if (!fontSelected && fontSelections.size() > 0 && fontSelections.get(0) instanceof RadioMenuItem) {
((RadioMenuItem) fontSelections.get(0)).setSelected(true);
}
}
});
}
i++;
}
// add a custom button to the top toolbar.
Node node = htmlEditor.lookup(".top-toolbar");
if (node instanceof ToolBar) {
ToolBar bar = (ToolBar) node;
ToggleButton supButton = new ToggleButton("x²");
ToggleButton subButton = new ToggleButton("x₂");
TextField txt = new TextField();
Button okBtn = new Button("OK");
Button clrBtn = new Button("CLEAR");
ToggleGroup group = new ToggleGroup();
supButton.setToggleGroup(group);
subButton.setToggleGroup(group);
Separator v1=new Separator();
v1.setOrientation(Orientation.VERTICAL);
Separator v2=new Separator();
v2.setOrientation(Orientation.VERTICAL);
txt.setDisable(true);
okBtn.setDisable(true);;
clrBtn.setDisable(true);
bar.getItems().add(v1);
bar.getItems().add(supButton);
bar.getItems().add(subButton);
bar.getItems().add(v2);
bar.getItems().add(txt);
bar.getItems().add(okBtn);
bar.getItems().add(clrBtn);
okBtn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
System.out.println(htmlEditor.getHtmlText());
if (supButton.isSelected()) {
txt.setPromptText(" Enter the superscript text ");
String text = htmlEditor.getHtmlText().replaceAll("</p></body></html>", "");
text = text.replaceAll("<html dir=\"ltr\"><head></head><body contenteditable=\"true\"><p>", "");
System.out.println(text);
text="<p>"+text + "<sup>"+ txt.getText()+"</sup></p>";
System.out.println(text);
htmlEditor.setHtmlText(text);
}
else if (subButton.isSelected()) {
txt.setPromptText(" Enter the superscript text ");
String text = htmlEditor.getHtmlText().replaceAll("</p></body></html>", "");
text = text.replaceAll("<html dir=\"ltr\"><head></head><body contenteditable=\"true\"><p>", "");
System.out.println(text);
text=text + "<sub>"+ txt.getText()+"</sup></p>";
System.out.println(text);
htmlEditor.setHtmlText(text);
}
}
});
clrBtn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
txt.clear();
}
});
supButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
if (supButton.isSelected()) {
txt.setPromptText(" Enter the superscript text ");
txt.setDisable(false);
okBtn.setDisable(false);;
clrBtn.setDisable(false);
}
}
});
subButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
if (subButton.isSelected()) {
txt.setPromptText(" Enter the subscript text ");
txt.setDisable(false);
okBtn.setDisable(false);;
clrBtn.setDisable(false);
}
}
});
}
}
private String convertSupText(String dsup) {
char[] cdsup = dsup.toCharArray();
String data="";
for (int i = 0; i < cdsup.length; i++) {
for (int j = 0; j < characters.length; j++) {
if (cdsup[i] == characters[j]) {
data = data + csup[j];
}
}
}
return data;
}
private String convertSubText(String dsup) {
char[] cdsup = dsup.toCharArray();
String data="";
for (int i = 0; i < cdsup.length; i++) {
for (int j = 0; j < character.length; j++) {
if (cdsup[i] == character[j]) {
data = data + csub[j];
}
}
}
return data;
}
// hide buttons containing nodes whose image url matches a given name pattern.
public void hideImageNodesMatching(Node node, Pattern imageNamePattern, int depth) {
if (node instanceof ImageView) {
ImageView imageView = (ImageView) node;
String url = imageView.getImage().impl_getUrl();
if (url != null && imageNamePattern.matcher(url).matches()) {
Node button = imageView.getParent().getParent();
button.setVisible(false);
button.setManaged(false);
}
}
if (node instanceof Parent) {
for (Node child : ((Parent) node).getChildrenUnmodifiable()) {
hideImageNodesMatching(child, imageNamePattern, depth + 1);
}
}
}
}
The problem is that after adding the subscript or superscript text, the cursor still remains in subscript or superscript mode and every time the text is added it goes on a newline.
#Manoj I think your problem is that you don't know what the HTMLeditor is doing with any text you enter in the textfield (aka WebPage). Appearantly it is applying the your <sub> tag to the next text you enter (adding 1 and typing a normal 2 afterwards results in 12):
<html dir="ltr"><head></head><body contenteditable="true"><p><br><sup>1</sup></p></body></html>
<html dir="ltr"><head></head><body contenteditable="true"><p><br><sup>1<font size="2">1</font></sup></p></body></html>
I looked into the files (HTMLEditor>HTMLEditorSkin>WebPage>twkExecuteCommand) and in the end commands like bold/italic are executed in a dll (jfxwebkit). My knowledge is exceeded here. I see no solution which would not involve rewriting the whole HTMLEditor + native libraries.
(just included this in an answer bc comment length was exceeded)
thought so. I have done a work around using webview along with html editor. And it works fine for now. The code is as follows:
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.geometry.Orientation;
import javafx.scene.Node;
import javafx.scene.control.Alert;
import javafx.scene.control.Separator;
import javafx.scene.control.Button;
import javafx.scene.control.ToolBar;
import javafx.scene.control.Tooltip;
import javafx.scene.web.HTMLEditor;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
public class FXMLDocumentController implements Initializable {
#FXML
private HTMLEditor HE;
#FXML
private WebView WV;
WebEngine webEngine;
Button supButton;
Button subButton;
Tooltip sup;
Tooltip sub;
Alert info= new Alert(Alert.AlertType.INFORMATION);;
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
webEngine = WV.getEngine();
supButton = new Button("x²");
subButton = new Button("x₂");
supButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
info.setTitle("SUCCESS");
info.setHeaderText("Information");
info.setContentText("Use <sup>Text to to superscripted</sup> to use superscript fuction.\n Press Preview button to preview the changes");
info.showAndWait();
}});
subButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
info.setTitle("SUCCESS");
info.setHeaderText("Information");
info.setContentText("Use <sub>Text to to subscripted</sub> to use subscript fuction.\n Press Preview button to preview the changes");
info.showAndWait();
}});
sup = new Tooltip();
sub = new Tooltip();
sup.setText(" Use <sup>Text to to superscripted</sup> to use superscript fuction.\n Press Preview button to preview the changes ");
sub.setText(" Use <sub>Text to to subscripted</sub> to use subscript fuction.\n Press Preview button to preview the changes ");
Node node = HE.lookup(".top-toolbar");
if (node instanceof ToolBar) {
ToolBar bar = (ToolBar) node;
Separator v2 = new Separator();
v2.setOrientation(Orientation.VERTICAL);
bar.getItems().add(supButton);
bar.getItems().add(subButton);
bar.getItems().add(v2);
}
supButton.setTooltip(sup);
subButton.setTooltip(sub);
}
#FXML
private void handleKeyTyped(ActionEvent event) {
String text = HE.getHtmlText();
text = text.replaceAll("<sup>", "<sup>");
text = text.replaceAll("</sup>", "</sup>");
text = text.replaceAll("<sub>", "<sub>");
text = text.replaceAll("</sub>", "</sub>");
webEngine.loadContent(text);
}
}

samsung gear live audio encoding

I'm currently working on an Android Wear app, and I'm looking toward audio recording. I've followed the tutorial on the Android developper website, and it works well on my Nexus 7, but not on the Samsung Gear Live I have for testing. The application just goes crashing all the time.
Digging a bit into the problem, I might have figured out that it was a problem with 2 parameters for the recorder to work: either the OutputFormat, or the AudioEncoder. I tried pairing and trying all the OutputFormat and AudioEncoder available, but without any luck.
So here's my question: did someone encounter the same problem? And if so, did you find the right combination of Format/Encoder?
I don't paste my code as it's exactly the same as in the documentation. Here is the link if you want to have a look: http://developer.android.com/guide/topics/media/audio-capture.html
Thank you in advance for your answers and your time :)
The root problem is that you cannot use MediaRecorder, even though the Android audio capture example does, but instead you need to use the AudioRecord class.
Also, I'd recommend streaming the raw data back to your phone to assemble it into an audio file as that is very thorny on a wearable.
For more, see this answer for more.
I have included a sample below that I got working.
import android.app.Activity;
import android.content.Intent;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.speech.RecognizerIntent;
import android.support.wearable.view.WatchViewStub;
import android.util.Log;
import android.widget.TextView;
import android.view.View;
import java.util.List;
public class MainActivity extends Activity {
private static final String TAG = MainActivity.class.getName();
private static final int SPEECH_REQUEST_CODE = 1;
private static final int RECORDER_SAMPLERATE = 44100;
private static final int RECORDER_CHANNELS = AudioFormat.CHANNEL_IN_STEREO;
private static final int RECORDER_AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT;
private TextView mTextView;
private AudioRecord recorder;
private int bufferSize = 0;
private Thread recordingThread = null;
private volatile boolean isRecording;
#Override
protected void onCreate(Bundle savedInstanceState) {
Log.v(TAG, "Creating MainActivity");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final WatchViewStub stub = (WatchViewStub) findViewById(R.id.watch_view_stub);
stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
#Override
public void onLayoutInflated(WatchViewStub stub) {
mTextView = (TextView) stub.findViewById(R.id.text);
}
});
bufferSize =
AudioRecord.getMinBufferSize(RECORDER_SAMPLERATE,
RECORDER_CHANNELS, RECORDER_AUDIO_ENCODING);
}
public void handleRecordButtonClick(View view) {
startAudioCapture();
}
public void handleStopButtonClick(View view) {
stopAudioCapture();
}
private void startAudioCapture() {
Log.v(TAG, "Starting audio capture");
recorder = new AudioRecord(MediaRecorder.AudioSource.MIC,
RECORDER_SAMPLERATE, RECORDER_CHANNELS, RECORDER_AUDIO_ENCODING, bufferSize);
if (recorder.getState() == AudioRecord.STATE_INITIALIZED) {
recorder.startRecording();
isRecording = true;
Log.v(TAG, "Successfully started recording");
recordingThread = new Thread(new Runnable() {
#Override
public void run() {
processRawAudioData();
}
}, "AudioRecorder Thread");
recordingThread.start();
} else {
Log.v(TAG, "Failed to started recording");
}
}
private void stopAudioCapture() {
Log.v(TAG, "Stop audio capture");
recorder.stop();
isRecording = false;
recorder.release();
}
private void processRawAudioData() {
byte data[] = new byte[bufferSize];
int read = 0;
while(isRecording) {
read = recorder.read(data, 0, bufferSize);
if(AudioRecord.ERROR_INVALID_OPERATION != read) {
Log.v(TAG, "Successfully read " + data.length + " bytes of audio");
}
}
}
}

Resources