/** * Copyright (C) 2009 Anders Aagaard <aagaande@gmail.com> * * This program 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. * * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ package com.neuron.trafikanten.views.realtime; import java.util.ArrayList; import java.util.TimeZone; import android.content.SharedPreferences; import android.content.res.Resources; import android.os.Bundle; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; import com.google.android.AnalyticsUtils; import com.neuron.trafikanten.HelperFunctions; import com.neuron.trafikanten.R; import com.neuron.trafikanten.dataProviders.IGenericProviderHandler; import com.neuron.trafikanten.dataProviders.trafikanten.TrafikantenDevi; import com.neuron.trafikanten.dataProviders.trafikanten.TrafikantenRealtime; import com.neuron.trafikanten.dataSets.DeviData; import com.neuron.trafikanten.dataSets.RealtimeData; import com.neuron.trafikanten.dataSets.StationData; import com.neuron.trafikanten.dataSets.realtime.GenericRealtimeList; import com.neuron.trafikanten.tasks.ShowTipsTask; import com.neuron.trafikanten.views.GenericDeviCreator; public class RealtimeView extends GenericRealtimeView { private static final String TAG = "Trafikanten-RealtimeView"; public static final String SETTING_HIDECA = "realtime_hideCaText"; private static final String KEY_FINISHEDLOADING = "finishedLoading"; /* * Options menu: */ private static final int HIDECA_ID = Menu.FIRST + 100; /* * Saved instance data */ // HACK STATIONICONS, this does not need to be public static public static StationData station; private boolean finishedLoading = false; // we're finishedLoading when devi has loaded successfully. /* * UI */ private LinearLayout devi; private TextView infoText; private TextView caText; /* * Other */ public SharedPreferences settings; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { //Debug.startMethodTracing("trafikanten-realtimeview"); super.onCreate(savedInstanceState,"/realtime", GenericRealtimeList.RENDERER_PLATFORM); /* * Setup view and adapter. */ setContentView(R.layout.realtime); devi = (LinearLayout) findViewById(R.id.devi); infoText = (TextView) findViewById(R.id.emptyText); caText = (TextView) findViewById(R.id.caInfoText); settings = getSharedPreferences("trafikanten", MODE_PRIVATE); /* * Load instance state */ if (savedInstanceState == null) { Bundle bundle = getIntent().getExtras(); /* * Most of the time we get a normal StationData.PARCELABLE, but shortcuts sends a simple bundle. */ if (bundle.containsKey(StationData.PARCELABLE)) { station = getIntent().getParcelableExtra(StationData.PARCELABLE); } else { station = StationData.readSimpleBundle(bundle); } load(); } else { station = savedInstanceState.getParcelable(StationData.PARCELABLE); finishedLoading = savedInstanceState.getBoolean(KEY_FINISHEDLOADING); infoText.setVisibility(realtimeList.getCount() > 0 ? View.GONE : View.VISIBLE); if (!finishedLoading) { load(); } } refreshTitle(); refreshDevi(); registerForContextMenu(getListView()); new ShowTipsTask(this, RealtimeView.class.getName(), R.string.tipRealtime, 38); } /* * Refreshes the title */ private void refreshTitle() { long lastUpdateDiff = (System.currentTimeMillis() - lastUpdate) / HelperFunctions.SECOND; if (lastUpdateDiff > 60) { lastUpdateDiff = lastUpdateDiff / 60; setTitle(getString(R.string.app_name) + " - " + station.stopName + " (" + lastUpdateDiff + " min " + getText(R.string.old) + ")"); } else { setTitle(getString(R.string.app_name) + " - " + station.stopName); } } /* * Refreshes station specific devi data. */ private static boolean _searchedTimezones = false; private static TimeZone _timeZone = TimeZone.getTimeZone("CET"); private static TimeZone getTimeZoneCET() { if (_searchedTimezones) { return _timeZone; } if (_timeZone == null) { _timeZone = TimeZone.getTimeZone("Europe/Oslo"); } if (_timeZone == null) { _timeZone = TimeZone.getTimeZone("Europe/Berlin"); } if (_timeZone == null) { _timeZone = TimeZone.getTimeZone("Europe/Amsterdam"); } _searchedTimezones = true; return _timeZone; } private void refreshDevi() { /* * Calculate a time difference it's easier to work with */ long timeDiff = timeDifference / 1000; if (timeDiff < 0) timeDiff = timeDiff * -1; /* * Render devi */ if ((station.devi == null || station.devi.size() == 0) && timeDiff < 60) { /* * Nothing to display */ devi.setVisibility(View.GONE); } else { /* * Render devi information */ devi.setVisibility(View.VISIBLE); devi.removeAllViews(); if (timeDiff >= 90) { DeviData deviData = new DeviData(); deviData.title = (String) getText(R.string.clockDiffTitle); deviData.description = (String) getText(R.string.clockDiffDescription); deviData.body = (String) getText(R.string.clockDiffBodyHead); final TimeZone timeZone = getTimeZoneCET(); TimeZone phoneTimezone = TimeZone.getDefault(); if (timeZone == null || !timeZone.equals(phoneTimezone)) { final Resources resources = getResources(); deviData.title = (String) getText(R.string.clockDiffTitleTimezone); if (timeZone == null) { deviData.body = String.format(resources.getString(R.string.clockDiffDescriptionTimezoneError), phoneTimezone.getID()); } else { deviData.body = String.format(resources.getString(R.string.clockDiffDescriptionTimezone), timeZone.getID(), phoneTimezone.getID()); } } if (timeDifference < 0) { deviData.body = deviData.body + "\n\n" + getText(R.string.clockDiffBodyYourClock) + " " + timeDiff + "s " + getText(R.string.clockDiffBodyBehind); } else { deviData.body = deviData.body + "\n\n" + getText(R.string.clockDiffBodyYourClock) + " " + timeDiff + "s " + getText(R.string.clockDiffBodyAhead); } deviData.validFrom = 0; deviData.validTo = 0; devi.addView(GenericDeviCreator.createDefaultDeviText(this, deviData.title, deviData, true), new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT)); } for (final DeviData deviData : station.devi) { devi.addView(GenericDeviCreator.createDefaultDeviText(this, deviData.title, deviData, true), new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT)); } } } @Override protected void onDestroy() { //Debug.stopMethodTracing(); super.onDestroy(); } @Override protected void clearView() { super.clearView(); finishedLoading = false; station.devi = new ArrayList<DeviData>(); devi.setVisibility(View.GONE); } /* * Load data, variable used to prevent updating data set on every iteration. */ private boolean caVisibilityChecked; @Override protected void load() { super.load(); caVisibilityChecked = settings.getBoolean(RealtimeView.SETTING_HIDECA, false); // if hideca = true we skip the visibility check AnalyticsUtils.getInstance(this).trackEvent("Data", "Realtime", "Data", 0); final TrafikantenRealtime realtimeProvider = createRealtimeProvider(this, station.stationId); realtimeProvider.start(new IGenericProviderHandler<RealtimeData>() { @Override public void onExtra(int what, Object obj) { switch (what) { case TrafikantenRealtime.MSG_TIMEDATA: timeDifference = (Long) obj; break; } } @Override public void onData(RealtimeData realtimeData) { if (!caVisibilityChecked && !realtimeData.realtime) { /* * check "ca info text" visibility */ caVisibilityChecked = true; caText.setVisibility(View.VISIBLE); } realtimeList.addData(realtimeData); } @Override public void onPostExecute(Exception exception) { setProgressBarIndeterminateVisibility(false); clearRealtimeProvider(realtimeProvider); if (exception != null) { Log.w(TAG,"onException " + exception); clearView(); infoText.setVisibility(View.VISIBLE); if (exception.getClass().getSimpleName().equals("ParseException")) { infoText.setText(R.string.trafikantenErrorParse); } else { infoText.setText(R.string.trafikantenErrorOther); } } else { refreshTitle(); /* * Show info text if view is empty */ infoText.setVisibility(realtimeList.getCount() > 0 ? View.GONE : View.VISIBLE); realtimeList.notifyDataSetChanged(); loadDevi(); } } @Override public void onPreExecute() { setProgressBarIndeterminateVisibility(true); } }); } /* * Load devi data */ private void loadDevi() { /* * Create list of lines - first create lineList */ ArrayList<Integer> lineList = new ArrayList<Integer>(); { final int count = realtimeList.getCount(); for (int i = 0; i < count; i++) { final RealtimeData realtimeData = realtimeList.getRealtimeItem(i); if (realtimeData != null) { if (!lineList.contains(realtimeData.lineId)) { lineList.add(realtimeData.lineId); } } } } /* * Create list of lines - then merge it into a comma seperated list */ StringBuffer deviLines = new StringBuffer(); { final int count = lineList.size(); for (int i = 0; i < count; i++) { if (i > 0) { deviLines.append(","); } deviLines.append(lineList.get(i)); } } AnalyticsUtils.getInstance(this).trackEvent("Data", "Realtime", "Devi", 0); deviProvider = new TrafikantenDevi(this, station.stationId, deviLines.toString(), new IGenericProviderHandler<DeviData>() { @Override public void onExtra(int what, Object obj) { /* Class has no extra data */ } @Override public void onData(DeviData deviData) { if (deviData.stops.contains(station.stationId)) { /* * Station specific data */ station.devi.add(deviData); } /* * Line data (will be ignored if line isn't shown in view, so no point in checking data.lines) */ realtimeList.addData(deviData); } @Override public void onPostExecute(Exception exception) { setProgressBarIndeterminateVisibility(false); finishedLoading = true; deviProvider = null; //Debug.startMethodTracing("trafikanten-realtimeview-scrolling"); if (exception != null) { Log.w(TAG,"onException " + exception); if (exception.getClass().getSimpleName().equals("ParseException")) { Toast.makeText(RealtimeView.this, R.string.trafikantenErrorParse, Toast.LENGTH_LONG).show(); } else { Toast.makeText(RealtimeView.this, R.string.trafikantenErrorOther, Toast.LENGTH_LONG).show(); } } else { refreshDevi(); realtimeList.notifyDataSetChanged(); } //Debug.startMethodTracing("scrolling"); } @Override public void onPreExecute() { setProgressBarIndeterminateVisibility(true); } }); } /** * menu code */ /* * Options menu, visible on menu button. * @see android.app.Activity#onCreateOptionsMenu(android.view.Menu) */ @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); final MenuItem showHideCaText = menu.add(0, HIDECA_ID, 0, R.string.changeCaTextVisibility); showHideCaText.setIcon(android.R.drawable.ic_menu_info_details); return true; } /* * Options menu item selected, options menu visible on menu button. * @see android.app.Activity#onOptionsItemSelected(android.view.MenuItem) */ @Override public boolean onOptionsItemSelected(MenuItem item) { switch(item.getItemId()) { case HIDECA_ID: boolean hideCaText = !(caText.getVisibility() == View.GONE); SharedPreferences.Editor editor = settings.edit(); if (hideCaText) { caText.setVisibility(View.GONE); editor.putBoolean(SETTING_HIDECA, true); } else { caText.setVisibility(View.VISIBLE); editor.putBoolean(SETTING_HIDECA, false); } editor.commit(); return true; } return super.onOptionsItemSelected(item); } @Override public void onOptionsMenuRefresh() { AnalyticsUtils.getInstance(this).trackEvent("Navigation", "Realtime", "Refresh", 0); load(); } @Override protected void refresh() { super.refresh(); refreshTitle(); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putParcelable(StationData.PARCELABLE, station); outState.putBoolean(KEY_FINISHEDLOADING, finishedLoading); } @Override public StationData getStation(int pos) { return station; } }