Only one callback may be registered to a function in a native module react native error - react-native

I recently added an android native module to my which listens on timezone and time changed broadcasts from the system and allows the app to perform some operations. The native module looks like this
public class TimezoneHandlerModule extends ReactContextBaseJavaModule {
private final Context context;
private final TimezoneChangeBroadcastReceiver timezoneChangeBroadcastReceiver;
private Callback onTimezoneChangeCallback;
public TimezoneHandlerModule(ReactApplicationContext reactContext) {
super(reactContext);
this.context = reactContext;
this.timezoneChangeBroadcastReceiver = new TimezoneChangeBroadcastReceiver();
}
private void registerForTimezoneChangeHandler() {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_TIME_CHANGED);
intentFilter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
getReactApplicationContext().registerReceiver(timezoneChangeBroadcastReceiver, intentFilter);
}
private void unregisterTimezoneChangeHandler() {
getReactApplicationContext().unregisterReceiver(timezoneChangeBroadcastReceiver);
}
public void setOnTimezoneChangeCallback(Callback onTimezoneChangeCallback) {
this.onTimezoneChangeCallback = onTimezoneChangeCallback;
}
/**
* #return the name of this module. This will be the name used to {#code require()} this module
* from javascript.
*/
#Override
public String getName() {
return "TimezoneHandler";
}
#ReactMethod
public void start(Callback onChange) {
Log.d(getName(), "Starting the timezone change handler");
this.registerForTimezoneChangeHandler();
this.setOnTimezoneChangeCallback(onChange);
}
#ReactMethod
public void stop() {
Log.d(getName(), "Stopping the timezone change handler");
this.unregisterTimezoneChangeHandler();
}
private class TimezoneChangeBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.d(getName(), "Received broadcast for timezone/time change " + intent.getAction());
final String action = intent.getAction();
if (action.equals(Intent.ACTION_TIME_CHANGED) || action.equals(Intent.ACTION_TIMEZONE_CHANGED)) {
TimezoneHandlerModule.this.onTimezoneChangeCallback.invoke();
}
}
}
}
Two react methods are exposed start and stop. start takes a function as a parameter which is invoked whenever a broadcast for timezone changed or time changed is received. After hooking up the native module and starting the app in emulator, I opened Settings and change the timezone and I can see that the relevant logs are printed.
11-24 17:07:21.837 1597-1597/com.xyz D/TimezoneHandler: Received broadcast for timezone/time change
11-24 17:07:21.837 1597-1907/com.xyz I/ReactNativeJS: Detected timezone change
When I change the timezone again, I see below error in the logcat output
1-24 17:22:42.356 1597-1597/com.galarmapp D/TimezoneHandler: Received broadcast for timezone/time change
11-24 17:22:42.365 1597-1907/com.galarmapp E/ReactNativeJS: The callback start() exists in module TimezoneHandler, but only one callback may be registered to a function in a native module.
11-24 17:22:42.367 1597-1908/com.galarmapp E/unknown:React: The callback start() exists in module TimezoneHandler, but only one callback may be registered to a function in a native module., stack:
__invokeCallback#12814:10
<unknown>#12685:24
guard#12604:3
invokeCallbackAndReturnFlushedQueue#12684:6
From the error message, it seems as if I am trying to attach a separate callback to the start function but I am not doing any such thing. I am calling the start method in the componentWillMount of the top level component and have confirmed that it is not called twice. I see that other people have also seen this error while trying different things but still don't understand the reason behind the problem.
Please share if you have any insights.

According to the documentation http://facebook.github.io/react-native/docs/native-modules-android.html#callbacks - "A native module is supposed to invoke its callback only once. It can, however, store the callback and invoke it later." Once you have done invoke() on the callback, you cannot use it again.
This particular use case of time zone change is better solved by sending events to javascript. See this documentation http://facebook.github.io/react-native/docs/native-modules-android.html#sending-events-to-javascript

Related

How to Take Screenshot when TestNG Assert fails?

String Actualvalue= d.findElement(By.xpath("//[#id=\"wrapper\"]/main/div[2]/div/div[1]/div/div[1]/div[2]/div/table/tbody/tr[1]/td[1]/a")).getText();
Assert.assertEquals(Actualvalue, "jumlga");
captureScreen(d, "Fail");
The assert should not be put before your capture screen. Because it will immediately shutdown the test process so your code
captureScreen(d, "Fail");
will be not reachable
This is how i usually do:
boolean result = false;
try {
// do stuff here
result = true;
} catch(Exception_class_Name ex) {
// code to handle error and capture screen shot
captureScreen(d, "Fail");
}
# then using assert
Assert.assertEquals(result, true);
1.
A good solution will be is to use a report framework like allure-reports.
Read here:allure-reports
2.
We don't our tests to be ugly by adding try catch in every test so we will use Listeners which are using an annotations system to "Listen" to our tests and act accordingly.
Example:
public class listeners extends commonOps implements ITestListener {
public void onTestFailure(ITestResult iTestResult) {
System.out.println("------------------ Starting Test: " + iTestResult.getName() + " Failed ------------------");
if (platform.equalsIgnoreCase("web"))
saveScreenshot();
}
}
Please note I only used the relevant method to your question and I suggest you read here:
TestNG Listeners
Now we will want to take a screenshot built in method by allure-reports every time a test fails so will add this method inside our listeners class
Example:
#Attachment(value = "Page Screen-Shot", type = "image/png")
public byte[] saveScreenshot(){
return ((TakesScreenshot)driver).getScreenshotAs(OutputType.BYTES);
}
Test example
#Listeners(listeners.class)
public class myTest extends commonOps {
#Test(description = "Test01: Add numbers and verify")
#Description("Test Description: Using Allure reports annotations")
public void test01_myFirstTest(){
Assert.assertEquals(result, true)
}
}
Note we're using at the beginning of the class an annotation of #Listeners(listeners.class) which allows our listeners to listen to our test, please mind the (listeners.class) can be any class you named your listeners.
The #Description is related to allure-reports and as the code snip suggests you can add additional info about the test.
Finally, our Assert.assertEquals(result, true) will take a screen shot in case the assertion fails because we enabled our listener.class to it.

How to persist/read-back Run Configuration parameters in Intellij plugin

I'm making a basic IntelliJ plugin that lets a user define Run Configuration (following the tutorial at [1]), and use said Run Configurations to execute the file open in the editor on a remote server.
My Run Configuration is simple (3 text fields), and I have it all working, however, after editing the Run Configuration, and click "Apply" or "OK" after changing values, the entered values are lost.
What is the correct way to persist and read-back values (both when the Run Configuration is re-opened as well as when the Run Configuration's Runner invoked)? It looks like I could try to create a custom persistence using [2], however, it seems like the Plugin framework should have a way to handle this already or at least hooks for when Apply/OK is pressed.
[1] https://www.jetbrains.org/intellij/sdk/docs/tutorials/run_configurations.html
[2] https://www.jetbrains.org/intellij/sdk/docs/basics/persisting_state_of_components.html
Hopefully, this post is a bit more clear to those new to IntelliJ plugin development and illustrates how persisting/loading Run Configurations can be achieved. Please read through the code comments as this is where much of the explanation takes place.
Also now that SettingsEditorImpl is my custom implementation of the SettingsEditor abstract class, and likewise, RunConfigurationImpl is my custom implementation of the RunConfigiration abstract class.
The first thing to do is to expose the form fields via custom getters on your SettingsEditorImpl (ie. getHost())
public class SettingsEditorImpl extends SettingsEditor<RunConfigurationImpl> {
private JPanel configurationPanel; // This is the outer-most JPanel
private JTextField hostJTextField;
public SettingsEditorImpl() {
super();
}
#NotNull
#Override
protected JComponent createEditor() {
return configurationPanel;
}
/* Gets the Form fields value */
private String getHost() {
return hostJTextField.getText();
}
/* Copy value FROM your custom runConfiguration back INTO the Form UI; This is to load previously saved values into the Form when it's opened. */
#Override
protected void resetEditorFrom(RunConfigurationImpl runConfiguration) {
hostJTextField.setText(StringUtils.defaultIfBlank(runConfiguration.getHost(), RUN_CONFIGURATION_HOST_DEFAULT));
}
/* Sync the value from the Form UI INTO the RunConfiguration which is what the rest of your code will interact with. This requires a way to set this value on your custom RunConfiguration, ie. RunConfigurationImpl##setHost(host) */
#Override
protected void applyEditorTo(RunConfigurationImpl runConfiguration) throws ConfigurationException {
runConfiguration.setHost(getHost());
}
}
So now, the custom SettingsEditor, which backs the Form UI, is set up to Sync field values In and Out of itself. Remember, the custom RunConfiguration is what is going to actually represent this configuration; the SettingsEditor implementation just represents the FORM (a subtle difference, but important).
Now we need a custom RunConfiguration ...
/* Annotate the class with #State and #Storage, which is used to define how this RunConfiguration's data will be persisted/loaded. */
#State(
name = Constants.PLUGIN_NAME,
storages = {#Storage(Constants.PLUGIN_NAME + "__run-configuration.xml")}
)
public class RunConfigurationImpl extends RunConfigurationBase {
// Its good to 'namespace' keys to your component;
public static final String KEY_HOST = Constants.PLUGIN_NAME + ".host";
private String host;
public RunConfigurationImpl(Project project, ConfigurationFactory factory, String name) {
super(project, factory, name);
}
/* Return an instances of the custom SettingsEditor ... see class defined above */
#NotNull
#Override
public SettingsEditor<? extends RunConfiguration> getConfigurationEditor() {
return new SettingsEditorImpl();
}
/* Return null, else we'll get a Startup/Connection tab in our Run Configuration UI in IntelliJ */
#Nullable
#Override
public SettingsEditor<ConfigurationPerRunnerSettings> getRunnerSettingsEditor(ProgramRunner runner) {
return null;
}
/* This is a pretty cool method. Every time SettingsEditor#applyEditorTo() is changed the values in this class, this method is run and can check/validate any fields! If RuntimeConfigurationException is thrown, the exceptions message is shown at the bottom of the Run Configuration UI in IntelliJ! */
#Override
public void checkConfiguration() throws RuntimeConfigurationException {
if (!StringUtils.startsWithAny(getHost(), "http://", "https://")) {
throw new RuntimeConfigurationException("Invalid host");
}
}
#Nullable
#Override
public RunProfileState getState(#NotNull Executor executor, #NotNull ExecutionEnvironment executionEnvironment) throws ExecutionException {
return null;
}
/* This READS any prior persisted configuration from the State/Storage defined by this classes annotations ... see above.
You must manually read and populate the fields using JDOMExternalizerUtil.readField(..).
This method is invoked at the "right time" by the plugin framework. You dont need to call this.
*/
#Override
public void readExternal(Element element) throws InvalidDataException {
super.readExternal(element);
host = JDOMExternalizerUtil.readField(element, KEY_HOST);
}
/* This WRITES/persists configurations TO the State/Storage defined by this classes annotations ... see above.
You must manually read and populate the fields using JDOMExternalizerUtil.writeField(..).
This method is invoked at the "right time" by the plugin framework. You dont need to call this.
*/
#Override
public void writeExternal(Element element) throws WriteExternalException {
super.writeExternal(element);
JDOMExternalizerUtil.writeField(element, KEY_HOST, host);
}
/* This method is what's used by the rest of the plugin code to access the configured 'host' value. The host field (variable) is written by
1. when writeExternal(..) loads a value from a persisted config.
2. when SettingsEditor#applyEditorTo(..) is called when the Form itself changes.
*/
public String getHost() {
return host;
}
/* This method sets the value, and is primarily used by the custom SettingEditor's SettingsEditor#applyEditorTo(..) method call */
public void setHost(String host) {
this.host = host;
}
}
To read these configuration values elsewhere, say for example a custom ProgramRunner, you would do something like:
final RunConfigurationImpl runConfiguration = (RunConfigurationImpl) executionEnvironment.getRunnerAndConfigurationSettings().getConfiguration();
runConfiguration.getHost(); // Returns the configured host value
See com.intellij.execution.configurations.RunConfigurationBase#readExternal as well as com.intellij.execution.configurations.RunConfigurationBase#loadState and com.intellij.execution.configurations.RunConfigurationBase#writeExternal

Titanium module - life cycle events not called

I am building a Titanium module for the Android platform and I want to use the life cycle events of the module (i.e. onDestroy, onPause, etc). I tried to use them by overriding these life cycle events in the module class like this:
#Kroll.module(name="custom", id="vub.ac.be.custom")
public class CustomModule extends KrollModule {
private static final String TAG = "customModule";
#Kroll.onAppCreate
public static void onAppCreate(TiApplication app) {
}
private void destroyServices(){
//...
}
#Override
public void onStop(Activity activity) {
Log.d(TAG, "STOPPING");
destroyServices();
super.onStop(activity);
}
#Override
public void onPause(Activity activity) {
Log.d(TAG, "[MODULE LIFECYCLE EVENT] pause");
super.onPause(activity);
}
#Override
public void onResume(Activity activity) {
Log.d(TAG, "[MODULE LIFECYCLE EVENT] resume");
super.onResume(activity);
}
#Override
public void onDestroy(Activity activity) {
Log.d(TAG, "[MODULE LIFECYCLE EVENT] destroy");
destroyService();
super.onDestroy(activity);
}
}
but when I opening and closing the application, these life cycle events are never called. Does anybody know how to use them, because only if I can use them I will be able to build the module I want. Thanks
Could this be the origin of my problems: inline link moddevguide
https://github.com/appcelerator/titanium_modules/blob/master/moddevguide/mobile/android/src/ti/moddevguide/ModdevguideModule.java
on line 72 they describe the following:
// Lifecycle
// NOTES:
//
// 1. Modules are created in the root context
// 2. Using navBarHidden (or fullscreen or modal) causes the window, when opened, to run in a new Android Activity.
// 3. The root context/activity will be stopped when a new activity is launched
// 4. Lifecycle notifications will NOT be received while the root activity is stopped.
I run the module in an application that uses navBarHidden, so as described a new android activity wil be started and the root activity is stopped. Whenever the root activity is stopped, the lifecycle notifications are received. Can anyone confirm this and does anybody know how to solve this? thanks
What version of SDK are you using? On 3.3.0 lifecycle callbacks is calling regardless to navigation bar hidden.

Getting Tab parameters inside LaunchConfigurationDelegate

I have a custom launch configuration. It currently has a JavaArgumentsTab() where I can enter things for VM arguments and Program arguments. But how do I actually get any values entered there?
Ideally I would get them inside my LaunchConfigurationDelegate's launch() method. I expected to find any text entered as arguments inside the LaunchConfiguration or other parameters to that method, and I'm sure this is a newbie question, but I really haven't found anything promising.
TabGroup:
public class LaunchConfigurationTabGroup extends AbstractLaunchConfigurationTabGroup {
#Override
public void createTabs(ILaunchConfigurationDialog dialog, String mode) {
ILaunchConfigurationTab[] tabs = new ILaunchConfigurationTab[] {
new JavaArgumentsTab(),
new CommonTab()
};
setTabs(tabs);
} }
LaunchConfigurationDelegate:
public class LaunchConfigurationDelegate implements ILaunchConfigurationDelegate {
#Override
public void launch(ILaunchConfiguration configuration, String mode,
ILaunch launch, IProgressMonitor monitor) throws CoreException {
// How to get anything entered on my Java tab here...?
} }
Everything from the tabs should already have been set as attribute values in the ILaunchConfiguration when launch is called.
The settings from JavaArgumentsTab are stored in the attributes using constants from IJavaLaunchConfigurationConstants.

null pointer exception on android asynctaskloader

I'm trying to do a twitter android application. I'm still working on the login.
So I'm using asynctaskloader after a friend suggested me to use it. I believe I get a null pointer exception at this line:
this.consumer = (OAuthConsumer) new getCommonsHttpOAuthConsumer(context);
here's my asynctaskloader class:
class getCommonsHttpOAuthConsumer extends AsyncTaskLoader{
public getCommonsHttpOAuthConsumer(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
#Override
public OAuthConsumer loadInBackground() {
// TODO Auto-generated method stub
return new CommonsHttpOAuthConsumer(Constants.CONSUMER_KEY, Constants.CONSUMER_SECRET);
}
}
what am I doing wrong? do you guys need to see more code?
thank you.
You're not using the loader class right.
You need to call the LoaderManager with this line:
getLoaderManager().initLoader(ID_FOR_THIS_LOADER, DATA_BUNDLE, CALLBACK);
If you're in a Fragment you need to add getActivity() at the beginning, and if you are using the android.support.v4.jar, you will call getSupportLoaderManager().
You place this line in your onCreate or onResume method. It will simply notify your activity that you want to start a new loader.
After that you'll need to implement the callbacks notifying that your loader is created/finished. This callbacks are implemented by the object you specified as third parameter (CALLBACK). It can be an activity, a fragment... You will find the syntax online.
Here is what it will look like:
// Callback called by your Activity
#Override
public Loader<OAuthConsumer> onCreateLoader(int id, Bundle arg1) {
loader = new getCommonsHttpOAuthConsumer();
return loader;
// After this method you're going in loadInBackground()
}
#Override
public void onLoadFinished(Loader<OAuthConsumer> loader, OAuthConsumer pl) {
// After loadInBackground() you arrive here, with your new object OAuthConsumer
this.consumer = pl;
}
It should work like this, hope it helps!