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