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.ConnectionList.CachePolicy;
import org.tyszecki.rozkladpkp.pln.Connection;
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 android.app.AlarmManager;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.Typeface;
import android.net.Uri;
import android.preference.Preference;
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.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.widget.RemoteViews;
import android.widget.TextView;
public class ConnectionsWidget extends AppWidgetProvider {
static final String ACTION_CONTROL = "org.tyszecki.rozkladpkp.ConnectionsWidget.WIDGET_CONTROL";
public static final String URI_SCHEME = "connections_widget";
final int CONNECTIONS_CACHED = 20;
private enum FieldType{DepartureTime,ArrivalTime,Duration,Changes,Row}
static int getViewID(FieldType field, int row)
{
//Możemy użyć do tego albo odbić i cache'ować wyniki, albo zahardcodować. Na razie niech będą zahardcodowane
switch(field)
{
case DepartureTime:
switch(row)
{
case 0: return R.id.departure_time_0;
case 1: return R.id.departure_time_1;
case 2: return R.id.departure_time_2;
case 3: return R.id.departure_time_3;
case 4: return R.id.departure_time_4;
}
case ArrivalTime:
switch(row)
{
case 0: return R.id.arrival_time_0;
case 1: return R.id.arrival_time_1;
case 2: return R.id.arrival_time_2;
case 3: return R.id.arrival_time_3;
case 4: return R.id.arrival_time_4;
}
case Duration:
switch(row)
{
case 0: return R.id.journey_time_0;
case 1: return R.id.journey_time_1;
case 2: return R.id.journey_time_2;
case 3: return R.id.journey_time_3;
case 4: return R.id.journey_time_4;
}
case Changes:
switch(row)
{
case 0: return R.id.changes_0;
case 1: return R.id.changes_1;
case 2: return R.id.changes_2;
case 3: return R.id.changes_3;
case 4: return R.id.changes_4;
}
case Row:
switch(row)
{
case 0: return R.id.row_0;
case 1: return R.id.row_1;
case 2: return R.id.row_2;
case 3: return R.id.row_3;
case 4: return R.id.row_4;
}
default:
return -1;
}
}
@Override
public void onDeleted(Context context, int[] appWidgetIds) {
super.onDeleted(context, appWidgetIds);
for(int widgetID : appWidgetIds)
{
//Anulowanie alarmu
Intent clickIntent = new Intent();
clickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,widgetID);
clickIntent.setAction(ACTION_CONTROL);
clickIntent.setData(Uri.withAppendedPath(Uri.parse(URI_SCHEME + "://widget/id/#scrollDown"), Integer.toString(widgetID)));
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, clickIntent, PendingIntent.FLAG_UPDATE_CURRENT);
((AlarmManager)context.getSystemService(Context.ALARM_SERVICE)).cancel(pendingIntent);
//Usunięcie ustawień
final SharedPreferences pref = context.getSharedPreferences("ConnectionWidget", Context.MODE_PRIVATE);
String tID = Integer.toString(widgetID);
Editor ed = pref.edit();
for (String i : new String[]{"depName", "arrName", "viaName", "SID", "ZID", "VID1", "Products"/*, "Attributes"*/})
if(pref.contains(i))
ed.remove(i+tID);
}
}
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
for (int widgetId : appWidgetIds)
update(appWidgetManager, context, widgetId, false);
}
@Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
if(intent != null)
Log.w("RozkladPKP", intent.getAction());
if(intent.getAction().equals(ACTION_CONTROL))
{
String frag = null;
if(intent.getData() != null)
frag = intent.getData().getFragment();
if(frag == null)
return;
int _id = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
if(_id == -1)
return;
String id = Integer.toString(_id);
if(frag.equals("return"))
{
SharedPreferences pref = context.getSharedPreferences("ConnectionWidget", Context.MODE_PRIVATE);
String sid = pref.getString("SID"+id,"");
String zid = pref.getString("ZID"+id,"");
pref.edit().putString("SID"+id, zid).putString("ZID"+id, sid).commit();
update(AppWidgetManager.getInstance(context), context, _id, false);
}
else if(frag.equals("scrollDown"))
{
SharedPreferences pref = context.getSharedPreferences("ConnectionWidget", Context.MODE_PRIVATE);
pref.edit().putInt("ScrollPos"+id, pref.getInt("ScrollPos"+id, 0)+1).commit();
update(AppWidgetManager.getInstance(context), context, _id, false);
}
else if(frag.equals("reload"))
{
update(AppWidgetManager.getInstance(context), context, _id, true);
}
}
}
static Spannable delayText(String time, int delay)
{
ForegroundColorSpan span;
if(delay <= 0)
span = new ForegroundColorSpan(Color.rgb(73,194,98));
else if(delay <= 5)
span = new ForegroundColorSpan(Color.rgb(197,170,73));
else
span = new ForegroundColorSpan(Color.rgb(220, 59, 76));
time += (delay >= 0) ? " +" : " ";
time += Integer.toString(delay);
SpannableStringBuilder spanBuilder = new SpannableStringBuilder();
spanBuilder.clearSpans();
spanBuilder.clear();
spanBuilder.append(time);
spanBuilder.setSpan(span, 6, time.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
return spanBuilder;
}
static class CTSResult{
public int arr_width,dep_width;
}
static private CTSResult calculateTextSizes(Context cx, PLN pln) {
TextPaint tp = new TextPaint();
tp.setTypeface(Typeface.DEFAULT_BOLD);
tp.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 23, cx.getResources().getDisplayMetrics()));
//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));
int dep_width = (int) (timew);
//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();
}
int arr_width = -1;
if(t != -1000)
{
timew = tp.measureText("23:55 +"+Integer.toString(t));
arr_width = (int) (timew);
}
else
arr_width = -1;
CTSResult ret = new CTSResult();
ret.arr_width = arr_width;
ret.dep_width = dep_width;
return ret;
}
public static void update(final AppWidgetManager appWidgetManager, final Context c, final int widgetID, boolean forceUpdate)
{
final String pkg = c.getPackageName();
//Na szybko wyświetlamy ładowanie...
appWidgetManager.updateAppWidget(widgetID, new RemoteViews(pkg, R.layout.connection_list_widget_loading));
final SharedPreferences pref = c.getSharedPreferences("ConnectionWidget", Context.MODE_PRIVATE);
final String id = Integer.toString(widgetID);
if(!pref.contains("depName"+id))
return;
Log.d("RozkladPKP", "Wywołanie aktualizacji");
Log.d("RozkladPKP", "Widżet "+id+"wyświetla połączenia: "+pref.getString("depName"+id, ":(") + " → "+ pref.getString("arrName"+id, ":("));
final int scrollPos = pref.getInt("ScrollPos"+id, 0);
ArrayList<SerializableNameValuePair> params = new ArrayList<SerializableNameValuePair>();
for (String i : new String[]{"SID", "ZID", "VID1"/*, "Products", "Attributes"*/})
if(pref.contains(i+id))
params.add(new SerializableNameValuePair(i, pref.getString(i+id, "")));
final String productString = pref.getString("Products"+id,"");
params.add(new SerializableNameValuePair("REQ0JourneyProduct_prod_list_1", productString));
Time t = new Time();
t.setToNow();
params.add(new SerializableNameValuePair("date",t.format("%d.%m.%Y")));
params.add(new SerializableNameValuePair("time",t.format("%H:%M")));
if(scrollPos >= 5)
forceUpdate = true; //Gwarantuje co najmniej 5 połączeń TODO: zależne od wielkości widżeta
ConnectionList clist = ConnectionList.forParameters(c, params, forceUpdate ? CachePolicy.NoCached : CachePolicy.CachedIfAvailable, 1);
clist.addObserver(new Observer() {
@Override
public void update(Observable observable, Object data) {
//To pole będzie zawierać prawdę, jeśli wywołanie jest pierwszym z dwóch.
if(data != null && (Boolean)data)
return;
Log.w("RozkladPKP", "Wywołanie obserwatora listy");
ConnectionList cl = (ConnectionList)observable;
PLN pln = cl.getPLN();
//Czy są opóźnienia?
boolean delays = pln.hasDelayInfo();
CTSResult dims = null;
if(delays)
dims = calculateTextSizes(c, pln);
if(pln.days().totalConnectionCount() < 10 && cl.scrollable())
{
Log.w("RozkladPKP", "Mało..."+Integer.toString(pln.days().totalConnectionCount()));
cl.fetchMore(false);
return;
}
int sp2 = scrollPos;
if(cl.scrollable())
{
Log.w("RozkladPKP", "Nowa lista");
//Nowo pobrana lista, trzeba ustalić do jakiej pozycji przewinąć scroll
//Pokazujemy tylko co najwyżej jedno połączenie z przeszłości
Time now = new Time();
now.setToNow();
sp2 = 0;
for(Connection connection : pln.days().getConnectionIterator())
if(Time.compare(connection.getDate(true), now) < 0)
sp2++;
else
break;
if(sp2 > 0)
sp2--;
pref.edit().putInt("ScrollPos"+id, sp2).commit();
}
Log.w("RozkladPKP", "SP2: "+Integer.toString(sp2));
cl.saveInCache(c, 1);
RemoteViews views = new RemoteViews(pkg, R.layout.connection_list_widget);
Time upalarm = null;
int cn = 0;
int rownum = 0;
int depw = 0,arrw = 0;
if(delays)
{
arrw = dims.arr_width;
depw = dims.dep_width;
}
for(Connection connection : pln.days().getConnectionIterator())
{
if(cn++ < sp2) continue;
if(rownum > 4) break;
UnboundConnection con = connection.connection;
if(cn == sp2+2)
{
upalarm = connection.getDate(true);
Log.i("RozkladPKP", "Ustawię alarm na: "+upalarm.format2445());
}
if(delays)
{
views.setInt(getViewID(FieldType.DepartureTime, rownum), "setWidth", depw);
if(arrw > 0)
views.setInt(getViewID(FieldType.ArrivalTime, rownum), "setWidth", arrw);
}
if(con.getChange() != null && con.getChange().departureDelay != -1)
views.setTextViewText(getViewID(FieldType.DepartureTime, rownum), delayText(con.getTrain(0).deptime.toString(),con.getChange().departureDelay));
else
views.setTextViewText(getViewID(FieldType.DepartureTime, rownum), con.getTrain(0).deptime.toString());
Train last = con.getTrain(con.trainCount-1);
if(last.getChange() != null && last.getChange().realarrtime != null)
views.setTextViewText(getViewID(FieldType.ArrivalTime, rownum), delayText(last.arrtime.toString(),last.getChange().realarrtime.difference(last.arrtime).intValue()));
else
views.setTextViewText(getViewID(FieldType.ArrivalTime, rownum), last.arrtime.toString());
views.setTextViewText(getViewID(FieldType.Duration, rownum), con.getJourneyTime().toString());
views.setTextViewText(getViewID(FieldType.Changes, rownum), Integer.toString(con.changesCount));
Intent intent = new Intent(c, ConnectionDetailsActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setData(Uri.withAppendedPath(Uri.parse(URI_SCHEME + "://widget/id/#connection"+Integer.toString(con.number)), Integer.toString(widgetID)));
intent.putExtra("PLNData", cl.getPLN().data);
intent.putExtra("ConnectionIndex", con.number);
intent.putExtra("StartDate", connection.getDate(false).format("%d.%m.%Y"));
//ni.putExtra("Attributes", extras.getSerializable("Attributes"));
intent.putExtra("Products", productString);
views.setOnClickPendingIntent(getViewID(FieldType.Row, rownum), PendingIntent.getActivity(c, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT));
rownum++;
}
views.setTextViewText(R.id.arrival_station, pln.arrivalStation().name);
views.setTextViewText(R.id.departure_station, pln.departureStation().name+ " →");
Intent clickIntent = new Intent();
clickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,widgetID);
clickIntent.setAction(ACTION_CONTROL);
clickIntent.setData(Uri.withAppendedPath(Uri.parse(URI_SCHEME + "://widget/id/#reload"), Integer.toString(widgetID)));
PendingIntent pendingIntent = PendingIntent.getBroadcast(c, 0, clickIntent, PendingIntent.FLAG_UPDATE_CURRENT);
views.setOnClickPendingIntent(R.id.reload_button, pendingIntent);
clickIntent = new Intent();
clickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,widgetID);
clickIntent.setAction(ACTION_CONTROL);
clickIntent.setData(Uri.withAppendedPath(Uri.parse(URI_SCHEME + "://widget/id/#return"), Integer.toString(widgetID)));
pendingIntent = PendingIntent.getBroadcast(c, 0, clickIntent, PendingIntent.FLAG_UPDATE_CURRENT);
views.setOnClickPendingIntent(R.id.return_button, pendingIntent);
clickIntent = new Intent();
clickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,widgetID);
clickIntent.setAction(ACTION_CONTROL);
clickIntent.setData(Uri.withAppendedPath(Uri.parse(URI_SCHEME + "://widget/id/#scrollDown"), Integer.toString(widgetID)));
pendingIntent = PendingIntent.getBroadcast(c, 0, clickIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Log.i("RozkladPKP", "Ustawiłem alarm na: "+upalarm.format2445());
((AlarmManager)c.getSystemService(Context.ALARM_SERVICE)).set(AlarmManager.RTC, upalarm.toMillis(false), pendingIntent);
appWidgetManager.updateAppWidget(widgetID, views);
}
});
}
}