/* * Copyright (C) 2010-2016 Paul Watts (paulcwatts@gmail.com) * University of South Florida (sjbarbeau@gmail.com) * * 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.ui; import android.content.Context; import android.content.res.Resources; import org.onebusaway.android.R; import org.onebusaway.android.io.elements.ObaArrivalInfo; import org.onebusaway.android.io.elements.ObaArrivalInfo.Frequency; import org.onebusaway.android.provider.ObaContract; import org.onebusaway.android.util.ArrivalInfoUtils; import org.onebusaway.android.util.UIUtils; import java.text.DateFormat; import java.util.Date; public final class ArrivalInfo { private final ObaArrivalInfo mInfo; private final long mEta; private final long mDisplayTime; private final String mStatusText; private final String mTimeText; private final String mNotifyText; private final int mColor; private static final int ms_in_mins = 60 * 1000; private final boolean mPredicted; private final boolean mIsArrival; private final boolean mIsRouteAndHeadsignFavorite; /** * @param includeArrivalDepartureInStatusLabel true if the arrival/departure label * should be * included in the status label false if it * should not */ public ArrivalInfo(Context context, ObaArrivalInfo info, long now, boolean includeArrivalDepartureInStatusLabel) { mInfo = info; // First, all times have to have to be converted to 'minutes' final long nowMins = now / ms_in_mins; long scheduled, predicted; // If this is the first stop in the sequence, show the departure time. if (info.getStopSequence() != 0) { scheduled = info.getScheduledArrivalTime(); predicted = info.getPredictedArrivalTime(); mIsArrival = true; } else { // Show departure time scheduled = info.getScheduledDepartureTime(); predicted = info.getPredictedDepartureTime(); mIsArrival = false; } final long scheduledMins = scheduled / ms_in_mins; final long predictedMins = predicted / ms_in_mins; if (predicted != 0) { mPredicted = true; mEta = predictedMins - nowMins; mDisplayTime = predicted; } else { mPredicted = false; mEta = scheduledMins - nowMins; mDisplayTime = scheduled; } mColor = ArrivalInfoUtils.computeColor(scheduledMins, predictedMins); mStatusText = computeStatusLabel(context, info, now, predicted, scheduledMins, predictedMins, includeArrivalDepartureInStatusLabel); mTimeText = computeTimeLabel(context); // Check if the user has marked this routeId/headsign/stopId as a favorite mIsRouteAndHeadsignFavorite = ObaContract.RouteHeadsignFavorites .isFavorite(info.getRouteId(), info.getHeadsign(), info.getStopId()); mNotifyText = computeNotifyText(context); } /** * @param includeArrivalDeparture true if the arrival/departure label should be included, false * if it should not */ private String computeStatusLabel(Context context, ObaArrivalInfo info, final long now, final long predicted, final long scheduledMins, final long predictedMins, boolean includeArrivalDeparture) { if (context == null) { // The Activity has been destroyed, so just return an empty string to avoid an NPE return ""; } final Resources res = context.getResources(); Frequency frequency = info.getFrequency(); if (frequency != null) { int headwayAsMinutes = (int) (frequency.getHeadway() / 60); DateFormat formatter = DateFormat.getTimeInstance(DateFormat.SHORT); int statusLabelId = -1; long time = 0; if (now < frequency.getStartTime()) { statusLabelId = R.string.stop_info_frequency_from; time = frequency.getStartTime(); } else { statusLabelId = R.string.stop_info_frequency_until; time = frequency.getEndTime(); } String label = formatter.format(new Date(time)); return context.getString(statusLabelId, headwayAsMinutes, label); } if (predicted != 0) { long delay = predictedMins - scheduledMins; if (mEta >= 0) { // Bus hasn't yet arrived/departed return ArrivalInfoUtils.computeArrivalLabelFromDelay(res, delay); } else { /** * Arrival/departure time has passed */ if (!includeArrivalDeparture) { // Don't include "depart" or "arrive" in label if (delay > 0) { // Delayed return res.getQuantityString( R.plurals.stop_info_status_late_without_arrive_depart, (int) delay, delay); } else if (delay < 0) { // Early delay = -delay; return res.getQuantityString( R.plurals.stop_info_status_early_without_arrive_depart, (int) delay, delay); } else { // On time return context.getString(R.string.stop_info_ontime); } } if (mIsArrival) { // Is an arrival time if (delay > 0) { // Arrived late return res.getQuantityString( R.plurals.stop_info_arrived_delayed, (int) delay, delay); } else if (delay < 0) { // Arrived early delay = -delay; return res .getQuantityString( R.plurals.stop_info_arrived_early, (int) delay, delay); } else { // Arrived on time return context.getString(R.string.stop_info_arrived_ontime); } } else { // Is a departure time if (delay > 0) { // Departed late return res.getQuantityString( R.plurals.stop_info_depart_delayed, (int) delay, delay); } else if (delay < 0) { // Departed early delay = -delay; return res .getQuantityString( R.plurals.stop_info_depart_early, (int) delay, delay); } else { // Departed on time return context.getString(R.string.stop_info_departed_ontime); } } } } else { // Scheduled times if (!includeArrivalDeparture) { return context.getString(R.string.stop_info_scheduled); } if (mIsArrival) { return context.getString(R.string.stop_info_scheduled_arrival); } else { return context.getString(R.string.stop_info_scheduled_departure); } } } private String computeTimeLabel(Context context) { if (context == null) { // The Activity has been destroyed, so just return an empty string to avoid an NPE return ""; } String displayTime = UIUtils.formatTime(context, getDisplayTime()); if (mEta >= 0) { // Bus hasn't yet arrived if (mIsArrival) { return context.getString(R.string.stop_info_time_arriving_at, displayTime); } else { return context .getString(R.string.stop_info_time_departing_at, displayTime); } } else { // Arrival/departure time has passed if (mIsArrival) { return context.getString(R.string.stop_info_time_arrived_at, displayTime); } else { return context.getString(R.string.stop_info_time_departed_at, displayTime); } } } private String computeNotifyText(Context context) { if (context == null) { // The Activity has been destroyed, so just return an empty string to avoid an NPE return ""; } final String routeDisplayName = UIUtils.getRouteDisplayName(mInfo); if (mEta > 0) { // Bus hasn't yet arrived/departed if (mIsArrival) { return context.getString(R.string.trip_stat_arriving, routeDisplayName, (int) (mEta)); } else { return context.getString(R.string.trip_stat_departing, routeDisplayName, (int) (mEta)); } } else if (mEta < 0) { // Bus arrived or departed if (mIsArrival) { return context.getString(R.string.trip_stat_gone_arrived, routeDisplayName); } else { return context.getString(R.string.trip_stat_gone_departed, routeDisplayName); } } else { // Bus is arriving/departing now if (mIsArrival) { return context.getString(R.string.trip_stat_lessthanone_arriving, routeDisplayName); } else { return context.getString(R.string.trip_stat_lessthanone_departing, routeDisplayName); } } } public final ObaArrivalInfo getInfo() { return mInfo; } public final long getEta() { return mEta; } public final long getDisplayTime() { return mDisplayTime; } public final String getStatusText() { return mStatusText; } public final String getTimeText() { return mTimeText; } public final String getNotifyText() { return mNotifyText; } /** * Returns true if this arrival info is for an arrival time, false if it is for a departure * time */ public final boolean isArrival() { return mIsArrival; } /** * Returns the resource code for the color that should be used for the arrival time * * @return the resource code for the color that should be used for the arrival time */ public final int getColor() { return mColor; } /** * Returns true if there is real-time arrival info available for this trip, false if there is * not * * @return true if there is real-time arrival info available for this trip, false if there is * not */ public final boolean getPredicted() { return mPredicted; } /** * Returns true if this route is a user-designated favorite, false if it is not * * @return true if this route is a user-designated favorite, false if it is not */ public final boolean isRouteAndHeadsignFavorite() { return mIsRouteAndHeadsignFavorite; } }