PocketSphinx is not listening - java

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!

Related

Kill Network Service Discovery from AsyncTask when done without leaks

I am trying to simply find a device with mDNS, save the IP and kill the "Network Service Discovery"(NSD) once done.
NSD and AsyncTask are in conflict here.
Getting the IP with NSD works but if the AsyncTask is not static it warns of Leaks.
If AsyncTask is static, NSD says from onPostExecute();
Non-static field 'mNsdManager' cannot be referenced from a static context
NSD is still killed from onDestroy if the App exits if i make the AsyncTask static but have to comment out mNsdManager.stopServiceDiscovery( mDiscoveryListener); in onPostExecute() to do it.
With NSD I get the IP in 5-15 seconds but NSD stays seriously busy if i can't kill it if AsyncTask is static.
If i satisfy AsyncTask by making it static mNsdManager complains:
Non-static field 'mNsdManager' cannot be referenced from a static context
The only way i can compile is make AsyncTask non-static and accept the possible leaks -OR- leave AsyncTask static and comment out the KILL line in onPostExecute().
2 ERRORS marked in the code below.
With Android Event based AsyncTask seems the best way, but is it the right way?
How can I kill mNsdManager and still make AsyncTask static to block leaks?
package com.fib.onacci.fibonacci;
private static final String TAG = "CLOCK : ";
private TextView mIP_address;
// NSD members, Allows app to discover the "fibonacci.local"
// Reference:
// http://developer.android.com/training/connect-devices-wirelessly/nsd.html
private static NsdManager mNsdManager;
private static NsdManager.DiscoveryListener mDiscoveryListener;
private NsdManager.ResolveListener mResolveListener;
private NsdServiceInfo mServiceInfo;
public String mRPiAddress;
public static String IPaddress ="-"; // something to look for change
private static final String SERVICE_TYPE = "_http._tcp.";
public class MainActivity extends AppCompatActivity {
private static final String TAG = "CLOCK: ";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new getIPtoTextView().execute(); // start the AsyncTask
// setup nDNS and find IP
mRPiAddress = "";
IPaddress = "-";
mNsdManager = (NsdManager)(getApplicationContext().getSystemService(Context.NSD_SERVICE));
initializeResolveListener();
initializeDiscoveryListener();
mNsdManager.discoverServices( SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, mDiscoveryListener);
} // END onCreate
// NSD start - Network discovery
ERROR This AsyncTask class should be static or leaks might occur
A static field will leak contexts.
private static class getIPtoTextView extends AsyncTask {
/** part of nDiscovery - find clock and kill discovery service
* `doInBackground` is run on a separate, background thread
*/
#Override
protected Void doInBackground(Void... params) {
String mloop = IPaddress;
while ( mloop.equals("-")) {
mloop = IPaddress;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Log.i( TAG, "doInBackground - IP Found - " + mloop );
return null;
}
/** part of nDiscovery
* `onPostExecute` is run after `doInBackground`, and it's
* run on the main/ui thread, so you it's safe to update ui
* components from it. (this is the correct way to update ui
* components.)
*/
#Override
protected void onPostExecute(Void param) {
Log.i( TAG, "onPostExecute - IP Found - " + IPaddress );
TextView IP_Window = findViewById(R.id.IP_address);
IP_Window.setText( IPaddress); // post IP address to TextView
ERROR Non-static field 'mNsdManager' cannot be referenced from a static context
mNsdManager.stopServiceDiscovery( mDiscoveryListener); // kill mDiscoveryListener
}
} // end asyncTask class
private void initializeDiscoveryListener() {
mDiscoveryListener = new NsdManager.DiscoveryListener() { // Listener
#Override
public void onDiscoveryStarted(String regType) {
}
#Override
public void onServiceFound(NsdServiceInfo service) { // service found!
String name = service.getServiceName();
String type = service.getServiceType();
if ( type.equals(SERVICE_TYPE) && name.contains("Fibonacci")) {
Log.i( TAG, "\n\tNSD Service Found # ' " + name + "'");
mNsdManager.resolveService(service, mResolveListener);
}
}
#Override
public void onServiceLost(NsdServiceInfo service) {
}
#Override
public void onDiscoveryStopped(String serviceType) {
}
#Override
public void onStartDiscoveryFailed(String serviceType, int errorCode) {
mNsdManager.stopServiceDiscovery(this);
}
#Override
public void onStopDiscoveryFailed(String serviceType, int errorCode) {
mNsdManager.stopServiceDiscovery(this);
}
};
}
private void initializeResolveListener() {
mResolveListener = new NsdManager.ResolveListener(){
#Override
public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
Log.i( TAG, "\n\t\tNSD Resolve failed " + errorCode + "\n\n" );
}
#Override
public void onServiceResolved( NsdServiceInfo serviceInfo ) {
mServiceInfo = serviceInfo;
InetAddress host = mServiceInfo.getHost();
IPaddress = host.getHostAddress();
mRPiAddress = IPaddress;
Log.i( TAG, "\n\t\tNSD Resolved address = " + IPaddress + "\n\n" );
}
};
}
#Override
protected void onPause() {
super.onPause();
if ( mDiscoveryListener != null) {
mNsdManager.stopServiceDiscovery( mDiscoveryListener);
}
}
#Override
protected void onResume() {
super.onResume();
if ( mDiscoveryListener != null) {
mIP_address.setText( R.string.searching ); // TextView - Searching -
try {
Thread.sleep( 1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
initializeDiscoveryListener();
}
}
#Override
protected void onDestroy() {
super.onDestroy();
mNsdManager.stopServiceDiscovery( mDiscoveryListener);
}
// NSD end //
}
a rewrite was needed because i could not make class getIPtoTextView static without too many errors.
Once i could make class getIPtoTextView static , i moved
mNsdManager.stopServiceDiscovery( mDiscoveryListener); to onPause() and onDestroy() all worked fine.

FFmpeg - cannot find ExecuteBinaryResponseHandler - Android/Java

I am trying to make a module for react-native that will change a video into a gif. I have little to no experience with android studios/java, but I would love to learn more! I am using this library to convert the video to a gif. Here is my code:
package com.reactlibrary;
import android.widget.Toast;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.github.hiteshsondhi88.libffmpeg.FFmpeg;
public class RNGifMakerModule extends ReactContextBaseJavaModule {
private final ReactApplicationContext reactContext;
public RNGifMakerModule(ReactApplicationContext reactContext) {
super(reactContext);
this.reactContext = reactContext;
}
#Override
public String getName() {
return "RNGifMakerModule";
}
#ReactMethod
public void alert(String message) {
Toast.makeText(getReactApplicationContext(), "Error", Toast.LENGTH_LONG).show();
String[] cmd = {"-i"
, message
, "Image.gif"};
conversion(cmd);
}
public void conversion(String[] cmd) {
FFmpeg ffmpeg = FFmpeg.getInstance(this.reactContext);
try {
// to execute "ffmpeg -version" command you just need to pass "-version"
ffmpeg.execute(cmd, new ExecuteBinaryResponseHandler() {
#Override
public void onStart() {
}
#Override
public void onProgress(String message) {
}
#Override
public void onFailure(String message) {
}
#Override
public void onSuccess(String message) {
}
#Override
public void onFinish() {
}
});
} catch (FFmpegCommandAlreadyRunningException e) {
// Handle if FFmpeg is already running
e.printStackTrace();
}
}
}
And I get this error:
Error:(43, 31) error: cannot find symbol class ExecuteBinaryResponseHandler
This seems odd to be, because in the documentation for ffmpeg-android-java it says to use almost exactly the same code.
Bounty
The bounty will be awarded to you if you can find a way to convert a video.mp4 into a gif. You do not necessarily have to use FFmpeg, but your solution has to work with java/android studios.
First of all you should init ffmpeg correctly.
FFmpeg ffmpeg = FFmpeg.getInstance(this.reactContext);
// please add following method after
ffmpeg.loadBinary(new FFmpegLoadBinaryResponseHandler() {
#Override
public void onFailure() {
// probably your device not supported
}
#Override
public void onSuccess() {
// you should init flag here (isLoaded, isReady etc.)
}
Only after onSuccess() you can work with commands.
Then please check following answer by LordNeckbeard.
So your code should be something like this:
if (isFFmpegLoaded) {
// ffmpeg.execute(commands from link from the answer)
}
Please do not forget to remove all spaces from command's string and "ffmpeg" word.
To keep command more readable I will recommend to build command like this:
final String[] command = new String[11]; // example of the first command in the answer
command[0] = "-y";
command[1] = "-ss";
command[2] = "30";
command[3] = "-t";
command[4] = "3";
command[5] = "-i";
command[6] = "-t";
command[7] = "filePath";
command[8] = "-vf";
command[9] = "fps=10,scale=320:-1:flags=lanczos,palettegen";
command[10] = "palette.png";
Please make sure that you have storage permission to work with file just in case you are working on external storage.
Based on this strategy ffmpeg works well for me. Thanks and good luck!
First of all, you should use: File - Invalidate Caches/Restart - Invalidate and Restart and try to reimport ExecuteBinaryResponseHan‌dler. If the problem hasn't been resolved you can try the small hack. Inside your project create package com.github.hiteshsondhi88.libffmpeg and class:
public class ExecuteBinaryResponseHandler implements FFmpegExecuteResponseHandler {
#Override
public void onSuccess(String message) {
}
#Override
public void onProgress(String message) {
}
#Override
public void onFailure(String message) {
}
#Override
public void onStart() {
}
#Override
public void onFinish() {
}
}
It should be as on image:
Then inside your build.gradle file in defaultConfig block add multiDexEnabled true
Then you will be able to use that class

Adobe Air ANE for android with java native extension trying to receive broadcast

I'm trying to create a native extension which can receive broadcasts, sent from a native android am as intent broadcasts.
The sending part works, I've tested this with a native app that has a broadcast receiver, but I cant get it to work in the native extension.
Here's what I have so far:
Here the java side of the ANE
public class ReceiverPhidget extends BroadcastReceiver {
private FREContext mFREContext;
public ReceiverPhidget(FREContext mFREContext) {
this.mFREContext = mFREContext;
}
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(IntentsKeys.INTENT_PHIDGET_CONNECTED)){
//Send listener in ANE project with message that phidget connected (not must)
System.out.println("Phidget connected");
mFREContext.dispatchStatusEventAsync("Yes", Keys.KEY_CONNECTED);
} else
if (action.equals(IntentsKeys.INTENT_PHIDGET_DISCONNECTED)){
//Send listener in ANE project with message that phidget disconnected (not must)
System.out.println("Phidget disconnected");
mFREContext.dispatchStatusEventAsync("Yes", Keys.KEY_DISCONNECTED);
} else
if (action.equals(IntentsKeys.INTENT_PHIDGET_GAIN_TAG)){
//Send listener with data in ANE project with message that phidget gain receive
String message = intent.getStringExtra(IntentsKeys.INTENT_PHIDGET_EXTRA_DATA);
System.out.println("Phidget gain message: " + message);
Log.d("TAG FOUND", message);
mFREContext.dispatchStatusEventAsync(message, Keys.KEY_TAG_GAIN);
}
}
public static IntentFilter getIntentFilter(){
final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(IntentsKeys.INTENT_PHIDGET_CONNECTED);
intentFilter.addAction(IntentsKeys.INTENT_PHIDGET_DISCONNECTED);
intentFilter.addAction(IntentsKeys.INTENT_PHIDGET_GAIN_TAG);
return intentFilter;
}
}
And the FREExtension
public class ReceiverExtension implements FREExtension {
private ReceiverPhidget mReceiverPhidget;
private ReceiverExtensionContext mContext;
#Override
public void initialize() {
mReceiverPhidget = new ReceiverPhidget(mContext);
mContext.getActivity().registerReceiver(mReceiverPhidget, ReceiverPhidget.getIntentFilter());
}
#Override
public FREContext createContext(String s) {
return mContext = new ReceiverExtensionContext();
}
#Override
public void dispose() {
mContext.getActivity().unregisterReceiver(mReceiverPhidget);
}
}
And here is the flash library side of the ANE
package nl.mediaheads.anetest.extension {
import flash.events.EventDispatcher;
import flash.events.StatusEvent;
import flash.external.ExtensionContext;
public class RFIDController extends EventDispatcher {
private var extContext:ExtensionContext;
private var channel:int;
private var scannedChannelList:Vector.<int>;
public function RFIDController() {
extContext = ExtensionContext.createExtensionContext(
"nl.mediaheads.anetest.exntension.RFIDController", "");
extContext.addEventListener(StatusEvent.STATUS, onStatus);
}
private function onStatus(event:StatusEvent):void {
if (event.level == EventKeys.KEY_TAG_GAIN) {
dispatchEvent (new TagEvent(TagEvent.TAG_GAINED, event.code) );
}
}
}
}
And here is my test mobile project class to test the ANE
package
{
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.text.TextField;
import nl.mediaheads.anetest.extension.RFIDController;
[SWF(width="1280", height="800", frameRate="60", backgroundColor="#ffffff")]
public class AneTestApp extends Sprite
{
private var tf:TextField;
private var rc:RFIDController;
public function AneTestApp()
{
super();
// support autoOrients
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.color = 0xFFFFFF;
addEventListener(Event.ADDED_TO_STAGE, onAdded);
}
private function onAdded(event:Event):void {
//
tf = new TextField();
tf.width = 200;
tf.height = 50;
tf.x = 10;
tf.y = 64;
tf.mouseEnabled = false;
tf.background = true;
tf.backgroundColor = 0xF50000;
addChild(tf);
rc = new RFIDController();
tf.text = "test 1";
this.addEventListener( TagEvent.TAG_GAINED , onTagAdded);
tf.text = "test 2";
//
}
private function onTagAdded(event:TagEvent):void
{
tf.text = event.params;
}
}
}
I have signed the ANE accordingly, I also signed the test app it's self.
I have a Log.d in the java part of the ANE which should pop up on log cat but it doesn't, also the textfield just becomes blank as soon as I initialized the RFIDController even without added the event listener.
If you need any more code or information to help me solve this problem feel free to ask.
I could really use some help because I'm completely lost, I've followed multiple tutorials and guide on how to do this, I should have done everything correctly, but I clearly have not.
UPDATE: 1
The extension xml
<extension xmlns="http://ns.adobe.com/air/extension/3.5">
<id>nl.mediaheads.anetest.exntension.RFIDController</id>
<versionNumber>0.0.1</versionNumber>
<platforms>
<platform name="Android-ARM">
<applicationDeployment>
<nativeLibrary>AneTest.jar</nativeLibrary>
<initializer>nl.mediaheads.anetest.ReceiverExtension</initializer>
<finalizer>nl.mediaheads.anetest.ReceiverExtension</finalizer>
</applicationDeployment>
</platform>
</platforms>
</extension>
UPDATE 2:
I fixed it, it was an context issue together with that flash somehow clean my custom event so I used status event to parse from the flash side of the ANE to the air application itself.
Currently you are creating your receiver at the initialisation point of the extension which will most likely be called before the context creation, so your context may be null at that point and causing your errors.
Try moving the creation of your ReceiverPhidget to the constructor of your ReceiverExtensionContext. Something like the following (I haven't tested this):
public class ReceiverExtensionContext extends FREContext
{
private ReceiverPhidget mReceiverPhidget;
public ReceiverExtensionContext()
{
mReceiverPhidget = new ReceiverPhidget( this );
getActivity().registerReceiver( mReceiverPhidget, ReceiverPhidget.getIntentFilter() );
}
#Override
public Map<String, FREFunction> getFunctions()
{
Map<String, FREFunction> functionMap = new HashMap<String, FREFunction>();
return functionMap;
}
#Override
public void dispose()
{
getActivity().unregisterReceiver( mReceiverPhidget );
}
}

javafx adding multiple strings into TextArea in one method -> Everything is written once at the end

I have a javafx program which I want to use to analyze a website. For the start I just want to print the sourcecode of a site into a TextArea, but before that, I write "loading website sourcecode..."
My target is to write "loading website sourcecode..." first, and then after some seconds when the parsing of the site is finished, add that sourcecode.
At the Moment when I press the button, nothing happens and after 3-5 seconds the "loading website sourcecode..." message and the sourcecode is displayed at once.
So I actually want to show strings one after another. I already googled for 2 hours and tried things with threads, invokelater, platform.runLater() and so on but nothing worked, the code is simple.
ModelView.java - Controller Class
package root;
import javafx.application.Platform;
...
public class ModelView {
#FXML public TextField UrlInput;
// This gets called when the button is pressed
public void checkUrl() throws InterruptedException
{
String url = UrlInput.getText();
LogWriter lw = new LogWriter();
lw.printMsg("loading website sourcecode...");
lw.printMsg(HTMLParser.directUrlToCode(url));
}
}
LogWriter.java
package root;
import javafx.beans.value.ObservableValue;
...
public class LogWriter extends Thread{
#FXML TextArea Log;
public LogWriter()
{
Log = (TextArea) Main.scene.lookup("#Log");
}
void printMsg(String s)
{
Log.setText(this.Log.getText()+"\n"+s);
}
}
EDIT:
There is not much to say about the HTMLParser methods, but I add that it extends Thread.
I tried changing ModelView.java to that:
ModelView.java - version 2
package root;
import javafx.application.Platform;
...
public class ModelView<V> {
#FXML public TextField UrlInput;
// This gets called when the button is pressed
public void checkUrl() throws InterruptedException
{
String url = UrlInput.getText();
LogWriter lw0 = new LogWriter();
lw0.start();
lw0.printMsg("loading website sourcecode...");
HTMLParser hp = new HTMLParser();
hp.start();
LogWriter lw1 = new LogWriter();
lw1.start();
lw1.printMsg(hp.directUrlToCode(url));
}
}
Still the same effect.
EDIT2:
This is another version I tried, in this case, "loading website sourcecode..." is not even displaying, I am going on with my tries...
ModelView.java checkUrl() - Version 3
public void checkUrl() throws InterruptedException
{
String url = UrlInput.getText();
Task<Void> task = new Task<Void>(){
#Override protected Void call() throws Exception {
if(isCancelled()) {
updateMessage("Cancelled");
LogWriter lw = new LogWriter();
lw.printMsg("loading website sourcecode...");
}
return null;
}
};
Thread t = new Thread(task);
t.start();
HTMLParser hp = new HTMLParser();
LogWriter lw1 = new LogWriter();
lw1.printMsg(hp.directUrlToCode(url));
}
First of all, manipulating an observable variable from outside of the JavaFX application thread is a bad idea. You won't be able to bind other variables to it (you'll get IllegalStateExceptions)
Second, I'd implement LogWriter like this:
// ...
public class LogWriter {
private final TextArea txtLog;
public LogWriter(TextArea txtLog) {
this.txtLog = txtLog;
}
void printMsg(final String s) {
if (!Platform.isFxApplicationThread()) {
Platform.runLater(new Runnable() {
#Override
public void run() {
printMsg(s);
}
});
} else {
txtLog.setText(txtLog.getText() + "\n" + s);
}
}
}
and use it this way:
//...
#FXML
private TextArea txtLog;
// ...
public void checkUrl() {
final String url = UrlInput.getText();
final LogWriter lw = new LogWriter(txtLog);
Task<String> task = new Task<String>() {
#Override
protected String call() {
lw.printMsg("loading website sourcecode...");
return HTMLParser.directUrlToCode(url);
}
#Override
protected void succeeded() {
super.succeeded();
lw.printMsg(getValue());
}
#Override
protected void failed() {
super.failed();
if (getException() != null) {
getException().printStackTrace();
}
lw.printMsg("failed!");
}
};
new Thread(task).start();
}
Note that HTMLParser does not need to extend Thread.

Passing function as a parameter in java

I'm getting familiar with Android framework and Java and wanted to create a general "NetworkHelper" class which would handle most of the networking code enabling me to just call web-pages from it.
I followed this article from the developer.android.com to create my networking class: http://developer.android.com/training/basics/network-ops/connecting.html
Code:
package com.example.androidapp;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.AsyncTask;
import android.util.Log;
/**
* #author tuomas
* This class provides basic helper functions and features for network communication.
*/
public class NetworkHelper
{
private Context mContext;
public NetworkHelper(Context mContext)
{
//get context
this.mContext = mContext;
}
/**
* Checks if the network connection is available.
*/
public boolean checkConnection()
{
//checks if the network connection exists and works as should be
ConnectivityManager connMgr = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
if (networkInfo != null && networkInfo.isConnected())
{
//network connection works
Log.v("log", "Network connection works");
return true;
}
else
{
//network connection won't work
Log.v("log", "Network connection won't work");
return false;
}
}
public void downloadUrl(String stringUrl)
{
new DownloadWebpageTask().execute(stringUrl);
}
//actual code to handle download
private class DownloadWebpageTask extends AsyncTask<String, Void, String>
{
#Override
protected String doInBackground(String... urls)
{
// params comes from the execute() call: params[0] is the url.
try {
return downloadUrl(urls[0]);
} catch (IOException e) {
return "Unable to retrieve web page. URL may be invalid.";
}
}
// Given a URL, establishes an HttpUrlConnection and retrieves
// the web page content as a InputStream, which it returns as
// a string.
private String downloadUrl(String myurl) throws IOException
{
InputStream is = null;
// Only display the first 500 characters of the retrieved
// web page content.
int len = 500;
try {
URL url = new URL(myurl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(10000 );
conn.setConnectTimeout(15000);
conn.setRequestMethod("GET");
conn.setDoInput(true);
// Starts the query
conn.connect();
int response = conn.getResponseCode();
Log.d("log", "The response is: " + response);
is = conn.getInputStream();
// Convert the InputStream into a string
String contentAsString = readIt(is, len);
return contentAsString;
// Makes sure that the InputStream is closed after the app is
// finished using it.
} finally {
if (is != null) {
is.close();
}
}
}
// Reads an InputStream and converts it to a String.
public String readIt(InputStream stream, int len) throws IOException, UnsupportedEncodingException
{
Reader reader = null;
reader = new InputStreamReader(stream, "UTF-8");
char[] buffer = new char[len];
reader.read(buffer);
return new String(buffer);
}
// onPostExecute displays the results of the AsyncTask.
#Override
protected void onPostExecute(String result)
{
//textView.setText(result);
Log.v("log", result);
}
}
}
In my activity class I use the class this way:
connHelper = new NetworkHelper(this);
...
if (connHelper.checkConnection())
{
//connection ok, download the webpage from provided url
connHelper.downloadUrl(stringUrl);
}
Problem I'm having is that I should somehow make a callback back to the activity and it should be definable in "downloadUrl()" function. For example when download finishes, public void "handleWebpage(String data)" function in activity is called with loaded string as its parameter.
I did some googling and found that I should somehow use interfaces to achieve this functionality. After reviewing few similar stackoverflow questions/answers I didn't get it working and I'm not sure if I understood interfaces properly: How do I pass method as a parameter in Java? To be honest using the anonymous classes is new for me and I'm not really sure where or how I should apply the example code snippets in the mentioned thread.
So my question is how I could pass the callback function to my network class and call it after download finishes? Where the interface declaration goes, implements keyword and so on?
Please note that I'm beginner with Java (have other programming background though) so I'd appreciate a throughout explanation :) Thank you!
Use a callback interface or an abstract class with abstract callback methods.
Callback interface example:
public class SampleActivity extends Activity {
//define callback interface
interface MyCallbackInterface {
void onDownloadFinished(String result);
}
//your method slightly modified to take callback into account
public void downloadUrl(String stringUrl, MyCallbackInterface callback) {
new DownloadWebpageTask(callback).execute(stringUrl);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//example to modified downloadUrl method
downloadUrl("http://google.com", new MyCallbackInterface() {
#Override
public void onDownloadFinished(String result) {
// Do something when download finished
}
});
}
//your async task class
private class DownloadWebpageTask extends AsyncTask<String, Void, String> {
final MyCallbackInterface callback;
DownloadWebpageTask(MyCallbackInterface callback) {
this.callback = callback;
}
#Override
protected void onPostExecute(String result) {
callback.onDownloadFinished(result);
}
//except for this leave your code for this class untouched...
}
}
The second option is even more concise. You do not even have to define an abstract method for "onDownloaded event" as onPostExecute does exactly what is needed. Simply extend your DownloadWebpageTask with an anonymous inline class inside your downloadUrl method.
//your method slightly modified to take callback into account
public void downloadUrl(String stringUrl, final MyCallbackInterface callback) {
new DownloadWebpageTask() {
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
callback.onDownloadFinished(result);
}
}.execute(stringUrl);
}
//...
NO interface, NO lib, NO Java 8 needed!
Just using Callable<V> from java.util.concurrent
public static void superMethod(String simpleParam, Callable<Void> methodParam) {
//your logic code [...]
//call methodParam
try {
methodParam.call();
} catch (Exception e) {
e.printStackTrace();
}
}
How to use it:
superMethod("Hello world", new Callable<Void>() {
public Void call() {
myParamMethod();
return null;
}
}
);
Where myParamMethod() is our passed method as parameter (in this case methodParam).
Yes, an interface is the best way IMHO. For example, GWT uses the command pattern with an interface like this:
public interface Command{
void execute();
}
In this way, you can pass function from a method to another
public void foo(Command cmd){
...
cmd.execute();
}
public void bar(){
foo(new Command(){
void execute(){
//do something
}
});
}
The out of the box solution is that this is not possible in Java. Java does not accept Higher-order functions. It can be achieved though by some "tricks". Normally the interface is the one used as you saw. Please take a look here for further information. You can also use reflection to achieve it, but this is error prone.
Using Interfaces may be the best way in Java Coding Architecture.
But, passing a Runnable object could work as well, and it would be much more practical and flexible, I think.
SomeProcess sp;
public void initSomeProcess(Runnable callbackProcessOnFailed) {
final Runnable runOnFailed = callbackProcessOnFailed;
sp = new SomeProcess();
sp.settingSomeVars = someVars;
sp.setProcessListener = new SomeProcessListener() {
public void OnDone() {
Log.d(TAG,"done");
}
public void OnFailed(){
Log.d(TAG,"failed");
//call callback if it is set
if (runOnFailed!=null) {
Handler h = new Handler();
h.post(runOnFailed);
}
}
};
}
/****/
initSomeProcess(new Runnable() {
#Override
public void run() {
/* callback routines here */
}
});
Reflection is never a good idea since it's harder to read and debug, but if you are 100% sure what you're doing, you can simply call something like set_method(R.id.button_profile_edit, "toggle_edit") to attach a method to a view. This is useful in fragment, but again, some people would consider it as anti-pattern so be warned.
public void set_method(int id, final String a_method)
{
set_listener(id, new View.OnClickListener() {
public void onClick(View v) {
try {
Method method = fragment.getClass().getMethod(a_method, null);
method.invoke(fragment, null);
} catch (Exception e) {
Debug.log_exception(e, "METHOD");
}
}
});
}
public void set_listener(int id, View.OnClickListener listener)
{
if (root == null) {
Debug.log("WARNING fragment", "root is null - listener not set");
return;
}
View view = root.findViewById(id);
view.setOnClickListener(listener);
}
Interface callback support generic type.
In Callbackable.java
public interface Callbackable<T> {
void call(T obj);
}
How to use:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
doSomeNetworkAction(new Callbackable<Integer>() {
#Override
public void call(Integer obj) {
System.out.println("have received: " + obj + " from network");
}
});
}
// You can change Integer to String or to any model class like Customer, Profile, Address...
public void doSomeNetworkAction(Callbackable<Integer> callback) {
// acb xyz...
callback.call(666);
}
}

Categories

Resources