1 // Copyright (c) 2014 OPEN CASCADE SAS
3 // This file is part of Open CASCADE Technology software library.
5 // This library is free software; you can redistribute it and/or modify it under
6 // the terms of the GNU Lesser General Public License version 2.1 as published
7 // by the Free Software Foundation, with special exception defined in the file
8 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
9 // distribution for complete text of the license and disclaimer of any warranty.
11 // Alternatively, this file may be used under the terms of Open CASCADE
12 // commercial license or contractual agreement.
14 package com.opencascade.jnisample;
17 import java.io.FileOutputStream;
18 import java.io.IOException;
19 import java.io.InputStream;
20 import java.io.OutputStream;
21 import java.util.Arrays;
22 import java.util.List;
24 import android.app.Activity;
25 import android.content.Context;
27 import android.content.Intent;
28 import android.content.res.AssetManager;
29 import android.content.res.Configuration;
30 import android.content.res.Resources;
31 import android.graphics.Point;
32 import android.graphics.drawable.Drawable;
33 import android.net.Uri;
34 import android.os.Bundle;
35 import android.os.Environment;
37 import android.text.Html;
38 import android.text.Html.ImageGetter;
39 import android.text.Spanned;
40 import android.util.TypedValue;
41 import android.view.Display;
42 import android.view.Gravity;
43 import android.view.MotionEvent;
44 import android.view.View;
45 import android.view.View.OnClickListener;
46 import android.view.ViewGroup;
47 import android.widget.ImageButton;
48 import android.widget.LinearLayout;
49 import android.view.ViewGroup.LayoutParams;
50 import android.widget.TextView;
51 import android.widget.Toast;
54 public class OcctJniActivity extends Activity implements OnClickListener
57 //! Auxiliary method to print temporary info messages
58 public static void printShortInfo (Activity theActivity,
61 Context aCtx = theActivity.getApplicationContext();
62 Toast aToast = Toast.makeText (aCtx, theInfo, Toast.LENGTH_LONG);
66 //! Load single native library
67 private static boolean loadLibVerbose (String theLibName,
68 StringBuilder theLoadedInfo,
69 StringBuilder theFailedInfo)
73 System.loadLibrary (theLibName);
74 theLoadedInfo.append ("Info: native library \"");
75 theLoadedInfo.append (theLibName);
76 theLoadedInfo.append ("\" has been loaded\n");
79 catch (java.lang.UnsatisfiedLinkError theError)
81 theFailedInfo.append ("Error: native library \"");
82 theFailedInfo.append (theLibName);
83 theFailedInfo.append ("\" is unavailable:\n " + theError.getMessage());
86 catch (SecurityException theError)
88 theFailedInfo.append ("Error: native library \"");
89 theFailedInfo.append (theLibName);
90 theFailedInfo.append ("\" can not be loaded for security reasons:\n " + theError.getMessage());
95 public static boolean wasNativesLoadCalled = false;
96 public static boolean areNativeLoaded = false;
97 public static String nativeLoaded = "";
98 public static String nativeFailed = "";
100 //! Auxiliary method to load native libraries
101 public boolean loadNatives()
103 if (wasNativesLoadCalled)
105 return areNativeLoaded;
107 wasNativesLoadCalled = true;
108 StringBuilder aLoaded = new StringBuilder();
109 StringBuilder aFailed = new StringBuilder();
111 // copy OCCT resources
112 String aResFolder = getFilesDir().getAbsolutePath();
113 copyAssetFolder (getAssets(), "Shaders", aResFolder + "/Shaders");
114 copyAssetFolder (getAssets(), "SHMessage", aResFolder + "/SHMessage");
115 copyAssetFolder (getAssets(), "XSMessage", aResFolder + "/XSMessage");
116 copyAssetFolder (getAssets(), "TObj", aResFolder + "/TObj");
117 copyAssetFolder (getAssets(), "UnitsAPI", aResFolder + "/UnitsAPI");
120 loadLibVerbose ("gnustl_shared", aLoaded, aFailed);
123 loadLibVerbose ("freetype", aLoaded, aFailed);
124 loadLibVerbose ("freeimage", aLoaded, aFailed);
127 !loadLibVerbose ("TKernel", aLoaded, aFailed)
128 || !loadLibVerbose ("TKMath", aLoaded, aFailed)
129 || !loadLibVerbose ("TKG2d", aLoaded, aFailed)
130 || !loadLibVerbose ("TKG3d", aLoaded, aFailed)
131 || !loadLibVerbose ("TKGeomBase", aLoaded, aFailed)
132 || !loadLibVerbose ("TKBRep", aLoaded, aFailed)
133 || !loadLibVerbose ("TKGeomAlgo", aLoaded, aFailed)
134 || !loadLibVerbose ("TKTopAlgo", aLoaded, aFailed)
135 || !loadLibVerbose ("TKShHealing", aLoaded, aFailed)
136 || !loadLibVerbose ("TKMesh", aLoaded, aFailed)
138 || !loadLibVerbose ("TKPrim", aLoaded, aFailed)
139 || !loadLibVerbose ("TKBO", aLoaded, aFailed)
140 || !loadLibVerbose ("TKBool", aLoaded, aFailed)
141 || !loadLibVerbose ("TKFillet", aLoaded, aFailed)
142 || !loadLibVerbose ("TKOffset", aLoaded, aFailed)
143 || !loadLibVerbose ("TKXSBase", aLoaded, aFailed)
144 || !loadLibVerbose ("TKIGES", aLoaded, aFailed)
145 || !loadLibVerbose ("TKSTEPBase", aLoaded, aFailed)
146 || !loadLibVerbose ("TKSTEPAttr", aLoaded, aFailed)
147 || !loadLibVerbose ("TKSTEP209", aLoaded, aFailed)
148 || !loadLibVerbose ("TKSTEP", aLoaded, aFailed)
149 // OCCT Visualization
150 || !loadLibVerbose ("TKService", aLoaded, aFailed)
151 || !loadLibVerbose ("TKHLR", aLoaded, aFailed)
152 || !loadLibVerbose ("TKV3d", aLoaded, aFailed)
153 || !loadLibVerbose ("TKOpenGl", aLoaded, aFailed)
155 || !loadLibVerbose ("TKJniSample", aLoaded, aFailed))
157 nativeLoaded = aLoaded.toString();
158 nativeFailed = aFailed.toString();
159 areNativeLoaded = false;
160 //exitWithError (theActivity, "Broken apk?\n" + theFailedInfo);
163 nativeLoaded = aLoaded.toString();
164 areNativeLoaded = true;
169 @Override protected void onCreate (Bundle theBundle)
171 super.onCreate (theBundle);
173 boolean isLoaded = loadNatives();
176 printShortInfo (this, nativeFailed);
177 OcctJniLogger.postMessage (nativeLoaded + "\n" + nativeFailed);
180 setContentView (R.layout.activity_main);
182 myOcctView = (OcctJniView )findViewById (R.id.custom_view);
183 myMessageTextView = (TextView )findViewById (R.id.message_view);
184 OcctJniLogger.setTextView (myMessageTextView);
186 createViewAndButtons (Configuration.ORIENTATION_LANDSCAPE);
188 myButtonPreferSize = defineButtonSize ((LinearLayout )findViewById (R.id.panel_menu));
189 ImageButton aScrollBtn = (ImageButton )findViewById (R.id.scroll_btn);
190 aScrollBtn.setY (myButtonPreferSize);
191 aScrollBtn.setOnTouchListener (new View.OnTouchListener()
194 public boolean onTouch (View theView, MotionEvent theEvent)
196 return onScrollBtnTouch (theView, theEvent);
200 onConfigurationChanged (getResources().getConfiguration());
202 Intent anIntent = getIntent();
203 Uri aDataUrl = anIntent != null ? anIntent.getData() : null;
204 String aDataPath = aDataUrl != null ? aDataUrl.getPath() : "";
205 myOcctView.open (aDataPath);
206 myLastPath = aDataPath;
209 //! Handle scroll events
210 private boolean onScrollBtnTouch (View theView,
211 MotionEvent theEvent)
213 switch (theEvent.getAction())
215 case MotionEvent.ACTION_DOWN:
217 LinearLayout aPanelMenu = (LinearLayout )findViewById (R.id.panel_menu);
218 boolean isLandscape = (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE);
219 if (aPanelMenu.getVisibility() == View.VISIBLE)
221 aPanelMenu.setVisibility (View.GONE);
224 ((ImageButton )theView).setImageResource (R.drawable.open_p);
229 ((ImageButton )theView).setImageResource (R.drawable.open_l);
235 aPanelMenu.setVisibility (View.VISIBLE);
238 ((ImageButton )theView).setImageResource (R.drawable.close_p);
239 theView.setY (myButtonPreferSize);
243 ((ImageButton )theView).setImageResource (R.drawable.close_l);
244 theView.setX (myButtonPreferSize);
253 //! Initialize views and buttons
254 private void createViewAndButtons (int theOrientation)
257 ImageButton anOpenButton = (ImageButton )findViewById (R.id.open);
258 anOpenButton.setOnClickListener (this);
261 ImageButton aFitAllButton = (ImageButton )findViewById (R.id.fit);
262 aFitAllButton.setOnClickListener (this);
263 aFitAllButton.setOnTouchListener (new View.OnTouchListener()
266 public boolean onTouch (View theView, MotionEvent theEvent)
268 return onTouchButton (theView, theEvent);
273 ImageButton aMessageButton = (ImageButton )findViewById (R.id.message);
274 aMessageButton.setOnClickListener (this);
277 ImageButton anInfoButton = (ImageButton )findViewById (R.id.info);
278 anInfoButton.setOnClickListener (this);
280 // font for text view
281 TextView anInfoView = (TextView )findViewById (R.id.info_view);
282 anInfoView.setTextSize (TypedValue.COMPLEX_UNIT_SP, 18);
284 // add submenu buttons
285 createSubmenuBtn (R.id.view, R.id.view_group,
286 Arrays.asList (R.id.proj_front, R.id.proj_top, R.id.proj_left,
287 R.id.proj_back, R.id.proj_bottom, R.id.proj_right),
288 Arrays.asList (R.drawable.proj_front, R.drawable.proj_top, R.drawable.proj_left,
289 R.drawable.proj_back, R.drawable.proj_bottom, R.drawable.proj_right),
293 @Override protected void onNewIntent (Intent theIntent)
295 super.onNewIntent (theIntent);
296 setIntent (theIntent);
299 @Override protected void onDestroy()
302 OcctJniLogger.setTextView (null);
305 @Override protected void onPause()
308 myOcctView.onPause();
311 @Override protected void onResume()
314 myOcctView.onResume();
316 Intent anIntent = getIntent();
317 Uri aDataUrl = anIntent != null ? anIntent.getData() : null;
318 String aDataPath = aDataUrl != null ? aDataUrl.getPath() : "";
319 if (!aDataPath.equals (myLastPath))
321 myOcctView.open (aDataPath);
322 myLastPath = aDataPath;
326 //! Copy folder from assets
327 private boolean copyAssetFolder (AssetManager theAssetMgr,
328 String theAssetFolder,
329 String theFolderPathTo)
333 String[] aFiles = theAssetMgr.list (theAssetFolder);
334 File aFolder = new File (theFolderPathTo);
337 for (String aFileIter : aFiles)
339 if (aFileIter.contains ("."))
341 isOk &= copyAsset (theAssetMgr,
342 theAssetFolder + "/" + aFileIter,
343 theFolderPathTo + "/" + aFileIter);
347 isOk &= copyAssetFolder (theAssetMgr,
348 theAssetFolder + "/" + aFileIter,
349 theFolderPathTo + "/" + aFileIter);
354 catch (Exception theError)
356 theError.printStackTrace();
361 //! Copy single file from assets
362 private boolean copyAsset (AssetManager theAssetMgr,
368 InputStream aStreamIn = theAssetMgr.open (thePathFrom);
369 File aFileTo = new File (thePathTo);
370 aFileTo.createNewFile();
371 OutputStream aStreamOut = new FileOutputStream (thePathTo);
372 copyStreamContent (aStreamIn, aStreamOut);
380 catch (Exception theError)
382 theError.printStackTrace();
388 private static void copyStreamContent (InputStream theIn,
389 OutputStream theOut) throws IOException
391 byte[] aBuffer = new byte[1024];
392 int aNbReadBytes = 0;
393 while ((aNbReadBytes = theIn.read (aBuffer)) != -1)
395 theOut.write (aBuffer, 0, aNbReadBytes);
399 //! Show/hide text view
400 private void switchTextView (TextView theTextView,
401 ImageButton theClickedBtn,
402 boolean theToSwitchOn)
404 if (theTextView != null
405 && theTextView.getVisibility() == View.GONE
408 theTextView.setVisibility (View.VISIBLE);
409 theClickedBtn.setBackgroundColor (getResources().getColor(R.color.pressedBtnColor));
410 setTextViewPosition (theTextView);
414 theTextView.setVisibility (View.GONE);
415 theClickedBtn.setBackgroundColor (getResources().getColor (R.color.btnColor));
419 //! Setup text view position
420 private void setTextViewPosition (TextView theTextView)
422 if (theTextView.getVisibility() != View.VISIBLE)
427 if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE)
429 theTextView.setX (myButtonPreferSize);
430 theTextView.setY (0);
434 theTextView.setX (0);
435 theTextView.setY (myButtonPreferSize);
440 public void onClick (View theButton)
442 ImageButton aClickedBtn = (ImageButton )theButton;
443 switch (aClickedBtn.getId())
447 switchTextView ((TextView )findViewById (R.id.info_view),
448 (ImageButton )findViewById (R.id.info), false);
449 switchTextView (myMessageTextView, aClickedBtn, true);
454 String aText = getString (R.string.info_html);
455 aText = String.format (aText, cppOcctMajorVersion(), cppOcctMinorVersion(), cppOcctMicroVersion());
456 Spanned aSpanned = Html.fromHtml (aText, new ImageGetter()
459 public Drawable getDrawable (String theSource)
461 Resources aResources = getResources();
462 int anId = aResources.getIdentifier (theSource, "drawable", getPackageName());
463 Drawable aRes = aResources.getDrawable (anId);
464 aRes.setBounds (0, 0, aRes.getIntrinsicWidth(), aRes.getIntrinsicHeight());
469 TextView anInfoView = (TextView )findViewById (R.id.info_view);
470 anInfoView.setText (aSpanned);
471 switchTextView (myMessageTextView, (ImageButton ) findViewById (R.id.message), false);
472 switchTextView (anInfoView, aClickedBtn, true);
480 case R.id.proj_front:
482 myOcctView.setProj (OcctJniRenderer.TypeOfOrientation.Xpos);
487 myOcctView.setProj (OcctJniRenderer.TypeOfOrientation.Yneg);
492 myOcctView.setProj (OcctJniRenderer.TypeOfOrientation.Zpos);
497 myOcctView.setProj (OcctJniRenderer.TypeOfOrientation.Xneg);
500 case R.id.proj_right:
502 myOcctView.setProj (OcctJniRenderer.TypeOfOrientation.Ypos);
505 case R.id.proj_bottom:
507 myOcctView.setProj (OcctJniRenderer.TypeOfOrientation.Zneg);
512 File aPath = Environment.getExternalStorageDirectory();
513 aClickedBtn.setBackgroundColor (getResources().getColor(R.color.pressedBtnColor));
514 if (myFileOpenDialog == null)
516 myFileOpenDialog = new OcctJniFileDialog (this, aPath);
517 myFileOpenDialog.setFileEndsWith (".brep");
518 myFileOpenDialog.setFileEndsWith (".rle");
519 myFileOpenDialog.setFileEndsWith (".iges");
520 myFileOpenDialog.setFileEndsWith (".igs");
521 myFileOpenDialog.setFileEndsWith (".step");
522 myFileOpenDialog.setFileEndsWith (".stp");
523 myFileOpenDialog.addFileListener (new OcctJniFileDialog.FileSelectedListener()
525 public void fileSelected (File theFile)
527 if (theFile != null && myOcctView != null)
529 myOcctView.open (theFile.getPath());
533 myFileOpenDialog.addDialogDismissedListener (new OcctJniFileDialog.DialogDismissedListener()
536 public void dialogDismissed()
538 ImageButton openButton = (ImageButton )findViewById (R.id.open);
539 openButton.setBackgroundColor (getResources().getColor(R.color.btnColor));
543 myFileOpenDialog.showDialog();
549 private void createSubmenuBtn (int theParentBtnId,
550 int theParentLayoutId,
551 final List<Integer> theNewButtonIds,
552 final List<Integer> theNewButtonImageIds,
556 final ImageButton aParentBtn = (ImageButton )findViewById (theParentBtnId);
558 ViewGroup.LayoutParams aParams = null;
559 LinearLayout parentLayout = (LinearLayout ) findViewById (theParentLayoutId);
560 for (Integer newButtonId : theNewButtonIds)
562 ImageButton aNewButton = (ImageButton )findViewById (newButtonId);
563 if (aNewButton == null)
565 aNewButton = (ImageButton )new ImageButton (this);
566 aNewButton.setId (newButtonId);
567 aNewButton.setImageResource (theNewButtonImageIds.get (aPosInList));
568 aNewButton.setLayoutParams (aParams);
569 parentLayout.addView (aNewButton);
572 aNewButton.setOnClickListener (this);
573 aNewButton.setVisibility (View.GONE);
575 aNewButton.setOnTouchListener (new View.OnTouchListener()
578 public boolean onTouch (View theView, MotionEvent theEvent)
580 return onTouchButton (theView, theEvent);
586 if (aParentBtn != null)
588 aParentBtn.setOnTouchListener (null);
589 aParentBtn.setOnTouchListener (new View.OnTouchListener()
592 public boolean onTouch (View theView, MotionEvent theEvent)
594 if (theEvent.getAction () == MotionEvent.ACTION_DOWN)
596 Boolean isVisible = false;
597 for (Integer aNewButtonId : theNewButtonIds)
599 ImageButton anBtn = (ImageButton )findViewById (aNewButtonId);
602 if (anBtn.getVisibility() == View.GONE)
604 anBtn.setVisibility (View.VISIBLE);
609 anBtn.setVisibility (View.GONE);
613 aParentBtn.setBackgroundColor (!isVisible ? getResources().getColor(R.color.btnColor) : getResources().getColor(R.color.pressedBtnColor));
621 //! Implements onTouch functionality
622 private boolean onTouchButton (View theView,
623 MotionEvent theEvent)
625 switch (theEvent.getAction())
627 case MotionEvent.ACTION_DOWN:
628 ((ImageButton )theView).setBackgroundColor (getResources().getColor (R.color.pressedBtnColor));
630 case MotionEvent.ACTION_UP:
631 ((ImageButton )theView).setBackgroundColor (getResources().getColor (R.color.btnColor));
637 //! Handle configuration change event
639 public void onConfigurationChanged (Configuration theNewConfig)
641 super.onConfigurationChanged (theNewConfig);
642 LinearLayout aLayoutPanelMenu = (LinearLayout )findViewById (R.id.panel_menu);
643 LayoutParams aPanelMenuLayoutParams = aLayoutPanelMenu.getLayoutParams();
645 LinearLayout aLayoutViewGroup = (LinearLayout )findViewById (R.id.view_group);
646 LayoutParams aViewGroupLayoutParams = aLayoutViewGroup.getLayoutParams();
647 ImageButton aScrollBtn = (ImageButton )findViewById (R.id.scroll_btn);
648 LayoutParams aScrollBtnLayoutParams = aScrollBtn.getLayoutParams();
650 myButtonPreferSize = defineButtonSize ((LinearLayout )findViewById (R.id.panel_menu));
651 defineButtonSize ((LinearLayout )findViewById (R.id.view_group));
653 switch (theNewConfig.orientation)
655 case Configuration.ORIENTATION_PORTRAIT:
657 setHorizontal (aLayoutPanelMenu, aPanelMenuLayoutParams);
658 setHorizontal (aLayoutViewGroup, aViewGroupLayoutParams);
659 aLayoutViewGroup.setGravity (Gravity.BOTTOM);
661 aScrollBtnLayoutParams.height = LayoutParams.WRAP_CONTENT;
662 aScrollBtnLayoutParams.width = LayoutParams.MATCH_PARENT;
663 aScrollBtn.setLayoutParams (aScrollBtnLayoutParams);
664 if (aLayoutPanelMenu.getVisibility() == View.VISIBLE)
666 aScrollBtn.setImageResource (R.drawable.close_p);
667 aScrollBtn.setY (myButtonPreferSize);
672 aScrollBtn.setImageResource (R.drawable.open_p);
678 case Configuration.ORIENTATION_LANDSCAPE:
680 setVertical (aLayoutPanelMenu, aPanelMenuLayoutParams);
681 setVertical (aLayoutViewGroup, aViewGroupLayoutParams);
682 aLayoutViewGroup.setGravity (Gravity.RIGHT);
684 aScrollBtnLayoutParams.height = LayoutParams.MATCH_PARENT;
685 aScrollBtnLayoutParams.width = LayoutParams.WRAP_CONTENT;
686 aScrollBtn.setLayoutParams (aScrollBtnLayoutParams);
687 if (aLayoutPanelMenu.getVisibility() == View.VISIBLE)
689 aScrollBtn.setImageResource (R.drawable.close_l);
690 aScrollBtn.setX (myButtonPreferSize);
695 aScrollBtn.setImageResource (R.drawable.open_l);
702 setTextViewPosition (myMessageTextView);
703 setTextViewPosition ((TextView )findViewById (R.id.info_view));
706 private void setHorizontal (LinearLayout theLayout,
707 LayoutParams theLayoutParams)
709 theLayout.setOrientation (LinearLayout.HORIZONTAL);
710 theLayoutParams.height = LayoutParams.WRAP_CONTENT;
711 theLayoutParams.width = LayoutParams.MATCH_PARENT;
712 theLayout.setLayoutParams (theLayoutParams);
715 private void setVertical (LinearLayout theLayout,
716 LayoutParams theLayoutParams)
718 theLayout.setOrientation (LinearLayout.VERTICAL);
719 theLayoutParams.height = LayoutParams.MATCH_PARENT;
720 theLayoutParams.width = LayoutParams.WRAP_CONTENT;
721 theLayout.setLayoutParams (theLayoutParams);
724 //! Define button size
725 private int defineButtonSize (LinearLayout theLayout)
727 boolean isLandscape = getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
728 Display aDisplay = getWindowManager().getDefaultDisplay();
729 Point aDispPnt = new Point();
730 aDisplay.getSize (aDispPnt);
732 int aNbChildren = theLayout.getChildCount();
733 int aHeight = aDispPnt.y / aNbChildren;
734 int aWidth = aDispPnt.x / aNbChildren;
736 for (int aChildIter = 0; aChildIter < aNbChildren; ++aChildIter)
738 View aView = theLayout.getChildAt (aChildIter);
739 if (aView instanceof ImageButton)
741 ImageButton aButton = (ImageButton )aView;
744 aButton.setMinimumWidth (aHeight);
748 aButton.setMinimumHeight (aWidth);
754 aResultSize = aHeight;
758 aResultSize = aWidth;
763 //! OCCT major version
764 private native long cppOcctMajorVersion();
766 //! OCCT minor version
767 private native long cppOcctMinorVersion();
769 //! OCCT micro version
770 private native long cppOcctMicroVersion();
772 private OcctJniView myOcctView;
773 private TextView myMessageTextView;
774 private String myLastPath;
775 private OcctJniFileDialog myFileOpenDialog;
776 private int myButtonPreferSize = 65;