/* * Copyright (C) 2014 Sean J. Barbeau (sjbarbeau@gmail.com), University of South Florida * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.onebusaway.android.view; import org.onebusaway.android.R; import org.onebusaway.android.app.Application; import org.onebusaway.android.util.LocationHelper; import android.content.Context; import android.content.SharedPreferences; import android.location.Location; import android.util.AttributeSet; import android.widget.TextView; import java.text.NumberFormat; import java.util.ArrayList; import java.util.Locale; /** * A TextView that updates itself with the distance to the given bus stop from the given location */ public class DistanceToStopView extends TextView implements LocationHelper.Listener { public interface Listener { /** * Called when the DistanceToStopView is showing information to the user */ void onInitializationComplete(); } private static String TAG = "DistanceToStopView"; Context mContext; Location mStopLocation; ArrayList<Listener> mListeners = new ArrayList<Listener>(); private static final float MILES_TO_METERS = 0.000621371f; private static final float MILES_THRESHOLD = 0.25f; private static final float KILOMETERS_THRESHOLD = 0.25f; private static final float MILES_TO_FEET = 5280; NumberFormat mNumberFormat; Locale mLocale; private static String IMPERIAL; private static String METRIC; private static String AUTOMATIC; SharedPreferences mSettings; private String preferredUnits; boolean mInitialized = false; public DistanceToStopView(Context context, AttributeSet attrs) { super(context, attrs); mContext = context; mNumberFormat = NumberFormat.getInstance(); mNumberFormat.setMaximumFractionDigits(1); mLocale = Locale.getDefault(); IMPERIAL = mContext.getString(R.string.preferences_preferred_units_option_imperial); METRIC = mContext.getString(R.string.preferences_preferred_units_option_metric); AUTOMATIC = mContext.getString(R.string.preferences_preferred_units_option_automatic); mSettings = Application.getPrefs(); preferredUnits = mSettings .getString(mContext.getString(R.string.preference_key_preferred_units), AUTOMATIC); } public synchronized void registerListener(Listener listener) { if (!mListeners.contains(listener)) { mListeners.add(listener); } } public synchronized void unregisterListener(Listener listener) { if (mListeners.contains(listener)) { mListeners.remove(listener); } } /** * Sets the stop location that this view measures the distance to * * @param location stop location */ public void setStopLocation(Location location) { mStopLocation = location; } /** * Should be called when the view is being shown again and the preferences * may have changed (typically, in onResume() of the parent context) */ public void refreshUnitsPreference() { preferredUnits = mSettings .getString(mContext.getString(R.string.preference_key_preferred_units), AUTOMATIC); } /** * Returns true if the view is initialized and ready to draw to the screen, false if it is not * * @return true if the view is initialized and ready to draw to the screen, false if it is not */ public boolean isInitialized() { return mInitialized; } @Override public void onLocationChanged(Location location) { if (mStopLocation != null) { if (!mInitialized) { mInitialized = true; // Notify listeners that we have both stop and real-time location for (Listener l : mListeners) { l.onInitializationComplete(); } } Float distance = location.distanceTo(mStopLocation); if (distance != null) { double miles = distance * MILES_TO_METERS; distance /= 1000; // Convert meters to kilometers if (preferredUnits.equalsIgnoreCase(AUTOMATIC)) { // Log.d(TAG, "Setting units automatically"); // If the country is set to USA, assume imperial, otherwise metric // TODO - Method of guessing metric/imperial can definitely be improved if (mLocale.getISO3Country().equalsIgnoreCase(Locale.US.getISO3Country())) { // Assume imperial setDistanceTextView(miles, IMPERIAL); } else { // Assume metric setDistanceTextView(distance, METRIC); } } else if (preferredUnits.equalsIgnoreCase(IMPERIAL)) { setDistanceTextView(miles, IMPERIAL); } else if (preferredUnits.equalsIgnoreCase(METRIC)) { setDistanceTextView(distance, METRIC); } return; } } // If we've gotten this far, distance isn't valid, so clear current text setText(""); } /** * Sets the text view that contains distance with units based on input parameters * * @param distance the distance to be used, in miles (for imperial) or kilometers (for metric) * @param units the units to be used from strings.xml, either preferences_preferred_units_option_metric * or preferences_preferred_units_option_imperial */ private void setDistanceTextView(double distance, String units) { // Set TextView text // TODO - Set ContentDescription to be read by screen readers if (units.equalsIgnoreCase( mContext.getString(R.string.preferences_preferred_units_option_imperial))) { // IMPERIAL // If the distance is greater than a quarter mile, show in miles, else show in feet if (distance > MILES_THRESHOLD) { // MILES mNumberFormat.setMaximumFractionDigits(1); setText(mNumberFormat.format(distance) + " " + mContext .getString(R.string.miles_abbreviation)); } else { // FEET mNumberFormat.setMaximumFractionDigits(0); double feet = distance * MILES_TO_FEET; setText(mNumberFormat.format(feet) + " " + mContext .getString(R.string.feet_abbreviation)); } } else if (units.equalsIgnoreCase(mContext.getString( R.string.preferences_preferred_units_option_metric))) { // METRIC if (distance > KILOMETERS_THRESHOLD) { // KILOMETERS mNumberFormat.setMaximumFractionDigits(1); setText(mNumberFormat.format(distance) + " " + mContext .getString(R.string.kilometers_abbreviation)); } else { // METERS mNumberFormat.setMaximumFractionDigits(0); double meters = distance * 1000; setText(mNumberFormat.format(meters) + " " + mContext .getString(R.string.meters_abbreviation)); } } } }