1

In my app I've a MapFragment with the classic "MyLocationButton". On older APIs I don't have any kind of issue, but since Google asked developers to use Runtime Permissions, I'm facing this problem: when the user clicks ALLOW, the already said button is not appearing, at least not until the user refreshes this fragment. Is there a way to catch the user's press to make the fragment refresh as soon as he's given permissions?

EDIT: I'm using nested fragments, and I've found out my problem is the same you can see at this page of the AOSP Issue Tracker. I've solved it using the last version of Android APIs (v24).

Android Permissions

I'll post my MapFragment:

public class MapFragment extends Fragment implements OnMapReadyCallback, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener, PetrolStationsArray.PetrolStationsListener {

    private GoogleMap mGoogleMap;
    private int mMapType;
    private static LatLng mPosition;
    private static float mZoom;
    private CoordinatorLayout mCoordinatorLayout;
    private View mLocationButton;
    private AppCompatImageButton mTurnGPSOn;

    private FragmentManager mFragmentManager;
    private FragmentTransaction mFragmentTransaction;

    private SupportMapFragment mSupportMapFragment;
    private SupportPlaceAutocompleteFragment mSupportPlaceAutocompleteFragment;

    private LocalBroadcastManager mLocalBroadcastManager;

    private PetrolStationsArray mPetrolStationsArray;

    protected LocationRequest mLocationRequest;
    protected GoogleApiClient mGoogleApiClient;
    Marker mCurrLocationMarker;

    private CountDownTimer mDragTimer;
    private boolean mTimerIsRunning = false;


    // Design pattern to instantiate a new fragment.
    public static MapFragment newInstance() {
        MapFragment fragment = new MapFragment();

        return fragment;
    }

    /********************************************************/

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Since API 23, Android requests you check for Location Permissions.
        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            checkLocationPermission();
        }

        mMapType = Integer.parseInt(loadPreferences(SETTINGS_PREFERENCES, MAP_KEY));
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_map, container, false);

        mLocalBroadcastManager = LocalBroadcastManager.getInstance(getContext());

        mPetrolStationsArray = PetrolStationsArray.get();

        mDragTimer = new CountDownTimer(DRAG_TIMER_INTERVAL, DRAG_TIMER_INTERVAL + 1) {
            @Override
            public void onTick(long l) {

            }

            @Override
            public void onFinish() {
                mTimerIsRunning = false;
                mPetrolStationsArray.setPosition(mPosition);
            }
        };

        return view;
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        // Initialise a new fragment inside of this one.
        mFragmentManager = getChildFragmentManager();
        mSupportMapFragment = (SupportMapFragment) mFragmentManager.findFragmentByTag(MAP_FRAGMENT_TAG);
        mSupportPlaceAutocompleteFragment = (SupportPlaceAutocompleteFragment) mFragmentManager.findFragmentByTag(AUTOCOMPLETE_FRAGMENT_TAG);

        mCoordinatorLayout = (CoordinatorLayout) view.findViewById(R.id.coordinator_layout);

        /*
        ** Never inflate fragments inside other fragments in a layout.xml!
        ** Do it programmatically.
        ** See here for reference: http://stackoverflow.com/a/19815266/4938112.
        */
        if (mSupportMapFragment == null) {
            mSupportMapFragment = new SupportMapFragment();
            fragmentTransaction(mFragmentManager, mSupportMapFragment, R.id.map_fragment_container, MAP_FRAGMENT_TAG);
        }

        // Asynchronous thread to load map.
        mSupportMapFragment.getMapAsync(this);

        if (mSupportPlaceAutocompleteFragment == null) {
            mSupportPlaceAutocompleteFragment = new SupportPlaceAutocompleteFragment();
            fragmentTransaction(mFragmentManager, mSupportPlaceAutocompleteFragment, R.id.card_view, AUTOCOMPLETE_FRAGMENT_TAG);
        }

        // Filter for a specific place type.
        AutocompleteFilter typeFilter = new AutocompleteFilter.Builder()
                .setTypeFilter(AutocompleteFilter.TYPE_FILTER_CITIES | AutocompleteFilter.TYPE_FILTER_ADDRESS)
                .build();

        mSupportPlaceAutocompleteFragment.setFilter(typeFilter);

        mSupportPlaceAutocompleteFragment.setOnPlaceSelectedListener(new PlaceSelectionListener() {
            @Override
            public void onPlaceSelected(Place place) {
                LatLng position = place.getLatLng();

                CameraUpdate centre = CameraUpdateFactory.newLatLng(position);
                CameraUpdate zoom = CameraUpdateFactory.zoomTo(ZOOM_LEVEL);

                mGoogleMap.moveCamera(centre);
                mGoogleMap.animateCamera(zoom);
            }

            @Override
            public void onError(Status status) {
                Log.d("PLACE_ERROR", "An error occurred: " + status);
            }
        });

        mTurnGPSOn = ((AppCompatImageButton) view.findViewById(R.id.turn_gps_on));

        mTurnGPSOn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // Ask if the user wants to open the GPS - settings.
                displayPromptToEnableGPS(getActivity());
            }
        });
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
    }

    @Override
    public void onMapReady(GoogleMap googleMap) {
        this.mGoogleMap = googleMap;

        mGoogleMap.setMapType(mMapType);

        if (mPosition != null) {
            CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngZoom(mPosition, mZoom);
            mGoogleMap.moveCamera(cameraUpdate);
        }

        mGoogleMap.setTrafficEnabled(true);
        mGoogleMap.getUiSettings().setMapToolbarEnabled(false);

        mGoogleMap.setOnCameraChangeListener(new GoogleMap.OnCameraChangeListener() {
            @Override
            public void onCameraChange(CameraPosition cameraPosition) {
                mPosition = cameraPosition.target;
                mZoom = cameraPosition.zoom;

                if (mTimerIsRunning) {
                    mDragTimer.cancel();
                }

                mDragTimer.start();
                mTimerIsRunning = true;
            }
        });

        // Get the "My Position" button.
        mLocationButton = ((View) mSupportMapFragment.getView().findViewById(Integer.parseInt("1")).getParent()).findViewById(Integer.parseInt("2"));
        RelativeLayout.LayoutParams rlp = (RelativeLayout.LayoutParams) mLocationButton.getLayoutParams();

        Resources r = getActivity().getResources();
        int px = (int) TypedValue.applyDimension(
                TypedValue.COMPLEX_UNIT_DIP,
                10,
                r.getDisplayMetrics()
        );

        // Position on right bottom.
        rlp.addRule(RelativeLayout.ALIGN_PARENT_TOP, 0);
        rlp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, RelativeLayout.TRUE);
        rlp.setMargins(0, 0, px, px);

        turnOnMyLocation();
    }

    @Override
    public void onPause() {
        super.onPause();

        // Stop location updates when Activity is no longer active.
        if (mGoogleApiClient != null && mGoogleApiClient.isConnected()) {
            LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
        }

        mPetrolStationsArray.removePetrolStationListener(this);
    }

    @Override
    public void onResume() {
        super.onResume();

        mPetrolStationsArray.setPetrolStationsListener(this);
    }

    @Override
    public void onListUpdated() {
        // Cleaning all the markers.
        if (mGoogleMap != null) {
            mGoogleMap.clear();
        }

        List<PetrolStation> petrolStationList = mPetrolStationsArray.getList();

        for (PetrolStation petrolStation : petrolStationList) {
            double lat = petrolStation.getLat();
            double lon = petrolStation.getLon();
            String name = petrolStation.getName();

            if (mGoogleMap != null) {
                mGoogleMap.addMarker(new MarkerOptions()
                        .position(new LatLng(lat, lon))
                        .title(name));
            }
        }
    }

    @Override
    public void onServerRequest() {

    }

    @Override
    public void onConnected(Bundle bundle) {
        mLocationRequest = new LocationRequest();
        mLocationRequest.setInterval(1000);
        mLocationRequest.setFastestInterval(1000);
        mLocationRequest.setPriority(LocationRequest.PRIORITY_LOW_POWER);

        if (ContextCompat.checkSelfPermission(getContext(), Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
            LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
        }
    }

    @Override
    public void onConnectionSuspended(int i) {
        // TODO
    }

    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {
        // TODO
    }

    @Override
    public void onLocationChanged(Location location) {
        if (mCurrLocationMarker != null) {
            mCurrLocationMarker.remove();
        }

        // Just a check to overtake the
        // 'java.lang.IllegalStateException: Fragment MapFragment{42f519d0} not attached to Activity'
        // exception I've encountered.
        if (!isAdded()) {
            return;
        }

        // Stop location updates.
        if (mGoogleApiClient != null) {
            LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
        }
    }

    // Start the transaction between different fragments.
    private void fragmentTransaction(FragmentManager mFragmentManager, Fragment fragment, int id, String tag) {
        mFragmentTransaction = mFragmentManager.beginTransaction();
        mFragmentTransaction.add(id, fragment, tag);
        mFragmentTransaction.commit();
        mFragmentManager.executePendingTransactions();
    }


    /*********************************************
    ************ LOCATION PERMISSIONS ************
    *********************************************/

    @Override
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);

        switch (requestCode) {
            case MY_PERMISSIONS_REQUEST_LOCATION: {
                // If request is cancelled, the result arrays are empty.
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    turnOnMyLocation();
                } else {
                    // Permission denied, boo! Disable the functionality that depends on this permission.
                    String message = getResources().getString(R.string.permission_denied);

                    Snackbar snackbar = Snackbar
                            .make(mCoordinatorLayout, message, Snackbar.LENGTH_LONG)
                            .setAction(R.string.close_snack_bar, new View.OnClickListener() {
                                @Override
                                public void onClick(View view) {

                                }
                            });

                    snackbar.show();
                }
            }
        }
    }

    // Ask for permission (START DIALOG) in Android APIs >= 23.
    public boolean checkLocationPermission() {
        if (ContextCompat.checkSelfPermission(getContext(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            // Should we show an explanation?
            if (shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION)) {
                // Prompt the user once explanation has been shown.
                requestPermissions(new String[] {
                        Manifest.permission.ACCESS_FINE_LOCATION
                }, MY_PERMISSIONS_REQUEST_LOCATION);
            } else {
                // No explanation needed, we can request the permission.
                requestPermissions(new String[] {
                        Manifest.permission.ACCESS_FINE_LOCATION
                }, MY_PERMISSIONS_REQUEST_LOCATION);
            }

            return false;
        } else {
            return true;
        }
    }

    public void turnOnMyLocation() {
        // Initialise Google Play Services.
        if (mGoogleApiClient == null) {
            buildGoogleApiClient();
        }

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (ContextCompat.checkSelfPermission(getContext(), Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
                mGoogleMap.setMyLocationEnabled(true);
            }
        } else {
            mGoogleMap.setMyLocationEnabled(true);
        }
    }

    // Let's build our Location Services API Client.
    protected synchronized void buildGoogleApiClient() {
        mGoogleApiClient = new GoogleApiClient
                .Builder(getContext())
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();

        mGoogleApiClient.connect();
    }

    public static void displayPromptToEnableGPS(final Activity activity) {
        final AlertDialog.Builder builder =  new AlertDialog.Builder(activity);
        final String action = Settings.ACTION_LOCATION_SOURCE_SETTINGS;
        final String message = MyFuelApp.getAppContext().getResources().getString(R.string.open_gps_settings);

        builder.setMessage(message)
                .setPositiveButton(MyFuelApp.getAppContext().getResources().getString(R.string.confirm_ok),
                        new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface d, int id) {
                                activity.startActivity(new Intent(action));
                                d.dismiss();
                            }
                        })
                .setNegativeButton(MyFuelApp.getAppContext().getResources().getString(R.string.deny_no),
                        new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface d, int id) {
                                d.cancel();
                            }
                        });
        builder.create().show();
    }

}
Davide3i
  • 1,035
  • 3
  • 15
  • 35
  • 3
    Normally this is done by using the onRequestPermissionsResult function. – Denny Weinberg Jul 23 '16 at 08:21
  • I've already tried to refresh the fragment inside my _onRequestPermissionsResult_ function, but I can't really understand how to make the fragment refresh. Any hints? Thank you for your reply. – Davide3i Jul 23 '16 at 08:23
  • http://stackoverflow.com/questions/20702333/refresh-fragment-at-reload this might help – Eenvincible Jul 23 '16 at 08:32
  • I've found out my problem is the same of those people: https://code.google.com/p/android/issues/detail?&id=189121 – Davide3i Jul 23 '16 at 12:19

2 Answers2

0

One way to solve is to ask before open your MapFragment. This way you only have to check if you have the permission or not at your fragment to load one view or another. The other way, is to add the button at your method turnOnMyLocation().

Serch
  • 1,031
  • 8
  • 4
  • I've found out my problem is the same of those people: https://code.google.com/p/android/issues/detail?&id=189121 – Davide3i Jul 23 '16 at 12:19
0

You can use

onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)

within this callback you should check the equality of the request code that you passed while requesting permission with the one in the parameter. If it matches, you can get the user result.

If user allow the permission, then you can simply detach and attach your fragment as follow:

  Fragment currentFragment = getFragmentManager().findFragmentByTag("FRAGMENT");
    FragmentTransaction fragTransaction = getFragmentManager().beginTransaction();
    fragTransaction.detach(currentFragment);
    fragTransaction.attach(currentFragment);
    fragTransaction.commit();

This is how your fragment will be reloaded and also your views gets refreshed. I hope by this you can address your problem.

Mayank Bhatnagar
  • 2,120
  • 1
  • 12
  • 20
  • I've found out my problem is the same of those people: https://code.google.com/p/android/issues/detail?&id=189121 – Davide3i Jul 23 '16 at 12:19