0025195: Samples - add Java sample for Android 4.x
authorkgv <kgv@opencascade.com>
Fri, 7 Nov 2014 08:20:13 +0000 (12:20 +0400)
committerbugmaster <bugmaster@opencascade.com>
Fri, 7 Nov 2014 14:53:53 +0000 (17:53 +0300)
44 files changed:
samples/java/jniviewer/.classpath [new file with mode: 0644]
samples/java/jniviewer/.externalToolBuilders/C++ Builder.launch [new file with mode: 0644]
samples/java/jniviewer/.gitignore [new file with mode: 0644]
samples/java/jniviewer/.project [new file with mode: 0644]
samples/java/jniviewer/.settings/org.eclipse.jdt.core.prefs [new file with mode: 0644]
samples/java/jniviewer/AndroidManifest.xml [new file with mode: 0644]
samples/java/jniviewer/ReadMe.md [new file with mode: 0644]
samples/java/jniviewer/jni/Android.mk [new file with mode: 0644]
samples/java/jniviewer/jni/Application.mk [new file with mode: 0644]
samples/java/jniviewer/jni/OcctJni_MsgPrinter.cxx [new file with mode: 0644]
samples/java/jniviewer/jni/OcctJni_MsgPrinter.hxx [new file with mode: 0644]
samples/java/jniviewer/jni/OcctJni_Viewer.cxx [new file with mode: 0644]
samples/java/jniviewer/jni/OcctJni_Viewer.hxx [new file with mode: 0644]
samples/java/jniviewer/jni/OcctJni_Window.cxx [new file with mode: 0644]
samples/java/jniviewer/jni/OcctJni_Window.hxx [new file with mode: 0644]
samples/java/jniviewer/project.properties [new file with mode: 0644]
samples/java/jniviewer/res/drawable-hdpi/close_l.png [new file with mode: 0644]
samples/java/jniviewer/res/drawable-hdpi/close_p.png [new file with mode: 0644]
samples/java/jniviewer/res/drawable-hdpi/fit.png [new file with mode: 0644]
samples/java/jniviewer/res/drawable-hdpi/ic_launcher.png [new file with mode: 0755]
samples/java/jniviewer/res/drawable-hdpi/info.png [new file with mode: 0644]
samples/java/jniviewer/res/drawable-hdpi/info_image.png [new file with mode: 0644]
samples/java/jniviewer/res/drawable-hdpi/message.png [new file with mode: 0644]
samples/java/jniviewer/res/drawable-hdpi/open.png [new file with mode: 0644]
samples/java/jniviewer/res/drawable-hdpi/open_l.png [new file with mode: 0644]
samples/java/jniviewer/res/drawable-hdpi/open_p.png [new file with mode: 0644]
samples/java/jniviewer/res/drawable-hdpi/proj_back.png [new file with mode: 0755]
samples/java/jniviewer/res/drawable-hdpi/proj_bottom.png [new file with mode: 0755]
samples/java/jniviewer/res/drawable-hdpi/proj_front.png [new file with mode: 0755]
samples/java/jniviewer/res/drawable-hdpi/proj_left.png [new file with mode: 0755]
samples/java/jniviewer/res/drawable-hdpi/proj_right.png [new file with mode: 0755]
samples/java/jniviewer/res/drawable-hdpi/proj_top.png [new file with mode: 0755]
samples/java/jniviewer/res/drawable-hdpi/view.png [new file with mode: 0644]
samples/java/jniviewer/res/drawable-mdpi/ic_launcher.png [new file with mode: 0755]
samples/java/jniviewer/res/drawable-xhdpi/ic_launcher.png [new file with mode: 0755]
samples/java/jniviewer/res/drawable-xxhdpi/ic_launcher.png [new file with mode: 0755]
samples/java/jniviewer/res/layout/activity_main.xml [new file with mode: 0644]
samples/java/jniviewer/res/values/id.xml [new file with mode: 0644]
samples/java/jniviewer/res/values/strings.xml [new file with mode: 0644]
samples/java/jniviewer/src/com/opencascade/jnisample/OcctJniActivity.java [new file with mode: 0644]
samples/java/jniviewer/src/com/opencascade/jnisample/OcctJniFileDialog.java [new file with mode: 0644]
samples/java/jniviewer/src/com/opencascade/jnisample/OcctJniLogger.java [new file with mode: 0644]
samples/java/jniviewer/src/com/opencascade/jnisample/OcctJniRenderer.java [new file with mode: 0644]
samples/java/jniviewer/src/com/opencascade/jnisample/OcctJniView.java [new file with mode: 0644]

diff --git a/samples/java/jniviewer/.classpath b/samples/java/jniviewer/.classpath
new file mode 100644 (file)
index 0000000..5176974
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
+       <classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
+       <classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
+       <classpathentry kind="src" path="src"/>
+       <classpathentry kind="src" path="gen"/>
+       <classpathentry kind="output" path="bin/classes"/>
+</classpath>
diff --git a/samples/java/jniviewer/.externalToolBuilders/C++ Builder.launch b/samples/java/jniviewer/.externalToolBuilders/C++ Builder.launch
new file mode 100644 (file)
index 0000000..cbe7a12
--- /dev/null
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="org.eclipse.ui.externaltools.ProgramBuilderLaunchConfigurationType">
+<stringAttribute key="org.eclipse.debug.core.ATTR_REFRESH_SCOPE" value="${working_set:&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;resources&gt;&#10;&lt;item path=&quot;/occtJniActivity/libs&quot; type=&quot;2&quot;/&gt;&#10;&lt;/resources&gt;}"/>
+<booleanAttribute key="org.eclipse.debug.ui.ATTR_LAUNCH_IN_BACKGROUND" value="false"/>
+<booleanAttribute key="org.eclipse.ui.externaltools.ATTR_BUILDER_ENABLED" value="true"/>
+<stringAttribute key="org.eclipse.ui.externaltools.ATTR_BUILD_SCOPE" value="${working_set:&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;resources&gt;&#10;&lt;item path=&quot;/occtJniActivity/jni&quot; type=&quot;2&quot;/&gt;&#10;&lt;/resources&gt;}"/>
+<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="~/develop/android-ndk-r10/ndk-build"/>
+<stringAttribute key="org.eclipse.ui.externaltools.ATTR_RUN_BUILD_KINDS" value="full,incremental,auto,"/>
+<stringAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS" value="V=1 jniall"/>
+<booleanAttribute key="org.eclipse.ui.externaltools.ATTR_TRIGGERS_CONFIGURED" value="true"/>
+<stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${workspace_loc:/occtJniActivity/jni}"/>
+</launchConfiguration>
diff --git a/samples/java/jniviewer/.gitignore b/samples/java/jniviewer/.gitignore
new file mode 100644 (file)
index 0000000..890072f
--- /dev/null
@@ -0,0 +1,4 @@
+/assets
+/bin
+/gen
+/libs
diff --git a/samples/java/jniviewer/.project b/samples/java/jniviewer/.project
new file mode 100644 (file)
index 0000000..c258add
--- /dev/null
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>occtJniActivity</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
+                       <triggers>auto,full,incremental,</triggers>
+                       <arguments>
+                               <dictionary>
+                                       <key>LaunchConfigHandle</key>
+                                       <value>&lt;project&gt;/.externalToolBuilders/C++ Builder.launch</value>
+                               </dictionary>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.jdt.core.javabuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>com.android.ide.eclipse.adt.ApkBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>com.android.ide.eclipse.adt.AndroidNature</nature>
+               <nature>org.eclipse.jdt.core.javanature</nature>
+       </natures>
+</projectDescription>
diff --git a/samples/java/jniviewer/.settings/org.eclipse.jdt.core.prefs b/samples/java/jniviewer/.settings/org.eclipse.jdt.core.prefs
new file mode 100644 (file)
index 0000000..b080d2d
--- /dev/null
@@ -0,0 +1,4 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.source=1.6
diff --git a/samples/java/jniviewer/AndroidManifest.xml b/samples/java/jniviewer/AndroidManifest.xml
new file mode 100644 (file)
index 0000000..c0cbbbb
--- /dev/null
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.opencascade.jnisample">
+    <application android:label="@string/app_name" android:icon="@drawable/ic_launcher">
+        <activity android:name="OcctJniActivity"
+                  android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
+                  android:launchMode="singleTask"
+                  android:configChanges="orientation|keyboardHidden|screenSize">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.VIEW" />
+
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.BROWSABLE" />
+
+                <data android:scheme="" />
+                <data android:scheme="file" />
+                <data android:scheme="content" />
+                <data android:host="*" />
+
+                <data android:pathPattern=".*\\.brep" />
+                <data android:pathPattern=".*\\.rle" />
+                <data android:pathPattern=".*\\.step" />
+                <data android:pathPattern=".*\\.stp" />
+                <data android:pathPattern=".*\\.iges" />
+                <data android:pathPattern=".*\\.igs" />
+            </intent-filter>
+        </activity>
+    </application>
+    <uses-feature android:glEsVersion="0x00020000"/>
+    <uses-sdk android:minSdkVersion="15"/>
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+</manifest>
diff --git a/samples/java/jniviewer/ReadMe.md b/samples/java/jniviewer/ReadMe.md
new file mode 100644 (file)
index 0000000..c1dd21d
--- /dev/null
@@ -0,0 +1,58 @@
+OCCT JniViewer sample for Android {#samples_java_android_occt}
+================== 
+
+This sample demonstrates simple way of using OCCT libraries in Android application written using Java.
+
+The connection between Java and OCCT (C++) level is provided by proxy library, libTKJniSample.so, written in C++ with exported JNI methods of Java class OcctJniRenderer.
+The proxy library contains single C++ class OcctJni_Viewer encapsulating OCCT viewer and providing functionality to manipulate this viewer
+and to import OCCT shapes from several supported formats of CAD files (IGES, STEP, BREP).
+
+This sample demonstrates indirect method of wrapping C++ to Java using manually created proxy library.
+Alternative method is available, wrapping individual OCCT classes to Java equivalents so that their full API is available to Java user
+and the code can be programmed on Java level similarly to C++ one.
+See description of OCCT Java Wrapper in Advanced Samples and Tools on OCCT web site at 
+http://www.opencascade.org/support/products/advsamples
+
+Run Eclipse from ADT (Android Developer Tools) for building the sample. To import sample project perform
+~~~~
+  File -> Import... -> Android -> Existing Android code into Workspace
+~~~~
+and specify this directory. The project re-build will be started immediately right after importation if "Build automatically" option is turned on (default in Eclipse).  
+Proxy library compilation and packaging is performed by NDK build script, called by "C++ Builder" configured within Eclipse project.
+The path to "ndk-build" tool from Android NDK (Native Development Kit) should be specified in Eclipse project properties:
+~~~~
+  Project -> Properties -> Builders -> C++ Builder -> Edit -> Location
+~~~~
+
+Now paths to OCCT C++ libraries and additional components should be specified in "jni/Android.mk" file:
+~~~~
+OCCT_ROOT := $(LOCAL_PATH)/../../../..
+
+FREETYPE_INC  := $(OCCT_ROOT)/../freetype/include/freetype2
+FREETYPE_LIBS := $(OCCT_ROOT)/../freetype/libs
+
+FREEIMAGE_INC  := $(OCCT_ROOT)/../FreeImage/include
+FREEIMAGE_LIBS := $(OCCT_ROOT)/../FreeImage/libs
+
+OCCT_INC  := $(OCCT_ROOT)/inc
+OCCT_LIBS := $(OCCT_ROOT)/and/libs
+~~~~
+The list of extra components (Freetype, FreeImage) depends on OCCT configuration.
+Variable $(TARGET_ARCH_ABI) is used within this script to refer to active architecture.
+E.g. for 32-bit ARM build (see variable *APP_ABI* in "jni/Application.mk")
+the folder *OCCT_LIBS* should contain sub-folder "armeabi-v7a" with OCCT libraries.
+
+FreeImage is optional and does not required for this sample, however you should include all extra libraries used for OCCT building
+and load the explicitly from Java code within OcctJniActivity::loadNatives() method, including toolkits from OCCT itself in proper order:
+~~~~
+    if (!loadLibVerbose ("TKernel", aLoaded, aFailed)
+     || !loadLibVerbose ("TKMath",  aLoaded, aFailed)
+     || !loadLibVerbose ("TKG2d",   aLoaded, aFailed)
+~~~~
+Note that C++ STL library is not part of Android system.
+Thus application must package this library as well as extra component.
+"gnustl_shared" STL implementation is expected within this sample.
+
+After successful build, the application can be packaged to Android:
+- Deploy and run application on connected device or emulator directly from Eclipse using adb interface by menu items "Run" and "Debug". This would sign package with debug certificate.
+- Prepare signed end-user package using wizard File -> Export -> Android -> Export Android Application.
diff --git a/samples/java/jniviewer/jni/Android.mk b/samples/java/jniviewer/jni/Android.mk
new file mode 100644 (file)
index 0000000..912d5e3
--- /dev/null
@@ -0,0 +1,215 @@
+LOCAL_PATH:= $(call my-dir)
+
+STL_INC := $(NDK_ROOT)/sources/cxx-stl/gnu-libstdc++/$(NDK_TOOLCHAIN_VERSION)/include $(NDK_ROOT)/sources/cxx-stl/gnu-libstdc++/$(NDK_TOOLCHAIN_VERSION)/libs/$(TARGET_ARCH_ABI)/include
+#STL_LIB := $(NDK_ROOT)/sources/cxx-stl/gnu-libstdc++/$(NDK_TOOLCHAIN_VERSION)/libs/$(TARGET_ARCH_ABI)/libgnustl_static.a
+STL_LIB := $(NDK_ROOT)/sources/cxx-stl/gnu-libstdc++/$(NDK_TOOLCHAIN_VERSION)/libs/$(TARGET_ARCH_ABI)/libgnustl_shared.so
+
+OCCT_ROOT := $(LOCAL_PATH)/../../../..
+
+FREETYPE_INC  := $(OCCT_ROOT)/../freetype/include/freetype2
+FREETYPE_LIBS := $(OCCT_ROOT)/../freetype/libs
+
+FREEIMAGE_INC  := $(OCCT_ROOT)/../FreeImage/include
+FREEIMAGE_LIBS := $(OCCT_ROOT)/../FreeImage/libs
+
+OCCT_INC  := $(OCCT_ROOT)/inc
+OCCT_LIBS := $(OCCT_ROOT)/and/libs
+
+ASSETDIR := $(LOCAL_PATH)/../assets
+
+$(ASSETDIR)/Shaders: $(ASSETDIR)
+       -mkdir -p $(ASSETDIR)
+       -mkdir -p $(ASSETDIR)/Shaders
+       cp -f -r $(OCCT_ROOT)/src/Shaders/*.* $(ASSETDIR)/Shaders
+
+$(ASSETDIR)/SHMessage: $(ASSETDIR)
+       -mkdir -p $(ASSETDIR)
+       -mkdir -p $(ASSETDIR)/SHMessage
+       cp -f -r $(OCCT_ROOT)/src/SHMessage/*.* $(ASSETDIR)/SHMessage
+
+$(ASSETDIR)/XSMessage: $(ASSETDIR)
+       -mkdir -p $(ASSETDIR)
+       -mkdir -p $(ASSETDIR)/XSMessage
+       cp -f -r $(OCCT_ROOT)/src/XSMessage/*.* $(ASSETDIR)/XSMessage
+
+$(ASSETDIR)/TObj: $(ASSETDIR)
+       -mkdir -p $(ASSETDIR)
+       -mkdir -p $(ASSETDIR)/TObj
+       cp -f -r $(OCCT_ROOT)/src/TObj/*.msg $(ASSETDIR)/TObj
+
+$(ASSETDIR)/UnitsAPI: $(ASSETDIR)
+       -mkdir -p $(ASSETDIR)
+       -mkdir -p $(ASSETDIR)/UnitsAPI
+       cp -f -r $(OCCT_ROOT)/src/UnitsAPI/*.dat $(ASSETDIR)/UnitsAPI
+
+pre_all: $(ASSETDIR)/Shaders $(ASSETDIR)/SHMessage $(ASSETDIR)/XSMessage $(ASSETDIR)/TObj $(ASSETDIR)/UnitsAPI
+
+jniall: pre_all all
+
+# STL libs
+include $(CLEAR_VARS)
+LOCAL_MODULE := SharedStl
+LOCAL_EXPORT_C_INCLUDES := $(STL_INC)
+LOCAL_SRC_FILES := $(STL_LIB)
+include $(PREBUILT_SHARED_LIBRARY)
+
+# 3rd-parties used in OCCT
+include $(CLEAR_VARS)
+LOCAL_MODULE := FreeType
+LOCAL_EXPORT_C_INCLUDES := $(FREETYPE_INC)
+LOCAL_SRC_FILES := $(FREETYPE_LIBS)/$(TARGET_ARCH_ABI)/libfreetype.so
+include $(PREBUILT_SHARED_LIBRARY)
+
+#include $(CLEAR_VARS)
+#LOCAL_MODULE := FreeImage
+#LOCAL_EXPORT_C_INCLUDES := $(FREEIMAGE_INC)
+#LOCAL_SRC_FILES := $(FREEIMAGE_LIBS)/$(TARGET_ARCH_ABI)/libfreeimage.so
+#include $(PREBUILT_SHARED_LIBRARY)
+
+# OCCT core
+include $(CLEAR_VARS)
+LOCAL_MODULE := OcctTKernel
+LOCAL_EXPORT_C_INCLUDES := $(OCCT_INC)
+LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKernel.so
+include $(PREBUILT_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := OcctTKMath
+LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKMath.so
+include $(PREBUILT_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := OcctTKG2d
+LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKG2d.so
+include $(PREBUILT_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := OcctTKG3d
+LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKG3d.so
+include $(PREBUILT_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := OcctTKGeomBase
+LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKGeomBase.so
+include $(PREBUILT_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := OcctTKBRep
+LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKBRep.so
+include $(PREBUILT_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := OcctTKGeomAlgo
+LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKGeomAlgo.so
+include $(PREBUILT_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := OcctTKTopAlgo
+LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKTopAlgo.so
+include $(PREBUILT_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := OcctTKShHealing
+LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKShHealing.so
+include $(PREBUILT_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := OcctTKMesh
+LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKMesh.so
+include $(PREBUILT_SHARED_LIBRARY)
+
+# OCCT Exchange
+include $(CLEAR_VARS)
+LOCAL_MODULE := OcctTKPrim
+LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKPrim.so
+include $(PREBUILT_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := OcctTKBO
+LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKBO.so
+include $(PREBUILT_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := OcctTKBool
+LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKBool.so
+include $(PREBUILT_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := OcctTKFillet
+LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKFillet.so
+include $(PREBUILT_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := OcctTKOffset
+LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKOffset.so
+include $(PREBUILT_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := OcctTKXSBase
+LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKXSBase.so
+include $(PREBUILT_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := OcctTKIGES
+LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKIGES.so
+include $(PREBUILT_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := OcctTKSTEPBase
+LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKSTEPBase.so
+include $(PREBUILT_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := OcctTKSTEPAttr
+LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKSTEPAttr.so
+include $(PREBUILT_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := OcctTKSTEP209
+LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKSTEP209.so
+include $(PREBUILT_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := OcctTKSTEP
+LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKSTEP.so
+include $(PREBUILT_SHARED_LIBRARY)
+
+# OCCT visualization
+include $(CLEAR_VARS)
+LOCAL_MODULE := OcctTKService
+LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKService.so
+include $(PREBUILT_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := OcctTKHLR
+LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKHLR.so
+include $(PREBUILT_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := OcctTKV3d
+LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKV3d.so
+include $(PREBUILT_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := OcctTKOpenGl
+LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKOpenGl.so
+include $(PREBUILT_SHARED_LIBRARY)
+
+# our sample
+include $(CLEAR_VARS)
+LOCAL_MODULE           := libTKJniSample
+LOCAL_C_INCLUDES       := $(STL_INC)
+#LOCAL_STATIC_LIBRARIES := $(STL_LIB) does not work
+LOCAL_CFLAGS           := -Wall
+LOCAL_CPP_EXTENSION    := .cxx .cpp
+LOCAL_CPP_FEATURES     := rtti exceptions
+LOCAL_SRC_FILES        := OcctJni_Viewer.cxx OcctJni_Window.cxx OcctJni_MsgPrinter.cxx
+LOCAL_SHARED_LIBRARIES := OcctTKernel OcctTKMath OcctTKG2d OcctTKG3d OcctTKGeomBase OcctTKBRep OcctTKGeomAlgo OcctTKTopAlgo OcctTKShHealing OcctTKMesh OcctTKPrim
+LOCAL_SHARED_LIBRARIES += OcctTKIGES OcctTKSTEP OcctTKXSBase
+LOCAL_SHARED_LIBRARIES += OcctTKService OcctTKHLR OcctTKV3d OcctTKOpenGl
+LOCAL_SHARED_LIBRARIES += SharedStl
+LOCAL_LDLIBS           := -llog -lGLESv2 -lEGL
+
+#LOCAL_LDLIBS += $(STL_LIB)
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/samples/java/jniviewer/jni/Application.mk b/samples/java/jniviewer/jni/Application.mk
new file mode 100644 (file)
index 0000000..1d6b633
--- /dev/null
@@ -0,0 +1,8 @@
+NDK_TOOLCHAIN_VERSION := 4.8
+APP_PLATFORM := android-15
+
+APP_ABI := armeabi-v7a
+#APP_ABI := all
+
+#APP_STL := gnustl_static
+#APP_STL := stlport_static
diff --git a/samples/java/jniviewer/jni/OcctJni_MsgPrinter.cxx b/samples/java/jniviewer/jni/OcctJni_MsgPrinter.cxx
new file mode 100644 (file)
index 0000000..8eef25f
--- /dev/null
@@ -0,0 +1,102 @@
+// Copyright (c) 2014 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include <OcctJni_MsgPrinter.hxx>
+
+#include <TCollection_AsciiString.hxx>
+#include <TCollection_ExtendedString.hxx>
+
+#include <android/log.h>
+
+IMPLEMENT_STANDARD_HANDLE (OcctJni_MsgPrinter, Message_Printer)
+IMPLEMENT_STANDARD_RTTIEXT(OcctJni_MsgPrinter, Message_Printer)
+
+// =======================================================================
+// function : OcctJni_MsgPrinter
+// purpose  :
+// =======================================================================
+OcctJni_MsgPrinter::OcctJni_MsgPrinter (JNIEnv* theJEnv,
+                                        jobject theJObj)
+: myJEnv (theJEnv),
+  myJObj (theJEnv->NewGlobalRef (theJObj)),
+  myJMet (NULL)
+{
+  jclass aJClass = theJEnv->GetObjectClass (theJObj);
+  myJMet = theJEnv->GetMethodID (aJClass, "postMessage", "(Ljava/lang/String;)V");
+  if (myJMet == NULL)
+  {
+    __android_log_write (ANDROID_LOG_FATAL, "jniSample", "Broken initialization of OcctJni_MsgPrinter!");
+  }
+}
+
+// =======================================================================
+// function : ~OcctJni_MsgPrinter
+// purpose  :
+// =======================================================================
+OcctJni_MsgPrinter::~OcctJni_MsgPrinter()
+{
+  //myJEnv->DeleteGlobalRef (myJObj);
+}
+
+// =======================================================================
+// function : Send
+// purpose  :
+// =======================================================================
+void OcctJni_MsgPrinter::Send (const TCollection_ExtendedString& theString,
+                               const Message_Gravity             theGravity,
+                               const Standard_Boolean            theToPutEndl) const
+{
+  if (theGravity >= myTraceLevel)
+  {
+    const TCollection_AsciiString aStr (theString);
+    OcctJni_MsgPrinter::Send (aStr, theGravity, theToPutEndl);
+  }
+}
+
+// =======================================================================
+// function : Send
+// purpose  :
+// =======================================================================
+void OcctJni_MsgPrinter::Send (const TCollection_AsciiString& theString,
+                               const Message_Gravity          theGravity,
+                               const Standard_Boolean         theToPutEndl) const
+{
+  if (theGravity < myTraceLevel)
+  {
+    return;
+  }
+
+  ///__android_log_write (ANDROID_LOG_DEBUG, "OcctJni_MsgPrinter", (TCollection_AsciiString(" @@ ") + theString).ToCString());
+  if (myJMet == NULL)
+  {
+    return;
+  }
+
+  jstring aJStr = myJEnv->NewStringUTF ((theString + "\n").ToCString());
+  myJEnv->CallObjectMethod (myJObj, myJMet, aJStr);
+  myJEnv->DeleteLocalRef (aJStr);
+}
+
+// =======================================================================
+// function : Send
+// purpose  :
+// =======================================================================
+void OcctJni_MsgPrinter::Send (const Standard_CString& theString,
+                               const Message_Gravity   theGravity,
+                               const Standard_Boolean  theToPutEndl) const
+{
+  if (theGravity >= myTraceLevel)
+  {
+    OcctJni_MsgPrinter::Send (TCollection_AsciiString (theString), theGravity, theToPutEndl);
+  }
+}
diff --git a/samples/java/jniviewer/jni/OcctJni_MsgPrinter.hxx b/samples/java/jniviewer/jni/OcctJni_MsgPrinter.hxx
new file mode 100644 (file)
index 0000000..0735033
--- /dev/null
@@ -0,0 +1,62 @@
+// Copyright (c) 2014 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef OcctJni_MsgPrinter_H
+#define OcctJni_MsgPrinter_H
+
+#include <Message_Printer.hxx>
+
+#include <jni.h>
+
+// Class providing connection between messenger interfaces in C++ and Java layers.
+class OcctJni_MsgPrinter : public Message_Printer
+{
+public:
+
+  //! Default constructor
+  OcctJni_MsgPrinter (JNIEnv* theJEnv,
+                      jobject theJObj);
+
+  //! Destructor.
+  ~OcctJni_MsgPrinter();
+
+  //! Redirection to TCollection_AsciiString method
+  virtual void Send (const TCollection_ExtendedString& theString,
+                     const Message_Gravity             theGravity,
+                     const Standard_Boolean            theToPutEndl) const;
+
+  //! Redirection to TCollection_AsciiString method
+  virtual void Send (const Standard_CString&           theString,
+                     const Message_Gravity             theGravity,
+                     const Standard_Boolean            theToPutEndl) const;
+
+  //! Main printing method
+  virtual void Send (const TCollection_AsciiString&    theString,
+                     const Message_Gravity             theGravity,
+                     const Standard_Boolean            theToPutEndl) const;
+
+private:
+
+  JNIEnv*   myJEnv;
+  jobject   myJObj;
+  jmethodID myJMet;
+
+public:
+
+  DEFINE_STANDARD_RTTI(OcctJni_MsgPrinter)
+
+};
+
+DEFINE_STANDARD_HANDLE(OcctJni_MsgPrinter, Message_Printer)
+
+#endif // OcctJni_MsgPrinter_H
diff --git a/samples/java/jniviewer/jni/OcctJni_Viewer.cxx b/samples/java/jniviewer/jni/OcctJni_Viewer.cxx
new file mode 100644 (file)
index 0000000..750f030
--- /dev/null
@@ -0,0 +1,810 @@
+// Copyright (c) 2014 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include <OcctJni_Viewer.hxx>
+#include <OcctJni_MsgPrinter.hxx>
+
+#include <AIS_Shape.hxx>
+#include <Image_AlienPixMap.hxx>
+#include <BRepTools.hxx>
+#include <Message_Messenger.hxx>
+#include <Message_MsgFile.hxx>
+#include <OpenGl_GraphicDriver.hxx>
+#include <OSD_Environment.hxx>
+#include <OSD_Timer.hxx>
+#include <Standard_Version.hxx>
+
+#include <BRepPrimAPI_MakeBox.hxx>
+
+#include <STEPControl_Reader.hxx>
+#include <IGESControl_Reader.hxx>
+#include <XSControl_WorkSession.hxx>
+
+#include <EGL/egl.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <jni.h>
+
+//! @return true if file exists
+static bool isFileExist (const TCollection_AsciiString& thePath)
+{
+  struct stat64 aStatBuffer;
+  return stat64 (thePath.ToCString(), &aStatBuffer) == 0;
+}
+
+//! Cut-off the last split character from the path and everything after it.
+static TCollection_AsciiString getParentDir (const TCollection_AsciiString& thePath)
+{
+  TCollection_AsciiString aPath = thePath;
+  char* aSplitter = (char* )aPath.ToCString();
+  for (char* anIter = aSplitter; *anIter != '\0'; ++anIter)
+  {
+    if (*anIter == '\\'
+     || *anIter == '/')
+    {
+      aSplitter = anIter;
+    }
+  }
+  *aSplitter = '\0'; // cut off file name or trailing folder
+  return TCollection_AsciiString (aPath.ToCString());
+}
+
+//! Set environment variable theVarName indicating location of resource
+//! file theFile so as to correspond to actual location of this file.
+//!
+//! The resource file is searched in directory where Test.Draw.dll is located,
+//! and if not found - also in subdirectory ../res from there.
+//! If file is found, environment variable is set for C subsystem.
+//! Otherwise, environment is not changed.
+//!
+//! If theToAddFileName is true, complete file name is set as value of the variable,
+//! if theToAddFileName is false, only path is set.
+Standard_Boolean setResourceEnv (const TCollection_AsciiString& theVarName,
+                                 const TCollection_AsciiString& theRoot,
+                                 const TCollection_AsciiString& theFile,
+                                 const Standard_Boolean         theToAddFileName)
+{
+  // use location of current assembly to figure out possible location of resource
+  TCollection_AsciiString aBaseDir = theRoot;
+
+  // check the same directory where binary is located
+  if (!isFileExist (aBaseDir + "/" + theFile))
+  {
+    // check subdirectory ../res
+    aBaseDir = getParentDir (aBaseDir) + "/res";
+    if (!isFileExist (aBaseDir + "/" + theFile))
+    {
+      return Standard_False;
+    }
+  }
+
+  // set C library environment
+  if (theToAddFileName)
+  {
+    aBaseDir = aBaseDir + "/" + theFile;
+  }
+
+  OSD_Environment anEnv (theVarName, aBaseDir);
+  anEnv.Build();
+  return Standard_True;
+}
+
+// =======================================================================
+// function : OcctJni_Viewer
+// purpose  :
+// =======================================================================
+OcctJni_Viewer::OcctJni_Viewer()
+{
+  // prepare necessary environment
+  TCollection_AsciiString aResRoot = "/data/data/com.opencascade.jnisample/files";
+
+  setResourceEnv ("CSF_TObjMessage",      aResRoot + "/TObj",      "TObj.msg",          Standard_False);
+  setResourceEnv ("CSF_UnitsLexicon",     aResRoot + "/UnitsAPI",  "Lexi_Expr.dat",     Standard_True);
+  setResourceEnv ("CSF_UnitsDefinition",  aResRoot + "/UnitsAPI",  "Units.dat",         Standard_True);
+  setResourceEnv ("CSF_ShadersDirectory", aResRoot + "/Shaders",   "Declarations.glsl", Standard_False);
+  setResourceEnv ("CSF_XSMessage",        aResRoot + "/XSMessage", "XSTEP.us",          Standard_False);
+  setResourceEnv ("CSF_SHMessage",        aResRoot + "/XSMessage", "SHAPE.us",          Standard_False);
+  //setResourceEnv ("CSF_PluginDefaults",   "Plugin",            Standard_False);
+
+  // make sure OCCT loads the dictionary
+  //UnitsAPI::SetLocalSystem (UnitsAPI_SI);
+
+  // load messages for TObj
+  Message_MsgFile::LoadFromEnv ("CSF_TObjMessage", "TObj", "msg");
+}
+
+// =======================================================================
+// function : init
+// purpose  :
+// =======================================================================
+bool OcctJni_Viewer::init()
+{
+  EGLint aCfgId = 0;
+  int aWidth = 0, aHeight = 0;
+  EGLDisplay anEglDisplay = eglGetCurrentDisplay();
+  EGLContext anEglContext = eglGetCurrentContext();
+  EGLSurface anEglSurf    = eglGetCurrentSurface (EGL_DRAW);
+  if (anEglDisplay == EGL_NO_DISPLAY
+   || anEglContext == EGL_NO_CONTEXT
+   || anEglSurf    == EGL_NO_SURFACE)
+  {
+    Message::DefaultMessenger()->Send ("Error: No active EGL context!", Message_Fail);
+    release();
+    return false;
+  }
+
+  eglQuerySurface (anEglDisplay, anEglSurf, EGL_WIDTH,     &aWidth);
+  eglQuerySurface (anEglDisplay, anEglSurf, EGL_HEIGHT,    &aHeight);
+  eglQuerySurface (anEglDisplay, anEglSurf, EGL_CONFIG_ID, &aCfgId);
+  const EGLint aConfigAttribs[] = { EGL_CONFIG_ID, aCfgId, EGL_NONE };
+  EGLint       aNbConfigs = 0;
+  void*        anEglConfig = NULL;
+  if (eglChooseConfig (anEglDisplay, aConfigAttribs, &anEglConfig, 1, &aNbConfigs) != EGL_TRUE)
+  {
+    Message::DefaultMessenger()->Send ("Error: EGL does not provide compatible configurations!", Message_Fail);
+    release();
+    return false;
+  }
+
+  TCollection_AsciiString anEglInfo = TCollection_AsciiString()
+      + "\n  EGLVersion:     " + eglQueryString (anEglDisplay, EGL_VERSION)
+      + "\n  EGLVendor:      " + eglQueryString (anEglDisplay, EGL_VENDOR)
+      + "\n  EGLClient APIs: " + eglQueryString (anEglDisplay, EGL_CLIENT_APIS)
+      + "\n  GLvendor:       " + (const char* )glGetString (GL_VENDOR)
+      + "\n  GLdevice:       " + (const char* )glGetString (GL_RENDERER)
+      + "\n  GLversion:      " + (const char* )glGetString (GL_VERSION) + " [GLSL: " + (const char* )glGetString (GL_SHADING_LANGUAGE_VERSION) + "]";
+    ::Message::DefaultMessenger()->Send (anEglInfo, Message_Info);
+
+  if (!myViewer.IsNull())
+  {
+    Handle(OpenGl_GraphicDriver) aDriver = Handle(OpenGl_GraphicDriver)::DownCast (myViewer->Driver());
+    Handle(OcctJni_Window)       aWindow = Handle(OcctJni_Window)::DownCast (myView->Window());
+    if (!aDriver->InitEglContext (anEglDisplay, anEglContext, anEglConfig))
+    {
+      Message::DefaultMessenger()->Send ("Error: OpenGl_GraphicDriver can not be initialized!", Message_Fail);
+      release();
+      return false;
+    }
+
+    aWindow->SetSize (aWidth, aHeight);
+    myView->SetWindow (aWindow, (Aspect_RenderingContext )anEglContext, NULL, NULL);
+    return true;
+  }
+
+  Handle(OpenGl_GraphicDriver) aDriver = new OpenGl_GraphicDriver (NULL, Standard_False);
+  aDriver->ChangeOptions().buffersNoSwap = Standard_True;
+//aDriver->ChangeOptions().glslWarnings  = Standard_True; /// for debug only!
+  if (!aDriver->InitEglContext (anEglDisplay, anEglContext, anEglConfig))
+  {
+    Message::DefaultMessenger()->Send ("Error: OpenGl_GraphicDriver can not be initialized!", Message_Fail);
+    release();
+    return false;
+  }
+
+  // create viewer
+  myViewer = new V3d_Viewer (aDriver, TCollection_ExtendedString("Viewer").ToExtString(), "", 1000.0,
+                             V3d_XposYnegZpos, Quantity_NOC_BLACK, V3d_ZBUFFER, V3d_GOURAUD, V3d_WAIT,
+                             Standard_True, Standard_False);
+  myViewer->SetDefaultLights();
+  myViewer->SetLightOn();
+
+  // create AIS context
+  myContext = new AIS_InteractiveContext (myViewer);
+  //myContext->SetDisplayMode (AIS_WireFrame);
+  myContext->SetDisplayMode (AIS_Shaded);
+
+  Handle(OcctJni_Window) aWindow = new OcctJni_Window (aWidth, aHeight);
+  myView = myViewer->CreateView();
+
+  myView->SetWindow (aWindow, (Aspect_RenderingContext )anEglContext, NULL, NULL);
+  myView->TriedronDisplay (Aspect_TOTP_RIGHT_LOWER, Quantity_NOC_WHITE, 0.08, V3d_ZBUFFER);
+
+  initContent();
+  return true;
+}
+
+// =======================================================================
+// function : release
+// purpose  :
+// =======================================================================
+void OcctJni_Viewer::release()
+{
+  myContext.Nullify();
+  myView.Nullify();
+  myViewer.Nullify();
+}
+
+// =======================================================================
+// function : resize
+// purpose  :
+// =======================================================================
+void OcctJni_Viewer::resize (int theWidth,
+                             int theHeight)
+{
+  if (myContext.IsNull())
+  {
+    Message::DefaultMessenger()->Send ("Resize failed - view is unavailable", Message_Fail);
+    return;
+  }
+
+  Handle(OpenGl_GraphicDriver) aDriver = Handle(OpenGl_GraphicDriver)::DownCast (myViewer->Driver());
+  Handle(OcctJni_Window)       aWindow = Handle(OcctJni_Window)::DownCast (myView->Window());
+  aWindow->SetSize (theWidth, theHeight);
+  //myView->MustBeResized(); // can be used instead of SetWindow() when EGLsurface has not been changed
+
+  EGLContext anEglContext = eglGetCurrentContext();
+  myView->SetWindow (aWindow, (Aspect_RenderingContext )anEglContext, NULL, NULL);
+  //saveSnapshot ("/sdcard/Download/tt.png", theWidth, theHeight);
+}
+
+// =======================================================================
+// function : initContent
+// purpose  :
+// =======================================================================
+void OcctJni_Viewer::initContent()
+{
+  myContext->RemoveAll (Standard_False);
+
+  OSD_Timer aTimer;
+  aTimer.Start();
+  if (!myShape.IsNull())
+  {
+    Handle(AIS_Shape) aShapePrs = new AIS_Shape (myShape);
+    myContext->Display (aShapePrs, Standard_False);
+  }
+  else
+  {
+    BRepPrimAPI_MakeBox aBuilder (1.0, 2.0, 3.0);
+    Handle(AIS_Shape) aShapePrs = new AIS_Shape (aBuilder.Shape());
+    myContext->Display (aShapePrs, Standard_False);
+  }
+  myView->FitAll();
+
+  aTimer.Stop();
+  Message::DefaultMessenger()->Send (TCollection_AsciiString() + "Presentation computed in " + aTimer.ElapsedTime() + " seconds", Message_Info);
+}
+
+//! Load shape from IGES file
+static TopoDS_Shape loadIGES (const TCollection_AsciiString& thePath)
+{
+  TopoDS_Shape          aShape;
+  IGESControl_Reader    aReader;
+  IFSelect_ReturnStatus aReadStatus = IFSelect_RetFail;
+  try
+  {
+    aReadStatus = aReader.ReadFile (thePath.ToCString());
+  }
+  catch (Standard_Failure)
+  {
+    Message::DefaultMessenger()->Send ("Error: IGES reader, computation error", Message_Fail);
+    return aShape;
+  }
+
+  if (aReadStatus != IFSelect_RetDone)
+  {
+    Message::DefaultMessenger()->Send ("Error: IGES reader, bad file format", Message_Fail);
+    return aShape;
+  }
+
+  // now perform the translation
+  aReader.TransferRoots();
+  if (aReader.NbShapes() <= 0)
+  {
+    Handle(XSControl_WorkSession) aWorkSession = new XSControl_WorkSession();
+    aWorkSession->SelectNorm ("IGES");
+    aReader.SetWS (aWorkSession, Standard_True);
+    aReader.SetReadVisible (Standard_False);
+    aReader.TransferRoots();
+  }
+  if (aReader.NbShapes() <= 0)
+  {
+    Message::DefaultMessenger()->Send ("Error: IGES reader, no shapes has been found", Message_Fail);
+    return aShape;
+  }
+  return aReader.OneShape();
+  /*TopoDS_Shape anImportedShape = aReader.OneShape();
+
+  // apply sewing on the imported shape
+  BRepBuilderAPI_Sewing aTool (0.0);
+  aTool.SetNonManifoldMode  (Standard_False);
+  aTool.SetFloatingEdgesMode(Standard_True);
+  aTool.Load (anImportedShape);
+  aTool.Perform();
+  TopoDS_Shape aSewedShape = aTool.SewedShape();
+
+  if (aSewedShape.IsNull())
+  {
+    Message::DefaultMessenger()->Send ("Error: Sewing result is empty", Message_Fail);
+    return aShape;
+  }
+  if (aSewedShape.IsSame(anImportedShape))
+  {
+    aShape = anImportedShape;
+  }
+  else
+  {
+    // apply shape healing
+    ShapeFix_Shape aShapeFixer(aSewedShape);
+    aShapeFixer.FixSolidMode() = 1;
+    aShapeFixer.FixFreeShellMode() = 1;
+    aShapeFixer.FixFreeFaceMode() = 1;
+    aShapeFixer.FixFreeWireMode() = 0;
+    aShapeFixer.FixSameParameterMode() = 0;
+    aShapeFixer.FixVertexPositionMode() = 0;
+    aShape = aShapeFixer.Perform() ? aShapeFixer.Shape() : aSewedShape;
+  }
+  return aShape;*/
+}
+
+//! Load shape from STEP file
+static TopoDS_Shape loadSTEP (const TCollection_AsciiString& thePath)
+{
+  STEPControl_Reader    aReader;
+  IFSelect_ReturnStatus aReadStatus = IFSelect_RetFail;
+  try
+  {
+    aReadStatus = aReader.ReadFile (thePath.ToCString());
+  }
+  catch (Standard_Failure)
+  {
+    Message::DefaultMessenger()->Send ("Error: STEP reader, computation error", Message_Fail);
+    return TopoDS_Shape();
+  }
+
+  if (aReadStatus != IFSelect_RetDone)
+  {
+    Message::DefaultMessenger()->Send ("Error: STEP reader, bad file format", Message_Fail);
+    return TopoDS_Shape();
+  }
+  else if (aReader.NbRootsForTransfer() <= 0)
+  {
+    Message::DefaultMessenger()->Send ("Error: STEP reader, shape is empty", Message_Fail);
+    return TopoDS_Shape();
+  }
+
+  // now perform the translation
+  aReader.TransferRoots();
+  return aReader.OneShape();
+}
+
+// =======================================================================
+// function : open
+// purpose  :
+// =======================================================================
+bool OcctJni_Viewer::open (const TCollection_AsciiString& thePath)
+{
+  myShape.Nullify();
+  if (!myContext.IsNull())
+  {
+    myContext->RemoveAll (Standard_False);
+  }
+  if (thePath.IsEmpty())
+  {
+    return false;
+  }
+
+  OSD_Timer aTimer;
+  aTimer.Start();
+  TCollection_AsciiString aFormatStr;
+  const Standard_Integer  aLen = thePath.Length();
+  if (aLen >= 5
+  && thePath.Value (aLen - 4) == '.')
+  {
+    aFormatStr = thePath.SubString (aLen - 3, aLen);
+  }
+  else if (aLen >= 4
+   && thePath.Value (aLen - 3) == '.')
+  {
+    aFormatStr = thePath.SubString (aLen - 2, aLen);
+  }
+  else if (aLen >= 3
+        && thePath.Value (aLen - 2) == '.')
+  {
+    aFormatStr = thePath.SubString (aLen - 1, aLen);
+  }
+  aFormatStr.LowerCase();
+
+  TopoDS_Shape aShape;
+  if (aFormatStr == "stp"
+   || aFormatStr == "step")
+  {
+    aShape = loadSTEP (thePath);
+  }
+  else if (aFormatStr == "igs"
+        || aFormatStr == "iges")
+  {
+    aShape = loadIGES (thePath);
+  }
+  else
+      // if (aFormatStr == "brep"
+      //  || aFormatStr == "rle")
+  {
+    BRep_Builder aBuilder;
+    if (!BRepTools::Read (aShape, thePath.ToCString(), aBuilder))
+    {
+      Message::DefaultMessenger()->Send (TCollection_AsciiString() + "Error: file '" + thePath + "' can not be opened!", Message_Info);
+      return false;
+    }
+  }
+  if (aShape.IsNull())
+  {
+    return false;
+  }
+  aTimer.Stop();
+  Message::DefaultMessenger()->Send (TCollection_AsciiString() + "File '" + thePath + "' loaded in " + aTimer.ElapsedTime() + " seconds", Message_Info);
+
+  myShape = aShape;
+  if (myContext.IsNull())
+  {
+    return true;
+  }
+
+  aTimer.Reset();
+  aTimer.Start();
+
+  Handle(AIS_Shape) aShapePrs = new AIS_Shape (aShape);
+  myContext->Display (aShapePrs, Standard_False);
+  myView->FitAll();
+
+  aTimer.Stop();
+  Message::DefaultMessenger()->Send (TCollection_AsciiString() + "Presentation computed in " + aTimer.ElapsedTime() + " seconds", Message_Info);
+  return true;
+}
+
+// =======================================================================
+// function : saveSnapshot
+// purpose  :
+// =======================================================================
+bool OcctJni_Viewer::saveSnapshot (const TCollection_AsciiString& thePath,
+                                   int theWidth,
+                                   int theHeight)
+{
+  if (myContext.IsNull()
+   || thePath.IsEmpty())
+  {
+    Message::DefaultMessenger()->Send ("Image dump failed - view is unavailable", Message_Fail);
+    return false;
+  }
+
+  if (theWidth  < 1
+   || theHeight < 1)
+  {
+    myView->Window()->Size (theWidth, theHeight);
+  }
+  if (theWidth  < 1
+   || theHeight < 1)
+  {
+    Message::DefaultMessenger()->Send ("Image dump failed - view is unavailable", Message_Fail);
+    return false;
+  }
+
+  Image_AlienPixMap anAlienImage;
+  if (!anAlienImage.InitTrash (Image_PixMap::ImgBGRA, theWidth, theHeight))
+  {
+    Message::DefaultMessenger()->Send (TCollection_AsciiString() + "RGBA image " + theWidth + "x" + theHeight + " allocation failed", Message_Fail);
+    return false;
+  }
+
+  // OpenGL ES does not support fetching data in BGRA format
+  // while FreeImage does not support RGBA format.
+  Image_PixMap anImage;
+  anImage.InitWrapper (Image_PixMap::ImgRGBA,
+                       anAlienImage.ChangeData(),
+                       anAlienImage.SizeX(),
+                       anAlienImage.SizeY(),
+                       anAlienImage.SizeRowBytes());
+  if (!myView->ToPixMap (anImage, theWidth, theHeight, Graphic3d_BT_RGBA))
+  {
+    Message::DefaultMessenger()->Send (TCollection_AsciiString() + "View dump to the image " + theWidth + "x" + theHeight + " failed", Message_Fail);
+  }
+
+  for (Standard_Size aRow = 0; aRow < anAlienImage.SizeY(); ++aRow)
+  {
+    for (Standard_Size aCol = 0; aCol < anAlienImage.SizeX(); ++aCol)
+    {
+      Image_ColorRGBA& aPixel = anAlienImage.ChangeValue<Image_ColorRGBA> (aRow, aCol);
+      std::swap (aPixel.r(), aPixel.b());
+      //aPixel.a() = 1.0;
+    }
+  }
+
+  if (!anAlienImage.Save (thePath))
+  {
+    Message::DefaultMessenger()->Send (TCollection_AsciiString() + "Image saving to path '" + thePath + "' failed", Message_Fail);
+    return false;
+  }
+  Message::DefaultMessenger()->Send (TCollection_AsciiString() + "View " + theWidth + "x" + theHeight + " dumped to image '" + thePath + "'", Message_Info);
+  return true;
+}
+
+// =======================================================================
+// function : redraw
+// purpose  :
+// =======================================================================
+void OcctJni_Viewer::redraw()
+{
+  if (myView.IsNull())
+  {
+    return;
+  }
+
+  myView->Redraw();
+}
+
+// =======================================================================
+// function : fitAll
+// purpose  :
+// =======================================================================
+void OcctJni_Viewer::fitAll()
+{
+  if (myView.IsNull())
+  {
+    return;
+  }
+
+  myView->FitAll();
+}
+
+// =======================================================================
+// function : startRotation
+// purpose  :
+// =======================================================================
+void OcctJni_Viewer::startRotation (int theStartX,
+                                    int theStartY)
+{
+  if (myView.IsNull())
+  {
+    return;
+  }
+
+  myView->StartRotation (theStartX, theStartY, 0.45);
+}
+
+// =======================================================================
+// function : onRotation
+// purpose  :
+// =======================================================================
+void OcctJni_Viewer::onRotation (int theX,
+                                 int theY)
+{
+  if (myView.IsNull())
+  {
+    return;
+  }
+
+  myView->Rotation (theX, theY);
+}
+
+// =======================================================================
+// function : onPanning
+// purpose  :
+// =======================================================================
+void OcctJni_Viewer::onPanning (int theDX,
+                                int theDY)
+{
+  if (myView.IsNull())
+  {
+    return;
+  }
+
+  myView->Pan (theDX, theDY);
+}
+
+// =======================================================================
+// function : onClick
+// purpose  :
+// =======================================================================
+void OcctJni_Viewer::onClick (int theX,
+                              int theY)
+{
+  if (myView.IsNull())
+  {
+    return;
+  }
+
+  myContext->MoveTo (theX, theY, myView, Standard_False);
+  myContext->Select (Standard_True);
+}
+
+// =======================================================================
+// function : stopAction
+// purpose  :
+// =======================================================================
+void OcctJni_Viewer::stopAction()
+{
+  if (myView.IsNull())
+  {
+    return;
+  }
+}
+
+#define jexp extern "C" JNIEXPORT
+
+jexp jlong JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppCreate (JNIEnv* theEnv,
+                                                                             jobject theObj)
+{
+  return jlong(new OcctJni_Viewer());
+}
+
+jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppDestroy (JNIEnv* theEnv,
+                                                                             jobject theObj,
+                                                                             jlong   theCppPtr)
+{
+  delete (OcctJni_Viewer* )theCppPtr;
+
+  Handle(Message_Messenger) aMsgMgr = Message::DefaultMessenger();
+  aMsgMgr->RemovePrinters (STANDARD_TYPE (OcctJni_MsgPrinter));
+}
+
+jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppRelease (JNIEnv* theEnv,
+                                                                             jobject theObj,
+                                                                             jlong   theCppPtr)
+{
+  ((OcctJni_Viewer* )theCppPtr)->release();
+}
+
+jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppInit (JNIEnv* theEnv,
+                                                                          jobject theObj,
+                                                                          jlong   theCppPtr)
+{
+  Handle(Message_Messenger) aMsgMgr = Message::DefaultMessenger();
+  aMsgMgr->RemovePrinters (STANDARD_TYPE (OcctJni_MsgPrinter));
+  aMsgMgr->AddPrinter (new OcctJni_MsgPrinter (theEnv, theObj));
+  ((OcctJni_Viewer* )theCppPtr)->init();
+}
+
+jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppResize (JNIEnv* theEnv,
+                                                                            jobject theObj,
+                                                                            jlong   theCppPtr,
+                                                                            jint    theWidth,
+                                                                            jint    theHeight)
+{
+  ((OcctJni_Viewer* )theCppPtr)->resize (theWidth, theHeight);
+}
+
+jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppOpen (JNIEnv* theEnv,
+                                                                          jobject theObj,
+                                                                          jlong   theCppPtr,
+                                                                          jstring thePath)
+{
+  const char* aPathPtr = theEnv->GetStringUTFChars (thePath, 0);
+  const TCollection_AsciiString aPath (aPathPtr);
+  theEnv->ReleaseStringUTFChars (thePath, aPathPtr);
+  ((OcctJni_Viewer* )theCppPtr)->open (aPath);
+}
+
+jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppRedraw (JNIEnv* theEnv,
+                                                                            jobject theObj,
+                                                                            jlong   theCppPtr)
+{
+  ((OcctJni_Viewer* )theCppPtr)->redraw();
+}
+
+jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppSetAxoProj (JNIEnv* theEnv,
+                                                                                jobject theObj,
+                                                                                jlong   theCppPtr)
+{
+  ((OcctJni_Viewer* )theCppPtr)->setProj (V3d_XposYnegZpos);
+}
+
+jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppSetXposProj (JNIEnv* theEnv,
+                                                                                 jobject theObj,
+                                                                                 jlong   theCppPtr)
+{
+  ((OcctJni_Viewer* )theCppPtr)->setProj (V3d_Xpos);
+}
+
+jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppSetYposProj (JNIEnv* theEnv,
+                                                                                 jobject theObj,
+                                                                                 jlong   theCppPtr)
+{
+  ((OcctJni_Viewer* )theCppPtr)->setProj (V3d_Ypos);
+}
+
+jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppSetZposProj (JNIEnv* theEnv,
+                                                                                 jobject theObj,
+                                                                                 jlong   theCppPtr)
+{
+  ((OcctJni_Viewer* )theCppPtr)->setProj (V3d_Zpos);
+}
+
+jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppSetXnegProj (JNIEnv* theEnv,
+                                                                                 jobject theObj,
+                                                                                 jlong   theCppPtr)
+{
+  ((OcctJni_Viewer* )theCppPtr)->setProj (V3d_Xneg);
+}
+
+jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppSetYnegProj (JNIEnv* theEnv,
+                                                                                 jobject theObj,
+                                                                                 jlong   theCppPtr)
+{
+  ((OcctJni_Viewer* )theCppPtr)->setProj (V3d_Yneg);
+}
+
+jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppSetZnegProj (JNIEnv* theEnv,
+                                                                                 jobject theObj,
+                                                                                 jlong   theCppPtr)
+{
+  ((OcctJni_Viewer* )theCppPtr)->setProj (V3d_Zneg);
+}
+
+jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppFitAll (JNIEnv* theEnv,
+                                                                            jobject theObj,
+                                                                            jlong   theCppPtr)
+{
+  ((OcctJni_Viewer* )theCppPtr)->fitAll();
+}
+
+jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppStartRotation (JNIEnv* theEnv,
+                                                                                   jobject theObj,
+                                                                                   jlong   theCppPtr,
+                                                                                   jint    theStartX,
+                                                                                   jint    theStartY)
+{
+  ((OcctJni_Viewer* )theCppPtr)->startRotation (theStartX, theStartY);
+}
+
+jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppOnRotation (JNIEnv* theEnv,
+                                                                                jobject theObj,
+                                                                                jlong   theCppPtr,
+                                                                                jint    theX,
+                                                                                jint    theY)
+{
+  ((OcctJni_Viewer* )theCppPtr)->onRotation (theX, theY);
+}
+
+jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppOnPanning (JNIEnv* theEnv,
+                                                                               jobject theObj,
+                                                                               jlong   theCppPtr,
+                                                                               jint    theDX,
+                                                                               jint    theDY)
+{
+  ((OcctJni_Viewer* )theCppPtr)->onPanning (theDX, theDY);
+}
+
+jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppOnClick (JNIEnv* theEnv,
+                                                                             jobject theObj,
+                                                                             jlong   theCppPtr,
+                                                                             jint    theX,
+                                                                             jint    theY)
+{
+  ((OcctJni_Viewer* )theCppPtr)->onClick (theX, theY);
+}
+
+jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppStopAction (JNIEnv* theEnv,
+                                                                                jobject theObj,
+                                                                                jlong   theCppPtr)
+{
+  ((OcctJni_Viewer* )theCppPtr)->stopAction();
+}
+
+jexp jlong JNICALL Java_com_opencascade_jnisample_OcctJniActivity_cppOcctMajorVersion (JNIEnv* theEnv,
+                                                                                       jobject theObj)
+{
+  return OCC_VERSION_MAJOR;
+}
+
+jexp jlong JNICALL Java_com_opencascade_jnisample_OcctJniActivity_cppOcctMinorVersion (JNIEnv* theEnv,
+                                                                                       jobject theObj)
+{
+  return OCC_VERSION_MINOR;
+}
+
+jexp jlong JNICALL Java_com_opencascade_jnisample_OcctJniActivity_cppOcctMicroVersion (JNIEnv* theEnv,
+                                                                                       jobject theObj)
+{
+  return OCC_VERSION_MAINTENANCE;
+}
diff --git a/samples/java/jniviewer/jni/OcctJni_Viewer.hxx b/samples/java/jniviewer/jni/OcctJni_Viewer.hxx
new file mode 100644 (file)
index 0000000..6bd0a41
--- /dev/null
@@ -0,0 +1,88 @@
+// Copyright (c) 2014 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include <OcctJni_Window.hxx>
+
+#include <AIS_InteractiveContext.hxx>
+#include <TopoDS_Shape.hxx>
+#include <V3d_Viewer.hxx>
+#include <V3d_View.hxx>
+
+//! Main C++ back-end for activity.
+class OcctJni_Viewer
+{
+
+public:
+
+  //! Empty constructor
+  OcctJni_Viewer();
+
+  //! Initialize the viewer
+  bool init();
+
+  //! Release the viewer
+  void release();
+
+  //! Resize the viewer
+  void resize (int theWidth,
+               int theHeight);
+
+  //! Open CAD file
+  bool open (const TCollection_AsciiString& thePath);
+
+  //! Take snapshot
+  bool saveSnapshot (const TCollection_AsciiString& thePath,
+                     int theWidth  = 0,
+                     int theHeight = 0);
+
+  //! Viewer update.
+  void redraw();
+
+  //! Move camera
+  void setProj (V3d_TypeOfOrientation theProj) { if (!myView.IsNull()) myView->SetProj (theProj); }
+
+  //! Fit All.
+  void fitAll();
+
+  //! Start rotation (remember first point position)
+  void startRotation (int theStartX,
+                      int theStartY);
+
+  //! Perform rotation (relative to first point)
+  void onRotation (int theX,
+                   int theY);
+
+  //! Perform panning
+  void onPanning (int theDX,
+                  int theDY);
+
+  //! Perform selection
+  void onClick (int theX,
+                int theY);
+
+  //! Stop previously started action
+  void stopAction();
+
+protected:
+
+  //! Reset viewer content.
+  void initContent();
+
+protected:
+
+  Handle(V3d_Viewer)             myViewer;
+  Handle(V3d_View)               myView;
+  Handle(AIS_InteractiveContext) myContext;
+  TopoDS_Shape                   myShape;
+
+};
diff --git a/samples/java/jniviewer/jni/OcctJni_Window.cxx b/samples/java/jniviewer/jni/OcctJni_Window.cxx
new file mode 100644 (file)
index 0000000..8f50abd
--- /dev/null
@@ -0,0 +1,17 @@
+// Copyright (c) 2014 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include <OcctJni_Window.hxx>
+
+IMPLEMENT_STANDARD_HANDLE (OcctJni_Window, Aspect_Window)
+IMPLEMENT_STANDARD_RTTIEXT(OcctJni_Window, Aspect_Window)
diff --git a/samples/java/jniviewer/jni/OcctJni_Window.hxx b/samples/java/jniviewer/jni/OcctJni_Window.hxx
new file mode 100644 (file)
index 0000000..d73c4a6
--- /dev/null
@@ -0,0 +1,106 @@
+// Copyright (c) 2014 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef OcctJni_Window_H
+#define OcctJni_Window_H
+
+#include <Aspect_Window.hxx>
+
+//! This class defines dummy window
+class OcctJni_Window : public Aspect_Window
+{
+
+public:
+
+  //! Creates a wrapper over existing Window handle
+  OcctJni_Window (const int theWidth, const int theHeight)
+  : myWidth (theWidth), myHeight(theHeight) {}
+
+  //! Returns native Window handle
+  virtual Aspect_Drawable NativeHandle() const { return 0; }
+
+  //! Returns parent of native Window handle
+  virtual Aspect_Drawable NativeParentHandle() const { return 0; }
+
+  virtual void Destroy() {}
+
+  //! Opens the window <me>
+  virtual void Map() const {}
+
+  //! Closes the window <me>
+  virtual void Unmap() const {}
+
+  //! Applies the resizing to the window <me>
+  virtual Aspect_TypeOfResize DoResize() const { return Aspect_TOR_UNKNOWN; }
+
+  //! Apply the mapping change to the window <me>
+  virtual Standard_Boolean DoMapping() const { return Standard_True; }
+
+  //! Returns True if the window <me> is opened
+  virtual Standard_Boolean IsMapped() const { return Standard_True; }
+
+  //! Returns The Window RATIO equal to the physical WIDTH/HEIGHT dimensions
+  virtual Quantity_Ratio Ratio() const { return 1.0; }
+
+  //! Returns The Window POSITION in PIXEL
+  virtual void Position (Standard_Integer& theX1,
+                         Standard_Integer& theY1,
+                         Standard_Integer& theX2,
+                         Standard_Integer& theY2) const
+  {
+    theX1 = 0;
+    theX2 = myWidth;
+    theY1 = 0;
+    theY2 = myHeight;
+  }
+
+  //! Set The Window POSITION in PIXEL
+  virtual void SetPosition (const Standard_Integer theX1,
+                            const Standard_Integer theY1,
+                            const Standard_Integer theX2,
+                            const Standard_Integer theY2)
+  {
+    myWidth  = theX2 - theX1;
+    myHeight = theY2 - theY1;
+  }
+
+  //! Returns The Window SIZE in PIXEL
+  virtual void Size (Standard_Integer& theWidth,
+                     Standard_Integer& theHeight) const
+  {
+    theWidth  = myWidth;
+    theHeight = myHeight;
+  }
+
+  //! Set The Window SIZE in PIXEL
+  virtual void SetSize (const Standard_Integer theWidth,
+                        const Standard_Integer theHeight)
+  {
+    myWidth  = theWidth;
+    myHeight = theHeight;
+  }
+
+private:
+
+  int myWidth;
+  int myHeight;
+
+public:
+
+  DEFINE_STANDARD_RTTI(OcctJni_Window)
+
+};
+
+DEFINE_STANDARD_HANDLE(OcctJni_Window, Aspect_Window)
+
+#endif // OcctJni_Window_H
diff --git a/samples/java/jniviewer/project.properties b/samples/java/jniviewer/project.properties
new file mode 100644 (file)
index 0000000..0840b4a
--- /dev/null
@@ -0,0 +1,14 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-15
diff --git a/samples/java/jniviewer/res/drawable-hdpi/close_l.png b/samples/java/jniviewer/res/drawable-hdpi/close_l.png
new file mode 100644 (file)
index 0000000..0125c4a
Binary files /dev/null and b/samples/java/jniviewer/res/drawable-hdpi/close_l.png differ
diff --git a/samples/java/jniviewer/res/drawable-hdpi/close_p.png b/samples/java/jniviewer/res/drawable-hdpi/close_p.png
new file mode 100644 (file)
index 0000000..b5ce8bd
Binary files /dev/null and b/samples/java/jniviewer/res/drawable-hdpi/close_p.png differ
diff --git a/samples/java/jniviewer/res/drawable-hdpi/fit.png b/samples/java/jniviewer/res/drawable-hdpi/fit.png
new file mode 100644 (file)
index 0000000..70daee1
Binary files /dev/null and b/samples/java/jniviewer/res/drawable-hdpi/fit.png differ
diff --git a/samples/java/jniviewer/res/drawable-hdpi/ic_launcher.png b/samples/java/jniviewer/res/drawable-hdpi/ic_launcher.png
new file mode 100755 (executable)
index 0000000..d27ba82
Binary files /dev/null and b/samples/java/jniviewer/res/drawable-hdpi/ic_launcher.png differ
diff --git a/samples/java/jniviewer/res/drawable-hdpi/info.png b/samples/java/jniviewer/res/drawable-hdpi/info.png
new file mode 100644 (file)
index 0000000..88d27c8
Binary files /dev/null and b/samples/java/jniviewer/res/drawable-hdpi/info.png differ
diff --git a/samples/java/jniviewer/res/drawable-hdpi/info_image.png b/samples/java/jniviewer/res/drawable-hdpi/info_image.png
new file mode 100644 (file)
index 0000000..bac9bea
Binary files /dev/null and b/samples/java/jniviewer/res/drawable-hdpi/info_image.png differ
diff --git a/samples/java/jniviewer/res/drawable-hdpi/message.png b/samples/java/jniviewer/res/drawable-hdpi/message.png
new file mode 100644 (file)
index 0000000..a3dc8cc
Binary files /dev/null and b/samples/java/jniviewer/res/drawable-hdpi/message.png differ
diff --git a/samples/java/jniviewer/res/drawable-hdpi/open.png b/samples/java/jniviewer/res/drawable-hdpi/open.png
new file mode 100644 (file)
index 0000000..68e5265
Binary files /dev/null and b/samples/java/jniviewer/res/drawable-hdpi/open.png differ
diff --git a/samples/java/jniviewer/res/drawable-hdpi/open_l.png b/samples/java/jniviewer/res/drawable-hdpi/open_l.png
new file mode 100644 (file)
index 0000000..6069ce3
Binary files /dev/null and b/samples/java/jniviewer/res/drawable-hdpi/open_l.png differ
diff --git a/samples/java/jniviewer/res/drawable-hdpi/open_p.png b/samples/java/jniviewer/res/drawable-hdpi/open_p.png
new file mode 100644 (file)
index 0000000..c0898e6
Binary files /dev/null and b/samples/java/jniviewer/res/drawable-hdpi/open_p.png differ
diff --git a/samples/java/jniviewer/res/drawable-hdpi/proj_back.png b/samples/java/jniviewer/res/drawable-hdpi/proj_back.png
new file mode 100755 (executable)
index 0000000..77ab199
Binary files /dev/null and b/samples/java/jniviewer/res/drawable-hdpi/proj_back.png differ
diff --git a/samples/java/jniviewer/res/drawable-hdpi/proj_bottom.png b/samples/java/jniviewer/res/drawable-hdpi/proj_bottom.png
new file mode 100755 (executable)
index 0000000..b0bb012
Binary files /dev/null and b/samples/java/jniviewer/res/drawable-hdpi/proj_bottom.png differ
diff --git a/samples/java/jniviewer/res/drawable-hdpi/proj_front.png b/samples/java/jniviewer/res/drawable-hdpi/proj_front.png
new file mode 100755 (executable)
index 0000000..b93d4d3
Binary files /dev/null and b/samples/java/jniviewer/res/drawable-hdpi/proj_front.png differ
diff --git a/samples/java/jniviewer/res/drawable-hdpi/proj_left.png b/samples/java/jniviewer/res/drawable-hdpi/proj_left.png
new file mode 100755 (executable)
index 0000000..bce95df
Binary files /dev/null and b/samples/java/jniviewer/res/drawable-hdpi/proj_left.png differ
diff --git a/samples/java/jniviewer/res/drawable-hdpi/proj_right.png b/samples/java/jniviewer/res/drawable-hdpi/proj_right.png
new file mode 100755 (executable)
index 0000000..8cdb338
Binary files /dev/null and b/samples/java/jniviewer/res/drawable-hdpi/proj_right.png differ
diff --git a/samples/java/jniviewer/res/drawable-hdpi/proj_top.png b/samples/java/jniviewer/res/drawable-hdpi/proj_top.png
new file mode 100755 (executable)
index 0000000..4ad098f
Binary files /dev/null and b/samples/java/jniviewer/res/drawable-hdpi/proj_top.png differ
diff --git a/samples/java/jniviewer/res/drawable-hdpi/view.png b/samples/java/jniviewer/res/drawable-hdpi/view.png
new file mode 100644 (file)
index 0000000..76ce879
Binary files /dev/null and b/samples/java/jniviewer/res/drawable-hdpi/view.png differ
diff --git a/samples/java/jniviewer/res/drawable-mdpi/ic_launcher.png b/samples/java/jniviewer/res/drawable-mdpi/ic_launcher.png
new file mode 100755 (executable)
index 0000000..4b86dbf
Binary files /dev/null and b/samples/java/jniviewer/res/drawable-mdpi/ic_launcher.png differ
diff --git a/samples/java/jniviewer/res/drawable-xhdpi/ic_launcher.png b/samples/java/jniviewer/res/drawable-xhdpi/ic_launcher.png
new file mode 100755 (executable)
index 0000000..cd79bea
Binary files /dev/null and b/samples/java/jniviewer/res/drawable-xhdpi/ic_launcher.png differ
diff --git a/samples/java/jniviewer/res/drawable-xxhdpi/ic_launcher.png b/samples/java/jniviewer/res/drawable-xxhdpi/ic_launcher.png
new file mode 100755 (executable)
index 0000000..a34301f
Binary files /dev/null and b/samples/java/jniviewer/res/drawable-xxhdpi/ic_launcher.png differ
diff --git a/samples/java/jniviewer/res/layout/activity_main.xml b/samples/java/jniviewer/res/layout/activity_main.xml
new file mode 100644 (file)
index 0000000..e7f3ebf
--- /dev/null
@@ -0,0 +1,166 @@
+<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"\r
+android:id="@+id/panel_main"\r
+android:layout_width="fill_parent"\r
+android:layout_height="fill_parent"\r
+android:stretchColumns="1">\r
+\r
+    <LinearLayout android:layout_height="fill_parent"\r
+    android:layout_width="fill_parent"\r
+    android:id="@+id/linearLayout2">\r
+\r
+        <FrameLayout\r
+        android:id="@+id/submenu_group"\r
+        android:layout_width="fill_parent"\r
+        android:layout_height="fill_parent" >\r
+\r
+            <com.opencascade.jnisample.OcctJniView\r
+            android:id="@+id/custom_view"\r
+            android:layout_width="fill_parent"\r
+            android:layout_height="fill_parent"\r
+            android:layout_gravity="bottom|end" >\r
+            </com.opencascade.jnisample.OcctJniView>\r
+\r
+            <ImageButton\r
+            android:id="@+id/scroll_btn"\r
+            style="?android:borderlessButtonStyle"\r
+            android:layout_width="match_parent"\r
+            android:layout_height="wrap_content"\r
+            android:background="@null"\r
+            android:src="@drawable/close_p" />\r
+\r
+            <LinearLayout\r
+            android:id="@+id/panel_menu"\r
+            android:layout_width="fill_parent"\r
+            android:layout_height="wrap_content"\r
+            android:orientation="horizontal" >\r
+                <ImageButton\r
+                android:id="@+id/open"\r
+                style="?android:borderlessButtonStyle"\r
+                android:layout_width="wrap_content"\r
+                android:layout_height="wrap_content"\r
+                android:layout_weight=".2"\r
+                android:background="@color/btnColor"\r
+                android:src="@drawable/open" />\r
+\r
+                <ImageButton\r
+                android:id="@+id/fit"\r
+                style="?android:borderlessButtonStyle"\r
+                android:layout_width="wrap_content"\r
+                android:layout_height="wrap_content"\r
+                android:layout_weight=".2"\r
+                android:background="@color/btnColor"\r
+                android:src="@drawable/fit" />\r
+\r
+                <ImageButton\r
+                android:id="@+id/view"\r
+                style="?android:borderlessButtonStyle"\r
+                android:layout_width="wrap_content"\r
+                android:layout_height="wrap_content"\r
+                android:layout_weight=".2"\r
+                android:background="@color/btnColor"\r
+                android:src="@drawable/view" />\r
+\r
+                <ImageButton\r
+                android:id="@+id/info"\r
+                style="?android:borderlessButtonStyle"\r
+                android:layout_width="wrap_content"\r
+                android:layout_height="wrap_content"\r
+                android:layout_weight=".2"\r
+                android:background="@color/btnColor"\r
+                android:src="@drawable/info" />\r
+\r
+                <ImageButton\r
+                android:id="@+id/message"\r
+                style="?android:borderlessButtonStyle"\r
+                android:layout_width="wrap_content"\r
+                android:layout_height="wrap_content"\r
+                android:layout_weight=".2"\r
+                android:background="@color/btnColor"\r
+                android:src="@drawable/message" />\r
+            </LinearLayout>\r
+\r
+                <TextView\r
+                android:id="@+id/message_view"\r
+                android:background="@color/viewColor"\r
+                android:text="Message Log"\r
+                android:textSize="16px"\r
+                android:textStyle="bold"\r
+                android:visibility="gone"\r
+                android:layout_width="wrap_content"\r
+                android:layout_height="wrap_content"/>\r
+\r
+            <TextView\r
+            android:id="@+id/info_view"\r
+            android:background="@color/viewColor"\r
+            android:gravity="center"\r
+            android:text="info Log"\r
+            android:textSize="16px"\r
+            android:textStyle="bold"\r
+            android:visibility="gone"\r
+            android:layout_width="fill_parent"\r
+            android:layout_height="wrap_content"/>\r
+\r
+            <LinearLayout\r
+            android:id="@+id/view_group"\r
+            android:layout_width="fill_parent"\r
+            android:layout_height="wrap_content"\r
+            android:layout_gravity="bottom|end"\r
+            android:orientation="horizontal" >\r
+\r
+                <ImageButton \r
+                style="?android:borderlessButtonStyle"\r
+                android:background="@color/btnColor" \r
+                android:id="@+id/proj_front"\r
+                android:layout_height="wrap_content" \r
+                android:layout_width="fill_parent"\r
+                android:src="@drawable/proj_front"\r
+                android:layout_weight=".16"/>\r
+\r
+                <ImageButton \r
+                style="?android:borderlessButtonStyle"\r
+                android:background="@color/btnColor" \r
+                android:id="@+id/proj_top"\r
+                android:layout_height="wrap_content" \r
+                android:layout_width="fill_parent"\r
+                android:src="@drawable/proj_top"\r
+                android:layout_weight=".16"/>\r
+\r
+                <ImageButton \r
+                style="?android:borderlessButtonStyle"\r
+                android:background="@color/btnColor" \r
+                android:id="@+id/proj_left"\r
+                android:layout_height="wrap_content" \r
+                android:layout_width="fill_parent"\r
+                android:src="@drawable/proj_left"\r
+                android:layout_weight=".16"/>\r
+\r
+                <ImageButton \r
+                style="?android:borderlessButtonStyle"\r
+                android:background="@color/btnColor" \r
+                android:id="@+id/proj_back"\r
+                android:layout_height="wrap_content" \r
+                android:layout_width="fill_parent"\r
+                android:src="@drawable/proj_back"\r
+                android:layout_weight=".16"/>\r
+\r
+                <ImageButton \r
+                style="?android:borderlessButtonStyle"\r
+                android:background="@color/btnColor" \r
+                android:id="@+id/proj_bottom"\r
+                android:layout_height="wrap_content" \r
+                android:layout_width="fill_parent"\r
+                android:src="@drawable/proj_bottom"\r
+                android:layout_weight=".16"/>\r
+\r
+                <ImageButton \r
+                style="?android:borderlessButtonStyle"\r
+                android:background="@color/btnColor" \r
+                android:id="@+id/proj_right"\r
+                android:layout_height="wrap_content" \r
+                android:layout_width="fill_parent"\r
+                android:src="@drawable/proj_right"\r
+                android:layout_weight=".16"/>\r
+            </LinearLayout>\r
+        </FrameLayout>\r
+    </LinearLayout>\r
+</TableLayout>\r
diff --git a/samples/java/jniviewer/res/values/id.xml b/samples/java/jniviewer/res/values/id.xml
new file mode 100644 (file)
index 0000000..56bde94
--- /dev/null
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+   <item name="open" type="id"/>
+   <item name="save" type="id"/>
+   <item name="screenshot" type="id"/>
+   <item name="mesh_none" type="id"/>
+   <item name="mesh_low_q" type="id"/>
+   <item name="mesh_norm_q" type="id"/>
+   <item name="mesh_high_q" type="id"/>
+   <item name="view1" type="id"/>
+   <item name="view2" type="id"/>
+   <item name="view3" type="id"/>
+   <item name="settings" type="id"/>
+   <item name="message" type="id"/>
+   <item name="lock" type="id"/>
+   <item name="info" type="id"/>
+</resources>
diff --git a/samples/java/jniviewer/res/values/strings.xml b/samples/java/jniviewer/res/values/strings.xml
new file mode 100644 (file)
index 0000000..6f2a431
--- /dev/null
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="app_name">OpenCASCADE JNI Java Sample</string>
+    <color name="btnColor">#484848</color>
+    <color name="pressedBtnColor">#0099CC</color>
+    <color name="viewColor">#66252525</color>
+        <string-array name="ext_to_save">
+        <item>.png</item>
+        <item>.jpg</item>
+    </string-array>
+    <string-array name="ext_to_exp">
+        <item>.brep</item>
+        <item>.rle</item>
+        <item>.iges</item>
+        <item>.igs</item>
+        <item>.step</item>
+        <item>.stp</item>
+    </string-array>
+    <string name="wireframe_shading_title">wireframe/shading</string>
+    <string name="color_title">color</string>
+    <string name="material_title">material</string>
+    <string name="transparency_title">transparency</string>
+    <string name="hidden_lines_title">show/hide hidden lines</string>
+    <string name="info_html" formatted="false">
+        <![CDATA[
+        <p>OpenCASCADE JNI Java Sample</p>
+        <p>Simple viewer for BREP, STEP and IGES files.</p>
+        <p>Driven by Open CASCADE Technology %d.%d.%d.</p>
+        <p>Copyright 2014 OPEN CASCADE SAS.</p>
+        <p><img src="info_image"></p>
+        <p>http://www.opencascade.com</p>
+        <p>http://www.opencascade.org</p>
+        ]]>
+    </string>
+</resources>
diff --git a/samples/java/jniviewer/src/com/opencascade/jnisample/OcctJniActivity.java b/samples/java/jniviewer/src/com/opencascade/jnisample/OcctJniActivity.java
new file mode 100644 (file)
index 0000000..7cb5366
--- /dev/null
@@ -0,0 +1,778 @@
+// Copyright (c) 2014 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+package com.opencascade.jnisample;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.List;
+
+import android.app.Activity;
+import android.content.Context;
+
+import android.content.Intent;
+import android.content.res.AssetManager;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Point;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Environment;
+
+import android.text.Html;
+import android.text.Html.ImageGetter;
+import android.text.Spanned;
+import android.util.TypedValue;
+import android.view.Display;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.TextView;
+import android.widget.Toast;
+
+//! Main activity
+public class OcctJniActivity extends Activity implements OnClickListener
+{
+
+  //! Auxiliary method to print temporary info messages
+  public static void printShortInfo (Activity     theActivity,
+                                     CharSequence theInfo)
+  {
+    Context aCtx   = theActivity.getApplicationContext();
+    Toast   aToast = Toast.makeText (aCtx, theInfo, Toast.LENGTH_LONG);
+    aToast.show();
+  }
+
+  //! Load single native library
+  private static boolean loadLibVerbose (String        theLibName,
+                                         StringBuilder theLoadedInfo,
+                                         StringBuilder theFailedInfo)
+  {
+    try
+    {
+      System.loadLibrary (theLibName);
+      theLoadedInfo.append ("Info:  native library \"");
+      theLoadedInfo.append (theLibName);
+      theLoadedInfo.append ("\" has been loaded\n");
+      return true;
+    }
+    catch (java.lang.UnsatisfiedLinkError theError)
+    {
+      theFailedInfo.append ("Error: native library \"");
+      theFailedInfo.append (theLibName);
+      theFailedInfo.append ("\" is unavailable:\n  " + theError.getMessage());
+      return false;
+    }
+    catch (SecurityException theError)
+    {
+      theFailedInfo.append ("Error: native library \"");
+      theFailedInfo.append (theLibName);
+      theFailedInfo.append ("\" can not be loaded for security reasons:\n  " + theError.getMessage());
+      return false;
+    }
+  }
+
+  public static boolean wasNativesLoadCalled = false;
+  public static boolean areNativeLoaded      = false;
+  public static String  nativeLoaded         = "";
+  public static String  nativeFailed         = "";
+
+  //! Auxiliary method to load native libraries
+  public boolean loadNatives()
+  {
+    if (wasNativesLoadCalled)
+    {
+      return areNativeLoaded;
+    }
+    wasNativesLoadCalled = true;
+    StringBuilder aLoaded = new StringBuilder();
+    StringBuilder aFailed = new StringBuilder();
+
+    // copy OCCT resources
+    String aResFolder = getFilesDir().getAbsolutePath();
+    copyAssetFolder (getAssets(), "Shaders",   aResFolder + "/Shaders");
+    copyAssetFolder (getAssets(), "SHMessage", aResFolder + "/SHMessage");
+    copyAssetFolder (getAssets(), "XSMessage", aResFolder + "/XSMessage");
+    copyAssetFolder (getAssets(), "TObj",      aResFolder + "/TObj");
+    copyAssetFolder (getAssets(), "UnitsAPI",  aResFolder + "/UnitsAPI");
+
+    // C++ runtime
+    loadLibVerbose ("gnustl_shared", aLoaded, aFailed);
+
+    // 3rd-parties
+    loadLibVerbose ("freetype",  aLoaded, aFailed);
+    loadLibVerbose ("freeimage", aLoaded, aFailed);
+
+    if (// OCCT modeling
+        !loadLibVerbose ("TKernel",      aLoaded, aFailed)
+     || !loadLibVerbose ("TKMath",       aLoaded, aFailed)
+     || !loadLibVerbose ("TKG2d",        aLoaded, aFailed)
+     || !loadLibVerbose ("TKG3d",        aLoaded, aFailed)
+     || !loadLibVerbose ("TKGeomBase",   aLoaded, aFailed)
+     || !loadLibVerbose ("TKBRep",       aLoaded, aFailed)
+     || !loadLibVerbose ("TKGeomAlgo",   aLoaded, aFailed)
+     || !loadLibVerbose ("TKTopAlgo",    aLoaded, aFailed)
+     || !loadLibVerbose ("TKShHealing",  aLoaded, aFailed)
+     || !loadLibVerbose ("TKMesh",       aLoaded, aFailed)
+        // exchange
+     || !loadLibVerbose ("TKPrim",       aLoaded, aFailed)
+     || !loadLibVerbose ("TKBO",         aLoaded, aFailed)
+     || !loadLibVerbose ("TKBool",       aLoaded, aFailed)
+     || !loadLibVerbose ("TKFillet",     aLoaded, aFailed)
+     || !loadLibVerbose ("TKOffset",     aLoaded, aFailed)
+     || !loadLibVerbose ("TKXSBase",     aLoaded, aFailed)
+     || !loadLibVerbose ("TKIGES",       aLoaded, aFailed)
+     || !loadLibVerbose ("TKSTEPBase",   aLoaded, aFailed)
+     || !loadLibVerbose ("TKSTEPAttr",   aLoaded, aFailed)
+     || !loadLibVerbose ("TKSTEP209",    aLoaded, aFailed)
+     || !loadLibVerbose ("TKSTEP",       aLoaded, aFailed)
+        // OCCT Visualization
+     || !loadLibVerbose ("TKService",    aLoaded, aFailed)
+     || !loadLibVerbose ("TKHLR",        aLoaded, aFailed)
+     || !loadLibVerbose ("TKV3d",        aLoaded, aFailed)
+     || !loadLibVerbose ("TKOpenGl",     aLoaded, aFailed)
+        // application code
+     || !loadLibVerbose ("TKJniSample",  aLoaded, aFailed))
+    {
+      nativeLoaded = aLoaded.toString();
+      nativeFailed = aFailed.toString();
+      areNativeLoaded = false;
+      //exitWithError (theActivity, "Broken apk?\n" + theFailedInfo);
+      return false;
+    }
+    nativeLoaded = aLoaded.toString();
+    areNativeLoaded = true;
+    return true;
+  }
+
+  //! Create activity
+  @Override protected void onCreate (Bundle theBundle)
+  {
+    super.onCreate (theBundle);
+
+    boolean isLoaded = loadNatives();
+    if (!isLoaded)
+    {
+      printShortInfo (this, nativeFailed);
+      OcctJniLogger.postMessage (nativeLoaded + "\n" + nativeFailed);
+    }
+
+    setContentView (R.layout.activity_main);
+    
+    myOcctView        = (OcctJniView )findViewById (R.id.custom_view);
+    myMessageTextView = (TextView    )findViewById (R.id.message_view);
+    OcctJniLogger.setTextView (myMessageTextView);
+
+    createViewAndButtons (Configuration.ORIENTATION_LANDSCAPE);
+
+    myButtonPreferSize = defineButtonSize ((LinearLayout )findViewById (R.id.panel_menu));
+    ImageButton aScrollBtn = (ImageButton )findViewById (R.id.scroll_btn);
+    aScrollBtn.setY (myButtonPreferSize);
+    aScrollBtn.setOnTouchListener (new View.OnTouchListener()
+    {
+      @Override
+      public boolean onTouch (View theView, MotionEvent theEvent)
+      {
+        return onScrollBtnTouch (theView, theEvent);
+      }
+    });
+
+    onConfigurationChanged (getResources().getConfiguration());
+
+    Intent anIntent = getIntent();
+    Uri    aDataUrl  = anIntent != null ? anIntent.getData() : null;
+    String aDataPath = aDataUrl != null ? aDataUrl.getPath() : "";
+    myOcctView.open (aDataPath);
+    myLastPath = aDataPath;
+  }
+
+  //! Handle scroll events
+  private boolean onScrollBtnTouch (View        theView,
+                                    MotionEvent theEvent)
+  {
+    switch (theEvent.getAction())
+    {
+      case MotionEvent.ACTION_DOWN:
+      {
+        LinearLayout aPanelMenu = (LinearLayout )findViewById (R.id.panel_menu);
+        boolean isLandscape = (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE);
+        if (aPanelMenu.getVisibility() == View.VISIBLE)
+        {
+          aPanelMenu.setVisibility (View.GONE);
+          if (!isLandscape)
+          {
+            ((ImageButton )theView).setImageResource (R.drawable.open_p);
+            theView.setY (0);
+          }
+          else
+          {
+            ((ImageButton )theView).setImageResource (R.drawable.open_l);
+            theView.setX (0);
+          }
+        }
+        else
+        {
+          aPanelMenu.setVisibility (View.VISIBLE);
+          if (!isLandscape)
+          {
+            ((ImageButton )theView).setImageResource (R.drawable.close_p);
+            theView.setY (myButtonPreferSize);
+          }
+          else
+          {
+            ((ImageButton )theView).setImageResource (R.drawable.close_l);
+            theView.setX (myButtonPreferSize);
+          }
+        }
+        break;
+      }
+    }
+    return false;
+  }
+
+  //! Initialize views and buttons
+  private void createViewAndButtons (int theOrientation)
+  {
+    // open button
+    ImageButton anOpenButton = (ImageButton )findViewById (R.id.open);
+    anOpenButton.setOnClickListener (this);
+
+    // fit all
+    ImageButton aFitAllButton = (ImageButton )findViewById (R.id.fit);
+    aFitAllButton.setOnClickListener (this);
+    aFitAllButton.setOnTouchListener (new View.OnTouchListener()
+    {
+      @Override
+      public boolean onTouch (View theView, MotionEvent theEvent)
+      {
+        return onTouchButton (theView, theEvent);
+      }
+    });
+    
+    // message
+    ImageButton aMessageButton = (ImageButton )findViewById (R.id.message);
+    aMessageButton.setOnClickListener (this);
+
+    // info
+    ImageButton anInfoButton = (ImageButton )findViewById (R.id.info);
+    anInfoButton.setOnClickListener (this);
+    
+    // font for text view
+    TextView anInfoView = (TextView )findViewById (R.id.info_view);
+    anInfoView.setTextSize (TypedValue.COMPLEX_UNIT_SP, 18);
+
+    // add submenu buttons
+    createSubmenuBtn (R.id.view, R.id.view_group,
+                      Arrays.asList (R.id.proj_front, R.id.proj_top,    R.id.proj_left,
+                                     R.id.proj_back,  R.id.proj_bottom, R.id.proj_right),
+                      Arrays.asList (R.drawable.proj_front, R.drawable.proj_top,    R.drawable.proj_left,
+                                     R.drawable.proj_back,  R.drawable.proj_bottom, R.drawable.proj_right),
+                      4);
+  }
+
+  @Override protected void onNewIntent (Intent theIntent)
+  {
+    super.onNewIntent (theIntent);
+    setIntent (theIntent);
+  }
+
+  @Override protected void onDestroy()
+  {
+    super.onDestroy();
+    OcctJniLogger.setTextView (null);
+  }
+
+  @Override protected void onPause()
+  {
+    super.onPause();
+    myOcctView.onPause();
+  }
+
+  @Override protected void onResume()
+  {
+    super.onResume();
+    myOcctView.onResume();
+
+    Intent anIntent = getIntent();
+    Uri    aDataUrl  = anIntent != null ? anIntent.getData() : null;
+    String aDataPath = aDataUrl != null ? aDataUrl.getPath() : "";
+    if (!aDataPath.equals (myLastPath))
+    {
+      myOcctView.open (aDataPath);
+      myLastPath = aDataPath;
+    }
+  }
+
+  //! Copy folder from assets
+  private boolean copyAssetFolder (AssetManager theAssetMgr,
+                                   String       theAssetFolder,
+                                   String       theFolderPathTo)
+  {
+    try
+    {
+      String[] aFiles  = theAssetMgr.list (theAssetFolder);
+      File     aFolder = new File (theFolderPathTo);
+      aFolder.mkdirs();
+      boolean isOk = true;
+      for (String aFileIter : aFiles)
+      {
+        if (aFileIter.contains ("."))
+        {
+          isOk &= copyAsset (theAssetMgr,
+                             theAssetFolder  + "/" + aFileIter,
+                             theFolderPathTo + "/" + aFileIter);
+        }
+        else
+        {
+          isOk &= copyAssetFolder (theAssetMgr,
+                                   theAssetFolder  + "/" + aFileIter,
+                                   theFolderPathTo + "/" + aFileIter);
+        }
+      }
+      return isOk;
+    }
+    catch (Exception theError)
+    {
+      theError.printStackTrace();
+      return false;
+    }
+  }
+
+  //! Copy single file from assets
+  private boolean copyAsset (AssetManager theAssetMgr,
+                             String       thePathFrom,
+                             String       thePathTo)
+  {
+    try
+    {
+      InputStream aStreamIn = theAssetMgr.open (thePathFrom);
+      File aFileTo = new File (thePathTo);
+      aFileTo.createNewFile();
+      OutputStream aStreamOut = new FileOutputStream (thePathTo);
+      copyStreamContent (aStreamIn, aStreamOut);
+      aStreamIn.close();
+      aStreamIn = null;
+      aStreamOut.flush();
+      aStreamOut.close();
+      aStreamOut = null;
+      return true;
+    }
+    catch (Exception theError)
+    {
+      theError.printStackTrace();
+      return false;
+    }
+  }
+
+  //! Copy single file
+  private static void copyStreamContent (InputStream  theIn,
+                                         OutputStream theOut) throws IOException
+  {
+    byte[] aBuffer = new byte[1024];
+    int aNbReadBytes = 0;
+    while ((aNbReadBytes = theIn.read (aBuffer)) != -1)
+    {
+      theOut.write (aBuffer, 0, aNbReadBytes);
+    }
+  }
+  
+  //! Show/hide text view
+  private void switchTextView (TextView    theTextView,
+                               ImageButton theClickedBtn,
+                               boolean     theToSwitchOn)
+  {
+    if (theTextView != null
+     && theTextView.getVisibility() == View.GONE
+     && theToSwitchOn)
+    {
+      theTextView.setVisibility (View.VISIBLE);
+      theClickedBtn.setBackgroundColor (getResources().getColor(R.color.pressedBtnColor));
+      setTextViewPosition (theTextView);
+    }
+    else
+    {
+      theTextView.setVisibility (View.GONE);
+      theClickedBtn.setBackgroundColor (getResources().getColor (R.color.btnColor));
+    }
+  }
+
+  //! Setup text view position
+  private void setTextViewPosition (TextView theTextView)
+  {
+    if (theTextView.getVisibility() != View.VISIBLE)
+    {
+      return;
+    }
+
+    if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE)
+    {
+      theTextView.setX (myButtonPreferSize);
+      theTextView.setY (0);
+    }
+    else
+    {
+      theTextView.setX (0);
+      theTextView.setY (myButtonPreferSize);
+    }
+  }
+
+  @Override
+  public void onClick (View theButton)
+  {
+    ImageButton aClickedBtn = (ImageButton )theButton;
+    switch (aClickedBtn.getId())
+    {
+      case R.id.message:
+      {
+        switchTextView ((TextView    )findViewById (R.id.info_view),
+                        (ImageButton )findViewById (R.id.info), false);
+        switchTextView (myMessageTextView, aClickedBtn, true);
+        return;
+      }
+      case R.id.info:
+      {
+        String aText = getString (R.string.info_html);
+        aText = String.format (aText, cppOcctMajorVersion(), cppOcctMinorVersion(), cppOcctMicroVersion());
+        Spanned aSpanned = Html.fromHtml (aText, new ImageGetter()
+        {
+          @Override
+          public Drawable getDrawable (String theSource)
+          {
+            Resources aResources = getResources();
+            int anId = aResources.getIdentifier (theSource, "drawable", getPackageName());
+            Drawable aRes = aResources.getDrawable (anId);
+            aRes.setBounds (0, 0, aRes.getIntrinsicWidth(), aRes.getIntrinsicHeight());
+            return aRes;
+          }
+        }, null);
+
+        TextView anInfoView = (TextView )findViewById (R.id.info_view);
+        anInfoView.setText (aSpanned);
+        switchTextView (myMessageTextView, (ImageButton ) findViewById (R.id.message), false);
+        switchTextView (anInfoView,        aClickedBtn,                                true);
+        return;
+      }
+      case R.id.fit:
+      {
+        myOcctView.fitAll();
+        return;
+      }
+      case R.id.proj_front:
+      {
+        myOcctView.setProj (OcctJniRenderer.TypeOfOrientation.Xpos);
+        return;
+      }
+      case R.id.proj_left:
+      {
+        myOcctView.setProj (OcctJniRenderer.TypeOfOrientation.Yneg);
+        return;
+      }
+      case R.id.proj_top:
+      {
+        myOcctView.setProj (OcctJniRenderer.TypeOfOrientation.Zpos);
+        return;
+      }
+      case R.id.proj_back:
+      {
+        myOcctView.setProj (OcctJniRenderer.TypeOfOrientation.Xneg);
+        return;
+      }
+      case R.id.proj_right:
+      {
+        myOcctView.setProj (OcctJniRenderer.TypeOfOrientation.Ypos);
+        return;
+      }
+      case R.id.proj_bottom:
+      {
+        myOcctView.setProj (OcctJniRenderer.TypeOfOrientation.Zneg);
+        return;
+      }
+      case R.id.open:
+      {
+        File aPath = Environment.getExternalStorageDirectory();
+        aClickedBtn.setBackgroundColor (getResources().getColor(R.color.pressedBtnColor));
+        if (myFileOpenDialog == null)
+        {
+          myFileOpenDialog = new OcctJniFileDialog (this, aPath);
+          myFileOpenDialog.setFileEndsWith (".brep");
+          myFileOpenDialog.setFileEndsWith (".rle");
+          myFileOpenDialog.setFileEndsWith (".iges");
+          myFileOpenDialog.setFileEndsWith (".igs");
+          myFileOpenDialog.setFileEndsWith (".step");
+          myFileOpenDialog.setFileEndsWith (".stp");
+          myFileOpenDialog.addFileListener (new OcctJniFileDialog.FileSelectedListener()
+          {
+            public void fileSelected (File theFile)
+            {
+              if (theFile != null && myOcctView != null)
+              {
+                myOcctView.open (theFile.getPath());
+              }
+            }
+          });
+          myFileOpenDialog.addDialogDismissedListener (new OcctJniFileDialog.DialogDismissedListener()
+          {
+            @Override
+            public void dialogDismissed()
+            {
+              ImageButton openButton = (ImageButton )findViewById (R.id.open);
+              openButton.setBackgroundColor (getResources().getColor(R.color.btnColor));
+            }
+          });
+        }
+        myFileOpenDialog.showDialog();
+        return;
+      }
+    }
+  }
+
+  private void createSubmenuBtn (int     theParentBtnId,
+                                 int     theParentLayoutId,
+                                 final List<Integer> theNewButtonIds,
+                                 final List<Integer> theNewButtonImageIds,
+                                 int     thePosition)
+  {
+    int aPosInList = 0;
+    final ImageButton aParentBtn = (ImageButton )findViewById (theParentBtnId);
+
+    ViewGroup.LayoutParams aParams = null;
+    LinearLayout parentLayout = (LinearLayout ) findViewById (theParentLayoutId);
+    for (Integer newButtonId : theNewButtonIds)
+    {
+      ImageButton aNewButton = (ImageButton )findViewById (newButtonId);
+      if (aNewButton == null)
+      {
+        aNewButton = (ImageButton )new ImageButton (this);
+        aNewButton.setId (newButtonId);
+        aNewButton.setImageResource (theNewButtonImageIds.get (aPosInList));
+        aNewButton.setLayoutParams (aParams);
+        parentLayout.addView (aNewButton);
+      }
+
+      aNewButton.setOnClickListener (this);
+      aNewButton.setVisibility (View.GONE);
+
+      aNewButton.setOnTouchListener (new View.OnTouchListener()
+      {
+        @Override
+        public boolean onTouch (View theView, MotionEvent theEvent)
+        {
+          return onTouchButton (theView, theEvent);
+        }
+      });
+      ++aPosInList;
+    }
+
+    if (aParentBtn != null)
+    {
+      aParentBtn.setOnTouchListener (null);
+      aParentBtn.setOnTouchListener (new View.OnTouchListener()
+      {
+        @Override
+        public boolean onTouch (View theView, MotionEvent theEvent)
+        {
+          if (theEvent.getAction () == MotionEvent.ACTION_DOWN)
+          {
+            Boolean isVisible = false;
+            for (Integer aNewButtonId : theNewButtonIds)
+            {
+              ImageButton anBtn = (ImageButton )findViewById (aNewButtonId);
+              if (anBtn != null)
+              {
+                if (anBtn.getVisibility() == View.GONE)
+                {
+                  anBtn.setVisibility (View.VISIBLE);
+                  isVisible = true;
+                }
+                else
+                {
+                  anBtn.setVisibility (View.GONE);
+                }
+              }
+            }
+            aParentBtn.setBackgroundColor (!isVisible ? getResources().getColor(R.color.btnColor) : getResources().getColor(R.color.pressedBtnColor));
+          }
+          return false;
+        }
+      });
+    }
+  }
+
+  //! Implements onTouch functionality
+  private boolean onTouchButton (View        theView,
+                                 MotionEvent theEvent)
+  {
+    switch (theEvent.getAction())
+    {
+      case MotionEvent.ACTION_DOWN:
+        ((ImageButton )theView).setBackgroundColor (getResources().getColor (R.color.pressedBtnColor));
+        break;
+      case MotionEvent.ACTION_UP:
+        ((ImageButton )theView).setBackgroundColor (getResources().getColor (R.color.btnColor));
+        break;
+    }
+    return false;
+  }
+
+  //! Handle configuration change event
+  @Override
+  public void onConfigurationChanged (Configuration theNewConfig)
+  {
+    super.onConfigurationChanged (theNewConfig);
+    LinearLayout aLayoutPanelMenu       = (LinearLayout )findViewById (R.id.panel_menu);
+    LayoutParams aPanelMenuLayoutParams = aLayoutPanelMenu.getLayoutParams();
+
+    LinearLayout aLayoutViewGroup       = (LinearLayout )findViewById (R.id.view_group);
+    LayoutParams aViewGroupLayoutParams = aLayoutViewGroup.getLayoutParams();
+    ImageButton  aScrollBtn             = (ImageButton )findViewById (R.id.scroll_btn);
+    LayoutParams aScrollBtnLayoutParams = aScrollBtn.getLayoutParams();
+
+    myButtonPreferSize = defineButtonSize ((LinearLayout )findViewById (R.id.panel_menu));
+    defineButtonSize ((LinearLayout )findViewById (R.id.view_group));
+
+    switch (theNewConfig.orientation)
+    {
+      case Configuration.ORIENTATION_PORTRAIT:
+      {
+        setHorizontal (aLayoutPanelMenu, aPanelMenuLayoutParams);
+        setHorizontal (aLayoutViewGroup, aViewGroupLayoutParams);
+        aLayoutViewGroup.setGravity (Gravity.BOTTOM);
+
+        aScrollBtnLayoutParams.height = LayoutParams.WRAP_CONTENT;
+        aScrollBtnLayoutParams.width  = LayoutParams.MATCH_PARENT;
+        aScrollBtn.setLayoutParams (aScrollBtnLayoutParams);
+        if (aLayoutPanelMenu.getVisibility() == View.VISIBLE)
+        {
+          aScrollBtn.setImageResource (R.drawable.close_p);
+          aScrollBtn.setY (myButtonPreferSize);
+          aScrollBtn.setX (0);
+        }
+        else
+        {
+          aScrollBtn.setImageResource (R.drawable.open_p);
+          aScrollBtn.setY (0);
+          aScrollBtn.setX (0);
+        }
+        break;
+      }
+      case Configuration.ORIENTATION_LANDSCAPE:
+      {
+        setVertical (aLayoutPanelMenu, aPanelMenuLayoutParams);
+        setVertical (aLayoutViewGroup, aViewGroupLayoutParams);
+        aLayoutViewGroup.setGravity (Gravity.RIGHT);
+
+        aScrollBtnLayoutParams.height = LayoutParams.MATCH_PARENT;
+        aScrollBtnLayoutParams.width  = LayoutParams.WRAP_CONTENT;
+        aScrollBtn.setLayoutParams (aScrollBtnLayoutParams);
+        if (aLayoutPanelMenu.getVisibility() == View.VISIBLE)
+        {
+          aScrollBtn.setImageResource (R.drawable.close_l);
+          aScrollBtn.setX (myButtonPreferSize);
+          aScrollBtn.setY (0);
+        }
+        else
+        {
+          aScrollBtn.setImageResource (R.drawable.open_l);
+          aScrollBtn.setY (0);
+          aScrollBtn.setX (0);
+        }
+        break;
+      }
+    }
+    setTextViewPosition (myMessageTextView);
+    setTextViewPosition ((TextView )findViewById (R.id.info_view));
+  }
+
+  private void setHorizontal (LinearLayout theLayout,
+                              LayoutParams theLayoutParams)
+  {
+    theLayout.setOrientation (LinearLayout.HORIZONTAL);
+    theLayoutParams.height = LayoutParams.WRAP_CONTENT;
+    theLayoutParams.width  = LayoutParams.MATCH_PARENT;
+    theLayout.setLayoutParams (theLayoutParams);
+  }
+
+  private void setVertical (LinearLayout theLayout,
+                            LayoutParams theLayoutParams)
+  {
+    theLayout.setOrientation (LinearLayout.VERTICAL);
+    theLayoutParams.height = LayoutParams.MATCH_PARENT;
+    theLayoutParams.width  = LayoutParams.WRAP_CONTENT;
+    theLayout.setLayoutParams (theLayoutParams);
+  }
+
+  //! Define button size
+  private int defineButtonSize (LinearLayout theLayout)
+  {
+    boolean isLandscape = getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
+    Display aDisplay    = getWindowManager().getDefaultDisplay();
+    Point   aDispPnt    = new Point();
+    aDisplay.getSize (aDispPnt);
+
+    int aNbChildren = theLayout.getChildCount();
+    int aHeight     = aDispPnt.y / aNbChildren;
+    int aWidth      = aDispPnt.x / aNbChildren;
+    int aResultSize = 0;
+    for (int aChildIter = 0; aChildIter < aNbChildren; ++aChildIter)
+    {
+      View aView = theLayout.getChildAt (aChildIter);
+      if (aView instanceof ImageButton)
+      {
+        ImageButton aButton = (ImageButton )aView;
+        if (isLandscape)
+        {
+          aButton.setMinimumWidth (aHeight);
+        }
+        else
+        {
+          aButton.setMinimumHeight (aWidth);
+        }
+      }
+    }
+    if (isLandscape)
+    {
+      aResultSize = aHeight;
+    }
+    else
+    {
+      aResultSize = aWidth;
+    }
+    return aResultSize;
+  }
+
+  //! OCCT major version
+  private native long cppOcctMajorVersion();
+
+  //! OCCT minor version
+  private native long cppOcctMinorVersion();
+
+  //! OCCT micro version
+  private native long cppOcctMicroVersion();
+
+  private OcctJniView       myOcctView;
+  private TextView          myMessageTextView;
+  private String            myLastPath;
+  private OcctJniFileDialog myFileOpenDialog;
+  private int               myButtonPreferSize = 65;
+
+}
diff --git a/samples/java/jniviewer/src/com/opencascade/jnisample/OcctJniFileDialog.java b/samples/java/jniviewer/src/com/opencascade/jnisample/OcctJniFileDialog.java
new file mode 100644 (file)
index 0000000..e12cc1a
--- /dev/null
@@ -0,0 +1,376 @@
+// Copyright (c) 2014 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+package com.opencascade.jnisample;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import com.opencascade.jnisample.ListenerList.FireHandler;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.graphics.Color;
+import android.os.Environment;
+import android.view.View;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.Spinner;
+
+//! Simple open file dialog
+public class OcctJniFileDialog
+{
+
+  public enum DialogMode
+  {
+    FileOpen, FileExport, FileSave
+  }
+
+  private static final String PARENT_DIR = "..";
+  private String[]   myFileList;
+  private File       myCurrentPath;
+  private DialogMode myDialogMode = DialogMode.FileOpen;
+
+  private ListenerList<FileSelectedListener>    myFileListenerList    = new ListenerList<OcctJniFileDialog.FileSelectedListener>();
+  private ListenerList<DialogDismissedListener> myDialogDismissedList = new ListenerList<DialogDismissedListener>();
+  private final Activity myActivity;
+  private List<String>   myFileEndsWith;
+  private EditText myFileNameInput;
+  private Spinner  myFileExtSpinner;
+  int myCurrentExtPositionInList = 0;
+
+  public interface FileSelectedListener
+  {
+    void fileSelected (File theFile);
+  }
+
+  public interface DialogDismissedListener
+  {
+    void dialogDismissed();
+  }
+
+  //! Main constructor.
+  public OcctJniFileDialog (Activity theActivity,
+                            File     thePath)
+  {
+    myActivity = theActivity;
+    if (!thePath.exists())
+    {
+      thePath = Environment.getExternalStorageDirectory();
+    }
+    loadFileList (thePath);
+  }
+
+  //! Create new dialog
+  public Dialog createFileDialog()
+  {
+    final Object[] anObjWrapper = new Object[1];
+    Dialog aDialog = null;
+    AlertDialog.Builder aBuilder = new AlertDialog.Builder (myActivity);
+
+    aBuilder.setTitle (myCurrentPath.getPath());
+    LinearLayout aTitleLayout = new LinearLayout (myActivity);
+    aTitleLayout.setLayoutParams (new LinearLayout.LayoutParams (LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
+    aTitleLayout.setOrientation (LinearLayout.VERTICAL);
+
+    ListView list = new ListView (myActivity);
+    list.setScrollingCacheEnabled(false);
+    list.setBackgroundColor (Color.parseColor ("#33B5E5"));
+
+    list.setAdapter (new ArrayAdapter<String> (myActivity, android.R.layout.select_dialog_item, myFileList));
+    list.setOnItemClickListener (new AdapterView.OnItemClickListener ()
+    {
+
+      public void onItemClick (AdapterView<?> arg0, View view, int pos, long id)
+      {
+        String fileChosen = myFileList[pos];
+        File aChosenFile = getChosenFile (fileChosen);
+        if (aChosenFile.isDirectory())
+        {
+          loadFileList (aChosenFile);
+          ((Dialog )anObjWrapper[0]).cancel();
+          ((Dialog )anObjWrapper[0]).dismiss();
+          showDialog();
+        }
+        else
+        {
+          if (myDialogMode == DialogMode.FileOpen)
+          {
+            ((Dialog )anObjWrapper[0]).cancel();
+            ((Dialog )anObjWrapper[0]).dismiss();
+            fireFileSelectedEvent (aChosenFile);
+          }
+          else
+          {
+            myFileNameInput.setText (aChosenFile.getName());
+          }
+        }
+      }
+    });
+    list.setLayoutParams (new LinearLayout.LayoutParams (LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT, 0.6f));
+    aTitleLayout.addView (list);
+
+    if (myDialogMode == DialogMode.FileSave
+     || myDialogMode == DialogMode.FileExport)
+    {
+      myFileNameInput  = new EditText (myActivity);
+      myFileExtSpinner = new Spinner  (myActivity);
+      ArrayAdapter<CharSequence> adapter = null;
+      if (myDialogMode == DialogMode.FileExport)
+      {
+        adapter = ArrayAdapter.createFromResource (myActivity, R.array.ext_to_exp,
+            android.R.layout.simple_spinner_item);
+      }
+      else
+      {
+        adapter = ArrayAdapter.createFromResource (myActivity, R.array.ext_to_save,
+            android.R.layout.simple_spinner_item);
+      }
+      // Specify the layout to use when the list of choices appears
+      adapter.setDropDownViewResource (android.R.layout.simple_spinner_dropdown_item);
+      // Apply the adapter to the spinner
+      myFileExtSpinner.setAdapter (adapter);
+      myFileExtSpinner.setSelection (myCurrentExtPositionInList);
+
+      myFileExtSpinner.setOnItemSelectedListener (new AdapterView.OnItemSelectedListener()
+      {
+
+        @Override
+        public void onNothingSelected (AdapterView<?> theParentView)
+        {
+          // your code here
+        }
+
+        @Override
+        public void onItemSelected (AdapterView<?> theParent, View theView, int thePosition, long theId)
+        {
+          if (myCurrentExtPositionInList != thePosition)
+          {
+            myCurrentExtPositionInList = thePosition;
+            setFileEndsWith (Arrays.asList (myFileExtSpinner.getSelectedItem().toString()));
+            loadFileList (myCurrentPath);
+            ((Dialog )anObjWrapper[0]).cancel();
+            ((Dialog )anObjWrapper[0]).dismiss();
+            showDialog();
+          }
+        }
+      });
+
+      myFileExtSpinner.setLayoutParams (new LinearLayout.LayoutParams (LayoutParams.MATCH_PARENT,
+          LayoutParams.WRAP_CONTENT, 0.2f));
+      // titleLayout.addView(fileExtSpinner);
+      myFileNameInput.setLayoutParams (new LinearLayout.LayoutParams (LayoutParams.MATCH_PARENT,
+          LayoutParams.WRAP_CONTENT, 0.2f));
+      LinearLayout aControlsView = new LinearLayout (myActivity);
+
+      aControlsView.addView (myFileNameInput);
+      aControlsView.addView (myFileExtSpinner);
+
+      aTitleLayout.addView (aControlsView);
+      aBuilder.setView (aTitleLayout);
+      aBuilder.setPositiveButton ("OK", new DialogInterface.OnClickListener()
+      {
+        @Override
+        public void onClick (DialogInterface theDialog, int theWhich)
+        {
+          if (theWhich >= 0)
+          {
+            String aFileChosen = myFileList[theWhich];
+            File aChosenFile = getChosenFile (aFileChosen);
+            fireFileSelectedEvent (aChosenFile);
+          }
+        }
+      }).setNegativeButton ("Cancel", null);
+    }
+    else
+    {
+      aBuilder.setNegativeButton ("Cancel", null);
+    }
+
+    aBuilder.setView (aTitleLayout);
+
+    aDialog = aBuilder.show();
+    aDialog.setOnDismissListener (new DialogInterface.OnDismissListener()
+    {
+      @Override
+      public void onDismiss (DialogInterface theDialog)
+      {
+        fireDialogDismissedEvent();
+      }
+    });
+    anObjWrapper[0] = aDialog;
+    return aDialog;
+  }
+
+  public void addFileListener (FileSelectedListener theListener)
+  {
+    myFileListenerList.add (theListener);
+  }
+
+  public void addDialogDismissedListener (DialogDismissedListener theListener)
+  {
+    myDialogDismissedList.add (theListener);
+  }
+
+  //! Show file dialog
+  public void showDialog()
+  {
+    createFileDialog().show();
+  }
+
+  private void fireFileSelectedEvent (final File theFile)
+  {
+    myFileListenerList.fireEvent (new FireHandler<OcctJniFileDialog.FileSelectedListener>()
+    {
+      public void fireEvent (FileSelectedListener theListener)
+      {
+        theListener.fileSelected (theFile);
+      }
+    });
+  }
+
+  private void fireDialogDismissedEvent()
+  {
+    myDialogDismissedList.fireEvent (new FireHandler<OcctJniFileDialog.DialogDismissedListener>()
+    {
+      public void fireEvent (DialogDismissedListener theListener)
+      {
+        theListener.dialogDismissed();
+      }
+    });
+  }
+
+  private void loadFileList (File thePath)
+  {
+    myCurrentPath = thePath;
+    List<String> aList = new ArrayList<String>();
+    if (thePath.exists())
+    {
+      if (thePath.getParentFile() != null)
+      {
+        aList.add (PARENT_DIR);
+      }
+      FilenameFilter aFilter = new FilenameFilter()
+      {
+        public boolean accept (File theDir, String theFilename)
+        {
+          File aSel = new File (theDir, theFilename);
+          if (!aSel.canRead())
+          {
+            return false;
+          }
+          boolean isEndWith = false;
+          if (myFileEndsWith != null)
+          {
+            for (String aFileExtIter : myFileEndsWith)
+            {
+              if (theFilename.toLowerCase().endsWith (aFileExtIter))
+              {
+                isEndWith = true;
+                break;
+              }
+            }
+          }
+          return isEndWith || aSel.isDirectory();
+        }
+      };
+      String[] aFileList1 = thePath.list (aFilter);
+      if (aFileList1 != null)
+      {
+        for (String aFileIter : aFileList1)
+        {
+          aList.add (aFileIter);
+        }
+      }
+    }
+    myFileList = (String[] )aList.toArray (new String[] {});
+  }
+
+  private File getChosenFile (String theFileChosen)
+  {
+    if (theFileChosen.equals (PARENT_DIR))
+      return myCurrentPath.getParentFile();
+    else
+      return new File (myCurrentPath, theFileChosen);
+  }
+
+  public void setFileEndsWith (String fileEndsWith)
+  {
+    if (myFileEndsWith == null)
+    {
+      myFileEndsWith = new ArrayList<String>();
+    }
+    if (myFileEndsWith.indexOf (fileEndsWith) == -1)
+    {
+      myFileEndsWith.add (fileEndsWith);
+    }
+  }
+
+  public void setFileEndsWith (List<String> theFileEndsWith)
+  {
+    myFileEndsWith = theFileEndsWith;
+  }
+
+  public DialogMode DialogMode()
+  {
+    return myDialogMode;
+  }
+
+  public void DialogMode (DialogMode theMode)
+  {
+    myDialogMode = theMode;
+  }
+}
+
+class ListenerList<L>
+{
+  private List<L> myListenerList = new ArrayList<L>();
+
+  public interface FireHandler<L>
+  {
+    void fireEvent (L theListener);
+  }
+
+  public void add (L theListener)
+  {
+    myListenerList.add (theListener);
+  }
+
+  public void fireEvent (FireHandler<L> theFireHandler)
+  {
+    List<L> aCopy = new ArrayList<L> (myListenerList);
+    for (L anIter : aCopy)
+    {
+      theFireHandler.fireEvent (anIter);
+    }
+  }
+
+  public void remove (L theListener)
+  {
+    myListenerList.remove (theListener);
+  }
+
+  public List<L> getListenerList()
+  {
+    return myListenerList;
+  }
+}
diff --git a/samples/java/jniviewer/src/com/opencascade/jnisample/OcctJniLogger.java b/samples/java/jniviewer/src/com/opencascade/jnisample/OcctJniLogger.java
new file mode 100644 (file)
index 0000000..cc8b1e4
--- /dev/null
@@ -0,0 +1,71 @@
+// Copyright (c) 2014 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+package com.opencascade.jnisample;
+
+import java.util.concurrent.locks.ReentrantLock;
+
+import android.util.Log;
+import android.widget.TextView;
+
+//! Auxiliary class for logging messages
+public class OcctJniLogger
+{
+
+  //! Setup text view
+  public static void setTextView (TextView theTextView)
+  {
+    if (myTextView != null)
+    {
+      myLog = myTextView.getText().toString();
+    }
+
+    myTextView = theTextView;
+    if (myTextView != null)
+    {
+      myTextView.setText (myLog);
+      myLog = "";
+    }
+  }
+
+  //! Interface implementation
+  public static void postMessage (String theText)
+  {
+    final String aCopy = new String (theText);
+    Log.e (myTag, theText);
+
+    myMutex.lock();
+    final TextView aView = myTextView;
+    if (aView == null)
+    {
+      myLog += aCopy;
+      myMutex.unlock();
+      return;
+    }
+
+    aView.post (new Runnable()
+    {
+      public void run()
+      {
+        aView.setText (aView.getText() + aCopy + "\n");
+      }
+    });
+    myMutex.unlock();
+  }
+
+  private static final String        myTag      = "occtJniViewer";
+  private static final ReentrantLock myMutex    = new ReentrantLock (true);
+  private static TextView            myTextView = null;
+  private static String              myLog      = "";
+
+}
diff --git a/samples/java/jniviewer/src/com/opencascade/jnisample/OcctJniRenderer.java b/samples/java/jniviewer/src/com/opencascade/jnisample/OcctJniRenderer.java
new file mode 100644 (file)
index 0000000..731037a
--- /dev/null
@@ -0,0 +1,218 @@
+// Copyright (c) 2014 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+package com.opencascade.jnisample;
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+import android.opengl.GLSurfaceView;
+
+//! Wrapper for C++ OCCT viewer.
+public class OcctJniRenderer implements GLSurfaceView.Renderer
+{
+
+  //! Wrapper for V3d_TypeOfOrientation
+  enum TypeOfOrientation
+  {
+    Xpos, // front
+    Ypos, // left
+    Zpos, // top
+    Xneg, // back
+    Yneg, // right
+    Zneg  // bottom
+  };
+
+  //! Empty constructor.
+  OcctJniRenderer()
+  {
+    if (OcctJniActivity.areNativeLoaded)
+    {
+      myCppViewer = cppCreate();
+    }
+  }
+
+  //! Open file.
+  public void open (String thePath)
+  {
+    if (myCppViewer != 0)
+    {
+      cppOpen (myCppViewer, thePath);
+    }
+  }
+
+  //! Update viewer.
+  public void onDrawFrame (GL10 theGl)
+  {
+    if (myCppViewer != 0)
+    {
+      cppRedraw (myCppViewer);
+    }
+  }
+
+  //! (re)initialize viewer.
+  public void onSurfaceChanged (GL10 theGl, int theWidth, int theHeight)
+  {
+    if (myCppViewer != 0)
+    {
+      cppResize (myCppViewer, theWidth, theHeight);
+    }
+  }
+
+  public void onSurfaceCreated (GL10 theGl, EGLConfig theEglConfig)
+  {
+    if (myCppViewer != 0)
+    {
+      cppInit (myCppViewer);
+    }
+  }
+
+  //! Initialize rotation (remember first point position)
+  public void onStartRotation (int theStartX, int theStartY)
+  {
+    if (myCppViewer != 0)
+    {
+      cppStartRotation (myCppViewer, theStartX, theStartY);
+    }
+  }
+
+  //! Perform rotation (relative to first point)
+  public void onRotation (int theX, int theY)
+  {
+    if (myCppViewer != 0)
+    {
+      cppOnRotation (myCppViewer, theX, theY);
+    }
+  }
+
+  //! Perform panning
+  public void onPanning (int theDX, int theDY)
+  {
+    if (myCppViewer != 0)
+    {
+      cppOnPanning (myCppViewer, theDX, theDY);
+    }
+  }
+
+  //! Perform selection
+  public void onClick (int theX, int theY)
+  {
+    if (myCppViewer != 0)
+    {
+      cppOnClick (myCppViewer, theX, theY);
+    }
+  }
+
+  //! Stop previously active action (e.g. discard first rotation point)
+  public void onStopAction()
+  {
+    if (myCppViewer != 0)
+    {
+      cppStopAction (myCppViewer);
+    }
+  }
+
+  //! Fit All
+  public void fitAll()
+  {
+    if (myCppViewer != 0)
+    {
+      cppFitAll (myCppViewer);
+    }
+  }
+
+  //! Move camera
+  public void setProj (TypeOfOrientation theProj)
+  {
+    if (myCppViewer == 0)
+    {
+      return;
+    }
+
+    switch (theProj)
+    {
+      case Xpos: cppSetXposProj (myCppViewer); break;
+      case Ypos: cppSetYposProj (myCppViewer); break;
+      case Zpos: cppSetZposProj (myCppViewer); break;
+      case Xneg: cppSetXnegProj (myCppViewer); break;
+      case Yneg: cppSetYnegProj (myCppViewer); break;
+      case Zneg: cppSetZnegProj (myCppViewer); break;
+    }
+  }
+
+  //! Post message to the text view.
+  public void postMessage (String theText)
+  {
+    OcctJniLogger.postMessage (theText);
+  }
+
+  //! Create instance of C++ class
+  private native long cppCreate();
+
+  //! Destroy instance of C++ class
+  private native void cppDestroy (long theCppPtr);
+
+  //! Initialize OCCT viewer (steal OpenGL ES context bound to this thread)
+  private native void cppInit    (long theCppPtr);
+
+  //! Resize OCCT viewer
+  private native void cppResize  (long theCppPtr, int theWidth, int theHeight);
+
+  //! Open CAD file
+  private native void cppOpen    (long theCppPtr, String thePath);
+
+  //! Handle detection in the viewer
+  private native void cppMoveTo  (long theCppPtr, int theX, int theY);
+
+  //! Redraw OCCT viewer
+  private native void cppRedraw  (long theCppPtr);
+
+  //! Fit All
+  private native void cppFitAll  (long theCppPtr);
+
+  //! Move camera
+  private native void cppSetXposProj (long theCppPtr);
+
+  //! Move camera
+  private native void cppSetYposProj (long theCppPtr);
+
+  //! Move camera
+  private native void cppSetZposProj (long theCppPtr);
+
+  //! Move camera
+  private native void cppSetXnegProj (long theCppPtr);
+
+  //! Move camera
+  private native void cppSetYnegProj (long theCppPtr);
+
+  //! Move camera
+  private native void cppSetZnegProj (long theCppPtr);
+
+  //! Initialize rotation
+  private native void cppStartRotation (long theCppPtr, int theStartX, int theStartY);
+
+  //! Perform rotation
+  private native void cppOnRotation    (long theCppPtr, int theX,  int theY);
+
+  //! Perform panning
+  private native void cppOnPanning     (long theCppPtr, int theDX, int theDY);
+
+  //! Perform selection
+  private native void cppOnClick       (long theCppPtr, int theX,  int theY);
+
+  //! Stop action (rotation / panning / scaling)
+  private native void cppStopAction    (long theCppPtr);
+
+  private long myCppViewer = 0;   //!< pointer to c++ class instance
+
+}
diff --git a/samples/java/jniviewer/src/com/opencascade/jnisample/OcctJniView.java b/samples/java/jniviewer/src/com/opencascade/jnisample/OcctJniView.java
new file mode 100644 (file)
index 0000000..7909d9c
--- /dev/null
@@ -0,0 +1,332 @@
+// Copyright (c) 2014 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+package com.opencascade.jnisample;
+
+import android.app.ActionBar.LayoutParams;
+import android.content.Context;
+import android.graphics.PointF;
+import android.opengl.GLSurfaceView;
+import android.util.AttributeSet;
+import android.util.SparseArray;
+import android.view.MotionEvent;
+import android.widget.RelativeLayout;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+
+//! OpenGL ES 2.0+ view.
+//! Performs rendering in parallel thread.
+class OcctJniView extends GLSurfaceView
+{
+
+  // ! Default constructor.
+  public OcctJniView (Context      theContext,
+                      AttributeSet theAttrs)
+  {
+    super (theContext, theAttrs);
+
+    setPreserveEGLContextOnPause (true);
+    setEGLContextFactory (new ContextFactory());
+    setEGLConfigChooser  (new ConfigChooser());
+
+    RelativeLayout.LayoutParams aLParams = new RelativeLayout.LayoutParams (LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
+    aLParams.addRule (RelativeLayout.ALIGN_TOP);
+
+    myRenderer = new OcctJniRenderer();
+    setRenderer (myRenderer);
+  }
+
+  //! Open file.
+  public void open (String thePath)
+  {
+    final String aPath = thePath;
+    queueEvent (new Runnable() { public void run() { myRenderer.open (aPath); }});
+  }
+
+  //! Create OpenGL ES 2.0+ context
+  private static class ContextFactory implements GLSurfaceView.EGLContextFactory
+  {
+    private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
+    public EGLContext createContext (EGL10      theEgl,
+                                     EGLDisplay theEglDisplay,
+                                     EGLConfig  theEglConfig)
+    {
+      if (theEglConfig == null)
+      {
+        return null;
+      }
+
+      // reset EGL errors stack
+      int anError = EGL10.EGL_SUCCESS;
+      while ((anError = theEgl.eglGetError()) != EGL10.EGL_SUCCESS) {}
+
+      int[]      anAttribs   = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE };
+      EGLContext aEglContext = theEgl.eglCreateContext (theEglDisplay, theEglConfig, EGL10.EGL_NO_CONTEXT, anAttribs);
+
+      while ((anError = theEgl.eglGetError()) != EGL10.EGL_SUCCESS)
+      {
+        OcctJniLogger.postMessage ("Error: eglCreateContext() " + String.format ("0x%x", anError));
+      }
+      return aEglContext;
+    }
+
+    public void destroyContext (EGL10      theEgl,
+                                EGLDisplay theEglDisplay,
+                                EGLContext theEglContext)
+    {
+      theEgl.eglDestroyContext (theEglDisplay, theEglContext);
+    }
+  }
+
+  //! Search for RGB24 config with depth and stencil buffers
+  private static class ConfigChooser implements GLSurfaceView.EGLConfigChooser
+  {
+    //! Reset EGL errors stack
+    private void popEglErrors (EGL10 theEgl)
+    {
+      int anError = EGL10.EGL_SUCCESS;
+      while ((anError = theEgl.eglGetError()) != EGL10.EGL_SUCCESS)
+      {
+        OcctJniLogger.postMessage ("EGL Error: " + String.format ("0x%x", anError));
+      }
+    }
+
+    //! Auxiliary method to dump EGL configuration - for debugging purposes
+    @SuppressWarnings("unused")
+    private void printConfig (EGL10      theEgl,
+                              EGLDisplay theEglDisplay,
+                              EGLConfig  theEglConfig)
+    {
+      int[] THE_ATTRIBS =
+      {
+        EGL10.EGL_BUFFER_SIZE, EGL10.EGL_ALPHA_SIZE, EGL10.EGL_BLUE_SIZE, EGL10.EGL_GREEN_SIZE, EGL10.EGL_RED_SIZE, EGL10.EGL_DEPTH_SIZE, EGL10.EGL_STENCIL_SIZE,
+        EGL10.EGL_CONFIG_CAVEAT,
+        EGL10.EGL_CONFIG_ID,
+        EGL10.EGL_LEVEL,
+        EGL10.EGL_MAX_PBUFFER_HEIGHT, EGL10.EGL_MAX_PBUFFER_PIXELS, EGL10.EGL_MAX_PBUFFER_WIDTH,
+        EGL10.EGL_NATIVE_RENDERABLE,  EGL10.EGL_NATIVE_VISUAL_ID,   EGL10.EGL_NATIVE_VISUAL_TYPE,
+        0x3030, // EGL10.EGL_PRESERVED_RESOURCES,
+        EGL10.EGL_SAMPLES, EGL10.EGL_SAMPLE_BUFFERS,
+        EGL10.EGL_SURFACE_TYPE,
+        EGL10.EGL_TRANSPARENT_TYPE, EGL10.EGL_TRANSPARENT_RED_VALUE, EGL10.EGL_TRANSPARENT_GREEN_VALUE, EGL10.EGL_TRANSPARENT_BLUE_VALUE,
+        0x3039, 0x303A, // EGL10.EGL_BIND_TO_TEXTURE_RGB, EGL10.EGL_BIND_TO_TEXTURE_RGBA,
+        0x303B, 0x303C, // EGL10.EGL_MIN_SWAP_INTERVAL, EGL10.EGL_MAX_SWAP_INTERVAL
+        EGL10.EGL_LUMINANCE_SIZE, EGL10.EGL_ALPHA_MASK_SIZE,
+        EGL10.EGL_COLOR_BUFFER_TYPE, EGL10.EGL_RENDERABLE_TYPE,
+        0x3042 // EGL10.EGL_CONFORMANT
+      };
+      String[] THE_NAMES =
+      {
+        "EGL_BUFFER_SIZE", "EGL_ALPHA_SIZE", "EGL_BLUE_SIZE", "EGL_GREEN_SIZE", "EGL_RED_SIZE", "EGL_DEPTH_SIZE", "EGL_STENCIL_SIZE",
+        "EGL_CONFIG_CAVEAT",
+        "EGL_CONFIG_ID",
+        "EGL_LEVEL",
+        "EGL_MAX_PBUFFER_HEIGHT", "EGL_MAX_PBUFFER_PIXELS", "EGL_MAX_PBUFFER_WIDTH",
+        "EGL_NATIVE_RENDERABLE",  "EGL_NATIVE_VISUAL_ID",   "EGL_NATIVE_VISUAL_TYPE",
+        "EGL_PRESERVED_RESOURCES",
+        "EGL_SAMPLES", "EGL_SAMPLE_BUFFERS",
+        "EGL_SURFACE_TYPE",
+        "EGL_TRANSPARENT_TYPE", "EGL_TRANSPARENT_RED_VALUE", "EGL_TRANSPARENT_GREEN_VALUE", "EGL_TRANSPARENT_BLUE_VALUE",
+        "EGL_BIND_TO_TEXTURE_RGB", "EGL_BIND_TO_TEXTURE_RGBA",
+        "EGL_MIN_SWAP_INTERVAL", "EGL_MAX_SWAP_INTERVAL",
+        "EGL_LUMINANCE_SIZE", "EGL_ALPHA_MASK_SIZE",
+        "EGL_COLOR_BUFFER_TYPE", "EGL_RENDERABLE_TYPE",
+        "EGL_CONFORMANT"
+      };
+      int[] aValue = new int[1];
+      for (int anAttrIter = 0; anAttrIter < THE_ATTRIBS.length; ++anAttrIter)
+      {
+        int    anAttr = THE_ATTRIBS[anAttrIter];
+        String aName  = THE_NAMES  [anAttrIter];
+        if (theEgl.eglGetConfigAttrib (theEglDisplay, theEglConfig, anAttr, aValue))
+        {
+          OcctJniLogger.postMessage (String.format ("  %s: %d\n", aName, aValue[0]));
+        }
+        else
+        {
+          popEglErrors (theEgl);
+        }
+      }
+    }
+
+    //! Interface implementation
+    public EGLConfig chooseConfig (EGL10      theEgl,
+                                   EGLDisplay theEglDisplay)
+    {
+      int EGL_OPENGL_ES2_BIT = 4;
+      int[] aCfgAttribs =
+      {
+        EGL10.EGL_RED_SIZE,     8,
+        EGL10.EGL_GREEN_SIZE,   8,
+        EGL10.EGL_BLUE_SIZE,    8,
+        EGL10.EGL_ALPHA_SIZE,   0,
+        EGL10.EGL_DEPTH_SIZE,  24,
+        EGL10.EGL_STENCIL_SIZE, 8,
+        EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+        EGL10.EGL_NONE
+      };
+
+      EGLConfig aConfigs[] = new EGLConfig[1];
+      int[]     aNbConfigs = new int[1];
+      if (!theEgl.eglChooseConfig (theEglDisplay, aCfgAttribs, aConfigs, 1, aNbConfigs)
+       || aConfigs[0] == null)
+      {
+        aCfgAttribs[4 * 2 + 1] = 16; // try config with smaller depth buffer
+        popEglErrors (theEgl);
+        if (!theEgl.eglChooseConfig (theEglDisplay, aCfgAttribs, aConfigs, 1, aNbConfigs)
+         || aConfigs[0] == null)
+        {
+          OcctJniLogger.postMessage ("Error: eglChooseConfig() has failed!");
+          return null;
+        }
+      }
+
+      //printConfig (theEgl, theEglDisplay, aConfigs[0]);
+      return aConfigs[0];
+    }
+  }
+
+  //! Callback to handle touch events
+  @Override public boolean onTouchEvent (MotionEvent theEvent)
+  {
+    int aPointerIndex = theEvent.getActionIndex();
+    int aPointerId    = theEvent.getPointerId (aPointerIndex);
+    int aMaskedAction = theEvent.getActionMasked();
+    switch (aMaskedAction)
+    {
+      case MotionEvent.ACTION_DOWN:
+      case MotionEvent.ACTION_POINTER_DOWN:
+      {
+        PointF aPntLast = null;
+        if (myActivePointers.size() >= 1)
+        {
+          aPntLast = myActivePointers.get (myActivePointers.keyAt (0));
+        }
+
+        final PointF aPnt = new PointF();
+        aPnt.x = theEvent.getX (aPointerIndex);
+        aPnt.y = theEvent.getY (aPointerIndex);
+        myActivePointers.put (aPointerId, aPnt);
+
+        switch (myActivePointers.size())
+        {
+          case 1:
+          {
+            final int aStartX = (int )aPnt.x;
+            final int aStartY = (int )aPnt.y;
+            queueEvent (new Runnable() { public void run() { myRenderer.onStartRotation (aStartX, aStartY); }});
+            break;
+          }
+          case 2:
+          {
+            myPanFrom.x = (aPntLast.x + aPnt.x) * 0.5f;
+            myPanFrom.y = (aPntLast.y + aPnt.y) * 0.5f;
+            break;
+          }
+        }
+
+        break;
+      }
+      case MotionEvent.ACTION_MOVE:
+      {
+        for (int aNbPointers = theEvent.getPointerCount(), aPntIter = 0; aPntIter < aNbPointers; ++aPntIter)
+        {
+          PointF aPnt = myActivePointers.get (theEvent.getPointerId (aPntIter));
+          if (aPnt != null)
+          {
+            aPnt.x = theEvent.getX (aPntIter);
+            aPnt.y = theEvent.getY (aPntIter);
+          }
+        }
+
+        switch (myActivePointers.size())
+        {
+          case 1:
+          {
+            PointF aPnt = myActivePointers.get (theEvent.getPointerId (0));
+            final int anX = (int )aPnt.x;
+            final int anY = (int )aPnt.y;
+            queueEvent (new Runnable() { public void run() { myRenderer.onRotation (anX, anY); }});
+            break;
+          }
+          case 2:
+          {
+            PointF aPnt1 = myActivePointers.get (myActivePointers.keyAt (0));
+            PointF aPnt2 = myActivePointers.get (myActivePointers.keyAt (1));
+            PointF aPntAver = new PointF ((aPnt1.x + aPnt2.x) * 0.5f,
+                                          (aPnt1.y + aPnt2.y) * 0.5f);
+            final int aDX = (int )(aPntAver.x - myPanFrom.x);
+            final int aDY = (int )(myPanFrom.y -aPntAver.y);
+            myPanFrom.x = aPntAver.x;
+            myPanFrom.y = aPntAver.y;
+            queueEvent (new Runnable() { public void run() { myRenderer.onPanning (aDX, aDY); }});
+          }
+        }
+        break;
+      }
+      case MotionEvent.ACTION_UP:
+      case MotionEvent.ACTION_POINTER_UP:
+      case MotionEvent.ACTION_CANCEL:
+      {
+        myActivePointers.remove (aPointerId);
+        if (myActivePointers.size() == 0)
+        {
+          final int aPressX      = (int )theEvent.getX (aPointerIndex);
+          final int aPressY      = (int )theEvent.getY (aPointerIndex);
+          double    aPressTimeMs = theEvent.getEventTime() - theEvent.getDownTime();
+          if (aPressTimeMs < 100.0)
+          {
+            queueEvent (new Runnable() { public void run() { myRenderer.onClick (aPressX, aPressY); }});
+            break;
+          }
+        }
+        else if (myActivePointers.size() == 1)
+        {
+          PointF    aPnt    = myActivePointers.get (myActivePointers.keyAt (0));
+          final int aStartX = (int )aPnt.x;
+          final int aStartY = (int )aPnt.y;
+          queueEvent (new Runnable() { public void run() { myRenderer.onStartRotation (aStartX, aStartY); }});
+        }
+        //queueEvent (new Runnable() { public void run() { myRenderer.onStopAction(); }});
+        break;
+      }
+    }
+    ///invalidate();
+    return true;
+  }
+
+  //! Fit All
+  public void fitAll()
+  {
+    queueEvent (new Runnable() { public void run() { myRenderer.fitAll(); }});
+  }
+
+  //! Move camera
+  public void setProj (final OcctJniRenderer.TypeOfOrientation theProj)
+  {
+    queueEvent (new Runnable() { public void run() { myRenderer.setProj (theProj); }});
+  }
+
+  //! OCCT viewer
+  private OcctJniRenderer     myRenderer = null;
+
+  //! Touch events cache
+  private SparseArray<PointF> myActivePointers = new SparseArray<PointF>();
+
+  //! Starting point for panning event
+  private PointF              myPanFrom  = new PointF (0.0f, 0.0f);
+
+}