Call to Places.GeoDataApi.getPlaceById leaks instance of my Activity - android

When I'm using Places.GeoDataApi.getPlaceById() LeakCanary detects that an instance of my Activity has leaked.
Here is what LeakCanary reports
* com.mypackage.PlaceSearchActivity has leaked:
* GC ROOT com.google.android.gms.location.places.zzl.mContext
* leaks com.mypackage.PlaceSearchActivity instance
And this is my code. Both methods are declared within my PlaceSearchActivity.
void lookupLatLng(final Location location, final int followUpAction) {
Task.callInBackground(new Callable<LatLng>() {
#Override
public LatLng call() throws Exception {
PlaceBuffer places = Places.GeoDataApi.getPlaceById(
getGoogleApiClient(), location.getPlaceId()).await();
LatLng result = places.get(0).getLatLng();
places.release();
return result;
}
}).continueWithTask(new Continuation<LatLng, Task<Void>>() {
#Override
public Task<Void> then(Task<LatLng> task) throws Exception {
if (task.isFaulted()) {
// TODO Place lookup failed
} else if (task.isCompleted()) {
switch (followUpAction) {
case ACTION_PICK:
location.setLatLngPoint(LatLngPoint.from(task.getResult()));
onLocationSelected(location);
break;
}
}
return task.makeVoid();
}
}, Task.UI_THREAD_EXECUTOR);
}
void onLocationSelected(Location location) {
Intent resultIntent = new Intent();
resultIntent.putExtra(EXTRA_RESULT, Parcels.wrap(location));
setResult(RESULT_OK, resultIntent);
ActivityCompat.finishAfterTransition(this);
}
I'm not sure what is causing the problem. Any help is appreciated. Thanks!
UPDATE: Memory leak is also detected with folowing code.
void lookupLatLng(Location location) {
Places.GeoDataApi.getPlaceById(getGoogleApiClient(), location.getPlaceId()).setResultCallback(
new ResultCallback<PlaceBuffer>() {
#Override
public void onResult(PlaceBuffer places) {
Place place = places.get(0);
LatLng latLng = place.getLatLng();
places.release();
}
}
);
}
It might be an issue within PlayServices library. I'm going to test this on a empty project just to verify the issue.

Related

Location is empty at the start

I am reframing my last question, which is unanswered, and I have rewritten the problem following Google's BasicLocation.
My main activity is defined as:
public class MainActivity extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener {
// private LocationCallback locationCallback;
// private FusedLocationProviderClient mFusedLocationClient;
private FusedLocationProviderClient mFusedLocationClient;
protected Location mLastLocation;
private static final int REQUEST_PERMISSIONS_REQUEST_CODE = 34;
private static final String TAG = MainActivity.class.getSimpleName();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.drawer_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
SectionsPagerAdapter sectionsPagerAdapter = new SectionsPagerAdapter(this, getSupportFragmentManager());
ViewPager viewPager = findViewById(R.id.view_pager);
viewPager.setAdapter(sectionsPagerAdapter);
DrawerLayout drawer = findViewById(R.id.drawer_layout);
NavigationView navigationView = findViewById(R.id.nav_view);
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
drawer.addDrawerListener(toggle);
toggle.syncState();
navigationView.setNavigationItemSelectedListener(this);
ImageButton leftNav = findViewById(R.id.left_nav);
ImageButton rightNav = findViewById(R.id.right_nav);
leftNav.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int tab = viewPager.getCurrentItem();
if (tab > 0) {
tab--;
viewPager.setCurrentItem(tab);
} else if (tab == 0) {
viewPager.setCurrentItem(tab);
}
}
});
rightNav.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int tab = viewPager.getCurrentItem();
tab++;
viewPager.setCurrentItem(tab);
}
});
}
#Override
public void onStart() {
super.onStart();
if (!checkPermissions()) {
requestPermissions();
} else {
getLastLocation();
}
}
with latlang.[Lat,Lang] is in a seperate file:
public class latlang {
public static double Lat;
public static double Lang;
}
and the location file, which is the first fragment in the viewpager is defined as:
public class SunFragment extends Fragment {
List<SunSession> sunsList;
Typeface sunfont;
//to be called by the MainActivity
public SunFragment() {
// Required empty public constructor
}
// Keys for storing activity state.
// private static final String KEY_CAMERA_POSITION = "camera_position";
private static final String KEY_LOCATION_NAME = "location_name";
public String location;//="No location name found";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Retrieve location and camera position from saved instance state.
if (savedInstanceState != null) {
location = savedInstanceState.getCharSequence(KEY_LOCATION_NAME).toString();
System.out.println("OnCreate location "+location);
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View rootView = inflater.inflate(R.layout.fragment_sun, container, false);
onSaveInstanceState(new Bundle());
//SecondFragment secondFragment = new SecondFragment();
//secondFragment.getDeviceLocation();
RecyclerView rv = rootView.findViewById(R.id.rv_recycler_view);
rv.setNestedScrollingEnabled(false);
rv.setHasFixedSize(true);
//MyAdapter adapter = new MyAdapter(new String[]{"Today", "Golden Hour", "Blue Hour", "Civil Twilight", "Nautical Twilight", "Astronomical Twilight", "Hello", "World"});
//rv.setAdapter(adapter);
LinearLayoutManager llm = new LinearLayoutManager(getActivity());
rv.setLayoutManager(llm);
System.out.println("location "+location);
/*
Reversegeocoding location
*/
String location="No location name found";
String errorMessage = "";
List<Address> addresses = null;
Geocoder geocoder = new Geocoder(getContext(), Locale.getDefault());
try {
addresses = geocoder.getFromLocation(
latlang.Lat,
latlang.Lang,
1);
} catch (IOException ioException) {
// Catch network or other I/O problems.
errorMessage = getString(R.string.service_not_available);
// Log.e(TAG, errorMessage, ioException);
if (getView() != null){
Snackbar.make(getView(), errorMessage, Snackbar.LENGTH_LONG).show();
}
} catch (IllegalArgumentException illegalArgumentException) {
// Catch invalid latitude or longitude values.
errorMessage = getString(R.string.invalid_lat_long_used);
if (getView() != null){
Snackbar.make(getView(),
"Illegal Latitude = " + latlang.Lat + ", Longitude = " +
latlang.Lang, Snackbar.LENGTH_LONG).show();
}
}
if (addresses == null || addresses.size() == 0) {
if (errorMessage.isEmpty()) {
System.out.println("Adress Empty No Address Found");// Snackbar.LENGTH_LONG).show();
location = "Lat:"+latlang.Lat+" Lang: "+latlang.Lang;
}
} else {
location = addresses.get(0).getAddressLine(0);//+", "+ addresses.get(0).getLocality();
/* for(int i = 0; i <= addresses.get(0).getMaxAddressLineIndex(); i++) {
location = addresses.get(0).getAddressLine(i);
}*/
}
The problem with this is evident from the logcat:
I/System.out: location null
I/Google Maps Android API: Google Play services package version: 17785022
I/Choreographer: Skipped 31 frames! The application may be doing too much work on its main thread.
I/System.out: Position:0
I/System.out: Position:1
I/System.out: Position:2
I/zygote: Do full code cache collection, code=202KB, data=177KB
I/zygote: After code cache collection, code=129KB, data=91KB
I/zygote: JIT allocated 56KB for compiled code of void android.view.View.<init>(android.content.Context, android.util.AttributeSet, int, int)
I/zygote: Background concurrent copying GC freed 44415(2MB) AllocSpace objects, 7(136KB) LOS objects, 49% free, 3MB/6MB, paused 294us total 102.458ms
I/System.out: Position:3
I/System.out: Position:4
I/zygote: Do partial code cache collection, code=193KB, data=126KB
I/zygote: After code cache collection, code=193KB, data=126KB
I/zygote: Increasing code cache capacity to 1024KB
I/zygote: JIT allocated 71KB for compiled code of void android.widget.TextView.<init>(android.content.Context, android.util.AttributeSet, int, int)
I/zygote: Compiler allocated 4MB to compile void android.widget.TextView.<init>(android.content.Context, android.util.AttributeSet, int, int)
E/MainActivity: Latit: 37.42342342342342
This shows, at the start, location is null,
I/System.out: location null
then the recyclerview of the sunfragment is created
I/System.out: Position:0
I/System.out: Position:1
I/System.out: Position:2
and after that I am getting the location:
E/MainActivity: Latit: 37.42342342342342
Link of the complete code:https://drive.google.com/file/d/1pMl_3Lf76sy82C0J4b-9ta4jbSHonJ2y/view?usp=sharing
Is it somehow possible to get the location first before creating the sunfragment's oncreateview?
I found something wrong about your code (I may be wrong):
Why fields of latlang are static? It doesn't looks like they should.
At SunFragment.onCreate() you are reading location if savedInstanceState != null. savedInstanceState is not null only if activity that holds this fragment was restored from saved state. It may not happen at all.
You should use fragment's arguments (Bundle) to pass initial data to fragment
You should implement Parcelable interface for latlang to be able to pass custom class thru Bundle
I think that's not everything but for me it seems like enough for this code to not work as you expected
As stated by the previous answer there are a lot of issues in your code.
Apart from that understand that last known location may not always return a value. Gps basically has two data types: Ephemeris (precise nav data) and Almanac (coarse data). Now when the receiver is cold started ie after gps has been off for more than 8-10 mins, there is basically no last known location (the duration may vary based on the device but the basic idea is this).
So when you do not get last known location, fetch the actual live location using the fused client. Also since you are saving the data in shared preference and fetching it in your fragment, i believe your fragment is going to heavily rely on this data. So i would suggest either of the following two approaches to get correct result
1) do not fetch the location in the nesting activity at all. Just do it in the fragment where it is needed. This will not work if other fragments in your viewpager also need the location.
2) If you must have the location in your activity and it is a dependency in the container fragments, you can use two approaches here as well. My approaches rely on event bus.. Event bus, otto or rxbus anything will do
2a) do not add anything to the viewpager. basically fetch the location first fully and then add stuff to the viewpager once you get the location callback.
2b) Add stuff to the viewpager from the start. In the activity once you get the location, use the event bus to inform the fragments of the same and on getting the event in the fragments, actually start what you need to do.
I have previously used both these approaches and everything works. Now it is entirely up to your use case to use what suits you. Either ways it is too long a code and too complicated to post everything here.
Right now you are using like this:
#Override
public void onStart() {
super.onStart();
if (!checkPermissions()) {
requestPermissions();
} else {
getLastLocation();
}
}
In your else statement check if getLastLocation() is not null. if not null, replace the fragment.
#Override
public void onStart() {
super.onStart();
if (!checkPermissions()) {
requestPermissions();
} else {
if(getLastLocation() != null){
//replace your fragment
}else{//Null Location}
}
}
Use a service class to handle location service precisely. Here, i'm giving you a custom LocationService class which i used in many projects to collect the location data from background continuously
LocationService.kt
import android.Manifest
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Binder
import android.os.Build
import android.os.IBinder
import android.os.Looper
import android.util.Log
import androidx.core.app.ActivityCompat
import androidx.core.app.NotificationCompat
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.location.LocationCallback
import com.google.android.gms.location.LocationRequest
import com.google.android.gms.location.LocationResult
import com.google.android.gms.location.LocationServices
class LocationService : Service() {
//Custom Location Binder class
inner class LocationBinder : Binder() {
val locationService: LocationService
get() = this#LocationService
}
companion object {
val TAG = "LocationService_123"
private val UPDATE_INTERVAL = (4 * 1000).toLong() /* 4 secs */
private val FASTEST_INTERVAL: Long = 2000 /* 2 sec */
private var INSTANCE: LocationService? = null
fun isInstanceCreated(): Boolean {
return (INSTANCE != null)
}
}
private var mFusedLocationClient: FusedLocationProviderClient? = null
private var mLocationListener: LocationListener? = null
private var mLocationBinder = LocationBinder()
fun setLocationListener(locationListener: LocationListener) {
this.mLocationListener = locationListener
}
override fun onBind(intent: Intent): IBinder? {
return mLocationBinder
}
override fun onCreate() {
INSTANCE = this
super.onCreate()
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
if (Build.VERSION.SDK_INT >= 26) {
val CHANNEL_ID = "ostad_gps"
val channel = NotificationChannel(
CHANNEL_ID,
"Ostad GPS",
NotificationManager.IMPORTANCE_DEFAULT)
(getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).createNotificationChannel(channel)
val notification = NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("")
.setContentText("").build()
startForeground(1, notification)
}
}
override fun onDestroy() {
INSTANCE = null
super.onDestroy()
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
Log.d(TAG, "onStartCommand: called.")
getLocation()
return Service.START_NOT_STICKY
}
private fun getLocation() {
// ---------------------------------- LocationRequest ------------------------------------
// Create the location request to start receiving updates
val mLocationRequestHighAccuracy = LocationRequest()
mLocationRequestHighAccuracy.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
mLocationRequestHighAccuracy.interval = UPDATE_INTERVAL
mLocationRequestHighAccuracy.fastestInterval = FASTEST_INTERVAL
// new Google API SDK v11 uses getFusedLocationProviderClient(this)
if (ActivityCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_FINE_LOCATION
) != PackageManager.PERMISSION_GRANTED
) {
Log.d(TAG, "getLocation: stopping the location mitchService.")
stopSelf()
return
}
Log.d(TAG, "getLocation: getting location information.")
mFusedLocationClient!!.requestLocationUpdates(
mLocationRequestHighAccuracy, object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult?) {
val location = locationResult!!.lastLocation
Log.d(TAG, "onLocationResult: got location result: $location")
if (location != null) {
if (mLocationListener != null)
mLocationListener!!.onLocationChanged(location)
}
}
},
Looper.myLooper()
) // Looper.myLooper tells this to repeat forever until thread is destroyed
}
}
add this in your AndroidManifest file and start the LocationService from you MainActivity.
By looking at the code, it seems you do not need a very accurate location, you will be fine with last known location. This value might be null in some cases, like you have already experienced. Simple answer to your question is no, you cannot get not null location before creating SunFragment. Following steps is to load location in background and update UI once found.
Request last known location in MainActivity
Keep a reference of last location in cache for easy loading and better user
experience
If last location is null, request location updates until you get a good fix
Have a listener in SunFragment to track location updates
Here are some code you need (Please do read them)
Use the LocationUtil to handle location related events (I prefer LocationManager over FusedLocationProviderClient);
public class LocationUtil {
public static void updateLastKnownLocation(Context context) {
LocationManager lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
if(hasSelfPermission(context, new String[]{
Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION})) {
try {
Location currentBestLocation;
Location gpsLocation = lm.getLastKnownLocation(LocationManager.GPS_PROVIDER);
Location lbsLocation = lm.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
if(isBetterLocation(lbsLocation, gpsLocation)) {
currentBestLocation = lbsLocation;
} else {
currentBestLocation = gpsLocation;
}
if(currentBestLocation == null) {
requestLocationUpdates(lm);
} else {
updateCacheLocation(currentBestLocation);
}
} catch (SecurityException se) {
// unlikely as permission checks
se.printStackTrace();
} catch (Exception e) {
// unexpected
e.printStackTrace();
}
}
}
private static void updateCacheLocation(Location location) {
if(location == null) return;
LocationLite temp = new LocationLite();
temp.lat = location.getLatitude();
temp.lon = location.getLongitude();
Gson gson = new Gson();
String locationString = gson.toJson(temp);
AppCache.setLastLocation(locationString);
}
#SuppressLint("MissingPermission")
private static void requestLocationUpdates(LocationManager lm) {
try {
lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 0.0F, new LocationListener() {
#Override
public void onLocationChanged(Location location) {
updateCacheLocation(location);
lm.removeUpdates(this);
}
#Override
public void onStatusChanged(String s, int i, Bundle bundle) {
// doing nothing
}
#Override
public void onProviderEnabled(String s) {
// doing nothing
}
#Override
public void onProviderDisabled(String s) {
// doing nothing
}
});
}catch (Exception e) {
e.printStackTrace();
}
}
private static boolean isBetterLocation(Location location, Location currentBestLocation) {
int TWO_MINUTES = 1000 * 60 * 2;
if (currentBestLocation == null) {
// A new location is always better than no location
return true;
}
if (location == null) {
// A new location is always better than no location
return false;
}
// Check whether the new location fix is newer or older
long timeDelta = location.getTime() - currentBestLocation.getTime();
boolean isSignificantlyNewer = timeDelta > TWO_MINUTES;
boolean isSignificantlyOlder = timeDelta < -TWO_MINUTES;
boolean isNewer = timeDelta > 0;
// If it's been more than two minutes since the current location, use the new location
// because the user has likely moved
if (isSignificantlyNewer) {
return true;
// If the new location is more than two minutes older, it must be worse
} else if (isSignificantlyOlder) {
return false;
}
// Check whether the new location fix is more or less accurate
int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation.getAccuracy());
boolean isLessAccurate = accuracyDelta > 0;
boolean isMoreAccurate = accuracyDelta < 0;
boolean isSignificantlyLessAccurate = accuracyDelta > 200;
// Check if the old and new location are from the same provider
boolean isFromSameProvider = isSameProvider(location.getProvider(), currentBestLocation.getProvider());
// Determine location quality using a combination of timeliness and accuracy
if (isMoreAccurate) {
return true;
} else if (isNewer && !isLessAccurate) {
return true;
} else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider) {
return true;
}
return false;
}
private static boolean isSameProvider(String provider1, String provider2) {
if (provider1 == null) {
return provider2 == null;
}
return provider1.equals(provider2);
}
public static boolean hasSelfPermission(Context context, String[] permissions) {
// Below Android M all permissions are granted at install time and are already available.
if (!(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)) {
return true;
}
// Verify that all required permissions have been granted
for (String permission : permissions) {
if (context.checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
return false;
}
}
return true;
}
}
Use AppCache to store last location;
public class AppCache {
public static final String KEY_LAST_LOCATION = "_key_last_location";
private static SharedPreferences mPreference;
static {
mPreference = PreferenceManager.getDefaultSharedPreferences(App.getApp().getApplicationContext());
}
public static String getLastLocation() {
return mPreference.getString(KEY_LAST_LOCATION, null);
}
public static String getLastLocation(String defaultValue) {
return mPreference.getString(KEY_LAST_LOCATION, defaultValue);
}
public static void setLastLocation(String lastLocation) {
mPreference.edit().putString(KEY_LAST_LOCATION, lastLocation).commit();
}
public static void registerPreferenceChangeListener(SharedPreferences.OnSharedPreferenceChangeListener listener) {
mPreference.registerOnSharedPreferenceChangeListener(listener);
}
public static void unregisterPreferenceChangeListener(SharedPreferences.OnSharedPreferenceChangeListener listener) {
mPreference.unregisterOnSharedPreferenceChangeListener(listener);
}
}
Put the following code into your MainActivity onCreate() This will call locationManager to get last known location and update app cache.
LocationUtil.updateLastKnownLocation(MainActivity.this);
Also replace fetchLocation(); in onRequestPermissionsResult method with above line of code, so it will look like;
#Override
public void onRequestPermissionsResult(...){
switch (requestCode) {
case 101:
{
...
// permission was granted
//fetchLocation();
LocationUtil.updateLastKnownLocation(MainActivity.this);
} else {
// Show some error
}
return;
}
}
}
I did not use your latlang class. (Please make sure all class names follow Java coding standards) Instead use LocationLite to store location in cache. Also I used GSON google library to convert and restore pojo to JSON and backward.
public class LocationLite {
public double lat;
public double lon;
public String address;
}
Final changes in SunFragment.
Make SunAdapter as a member variable, and SharedPreferences.OnSharedPreferenceChangeListener to listen to any changes on location value.
SunAdapter mAdapter;
SharedPreferences.OnSharedPreferenceChangeListener mPreferenceChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if(AppCache.KEY_LAST_LOCATION.equalsIgnoreCase(key)) {
// location value has change, update data-set
SunSession sunSession = sunsList.get(0);
sunSession.setId(sharedPreferences.getString(key, "No Location"));
sunsList.add(0, sunSession);
mAdapter.notifyDataSetChanged();
}
}
};
Start listening to preference changes in onStart() and unregister in onStop()
#Override
public void onStart() {
super.onStart();
AppCache.registerPreferenceChangeListener(mPreferenceChangeListener);
}
#Override
public void onStop() {
super.onStop();
AppCache.unregisterPreferenceChangeListener(mPreferenceChangeListener);
}
Finally when populating first SunSession use the following instead location local variable. So It will look like following;
sunsList.add(
new SunSession(
AppCache.getLastLocation("Searching location..."),
"",
sun_rise,
"",
sun_set,
"&#xf0c9",
moon_rise,
"&#xf0ca",
moon_set));
That's all. Feel free to ask anything you do not understand.

location is always null at startup [duplicate]

I am reframing my last question, which is unanswered, and I have rewritten the problem following Google's BasicLocation.
My main activity is defined as:
public class MainActivity extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener {
// private LocationCallback locationCallback;
// private FusedLocationProviderClient mFusedLocationClient;
private FusedLocationProviderClient mFusedLocationClient;
protected Location mLastLocation;
private static final int REQUEST_PERMISSIONS_REQUEST_CODE = 34;
private static final String TAG = MainActivity.class.getSimpleName();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.drawer_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
SectionsPagerAdapter sectionsPagerAdapter = new SectionsPagerAdapter(this, getSupportFragmentManager());
ViewPager viewPager = findViewById(R.id.view_pager);
viewPager.setAdapter(sectionsPagerAdapter);
DrawerLayout drawer = findViewById(R.id.drawer_layout);
NavigationView navigationView = findViewById(R.id.nav_view);
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
drawer.addDrawerListener(toggle);
toggle.syncState();
navigationView.setNavigationItemSelectedListener(this);
ImageButton leftNav = findViewById(R.id.left_nav);
ImageButton rightNav = findViewById(R.id.right_nav);
leftNav.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int tab = viewPager.getCurrentItem();
if (tab > 0) {
tab--;
viewPager.setCurrentItem(tab);
} else if (tab == 0) {
viewPager.setCurrentItem(tab);
}
}
});
rightNav.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int tab = viewPager.getCurrentItem();
tab++;
viewPager.setCurrentItem(tab);
}
});
}
#Override
public void onStart() {
super.onStart();
if (!checkPermissions()) {
requestPermissions();
} else {
getLastLocation();
}
}
with latlang.[Lat,Lang] is in a seperate file:
public class latlang {
public static double Lat;
public static double Lang;
}
and the location file, which is the first fragment in the viewpager is defined as:
public class SunFragment extends Fragment {
List<SunSession> sunsList;
Typeface sunfont;
//to be called by the MainActivity
public SunFragment() {
// Required empty public constructor
}
// Keys for storing activity state.
// private static final String KEY_CAMERA_POSITION = "camera_position";
private static final String KEY_LOCATION_NAME = "location_name";
public String location;//="No location name found";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Retrieve location and camera position from saved instance state.
if (savedInstanceState != null) {
location = savedInstanceState.getCharSequence(KEY_LOCATION_NAME).toString();
System.out.println("OnCreate location "+location);
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View rootView = inflater.inflate(R.layout.fragment_sun, container, false);
onSaveInstanceState(new Bundle());
//SecondFragment secondFragment = new SecondFragment();
//secondFragment.getDeviceLocation();
RecyclerView rv = rootView.findViewById(R.id.rv_recycler_view);
rv.setNestedScrollingEnabled(false);
rv.setHasFixedSize(true);
//MyAdapter adapter = new MyAdapter(new String[]{"Today", "Golden Hour", "Blue Hour", "Civil Twilight", "Nautical Twilight", "Astronomical Twilight", "Hello", "World"});
//rv.setAdapter(adapter);
LinearLayoutManager llm = new LinearLayoutManager(getActivity());
rv.setLayoutManager(llm);
System.out.println("location "+location);
/*
Reversegeocoding location
*/
String location="No location name found";
String errorMessage = "";
List<Address> addresses = null;
Geocoder geocoder = new Geocoder(getContext(), Locale.getDefault());
try {
addresses = geocoder.getFromLocation(
latlang.Lat,
latlang.Lang,
1);
} catch (IOException ioException) {
// Catch network or other I/O problems.
errorMessage = getString(R.string.service_not_available);
// Log.e(TAG, errorMessage, ioException);
if (getView() != null){
Snackbar.make(getView(), errorMessage, Snackbar.LENGTH_LONG).show();
}
} catch (IllegalArgumentException illegalArgumentException) {
// Catch invalid latitude or longitude values.
errorMessage = getString(R.string.invalid_lat_long_used);
if (getView() != null){
Snackbar.make(getView(),
"Illegal Latitude = " + latlang.Lat + ", Longitude = " +
latlang.Lang, Snackbar.LENGTH_LONG).show();
}
}
if (addresses == null || addresses.size() == 0) {
if (errorMessage.isEmpty()) {
System.out.println("Adress Empty No Address Found");// Snackbar.LENGTH_LONG).show();
location = "Lat:"+latlang.Lat+" Lang: "+latlang.Lang;
}
} else {
location = addresses.get(0).getAddressLine(0);//+", "+ addresses.get(0).getLocality();
/* for(int i = 0; i <= addresses.get(0).getMaxAddressLineIndex(); i++) {
location = addresses.get(0).getAddressLine(i);
}*/
}
The problem with this is evident from the logcat:
I/System.out: location null
I/Google Maps Android API: Google Play services package version: 17785022
I/Choreographer: Skipped 31 frames! The application may be doing too much work on its main thread.
I/System.out: Position:0
I/System.out: Position:1
I/System.out: Position:2
I/zygote: Do full code cache collection, code=202KB, data=177KB
I/zygote: After code cache collection, code=129KB, data=91KB
I/zygote: JIT allocated 56KB for compiled code of void android.view.View.<init>(android.content.Context, android.util.AttributeSet, int, int)
I/zygote: Background concurrent copying GC freed 44415(2MB) AllocSpace objects, 7(136KB) LOS objects, 49% free, 3MB/6MB, paused 294us total 102.458ms
I/System.out: Position:3
I/System.out: Position:4
I/zygote: Do partial code cache collection, code=193KB, data=126KB
I/zygote: After code cache collection, code=193KB, data=126KB
I/zygote: Increasing code cache capacity to 1024KB
I/zygote: JIT allocated 71KB for compiled code of void android.widget.TextView.<init>(android.content.Context, android.util.AttributeSet, int, int)
I/zygote: Compiler allocated 4MB to compile void android.widget.TextView.<init>(android.content.Context, android.util.AttributeSet, int, int)
E/MainActivity: Latit: 37.42342342342342
This shows, at the start, location is null,
I/System.out: location null
then the recyclerview of the sunfragment is created
I/System.out: Position:0
I/System.out: Position:1
I/System.out: Position:2
and after that I am getting the location:
E/MainActivity: Latit: 37.42342342342342
Link of the complete code:https://drive.google.com/file/d/1pMl_3Lf76sy82C0J4b-9ta4jbSHonJ2y/view?usp=sharing
Is it somehow possible to get the location first before creating the sunfragment's oncreateview?
I found something wrong about your code (I may be wrong):
Why fields of latlang are static? It doesn't looks like they should.
At SunFragment.onCreate() you are reading location if savedInstanceState != null. savedInstanceState is not null only if activity that holds this fragment was restored from saved state. It may not happen at all.
You should use fragment's arguments (Bundle) to pass initial data to fragment
You should implement Parcelable interface for latlang to be able to pass custom class thru Bundle
I think that's not everything but for me it seems like enough for this code to not work as you expected
As stated by the previous answer there are a lot of issues in your code.
Apart from that understand that last known location may not always return a value. Gps basically has two data types: Ephemeris (precise nav data) and Almanac (coarse data). Now when the receiver is cold started ie after gps has been off for more than 8-10 mins, there is basically no last known location (the duration may vary based on the device but the basic idea is this).
So when you do not get last known location, fetch the actual live location using the fused client. Also since you are saving the data in shared preference and fetching it in your fragment, i believe your fragment is going to heavily rely on this data. So i would suggest either of the following two approaches to get correct result
1) do not fetch the location in the nesting activity at all. Just do it in the fragment where it is needed. This will not work if other fragments in your viewpager also need the location.
2) If you must have the location in your activity and it is a dependency in the container fragments, you can use two approaches here as well. My approaches rely on event bus.. Event bus, otto or rxbus anything will do
2a) do not add anything to the viewpager. basically fetch the location first fully and then add stuff to the viewpager once you get the location callback.
2b) Add stuff to the viewpager from the start. In the activity once you get the location, use the event bus to inform the fragments of the same and on getting the event in the fragments, actually start what you need to do.
I have previously used both these approaches and everything works. Now it is entirely up to your use case to use what suits you. Either ways it is too long a code and too complicated to post everything here.
Right now you are using like this:
#Override
public void onStart() {
super.onStart();
if (!checkPermissions()) {
requestPermissions();
} else {
getLastLocation();
}
}
In your else statement check if getLastLocation() is not null. if not null, replace the fragment.
#Override
public void onStart() {
super.onStart();
if (!checkPermissions()) {
requestPermissions();
} else {
if(getLastLocation() != null){
//replace your fragment
}else{//Null Location}
}
}
Use a service class to handle location service precisely. Here, i'm giving you a custom LocationService class which i used in many projects to collect the location data from background continuously
LocationService.kt
import android.Manifest
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Binder
import android.os.Build
import android.os.IBinder
import android.os.Looper
import android.util.Log
import androidx.core.app.ActivityCompat
import androidx.core.app.NotificationCompat
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.location.LocationCallback
import com.google.android.gms.location.LocationRequest
import com.google.android.gms.location.LocationResult
import com.google.android.gms.location.LocationServices
class LocationService : Service() {
//Custom Location Binder class
inner class LocationBinder : Binder() {
val locationService: LocationService
get() = this#LocationService
}
companion object {
val TAG = "LocationService_123"
private val UPDATE_INTERVAL = (4 * 1000).toLong() /* 4 secs */
private val FASTEST_INTERVAL: Long = 2000 /* 2 sec */
private var INSTANCE: LocationService? = null
fun isInstanceCreated(): Boolean {
return (INSTANCE != null)
}
}
private var mFusedLocationClient: FusedLocationProviderClient? = null
private var mLocationListener: LocationListener? = null
private var mLocationBinder = LocationBinder()
fun setLocationListener(locationListener: LocationListener) {
this.mLocationListener = locationListener
}
override fun onBind(intent: Intent): IBinder? {
return mLocationBinder
}
override fun onCreate() {
INSTANCE = this
super.onCreate()
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
if (Build.VERSION.SDK_INT >= 26) {
val CHANNEL_ID = "ostad_gps"
val channel = NotificationChannel(
CHANNEL_ID,
"Ostad GPS",
NotificationManager.IMPORTANCE_DEFAULT)
(getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).createNotificationChannel(channel)
val notification = NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("")
.setContentText("").build()
startForeground(1, notification)
}
}
override fun onDestroy() {
INSTANCE = null
super.onDestroy()
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
Log.d(TAG, "onStartCommand: called.")
getLocation()
return Service.START_NOT_STICKY
}
private fun getLocation() {
// ---------------------------------- LocationRequest ------------------------------------
// Create the location request to start receiving updates
val mLocationRequestHighAccuracy = LocationRequest()
mLocationRequestHighAccuracy.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
mLocationRequestHighAccuracy.interval = UPDATE_INTERVAL
mLocationRequestHighAccuracy.fastestInterval = FASTEST_INTERVAL
// new Google API SDK v11 uses getFusedLocationProviderClient(this)
if (ActivityCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_FINE_LOCATION
) != PackageManager.PERMISSION_GRANTED
) {
Log.d(TAG, "getLocation: stopping the location mitchService.")
stopSelf()
return
}
Log.d(TAG, "getLocation: getting location information.")
mFusedLocationClient!!.requestLocationUpdates(
mLocationRequestHighAccuracy, object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult?) {
val location = locationResult!!.lastLocation
Log.d(TAG, "onLocationResult: got location result: $location")
if (location != null) {
if (mLocationListener != null)
mLocationListener!!.onLocationChanged(location)
}
}
},
Looper.myLooper()
) // Looper.myLooper tells this to repeat forever until thread is destroyed
}
}
add this in your AndroidManifest file and start the LocationService from you MainActivity.
By looking at the code, it seems you do not need a very accurate location, you will be fine with last known location. This value might be null in some cases, like you have already experienced. Simple answer to your question is no, you cannot get not null location before creating SunFragment. Following steps is to load location in background and update UI once found.
Request last known location in MainActivity
Keep a reference of last location in cache for easy loading and better user
experience
If last location is null, request location updates until you get a good fix
Have a listener in SunFragment to track location updates
Here are some code you need (Please do read them)
Use the LocationUtil to handle location related events (I prefer LocationManager over FusedLocationProviderClient);
public class LocationUtil {
public static void updateLastKnownLocation(Context context) {
LocationManager lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
if(hasSelfPermission(context, new String[]{
Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION})) {
try {
Location currentBestLocation;
Location gpsLocation = lm.getLastKnownLocation(LocationManager.GPS_PROVIDER);
Location lbsLocation = lm.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
if(isBetterLocation(lbsLocation, gpsLocation)) {
currentBestLocation = lbsLocation;
} else {
currentBestLocation = gpsLocation;
}
if(currentBestLocation == null) {
requestLocationUpdates(lm);
} else {
updateCacheLocation(currentBestLocation);
}
} catch (SecurityException se) {
// unlikely as permission checks
se.printStackTrace();
} catch (Exception e) {
// unexpected
e.printStackTrace();
}
}
}
private static void updateCacheLocation(Location location) {
if(location == null) return;
LocationLite temp = new LocationLite();
temp.lat = location.getLatitude();
temp.lon = location.getLongitude();
Gson gson = new Gson();
String locationString = gson.toJson(temp);
AppCache.setLastLocation(locationString);
}
#SuppressLint("MissingPermission")
private static void requestLocationUpdates(LocationManager lm) {
try {
lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 0.0F, new LocationListener() {
#Override
public void onLocationChanged(Location location) {
updateCacheLocation(location);
lm.removeUpdates(this);
}
#Override
public void onStatusChanged(String s, int i, Bundle bundle) {
// doing nothing
}
#Override
public void onProviderEnabled(String s) {
// doing nothing
}
#Override
public void onProviderDisabled(String s) {
// doing nothing
}
});
}catch (Exception e) {
e.printStackTrace();
}
}
private static boolean isBetterLocation(Location location, Location currentBestLocation) {
int TWO_MINUTES = 1000 * 60 * 2;
if (currentBestLocation == null) {
// A new location is always better than no location
return true;
}
if (location == null) {
// A new location is always better than no location
return false;
}
// Check whether the new location fix is newer or older
long timeDelta = location.getTime() - currentBestLocation.getTime();
boolean isSignificantlyNewer = timeDelta > TWO_MINUTES;
boolean isSignificantlyOlder = timeDelta < -TWO_MINUTES;
boolean isNewer = timeDelta > 0;
// If it's been more than two minutes since the current location, use the new location
// because the user has likely moved
if (isSignificantlyNewer) {
return true;
// If the new location is more than two minutes older, it must be worse
} else if (isSignificantlyOlder) {
return false;
}
// Check whether the new location fix is more or less accurate
int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation.getAccuracy());
boolean isLessAccurate = accuracyDelta > 0;
boolean isMoreAccurate = accuracyDelta < 0;
boolean isSignificantlyLessAccurate = accuracyDelta > 200;
// Check if the old and new location are from the same provider
boolean isFromSameProvider = isSameProvider(location.getProvider(), currentBestLocation.getProvider());
// Determine location quality using a combination of timeliness and accuracy
if (isMoreAccurate) {
return true;
} else if (isNewer && !isLessAccurate) {
return true;
} else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider) {
return true;
}
return false;
}
private static boolean isSameProvider(String provider1, String provider2) {
if (provider1 == null) {
return provider2 == null;
}
return provider1.equals(provider2);
}
public static boolean hasSelfPermission(Context context, String[] permissions) {
// Below Android M all permissions are granted at install time and are already available.
if (!(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)) {
return true;
}
// Verify that all required permissions have been granted
for (String permission : permissions) {
if (context.checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
return false;
}
}
return true;
}
}
Use AppCache to store last location;
public class AppCache {
public static final String KEY_LAST_LOCATION = "_key_last_location";
private static SharedPreferences mPreference;
static {
mPreference = PreferenceManager.getDefaultSharedPreferences(App.getApp().getApplicationContext());
}
public static String getLastLocation() {
return mPreference.getString(KEY_LAST_LOCATION, null);
}
public static String getLastLocation(String defaultValue) {
return mPreference.getString(KEY_LAST_LOCATION, defaultValue);
}
public static void setLastLocation(String lastLocation) {
mPreference.edit().putString(KEY_LAST_LOCATION, lastLocation).commit();
}
public static void registerPreferenceChangeListener(SharedPreferences.OnSharedPreferenceChangeListener listener) {
mPreference.registerOnSharedPreferenceChangeListener(listener);
}
public static void unregisterPreferenceChangeListener(SharedPreferences.OnSharedPreferenceChangeListener listener) {
mPreference.unregisterOnSharedPreferenceChangeListener(listener);
}
}
Put the following code into your MainActivity onCreate() This will call locationManager to get last known location and update app cache.
LocationUtil.updateLastKnownLocation(MainActivity.this);
Also replace fetchLocation(); in onRequestPermissionsResult method with above line of code, so it will look like;
#Override
public void onRequestPermissionsResult(...){
switch (requestCode) {
case 101:
{
...
// permission was granted
//fetchLocation();
LocationUtil.updateLastKnownLocation(MainActivity.this);
} else {
// Show some error
}
return;
}
}
}
I did not use your latlang class. (Please make sure all class names follow Java coding standards) Instead use LocationLite to store location in cache. Also I used GSON google library to convert and restore pojo to JSON and backward.
public class LocationLite {
public double lat;
public double lon;
public String address;
}
Final changes in SunFragment.
Make SunAdapter as a member variable, and SharedPreferences.OnSharedPreferenceChangeListener to listen to any changes on location value.
SunAdapter mAdapter;
SharedPreferences.OnSharedPreferenceChangeListener mPreferenceChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if(AppCache.KEY_LAST_LOCATION.equalsIgnoreCase(key)) {
// location value has change, update data-set
SunSession sunSession = sunsList.get(0);
sunSession.setId(sharedPreferences.getString(key, "No Location"));
sunsList.add(0, sunSession);
mAdapter.notifyDataSetChanged();
}
}
};
Start listening to preference changes in onStart() and unregister in onStop()
#Override
public void onStart() {
super.onStart();
AppCache.registerPreferenceChangeListener(mPreferenceChangeListener);
}
#Override
public void onStop() {
super.onStop();
AppCache.unregisterPreferenceChangeListener(mPreferenceChangeListener);
}
Finally when populating first SunSession use the following instead location local variable. So It will look like following;
sunsList.add(
new SunSession(
AppCache.getLastLocation("Searching location..."),
"",
sun_rise,
"",
sun_set,
"&#xf0c9",
moon_rise,
"&#xf0ca",
moon_set));
That's all. Feel free to ask anything you do not understand.

PendingResult<PlaceLikelihoodBuffer> is getting executed for the second time

I am trying to make an application where user current place will be messaged to predefined number. I have implemented ShakeDetector library. Everything is working fine but I am getting the place name for the second time when I am shaking the device. [Please see the screenshots]
I put a debug point on the lines on PendingResult part.
For the first time the "for loop" is not executing but when I am shaking it for the second time the for loop is executing and I am getting the places. Whats wrong? Does PendingResult work as asynctask in android?
PS: I faced the similar problem while I was working with Google Fit API.the PendingResult was getting executed when I am returning to previous activity by pressing back button.I resolved the issue by creating different fragments and providing single GoogleApiClient to each of them.But as you can guess there is no scope of this here. What should I do plz help.
here is my code of the PhoneService.java where everything is happening.
public class PhoneService extends Service implements GoogleApiClient.OnConnectionFailedListener,GoogleApiClient.ConnectionCallbacks {
GoogleApiClient googleApiClient;
String place="";
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
ShakeDetector.create(this, new ShakeDetector.OnShakeListener() {
#Override
public void OnShake() {
try{
Toast.makeText(PhoneService.this,"Shake Detected",Toast.LENGTH_SHORT).show();
buildApiClient();
}catch (SecurityException e){
}
}
});
ShakeDetector.updateConfiguration(3f, 4);
ShakeDetector.start();
Toast.makeText(this,"Service started",Toast.LENGTH_SHORT).show();
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
ShakeDetector.stop();
}
public void buildApiClient(){
googleApiClient= new GoogleApiClient
.Builder(this)
.addApi(Places.GEO_DATA_API)
.addApi(Places.PLACE_DETECTION_API)
.addConnectionCallbacks(PhoneService.this)
.addOnConnectionFailedListener(PhoneService.this)
.build();
googleApiClient.connect();
}
#Override
public void onConnected(#Nullable Bundle bundle) {
Toast.makeText(PhoneService.this,"Connected",Toast.LENGTH_SHORT).show();
try{
if(googleApiClient.isConnected()){
PendingResult<PlaceLikelihoodBuffer> result = Places.PlaceDetectionApi
.getCurrentPlace(googleApiClient, null);
result.setResultCallback(new ResultCallback<PlaceLikelihoodBuffer>() {
#Override
public void onResult(PlaceLikelihoodBuffer likelyPlaces) {
for (PlaceLikelihood placeLikelihood : likelyPlaces) {
place=placeLikelihood.getPlace().getName()+"";
Log.d("PLACE",place);
}
likelyPlaces.release();
try{
SmsManager smsManager=SmsManager.getDefault();
smsManager.sendTextMessage("7098027655",null,"I am here at "+place+".Please pick me up.",null,null);
}catch (SecurityException e){
}
}
});
Intent callIntent = new Intent(Intent.ACTION_CALL);
callIntent.setData(Uri.parse("tel:7098027655"));
callIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(callIntent);
}
}catch (SecurityException e){
}
}
#Override
public void onConnectionSuspended(int i) {
Toast.makeText(PhoneService.this,"Connection Suspended",Toast.LENGTH_SHORT).show();
}
#Override
public void onConnectionFailed(#NonNull ConnectionResult connectionResult) {
Toast.makeText(PhoneService.this,"Connection Failed",Toast.LENGTH_SHORT).show();
}
}
You're creating and connecting a brand new GoogleApiClient every time the user shakes the device. You might have more luck creating and connecting the client once in the service's onCreate() method.

How can I solve this race condition involving PlacePicker?

I have a race condition between the PlacePicker activity and my own activity that I want to start immediately after PlacePicker ends.
Here is how my app works:
It begins in PlaceActivity. In PlaceActivity's onStart, I connect to the GoogleAPI. In onConnected, I use Places to determine the user's location. If the location has a probability of less than .8, it invokes the PlacePicker (which is it's own activity). The Placepicker is created with startActivityForResult. Within the corresponding onActivityResult, RatingsActivity is started and is passed the place from the PlacePicker. The problem is that there is a race between PlaceActivity restarting after PlacePicker stops, and the start of RatingsActivity. How do I fix this? I know I could require the user to press another button to find their location, but I would much rather it happen automatically in onStart.
#Override
public void onStart() {
super.onStart();
mGoogleApiClient.connect();
}
#Override
public void onConnected(Bundle bundle) {
detectCurrentPlace();
}
public void detectCurrentPlace() {
PendingResult<PlaceLikelihoodBuffer> result = Places.PlaceDetectionApi.getCurrentPlace(mGoogleApiClient, null);
//Find the most probable place
//If prob is greater than threshold, assume this is the correct place.
//Otherwise, open placepicker
final double thresh = .8;
//intent.putExtra("com.parse.starter.name", mostProbPlace.getName());
//intent.putExtra("com.parse.starter.address", mostProbPlace.getAddress());
result.setResultCallback(new ResultCallback<PlaceLikelihoodBuffer>() {
#Override
public void onResult(PlaceLikelihoodBuffer placeLikelihoods) {
double highestProb = 0;
PlaceLikelihood mostProbPlace = null;
for(PlaceLikelihood p : placeLikelihoods) {
if(p.getLikelihood() > highestProb) {
highestProb = p.getLikelihood();
mostProbPlace = p;
}
StringBuffer types = new StringBuffer();
for (int type : p.getPlace().getPlaceTypes()) {
types.append(", " + type);
}
Log.i(TAG, String.format("Place '%s' has liklihood: %g", p.getPlace().getName(), p.getLikelihood()));
Log.i(TAG, String.format("Website: '%s; Types: %s", p.getPlace().getWebsiteUri(), types));
}
placeLikelihoods.release();
//Log.i(TAG, "Probability of place: " + mostProbPlace.getLikelihood());
if(highestProb > thresh) {
//I'm not sure if this line is right
Intent intent = new Intent(getApplicationContext(), RatingActivity.class);
intent.putExtra("com.parse.starter.name", mostProbPlace.getPlace().getName());
intent.putExtra("com.parse.starter.address", mostProbPlace.getPlace().getAddress());
startActivity(intent);
}
else {
createPlacePicker();
}
placeLikelihoods.release();
}
});
}
public void createPlacePicker() {
PlacePicker.IntentBuilder builder = new PlacePicker.IntentBuilder();
Context context = getApplicationContext();
try {
startActivityForResult(builder.build(context), PLACE_PICKER_REQUEST);
} catch(GooglePlayServicesRepairableException e) {
e.printStackTrace();
Log.d(TAG, "REPAIRABLE_SERVICES");
} catch(GooglePlayServicesNotAvailableException e) {
e.printStackTrace();
Log.d(TAG, "NOTAVAILABLE_SERVICES");
}
}
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode == PLACE_PICKER_REQUEST) {
if(resultCode == RESULT_OK) {
Place place = PlacePicker.getPlace(data, this);
String toastMsg = String.format("Place: %s", place.getName());
Toast.makeText(this, toastMsg, Toast.LENGTH_LONG).show();
Intent intent = new Intent(this, RatingActivity.class);
intent.putExtra("com.parse.starter.name", place.getName());
intent.putExtra("com.parse.starter.address", place.getAddress());
startActivity(intent);
}
}
}
From what I can tell, the race condition is caused by the fact you are running detectCurrentPlace every time onStart is called.
I think you just need to store the state of your PlaceActivity when the activity is started, so you can modify the behaviour of onStart depending upon whether it is being called from the launch, or as a result of the return from PlacePicker.
class PlaceActivity {
boolean mRunningPlacePicker;
#Override
public void onStart() {
super.onStart();
if (mRunningPlacePicker) {
// we've returned from placepicker - don't run the detectCurrentPlace again
// because RatingsActivity has already launched from onActivityResult
...
} else {
mGoogleApiClient.connect();
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// save the state in case Android destroys our activity
outState.putBoolean("mRunningPlacePicker", mRunningPlacePicker);
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// restore the state
if (savedInstanceState != null) {
mRunningPlacePicker = savedInstanceState.getBoolean("mRunningPlacePicker");
}
}
#Override
public void onConnected(Bundle bundle) {
detectCurrentPlace();
}
public void detectCurrentPlace() {
// do your deciding...
result.setResultCallback(new ResultCallback<PlaceLikelihoodBuffer>() {
#Override
public void onResult(PlaceLikelihoodBuffer placeLikelihoods) {
...
if (highestProb > thresh) {
...
} else {
// save the fact we are running the place picker
mRunningPlacePicker = true;
createPlacePicker();
}
}
}

Android - Google Maps v2 - Retrieve and show position from a files containing coordinates

I would like to retrieve some positions (Latitude and Longitude) which are contained in a TXT file, and show them on my Map.
I know how to show my actual position, through the GPS of my tablet. I can update my position following this example.
public class MainActivity extends FragmentActivity implements LocationListener
{
private LocationManager locationmanager;
private GoogleMap googlemap;
private Marker marker;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initializeMap();
}
private void initializeMap()
{
if(googlemap==null)
{
googlemap=((MapFragment)getFragmentManager().findFragmentById(R.id.map)).getMap();
marker=googlemap.addMarker(new MarkerOptions().title("Vous etes la").position(new LatLng(0,0)));
if(googlemap==null)
{
Toast.makeText(getApplicationContext(), "Failed to create map",Toast.LENGTH_SHORT).show();
}
}
}
protected void onResume()
{
super.onResume();
locationmanager=(LocationManager)this.getSystemService(LOCATION_SERVICE);
if(locationmanager.isProviderEnabled(LocationManager.GPS_PROVIDER))
{
abonnementGps();
}
initializeMap();
}
public void onPause()
{
super.onPause();
desabonnementGps();
}
public void abonnementGps()
{
locationmanager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 19, this);
}
public void desabonnementGps()
{
locationmanager.removeUpdates(this);
}
#Override
public void onLocationChanged(Location location)
{
final StringBuilder msg=new StringBuilder("Lati :");
msg.append(location.getLatitude());
msg.append("\nLogi :");
msg.append(location.getLongitude());
Toast.makeText(this, msg.toString(),Toast.LENGTH_SHORT).show();
final LatLng latilongi=new LatLng(location.getLatitude(), location.getLongitude());
googlemap.moveCamera(CameraUpdateFactory.newLatLngZoom(latilongi,19));
marker.setPosition(latilongi);
}
#Override
public void onProviderDisabled(String provider)
{
if("gps".equals(provider))
{
desabonnementGps();
}
}
#Override
public void onProviderEnabled(String provider) {
if("gps".equals(provider))
{
abonnementGps();
}
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
// TODO Auto-generated method stub
}
}
I also know how to read a text file :
// METHODE POUR LIRE UN FICHIER TEXTE
public String readText(String filepath) throws Exception
{
String text="";
try
{
InputStream inputs=new FileInputStream(filepath);
InputStreamReader inputsreader=new InputStreamReader(inputs);
BufferedReader buffer=new BufferedReader(inputsreader);
String line;
while((line=buffer.readLine())!=null)
{
System.out.println(line);
text+=line+"\n";
}
buffer.close();
}
catch(FileNotFoundException e)
{
System.out.println(e);
}
return text;
}
For each line, I'd like to update my position, through the onLocationChanged(). But when I tried to call the method onLocationChanged() from readText(), my application was crashing.
NB : I changed the method public void onLocationChanged() to public void onLocationChanged(LatLng latlong) and the method public String readText(String str) to public LatLng readText(String str).
I think my ideas are not clear and I mix everything I've just learned.
Can someone guide me please ?
Thank you in advance for your help.
Best regards,
Tofuw
you dont call onLocationChanged manually, it gets called when your location changes so if you want to write to a file when a new location comes in you should do it in onLocationChanged
also you cannot change the method onLocationChanged to onLocationChanged(LatLng latlong) that is not how it works and you will not get the callbacks to the method. you need to keep it as is, you cant just go changing API methods like that
You need to add a new function to do the location change using the lat/lng from your text file:
public void MyLocationChanged(string slatlng )
{
string[] coord = slatlng.Split(',');
final LatLng latilongi=new LatLng( Double.parseDouble( coord[ 0 ] ), Double.parseDouble( coord[ 1 ] ) );
googlemap.moveCamera(CameraUpdateFactory.newLatLngZoom(latilongi,19));
marker.setPosition(latilongi);
}
Then just call this function from your text reading function, passing it the read line (assuming it is in the form: latitude,longitude.
I succeeded in solve my problem thanks to #Andrew-OpenGeoCode and #tyczj !
Here is my simple example, for those who are searching to retrieve and show positions from a files containing coordinates.
public class MainActivity extends FragmentActivity
{
// GoogleMaps
private GoogleMap googlemap;
private Marker marker;
private TCPClient tcpclient;
private Button bNavigation;
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Connect to server - See innerClass connectTask below
try{ initializeMap(); }
catch(Exception e){ e.printStackTrace(); }
bNavigation=(Button)findViewById(R.id.bNavigation);
bNavigation.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
String filepath="/storage/sdcard0/TrameGPS/tramegps.txt";
String texttosend="";
// Reading the TXT
try
{
texttosend=readText(filepath);
}
catch (Exception e1)
{
e1.printStackTrace();
}
}
});
}
public void initializeMap()
{
if(googlemap==null)
{
LatLng startposition=new LatLng(48.853,2.35);
googlemap=((MapFragment)getFragmentManager().findFragmentById(R.id.map)).getMap();
marker=googlemap.addMarker(new MarkerOptions().title("You are here !").position(startposition));
googlemap.moveCamera(CameraUpdateFactory.newLatLngZoom(startposition, 19));
if(googlemap==null)
Toast.makeText(getApplicationContext(), "Failed to load map !", Toast.LENGTH_SHORT).show();
}
}
// READING A TEXT FILE WHICH CONTAINS COORDINATES
public String readText(String filepath) throws Exception
{
String text="";
try
{
InputStream inputs=new FileInputStream(filepath);
InputStreamReader inputsreader=new InputStreamReader(inputs);
BufferedReader buffer=new BufferedReader(inputsreader);
String line;
while((line=buffer.readLine())!=null)
{
String[]coordinates=line.split(",");
if(coordinates[0].equals("$GPRMC"))
{
String coord=coordinates[3]+","+coordinates[5];
changePosition(Double.parseDouble(coordinates[3]),Double.parseDouble(coordinates[5]));
}
}
buffer.close();
}
catch(FileNotFoundException e)
{
System.out.println(e);
}
return text;
}
public void changePosition(Double a,Double b)
{
final StringBuilder notification=new StringBuilder("Latitude : ");
notification.append(a);
notification.append("\nLongitude : ");
notification.append(b);
Toast.makeText(this, notification, Toast.LENGTH_SHORT).show();
// UPDATE THE COORDINATES !!!! UPDATE THE COORDINATES !!!
final LatLng latilongi=new LatLng(a, b);
googlemap.moveCamera(CameraUpdateFactory.newLatLngZoom(latilongi, 19));
marker.setPosition(latilongi);
}
}
I hope it will help you.
Once again, thanks to tyczj and Andrew-OpenGeoCode !
Best regards,
Tofuw

Categories

Resources