I'm trying to create a class that sequentially performs the Bluetooth tasks without user intervention aside from starting the process. In this class an external event calls the overridden method "executeCentral" from there it calls setup() to enable and request permissions. If they complete successfully the initialize() method is called and waits for one second before calling the Bluetooth initialize() which is executed in the EDT. If it runs without exception it calls startScanning() which also waits 1 second before calling Bluetooth startScan() in the EDT. After scanning has started it waits 10 seconds before calling Bluetooth stopScan() in the EDT.
I recreated the project for a clean setup and used the "downloader" in the Codename One Settings. It compiles successfully and runs, but reports an exception on "Bluetooth not initialized"
Any idea on what I am doing wrong? I'm under the impression that all calls must be done in the EDT.
The single form BTDemo compiles and executes each task as a separate user initiated event.
public class UITaskBluetoothEx extends com.crumptech.library.mobile.ui.tasks.UITaskBluetooth {
protected Bluetooth bt = new Bluetooth();
protected Map devices = new HashMap();
public UITaskBluetoothEx() {
super();
}
#Override
public String getReplacement() {
return "UITaskBluetoothEx";
}
protected void showDebug(String message) {
Display.getInstance().callSerially(new Runnable() {
#Override
public void run() {
UIApplication.showDebug("UITaskBluetoothEx " + message);
completed(result(false));
}
});
}
#Override
protected void executeCentral() {
bt = new Bluetooth();
try {
setup();
initialize();
} catch (Exception e) {
showDebug(e.getMessage());
}
}
protected void setup() throws IOException {
if (!bt.isEnabled()) {
bt.enable();
}
if (!bt.hasPermission()) {
bt.requestPermission();
}
}
protected void initialize() {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
#Override
public void run() {
Display.getInstance().callSerially(new Runnable() {
#Override
public void run() {
try {
if (!bt.isInitialized()) {
bt.initialize(true, false, "ShopMyLocalStores");
}
startScanning();
} catch (Exception e) {
showDebug(e.getMessage());
}
}
});
}
}, 1000);
}
protected void startScanning() {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
#Override
public void run() {
Display.getInstance().callSerially(new Runnable() {
#Override
public void run() {
try {
if (!bt.isScanning()) {
bt.startScan(new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
try {
JSONObject res = (JSONObject) evt.getSource();
if (res.getString("status").equals("scanResult")) {
if (!devices.containsKey(res.getString("address"))) {
devices.put(res.getString("address"), res);
}
}
} catch (JSONException e) {
}
}
}, null, true, Bluetooth.SCAN_MODE_LOW_POWER, Bluetooth.MATCH_MODE_STICKY, Bluetooth.MATCH_NUM_MAX_ADVERTISEMENT, Bluetooth.CALLBACK_TYPE_ALL_MATCHES);
stopScanning();
}
} catch (Exception e) {
showDebug(e.getMessage());
}
}
});
}
}, 1000);
}
protected void stopScanning() {
try {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
#Override
public void run() {
Display.getInstance().callSerially(new Runnable() {
#Override
public void run() {
try {
if (bt.isScanning()) {
bt.stopScan();
}
} catch (Exception e) {
showDebug(e.getMessage());
}
showResults();
}
});
}
}, 10000);
} catch (Exception e) {
}
}
protected void showResults() {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
#Override
public void run() {
Display.getInstance().callSerially(new Runnable() {
#Override
public void run() {
String text = "";
Iterator it = devices.entrySet().iterator();
while (it.hasNext()) {
Map.Entry pair = (Map.Entry) it.next();
text += (pair.getKey() + " = " + pair.getValue() + "\r\n");
}
UIApplication.showDebug(text);
completed(result(true));
}
});
}
}, 1000);
}
}
It looks like some methods currently aren't supported on iOS. These will throw IOExceptions if they are called on iOS. This is a limitation that is baked into the Cordova plugin that we ported. These methods literally return "Unsupported Operation" inside the plugin. I'm not sure if these are just omissions of the plugin, or if they can't be supported. The list of methods that are currently unsupported on iOS are:
isEnabled()
enable()
disable()
mtu()
requestConnectionPriority()
hasPermission()
requestPermission()
isLocationEnabled()
requestLocation()
I have marked these in the javadocs for the Bluetooth class to help identify them. We'll likely have to do something here to clean it up ... perhaps an exception is not the best thing.
In any case, your test app is failing because you call isEnabled() and initialize() inside the same try/catch block. isEnabled throws an exception so it never gets to initialize() and your tests aren't run.
I have adapted your code into my own test case, and made that modification, and it appears to run fine.
Related
I have a Java swing UI where I need to disable a button on the UI if I could not detect an active internet connection.
The code function properly so far, but when I get disconnected from the internet, it does not retrigger the method to update the boolean flag.
How can I add an event on that flag to let my button consume it during the lifetime of the application?
public class Main {
private static JButton button;
private static boolean testButtonEnabled;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
/*
* function that returns true/false if connected to the internet
*/
if(Utils.isConnectedToInternet()) {
logger.debug("System is connected to the internet");
testButtonEnabled=true;
} else {
logger.debug("System is not connected to the internet");
testButtonEnabled=false;
}
Main window = new Main();
window.frame.setVisible(true);
button = new JButton("my button");
/*
* set the internet status
*/
button.setVisible(testButtonEnabled);
}
}
}
}
Again, this code:
SwingWorker<Void, String> worker = new SwingWorker<Void, String>()
{
public Void doInBackground()
{
while(true)
{
try {
isConnectedToInternet = Utils.isConnectedToInternet();
if (isConnectedToInternet) {
btn_online2.setEnabled(isConnectedToInternet);
} else {
btn_online2.setEnabled(isConnectedToInternet);
}
logger.debug("Internet connection status: " + isConnectedToInternet);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
worker.execute();
is dangerous since it makes mutational changes to a Swing component from a background thread. While this code may work 95% of the time, it can fail in unpredicatable ways and at unpredicatable times. Better to only mutate Swing components on the event thread. For instance, even this would be better:
SwingWorker<Void, String> worker = new SwingWorker<Void, String>() {
#Override
public Void doInBackground() {
while(true) {
try {
isConnectedToInternet = Utils.isConnectedToInternet();
// note that there is no need for the if/else block
SwingUtilities.invokeLater(() -> {
btn_online2.setEnabled(isConnectedToInternet);
});
logger.debug("Internet connection status: " + isConnectedToInternet);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
worker.execute();
or better still, using SwingWorker's publish/process:
SwingWorker<Void, Boolean> worker = new SwingWorker<Void, Boolean>() {
#Override
public Void doInBackground() {
while(true) {
try {
isConnectedToInternet = Utils.isConnectedToInternet();
// note that there is no need for the if/else block
publish(Utils.isConnectedToInternet());
logger.debug("Internet connection status: " + isConnectedToInternet);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
#Override
protected void process(List<Boolean> chunks) {
for (Boolean chunk : chunks) {
btn_online2.setEnabled(chunk);
}
}
};
worker.execute();
I was able to resolve using the following method in the initialize() of the frame.
Adding that change part of the swingworker allowed the button to check the flag and assign it to itself whenever it changes.
Now whenever the internet is disconnected the button is disabled and whenever connected the button is enabled.
SwingWorker<Void, String> worker = new SwingWorker<Void, String>()
{
public Void doInBackground()
{
while(true)
{
try {
isConnectedToInternet = Utils.isConnectedToInternet();
if (isConnectedToInternet) {
btn_online2.setEnabled(isConnectedToInternet);
} else {
btn_online2.setEnabled(isConnectedToInternet);
}
logger.debug("Internet connection status: " + isConnectedToInternet);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
worker.execute();
My goal is to change the screen of a user when the game can't reach the backend anymore. My code executes as expected except the screen never changes. Here's the initial call:
timer.testTimeToServer(api, game);
Here's the timer object's class. I put (my url) in place of the actual IP address of my backend:
public class CustomTimer {
private static final float timeToDrop = 2000;
private float time = 0;
private StopWatch watch = new StopWatch();
public void testTimeToServer(ApiCall api,final proofOfConcept game){
watch.start();
api.httpGetWithCallback("(my url)/api/v1/character", new CallBack(){
#Override
public void callback(String resp){
System.out.println("Server Responded");
time = watch.getTime();
watch.stop();
watch.reset();
if(time > timeToDrop){
game.setScreen(new GameOverScreen(game, false));
System.out.println("Should have switched screen")
}
}
});
}
}
Here's the httpGetWithCallback method in the api object:
public void httpGetWithCallback (final String URL, final CallBack callback){
Thread th = new Thread(new Runnable(){
#Override
public void run() {
Gdx.app.postRunnable(new Runnable() {
#Override
public void run() {
Net.HttpRequest httpRequest = new Net.HttpRequest(Net.HttpMethods.GET);
httpRequest.setUrl(URL);
httpRequest.setHeader("Content-Type", "application/json");
httpRequest.setTimeOut(timeoutTimeInMilli);
Gdx.net.sendHttpRequest(httpRequest, new Net.HttpResponseListener() {
#Override
public void handleHttpResponse(Net.HttpResponse httpResponse) {
String successValue = httpResponse.getResultAsString();
if (successValue.contains("\"total_count\": 0"))//wrong credentials
{
callback.callback("EMPTY");
} else//there was a match yo! should probably have a unique conststraint on username. too hard eff it
{
callback.callback(successValue);
}
}
#Override
public void failed(Throwable t) {
callback.callback("FAILED");
}
#Override
public void cancelled() {
callback.callback("CANCELLED");
}
});
}
}
);
}
});
th.start();
threads.add(th);
}
I'm stumped because the code prints out "Should have switched screens" so it's acting like expected except for the fact that the game is frozen up and the screen switch never actually happens.
The Lazy Way:
on your main game class:
public static ProofOfConcept game;
and your method
public void testTimeToServer(ApiCall api){
watch.start();
api.httpGetWithCallback("(my url)/api/v1/character", new CallBack(){
#Override
public void callback(String resp){
System.out.println("Server Responded");
time = watch.getTime();
watch.stop();
watch.reset();
if(time > timeToDrop){
Main.game.setScreen(new GameOverScreen(false));
System.out.println("Should have switched screen")
}
}
});
}
}
The Right Way
You can create a callback inside your ProofOfConcept class that every frame on the render method checks the result and change the screen.
I want to update the text of a notification every second. i wrote this code
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
builder.setSmallIcon(R.drawable.ic_launcher);
builder.setContentTitle("Text");
builder.setContentText("value updated every 1 second");
}
}
}, 1*1000);
but the value is not updated every 1 second but only one time. why? how can i do this?
Try to use Timers Handler ll invoke one time.
Timer timer = new Timer();
TimerTask task = new TimerTask() {
#Override
public void run() {
updateUI();
}
};
timer.scheduleAtFixedRate(task, 0,1000);
put your Handler to separate method
public void updateUI(){
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
builder.setSmallIcon(R.drawable.ic_launcher);
builder.setContentTitle("Text");
builder.setContentText("value updated every 1 second");
}
}, 1000);
}
his code starts an thread which sleeps 1000 milliseconds every round.
Thread t = new Thread() {
#Override
public void run() {
try {
while (!isInterrupted()) {
Thread.sleep(1000);
runOnUiThread(new Runnable() {
#Override
public void run() {
// update TextView here!
}
});
}
} catch (InterruptedException e) {
}
}
};
t.start();
I'm new with JavaFX and I've a little problem with a thread: I can execute it twice and I can't find why.
Here is a sum-upt of my code:
Task<Void> task = new Task<Void>() {
#Override public Void call() throws ImageLoadedException, HomographyException, IOException {
try{
System.out.println("GO !");
return null;
}
catch (Exception e){
e.printStackTrace();
}
return null;
}
#Override
protected void succeeded() {
super.succeeded();
System.out.println("SUCCEEDED");
}
};
#FXML protected void launch(ActionEvent event){
new Thread(task).start();
}
When I click a first time the button who start my thread, my task run without any problem (my console display "GO !" and "SUCCEEDED").
But if I click a second time, nothing append. Am I doing something wrong ? Can't we use a thread more than once ?
From the Thread.start() documentation : No
It is never legal to start a thread more than once. In particular, a
thread may not be restarted once it has completed execution.
From the Concurrency in JavaFX tutorial :
The Task class defines a one-time object that cannot be reused. If you
need a reusable Worker object, use the Service class.
So, you have to consider the Service class rather than Task.
Edit: this should work for you:
Service service = new Service<>(task);
//Updated use this to create a new Service object instead
Service service = new Service() {
#Override
protected Task createTask() {
return new Task() {
#Override
protected Void call() throws Exception {
//Your codes here
return null;
}
};
}
};
#FXML protected void launch(ActionEvent event){
if (!service.isRunning()) {
service.reset();
service.start();
}
}
With a button can fire new tasks
Button btn = new Button();
btn.setText("New task");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
Executor ex=new Executor("Task"+count);
ex.start();
count++;
System.out.println("Task Starting...");
}
});
Do it with a wraper class
import java.io.IOException;
import javafx.concurrent.Task;
public class Executor {
private String name;
private Task<Void> task;
public Executor(final String name) {
this.name=name;
task = new Task<Void>() {
#Override
public Void call() throws IOException, InterruptedException {
try {
int i=0;
while(i<20){
System.out.println(name);
Thread.sleep(2000);
i++;
}
return null;
} catch (IllegalThreadStateException e) {
System.out.println(e);
}
return null;
}
#Override
protected void succeeded() {
super.succeeded();
try {
System.out.println(name+" finish");
} catch (Exception ex) {
System.out.println(ex);
}
}
};
}
public void start() {
try {
Thread th = new Thread(task);
th.start();
} catch (Exception ex) {
System.out.println(ex);
}
}
}
I'm designing a simple JavaFX form.
First, I load the JavaFX environment (and wait for it to finish), with something like this :
final CountDownLatch latch_l = new CountDownLatch(1);
try {
// init the JavaFX environment
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new JFXPanel(); // init JavaFX
latch_l.countDown();
}
});
latch_l.await();
}
This works fine. (the reason why I need to first load the JavaFX this way, is because it's mainly a Swing application, with some JavaFX components inside, but they are loaded later)
Now, I'd like to add a splash-screen on launch, and displays it while the JavaFX environment loads (and in fact put in on-screen for like 5 seconds, because there are logo, trademark etc.. of the application I need to show)
So I came up with a SplashScreen class, which just displays a JWindow on-screen, like that :
public class SplashScreen {
protected JWindow splashScreen_m = new JWindow();
protected Integer splashScreenDuration_m = 5000;
public void show() {
// fill the splash-screen with informations
...
// display the splash-screen
splashScreen_m.validate();
splashScreen_m.pack();
splashScreen_m.setLocationRelativeTo(null);
splashScreen_m.setVisible(true);
}
public void unload() {
// unload the splash-screen
splashScreen_m.setVisible(false);
splashScreen_m.dispose();
}
}
Now, I want for the splash-screen to load and display itself 5 seconds.
Meanwhile, I want the JavaFX environment to load, too.
So I updated the CountDownLatch like this :
final CountDownLatch latch_l = new CountDownLatch(2); // now countdown is set to 2
final SplashScreen splash_l = new SplashScreen();
try {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// show splash-screen
splash_l.show();
latch_l.countDown();
// init the JavaFX environment
new JFXPanel(); // init JavaFX
latch_l.countDown();
}
});
latch_l.await();
splash_l.unload();
}
So, it's working, but the splash only stays for the JavaFX environment to load, so basically it unloads very quickly (which is normal, given the code I wrote).
How to display the splash-screen for 5 seconds minimum (if the JavaFX loads faster) without freezing the EDT ?
Thanks.
The most significant issue is you're blocking the Event Dispatching Thread, meaning that it can't display/update anything while it's blocked. The same problem applies to JavaFX.
You should, also, never update either from anything other then they respective event queues.
Now, there are any number of ways you might be able to go about this, but SwingWorker is probably the simplest for the time been.
I apologise, this is the entire exposure to JavaFX I've had...
public class TestJavaFXLoader extends JApplet {
public static void main(String[] args) {
new TestJavaFXLoader();
}
public TestJavaFXLoader() throws HeadlessException {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
Loader loader = new Loader();
loader.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals("state") && evt.getNewValue().equals(SwingWorker.StateValue.DONE)) {
System.out.println("Load main app here :D");
}
}
});
loader.load();
}
});
}
public class Loader extends SwingWorker<Object, String> {
private JWindow splash;
private JLabel subMessage;
public Loader() {
}
protected void loadSplashScreen() {
try {
splash = new JWindow();
JLabel content = new JLabel(new ImageIcon(ImageIO.read(...))));
content.setLayout(new GridBagLayout());
splash.setContentPane(content);
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
subMessage = createLabel("");
splash.add(createLabel("Loading, please wait"), gbc);
splash.add(subMessage, gbc);
splash.pack();
splash.setLocationRelativeTo(null);
splash.setVisible(true);
} catch (IOException ex) {
ex.printStackTrace();
}
}
protected JLabel createLabel(String msg) {
JLabel message = new JLabel("Loading, please wait");
message.setForeground(Color.CYAN);
Font font = message.getFont();
message.setFont(font.deriveFont(Font.BOLD, 24));
return message;
}
public void load() {
if (!EventQueue.isDispatchThread()) {
try {
SwingUtilities.invokeAndWait(new Runnable() {
#Override
public void run() {
loadSplashScreen();
}
});
} catch (Exception exp) {
exp.printStackTrace();
}
} else {
loadSplashScreen();
}
execute();
}
#Override
protected void done() {
splash.dispose();
}
#Override
protected void process(List<String> chunks) {
subMessage.setText(chunks.get(chunks.size() - 1));
}
#Override
protected Object doInBackground() throws Exception {
publish("Preparing to load application");
try {
Thread.sleep(2500);
} catch (InterruptedException interruptedException) {
}
publish("Loading JavaFX...");
runAndWait(new Runnable() {
#Override
public void run() {
new JFXPanel();
}
});
try {
Thread.sleep(2500);
} catch (InterruptedException interruptedException) {
}
return null;
}
public void runAndWait(final Runnable run)
throws InterruptedException, ExecutionException {
if (Platform.isFxApplicationThread()) {
try {
run.run();
} catch (Exception e) {
throw new ExecutionException(e);
}
} else {
final Lock lock = new ReentrantLock();
final Condition condition = lock.newCondition();
lock.lock();
try {
Platform.runLater(new Runnable() {
#Override
public void run() {
lock.lock();
try {
run.run();
} catch (Throwable e) {
e.printStackTrace();
} finally {
try {
condition.signal();
} finally {
lock.unlock();
}
}
}
});
condition.await();
// if (throwableWrapper.t != null) {
// throw new ExecutionException(throwableWrapper.t);
// }
} finally {
lock.unlock();
}
}
}
}
}
I found the runAndWait code here