· 7 years ago · Feb 12, 2019, 05:58 AM
1package com.cityworks.View.Activities;
2
3import android.annotation.SuppressLint;
4import android.content.BroadcastReceiver;
5import android.content.Context;
6import android.content.Intent;
7import android.content.IntentFilter;
8import android.graphics.drawable.BitmapDrawable;
9import android.os.AsyncTask;
10import android.os.Bundle;
11import android.support.annotation.NonNull;
12import android.support.v4.content.ContextCompat;
13import android.support.v4.content.LocalBroadcastManager;
14import android.support.v7.widget.DividerItemDecoration;
15import android.support.v7.widget.LinearLayoutManager;
16import android.support.v7.widget.RecyclerView;
17import android.text.format.DateFormat;
18import android.util.Log;
19import android.view.LayoutInflater;
20import android.view.Menu;
21import android.view.MenuItem;
22import android.view.MotionEvent;
23import android.view.View;
24import android.view.ViewGroup;
25import android.widget.Button;
26import android.widget.ImageButton;
27import android.widget.ImageView;
28import android.widget.ProgressBar;
29import android.widget.TextView;
30import android.widget.Toast;
31
32import com.cityworks.AppConstants;
33import com.cityworks.AppState;
34import com.cityworks.Model.Interfaces.IDbDataAccess;
35import com.cityworks.Model.Interfaces.IDispatchable;
36import com.cityworks.Model.Interfaces.IDispatcher;
37import com.cityworks.Model.Interfaces.IFileManager;
38import com.cityworks.Model.Interfaces.ILogger;
39import com.cityworks.Model.Interfaces.INavigator;
40import com.cityworks.Model.Interfaces.IPreferenceData;
41import com.cityworks.Model.Interfaces.IProgressUpdater;
42import com.cityworks.Model.Interfaces.IResources;
43import com.cityworks.Model.Interfaces.IWorkActivityManager;
44import com.cityworks.Model.MapLayer;
45import com.cityworks.Model.MapPin;
46import com.cityworks.Model.MobileMapCacheUpdate;
47import com.cityworks.Model.Request;
48import com.cityworks.Model.SelectedEntity;
49import com.cityworks.Model.WorkOrder;
50import com.cityworks.Presentation.SyncPresenter;
51import com.cityworks.R;
52import com.cityworks.View.Activities.Common.CreateActivity;
53import com.cityworks.View.Controls.BaseDialogFragment;
54import com.cityworks.View.Controls.CreateWorkActivityDialogFragment;
55import com.cityworks.View.Controls.GeoDatabaseHelper;
56import com.cityworks.View.Controls.MapCachePromptDialog;
57import com.cityworks.View.Controls.ReauthenticateDialog;
58import com.cityworks.View.Controls.SyncErrorDialog;
59import com.cityworks.View.Controls.SyncFeatureServiceErrorDialog;
60import com.cityworks.View.Controls.SyncProgressDialog;
61import com.cityworks.View.CustomWidget.MapSelectionView;
62import com.cityworks.View.MapLayersLoadingObservable;
63import com.cityworks.View.Services.DownloadFileService;
64import com.cityworks.View.Tools.Functions;
65import com.cityworks.View.Tools.GISUtil;
66import com.cityworks.core.EntityConfiguration;
67import com.cityworks.core.GISLayer;
68import com.cityworks.core.GISServiceType;
69import com.cityworks.core.MobileMapCache;
70import com.cityworks.core.Services;
71import com.cityworks.core.SimpleGisServiceInfo;
72import com.cityworks.dataInterfaces.SearchDefinitionResultData;
73import com.cityworks.infrastructure.DateUtil;
74import com.cityworks.infrastructure.PermissionUtil;
75import com.cityworks.infrastructure.StringUtils;
76import com.cityworks.infrastructure.ValueUtil;
77import com.cityworks.models.CaObject;
78import com.cityworks.models.CaTask;
79import com.cityworks.models.Inspection;
80import com.cityworks.models.SearchDefinitionResult;
81import com.esri.arcgisruntime.arcgisservices.ArcGISMapServiceInfo;
82import com.esri.arcgisruntime.arcgisservices.MapServiceLayerIdInfo;
83import com.esri.arcgisruntime.concurrent.ListenableFuture;
84import com.esri.arcgisruntime.data.Feature;
85import com.esri.arcgisruntime.data.FeatureQueryResult;
86import com.esri.arcgisruntime.data.Geodatabase;
87import com.esri.arcgisruntime.data.GeodatabaseFeatureTable;
88import com.esri.arcgisruntime.data.QueryParameters;
89import com.esri.arcgisruntime.data.ServiceFeatureTable;
90import com.esri.arcgisruntime.data.TileCache;
91import com.esri.arcgisruntime.geometry.Envelope;
92import com.esri.arcgisruntime.geometry.Geometry;
93import com.esri.arcgisruntime.geometry.GeometryEngine;
94import com.esri.arcgisruntime.geometry.Multipoint;
95import com.esri.arcgisruntime.geometry.Point;
96import com.esri.arcgisruntime.geometry.SpatialReference;
97import com.esri.arcgisruntime.layers.ArcGISMapImageLayer;
98import com.esri.arcgisruntime.layers.ArcGISSublayer;
99import com.esri.arcgisruntime.layers.ArcGISTiledLayer;
100import com.esri.arcgisruntime.layers.ArcGISVectorTiledLayer;
101import com.esri.arcgisruntime.layers.FeatureLayer;
102import com.esri.arcgisruntime.layers.Layer;
103import com.esri.arcgisruntime.layers.RasterLayer;
104import com.esri.arcgisruntime.layers.SublayerList;
105import com.esri.arcgisruntime.loadable.LoadStatus;
106import com.esri.arcgisruntime.location.LocationDataSource;
107import com.esri.arcgisruntime.mapping.ArcGISMap;
108import com.esri.arcgisruntime.mapping.Basemap;
109import com.esri.arcgisruntime.mapping.MobileMapPackage;
110import com.esri.arcgisruntime.mapping.Viewpoint;
111import com.esri.arcgisruntime.mapping.view.Callout;
112import com.esri.arcgisruntime.mapping.view.DefaultMapViewOnTouchListener;
113import com.esri.arcgisruntime.mapping.view.DrawStatus;
114import com.esri.arcgisruntime.mapping.view.DrawStatusChangedEvent;
115import com.esri.arcgisruntime.mapping.view.DrawStatusChangedListener;
116import com.esri.arcgisruntime.mapping.view.Graphic;
117import com.esri.arcgisruntime.mapping.view.GraphicsOverlay;
118import com.esri.arcgisruntime.mapping.view.IdentifyGraphicsOverlayResult;
119import com.esri.arcgisruntime.mapping.view.LocationDisplay;
120import com.esri.arcgisruntime.mapping.view.MapView;
121import com.esri.arcgisruntime.portal.Portal;
122import com.esri.arcgisruntime.portal.PortalItem;
123import com.esri.arcgisruntime.raster.ImageServiceRaster;
124import com.esri.arcgisruntime.security.AuthenticationManager;
125import com.esri.arcgisruntime.security.DefaultAuthenticationChallengeHandler;
126import com.esri.arcgisruntime.security.UserCredential;
127import com.esri.arcgisruntime.symbology.PictureMarkerSymbol;
128import com.esri.arcgisruntime.symbology.SimpleFillSymbol;
129import com.esri.arcgisruntime.symbology.SimpleLineSymbol;
130import com.esri.arcgisruntime.util.ListenableList;
131
132import java.io.File;
133import java.util.ArrayList;
134import java.util.Date;
135import java.util.HashMap;
136import java.util.List;
137import java.util.Observable;
138import java.util.concurrent.ExecutionException;
139
140import static com.cityworks.AppConstants.FEATURE_ATTRIBUTE_OBJECT_ID;
141
142public class MapActivity extends BaseMenuActivity implements SyncPresenter.ISyncableView, ReauthenticateDialog.IReauthenticateDialogListener, MapCachePromptDialog.IMapCachePromptDialogListener, CreateWorkActivityDialogFragment.Listener, MapSelectionView.IMapSelectionViewListener {
143
144 private static final String TAG = "CW-MapActivity";
145
146 public static final String ARG_ZOOM_TO_WORK_ACTIVITY_ID = "zoomToWorkActivityId";
147 public static final String ARG_ZOOM_TO_WORK_ACTIVITY_KIND = "zoomToWorkActivityKind";
148
149 public static final String EXTRA_LAYERS_CHANGED = "EXTRA_LAYERS_CHANGED";
150 public static final String EXTRA_REMOVED_ASSET_FEATURE_LIST = "EXTRA_REMOVED_ASSET_FEATURE_LIST";
151
152 private static final int CALLOUT_CORNER_CURVE = 3;
153 private static final int CALLOUT_MAX_RESULTS = 100;
154 private static final int GRAPHIC_TOLOERANCE = 1;
155 private static final String PIN = "PIN";
156
157 private final int createActivityRequestCode = 1001;
158 private final int mapLayersActivityRequestCode = 1002;
159 private final int selectedAssetsActivityRequestCode = 1003;
160
161 private boolean isPaused;
162 private boolean pendingUpdate;
163 private String workActivityType;
164
165 private MenuItem syncMenuItem;
166 private SyncPresenter syncPresenter;
167
168 private BroadcastReceiver downloadReceiver;
169 private BroadcastReceiver workActivityUpdatedReceiver;
170
171 private Button btnContinue, btnSelected, btnCancel;
172 private ImageButton btnCreate, btnLayer;
173 private ImageButton btnStartSelection, btnClearSelection, btnLocation;
174 private MapView mapView;
175 private MapSelectionView mapSelectionView;
176 private ProgressBar pgrSelection;
177 private SyncProgressDialog syncProgressDialog;
178 private SyncErrorDialog syncErrorDialog;
179 private SyncFeatureServiceErrorDialog syncFeatureServiceErrorDialog;
180
181 private IDbDataAccess dbDataAccess;
182 private IDispatcher dispatcher;
183 private IFileManager fileManager;
184 private ILogger log;
185 private INavigator nav;
186 private IPreferenceData prefs;
187 private IResources resources;
188 private SearchDefinitionResultData searchDefinitionResult;
189 private IWorkActivityManager workActivityManager;
190
191 private boolean locationState;
192 private String zoomToWorkActivityId;
193 private String zoomToWorkActivityKind;
194
195 private boolean casesHidden;
196 private boolean inspectionsHidden;
197 private List<String> layersHidden;
198 private boolean layersDefaultVisibilityChecked;
199 private ArrayList<Integer> savedSearchesHidden;
200 private boolean serviceRequestsHidden;
201 private boolean workOrdersHidden;
202
203 private int CW_WKID;
204
205 private ArrayList<Layer> operationalLayers;
206 private ArrayList<SimpleGisServiceInfo> simpleGisServiceInfos;
207
208 private Callout callout;
209 private UserCredential credential;
210
211 private Layer baseLayer;
212 private GraphicsOverlay graphicsOverlay;
213 private FeatureLayer selectableLayer;
214 private FeatureLayer selectableGeoDatabaseLayer;
215 private Graphic selectionGraphic;
216 private GraphicsOverlay selectionOverlay;
217
218 private PictureMarkerSymbol caseIcon;
219 private PictureMarkerSymbol inspectionIcon;
220 private PictureMarkerSymbol serviceRequestIcon;
221 private PictureMarkerSymbol workOrderIcon;
222
223 private RecyclerView calloutRecyclerView;
224 private View mapCalloutMultipleView;
225 private View templateItemMapCalloutView;
226
227 private boolean locatingEnabled;
228
229 private Context context;
230
231 private ArcGISMap map;
232 private Graphic createGraphic;
233 private Point initialMapPoint;
234 private LocationDisplay locationManager;
235
236 @Override
237 public void onCreate(Bundle savedInstanceState) {
238 super.onCreate(savedInstanceState);
239 setContentView(R.layout.activity_map);
240 setActionBarTitle(R.string.title_activity_map);
241
242 context = this;
243
244 // Get any arguments.
245 String zoomToWorkActivityId = getIntent().getStringExtra(ARG_ZOOM_TO_WORK_ACTIVITY_ID);
246 String zoomToWorkActivityKind = getIntent().getStringExtra(ARG_ZOOM_TO_WORK_ACTIVITY_KIND);
247
248 // Resolve dependencies.
249 dbDataAccess = Resolver.DbDataAccess();
250 dispatcher = Resolver.Dispatcher();
251 fileManager = Resolver.FileManager();
252 log = Resolver.Logger();
253 nav = Resolver.Navigator();
254 prefs = Resolver.PreferenceData();
255 resources = Resolver.Resources();
256 searchDefinitionResult = Resolver.SearchDefinitionResultData();
257 workActivityManager = Resolver.WorkActivityManager();
258
259 btnContinue = findViewById(R.id.map_btn_continue);
260
261 btnContinue.setOnClickListener(v -> continueCreate());
262 btnCancel = findViewById(R.id.map_btn_cancel);
263 btnCancel.setOnClickListener(v -> cancelCreate());
264
265 pgrSelection = findViewById(R.id.map_selection_progress);
266
267 btnSelected = findViewById(R.id.map_btn_selected);
268 btnSelected.setOnClickListener(v -> viewSelection());
269
270 mapView = findViewById(R.id.mapViewInFrag);
271
272 btnStartSelection = findViewById(R.id.map_btn_selection);
273 btnStartSelection.setOnClickListener(v -> startSelection());
274
275 btnClearSelection = findViewById(R.id.map_btn_clear_selection);
276 btnClearSelection.setOnClickListener(v -> clearSelection());
277
278 btnLocation = findViewById(R.id.map_btn_location);
279 btnLocation.setOnClickListener(v -> {
280 if (PermissionUtil.checkLocation(MapActivity.this)) {
281 toggleLocateClicked();
282 }
283 else {
284 PermissionUtil.requestLocation(MapActivity.this);
285 }
286 });
287
288 btnCreate = findViewById(R.id.map_btn_create_workorder);
289 btnCreate.setOnClickListener(v -> {
290 CreateWorkActivityDialogFragment createWorkActivityDialogFragment = CreateWorkActivityDialogFragment.newInstance(MapActivity.this, false, false, false);
291 createWorkActivityDialogFragment.show(getSupportFragmentManager(), CreateWorkActivityDialogFragment.TAG);
292 });
293
294 btnLayer = findViewById(R.id.map_btn_select_layer);
295 btnLayer.setOnClickListener(v -> {
296 Intent intent = new Intent(MapActivity.this, MapLayersActivity.class);
297 startActivityForResult(intent, mapLayersActivityRequestCode);
298 });
299
300 mapSelectionView = findViewById(R.id.map_activity_selection_view);
301 mapSelectionView.listener = this;
302
303 syncPresenter = new SyncPresenter(this,
304 Resolver.SyncManager(),
305 resources,
306 Resolver.Services(),
307 Resolver.Dispatcher(),
308 Resolver.WorkOrderData(), Resolver.InspectionData(), Resolver.DbDataAccess(),
309 new SyncPresenter.ISyncFinishedListener() {
310 @Override
311 public void entityConfigurationSynced(boolean success) {
312 fetchGeodatabaseLayers();
313 }
314
315 @Override
316 public void SyncFinished(boolean success) {
317 syncFinished(success);
318 }
319 },
320 Resolver.PreferenceData());
321
322 syncProgressDialog = new SyncProgressDialog(context, () -> syncPresenter.cancelSync());
323 syncErrorDialog = new SyncErrorDialog(context, () -> nav.ErrorLog());
324
325 syncFeatureServiceErrorDialog = new SyncFeatureServiceErrorDialog(context, (boolean isSyncInitiated) -> {
326 GeoDatabaseHelper.getInstance().setSyncInitiated(isSyncInitiated);
327 fetchGeodatabaseLayers();
328 });
329
330 downloadReceiver = new BroadcastReceiver() {
331 @Override
332 public void onReceive(Context context, Intent intent) {
333 // Extract data included in the Intent
334 String status = intent.getStringExtra(DownloadFileService.BROADCAST_EXTRA_STATUS_KEY);
335 Log.d(TAG, "BroadcastMessage received: " + status);
336 downloadComplete(status);
337 }
338 };
339
340 // Make sure this receiver hasn't been registered.
341 if (workActivityUpdatedReceiver == null) {
342 workActivityUpdatedReceiver = new BroadcastReceiver() {
343 @Override
344 public void onReceive(Context context, Intent intent) {
345 String action = intent.getAction();
346
347 // Make sure it is the correct action.
348 if (action != null && (action.equals(AppConstants.NOTIFICATION_WORK_ACTIVITY_UPDATED) || action.equals(AppConstants.NOTIFICATION_WORK_ACTIVITY_CREATED))) {
349 displayPins(false);
350 callout.dismiss();
351
352 // Clear the current selection.
353 clearSelection();
354 }
355 }
356 };
357
358 // Watch for work activity updated broadcasts.
359 LocalBroadcastManager.getInstance(context).registerReceiver(workActivityUpdatedReceiver, new IntentFilter(AppConstants.NOTIFICATION_WORK_ACTIVITY_UPDATED));
360
361 // Watch for work activity created broadcasts.
362 LocalBroadcastManager.getInstance(context).registerReceiver(workActivityUpdatedReceiver, new IntentFilter(AppConstants.NOTIFICATION_WORK_ACTIVITY_CREATED));
363 }
364
365 // Hide the selection results button.
366 btnSelected.setVisibility(View.INVISIBLE);
367
368 // Hide the create buttons.
369 btnContinue.setVisibility(View.INVISIBLE);
370 btnCancel.setVisibility(View.INVISIBLE);
371
372 // Reset any previous selection.
373 clearSelection();
374
375 // Setup the presenters.
376 init(zoomToWorkActivityId, zoomToWorkActivityKind);
377 syncPresenter.Init();
378
379 // Set map state.
380 if (AppState.SelectableMapLayer == null) {
381 btnStartSelection.setEnabled(false);
382 btnClearSelection.setEnabled(false);
383 }
384 else {
385 btnStartSelection.setEnabled(true);
386 btnClearSelection.setEnabled(true);
387 }
388 }
389
390 public void init() {
391 init(null, null);
392 }
393
394 public void init(String zoomToWorkActivityId, String zoomToWorkActivityKind) {
395 this.zoomToWorkActivityId = zoomToWorkActivityId;
396 this.zoomToWorkActivityKind = zoomToWorkActivityKind;
397
398 CW_WKID = prefs.getSpatialReference();
399
400 // Get any hidden layers.
401 casesHidden = prefs.getCasesHidden();
402 inspectionsHidden = prefs.getInspectionsHidden();
403 layersHidden = prefs.getLayersHidden();
404 layersDefaultVisibilityChecked = prefs.getLayersDefaultVisibilityChecked();
405 savedSearchesHidden = prefs.getSavedSearchesHidden();
406 serviceRequestsHidden = prefs.getServiceRequestsHidden();
407 workOrdersHidden = prefs.getWorkOrdersHidden();
408
409 processDownloadedMapCache();
410
411 // Get the map services.
412 simpleGisServiceInfos = dbDataAccess.GetAllSimpleGisServiceInfo();
413
414 // Remove the geocode entry
415 for (int i = 0; i < simpleGisServiceInfos.size(); i++) {
416 if (simpleGisServiceInfos.get(i).ServiceType == GISServiceType.GEOCODE) {
417 simpleGisServiceInfos.remove(i);
418 break;
419 }
420 }
421
422 // Disable the map buttons.
423 enableMapButtons(false);
424
425 initiateLayerData();
426 }
427
428 @SuppressLint("ClickableViewAccessibility")
429 private void initiateLayerData() {
430
431 // Setup the credential & Only send in username and password if not empty so we don't get a crash.
432 if (!StringUtils.isNullOrWhitespace(prefs.getUsername()) && !StringUtils.isNullOrWhitespace(prefs.getPassword())) {
433 credential = new UserCredential(GISUtil.convertWindowsAuthUsernameToPortalUsername(prefs.getUsername()), prefs.getPassword());
434 }
435
436 // Specify the authentication dialog handler if needed for layer authentication.
437 DefaultAuthenticationChallengeHandler handler = new DefaultAuthenticationChallengeHandler(this);
438 AuthenticationManager.setAuthenticationChallengeHandler(handler);
439
440 // Remove all the layers and reset them.
441 map = new ArcGISMap();
442
443 baseLayer = null;
444 graphicsOverlay = null;
445 operationalLayers = new ArrayList<>();
446 selectableLayer = null;
447 selectableGeoDatabaseLayer = null;
448 selectionOverlay = null;
449 selectionGraphic = null;
450
451 // Create Map Pins icons
452 caseIcon = createMapPinIcon(R.drawable.permit_pin);
453 inspectionIcon = createMapPinIcon(R.drawable.inspection_pin);
454 serviceRequestIcon = createMapPinIcon(R.drawable.request_pin);
455 workOrderIcon = createMapPinIcon(R.drawable.work_order_pin);
456
457 // Setup the callout.
458 callout = mapView.getCallout();
459 callout.dismiss();
460
461 Callout.Style style = new Callout.Style(context);
462 style.setCornerRadius(CALLOUT_CORNER_CURVE);
463 callout.setStyle(style);
464
465 templateItemMapCalloutView = LayoutInflater.from(context).inflate(R.layout.template_item_map_callout, null);
466
467 mapCalloutMultipleView = LayoutInflater.from(context).inflate(R.layout.map_callout_multiple, null);
468
469 LinearLayoutManager calloutLayoutManager = new LinearLayoutManager(context);
470
471 calloutRecyclerView = mapCalloutMultipleView.findViewById(R.id.mapCalloutLayout);
472 calloutRecyclerView.setLayoutManager(calloutLayoutManager);
473
474 DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(calloutRecyclerView.getContext(), calloutLayoutManager.getOrientation());
475 calloutRecyclerView.addItemDecoration(dividerItemDecoration);
476
477 mapView.setOnTouchListener(new DefaultMapViewOnTouchListener(context, mapView) {
478
479 @Override
480 public boolean onSingleTapConfirmed(MotionEvent motionEvent) {
481 android.graphics.Point screenPoint = new android.graphics.Point(Math.round(motionEvent.getX()),
482 Math.round(motionEvent.getY()));
483
484 showCallout(screenPoint);
485 return true;
486 }
487 });
488
489 // Process the layer configuration.
490 processSimpleGisServiceInfos();
491 }
492
493 private void setUpMap() {
494 // Load the base layer.
495 addBaseLayer();
496
497 mapView.setMap(map);
498
499 // Remove any draw status listener
500 mapView.removeDrawStatusChangedListener(drawStatusChangedListener);
501
502 // Add draw status listener
503 mapView.addDrawStatusChangedListener(drawStatusChangedListener);
504 }
505
506 private PictureMarkerSymbol createMapPinIcon(int drawablePin) {
507 BitmapDrawable drawable = (BitmapDrawable) ContextCompat.getDrawable(context, drawablePin);
508 ListenableFuture<PictureMarkerSymbol> listenableFuture;
509 PictureMarkerSymbol pictureMarkerSymbol = null;
510 if (drawable != null) {
511 listenableFuture = PictureMarkerSymbol.createAsync(drawable);
512 try {
513 pictureMarkerSymbol = listenableFuture.get();
514 pictureMarkerSymbol.setOffsetX(0);
515 pictureMarkerSymbol.setOffsetY(pictureMarkerSymbol.getHeight() / 2);
516 pictureMarkerSymbol.setWidth(35);
517 pictureMarkerSymbol.setHeight(35);
518 }
519 catch (InterruptedException | ExecutionException e) {
520 e.printStackTrace();
521 }
522 }
523 return pictureMarkerSymbol;
524 }
525
526 private void processDownloadedMapCache() {
527 ArrayList<MobileMapCacheUpdate> updates = dbDataAccess.GetAllMobileMapCacheUpdates();
528 MobileMapCacheUpdate finishedUpdate = null;
529
530 for (MobileMapCacheUpdate update : updates) {
531 if (update.DownloadComplete) {
532 finishedUpdate = update;
533 break;
534 }
535 }
536
537 if (finishedUpdate != null) {
538 //we have an update that has successfully downloaded...
539
540 //first we need to delete the old file if it exists
541 MobileMapCache currentCache = dbDataAccess.GetMobileMapCache(finishedUpdate.MobileMapCacheId);
542 if (currentCache != null) {
543 fileManager.DeleteFileIfExists(fileManager.GetMapCacheDirectory(), currentCache.FileName);
544
545 dbDataAccess.DeleteMobileMapCache(finishedUpdate.MobileMapCacheId);
546 }
547
548 //then we need to copy it over to the MobileMapCache table
549 //and update the actual file
550
551 MobileMapCache newCache = new MobileMapCache();
552 newCache.MobileMapCacheId = finishedUpdate.MobileMapCacheId;
553 newCache.Description = finishedUpdate.Description;
554 newCache.FileName = finishedUpdate.FileName;
555 newCache.FileSize = finishedUpdate.FileSize;
556 newCache.DateModified = finishedUpdate.DateModified;
557
558 dbDataAccess.AddMobileMapCache(newCache);
559 dbDataAccess.DeleteMobileMapCacheUpdate(finishedUpdate.MobileMapCacheId);
560
561 fileManager.RenameFile(finishedUpdate.LocalFilePath, finishedUpdate.FileName);
562 }
563 }
564
565 public void syncFinished(boolean success) {
566 if (success) {
567
568 ArrayList<MobileMapCacheUpdate> updateList = dbDataAccess.GetAllMobileMapCacheUpdates();
569 if (updateList.size() > 0) {
570 MobileMapCacheUpdate update = updateList.get(0);
571
572 promptToDownloadMapCache(update);
573 }
574
575 // Remove the existing graphic overlay.
576 if (graphicsOverlay != null) {
577 mapView.getGraphicsOverlays().remove(graphicsOverlay);
578 }
579
580 // Remove the last view point of map in AppState.
581 AppState.LastMapViewPoint = null;
582
583 // Reinitialize the map after a sync.
584 init();
585 }
586 else {
587 // Reset syncInitiated value.
588 GeoDatabaseHelper.getInstance().setSyncInitiated(false);
589 }
590 }
591
592 public void singleTapListener(android.graphics.Point screenPoint) {
593 if (createGraphic != null) {
594 Point point = mapView.screenToLocation(screenPoint);
595 moveCreateGraphic(point);
596 }
597 }
598
599 @Override
600 protected void onStart() {
601 super.onStart();
602
603 // Check for any updates.
604 syncPresenter.CheckPendingUpdates();
605 }
606
607 @Override
608 public void onDestroy() {
609 super.onDestroy();
610
611 if (workActivityUpdatedReceiver != null) {
612 LocalBroadcastManager.getInstance(context).unregisterReceiver(workActivityUpdatedReceiver);
613 }
614 }
615
616 @Override
617 public void onResume() {
618 super.onResume();
619
620 LocalBroadcastManager.getInstance(context).registerReceiver(downloadReceiver, new IntentFilter(DownloadFileService.BROADCAST_ACTION));
621
622 isPaused = false;
623
624 mapView.resume();
625 if (locationManager != null && locatingEnabled) {
626 locationManager.startAsync();
627 }
628 }
629
630 @Override
631 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
632 if (resultCode == RESULT_OK) {
633
634 switch (requestCode) {
635 case createActivityRequestCode:
636 displayPins(false);
637 break;
638 case mapLayersActivityRequestCode:
639 // Remove the existing graphic overlay.
640 if (graphicsOverlay != null) {
641 mapView.getGraphicsOverlays().remove(graphicsOverlay);
642 }
643
644 setSelectableMapLayer();
645
646 // If the layers changed reload the map.
647 if (data.getBooleanExtra(EXTRA_LAYERS_CHANGED, false)) {
648 init();
649 }
650
651 break;
652 case selectedAssetsActivityRequestCode:
653 ArrayList<Integer> objectIds = data.getIntegerArrayListExtra(EXTRA_REMOVED_ASSET_FEATURE_LIST);
654 updateFeatures(objectIds);
655 break;
656 default:
657 break;
658 }
659 }
660 }
661
662 @Override
663 public boolean onOptionsItemSelected(MenuItem item) {
664 switch (item.getItemId()) {
665 case android.R.id.home:
666 break;
667 case R.id.menu_sync:
668 // Reset the map selection.
669 AppState.SelectableMapLayer = null;
670 setSelectableMapLayer();
671
672 // Set syncInitiated value to check its value later on while downloading geoDatabase.
673 GeoDatabaseHelper.getInstance().setSyncInitiated(true);
674
675 // Start the sync.
676 Date lastSync = prefs.getLastSync();
677 if (lastSync == null) {
678 syncPresenter.BeginSync(true);
679 }
680 else {
681 syncPresenter.BeginSync(prefs.getFullSync());
682 }
683 break;
684 }
685 return super.onOptionsItemSelected(item);
686 }
687
688 @Override
689 public void onPause() {
690 super.onPause();
691 // Unregister since the activity is not visible
692 LocalBroadcastManager.getInstance(context).unregisterReceiver(downloadReceiver);
693
694 mapView.getLocationDisplay().stop();
695 mapView.pause();
696
697 isPaused = true;
698
699 // Save the last view point of map in AppState.
700 AppState.LastMapViewPoint = mapView.getCurrentViewpoint(Viewpoint.Type.BOUNDING_GEOMETRY);
701 }
702
703 @Override
704 public boolean onCreateOptionsMenu(Menu menu) {
705 getMenuInflater().inflate(R.menu.activity_map, menu);
706
707 syncMenuItem = menu.findItem(R.id.menu_sync);
708
709 //call these here because we might have called them before
710 // onCreateOptionsMenu was ever called
711 ShowPendingUpdate(pendingUpdate);
712
713 return true;
714 }
715
716 @Override
717 public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
718 switch (requestCode) {
719 case PermissionUtil.PERMISSION_REQUEST_CODE:
720 if (PermissionUtil.granted(permissions, grantResults)) {
721 toggleLocateClicked();
722 }
723 break;
724 }
725 }
726
727 @Override
728 public void SetSyncCancelVisibility(boolean visible) {
729 syncProgressDialog.setCancelVisibility(visible);
730 }
731
732 @Override
733 public void SetSyncProgressVisibility(boolean value) {
734 if (value) {
735 syncProgressDialog.show();
736 syncProgressDialog.setCancelEnabled(true);
737 }
738 else {
739 syncProgressDialog.dismiss();
740 }
741 }
742
743 @Override
744 public void SetSyncProgress(int progress) {
745 syncProgressDialog.setProgress(progress);
746 }
747
748 @Override
749 public void ShowSyncError(String title, String message, boolean promptToView) {
750 syncErrorDialog.Show(title, message, promptToView);
751 }
752
753 @Override
754 public void ShowPendingUpdate(boolean state) {
755 //this is silly code necessary because onCreateOptionsMenu happens after any and all
756 // on***** callbacks
757 if (syncMenuItem != null) {
758 if (state) {
759 syncMenuItem.setIcon(R.drawable.sync_pending);
760 }
761 else {
762 syncMenuItem.setIcon(R.drawable.sync);
763 }
764 }
765 else {
766
767 pendingUpdate = state;
768 }
769 }
770
771 @Override
772 public void ShowReauthenticateForm(String currentUsername) {
773 BaseDialogFragment reAuthenticateDialog = ReauthenticateDialog.newInstance(currentUsername);
774 reAuthenticateDialog.show(getSupportFragmentManager(), "reauth");
775 }
776
777 @Override
778 public void showCouldNotSyncAlert() {
779
780 }
781
782 @Override
783 public void SetTaskName(String taskName) {
784 syncProgressDialog.setMessage(taskName);
785 }
786
787 public void mapSelectionCompleted() {
788 runOnUiThread(() -> {
789
790 //Hide progress on selection
791 pgrSelection.setVisibility(View.GONE);
792 btnStartSelection.setVisibility(View.VISIBLE);
793 btnClearSelection.setEnabled(true);
794
795 btnSelected.setVisibility(View.VISIBLE);
796
797 if (AppState.SelectedFeatures != null) {
798 btnSelected.setText(String.format("%s %s", AppState.SelectedFeaturesCount, getString(R.string.assets_selected)));
799 }
800 else {
801 btnSelected.setText(String.format("%s %s", 0, getString(R.string.assets_selected)));
802 }
803 });
804 }
805
806 public void promptToDownloadMapCache(MobileMapCacheUpdate cache) {
807 //we need this because a sync can finish AFTER the activity has been paused
808 if (!isPaused) {
809 BaseDialogFragment mapCachePrompt = MapCachePromptDialog.newInstance(cache.MobileMapCacheId,
810 cache.Description,
811 String.valueOf(cache.FileSize) + " " + resources.getString(R.string.kilobyte),
812 (String) DateFormat.format(resources.getString(R.string.date_short),
813 cache.DateModified));
814 mapCachePrompt.show(getSupportFragmentManager(), "mapCachePrompt");
815 }
816 }
817
818 public void showToastMessage(String string) {
819 Toast.makeText(context, string, Toast.LENGTH_SHORT).show();
820 }
821
822 public void startDownloadService(int mobileMapCacheId) {
823 Intent downloadIntent = new Intent(MapActivity.this, DownloadFileService.class);
824 downloadIntent.putExtra(DownloadFileService.EXTRA_KEY_ACTION, DownloadFileService.EXTRA_VALUE_ACTION_DOWNLOAD);
825 downloadIntent.putExtra(DownloadFileService.EXTRA_KEY_TYPE, DownloadFileService.EXTRA_VALUE_TYPE_MAP_CACHE);
826 downloadIntent.putExtra(DownloadFileService.EXTRA_KEY_ID, mobileMapCacheId);
827
828 startService(downloadIntent);
829 }
830
831 public void enableMapButtons(boolean state) {
832 btnCreate.setEnabled(state);
833 btnLayer.setEnabled(state);
834 btnLocation.setEnabled(state);
835 }
836
837 @Override
838 public void ReauthenticateLogInClicked(String password) {
839 syncPresenter.Reauthenticate(password);
840 }
841
842 @Override
843 public void MapCacheDownloadClicked(int mobileMapCacheId) {
844 Log.d(TAG, "MapCacheDownloadClicked, mobileMapCacheId=" + mobileMapCacheId);
845 startDownloadService(mobileMapCacheId);
846 showToastMessage(resources.getString(R.string.downloading_cache));
847 }
848
849 private void addNewGraphic(String workActivityKind) {
850 btnContinue.setVisibility(View.VISIBLE);
851 btnCancel.setVisibility(View.VISIBLE);
852
853 // Remove and clear graphic if already set.
854 if (createGraphic != null) {
855 graphicsOverlay.getGraphics().remove(createGraphic);
856 createGraphic = null;
857 }
858
859 android.graphics.Point screenPoint = new android.graphics.Point(
860 Math.round(mapView.getWidth() / 2), Math.round(mapView.getHeight() / 2));
861 Point point = mapView.screenToLocation(screenPoint);
862
863 if (workActivityKind.equalsIgnoreCase(AppConstants.WORK_ACTIVITY_KIND_INSPECTION)) {
864 addCreateGraphicAtPoint(point, R.drawable.inspection_pin_new);
865 }
866 else if (workActivityKind.equalsIgnoreCase(AppConstants.WORK_ACTIVITY_KIND_REQUEST)) {
867 addCreateGraphicAtPoint(point, R.drawable.request_pin_new);
868 }
869 else if (workActivityKind.equalsIgnoreCase(AppConstants.WORK_ACTIVITY_KIND_WORK_ORDER)) {
870 addCreateGraphicAtPoint(point, R.drawable.work_order_pin_new);
871 }
872 }
873
874 private void continueCreate() {
875 if (createGraphic != null) {
876 // Get the unattached pin info.
877 Envelope envelope = getEnvelopeFromCreateGraphic(createGraphic);
878 double x = 0, y = 0;
879 if (envelope != null) {
880 x = envelope.getCenter().getX();
881 y = envelope.getCenter().getY();
882 }
883 // Pass in x,y and open the create screen.
884 Intent intent = new Intent(this, CreateActivity.class);
885 intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
886 intent.putExtra(CreateActivity.EXTRA_X, x);
887 intent.putExtra(CreateActivity.EXTRA_Y, y);
888 intent.putExtra(CreateActivity.EXTRA_WORK_ACTIVITY_KIND, workActivityType);
889
890 // Reset the create.
891 resetCreate();
892
893 // Show the create screen.
894 startActivityForResult(intent, createActivityRequestCode);
895 }
896 }
897
898 private void cancelCreate() {
899 resetCreate();
900 }
901
902 private void resetCreate() {
903 btnContinue.setVisibility(View.GONE);
904 btnCancel.setVisibility(View.GONE);
905
906 if (createGraphic != null) {
907 graphicsOverlay.getGraphics().remove(createGraphic);
908 createGraphic = null;
909 }
910 }
911
912 // Map Selection
913
914 private void clearSelection() {
915 // End current selection.
916 endSelection();
917
918 // Reset the selectable layer.
919 if (selectableLayer != null) {
920 // Remove selectable layer from the map.
921 map.getOperationalLayers().remove(selectableLayer);
922
923 // Clear the selectable layer.
924 selectableLayer = null;
925 }
926
927 // Clear the selection from downloaded feature layers.
928 if (selectableGeoDatabaseLayer != null) {
929 selectableGeoDatabaseLayer.clearSelection();
930 }
931
932 // Clear the selected features.
933 AppState.SelectedEntities = null;
934 AppState.SelectedFeatures = null;
935 AppState.SelectedFeaturesCount = 0;
936 AppState.SelectedFeatureAttributes = new HashMap<>();
937
938 // Hide selected button.
939 btnSelected.setVisibility(View.INVISIBLE);
940
941 btnClearSelection.setEnabled(false);
942 }
943
944 private void startSelection() {
945 clearSelection();
946
947 btnStartSelection.setSelected(true);
948 btnClearSelection.setEnabled(false);
949
950 mapSelectionView.setVisibility(View.VISIBLE);
951 }
952
953 private void viewSelection() {
954 Intent intent = new Intent(MapActivity.this, SelectedAssetsActivity.class);
955 startActivityForResult(intent, selectedAssetsActivityRequestCode);
956 }
957
958 // Map Layers Delegate
959
960 private void setSelectableMapLayer() {
961 clearSelection();
962
963 if (AppState.SelectableMapLayer == null) {
964 btnStartSelection.setEnabled(false);
965 }
966 else {
967 btnStartSelection.setEnabled(true);
968 }
969 }
970
971 public void endSelection() {
972 // Hide the view that captures all the selection input.
973 mapSelectionView.setVisibility(View.INVISIBLE);
974
975 // Set the icon to show the selection mode has ended.
976 btnStartSelection.setSelected(false);
977 }
978
979 public void workActivityTypeSelected(String workActivityType) {
980 this.workActivityType = workActivityType;
981
982 if (AppState.SelectedFeatures == null || AppState.SelectedFeaturesCount == 0) {
983 addNewGraphic(workActivityType);
984 }
985 else {
986 // Reset the create.
987 resetCreate();
988
989 // Show the create activity.
990 Intent intent = new Intent(MapActivity.this, CreateActivity.class);
991 intent.putExtra(CreateActivity.EXTRA_WORK_ACTIVITY_KIND, workActivityType);
992 startActivityForResult(intent, createActivityRequestCode);
993 }
994 }
995
996 public void downloadComplete(String status) {
997 if (status.equals(DownloadFileService.BROADCAST_EXTRA_STATUS_SUCCESS)) {
998 showToastMessage(resources.getString(R.string.download_complete));
999
1000 // Reload the map.
1001 init();
1002 }
1003 else if (status.equals(DownloadFileService.BROADCAST_EXTRA_STATUS_FAILURE)) {
1004 showToastMessage(resources.getString(R.string.error_downloading_cache));
1005 }
1006 }
1007
1008 public void toggleLocateClicked() {
1009 locationState = !locationState;
1010 btnLocation.setSelected(locationState);
1011
1012 enableLocation(locationState);
1013 }
1014
1015 private void openWorkActivityPage(String kind, String activityId) {
1016 switch (kind) {
1017 case AppConstants.WORK_ACTIVITY_KIND_INSPECTION:
1018 nav.InspectionDetail(Integer.parseInt(activityId));
1019 break;
1020 case AppConstants.WORK_ACTIVITY_KIND_WORK_ORDER:
1021 nav.WorkOrderDetail(activityId);
1022 break;
1023 case AppConstants.WORK_ACTIVITY_KIND_REQUEST:
1024 nav.RequestDetail(Integer.parseInt(activityId));
1025 break;
1026 case AppConstants.WORK_ACTIVITY_KIND_CASE:
1027 nav.CaseDetail((Integer.parseInt(activityId)));
1028 break;
1029 }
1030 }
1031
1032 private void displayPins(boolean zoomToExtent) {
1033 dispatcher.dispatch(new DisplayPinsDispatchable(zoomToExtent));
1034 }
1035
1036 private void processSimpleGisServiceInfos() {
1037
1038 MapLayersLoadingObservable layersLoadObservable = new MapLayersLoadingObservable();
1039 layersLoadObservable.addObserver((o, arg) -> {
1040 // Add base layer.
1041 if (simpleGisServiceInfos.size() >= 1) {
1042 baseLayer = createLayer(simpleGisServiceInfos.get(0));
1043 }
1044
1045 // Add operational layers if there are any.
1046 if (simpleGisServiceInfos.size() > 1) {
1047 // Skip the first layer because it is the base layer.
1048 for (int i = 1; i < simpleGisServiceInfos.size(); i++) {
1049 SimpleGisServiceInfo simpleGisServiceInfo = simpleGisServiceInfos.get(i);
1050
1051 // Create the layer.
1052 Layer layer = createLayer(simpleGisServiceInfo);
1053
1054 // Make sure the layer was created.
1055 if (layer != null) {
1056 operationalLayers.add(layer);
1057 }
1058 else {
1059 Log.w(TAG, "Unknown map service type! " + simpleGisServiceInfo.ServiceType);
1060 }
1061 }
1062 }
1063
1064 setUpMap();
1065 });
1066
1067 layersLoadObservable.updateTotalCount(simpleGisServiceInfos.size());
1068
1069 // Add the first web map if one is configured.
1070 for (SimpleGisServiceInfo simpleGisServiceInfo : simpleGisServiceInfos) {
1071 if (simpleGisServiceInfo.ServiceType == GISServiceType.WEBMAP) {
1072 Portal portal = null;
1073
1074 if (simpleGisServiceInfo.TokenRequired) {
1075 if (prefs.getAGOLLogin()) {
1076 portal = new Portal(AppConstants.AGOL_URL, true);
1077 }
1078 else if (prefs.getPortalLogin()) {
1079 portal = new Portal(Functions.MakeUrl(prefs.getPortalSSL(), prefs.getPortalHost(), prefs.getPortalSite()).toString(), true);
1080 }
1081
1082 if (credential != null && portal != null) {
1083 portal.setCredential(credential);
1084 }
1085 }
1086 else {
1087 if (prefs.getAGOLLogin()) {
1088 portal = new Portal(AppConstants.AGOL_URL);
1089 }
1090 else if (prefs.getPortalLogin()) {
1091 portal = new Portal(Functions.MakeUrl(prefs.getPortalSSL(), prefs.getPortalHost(), prefs.getPortalSite()).toString());
1092 }
1093 }
1094
1095 if (portal != null) {
1096 PortalItem portalItem = new PortalItem(portal, simpleGisServiceInfo.Service);
1097
1098 map = new ArcGISMap(portalItem);
1099
1100 mapView.setMap(map);
1101
1102 map.addDoneLoadingListener(() -> {
1103 if (map.getLoadStatus() == LoadStatus.LOADED) {
1104
1105 // Add all operational layers from WebMap
1106 operationalLayers.addAll(map.getOperationalLayers());
1107
1108 prefs.setWebMapItemId(map.getItem().getItemId());
1109
1110 if (mapView.getSpatialReference() != null) {
1111 prefs.setSpatialReference(mapView.getSpatialReference().getWkid());
1112 }
1113
1114 layersLoadObservable.allLoaded();
1115 }
1116 });
1117
1118 map.loadAsync();
1119 }
1120
1121 break;
1122 }
1123 else if (simpleGisServiceInfo.ServiceType == GISServiceType.MOBILEMAPPACKAGE) {
1124 MobileMapCache mapCache = dbDataAccess.GetMobileMapCache(Integer.parseInt(simpleGisServiceInfo.Service));
1125
1126 if (mapCache != null) {
1127 String path = fileManager.GetMapCacheDirectory() + "/" + mapCache.FileName;
1128 File file = new File(path);
1129
1130 if (file.exists()) {
1131 MobileMapPackage mobileMapPackage = new MobileMapPackage(path);
1132 mobileMapPackage.addDoneLoadingListener(() -> {
1133 if (mobileMapPackage.getLoadStatus() == LoadStatus.LOADED) {
1134 // Get the map from the package and set it to the MapView
1135 List<ArcGISMap> arcGISMaps = mobileMapPackage.getMaps();
1136 if (arcGISMaps.size() > 0) {
1137 map = arcGISMaps.get(0);
1138
1139 map.addDoneLoadingListener(() -> {
1140 if (map.getLoadStatus() == LoadStatus.LOADED) {
1141
1142 // Add all operational layers from Mobile Map Package
1143 operationalLayers.addAll(map.getOperationalLayers());
1144
1145 layersLoadObservable.allLoaded();
1146 }
1147 });
1148
1149 map.loadAsync();
1150 }
1151 }
1152 else if (mobileMapPackage.getLoadStatus() == LoadStatus.FAILED_TO_LOAD) {
1153 Log.e(TAG, "MobileMap Package failed to load");
1154 }
1155 });
1156 mobileMapPackage.loadAsync();
1157 }
1158 }
1159 else {
1160 layersLoadObservable.loaded();
1161 }
1162 break;
1163 }
1164 else {
1165 layersLoadObservable.loaded();
1166 }
1167 }
1168 }
1169
1170 private Layer createLayer(SimpleGisServiceInfo simpleGisServiceInfo) {
1171 Layer ret = null;
1172
1173 boolean tokenRequired = ValueUtil.getValueOrDefault(simpleGisServiceInfo.TokenRequired);
1174
1175 if (simpleGisServiceInfo.ServiceType == GISServiceType.DYNAMICMAP) {
1176 ret = new ArcGISMapImageLayer(simpleGisServiceInfo.Service);
1177 ret.setName(simpleGisServiceInfo.ServiceName);
1178 if (tokenRequired && credential != null) {
1179 ((ArcGISMapImageLayer) ret).setCredential(credential);
1180 }
1181
1182 if (layersHidden.contains(simpleGisServiceInfo.Service)) {
1183 ret.setVisible(false);
1184 }
1185 }
1186 else if (simpleGisServiceInfo.ServiceType == GISServiceType.TILEDMAP) {
1187 ret = new ArcGISTiledLayer(simpleGisServiceInfo.Service);
1188 ret.setName(simpleGisServiceInfo.ServiceName);
1189 if (tokenRequired && credential != null) {
1190 ((ArcGISTiledLayer) ret).setCredential(credential);
1191 }
1192
1193 if (layersHidden.contains(simpleGisServiceInfo.Service)) {
1194 ret.setVisible(false);
1195 }
1196 }
1197 else if (simpleGisServiceInfo.ServiceType == GISServiceType.IMAGEMAP) {
1198 ImageServiceRaster imageServiceRaster = new ImageServiceRaster(simpleGisServiceInfo.Service);
1199 if (tokenRequired && credential != null) {
1200 imageServiceRaster.setCredential(credential);
1201 }
1202
1203 ret = new RasterLayer(imageServiceRaster);
1204 ret.setName(simpleGisServiceInfo.ServiceName);
1205
1206 if (layersHidden.contains(simpleGisServiceInfo.Service)) {
1207 ret.setVisible(false);
1208 }
1209 }
1210 else if (simpleGisServiceInfo.ServiceType == GISServiceType.FEATURELAYER) {
1211 ServiceFeatureTable serviceFeatureTable = new ServiceFeatureTable(simpleGisServiceInfo.Service);
1212 if (simpleGisServiceInfo.TokenRequired && credential != null) {
1213 serviceFeatureTable.setCredential(credential);
1214 }
1215 ret = new FeatureLayer(serviceFeatureTable);
1216 ret.setName(simpleGisServiceInfo.ServiceName);
1217
1218 if (layersHidden.contains(simpleGisServiceInfo.Service)) {
1219 ret.setVisible(false);
1220 }
1221 }
1222 else if (simpleGisServiceInfo.ServiceType == GISServiceType.TILEPACKAGE) {
1223 MobileMapCache mapCache = dbDataAccess.GetMobileMapCache(Integer.parseInt(simpleGisServiceInfo.Service));
1224
1225 if (mapCache != null) {
1226 // Create the path.
1227 String path = fileManager.GetMapCacheDirectory() + "/" + mapCache.FileName;
1228
1229 // Get the file.
1230 File file = new File(path);
1231
1232 // Make sure the file exists.
1233 if (file.exists()) {
1234 TileCache tileCache = new TileCache(path);
1235
1236 // Load the tiled layer.
1237 ret = new ArcGISTiledLayer(tileCache);
1238 ret.setName(simpleGisServiceInfo.ServiceName);
1239
1240 if (layersHidden.contains(simpleGisServiceInfo.Service)) {
1241 ret.setVisible(false);
1242 }
1243 }
1244 }
1245 }
1246 else if (simpleGisServiceInfo.ServiceType == GISServiceType.VECTORTILELAYER) {
1247 ret = new ArcGISVectorTiledLayer(simpleGisServiceInfo.Service);
1248 ret.setName(simpleGisServiceInfo.ServiceName);
1249 if (tokenRequired && credential != null) {
1250 ((ArcGISVectorTiledLayer) ret).setCredential(credential);
1251 }
1252
1253 if (layersHidden.contains(simpleGisServiceInfo.Service)) {
1254 ret.setVisible(false);
1255 }
1256 }
1257
1258 return ret;
1259 }
1260
1261 private void addBaseLayer() {
1262 if (baseLayer != null) {
1263 Basemap basemap = new Basemap(baseLayer);
1264 map.setBasemap(basemap);
1265 }
1266 }
1267
1268 private void addOperationalLayers() {
1269 if (operationalLayers != null) {
1270 map.getOperationalLayers().clear();
1271 map.getOperationalLayers().addAll(operationalLayers);
1272 }
1273 }
1274
1275 private void addGraphicsOverlay() {
1276 graphicsOverlay = new GraphicsOverlay();
1277 mapView.getGraphicsOverlays().add(graphicsOverlay);
1278
1279 selectionOverlay = new GraphicsOverlay();
1280 mapView.getGraphicsOverlays().add(selectionOverlay);
1281
1282 // Add the selectableLayer if it exists.
1283 if (selectableLayer != null) {
1284 map.getOperationalLayers().add(selectableLayer);
1285 }
1286 }
1287
1288 MapLayersLoadingObservable layersLoadObservable;
1289
1290 private void setupSelectableLayers() {
1291 AppState.SelectableMapLayers = new ArrayList<>();
1292
1293 layersLoadObservable = new MapLayersLoadingObservable();
1294 layersLoadObservable.addObserver((o, arg) -> {
1295
1296 syncEntityConfigurations();
1297
1298 });
1299
1300 layersLoadObservable.updateTotalCount(operationalLayers.size());
1301
1302 for (Layer operationalLayer : operationalLayers) {
1303 addSelectableLayers(operationalLayer);
1304 }
1305
1306 if (!layersDefaultVisibilityChecked) {
1307 prefs.setLayersDefaultVisibilityChecked(true);
1308 }
1309 }
1310
1311 private void syncEntityConfigurations() {
1312 ArrayList<String> layerEntityTypes = new ArrayList<>();
1313
1314 for (MapLayer mapLayer : AppState.SelectableMapLayers) {
1315 if (mapLayer.getGeodatabaseFeatureTable() != null) {
1316 ArrayList<EntityConfiguration> entityConfigurations = dbDataAccess.getEntityConfigurationByTableName(mapLayer.getName());
1317 if (entityConfigurations.isEmpty()) {
1318 layerEntityTypes.add(mapLayer.getName());
1319 }
1320 }
1321 }
1322
1323 if (layerEntityTypes.size() > 0) {
1324 syncPresenter.syncEntityConfigurations(layerEntityTypes);
1325 }
1326 else {
1327 fetchGeodatabaseLayers();
1328 }
1329 }
1330
1331 private void fetchGeodatabaseLayers() {
1332 GeoDatabaseHelper.getInstance().fetchFeatureServiceUrls(this, new GeoDatabaseHelper.OnCompleteListener() {
1333 @Override
1334 public void onComplete() {
1335 refreshLayersWithGeoDatabase();
1336 }
1337
1338 @Override
1339 public void onError(boolean isSyncInitiated) {
1340 syncFeatureServiceErrorDialog.show(isSyncInitiated);
1341 }
1342 }, simpleGisServiceInfos);
1343 }
1344
1345 private void addSelectableLayers(Layer layer) {
1346 if (layer instanceof ArcGISMapImageLayer) {
1347 ArcGISMapImageLayer arcGISMapImageLayer = (ArcGISMapImageLayer) layer;
1348 arcGISMapImageLayer.addDoneLoadingListener(() -> {
1349 if (arcGISMapImageLayer.getLoadStatus() == LoadStatus.LOADED) {
1350 ArcGISMapServiceInfo mapServiceInfo = arcGISMapImageLayer.getMapServiceInfo();
1351
1352 SublayerList subLayers = arcGISMapImageLayer.getSublayers();
1353
1354 List<String> layerHiddenNames = new ArrayList<>();
1355
1356 // Make sure the map service info exists.
1357 if (mapServiceInfo != null) {
1358 List<MapServiceLayerIdInfo> layerInfos = mapServiceInfo.getLayerInfos();
1359
1360 // Update the Total count of layers to load.
1361 // layerInfos contain the count of layer itself + sublayers count, but we just need the sublayers, so remove 1 from the layerInfos
1362 layersLoadObservable.updateTotalCount(layerInfos.size() - 1);
1363
1364 for (int j = 0; j < layerInfos.size(); j++) {
1365 MapServiceLayerIdInfo layerInfo = layerInfos.get(j);
1366
1367 if (layerInfo.getSubLayerIds().size() == 0) {
1368 String layerUrl = String.format("%s/%s", mapServiceInfo.getUrl(), layerInfo.getId());
1369
1370 // Determine if the layer has a credential.
1371 boolean hasCredential = arcGISMapImageLayer.getCredential() != null;
1372
1373 // Get the layers default visibility is checked or not
1374 if (!layersDefaultVisibilityChecked) {
1375 if (!layerInfo.getDefaultVisibility() && !layersHidden.contains(layerUrl)) {
1376 layersHidden.add(layerUrl);
1377 }
1378 }
1379
1380 // Create the map layer.
1381 MapLayer mapLayer = new MapLayer(hasCredential, (int) layerInfo.getId(), layerUrl, arcGISMapImageLayer.getName(), layerInfo.getName());
1382
1383 Log.d(TAG, "Layer: " + layerInfo.getName() + " (" + layerInfo.getId() + ") MapLayerId: " + mapLayer.getId());
1384 AppState.SelectableMapLayers.add(mapLayer);
1385
1386 if (layersHidden.contains(layerUrl)) {
1387 layerHiddenNames.add(mapLayer.getName());
1388 }
1389 }
1390
1391 // Increment the count of the loaded layers.
1392 layersLoadObservable.loaded();
1393 }
1394
1395 for (ArcGISSublayer subLayer : subLayers) {
1396 for (ArcGISSublayer arcGISSublayer : subLayer.getSublayers()) {
1397 if (layerHiddenNames.contains(arcGISSublayer.getName())) {
1398 arcGISSublayer.setVisible(false);
1399 }
1400 else {
1401 arcGISSublayer.setVisible(true);
1402 }
1403 }
1404 }
1405 }
1406
1407 // Update layers hidden
1408 prefs.setLayersHidden(layersHidden);
1409 }
1410 });
1411 arcGISMapImageLayer.loadAsync();
1412 }
1413 else if (layer instanceof FeatureLayer) {
1414 FeatureLayer featureLayer = (FeatureLayer) layer;
1415 if (featureLayer.getFeatureTable() instanceof ServiceFeatureTable) {
1416 ServiceFeatureTable serviceFeatureTable = (ServiceFeatureTable) featureLayer.getFeatureTable();
1417
1418 if (serviceFeatureTable != null) {
1419 serviceFeatureTable.addDoneLoadingListener(() -> {
1420
1421 if (serviceFeatureTable.getLoadStatus() == LoadStatus.LOADED) {
1422 // Determine if the layer has a credential.
1423 boolean hasCredential = serviceFeatureTable.getCredential() != null;
1424
1425 if (serviceFeatureTable.getLayerInfo() != null) {
1426 String serviceLayerName = serviceFeatureTable.getLayerInfo().getServiceLayerName();
1427
1428 // Create the map layer.
1429 MapLayer mapLayer = new MapLayer(hasCredential, (int) serviceFeatureTable.getServiceLayerId(),
1430 serviceFeatureTable.getUri(), serviceLayerName, serviceLayerName);
1431
1432 if (layersHidden.contains(mapLayer.getId())) {
1433 featureLayer.setVisible(false);
1434 }
1435 else {
1436 featureLayer.setVisible(true);
1437 }
1438
1439 Log.d(TAG, "Layer: " + featureLayer.getName() + " (" + serviceFeatureTable.getServiceLayerId()
1440 + ") MapLayerId: " + mapLayer.getId());
1441
1442 AppState.SelectableMapLayers.add(mapLayer);
1443
1444 // Increment the count of the loaded layers.
1445 layersLoadObservable.loaded();
1446 }
1447 }
1448 });
1449 serviceFeatureTable.loadAsync();
1450 }
1451 }
1452 else if (featureLayer.getFeatureTable() instanceof GeodatabaseFeatureTable) {
1453 GeodatabaseFeatureTable geodatabaseFeatureTable = (GeodatabaseFeatureTable) featureLayer.getFeatureTable();
1454
1455 if (geodatabaseFeatureTable != null) {
1456 geodatabaseFeatureTable.addDoneLoadingListener(() -> {
1457
1458 if (geodatabaseFeatureTable.getLoadStatus() == LoadStatus.LOADED) {
1459
1460 if (geodatabaseFeatureTable.getLayerInfo() != null) {
1461 String serviceLayerName = geodatabaseFeatureTable.getLayerInfo().getServiceLayerName();
1462
1463 // Create the map layer.
1464 MapLayer mapLayer = new MapLayer((int) geodatabaseFeatureTable.getServiceLayerId(),
1465 serviceLayerName, serviceLayerName, geodatabaseFeatureTable);
1466
1467 if (layersHidden.contains(mapLayer.getId())) {
1468 featureLayer.setVisible(false);
1469 }
1470 else {
1471 featureLayer.setVisible(true);
1472 }
1473
1474 Log.d(TAG, "Layer: " + featureLayer.getName() + " (" + geodatabaseFeatureTable.getServiceLayerId()
1475 + ") MapLayerId: " + mapLayer.getId());
1476
1477 AppState.SelectableMapLayers.add(mapLayer);
1478
1479 // Increment the count of the loaded layers.
1480 layersLoadObservable.loaded();
1481 }
1482 }
1483 });
1484 geodatabaseFeatureTable.loadAsync();
1485 }
1486 }
1487 }
1488 }
1489
1490 @Override
1491 public void startSelectAtPoint(float x, float y) {
1492 if (selectionOverlay != null) {
1493 android.graphics.Point screenPoint = new android.graphics.Point(Math.round(x), Math.round(y));
1494 initialMapPoint = mapView.screenToLocation(screenPoint);
1495
1496 // Create symbol for selection layer
1497 SimpleFillSymbol innerSymbol = new SimpleFillSymbol();
1498 innerSymbol.setColor(ContextCompat.getColor(context, R.color.map_selection_background));
1499 SimpleLineSymbol lineSymbol = new SimpleLineSymbol();
1500 lineSymbol.setColor(ContextCompat.getColor(context, R.color.map_selection_outline));
1501 innerSymbol.setOutline(lineSymbol);
1502
1503 Envelope envelope = new Envelope(initialMapPoint.getX(), initialMapPoint.getY(), initialMapPoint.getX(), initialMapPoint.getY(), mapView.getSpatialReference());
1504
1505 selectionGraphic = new Graphic(envelope, innerSymbol);
1506
1507 selectionOverlay.getGraphics().add(selectionGraphic);
1508 }
1509 }
1510
1511 @Override
1512 public void moveSelectToPoint(float x, float y) {
1513 if (selectionOverlay != null && selectionGraphic != null) {
1514 android.graphics.Point screenPoint = new android.graphics.Point(Math.round(x), Math.round(y));
1515 Point mapPoint = mapView.screenToLocation(screenPoint);
1516
1517 Envelope envelope = new Envelope(initialMapPoint.getX(), initialMapPoint.getY(), mapPoint.getX(), mapPoint.getY(), mapView.getSpatialReference());
1518
1519 selectionGraphic.setGeometry(envelope);
1520 }
1521 }
1522
1523 @Override
1524 public void endSelectAtPoint(float x, float y) {
1525 if (selectionOverlay != null) {
1526 // Show progress on selection complete
1527 btnStartSelection.setVisibility(View.INVISIBLE);
1528 pgrSelection.setVisibility(View.VISIBLE);
1529
1530 // Clear the selection overlay.
1531 selectionOverlay.getGraphics().clear();
1532
1533 endSelection();
1534
1535 executeSelection();
1536 }
1537 }
1538
1539 // Callout
1540 public void showCallOut(String activityId, String kind) {
1541 if (graphicsOverlay != null) {
1542 ListenableList<Graphic> graphics = graphicsOverlay.getGraphics();
1543
1544 if (graphics != null) {
1545 for (Graphic graphic : graphics) {
1546 String pinJson = (String) graphic.getAttributes().get(PIN);
1547
1548 if (pinJson != null && pinJson.length() > 0) {
1549 MapPin pin = Services.fromJson(pinJson, MapPin.class);
1550
1551 if (pin.Kind.equalsIgnoreCase(kind) && pin.ActivityId.equalsIgnoreCase(activityId)) {
1552 List<MapPin> mapPins = new ArrayList<>();
1553 Point point = (Point) graphic.getGeometry();
1554
1555 mapPins.add(pin);
1556
1557 showCallout(point, mapPins, true);
1558 break;
1559 }
1560 }
1561 }
1562 }
1563 }
1564 }
1565
1566 private void showCallout(android.graphics.Point screenPoint) {
1567 // Hide the callout.
1568 callout.dismiss();
1569
1570 if (graphicsOverlay != null) {
1571 List<MapPin> mapPins = new ArrayList<>();
1572
1573 final ListenableFuture<IdentifyGraphicsOverlayResult> identifyGraphic =
1574 mapView.identifyGraphicsOverlayAsync(graphicsOverlay, screenPoint,
1575 Functions.dpToPx(GRAPHIC_TOLOERANCE, context), false, CALLOUT_MAX_RESULTS);
1576
1577 identifyGraphic.addDoneListener(() -> {
1578 try {
1579 IdentifyGraphicsOverlayResult grOverlayResult = identifyGraphic.get();
1580 List<Graphic> graphics = grOverlayResult.getGraphics();
1581
1582 if (graphics.size() > 0) {
1583 for (Graphic graphic : graphics) {
1584 String pinJson = (String) graphic.getAttributes().get(PIN);
1585
1586 if (pinJson != null && pinJson.length() > 0) {
1587 mapPins.add(Services.fromJson(pinJson, MapPin.class));
1588 }
1589 }
1590
1591 // Grab the first point for the location of the callout.
1592 Point point = (Point) graphics.get(0).getGeometry();
1593 showCallout(point, mapPins, false);
1594 }
1595 else {
1596 singleTapListener(screenPoint);
1597 }
1598 }
1599 catch (InterruptedException | ExecutionException ie) {
1600 ie.printStackTrace();
1601 }
1602 });
1603 }
1604 else {
1605 singleTapListener(screenPoint);
1606 }
1607 }
1608
1609 private void showCallout(Point point, List<MapPin> mapPins, boolean centerMapAtPoint) {
1610 if (mapPins != null && mapPins.size() > 0) {
1611
1612 if (mapPins.size() > 1) {
1613 CalloutRecyclerViewAdapter callOutAdapter = new CalloutRecyclerViewAdapter(mapPins);
1614 calloutRecyclerView.setAdapter(callOutAdapter);
1615
1616 callout.show(mapCalloutMultipleView, point);
1617 }
1618 else {
1619 MapPin mapPin = mapPins.get(0);
1620
1621 if (templateItemMapCalloutView != null) {
1622
1623 TextView lblTitle = templateItemMapCalloutView.findViewById(R.id.template_item_map_callout_title);
1624
1625 if (Integer.parseInt(mapPin.ActivityId) > 0 &&
1626 StringUtils.notEqualIgnoreCaseAndNull(mapPin.Kind, AppConstants.WORK_ACTIVITY_KIND_CASE)) {
1627 lblTitle.setText(String.format("%s #%s", mapPin.Title, mapPin.ActivityId));
1628 }
1629 else {
1630 lblTitle.setText(mapPin.Title);
1631 }
1632
1633 TextView lblDescription = templateItemMapCalloutView.findViewById(R.id.template_item_map_callout_description);
1634 lblDescription.setText(mapPin.Description);
1635
1636 ImageView calloutImageView = templateItemMapCalloutView.findViewById(R.id.template_item_map_callout_icon);
1637 calloutImageView.setVisibility(View.GONE);
1638
1639 ImageButton btnOpenInsp = templateItemMapCalloutView.findViewById(R.id.template_item_map_callout_open);
1640 btnOpenInsp.setTag(mapPin);
1641 btnOpenInsp.setOnClickListener(v -> {
1642 MapPin mapPin1 = (MapPin) v.getTag();
1643 openWorkActivityPage(mapPin1.Kind, mapPin1.ActivityId);
1644 });
1645
1646 callout.show(templateItemMapCalloutView, point);
1647 }
1648 }
1649
1650 if (centerMapAtPoint) {
1651 Viewpoint viewpoint = new Viewpoint(point, 100000);
1652 mapView.setViewpointAsync(viewpoint);
1653 }
1654 }
1655 }
1656
1657 public void addCreateGraphicAtPoint(Point point, int drawableResourceId) {
1658 PictureMarkerSymbol icon = createMapPinIcon(drawableResourceId);
1659
1660 if (icon != null) {
1661 createGraphic = new Graphic(point, icon);
1662 graphicsOverlay.getGraphics().add(createGraphic);
1663 }
1664 }
1665
1666 public void moveCreateGraphic(Point point) {
1667 // Update point values in existing create graphic.
1668 createGraphic.setGeometry(point);
1669 }
1670
1671 public void addPinsToLayer(ArrayList<MapPin> pinList) {
1672 // Make sure the graphics layer has been created.
1673
1674 SpatialReference mapSpatialRef = mapView.getSpatialReference();
1675
1676 if (graphicsOverlay != null && mapSpatialRef != null) {
1677 SpatialReference cwSpatialRef = SpatialReference.create(CW_WKID);
1678
1679 // Remove all graphics if exists.
1680 int size = graphicsOverlay.getGraphics().size();
1681 for (int i = size - 1; i >= 0; i--) {
1682 graphicsOverlay.getGraphics().remove(i);
1683 }
1684
1685 List<Point> points = new ArrayList<>();
1686 for (Integer i = 0; i < pinList.size(); i++) {
1687 MapPin pin = pinList.get(i);
1688
1689 Point sourcePoint = new Point(pin.X, pin.Y, cwSpatialRef);
1690 points.add(sourcePoint);
1691 }
1692 Multipoint multiPoint = new Multipoint(points);
1693
1694 Multipoint projectedMultiPoint = (Multipoint) GeometryEngine.project(multiPoint, mapSpatialRef);
1695
1696 if (projectedMultiPoint.getPoints().size() == pinList.size()) {
1697 for (Integer i = 0; i < pinList.size(); i++) {
1698 MapPin pin = pinList.get(i);
1699
1700 HashMap<String, Object> attrMap = new HashMap<>();
1701 attrMap.put(PIN, Services.toJson(pin));
1702
1703 PictureMarkerSymbol icon = null;
1704
1705 switch (pin.Kind) {
1706 case AppConstants.WORK_ACTIVITY_KIND_INSPECTION:
1707 icon = inspectionIcon;
1708 break;
1709 case AppConstants.WORK_ACTIVITY_KIND_WORK_ORDER:
1710 icon = workOrderIcon;
1711 break;
1712 case AppConstants.WORK_ACTIVITY_KIND_REQUEST:
1713 icon = serviceRequestIcon;
1714 break;
1715 case AppConstants.WORK_ACTIVITY_KIND_CASE:
1716 icon = caseIcon;
1717 break;
1718 }
1719
1720 if (icon != null) {
1721 Graphic graphic = new Graphic(projectedMultiPoint.getPoints().get(i), attrMap, icon);
1722 graphicsOverlay.getGraphics().add(graphic);
1723 }
1724 }
1725 }
1726 }
1727 }
1728
1729 public void zoomToPinExtent() {
1730 if (graphicsOverlay != null && graphicsOverlay.getGraphics().size() > 0) {
1731
1732 // Create a buffered geometry of a reasonable size around the first point.
1733 Graphic graphic = graphicsOverlay.getGraphics().get(0);
1734
1735 Geometry bufferedGeometry = GeometryEngine.buffer(graphic.getGeometry(), AppConstants.MAP_ZOOM_PADDING);
1736
1737 Envelope graphicsOverlayExtent = graphicsOverlay.getExtent();
1738
1739 // Determine the area of graphic overlay
1740 double graphicsOverlayArea = GeometryEngine.area(graphicsOverlayExtent);
1741
1742 Viewpoint viewpoint;
1743 if (graphicsOverlayArea < (AppConstants.MAP_ZOOM_PADDING * AppConstants.MAP_ZOOM_PADDING) || graphicsOverlay.getGraphics().size() == 1) {
1744 viewpoint = new Viewpoint(bufferedGeometry.getExtent());
1745 }
1746 else {
1747 Envelope visibleMapEnvelope = new Envelope(graphicsOverlayExtent.getXMin() - AppConstants.MAP_ZOOM_PADDING,
1748 graphicsOverlayExtent.getYMin(), graphicsOverlayExtent.getXMax() + AppConstants.MAP_ZOOM_PADDING,
1749 graphicsOverlayExtent.getYMax(), graphic.getGeometry().getSpatialReference());
1750 viewpoint = new Viewpoint(visibleMapEnvelope);
1751 }
1752
1753 // Set the initial viewpoint so that map scale doesn't change automatically
1754 if (AppState.LastMapViewPoint != null) {
1755 mapView.setViewpoint(AppState.LastMapViewPoint);
1756 map.setInitialViewpoint(AppState.LastMapViewPoint);
1757 }
1758 else {
1759 mapView.setViewpoint(viewpoint);
1760 map.setInitialViewpoint(viewpoint);
1761 }
1762 }
1763 else if (AppState.LastMapViewPoint != null) {
1764 mapView.setViewpoint(AppState.LastMapViewPoint);
1765 map.setInitialViewpoint(AppState.LastMapViewPoint);
1766 }
1767 }
1768
1769 public Envelope getEnvelopeFromCreateGraphic(Graphic graphic) {
1770 Envelope env = null;
1771 SpatialReference sourceSpatialRef = SpatialReference.create(CW_WKID);
1772
1773 if (graphic != null) {
1774 Geometry geometry = graphic.getGeometry();
1775
1776 if (geometry != null) {
1777 Geometry projectedGeometry = GeometryEngine.project(geometry, sourceSpatialRef);
1778 env = projectedGeometry.getExtent();
1779 }
1780 }
1781 return env;
1782 }
1783
1784 public Envelope getEnvelopeFromGeometry(Geometry geometry) {
1785 SpatialReference sourceSpatialRef = SpatialReference.create(CW_WKID);
1786 Geometry projectedGeometry = GeometryEngine.project(geometry, sourceSpatialRef);
1787 return projectedGeometry.getExtent();
1788 }
1789
1790 public void enableLocation(boolean state) {
1791 locatingEnabled = state;
1792 if (locatingEnabled) {
1793 locationManager = mapView.getLocationDisplay();
1794 locationManager.setAutoPanMode(LocationDisplay.AutoPanMode.RECENTER);
1795 locationManager.setShowPingAnimation(true);
1796 locationManager.addLocationChangedListener(new LocationDisplay.LocationChangedListener() {
1797
1798 boolean locationChanged = false;
1799
1800 @Override
1801 public void onLocationChanged(LocationDisplay.LocationChangedEvent locationChangedEvent) {
1802 LocationDataSource.Location location = locationChangedEvent.getLocation();
1803
1804 if (!locationChanged) {
1805 locationChanged = true;
1806 Point wgspoint = location.getPosition();
1807 Point mapPoint = (Point) GeometryEngine.project(wgspoint, mapView.getSpatialReference());
1808
1809 Viewpoint viewpoint = new Viewpoint(mapPoint, 100000);
1810 mapView.setViewpoint(viewpoint);
1811 }
1812 }
1813 });
1814
1815 locationManager.startAsync();
1816 }
1817 else {
1818 locationManager.stop();
1819 }
1820 }
1821
1822 public void updateFeatures(ArrayList<Integer> objectIds) {
1823
1824 ArrayList<Feature> removedFeatures = new ArrayList<>();
1825 for (Feature feature : AppState.SelectedFeatures) {
1826 Object objectId = feature.getAttributes().get(FEATURE_ATTRIBUTE_OBJECT_ID);
1827 if (objectIds.contains(((Long) objectId).intValue())) {
1828 removedFeatures.add(feature);
1829 AppState.SelectedFeatureAttributes.remove(objectId);
1830 }
1831 }
1832
1833 if (removedFeatures.size() > 0 && selectableLayer != null) {
1834 selectableLayer.unselectFeatures(removedFeatures);
1835
1836 ListenableFuture<FeatureQueryResult> selected = selectableLayer.getSelectedFeaturesAsync();
1837 try {
1838 FeatureQueryResult features = selected.get();
1839 selected.addDoneListener(() -> selectFeatures(features, false));
1840 }
1841 catch (ExecutionException | InterruptedException e) {
1842 e.printStackTrace();
1843 }
1844 }
1845
1846 // Remove the assets from downloaded feature layers.
1847 if (removedFeatures.size() > 0 && selectableGeoDatabaseLayer != null) {
1848 selectableGeoDatabaseLayer.unselectFeatures(removedFeatures);
1849 ListenableFuture<FeatureQueryResult> selected = selectableGeoDatabaseLayer.getSelectedFeaturesAsync();
1850 try {
1851 FeatureQueryResult features = selected.get();
1852 selected.addDoneListener(() -> selectFeatures(features, false));
1853 }
1854 catch (ExecutionException | InterruptedException e) {
1855 e.printStackTrace();
1856 }
1857 }
1858 }
1859
1860 private void executeSelection() {
1861 if (selectableLayer == null && AppState.SelectableMapLayer != null) {
1862
1863 Geometry geometry = GeometryEngine.simplify(selectionGraphic.getGeometry());
1864 QueryParameters query = new QueryParameters();
1865 query.setGeometry(geometry);
1866
1867 if (AppState.SelectableMapLayer.getGeodatabaseFeatureTable() != null) {
1868 GeodatabaseFeatureTable geodatabaseFeatureTable = AppState.SelectableMapLayer.getGeodatabaseFeatureTable();
1869 selectableLayer = geodatabaseFeatureTable.getFeatureLayer();
1870
1871 map.getOperationalLayers().add(selectableLayer);
1872 new SelectFeaturesTask(query, geodatabaseFeatureTable, false).execute();
1873 }
1874 else {
1875 HashMap<String, GeoDatabaseHelper.FeatureServiceItems> featureServiceItemsHashMap = GeoDatabaseHelper.getInstance().getFeatureServiceItemsHashMap();
1876 if (featureServiceItemsHashMap != null) {
1877 for (String url : featureServiceItemsHashMap.keySet()) {
1878 for (GeodatabaseFeatureTable geodatabaseFeatureTable : featureServiceItemsHashMap.get(url).getGeoDatabaseFeatureTable()) {
1879 if (StringUtils.equalIgnoreCaseAndNull(geodatabaseFeatureTable.getTableName(), AppState.SelectableMapLayer.getName())) {
1880 selectableGeoDatabaseLayer = geodatabaseFeatureTable.getFeatureLayer();
1881 new SelectFeaturesTask(query, geodatabaseFeatureTable, true).execute();
1882 return;
1883 }
1884 }
1885 }
1886 }
1887
1888 ServiceFeatureTable serviceFeatureTable = new ServiceFeatureTable(AppState.SelectableMapLayer.getLayerUrl());
1889 if (AppState.SelectableMapLayer.hasCredential() && credential != null) {
1890 serviceFeatureTable.setCredential(credential);
1891 }
1892
1893 selectableLayer = new FeatureLayer(serviceFeatureTable);
1894
1895 map.getOperationalLayers().add(selectableLayer);
1896 new SelectFeaturesTask(query, serviceFeatureTable, false).execute();
1897 }
1898 }
1899 }
1900
1901 private class SelectFeaturesTask extends AsyncTask<Void, Void, FeatureQueryResult> {
1902
1903 private QueryParameters query;
1904 private ServiceFeatureTable featureTable;
1905 private boolean selectionOnGeoDatabaseLayer;
1906 private GeodatabaseFeatureTable geodatabaseFeatureTable;
1907
1908 SelectFeaturesTask(QueryParameters query, ServiceFeatureTable featureTable, boolean selectionOnGeoDatabaseLayer) {
1909 this.query = query;
1910 this.featureTable = featureTable;
1911 this.selectionOnGeoDatabaseLayer = selectionOnGeoDatabaseLayer;
1912 }
1913
1914 SelectFeaturesTask(QueryParameters query, GeodatabaseFeatureTable geodatabaseFeatureTable, boolean selectionOnGeoDatabaseLayer) {
1915 this.query = query;
1916 this.geodatabaseFeatureTable = geodatabaseFeatureTable;
1917 this.selectionOnGeoDatabaseLayer = selectionOnGeoDatabaseLayer;
1918 }
1919
1920 @Override
1921 protected FeatureQueryResult doInBackground(Void... voids) {
1922 final ListenableFuture<FeatureQueryResult> selectFuture;
1923 if (featureTable != null) {
1924 selectFuture = featureTable.queryFeaturesAsync(query, ServiceFeatureTable.QueryFeatureFields.LOAD_ALL);
1925 }
1926 else {
1927 selectFuture = geodatabaseFeatureTable.queryFeaturesAsync(query);
1928 }
1929 FeatureQueryResult features = null;
1930 try {
1931 features = selectFuture.get();
1932 if (!selectionOnGeoDatabaseLayer) {
1933 selectableLayer.selectFeatures(features);
1934 }
1935 else {
1936 selectableGeoDatabaseLayer.selectFeatures(features);
1937 }
1938 }
1939 catch (InterruptedException | ExecutionException e) {
1940 e.printStackTrace();
1941 }
1942 return features;
1943 }
1944
1945 @Override
1946 protected void onPostExecute(FeatureQueryResult features) {
1947 selectFeatures(features, true);
1948 }
1949 }
1950
1951 private void selectFeatures(FeatureQueryResult features, boolean updateAttributeSet) {
1952 if (features != null) {
1953 // Save the raw selected features to the app state.
1954 AppState.SelectedFeatures = features;
1955
1956 // Initialize the arrays.
1957 ArrayList<SelectedEntity> selectedEntities = new ArrayList<>();
1958 ArrayList<EntityConfiguration> entityConfigurations = new ArrayList<>();
1959
1960 // Attempt the gis layer from the the layer name.
1961 GISLayer gisLayer = dbDataAccess.loadGisLayerByNameFromDatabase(AppState.SelectableMapLayer.getName());
1962
1963 // If the GIS layer was found, attempt to get the entity configuration.
1964 if (gisLayer != null && gisLayer.EntityType != null) {
1965 entityConfigurations = dbDataAccess.getEntityConfigurationByTableName(gisLayer.EntityType);
1966 }
1967
1968 for (Feature feature : features) {
1969
1970 if (updateAttributeSet) {
1971 // Add all attributes values from the feature.
1972 HashMap<String, Object> attribute = new HashMap<>();
1973 for (String key : feature.getAttributes().keySet()) {
1974 attribute.put(key.toLowerCase(), feature.getAttributes().get(key));
1975 }
1976 AppState.SelectedFeatureAttributes.put(feature.getAttributes().get(FEATURE_ATTRIBUTE_OBJECT_ID), attribute);
1977 }
1978
1979 SelectedEntity entity = new SelectedEntity();
1980
1981 Envelope envelope = getEnvelopeFromGeometry(feature.getGeometry());
1982 entity.setX(envelope.getCenter().getX());
1983 entity.setY(envelope.getCenter().getY());
1984
1985 if (entityConfigurations.size() > 0) {
1986 EntityConfiguration entityConfiguration = entityConfigurations.get(0);
1987
1988 entity.setEntityType(entityConfiguration.EntityType);
1989 entity.setEntityUId(String.valueOf(AppState.SelectedFeatureAttributes
1990 .get(feature.getAttributes().get(FEATURE_ATTRIBUTE_OBJECT_ID))
1991 .get(entityConfiguration.EntityUidField.toLowerCase())));
1992 }
1993
1994 selectedEntities.add(entity);
1995 }
1996
1997 // Save the processed selected entities to the app state.
1998 AppState.SelectedEntities = selectedEntities;
1999 AppState.SelectedFeaturesCount = selectedEntities.size();
2000 }
2001
2002 // Notify that the map selected has completed.
2003 mapSelectionCompleted();
2004 }
2005
2006 DrawStatusChangedListener drawStatusChangedListener = new DrawStatusChangedListener() {
2007 @Override
2008 public void drawStatusChanged(DrawStatusChangedEvent drawStatusChangedEvent) {
2009 if (drawStatusChangedEvent.getDrawStatus() == DrawStatus.COMPLETED) {
2010 mapView.removeDrawStatusChangedListener(drawStatusChangedListener);
2011
2012 //Load Additional Layers
2013 loadAdditionalLayers();
2014 }
2015 }
2016 };
2017
2018 private void loadAdditionalLayers() {
2019 // Load the operational layer.
2020 addOperationalLayers();
2021
2022 //Load the graphic overlays.
2023 addGraphicsOverlay();
2024
2025 // Setup legend and selectable layers.
2026 setupSelectableLayers();
2027
2028 // Show the map pins.
2029 displayPins(true);
2030
2031 // Enable the map buttons.
2032 enableMapButtons(true);
2033 }
2034
2035 CheckLoadObservable geoDatabaseLoadObservable;
2036
2037 private void refreshLayersWithGeoDatabase() {
2038 HashMap<String, GeoDatabaseHelper.FeatureServiceItems> featureServiceItemsHashMap =
2039 GeoDatabaseHelper.getInstance().getFeatureServiceItemsHashMap();
2040
2041 geoDatabaseLoadObservable = new CheckLoadObservable();
2042 geoDatabaseLoadObservable.addObserver((o, arg) -> addFeatureLayerFromDB());
2043
2044 geoDatabaseLoadObservable.updateTotalCount(featureServiceItemsHashMap.size());
2045
2046 for (String url : featureServiceItemsHashMap.keySet()) {
2047 Geodatabase generatedGeoDatabase = featureServiceItemsHashMap.get(url).getGeneratedGeoDatabase();
2048
2049 if (generatedGeoDatabase != null) {
2050 generatedGeoDatabase.addDoneLoadingListener(() -> {
2051 if (generatedGeoDatabase.getLoadStatus() == LoadStatus.LOADED) {
2052
2053 CheckLoadObservable featureTableLoadObservable = new CheckLoadObservable();
2054 featureTableLoadObservable.addObserver((o, arg) -> geoDatabaseLoadObservable.loaded());
2055
2056 featureTableLoadObservable.updateTotalCount(generatedGeoDatabase.getGeodatabaseFeatureTables().size());
2057
2058 for (GeodatabaseFeatureTable featureTable : generatedGeoDatabase.getGeodatabaseFeatureTables()) {
2059 featureTable.addDoneLoadingListener(() -> {
2060 if (featureTable.getLoadStatus() == LoadStatus.LOADED) {
2061 featureServiceItemsHashMap.get(url).getGeoDatabaseFeatureTable().add(featureTable);
2062 featureTableLoadObservable.loaded();
2063 }
2064 });
2065 featureTable.loadAsync();
2066 }
2067 }
2068 });
2069 generatedGeoDatabase.loadAsync();
2070 }
2071 else {
2072 Log.d(TAG, "GeoDatabase null: " + url);
2073 }
2074 }
2075 }
2076
2077 private void addFeatureLayerFromDB() {
2078 Log.d(TAG, "All Loaded");
2079 HashMap<String, GeoDatabaseHelper.FeatureServiceItems> featureServiceItemsHashMap =
2080 GeoDatabaseHelper.getInstance().getFeatureServiceItemsHashMap();
2081
2082 for (String url : featureServiceItemsHashMap.keySet()) {
2083 ArrayList<GeodatabaseFeatureTable> geoDatabaseFeatureTables = featureServiceItemsHashMap.get(url).getGeoDatabaseFeatureTable();
2084 for (GeodatabaseFeatureTable featureTable : geoDatabaseFeatureTables) {
2085
2086 FeatureLayer geoDatabaseFeatureLayer;
2087 if (featureTable.getFeatureLayer() == null) {
2088 geoDatabaseFeatureLayer = new FeatureLayer(featureTable);
2089 }
2090 else {
2091 geoDatabaseFeatureLayer = featureTable.getFeatureLayer();
2092 }
2093 geoDatabaseFeatureLayer.setSelectionWidth(4);
2094
2095 // Change layer visibility if hidden
2096 String serviceLayerName = featureTable.getLayerInfo().getServiceLayerName();
2097 String id = String.format("%s-%s", serviceLayerName, featureTable.getServiceLayerId());
2098
2099 if (layersHidden.contains(id)) {
2100 geoDatabaseFeatureLayer.setVisible(false);
2101 }
2102 else {
2103 geoDatabaseFeatureLayer.setVisible(true);
2104 }
2105
2106 // add geoDatabase layer to the map as a feature layer and make it selectable
2107 for (int i = 0; i < map.getOperationalLayers().size(); i++) {
2108 Layer layer = map.getOperationalLayers().get(i);
2109 if (layer instanceof FeatureLayer &&
2110 ((FeatureLayer) layer).getFeatureTable() instanceof ServiceFeatureTable) {
2111 ServiceFeatureTable featureTable1 = ((ServiceFeatureTable) ((FeatureLayer) layer).getFeatureTable());
2112 if (featureTable1.getLayerInfo().getServiceLayerName().equals(geoDatabaseFeatureLayer.getName())) {
2113 mapView.getMap().getOperationalLayers().remove(i);
2114 mapView.getMap().getOperationalLayers().add(i, geoDatabaseFeatureLayer);
2115 break;
2116 }
2117 }
2118 }
2119 }
2120 }
2121 }
2122
2123 class CalloutRecyclerViewAdapter extends RecyclerView.Adapter<CalloutRecyclerViewAdapter.CallOutViewHolder> {
2124
2125 private final int VIEW_TYPE_HEADER_LABEL = 0;
2126 private final int VIEW_TYPE_WORK_ACTIVITY = 1;
2127
2128 private List<MapPin> mapPins;
2129
2130 CalloutRecyclerViewAdapter(List<MapPin> mapPins) {
2131 this.mapPins = mapPins;
2132 }
2133
2134 @NonNull
2135 @Override
2136 public CalloutRecyclerViewAdapter.CallOutViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
2137 View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.template_item_map_callout, parent, false);
2138 return new CalloutRecyclerViewAdapter.CallOutViewHolder(view);
2139 }
2140
2141 @Override
2142 public void onBindViewHolder(@NonNull CalloutRecyclerViewAdapter.CallOutViewHolder holder, int position) {
2143 if (getItemViewType(position) == VIEW_TYPE_HEADER_LABEL) {
2144 holder.bindHeader();
2145 }
2146 else {
2147 holder.bindView(mapPins.get(position - 1));
2148 }
2149 }
2150
2151 @Override
2152 public int getItemViewType(int position) {
2153 if (position == 0) {
2154 return VIEW_TYPE_HEADER_LABEL;
2155 }
2156 else {
2157 return VIEW_TYPE_WORK_ACTIVITY;
2158 }
2159 }
2160
2161 @Override
2162 public int getItemCount() {
2163 return mapPins.size() + 1;
2164 }
2165
2166 class CallOutViewHolder extends RecyclerView.ViewHolder {
2167 private final TextView descriptionTextView;
2168 private final ImageView iconImageView;
2169 private final ImageButton openImageButton;
2170 private final TextView titleTextView;
2171
2172 CallOutViewHolder(View itemView) {
2173 super(itemView);
2174
2175 descriptionTextView = itemView.findViewById(R.id.template_item_map_callout_description);
2176 iconImageView = itemView.findViewById(R.id.template_item_map_callout_icon);
2177 openImageButton = itemView.findViewById(R.id.template_item_map_callout_open);
2178 titleTextView = itemView.findViewById(R.id.template_item_map_callout_title);
2179 }
2180
2181 void bindView(MapPin mapPin) {
2182 if (StringUtils.isNullOrWhitespace(mapPin.Description)) {
2183 descriptionTextView.setVisibility(View.GONE);
2184 }
2185 else {
2186 descriptionTextView.setText(mapPin.Description);
2187 descriptionTextView.setVisibility(View.VISIBLE);
2188 }
2189
2190 openImageButton.setTag(mapPin);
2191 openImageButton.setOnClickListener(v -> {
2192 MapPin mapPin1 = (MapPin) v.getTag();
2193 openWorkActivityPage(mapPin1.Kind, mapPin1.ActivityId);
2194 // listener.PinClicked(mapPin);
2195 });
2196 openImageButton.setVisibility(View.VISIBLE);
2197
2198 if (Integer.parseInt(mapPin.ActivityId) > 0 &&
2199 StringUtils.notEqualIgnoreCaseAndNull(mapPin.Kind, AppConstants.WORK_ACTIVITY_KIND_CASE)) {
2200 titleTextView.setText(String.format("%s #%s", mapPin.Title, mapPin.ActivityId));
2201 }
2202 else {
2203 titleTextView.setText(mapPin.Title);
2204 }
2205
2206 if (mapPin.Kind.equalsIgnoreCase(AppConstants.WORK_ACTIVITY_KIND_INSPECTION)) {
2207 iconImageView.setImageResource(R.drawable.inspection);
2208 }
2209 else if (mapPin.Kind.equalsIgnoreCase(AppConstants.WORK_ACTIVITY_KIND_CASE)) {
2210 iconImageView.setImageResource(R.drawable.permit);
2211 }
2212 else if (mapPin.Kind.equalsIgnoreCase(AppConstants.WORK_ACTIVITY_KIND_REQUEST)) {
2213 iconImageView.setImageResource(R.drawable.request);
2214 }
2215 else if (mapPin.Kind.equalsIgnoreCase(AppConstants.WORK_ACTIVITY_KIND_WORK_ORDER)) {
2216 iconImageView.setImageResource(R.drawable.work_order);
2217 }
2218 }
2219
2220 void bindHeader() {
2221 descriptionTextView.setVisibility(View.GONE);
2222 iconImageView.setVisibility(View.GONE);
2223 openImageButton.setVisibility(View.GONE);
2224
2225 titleTextView.setVisibility(View.VISIBLE);
2226 titleTextView.setText(String.format("%d " + resources.getString(R.string.work_activities_found), mapPins.size()));
2227 }
2228 }
2229 }
2230
2231 private class DisplayPinsDispatchable implements IDispatchable {
2232 private final boolean zoomToExtent;
2233 ArrayList<MapPin> pinList;
2234 Date startTime;
2235
2236 DisplayPinsDispatchable(boolean zoomToExtent) {
2237 this.zoomToExtent = zoomToExtent;
2238 }
2239
2240 @Override
2241 public void preExecute() {
2242 startTime = DateUtil.currentDateAndTime();
2243 log.i(TAG, "displayPins started!");
2244 }
2245
2246 @Override
2247 public void execute(IProgressUpdater progUpdater) {
2248 pinList = new ArrayList<>();
2249
2250 if (!inspectionsHidden) {
2251 ArrayList<Inspection> inspectionList = workActivityManager.GetInspectionsForMap();
2252 for (Inspection inspection : inspectionList) {
2253 pinList.add(new MapPin(inspection.x,
2254 inspection.y,
2255 inspection.inspTemplateName,
2256 inspection.location,
2257 String.valueOf(inspection.inspectionId),
2258 AppConstants.WORK_ACTIVITY_KIND_INSPECTION));
2259 }
2260 }
2261
2262 if (!workOrdersHidden) {
2263 ArrayList<WorkOrder> workOrderList = workActivityManager.GetWorkOrdersForMap();
2264 for (WorkOrder workOrder : workOrderList) {
2265 pinList.add(new MapPin(workOrder.WOXCoordinate,
2266 workOrder.WOYCoordinate,
2267 workOrder.Description,
2268 workOrder.Location,
2269 workOrder.WorkOrderId,
2270 AppConstants.WORK_ACTIVITY_KIND_WORK_ORDER));
2271 }
2272 }
2273
2274 if (!serviceRequestsHidden) {
2275 ArrayList<Request> requestList = workActivityManager.GetRequestsForMap();
2276 for (Request request : requestList) {
2277 pinList.add(new MapPin(request.SRX,
2278 request.SRY,
2279 request.Description,
2280 request.ProbAddress,
2281 String.valueOf(request.RequestId),
2282 AppConstants.WORK_ACTIVITY_KIND_REQUEST));
2283 }
2284 }
2285
2286 if (!casesHidden) {
2287 HashMap<CaTask, CaObject> caseList = workActivityManager.GetCasesForMap();
2288 for (CaTask caTask : caseList.keySet()) {
2289 CaObject caObject = caseList.get(caTask);
2290 String title = String.format("%s #%s", caTask.taskDesc, caObject.caseNumber);
2291
2292 pinList.add(new MapPin(caObject.x,
2293 caObject.y,
2294 title,
2295 caObject.location,
2296 String.valueOf(caTask.caTaskId),
2297 AppConstants.WORK_ACTIVITY_KIND_CASE));
2298 }
2299 }
2300
2301 if (pinList.size() > 0) {
2302 ArrayList<MapPin> removedPinsList = new ArrayList<>();
2303
2304 for (Integer searchId : savedSearchesHidden) {
2305 List<SearchDefinitionResult> searchDefinitionResults = searchDefinitionResult.bySearchId(searchId);
2306
2307 for (MapPin mapPin : pinList) {
2308 for (SearchDefinitionResult searchDefinitionResult : searchDefinitionResults) {
2309 if (StringUtils.equalIgnoreCaseAndNull(mapPin.ActivityId, String.valueOf(searchDefinitionResult.workActivityId))) {
2310 removedPinsList.add(mapPin);
2311 break;
2312 }
2313 }
2314 }
2315 }
2316
2317 for (MapPin mapPin : removedPinsList) {
2318 pinList.remove(mapPin);
2319 }
2320 }
2321 }
2322
2323 @Override
2324 public void progressUpdate(int progress) {
2325
2326 }
2327
2328 @Override
2329 public void postExecute() {
2330 addPinsToLayer(pinList);
2331
2332 Date endTime = DateUtil.currentDateAndTime();
2333 double seconds = (endTime.getTime() - startTime.getTime()) / 1000.0;
2334 log.i(TAG, String.format("Display pins finished! %s seconds", seconds));
2335
2336 if (zoomToExtent) {
2337 zoomToPinExtent();
2338 }
2339
2340 if (zoomToWorkActivityId != null && zoomToWorkActivityKind != null) {
2341 showCallOut(zoomToWorkActivityId, zoomToWorkActivityKind);
2342 }
2343 }
2344
2345 @Override
2346 public void updateTaskName(String taskName) {
2347
2348 }
2349 }
2350
2351 public class CheckLoadObservable extends Observable {
2352
2353 private int totalCount;
2354 private int loadedCount;
2355
2356 void updateTotalCount(int count) {
2357 totalCount += count;
2358 }
2359
2360 void loaded() {
2361 loadedCount++;
2362 if (totalCount == loadedCount) {
2363 setChanged();
2364 notifyObservers();
2365 }
2366 }
2367 }
2368}