Permalink
Browse files

[Mobile] Fixed bug where the "Alerts" button's colour with change out…

… of sync with the currently-selected tab.

[Mobile] Performing a swipe refresh on the stop forecast screen now displays a Snackbar with the created time according to the API.
[Mobile] Bumped API version to 3 in order to support this.
[Mobile] Added analytics code to detect any anomalies during retrieval of the created time.
  • Loading branch information...
thecosmicfrog committed Dec 11, 2018
1 parent 22ec07e commit 70a0886eba3a9983c8261b20a64f49427be49e85
@@ -8,8 +8,8 @@ android {
applicationId "org.thecosmicfrog.luasataglance"
minSdkVersion 21
targetSdkVersion 27
versionCode 116
versionName "0.116"
versionCode 121
versionName "0.121"
}
buildTypes {
release {
@@ -57,7 +57,11 @@
import org.thecosmicfrog.luasataglance.view.StatusCardView;
import org.thecosmicfrog.luasataglance.view.StopForecastCardView;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Timer;
@@ -313,7 +317,7 @@ public void setUserVisibleHint(boolean isVisibleToUser) {

Preferences.saveSelectedStopName(getContext(), Constant.NO_LINE, stopName);

loadStopForecast(stopName);
loadStopForecast(stopName, false);

shouldAutoReload = true;
} else {
@@ -384,55 +388,62 @@ private boolean initFragment() {
public void onItemSelected(AdapterView<?> parent, View view, int position,
long id) {
/*
* If the Spinner's selected item is "Select a stop...", we don't need to
* do anything. Just clear the stop forecast and get out of here.
* onItemSelected() is triggered on creation of the tab. Prevent this by
* only triggering when the tab is visible to user. This is to prevent the
* Alerts button changing colour out of sync with the currently-visible tab.
*/
if (position == 0) {
shouldAutoReload = false;

swipeRefreshLayout.setEnabled(false);
if (isVisibleToUser) {
/*
* If the Spinner's selected item is "Select a stop...", we don't need to
* do anything. Just clear the stop forecast and get out of here.
*/
if (position == 0) {
shouldAutoReload = false;

clearStopForecast();
swipeRefreshLayout.setEnabled(false);

return;
} else {
swipeRefreshLayout.setEnabled(true);
}
clearStopForecast();

shouldAutoReload = true;

/* Hide the select stop tutorial, if it is visible. */
StopForecastUtil.displayTutorial(
rootView,
line,
Constant.TUTORIAL_SELECT_STOP,
false
);

/* Show the notifications tutorial. */
StopForecastUtil.displayTutorial(
rootView,
line,
Constant.TUTORIAL_NOTIFICATIONS,
true
);
return;
} else {
swipeRefreshLayout.setEnabled(true);
}

/*
* Get the stop name from the current position of the Spinner, save it to
* SharedPreferences, then load a stop forecast with it.
*/
String selectedStopName =
spinnerCardView
.getSpinnerStops().getItemAtPosition(position).toString();
shouldAutoReload = true;

loadStopForecast(selectedStopName);
/* Hide the select stop tutorial, if it is visible. */
StopForecastUtil.displayTutorial(
rootView,
line,
Constant.TUTORIAL_SELECT_STOP,
false
);

if (isVisibleToUser) {
Preferences.saveSelectedStopName(
getContext(),
/* Show the notifications tutorial. */
StopForecastUtil.displayTutorial(
rootView,
line,
selectedStopName
Constant.TUTORIAL_NOTIFICATIONS,
true
);

/*
* Get the stop name from the current position of the Spinner, save it to
* SharedPreferences, then load a stop forecast with it.
*/
String selectedStopName =
spinnerCardView
.getSpinnerStops().getItemAtPosition(position).toString();

loadStopForecast(selectedStopName, false);

if (isVisibleToUser) {
Preferences.saveSelectedStopName(
getContext(),
line,
selectedStopName
);
}
}
}

@@ -458,7 +469,8 @@ public void onRefresh() {
/* Start the refresh animation. */
swipeRefreshLayout.setRefreshing(true);
loadStopForecast(
Preferences.selectedStopName(getContext(), line)
Preferences.selectedStopName(getContext(), line),
true
);
}
}
@@ -765,7 +777,8 @@ public void run() {
Preferences.selectedStopName(
getContext(),
line
)
),
false
);
}
}
@@ -781,11 +794,13 @@ public void run() {
/**
* Load the stop forecast for a particular stop.
* @param stopName The stop for which to load a stop forecast.
* @param shouldShowSnackbar Whether or not we should show a Snackbar to the user with the API
* created time.
*/
private void loadStopForecast(String stopName) {
private void loadStopForecast(String stopName, final boolean shouldShowSnackbar) {
final String API_URL = "https://api.thecosmicfrog.org/cgi-bin";
final String API_ACTION = "times";
final String API_VER = "2";
final String API_VER = "3";

setIsLoading(true);

@@ -816,6 +831,17 @@ public void success(ApiTimes apiTimes, Response response) {
/* Stop the refresh animations. */
setIsLoading(false);
swipeRefreshLayout.setRefreshing(false);

if (shouldShowSnackbar) {
String apiCreatedTime = getApiCreatedTime(apiTimes);

if (apiCreatedTime != null) {
StopForecastUtil.showSnackbar(
getActivity(),
"Times updated at " + apiCreatedTime
);
}
}
} else {
Analytics.nullApitimes(
getContext(),
@@ -883,6 +909,41 @@ public void failure(RetrofitError retrofitError) {
);
}

/**
* Get the "created" time from the API response and format it so that only the time (and not
* date) is returned.
* @param apiTimes ApiTimes object.
* @return String representing the 24hr time (HH:mm:ss) of the API's "created" time.
*/
private String getApiCreatedTime(ApiTimes apiTimes) {
try {
Date currentTime = new SimpleDateFormat(
"yyyy-MM-dd'T'HH:mm:ss",
Locale.getDefault()
).parse(apiTimes.getCreatedTime());

DateFormat dateFormat =
new SimpleDateFormat("HH:mm:ss", Locale.getDefault());

return dateFormat.format(currentTime);
} catch (NullPointerException e) {
Log.e(
LOG_TAG,
"Failed to find content view during Snackbar creation."
);
} catch (ParseException e) {
Log.e(LOG_TAG, "Failed to parse created time from API.");

Analytics.apiCreatedParseError(
getContext(),
"api_error",
"api_created_parse_error_mobile"
);
}

return null;
}

/**
* Draw stop forecast to screen.
* @param stopForecast StopForecast object containing data for requested stop.
@@ -30,13 +30,20 @@

public class ApiTimes {

@SerializedName("created")
private String createdTime;

private String message;

@SerializedName("status")
private StopForecastStatus stopForecastStatus;

private List<Tram> trams;

public String getCreatedTime() {
return createdTime;
}

public String getMessage() {
return message;
}
@@ -49,6 +56,10 @@ public StopForecastStatus getStopForecastStatus() {
return trams;
}

public void setCreatedTime(String c) {
createdTime = c;
}

public void setMessage(String m) {
message = m;
}
@@ -31,14 +31,30 @@

public final class Analytics {

private static final String EVENT_API_CREATED_PARSE_ERROR = "api_created_parse_error";
private static final String EVENT_HTTP_ERROR = "http_error";
private static final String EVENT_NULL_APITIMES = "null_apitimes";
private static final String LOG_TAG = Analytics.class.getSimpleName();

private static FirebaseAnalytics firebaseAnalytics;

public static void httpError(Context context, String contentType, String itemId) {
public static void apiCreatedParseError(Context context, String contentType, String itemId) {
try {
firebaseAnalytics = FirebaseAnalytics.getInstance(context);
} catch (Exception e) {
Log.e(LOG_TAG, "Error gathering analytics for " + itemId + ".");
Log.e(LOG_TAG, Log.getStackTraceString(e));
}

if (firebaseAnalytics != null) {
Bundle params = new Bundle();
params.putString(FirebaseAnalytics.Param.CONTENT_TYPE, contentType);
params.putString(FirebaseAnalytics.Param.ITEM_ID, itemId);
firebaseAnalytics.logEvent(EVENT_API_CREATED_PARSE_ERROR, params);
}
}

public static void httpError(Context context, String contentType, String itemId) {
try {
firebaseAnalytics = FirebaseAnalytics.getInstance(context);
} catch (Exception e) {
@@ -21,6 +21,8 @@

package org.thecosmicfrog.luasataglance.util;

import android.app.Activity;
import android.support.design.widget.Snackbar;
import android.util.Log;
import android.view.View;

@@ -224,4 +226,17 @@ public static StopForecast createStopForecast(ApiTimes apiTimes) {

return stopForecast;
}

/**
* Show Snackbar.
* @param activity Activity on which to display Snackbar.
* @param message Message to display on Snackbar.
*/
public static void showSnackbar(Activity activity, String message) {
Snackbar.make(
activity.findViewById(android.R.id.content),
message,
Snackbar.LENGTH_LONG
).show();
}
}
@@ -23,8 +23,8 @@
App
-->
<string name="app_name">Luas at a Glance</string>
<string name="version_code" translatable="false">116</string>
<string name="version_name" translatable="false">0.116</string>
<string name="version_code" translatable="false">121</string>
<string name="version_name" translatable="false">0.121</string>

<!--
Activities
@@ -10,8 +10,8 @@ android {
applicationId "org.thecosmicfrog.luasataglance"
minSdkVersion 21
targetSdkVersion 27
versionCode 11600
versionName "0.116"
versionCode 12100
versionName "0.121"
}
buildTypes {
release {
@@ -23,7 +23,7 @@ android {
productFlavors {
wear2 {
minSdkVersion 25
versionCode 11601
versionCode 12101
}
}
}
@@ -23,8 +23,8 @@
App
-->
<string name="app_name">Luas at a Glance</string>
<string name="version_code" translatable="false">116</string>
<string name="version_name" translatable="false">0.116</string>
<string name="version_code" translatable="false">121</string>
<string name="version_name" translatable="false">0.121</string>

<!--
Activities

0 comments on commit 70a0886

Please sign in to comment.