Branch io link +clicked_branch_link is always false - react-native

I'm trying to implement Branch io links to my android app but I cant get them to work correctly. Using this guide: https://help.branch.io/developers-hub/docs/react-native
Dashboard configuration:
Android URI Scheme
https://com.example.host://
Android App links are enabled, app is selected from the play store and I have the SHA256 Cert Fingerprints entered.
Android manifest
<!-- Branch URI Scheme -->
<intent-filter>
<data android:scheme="example.scheme" android:host="open" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
<!-- Branch App Links -->
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" android:host="com.example.host"/>
<data android:scheme="https" android:host="com.example.host"/>
</intent-filter>
<!-- Branch keys -->
<!-- (Omit if setting keys in branch.json) -->
<meta-data android:name="io.branch.sdk.BranchKey" android:value="key_live_mykey"/>
<meta-data android:name="io.branch.sdk.BranchKey.test" android:value="key_test_mykey"/>
MainActivity
package com.efri_mobile;
import com.facebook.react.ReactActivity;
import com.facebook.react.ReactActivityDelegate;
import com.zoontek.rnbootsplash.RNBootSplash;
import io.branch.rnbranch.*;
import android.content.Intent;
public class MainActivity extends ReactActivity {
/**
* Returns the name of the main component registered from JavaScript. This is used to schedule
* rendering of the component.
*/
#Override
protected String getMainComponentName() {
return "efrimobile";
}
#Override
protected void onStart() {
super.onStart();
RNBranchModule.initSession(getIntent().getData(), this);
}
#Override
public void onNewIntent(Intent intent) {
super.onNewIntent(intent);
RNBranchModule.onNewIntent(intent);
}
#Override
protected ReactActivityDelegate createReactActivityDelegate() {
return new ReactActivityDelegate(this, getMainComponentName()) {
#Override
protected void loadApp(String appKey) {
RNBootSplash.init(MainActivity.this); // <- initialize the splash screen
super.loadApp(appKey);
}
};
}
}
MainApplication
package com.efri_mobile;
import android.app.Application;
import android.content.Context;
import com.facebook.react.PackageList;
import com.facebook.react.ReactApplication;
import com.oblador.vectoricons.VectorIconsPackage;
import com.oblador.vectoricons.VectorIconsPackage;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.soloader.SoLoader;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import io.branch.rnbranch.RNBranchModule;
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost =
new ReactNativeHost(this) {
#Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
#Override
protected List<ReactPackage> getPackages() {
#SuppressWarnings("UnnecessaryLocalVariable")
List<ReactPackage> packages = new PackageList(this).getPackages();
// Packages that cannot be autolinked yet can be added manually here, for example:
// packages.add(new MyReactNativePackage());
return packages;
}
#Override
protected String getJSMainModuleName() {
return "index";
}
};
#Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
#Override
public void onCreate() {
super.onCreate();
RNBranchModule.getAutoInstance(this);
SoLoader.init(this, /* native exopackage */ false);
initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
}
/**
* Loads Flipper in React Native templates. Call this in the onCreate method with something like
* initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
*
* #param context
* #param reactInstanceManager
*/
private static void initializeFlipper(
Context context, ReactInstanceManager reactInstanceManager) {
if (BuildConfig.DEBUG) {
try {
/*
We use reflection here to pick up the class that initializes Flipper,
since Flipper library is not available in release mode
*/
Class<?> aClass = Class.forName("com.efri_mobile.ReactNativeFlipper");
aClass
.getMethod("initializeFlipper", Context.class, ReactInstanceManager.class)
.invoke(null, context, reactInstanceManager);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
App.tsx
//Read branch link
useEffect(() => {
const unsubscribe = branch.subscribe(({ error, params, uri }) => {
if (error) {
console.error('Error from Branch: ' + error);
return;
}
console.log('PARAMS: ', params, uri);
});
return () => unsubscribe();
});
I am opening the link (example.app.link) from my dashboard app by clicking on a button. App opens correctly but +clicked_branch_link is always false.
I'm Not using the test link. The live key is correctly set in my android manifest.
This is my third attempt at implementing branch links in a year, and I always had problems with this part. What am I missing? Do I need to have Branch URI scheme if I want to use App links?

After a long time, I finally found a solution that somewhat works. The branch guide is quite misleading and following it doesn't work at all.
I followed this guide: https://dev.to/chakrihacker/integrating-branch-in-react-native-0-60-3lfj
If you follow the main branch guide, make sure you change parts of that to match the ones below:
info.plist
use the following for adding keys and URL schemas
<key>branch_key</key>
<string>key_live_******</string>
<key>branch_app_domain</key>
<string>9jzhz.app.link</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>branchapp</string>
</array>
</dict>
</array>
AppDelegate
There are some different things here and in the main guide
#import <RNBranch/RNBranch.h>
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Branch
[RNBranch initSessionWithLaunchOptions:launchOptions isReferrable:YES];
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
moduleName:#"BranchExample"
initialProperties:nil];
rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [UIViewController new];
rootViewController.view = rootView;
self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible];
return YES;
}
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
if (![RNBranch.branch application:app openURL:url options:options]) {
// do other deep link routing for the Facebook SDK, Pinterest SDK, etc
}
return YES;
}
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray *restorableObjects))restorationHandler {
return [RNBranch continueUserActivity:userActivity];
}
Make sure you update the key, branch_app_domain and branchapp url scheme with your values
And the most important thing: USE THE QUICK LINKS!
Using the main link does not work! You must create the quick link in the branch dashboard, and use that instead!
I got it to work for IOS and android.

Related

error activity need to extend ReactFragmentActivity or ReactCompatActivity when app is opened from an 3rd party app

I have a react-native application that is crushing if it is open from ODK Collect. It opens and works perfectly if it is open from its icon.
The error is In order to use RNScreens components your app's activity need to extend ReactFragmentActivity or ReactCompatActivity.
both which are deprecated. I am using react-native-screens ^3.8.0. I have tried various suggested solutions but I am not going anywhere.
Manifest:
<activity android:name=".DActivity" android:excludeFromRecents="true" android:noHistory="true">
<intent-filter>
<action android:name="com.d.odk"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
DActivity.java:
import android.app.Activity;
import android.os.Bundle;
import android.view.KeyEvent;
import com.babisoft.ReactNativeLocalization.ReactNativeLocalizationPackage;
import com.facebook.react.common.LifecycleState;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
import com.facebook.react.ReactRootView;
import com.facebook.react.shell.MainReactPackage;
import com.lugg.ReactNativeConfig.ReactNativeConfigPackage;
import com.oblador.keychain.KeychainPackage;
import com.reactnativecommunity.asyncstorage.AsyncStoragePackage;
import com.swmansion.gesturehandler.react.RNGestureHandlerPackage;
import java.util.logging.Logger;
import com.swmansion.reanimated.ReanimatedPackage;
import com.swmansion.rnscreens.RNScreensPackage;
import com.th3rdwave.safeareacontext.SafeAreaContextPackage;
public class DActivity extends Activity implements DefaultHardwareBackBtnHandler {
private ReactInstanceManager mReactInstanceManager;
private ReactRootView mReactRootView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); //tried null as well
mReactRootView = new ReactRootView(this);
Bundle initialProps = new Bundle();
initialProps.putString("AppCaller", "Denes");
mReactInstanceManager = ReactInstanceManager.builder()
.setApplication(getApplication())
.setCurrentActivity(this)
.setBundleAssetName("index.android.bundle")
.setJSMainModulePath("index")
.addPackage(new MainReactPackage())
.addPackage(new KeychainPackage())
.addPackage(new AsyncStoragePackage())
.addPackage(new ReactNativeConfigPackage())
.addPackage(new ReactNativeLocalizationPackage())
.addPackage(new ReanimatedPackage())
.addPackage(new RNScreensPackage())
.addPackage(new SafeAreaContextPackage())
.addPackage(new RNGestureHandlerPackage())
.setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(LifecycleState.RESUMED)
.build();
mReactRootView.startReactApplication(mReactInstanceManager, "app", initialProps);
setContentView(mReactRootView);
}
#Override
public void onBackPressed() {
if (mReactInstanceManager != null) {
mReactInstanceManager.onBackPressed();
} else {
super.onBackPressed();
}
}
#Override
public void invokeDefaultOnBackPressed() {
super.onBackPressed();
}
#Override
protected void onPause() {
super.onPause();
if (mReactInstanceManager != null) {
mReactInstanceManager.onHostPause(this);
}
}
#Override
protected void onResume() {
super.onResume();
if (mReactInstanceManager != null) {
mReactInstanceManager.onHostResume(this, this);
}
}
#Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_MENU && mReactInstanceManager != null) {
mReactInstanceManager.showDevOptionsDialog();
return true;
}
return super.onKeyUp(keyCode, event);
}
#Override
protected void onDestroy() {
super.onDestroy();
if (mReactInstanceManager != null) {
mReactInstanceManager.onHostDestroy(this);
}
if (mReactRootView != null) {
mReactRootView.unmountReactApplication();
}
}
}

Users of android app are not showing up in Firebase

I made an android app and in that I just created login and registration page using firebase but when I sign up with a new account then that respective account should be recorded or it should be displayed in the user section of my Firebase But in the user section "No users are added yet" this message is displayed. Why is it so?
This is my AndroidManifest.xml.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.easylearn">
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".HomeActivity"></activity>
<activity android:name=".LoginActivity" />
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
This is my MainActivity.java
package com.example.easylearn;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.auth.FirebaseAuth;
public class MainActivity extends AppCompatActivity {
EditText emailId,password;
Button btnSignUp;
TextView tvSignIn;
FirebaseAuth mFirebaseAuth;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mFirebaseAuth = FirebaseAuth.getInstance();
emailId = findViewById(R.id.editText);
password = findViewById(R.id.editText2);
btnSignUp = findViewById(R.id.button2);
tvSignIn = findViewById(R.id.textView);
btnSignUp.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String email = emailId.getText().toString();
String pwd = password.getText().toString();
if(email.isEmpty())
{
emailId.setError("Please enter email id");
emailId.requestFocus();
}
else if(pwd.isEmpty())
{
password.setError("Please enter password");
password.requestFocus();
}
else if(email.isEmpty() && pwd.isEmpty())
{
Toast.makeText(MainActivity.this,"Fiels are Empty!",Toast.LENGTH_SHORT).show();
}
else if(!(email.isEmpty() && pwd.isEmpty()))
{
mFirebaseAuth.createUserWithEmailAndPassword(email,pwd).addOnCompleteListener(MainActivity.this,
new OnCompleteListener<com.google.firebase.auth.AuthResult>() {
#Override
public void onComplete(#NonNull Task<com.google.firebase.auth.AuthResult> task)
{
if(task.isSuccessful())
{
Toast.makeText(MainActivity.this,"SignUp Unsuccessful,Please Try
Again..",Toast.LENGTH_SHORT).show();
}
else {
startActivity(new Intent(MainActivity.this,HomeActivity.class));
}
}
});
}
else
{
Toast.makeText(MainActivity.this,"Error Occured!",Toast.LENGTH_SHORT).show();
}
}
});
tvSignIn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent i = new Intent(MainActivity.this,LoginActivity.class);
startActivity(i);
}
});
}
}
This is my LoginActivity.java
package com.example.easylearn;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import com.google.firebase.auth.FirebaseUser;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.auth.FirebaseAuth;
public class LoginActivity extends AppCompatActivity {
EditText emailId,password;
Button btnSignIn;
TextView tvSignUp;
FirebaseAuth mFirebaseAuth;
private FirebaseAuth.AuthStateListener mAuthStateListener;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
mFirebaseAuth = FirebaseAuth.getInstance();
emailId = findViewById(R.id.editText);
password = findViewById(R.id.editText2);
btnSignIn = findViewById(R.id.button2);
tvSignUp = findViewById(R.id.textView);
mAuthStateListener = new FirebaseAuth.AuthStateListener() {
#Override
public void onAuthStateChanged(#NonNull FirebaseAuth firebaseAuth) {
FirebaseUser mFirebaseUser = mFirebaseAuth.getCurrentUser();
if(mFirebaseUser != null){
Toast.makeText(LoginActivity.this,"You are logged in",Toast.LENGTH_SHORT).show();
Intent i = new Intent(LoginActivity.this,HomeActivity.class);
startActivity(i);
}
else{
Toast.makeText(LoginActivity.this,"Please login",Toast.LENGTH_SHORT).show();
}
}
};
btnSignIn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String email = emailId.getText().toString();
String pwd = password.getText().toString();
if(email.isEmpty())
{
emailId.setError("Please enter email id");
emailId.requestFocus();
}
else if(pwd.isEmpty())
{
password.setError("Please enter password");
password.requestFocus();
}
else if(email.isEmpty() && pwd.isEmpty())
{
Toast.makeText(LoginActivity.this,"Fiels are Empty!",Toast.LENGTH_SHORT).show();
}
else if(!(email.isEmpty() && pwd.isEmpty()))
{
mFirebaseAuth.signInWithEmailAndPassword(email,pwd).addOnCompleteListener(LoginActivity.this, new
OnCompleteListener<com.google.firebase.auth.AuthResult>() {
#Override
public void onComplete(#NonNull Task<com.google.firebase.auth.AuthResult> task)
{
if(!task.isSuccessful()){
Toast.makeText(LoginActivity.this,"Login Error,Please Login
again..",Toast.LENGTH_SHORT).show();
}
else{
Intent inToHome = new Intent(LoginActivity.this,HomeActivity.class);
startActivity(inToHome);
}
}
});
}
else
{
Toast.makeText(LoginActivity.this,"Error Occured!",Toast.LENGTH_SHORT).show();
}
}
});
tvSignUp.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intSignUp = new Intent(LoginActivity.this,MainActivity.class);
startActivity(intSignUp);
}
});
}
#Override
protected void onStart() {
super.onStart();
mFirebaseAuth.addAuthStateListener(mAuthStateListener);
}
}`

NPE in JavaFX when call the field in controller

I want to make my undecorated scene draggable. My root layout contain toolbar with controls and content. I gave id for toolbar and set controller for .fxml layout.
Then i do all steps like in Dragging an undecorated Stage in JavaFX
But i have null pointer, when try to call EffectUtilities.makeDraggable(stage, tool_bar);
Here is the code:
Main
public class UpdaterMain extends Application{
private Stage primaryStage;
private BorderPane rootLayout;
#Override
public void start(Stage primaryStage) throws Exception {
this.primaryStage = primaryStage;
this.primaryStage.initStyle(StageStyle.UNDECORATED);
this.primaryStage.initStyle(StageStyle.TRANSPARENT);
initRootLayout();
showContentOverview();
}
/**
* Initializes the root layout.
*/
public void initRootLayout() {
try {
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(UpdaterMain.class.getResource("RootLayout.fxml"));
rootLayout = (BorderPane) loader.load();
// Show the scene containing the root layout.
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
UpdaterMainController controller =
loader.getController();
controller.registerStage(primaryStage); <<--Here is NPE
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Shows main content
*/
public void showContentOverview() {
try {
// Load person overview.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(UpdaterMain.class.getResource("updater-layout.fxml"));
SplitPane contentOverview = loader.load();
// Set person overview into the center of root layout.
rootLayout.setCenter(contentOverview);
} catch (IOException e) {
e.printStackTrace();
}
}
public Stage getPrimaryStage() {
return primaryStage;
}
public static void main(String[] args) {
launch(args);
}
}
Controller
import com.iminegination.utils.EffectUtilities;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.ToolBar;
import javafx.stage.Stage;
public class UpdaterMainController {
#FXML
private ToolBar tool_bar;
public void registerStage(Stage stage) {
EffectUtilities.makeDraggable(stage, tool_bar); <<--Here is NPE (tool_bar is null, but why?)
}
public UpdaterMainController() {
}
}
layout.fxml
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="690.0" prefWidth="1024.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.iminegination.updater.view.UpdaterMainController">
<top>
<ToolBar id="tool_bar" nodeOrientation="RIGHT_TO_LEFT" prefHeight="40.0" prefWidth="200.0">
<items>
<Button id="exitButton" mnemonicParsing="false" text="X" onAction="#click"/>
<Button mnemonicParsing="false" text="Settings" />
<Button mnemonicParsing="false" text="_" />
</items>
</ToolBar>
</top>
</BorderPane>
You need to define an fx:id attribute for your controls in FXML.
In your FXML you have:
<ToolBar id="tool_bar" . . .
You should have:
<ToolBar id="tool_bar" fx:id="tool_bar" . . .
id defines the CSS id.
fx:id defines the link between the FXML and the controller variable names.

My android application cannot play again when clicking button

I'm new in android programming. My first application is an android mediaplayer.
I built two buttons : one to play a song, and another to stop it.
My application is running correctly ; the problem is that i can play and stop it, but I cant play the song again.
I tried to use setDataSource() but it triggers an error.
Here's the code ; the file is in raw/song.mp3
package com.example.test6;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.app.Activity;
import android.view.View;
import android.widget.Button;
public class MainActivity extends Activity {
MediaPlayer mp;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mp = MediaPlayer.create(this,R.raw.song);
setContentView(R.layout.activity_main);
final Button btnPlay = (Button)this.findViewById(R.id.button1);
final Button btnStop = (Button)this.findViewById(R.id.button2);
btnPlay.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
mp.start();
}
});
btnStop.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
mp.stop();
mp.reset();
if(mp.isPlaying()){
mp.stop();
}else{
mp.setDataSource("res/raw/song.mp3");
mp.prepare();
mp.start();
}
});
}
}
First of all, you don't need to set the data source manually if you're doing the creation like this: MediaPlayer.create(this, R.raw.song); Also, in that case, you don't need to call prepare() because MediaPlayer.create() does that for you.
I would suggest this kind of approach (assuming you don't want the start button to do anything if it's already playing):
Start button listener:
if (mp==null) {
mp = MediaPlayer.create(this, R.raw.song);
}
if (!mp.isPlaying()) {
mp.start();
}
Stop button listener:
if (mp!=null) {
if (mp.isPlaying()) {
mp.stop();
}
mp.release();
mp = null;
}
I didn't test this, so I'm looking forward to your response

JavaFX app in System Tray

I am Making a Simple App using JavaFX UI, The app simply just do that:
has a systray icon, which when clicked shows a window, when clicked again hides it, on rightclick shows a menu with 1 "exit" item
I already Made the UI and put the App in the Sys Tray, but i can't show/hide it using Normal Actionlistener method, but i got this error:
Exception in thread "AWT-EventQueue-0" java.lang.IllegalStateException: Not on FX application thread; currentThread = AWT-EventQueue-0
here is the Code:
import java.awt.Image;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import java.awt.SystemTray;
import java.awt.Toolkit;
import java.awt.TrayIcon;
import java.awt.event.ActionListener;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class Main extends Application{
public static void main(String[] args) {
launch(args);
}
#Override
public void start(final Stage primaryStage) {
primaryStage.setTitle("Hello World!");
Button btn = new Button();
btn.setText("Say 'Hello World'");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.println("Hello World!"); }
});
StackPane root = new StackPane();
root.getChildren().add(btn);
primaryStage.setScene(new Scene(root, 300, 250));
primaryStage.show();
if (SystemTray.isSupported()) {
SystemTray tray = SystemTray.getSystemTray();
Image image = Toolkit.getDefaultToolkit().getImage("Germany-politcal-map.jpg");
PopupMenu popup = new PopupMenu();
MenuItem item = new MenuItem("Exit");
popup.add(item);
TrayIcon trayIcon = new TrayIcon(image, "Amr_Trial", popup);
ActionListener listener = new ActionListener() {
#Override
public void actionPerformed(java.awt.event.ActionEvent arg0) {
// TODO Auto-generated method stub
System.exit(0);
}
};
ActionListener listenerTray = new ActionListener() {
#Override
public void actionPerformed(java.awt.event.ActionEvent arg0) {
// TODO Auto-generated method stub
primaryStage.hide();
}
};
trayIcon.addActionListener(listenerTray);
item.addActionListener(listener);
try{
tray.add(trayIcon);
}catch (Exception e) {
System.err.println("Can't add to tray");
}
} else {
System.err.println("Tray unavailable");
}
//
}
}
Wrap the code in the actionListener which calls back to JavaFX in Platform.runLater. This will execute the code which interfaces with the JavaFX system on the JavaFX application thread rather than trying to do it on the Swing event thread (which is what is causing you issues).
For example:
ActionListener listenerTray = new ActionListener() {
#Override public void actionPerformed(java.awt.event.ActionEvent event) {
Platform.runLater(new Runnable() {
#Override public void run() {
primaryStage.hide();
}
});
}
};
By default the application will shutdown when it's last window is hidden. To override this default behaviour, invoke Platform.setImplicitExit(false) before you show the first application Stage. You will then need to explicitly call Platform.exit() when you need the application to really shutdown.
I created a demo for using the AWT system tray within a JavaFX application.
You should only modify the javafx classes on the javafx thread, the listeners on the tray icon are likely to be running on the swing thread. You can do this by posting a runnable to Platform#runLater like so:
Platform.runLater(new Runnable() {
public void run() {
primaryStage.hide();
}
});
The system tray is not supported in JavaFX yet. You could track the progress on this task under the following JIRA issue: https://bugs.openjdk.java.net/browse/JDK-8090475
The issue also provides a workaround, which could be used in JavaFX 8 to get the basic support.
The feature is not planned for JavaFX 8, so it might be released in one of the following updates or even in JavaFX 9.
Shameless self-plug, but I developed a small wrapper library for JavaFX icons that use the SystemTray called FXTrayIcon.
It abstracts away all of the nasty AWT bits and eliminates having to guess which thread you should be running code on. It's available as a dependency on Maven Central.
I resolved your issue. JavaFX with AWT. I have one example of a application that shows and hides when you make left clic. i really hope works for you
import java.awt.AWTException;
import java.awt.Image;
import java.awt.SystemTray;
import java.awt.Toolkit;
import java.awt.TrayIcon;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.net.URL;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class MainApp2 extends Application {
int stateWindow = 1;
#Override
public void start(final Stage stage) throws Exception {
//Check the SystemTray is supported
if (!SystemTray.isSupported()) {
System.out.println("SystemTray is not supported");
return;
}
URL url = System.class.getResource("/image/yourImage.png");
Image image = Toolkit.getDefaultToolkit().getImage(url);
//image dimensions must be 16x16 on windows, works for me
final TrayIcon trayIcon = new TrayIcon(image, "application name");
final SystemTray tray = SystemTray.getSystemTray();
//Listener left clic XD
trayIcon.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent event) {
if (event.getButton() == MouseEvent.BUTTON1) {
Platform.runLater(new Runnable() {
#Override
public void run() {
if (stateWindow == 1) {
stage.hide();
stateWindow = 0;
} else if (stateWindow == 0) {
stage.show();
stateWindow = 1;
}
}
});
}
}
});
try {
tray.add(trayIcon);
} catch (AWTException e) {
System.out.println("TrayIcon could not be added.");
}
stage.setTitle("Hello man!");
Button btn = new Button();
btn.setText("Say 'Hello man'");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.println("Hello man!");
}
});
StackPane root = new StackPane();
root.getChildren().add(btn);
stage.setScene(new Scene(root, 300, 250));
Platform.setImplicitExit(false);
stage.show();
}
/**
* The main() method is ignored in correctly deployed JavaFX application.
* main() serves only as fallback in case the application can not be
* launched through deployment artifacts, e.g., in IDEs with limited FX
* support. NetBeans ignores main().
*
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}