busradar.madison.StopDialog.java Source code

Java tutorial

Introduction

Here is the source code for busradar.madison.StopDialog.java

Source

// Copyright (C) 2010 Aleksandr Dobkin, Michael Choi, and Christopher Mills.
// 
// This file is part of BusRadar <https://github.com/orgs/busradar/>.
// 
// BusRadar 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.
// 
// BusRadar 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.

package busradar.madison;

import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;

import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.SocketException;
import java.net.URL;
import java.net.URLConnection;
import java.net.UnknownHostException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Locale;
import java.util.Scanner;
import java.util.regex.Pattern;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.http.conn.ssl.SSLSocketFactory;

import android.app.Dialog;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Typeface;
import android.text.Html;
import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.HorizontalScrollView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.RelativeLayout.LayoutParams;
import android.widget.TextView;
import android.widget.Toast;

import static busradar.madison.ProblemReporter.*;

public final class StopDialog extends Dialog {

    //   static javax.net.ssl.SSLSocketFactory sslsockfactory;
    //   
    //   static {
    //      // Imports: javax.net.ssl.TrustManager, javax.net.ssl.X509TrustManager
    //      try {
    //          // Create a trust manager that does not validate certificate chains
    //          final TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
    //              public void checkClientTrusted( final X509Certificate[] chain, final String authType ) {
    //              }
    //              public void checkServerTrusted( final X509Certificate[] chain, final String authType ) {
    //              }
    //              public X509Certificate[] getAcceptedIssuers() {
    //                  return null;
    //              }
    //          } };
    //          
    //          // Install the all-trusting trust manager
    //          final SSLContext sslContext = SSLContext.getInstance( "TLS" );
    //          sslContext.init( null, trustAllCerts, new java.security.SecureRandom() );
    //          // Create an ssl socket factory with our all-trusting manager
    //          sslsockfactory = sslContext.getSocketFactory();
    //          
    //          
    //          
    //      } catch ( final Exception e ) {
    //          e.printStackTrace();
    //      }
    //   
    //   }

    final static Pattern num_vehicles_re = Pattern.compile("Next (\\d) Vehicles Arrive at:");
    final static Pattern time_re = Pattern.compile("(\\d\\d?:\\d\\d [AP]\\.M\\.).*TO (.*)<");
    final static Pattern time_re_backup = Pattern.compile("(\\d\\d?:\\d\\d [AP]\\.M\\.)");

    //final static Pattern no_busses_re = Pattern.compile("No stops with upcoming crossings times found\\.");
    //final static Pattern no_timepoints_re = Pattern.compile("No stop information is found with this time point\\.");
    final static int route_list_id = 1;
    final static int time_list_id = 2;
    final static int stop_num_id = 3;

    ListView list_view;
    TextView status_text;
    TextView cur_loading_text;
    BaseAdapter times_adapter;
    final static String TRANSITTRACKER_URL = "http://webwatch.cityofmadison.com/webwatch/MobileAda.aspx?";

    static class RouteURL {
        static final int LOADING = 0;
        static final int DONE = 1;
        static final int NO_MORE_TODAY = 2;
        static final int NO_TIMEPOINTS = 3;
        static final int NO_STOPS_UNKONWN = 4;
        static final int ERROR = 5;

        int route;
        String url;
        //String text;
        int status = 0;
    }

    static final class RouteTime implements Comparable<RouteTime> {
        int route;
        String dir;
        String time;
        Date date;

        public int compareTo(RouteTime another) {
            return date.compareTo(another.date);

        }
    }

    final class CellView extends RelativeLayout {
        TextView route_textview;
        TextView dir_textview;
        TextView time_textview;

        CellView(Context ctx) {
            super(ctx);

            setPadding(5, 0, 5, 0);
            addView(route_textview = new TextView(ctx) {
                {
                    setId(1);
                    setTextColor(0xffffffff);
                    setTypeface(Typeface.DEFAULT_BOLD);
                    setTextSize(TypedValue.COMPLEX_UNIT_PX, getTextSize() * 2f);
                }
            }, new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT) {
                {
                    addRule(RelativeLayout.ALIGN_PARENT_TOP);
                }
            });
            addView(dir_textview = new TextView(ctx) {
                {
                    setTextColor(0xffffffff);
                }
            }, new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT) {
                {
                    addRule(RelativeLayout.BELOW, 1);
                }
            });
            addView(time_textview = new TextView(ctx) {
                {
                    setTextSize(TypedValue.COMPLEX_UNIT_PX, getTextSize() * 2f);
                    setTextColor(0xffffffff);
                }
            }, new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT) {
                {
                    addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
                }
            });
        }
    }

    int stopid;
    RouteURL[] routes;
    ArrayList<RouteTime> times = new ArrayList<RouteTime>();
    ArrayList<RouteTime> curr_times = times;
    RouteURL selected_route;

    StopDialog(final Context ctx, final int stopid, final int lat, final int lon) {
        super(ctx);
        this.stopid = stopid;

        // getWindow().requestFeature(Window.FEATURE_LEFT_ICON);
        getWindow().setLayout(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);

        final String name = DB.getStopName(stopid);
        setTitle(name);

        routes = get_time_urls(StopDialog.this.stopid);

        // getWindow().setLayout(LayoutParams.FILL_PARENT,
        // LayoutParams.FILL_PARENT);

        setContentView(new RelativeLayout(ctx) {
            {
                addView(new TextView(ctx) {
                    {
                        setId(stop_num_id);
                        setText(Html.fromHtml(String.format(
                                "[<a href='http://www.cityofmadison.com/metro/BusStopDepartures/StopID/%04d.pdf'>%04d</a>]",
                                stopid, stopid)));
                        setPadding(0, 0, 5, 0);
                        this.setMovementMethod(LinkMovementMethod.getInstance());
                    }
                }, new RelativeLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT) {
                    {
                        addRule(ALIGN_PARENT_RIGHT);
                    }
                });

                addView(new ImageView(ctx) {
                    boolean enabled;

                    @Override
                    public void setEnabled(boolean e) {
                        enabled = e;

                        setImageResource(e ? R.drawable.love_enabled : R.drawable.love_disabled);
                        if (e) {
                            G.favorites.add_favorite_stop(stopid, name, lat, lon);
                            Toast.makeText(ctx, "Added stop to Favorites", Toast.LENGTH_SHORT).show();
                        } else {
                            G.favorites.remove_favorite_stop(stopid);
                            Toast.makeText(ctx, "Removed stop from Favorites", Toast.LENGTH_SHORT).show();
                        }
                    }

                    {
                        enabled = G.favorites.is_stop_favorite(stopid);
                        setImageResource(enabled ? R.drawable.love_enabled : R.drawable.love_disabled);

                        setPadding(0, 0, 10, 0);
                        setOnClickListener(new OnClickListener() {
                            public void onClick(View v) {
                                setEnabled(!enabled);
                            }
                        });
                    }
                }, new RelativeLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT) {
                    {
                        addRule(LEFT_OF, stop_num_id);
                        setMargins(0, -3, 0, 0);
                    }
                });

                addView(cur_loading_text = new TextView(ctx) {
                    {
                        setText("Loading...");
                        setPadding(5, 0, 0, 0);
                    }
                });
                addView(new HorizontalScrollView(ctx) {
                    {
                        setId(route_list_id);
                        setHorizontalScrollBarEnabled(false);

                        addView(new LinearLayout(ctx) {
                            float text_size;
                            Button cur_button;
                            {
                                int last_route = -1;
                                for (int i = 0; i < routes.length; i++) {

                                    final RouteURL route = routes[i];

                                    if (route.route == last_route)
                                        continue;
                                    last_route = route.route;

                                    addView(new Button(ctx) {
                                        public void setEnabled(boolean e) {
                                            if (e) {
                                                setBackgroundColor(0xff000000 | G.route_points[route.route].color);
                                                setTextSize(TypedValue.COMPLEX_UNIT_PX, text_size * 1.5f);
                                            } else {
                                                setBackgroundColor(0x90000000 | G.route_points[route.route].color);
                                                setTextSize(TypedValue.COMPLEX_UNIT_PX, text_size);
                                            }
                                        }

                                        {
                                            setText(G.route_points[route.route].name);
                                            setTextColor(0xffffffff);
                                            setTypeface(Typeface.DEFAULT_BOLD);
                                            text_size = getTextSize();

                                            if (G.active_route == route.route) {
                                                setEnabled(true);
                                                cur_button = this;
                                            } else
                                                setEnabled(false);

                                            final Button b = this;

                                            setOnClickListener(new OnClickListener() {
                                                public void onClick(View v) {
                                                    if (cur_button != null) {
                                                        cur_button.setEnabled(false);
                                                    }

                                                    if (cur_button == b) {
                                                        cur_button.setEnabled(false);
                                                        cur_button = null;

                                                        selected_route = null;
                                                        update_time_display();
                                                    } else {
                                                        cur_button = b;
                                                        cur_button.setEnabled(true);

                                                        selected_route = route;
                                                        update_time_display();
                                                    }
                                                }
                                            });

                                        }
                                    });
                                }
                            }
                        });
                    }
                }, new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT) {
                    {
                        addRule(RelativeLayout.BELOW, stop_num_id);
                        addRule(RelativeLayout.CENTER_HORIZONTAL);
                    }
                });

                addView(status_text = new TextView(ctx) {
                    {
                        setText("");
                    }
                }, new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT) {
                    {
                        addRule(RelativeLayout.BELOW, route_list_id);
                        addRule(RelativeLayout.CENTER_HORIZONTAL);
                    }
                });
                addView(list_view = new ListView(ctx) {
                    {
                        setId(time_list_id);
                        setVerticalScrollBarEnabled(false);
                        setAdapter(times_adapter = new BaseAdapter() {

                            public View getView(final int position, View convertView, ViewGroup parent) {
                                CellView v;

                                if (convertView == null)
                                    v = new CellView(ctx);
                                else
                                    v = (CellView) convertView;

                                RouteTime rt = curr_times.get(position);
                                v.setBackgroundColor(G.route_points[rt.route].color | 0xff000000);
                                v.route_textview.setText(G.route_points[rt.route].name);
                                if (rt.dir != null)
                                    v.dir_textview.setText("to " + rt.dir);
                                v.time_textview.setText(rt.time);

                                return v;

                            }

                            public int getCount() {
                                return curr_times.size();
                            }

                            public Object getItem(int position) {
                                return null;
                            }

                            public long getItemId(int position) {
                                return 0;
                            }

                        });
                    }
                }, new RelativeLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT) {
                    {
                        addRule(RelativeLayout.BELOW, route_list_id);
                    }
                });
            }
        });

        TextView title = (TextView) findViewById(android.R.id.title);
        title.setEllipsize(TextUtils.TruncateAt.MARQUEE);
        title.setSelected(true);
        title.setTextColor(0xffffffff);
        title.setMarqueeRepeatLimit(-1);

        // getWindow().set, value)
        // getWindow().setFeatureDrawableResource(Window.FEATURE_LEFT_ICON,
        // android.R.drawable.ic_dialog_info);

        // title.setGravity(Gravity.CENTER_HORIZONTAL|Gravity.CENTER_VERTICAL);

        if (G.active_route >= 0)
            for (int i = 0; i < routes.length; i++)
                if (routes[i].route == G.active_route) {
                    selected_route = routes[i];

                    RouteURL[] rnew = new RouteURL[routes.length];
                    rnew[0] = selected_route;

                    for (int j = 0, k = 1; j < routes.length; j++)
                        if (j != i)
                            rnew[k++] = routes[j];

                    routes = rnew;
                    break;
                }
        update_time_display();
    }

    @Override
    public void show() {
        new Thread() {
            @Override
            public void run() {

                for (final RouteURL r : routes) {
                    G.activity.runOnUiThread(new Runnable() {
                        public void run() {
                            cur_loading_text
                                    .setText(String.format("Loading route %s...", G.route_points[r.route].name));
                        }
                    });

                    final ArrayList<RouteTime> curtimes = new ArrayList<RouteTime>();
                    try {
                        System.err.printf("BusRadar URL %s\n", TRANSITTRACKER_URL + r.url);
                        URL url = new URL(TRANSITTRACKER_URL + r.url);
                        URLConnection url_conn = url.openConnection();
                        if (url_conn instanceof HttpsURLConnection) {
                            ((HttpsURLConnection) url_conn).setHostnameVerifier(new HostnameVerifier() {

                                public boolean verify(String hostname, SSLSession session) {
                                    return true;
                                }
                            });
                        }
                        InputStream is = url_conn.getInputStream();
                        Scanner scan = new Scanner(is, "UTF-8");

                        //String outstr_cur = "Route " + r.route + "\n";

                        if (scan.findWithinHorizon(num_vehicles_re, 0) != null) {

                            while (scan.findWithinHorizon(time_re, 0) != null) {
                                RouteTime time = new RouteTime();
                                time.route = r.route;
                                time.time = scan.match().group(1).replace(".", "");
                                time.dir = scan.match().group(2);
                                //time.date = DateFormat.getTimeInstance(DateFormat.SHORT).parse(time.time);

                                SimpleDateFormat f = new SimpleDateFormat("h:mm aa", Locale.US);
                                time.date = f.parse(time.time);
                                r.status = RouteURL.DONE;

                                //outstr_cur += String.format("%s to %s\n", time.time, time.dir);
                                curtimes.add(time);
                            }

                            while (scan.findWithinHorizon(time_re_backup, 0) != null) {
                                RouteTime time = new RouteTime();
                                time.route = r.route;
                                time.time = scan.match().group(1).replace(".", "");
                                //time.dir = scan.match().group(2);
                                //time.date = DateFormat.getTimeInstance(DateFormat.SHORT).parse(time.time);

                                SimpleDateFormat f = new SimpleDateFormat("h:mm aa", Locale.US);
                                time.date = f.parse(time.time);
                                r.status = RouteURL.DONE;

                                //outstr_cur += String.format("%s to %s\n", time.time, time.dir);
                                curtimes.add(time);
                            }

                        }

                        //                  else if (scan.findWithinHorizon(no_busses_re, 0) != null) {
                        //                     r.status = RouteURL.NO_MORE_TODAY; 
                        //                  } 
                        //                  else if (scan.findWithinHorizon(no_timepoints_re, 0) != null) {
                        //                     r.status = RouteURL.NO_TIMEPOINTS;
                        //                  }
                        //                  else {
                        //                     r.status = RouteURL.ERROR;
                        //                     System.out.printf("BusRadar: Could not get stop info for %s\n", r.url);
                        //                     
                        //                     throw new Exception("Error parsing TransitTracker webpage.");
                        //                  }
                        else {
                            r.status = RouteURL.NO_STOPS_UNKONWN;
                        }

                        //r.text = outstr_cur;

                        G.activity.runOnUiThread(new Runnable() {
                            public void run() {
                                times.addAll(curtimes);
                                StopDialog.this.update_times();
                            }
                        });

                    }
                    //               catch  (final IOException ioe) {
                    //                  log_problem(ioe);
                    //                  G.activity.runOnUiThread(new Runnable() {
                    //                     public void run() {
                    //                        final Context ctx = StopDialog.this.getContext();
                    //                        
                    //                        StopDialog.this.setContentView(new RelativeLayout(ctx) {{
                    //                           addView(new TextView(ctx) {{
                    //                              setText(Html.fromHtml("Error downloading data. Is the data connection enabled?"+
                    //                                                  "<p>Report problems to <a href='mailto:support@busradarapp.com'>support@busradarapp.com</a><p>"+ioe));
                    //                              setPadding(5, 5, 5, 5);
                    //                              this.setMovementMethod(LinkMovementMethod.getInstance());
                    //                           }}, new RelativeLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
                    //                        }});
                    //                     }
                    //                  });                  
                    //                  return;
                    //               }
                    catch (Exception e) {
                        log_problem(e);

                        String custom_msg = "";
                        final String turl = TRANSITTRACKER_URL + r.url;
                        if ((e instanceof SocketException) || (e instanceof UnknownHostException)) {
                            // data connection doesn't work
                            custom_msg = "Error downloading data. Is the data connection enabled?"
                                    + "<p>Report problems to <a href='mailto:support@busradarapp.com'>support@busradarapp.com</a><p>"
                                    + TextUtils.htmlEncode(e.toString());
                        } else {

                            String rurl = String.format(
                                    "http://www.cityofmadison.com/metro/BusStopDepartures/StopID/%04d.pdf", stopid);
                            custom_msg = "Trouble retrieving real-time arrival estimates from <a href='" + turl
                                    + "'>this</a> TransitTracker webpage, which is displayed below. "
                                    + "Meanwhile, try PDF timetable <a href='" + rurl + "'>here</a>. "
                                    + "Contact us at <a href='mailto:support@busradarapp.com'>support@busradarapp.com</a> to report the problem.<p>"
                                    + TextUtils.htmlEncode(e.toString());
                        }

                        final String msg = custom_msg;

                        G.activity.runOnUiThread(new Runnable() {
                            public void run() {
                                final Context ctx = StopDialog.this.getContext();

                                StopDialog.this.setContentView(new RelativeLayout(ctx) {
                                    {
                                        addView(new TextView(ctx) {
                                            {
                                                setId(1);
                                                setText(Html.fromHtml(msg));
                                                setPadding(5, 5, 5, 5);
                                                this.setMovementMethod(LinkMovementMethod.getInstance());
                                            }
                                        }, new RelativeLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));

                                        addView(new WebView(ctx) {
                                            {
                                                setWebViewClient(new WebViewClient());
                                                loadUrl(turl);
                                            }
                                        }, new RelativeLayout.LayoutParams(LayoutParams.FILL_PARENT,
                                                LayoutParams.FILL_PARENT) {
                                            {
                                                addRule(RelativeLayout.BELOW, 1);
                                            }
                                        });
                                    }
                                });
                            }
                        });
                        return;
                    }

                }

                G.activity.runOnUiThread(new Runnable() {
                    public void run() {
                        cur_loading_text.setText("");
                    }
                });
            }
        }.start();

        super.show();

    }

    void update_times() {
        Collections.sort(times);
        update_time_display();
    }

    void update_time_display() {
        if (selected_route != null) {
            switch (selected_route.status) {
            case RouteURL.DONE:
                status_text.setText("");
                break;
            case RouteURL.LOADING:
                status_text.setText("Loading...");
                break;
            case RouteURL.NO_MORE_TODAY:
                status_text.setText("No more stops today.");
                break;
            case RouteURL.NO_TIMEPOINTS:
                status_text.setText("Does not run today.");
                break;
            case RouteURL.NO_STOPS_UNKONWN:
                status_text.setText("No stops to show.");
                break;
            case RouteURL.ERROR:
                status_text.setText("Unkown error.");
                break;
            default:
                throw new Error();
            }

            ArrayList<RouteTime> list = new ArrayList<RouteTime>();
            for (RouteTime t : times) {
                if (t.route == selected_route.route)
                    list.add(t);
            }
            curr_times = list;
        } else {
            curr_times = times;
            status_text.setText("");
        }

        times_adapter.notifyDataSetChanged();

    }

    static RouteURL[] get_time_urls(int stopid) {
        Cursor c = G.db.rawQuery("SELECT url, route " + "FROM routestops " + "WHERE stopid = ? " + "ORDER BY route",
                new String[] { stopid + "" });

        RouteURL[] list = new RouteURL[c.getCount()];
        //ArrayList<RouteURL> list = new ArrayLst<RouteURL>();
        //int last = -1;
        int i = 0;
        while (c.moveToNext()) {
            RouteURL r = new RouteURL();
            r.url = c.getString(0);
            r.route = c.getInt(1);
            list[i++] = r;

        }

        c.close();
        return list;
    }
}