I try to learn Android by myself. And in my app, I want to use fragment to show google map and when user open my app, I want to get current location by using GoogleApiClient
My fragment code:
public class HomeFragment extends Fragment implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
public interface comuticateParent {
public void sendMess(String text);
}
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
// TODO: Rename and change types of parameters
private String mParam1;
private String mParam2;
public HomeFragment() {
// Required empty public constructor
}
public static HomeFragment newInstance(String param1, String param2) {
HomeFragment fragment = new HomeFragment();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}
comuticateParent callback;
Button btn1;
TextView textView;
MapView mMapView;
GoogleApiClient mGoogleApiClient;
Location mLastLocation;
LocationRequest mLocationRequest;
private GoogleMap googleMap;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
}
if (mGoogleApiClient == null) {
mGoogleApiClient = new GoogleApiClient.Builder(getActivity())
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_home, container, false);
mMapView = (MapView) rootView.findViewById(R.id.mapView);
mMapView.onCreate(savedInstanceState);
mMapView.onResume(); // needed to get the map to display immediately
try {
MapsInitializer.initialize(getActivity().getApplicationContext());
} catch (Exception e) {
e.printStackTrace();
}
mMapView.getMapAsync(new OnMapReadyCallback() {
#Override
public void onMapReady(GoogleMap mMap) {
googleMap = mMap;
googleMap.setMyLocationEnabled(true);
mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
Log.d("onCreateView", Boolean.toString(mGoogleApiClient.isConnected()));
googleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(mylocation, 13));
}
});
return rootView;
}
#Override
public void onStart() {
mGoogleApiClient.connect();
Log.d("ConnectonStart", "Connected ");
Log.d("ONstart", Boolean.toString(mGoogleApiClient.isConnected()));
super.onStart();
}
#Override
public void onStop() {
mGoogleApiClient.disconnect();
super.onStop();
}
#Override
public void onResume() {
mGoogleApiClient.connect();
super.onResume();
mMapView.onResume();
}
#Override
public void onPause() {
super.onPause();
mMapView.onPause();
}
#Override
public void onDestroy() {
super.onDestroy();
mMapView.onDestroy();
}
#Override
public void onLowMemory() {
super.onLowMemory();
mMapView.onLowMemory();
}
#Override
public void onConnected(Bundle bundle) {
Log.d("Connect", "Connected ");
Log.d("onConnected", Boolean.toString(mGoogleApiClient.isConnected()));
}
#Override
public void onConnectionSuspended(int i) {
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
Log.d("Connect", "failed ");
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
Activity activity;
if (context instanceof Activity) {
activity = (Activity) context;
callback = (comuticateParent) getActivity();
}
}
}
And problem here:
-Log in method onCreateView appears before log in method onconnected, so I can't get getLastLocation() because The googleAPIClient is not connect yet.
I have search in google but I don't know how to fix it.
Please help me!
Sorry about my best English.
First problem Log in method onCreateView appears before log in method onconnected, because GoogleApiClient will accessing Google APIs, verify app configuration, verify certificate fingerprint, etc. It take too long to process. To avoid blocking the UI thread, it'll execute in another thread and use asynchronous callback.
Second problem I can't get getLastLocation() because The googleAPIClient is not connect yet, because FusedLocationApi will only maintain background, so you need to get GoogleMap in background.
See my sample here: https://gist.github.com/quydm/a458d908c4da2496672f83372304f417
You will have to wait until the GoogleApiClient has connected before you can request location updates. In order to do so, you would move this block of code (currently found in your onCreateView() method):
mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
Log.d("onCreateView", Boolean.toString(mGoogleApiClient.isConnected()));
googleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(mylocation, 13));
to the body of the overridden method onConnected().
You cannot have this code directly in onCreateView() because of the very problem you had. The connection must be established first before any location requests are sent. The connection can establish quickly or take longer than normal. It depends on many things outside of your control.
For that reason, I would suggest showing some sort of progress bar to indicate that location services is connecting and then remove the progress bar when onConnected() is called. Of course this depends entirely on your app. If location services is critical for the user to have then this is a must, if not, then you may not need to add it.
Related
I am a newbie to android development. I want to develop an app which have three tabs.
First tab will be My Location in which there is a map and a pin which is marking my location
On second tab there will be a camera option, through which i can take pictures and those pictures will be saved in the memory located by OS to the app.
The saved pictures will be shown in a slider
As for 2 and 3, i'll have some idea but for 1 i couldn't find any help. I did find some tutorials but all of them are for eclipse but i am using android studio.
During a study i found that Fragment is a way to do it so i started to follow this tutorial, but as i said earlier i am newbie and all tutorials are for eclipse.
Update 1
I have created 3 classes and 3 layout files each for each tab, bellow is the class for MyLocation tab
public class MyLocation extends Fragment{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.my_location, container, false);
return rootView;
} }
I am stuck to it now and don't know what to do
Any help would be highly appreciated
If you want to create google map inside fragment than go threw this code
public class GoogleMapFragment extends Fragment implements OnMapReadyCallback,
GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener{
private GoogleMap googleMap;
GoogleApiClient mGoogleApiClient;
Location mLastLocation;
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_demo, container, false);
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (mGoogleApiClient == null) {
mGoogleApiClient = new GoogleApiClient.Builder(getActivity())
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
}
mGoogleApiClient.connect();
}
#Override
public void onResume() {
super.onResume();
intiGoogleMap();
}
#Override
public void onStart() {
mGoogleApiClient.connect();
super.onStart();
}
#Override
public void onStop() {
mGoogleApiClient.disconnect();
super.onStop();
}
#Override
public void onDestroyView() {
super.onDestroyView();
SupportMapFragment supportMapFragment = ((SupportMapFragment) getFragmentManager().findFragmentById(R.id.mapMapAddress));
if (supportMapFragment != null)
getFragmentManager().beginTransaction().remove(supportMapFragment).commit();
}
private void intiGoogleMap() {
SupportMapFragment supportMapFragment = ((SupportMapFragment) getFragmentManager().findFragmentById(R.id.mapMapAddress));
supportMapFragment.getMapAsync(this);
}
#Override
public void onConnected(#Nullable Bundle bundle) {
//First check permissions if accept or reject
if (AppUtils.checkPermission(Manifest.permission.ACCESS_FINE_LOCATION, getActivity()) &&
AppUtils.checkPermission(Manifest.permission.ACCESS_COARSE_LOCATION, getActivity())) {
mLastLocation = LocationServices.FusedLocationApi.getLastLocation(
mGoogleApiClient);
if (googleMap != null)
if (mLastLocation != null) {
//here you can place your marker
}
} else {
Toast.makeText(getActivity(), "Check your permission", Toast.LENGTH_LONG).show();
}
}
#Override
public void onConnectionSuspended(int i) {
}
#Override
public void onConnectionFailed(#NonNull ConnectionResult connectionResult) {
}
#Override
public void onMapReady(GoogleMap googleMap) {
if (googleMap != null) {
this.googleMap = googleMap;
this.googleMap.getUiSettings().setMyLocationButtonEnabled(true);
this.googleMap.getUiSettings().setCompassEnabled(true);
}
}
}
and fragment_demo.xml is
<?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">
<fragment
android:id="#+id/mapMapAddress"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
and try to run this will be showing of google map, and make sure all permissions and you have created app in google developer console. Also make sure adding Android Key in manifest file.
I am having some random crashes in super.onResume with Google maps. It only happens in debug mode, not in production. It is weird how the map is added to the fragment (not coded by myself), but it was working properly before. Just some day started to crash. Below you can see the error and the code. I would really appreciate some help!
Fatal Exception: java.lang.NullPointerException
com.google.maps.api.android.lib6.e.y.b (Unknown Source)
com.google.android.gms.maps.MapFragment.onResume (Unknown Source)
com.xxxx.xxxx.Fragments.FragmentMap.onResume (FragmentMap.java:983)
com.xxxx.xxxx.Fragments.FragmentMap.onConnected(FragmentMap.java:2667)
com.google.android.gms.common.internal.zzk.zzk (Unknown Source)
dalvik.system.NativeStart.main (NativeStart.java)
And this is the Fragment where is painting the Map:
public class FragmentMap extends MapFragment implements LocationListener,
GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, ResultCallback<LocationSettingsResult> {
GoogleMap map;
GoogleApiClient
{...}
#Override
public void onCreate(Bundle savedInstanceState) {
//Instantiate here all not view objects (Fragments good practices)
super.onCreate(savedInstanceState);
setRetainInstance(true);
if (checkPlayServices()) {
// Building the GoogleApi client
buildGoogleApiClient();
createLocationRequest();
buildLocationSettingsRequest();
checkLocationSettings();
}
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
map = this.getMap();
map.getUiSettings().setZoomControlsEnabled(false);
map.getUiSettings().setMyLocationButtonEnabled(false);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View mapView = super.onCreateView(inflater, container, savedInstanceState);
RelativeLayout rl = new RelativeLayout(getActivity());
if (condition1) {
View firstOverlay = inflater.inflate(R.layout.first_map_layout, null);
rl.addView(mapView);
rl.addView(firstOverlay);
setHasOptionsMenu(true);
} else {
View srcvAddress = inflater.inflate(R.layout.address_finder, null);
rl.addView(mapView);
rl.addView(srcvAddress);
}
return rl;
}
#Override
public void onResume() {
super.onResume(); <----------------------- **HERE CRASHES!!**
stopUpdatePositionService();
if (mGoogleApiClient.isConnected()) {
{...}
}
}
/**
* Creating google api client object
*/
private synchronized void buildGoogleApiClient() {
mGoogleApiClient = new GoogleApiClient.Builder(getActivity())
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API).build();
}
/**
* Stopping location updates
*/
private void stopLocationUpdates() {
if (mGoogleApiClient.isConnected())
LocationServices.FusedLocationApi.removeLocationUpdates(
mGoogleApiClient, this);
}
#Override
public void onStart() {
super.onStart();
if (mGoogleApiClient != null) {
mGoogleApiClient.connect();
}
}
/**
* Google api callback methods
*/
#Override
public void onConnectionFailed(#NonNull ConnectionResult result) {
Log.i(TAG, "Connection failed: ConnectionResult.getErrorCode() = "
+ result.getErrorCode());
}
#Override
public void onConnected(Bundle arg0) {
// Once connected with google api, get the location
onResume();
//displayLocation();
}
#Override
public void onConnectionSuspended(int arg0) {
mGoogleApiClient.connect();
}
/**
* Creating location request object
*/
private void createLocationRequest() {
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(UPDATE_INTERVAL);
mLocationRequest.setFastestInterval(FATEST_INTERVAL);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
mLocationRequest.setSmallestDisplacement(DISPLACEMENT);
}
Thanks in advance!
map=this.getmap();---> might be the problem
instead try this
map= ((MapFragment) getFragmentManager().findFragmentById(R.id.map)).getMap();//where R.id.map is google map fragment
Note for future: Forget about getMap. It is already deprecated. When I moved everything to the new getMapAsync it is working everything fine
I am using retrofit to fetch data online.However I cannot find any solution to my problem. I want to call another fragment and display more of the details about the marker I clicked and pass those values to another fragment. Can someone please help me on this. Any help and solutions are well appreciated.
MapFragment.jav
RestAdapter adapter = new RestAdapter.Builder()
.setEndpoint(getString(R.string.fine_dinings))
.build();
RestaurantPlacesApiInterface restaurantPlacesApiInterface =
adapter.create(RestaurantPlacesApiInterface.class);
restaurantPlacesApiInterface.getStreams(new Callback<List<RestaurantPlaces>>() {
#Override
public void success(List<RestaurantPlaces> restaurantPlaces, Response response) {
for (RestaurantPlaces restaurantPlace : restaurantPlaces){
MarkerOptions options = new MarkerOptions().position(new LatLng(restaurantPlace.getLatitude(),
restaurantPlace.getLongitude()));
options.title(restaurantPlace.getName());
options.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_GREEN));
getMap().addMarker(options);
}
}
#Override
public void failure(RetrofitError error) {
}
});
}
You need to use this:
#Override
public boolean onMarkerClick(Marker marker) {
// call fragment and pass data.
return false;
}
If you return false the click is not consumed.
If you need help implementing this let me know, it's fairly simple.
Here is a quick sample, please change the names to match your own code:
public class MapActivity implements OnMapReadyCallback, GoogleMap.OnMarkerClickListener {
private GoogleMap mGoogleMap;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_map);
initMap();
}
public void initMap() {
MapFragment map = (MapFragment) getFragmentManager().findFragmentById(R.id.mapFragment);
map.getMapAsync(this);
}
#Override
public void onMapReady(GoogleMap googleMap) {
try {
if (googleMap != null) {
mGoogleMap = googleMap;
mGoogleMap.setOnMarkerClickListener(this);
mGoogleMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
// Now make your retrofit call
}
} catch (Exception e) {
e.printStackTrace();
Log.e("ERROR", "GOOGLE MAPS NOT LOADED");
}
}
#Override
public boolean onMarkerClick(Marker marker) {
Bundle bundle = new Bundle();
bundle.putString("myString", "value");
// set Fragment class Arguments
MyFragment myFragment= new MyFragment();
myFragment.setArguments(bundle);
// launch fragment
return false;
}
}
better to use interface in fragment and implement that interface in (activity where RestAdapter used)
I am trying to get the last known location using google services API, but after I build the GoogleApiClient, no callback method is ever fired.
My activity looks like that :
public class MainActivity extends Activity implements FragmentObserver, SessionSpotListObserver,
ConnectionCallbacks, OnConnectionFailedListener{
//Objects used for the location API
private Location mLastLocation;
private GoogleApiClient mGoogleApiClient;
// Request code to use when launching the resolution activity
private static final int REQUEST_RESOLVE_ERROR = 1001;
// Unique tag for the error dialog fragment
private static final String DIALOG_ERROR = "dialog_error";
// Bool to track whether the app is already resolving an error
private boolean mResolvingError = false;
public static final String STATE_RESOLVING_ERROR = "resolving_state";
//Request code to use when launching the activity to fix the connection to google API
private static final int REQUEST_SOLVE_CONNEXION = 999;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//We make sure that google play service is available on the device
if (GooglePlayServicesUtil.isGooglePlayServicesAvailable(this) == ConnectionResult.SUCCESS){
//We get a connection to the Google Play Service API to get the location of the user
buildGoogleApiClient();
}
else {
GooglePlayServicesUtil.getErrorDialog(GooglePlayServicesUtil.isGooglePlayServicesAvailable(this),
this,
REQUEST_SOLVE_CONNEXION);
}
}
#Override
public void onConnectionFailed(ConnectionResult result) {
if (mResolvingError) {
// Already attempting to resolve an error.
return;
} else if (result.hasResolution()) {
try {
mResolvingError = true;
result.startResolutionForResult(this, REQUEST_RESOLVE_ERROR);
} catch (SendIntentException e) {
// There was an error with the resolution intent. Try again.
mGoogleApiClient.connect();
}
} else {
// Show dialog using GooglePlayServicesUtil.getErrorDialog()
showErrorDialog(result.getErrorCode());
mResolvingError = true;
}
}
// The rest of this code is all about building the error dialog
/* Creates a dialog for an error message */
private void showErrorDialog(int errorCode) {
// Create a fragment for the error dialog
ErrorDialogFragment dialogFragment = new ErrorDialogFragment();
// Pass the error that should be displayed
Bundle args = new Bundle();
args.putInt(DIALOG_ERROR, errorCode);
dialogFragment.setArguments(args);
dialogFragment.show(getFragmentManager(), "errordialog");
}
/* Called from ErrorDialogFragment when the dialog is dismissed. */
public void onDialogDismissed() {
mResolvingError = false;
}
/* A fragment to display an error dialog */
public static class ErrorDialogFragment extends DialogFragment {
public ErrorDialogFragment() { }
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// Get the error code and retrieve the appropriate dialog
int errorCode = this.getArguments().getInt(DIALOG_ERROR);
return GooglePlayServicesUtil.getErrorDialog(errorCode,
this.getActivity(), REQUEST_RESOLVE_ERROR);
}
#Override
public void onDismiss(DialogInterface dialog) {
((MainActivity)getActivity()).onDialogDismissed();
}
}
#Override
public void onConnected(Bundle arg0) {
mLastLocation = LocationServices.FusedLocationApi.getLastLocation(
mGoogleApiClient);
Log.d("API Connection", "The API has connected and the last location is :" + mLastLocation);
if (mLastLocation != null) {
}
}
#Override
public void onConnectionSuspended(int arg0) {
// TODO Auto-generated method stub
}
/**
* Creates the connexion to the Google API. Once the API is connected, the
* onConnected method is called.
*/
protected synchronized void buildGoogleApiClient() {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(LocationServices.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
}
I placed breakpoints on all callback methods, that is how I know that none is called.
Because at this stage I am not using Google Map Api, I did not register my app to get a key. Do I need to do that even if I just get the location ?
Don't hesitate to tell me if you need more info.
Thank you all.
You never call mGoogleApiClient.connect() after building your GoogleApiClient. Your onCreate() should instead be:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
buildGoogleApiClient();
mGoogleApiClient.connect();
}
Note that there is no need to call GooglePlayServicesUtil.isGooglePlayServicesAvailable() if you are using GoogleApiClient as connect() includes that check as well.
consider calling onLocationChanged() and passing it's Location parameter to mLastLocation for continuous location update when the user location changes. Also you might want to reduce the drain on your battery by setting LocationRequest() interval and distance to a small value.
I have a layout with ActionBar.NAVIGATION_MODE_TABS. The first tab is to display the results in listview and the second in a map. Everything worked great until I have upgraded to the latest version of Google Play Services lib.
Here the fragment used to display the map:
public class PlaceMapFragment extends Fragment {
MapView mapView;
GoogleMap map;
ResultsListener mCallback;
List<Place> places;
private HashMap<String, Place> placeMarker = new HashMap<String, Place>();
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
places = mCallback.getPlaces();
View rootView = inflater.inflate(R.layout.mapview, container, false);
mapView = (MapView) rootView.findViewById(R.id.mapview);
mapView.onCreate(savedInstanceState);
map = mapView.getMap();
setUpMap();
return rootView;
}
private void setUpMap() {
map.getUiSettings().setMyLocationButtonEnabled(true);
map.setMyLocationEnabled(true);
/*
try {
MapsInitializer.initialize(this.getActivity());
} catch (GooglePlayServicesNotAvailableException e) {
e.printStackTrace();
}
*/
// add markers
}
#Override
public void onResume() {
mapView.onResume();
super.onResume();
setUpMap();
}
#Override
public void onPause() {
mapView.onPause();
super.onPause();
}
#Override
public void onDestroy() {
mapView.onDestroy();
super.onDestroy();
}
#Override
public void onLowMemory() {
super.onLowMemory();
mapView.onLowMemory();
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mapView.onSaveInstanceState(outState);
}
public interface ResultsListener {
public List<Place> getPlaces();
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception
try {
mCallback = (ResultsListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement ResultsListener");
}
}
}
I get these errors:
04-05 03:58:22.040: E/AndroidRuntime(661): FATAL EXCEPTION: main
04-05 03:58:22.040: E/AndroidRuntime(661): java.lang.NullPointerException
04-05 03:58:22.040: E/AndroidRuntime(661): at com.tomsyweb.mapslee.fragments.PlaceMapFragment.setUpMap(PlaceMapFragment.java:54)
04-05 03:58:22.040: E/AndroidRuntime(661): at com.tomsyweb.mapslee.fragments.PlaceMapFragment.onCreateView(PlaceMapFragment.java:48)
I have commented the MapsInitializer.initialize(this.getActivity()); part because GooglePlayServicesNotAvailableException gives error.
mapview.xml
<com.google.android.gms.maps.MapView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/mapview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
My code is taken from Getting Google Map Fragment in onCreateView using ActionBar tabs
Your map may be null if the device doesn't have the latest version of Maps installed, or perhaps for other reasons. Trying to create it may even prompt the user to start a different activity to update their device. So it's recommended to create an initMap() method like this, and call it in your onCreate(...) and onResume():
private void initMap() {
if(map == null) {
map = ((MapFragment) getFragmentManager().findFragmentById(R.id.map)).getMap();
if(map != null) {
// set up map
}
}
}