getImage();\r\n}\r\n<\/pre>\nWe are working on an implementation on iOS. Once this is ready, this functionality can be added to Charm Down, and developers don’t have to write boilerplate code for platform-specific functionality. For now, the platform-detection is done in the sample below.<\/p>\n
In order to call that Android native code from the JavaFX code in the main package, we create an abstract class with an abstract method returning the native interface. We will subclass it in the specific platforms, providing native implementations of the service, as well.<\/p>\n
\r\npublic abstract class GoNativePlatform {\r\n public abstract NativeService getNativeService();\r\n}\r\n<\/pre>\nOnce we know the platform we are running on, we can load at runtime the specific subclass:<\/p>\n
\r\npublic class GoNativePlatformFactory {\r\n \r\n public static GoNativePlatform getPlatform() {\r\n try {\r\n return (GoNativePlatform) Class.forName(getPlatformClassName()).newInstance();\r\n } catch (Throwable ex) {\r\n Logger.getLogger(GoNativePlatformFactory.class.getName()).log(Level.SEVERE, null, ex);\r\n return null;\r\n }\r\n }\r\n\r\n private static String getPlatformClassName() {\r\n switch ( System.getProperty(\"javafx.platform\", \"desktop\") ) {\r\n case \"android\": return \"com.gluonhq.demo.gonative.AndroidPlatform\";\r\n case \"ios\": return \"com.gluonhq.demo.gonative.IosPlatform\";\r\n default : return \"com.gluonhq.demo.gonative.DesktopPlatform\";\r\n }\r\n }\r\n}\r\n<\/pre>\nFinally, in our JavaFX application class we can create a platform-agnostic instance of the service, that will run only on the specific platform:<\/p>\n
\r\npublic class GoNativeFX extends MobileApplication {\r\n\r\n @Override public void init() {\r\n final NativeService nativeService = GoNativePlatformFactory.getPlatform().getNativeService();\r\n }\r\n}\r\n<\/pre>\nIn case we are running on Android, this will be the subclass called when getPlatform()<\/code> is invoked:<\/p>\n\r\npublic class AndroidPlatform extends GoNativePlatform {\r\n\r\n private AndroidNativeService nativeService;\r\n \r\n @Override public NativeService getNativeService() {\r\n if (nativeService == null) {\r\n nativeService = new AndroidNativeService();\r\n }\r\n return nativeService;\r\n }\r\n}\r\n<\/pre>\nThe service<\/h3>\n
Time to define the service on Android:<\/p>\n
\r\npublic class AndroidNativeService implements NativeService {\r\n\r\n @Override public void takePicture() {\r\n }\r\n\r\n @Override public void retrievePicture() {\r\n }\r\n\r\n @Override public ObjectProperty getImage() {\r\n return new SimpleObjectProperty<>();\r\n }\r\n<\/pre>\nAnd finally, time to get dirty going down into native Android code, fulfilling the required services.<\/p>\n
Let’s focus on taking a picture with the camera.<\/p>\n
Launching the activity<\/h3>\n
Usually, we create an Intent, and launch it from our JavaFX activity:<\/p>\n
\r\nprivate static final int TAKE_PICTURE = 2; \r\n\r\n@Override public void takePicture() {\r\n \/\/ create Intent\r\n Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);\r\n\r\n \/\/ launch Activity \r\n FXActivity.getInstance().startActivityForResult(intent, TAKE_PICTURE);\r\n}\r\n<\/pre>\nThis activity will launch the camera on the device, outside the JavaFX thread and outside our application.<\/p>\n
Waiting for the activity to finish<\/h3>\n
With the new implementation provided in the JavaFX Mobile SDK for Android, we are able to wait until the launched activity finishes, process the result, and go back to our app and JavaFX thread. So we have to add the result handler before launching the activity:<\/p>\n
\r\nprivate static final int TAKE_PICTURE = 2; \r\n\r\nprivate final ObjectProperty fxImage = new SimpleObjectProperty<>();\r\n\r\n@Override public void takePicture() {\r\n \/\/ create the Intent\r\n Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);\r\n\r\n \/\/ provide Uri to store the picture, adding extra parameters to intent\r\n File photo = new File(Environment.getExternalStorageDirectory(), \"Pic.jpg\");\r\n intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photo));\r\n final Uri imageUri = Uri.fromFile(photo);\r\n\r\n \/\/ Add result handler\r\n FXActivity.getInstance().setOnActivityResultHandler((requestCode, resultCode, data) -> {\r\n if (requestCode == TAKE_PICTURE && resultCode == RESULT_OK) {\r\n \/\/ retrieve picture from defined location \r\n String selectedImagePath = getPath(FXActivity.getInstance(), imageUri);\r\n Uri converted = Uri.fromFile(new File(selectedImagePath));\r\n \r\n \/\/ generate image and add it to property \r\n fxImage.set(new Image(converted.toString(), 300, 300, true, true));\r\n }\r\n });\r\n\r\n \/\/ launch activity\r\n FXActivity.getInstance().startActivityForResult(intent, TAKE_PICTURE);\r\n}\r\n<\/pre>\nGetting the image<\/h3>\n
Now, in our main application, we can simply listen to changes in fxImage<\/code>, and load the new image inside an ImageView<\/code>.<\/p>\n\r\nfinal ImageView imageView = new ImageView();\r\nnativeService.getImage().addListener((obs, oldImage, newImage) -> imageView.setImage(newImage));\r\n\r\nfinal Button buttonCamera = new Button(\"Take a Picture\", MaterialDesignIcon.CAMERA.graphic());\r\nbuttonCamera.setOnAction(e -> nativeService.takePicture());\r\n<\/pre>\nAs a result, we can take a picture with our android device’s camera, and load it in our application. This is shown in the screenshots below:<\/p>\n\n\t\t