4

I'm developing an app that has a app feature for displaying teams on Google maps as markers.

I'm able to display myself as marker that updates when I move & others as markers on a fragment.

The issue is markers are displayed ONLY THE 1st TIME that I go to MapFragment. When I navigate away to another fragment and come back to the map, I see an EMPTY map with no markers & no zoom buttons.

Attempt #3, please see history for previous implementations which are slightly different:

My fragment layout:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
    <fragment xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/incident_map"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:name="com.google.android.gms.maps.SupportMapFragment"
        />
    <ProgressBar
        android:id="@+id/incident_map_progress_bar"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="visible"/>
    <!--<com.google.android.gms.maps.MapView
        android:id="@+id/incident_map"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />-->

</RelativeLayout>

My IncidentMapFragment code, which is now updated based on user feedback. The updates are minimal. Instead on onResume() im using onActivityCreated() and instead of onPause() Im using onSaveInstanceState(), see updated code below

    package com.xyz.fragments;

//i did not include imports 
//Based  on https://developers.google.com/maps/documentation/android-sdk/map-with-marker
public class IncidentMapFragment extends Fragment implements OnMapReadyCallback {

    public static final String TAG = "IncidentMapFragment";
    private static  IncidentMapFragment incidentMapFragment = null;
    public static IncidentMapFragment instance() {
        if (incidentMapFragment==null)
        {
            incidentMapFragment =   new IncidentMapFragment();
        }
        return incidentMapFragment;
    }

    private MapView mapView;
    private static GoogleMap map;
    private ProgressBar progressBar;
    private SupportMapFragment mapFragment;
    public static final int UPDATE_MY_CURRENT_LOCATION = 0;
    public static final int UPDATE_MY_TEAMS_CURRENT_LOCATIONS = 1;
    public static final int UPDATE_ALL_TEAMS_CURRENT_LOCATIONS = 2;
    public static final int UPDATE_ALL_LOCATION_BASED_EVENTS = 3;
    private static Context context;
    private int MY_DEFINED_ACCESS_COARSE_LOCATION_CALLBACK_RESULT = 1;
    private int MY_DEFINED_ACCESS_FINE_LOCATION_CALLBACK_RESULT = 2;
    boolean flagCoarseLocation = false;
    boolean flagFineLocation = false;
    private WamsUnitVehicleUnitRelationshipDao vehicleUnitRelationshipDao = new WamsUnitVehicleUnitRelationshipDaoImpl();
    private static WamsUnitVehicleUnitRelationship newVehicleUnitRelationship = null;
    private static CameraPosition cp;
    private static Bundle markersBundle = new Bundle();
    private static Bundle saveStateBundle = new Bundle();
    private boolean createdStateInDestroyView = false;
    private static final String SAVED_BUNDLE_TAG = "IncidentMapFragment_SAVE_STATE";

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.incident_map_fragment, container, false);
        context = rootView.getContext();
        progressBar = rootView.findViewById(R.id.incident_map_progress_bar);
        try {

            FragmentManager fm = getActivity().getSupportFragmentManager();
            if (savedInstanceState == null) {
                mapFragment = SupportMapFragment.newInstance();
                fm.beginTransaction().replace(R.id.incident_map, mapFragment,TAG).commit();
            }
            else {
                mapFragment = (SupportMapFragment) fm
                        .findFragmentByTag(TAG);
            }

            if (savedInstanceState!=null) {
                saveStateBundle = savedInstanceState.getBundle(SAVED_BUNDLE_TAG);
                //restore camera
                cp = saveStateBundle.getParcelable("cp");
                //restore bundle of markers
                markersBundle = saveStateBundle.getParcelable("markersBundle");

                if (cp!=null && markersBundle!=null)
                {
                    reDrawMarkers(markersBundle);
                }
            }



        } catch (Exception exc) {
            Log.e(TAG, exc.getMessage());
            exc.printStackTrace();
        }
        return rootView;
    }


    @Override
    public void onActivityCreated(Bundle bundle) {
        super.onActivityCreated(bundle);
        Log.i(TAG,"onActivityCreated()");
        newVehicleUnitRelationship = vehicleUnitRelationshipDao.getWamsUnitVehicleUnitRelationship(NgfrApp.getInstance().getUniqueDeviceId());


        if (mapFragment!=null) {
            mapFragment.getMapAsync(this);
        }

    }

    @Override
    public void onSaveInstanceState(@NonNull Bundle outState) {
        Log.i(TAG,"onSaveInstanceState()");
        outState.putBundle(SAVED_BUNDLE_TAG, saveState());
        createdStateInDestroyView = false;
        super.onSaveInstanceState(outState);
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        saveStateBundle = saveState();
        createdStateInDestroyView = true;
        cp = null;
        markersBundle = null;

    }

    private Bundle saveState() {
        Bundle state = new Bundle();
        state.putParcelable("cp", cp);
        state.putParcelable("markersBundle", markersBundle);
        return state;
    }

    /*Handler used by outside class such as MqttBroker.
    1) UPDATE_MY_CURRENT_LOCATION. When my location service send a update location(lat,long) call updateMarkersOnMap() which creates a new AsyncTask to update teh display
    2) UPDATE_MY_TEAMS_CURRENT_LOCATIONS. When the MQTT dservice gets new location(lat,long) call updateMarkersOnMap() which creates a new AsyncTask to update teh display
    3) UPDATE_ALL_TEAMS_CURRENT_LOCATIONS, not implemented.
     */
    public static final Handler updateIncidentMap = new Handler(Looper.getMainLooper()) {
        public void handleMessage(Message msg) {
            //if the context is null then the MapFragment & GoogleMap objects are NOT instantiated and updating the maps non-existant UI will cause exceptions, NPE, and crashes!
            if (context != null) {
                Location myCurrentLocation = null;
                final int what = msg.what;
                switch (what) {
                    case UPDATE_MY_CURRENT_LOCATION:
                        Log.i(TAG,"UPDATE_MY_CURRENT_LOCATION");
                        myCurrentLocation = (Location) msg.obj;
                        if (map != null) {
                            updateMarkersOnMap(map,markersBundle, myCurrentLocation.getLatitude(),myCurrentLocation.getLongitude(),newVehicleUnitRelationship.getWamsId(), newVehicleUnitRelationship.getVehicleUnitId());
                        }

                        break;
                    case UPDATE_MY_TEAMS_CURRENT_LOCATIONS:
                        Log.i(TAG,"UPDATE_MY_TEAMS_CURRENT_LOCATIONS");
                        if (map != null) {
                            WamsLocationMarker wamsLocationMarker = (WamsLocationMarker) msg.obj;
                            updateMarkersOnMap(map, markersBundle,wamsLocationMarker.getLat(),wamsLocationMarker.getLon(), wamsLocationMarker.getClientId(), wamsLocationMarker.getVehicleId());
                            //mock team
                            mockTeam(map,markersBundle,  wamsLocationMarker.getLat(),wamsLocationMarker.getLon());
                        }
                        break;
                    case UPDATE_ALL_TEAMS_CURRENT_LOCATIONS:
                        break;
                    default:
                        break;
                }
            } //end if context  is NOt null
        } //end handleMessage
    };

    /*I added the @SuppressLint("MissingPermission") because we are handling this in permissionHelper(getActivity()),
    and the IDE is too naive to know*/
    @SuppressLint("MissingPermission")
    @Override
    public void onMapReady(GoogleMap googleMap) {
        Log.i(TAG, "onMapReady()");
        try {

            //remove progress bar
            progressBar.setVisibility(GONE);
            //initially zoom in my location
            map = googleMap;
            if (permissionHelper(getActivity()))
            {
                map.setMyLocationEnabled(true);
                map.getUiSettings().setZoomControlsEnabled(true);
                map.getUiSettings().setCompassEnabled(true);
            }
            if (cp != null) {
                map.moveCamera(CameraUpdateFactory.newCameraPosition(cp));
                cp = null;
            }

            reDrawMarkers(markersBundle);

        }
        catch (NullPointerException exc)
        {
            exc.printStackTrace();
        }
    }


    private static void updateMarkersOnMap(GoogleMap map,Bundle bundle, double lat,double log, String id, String vehicleId) {

        final GoogleMap _map = map;
        final double _lat = lat;
        final double _log = log;
        final String _id = id;
        final String _vehicleId = vehicleId;

        new AsyncTask < Void, Void, WamsLocationMarker > () {

            boolean update = false;
            @Override
            protected WamsLocationMarker doInBackground(Void...voids) {

                Marker marker = null;
                WamsLocationMarker wamsLocationMarker=null;
                try {
                    Log.i(TAG,"async map: "+map);
                    if (_map != null && bundle!=null)
                    {
                        Log.i(TAG,_map.toString());
                        //if the wamsLocationMarker exists,  then UPDATE
                        if (bundle.containsKey(_id)) {

                            Log.i(TAG,"markersBundle.containsKey(_id): "+ bundle.containsKey(_id));
                            Log.i(TAG,"update true");
                            //get from hashmap
                            wamsLocationMarker = (WamsLocationMarker)bundle.get(_id);
                            update = true;
                        } else {
                            //add to map
                            //if the ids are equal then this is YOU
                            wamsLocationMarker = new WamsLocationMarkerFactory().createWamsLocationMarker(_id, _vehicleId, _lat, _log);
                            Log.i(TAG,"WamsLocationMarker");
                            Log.i(TAG,"update false");
                        }
                    } else {

                        Log.e(TAG, " updateMarkersOnMap() map is " + _map);
                    }

                }
                catch (NullPointerException exc)
                {
                    exc.printStackTrace();
                }
                return wamsLocationMarker;
            }

            @Override
            protected void onPostExecute(WamsLocationMarker wamsLocationMarker) {
                super.onPostExecute(wamsLocationMarker);
                try {
                    if (wamsLocationMarker != null) {
                        Log.i(TAG,"onPostExecute() update:"+update+",wamsLocationMarker:"+wamsLocationMarker);

                        if (update) {

                            Log.i(TAG,"onPostExecute() update:"+update);

                            //UPDATE wth new lat & long if the markers coordinates have change, else dont redraw, beacuse the draw causes a refresh affect & its also a waste computationally
                            if (wamsLocationMarker.getMarker().getPosition().latitude != _lat && wamsLocationMarker.getMarker().getPosition().longitude != _log) {

                                LatLng latLng = new LatLng(_lat, _log);
                                //UPDATE THE MARKER POSITION
                                wamsLocationMarker.getMarker().setPosition(latLng);
                            }

                        } else {
                            //ADD A NEW MARKER
                            Marker marker = _map.addMarker(wamsLocationMarker.getMakerOptions());
                            Log.i(TAG,"ASYNC MARKER:"+marker.getId());
                            wamsLocationMarker.setMarker(marker);
                            markersBundle.remove(wamsLocationMarker.getClientId());
                            markersBundle.putParcelable(wamsLocationMarker.getClientId(), wamsLocationMarker);
                        }
                    }
                }
                catch (NullPointerException exc)
                {
                    exc.printStackTrace();
                }

            }
        }.execute();

    }


      public void reDrawMarkers(Bundle bundle) {
    Log.i(TAG,"reDrawMarkers()");
    if (bundle!=null) {
        Set<String> keys = bundle.keySet();
        WamsLocationMarker wamsLocationMarker = null;
        for (String k : keys) {
            Log.i(TAG, "key:" + k);
            wamsLocationMarker = (WamsLocationMarker) bundle.getParcelable(k);
            updateMarkersOnMap(map, bundle, wamsLocationMarker.getLat(), wamsLocationMarker.getLon(), wamsLocationMarker.getClientId(), wamsLocationMarker.getVehicleId());
        }
    }
}


    //Just for test - Im mocking some there points to represent other teams mates on the map, this is just for testing
    private static void mockTeam(GoogleMap map, Bundle bundle, double _lat, double _log) {

        updateMarkersOnMap(map,bundle, _lat+0.001, _log , "mock111111111", newVehicleUnitRelationship.getVehicleUnitId());


        updateMarkersOnMap(map,bundle,_lat, _log+0.001 ,"mock222222222", newVehicleUnitRelationship.getVehicleUnitId());


        updateMarkersOnMap(map,bundle, _lat-0.001, _log,"mock33333333", newVehicleUnitRelationship.getVehicleUnitId());

        updateMarkersOnMap(map,bundle, _lat, _log-0.001,"mock444444444", newVehicleUnitRelationship.getVehicleUnitId());


    }

    //Ask the user if  required & attempt to grant required location services
    private boolean permissionHelper(Activity activity) {

        String permission = Manifest.permission.ACCESS_COARSE_LOCATION;
        int res = getContext().checkCallingPermission(permission);
        //if the required coarse & fine permissions are granted then return true else proceed to get the permissions
        if (res == PackageManager.PERMISSION_GRANTED) {
            Log.i(TAG, "ACCESS_COARSE_LOCATION GRANTED");

            permission = Manifest.permission.ACCESS_FINE_LOCATION;
            res = getContext().checkCallingPermission(permission);
            if (res == PackageManager.PERMISSION_GRANTED) {
                Log.i(TAG, "ACCESS_FINE_LOCATION GRANTED");
                flagFineLocation = true;
            } else {
                Log.i(TAG, "ACCESS_FINE_LOCATION NOT GRANTED");
            }

            flagCoarseLocation = true;
        }
        //prompt user for COARSE location permission. If the user cancel then display toast & navigate back
        if (!flagCoarseLocation) {

            if (ActivityCompat.shouldShowRequestPermissionRationale(activity,
                    Manifest.permission.ACCESS_COARSE_LOCATION)) {

                dialogForCoarsePermission();

            } else {
                ActivityCompat.requestPermissions(activity,
                        new String[] {
                                Manifest.permission.ACCESS_COARSE_LOCATION
                        },
                        MY_DEFINED_ACCESS_COARSE_LOCATION_CALLBACK_RESULT);
                flagCoarseLocation = true;

            }
        }
        //prompt user for FINE location permission. If the user cancel then display toast & navigate back
        if (!flagFineLocation) {




            if (ActivityCompat.shouldShowRequestPermissionRationale(activity,
                    Manifest.permission.ACCESS_FINE_LOCATION)) {

                dialogForFinePermission();

            } else {
                ActivityCompat.requestPermissions(activity,
                        new String[] {
                                Manifest.permission.ACCESS_COARSE_LOCATION
                        },
                        MY_DEFINED_ACCESS_FINE_LOCATION_CALLBACK_RESULT);
                flagFineLocation = true;


            }
        }

        if (!flagCoarseLocation)
        {
            Log.i(TAG, "ACCESS_COARSE_LOCATION NOT GRANTED");
        }
        else
        {

            Log.i(TAG, "ACCESS_COARSE_LOCATION GRANTED");
        }
        if (!flagFineLocation)
        {
            Log.i(TAG, "ACCESS_FINE_LOCATION NOT GRANTED");
        }
        else
        {
            Log.i(TAG, "ACCESS_FINE_LOCATION GRANTED");
        }

        return (flagCoarseLocation && flagFineLocation);
    }

    private void dialogForCoarsePermission() {
        DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                switch (which) {
                    case DialogInterface.BUTTON_POSITIVE:
                        //Yes button clicked
                        ActivityCompat.requestPermissions(getActivity(),
                                new String[] {
                                        Manifest.permission.ACCESS_COARSE_LOCATION
                                },
                                MY_DEFINED_ACCESS_COARSE_LOCATION_CALLBACK_RESULT);
                        break;

                    case DialogInterface.BUTTON_NEGATIVE:
                        //No button clicked
                        break;
                }
            }
        };

        AlertDialog.Builder builder = new AlertDialog.Builder(context);
        builder.setMessage("The map requires coarse location permission.Please it by pressing Yes. ").setPositiveButton("Yes", dialogClickListener)
                .setNegativeButton("No", dialogClickListener).show();
    }

    private void dialogForFinePermission() {
        DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                switch (which) {
                    case DialogInterface.BUTTON_POSITIVE:
                        //Yes button clicked
                        ActivityCompat.requestPermissions(getActivity(),
                                new String[] {
                                        Manifest.permission.ACCESS_FINE_LOCATION
                                },
                                MY_DEFINED_ACCESS_FINE_LOCATION_CALLBACK_RESULT);
                        break;

                    case DialogInterface.BUTTON_NEGATIVE:
                        //No button clicked
                        break;
                }
            }
        };

        AlertDialog.Builder builder = new AlertDialog.Builder(context);
        builder.setMessage("The map ALSO requires fine location permission.Please it by pressing Yes. ").setPositiveButton("Yes", dialogClickListener)
                .setNegativeButton("No", dialogClickListener).show();
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        Log.i(TAG, "onRequestPermissionsResult() request code:" + requestCode); // Log printed
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == MY_DEFINED_ACCESS_COARSE_LOCATION_CALLBACK_RESULT) {
            flagCoarseLocation = true;
            Toast.makeText(context, "Coarse location permission granted.", Toast.LENGTH_SHORT).show();

        }
        if (requestCode == MY_DEFINED_ACCESS_FINE_LOCATION_CALLBACK_RESULT) {
            flagFineLocation = true;
            Toast.makeText(context, "Fine location permission granted.", Toast.LENGTH_SHORT).show();
        }

    }
}

I provided the GUI & corresponding logs for this fragment only.

The 1st time I navigate to the fragment. Everything is ok, and you see the go to my location button & zoom buttons:

enter image description here

Now the issue appears when I navigate to 1st fragment called "Services" or if leave app in the background for a while. I cant see the markers anymore & new location updates coming via the network are added however are NOT being drawn. Map zoom buttons are also missing!!!

See snapshot & logs below: enter image description here

08-16 08:06:03.358 1314-1314/com.xyz I/IncidentMapFragment: onSaveInstanceState()

08-16 08:06:03.836 1314-1314/com.xyz I/IncidentMapFragment: reDrawMarkers()
    key:015140000100161
    key:mock33333333
    key:mock444444444
    key:mock111111111
    key:mock222222222

08-16 08:06:03.836 1314-1340/com.xyz I/IncidentMapFragment: async map: com.google.android.gms.maps.GoogleMap@1e72e3b
    com.google.android.gms.maps.GoogleMap@1e72e3b
    markersBundle.containsKey(_id): true
    update true
    async map: com.google.android.gms.maps.GoogleMap@1e72e3b
    com.google.android.gms.maps.GoogleMap@1e72e3b
    markersBundle.containsKey(_id): true
    update true
    async map: com.google.android.gms.maps.GoogleMap@1e72e3b
    com.google.android.gms.maps.GoogleMap@1e72e3b
    markersBundle.containsKey(_id): true
    update true
    async map: com.google.android.gms.maps.GoogleMap@1e72e3b
    com.google.android.gms.maps.GoogleMap@1e72e3b
    markersBundle.containsKey(_id): true
    update true
    async map: com.google.android.gms.maps.GoogleMap@1e72e3b
    com.google.android.gms.maps.GoogleMap@1e72e3b
    markersBundle.containsKey(_id): true
    update true

08-16 08:06:03.837 1314-1314/com.xyz I/IncidentMapFragment: onActivityCreated()

08-16 08:06:03.851 1314-1314/com.xyz I/IncidentMapFragment: onPostExecute() update:true,wamsLocationMarker:com.xyz.mapping.WamsLocationMarker@19b04df
    onPostExecute() update:true
    onPostExecute() update:true,wamsLocationMarker:com.xyz.mapping.WamsLocationMarker@47c13e2
    onPostExecute() update:true
    onPostExecute() update:true,wamsLocationMarker:com.xyz.mapping.WamsLocationMarker@6b22ea9
    onPostExecute() update:true
    onPostExecute() update:true,wamsLocationMarker:com.xyz.mapping.WamsLocationMarker@e387818

08-16 08:06:03.852 1314-1314/com.xyz I/IncidentMapFragment: onPostExecute() update:true
    onPostExecute() update:true,wamsLocationMarker:com.xyz.mapping.WamsLocationMarker@dab0d7
    onPostExecute() update:true
    onMapReady()
    ACCESS_COARSE_LOCATION GRANTED
    ACCESS_FINE_LOCATION GRANTED

08-16 08:06:03.853 1314-1314/com.xyz I/IncidentMapFragment: reDrawMarkers()
08-16 08:06:03.854 1314-1314/com.xyz I/IncidentMapFragment: key:015140000100161
    key:mock33333333
    key:mock444444444
    key:mock111111111
    key:mock222222222

08-16 08:06:03.854 1314-1338/com.xyz I/IncidentMapFragment: async map: com.google.android.gms.maps.GoogleMap@b7d1933
    com.google.android.gms.maps.GoogleMap@b7d1933
    markersBundle.containsKey(_id): true
    update true
    async map: com.google.android.gms.maps.GoogleMap@b7d1933
    com.google.android.gms.maps.GoogleMap@b7d1933
    markersBundle.containsKey(_id): true
    update true
    async map: com.google.android.gms.maps.GoogleMap@b7d1933
    com.google.android.gms.maps.GoogleMap@b7d1933
    markersBundle.containsKey(_id): true
    update true
    async map: com.google.android.gms.maps.GoogleMap@b7d1933
    com.google.android.gms.maps.GoogleMap@b7d1933
    markersBundle.containsKey(_id): true
    update true
    async map: com.google.android.gms.maps.GoogleMap@b7d1933
    com.google.android.gms.maps.GoogleMap@b7d1933
    markersBundle.containsKey(_id): true
    update true

08-16 08:06:03.865 1314-1314/com.xyz I/IncidentMapFragment: onPostExecute() update:true,wamsLocationMarker:com.xyz.mapping.WamsLocationMarker@19b04df
    onPostExecute() update:true
    onPostExecute() update:true,wamsLocationMarker:com.xyz.mapping.WamsLocationMarker@47c13e2
    onPostExecute() update:true
    onPostExecute() update:true,wamsLocationMarker:com.xyz.mapping.WamsLocationMarker@6b22ea9
    onPostExecute() update:true
    onPostExecute() update:true,wamsLocationMarker:com.xyz.mapping.WamsLocationMarker@e387818
    onPostExecute() update:true
    onPostExecute() update:true,wamsLocationMarker:com.xyz.mapping.WamsLocationMarker@dab0d7
    onPostExecute() update:true

See debug window: state is restored but not drawing enter image description here My logs show the markers are being added again with no errors or exceptions, but not being displayed?** Why? And how can i fix this?

Thanks

Renz Manacmol
  • 869
  • 12
  • 27
cyber101
  • 2,822
  • 14
  • 50
  • 93

3 Answers3

0

Obviously the 1st fragment is calling onResume(),onMapReady(),onPause(), which is resetting the map

You are probably handling it wrongly therefore I still assume the problem is still about the Activity /Fragment State Change.

One easy tweak to see whether it is due to the fragment state change is to stoer all the values to the SharedPreference whenever a user place a marker, and assign them to the map every time the state is change (onPause, onResume etc).

For Fragment State Change Explanation

Different events, some user-triggered and some system-triggered, can cause an Activity / Fragment to transition from one state to another as well as resetting the activity/fragment. Therefore, in android, you need to handle different state of your activity as well as fragment.

For handling activity state change, you could reference Android doc.

For your case:

There is a really really good example and details on how to handle Fragment State Change.

I hope it helps!

Angus
  • 3,680
  • 1
  • 12
  • 27
  • Im going to follow that in detail & re-implement and tell give you an update today. – cyber101 Aug 14 '18 at 17:54
  • I updated the code based on the fragment tutorial,see the updated code above under section/attempt #2. However as i suspected i should not matter because I was saving the makers in a static hashmap that does not get destroyed due to its being static. I was using onResume, onPause, and setUserVisibleHint() call myResume() which added the markers back. On the other hand the tutorial is absolutely right I should use onSaveInstanceState() instead on onPause() & onActivityCreated() instead on onResume(), which im doing now. As before markers are being added but NOT appearing!. – cyber101 Aug 15 '18 at 23:58
  • @cyber101 ok, I will go line by line and try to sort it out – Angus Aug 16 '18 at 01:28
  • I refactored the code & tested again best some fragment instance saving "design patterns". The issue was & still is not saving the instance of marks,etc.. but why Im not able to redraw. The map is clearly there & markers are clearly being added, but why are they NOT appearing? – cyber101 Aug 16 '18 at 02:17
  • @cyber101 so does it load the variables when you reopen the fragment (use log)? *I'm working now, so it takes time to check your code – Angus Aug 16 '18 at 02:33
  • can you upload the latest log? I want to see whether what I think is true – Angus Aug 16 '18 at 05:28
  • i want to see Log.i(TAG,"key:"+k); – Angus Aug 16 '18 at 05:51
  • I added the lastest log for when i navigate back & see no markers. – cyber101 Aug 16 '18 at 15:11
  • @cyber101 did you try the code I posted in another answer? – Angus Aug 16 '18 at 15:13
  • ,I will try it now, Im looking to see what the major difference is, because if you saw I updated the code based on your & previous user recommendation, so i just want to take in your difference, will let you know in 30 min – cyber101 Aug 16 '18 at 17:20
  • I tried the code with one difference,restoration of state should be done in onCreateView(), the difference is `if (savedInstanceState!=null) { bundleOfMarkers = savedInstanceState.getBundle("bundleOfMarkers"); myResume(); }` instead of `@Override protected void onRestoreInstanceState(Bundle savedInstanceState) { Log.i(TAG,"onRestoreInstanceState()"); super.onRestoreInstanceState(savedInstanceState); bundleOfMarkers = savedInstanceState.getBundle("bundleOfMarkers"); myResume(); }` which is incorrect. – cyber101 Aug 16 '18 at 17:51
  • ,I added snapshot confirming state is restored, see the snapshot of my debugger, titled "state is restored but not drawing". – cyber101 Aug 16 '18 at 17:53
  • Ok Im going to hack this now, so my fragment launches an activity which contains the map. At thsi point the project go on...I will update you on if this works. – cyber101 Aug 16 '18 at 20:57
  • @cyber101 I made quite some changes not just 1 place, some flow I have change... and make changes throughout your code, you can copy and paste to try it out – Angus Aug 16 '18 at 23:48
  • I copied & pasted the code minus the onRestoreInstanceState() because state in fragment should be restored in onCreateView() or onActivityCreated(). I confirmed it made no difference. See the snap shot. Although we get back state there is no redraw. Anyways im trying activity approach will let you know shortly... – cyber101 Aug 17 '18 at 00:21
  • Refer to"onRestoreInstanceState() because state in fragment should be restored in onCreateView() or onActivityCreated()." You are wrong! Why do you put in onCreateView?? onRestoreInstanceState() will auto assign the variable value... – Angus Aug 17 '18 at 01:28
  • https://stackoverflow.com/questions/23472415/should-i-restore-savedinstancestate-in-oncreate-or-in-onrestoreinstancestate – Angus Aug 17 '18 at 01:28
  • Ok sure but onCreateView has savedInstanceState in its signature & im confirm that the data is present. The issue is NOT state its redrawing markers & buttons. – cyber101 Aug 17 '18 at 01:56
  • neither GoogleMap class nor MapFragment have a redraw() method or invalidate(). You can draw by using the many addXxx() functions which I'm already doing, with no errors but the markers are NOT showing. – cyber101 Aug 17 '18 at 05:47
  • How you add the marker to the map, u do it in onRestoreInstanceState – Angus Aug 17 '18 at 06:02
  • No if you check the code its done, for 1st you do onMapReady()->myResume()->updateMarkersOnMap()->AddMarkers() & for navigation back onRestoreInstanceState()->updateMarkersOnMap()->AddMarkers(); which probably means although the GoogleMap object is there & not null, its NOT ready :) I will try something and let you know – cyber101 Aug 17 '18 at 06:12
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/178228/discussion-between-cyber101-and-angus-tay). – cyber101 Aug 17 '18 at 22:42
  • I rewrote the logic with Activity & it works like charm, I will post the update today. – cyber101 Aug 20 '18 at 17:39
0

You don't have to store state changes for this as other answer suggested. But the MapFragment implementation is wrong. You have to fix it.

Change the code in onActivityCreated() to this and move it to onCreate(). You don't have to override onActivityCreated at all.

mapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.incident_map);
mapFragment.getMapAsync(this);
Bertram Gilfoyle
  • 9,899
  • 6
  • 42
  • 67
0

Assuming the code you gave is correct, replace your code with this. Try it out, I have make some changes

package com.xyz.fragments;

//I didnt not include imports in order to have a smaller post on Stackoverflow

//Based  on https://developers.google.com/maps/documentation/android-sdk/map-with-marker
public class IncidentMapFragment extends Fragment implements OnMapReadyCallback {

    public static final String TAG = "IncidentMapFragment";
    private static  IncidentMapFragment incidentMapFragment = null;
    public static IncidentMapFragment instance() {
        if (incidentMapFragment==null)
        {
            incidentMapFragment =   new IncidentMapFragment();
        }
        return incidentMapFragment;
    }

    private MapView mapView;
    private static GoogleMap map;
    private ProgressBar progressBar;
    private SupportMapFragment mapFragment;
    public static final int UPDATE_MY_CURRENT_LOCATION = 0;
    public static final int UPDATE_MY_TEAMS_CURRENT_LOCATIONS = 1;
    public static final int UPDATE_ALL_TEAMS_CURRENT_LOCATIONS = 2;
    public static final int UPDATE_ALL_LOCATION_BASED_EVENTS = 3;
    private static Context context;
    private int MY_DEFINED_ACCESS_COARSE_LOCATION_CALLBACK_RESULT = 1;
    private int MY_DEFINED_ACCESS_FINE_LOCATION_CALLBACK_RESULT = 2;
    boolean flagCoarseLocation = false;
    boolean flagFineLocation = false;
    private static CameraPosition cp;
    private WamsUnitVehicleUnitRelationshipDao vehicleUnitRelationshipDao = new WamsUnitVehicleUnitRelationshipDaoImpl();
    private static WamsUnitVehicleUnitRelationship newVehicleUnitRelationship = null;
    private static Bundle bundleOfMarkers = new Bundle();


    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.incident_map_fragment, container, false);
        context = rootView.getContext();
        Bundle bundleOfMarkers = new Bundle();
        progressBar = rootView.findViewById(R.id.incident_map_progress_bar);
        try {

            FragmentManager fm = getActivity().getSupportFragmentManager();
            mapFragment = (SupportMapFragment) fm.findFragmentById(R.id.incident_map);
            if (mapFragment == null) {
                mapFragment = SupportMapFragment.newInstance();
                fm.beginTransaction().replace(R.id.incident_map, mapFragment).commit();
            }

        } catch (Exception exc) {
            Log.e(TAG, exc.getMessage());
            exc.printStackTrace();
        }
        return rootView;
    }


    @Override
    public void onActivityCreated(Bundle bundle) {
        super.onActivityCreated(bundle);
        Log.i(TAG,"onActivityCreated()");
        newVehicleUnitRelationship = vehicleUnitRelationshipDao.getWamsUnitVehicleUnitRelationship(NgfrApp.getInstance().getUniqueDeviceId());
        if (bundle!=null) {
            bundleOfMarkers = bundle.getBundle("bundleOfMarkers");
            cp = bundle.getParcelable("cp");
        }
        if (mapFragment!=null) {
            mapFragment.getMapAsync(this);
        }

    }

    /*Handler used by outside class such as MqttBroker.
    1) UPDATE_MY_CURRENT_LOCATION. When my location service send a update location(lat,long) call updateMarkersOnMap() which creates a new AsyncTask to update teh display
    2) UPDATE_MY_TEAMS_CURRENT_LOCATIONS. When the MQTT dservice gets new location(lat,long) call updateMarkersOnMap() which creates a new AsyncTask to update teh display
    3) UPDATE_ALL_TEAMS_CURRENT_LOCATIONS, not implemented.
     */
    public static final Handler updateIncidentMap = new Handler(Looper.getMainLooper()) {
        public void handleMessage(Message msg) {
            //if the context is null then the MapFragment & GoogleMap objects are NOT instantiated and updating the maps non-existant UI will cause exceptions, NPE, and crashes!
            if (context != null) {
                Location myCurrentLocation = null;
                final int what = msg.what;
                switch (what) {
                    case UPDATE_MY_CURRENT_LOCATION:
                        Log.i(TAG,"UPDATE_MY_CURRENT_LOCATION");
                        myCurrentLocation = (Location) msg.obj;
                        if (map != null) {
                            updateMarkersOnMap(map, myCurrentLocation.getLatitude(),myCurrentLocation.getLongitude(),newVehicleUnitRelationship.getWamsId(), newVehicleUnitRelationship.getVehicleUnitId());
                        }

                        break;
                    case UPDATE_MY_TEAMS_CURRENT_LOCATIONS:
                        Log.i(TAG,"UPDATE_MY_TEAMS_CURRENT_LOCATIONS");
                        if (map != null) {
                            WamsLocationMarker wamsLocationMarker = (WamsLocationMarker) msg.obj;
                            updateMarkersOnMap(map, wamsLocationMarker.getLat(),wamsLocationMarker.getLon(), wamsLocationMarker.getClientId(), wamsLocationMarker.getVehicleId());
                            //mock team
                            mockTeam(map, wamsLocationMarker.getLat(),wamsLocationMarker.getLon());
                        }
                        break;
                    case UPDATE_ALL_TEAMS_CURRENT_LOCATIONS:
                        break;
                    default:
                        break;
                }
            } //end if context  is NOt null
        } //end handleMessage
    };

    /*I added the @SuppressLint("MissingPermission") because we are handling this in permissionHelper(getActivity()),
    and the IDE is too naive to know*/
    @SuppressLint("MissingPermission")
    @Override
    public void onMapReady(GoogleMap googleMap) {
        Log.i(TAG, "onMapReady()");
        try {

            //remove progress bar
            progressBar.setVisibility(GONE);
            //initially zoom in my location
            map = googleMap;
            if (permissionHelper(getActivity()))
            {
                map.setMyLocationEnabled(true);
                map.getUiSettings().setZoomControlsEnabled(true);
                map.getUiSettings().setCompassEnabled(true);
            }
            if (cp != null) {
                map.moveCamera(CameraUpdateFactory.newCameraPosition(cp));
                cp = null;
            }

            myResume();

        }
        catch (NullPointerException exc)
        {
            exc.printStackTrace();
        }
    }


    private static void updateMarkersOnMap(GoogleMap map,double lat,double log, String id, String vehicleId) {

        final GoogleMap _map = map;
        final double _lat = lat;
        final double _log = log;
        final String _id = id;
        final String _vehicleId = vehicleId;

        new AsyncTask < Void, Void, WamsLocationMarker > () {

            boolean update = false;
            @Override
            protected WamsLocationMarker doInBackground(Void...voids) {

                Marker marker = null;
                WamsLocationMarker wamsLocationMarker=null;
                try {
                    Log.i(TAG,"async map: "+map);
                    if (_map != null)
                    {
                        Log.i(TAG,_map.toString());
                        //if the wamsLocationMarker exists,  then UPDATE
                        if (bundleOfMarkers.containsKey(_id)) {

                            Log.i(TAG,"bundleOfMarkers.containsKey(_id): "+bundleOfMarkers.containsKey(_id));
                            Log.i(TAG,"update true");
                            //get from hashmap
                            wamsLocationMarker = (WamsLocationMarker)bundleOfMarkers.get(_id);
                            update = true;
                        } else {
                            //add to map
                            //if the ids are equal then this is YOU
                            wamsLocationMarker = new WamsLocationMarkerFactory().createWamsLocationMarker(_id, _vehicleId, _lat, _log);
                            Log.i(TAG,"WamsLocationMarker");
                            Log.i(TAG,"update false");
                        }
                    } else {

                        Log.e(TAG, " updateMarkersOnMap() map is " + _map);
                    }

                }
                catch (NullPointerException exc)
                {
                    exc.printStackTrace();
                }
                return wamsLocationMarker;
            }

            @Override
            protected void onPostExecute(WamsLocationMarker wamsLocationMarker) {
                super.onPostExecute(wamsLocationMarker);
                try {
                    if (wamsLocationMarker != null) {
                        Log.i(TAG,"onPostExecute() update:"+update+",wamsLocationMarker:"+wamsLocationMarker);

                        if (update) {

                            Log.i(TAG,"onPostExecute() update:"+update);

                            //UPDATE wth new lat & long if the markers coordinates have change, else dont redraw, beacuse the draw causes a refresh affect & its also a waste computationally
                            if (wamsLocationMarker.getMarker().getPosition().latitude != _lat && wamsLocationMarker.getMarker().getPosition().longitude != _log) {

                                LatLng latLng = new LatLng(_lat, _log);
                                //UPDATE THE MARKER POSITION
                                wamsLocationMarker.getMarker().setPosition(latLng);
                            }

                        } else {
                            //ADD A NEW MARKER
                            Marker marker = _map.addMarker(wamsLocationMarker.getMakerOptions());
                            Log.i(TAG,"ASYNC MARKER:"+marker.getId());
                            wamsLocationMarker.setMarker(marker);
                            bundleOfMarkers.remove(wamsLocationMarker.getClientId());
                            bundleOfMarkers.putParcelable(wamsLocationMarker.getClientId(), wamsLocationMarker);
                        }
                    }
                }
                catch (NullPointerException exc)
                {
                    exc.printStackTrace();
                }

            }
        }.execute();

    }

    /*I also tried using onSaveInstanceState & onViewStateRestored. I saved the markers on map in Bundle & put them in parceable.
    However I was unable to redraw the map*/

    @Override
    public void onSaveInstanceState(@NonNull Bundle outState) {
        super.onSaveInstanceState(outState);
        Log.i(TAG,"onSaveInstanceState()");
        myPause();
        outState.putBundle("bundleOfMarkers", bundleOfMarkers);
        outState.putParcelable("cp",cp); 
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        Log.i(TAG,"onRestoreInstanceState()");
        super.onRestoreInstanceState(savedInstanceState);
        bundleOfMarkers = savedInstanceState.getBundle("bundleOfMarkers");
        myResume();
    }
       /*
    @Override
    public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
        Log.i(TAG,"onViewStateRestored(),savedInstanceState:"+savedInstanceState);
        super.onViewStateRestored(savedInstanceState);

    }*/


   /* @Override

    public void onResume() {
        super.onResume();
        Log.i(TAG,"onResume()");
        if (mapFragment!=null) {
            mapFragment.getMapAsync(this);
        }
        newVehicleUnitRelationship = vehicleUnitRelationshipDao.getWamsUnitVehicleUnitRelationship(NgfrApp.getInstance().getUniqueDeviceId());



    }*/

    /*@Override
    public void onPause() {
        Log.i(TAG,"onPause()");
        super.onPause();
        myPause();
    }*/


   public void myResume() {
       Log.i(TAG,"myResume()");
       Set<String> keys = bundleOfMarkers.keySet();
       WamsLocationMarker wamsLocationMarker = null;
       for (String k : keys) {
           Log.i(TAG,"key:"+k);
           wamsLocationMarker = (WamsLocationMarker) bundleOfMarkers.getParcelable(k);
           updateMarkersOnMap(map, wamsLocationMarker.getLat(), wamsLocationMarker.getLon(), wamsLocationMarker.getClientId(), wamsLocationMarker.getVehicleId());
       }
   }

   public void myPause()
   {
       Log.i(TAG,"myPause()");
       if (map!=null) {
           //lets get  the camera position & markers
           cp = map.getCameraPosition();
       }

       //keys in hashmap
       Set<String> keys = IncidentMapFragment.bundleOfMarkers.keySet();
       WamsLocationMarker wamsLocationMarker = null;
       for (String k : keys) {
           wamsLocationMarker = (WamsLocationMarker) IncidentMapFragment.bundleOfMarkers.get(k);
           bundleOfMarkers.putParcelable(k,wamsLocationMarker);
       }
   }

    //Just for test - Im mocking some there points to represent other teams mates on the map, this is just for testing
    private static void mockTeam(GoogleMap map, double _lat, double _log) {

        updateMarkersOnMap(map, _lat+0.001, _log , "mock111111111", newVehicleUnitRelationship.getVehicleUnitId());


        updateMarkersOnMap(map,_lat, _log+0.001 ,"mock222222222", newVehicleUnitRelationship.getVehicleUnitId());


        updateMarkersOnMap(map, _lat-0.001, _log,"mock33333333", newVehicleUnitRelationship.getVehicleUnitId());

        updateMarkersOnMap(map, _lat, _log-0.001,"mock444444444", newVehicleUnitRelationship.getVehicleUnitId());


    }

    //Ask the user if  required & attempt to grant required location services
    private boolean permissionHelper(Activity activity) {

        String permission = Manifest.permission.ACCESS_COARSE_LOCATION;
        int res = getContext().checkCallingPermission(permission);
        //if the required coarse & fine permissions are granted then return true else proceed to get the permissions
        if (res == PackageManager.PERMISSION_GRANTED) {
            Log.i(TAG, "ACCESS_COARSE_LOCATION GRANTED");

            permission = Manifest.permission.ACCESS_FINE_LOCATION;
            res = getContext().checkCallingPermission(permission);
            if (res == PackageManager.PERMISSION_GRANTED) {
                Log.i(TAG, "ACCESS_FINE_LOCATION GRANTED");
                flagFineLocation = true;
            } else {
                Log.i(TAG, "ACCESS_FINE_LOCATION NOT GRANTED");
            }

            flagCoarseLocation = true;
        }
        //prompt user for COARSE location permission. If the user cancel then display toast & navigate back
        if (!flagCoarseLocation) {

            if (ActivityCompat.shouldShowRequestPermissionRationale(activity,
                    Manifest.permission.ACCESS_COARSE_LOCATION)) {

                dialogForCoarsePermission();

            } else {
                ActivityCompat.requestPermissions(activity,
                        new String[] {
                                Manifest.permission.ACCESS_COARSE_LOCATION
                        },
                        MY_DEFINED_ACCESS_COARSE_LOCATION_CALLBACK_RESULT);
                flagCoarseLocation = true;

            }
        }
        //prompt user for FINE location permission. If the user cancel then display toast & navigate back
        if (!flagFineLocation) {

            if (ActivityCompat.shouldShowRequestPermissionRationale(activity,
                    Manifest.permission.ACCESS_FINE_LOCATION)) {

                dialogForFinePermission();

            } else {
                ActivityCompat.requestPermissions(activity,
                        new String[] {
                                Manifest.permission.ACCESS_COARSE_LOCATION
                        },
                        MY_DEFINED_ACCESS_FINE_LOCATION_CALLBACK_RESULT);
                flagFineLocation = true;


            }
        }

        if (!flagCoarseLocation)
        {
            Log.i(TAG, "ACCESS_COARSE_LOCATION NOT GRANTED");
        }
        else
        {

            Log.i(TAG, "ACCESS_COARSE_LOCATION GRANTED");
        }
        if (!flagFineLocation)
        {
            Log.i(TAG, "ACCESS_FINE_LOCATION NOT GRANTED");
        }
        else
        {
            Log.i(TAG, "ACCESS_FINE_LOCATION GRANTED");
        }

        return (flagCoarseLocation && flagFineLocation);
    }

    private void dialogForCoarsePermission() {
        DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                switch (which) {
                    case DialogInterface.BUTTON_POSITIVE:
                        //Yes button clicked
                        ActivityCompat.requestPermissions(getActivity(),
                                new String[] {
                                        Manifest.permission.ACCESS_COARSE_LOCATION
                                },
                                MY_DEFINED_ACCESS_COARSE_LOCATION_CALLBACK_RESULT);
                        break;

                    case DialogInterface.BUTTON_NEGATIVE:
                        //No button clicked
                        break;
                }
            }
        };

        AlertDialog.Builder builder = new AlertDialog.Builder(context);
        builder.setMessage("The map requires coarse location permission.Please it by pressing Yes. ").setPositiveButton("Yes", dialogClickListener)
                .setNegativeButton("No", dialogClickListener).show();
    }

    private void dialogForFinePermission() {
        DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                switch (which) {
                    case DialogInterface.BUTTON_POSITIVE:
                        //Yes button clicked
                        ActivityCompat.requestPermissions(getActivity(),
                                new String[] {
                                        Manifest.permission.ACCESS_FINE_LOCATION
                                },
                                MY_DEFINED_ACCESS_FINE_LOCATION_CALLBACK_RESULT);
                        break;

                    case DialogInterface.BUTTON_NEGATIVE:
                        //No button clicked
                        break;
                }
            }
        };

        AlertDialog.Builder builder = new AlertDialog.Builder(context);
        builder.setMessage("The map ALSO requires fine location permission.Please it by pressing Yes. ").setPositiveButton("Yes", dialogClickListener)
                .setNegativeButton("No", dialogClickListener).show();
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        Log.i(TAG, "onRequestPermissionsResult() request code:" + requestCode); // Log printed
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == MY_DEFINED_ACCESS_COARSE_LOCATION_CALLBACK_RESULT) {
            flagCoarseLocation = true;
            Toast.makeText(context, "Coarse location permission granted.", Toast.LENGTH_SHORT).show();

        }
        if (requestCode == MY_DEFINED_ACCESS_FINE_LOCATION_CALLBACK_RESULT) {
            flagFineLocation = true;
            Toast.makeText(context, "Fine location permission granted.", Toast.LENGTH_SHORT).show();
        }

    }


    }


**Fragment Adapter class:**

    package com.xyz.views;

//didnt include imports

    public class MainActivityViewPagerAdapter extends FragmentStatePagerAdapter {
        public MainActivityViewPagerAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int position) {
            Fragment returnFragment=null;

            switch(position) {
                case 0:
                    returnFragment = ServicesFragment.newInstance();
                    break;
                case 1:
                    returnFragment = BiometricsFragment.newInstance();
                    break;
                case 2:
                     returnFragment =IncidentMapFragment.newInstance();
                     break;
            }

            return returnFragment;
        }

        @Override
        public int getCount() {
            return 3;
        }

        public CharSequence getPageTitle(int position) {
            CharSequence title=null;
            switch (position) {
                case 0:
                    title = "Services";
                    break;
                case 1:
                    title = "Biometrics";
                    break;
                case 2:
                    title = "Incident Map";
                    break;

            }

            return title;


        }
    }
Angus
  • 3,680
  • 1
  • 12
  • 27