/*******************************************************************************
* This file is part of the RozkladPKP project.
*
* RozkladPKP 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 3 of the License, or
* (at your option) any later version.
*
* RozkladPKP 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 RozkladPKP. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
package org.tyszecki.rozkladpkp;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;
import org.tyszecki.rozkladpkp.pln.UnboundConnection;
import org.tyszecki.rozkladpkp.pln.PLN;
import org.tyszecki.rozkladpkp.pln.PLN.Train;
import org.tyszecki.rozkladpkp.pln.PLN.TrainChange;
import org.tyszecki.rozkladpkp.servers.HafasServer;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.Typeface;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.TextPaint;
import android.text.format.Time;
import android.text.style.ForegroundColorSpan;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
public class ConnectionListItemAdapter extends BaseAdapter implements Observer{
public final static int HEADER = 0;
public final static int NORMAL = 1;
public final static int SCROLL = 2;
public final static int WARNING = 3;
public final static int WARNING_STATIC = 1;
public final static int WARNING_SERVER = 2;
private ArrayList<Integer> items;
private PLN pln;
Context c;
private boolean backupServerWarning = false;
private boolean scrolling = false;
private boolean delayInfo = false;
private String depStation;
private ConnectionList clist;
private int dep_width,arr_width;
private int textSize;
private SpannableStringBuilder spanBuilder = new SpannableStringBuilder();
private ForegroundColorSpan greenSpan = new ForegroundColorSpan(Color.rgb(73,194,98));
private ForegroundColorSpan redSpan = new ForegroundColorSpan(Color.rgb(220, 59, 76));
private ForegroundColorSpan yellowSpan = new ForegroundColorSpan(Color.rgb(197,170,73));
public ConnectionListItemAdapter(Context context) {
c = context;
items = new ArrayList<Integer>();
TypedArray t = c.obtainStyledAttributes(new int[]{android.R.attr.textSize});
textSize = t.getDimensionPixelSize(0, -1);
if(textSize == -1)
textSize = (int) ((new TextView(c)).getTextSize());
}
public void setConnectionList(ConnectionList list)
{
clist = list;
list.addObserver(this);
}
private void calculateTextSizes() {
TextPaint tp = new TextPaint();
tp.setTypeface(Typeface.DEFAULT_BOLD);
tp.setTextSize(textSize+3);
//Obliczenie wielkości pola tekstowego dla odjazdów
int t = 0;
for(int i = 0; i < pln.connectionCount(); ++i)
{
UnboundConnection c = pln.connections[i];
if(c.getChange() != null && Math.abs(c.getChange().departureDelay) > t)
t = Math.abs(c.getChange().departureDelay);
}
float timew = tp.measureText("23:55 +"+Integer.toString(t));
dep_width = (int) (timew+1);
//Obliczenie wielkości pola tekstowego dla przyjazdów
t = -1000;
for(int i = 0; i < pln.connectionCount(); ++i)
{
Train tr = pln.connections[i].getTrain(pln.connections[i].trainCount-1);
TrainChange c = tr.getChange();
if(c != null && c.realarrtime != null && c.realarrtime.difference(tr.arrtime).intValue() > t)
t = c.realarrtime.difference(tr.arrtime).intValue();
}
if(t != -1000)
{
timew = tp.measureText("23:55 +"+Integer.toString(t));
arr_width = (int) (timew+1);
}
else
arr_width = -1;
}
Spannable delayText(String time, int delay)
{
ForegroundColorSpan span;
if(delay <= 0)
span = greenSpan;
else if(delay <= 5)
span = yellowSpan;
else
span = redSpan;
time += (delay >= 0) ? " +" : " ";
time += Integer.toString(delay);
spanBuilder.clearSpans();
spanBuilder.clear();
spanBuilder.append(time);
spanBuilder.setSpan(span, 6, time.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
return spanBuilder;
}
public View getView(int position, View v, ViewGroup parent) {
LayoutInflater vi = null;
if (v == null)
vi = (LayoutInflater)c.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if(scrolling && (position == 0 || position == getCount()-1))
v = fillScrollingItem(position == 0, (v == null) ? vi.inflate(R.layout.scrollitem, null) : v); //Nast/Pop
else if(position < headerItems())
v = fillWarningItem(scrolling ? WARNING_SERVER : WARNING_STATIC, (v == null) ? vi.inflate(R.layout.warning_item, null) : v); //Ostrzeżenia
else
{
//Data lub połączenie
position = items.get(position - headerItems());
if(position < 0) //Data
v = fillDateItem(-position-1, (v == null) ? vi.inflate(R.layout.common_date_header_row, null) : v);
else //Połączenie
v = fillConnectionItem(position, (v == null) ? vi.inflate(R.layout.connection_list_row, null) : v);
}
return v;
}
private View fillConnectionItem(int connectionIndex, View view) {
UnboundConnection o = pln.connections[connectionIndex];
TextView tt = (TextView) view.findViewById(R.id.departure_time);
TextView bt = (TextView) view.findViewById(R.id.arrival_time);
((ImageView) view.findViewById(R.id.info_icon)).setVisibility(o.hasMessages() ? View.VISIBLE : View.GONE);
((ImageView) view.findViewById(R.id.walk_icon)).setVisibility(o.getTrain(0).depstation.name.equals(depStation) ? View.GONE : View.VISIBLE);
int tl = o.trainCount;
if(delayInfo)
{
tt.setWidth(dep_width);
if(arr_width > 0)
bt.setWidth(arr_width);
}
String deptime = o.getTrain(0).deptime.toString();
if(o.getChange() != null && o.getChange().departureDelay != -1)
tt.setText(delayText(deptime, o.getChange().departureDelay));
else
tt.setText(deptime);
Train last = o.getTrain(tl-1);
String arrtime = last.arrtime.toString();
if(last.getChange() != null && last.getChange().realarrtime != null)
bt.setText(delayText(arrtime, last.getChange().realarrtime.difference(last.arrtime).intValue()));
else
bt.setText(arrtime);
((TextView) view.findViewById(R.id.changes)).setText(Integer.toString(o.changesCount));
((TextView) view.findViewById(R.id.duration)).setText(o.getJourneyTime().toLongString());
LinearLayout lay = (LinearLayout)view.findViewById(R.id.type_icons);
lay.removeAllViews();
for(int i = 0; i < tl; i++)
{
if(o.getTrain(i).number.equals("Fußweg") || o.getTrain(i).number.equals("Übergang"))
continue;
String s = CommonUtils.trainType(o.getTrain(i).number);
s = (s.length() > 0 ? s : "OS");
TextView t = new TextView(c);
t.setText(s);
t.setTextColor(Color.BLACK);
t.setGravity(Gravity.CENTER_VERTICAL);
t.setSingleLine();
t.setBackgroundResource(CommonUtils.drawableForTrainType(s));
lay.addView(t);
}
return view;
}
private View fillDateItem(int dayNumber, View view) {
TextView head = (TextView) view.findViewById(R.id.conn_header);
head.setText(pln.days().getDay(dayNumber).format("%A, %d.%m.%Y"));
return view;
}
private View fillWarningItem(int type, View view) {
if(type == WARNING_SERVER)
((TextView) view.findViewById(R.id.text)).setText("Uwaga! Z powodu problemów z serwerem systemu SITKOL, zwrócono wyniki z serwera kolei niemieckich. Mogą one zawierać mniej szczegółów i być mniej nieaktualne. Proszę zachować ostrożność.");
else
((TextView) view.findViewById(R.id.text)).setText("Oglądasz w tej chwili zapisane wyniki wyszukiwania. Dotknij tutaj, aby rozpocząć nowe wyszukiwanie.");
return view;
}
private View fillScrollingItem(boolean top, View view) {
TextView head = (TextView) view.findViewById(R.id.scrollitem_text);
if(top)
head.setText("Wcześniejsze połączenia");
else
head.setText("Późniejsze połączenia");
return view;
}
@Override
public int getCount() {
return items.size()+staticItems();
}
@Override
public Object getItem(int arg0) {
return null;
}
@Override
public long getItemId(int arg0) {
return arg0;
}
public int getConnectionAt(int position)
{
Log.i("RozkladPKP", Integer.toString(position));
int c = -1;
try{
c = items.get(position - headerItems());
}
catch(Exception e){}
if(c < 0)
c = -1;
return c;
}
public Time getDateForConnectionAt(int position)
{
position -= headerItems();
int p = 0;
do{
p = items.get(position--);
}while(position >= 0 && p >= 0);
return pln.days().getDay(-p-1);
}
@Override
public int getItemViewType(int position) {
if(scrolling && position == 0)
return SCROLL;
else if(position < headerItems())
return WARNING;
else if(position - headerItems() < items.size())
{
//Data lub połączenie
position = items.get(position - headerItems());
if(position < 0) //Data
return HEADER;
else //Połączenie
return NORMAL;
}
else
return SCROLL;
//TODO: DRY!
}
@Override
public int getViewTypeCount() {
return 4;
}
@Override
public boolean hasStableIds() {
return false;
}
@Override
public boolean isEmpty() {
return items.size() == 0;
}
public boolean areAllItemsSelectable() {
return false;
}
@Override
public boolean isEnabled(int position) {
return (getItemViewType(position) != HEADER);
}
public int headerItems()
{
//+1, ponieważ jeśli scrolling, to 1, a jeśli !scrolling, to też 1 (ostrzeżenie o statycznym)
return (backupServerWarning ? 1 : 0) + 1;
}
public int footerItems()
{
return (scrolling ? 1 : 0);
}
public int staticItems()
{
return headerItems() + footerItems();
}
String getContentForSharing()
{
String msg = pln.departureStation().name + " - " + pln.arrivalStation().name+" ";
for(Map.Entry<Integer, List<Integer>> l : pln.days().getDaysMap().entrySet())
{
msg += pln.days().getDay(l.getKey()).format("%d.%m.%Y") + ":\n";
for(Integer c : l.getValue())
{
UnboundConnection con = pln.connections[c];
if(con.changesCount == 0)
msg += con.getTrain(0).deptime.toString()+",\n";
else
{
msg += con.getTrain(0).deptime.toString();
msg += con.trainCount > 2 ? " z przesiadkami (" : " z przesiadką (";
for(int i = 1; i < con.trainCount; ++i)
{
Train tr = con.getTrain(i);
msg += tr.depstation.name + " "+tr.deptime.toString();
if(i < con.trainCount -1 )
msg += ", ";
}
msg += "),\n";
}
}
}
return msg.substring(0,msg.length()-2)+'.';
}
@Override
public void update(Observable observable, Object data) {
if(clist.getLastError() != HafasServer.DOWNLOAD_OK || clist.getPLN() == null || clist.getPLN().conCnt == 0)
return;
pln = clist.getPLN();
depStation = pln.departureStation().name;
scrolling = clist.scrollable();
backupServerWarning = clist.getServerId() > 0;
delayInfo = pln.hasDelayInfo();
if(delayInfo)
calculateTextSizes();
items.clear();
for(Map.Entry<Integer, List<Integer>> l : pln.days().getDaysMap().entrySet())
{
//Dni są oznaczone liczbami ujemnymi, zaczynając od -1
//Pierwszy dzień to -1, następny to -2, itd.
items.add(-l.getKey()-1);
//Dodanie numerów połączeń
for(Integer i : l.getValue())
items.add(i);
}
notifyDataSetChanged();
}
}