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).
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();
}
}