javaandroidjavafxgluon-mobilejavafxports

image chooser javafx android mobile


How to choose image from android directory/gallery in javafxmobile-plugin. I am unable to choose files like images in android device. when i open directory app hangs and suddenly close. Any help???

I already have tried in android device using javafx file chooser && directory choose but notthing helps. also i found no helpful post on the stackoverflow

my build.gradle file:

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'org.javafxports:jfxmobile-plugin:1.3.16'
    }
}

apply plugin: 'org.javafxports.jfxmobile'

repositories {
    jcenter()
    maven {
        url 'http://nexus.gluonhq.com/nexus/content/repositories/releases'
    }
}

mainClassName = 'com.maqboolsolutions.directorychooserandroid.App'

ext.CHARM_DOWN_VERSION = "1.0.0"

dependencies { 
    compile "com.gluonhq:charm-down-common:$CHARM_DOWN_VERSION"
    desktopRuntime "com.gluonhq:charm-down-desktop:$CHARM_DOWN_VERSION"
    androidRuntime "com.gluonhq:charm-down-android:$CHARM_DOWN_VERSION"

    compile 'com.airhacks:afterburner.mfx:1.6.3'
}

jfxmobile {
    javafxportsVersion = '8.60.11'

    android {
        manifest = 'src/android/AndroidManifest.xml'

        packagingOptions {
            exclude 'META-INF/INDEX.LIST'
        }

        dexOptions {
            javaMaxHeapSize "4g"
        }
    }
}

.java codding:

@FXML
private void btnCooserOnAction(ActionEvent event) {
    DirectoryChooser directoryChooser = new DirectoryChooser();

    Stage primaryStage = (Stage) stackPaneRoot.getScene().getWindow();

    File selectedDirectory = directoryChooser.showDialog(primaryStage);

    if (selectedDirectory == null) {
        lblPath.setText("No Directory selected");
    } else {
        lblPath.setText(selectedDirectory.getAbsolutePath());
    }
}

App not response. and close...


Solution

  • After some digging and based on helpful comments from @josé-pereda, here’s the updated and final answer for anyone revisiting similar issues, even though JavaFX Mobile and Charm Down for JDK 1.8 have been deprecated for years. While GluonFX is the recommended modern replacement (supporting Java 11+ via GraalVM), here’s a solution for those still relying on older setups.


    Key Points:

    1. Charm Down Plugins: Charm Down plugins are open-source and can be used independently of Gluon Mobile. For example, you can use the PicturesService plugin to access the gallery or take photos without needing a paid Gluon Mobile license (which only removes the nag screen in the free version).
    2. Updated Libraries: Use Charm Down v3.8.6 (not the older 1.x versions). It offers much better compatibility and stability:

    Example Gradle Setup:

    buildscript {
        repositories {
            jcenter()
        }
        dependencies {
            classpath 'org.javafxports:jfxmobile-plugin:1.3.18'
        }
    }
    
    apply plugin: 'org.javafxports.jfxmobile'
    
    repositories {
        jcenter()
        maven {
            url 'https://nexus.gluonhq.com/nexus/content/repositories/releases'
        }
    }
    
    mainClassName = 'org.javafxports.hellofx.App'
    
    dependencies {
    //    compile 'com.gluonhq:charm-down-plugin-pictures-android:3.8.6'
        compile 'org.lsposed.hiddenapibypass:hiddenapibypass:4.3'
    }
    
    jfxmobile {
        downConfig {
            version = '3.8.6'
            plugins 'display', 'lifecycle', 'statusbar', 'storage', 'pictures'
        }
    
        android {
            manifest = 'src/android/AndroidManifest.xml'
            compileSdkVersion '35'
            buildToolsVersion '35.0.0'
    
            dexOptions {
                incremental true
                javaMaxHeapSize "7g"
            }
        }
    }
    
    project.afterEvaluate {
        explodeAarDependencies(project.configurations.androidCompile)
        explodeAarDependencies(project.configurations.compile)
    }
    

    Example Implementation for Picture Selection:

    App.java

    public class App extends Application {
    
        static {
            if (Platform.isAndroid()) {
                HiddenApiBypass.addHiddenApiExemptions("L");
            }
        }
    
        @Override
        public void start(Stage primaryStage) throws IOException {
            FXMLLoader loader = new FXMLLoader(getClass().getResource("/org/javafxports/hellofx/main.fxml"));
            Parent root = loader.load();
    
            Scene scene;
            if (Platform.isDesktop()) {
                scene = new Scene(root);
            } else {
                Rectangle2D visualBounds = Screen.getPrimary().getVisualBounds();
                scene = new Scene(root, visualBounds.getWidth(), visualBounds.getHeight());
            }
    
            primaryStage.setTitle("JavaFX 8 with FXML");
            primaryStage.setScene(scene);
            primaryStage.show();
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    }
    

    MainController.java:

    public class MainController {
    
        @FXML
        private ImageView imageView;
    
        @FXML
        void btnLoadOnAction(ActionEvent event) {
            if (Platform.isDesktop()) {
                try {
                    FileChooser fileChooser = new FileChooser();
                    fileChooser.setTitle("Select an Image");
    
                    fileChooser.getExtensionFilters().addAll(
                            new FileChooser.ExtensionFilter("Image Files", "*.png", "*.jpg", "*.jpeg", "*.gif")
                    );
    
                    File selectedFile = fileChooser.showOpenDialog(null);
    
                    if (selectedFile != null) {
                        Image image = new Image(selectedFile.toURI().toString());
                        imageView.setImage(image);
                    }
                } catch (Exception ex) {
                    Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, ex);
                }
            } else if (Platform.isAndroid()) {
                Services.get(PicturesService.class).ifPresent(service ->
                        service.loadImageFromGallery().ifPresent(image ->
                                imageView.setImage(image)));
    
    //            Services.get(PicturesService.class).ifPresent(service ->
    //                    service.takePhoto(true).ifPresent(image ->
    //                            imageView.setImage(image)));
            }
        }
    
        @FXML
        void initialize() {
        }
    }
    

    AndroidManifest.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
              package="org.javafxports.hellofx"
              android:versionCode="1"
              android:versionName="1.0">
    
        <uses-sdk
                android:minSdkVersion="21"
                android:targetSdkVersion="35"/>
    
        <supports-screens android:xlargeScreens="true"/>
    
        <uses-permission android:name="android.permission.INTERNET"/>
        <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
        <uses-permission android:name="android.permission.CAMERA"/>
    
        <application
                android:label="HelloFX"
                android:name="android.support.multidex.MultiDexApplication"
                android:icon="@mipmap/ic_launcher"
                android:requestLegacyExternalStorage="true">
    
            <activity
                    android:name="javafxports.android.FXActivity"
                    android:label="HelloFX"
                    android:configChanges="orientation|screenSize"
                    android:exported="true">
    
                <meta-data
                        android:name="main.class"
                        android:value="org.javafxports.hellofx.App"/>
                <meta-data
                        android:name="debug.port"
                        android:value="0"/>
    
                <intent-filter>
                    <action android:name="android.intent.action.MAIN"/>
                    <category android:name="android.intent.category.LAUNCHER"/>
                </intent-filter>
    
            </activity>
    
            <activity android:name="com.gluonhq.impl.charm.down.plugins.android.PermissionRequestActivity"/>
    
            <provider
                    android:name="android.support.v4.content.FileProvider"
                    android:authorities="org.javafxports.hellofx.fileprovider"
                    android:exported="false"
                    android:grantUriPermissions="true">
    
                <meta-data
                        android:name="android.support.FILE_PROVIDER_PATHS"
                        android:resource="@xml/file_provider_paths"/>
    
            </provider>
    
        </application>
    </manifest>
    

    xml/file_provider_paths.xml

    <?xml version="1.0" encoding="utf-8"?>
    <paths>
        <external-path name="external_files" path="."/>
    </paths>
    

    Summary:

    Using Charm Down PicturesService in a JavaFX/JavaFXPorts project allows image selection and camera integration. This avoids the need for Gluon Mobile entirely. However, for modern Java (11+), I strongly recommend transitioning to GluonFX, which is robust, actively maintained, and integrates well with GraalVM.

    Edit:

    Complete working project to demonstrate this issue: https://github.com/Maqbool-Solutions-Open-Source/hellofx-javafxports-picture-chooser.git