I volunteered to help a friend with a project, but I fear I may be over my head. The basic idea is to take a combination of 3 inputs and based on the 6 available combinations, play a specific video. I am using vlcj to load and play the video(s), with key listeners on top of that. I was finally able to get them working and could load each of the 6 videos if I only listen/use a single input per video. I'm getting hung up on the part where I wait for 3 inputs, and based on that combination, play a single video. Below is what I have so far. I'm thinking build out an array of the 3 inputs and then check the contents of that array as compared to the 6 possible combinations. The only problem is that I'm pretty sure I', not building it properly/cutting it off after 3 inputs/not properly comparing it in the code. When I type in 1, 2, 3 on my keyboard while the program is running, nothing ever happens.
The code Might look a little sloppy since a lot of it was put together looking at various posts here and the tutorials vlcj offers. Any help would be appreciated.
package javaapplication9;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.Canvas;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import uk.co.caprica.vlcj.component.EmbeddedMediaPlayerComponent;
import uk.co.caprica.vlcj.discovery.NativeDiscovery;
public class JavaApplication9 {
private final JFrame frame;
int[] tools = new int[3];
int[] vid1 = {1, 2, 3};
int[] vid2 = {1, 3, 2};
int[] vid3 = {2, 1, 3};
int[] vid4 = {2, 3, 1};
int[] vid5 = {3, 1, 2};
int[] vid6 = {3, 2, 1};
private final EmbeddedMediaPlayerComponent mediaPlayerComponent;
public static void main(final String[] args) {
new NativeDiscovery().discover();
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new JavaApplication9(args);
}
});
}
public JavaApplication9(String[] args) {
frame = new JFrame("Tutorials");
frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
frame.setUndecorated(true);
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
mediaPlayerComponent.release();
System.exit(0);
}
});
mediaPlayerComponent = new EmbeddedMediaPlayerComponent();
frame.setContentPane(mediaPlayerComponent);
frame.setVisible(true);
Canvas videoSurface = mediaPlayerComponent.getVideoSurface();
mediaPlayerComponent.getVideoSurface().requestFocus();
mediaPlayerComponent.getMediaPlayer().playMedia("file location0");
videoSurface.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
int key = e.getKeyChar();
for (int i = 0; i < tools.length; i++){
tools[i] = key;
}
/*
if (key == KeyEvent.VK_1){
mediaPlayerComponent.getMediaPlayer().playMedia("file location1");
}
if (key == KeyEvent.VK_2){
mediaPlayerComponent.getMediaPlayer().playMedia("file location2");
}
if (key == KeyEvent.VK_3){
mediaPlayerComponent.getMediaPlayer().playMedia("file location3");
}
if (key == KeyEvent.VK_4){
mediaPlayerComponent.getMediaPlayer().playMedia("file location4");
}
if (key == KeyEvent.VK_5){
mediaPlayerComponent.getMediaPlayer().playMedia("file location5");
}
if (key == KeyEvent.VK_6){
mediaPlayerComponent.getMediaPlayer().playMedia("file location6");
}
if (key == KeyEvent.VK_ESCAPE){
frame.dispatchEvent(new WindowEvent(frame, WindowEvent.WINDOW_CLOSING));
}
*/
}
});
if (tools == vid1){
mediaPlayerComponent.getMediaPlayer().playMedia("file location1");
}
}
}
All of the stuff in the comment section is how I got it working with a single input per video (using e.getKeyCode() instead of e.getKeyChar())
Thanks!
UPDATE
@MadProgrammer - what your update included is essentially what I wound up doing using the list1.equals(list2) command, though I assume your method is a much more foolproof way to go about this.
I am still running into the issue of the array being built with inputs (will only be using 1,2,3 in the final product) does not update a key's position in the array if that key already exists in the array. For example, I press keys 1,2,3 and build array/list [key1, key2, key3]
, which would play vid1
. If I want to immediately play vid3
after, and try to punch in 2,1,3, a new array does not build because all those inputs are already part of the output array, despite being a different order. However, going to 4,5,6 then back to 2,1,3 will work.
Is there a way to completely clear the array when a 4th key is pressed, and have that 4th key be in position 0 of the 2nd array being built, leading to a new output array - a rearranged version of the first?
KeyListener
is fickle, in order to generate key events it must not only be focusable, but must also have key board focus.
First, you will need to make mediaPlayerComponent
or videoSurface
focusable (setFocsuable
) and use requestFocusInWindow
to give focus to the component.
This might not work, as other things might take focus away from your component, so a MouseListener
might be useful, so you can call requestFocusInWindow
when the video surface is clicked.
A better solution would be to use the key bindings API which has been designed to help solve this problem. The problem is, it's only designed to work with lightweight (Swing) components, not AWT components.
You could, however, use a JPanel
as the frame's contentPane
and add you mediaPlayerComponent
to it. This way you could use the JPanel
and bind you keys to it, for example...
import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import uk.co.caprica.vlcj.component.EmbeddedMediaPlayerComponent;
import uk.co.caprica.vlcj.discovery.NativeDiscovery;
/**
*
* @author swhitehead
*/
public class Player {
private final JFrame frame;
int[] tools = new int[3];
int[] vid1 = {1, 2, 3};
int[] vid2 = {1, 3, 2};
int[] vid3 = {2, 1, 3};
int[] vid4 = {2, 3, 1};
int[] vid5 = {3, 1, 2};
int[] vid6 = {3, 2, 1};
private final EmbeddedMediaPlayerComponent mediaPlayerComponent;
public static void main(final String[] args) {
new NativeDiscovery().discover();
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new Player(args);
}
});
}
private List<String> inputs = new ArrayList<>(3);
public Player(String[] args) {
frame = new JFrame("Tutorials");
frame.setSize(200, 200);
// frame.setUndecorated(true);
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
mediaPlayerComponent.release();
System.exit(0);
}
});
JPanel content = new JPanel(new BorderLayout());
bindKey(content, KeyEvent.VK_1, "key1");
bindKey(content, KeyEvent.VK_2, "key2");
bindKey(content, KeyEvent.VK_3, "key3");
bindKey(content, KeyEvent.VK_4, "key4");
bindKey(content, KeyEvent.VK_5, "key5");
bindKey(content, KeyEvent.VK_6, "key6");
frame.setContentPane(content);
mediaPlayerComponent = new EmbeddedMediaPlayerComponent();
frame.add(mediaPlayerComponent);
frame.setVisible(true);
Canvas videoSurface = mediaPlayerComponent.getVideoSurface();
mediaPlayerComponent.getVideoSurface().requestFocus();
// mediaPlayerComponent.getMediaPlayer().playMedia("file location0");
//
// if (tools == vid1) {
// mediaPlayerComponent.getMediaPlayer().playMedia("file location1");
// }
}
protected void bindKey(JComponent component, int keyCode, String name) {
InputMap inputMap = component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
ActionMap actionMap = component.getActionMap();
inputMap.put(KeyStroke.getKeyStroke(keyCode, 0, false), name + ".pressed");
actionMap.put(name + ".pressed", new PressAction(name));
}
protected void inputDidChange() {
System.out.println(inputs);
}
public class PressAction extends InputAction {
public PressAction(String name) {
super(name, true);
}
}
public class ReleaseAction extends InputAction {
public ReleaseAction(String name) {
super(name, false);
}
}
public class InputAction extends AbstractAction {
private String name;
private boolean pressed;
public InputAction(String name, boolean pressed) {
this.name = name;
this.pressed = pressed;
}
@Override
public void actionPerformed(ActionEvent e) {
boolean didChange = false;
if (pressed) {
if (!inputs.contains(name)) {
while (inputs.size() > 2) {
inputs.remove(0);
}
inputs.add(name);
didChange = true;
}
} else {
didChange = inputs.remove(name);
}
if (didChange) {
inputDidChange();
}
}
}
}
See How to Use Key Bindings for more details
Updated
If I declare vid1 as 1,2,3 and vid2 as 4,5,6 it works flawlessly to play 2 seperate videos. If I use a different order like stated above (1,3,2) right after the 1,2,3 input array, it doesn't like it and will just keep playing the first video over again with each key press. Is there a way to clear the array and start over again once a 4th input is pressed, rather than overwriting the array using the most recent key presses? This method only seems to work if the 2nd set of inputs are entirely different from the 1st.
This is because List#contains
doesn't care about the order the elements are in...
So, given...
private List<String> vid1 = new ArrayList<>(Arrays.asList(new String[]{"key1", "key2", "key3"}));
private List<String> vid2 = new ArrayList<>(Arrays.asList(new String[]{"key4", "key5", "key6"}));
private List<String> vid3 = new ArrayList<>(Arrays.asList(new String[]{"key1", "key3", "key2"}));
I used...
protected void inputDidChange() {
System.out.println(inputs);
if (contains(vid1, inputs)) {
System.out.println("vid1");
} else if (contains(vid2, inputs)) {
System.out.println("vid2");
} else if (contains(vid3, inputs)) {
System.out.println("vid3");
}
}
protected boolean contains(List<String> source, List<String> compare) {
boolean contains = source.size() == compare.size();
if (contains) {
for (int index = 0; index < source.size(); index++) {
if (!source.get(index).equals(compare.get(index))) {
contains = false;
break;
}
}
}
return contains;
}
which checks each element at each index to see if they are equal
Is there a way to completely clear the array when a 4th key is pressed, and have that 4th key be in position 0 of the 2nd array being built, leading to a new output array - a rearranged version of the first?
You could change the actionPerformed
method of the InputAction
to something more like...
@Override
public void actionPerformed(ActionEvent e) {
boolean didChange = false;
if (pressed) {
if (!inputs.contains(name)) {
if (inputs.size() >= 3) {
inputs.clear();
}
inputs.add(name);
didChange = true;
}
} else {
didChange = inputs.remove(name);
}
if (didChange) {
inputDidChange();
}
}
Basically, if the size of the inputs
is >= 3
when a key is pressed, the List
is cleared and the new key is added
Remember, just because I use String
s for the "commands", doesn't mean you have to, you can use int
or any other object you want