Okay, here goes nothing, I've been working on a twitch bot for my channel for some time now, making stupid little functions and stuff just to keep me coding and learning new things. I've taken on a project that seems to have gotten very complicated for me. If you have been to twitch I'm sure you have seen a loyalty bot that gives digital play money for time spent in the channel, so I wanted to add that feature with a twist. rather then use a local spreadsheet or MySQL or something that I've never used before I wanted to use the Google docs API to update an online spreadsheet so that the information could be seen by my viewers without the need for constant chat spamming for how many currency they have. (if I've already made a huge mistake in the planning phase don't hesitate to send me in the right direction with this)
so the basics, I'm using Pircbot to create a chat bot for twitch (irc based chat system). I want only two columns for now A being usernames and B being currency.
So first plan was when people join the chat look to see if they are on the spread sheet already and add them if they are not there, then implement a system for every ten minutes pushing out a value++ to anyone who is in the chat.
so on join run a function, simple enough:
#Override
protected void onJoin(String channel, String sender, String login, String hostname) {
//attempt to add new person to the spread sheet if they are not already listed.
//cells(0,0) (a,1)
try {
ss.addUser(sender);
}catch (IOException w) {
System.out.println(w);
}
catch (ServiceException q){
System.out.println(q);
}
now comes the part I'm getting really lost on, I can't seem to figure out if this is working, but I have a strong feeling the code I found and was following is old and no longer right. here is the class for handling the spread sheet:
class ss{
public static final String GOOGLE_ACCOUNT_USERNAME = "xxxx#gmail.com";
public static final String GOOGLE_ACCOUNT_PASSWORD = "xxxx";
public static final String SPREADSHEET_URL = "https://spreadsheets.google.com/feeds/spreadsheets/"code after my sheet"";
public static void addUser(String user) throws IOException, ServiceException {
/** Our view of Google Spreadsheets as an authenticated Google user. */
SpreadsheetService service = new SpreadsheetService("Print Google Spreadsheet Demo");
// Login and prompt the user to pick a sheet to use.
service.setUserCredentials(GOOGLE_ACCOUNT_USERNAME, GOOGLE_ACCOUNT_PASSWORD);
// Load sheet
URL metafeedUrl = new URL(SPREADSHEET_URL);
SpreadsheetEntry spreadsheet = service.getEntry(metafeedUrl, SpreadsheetEntry.class);
URL listFeedUrl = ((WorksheetEntry) spreadsheet.getWorksheets().get(0)).getListFeedUrl();
// Creating a local representation of the new row.
ListEntry row = new ListEntry();
row.getCustomElements().setValueLocal("Name", user);
row.getCustomElements().setValueLocal("Currency", "0");
}
}
as you can see, I got as for as trying to add new users as they enter the channel, but unfortunately all it does is throw errors:
java.lang.NoClassDefFoundError: Could not initialize class
com.google.gdata.client.spreadsheet.SpreadsheetService
at ss.addUser(didbot.java:30)
at didbot.onJoin(didbot.java:186)
at org.jibble.pircbot.PircBot.handleLine(PircBot.java:1000)
at org.jibble.pircbot.InputThread.run(InputThread.java:92)
total code if you are brave enough to see the mass text wall of my failure in coding xD
import org.jibble.pircbot.PircBot;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintStream;
import java.net.URL;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Date;
import com.google.gdata.client.spreadsheet.SpreadsheetService;
import com.google.gdata.data.spreadsheet.SpreadsheetEntry;
import com.google.gdata.data.spreadsheet.WorksheetEntry;
import com.google.gdata.data.spreadsheet.*;
import com.google.gdata.util.ServiceException;
/**
* Created by Evan on 11/4/2014.
*/
class ss{
public static final String GOOGLE_ACCOUNT_USERNAME = "*email*#gmail.com"; // Fill in google account username
public static final String GOOGLE_ACCOUNT_PASSWORD = "*password*"; // Fill in google account password
public static final String SPREADSHEET_URL = "https://spreadsheets.google.com/feeds/spreadsheets/*code*"; // Fill in google spreadsheet URI
public static void addUser(String user) throws IOException, ServiceException {
/** Our view of Google Spreadsheets as an authenticated Google user. */
SpreadsheetService service = new SpreadsheetService("Print Google Spreadsheet Demo");
// Login and prompt the user to pick a sheet to use.
service.setUserCredentials(GOOGLE_ACCOUNT_USERNAME, GOOGLE_ACCOUNT_PASSWORD);
// Load sheet
URL metafeedUrl = new URL(SPREADSHEET_URL);
SpreadsheetEntry spreadsheet = service.getEntry(metafeedUrl, SpreadsheetEntry.class);
//Here we are working with worksheet 0, if you have multiple worksheet, then change this value to get the corresponding worksheet
URL listFeedUrl = ((WorksheetEntry) spreadsheet.getWorksheets().get(0)).getListFeedUrl();
// Creating a local representation of the new row.
ListEntry row = new ListEntry();
row.getCustomElements().setValueLocal("Name", user);
row.getCustomElements().setValueLocal("Credits", "0");
// Sending the new row for insertion into worksheet.
}
}
public class didbot extends PircBot {
public didbot() {
this.setName("didbot");
ClassExecutingTask executingTask = new ClassExecutingTask();
executingTask.start();
}
public class ClassExecutingTask {
long delay = 10 * 1000; // 600000 = 10 minutes
LoopTask task = new LoopTask();
Timer timer = new Timer("TaskName");
public void start() {
timer.cancel();
timer = new Timer("TaskName");
Date executionDate = new Date(); // no params = now
timer.scheduleAtFixedRate(task, executionDate, delay);
}
public void addCredits (String veiwer, int a){
}
private class LoopTask extends TimerTask {
public void run() {
//add credits here to add one credit every 10 minutes
//sendMessage(channel, sender + ", thank you for checking out the channel!");
// getUsers("");
}
}
}
#Override
public void onMessage(String channel, String sender, String login, String hostname, String message) {
/* if (message.equalsIgnoreCase("time")) {
String time = new java.util.Date().toString();
sendMessage(channel, sender + ": The time is now " + time);
}*/
if (message.contains("!songrequest ")) {
String songRequest = message.substring(13);
if (!songRequest.isEmpty()) {
try {
PrintStream srout = new PrintStream("C:/Users/songRequest.txt");
srout.println(songRequest);
System.out.println(songRequest);
srout.close();
} catch (FileNotFoundException e) {
System.out.println(e);
}
}
}
}
#Override
protected void onJoin(String channel, String sender, String login, String hostname) {
//attempt to add new person to the spread sheet if they are not already listed.
//cells(0,0) (a,1)
try {
ss.addUser(sender);
}catch (IOException w) {
System.out.println(w);
}
catch (ServiceException q){
System.out.println(q);
}
// getUsers("");
}
}
Related
I would like to implement the library PocketSphinx in my Android project but I fail with it since nothing happens. It doesn't work and I don't get any errors.
This is how I tried it:
Added pocketsphinx-android-5prealpha-release.aar to /app/libs
Added assets.xml to /app
Aded the following to /app/build.gradle:
ant.importBuild 'assets.xml'
preBuild.dependsOn(list, checksum)
clean.dependsOn(clean_assets)
Added sync (with all sub-files) into /app/assets
Cloned the following repos into my root-directory:
git clone https://github.com/cmusphinx/sphinxbase
git clone https://github.com/cmusphinx/pocketsphinx
git clone https://github.com/cmusphinx/pocketsphinx-android
Executed gradle build
This is how my code looks like:
import android.app.Service;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.IBinder;
import android.util.Log;
import androidx.annotation.Nullable;
import java.io.File;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import ch.yourclick.kitt.R;
import edu.cmu.pocketsphinx.Assets;
import edu.cmu.pocketsphinx.Hypothesis;
import edu.cmu.pocketsphinx.RecognitionListener;
import edu.cmu.pocketsphinx.SpeechRecognizer;
import edu.cmu.pocketsphinx.SpeechRecognizerSetup;
public class SttService extends Service implements RecognitionListener {
private static final String TAG = "SstService";
/* Named searches allow to quickly reconfigure the decoder */
private static final String KWS_SEARCH = "wakeup";
private static final String FORECAST_SEARCH = "forecast";
private static final String DIGITS_SEARCH = "digits";
private static final String PHONE_SEARCH = "phones";
private static final String MENU_SEARCH = "menu";
/* Keyword we are looking for to activate menu */
private static final String KEYPHRASE = "oh mighty computer";
/* Used to handle permission request */
private static final int PERMISSIONS_REQUEST_RECORD_AUDIO = 1;
private SpeechRecognizer recognizer;
private HashMap<String, Integer> captions;
public SttService() {
// Prepare the data for UI
captions = new HashMap<>();
captions.put(KWS_SEARCH, R.string.kws_caption);
captions.put(MENU_SEARCH, R.string.menu_caption);
captions.put(DIGITS_SEARCH, R.string.digits_caption);
captions.put(PHONE_SEARCH, R.string.phone_caption);
captions.put(FORECAST_SEARCH, R.string.forecast_caption);
Log.e(TAG, "SttService: Preparing the recognition");
// Recognizer initialization is a time-consuming and it involves IO,
// so we execute it in async task
new SetupTask(this).execute();
}
private static class SetupTask extends AsyncTask<Void, Void, Exception> {
WeakReference<SttService> activityReference;
SetupTask(SttService activity) {
this.activityReference = new WeakReference<>(activity);
}
#Override
protected Exception doInBackground(Void... params) {
try {
Assets assets = new Assets(activityReference.get());
File assetDir = assets.syncAssets();
activityReference.get().setupRecognizer(assetDir);
} catch (IOException e) {
return e;
}
return null;
}
#Override
protected void onPostExecute(Exception result) {
if (result != null) {
Log.e(TAG, "onPostExecute: Failed to init recognizer " + result);
} else {
activityReference.get().switchSearch(KWS_SEARCH);
}
}
}
#Override
public void onDestroy() {
super.onDestroy();
if (recognizer != null) {
recognizer.cancel();
recognizer.shutdown();
}
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
/**
* In partial result we get quick updates about current hypothesis. In
* keyword spotting mode we can react here, in other modes we need to wait
* for final result in onResult.
*/
#Override
public void onPartialResult(Hypothesis hypothesis) {
if (hypothesis == null)
return;
String text = hypothesis.getHypstr();
if (text.equals(KEYPHRASE))
switchSearch(MENU_SEARCH);
else if (text.equals(DIGITS_SEARCH))
switchSearch(DIGITS_SEARCH);
else if (text.equals(PHONE_SEARCH))
switchSearch(PHONE_SEARCH);
else if (text.equals(FORECAST_SEARCH))
switchSearch(FORECAST_SEARCH);
else
Log.e(TAG, "onPartialResult: " + text);
}
/**
* This callback is called when we stop the recognizer.
*/
#Override
public void onResult(Hypothesis hypothesis) {
if (hypothesis != null) {
String text = hypothesis.getHypstr();
Log.e(TAG, "onResult: " + text);
}
}
#Override
public void onBeginningOfSpeech() {
}
/**
* We stop recognizer here to get a final result
*/
#Override
public void onEndOfSpeech() {
if (!recognizer.getSearchName().equals(KWS_SEARCH))
switchSearch(KWS_SEARCH);
}
private void switchSearch(String searchName) {
recognizer.stop();
// If we are not spotting, start listening with timeout (10000 ms or 10 seconds).
if (searchName.equals(KWS_SEARCH))
recognizer.startListening(searchName);
else
recognizer.startListening(searchName, 10000);
String caption = getResources().getString(captions.get(searchName));
Log.e(TAG, "switchSearch: "+ caption);
}
private void setupRecognizer(File assetsDir) throws IOException {
// The recognizer can be configured to perform multiple searches
// of different kind and switch between them
recognizer = SpeechRecognizerSetup.defaultSetup()
.setAcousticModel(new File(assetsDir, "en-us-ptm"))
.setDictionary(new File(assetsDir, "cmudict-en-us.dict"))
.setRawLogDir(assetsDir) // To disable logging of raw audio comment out this call (takes a lot of space on the device)
.getRecognizer();
recognizer.addListener(this);
/* In your application you might not need to add all those searches.
They are added here for demonstration. You can leave just one.
*/
// Create keyword-activation search.
recognizer.addKeyphraseSearch(KWS_SEARCH, KEYPHRASE);
// Create grammar-based search for selection between demos
File menuGrammar = new File(assetsDir, "menu.gram");
recognizer.addGrammarSearch(MENU_SEARCH, menuGrammar);
// Create grammar-based search for digit recognition
File digitsGrammar = new File(assetsDir, "digits.gram");
recognizer.addGrammarSearch(DIGITS_SEARCH, digitsGrammar);
// Create language model search
File languageModel = new File(assetsDir, "weather.dmp");
recognizer.addNgramSearch(FORECAST_SEARCH, languageModel);
// Phonetic search
File phoneticModel = new File(assetsDir, "en-phone.dmp");
recognizer.addAllphoneSearch(PHONE_SEARCH, phoneticModel);
}
#Override
public void onError(Exception error) {
Log.e(TAG, "onError: " + error.getMessage());
}
#Override
public void onTimeout() {
switchSearch(KWS_SEARCH);
}
}
My code is almost the same as pocketsphinx-android-demo. The only differences are that I am doing this in a service class, instead of an Activity and I am not asking the user for microphone permission since I do that in the MainActity already. Well, my code has some warnings but no errors.
When I run my app, I get this message (see the full stack trace):
E/SstService: switchSearch: To start demonstration say "oh mighty
computer".
But when I say "oh mighty computer" (or anything else), nothing happens. I don't even get an error. So I have no idea where I am stuck and what I am doing wrong.
If there is someone familiar with that library, any help will be appreciated!
My overall goal is to be able to automatically download a daily report using the bing ads API. To do this, I need to authenticate with OAuth (the old PasswordAuthentication method doesn't work because I have a new microsoft account). I have been through the "Authorization Code Grant Flow" manually and authorised myself successfully. The problem is:
the token is only valid for 1 hour
when the token expires, the process requires the user to manually login using a web browser again and re-allow the app access
Here's an example desktop app using OAuth
Does somebody know either
a more fitting way of authenticating?
or a way of bypassing the user interaction?
SOLUTION:
As mentioned by #eric urban it is only necessary to authorize manually, once. after that, the refresh token will do. (Not really obvious just looking at the example desktop app!)
I wrote a class to deal with all the OAuth stuff and persist the refresh token to a file
public class OAuthRefreshToken {
private static String refreshTokenFileName = "./bingAdsRefreshToken.txt";
private static String ClientId = "XXXXX";
private final OAuthDesktopMobileAuthCodeGrant oAuthDesktopMobileAuthCodeGrant = new OAuthDesktopMobileAuthCodeGrant(ClientId);
private String refreshToken;
public OAuthRefreshToken() {
oAuthDesktopMobileAuthCodeGrant.setNewTokensListener(new NewOAuthTokensReceivedListener() {
#Override
public void onNewOAuthTokensReceived(OAuthTokens newTokens) {
String refreshTime = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
.format(new java.util.Date());
refreshToken = newTokens.getRefreshToken();
System.out.printf("Token refresh time: %s\n", refreshTime);
writeRefreshTokenToFile();
}
});
getRefreshTokenFromFile();
refreshAccessToken();
}
public OAuthRefreshToken(String refreshToken) {
this.refreshToken = refreshToken;
writeRefreshTokenToFile();
}
public OAuthDesktopMobileAuthCodeGrant getoAuthDesktopMobileAuthCodeGrant() {
return oAuthDesktopMobileAuthCodeGrant;
}
private void refreshAccessToken(){
oAuthDesktopMobileAuthCodeGrant.requestAccessAndRefreshTokens(refreshToken);
}
private void getRefreshTokenFromFile(){
try {
refreshToken = readFile(refreshTokenFileName, Charset.defaultCharset());
} catch (IOException e) {
e.printStackTrace();
}
}
private static String readFile(String path, Charset encoding)
throws IOException
{
byte[] encoded = Files.readAllBytes(Paths.get(path));
return new String(encoded, encoding);
}
private void writeRefreshTokenToFile(){
File refreshTokenFile = new File(refreshTokenFileName);
try {
FileWriter f2 = new FileWriter(refreshTokenFile);
f2.write(refreshToken);
f2.close();
} catch (IOException e) {
e.printStackTrace();
return;
}
System.out.printf("New refresh token: %s\n", refreshToken);
System.out.printf("Stored Safely in: %s\n", refreshTokenFileName);
}
}
Use it in your app like:
final OAuthRefreshToken oAuthRefreshToken = new OAuthRefreshToken();
final OAuthDesktopMobileAuthCodeGrant oAuthDesktopMobileAuthCodeGrant = oAuthRefreshToken.getoAuthDesktopMobileAuthCodeGrant();
You are correct that user consent is required up front (once). Thereafter you can use the refresh token to request additional access tokens without user interaction. For details about Authorization Code grant flow using the Bing Ads Java SDK please see Getting Started Using Java with Bing Ads Services. Does this help?
The refresh token should not expire that quickly, they are usually permanent or last a very long time. These can however be revoked, or invalidated if you request too many of them. i believe when you have requested more than 25 different refresh tokens, they older ones start to become invalid.
I have been trying for awhile to figure out an issue with Asynchronous i/o in an android application that I am working on.
This application is required to download data to from a series of tables from Microsoft Dynamics CRM.
Once the data has been down it must preform a series of operations on the data to fill out some forms.
My problem is that I must wait for the downloads to be complete in order to start the update process.
If I add a any form of wait to my code it seems that it blocks indefinitely and never executes the callback.
I have tried methods using AtomicBooleans, AtomicIntegers, and CountDownLatchs with no success.
Here is an example using an AtomicInteger.
final CountDownLatch latch = new CountDownLatch(1);
OrganizationServiceProxy orgService;
orgService = new OrganizationServiceProxy(Constant.ENDPOINT, CRMLogin.getRequestInterceptor());
ColumnSet columnSet = new ColumnSet();
columnSet.AddColumns(AccountEntry.FETCH_COLS);
orgService.Retrieve(AccountEntry.ENTITY, UUID.fromString(accountid), columnSet, new Callback<Entity>() {
#Override
public void success(Entity entity, Response response) {
Account account = new Account();
//Load the existing fields for the account
account.load(index);
String activityid = account.getValue(AccountEntry.ACTIVITY_ID);
String recordid = account.getValue(AccountEntry.RECORD_ID);
String name = account.getValue(AccountEntry.ACCOUNT_NAME);
//Overload the fields for the account
account.load(entity);
//Reset overloaded fields on the account.
account.setValue(AccountEntry.ACTIVITY_ID, activityid);
account.setValue(AccountEntry.RECORD_ID, recordid);
account.setValue(AccountEntry.ACCOUNT_NAME, name);
//overwrite the record in the database.
account.setValue(AccountEntry.SYNCED, "1");
account.update();
Log.d("pullAccount>>>", accountid + " " + "pulled.");
latch.countDown();
}
#Override
public void failure(RetrofitError error) {
Log.d("pullAccount>>>", accountid + " " + error.getMessage());
latch.countDown();
}
});
try{
latch.await(); //THIS BLOCKS FOREVER AND EVER
}
catch (Exception e){
}
Of note is the CallBack is implemented using Retrofit.
Any guidance would be greatly appreciated.
Thank you.
Look at AsyncTask it will handle what you want in a way that Android is optimized for. There is example usage here
http://developer.android.com/reference/android/os/AsyncTask.html
EDIT:
I kinda threw this together, let me know if it works as you would expect
public class AsyncOrganizationService extends AsyncTask<Void, Void, Entity> {
#Override
protected Entity doInBackground(Void... params) {
final CountDownLatch blocker = new CountDownLatch(1);
OrganizationServiceProxy orgService;
orgService = new OrganizationServiceProxy(Constant.ENDPOINT, CRMLogin.getRequestInterceptor());
ColumnSet columnSet = new ColumnSet();
columnSet.AddColumns(AccountEntry.FETCH_COLS);
final SettableFuture<Entity> result = SettableFuture.create();
orgService.Retrieve(AccountEntry.ENTITY, UUID.fromString(accountid), columnSet, new SortedList.Callback<Entity>() {
#Override
public void success(Entity entity, HttpHelper.Response response) {
result.set(entity);
blocker.countDown();
}
});
try {
blocker.await();
return result.get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
}
#Override
protected void onPostExecute(Entity entity) {
Account account = new Account();
//Load the existing fields for the account
account.load(index);
String activityid = account.getValue(AccountEntry.ACTIVITY_ID);
String recordid = account.getValue(AccountEntry.RECORD_ID);
String name = account.getValue(AccountEntry.ACCOUNT_NAME);
//Overload the fields for the account
account.load(entity);
//Reset overloaded fields on the account.
account.setValue(AccountEntry.ACTIVITY_ID, activityid);
account.setValue(AccountEntry.RECORD_ID, recordid);
account.setValue(AccountEntry.ACCOUNT_NAME, name);
//overwrite the record in the database.
account.setValue(AccountEntry.SYNCED, "1");
account.update();
Log.d("pullAccount>>>", accountid + " " + "pulled.");
}
Im using Guava's SettableFuture class (http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/util/concurrent/SettableFuture.html). Guava is quite an amazing library - if you're not using it you should consider doing so. Otherwise, you could whip something up really quick
How can I safely update the widgets on a JavaFX GUI from within a JavaFX Service. I remember when I was developing with Swing, I used to 'invoke later' and other various swing worker utilities to ensure that all updates to the UI were handled safely in the Java Event Thread. Here is an example of a simple service thread that handles datagram messages. The bit that is missing is where the datagram message is parsed and corresponding UI widgets are updated. As you can see the service class is very simplistic.
I'm not sure if I need to use simple binding properties (like message) or alternatively should I should pass widgets to the constructor of my StatusListenerService (which is probably not the best thing to do). Can someone give me a good similar example that I would work from.
public class StatusListenerService extends Service<Void> {
private final int mPortNum;
/**
*
* #param aPortNum server listen port for inbound status messages
*/
public StatusListenerService(final int aPortNum) {
this.mPortNum = aPortNum;
}
#Override
protected Task<Void> createTask() {
return new Task<Void>() {
#Override
protected Void call() throws Exception {
updateMessage("Running...");
try {
DatagramSocket serverSocket = new DatagramSocket(mPortNum);
// allocate space for received datagrams
byte[] bytes = new byte[512];
//message.setByteBuffer(ByteBuffer.wrap(bytes), 0);
DatagramPacket packet = new DatagramPacket(bytes, bytes.length);
while (!isCancelled()) {
serverSocket.receive(packet);
SystemStatusMessage message = new SystemStatusMessage();
message.setByteBuffer(ByteBuffer.wrap(bytes), 0);
}
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
updateMessage("Cancelled");
return null;
}
};
}
}
The "low-level" approach is to use Platform.runLater(Runnable r) to update the UI. This will execute r on the FX Application Thread, and is the equivalent of Swing's SwingUtilities.invokeLater(...). So one approach is simply to call Platform.runLater(...) from inside your call() method and update the UI. As you point out, though, this essentially requires the service knowing details of the UI, which is undesirable (though there are patterns that work around this).
Task defines some properties and has corresponding updateXXX methods, such as the updateMessage(...) method you call in your example code. These methods are safe to call from any thread, and result in an update to the corresponding property to be executed on the FX Application Thread. (So, in your example, you can safely bind the text of a label to the messageProperty of the service.) As well as ensuring the updates are performed on the correct thread, these updateXXX methods also throttle the updates, so that you can essentially call them as often as you like without flooding the FX Application Thread with too many events to process: updates that occur within a single frame of the UI will be coalesced so that only the last such update (within a given frame) is visible.
You could leverage this to update the valueProperty of the task/service, if it is appropriate for your use case. So if you have some (preferably immutable) class that represents the result of parsing the packet (let's call it PacketData; but maybe it is as simple as a String), you make
public class StatusListener implements Service<PacketData> {
// ...
#Override
protected Task<PacketData> createTask() {
return new Task<PacketData>() {
// ...
#Override
public PacketData call() {
// ...
while (! isCancelled()) {
// receive packet, parse data, and wrap results:
PacketData data = new PacketData(...);
updateValue(data);
}
return null ;
}
};
}
}
Now you can do
StatusListener listener = new StatusListener();
listener.valueProperty().addListener((obs, oldValue, newValue) -> {
// update UI with newValue...
});
listener.start();
Note that the value is updated to null by the code when the service is cancelled, so with the implementation I outlined you need to make sure that your listener on the valueProperty() handles this case.
Also note that this will coalesce consecutive calls to updateValue() if they occur within the same frame rendering. So this is not an appropriate approach if you need to be sure to process every data in your handler (though typically such functionality would not need to be performed on the FX Application Thread anyway). This is a good approach if your UI is only going to need to show the "most recent state" of the background process.
SSCCE showing this technique:
import java.util.Random;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class LongRunningTaskExample extends Application {
#Override
public void start(Stage primaryStage) {
CheckBox enabled = new CheckBox("Enabled");
enabled.setDisable(true);
CheckBox activated = new CheckBox("Activated");
activated.setDisable(true);
Label name = new Label();
Label value = new Label();
Label serviceStatus = new Label();
StatusService service = new StatusService();
serviceStatus.textProperty().bind(service.messageProperty());
service.valueProperty().addListener((obs, oldValue, newValue) -> {
if (newValue == null) {
enabled.setSelected(false);
activated.setSelected(false);
name.setText("");
value.setText("");
} else {
enabled.setSelected(newValue.isEnabled());
activated.setSelected(newValue.isActivated());
name.setText(newValue.getName());
value.setText("Value: "+newValue.getValue());
}
});
Button startStop = new Button();
startStop.textProperty().bind(Bindings
.when(service.runningProperty())
.then("Stop")
.otherwise("Start"));
startStop.setOnAction(e -> {
if (service.isRunning()) {
service.cancel() ;
} else {
service.restart();
}
});
VBox root = new VBox(5, serviceStatus, name, value, enabled, activated, startStop);
root.setAlignment(Pos.CENTER);
Scene scene = new Scene(root, 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
private static class StatusService extends Service<Status> {
#Override
protected Task<Status> createTask() {
return new Task<Status>() {
#Override
protected Status call() throws Exception {
Random rng = new Random();
updateMessage("Running");
while (! isCancelled()) {
// mimic sporadic data feed:
try {
Thread.sleep(rng.nextInt(2000));
} catch (InterruptedException exc) {
Thread.currentThread().interrupt();
if (isCancelled()) {
break ;
}
}
Status status = new Status("Status "+rng.nextInt(100),
rng.nextInt(100), rng.nextBoolean(), rng.nextBoolean());
updateValue(status);
}
updateMessage("Cancelled");
return null ;
}
};
}
}
private static class Status {
private final boolean enabled ;
private final boolean activated ;
private final String name ;
private final int value ;
public Status(String name, int value, boolean enabled, boolean activated) {
this.name = name ;
this.value = value ;
this.enabled = enabled ;
this.activated = activated ;
}
public boolean isEnabled() {
return enabled;
}
public boolean isActivated() {
return activated;
}
public String getName() {
return name;
}
public int getValue() {
return value;
}
}
public static void main(String[] args) {
launch(args);
}
}
I'm trying to write a simple test of the Firebase user creation and authentication routines so that I can test my Firebase security settings. The code runs smoothly but the callbacks are not invoked and no users are created on the Firebase side.
The output of below with the print statements is:
Begin process
Start Create User
Creating User
End Creating User
End process
Process finished with exit code 0
The code is using the firebase-client-android-2.2.3.jar file for the Firebase classes though I'm just running my test as a java application on a Mac OS. Later this will go into an Android app but I'd like to be able to run it inside my IDE for now. Any insights from experienced Firebase coders much appreciated.
import com.firebase.client.Firebase;
import com.firebase.client.AuthData;
import com.firebase.client.FirebaseError;
import java.util.*;
public class FireRulesTest {
static String firebase_baseUrl = "https://<myfirebase>.firebaseio.com/";
public static void main(String[] args)
throws FirebaseException {
System.out.println("Begin process");
FireRulesTest tester = new FireRulesTest();
tester.createUser();
System.out.println("End process");
}
private void createUser()
throws FirebaseException {
try {
System.out.println("Start Create User");
final String mEmail = "me#email.com";
final String mPassword = "password";
final Firebase ref = new Firebase(firebase_baseUrl);
System.out.println("Creating User");
ref.createUser(mEmail, mPassword,
new Firebase.ValueResultHandler<Map<String, Object>>() {
#Override
public void onSuccess(Map<String, Object> result) {
System.out.println("Successfully created user account with uid: " + result.get("uid"));
ref.authWithPassword(mEmail, mPassword, new Firebase.AuthResultHandler() {
#Override
public void onAuthenticated(AuthData authData) {
//success, save auth data
HashMap<String, Object> authMap = new HashMap<String, Object>();
authMap.put("uid", authData.getUid());
authMap.put("token", authData.getToken());
authMap.put("email", mEmail);
authMap.put("password", mPassword);
Firebase currentUserRef = new Firebase(firebase_baseUrl + "movo/users/" + authData.getUid());
authMap.put("currentUser", currentUserRef);
System.out.println("User ID: " + authData.getUid() +
", Provider: " + authData.getProvider() +
", Expires:" + authData.getExpires());
System.out.println("Authentication complete");
}
#Override
public void onAuthenticationError(FirebaseError firebaseError) {
System.out.println("Authentication Error authenticating newly created user. This could be an issue. ");
System.out.println(firebaseError.getMessage());
}
});
}
#Override
public void onError(FirebaseError firebaseError) {
System.out.println("On Error authenticating newly created user. This could be an issue. ");
System.out.println(firebaseError.getMessage());
}
});
System.out.println("End Creating User");
} catch (Exception fbe) {
System.out.println("Exception: " + fbe.getMessage());
fbe.printStackTrace();
}
}
}
You'll want to add a Thread.sleep at the end of the program. Likely your program exits before Firebase gets a chance to send anything to the server.
A more proper solution would be to introduce actual lifecycle management into your app, e.g. waiting for the createUser call to finish. But given that you'll be migrating this to Android (which handles app lifecycle completely different anyway) that might not be worth the effort.