package edu.mit.mobile.android.locast.widget; /* * LocationLink.java * Copyright (C) 2010 MIT Mobile Experience Lab * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import java.io.IOException; import java.util.List; import java.util.Locale; import android.content.Context; import android.location.Address; import android.location.Geocoder; import android.location.Location; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.AsyncTask; import android.os.Parcel; import android.os.Parcelable; import android.util.AttributeSet; import android.widget.Button; import edu.mit.mobile.android.locast.ver2.R; /** * A clickable link that shows a reverse-geocoded location. * * @author steve * */ public class LocationLink extends Button { public static final String TAG = LocationLink.class.getSimpleName(); private Location location; private static String LAT_LON_FORMAT = "%.4f, %.4f"; private String geocodedName; private final int noLocationResId = R.string.location_link_no_location; private boolean showAccuracy = true; final ConnectivityManager cm; public LocationLink(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); setText(noLocationResId); cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); } public LocationLink(Context context, AttributeSet attrs){ this(context, attrs, 0); } public LocationLink(Context context){ this(context, null); } /** * Sets the location that is displayed on the link. Will display accuracy information if present * and if showAccuracy is set. * * @param location */ public void setLocation(Location location){ // only update if it's a distinctly new location. if (this.location == null || location == null || this.location.distanceTo(location) != 0){ this.location = location; geocodedName = null; // invalidated updateLabel(); } } /** * Sets the location that is displayed on the link. Will display accuracy information if present * and if showAccuracy is set. * * @param latitude * @param longitude */ public void setLocation(double latitude, double longitude){ final Location l = new Location("internal"); l.setLatitude(latitude); l.setLongitude(longitude); setLocation(l); } public Location getLocation(){ return location; } /** * Shows the accuracy of the location. * * @param showAccuracy */ public void setShowAccuracy(boolean showAccuracy){ this.showAccuracy = showAccuracy; } public boolean getShowAccuracy(){ return showAccuracy; } private void setGeocodedName(String placename){ geocodedName = placename; updateLabel(); } private void updateLabel(){ if (location == null){ setText(noLocationResId); }else{ String accuracy = ""; if (showAccuracy && location.hasAccuracy()){ final float accuracyVal = location.getAccuracy(); if (accuracyVal <= 500){ accuracy = " " + getResources().getString(R.string.location_link_accuracy_meter, accuracyVal); }else{ accuracy = " " + getResources().getString(R.string.location_link_accuracy_kilometer, accuracyVal / 1000.0); } } String placename; if (geocodedName == null){ placename = String.format(LAT_LON_FORMAT, location.getLatitude(), location.getLongitude()); final NetworkInfo activeNet = cm.getActiveNetworkInfo(); final boolean hasNetConnection = activeNet != null && activeNet.isConnected(); if (hasNetConnection){ final GeocoderTask t = new GeocoderTask(); t.execute(location); } }else{ placename = geocodedName; } setText(placename + accuracy); } } @Override public void onRestoreInstanceState(Parcelable state) { if (!state.getClass().equals(SavedState.class)){ super.onRestoreInstanceState(state); return; } final SavedState ss = (SavedState)state; super.onRestoreInstanceState(ss.getSuperState()); this.location = ss.location; this.geocodedName = ss.geocodedName; updateLabel(); } @Override public Parcelable onSaveInstanceState() { final Parcelable superState = super.onSaveInstanceState(); final SavedState ss = new SavedState(superState); ss.geocodedName = geocodedName; ss.location = location; return ss; } /** * This will preserve the looked-up placename, preventing unnecessary geocoder lookups. * @author stevep * */ private class SavedState extends BaseSavedState { String geocodedName; Location location; public SavedState(Parcel in) { super(in); geocodedName = in.readString(); location = Location.CREATOR.createFromParcel(in); } public SavedState(Parcelable superState){ super(superState); } @Override public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeString(geocodedName); dest.writeParcelable(location, flags); } } private class GeocoderTask extends AsyncTask<Location, Long, String> { @Override protected String doInBackground(Location... params) { final Location location = params[0]; final Geocoder geocoder = new Geocoder(getContext(), Locale.getDefault()); try { final List<Address> addresses = geocoder.getFromLocation(location.getLatitude(), location.getLongitude(), 1); if (addresses.size() > 0){ final Address thisLocation = addresses.get(0); // TODO fixme return thisLocation.toString(); //return AddressUtils.addressToName(thisLocation); } } catch (final IOException e) { e.printStackTrace(); } return null; } @Override protected void onPostExecute(String result) { if (result != null){ setGeocodedName(result); } } } }