package com.mehmetakiftutuncu.eshotroid.fragment;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.support.v4.app.Fragment;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.ScrollView;
import android.widget.TableLayout;
import android.widget.TextView;
import com.mehmetakiftutuncu.eshotroid.BuildConfig;
import com.mehmetakiftutuncu.eshotroid.R;
import com.mehmetakiftutuncu.eshotroid.activity.Times;
import com.mehmetakiftutuncu.eshotroid.model.Bus;
import com.mehmetakiftutuncu.eshotroid.model.BusTime;
import com.mehmetakiftutuncu.eshotroid.model.BusTimeTypes;
import com.mehmetakiftutuncu.eshotroid.utility.Constants;
import com.mehmetakiftutuncu.eshotroid.utility.Messages;
/** Fragment of all busses page
*
* @author mehmetakiftutuncu */
public class BusTimesFragment extends Fragment
{
/** Interface for ensuring the implementation of loading bus times */
public interface BusTimesListener
{
/** Reads bus times from the database if possible, if not tries to
* download them */
public void onBusTimesLoaded();
}
/** Reference to the activity that implements {@link BusTimesListener} */
private BusTimesListener listenerActivity;
/** Root {@link View} of the fragment UI */
private View layout;
/** {@link TextView} in which the route of the bus will be shown */
private TextView route;
/** {@link TextView} in which the source of the bus will be shown */
private TextView source;
/** {@link TextView} in which the destination of the bus will be shown */
private TextView destination;
/** {@link TableLayout} in which the times of the bus will be shown */
private TableLayout busTimesLayout;
/** {@link ScrollView} that contains {@link BusTimesFragment#busTimesLayout} */
private ScrollView busTimesScrollLayout;
/** List of {@link BusTime} objects showing times of the current bus for
* current type */
private ArrayList<BusTime> busTimes;
/** Type of the bus times corresponding to one of the values returning from
* {@link BusTimeTypes#getCode()}
*
* This will be set through the fragment's arguments */
private String type;
/** Flag indicating if the times of selected type for the bus is currently
* being refreshed */
private boolean isRefreshing = true;
/** Flag indicating if the UI is ready to show the times */
private boolean isWaitingForLayout = true;
/** Tag for debugging */
private static final String LOG_TAG = "Eshotroid_BusTimesFragment";
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
Log.d(LOG_TAG, "Creating fragment UI for " + type + "...");
// Create the view of this fragment
layout = inflater.inflate(R.layout.fragment_bus_times, container, false);
// Notify that layout is ready
isWaitingForLayout = false;
return layout;
}
@Override
public void onAttach(Activity activity)
{
super.onAttach(activity);
// Ensure attached activity implements the interface
try
{
Bundle arguments = getArguments();
if(arguments != null && arguments.containsKey(Constants.BUS_TIMES_TYPE_EXTRA))
{
type = arguments.getString(Constants.BUS_TIMES_TYPE_EXTRA);
}
if(BuildConfig.DEBUG) Log.d(LOG_TAG, "Attaching BusTimesFragment for " + type + "...");
listenerActivity = (BusTimesListener) activity;
}
catch(Exception e)
{
throw new ClassCastException(activity.getClass().getName() + " should implement BusTimesListener!");
}
}
/** @return {@link BusTimesFragment#isRefreshing} */
public boolean isRefreshing()
{
return isRefreshing;
}
/** Sets {@link BusTimesFragment#isRefreshing} as given value */
public void setRefreshing(boolean isRefreshing)
{
this.isRefreshing = isRefreshing;
if(layout != null)
{
if(isRefreshing)
{
layout.setVisibility(View.GONE);
}
else
{
layout.setVisibility(View.VISIBLE);
}
}
}
/** @return {@link BusTimesFragment#busTimes} */
public ArrayList<BusTime> getBusTimes()
{
return busTimes;
}
/** Sets {@link BusTimesFragment#busTimes} as given value */
public void setBusTimes(ArrayList<BusTime> busTimes)
{
if(BuildConfig.DEBUG) Log.d(LOG_TAG, "Setting bus times data set...");
this.busTimes = busTimes;
// Show loaded bus times
showBusTimes(true);
}
/** Shows the bus times
*
* @param isShowing If true, times will be shown, if false, times will be
* hidden */
public void showBusTimes(final boolean isShowing)
{
new Thread(new Runnable()
{
@Override
public void run()
{
// Wait here until layout is not null
while(isWaitingForLayout);
// When layout is ready, post following to UI thread to update times
new Handler(Looper.getMainLooper()).post(new Runnable()
{
@Override
public void run()
{
// Get components
if(route == null)
{
route = (TextView) layout.findViewById(R.id.textView_busTimes_route);
}
if(source == null)
{
source = (TextView) layout.findViewById(R.id.textView_busTimes_source);
}
if(destination == null)
{
destination = (TextView) layout.findViewById(R.id.textView_busTimes_destination);
}
if(busTimesLayout == null)
{
busTimesLayout = (TableLayout) layout.findViewById(R.id.tableLayout_busTimes);
}
if(busTimesScrollLayout == null)
{
busTimesScrollLayout = (ScrollView) layout.findViewById(R.id.scrollView_busTimes);
}
// Clear route, source and destination
route.setText("");
source.setText("");
destination.setText("");
// Clear the times table
busTimesLayout.removeAllViews();
if(isShowing)
{
if(BuildConfig.DEBUG) Log.d(LOG_TAG, "Showing bus times...");
// Get the current bus
Bus bus = ((Times) listenerActivity).getBus();
// Set route, source and destination
route.setText(bus.getRoute());
source.setText(bus.getSource());
destination.setText(bus.getDestination());
// Get a layout inflater
ActionBarActivity activity = (ActionBarActivity) listenerActivity;
LayoutInflater inflater = activity.getLayoutInflater();
// If there are bus times loaded
if(busTimes != null)
{
boolean isSourceNextTimeFound = false;
boolean isDestinationNextTimeFound = false;
int sourceNextTimeIndex = -1;
int destinationNextTimeIndex = -1;
// For each bus time
for(int i = 0; i < busTimes.size(); i++)
{
// Get current bus time
BusTime busTime = busTimes.get(i);
// Inflate the view for a row
final View row = inflater.inflate(R.layout.item_times, null);
// Find references to source and destination
RelativeLayout sourceLayout = (RelativeLayout) row.findViewById(R.id.relativeLayout_item_times_source);
RelativeLayout destinationLayout = (RelativeLayout) row.findViewById(R.id.relativeLayout_item_times_destination);
TextView source = (TextView) row.findViewById(R.id.textView_item_times_source);
TextView destination = (TextView) row.findViewById(R.id.textView_item_times_destination);
ImageView isWheelChairEnabledSource = (ImageView) row.findViewById(R.id.imageView_item_times_wheelChair_source);
ImageView isWheelChairEnabledDestination = (ImageView) row.findViewById(R.id.imageView_item_times_wheelChair_destination);
/* Set click listeners so users can see the remaining time once
* they click on a time */
source.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v)
{
showRemainingTime(((TextView) v).getText().toString());
}
});
destination.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v)
{
showRemainingTime(((TextView) v).getText().toString());
}
});
// Set the text inside the source and destination fields
source.setText(busTime.getTimeFromSource());
destination.setText(busTime.getTimeFromDestination());
// Show wheel chair enabled status
if(busTime.isWheelChairEnabledSource() && !busTime.getTimeFromSource().equals(""))
{
isWheelChairEnabledSource.setVisibility(View.VISIBLE);
}
if(busTime.isWheelChairEnabledDestination() && !busTime.getTimeFromDestination().equals(""))
{
isWheelChairEnabledDestination.setVisibility(View.VISIBLE);
}
// Set time status for source
if(!isSourceNextTimeFound && !isTimePassed(busTime.getTimeFromSource()))
{
sourceLayout.setBackgroundColor(getResources().getColor(R.color.item_times_status_next));
isSourceNextTimeFound = true;
sourceNextTimeIndex = i;
}
// Set time status for destination
if(!isDestinationNextTimeFound && !isTimePassed(busTime.getTimeFromDestination()))
{
destinationLayout.setBackgroundColor(getResources().getColor(R.color.item_times_status_next));
isDestinationNextTimeFound = true;
destinationNextTimeIndex = i;
}
// Add the generated row to the table
busTimesLayout.addView(row);
}
// Scroll
scrollToShowNextTime(sourceNextTimeIndex, destinationNextTimeIndex);
}
}
}
});
}
}).start();
}
private void scrollToShowNextTime(final int sourceNextTimeIndex, final int destinationNextTimeIndex)
{
new Handler(Looper.getMainLooper()).post(new Runnable()
{
@Override
public void run()
{
try
{
// Get item
RelativeLayout nextTimeRow = (RelativeLayout) ((ViewGroup) busTimesLayout.getChildAt(0)).getChildAt(0);
TextView nextTimeText = (TextView) nextTimeRow.getChildAt(0);
int numberOfRowsToScroll = 0;
// Calculate number of rows to scroll
if(sourceNextTimeIndex != -1 && destinationNextTimeIndex != -1)
{
if(sourceNextTimeIndex < destinationNextTimeIndex)
numberOfRowsToScroll = destinationNextTimeIndex;
else
numberOfRowsToScroll = sourceNextTimeIndex;
}
else
{
if(sourceNextTimeIndex != -1)
numberOfRowsToScroll = sourceNextTimeIndex;
else
numberOfRowsToScroll = destinationNextTimeIndex;
}
// Calculate the height of a row
int textSize = (int) nextTimeText.getTextSize();
int textPadding = getResources().getDimensionPixelSize(R.dimen.item_times_padding) * 2;
int rowHeight = textSize + textPadding;
int scrollAmount = numberOfRowsToScroll * rowHeight;
// Scroll
busTimesScrollLayout.scrollTo(0, scrollAmount);
}
catch(Exception e)
{
Log.e(LOG_TAG, "Error occurred while scrolling to show the next time!", e);
}
}
});
}
/** Shows the remaining time to the given time if the given time has not
* passed yet
*
* @param time Time as a string with format HH:MM */
@SuppressLint("SimpleDateFormat")
private void showRemainingTime(String time)
{
boolean isTimePassed = false;
try
{
SimpleDateFormat format = new SimpleDateFormat("HH:mm");
String currentTime = format.format(new Date(System.currentTimeMillis()));
String[] currentTokens = currentTime.split(":");
String givenTime = format.format(format.parse(time));
String[] givenTokens = givenTime.split(":");
int currentHours = Integer.parseInt(currentTokens[0]);
int currentMinutes = Integer.parseInt(currentTokens[1]);
int givenHours = Integer.parseInt(givenTokens[0]);
int givenMinutes = Integer.parseInt(givenTokens[1]);
if(givenHours < currentHours)
{
isTimePassed = true;
}
else if(givenHours == currentHours)
{
if(givenMinutes < currentMinutes)
{
isTimePassed = true;
}
}
if(!isTimePassed)
{
int remainingHours = givenHours - currentHours;
int remainingMinutes = givenMinutes - currentMinutes;
if(remainingMinutes < 0)
{
remainingHours--;
remainingMinutes += 60;
}
if(remainingHours == 0)
{
Messages.getInstance().showPositive(getActivity(),
getString(R.string.info_remainingTimeShort, time, remainingMinutes));
}
else
{
Messages.getInstance().showPositive(getActivity(),
getString(R.string.info_remainingTime, time, remainingHours,
remainingMinutes));
}
}
else
{
Messages.getInstance().showNeutral(getActivity(),
getString(R.string.info_selectedTimePassed));
}
}
catch(Exception e)
{
}
}
/** Checks if the given time is passed
*
* @param time Time as a string with format HH:MM
*
* @return true if the time is passed, false otherwise or an error occurs */
@SuppressLint("SimpleDateFormat")
private boolean isTimePassed(String time)
{
boolean result = false;
try
{
// If given time is empty, assume it is passed
if(time.equals(""))
{
return true;
}
SimpleDateFormat format = new SimpleDateFormat("HH:mm");
String currentTime = format.format(new Date(System.currentTimeMillis()));
String[] currentTokens = currentTime.split(":");
String givenTime = format.format(format.parse(time));
String[] givenTokens = givenTime.split(":");
int currentHours = Integer.parseInt(currentTokens[0]);
int currentMinutes = Integer.parseInt(currentTokens[1]);
int givenHours = Integer.parseInt(givenTokens[0]);
int givenMinutes = Integer.parseInt(givenTokens[1]);
if(givenHours == 0)
{
givenHours = 24;
}
if(givenHours < currentHours)
{
return true;
}
else if(givenHours == currentHours)
{
if(givenMinutes < currentMinutes)
{
return true;
}
}
}
catch(Exception e)
{
}
return result;
}
}