1 /***************************************************************************
2  *
3  * Project:  OpenCPN
4  * Purpose:  OpenCPN Android support utilities
5  * Author:   David Register
6  *
7  ***************************************************************************
8  *   Copyright (C) 2015 by David S. Register                               *
9  *                                                                         *
10  *   This program is free software; you can redistribute it and/or modify  *
11  *   it under the terms of the GNU General Public License as published by  *
12  *   the Free Software Foundation; either version 2 of the License, or     *
13  *   (at your option) any later version.                                   *
14  *                                                                         *
15  *   This program is distributed in the hope that it will be useful,       *
16  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
17  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
18  *   GNU General Public License for more details.                          *
19  *                                                                         *
20  *   You should have received a copy of the GNU General Public License     *
21  *   along with this program; if not, write to the                         *
22  *   Free Software Foundation, Inc.,                                       *
23  *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,  USA.         *
24  **************************************************************************/
25 #include "wx/wxprec.h"
26 
27 #ifndef  WX_PRECOMP
28 #include "wx/wx.h"
29 #endif //precompiled headers
30 
31 #include <wx/tokenzr.h>
32 #include <wx/aui/aui.h>
33 #include <wx/fontpicker.h>
34 #include <wx/filepicker.h>
35 #include <wx/zipstrm.h>
36 
37 #include <QtAndroidExtras/QAndroidJniObject>
38 
39 #include "config.h"
40 #include "dychart.h"
41 #include "androidUTIL.h"
42 #include "OCPN_DataStreamEvent.h"
43 #include "chart1.h"
44 #include "AISTargetQueryDialog.h"
45 #include "AISTargetAlertDialog.h"
46 #include "AISTargetListDialog.h"
47 #include "TrackPropDlg.h"
48 #include "S57QueryDialog.h"
49 #include "options.h"
50 #include "routemanagerdialog.h"
51 #include "chartdb.h"
52 #include "s52plib.h"
53 #include "s52utils.h"
54 #include "s52s57.h"
55 #include "navutil.h"
56 #include "TCWin.h"
57 #include "ocpn_plugin.h"
58 #include "about.h"
59 #include "OCPNPlatform.h"
60 #include "multiplexer.h"
61 #include "chartdbs.h"
62 #include "glChartCanvas.h"
63 #include "chcanv.h"
64 #include "MarkInfo.h"
65 #include "RoutePropDlgImpl.h"
66 #include "MUIBar.h"
67 #include "toolbar.h"
68 #include "NavObjectCollection.h"
69 #include "toolbar.h"
70 #include "iENCToolbar.h"
71 #include "Select.h"
72 #include "routeman.h"
73 #include "CanvasOptions.h"
74 #include "SerialDataStream.h"
75 
76 const wxString AndroidSuppLicense =
77 wxT("<br><br>The software included in this product contains copyrighted software that is licensed under the GPL.")
78 wxT("A copy of that license is shown above.")
79 wxT("You may obtain the complete Corresponding Source code from us for ")
80 wxT("a period of three years after our last shipment of this product, ")
81 wxT("by sending a money order or check for $5 to:<br><br>")
82 wxT("GPL Compliance Division<br>")
83 wxT("Dyad Inc.<br>")
84 wxT("31 Ocean Reef Dr<br>")
85 wxT("# C101-449<br>")
86 wxT("Key Largo, FL 33037-5282<br>")
87 wxT("United States<br><br>")
88 wxT("Please write “source for OpenCPN Version {insert version here} in the memo line of your payment.<br><br>");
89 
90 
91 #if !defined(NAN)
92 static const long long lNaN = 0xfff8000000000000;
93 #define NAN (*(double*)&lNaN)
94 #endif
95 
96 
97 
98 class androidUtilHandler;
99 
100 
101 
102 extern MyFrame                  *gFrame;
103 extern const wxEventType wxEVT_OCPN_DATASTREAM;
104 //extern const wxEventType wxEVT_DOWNLOAD_EVENT;
105 
106 wxEvtHandler                    *s_pAndroidNMEAMessageConsumer;
107 wxEvtHandler                    *s_pAndroidBTNMEAMessageConsumer;
108 
109 extern AISTargetAlertDialog      *g_pais_alert_dialog_active;
110 extern AISTargetQueryDialog      *g_pais_query_dialog_active;
111 extern AISTargetListDialog       *g_pAISTargetList;
112 //extern MarkInfoImpl              *pMarkPropDialog;
113 extern RoutePropDlgImpl          *pRoutePropDialog;
114 extern TrackPropDlg              *pTrackPropDialog;
115 extern S57QueryDialog            *g_pObjectQueryDialog;
116 extern options                   *g_options;
117 extern bool                       g_bSleep;
118 androidUtilHandler               *g_androidUtilHandler;
119 extern wxDateTime                 g_start_time;
120 extern RouteManagerDialog        *pRouteManagerDialog;
121 extern about                     *g_pAboutDlgLegacy;
122 extern bool                      g_bFullscreen;
123 extern OCPNPlatform              *g_Platform;
124 
125 // Static globals
126 extern ChartDB                   *ChartData;
127 extern MyConfig                  *pConfig;
128 
129 
130 //   Preferences globals
131 extern bool             g_bShowOutlines;
132 extern bool             g_bShowChartBar;
133 extern bool             g_bShowDepthUnits;
134 extern bool             g_bskew_comp;
135 extern bool             g_bopengl;
136 extern bool             g_bsmoothpanzoom;
137 extern bool             g_bShowMag;
138 extern double           g_UserVar;
139 extern int              g_chart_zoom_modifier;
140 extern int              g_NMEAAPBPrecision;
141 extern wxString         g_TalkerIdText;
142 
143 extern wxString         *pInit_Chart_Dir;
144 extern wxArrayOfConnPrm *g_pConnectionParams;
145 extern bool             g_bfilter_cogsog;
146 extern int              g_COGFilterSec;
147 extern int              g_SOGFilterSec;
148 
149 
150 extern bool             g_bDisplayGrid;
151 
152 //    AIS Global configuration
153 extern bool             g_bCPAMax;
154 extern double           g_CPAMax_NM;
155 extern bool             g_bCPAWarn;
156 extern double           g_CPAWarn_NM;
157 extern bool             g_bTCPA_Max;
158 extern double           g_TCPA_Max;
159 extern bool             g_bMarkLost;
160 extern double           g_MarkLost_Mins;
161 extern bool             g_bRemoveLost;
162 extern double           g_RemoveLost_Mins;
163 extern bool             g_bShowCOG;
164 extern double           g_ShowCOG_Mins;
165 extern bool             g_bAISShowTracks;
166 extern double           g_AISShowTracks_Mins;
167 extern bool             g_bHideMoored;
168 extern double           g_ShowMoored_Kts;
169 extern bool             g_bAIS_CPA_Alert;
170 extern bool             g_bAIS_CPA_Alert_Audio;
171 extern wxString         g_sAIS_Alert_Sound_File;
172 extern bool             g_bAIS_CPA_Alert_Suppress_Moored;
173 extern bool             g_bShowAreaNotices;
174 extern bool             g_bDrawAISSize;
175 extern bool             g_bShowAISName;
176 extern int              g_Show_Target_Name_Scale;
177 extern bool             g_bWplIsAprsPosition;
178 
179 extern int              g_iNavAidRadarRingsNumberVisible;
180 extern float            g_fNavAidRadarRingsStep;
181 extern int              g_pNavAidRadarRingsStepUnits;
182 extern int              g_iWaypointRangeRingsNumber;
183 extern float            g_fWaypointRangeRingsStep;
184 extern int              g_iWaypointRangeRingsStepUnits;
185 extern wxColour         g_colourWaypointRangeRingsColour;
186 extern bool             g_bWayPointPreventDragging;
187 
188 extern bool             g_bPreserveScaleOnX;
189 extern bool             g_bPlayShipsBells;
190 extern int              g_iSoundDeviceIndex;
191 extern bool             g_bFullscreenToolbar;
192 
193 extern int              g_OwnShipIconType;
194 extern double           g_n_ownship_length_meters;
195 extern double           g_n_ownship_beam_meters;
196 extern double           g_n_gps_antenna_offset_y;
197 extern double           g_n_gps_antenna_offset_x;
198 extern int              g_n_ownship_min_mm;
199 extern double           g_n_arrival_circle_radius;
200 
201 extern bool             g_bEnableZoomToCursor;
202 extern bool             g_bTrackDaily;
203 extern bool             g_bHighliteTracks;
204 extern double           g_TrackIntervalSeconds;
205 extern double           g_TrackDeltaDistance;
206 extern double           g_TrackDeltaDistance;
207 extern int              g_nTrackPrecision;
208 
209 extern int              g_iSDMMFormat;
210 extern int              g_iDistanceFormat;
211 extern int              g_iSpeedFormat;
212 
213 extern bool             g_bAdvanceRouteWaypointOnArrivalOnly;
214 
215 extern int              g_cm93_zoom_factor;
216 
217 extern int              g_COGAvgSec;
218 
219 extern bool             g_bCourseUp;
220 extern bool             g_bLookAhead;
221 
222 extern double           g_ownship_predictor_minutes;
223 extern double           g_ownship_HDTpredictor_miles;
224 extern double           gLat, gLon, gCog, gSog, gHdt, gVar;
225 
226 extern bool             g_bAISRolloverShowClass;
227 extern bool             g_bAISRolloverShowCOG;
228 extern bool             g_bAISRolloverShowCPA;
229 
230 extern bool             g_bAIS_ACK_Timeout;
231 extern double           g_AckTimeout_Mins;
232 
233 extern bool             g_bQuiltEnable;
234 extern bool             g_bFullScreenQuilt;
235 extern bool             g_bConfirmObjectDelete;
236 extern wxString         g_GPS_Ident;
237 extern bool             g_bGarminHostUpload;
238 
239 #if wxUSE_XLOCALE || !wxCHECK_VERSION(3,0,0)
240 extern wxLocale         *plocale_def_lang;
241 #endif
242 
243 //extern OCPN_Sound        g_anchorwatch_sound;
244 extern bool             g_bMagneticAPB;
245 
246 extern bool             g_fog_overzoom;
247 extern double           g_overzoom_emphasis_base;
248 extern bool             g_oz_vector_scale;
249 extern bool             g_bShowStatusBar;
250 
251 extern ocpnGLOptions    g_GLOptions;
252 
253 
254 extern s52plib          *ps52plib;
255 
256 extern wxString         g_locale;
257 extern bool             g_bportable;
258 extern bool             g_bdisable_opengl;
259 
260 extern ChartGroupArray  *g_pGroupArray;
261 
262 
263 extern bool             g_bUIexpert;
264 //    Some constants
265 #define ID_CHOICE_NMEA  wxID_HIGHEST + 1
266 
267 //extern wxArrayString *EnumerateSerialPorts(void);           // in chart1.cpp
268 
269 extern wxArrayString    TideCurrentDataSet;
270 extern wxString         g_TCData_Dir;
271 
272 extern AIS_Decoder      *g_pAIS;
273 extern bool             g_bserial_access_checked;
274 
275 extern options          *g_pOptions;
276 
277 extern bool             g_btouch;
278 extern bool             g_bresponsive;
279 extern bool             g_bAutoHideToolbar;
280 extern int              g_nAutoHideToolbar;
281 extern int              g_GUIScaleFactor;
282 extern int              g_ChartScaleFactor;
283 
284 extern double           g_config_display_size_mm;
285 extern float            g_ChartScaleFactorExp;
286 extern bool             g_config_display_size_manual;
287 
288 extern Multiplexer      *g_pMUX;
289 extern bool             b_inCloseWindow;
290 extern bool             g_config_display_size_manual;
291 extern MarkInfoDlg      *g_pMarkInfoDialog;
292 extern PlugInManager    *g_pi_manager;
293 extern iENCToolbar      *g_iENCToolbar;
294 extern int              g_iENCToolbarPosX;
295 extern int              g_iENCToolbarPosY;
296 extern ocpnFloatingToolbarDialog *g_MainToolbar;
297 extern int              g_maintoolbar_x;
298 extern int              g_maintoolbar_y;
299 extern long             g_maintoolbar_orient;
300 extern int              g_restore_stackindex;
301 extern int              g_restore_dbindex;
302 extern ChartStack       *pCurrentStack;
303 extern Select           *pSelect;
304 extern WayPointman      *pWayPointMan;
305 extern bool             g_bCruising;
306 extern RoutePoint       *pAnchorWatchPoint1;
307 extern RoutePoint       *pAnchorWatchPoint2;
308 extern bool             g_bAutoAnchorMark;
309 extern wxAuiManager     *g_pauimgr;
310 extern wxString         g_AisTargetList_perspective;
311 
312 extern ocpnFloatingToolbarDialog *g_MainToolbar;
313 
314 WX_DEFINE_ARRAY_PTR(ChartCanvas*, arrayofCanvasPtr);
315 extern arrayofCanvasPtr g_canvasArray;
316 
317 wxString callActivityMethod_vs(const char *method);
318 wxString callActivityMethod_is(const char *method, int parm);
319 
320 
321 //      Globals, accessible only to this module
322 
323 JavaVM *java_vm;
324 JNIEnv* global_jenv;
325 bool     b_androidBusyShown;
326 double   g_androidDPmm;
327 double   g_androidDensity;
328 
329 bool            g_bExternalApp;
330 
331 wxString        g_androidFilesDir;
332 wxString        g_androidCacheDir;
333 wxString        g_androidExtFilesDir;
334 wxString        g_androidExtCacheDir;
335 wxString        g_androidExtStorageDir;
336 
337 int             g_mask;
338 int             g_sel;
339 int             g_ActionBarHeight;
340 int             g_follow_state;
341 bool            g_track_active;
342 bool            bGPSEnabled;
343 
344 wxSize          config_size;
345 
346 bool            s_bdownloading;
347 wxString        s_requested_url;
348 wxEvtHandler    *s_download_evHandler;
349 bool            g_running;
350 bool            g_bstress1;
351 extern int      g_GUIScaleFactor;
352 
353 wxString        g_deviceInfo;
354 
355 int             s_androidMemTotal;
356 int             s_androidMemUsed;
357 bool            g_backEnabled;
358 bool            g_bFullscreenSave;
359 bool            s_optionsActive;
360 
361 extern int ShowNavWarning();
362 extern bool     g_btrackContinuous;
363 
364 int doAndroidPersistState();
365 
366 bool            bInConfigChange;
367 AudioDoneCallback s_soundCallBack;
368 
369 //      Some dummy devices to ensure plugins have static access to these classes not used elsewhere
370 wxFontPickerEvent       g_dummy_wxfpe;
371 
372 #define ANDROID_EVENT_TIMER 4389
373 #define ANDROID_STRESS_TIMER 4388
374 #define ANDROID_RESIZE_TIMER 4387
375 
376 #define ACTION_NONE                     -1
377 #define ACTION_RESIZE_PERSISTENTS       1
378 #define ACTION_FILECHOOSER_END          3
379 #define ACTION_COLORDIALOG_END          4
380 #define ACTION_POSTASYNC_END            5
381 
382 #define SCHEDULED_EVENT_CLEAN_EXIT      5498
383 
384 class androidUtilHandler : public wxEvtHandler
385 {
386  public:
387      androidUtilHandler();
~androidUtilHandler()388     ~androidUtilHandler() {}
389 
390     void onTimerEvent(wxTimerEvent &event);
391     void onStressTimer(wxTimerEvent &event);
392     void OnResizeTimer(wxTimerEvent &event);
393     void OnScheduledEvent( wxCommandEvent& event );
394 
GetStringResult()395     wxString GetStringResult(){ return m_stringResult; }
396 
397     wxTimer     m_eventTimer;
398     int         m_action;
399     bool        m_done;
400     wxString    m_stringResult;
401     wxTimer     m_stressTimer;
402     wxTimer     m_resizeTimer;
403     int         timer_sequence;
404     int         m_bskipConfirm;
405     DECLARE_EVENT_TABLE()
406 };
407 
408 const char  wxMessageBoxCaptionStr [] = "Message";
409 
410 
BEGIN_EVENT_TABLE(androidUtilHandler,wxEvtHandler)411 BEGIN_EVENT_TABLE ( androidUtilHandler, wxEvtHandler )
412 EVT_TIMER ( ANDROID_EVENT_TIMER, androidUtilHandler::onTimerEvent )
413 EVT_TIMER ( ANDROID_RESIZE_TIMER, androidUtilHandler::OnResizeTimer )
414 EVT_COMMAND(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, androidUtilHandler::OnScheduledEvent )
415 
416 END_EVENT_TABLE()
417 
418 androidUtilHandler::androidUtilHandler()
419 {
420     m_eventTimer.SetOwner( this, ANDROID_EVENT_TIMER );
421     m_stressTimer.SetOwner( this, ANDROID_STRESS_TIMER );
422     m_resizeTimer.SetOwner(this, ANDROID_RESIZE_TIMER);
423 
424     m_bskipConfirm = false;
425 
426     // We do a few little dummy class accesses here, to cause the static link to wxWidgets to bring in some
427     // class members required by some plugins, that would be missing otherwise.
428 
429     wxRegion a(0,0,1,1);
430     wxRegion b(0,0,2,2);
431     bool c = a.IsEqual(b);
432 
433     wxFilePickerCtrl *pfpc = new wxFilePickerCtrl();
434 
435     wxZipEntry *entry = new wxZipEntry();
436 
437 }
438 
439 
onTimerEvent(wxTimerEvent & event)440 void androidUtilHandler::onTimerEvent(wxTimerEvent &event)
441 {
442 //    qDebug() << "onTimerEvent";
443 
444     switch(m_action){
445         case ACTION_RESIZE_PERSISTENTS:            //  Handle rotation/resizing of persistent dialogs
446 
447             // AIS Target Query
448             if( g_pais_query_dialog_active ) {
449                 qDebug() << "AISB";
450 
451                 bool bshown = g_pais_query_dialog_active->IsShown();
452                 g_pais_query_dialog_active->Hide();
453                 g_pais_query_dialog_active->RecalculateSize();
454                 if(bshown){
455                     qDebug() << "AISC";
456                     g_pais_query_dialog_active->Show();
457                     g_pais_query_dialog_active->Raise();
458                 }
459             }
460 
461             // Route Props
462             if(RoutePropDlgImpl::getInstanceFlag()){
463                 bool bshown = pRoutePropDialog->IsShown();
464                 if(bshown){
465                     pRoutePropDialog->Hide();
466                     pRoutePropDialog->RecalculateSize();
467                     pRoutePropDialog->Show();
468                 }
469                 else{
470                     pRoutePropDialog->Destroy();
471                     pRoutePropDialog = NULL;
472                 }
473             }
474 
475             // Track Props
476             if(TrackPropDlg::getInstanceFlag()){
477                 bool bshown = pTrackPropDialog->IsShown();
478                 if(bshown){
479                     pTrackPropDialog->Hide();
480                     pTrackPropDialog->RecalculateSize();
481                     pTrackPropDialog->Show();
482                 }
483                 else{
484                     pTrackPropDialog->Destroy();
485                     pTrackPropDialog = NULL;
486                 }
487             }
488 
489             // Mark Props
490 
491             if(g_pMarkInfoDialog){
492                 bool bshown = g_pMarkInfoDialog->IsShown();
493                 g_pMarkInfoDialog->Hide();
494                 g_pMarkInfoDialog->RecalculateSize();
495                 if(bshown){
496                     if(g_pMarkInfoDialog->m_SaveDefaultDlg){
497                         g_pMarkInfoDialog->m_SaveDefaultDlg->Destroy();
498                         g_pMarkInfoDialog->m_SaveDefaultDlg = NULL;
499                     }
500                     g_pMarkInfoDialog->Show();
501                 }
502 
503             }
504 
505             // ENC Object Query
506             if(g_pObjectQueryDialog){
507                 bool bshown = g_pObjectQueryDialog->IsShown();
508                 g_pObjectQueryDialog->Hide();
509                 g_pObjectQueryDialog->RecalculateSize();
510                 if(bshown){
511                     g_pObjectQueryDialog->Show();
512                 }
513             }
514 
515 
516             // AIS Target List dialog
517             if(g_pAISTargetList){
518                 qDebug() << "ATLA";
519                 bool bshown = g_pAISTargetList->IsShown();
520                 g_pAISTargetList->Hide();
521                 g_pAISTargetList->RecalculateSize();
522                 if(bshown){
523                     qDebug() << "ATLB";
524                     g_pAISTargetList->Show();
525                     g_pAISTargetList->Raise();
526                 }
527             }
528 
529             // Tide/Current window
530             if( gFrame->GetPrimaryCanvas()->getTCWin()){
531                 bool bshown = gFrame->GetPrimaryCanvas()->getTCWin()->IsShown();
532                 gFrame->GetPrimaryCanvas()->getTCWin()->Hide();
533                 gFrame->GetPrimaryCanvas()->getTCWin()->RecalculateSize();
534                 if(bshown){
535                     gFrame->GetPrimaryCanvas()->getTCWin()->Show();
536                     gFrame->GetPrimaryCanvas()->getTCWin()->Refresh();
537                 }
538             }
539 
540             // Route Manager dialog
541             if(RouteManagerDialog::getInstanceFlag()){
542                 bool bshown = pRouteManagerDialog->IsShown();
543                 if(bshown){
544                     pRouteManagerDialog->Hide();
545                     pRouteManagerDialog->RecalculateSize();
546                     pRouteManagerDialog->Show();
547                 }
548                 else{
549                     pRouteManagerDialog->Destroy();
550                     pRouteManagerDialog = NULL;
551                 }
552 
553             }
554 
555             // About dialog
556             if(g_pAboutDlgLegacy){
557                 bool bshown = g_pAboutDlgLegacy->IsShown();
558                 if(bshown){
559                     g_pAboutDlgLegacy->Hide();
560                     g_pAboutDlgLegacy->RecalculateSize();
561                     g_pAboutDlgLegacy->Show();
562                 }
563             }
564 
565             bInConfigChange = false;
566 
567             break;
568 
569         case ACTION_FILECHOOSER_END:            //  Handle polling of android Dialog
570             {
571                 //qDebug() << "chooser poll";
572                 //  Get a reference to the running FileChooser
573                 QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative",
574                 "activity", "()Landroid/app/Activity;");
575 
576                 if ( !activity.isValid() ){
577                     //qDebug() << "onTimerEvent : Activity is not valid";
578                     return;
579                 }
580 
581                 //  Call the method which tracks the completion of the Intent.
582                 QAndroidJniObject data = activity.callObjectMethod("isFileChooserFinished", "()Ljava/lang/String;");
583 
584                 jstring s = data.object<jstring>();
585 
586                 JNIEnv* jenv;
587 
588                 //  Need a Java environment to decode the resulting string
589                 if (java_vm->GetEnv( (void **) &jenv, JNI_VERSION_1_6) != JNI_OK) {
590                     //qDebug() << "GetEnv failed.";
591                 }
592                 else {
593 
594                     // The string coming back will be one of:
595                     //  "no"   ......Intent not done yet.
596                     //  "cancel:"   .. user cancelled intent.
597                     //  "file:{file_name}"  .. user selected this file, fully qualified.
598                     if(!s){
599                         //qDebug() << "isFileChooserFinished returned null";
600                     }
601                     else if( (jenv)->GetStringLength( s )){
602                         const char *ret_string = (jenv)->GetStringUTFChars(s, NULL);
603 //                        qDebug() << "isFileChooserFinished returned " << ret_string;
604                         if( !strncmp(ret_string, "cancel:", 7) ){
605                             m_done = true;
606                             m_stringResult = _T("cancel:");
607                         }
608                         else if( !strncmp(ret_string, "file:", 5) ){
609                             m_done = true;
610                             m_stringResult = wxString(ret_string, wxConvUTF8);
611                         }
612                     }
613                 }
614 
615 
616                 break;
617             }
618 
619         case ACTION_COLORDIALOG_END:            //  Handle polling of android Dialog
620             {
621                 //qDebug() << "colorpicker poll";
622                 //  Get a reference to the running FileChooser
623                 QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative",
624                                                                                        "activity", "()Landroid/app/Activity;");
625 
626                 if ( !activity.isValid() ){
627                     //qDebug() << "onTimerEvent : Activity is not valid";
628                     return;
629                 }
630 
631                 //  Call the method which tracks the completion of the Intent.
632                 QAndroidJniObject data = activity.callObjectMethod("isColorPickerDialogFinished", "()Ljava/lang/String;");
633 
634                 jstring s = data.object<jstring>();
635 
636                 JNIEnv* jenv;
637 
638                 //  Need a Java environment to decode the resulting string
639                 if (java_vm->GetEnv( (void **) &jenv, JNI_VERSION_1_6) != JNI_OK) {
640                     //qDebug() << "GetEnv failed.";
641                 }
642                 else {
643 
644                     // The string coming back will be one of:
645                     //  "no"   ......Dialog not done yet.
646                     //  "cancel:"   .. user cancelled Dialog.
647                     //  "color: ".
648                     if(!s){
649                         qDebug() << "isColorPickerDialogFinished returned null";
650                     }
651                     else if( (jenv)->GetStringLength( s )){
652                         const char *ret_string = (jenv)->GetStringUTFChars(s, NULL);
653                         //qDebug() << "isColorPickerDialogFinished returned " << ret_string;
654                         if( !strncmp(ret_string, "cancel:", 7) ){
655                             m_done = true;
656                             m_stringResult = _T("cancel:");
657                         }
658                         else if( !strncmp(ret_string, "color:", 6) ){
659                             m_done = true;
660                             m_stringResult = wxString(ret_string, wxConvUTF8);
661                         }
662                     }
663                 }
664 
665 
666                 break;
667             }
668 
669         case ACTION_POSTASYNC_END:            //  Handle polling of android async POST task end
670             {
671                 //qDebug() << "colorpicker poll";
672                 //  Get a reference to the running FileChooser
673                 QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative",
674                                                                                        "activity", "()Landroid/app/Activity;");
675 
676                 if ( !activity.isValid() ){
677                     //qDebug() << "onTimerEvent : Activity is not valid";
678                     return;
679                 }
680 
681                 //  Call the method which tracks the completion of the POST async task.
682                 QAndroidJniObject data = activity.callObjectMethod("checkPostAsync", "()Ljava/lang/String;");
683 
684                 jstring s = data.object<jstring>();
685 
686                 JNIEnv* jenv;
687 
688                 //  Need a Java environment to decode the resulting string
689                 if (java_vm->GetEnv( (void **) &jenv, JNI_VERSION_1_6) != JNI_OK) {
690                     //qDebug() << "GetEnv failed.";
691                 }
692                 else {
693 
694                     // The string coming back will be either:
695                     //  "ACTIVE"   ......Post command not done yet.
696                     //  A valid XML response body.
697                     if(!s){
698                         qDebug() << "checkPostAsync returned null";
699                     }
700                     else if( (jenv)->GetStringLength( s )){
701                         const char *ret_string = (jenv)->GetStringUTFChars(s, NULL);
702                         qDebug() << "checkPostAsync returned " << ret_string;
703                         if( strncmp(ret_string, "ACTIVE", 6) ){         // Must be done....
704                             m_done = true;
705                             m_stringResult =  wxString(ret_string, wxConvUTF8);
706                         }
707                     }
708                 }
709 
710 
711                 break;
712             }
713 
714 
715         default:
716             break;
717     }
718 
719 }
720 
OnResizeTimer(wxTimerEvent & event)721 void androidUtilHandler::OnResizeTimer(wxTimerEvent &event)
722 {
723     if(timer_sequence == 0){
724     //  On QT, we need to clear the status bar item texts to prevent the status bar from
725     //  growing the parent frame due to unexpected width changes.
726 //         if( m_pStatusBar != NULL ){
727 //             int widths[] = { 2,2,2,2,2 };
728 //            m_pStatusBar->SetStatusWidths( m_StatusBarFieldCount, widths );
729 //
730 //             for(int i=0 ; i <  m_pStatusBar->GetFieldsCount() ; i++){
731 //                 m_pStatusBar->SetStatusText(_T(""), i);
732 //             }
733 //         }
734         qDebug() << "sequence 0";
735 
736         timer_sequence++;
737         //  This timer step needs to be long enough to allow Java induced size change to take effect
738         //  in another thread.
739         m_resizeTimer.Start(1000, wxTIMER_ONE_SHOT);
740         return;
741     }
742 
743 
744 
745     if(timer_sequence == 1){
746         qDebug() << "sequence 1" << config_size.x;
747         gFrame->SetSize(config_size);
748         timer_sequence++;
749         if(!m_bskipConfirm)
750             m_resizeTimer.Start(10, wxTIMER_ONE_SHOT);
751         m_bskipConfirm = false;
752         return;
753     }
754 
755     if(timer_sequence == 2){
756         timer_sequence++;
757         m_resizeTimer.Start(10, wxTIMER_ONE_SHOT);
758         return;
759     }
760 
761     if(timer_sequence == 3){
762         qDebug() << "sequence 3";
763         androidConfirmSizeCorrection();
764 
765         timer_sequence++;
766         m_resizeTimer.Start(10, wxTIMER_ONE_SHOT);
767         return;
768     }
769 
770     if(timer_sequence == 4){
771         qDebug() << "sequence 4";
772 
773         //  Raise the resized options dialog.
774         //  This has no effect if the dialog is not already shown.
775         if(g_options)
776             g_options->Raise();
777 
778         resizeAndroidPersistents();
779         return;
780     }
781 
782 }
783 
784 
785 
786 
787 
788 
789 
790 
791 
792 int stime;
793 
onStressTimer(wxTimerEvent & event)794 void androidUtilHandler::onStressTimer(wxTimerEvent &event){
795 
796     g_GUIScaleFactor = -5;
797     g_ChartScaleFactor = -5;
798     gFrame->SetGPSCompassScale();
799 
800     s_androidMemUsed  = 80;
801 
802     g_GLOptions.m_bTextureCompression = 0;
803     g_GLOptions.m_bTextureCompressionCaching = 0;
804 
805     if(600 == stime++) androidTerminate();
806 
807 }
808 
OnScheduledEvent(wxCommandEvent & event)809 void androidUtilHandler::OnScheduledEvent( wxCommandEvent& event )
810 {
811     switch( event.GetId() ){
812 
813         case SCHEDULED_EVENT_CLEAN_EXIT:
814 //             gFrame->FrameTimer1.Stop();
815 //             gFrame->FrameCOGTimer.Stop();
816 //
817 //             doAndroidPersistState();
818 //             androidTerminate();
819             break;
820 
821         case ID_CMD_TRIGGER_RESIZE:
822             qDebug() << "Trigger Resize";
823             timer_sequence = 0;
824             m_resizeTimer.Start(10, wxTIMER_ONE_SHOT);
825             bInConfigChange = true;
826             break;
827 
828         case ID_CMD_SOUND_FINISHED:
829             //qDebug() << "Trigger SoundFinished";
830             if(s_soundCallBack){
831                s_soundCallBack(0);              // No user data
832                s_soundCallBack = 0;
833             }
834             break;
835 
836 /*
837         case ID_CMD_STOP_RESIZE:
838            // Stop any underway timer chain
839             qDebug() << "Stop Resize";
840             m_resizeTimer.Stop();
841             m_eventTimer.Stop();
842             timer_sequence = 0;
843             bInConfigChange = false;
844             break;
845             */
846 
847 
848         default:
849             break;
850     }
851 }
852 
853 
854 
androidUtilInit(void)855 bool androidUtilInit( void )
856 {
857     qDebug() << "androidUtilInit()";
858 
859     g_androidUtilHandler = new androidUtilHandler();
860 
861     //  Initialize some globals
862 
863     s_androidMemTotal  = 100;
864     s_androidMemUsed  = 50;
865 
866     wxString dirs = callActivityMethod_vs("getSystemDirs");
867     qDebug() << "dirs: " << dirs.mb_str();
868 
869     wxStringTokenizer tk(dirs, _T(";"));
870     if( tk.HasMoreTokens() ){
871         wxString token = tk.GetNextToken();
872         if(wxNOT_FOUND != token.Find(_T("EXTAPP")))
873             g_bExternalApp = true;
874 
875         token = tk.GetNextToken();
876         g_androidFilesDir = token;              // used for "home dir"
877         token = tk.GetNextToken();
878         g_androidCacheDir = token;
879         token = tk.GetNextToken();
880         g_androidExtFilesDir = token;           // used as PrivateDataDir, "/storage/emulated/0/Android/data/org.opencpn.opencpn/files"
881                                                 // if app has been moved to sdcard, this gives like (on Android 6)
882                                                 // /storage/2385-1BF8/Android/data/org.opencpn.opencpn/files
883         token = tk.GetNextToken();
884         g_androidExtCacheDir = token;
885         token = tk.GetNextToken();
886         g_androidExtStorageDir = token;
887 
888     }
889 
890     g_mask = -1;
891     g_sel = -1;
892 
893 
894     wxStringTokenizer tku(g_androidExtFilesDir, _T("/") );
895     while( tku.HasMoreTokens() )
896     {
897         wxString s1 = tku.GetNextToken();
898 
899         if(s1.Find(_T("org.")) != wxNOT_FOUND){
900             if(s1 != _T("org.opencpn.opencpn") ) g_bstress1 = true;
901         }
902     }
903 
904     if(g_bstress1){
905         g_androidUtilHandler->Connect( g_androidUtilHandler->m_stressTimer.GetId(), wxEVT_TIMER, wxTimerEventHandler( androidUtilHandler::onStressTimer ), NULL, g_androidUtilHandler );
906         g_androidUtilHandler->m_stressTimer.Start(1000, wxTIMER_CONTINUOUS);
907     }
908 
909     return true;
910 }
911 
912 
getAndroidConfigSize()913 wxSize getAndroidConfigSize()
914 {
915     return config_size;
916 }
917 
resizeAndroidPersistents()918 void resizeAndroidPersistents()
919 {
920 
921      if(g_androidUtilHandler){
922          g_androidUtilHandler->m_action = ACTION_RESIZE_PERSISTENTS;
923          g_androidUtilHandler->m_eventTimer.Start(100, wxTIMER_ONE_SHOT);
924      }
925 }
926 
JNI_OnLoad(JavaVM * vm,void * reserved)927 jint JNI_OnLoad(JavaVM *vm, void *reserved)
928 {
929     java_vm = vm;
930 
931     // Get JNI Env for all function calls
932     if (vm->GetEnv( (void **) &global_jenv, JNI_VERSION_1_6) != JNI_OK) {
933         return -1;
934     }
935 
936     return JNI_VERSION_1_6;
937 }
938 
sendNMEAMessageEvent(wxString & msg)939 void sendNMEAMessageEvent(wxString &msg)
940 {
941     wxCharBuffer abuf = msg.ToUTF8();
942     if( abuf.data() ){                            // OK conversion?
943         std::string s(abuf.data());
944 //    qDebug() << tstr;
945         OCPN_DataStreamEvent Nevent(wxEVT_OCPN_DATASTREAM, 0);
946         Nevent.SetNMEAString( s );
947         Nevent.SetStream( NULL );
948         if(s_pAndroidNMEAMessageConsumer)
949             s_pAndroidNMEAMessageConsumer->AddPendingEvent(Nevent);
950     }
951 }
952 
953 
954 
955 //      OCPNNativeLib
956 //      This is a set of methods which can be called from the android activity context.
957 
958 extern "C"{
Java_org_opencpn_OCPNNativeLib_test(JNIEnv * env,jobject obj)959 JNIEXPORT jint JNICALL Java_org_opencpn_OCPNNativeLib_test(JNIEnv *env, jobject obj)
960 {
961     //qDebug() << "test";
962 
963     return 55;
964 }
965 }
966 
967 extern "C"{
Java_org_opencpn_OCPNNativeLib_processSailTimer(JNIEnv * env,jobject obj,double WindAngleMagnetic,double WindSpeedKnots)968     JNIEXPORT jint JNICALL Java_org_opencpn_OCPNNativeLib_processSailTimer(JNIEnv *env, jobject obj, double WindAngleMagnetic, double WindSpeedKnots)
969     {
970         //  The NMEA message target handler may not be setup yet, if no connections are defined or enabled.
971         //  But we may want to synthesize messages from the Java app, even without a definite connection, and we want to process these messages too.
972         //  So assume that the global MUX, if present, will handle these synthesized messages.
973         if( !s_pAndroidNMEAMessageConsumer && g_pMUX )
974             s_pAndroidNMEAMessageConsumer = g_pMUX;
975 
976 
977         double wind_angle_mag = 0;
978         double apparent_wind_angle = 0;
979 
980         double app_windSpeed = 0;
981         double true_windSpeed = 0;
982         double true_windDirection = 0;
983 
984         {
985             {
986 
987                 // Need to correct the Magnetic wind angle to True
988                 // TODO  Punt for mow
989                 double variation = gVar;
990                 //qDebug() << "gVar" << gVar;
991 
992                 //  What to use for TRUE ownship head?
993                 //TODO Look for HDT message contents, if available
994                 double osHead = gCog;
995                 bool buseCOG = true;
996                 //qDebug() << "gHdt" << gHdt;
997 
998                 if( !wxIsNaN(gHdt) ){
999                     osHead = gHdt;
1000                     buseCOG = false;
1001                 }
1002 
1003                 // What SOG to use?
1004                 double osSog = gSog;
1005 
1006                 wind_angle_mag = WindAngleMagnetic;
1007                 app_windSpeed = WindSpeedKnots;
1008 
1009                 // Compute the apparent wind angle
1010                 // If using gCog for ownship head, require speed to be > 0.2 knots
1011                 // If not useing cGog for head, assume we must be using a true heading sensor, so always valid
1012                 if( !wxIsNaN(osHead) && ( (!buseCOG)  ||  (buseCOG && osSog > 0.2) ) ){
1013                     apparent_wind_angle = wind_angle_mag - (osHead - variation);
1014                 }
1015                 else{
1016                     apparent_wind_angle = 0;
1017                 }
1018                 if(apparent_wind_angle < 0)
1019                     apparent_wind_angle += 360.;
1020                 if(apparent_wind_angle > 360.)
1021                     apparent_wind_angle -= 360.;
1022 
1023 
1024                 //  Using the "Law of cosines", compute the true wind speed
1025                 if( !wxIsNaN(osSog) ){
1026                         true_windSpeed = sqrt( (osSog * osSog) + (app_windSpeed * app_windSpeed) - (2 * osSog * app_windSpeed * cos(apparent_wind_angle * PI / 180.)) );
1027                 }
1028                 else{
1029                         true_windSpeed = app_windSpeed;
1030                 }
1031 
1032                     // Rearranging the Law of cosines, we calculate True Wind Direction
1033                 if( ( !wxIsNaN(osSog) ) && ( !wxIsNaN(osHead) )  && ( osSog > 0.2)  &&  (true_windSpeed > 1) ){
1034                         double acosTW = ((osSog * osSog) + (true_windSpeed * true_windSpeed) - (app_windSpeed * app_windSpeed)) / (2 * osSog * true_windSpeed);
1035 
1036                         double twd0 = acos( acosTW) *  ( 180. / PI );
1037 
1038                         // OK on the beat...
1039                         if(apparent_wind_angle > 180.){
1040                             true_windDirection = osHead + 180 + twd0;
1041                         }
1042                         else{
1043                             true_windDirection = osHead + 180 - twd0;
1044                         }
1045                 }
1046                 else{
1047                         true_windDirection = wind_angle_mag + variation;
1048                 }
1049 
1050                 if(true_windDirection < 0)
1051                         true_windDirection += 360.;
1052                 if(true_windDirection > 360.)
1053                         true_windDirection -= 360.;
1054 
1055                 //qDebug() << wind_angle_mag << app_windSpeed << apparent_wind_angle << true_windSpeed << true_windDirection;
1056 
1057                 if( s_pAndroidNMEAMessageConsumer ) {
1058 
1059                         NMEA0183        parser;
1060 
1061                         // Now make some NMEA messages
1062                         // We dont want to pass the incoming MWD message thru directly, since it is not really correct.  The angle is correct, but the speed is relative.
1063                         //  Make a new MWD sentence with calculated values
1064                         parser.TalkerID = _T("OS");
1065 
1066                         // MWD
1067                         SENTENCE sntd;
1068                         parser.Mwd.WindAngleTrue = true_windDirection;
1069                         parser.Mwd.WindAngleMagnetic = wind_angle_mag;
1070                         parser.Mwd.WindSpeedKnots = true_windSpeed;
1071                         parser.Mwd.WindSpeedms = true_windSpeed * 0.5144;           // convert kts to m/s
1072                         parser.Mwd.Write( sntd );
1073                         sendNMEAMessageEvent(sntd.Sentence);
1074 
1075                         // Now make two MWV sentences
1076                         // Apparent
1077                         SENTENCE snt;
1078                         parser.Mwv.WindAngle = apparent_wind_angle;
1079                         parser.Mwv.WindSpeed = app_windSpeed;
1080                         parser.Mwv.WindSpeedUnits = _T("N");
1081                         parser.Mwv.Reference = _T("R");
1082                         parser.Mwv.IsDataValid = NTrue;
1083                         parser.Mwv.Write( snt );
1084                         sendNMEAMessageEvent(snt.Sentence);
1085 
1086                         // True
1087                         SENTENCE sntt;
1088                         double true_relHead = 0;
1089                         if( !wxIsNaN(osHead) && ( (!buseCOG)  ||  (buseCOG && osSog > 0.2) ) )
1090                             true_relHead = true_windDirection - osHead;
1091 
1092                         if(true_relHead < 0)
1093                             true_relHead += 360.;
1094                         if(true_relHead > 360.)
1095                             true_relHead -= 360.;
1096 
1097                         parser.Mwv.WindAngle = true_relHead;
1098                         parser.Mwv.WindSpeed = true_windSpeed;
1099                         parser.Mwv.WindSpeedUnits = _T("N");
1100                         parser.Mwv.Reference = _T("T");
1101                         parser.Mwv.IsDataValid = NTrue;
1102                         parser.Mwv.Write( sntt );
1103                         sendNMEAMessageEvent(sntt.Sentence);
1104 
1105                 }
1106             }
1107         }
1108 
1109         return 52;
1110     }
1111 }
1112 
1113 extern "C"{
Java_org_opencpn_OCPNNativeLib_processNMEA(JNIEnv * env,jobject obj,jstring nmea_string)1114     JNIEXPORT jint JNICALL Java_org_opencpn_OCPNNativeLib_processNMEA(JNIEnv *env, jobject obj, jstring nmea_string)
1115     {
1116         //  The NMEA message target handler may not be setup yet, if no connections are defined or enabled.
1117         //  But we may get synthesized messages from the Java app, even without a definite connection, and we want to process these messages too.
1118         //  So assume that the global MUX, if present, will handle these messages.
1119         wxEvtHandler  *consumer = s_pAndroidNMEAMessageConsumer;
1120 
1121         if( !consumer && g_pMUX )
1122             consumer = g_pMUX;
1123 
1124 
1125         const char *string = env->GetStringUTFChars(nmea_string, NULL);
1126 
1127         //qDebug() << "ProcessNMEA: " << string;
1128 
1129         char tstr[200];
1130         strncpy(tstr, string, 190);
1131         strcat(tstr, "\r\n");
1132 
1133         if( consumer ) {
1134             OCPN_DataStreamEvent Nevent(wxEVT_OCPN_DATASTREAM, 0);
1135             Nevent.SetNMEAString( tstr );
1136             Nevent.SetStream( NULL );
1137 
1138             consumer->AddPendingEvent(Nevent);
1139         }
1140 
1141         return 66;
1142     }
1143 }
1144 
1145 extern "C"{
Java_org_opencpn_OCPNNativeLib_processBTNMEA(JNIEnv * env,jobject obj,jstring nmea_string)1146     JNIEXPORT jint JNICALL Java_org_opencpn_OCPNNativeLib_processBTNMEA(JNIEnv *env, jobject obj, jstring nmea_string)
1147     {
1148         const char *string = env->GetStringUTFChars(nmea_string, NULL);
1149         wxString wstring = wxString(string, wxConvUTF8);
1150 
1151         char tstr[200];
1152         strncpy(tstr, string, 190);
1153         strcat(tstr, "\r\n");
1154 
1155         if( s_pAndroidBTNMEAMessageConsumer ) {
1156             OCPN_DataStreamEvent Nevent(wxEVT_OCPN_DATASTREAM, 0);
1157             Nevent.SetNMEAString( tstr );
1158             Nevent.SetStream( NULL );
1159 
1160             s_pAndroidBTNMEAMessageConsumer->AddPendingEvent(Nevent);
1161         }
1162 
1163         return 77;
1164     }
1165 }
1166 
1167 
1168 extern "C"{
Java_org_opencpn_OCPNNativeLib_onConfigChange(JNIEnv * env,jobject obj)1169     JNIEXPORT jint JNICALL Java_org_opencpn_OCPNNativeLib_onConfigChange(JNIEnv *env, jobject obj)
1170     {
1171         qDebug() << "onConfigChange";
1172 
1173         wxLogMessage(_T("onConfigChange"));
1174         GetAndroidDisplaySize();
1175 
1176         wxSize new_size = getAndroidDisplayDimensions();
1177         qDebug() << "NewSize: " << new_size.x << new_size.y;
1178         config_size = new_size;
1179 
1180 //         wxCommandEvent evts(wxEVT_COMMAND_MENU_SELECTED);
1181 //         evts.SetId( ID_CMD_STOP_RESIZE );
1182 //             g_androidUtilHandler->AddPendingEvent(evts);
1183 
1184         wxCommandEvent evt(wxEVT_COMMAND_MENU_SELECTED);
1185         evt.SetId( ID_CMD_TRIGGER_RESIZE );
1186             g_androidUtilHandler->AddPendingEvent(evt);
1187 
1188 
1189         return 77;
1190     }
1191 }
1192 
1193 extern "C"{
Java_org_opencpn_OCPNNativeLib_onMouseWheel(JNIEnv * env,jobject obj,int dir)1194     JNIEXPORT jint JNICALL Java_org_opencpn_OCPNNativeLib_onMouseWheel(JNIEnv *env, jobject obj, int dir)
1195     {
1196 
1197         wxMouseEvent evt(wxEVT_MOUSEWHEEL);
1198         evt.m_wheelRotation = dir;
1199 
1200         if(gFrame->GetPrimaryCanvas()){
1201             gFrame->GetPrimaryCanvas()->GetEventHandler()->AddPendingEvent(evt);
1202         }
1203 
1204         return 77;
1205     }
1206 }
1207 
1208 extern "C"{
Java_org_opencpn_OCPNNativeLib_onMenuKey(JNIEnv * env,jobject obj)1209     JNIEXPORT jint JNICALL Java_org_opencpn_OCPNNativeLib_onMenuKey(JNIEnv *env, jobject obj)
1210     {
1211 
1212 //         if(g_MainToolbar){
1213 //             g_MainToolbar->Show( !g_MainToolbar->IsShown() );
1214 //         }
1215 
1216         return 88;
1217     }
1218 }
1219 
1220 extern "C"{
Java_org_opencpn_OCPNNativeLib_onStop(JNIEnv * env,jobject obj)1221     JNIEXPORT jint JNICALL Java_org_opencpn_OCPNNativeLib_onStop(JNIEnv *env, jobject obj)
1222     {
1223         qDebug() << "onStop";
1224         wxLogMessage(_T("onStop"));
1225 
1226 
1227         //  App may be summarily killed after this point due to OOM condition.
1228         //  So we need to persist some dynamic data.
1229         if(pConfig){
1230 
1231         //  Persist the config file, especially to capture the viewport location,scale etc.
1232             pConfig->UpdateSettings();
1233 
1234         //  There may be unsaved objects at this point, and a navobj.xml.changes restore file
1235         //  We commit the navobj deltas, and flush the restore file
1236         //  Pass flag "true" to also recreate a new empty "changes" file
1237             pConfig->UpdateNavObj( true );
1238 
1239         }
1240 
1241         g_running = false;
1242 
1243         qDebug() << "onStop return 98";
1244         return 98;
1245     }
1246 }
1247 
1248 extern "C"{
Java_org_opencpn_OCPNNativeLib_onStart(JNIEnv * env,jobject obj)1249     JNIEXPORT jint JNICALL Java_org_opencpn_OCPNNativeLib_onStart(JNIEnv *env, jobject obj)
1250     {
1251         qDebug() << "onStart";
1252         wxLogMessage(_T("onStart"));
1253 
1254         if(g_bstress1) ShowNavWarning();
1255 
1256         g_running = true;
1257 
1258         return 99;
1259     }
1260 }
1261 
1262 extern "C"{
Java_org_opencpn_OCPNNativeLib_onPause(JNIEnv * env,jobject obj)1263     JNIEXPORT jint JNICALL Java_org_opencpn_OCPNNativeLib_onPause(JNIEnv *env, jobject obj)
1264     {
1265         qDebug() << "onPause";
1266         wxLogMessage(_T("onPause"));
1267         g_bSleep = true;
1268 
1269         callActivityMethod_is("setTrackContinuous", (int)g_btrackContinuous);
1270 
1271         if(!g_btrackContinuous)
1272             androidGPSService( GPS_OFF );
1273 
1274         return 97;
1275     }
1276 }
1277 
1278 extern "C"{
Java_org_opencpn_OCPNNativeLib_onResume(JNIEnv * env,jobject obj)1279     JNIEXPORT jint JNICALL Java_org_opencpn_OCPNNativeLib_onResume(JNIEnv *env, jobject obj)
1280     {
1281         qDebug() << "onResume";
1282         wxLogMessage(_T("onResume"));
1283 
1284         int ret = 96;
1285 
1286         g_bSleep = false;
1287 
1288         if(bGPSEnabled)
1289             androidGPSService( GPS_ON );
1290 
1291         wxCommandEvent evt0(wxEVT_COMMAND_MENU_SELECTED);
1292         evt0.SetId( ID_CMD_CLOSE_ALL_DIALOGS );
1293         if(gFrame && gFrame->GetEventHandler())
1294             gFrame->GetEventHandler()->AddPendingEvent(evt0);
1295 
1296         wxCommandEvent evt(wxEVT_COMMAND_MENU_SELECTED);
1297         evt.SetId( ID_CMD_INVALIDATE );
1298         if(gFrame)
1299             gFrame->GetEventHandler()->AddPendingEvent(evt);
1300 
1301         //  Check screen orientation is sensible
1302         int orient = androidGetScreenOrientation();
1303         qDebug() << "Orient: " << orient;
1304         if(gFrame && gFrame->GetPrimaryCanvas()){
1305             qDebug() << "Size: " << gFrame->GetSize().x << gFrame->GetSize().y;
1306             qDebug() << "CanvasSize: " << gFrame->GetPrimaryCanvas()->GetSize().x << gFrame->GetPrimaryCanvas()->GetSize().y;
1307 
1308             if(gFrame->GetSize().y > gFrame->GetSize().x){
1309                 qDebug() << "gFrame is Portrait";
1310                 if((orient == 2) || (orient == 4)){
1311                     qDebug() << "NEEDS RESIZE";
1312                     GetAndroidDisplaySize();
1313                     wxSize new_size = getAndroidDisplayDimensions();
1314                     qDebug() << "NewSize: " << new_size.x << new_size.y;
1315                     config_size = new_size;
1316 
1317                     wxCommandEvent evt(wxEVT_COMMAND_MENU_SELECTED);
1318                     evt.SetId( ID_CMD_TRIGGER_RESIZE );
1319                     if(g_androidUtilHandler)
1320                         g_androidUtilHandler->AddPendingEvent(evt);
1321                 }
1322             }
1323             else{
1324                 qDebug() << "gFrame is Landscape";
1325                 if((orient == 1) || (orient == 3)){
1326                     qDebug() << "NEEDS RESIZE";
1327                     GetAndroidDisplaySize();
1328                     wxSize new_size = getAndroidDisplayDimensions();
1329                     qDebug() << "NewSize: " << new_size.x << new_size.y;
1330                     config_size = new_size;
1331 
1332                     wxCommandEvent evt(wxEVT_COMMAND_MENU_SELECTED);
1333                     evt.SetId( ID_CMD_TRIGGER_RESIZE );
1334                     if(g_androidUtilHandler)
1335                         g_androidUtilHandler->AddPendingEvent(evt);
1336                 }
1337             }
1338         }
1339 
1340         return ret;
1341     }
1342 }
1343 
1344 extern "C"{
Java_org_opencpn_OCPNNativeLib_onDestroy(JNIEnv * env,jobject obj)1345     JNIEXPORT jint JNICALL Java_org_opencpn_OCPNNativeLib_onDestroy(JNIEnv *env, jobject obj)
1346     {
1347         qDebug() << "onDestroy";
1348         wxLogMessage(_T("onDestroy"));
1349 
1350         if(pConfig){
1351             //  Persist the config file, especially to capture the viewport location,scale, locale etc.
1352             pConfig->UpdateSettings();
1353         }
1354 
1355         g_running = false;
1356 
1357         return 98;
1358     }
1359 }
1360 
1361 extern "C"{
Java_org_opencpn_OCPNNativeLib_selectChartDisplay(JNIEnv * env,jobject obj,int type,int family)1362     JNIEXPORT jint JNICALL Java_org_opencpn_OCPNNativeLib_selectChartDisplay(JNIEnv *env, jobject obj, int type, int family)
1363     {
1364 
1365         wxCommandEvent evt(wxEVT_COMMAND_MENU_SELECTED);
1366         if(type == CHART_TYPE_CM93COMP){
1367             evt.SetId( ID_CMD_SELECT_CHART_TYPE );
1368             evt.SetExtraLong( CHART_TYPE_CM93COMP);
1369         }
1370         else{
1371             evt.SetId( ID_CMD_SELECT_CHART_FAMILY );
1372             evt.SetExtraLong( family);
1373         }
1374 
1375         if(gFrame)
1376             gFrame->GetEventHandler()->AddPendingEvent(evt);
1377 
1378         return 74;
1379     }
1380 }
1381 
1382 extern "C"{
Java_org_opencpn_OCPNNativeLib_invokeCmdEventCmdString(JNIEnv * env,jobject obj,int cmd_id,jstring s)1383     JNIEXPORT jint JNICALL Java_org_opencpn_OCPNNativeLib_invokeCmdEventCmdString(JNIEnv *env, jobject obj, int cmd_id, jstring s)
1384     {
1385          const char *sparm;
1386         wxString wx_sparm;
1387         JNIEnv* jenv;
1388 
1389         //  Need a Java environment to decode the string parameter
1390         if (java_vm->GetEnv( (void **) &jenv, JNI_VERSION_1_6) != JNI_OK) {
1391             //qDebug() << "GetEnv failed.";
1392         }
1393         else {
1394             sparm = (jenv)->GetStringUTFChars(s, NULL);
1395             wx_sparm = wxString(sparm, wxConvUTF8);
1396         }
1397 
1398         //qDebug() << "invokeCmdEventCmdString" << cmd_id << s;
1399 
1400         wxCommandEvent evt(wxEVT_COMMAND_MENU_SELECTED);
1401         evt.SetId( cmd_id );
1402         evt.SetString( wx_sparm);
1403 
1404         if(gFrame){
1405             qDebug() << "add event" << cmd_id << wx_sparm.mbc_str();
1406             gFrame->GetEventHandler()->AddPendingEvent(evt);
1407         }
1408         else
1409             qDebug() << "No frame for EventCmdString";
1410 
1411 
1412         return 71;
1413     }
1414 }
1415 
1416 
1417 extern "C"{
Java_org_opencpn_OCPNNativeLib_invokeMenuItem(JNIEnv * env,jobject obj,int item)1418     JNIEXPORT jint JNICALL Java_org_opencpn_OCPNNativeLib_invokeMenuItem(JNIEnv *env, jobject obj, int item)
1419     {
1420         if(!gFrame)                     // App Frame not yet set up, on slow devices
1421             return 71;
1422 
1423         wxString msg1;
1424         msg1.Printf(_T("invokeMenuItem: %d"), item);
1425         wxLogMessage(msg1);
1426 
1427         // If in Route Create, disable all other menu items
1428          if( gFrame && (gFrame->GetFocusCanvas()->m_routeState > 1 ) && (OCPN_ACTION_ROUTE != item) ) {
1429              wxLogMessage(_T("invokeMenuItem A"));
1430              return 72;
1431          }
1432 
1433         wxLogMessage(_T("invokeMenuItem B"));
1434 
1435         wxCommandEvent evt(wxEVT_COMMAND_MENU_SELECTED);
1436 
1437         switch(item){
1438             case OCPN_ACTION_FOLLOW:
1439                 evt.SetId( ID_MENU_NAV_FOLLOW );
1440                 gFrame->GetEventHandler()->AddPendingEvent(evt);
1441                 break;
1442 
1443             case OCPN_ACTION_ROUTE:
1444                 evt.SetId( ID_MENU_ROUTE_NEW );
1445                 gFrame->GetEventHandler()->AddPendingEvent(evt);
1446                 break;
1447 
1448             case OCPN_ACTION_RMD:
1449                 evt.SetId( ID_MENU_ROUTE_MANAGER );
1450                 gFrame->GetEventHandler()->AddPendingEvent(evt);
1451                 break;
1452 
1453             case OCPN_ACTION_SETTINGS_BASIC:
1454                 evt.SetId( ID_MENU_SETTINGS_BASIC );
1455                 wxLogMessage(_T("invokeMenuItem OCPN_ACTION_SETTINGS_BASIC"));
1456                 gFrame->GetEventHandler()->AddPendingEvent(evt);
1457                 break;
1458 
1459             case OCPN_ACTION_TRACK_TOGGLE:
1460                 evt.SetId( ID_MENU_NAV_TRACK );
1461                 gFrame->GetEventHandler()->AddPendingEvent(evt);
1462                 break;
1463 
1464             case OCPN_ACTION_MOB:
1465                 evt.SetId( ID_MENU_MARK_MOB );
1466                 gFrame->GetEventHandler()->AddPendingEvent(evt);
1467                 break;
1468 
1469             case OCPN_ACTION_TIDES_TOGGLE:
1470                 evt.SetId( ID_MENU_SHOW_TIDES );
1471                 gFrame->GetEventHandler()->AddPendingEvent(evt);
1472                 break;
1473 
1474             case OCPN_ACTION_CURRENTS_TOGGLE:
1475                 evt.SetId( ID_MENU_SHOW_CURRENTS );
1476                 gFrame->GetEventHandler()->AddPendingEvent(evt);
1477                 break;
1478 
1479             case OCPN_ACTION_ENCTEXT_TOGGLE:
1480                 evt.SetId( ID_MENU_ENC_TEXT );
1481                 gFrame->GetEventHandler()->AddPendingEvent(evt);
1482                 break;
1483 
1484             case OCPN_ACTION_ENCSOUNDINGS_TOGGLE:
1485                 evt.SetId( ID_MENU_ENC_SOUNDINGS );
1486                 gFrame->GetEventHandler()->AddPendingEvent(evt);
1487                 break;
1488 
1489             case OCPN_ACTION_ENCLIGHTS_TOGGLE:
1490                 evt.SetId( ID_MENU_ENC_LIGHTS );
1491                 gFrame->GetEventHandler()->AddPendingEvent(evt);
1492                 break;
1493 
1494             default:
1495                 break;
1496         }
1497 
1498         return 73;
1499     }
1500 }
1501 
1502 
1503 extern "C"{
Java_org_opencpn_OCPNNativeLib_getVPCorners(JNIEnv * env,jobject obj)1504     JNIEXPORT jstring JNICALL Java_org_opencpn_OCPNNativeLib_getVPCorners(JNIEnv *env, jobject obj)
1505     {
1506 //        qDebug() << "getVPCorners";
1507 
1508         wxString s;
1509 
1510         if(gFrame->GetPrimaryCanvas()){
1511             LLBBox vbox;
1512             vbox = gFrame->GetPrimaryCanvas()->GetVP().GetBBox();
1513             s.Printf(_T("%g;%g;%g;%g;"), vbox.GetMaxLat(), vbox.GetMaxLon(), vbox.GetMinLat(), vbox.GetMinLon());
1514         }
1515 
1516         jstring ret = (env)->NewStringUTF(s.c_str());
1517 
1518         return ret;
1519     }
1520 
1521 }
1522 
1523 extern "C"{
Java_org_opencpn_OCPNNativeLib_getVPS(JNIEnv * env,jobject obj)1524     JNIEXPORT jstring JNICALL Java_org_opencpn_OCPNNativeLib_getVPS(JNIEnv *env, jobject obj)
1525     {
1526         wxString s;
1527 
1528         if(gFrame->GetPrimaryCanvas()){
1529             ViewPort vp = gFrame->GetPrimaryCanvas()->GetVP();
1530             s.Printf(_T("%g;%g;%g;%g;%g;"), vp.clat, vp.clon, vp.view_scale_ppm, gLat, gLon);
1531 
1532         }
1533 
1534         jstring ret = (env)->NewStringUTF(s.c_str());
1535 
1536         return ret;
1537     }
1538 
1539 }
1540 
1541 extern "C"{
Java_org_opencpn_OCPNNativeLib_getTLWCount(JNIEnv * env,jobject obj)1542     JNIEXPORT int JNICALL Java_org_opencpn_OCPNNativeLib_getTLWCount(JNIEnv *env, jobject obj)
1543     {
1544         int ret = 0;
1545         wxWindowList::compatibility_iterator node = wxTopLevelWindows.GetFirst();
1546         while (node)
1547         {
1548             wxWindow* win = node->GetData();
1549             if(win->IsShown() && !win->IsKindOf( CLASSINFO( CanvasOptions )))
1550                 ret++;
1551 
1552             node = node->GetNext();
1553         }
1554         return ret;
1555     }
1556 }
1557 
1558 extern "C"{
Java_org_opencpn_OCPNNativeLib_notifyFullscreenChange(JNIEnv * env,jobject obj,bool bFull)1559     JNIEXPORT int JNICALL Java_org_opencpn_OCPNNativeLib_notifyFullscreenChange(JNIEnv *env, jobject obj, bool bFull)
1560     {
1561         g_bFullscreen = bFull;
1562         return 1;
1563     }
1564 }
1565 
1566 
1567 extern "C"{
Java_org_opencpn_OCPNNativeLib_setDownloadStatus(JNIEnv * env,jobject obj,int status,jstring url)1568     JNIEXPORT jint JNICALL Java_org_opencpn_OCPNNativeLib_setDownloadStatus(JNIEnv *env, jobject obj, int status, jstring url)
1569     {
1570 //        qDebug() << "setDownloadStatus";
1571 
1572         const char *sparm;
1573         wxString wx_sparm;
1574         JNIEnv* jenv;
1575 
1576         //  Need a Java environment to decode the string parameter
1577         if (java_vm->GetEnv( (void **) &jenv, JNI_VERSION_1_6) != JNI_OK) {
1578             //qDebug() << "GetEnv failed.";
1579         }
1580         else {
1581             sparm = (jenv)->GetStringUTFChars(url, NULL);
1582             wx_sparm = wxString(sparm, wxConvUTF8);
1583         }
1584 
1585         if(s_bdownloading && wx_sparm.IsSameAs(s_requested_url) ){
1586 
1587 //            qDebug() << "Maybe mine...";
1588             //  We simply pass the event on to the core download manager methods,
1589             //  with parameters crafted to the event
1590             OCPN_downloadEvent ev(wxEVT_DOWNLOAD_EVENT, 0);
1591 
1592             OCPN_DLCondition dl_condition = OCPN_DL_EVENT_TYPE_UNKNOWN;
1593             OCPN_DLStatus dl_status = OCPN_DL_UNKNOWN;
1594 
1595             //  Translate Android status values to OCPN
1596             switch (status){
1597                 case 16:                                // STATUS_FAILED
1598                     dl_condition = OCPN_DL_EVENT_TYPE_END;
1599                     dl_status = OCPN_DL_FAILED;
1600                     break;
1601 
1602                 case 8:                                 // STATUS_SUCCESSFUL
1603                     dl_condition = OCPN_DL_EVENT_TYPE_END;
1604                     dl_status = OCPN_DL_NO_ERROR;
1605                     break;
1606 
1607                 case 4:                                 //  STATUS_PAUSED
1608                 case 2:                                 //  STATUS_RUNNING
1609                 case 1:                                 //  STATUS_PENDING
1610                    dl_condition = OCPN_DL_EVENT_TYPE_PROGRESS;
1611                    dl_status = OCPN_DL_NO_ERROR;
1612             }
1613 
1614             ev.setDLEventCondition( dl_condition );
1615             ev.setDLEventStatus( dl_status );
1616 
1617             if(s_download_evHandler){
1618 //                qDebug() << "Sending event...";
1619                 s_download_evHandler->AddPendingEvent(ev);
1620             }
1621 
1622 
1623         }
1624 
1625 
1626         return 77;
1627     }
1628 
1629 }
1630 
1631 extern "C"{
Java_org_opencpn_OCPNNativeLib_sendPluginMessage(JNIEnv * env,jobject obj,jstring msgID,jstring msg)1632     JNIEXPORT jint JNICALL Java_org_opencpn_OCPNNativeLib_sendPluginMessage(JNIEnv *env, jobject obj, jstring msgID, jstring msg)
1633     {
1634         const char *sparm;
1635         wxString MsgID;
1636         wxString Msg;
1637         JNIEnv* jenv;
1638 
1639         //  Need a Java environment to decode the string parameter
1640         if (java_vm->GetEnv( (void **) &jenv, JNI_VERSION_1_6) != JNI_OK) {
1641             //qDebug() << "GetEnv failed.";
1642         }
1643         else {
1644             sparm = (jenv)->GetStringUTFChars(msgID, NULL);
1645             MsgID = wxString(sparm, wxConvUTF8);
1646 
1647             sparm = (jenv)->GetStringUTFChars(msg, NULL);
1648             Msg = wxString(sparm, wxConvUTF8);
1649 
1650         }
1651 
1652         SendPluginMessage( MsgID, Msg );
1653 
1654         return 74;
1655     }
1656 }
1657 
androidTerminate()1658 void androidTerminate(){
1659     callActivityMethod_vs("terminateApp");
1660 }
1661 
1662 
CheckPendingJNIException()1663 bool CheckPendingJNIException()
1664 {
1665     JNIEnv* jenv;
1666 
1667     if (java_vm->GetEnv( (void **) &jenv, JNI_VERSION_1_6) != JNI_OK)
1668         return true;
1669 
1670     if( (jenv)->ExceptionCheck() == JNI_TRUE ) {
1671 
1672         // Handle exception here.
1673         (jenv)->ExceptionDescribe(); // writes to logcat
1674         (jenv)->ExceptionClear();
1675 
1676         return false;           // There was a pending exception, but cleared OK
1677                                 // interesting discussion:  http://blog.httrack.com/blog/2013/08/23/catching-posix-signals-on-android/
1678     }
1679 
1680     return false;
1681 
1682 }
1683 
1684 
callActivityMethod_vs(const char * method)1685 wxString callActivityMethod_vs(const char *method)
1686 {
1687     if(CheckPendingJNIException())
1688         return _T("NOK");
1689 
1690     JNIEnv* jenv;
1691 
1692     wxString return_string;
1693     QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative",
1694                                                                            "activity", "()Landroid/app/Activity;");
1695     if(CheckPendingJNIException())
1696         return _T("NOK");
1697 
1698     if ( !activity.isValid() ){
1699         //qDebug() << "Activity is not valid";
1700         return return_string;
1701     }
1702 
1703     //  Call the desired method
1704     QAndroidJniObject data = activity.callObjectMethod(method, "()Ljava/lang/String;");
1705     if(CheckPendingJNIException())
1706         return _T("NOK");
1707 
1708     jstring s = data.object<jstring>();
1709     //qDebug() << s;
1710 
1711     if(s){
1712         //  Need a Java environment to decode the resulting string
1713         if (java_vm->GetEnv( (void **) &jenv, JNI_VERSION_1_6) != JNI_OK) {
1714             //qDebug() << "GetEnv failed.";
1715         }
1716         else {
1717             const char *ret_string = (jenv)->GetStringUTFChars(s, NULL);
1718             return_string = wxString(ret_string, wxConvUTF8);
1719         }
1720     }
1721 
1722     return return_string;
1723 }
1724 
1725 
1726 
callActivityMethod_is(const char * method,int parm)1727 wxString callActivityMethod_is(const char *method, int parm)
1728 {
1729     if(CheckPendingJNIException())
1730         return _T("NOK");
1731     JNIEnv* jenv;
1732 
1733     wxString return_string;
1734     QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative",
1735                                                                            "activity", "()Landroid/app/Activity;");
1736 
1737     if ( !activity.isValid() ){
1738         //qDebug() << "Activity is not valid";
1739         return return_string;
1740     }
1741 
1742     //  Call the desired method
1743     QAndroidJniObject data = activity.callObjectMethod(method, "(I)Ljava/lang/String;", parm);
1744     if(CheckPendingJNIException())
1745         return _T("NOK");
1746 
1747     jstring s = data.object<jstring>();
1748 
1749     //  Need a Java environment to decode the resulting string
1750     if (java_vm->GetEnv( (void **) &jenv, JNI_VERSION_1_6) != JNI_OK) {
1751         //qDebug() << "GetEnv failed.";
1752     }
1753     else {
1754         const char *ret_string = (jenv)->GetStringUTFChars(s, NULL);
1755         return_string = wxString(ret_string, wxConvUTF8);
1756     }
1757 
1758     return return_string;
1759 
1760 }
1761 
callActivityMethod_iis(const char * method,int parm1,int parm2)1762 wxString callActivityMethod_iis(const char *method, int parm1, int parm2)
1763 {
1764     if(CheckPendingJNIException())
1765         return _T("NOK");
1766 
1767     JNIEnv* jenv;
1768 
1769     wxString return_string;
1770     QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative",
1771                                                                            "activity", "()Landroid/app/Activity;");
1772     if(CheckPendingJNIException())
1773         return _T("NOK");
1774 
1775     if ( !activity.isValid() ){
1776         //qDebug() << "Activity is not valid";
1777         return return_string;
1778     }
1779 
1780     //  Call the desired method
1781     QAndroidJniObject data = activity.callObjectMethod(method, "(II)Ljava/lang/String;", parm1, parm2);
1782     if(CheckPendingJNIException())
1783         return _T("NOK");
1784 
1785     jstring s = data.object<jstring>();
1786 
1787     //  Need a Java environment to decode the resulting string
1788     if (java_vm->GetEnv( (void **) &jenv, JNI_VERSION_1_6) != JNI_OK) {
1789         //qDebug() << "GetEnv failed.";
1790     }
1791     else {
1792         const char *ret_string = (jenv)->GetStringUTFChars(s, NULL);
1793         return_string = wxString(ret_string, wxConvUTF8);
1794     }
1795 
1796     return return_string;
1797 
1798 }
1799 
callActivityMethod_ss(const char * method,wxString parm)1800 wxString callActivityMethod_ss(const char *method, wxString parm)
1801 {
1802     if(CheckPendingJNIException())
1803         return _T("NOK");
1804     JNIEnv* jenv;
1805 
1806     wxString return_string;
1807     QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative",
1808                                                                            "activity", "()Landroid/app/Activity;");
1809     if(CheckPendingJNIException())
1810         return _T("NOK");
1811 
1812     if ( !activity.isValid() ){
1813         //qDebug() << "Activity is not valid";
1814         return return_string;
1815     }
1816 
1817     //  Need a Java environment to decode the resulting string
1818     if (java_vm->GetEnv( (void **) &jenv, JNI_VERSION_1_6) != JNI_OK) {
1819         //qDebug() << "GetEnv failed.";
1820         return _T("jenv Error");
1821     }
1822 
1823     jstring p = (jenv)->NewStringUTF(parm.c_str());
1824 
1825 
1826     //  Call the desired method
1827     //qDebug() << "Calling method_ss";
1828     //qDebug() << method;
1829 
1830     QAndroidJniObject data = activity.callObjectMethod(method, "(Ljava/lang/String;)Ljava/lang/String;", p);
1831 
1832     (jenv)->DeleteLocalRef(p);
1833 
1834     if(CheckPendingJNIException())
1835         return _T("NOK");
1836 
1837     //qDebug() << "Back from method_ss";
1838 
1839     jstring s = data.object<jstring>();
1840 
1841     if( (jenv)->GetStringLength( s )){
1842         const char *ret_string = (jenv)->GetStringUTFChars(s, NULL);
1843         return_string = wxString(ret_string, wxConvUTF8);
1844     }
1845 
1846     return return_string;
1847 
1848 }
1849 
callActivityMethod_s2s(const char * method,wxString parm1,wxString parm2)1850 wxString callActivityMethod_s2s(const char *method, wxString parm1, wxString parm2)
1851 {
1852     if(CheckPendingJNIException())
1853         return _T("NOK");
1854     JNIEnv* jenv;
1855 
1856     wxString return_string;
1857     QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative",
1858                                                                            "activity", "()Landroid/app/Activity;");
1859     if(CheckPendingJNIException())
1860         return _T("NOK");
1861 
1862     if ( !activity.isValid() ){
1863         //qDebug() << "Activity is not valid";
1864         return return_string;
1865     }
1866 
1867     //  Need a Java environment to decode the resulting string
1868     if (java_vm->GetEnv( (void **) &jenv, JNI_VERSION_1_6) != JNI_OK) {
1869         //qDebug() << "GetEnv failed.";
1870         return _T("jenv Error");
1871     }
1872 
1873     wxCharBuffer p1b = parm1.ToUTF8();
1874     jstring p1 = (jenv)->NewStringUTF(p1b.data());
1875 
1876     wxCharBuffer p2b = parm2.ToUTF8();
1877     jstring p2 = (jenv)->NewStringUTF(p2b.data());
1878 
1879     //  Call the desired method
1880     //qDebug() << "Calling method_s2s" << " (" << method << ")";
1881 
1882     QAndroidJniObject data = activity.callObjectMethod(method, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", p1, p2);
1883 
1884     (jenv)->DeleteLocalRef(p1);
1885     (jenv)->DeleteLocalRef(p2);
1886 
1887     if(CheckPendingJNIException())
1888         return _T("NOK");
1889 
1890     //qDebug() << "Back from method_s2s";
1891 
1892     jstring s = data.object<jstring>();
1893 
1894     if( (jenv)->GetStringLength( s )){
1895         const char *ret_string = (jenv)->GetStringUTFChars(s, NULL);
1896         return_string = wxString(ret_string, wxConvUTF8);
1897     }
1898 
1899     return return_string;
1900 
1901 }
1902 
callActivityMethod_s3s(const char * method,wxString parm1,wxString parm2,wxString parm3)1903 wxString callActivityMethod_s3s(const char *method, wxString parm1, wxString parm2, wxString parm3)
1904 {
1905     if(CheckPendingJNIException())
1906         return _T("NOK");
1907     JNIEnv* jenv;
1908 
1909     wxString return_string;
1910     QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative",
1911                                                                            "activity", "()Landroid/app/Activity;");
1912     if(CheckPendingJNIException())
1913         return _T("NOK");
1914 
1915     if ( !activity.isValid() ){
1916         return return_string;
1917     }
1918 
1919     //  Need a Java environment to decode the resulting string
1920     if (java_vm->GetEnv( (void **) &jenv, JNI_VERSION_1_6) != JNI_OK) {
1921         return _T("jenv Error");
1922     }
1923 
1924     wxCharBuffer p1b = parm1.ToUTF8();
1925     jstring p1 = (jenv)->NewStringUTF(p1b.data());
1926 
1927     wxCharBuffer p2b = parm2.ToUTF8();
1928     jstring p2 = (jenv)->NewStringUTF(p2b.data());
1929 
1930     wxCharBuffer p3b = parm3.ToUTF8();
1931     jstring p3 = (jenv)->NewStringUTF(p3b.data());
1932 
1933     //  Call the desired method
1934     //qDebug() << "Calling method_s3s" << " (" << method << ")";
1935 
1936     QAndroidJniObject data = activity.callObjectMethod(method, "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
1937                                                        p1, p2, p3);
1938     (jenv)->DeleteLocalRef(p1);
1939     (jenv)->DeleteLocalRef(p2);
1940     (jenv)->DeleteLocalRef(p3);
1941 
1942     if(CheckPendingJNIException())
1943         return _T("NOK");
1944 
1945     //qDebug() << "Back from method_s3s";
1946 
1947     jstring s = data.object<jstring>();
1948 
1949     if( (jenv)->GetStringLength( s )){
1950         const char *ret_string = (jenv)->GetStringUTFChars(s, NULL);
1951         return_string = wxString(ret_string, wxConvUTF8);
1952     }
1953 
1954     return return_string;
1955 
1956 }
1957 
1958 
callActivityMethod_s4s(const char * method,wxString parm1,wxString parm2,wxString parm3,wxString parm4)1959 wxString callActivityMethod_s4s(const char *method, wxString parm1, wxString parm2, wxString parm3, wxString parm4)
1960 {
1961     if(CheckPendingJNIException())
1962         return _T("NOK");
1963     JNIEnv* jenv;
1964 
1965     wxString return_string;
1966     QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative",
1967                                                                            "activity", "()Landroid/app/Activity;");
1968     if(CheckPendingJNIException())
1969         return _T("NOK");
1970 
1971     if ( !activity.isValid() ){
1972         //qDebug() << "Activity is not valid";
1973         return return_string;
1974     }
1975 
1976     //  Need a Java environment to decode the resulting string
1977     if (java_vm->GetEnv( (void **) &jenv, JNI_VERSION_1_6) != JNI_OK) {
1978         //qDebug() << "GetEnv failed.";
1979         return _T("jenv Error");
1980     }
1981 
1982     wxCharBuffer p1b = parm1.ToUTF8();
1983     jstring p1 = (jenv)->NewStringUTF(p1b.data());
1984 
1985     wxCharBuffer p2b = parm2.ToUTF8();
1986     jstring p2 = (jenv)->NewStringUTF(p2b.data());
1987 
1988     wxCharBuffer p3b = parm3.ToUTF8();
1989     jstring p3 = (jenv)->NewStringUTF(p3b.data());
1990 
1991     wxCharBuffer p4b = parm4.ToUTF8();
1992     jstring p4 = (jenv)->NewStringUTF(p4b.data());
1993 
1994     //const char *ts = (jenv)->GetStringUTFChars(p2, NULL);
1995     //qDebug() << "Test String p2" << ts;
1996 
1997     //  Call the desired method
1998     //qDebug() << "Calling method_s4s" << " (" << method << ")";
1999 
2000     QAndroidJniObject data = activity.callObjectMethod(method, "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
2001                                                        p1, p2, p3, p4);
2002     (jenv)->DeleteLocalRef(p1);
2003     (jenv)->DeleteLocalRef(p2);
2004     (jenv)->DeleteLocalRef(p3);
2005     (jenv)->DeleteLocalRef(p4);
2006 
2007     if(CheckPendingJNIException())
2008         return _T("NOK");
2009 
2010     //qDebug() << "Back from method_s4s";
2011 
2012     jstring s = data.object<jstring>();
2013 
2014      if( (jenv)->GetStringLength( s )){
2015          const char *ret_string = (jenv)->GetStringUTFChars(s, NULL);
2016          return_string = wxString(ret_string, wxConvUTF8);
2017      }
2018 
2019     return return_string;
2020 
2021 }
2022 
callActivityMethod_s2s2i(const char * method,wxString parm1,wxString parm2,int parm3,int parm4)2023 wxString callActivityMethod_s2s2i(const char *method, wxString parm1, wxString parm2, int parm3, int parm4)
2024 {
2025     if(CheckPendingJNIException())
2026         return _T("NOK");
2027 
2028     wxString return_string;
2029     QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative",
2030                                                                            "activity", "()Landroid/app/Activity;");
2031     if(CheckPendingJNIException())
2032         return _T("NOK");
2033 
2034     if ( !activity.isValid() ){
2035         return return_string;
2036     }
2037 
2038     //  Need a Java environment to decode the resulting string
2039     JNIEnv* jenv;
2040 
2041     if (java_vm->GetEnv( (void **) &jenv, JNI_VERSION_1_6) != JNI_OK) {
2042         //qDebug() << "GetEnv failed.";
2043         return _T("jenv Error");
2044     }
2045 
2046     wxCharBuffer p1b = parm1.ToUTF8();
2047     jstring p1 = (jenv)->NewStringUTF(p1b.data());
2048 
2049     wxCharBuffer p2b = parm2.ToUTF8();
2050     jstring p2 = (jenv)->NewStringUTF(p2b.data());
2051 
2052     //qDebug() << "Calling method_s2s2i" << " (" << method << ")";
2053     //qDebug() << parm3 << parm4;
2054 
2055     QAndroidJniObject data = activity.callObjectMethod(method, "(Ljava/lang/String;Ljava/lang/String;II)Ljava/lang/String;",
2056                                                        p1, p2, parm3, parm4);
2057 
2058     (jenv)->DeleteLocalRef(p1);
2059     (jenv)->DeleteLocalRef(p2);
2060 
2061     if(CheckPendingJNIException())
2062         return _T("NOK");
2063 
2064     jstring s = data.object<jstring>();
2065 
2066     if( (jenv)->GetStringLength( s )){
2067         const char *ret_string = (jenv)->GetStringUTFChars(s, NULL);
2068              return_string = wxString(ret_string, wxConvUTF8);
2069      }
2070 
2071     return return_string;
2072 
2073 }
2074 
2075 
callActivityMethod_ssi(const char * method,wxString parm1,int parm2)2076 wxString callActivityMethod_ssi(const char *method, wxString parm1, int parm2)
2077 {
2078     if(CheckPendingJNIException())
2079         return _T("NOK");
2080 
2081     wxString return_string;
2082     QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative",
2083                                                                            "activity", "()Landroid/app/Activity;");
2084     if(CheckPendingJNIException())
2085         return _T("NOK");
2086 
2087     if ( !activity.isValid() ){
2088         return return_string;
2089     }
2090 
2091     //  Need a Java environment to decode the resulting string
2092     JNIEnv* jenv;
2093 
2094     if (java_vm->GetEnv( (void **) &jenv, JNI_VERSION_1_6) != JNI_OK) {
2095         //qDebug() << "GetEnv failed.";
2096         return _T("jenv Error");
2097     }
2098 
2099     wxCharBuffer p1b = parm1.ToUTF8();
2100     jstring p1 = (jenv)->NewStringUTF(p1b.data());
2101 
2102     QAndroidJniObject data = activity.callObjectMethod(method, "(Ljava/lang/String;I)Ljava/lang/String;", p1, parm2);
2103 
2104     (jenv)->DeleteLocalRef(p1);
2105 
2106     if(CheckPendingJNIException())
2107         return _T("NOK");
2108 
2109     jstring s = data.object<jstring>();
2110 
2111     if( (jenv)->GetStringLength( s )){
2112         const char *ret_string = (jenv)->GetStringUTFChars(s, NULL);
2113              return_string = wxString(ret_string, wxConvUTF8);
2114     }
2115 
2116     return return_string;
2117 
2118 }
2119 
androidGetAndroidSystemLocale()2120 wxString androidGetAndroidSystemLocale()
2121 {
2122     return callActivityMethod_vs("getAndroidLocaleString");
2123 }
2124 
androidGetFullscreen()2125 bool androidGetFullscreen()
2126 {
2127     wxString s = callActivityMethod_vs("getFullscreen");
2128 
2129     return s == _T("YES");
2130 }
2131 
androidSetFullscreen(bool bFull)2132 bool androidSetFullscreen( bool bFull )
2133 {
2134     callActivityMethod_is("setFullscreen", (int)bFull);
2135 
2136     return true;
2137 }
2138 
androidDisableFullScreen()2139 void androidDisableFullScreen()
2140 {
2141     if(g_bFullscreen){
2142         g_bFullscreenSave = true;
2143         androidSetFullscreen( false );
2144     }
2145 }
2146 
androidRestoreFullScreen()2147 void androidRestoreFullScreen()
2148 {
2149     if(g_bFullscreenSave){
2150         g_bFullscreenSave = false;
2151         androidSetFullscreen( true );
2152     }
2153 }
2154 
androidGetScreenOrientation()2155 int androidGetScreenOrientation(){
2156     wxString s = callActivityMethod_vs("getScreenOrientation");
2157     long result = -1;
2158     s.ToLong(&result);
2159     return result;
2160 }
2161 
androidLaunchHelpView()2162 void androidLaunchHelpView()
2163 {
2164     qDebug() << "androidLaunchHelpView ";
2165     wxString val = callActivityMethod_vs("isHelpAvailable");
2166     if(val.IsSameAs(_T("YES"))){
2167         callActivityMethod_vs("launchHelpBook");
2168     }
2169     else{
2170         wxString msg = _("OpenCPN Help is not installed.\nWould you like to install from Google PlayStore now?");
2171         if(androidShowSimpleYesNoDialog( _T("OpenCPN"), msg ))
2172             androidInstallPlaystoreHelp();
2173     }
2174 }
2175 
androidLaunchBrowser(wxString URL)2176 void androidLaunchBrowser( wxString URL )
2177 {
2178     qDebug() << "androidLaunchBrowser";
2179     callActivityMethod_ss("launchWebView", URL);
2180 }
2181 
androidDisplayTimedToast(wxString message,int timeMillisec)2182 void androidDisplayTimedToast(wxString message, int timeMillisec)
2183 {
2184     callActivityMethod_ssi("showTimedToast", message, timeMillisec);
2185 }
2186 
androidCancelTimedToast()2187 void androidCancelTimedToast()
2188 {
2189     callActivityMethod_vs("cancelTimedToast");
2190 }
2191 
androidDisplayToast(wxString message)2192 void androidDisplayToast(wxString message)
2193 {
2194     callActivityMethod_ss("showToast", message);
2195 }
2196 
androidEnableRotation(void)2197 void androidEnableRotation( void )
2198 {
2199     callActivityMethod_vs("EnableRotation");
2200 }
2201 
androidDisableRotation(void)2202 void androidDisableRotation( void )
2203 {
2204     callActivityMethod_vs("DisableRotation");
2205 }
2206 
androidShowDisclaimer(wxString title,wxString msg)2207 bool androidShowDisclaimer( wxString title, wxString msg )
2208 {
2209     if(CheckPendingJNIException())
2210         return false;
2211 
2212     wxString return_string;
2213     QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative",
2214                                                                            "activity", "()Landroid/app/Activity;");
2215     if(CheckPendingJNIException())
2216         return false;
2217 
2218     if ( !activity.isValid() )
2219         return false;
2220 
2221     JNIEnv* jenv;
2222 
2223     //  Need a Java environment to decode the resulting string
2224     if (java_vm->GetEnv( (void **) &jenv, JNI_VERSION_1_6) != JNI_OK)
2225         return false;
2226 
2227 
2228     wxCharBuffer p1b = title.ToUTF8();
2229     jstring p1 = (jenv)->NewStringUTF(p1b.data());
2230 
2231     // Convert for wxString-UTF8  to jstring-UTF16
2232     wxWCharBuffer b = msg.wc_str();
2233     jstring p2 = (jenv)->NewString( (jchar *)b.data(), msg.Len() * 2);
2234 
2235     QAndroidJniObject data = activity.callObjectMethod( "disclaimerDialog", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", p1, p2);
2236 
2237     (jenv)->DeleteLocalRef(p1);
2238     (jenv)->DeleteLocalRef(p2);
2239 
2240     if(CheckPendingJNIException())
2241         return false;
2242 
2243     jstring s = data.object<jstring>();
2244 
2245     if( (jenv)->GetStringLength( s )){
2246         const char *ret_string = (jenv)->GetStringUTFChars(s, NULL);
2247         return_string = wxString(ret_string, wxConvUTF8);
2248     }
2249 
2250 
2251     return (return_string == _T("OK"));
2252 }
2253 
androidShowSimpleOKDialog(wxString title,wxString msg)2254 bool androidShowSimpleOKDialog( wxString title, wxString msg )
2255 {
2256     if(CheckPendingJNIException())
2257         return false;
2258 
2259     wxString return_string;
2260     QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative",
2261                                                                            "activity", "()Landroid/app/Activity;");
2262     if(CheckPendingJNIException())
2263         return false;
2264 
2265     if ( !activity.isValid() )
2266         return false;
2267 
2268     JNIEnv* jenv;
2269 
2270     //  Need a Java environment to decode the resulting string
2271     if (java_vm->GetEnv( (void **) &jenv, JNI_VERSION_1_6) != JNI_OK)
2272         return false;
2273 
2274 
2275     wxCharBuffer p1b = title.ToUTF8();
2276     jstring p1 = (jenv)->NewStringUTF(p1b.data());
2277 
2278     // Convert for wxString-UTF8  to jstring-UTF16
2279     wxWCharBuffer b = msg.wc_str();
2280     jstring p2 = (jenv)->NewString( (jchar *)b.data(), msg.Len() * 2);
2281 
2282     QAndroidJniObject data = activity.callObjectMethod( "simpleOKDialog", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", p1, p2);
2283 
2284     (jenv)->DeleteLocalRef(p1);
2285     (jenv)->DeleteLocalRef(p2);
2286 
2287     if(CheckPendingJNIException())
2288         return false;
2289 
2290     jstring s = data.object<jstring>();
2291 
2292     if( (jenv)->GetStringLength( s )){
2293         const char *ret_string = (jenv)->GetStringUTFChars(s, NULL);
2294         return_string = wxString(ret_string, wxConvUTF8);
2295     }
2296 
2297 
2298     return (return_string == _T("OK"));
2299 }
2300 
androidShowSimpleYesNoDialog(wxString title,wxString msg)2301 bool androidShowSimpleYesNoDialog( wxString title, wxString msg )
2302 {
2303     if(CheckPendingJNIException())
2304         return false;
2305 
2306     wxString return_string;
2307     QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative",
2308                                                                            "activity", "()Landroid/app/Activity;");
2309     if(CheckPendingJNIException())
2310         return false;
2311 
2312     if ( !activity.isValid() )
2313         return false;
2314 
2315     JNIEnv* jenv;
2316 
2317     //  Need a Java environment to decode the resulting string
2318     if (java_vm->GetEnv( (void **) &jenv, JNI_VERSION_1_6) != JNI_OK)
2319         return false;
2320 
2321 
2322     wxCharBuffer p1b = title.ToUTF8();
2323     jstring p1 = (jenv)->NewStringUTF(p1b.data());
2324 
2325     // Convert for wxString-UTF8  to jstring-UTF16
2326     wxWCharBuffer b = msg.wc_str();
2327     jstring p2 = (jenv)->NewString( (jchar *)b.data(), msg.Len() * 2);
2328 
2329     QAndroidJniObject data = activity.callObjectMethod( "simpleYesNoDialog", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", p1, p2);
2330 
2331     (jenv)->DeleteLocalRef(p1);
2332     (jenv)->DeleteLocalRef(p2);
2333 
2334     if(CheckPendingJNIException())
2335         return false;
2336 
2337     jstring s = data.object<jstring>();
2338 
2339     if( (jenv)->GetStringLength( s )){
2340         const char *ret_string = (jenv)->GetStringUTFChars(s, NULL);
2341         return_string = wxString(ret_string, wxConvUTF8);
2342     }
2343 
2344 
2345     return (return_string == _T("YES"));
2346 }
2347 
androidInstallPlaystoreHelp()2348 bool androidInstallPlaystoreHelp()
2349 {
2350    qDebug() << "androidInstallPlaystoreHelp";
2351  //  return false;
2352 
2353    if(CheckPendingJNIException())
2354         return false;
2355 
2356     wxString return_string;
2357     QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative",
2358                                                                            "activity", "()Landroid/app/Activity;");
2359     if(CheckPendingJNIException())
2360         return false;
2361 
2362     if ( !activity.isValid() )
2363         return false;
2364 
2365     JNIEnv* jenv;
2366 
2367     //  Need a Java environment to decode the resulting string
2368     if (java_vm->GetEnv( (void **) &jenv, JNI_VERSION_1_6) != JNI_OK)
2369         return false;
2370 
2371 
2372     QAndroidJniObject data = activity.callObjectMethod( "installPlaystoreHelp", "()Ljava/lang/String;");
2373 
2374     if(CheckPendingJNIException())
2375         return false;
2376 
2377     jstring s = data.object<jstring>();
2378 
2379     if( (jenv)->GetStringLength( s )){
2380         const char *ret_string = (jenv)->GetStringUTFChars(s, NULL);
2381         return_string = wxString(ret_string, wxConvUTF8);
2382     }
2383 
2384     return (return_string == _T("OK"));
2385 }
2386 
2387 
androidGetTZOffsetMins()2388 int androidGetTZOffsetMins()
2389 {
2390     // Get the TZ offset (from UTC) of the local machine, in minutes.  Includes DST, if applicable
2391     wxString result = callActivityMethod_vs("getAndroidTZOffsetMinutes");
2392     qDebug() << "androidGetTZOffsetMins result: " << result.mb_str();
2393     long value = 0;
2394     result.ToLong(&value);
2395     return (int)value;
2396 }
2397 
2398 extern PlatSpec android_plat_spc;
2399 
androidGetDeviceInfo()2400 wxString androidGetDeviceInfo()
2401 {
2402     if(!g_deviceInfo.Length())
2403         g_deviceInfo = callActivityMethod_vs("getDeviceInfo");
2404 
2405     wxStringTokenizer tkz(g_deviceInfo, _T("\n"));
2406     while( tkz.HasMoreTokens() )
2407     {
2408         wxString s1 = tkz.GetNextToken();
2409         if(wxNOT_FOUND != s1.Find(_T("OS API Level"))){
2410             int a = s1.Find(_T("{"));
2411             if(wxNOT_FOUND != a){
2412                 wxString b = s1.Mid(a+1, 2);
2413                 memset(android_plat_spc.msdk, 0, sizeof(android_plat_spc.msdk));
2414                 strncpy(android_plat_spc.msdk, b.c_str(), 2);
2415             }
2416         }
2417         if(wxNOT_FOUND != s1.Find(_T("opencpn"))){
2418             strcpy(&android_plat_spc.hn[0], s1.c_str());
2419         }
2420     }
2421 
2422     return g_deviceInfo;
2423 }
2424 
androidGetHomeDir()2425 wxString androidGetHomeDir()
2426 {
2427     return g_androidFilesDir + _T("/");
2428 }
2429 
androidGetPrivateDir()2430 wxString androidGetPrivateDir()                 // Used for logfile, config file, navobj, and the like
2431 {
2432     if(g_bExternalApp){
2433 
2434         // should check storage availability
2435 #if 0
2436 /* Checks if external storage is available for read and write */
2437         public boolean isExternalStorageWritable() {
2438             String state = Environment.getExternalStorageState();
2439             if (Environment.MEDIA_MOUNTED.equals(state)) {
2440                 return true;
2441             }
2442             return false;
2443         }
2444 
2445         /* Checks if external storage is available to at least read */
2446         public boolean isExternalStorageReadable() {
2447             String state = Environment.getExternalStorageState();
2448             if (Environment.MEDIA_MOUNTED.equals(state) ||
2449                 Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
2450                 return true;
2451                 }
2452                 return false;
2453         }
2454 #endif
2455         return g_androidExtFilesDir;
2456     }
2457 
2458     // We choose to use the ExtFiles directory always , so that the contents of logfiles, navobjs, etc.
2459     // may always be accessible by simple Android File Explorers...
2460     return g_androidExtFilesDir;
2461 }
2462 
androidGetSharedDir()2463 wxString androidGetSharedDir()                 // Used for assets like uidata, s57data, etc
2464 {
2465 //    if(g_bExternalApp){
2466 //        if(g_androidExtFilesDir.Length())
2467 //            return g_androidExtFilesDir + _T("/");
2468 //    }
2469 
2470     return g_androidExtFilesDir + _T("/");
2471 }
2472 
androidGetCacheDir()2473 wxString androidGetCacheDir()                 // Used for raster_texture_cache, mmsitoname.csv, etc
2474 {
2475 //    if(g_bExternalApp){
2476 //        if(g_androidExtCacheDir.Length())
2477 //            return g_androidExtCacheDir;
2478 //    }
2479 
2480     return g_androidExtCacheDir;
2481 }
2482 
2483 // Android notes:
2484 /* Note: don't be confused by the word "external" here.
2485  * This directory can better be thought as media/shared storage.
2486  * It is a filesystem that can hold a relatively large amount of data
2487  * and that is shared across all applications (does not enforce permissions).
2488  * Traditionally this is an SD card, but it may also be implemented as built-in storage
2489  * in a device that is distinct from the protected internal storage
2490  * and can be mounted as a filesystem on a computer.
2491  */
2492 
androidGetExtStorageDir()2493 wxString androidGetExtStorageDir()                 // Used for Chart storage, typically
2494 {
2495     return g_androidExtStorageDir;
2496 }
2497 
androidSetRouteAnnunciator(bool viz)2498 extern void androidSetRouteAnnunciator(bool viz)
2499 {
2500     callActivityMethod_is("setRouteAnnunciator", viz?1:0);
2501 }
2502 
androidSetFollowTool(int state,bool forceUpdate)2503 extern void androidSetFollowTool(int state, bool forceUpdate)
2504 {
2505 //    qDebug() << "setFollowIconState" << bactive;
2506 
2507     if( (g_follow_state != state) || forceUpdate)
2508         callActivityMethod_is("setFollowIconState", state);
2509 
2510     g_follow_state = state;
2511 }
2512 
androidSetTrackTool(bool bactive)2513 extern void androidSetTrackTool(bool bactive)
2514 {
2515     if(g_track_active != bactive)
2516         callActivityMethod_is("setTrackIconState", bactive?1:0);
2517 
2518     g_track_active = bactive;
2519 }
2520 
2521 
androidSetChartTypeMaskSel(int mask,wxString & indicator)2522 void androidSetChartTypeMaskSel( int mask, wxString &indicator)
2523 {
2524     int sel = 0;
2525     if(wxNOT_FOUND != indicator.Find( _T("raster")))
2526         sel = 1;
2527     else if(wxNOT_FOUND != indicator.Find( _T("vector")))
2528         sel = 2;
2529     else if(wxNOT_FOUND != indicator.Find( _T("cm93")))
2530         sel = 4;
2531 
2532     if((g_mask != mask) || (g_sel != sel)){
2533 //        qDebug() << "androidSetChartTypeMaskSel" << mask << sel;
2534         callActivityMethod_iis("configureNavSpinnerTS", mask, sel);
2535         g_mask = mask;
2536         g_sel = sel;
2537     }
2538 }
2539 
2540 
androidEnableBackButton(bool benable)2541 void androidEnableBackButton(bool benable)
2542 {
2543     callActivityMethod_is("setBackButtonState", benable?1:0);
2544     g_backEnabled = benable;
2545 }
2546 
androidEnableBackButtonCheck(bool benable)2547 void androidEnableBackButtonCheck(bool benable)
2548 {
2549     if(g_backEnabled != benable)
2550         androidEnableBackButton(benable);
2551 }
2552 
2553 
androidGetMemoryStatus(int * mem_total,int * mem_used)2554 bool androidGetMemoryStatus( int *mem_total, int *mem_used )
2555 {
2556 
2557     //  On android, We arbitrarily declare that we have used 50% of available memory.
2558     if(mem_total)
2559         *mem_total = s_androidMemTotal * 1024;
2560     if(mem_used)
2561         *mem_used = s_androidMemUsed * 1024;
2562     return true;
2563 
2564 #if 0
2565 
2566     //  Get a reference to the running native activity
2567     QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative",
2568                                                                            "activity", "()Landroid/app/Activity;");
2569 
2570     if ( !activity.isValid() ){
2571         qDebug() << "Activity is not valid";
2572         return false;
2573     }
2574 
2575     unsigned long android_processID = wxGetProcessId();
2576 
2577     //  Call the desired method
2578     QAndroidJniObject data = activity.callObjectMethod("getMemInfo", "(I)Ljava/lang/String;", (int)android_processID);
2579 
2580 //    wxString return_string;
2581     jstring s = data.object<jstring>();
2582 
2583     int mu = 50;
2584     //  Need a Java environment to decode the resulting string
2585     if (java_vm->GetEnv( (void **) &jenv, JNI_VERSION_1_6) != JNI_OK) {
2586         qDebug() << "GetEnv failed.";
2587     }
2588     else {
2589         const char *ret_string = (jenv)->GetStringUTFChars(s, NULL);
2590         mu = atoi(ret_string);
2591 
2592     }
2593 
2594     if(mem_used)
2595         *mem_used = mu;
2596 
2597 
2598     return true;
2599 #endif
2600 }
2601 
GetAndroidDisplaySize()2602 double GetAndroidDisplaySize()
2603 {
2604 
2605     double ret = 200.;          // sane default
2606 
2607     //  Get a reference to the running native activity
2608     QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative",
2609                                                                            "activity", "()Landroid/app/Activity;");
2610 
2611     if ( !activity.isValid() ){
2612         //qDebug() << "Activity is not valid";
2613         return false;
2614     }
2615 
2616     //  Call the desired method
2617     QAndroidJniObject data = activity.callObjectMethod("getDisplayMetrics", "()Ljava/lang/String;");
2618 
2619     wxString return_string;
2620     jstring s = data.object<jstring>();
2621 
2622     JNIEnv* jenv;
2623     //  Need a Java environment to decode the resulting string
2624     if (java_vm->GetEnv( (void **) &jenv, JNI_VERSION_1_6) != JNI_OK) {
2625         //qDebug() << "GetEnv failed.";
2626     }
2627     else {
2628         const char *ret_string = (jenv)->GetStringUTFChars(s, NULL);
2629         return_string = wxString(ret_string, wxConvUTF8);
2630     }
2631 
2632     //  Return string may have commas instead of periods, if using Euro locale
2633     //  We just fix it here...
2634     return_string.Replace( _T(","), _T(".") );
2635 
2636     wxSize screen_size = wxSize(1,2);
2637 
2638     if(QApplication::desktop()){                // Desktop might not yet be initialized
2639         screen_size = ::wxGetDisplaySize();
2640     }
2641 
2642     wxString msg;
2643 
2644     //int ssx, ssy;
2645     //::wxDisplaySize(&ssx, &ssy);
2646     //msg.Printf(_T("wxDisplaySize(): %d %d"), ssx, ssy);
2647     //wxLogMessage(msg);
2648 
2649     double density = 1.0;
2650     long androidWidth = 2;
2651     long androidHeight = 1;
2652     long androidDmWidth = 2;
2653     long androidDmHeight = 1;
2654     long abh = 1;
2655 
2656     wxStringTokenizer tk(return_string, _T(";"));
2657     if( tk.HasMoreTokens() ){
2658         wxString token = tk.GetNextToken();     // xdpi
2659         token = tk.GetNextToken();              // density
2660 
2661         token.ToDouble( &density );
2662 
2663         token = tk.GetNextToken();              // ldpi
2664 
2665         token = tk.GetNextToken();              // width
2666         token.ToLong( &androidWidth );
2667         token = tk.GetNextToken();              // height - statusBarHeight
2668         token = tk.GetNextToken();              // width
2669         token = tk.GetNextToken();              // height
2670         token.ToLong( &androidHeight );
2671 
2672         token = tk.GetNextToken();              // dm.widthPixels
2673         token.ToLong( &androidDmWidth );
2674         token = tk.GetNextToken();              // dm.heightPixels
2675         token.ToLong( &androidDmHeight );
2676 
2677         token = tk.GetNextToken();              // actionBarHeight
2678         token.ToLong( &abh );
2679 
2680     }
2681 
2682     double ldpi = 160. * density;
2683     if(ldpi < 160)
2684         ldpi = 160.;
2685 
2686     // Find the max dimension among all possibilities
2687     double maxDim = wxMax(screen_size.x, screen_size.y);
2688     maxDim = wxMax(maxDim, androidHeight);
2689     maxDim = wxMax(maxDim, androidWidth);
2690 
2691     ret = (maxDim / ldpi) * 25.4;
2692 
2693     if(ret < 75){               // 3 inches is too small....
2694         double ret_bad = ret;
2695         ret = 100;
2696         msg.Printf(_T("WARNING: Android Auto Display Size OVERRIDE_TOO_SMALL: %g  ldpi: %g  density: %g correctedsize: %g "), ret_bad, ldpi, density, ret);
2697     }
2698     else if(ret > 400){         // Too large
2699         double ret_bad = ret;
2700         ret = 400;
2701         msg.Printf(_T("WARNING: Android Auto Display Size OVERRIDE_TOO_LARGE: %g  ldpi: %g  density: %g corrected size: %g"), ret_bad, ldpi, density, ret);
2702     }
2703     else{
2704         msg.Printf(_T("Android Auto Display Size (mm, est.): %g   ldpi: %g  density: %g"), ret, ldpi, density);
2705     }
2706 
2707     //  Save some items as global statics for convenience
2708     g_androidDPmm = ldpi / 25.4;
2709     g_androidDensity = density;
2710     g_ActionBarHeight = wxMax(abh, 50);
2711 
2712     //qDebug() << "GetAndroidDisplaySize" << ldpi << g_androidDPmm;
2713 
2714     return ret;
2715 }
2716 
getAndroidActionBarHeight()2717 int getAndroidActionBarHeight()
2718 {
2719     return g_ActionBarHeight;
2720 }
2721 
getAndroidDPmm()2722 double getAndroidDPmm()
2723 {
2724     // Returns an estimate based on the pixel density reported
2725     GetAndroidDisplaySize();
2726 
2727     //qDebug() << "getAndroidDPmm" << g_androidDPmm;
2728 
2729     // User override?
2730     if(g_config_display_size_manual && (g_config_display_size_mm > 0) ){
2731         double maxDim = wxMax(::wxGetDisplaySize().x, ::wxGetDisplaySize().y);
2732         double size_mm = g_config_display_size_mm;
2733         size_mm = wxMax(size_mm, 50);
2734         size_mm = wxMin(size_mm, 400);
2735         double ret = maxDim / size_mm;
2736         //qDebug() << "getAndroidDPmm override" << maxDim << size_mm << g_config_display_size_mm;
2737 
2738         return ret;
2739     }
2740 
2741 
2742     if(g_androidDPmm > 0.01)
2743         return g_androidDPmm;
2744     else
2745         return 160. / 25.4;
2746 }
2747 
getAndroidDisplayDensity()2748 double getAndroidDisplayDensity()
2749 {
2750     if( g_androidDensity < 0.01){
2751         GetAndroidDisplaySize();
2752     }
2753 
2754 //    qDebug() << "g_androidDensity" << g_androidDensity;
2755 
2756     if(g_androidDensity > 0.01)
2757         return g_androidDensity;
2758     else
2759         return 1.0;
2760 }
2761 
2762 
getAndroidDisplayDimensions(void)2763 wxSize getAndroidDisplayDimensions( void )
2764 {
2765     wxSize sz_ret = ::wxGetDisplaySize();               // default, probably reasonable, but maybe not accurate
2766 
2767     QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative",
2768                                                                            "activity", "()Landroid/app/Activity;");
2769 
2770     if ( !activity.isValid() ){
2771         //qDebug() << "Activity is not valid";
2772         return sz_ret;
2773     }
2774 
2775     //  Call the desired method
2776     QAndroidJniObject data = activity.callObjectMethod("getDisplayMetrics", "()Ljava/lang/String;");
2777 
2778     wxString return_string;
2779     jstring s = data.object<jstring>();
2780 
2781     //  Need a Java environment to decode the resulting string
2782     JNIEnv* jenv;
2783     if (java_vm->GetEnv( (void **) &jenv, JNI_VERSION_1_6) != JNI_OK) {
2784         //qDebug() << "GetEnv failed.";
2785     }
2786     else {
2787         const char *ret_string = (jenv)->GetStringUTFChars(s, NULL);
2788         return_string = wxString(ret_string, wxConvUTF8);
2789     }
2790 
2791     //167.802994;1.000000;160;1024;527;1024;552;1024;552;56
2792      wxStringTokenizer tk(return_string, _T(";"));
2793     if( tk.HasMoreTokens() ){
2794         wxString token = tk.GetNextToken();     // xdpi
2795         token = tk.GetNextToken();              // density
2796         token = tk.GetNextToken();              // densityDPI
2797 
2798         token = tk.GetNextToken();
2799         long a = 1000;
2800         if(token.ToLong( &a ))
2801             sz_ret.x = a;
2802 
2803         token = tk.GetNextToken();
2804         long b = 1000;
2805         if(token.ToLong( &b ))
2806             sz_ret.y = b;
2807 
2808         token = tk.GetNextToken();
2809         token = tk.GetNextToken();
2810 
2811         token = tk.GetNextToken();
2812         token = tk.GetNextToken();
2813 
2814         long abh = 0;
2815         token = tk.GetNextToken();              //  ActionBar height, if shown
2816         if(token.ToLong( &abh ))
2817             sz_ret.y -= abh;
2818 
2819 
2820 
2821     }
2822 
2823 //    qDebug() << sz_ret.x << sz_ret.y;
2824 
2825     return sz_ret;
2826 
2827 }
2828 
androidConfirmSizeCorrection()2829 void androidConfirmSizeCorrection()
2830 {
2831     //  There is some confusion about the ActionBar size during configuration changes.
2832     //  We need to confirm the calculated display size, and fix it if necessary.
2833     //  This happens during staged resize events
2834 
2835     wxLogMessage(_T("androidConfirmSizeCorrection"));
2836     wxSize targetSize = getAndroidDisplayDimensions();
2837     qDebug() << "Confirming" << targetSize.y << config_size.y;
2838     if(config_size != targetSize){
2839         qDebug() << "Correcting";
2840         gFrame->SetSize(targetSize);
2841         config_size = targetSize;
2842     }
2843 }
2844 
androidForceFullRepaint(bool b_skipConfirm)2845 void androidForceFullRepaint( bool b_skipConfirm)
2846 {
2847 
2848         wxLogMessage(_T("androidForceFullRepaint"));
2849         wxSize targetSize = getAndroidDisplayDimensions();
2850         wxSize tempSize = targetSize;
2851         tempSize.y--;
2852         gFrame->SetSize(tempSize);
2853 
2854         GetAndroidDisplaySize();
2855 
2856         wxSize new_size = getAndroidDisplayDimensions();
2857         config_size = new_size;
2858 
2859         g_androidUtilHandler->m_bskipConfirm = b_skipConfirm;
2860 
2861         wxCommandEvent evt(wxEVT_COMMAND_MENU_SELECTED);
2862         evt.SetId( ID_CMD_TRIGGER_RESIZE );
2863         if(gFrame && gFrame->GetEventHandler()){
2864             g_androidUtilHandler->AddPendingEvent(evt);
2865         }
2866 
2867 }
2868 
androidShowBusyIcon()2869 void androidShowBusyIcon()
2870 {
2871     if(b_androidBusyShown)
2872         return;
2873 
2874     //qDebug() << "ShowBusy";
2875 
2876     //  Get a reference to the running native activity
2877     QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative",
2878                                                                            "activity", "()Landroid/app/Activity;");
2879     if ( !activity.isValid() ){
2880         //qDebug() << "Activity is not valid";
2881         return;
2882     }
2883 
2884     //  Call the desired method
2885     QAndroidJniObject data = activity.callObjectMethod("showBusyCircle", "()Ljava/lang/String;");
2886 
2887     b_androidBusyShown = true;
2888 }
2889 
androidHideBusyIcon()2890 void androidHideBusyIcon()
2891 {
2892     if(!b_androidBusyShown)
2893         return;
2894 
2895     //  Get a reference to the running native activity
2896     QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative",
2897                                                                            "activity", "()Landroid/app/Activity;");
2898 
2899     if ( !activity.isValid() ){
2900         //qDebug() << "Activity is not valid";
2901         return;
2902     }
2903 
2904     //  Call the desired method
2905     QAndroidJniObject data = activity.callObjectMethod("hideBusyCircle", "()Ljava/lang/String;");
2906 
2907     b_androidBusyShown = false;
2908 }
2909 
androidGetVersionCode()2910 int androidGetVersionCode()
2911 {
2912        //  Get a reference to the running native activity
2913     QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative",
2914                                                                            "activity", "()Landroid/app/Activity;");
2915 
2916     if ( !activity.isValid() ){
2917         //qDebug() << "Activity is not valid";
2918         return false;
2919     }
2920 
2921     //  Call the desired method
2922     QAndroidJniObject data = activity.callObjectMethod("getAndroidVersionCode", "()Ljava/lang/String;");
2923 
2924     wxString return_string;
2925     jstring s = data.object<jstring>();
2926 
2927     JNIEnv* jenv;
2928     //  Need a Java environment to decode the resulting string
2929     if (java_vm->GetEnv( (void **) &jenv, JNI_VERSION_1_6) != JNI_OK) {
2930         //qDebug() << "GetEnv failed.";
2931     }
2932     else {
2933         const char *ret_string = (jenv)->GetStringUTFChars(s, NULL);
2934         return_string = wxString(ret_string, wxConvUTF8);
2935     }
2936 
2937     long rv;
2938     return_string.ToLong(&rv);
2939 
2940     return rv;
2941 }
2942 
androidGetVersionName()2943 wxString androidGetVersionName()
2944 {
2945        //  Get a reference to the running native activity
2946     QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative",
2947                                                                            "activity", "()Landroid/app/Activity;");
2948 
2949     if ( !activity.isValid() ){
2950         //qDebug() << "Activity is not valid";
2951         return _T("ERROR");
2952     }
2953 
2954     //  Call the desired method
2955     QAndroidJniObject data = activity.callObjectMethod("getAndroidVersionName", "()Ljava/lang/String;");
2956 
2957     wxString return_string;
2958     jstring s = data.object<jstring>();
2959 
2960     JNIEnv* jenv;
2961     //  Need a Java environment to decode the resulting string
2962     if (java_vm->GetEnv( (void **) &jenv, JNI_VERSION_1_6) != JNI_OK) {
2963         //qDebug() << "GetEnv failed.";
2964     }
2965     else {
2966         const char *ret_string = (jenv)->GetStringUTFChars(s, NULL);
2967         return_string = wxString(ret_string, wxConvUTF8);
2968     }
2969 
2970     return return_string;
2971 }
2972 
2973 //---------------------------------------------------------------
2974 //      GPS Device Support
2975 //---------------------------------------------------------------
androidDeviceHasGPS()2976 bool androidDeviceHasGPS()
2977 {
2978     wxString query = androidGPSService( GPS_PROVIDER_AVAILABLE );
2979     wxLogMessage( query);
2980 
2981     bool result = query.Upper().IsSameAs(_T("YES"));
2982     if(result){
2983         //qDebug() << "Android Device has internal GPS";
2984         wxLogMessage(_T("Android Device has internal GPS"));
2985     }
2986     else{
2987         //qDebug() << "Android Device has NO internal GPS";
2988         wxLogMessage(_T("Android Device has NO internal GPS"));
2989     }
2990     return result;
2991 }
2992 
androidStartNMEA(wxEvtHandler * consumer)2993 bool androidStartNMEA(wxEvtHandler *consumer)
2994 {
2995     s_pAndroidNMEAMessageConsumer = consumer;
2996 
2997     //qDebug() << "androidStartNMEA";
2998     wxString s;
2999 
3000     s = androidGPSService( GPS_ON );
3001     wxLogMessage(s);
3002     if(s.Upper().Find(_T("DISABLED")) != wxNOT_FOUND){
3003         OCPNMessageBox(NULL,
3004                        _("Your android device has an internal GPS, but it is disabled.\n\
3005                        Please visit android Settings/Location dialog to enable GPS"),
3006                         _T("OpenCPN"), wxOK );
3007 
3008         androidStopNMEA();
3009         return false;
3010     }
3011     else
3012         bGPSEnabled = true;
3013 
3014     return true;
3015 }
3016 
androidStopNMEA()3017 bool androidStopNMEA()
3018 {
3019     s_pAndroidNMEAMessageConsumer = NULL;
3020 
3021     wxString s = androidGPSService( GPS_OFF );
3022 
3023     bGPSEnabled = false;
3024 
3025     return true;
3026 }
3027 
3028 
androidGPSService(int parm)3029 wxString androidGPSService(int parm)
3030 {
3031     QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative",
3032                                                                            "activity", "()Landroid/app/Activity;");
3033 
3034     if ( !activity.isValid() ){
3035         //qDebug() << "Activity is not valid";
3036         return _T("Activity is not valid");
3037     }
3038 
3039     //  Call the desired method
3040     QAndroidJniObject data = activity.callObjectMethod("queryGPSServer", "(I)Ljava/lang/String;", parm);
3041 
3042     wxString return_string;
3043     jstring s = data.object<jstring>();
3044 
3045     if( s == NULL )
3046         return return_string;
3047 
3048     //  Need a Java environment to decode the resulting string
3049     JNIEnv* jenv;
3050     if (java_vm->GetEnv( (void **) &jenv, JNI_VERSION_1_6) != JNI_OK) {
3051         //qDebug() << "GetEnv failed.";
3052     }
3053     else {
3054         const char *ret_string = (jenv)->GetStringUTFChars(s, NULL);
3055         return_string = wxString(ret_string, wxConvUTF8);
3056     }
3057 
3058      return return_string;
3059 }
3060 
3061 
androidDeviceHasBlueTooth()3062 bool androidDeviceHasBlueTooth()
3063 {
3064     QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative",
3065                                                                            "activity", "()Landroid/app/Activity;");
3066 
3067     if ( !activity.isValid() ){
3068         //qDebug() << "Activity is not valid";
3069         return _T("Activity is not valid");
3070     }
3071 
3072     //  Call the desired method
3073     QAndroidJniObject data = activity.callObjectMethod("hasBluetooth", "(I)Ljava/lang/String;", 0);
3074 
3075     wxString query;
3076     jstring s = data.object<jstring>();
3077 
3078     //  Need a Java environment to decode the resulting string
3079     JNIEnv* jenv;
3080     if (java_vm->GetEnv( (void **) &jenv, JNI_VERSION_1_6) != JNI_OK) {
3081         //qDebug() << "GetEnv failed.";
3082     }
3083     else {
3084         const char *ret_string = (jenv)->GetStringUTFChars(s, NULL);
3085         query = wxString(ret_string, wxConvUTF8);
3086     }
3087 
3088     bool result = query.Upper().IsSameAs(_T("YES"));
3089 
3090     if(result){
3091         //qDebug() << "Android Device has internal Bluetooth";
3092         wxLogMessage(_T("Android Device has internal Bluetooth"));
3093     }
3094     else{
3095         //qDebug() << "Android Device has NO internal Bluetooth";
3096         wxLogMessage(_T("Android Device has NO internal Bluetooth"));
3097     }
3098 
3099     return result;
3100 }
3101 
androidStartBluetoothScan()3102 bool androidStartBluetoothScan()
3103 {
3104     wxString result = callActivityMethod_is("startBlueToothScan", 0);
3105 
3106     return true;
3107 
3108 }
3109 
androidStopBluetoothScan()3110 bool androidStopBluetoothScan()
3111 {
3112     wxString result = callActivityMethod_is("stopBlueToothScan", 0);
3113 
3114     return true;
3115 
3116 }
3117 
androidStartBT(wxEvtHandler * consumer,wxString mac_address)3118 bool androidStartBT(wxEvtHandler *consumer, wxString mac_address )
3119 {
3120     s_pAndroidBTNMEAMessageConsumer = consumer;
3121 
3122     if(mac_address.Find(':') ==  wxNOT_FOUND)   //  does not look like a mac address
3123         return false;
3124 
3125     wxString result = callActivityMethod_ss("startBTService", mac_address);
3126 
3127     return true;
3128 }
3129 
androidStopBT()3130 bool androidStopBT()
3131 {
3132     //qDebug() << "androidStopBT";
3133 
3134     s_pAndroidBTNMEAMessageConsumer = NULL;
3135 
3136     wxString result = callActivityMethod_is("stopBTService", 0);
3137 
3138     return true;
3139 }
3140 
3141 
androidGetBluetoothScanResults()3142 wxArrayString androidGetBluetoothScanResults()
3143 {
3144     wxArrayString ret_array;
3145 
3146     wxString result = callActivityMethod_is("getBlueToothScanResults", 0);
3147 
3148     wxStringTokenizer tk(result, _T(";"));
3149     while ( tk.HasMoreTokens() )
3150     {
3151         wxString token = tk.GetNextToken();
3152         ret_array.Add(token);
3153     }
3154 
3155     if(!ret_array.GetCount())
3156         ret_array.Add(_("Nothing found"));
3157 
3158     return ret_array;
3159 }
3160 
androidSendBTMessage(wxString & payload)3161 bool androidSendBTMessage( wxString &payload )
3162 {
3163     wxString result = callActivityMethod_ss("sendBTMessage", payload);
3164 
3165     return true;
3166 }
3167 
androidCheckOnline()3168 bool androidCheckOnline()
3169 {
3170     wxString val = callActivityMethod_vs("isNetworkAvailable");
3171     return val.IsSameAs(_T("YES"));
3172 }
3173 
androidGetSerialPortsArray(void)3174 wxArrayString *androidGetSerialPortsArray( void )
3175 {
3176 
3177     wxArrayString *pret_array = new wxArrayString;
3178     wxString result = callActivityMethod_is("scanSerialPorts", 0);
3179 
3180     wxStringTokenizer tk(result, _T(";"));
3181     while ( tk.HasMoreTokens() )
3182     {
3183         wxString token = tk.GetNextToken();
3184         pret_array->Add(token);
3185     }
3186 
3187     return pret_array;
3188 }
3189 
androidStartUSBSerial(wxString & portname,wxString baudRate,wxEvtHandler * consumer)3190 bool androidStartUSBSerial(wxString &portname, wxString baudRate, wxEvtHandler *consumer)
3191 {
3192     wxString result = callActivityMethod_s2s("startSerialPort", portname, baudRate);
3193 
3194     s_pAndroidNMEAMessageConsumer = consumer;
3195 
3196     return true;
3197 }
3198 
androidStopUSBSerial(wxString & portname)3199 bool androidStopUSBSerial(wxString &portname)
3200 {
3201     s_pAndroidNMEAMessageConsumer = NULL;
3202 
3203     //  If app is closing down, the USB serial ports will go away automatically.
3204     //  So no need here.
3205     //  In fact, stopSerialPort() causes an occasional error when closing app.
3206     //  Dunno why, difficult to debug.
3207     if(!b_inCloseWindow)
3208         wxString result = callActivityMethod_ss("stopSerialPort", portname);
3209 
3210     return true;
3211 }
3212 
androidWriteSerial(wxString & portname,wxString & message)3213 bool androidWriteSerial(wxString &portname, wxString& message)
3214 {
3215     wxString result = callActivityMethod_s2s("writeSerialPort", portname, message);
3216     return true;
3217 }
3218 
3219 
androidFileChooser(wxString * result,const wxString & initDir,const wxString & title,const wxString & suggestion,const wxString & wildcard,bool dirOnly,bool addFile)3220 int androidFileChooser( wxString *result, const wxString &initDir, const wxString &title,
3221                         const wxString &suggestion, const wxString &wildcard, bool dirOnly, bool addFile)
3222 {
3223     wxString tresult;
3224 
3225     //  Start a timer to poll for results
3226     if(g_androidUtilHandler){
3227         g_androidUtilHandler->m_eventTimer.Stop();
3228         g_androidUtilHandler->m_done = false;
3229 
3230         wxString activityResult;
3231         if(dirOnly)
3232             activityResult = callActivityMethod_s2s2i("DirChooserDialog", initDir, title, addFile, 0);
3233 
3234         else
3235             activityResult = callActivityMethod_s4s("FileChooserDialog", initDir, title, suggestion, wildcard);
3236 
3237         if(activityResult == _T("OK") ){
3238             //qDebug() << "ResultOK, starting spin loop";
3239             g_androidUtilHandler->m_action = ACTION_FILECHOOSER_END;
3240             g_androidUtilHandler->m_eventTimer.Start(1000, wxTIMER_CONTINUOUS);
3241 
3242         //  Spin, waiting for result
3243             while(!g_androidUtilHandler->m_done){
3244                 wxMilliSleep(50);
3245                 wxSafeYield(NULL, true);
3246             }
3247 
3248             //qDebug() << "out of spin loop";
3249             g_androidUtilHandler->m_action = ACTION_NONE;
3250             g_androidUtilHandler->m_eventTimer.Stop();
3251 
3252 
3253             tresult = g_androidUtilHandler->GetStringResult();
3254 
3255             if( tresult.StartsWith(_T("cancel:")) ){
3256                 //qDebug() << "Cancel1";
3257                 return wxID_CANCEL;
3258             }
3259             else if( tresult.StartsWith(_T("file:")) ){
3260                 if(result){
3261                     *result = tresult.AfterFirst(':');
3262                     //qDebug() << "OK";
3263                     return wxID_OK;
3264                 }
3265                 else{
3266                     //qDebug() << "Cancel2";
3267                     return wxID_CANCEL;
3268                 }
3269             }
3270         }
3271         else{
3272             //qDebug() << "Result NOT OK";
3273         }
3274 
3275     }
3276 
3277     return wxID_CANCEL;
3278 }
3279 
3280 
3281 
InvokeJNIPreferences(wxString & initial_settings)3282 bool InvokeJNIPreferences( wxString &initial_settings)
3283 {
3284     bool ret = true;
3285     wxCharBuffer abuf = initial_settings.ToUTF8();
3286     if( !abuf.data() )
3287         return false;
3288 
3289     //  Create the method parameter(s)
3290         QAndroidJniObject param1 = QAndroidJniObject::fromString(abuf.data());
3291 
3292         //  Get a reference to the running native activity
3293         QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative",
3294         "activity", "()Landroid/app/Activity;");
3295 
3296         if ( !activity.isValid() ){
3297             //qDebug() << "Activity is not valid";
3298             return false;
3299         }
3300 
3301         //  Call the desired method
3302         activity.callObjectMethod("doAndroidSettings", "(Ljava/lang/String;)Ljava/lang/String;", param1.object<jstring>());
3303 
3304 
3305         return ret;
3306 }
3307 
BuildAndroidSettingsString(void)3308 wxString BuildAndroidSettingsString( void )
3309 {
3310     wxString result;
3311 
3312     //  Start with chart dirs
3313     if( ChartData){
3314         wxArrayString chart_dir_array = ChartData->GetChartDirArrayString();
3315 
3316         for(unsigned int i=0 ; i < chart_dir_array.GetCount() ; i++){
3317             result += _T("ChartDir:");
3318             result += chart_dir_array[i];
3319             result += _T(";");
3320         }
3321     }
3322 
3323     //  Now the simple Boolean parameters
3324         result += _T("prefb_lookahead:") + wxString(g_bLookAhead == 1 ? _T("1;") : _T("0;"));
3325         result += _T("prefb_quilt:") + wxString(g_bQuiltEnable == 1 ? _T("1;") : _T("0;"));
3326         result += _T("prefb_showgrid:") + wxString(g_bDisplayGrid == 1 ? _T("1;") : _T("0;"));
3327         result += _T("prefb_showoutlines:") + wxString(g_bShowOutlines == 1 ? _T("1;") : _T("0;"));
3328         result += _T("prefb_showdepthunits:") + wxString(g_bShowDepthUnits == 1 ? _T("1;") : _T("0;"));
3329         result += _T("prefb_lockwp:") + wxString(g_bWayPointPreventDragging == 1 ? _T("1;") : _T("0;"));
3330         result += _T("prefb_confirmdelete:") + wxString(g_bConfirmObjectDelete == 1 ? _T("1;") : _T("0;"));
3331         result += _T("prefb_expertmode:") + wxString(g_bUIexpert == 1 ? _T("1;") : _T("0;"));
3332 
3333         if(ps52plib){
3334             result += _T("prefb_showlightldesc:") + wxString(ps52plib->m_bShowLdisText == 1 ? _T("1;") : _T("0;"));
3335             result += _T("prefb_showimptext:") + wxString(ps52plib->m_bShowS57ImportantTextOnly == 1 ? _T("1;") : _T("0;"));
3336             result += _T("prefb_showSCAMIN:") + wxString(ps52plib->m_bUseSCAMIN == 1 ? _T("1;") : _T("0;"));
3337             result += _T("prefb_showsound:") + wxString(ps52plib->m_bShowSoundg == 1 ? _T("1;") : _T("0;"));
3338             result += _T("prefb_showATONLabels:") + wxString(ps52plib->m_bShowAtonText == 1 ? _T("1;") : _T("0;"));
3339         }
3340     // Some other assorted values
3341         result += _T("prefs_navmode:") + wxString(g_bCourseUp == 0 ? _T("North Up;") : _T("Course Up;"));
3342         result += _T("prefs_chartInitDir:") + *pInit_Chart_Dir + _T(";");
3343 
3344         wxString s;
3345         double sf = (g_GUIScaleFactor * 10) + 50.;
3346         s.Printf( _T("%3.0f;"), sf );
3347         s.Trim(false);
3348         result += _T("prefs_UIScaleFactor:") + s;
3349 
3350         sf = (g_ChartScaleFactor * 10) + 50.;
3351         s.Printf( _T("%3.0f;"), sf );
3352         s.Trim(false);
3353         result += _T("prefs_chartScaleFactor:") + s;
3354 
3355         if(ps52plib){
3356             wxString nset = _T("Base");
3357             switch( ps52plib->GetDisplayCategory() ){
3358                 case ( DISPLAYBASE ):
3359                     nset = _T("Base;");
3360                     break;
3361                 case ( STANDARD ):
3362                     nset = _T("Standard;");
3363                     break;
3364                 case ( OTHER ):
3365                     nset = _T("All;");
3366                     break;
3367                 case ( MARINERS_STANDARD ):
3368                     nset = _T("Mariner Standard;");
3369                     break;
3370                 default:
3371                     nset = _T("Base;");
3372                     break;
3373             }
3374             result += _T("prefs_displaycategory:") + nset;
3375 
3376 
3377             if( ps52plib->m_nSymbolStyle == PAPER_CHART )
3378                 nset = _T("Paper Chart;");
3379             else
3380                 nset = _T("Simplified;");
3381             result += _T("prefs_vectorgraphicsstyle:") + nset;
3382 
3383             if( ps52plib->m_nBoundaryStyle == PLAIN_BOUNDARIES )
3384                 nset = _T("Plain;");
3385             else
3386                 nset = _T("Symbolized;");
3387             result += _T("prefs_vectorboundarystyle:") + nset;
3388 
3389             if( S52_getMarinerParam( S52_MAR_TWO_SHADES ) == 1.0 )
3390                 nset = _T("2;");
3391             else
3392                 nset = _T("4;");
3393             result += _T("prefs_vectorchartcolors:") + nset;
3394 
3395             // depth unit conversion factor
3396 
3397             float conv = 1;
3398             int depthUnit = ps52plib->m_nDepthUnitDisplay;
3399             if ( depthUnit == 0 ) // feet
3400                 conv = 0.3048f; // international definiton of 1 foot is 0.3048 metres
3401                 else if ( depthUnit == 2 ) // fathoms
3402                 conv = 0.3048f * 6; // 1 fathom is 6 feet
3403 
3404             s.Printf( _T("%4.2f;"), S52_getMarinerParam( S52_MAR_SHALLOW_CONTOUR ) / conv );
3405             s.Trim(false);
3406             result += _T("prefs_shallowdepth:") + s;
3407 
3408             s.Printf( _T("%4.2f;"), S52_getMarinerParam( S52_MAR_SAFETY_CONTOUR ) / conv );
3409             s.Trim(false);
3410             result += _T("prefs_safetydepth:") + s;
3411 
3412             s.Printf( _T("%4.2f;"), S52_getMarinerParam( S52_MAR_DEEP_CONTOUR ) / conv );
3413             s.Trim(false);
3414             result += _T("prefs_deepdepth:") + s;
3415 
3416             //  Scale slider range from -5 -- 5 in OCPN options.
3417             //  On Android, the range is 0 -- 100
3418             //  So, convert
3419         }
3420 
3421         // Connections
3422 
3423         // Internal GPS.
3424         for ( size_t i = 0; i < g_pConnectionParams->Count(); i++ )
3425         {
3426             ConnectionParams *cp = g_pConnectionParams->Item(i);
3427             if(INTERNAL_GPS == cp->Type){
3428                 result += _T("prefb_internalGPS:");
3429                 result += cp->bEnabled ? _T("1;") : _T("0;");
3430             }
3431             if(SERIAL == cp->Type){
3432                 if(wxNOT_FOUND != cp->GetPortStr().Find(_T("PL2303"))){
3433                     result += _T("prefb_PL2303:");
3434                     result += cp->bEnabled ? _T("1;") : _T("0;");
3435                 }
3436                 else if(wxNOT_FOUND != cp->GetPortStr().Find(_T("dAISy"))){
3437                     result += _T("prefb_dAISy:");
3438                     result += cp->bEnabled ? _T("1;") : _T("0;");
3439                 }
3440                 else if(wxNOT_FOUND != cp->GetPortStr().Find(_T("FT232R"))){
3441                     result += _T("prefb_FT232R:");
3442                     result += cp->bEnabled ? _T("1;") : _T("0;");
3443                 }
3444                 else if(wxNOT_FOUND != cp->GetPortStr().Find(_T("FT231X"))){
3445                     result += _T("prefb_FT231X:");
3446                     result += cp->bEnabled ? _T("1;") : _T("0;");
3447                 }
3448                 else if(wxNOT_FOUND != cp->GetPortStr().Find(_T("USBDP"))){
3449                     result += _T("prefb_USBDP:");
3450                     result += cp->bEnabled ? _T("1;") : _T("0;");
3451                 }
3452             }
3453         }
3454 
3455     wxLogMessage(_T("BuildAndroidSettingsString: ") + result);
3456 
3457     return result;
3458 }
3459 
3460 const wxString AUSBNames[] = { _T("AUSBSerial:Prolific_PL2303"), _T("AUSBSerial:FTDI_FT232R"), _T("AUSBSerial:FTDI_FT231X"), _T("AUSBSerial:dAISy"),
3461                 _T("AUSBSerial:USBDP"), _T("LASTENTRY") };
3462 const wxString AUSBPrefs[] = { _T("prefb_PL2303"),               _T("prefb_FT232R"),           _T("prefb_FT231X"),           _T("prefb_dAISy"),
3463                 _T("prefb_USBDP"),         _T("LASTENTRY") };
3464 
3465 
androidApplySettingsString(wxString settings,ArrayOfCDI * pACDI)3466 int androidApplySettingsString( wxString settings, ArrayOfCDI *pACDI)
3467 {
3468 
3469     //  Parse the passed settings string
3470     bool bproc_InternalGPS = false;
3471     bool benable_InternalGPS = false;
3472 
3473     int rr = GENERIC_CHANGED;
3474 
3475     // extract chart directories
3476 
3477     if(ChartData){
3478         wxStringTokenizer tkd(settings, _T(";"));
3479         while ( tkd.HasMoreTokens() ){
3480             wxString token = tkd.GetNextToken();
3481 
3482             if(token.StartsWith( _T("ChartDir"))){
3483                 wxString dir = token.AfterFirst(':');
3484                 if(dir.Length()){
3485                     ChartDirInfo cdi;
3486                     cdi.fullpath = dir.Trim();
3487                     cdi.magic_number = ChartData->GetMagicNumberCached(dir.Trim());
3488                     pACDI->Add(cdi);
3489                 }
3490             }
3491         }
3492 
3493         // Scan for changes
3494         if(!ChartData->CompareChartDirArray( *pACDI )){
3495             rr |= VISIT_CHARTS;
3496             rr |= CHANGE_CHARTS;
3497             wxLogMessage(_T("Chart Dir List change detected"));
3498         }
3499     }
3500 
3501 
3502     wxStringTokenizer tk(settings, _T(";"));
3503     while ( tk.HasMoreTokens() )
3504     {
3505         wxString token = tk.GetNextToken();
3506         wxString val = token.AfterFirst(':');
3507 
3508         //  Binary switches
3509 
3510         if(token.StartsWith( _T("prefb_lookahead"))){
3511             g_bLookAhead = val.IsSameAs(_T("1"));
3512         }
3513         else if(token.StartsWith( _T("prefb_quilt"))){
3514             g_bQuiltEnable = val.IsSameAs(_T("1"));
3515         }
3516         else if(token.StartsWith( _T("prefb_lockwp"))){
3517             g_bWayPointPreventDragging = val.IsSameAs(_T("1"));
3518         }
3519         else if(token.StartsWith( _T("prefb_showdepthunits"))){
3520             g_bShowDepthUnits = val.IsSameAs(_T("1"));
3521         }
3522         else if(token.StartsWith( _T("prefb_confirmdelete"))){
3523             g_bConfirmObjectDelete = val.IsSameAs(_T("1"));
3524         }
3525         else if(token.StartsWith( _T("prefb_showgrid"))){
3526             g_bDisplayGrid = val.IsSameAs(_T("1"));
3527         }
3528         else if(token.StartsWith( _T("prefb_showoutlines"))){
3529             g_bShowOutlines = val.IsSameAs(_T("1"));
3530         }
3531         else if(token.StartsWith( _T("prefb_expertmode"))){
3532             g_bUIexpert = val.IsSameAs(_T("1"));
3533         }
3534         else if(token.StartsWith( _T("prefb_internalGPS"))){
3535             bproc_InternalGPS = true;
3536             benable_InternalGPS = val.IsSameAs(_T("1"));
3537         }
3538         else if(token.StartsWith( _T("prefs_navmode"))){
3539             g_bCourseUp = val.IsSameAs(_T("Course Up"));
3540         }
3541         else if(token.StartsWith( _T("prefb_trackOnPause"))){
3542             g_btrackContinuous = val.IsSameAs(_T("1"));
3543         }
3544 
3545 
3546         //  Strings, etc.
3547 
3548         else if(token.StartsWith( _T("prefs_UIScaleFactor"))){
3549             double a;
3550             if(val.ToDouble(&a))
3551                 g_GUIScaleFactor = wxRound( (a / 10.) - 5.);
3552         }
3553 
3554         else if(token.StartsWith( _T("prefs_chartScaleFactor"))){
3555             double a;
3556             if(val.ToDouble(&a)){
3557                 g_ChartScaleFactor = wxRound( (a / 10.) - 5.);
3558                 g_ChartScaleFactorExp = g_Platform->getChartScaleFactorExp( g_ChartScaleFactor );
3559             }
3560         }
3561 
3562         else if(token.StartsWith( _T("prefs_chartInitDir"))){
3563             *pInit_Chart_Dir = val;
3564         }
3565 
3566         if(ps52plib){
3567             float conv = 1;
3568             int depthUnit = ps52plib->m_nDepthUnitDisplay;
3569             if ( depthUnit == 0 ) // feet
3570                 conv = 0.3048f; // international definiton of 1 foot is 0.3048 metres
3571                 else if ( depthUnit == 2 ) // fathoms
3572                 conv = 0.3048f * 6; // 1 fathom is 6 feet
3573 
3574                 if(token.StartsWith( _T("prefb_showsound"))){
3575                     bool old_val = ps52plib->m_bShowSoundg;
3576                     ps52plib->m_bShowSoundg = val.IsSameAs(_T("1"));
3577                     if(old_val != ps52plib->m_bShowSoundg)
3578                         rr |= S52_CHANGED;
3579                 }
3580                 else if(token.StartsWith( _T("prefb_showSCAMIN"))){
3581                     bool old_val = ps52plib->m_bUseSCAMIN;
3582                     ps52plib->m_bUseSCAMIN = val.IsSameAs(_T("1"));
3583                     if(old_val != ps52plib->m_bUseSCAMIN)
3584                         rr |= S52_CHANGED;
3585                 }
3586                 else if(token.StartsWith( _T("prefb_showimptext"))){
3587                     bool old_val = ps52plib->m_bShowS57ImportantTextOnly;
3588                     ps52plib->m_bShowS57ImportantTextOnly = val.IsSameAs(_T("1"));
3589                     if(old_val != ps52plib->m_bShowS57ImportantTextOnly)
3590                         rr |= S52_CHANGED;
3591                 }
3592                 else if(token.StartsWith( _T("prefb_showlightldesc"))){
3593                     bool old_val = ps52plib->m_bShowLdisText;
3594                     ps52plib->m_bShowLdisText = val.IsSameAs(_T("1"));
3595                     if(old_val != ps52plib->m_bShowLdisText)
3596                         rr |= S52_CHANGED;
3597                 }
3598                 else if(token.StartsWith( _T("prefb_showATONLabels"))){
3599                     bool old_val = ps52plib->m_bShowAtonText;
3600                     ps52plib->m_bShowAtonText = val.IsSameAs(_T("1"));
3601                     if(old_val != ps52plib->m_bShowAtonText)
3602                         rr |= S52_CHANGED;
3603                 }
3604 
3605                 else if(token.StartsWith( _T("prefs_displaycategory"))){
3606                     _DisCat old_nset = ps52plib->GetDisplayCategory();
3607 
3608                     _DisCat nset = DISPLAYBASE;
3609                     if(wxNOT_FOUND != val.Lower().Find(_T("base")))
3610                         nset = DISPLAYBASE;
3611                     else if(wxNOT_FOUND != val.Lower().Find(_T("mariner")))
3612                         nset = MARINERS_STANDARD;
3613                     else if(wxNOT_FOUND != val.Lower().Find(_T("standard")))
3614                         nset = STANDARD;
3615                     else if(wxNOT_FOUND != val.Lower().Find(_T("all")))
3616                         nset = OTHER;
3617 
3618                     if(nset != old_nset){
3619                         rr |= S52_CHANGED;
3620                         ps52plib-> SetDisplayCategory( nset );
3621                     }
3622                 }
3623 
3624                 else if(token.StartsWith( _T("prefs_shallowdepth"))){
3625                     double old_dval = S52_getMarinerParam( S52_MAR_SHALLOW_CONTOUR );
3626                     double dval;
3627                     if(val.ToDouble(&dval)){
3628                         if(fabs(dval - old_dval) > .001){
3629                             S52_setMarinerParam( S52_MAR_SHALLOW_CONTOUR, dval * conv );
3630                             rr |= S52_CHANGED;
3631                         }
3632                     }
3633                 }
3634 
3635                 else if(token.StartsWith( _T("prefs_safetydepth"))){
3636                     double old_dval = S52_getMarinerParam( S52_MAR_SAFETY_CONTOUR );
3637                     double dval;
3638                     if(val.ToDouble(&dval)){
3639                         if(fabs(dval - old_dval) > .001){
3640                             S52_setMarinerParam( S52_MAR_SAFETY_CONTOUR, dval * conv );
3641                             rr |= S52_CHANGED;
3642                         }
3643                     }
3644                 }
3645 
3646                 else if(token.StartsWith( _T("prefs_deepdepth"))){
3647                     double old_dval = S52_getMarinerParam( S52_MAR_DEEP_CONTOUR );
3648                     double dval;
3649                     if(val.ToDouble(&dval)){
3650                         if(fabs(dval - old_dval) > .001){
3651                             S52_setMarinerParam( S52_MAR_DEEP_CONTOUR, dval * conv );
3652                             rr |= S52_CHANGED;
3653                         }
3654                     }
3655                 }
3656 
3657                 else if(token.StartsWith( _T("prefs_vectorgraphicsstyle"))){
3658                     LUPname old_LUP = ps52plib->m_nSymbolStyle;
3659 
3660                     if(wxNOT_FOUND != val.Lower().Find(_T("paper")))
3661                         ps52plib->m_nSymbolStyle = PAPER_CHART;
3662                     else if(wxNOT_FOUND != val.Lower().Find(_T("simplified")))
3663                         ps52plib->m_nSymbolStyle = SIMPLIFIED;
3664 
3665                     if(old_LUP != ps52plib->m_nSymbolStyle)
3666                         rr |= S52_CHANGED;
3667 
3668                 }
3669 
3670                 else if(token.StartsWith( _T("prefs_vectorboundarystyle"))){
3671                     LUPname old_LUP = ps52plib->m_nBoundaryStyle;
3672 
3673                     if(wxNOT_FOUND != val.Lower().Find(_T("plain")))
3674                         ps52plib->m_nBoundaryStyle = PLAIN_BOUNDARIES;
3675                     else if(wxNOT_FOUND != val.Lower().Find(_T("symbolized")))
3676                         ps52plib->m_nBoundaryStyle = SYMBOLIZED_BOUNDARIES;
3677 
3678                     if(old_LUP != ps52plib->m_nBoundaryStyle)
3679                         rr |= S52_CHANGED;
3680 
3681                 }
3682 
3683                 else if(token.StartsWith( _T("prefs_vectorchartcolors"))){
3684                     double old_dval = S52_getMarinerParam( S52_MAR_TWO_SHADES );
3685 
3686                     if(wxNOT_FOUND != val.Lower().Find(_T("2")))
3687                         S52_setMarinerParam( S52_MAR_TWO_SHADES, 1. );
3688                     else if(wxNOT_FOUND != val.Lower().Find(_T("4")))
3689                         S52_setMarinerParam( S52_MAR_TWO_SHADES, 0. );
3690 
3691                     double new_dval = S52_getMarinerParam( S52_MAR_TWO_SHADES );
3692                     if(fabs(new_dval - old_dval) > .1){
3693                         rr |= S52_CHANGED;
3694                     }
3695                 }
3696         }
3697     }
3698 
3699     // Process Internal GPS Connection
3700     if(g_pConnectionParams && bproc_InternalGPS){
3701 
3702         //  Does the connection already exist?
3703         ConnectionParams *pExistingParams = NULL;
3704         ConnectionParams *cp = NULL;
3705 
3706         for ( size_t i = 0; i < g_pConnectionParams->Count(); i++ )
3707         {
3708             ConnectionParams *xcp = g_pConnectionParams->Item(i);
3709             if(INTERNAL_GPS == xcp->Type){
3710                 pExistingParams = xcp;
3711                 cp = xcp;
3712                 break;
3713             }
3714         }
3715 
3716         bool b_action = true;
3717         if(pExistingParams){
3718             if(pExistingParams->bEnabled == benable_InternalGPS)
3719                 b_action = false;                    // nothing to do...
3720                 else
3721                     cp->bEnabled = benable_InternalGPS;
3722         }
3723         else if(benable_InternalGPS){           //  Need a new Params
3724             // make a generic config string for InternalGPS.
3725             wxString sGPS = _T("2;3;;0;0;;0;1;0;0;;0;;1;0;0;0;0");          // 17 parms
3726             ConnectionParams *new_params = new ConnectionParams(sGPS);
3727 
3728             new_params->bEnabled = benable_InternalGPS;
3729             g_pConnectionParams->Add(new_params);
3730             cp = new_params;
3731         }
3732 
3733 
3734         if(b_action && cp){                               // something to do?
3735 
3736             // Terminate and remove any existing stream with the same port name
3737             DataStream *pds_existing = g_pMUX->FindStream( cp->GetDSPort() );
3738             if(pds_existing)
3739                 g_pMUX->StopAndRemoveStream( pds_existing );
3740 
3741 
3742             if( cp->bEnabled ) {
3743                 dsPortType port_type = cp->IOSelect;
3744                 DataStream *dstr = makeSerialDataStream(g_pMUX,
3745                                                    cp->Type,
3746                                                    cp->GetDSPort(),
3747                                                    wxString::Format(wxT("%i"), cp->Baudrate),
3748                                                    port_type,
3749                                                    cp->Priority,
3750                                                    cp->Garmin);
3751 
3752 #if 0
3753                 DataStream *dstr = new DataStream( g_pMUX,
3754                                                    cp->Type,
3755                                                    cp->GetDSPort(),
3756                                                    wxString::Format(wxT("%i"), cp->Baudrate),
3757                                                                     port_type,
3758                                                                     cp->Priority,
3759                                                                     cp->Garmin);
3760 #endif
3761                                                    dstr->SetInputFilter(cp->InputSentenceList);
3762                                                    dstr->SetInputFilterType(cp->InputSentenceListType);
3763                                                    dstr->SetOutputFilter(cp->OutputSentenceList);
3764                                                    dstr->SetOutputFilterType(cp->OutputSentenceListType);
3765                                                    dstr->SetChecksumCheck(cp->ChecksumCheck);
3766 
3767                                                    g_pMUX->AddStream(dstr);
3768 
3769                                                    cp->b_IsSetup = true;
3770             }
3771         }
3772     }
3773 
3774     // Process USB Serial Connections
3775     bool b_newGlobalSettings = false;
3776     if(g_pConnectionParams){
3777 
3778         int i = 0;
3779         while( wxNOT_FOUND == AUSBPrefs[i].Find(_T("LASTENTRY")) ){
3780             wxStringTokenizer tk(settings, _T(";"));
3781             while ( tk.HasMoreTokens() )
3782             {
3783                 wxString token = tk.GetNextToken();
3784                 wxString pref = token.BeforeFirst(':');
3785                 wxString val = token.AfterFirst(':');
3786                 wxString extraString;
3787 
3788 
3789                 bool benabled = false;
3790 
3791                 if(pref.IsSameAs(AUSBPrefs[i])){
3792 
3793                     wxLogMessage(_T("pref: ") + pref);
3794                     wxLogMessage(_T("val: ") + val);
3795 
3796                     if(pref.Contains(_T("USBDP"))){
3797                         extraString = val.AfterFirst(':');
3798                         wxLogMessage(_T("extra: ") + extraString);
3799                     }
3800 
3801                     wxLogMessage(_T("found pref ") + pref);
3802 
3803                     //  Does the connection already exist?
3804                     ConnectionParams *pExistingParams = NULL;
3805                     ConnectionParams *cp = NULL;
3806 
3807                     wxString target = AUSBNames[i] + _T("-") + extraString;
3808 
3809                     for ( unsigned int j = 0; j < g_pConnectionParams->Count(); j++ )
3810                     {
3811                         ConnectionParams *xcp = g_pConnectionParams->Item(j);
3812                         wxLogMessage( _T("    Checking: ") + target + " .. " +xcp->GetDSPort());
3813 
3814                         if( (SERIAL == xcp->Type) && (target.IsSameAs(xcp->GetDSPort().AfterFirst(':'))) ){
3815                             pExistingParams = xcp;
3816                             cp = xcp;
3817                             benabled = val.BeforeFirst(':').IsSameAs(_T("1"));
3818                             break;
3819                         }
3820                     }
3821 
3822 
3823                     bool b_action = true;
3824                     if(pExistingParams){
3825                         wxLogMessage(_T("Using existing connection  ") + target);
3826 
3827                         if(pExistingParams->bEnabled == benabled){
3828                             b_action = false;                    // nothing to do...
3829                         }
3830                         else
3831                             cp->bEnabled = benabled;
3832                     }
3833                     else if(val.BeforeFirst(':').IsSameAs(_T("1"))){           //  Need a new Params
3834                         // make a generic config string.
3835                         //0;1;;0;0;/dev/ttyS0;4800;1;0;0;;0;;1;0;0;0;0        17 parms
3836 
3837                         wxString sSerial = _T("0;1;;0;0;");
3838                         sSerial += AUSBNames[i];
3839                         sSerial += _T("-") + extraString;
3840                         sSerial += _T(";4800;1;0;0;;0;;1;0;0;0;0");
3841 
3842                         wxLogMessage(_T("Adding connection  ") + sSerial);
3843 
3844                         ConnectionParams *new_params = new ConnectionParams(sSerial);
3845 
3846                         new_params->bEnabled = true;
3847                         g_pConnectionParams->Add(new_params);
3848                         cp = new_params;
3849                         rr |= NEED_NEW_OPTIONS;
3850                     }
3851 
3852 
3853 
3854 
3855                     if(b_action && cp){                               // something to do?
3856                         rr |= NEED_NEW_OPTIONS;
3857 
3858                         // Terminate and remove any existing stream with the same port name
3859                         DataStream *pds_existing = g_pMUX->FindStream( cp->GetDSPort() );
3860                         if(pds_existing)
3861                             g_pMUX->StopAndRemoveStream( pds_existing );
3862 
3863 
3864                         if( cp->bEnabled ) {
3865                             dsPortType port_type = cp->IOSelect;
3866 #if 0
3867                             DataStream *dstr = new DataStream( g_pMUX,
3868                                                                cp->Type,
3869                                                                cp->GetDSPort(),
3870                                                                wxString::Format(wxT("%i"), cp->Baudrate),
3871                                                                port_type,
3872                                                                cp->Priority,
3873                                                                cp->Garmin);
3874 #endif
3875                             DataStream *dstr = makeSerialDataStream(g_pMUX,
3876                                                    cp->Type,
3877                                                    cp->GetDSPort(),
3878                                                    wxString::Format(wxT("%i"), cp->Baudrate),
3879                                                    port_type,
3880                                                    cp->Priority,
3881                                                    cp->Garmin);
3882 
3883                             dstr->SetInputFilter(cp->InputSentenceList);
3884                             dstr->SetInputFilterType(cp->InputSentenceListType);
3885                             dstr->SetOutputFilter(cp->OutputSentenceList);
3886                             dstr->SetOutputFilterType(cp->OutputSentenceListType);
3887                             dstr->SetChecksumCheck(cp->ChecksumCheck);
3888 
3889                             g_pMUX->AddStream(dstr);
3890 
3891                             cp->b_IsSetup = true;
3892                         }
3893                     }
3894                 }
3895             }   // found pref
3896 
3897             i++;
3898         }       // while
3899     }
3900 
3901     return rr;
3902 }
3903 
3904 
3905 
DoAndroidPreferences(void)3906 bool DoAndroidPreferences( void )
3907 {
3908     wxLogMessage(_T("Start DoAndroidPreferences"));
3909 
3910     wxString settings = BuildAndroidSettingsString();
3911 
3912     wxLogMessage(_T("Call InvokeJNIPreferences"));
3913     InvokeJNIPreferences(settings);
3914 
3915     return true;
3916 }
3917 
doAndroidPOST(const wxString & url,wxString & parms,int timeoutMsec)3918 wxString doAndroidPOST( const wxString &url, wxString &parms, int timeoutMsec)
3919 {
3920     //  Start a timer to poll for results
3921     if(g_androidUtilHandler){
3922         g_androidUtilHandler->m_eventTimer.Stop();
3923         g_androidUtilHandler->m_done = false;
3924 
3925         androidShowBusyIcon();
3926 
3927         wxString stimeout;
3928         stimeout.Printf(_T("%d"), timeoutMsec);
3929         wxString result = callActivityMethod_s3s( "doHttpPostAsync", url, parms, stimeout );
3930 
3931         if(result == _T("OK") ){
3932             qDebug() << "doHttpPostAsync ResultOK, starting spin loop";
3933             g_androidUtilHandler->m_action = ACTION_POSTASYNC_END;
3934             g_androidUtilHandler->m_eventTimer.Start(500, wxTIMER_CONTINUOUS);
3935 
3936             //  Spin, waiting for result
3937             while(!g_androidUtilHandler->m_done){
3938                 wxMilliSleep(50);
3939                 wxSafeYield(NULL, true);
3940             }
3941 
3942             qDebug() << "out of spin loop";
3943             g_androidUtilHandler->m_action = ACTION_NONE;
3944             g_androidUtilHandler->m_eventTimer.Stop();
3945             androidHideBusyIcon();
3946 
3947             wxString presult = g_androidUtilHandler->GetStringResult();
3948 
3949             return presult;
3950         }
3951         else{
3952             qDebug() << "doHttpPostAsync Result NOT OK";
3953             androidHideBusyIcon();
3954         }
3955     }
3956 
3957     return wxEmptyString;
3958 }
3959 
3960 
startAndroidFileDownload(const wxString & url,const wxString & destination,wxEvtHandler * evh,long * dl_id)3961 int startAndroidFileDownload( const wxString &url, const wxString& destination, wxEvtHandler *evh, long *dl_id )
3962 {
3963 //    if(evh)
3964     {
3965         s_bdownloading = true;
3966         s_requested_url = url;
3967         s_download_evHandler = evh;
3968 
3969         wxString result = callActivityMethod_s2s( "downloadFile", url, destination );
3970 
3971         androidShowBusyIcon();
3972 
3973         if( result.IsSameAs(_T("NOK")) )
3974             return 1;                       // general error
3975 
3976   //      wxLogMessage(_T("downloads2s result: ") + result);
3977         long dl_ID;
3978         wxStringTokenizer tk(result, _T(";"));
3979         if( tk.HasMoreTokens() ){
3980             wxString token = tk.GetNextToken();
3981             if(token.IsSameAs(_T("OK"))){
3982                 token = tk.GetNextToken();
3983                 token.ToLong(&dl_ID);
3984                 *dl_id = dl_ID;
3985   //              qDebug() << dl_ID;
3986                 return 0;
3987             }
3988         }
3989     }
3990 
3991     return -1;
3992 }
3993 
queryAndroidFileDownload(long dl_ID,wxString * result)3994 int queryAndroidFileDownload( long dl_ID, wxString *result )
3995 {
3996 //    qDebug() << dl_ID;
3997 
3998     wxString stat = callActivityMethod_is( "getDownloadStatus", (int)dl_ID );
3999     if(result)
4000         *result = stat;
4001 
4002 //    wxLogMessage( _T("queryAndroidFileDownload: ") + stat);
4003 
4004     if( stat.IsSameAs(_T("NOK")) )
4005         return 1;                       // general error
4006     else
4007         return 0;
4008 
4009 }
4010 
finishAndroidFileDownload(void)4011 void finishAndroidFileDownload( void )
4012 {
4013     s_bdownloading = false;
4014     s_requested_url.Clear();
4015     s_download_evHandler = NULL;
4016     androidHideBusyIcon();
4017 
4018     return;
4019 }
4020 
4021 
cancelAndroidFileDownload(long dl_ID)4022 void cancelAndroidFileDownload( long dl_ID )
4023 {
4024     wxString stat = callActivityMethod_is( "cancelDownload", (int)dl_ID );
4025 }
4026 
4027 
AndroidUnzip(wxString & zipFile,wxString & destDir,int nStrip,bool bRemoveZip)4028 bool AndroidUnzip(wxString& zipFile, wxString& destDir, int nStrip, bool bRemoveZip)
4029 {
4030     wxString ns;
4031     ns.Printf(_T("%d"), nStrip);
4032 
4033     wxString br;
4034     br.Printf(_T("%d"), bRemoveZip);
4035 
4036     wxString stat = callActivityMethod_s4s( "unzipFile", zipFile, destDir, ns, br  );
4037 
4038     if(wxNOT_FOUND == stat.Find(_T("OK")))
4039         return false;
4040 
4041     qDebug() << "unzip start";
4042 
4043     bool bDone = false;
4044     while (!bDone){
4045         wxMilliSleep(1000);
4046         wxSafeYield(NULL, true);
4047 
4048         qDebug() << "unzip poll";
4049 
4050         wxString result = callActivityMethod_ss( "getUnzipStatus", _T("") );
4051         if(wxNOT_FOUND != result.Find(_T("DONE")))
4052             bDone = true;
4053     }
4054     qDebug() << "unzip done";
4055 
4056     return true;
4057 
4058 }
4059 
4060 
getFontQtStylesheet(wxFont * font)4061 wxString getFontQtStylesheet(wxFont *font)
4062 {
4063     // wxString classes = _T("QLabel, QPushButton, QTreeWidget, QTreeWidgetItem, QCheckBox");
4064     wxString classes = _T("QWidget ");
4065 
4066     wxString qstyle = classes + _T("{  font-family: ") + font->GetFaceName() + _T(";font-style: ");
4067     switch(font->GetStyle()){
4068         case wxFONTSTYLE_ITALIC:
4069             qstyle += _T("italic;");
4070             break;
4071         case wxFONTSTYLE_NORMAL:
4072         default:
4073             qstyle += _T("normal;");
4074             break;
4075     }
4076     qstyle += _T("font-weight: ");
4077     switch(font->GetWeight()){
4078         case wxFONTWEIGHT_BOLD:
4079             qstyle += _T("bold;");
4080             break;
4081         case wxFONTWEIGHT_LIGHT:
4082             qstyle += _T("light;");
4083             break;
4084         case wxFONTWEIGHT_NORMAL:
4085         default:
4086             qstyle += _T("normal;");
4087             break;
4088     }
4089 
4090     qstyle += _T("font-size: ");
4091     wxString fontSize;
4092     fontSize.Printf(_T("%dpt }"), font->GetPointSize());
4093     qstyle += fontSize;
4094 
4095     //  Oddity here....
4096     //  If this line is active, this particular style is applied to ListCtrl() in PlugIns,
4097     //  But not TreeCtrl.....
4098     //  ????
4099     //qstyle += _T("QTreeWidget::item{ border-color:red; border-style:outset; border-width:2px; color:black; }");
4100 
4101     return qstyle;
4102 
4103 }
4104 
4105 
4106 
androidPlaySound(wxString soundfile,AudioDoneCallback callBack)4107 bool androidPlaySound( wxString soundfile, AudioDoneCallback callBack )
4108 {
4109     //qDebug() << "androidPlay";
4110     s_soundCallBack = callBack;
4111     wxString result = callActivityMethod_ss("playSound", soundfile);
4112 
4113     return true;
4114 }
4115 
4116 extern "C"{
Java_org_opencpn_OCPNNativeLib_onSoundFinished(JNIEnv * env,jobject obj)4117     JNIEXPORT jint JNICALL Java_org_opencpn_OCPNNativeLib_onSoundFinished(JNIEnv *env, jobject obj)
4118     {
4119         qDebug() << "onSoundFinished";
4120 
4121         if(s_soundCallBack){
4122             wxCommandEvent evt(wxEVT_COMMAND_MENU_SELECTED);
4123             evt.SetId( ID_CMD_SOUND_FINISHED );
4124             if(g_androidUtilHandler)
4125                 g_androidUtilHandler->AddPendingEvent(evt);
4126         }
4127 
4128 
4129         return 98;
4130     }
4131 }
4132 
androidGetSupplementalLicense(void)4133 wxString androidGetSupplementalLicense( void )
4134 {
4135     wxString result = callActivityMethod_vs("getGMAPILicense");
4136 
4137     result += AndroidSuppLicense;
4138 
4139     return result;
4140 }
4141 
4142 
4143 
4144 
androidTraverseDir(wxString dir,wxString filespec)4145 wxArrayString androidTraverseDir( wxString dir, wxString filespec)
4146 {
4147     wxArrayString result;
4148     if(strncmp(android_plat_spc.msdk, "17", 2))   // skip unless running Android 4.2.2, especially Samsung...
4149         return result;
4150 
4151     wxString ir = callActivityMethod_s2s("getAllFilesWithFilespec", dir, filespec);
4152 
4153     wxStringTokenizer tk(ir, _T(";"));
4154     while( tk.HasMoreTokens() ){
4155         result.Add(tk.GetNextToken());
4156     }
4157 
4158     return result;
4159 }
4160 
androidEnableOptionsMenu(bool bEnable)4161 void androidEnableOptionsMenu( bool bEnable )
4162 {
4163     callActivityMethod_is("enableOptionsMenu", bEnable?1:0);
4164 }
4165 
4166 
4167 //    Android specific style sheet management
4168 
4169 //  ------------Runtime modified globals
4170 QString qtStyleSheetDialog;
4171 QString qtStyleSheetListBook;
4172 QString qtStyleSheetScrollbars;
4173 
4174 //--------------Stylesheet prototypes
4175 
4176 //  Generic dialog stylesheet
4177 //  Typically adjusted at runtime for display density
4178 
4179 QString qtStyleSheetDialogProto = "\
4180 QSlider::groove\
4181 {\
4182     border: 1px solid #999999;\
4183     background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #E6E6E6, stop:1 #EEEEEE);\
4184 }\
4185 QSlider::groove:disabled\
4186 {\
4187     background: #efefef;\
4188 }\
4189 \
4190 QSlider::handle\
4191 {\
4192     background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #7cb0e9, stop:1 #7cb0e9);\
4193     border: 1px solid #5c5c5c;\
4194     border-radius: 3px;\
4195     width: 80px;\
4196     height: 45px;\
4197 }\
4198 \
4199 QSlider::handle:disabled\
4200 {\
4201     background: #D3D0CD;\
4202 }\
4203 \
4204 QScrollBar:horizontal {\
4205     border: 0px solid grey;\
4206     background-color: transparent;\
4207     height: 4px;\
4208     margin: 0px 1px 0 1px;\
4209 }\
4210 QScrollBar::handle:horizontal {\
4211     background-color: rgb(150, 150, 150);\
4212     min-width: 20px;\
4213 }\
4214 QScrollBar::add-line:horizontal {\
4215     border: 0px solid grey;\
4216     background: transparent;\
4217     width: 1px;\
4218     subcontrol-position: right;\
4219     subcontrol-origin: margin;\
4220 }\
4221 \
4222 QScrollBar::sub-line:horizontal {\
4223     border: 0px solid grey;\
4224     background: transparent;\
4225     width: 1px;\
4226     subcontrol-position: left;\
4227     subcontrol-origin: margin;\
4228 }\
4229 \
4230 QScrollBar:vertical {\
4231     border: 0px solid grey;\
4232     background-color: transparent;\
4233     width: 4px;\
4234     margin: 1px 0px 1px 0px;\
4235 }\
4236 QScrollBar::handle:vertical {\
4237     background-color: rgb(150, 150, 150);\
4238     min-height: 20px;\
4239 }\
4240 QScrollBar::add-line:vertical {\
4241     border: 0px solid grey;\
4242     background: transparent;\
4243     height: 1px;\
4244     subcontrol-position: top;\
4245     subcontrol-origin: margin;\
4246 }\
4247 \
4248 QScrollBar::sub-line:vertical {\
4249     border: 0px solid grey;\
4250     background: transparent;\
4251     height: 1px;\
4252     subcontrol-position: bottom;\
4253     subcontrol-origin: margin;\
4254 }\
4255 \
4256 QTreeWidget QScrollBar:vertical {\
4257     border: 0px solid grey;\
4258     background-color: rgb(240, 240, 240);\
4259     width: 35px;\
4260     margin: 1px 0px 1px 0px;\
4261 }\
4262 QTreeWidget QScrollBar::handle:vertical {\
4263     background-color: rgb(200, 200, 200);\
4264     min-height: 20px;\
4265     border-radius: 10px;\
4266 }\
4267 QTreeWidget QScrollBar::add-line:vertical {\
4268     border: 0px solid grey;\
4269     background: #32CC99;\
4270     height: 0px;\
4271     subcontrol-position: top;\
4272     subcontrol-origin: margin;\
4273 }\
4274 \
4275 QTreeWidget QScrollBar::sub-line:vertical {\
4276     border: 0px solid grey;\
4277     background: #32CC99;\
4278     height: 0px;\
4279     subcontrol-position: bottom;\
4280     subcontrol-origin: margin;\
4281 }\
4282 \
4283 QTreeWidget QScrollBar:horizontal {\
4284     border: 0px solid grey;\
4285     background-color: rgb(240, 240, 240);\
4286     height: 35px;\
4287     margin: 0px 1px 0 1px;\
4288 }\
4289 QTreeWidget QScrollBar::handle:horizontal {\
4290     background-color: rgb(200, 200, 200);\
4291     min-width: 20px;\
4292     border-radius: 10px;\
4293 }\
4294 QTreeWidget QScrollBar::add-line:horizontal {\
4295     border: 0px solid grey;\
4296     background: #32CC99;\
4297     width: 0px;\
4298     subcontrol-position: right;\
4299     subcontrol-origin: margin;\
4300 }\
4301 \
4302 QTreeWidget QScrollBar::sub-line:horizontal {\
4303     border: 0px solid grey;\
4304     background: #32CC99;\
4305     width: 0px;\
4306     subcontrol-position: left;\
4307     subcontrol-origin: margin;\
4308 }\
4309 \
4310 #OCPNCheckedListCtrl QScrollBar::vertical {\
4311     border: 0px solid grey;\
4312     background-color: rgb(240, 240, 240);\
4313     width: 45px;\
4314     margin: 1px 0px 1px 0px;\
4315 }\
4316 #OCPNCheckedListCtrl QScrollBar::handle:vertical {\
4317     background-color: rgb(180, 180, 180);\
4318     min-height: 45px;\
4319     border-radius: 6px;\
4320 }\
4321 #OCPNCheckedListCtrl QScrollBar::add-line:vertical {\
4322     border: 0px solid grey;\
4323     background: #32CC99;\
4324     height: 0px;\
4325     subcontrol-position: top;\
4326     subcontrol-origin: margin;\
4327 }\
4328 \
4329 #OCPNCheckedListCtrl QScrollBar::sub-line:vertical {\
4330     border: 0px solid grey;\
4331     background: #32CC99;\
4332     height: 0px;\
4333     subcontrol-position: bottom;\
4334     subcontrol-origin: margin;\
4335 }";
4336 
4337 
4338 
4339 QString qtStyleSheetScrollbarsProto ="\
4340 QScrollBar:horizontal {\
4341     border: 0px solid grey;\
4342     background-color: transparent;\
4343     height: 35px;\
4344     margin: 0px 1px 0 1px;\
4345 }\
4346 QScrollBar::handle:horizontal {\
4347     background-color: #7cb0e9;\
4348     min-width: 20px;\
4349 }\
4350 QScrollBar::add-line:horizontal {\
4351     border: 0px solid grey;\
4352     background: transparent;\
4353     width: 1px;\
4354     subcontrol-position: right;\
4355     subcontrol-origin: margin;\
4356 }\
4357 \
4358 QScrollBar::sub-line:horizontal {\
4359     border: 0px solid grey;\
4360     background: transparent;\
4361     width: 1px;\
4362     subcontrol-position: left;\
4363     subcontrol-origin: margin;\
4364 }\
4365 \
4366 QScrollBar:vertical {\
4367     border: 0px solid grey;\
4368     background-color: transparent;\
4369     width: 35px;\
4370     margin: 1px 0px 1px 0px;\
4371 }\
4372 QScrollBar::handle:vertical {\
4373     background-color: #7cb0e9;\
4374     min-height: 20px;\
4375 }\
4376 QScrollBar::add-line:vertical {\
4377     border: 0px solid grey;\
4378     background: transparent;\
4379     height: 1px;\
4380     subcontrol-position: top;\
4381     subcontrol-origin: margin;\
4382 }\
4383 \
4384 QScrollBar::sub-line:vertical {\
4385     border: 0px solid grey;\
4386     background: transparent;\
4387     height: 1px;\
4388     subcontrol-position: bottom;\
4389     subcontrol-origin: margin;\
4390 }";
4391 
4392 
4393 
prepareStyleIcon(wxString icon_file,int size)4394 std::string prepareStyleIcon( wxString icon_file, int size )
4395 {
4396     wxString data_locn = g_Platform->GetSharedDataDir();
4397     data_locn.Append( _T("styles/") );
4398 
4399     wxString file = data_locn + icon_file;
4400 
4401     wxImage Image(file, wxBITMAP_TYPE_PNG);
4402     wxImage scaledImage = Image.Scale( size, size, wxIMAGE_QUALITY_HIGH );
4403 
4404     wxString save_file = g_Platform->GetPrivateDataDir() + _T("/") + icon_file;
4405     scaledImage.SaveFile(save_file, wxBITMAP_TYPE_PNG);
4406 
4407     wxCharBuffer buf = save_file.ToUTF8();
4408     std::string ret(buf);
4409     return ret;
4410 }
4411 
prepareAndroidSliderStyleSheet(int sliderWidth)4412 QString prepareAndroidSliderStyleSheet(int sliderWidth)
4413 {
4414     QString qtStyleSheetSlider;
4415 
4416 
4417     //  Create and fix up the qtStyleSheetDialog for generic dialog
4418 
4419     // adjust the Slider specification
4420 
4421     int slider_handle_width = wxMax(g_Platform->GetDisplayDPmm() * 6, sliderWidth / 5);
4422 
4423     char sb[600];
4424     snprintf(sb, sizeof(sb),
4425     "QSlider::groove { border: 1px solid #999999;  background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #E6E6E6, stop:1 #EEEEEE); } \
4426     QSlider::groove:disabled { background: #efefef; } \
4427     QSlider::handle { background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #7cb0e9, stop:1 #7cb0e9); border: 1px solid #5c5c5c; \
4428     border-radius: 3px; width: %dpx; height: 45px; } \
4429     QSlider::handle:disabled { background: #D3D0CD;}", slider_handle_width);
4430 
4431     qtStyleSheetSlider.append(sb);
4432 
4433     return qtStyleSheetSlider;
4434 }
4435 
4436 
prepareAndroidStyleSheets()4437 void prepareAndroidStyleSheets()
4438 {
4439 
4440     //  Create and fix up the qtStyleSheetDialog for generic dialog
4441     qtStyleSheetDialog.clear();
4442     qtStyleSheetDialog.append(qtStyleSheetDialogProto);
4443 
4444     // add the Slider specification
4445 
4446     int slider_handle_width = g_Platform->GetDisplayDPmm() * 6;
4447 
4448     char sb[400];
4449     snprintf(sb, sizeof(sb),
4450     "QSlider::groove { border: 1px solid #999999;  background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #E6E6E6, stop:1 #EEEEEE); } \
4451     QSlider::groove:disabled { background: #efefef; } \
4452     QSlider::handle { background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #7cb0e9, stop:1 #7cb0e9); border: 1px solid #5c5c5c; \
4453     border-radius: 3px; width: %dpx; height: 45px; } \
4454     QSlider::handle:disabled { background: #D3D0CD;}", slider_handle_width);
4455 
4456     qtStyleSheetDialog.append(sb);
4457 
4458 
4459 
4460     // add the checkbox specification
4461     int cbSize = 30 * getAndroidDisplayDensity();
4462     char cb[400];
4463 
4464     // icons
4465      // Checked box
4466     std::string cbs = prepareStyleIcon(_T("chek_full.png"), cbSize);
4467     //  Empty box
4468     std::string ucbs = prepareStyleIcon(_T("chek_empty.png"), cbSize);
4469 
4470     snprintf(cb, sizeof(cb), "QCheckBox { spacing: 25px;}\
4471     QCheckBox::indicator { width: %dpx;   height: %dpx;}\
4472     QCheckBox::indicator:checked {image: url(%s);}\
4473     QCheckBox::indicator:unchecked {image: url(%s);}", cbSize, cbSize, cbs.c_str(), ucbs.c_str());
4474 
4475     qtStyleSheetDialog.append(cb);
4476 
4477 
4478     //   The qTabBar buttons as in a listbook
4479     qtStyleSheetListBook.clear();
4480 
4481     // compute the tabbar button size
4482     int tbbSize = 50 * getAndroidDisplayDensity();
4483     char tbb[400];
4484 
4485 
4486     std::string tbbl = prepareStyleIcon(_T("tabbar_button_left.png"), tbbSize);
4487     std::string tbbr = prepareStyleIcon(_T("tabbar_button_right.png"), tbbSize);
4488 
4489     snprintf(tbb, sizeof(tbb), "QTabBar::scroller { width: %dpx; }\
4490     QTabBar QToolButton::right-arrow { image: url(%s); }\
4491     QTabBar QToolButton::left-arrow { image: url(%s); }", tbbSize, tbbr.c_str(), tbbl.c_str());
4492 
4493     qtStyleSheetListBook.append(tbb);
4494 
4495 
4496     // A simple stylesheet with scrollbars only
4497     qtStyleSheetScrollbars.clear();
4498     qtStyleSheetScrollbars.append(qtStyleSheetScrollbarsProto);
4499 }
4500 
setChoiceStyleSheet(wxChoice * win,int refDim)4501 void setChoiceStyleSheet( wxChoice *win, int refDim)
4502 {
4503     //qDebug() << "refDim" << refDim;
4504 
4505     float fontDimFloat = ((float)refDim) * 0.5;
4506     int fontDim = (int)fontDimFloat;
4507     int pixRadius = refDim / 4;
4508 
4509     QString styleString;
4510     char sb[1400];
4511 
4512 
4513      //  This one control the appearance of the "un-dropped" control.
4514     snprintf(sb, sizeof(sb),
4515              "QComboBox { font-size: %dpx; font-weight: bold; min-height: %dpx; color: rgb(0,0,0); background-color: rgb(250,250,250); }", fontDim, refDim );
4516     styleString.append(sb);
4517 
4518     // This one controls the color and style of the drop list items
4519     snprintf(sb, sizeof(sb),
4520              "QComboBox QListView::item { color: rgb(0,0,0); background-color: rgb(95, 163, 237); }");
4521     styleString.append(sb);
4522 
4523 
4524     // This one controls the drop list font
4525     snprintf(sb, sizeof(sb),
4526              "QComboBox QAbstractItemView { font-size: %dpx; font-weight: bold;}", fontDim);
4527     styleString.append(sb);
4528 
4529     // This one is necessary to set min height of drop list items, otherwise they are squished.
4530     snprintf(sb, sizeof(sb),
4531              "QComboBox QAbstractItemView::item {  min-height: %dpx; border: 10px outset darkgray; border-radius: %dpx;  }", refDim, pixRadius);
4532     styleString.append(sb);
4533 
4534     //qDebug() << styleString;
4535 
4536     win->GetHandle()->setView(new QListView());         // Magic
4537     win->GetHandle()->setStyleSheet(styleString);
4538 
4539 
4540 }
4541 
4542 
setMenuStyleSheet(wxMenu * win,const wxFont & font)4543 void setMenuStyleSheet( wxMenu *win, const wxFont& font)
4544 {
4545     if(!win)
4546         return;
4547 
4548     int points = font.GetPointSize();
4549     int fontPix = points / g_Platform->getFontPointsperPixel();
4550 
4551     //qDebug() << points << g_Platform->getFontPointsperPixel() << fontPix;
4552 
4553     QString styleString;
4554     char sb[1400];
4555 
4556     snprintf(sb, sizeof(sb),
4557              "QMenu { font: bold %dpx; }", fontPix );
4558     styleString.append(sb);
4559 
4560     snprintf(sb, sizeof(sb),
4561              "QMenu::separator { height: 4px; background: lightblue; margin-left: 10px; margin-right: 5px; }");
4562     styleString.append(sb);
4563 
4564 
4565     //qDebug() << styleString;
4566 
4567     win->GetHandle()->setStyleSheet(styleString);
4568 }
4569 
4570 
getAdjustedDialogStyleSheet()4571 QString getAdjustedDialogStyleSheet()
4572 {
4573     return qtStyleSheetDialog;
4574 }
4575 
getListBookStyleSheet()4576 QString getListBookStyleSheet()
4577 {
4578     return qtStyleSheetListBook;
4579 }
4580 
getScrollBarsStyleSheet()4581 QString getScrollBarsStyleSheet()
4582 {
4583     return qtStyleSheetScrollbars;
4584 }
4585 
4586 
4587 //      SVG Support
loadAndroidSVG(const wxString filename,unsigned int width,unsigned int height)4588 wxBitmap loadAndroidSVG( const wxString filename, unsigned int width, unsigned int height )
4589 {
4590     wxCharBuffer abuf = filename.ToUTF8();
4591     if( abuf.data() ){                            // OK conversion?
4592         std::string s(abuf.data());
4593         //qDebug() << "loadAndroidSVG" << s.c_str();
4594     }
4595     else{
4596         qDebug() << "loadAndroidSVG FAIL";
4597     }
4598 
4599     // Destination file location
4600     wxString save_file_dir = g_Platform->GetPrivateDataDir() + _T("/") + _T("icons");
4601     if( !wxDirExists(save_file_dir) )
4602         wxMkdir( save_file_dir);
4603 
4604     wxFileName fsvg(filename);
4605     wxFileName fn(save_file_dir + _T("/") + fsvg.GetFullName());
4606     fn.SetExt(_T("png"));
4607 
4608 /*
4609        //Caching does not work well, since we always build each icon twice.
4610     if(fn.FileExists()){
4611         wxBitmap bmp_test(fn.GetFullPath(), wxBITMAP_TYPE_PNG);
4612         if(bmp_test.IsOk()){
4613             if((bmp_test.GetWidth() == (int)width) && (bmp_test.GetHeight() == (int)height))
4614                 return bmp_test;
4615         }
4616     }
4617 */
4618 
4619     wxString val = callActivityMethod_s2s2i("buildSVGIcon", filename, fn.GetFullPath(), width, height);
4620     if( val == _T("OK") ){
4621 //        qDebug() << "OK";
4622 
4623         return wxBitmap(fn.GetFullPath(), wxBITMAP_TYPE_PNG);
4624     }
4625     else{
4626         return wxBitmap(width, height);
4627     }
4628 }
4629 
androidTestCPP()4630 void androidTestCPP()
4631 {
4632     callActivityMethod_vs("callFromCpp");
4633 }
4634 
androidColorPicker(unsigned int initialColor)4635 unsigned int androidColorPicker( unsigned int initialColor)
4636 {
4637     if(g_androidUtilHandler){
4638         g_androidUtilHandler->m_eventTimer.Stop();
4639         g_androidUtilHandler->m_done = false;
4640 
4641         wxString val = callActivityMethod_is("doColorPickerDialog", initialColor);
4642 
4643 
4644         if(val == _T("OK") ){
4645             //qDebug() << "ResultOK, starting spin loop";
4646             g_androidUtilHandler->m_action = ACTION_COLORDIALOG_END;
4647             g_androidUtilHandler->m_eventTimer.Start(1000, wxTIMER_CONTINUOUS);
4648 
4649             //  Spin, waiting for result
4650             while(!g_androidUtilHandler->m_done){
4651                 wxMilliSleep(50);
4652                 wxSafeYield(NULL, true);
4653             }
4654 
4655             //qDebug() << "out of spin loop";
4656             g_androidUtilHandler->m_action = ACTION_NONE;
4657             g_androidUtilHandler->m_eventTimer.Stop();
4658 
4659 
4660             wxString tresult = g_androidUtilHandler->GetStringResult();
4661 
4662             if( tresult.StartsWith(_T("cancel:")) ){
4663                 //qDebug() << "Cancel1";
4664                 return initialColor;
4665             }
4666             else if( tresult.StartsWith(_T("color:")) ){
4667                 wxString color = tresult.AfterFirst(':');
4668                 long a;
4669                 color.ToLong(&a);
4670                 unsigned int b = a;
4671 
4672                 //char cc[30];
4673                 //sprintf(cc, "%0X", b);
4674                 //qDebug() << "OK " << cc;
4675 
4676                 return b;
4677             }
4678         }
4679         else{
4680             qDebug() << "Result NOT OK";
4681         }
4682     }
4683     return 0;
4684 }
4685 
AndroidSecureCopyFile(wxString in,wxString out)4686 bool AndroidSecureCopyFile(wxString in, wxString out)
4687 {
4688     bool bret = true;
4689 
4690     wxString result = callActivityMethod_s2s("SecureFileCopy", in, out);
4691 
4692     if(wxNOT_FOUND == result.Find(_T("OK")))
4693         bret = false;
4694 
4695     return bret;
4696 }
4697 
doAndroidPersistState()4698 int doAndroidPersistState()
4699 {
4700     qDebug() << "doAndroidPersistState() starting...";
4701     wxLogMessage( _T("doAndroidPersistState() starting...") );
4702 
4703     // We save perspective before closing to restore position next time
4704     // Pane is not closed so the child is not notified (OnPaneClose)
4705     if(g_pauimgr){
4706         if( g_pAISTargetList ) {
4707             wxAuiPaneInfo &pane = g_pauimgr->GetPane( g_pAISTargetList );
4708             g_AisTargetList_perspective = g_pauimgr->SavePaneInfo( pane );
4709             g_pauimgr->DetachPane( g_pAISTargetList );
4710 
4711             pConfig->SetPath( _T ( "/AUI" ) );
4712             pConfig->Write( _T ( "AUIPerspective" ), g_pauimgr->SavePerspective() );
4713         }
4714     }
4715 
4716 
4717 
4718     //    Deactivate the PlugIns, allowing them to save state
4719     if( g_pi_manager ) {
4720         g_pi_manager->DeactivateAllPlugIns();
4721     }
4722 
4723     /*
4724      Automatically drop an anchorage waypoint, if enabled
4725      On following conditions:
4726      1.  In "Cruising" mode, meaning that speed has at some point exceeded 3.0 kts.
4727      2.  Current speed is less than 0.5 kts.
4728      3.  Opencpn has been up at least 30 minutes
4729      4.  And, of course, opencpn is going down now.
4730      5.  And if there is no anchor watch set on "anchor..." icon mark           // pjotrc 2010.02.15
4731      */
4732     if( g_bAutoAnchorMark ) {
4733         bool watching_anchor = false;                                           // pjotrc 2010.02.15
4734         if( pAnchorWatchPoint1 )                                               // pjotrc 2010.02.15
4735         watching_anchor = ( pAnchorWatchPoint1->GetIconName().StartsWith( _T("anchor") ) ); // pjotrc 2010.02.15
4736         if( pAnchorWatchPoint2 )                                               // pjotrc 2010.02.15
4737         watching_anchor |= ( pAnchorWatchPoint2->GetIconName().StartsWith( _T("anchor") ) ); // pjotrc 2010.02.15
4738 
4739         wxDateTime now = wxDateTime::Now();
4740         wxTimeSpan uptime = now.Subtract( g_start_time );
4741 
4742         if( !watching_anchor && ( g_bCruising ) && ( gSog < 0.5 )
4743                 && ( uptime.IsLongerThan( wxTimeSpan( 0, 30, 0, 0 ) ) ) )     // pjotrc 2010.02.15
4744                 {
4745             //    First, delete any single anchorage waypoint closer than 0.25 NM from this point
4746             //    This will prevent clutter and database congestion....
4747 
4748             wxRoutePointListNode *node = pWayPointMan->GetWaypointList()->GetFirst();
4749             while( node ) {
4750                 RoutePoint *pr = node->GetData();
4751                 if( pr->GetName().StartsWith( _T("Anchorage") ) ) {
4752                     double a = gLat - pr->m_lat;
4753                     double b = gLon - pr->m_lon;
4754                     double l = sqrt( ( a * a ) + ( b * b ) );
4755 
4756                     // caveat: this is accurate only on the Equator
4757                     if( ( l * 60. * 1852. ) < ( .25 * 1852. ) ) {
4758                         pConfig->DeleteWayPoint( pr );
4759                         pSelect->DeleteSelectablePoint( pr, SELTYPE_ROUTEPOINT );
4760                         delete pr;
4761                         break;
4762                     }
4763                 }
4764 
4765                 node = node->GetNext();
4766             }
4767 
4768             wxString name = now.Format();
4769             name.Prepend( _("Anchorage created ") );
4770             RoutePoint *pWP = new RoutePoint( gLat, gLon, _T("anchorage"), name, _T("") );
4771             pWP->m_bShowName = false;
4772             pWP->m_bIsolatedMark = true;
4773 
4774             pConfig->AddNewWayPoint( pWP, -1 );       // use auto next num
4775         }
4776     }
4777 
4778     if( gFrame->GetPrimaryCanvas()->GetpCurrentStack() ) {
4779         g_restore_stackindex = gFrame->GetPrimaryCanvas()->GetpCurrentStack()->CurrentStackEntry;
4780         g_restore_dbindex = gFrame->GetPrimaryCanvas()->GetpCurrentStack()->GetCurrentEntrydbIndex();
4781         if(gFrame->GetPrimaryCanvas() && gFrame->GetPrimaryCanvas()->GetQuiltMode())
4782             g_restore_dbindex = gFrame->GetPrimaryCanvas()->GetQuiltReferenceChartIndex();
4783     }
4784 
4785     if( g_MainToolbar ) {
4786         wxPoint tbp = g_MainToolbar->GetPosition();
4787         wxPoint tbp_incanvas = gFrame->GetPrimaryCanvas()->ScreenToClient( tbp );
4788         g_maintoolbar_x = tbp_incanvas.x;
4789         g_maintoolbar_y = tbp_incanvas.y;
4790         g_maintoolbar_orient = g_MainToolbar->GetOrient();
4791     }
4792 
4793     if(g_iENCToolbar){
4794         wxPoint locn = g_iENCToolbar->GetPosition();
4795         wxPoint tbp_incanvas = gFrame->GetPrimaryCanvas()->ScreenToClient( locn );
4796         g_iENCToolbarPosY = tbp_incanvas.y;
4797         g_iENCToolbarPosX = tbp_incanvas.x;
4798     }
4799 
4800     pConfig->UpdateSettings();
4801     pConfig->UpdateNavObj();
4802 
4803     delete pConfig->m_pNavObjectChangesSet;
4804 
4805     //Remove any leftover Routes and Waypoints from config file as they were saved to navobj before
4806     pConfig->DeleteGroup( _T ( "/Routes" ) );
4807     pConfig->DeleteGroup( _T ( "/Marks" ) );
4808     pConfig->Flush();
4809 
4810     delete pConfig;             // All done
4811     pConfig = NULL;
4812 
4813 
4814     //    Unload the PlugIns
4815     //      Note that we are waiting until after the canvas is destroyed,
4816     //      since some PlugIns may have created children of canvas.
4817     //      Such a PlugIn must stay intact for the canvas dtor to call DestoryChildren()
4818 
4819     if(ChartData)
4820         ChartData->PurgeCachePlugins();
4821 
4822     if( g_pi_manager ) {
4823         g_pi_manager->UnLoadAllPlugIns();
4824         delete g_pi_manager;
4825         g_pi_manager = NULL;
4826     }
4827 
4828     wxLogMessage( _T("doAndroidPersistState() finished cleanly.") );
4829     qDebug() << "doAndroidPersistState() finished cleanly.";
4830 
4831     wxLogMessage( _T("Closing logfile, Terminating App.") );
4832 
4833     wxLog::FlushActive();
4834     g_Platform->CloseLogFile();
4835 
4836     return 0;
4837 }
4838 
4839 
4840 
4841 
4842 extern "C"{
Java_org_opencpn_OCPNNativeLib_ScheduleCleanExit(JNIEnv * env,jobject obj)4843     JNIEXPORT int JNICALL Java_org_opencpn_OCPNNativeLib_ScheduleCleanExit(JNIEnv *env, jobject obj)
4844     {
4845         qDebug() << "Java_org_opencpn_OCPNNativeLib_ScheduleCleanExit";
4846         wxCommandEvent evt(wxEVT_COMMAND_MENU_SELECTED);
4847         evt.SetId( SCHEDULED_EVENT_CLEAN_EXIT );
4848         if(g_androidUtilHandler ){
4849             g_androidUtilHandler->AddPendingEvent(evt);
4850         }
4851 
4852         return 1;
4853     }
4854 }
4855 
4856 
4857 
4858