Java tutorial
/* BusTO - Arrival times for Turin public transports. Copyright (C) 2014 Valerio Bozzolan 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 it.reyboz.bustorino; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.database.sqlite.SQLiteDatabase; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.NavUtils; import android.support.v4.widget.SwipeRefreshLayout; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import android.widget.AdapterView; import android.widget.Button; import android.widget.EditText; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; import com.google.zxing.integration.android.IntentIntegrator; import com.google.zxing.integration.android.IntentResult; import com.melnykov.fab.FloatingActionButton; import java.util.List; import java.util.concurrent.atomic.AtomicReference; import it.reyboz.bustorino.backend.ArrivalsFetcher; import it.reyboz.bustorino.backend.Fetcher; import it.reyboz.bustorino.backend.FiveTNormalizer; import it.reyboz.bustorino.backend.FiveTScraperFetcher; import it.reyboz.bustorino.backend.FiveTStopsFetcher; import it.reyboz.bustorino.backend.GTTJSONFetcher; import it.reyboz.bustorino.backend.GTTStopsFetcher; import it.reyboz.bustorino.backend.Palina; import it.reyboz.bustorino.backend.Route; import it.reyboz.bustorino.backend.Stop; import it.reyboz.bustorino.backend.StopsFinderByName; import it.reyboz.bustorino.middleware.UserDB; import it.reyboz.bustorino.middleware.AsyncWget; import it.reyboz.bustorino.middleware.PalinaAdapter; import it.reyboz.bustorino.middleware.RecursionHelper; import it.reyboz.bustorino.middleware.StopAdapter; import it.reyboz.bustorino.middleware.StopsDB; public class ActivityMain extends AppCompatActivity { /* * Layout elements */ private EditText busStopSearchByIDEditText; private EditText busStopSearchByNameEditText; private TextView busStopNameTextView; private ProgressBar progressBar; private TextView howDoesItWorkTextView; private Button hideHintButton; private MenuItem actionHelpMenuItem; private SwipeRefreshLayout swipeRefreshLayout; private ListView resultsListView; private FloatingActionButton floatingActionButton; /* * Serach mode */ private static final int SEARCH_BY_NAME = 0; private static final int SEARCH_BY_ID = 1; private static final int SEARCH_BY_ROUTE = 2; // TODO: implement this (bug #1512948) private int searchMode; /* * Options */ private final String OPTION_SHOW_LEGEND = "show_legend"; /** * Last successfully searched bus stop ID */ private @Nullable Stop lastSuccessfullySearchedBusStop = null; /* // useful for testing: public class MockFetcher implements ArrivalsFetcher { @Override public Palina ReadArrivalTimesAll(String routeID, AtomicReference<result> res) { SystemClock.sleep(5000); res.set(result.SERVER_ERROR); return new Palina(); } } private ArrivalsFetcher[] ArrivalFetchers = {new MockFetcher(), new MockFetcher(), new MockFetcher(), new MockFetcher(), new MockFetcher()};*/ private RecursionHelper<ArrivalsFetcher> ArrivalFetchersRecursionHelper = new RecursionHelper<>( new ArrivalsFetcher[] { new GTTJSONFetcher(), new FiveTScraperFetcher() }); private RecursionHelper<StopsFinderByName> StopsFindersByNameRecursionHelper = new RecursionHelper<>( new StopsFinderByName[] { new GTTStopsFetcher(), new FiveTStopsFetcher() }); private StopsDB stopsDB; private UserDB userDB; ///////////////////////////////// EVENT HANDLERS /////////////////////////////////////////////// /* * @see swipeRefreshLayout */ private Handler handler = new Handler(); private final Runnable refreshing = new Runnable() { public void run() { if (lastSuccessfullySearchedBusStop == null) { Toast.makeText(getApplicationContext(), R.string.query_too_short, Toast.LENGTH_SHORT).show(); } else { new asyncWgetBusStopFromBusStopID(lastSuccessfullySearchedBusStop.ID, ArrivalFetchersRecursionHelper, lastSuccessfullySearchedBusStop); } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.stopsDB = new StopsDB(getApplicationContext()); this.userDB = new UserDB(getApplicationContext()); setContentView(R.layout.activity_main); busStopSearchByIDEditText = (EditText) findViewById(R.id.busStopSearchByIDEditText); busStopSearchByNameEditText = (EditText) findViewById(R.id.busStopSearchByNameEditText); busStopNameTextView = (TextView) findViewById(R.id.busStopNameTextView); progressBar = (ProgressBar) findViewById(R.id.progressBar); howDoesItWorkTextView = (TextView) findViewById(R.id.howDoesItWorkTextView); hideHintButton = (Button) findViewById(R.id.hideHintButton); resultsListView = (ListView) findViewById(R.id.resultsListView); swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipeRefreshLayout); floatingActionButton = (FloatingActionButton) findViewById(R.id.floatingActionButton); if (floatingActionButton != null) { floatingActionButton.attachToListView(resultsListView); } busStopSearchByIDEditText.setSelectAllOnFocus(true); busStopSearchByIDEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() { @Override public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { // IME_ACTION_SEARCH alphabetical option if (actionId == EditorInfo.IME_ACTION_SEARCH) { onSearchClick(v); return true; } return false; } }); busStopSearchByNameEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() { @Override public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { // IME_ACTION_SEARCH alphabetical option if (actionId == EditorInfo.IME_ACTION_SEARCH) { onSearchClick(v); return true; } return false; } }); // Called when the layout is pulled down swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { @Override public void onRefresh() { handler.post(refreshing); } }); /** * @author Marco Gagino!!! */ swipeRefreshLayout.setColorSchemeColors(R.color.blue_500, R.color.orange_500); // setColorScheme is deprecated, setColorSchemeColors isn't setSearchModeBusStopID(); //---------------------------- START INTENT CHECK QUEUE ------------------------------------ // Intercept calls from URL intent boolean tryedFromIntent = false; String busStopID = null; String busStopDisplayName = null; Uri data = getIntent().getData(); if (data != null) { busStopID = getBusStopIDFromUri(data); tryedFromIntent = true; } // Intercept calls from other activities if (!tryedFromIntent) { Bundle b = getIntent().getExtras(); if (b != null) { busStopID = b.getString("bus-stop-ID"); busStopDisplayName = b.getString("bus-stop-display-name"); /** * I'm not very sure if you are coming from an Intent. * Some launchers work in strange ways. */ tryedFromIntent = busStopID != null; } } //---------------------------- END INTENT CHECK QUEUE -------------------------------------- if (busStopID == null) { // Show keyboard if can't start from intent showKeyboard(); // You haven't obtained anything... from an intent? if (tryedFromIntent) { // This shows a luser warning ArrivalFetchersRecursionHelper.reset(); Toast.makeText(getApplicationContext(), R.string.insert_bus_stop_number_error, Toast.LENGTH_SHORT) .show(); } } else { // If you are here an intent has worked successfully setBusStopSearchByIDEditText(busStopID); this.lastSuccessfullySearchedBusStop = new Stop(busStopID); // forcing it as user name even though it could be standard name, it doesn't really matter this.lastSuccessfullySearchedBusStop.setStopUserName(busStopDisplayName); new asyncWgetBusStopFromBusStopID(busStopID, ArrivalFetchersRecursionHelper, lastSuccessfullySearchedBusStop); } Log.d("MainActivity", "Created"); } /** * Reload bus stop timetable when it's fulled resumed from background. */ @Override protected void onPostResume() { super.onPostResume(); Log.d("ActivityMain", "onPostResume fired. Last successfully bus stop ID: " + lastSuccessfullySearchedBusStop); if (searchMode == SEARCH_BY_ID && lastSuccessfullySearchedBusStop != null) { setBusStopSearchByIDEditText(lastSuccessfullySearchedBusStop.ID); new asyncWgetBusStopFromBusStopID(lastSuccessfullySearchedBusStop.ID, ArrivalFetchersRecursionHelper, lastSuccessfullySearchedBusStop); } } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); actionHelpMenuItem = menu.findItem(R.id.action_help); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. switch (item.getItemId()) { case android.R.id.home: // Respond to the action bar's Up/Home button NavUtils.navigateUpFromSameTask(this); return true; case R.id.action_help: showHints(); return true; case R.id.action_favorites: startActivity(new Intent(ActivityMain.this, ActivityFavorites.class)); return true; case R.id.action_about: startActivity(new Intent(ActivityMain.this, ActivityAbout.class)); return true; case R.id.action_news: openIceweasel("http://blog.reyboz.it/tag/busto/"); return true; case R.id.action_bugs: openIceweasel("https://bugs.launchpad.net/bus-torino"); return true; case R.id.action_source: openIceweasel("https://code.launchpad.net/bus-torino"); return true; case R.id.action_licence: openIceweasel("http://www.gnu.org/licenses/gpl-3.0.html"); return true; case R.id.action_author: openIceweasel("http://boz.reyboz.it?lovebusto"); return true; } return super.onOptionsItemSelected(item); } /** * OK this is pure shit * * @param v View clicked */ public void onSearchClick(View v) { if (searchMode == SEARCH_BY_ID) { String busStopID = busStopSearchByIDEditText.getText().toString(); new asyncWgetBusStopFromBusStopID(busStopID, ArrivalFetchersRecursionHelper, lastSuccessfullySearchedBusStop); } else { // searchMode == SEARCH_BY_NAME String query = busStopSearchByNameEditText.getText().toString(); new asyncWgetBusStopSuggestions(query, stopsDB, StopsFindersByNameRecursionHelper); } } /** * QR scan button clicked * * @param v View QRButton clicked */ public void onQRButtonClick(View v) { IntentIntegrator integrator = new IntentIntegrator(this); integrator.initiateScan(); } /** * Receive the Barcode Scanner Intent * */ public void onActivityResult(int requestCode, int resultCode, Intent intent) { IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent); Uri uri; try { uri = Uri.parse(scanResult != null ? scanResult.getContents() : null); // this apparently prevents NullPointerException. Somehow. } catch (NullPointerException e) { Toast.makeText(getApplicationContext(), R.string.no_qrcode, Toast.LENGTH_SHORT).show(); return; } String busStopID = getBusStopIDFromUri(uri); busStopSearchByIDEditText.setText(busStopID); new asyncWgetBusStopFromBusStopID(busStopID, ArrivalFetchersRecursionHelper, lastSuccessfullySearchedBusStop); } public void onHideHint(View v) { hideHints(); setOption(OPTION_SHOW_LEGEND, false); } public void onToggleKeyboardLayout(View v) { if (searchMode == SEARCH_BY_NAME) { setSearchModeBusStopID(); if (busStopSearchByIDEditText.requestFocus()) { showKeyboard(); } } else { // searchMode == SEARCH_BY_ID setSearchModeBusStopName(); if (busStopSearchByNameEditText.requestFocus()) { showKeyboard(); } } } ///////////////////////////////// STOPS FINDER BY NAME ///////////////////////////////////////// private class asyncWgetBusStopSuggestions extends AsyncWget<StopsFinderByName> { private final String query; private AtomicReference<Fetcher.result> res; private List<Stop> s; private StopsDB db; public asyncWgetBusStopSuggestions(@Nullable String query, @NonNull StopsDB sdb, @NonNull RecursionHelper<StopsFinderByName> r) { super(getApplicationContext(), r); toggleSpinner(true); this.query = query; this.res = new AtomicReference<>(); this.db = sdb; r.reset(); if (query == null || query.length() <= 0) { Toast.makeText(this.c, R.string.insert_bus_stop_name_error, Toast.LENGTH_SHORT).show(); toggleSpinner(false); } else { this.execute(); } } @Override protected boolean tryFetcher(StopsFinderByName f) { // gets opened multiple times, whatever. this.db.openIfNeeded(); this.s = f.FindByName(this.query, this.db, this.res); this.db.closeIfNeeded(); switch (this.res.get()) { case CLIENT_OFFLINE: publishProgress(R.string.network_error); return false; case SERVER_ERROR: if (isConnected()) { publishProgress(R.string.parsing_error); } else { publishProgress(R.string.network_error); } return false; case PARSER_ERROR: default: publishProgress(R.string.internal_error); return false; case QUERY_TOO_SHORT: publishProgress(R.string.query_too_short); return false; case EMPTY_RESULT_SET: publishProgress(R.string.no_bus_stop_have_this_name); return false; case OK: return true; } } @Override protected void onPostExecute(Void useless) { // something really bad happened if (this.isCancelled() || !this.terminated || this.s == null) { toggleSpinner(false); this.onCancelled(); return; } // no results if (this.failedAll) { toggleSpinner(false); // TODO: an error message would be a good idea, here return; } hideKeyboard(); busStopNameTextView.setVisibility(View.GONE); prepareGUIForBusStops(); resultsListView.setAdapter(new StopAdapter(this.c, this.s)); resultsListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { /** * Casting because of Javamerda * @url http://stackoverflow.com/questions/30549485/androids-list-view-parameterized-type-in-adapterview-onitemclicklistener */ Stop busStop = (Stop) parent.getItemAtPosition(position); Intent intent = new Intent(ActivityMain.this, ActivityMain.class); Bundle b = new Bundle(); b.putString("bus-stop-ID", busStop.ID); intent.putExtras(b); startActivity(intent); } }); toggleSpinner(false); } @Override protected void onCancelled() { toggleSpinner(false); } } ///////////////////////////////// ARRIVALS FETCHER ///////////////////////////////////////////// private class asyncWgetBusStopFromBusStopID extends AsyncWget<ArrivalsFetcher> { private final String stopID; private AtomicReference<Fetcher.result> res; private Palina p; /** * Begins its life as a copy of lastSuccessfullySearchedBusStop, ends as null if it needs * replacing or stays the same if it stayed the same */ private Stop lastSearchedBusStop; asyncWgetBusStopFromBusStopID(@Nullable String stopID, @NonNull RecursionHelper<ArrivalsFetcher> r, @Nullable Stop lastSearchedBusStop) { super(getApplicationContext(), r); toggleSpinner(true); this.stopID = stopID; this.lastSearchedBusStop = lastSearchedBusStop; this.res = new AtomicReference<>(); if (stopID == null || stopID.length() <= 0) { // we're still in UI thread, no need to mess with Progress Toast.makeText(this.c, R.string.insert_bus_stop_number_error, Toast.LENGTH_SHORT).show(); toggleSpinner(false); } else { this.execute(); } } @Override protected boolean tryFetcher(ArrivalsFetcher f) { this.p = f.ReadArrivalTimesAll(this.stopID, this.res); switch (this.res.get()) { case CLIENT_OFFLINE: publishProgress(R.string.network_error); return false; case SERVER_ERROR: if (isConnected()) { publishProgress(R.string.parsing_error); } else { publishProgress(R.string.network_error); } return false; case QUERY_TOO_SHORT: // should never happen here, but to err on the side of caution... publishProgress(R.string.query_too_short); return false; case PARSER_ERROR: default: // there are no other cases but Android Studio is still complaining... publishProgress(R.string.internal_error); return false; case EMPTY_RESULT_SET: publishProgress(R.string.no_passages); return false; case OK: // WARNING: lots of branches ahead. // did we search for anything? if (this.lastSearchedBusStop != null) { // different stop? if (!this.lastSearchedBusStop.ID.equals(p.ID)) { // remove it, get new name this.lastSearchedBusStop = null; getNameOrGetRekt(); } else { // searched and it's the same String sn = lastSearchedBusStop.getStopDisplayName(); if (sn == null) { // something really bad happened, start from scratch this.lastSearchedBusStop = null; getNameOrGetRekt(); } else { // "merge" Stop over Palina and we're good to go this.p.mergeNameFrom(lastSearchedBusStop); } } } else { // not searched yet getNameOrGetRekt(); } return true; } } /** * Run this in a background thread.<br> * Sets a stop name for this.p, guaranteed not to be null! */ private void getNameOrGetRekt() { String nameMaybe; SQLiteDatabase udb = userDB.getReadableDatabase(); // does it already have a name (for fetchers that support it, or already got from favorites)? nameMaybe = this.p.getStopDisplayName(); if (nameMaybe != null && nameMaybe.length() > 0) { return; } // ok, let's search favorites. String usernameMaybe = UserDB.getStopUserName(udb, this.p.ID); if (usernameMaybe != null && usernameMaybe.length() > 0) { this.p.setStopUserName(usernameMaybe); return; } // let's try StopsDB, then. StopsDB db = new StopsDB(this.c); db.openIfNeeded(); nameMaybe = db.getNameFromID(this.p.ID); db.closeIfNeeded(); if (nameMaybe != null && nameMaybe.length() > 0) { this.p.setStopName(nameMaybe); return; } // no name to be found anywhere, don't bother searching it next time this.p.setStopName(""); } @Override protected void onPostExecute(Void useless) { // something really bad happened if (this.isCancelled() || !this.terminated || this.p == null) { toggleSpinner(false); this.onCancelled(); return; } // no results if (this.failedAll) { toggleSpinner(false); // TODO: an error message would be a good idea, here return; } hideKeyboard(); if (this.lastSearchedBusStop == null) { // did we gather any new information about this Stop? Then update it (casting from Palina) lastSuccessfullySearchedBusStop = this.p; } String displayName = p.getStopDisplayName(); String displayStuff; if (displayName != null && displayName.length() > 0) { displayStuff = p.ID.concat(" - ").concat(displayName); } else { displayStuff = p.ID; } busStopNameTextView.setText(String.format(getString(R.string.passages), displayStuff)); // Shows hints if (getOption(OPTION_SHOW_LEGEND, true)) { showHints(); } //else { // hideHints(); //} prepareGUIForBusLines(); resultsListView.setAdapter(new PalinaAdapter(this.c, p)); resultsListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { String routeName; Route r = (Route) parent.getItemAtPosition(position); routeName = FiveTNormalizer.routeInternalToDisplay(r.name); if (routeName == null) { routeName = r.name; } if (r.destinazione == null || r.destinazione.length() == 0) { Toast.makeText(getApplicationContext(), getString(R.string.route_towards_unknown, routeName), Toast.LENGTH_SHORT).show(); } else { Toast.makeText(getApplicationContext(), getString(R.string.route_towards_destination, routeName, r.destinazione), Toast.LENGTH_SHORT).show(); } } }); toggleSpinner(false); } @Override protected void onCancelled() { toggleSpinner(false); } } ///////////////////////////////// OTHER STUFF ////////////////////////////////////////////////// private boolean isConnected() { ConnectivityManager connMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo networkInfo = connMgr.getActiveNetworkInfo(); return networkInfo != null && networkInfo.isConnected(); } public void addToFavorites(View v) { if (lastSuccessfullySearchedBusStop != null) { new AsyncAddToFavorites(this).execute(); } } private class AsyncAddToFavorites extends AsyncTask<Void, Void, Boolean> { Context c; public AsyncAddToFavorites(Context c) { this.c = c; } @Override protected Boolean doInBackground(Void... voids) { SQLiteDatabase db = userDB.getWritableDatabase(); Boolean result = UserDB.addOrUpdateStop(lastSuccessfullySearchedBusStop, db); db.close(); return result; } @Override protected void onPostExecute(Boolean result) { super.onPostExecute(result); if (result) { Toast.makeText(this.c, R.string.added_in_favorites, Toast.LENGTH_SHORT).show(); } else { Toast.makeText(this.c, R.string.cant_add_to_favorites, Toast.LENGTH_SHORT).show(); } } } // TODO: try to move these 2 methods to a separate class private void setOption(String optionName, boolean value) { SharedPreferences.Editor editor = getPreferences(MODE_PRIVATE).edit(); editor.putBoolean(optionName, value); editor.commit(); } private boolean getOption(String optionName, boolean optDefault) { SharedPreferences preferences = getPreferences(MODE_PRIVATE); return preferences.getBoolean(optionName, optDefault); } ////////////////////////////////////// GUI HELPERS ///////////////////////////////////////////// private void showKeyboard() { InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); View view = searchMode == SEARCH_BY_ID ? busStopSearchByIDEditText : busStopSearchByNameEditText; imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT); } private void hideKeyboard() { View view = getCurrentFocus(); if (view != null) { ((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE)) .hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); } } private void setSearchModeBusStopID() { searchMode = SEARCH_BY_ID; busStopSearchByNameEditText.setVisibility(View.GONE); busStopSearchByNameEditText.setText(""); busStopSearchByIDEditText.setVisibility(View.VISIBLE); floatingActionButton.setImageResource(R.drawable.alphabetical); } private void setSearchModeBusStopName() { searchMode = SEARCH_BY_NAME; busStopSearchByIDEditText.setVisibility(View.GONE); busStopSearchByIDEditText.setText(""); busStopSearchByNameEditText.setVisibility(View.VISIBLE); floatingActionButton.setImageResource(R.drawable.numeric); } /** * Having that cursor at the left of the edit text makes me cancer. * @param busStopID bus stop ID */ private void setBusStopSearchByIDEditText(String busStopID) { busStopSearchByIDEditText.setText(busStopID); busStopSearchByIDEditText.setSelection(busStopID.length()); } private void showHints() { howDoesItWorkTextView.setVisibility(View.VISIBLE); hideHintButton.setVisibility(View.VISIBLE); actionHelpMenuItem.setVisible(false); } private void hideHints() { howDoesItWorkTextView.setVisibility(View.GONE); hideHintButton.setVisibility(View.GONE); actionHelpMenuItem.setVisible(true); } private void toggleSpinner(boolean enable) { if (enable) { //already set by the RefreshListener when needed //swipeRefreshLayout.setRefreshing(true); progressBar.setVisibility(View.VISIBLE); } else { swipeRefreshLayout.setRefreshing(false); progressBar.setVisibility(View.GONE); } } private void prepareGUIForBusLines() { busStopNameTextView.setVisibility(View.VISIBLE); busStopNameTextView.setClickable(true); swipeRefreshLayout.setEnabled(true); swipeRefreshLayout.setVisibility(View.VISIBLE); resultsListView.setVisibility(View.VISIBLE); actionHelpMenuItem.setVisible(true); } private void prepareGUIForBusStops() { busStopNameTextView.setText(getString(R.string.results)); busStopNameTextView.setClickable(false); busStopNameTextView.setVisibility(View.VISIBLE); swipeRefreshLayout.setEnabled(false); swipeRefreshLayout.setVisibility(View.VISIBLE); resultsListView.setVisibility(View.VISIBLE); actionHelpMenuItem.setVisible(false); } /** * Open an URL in the default browser. * * @param url URL */ public void openIceweasel(String url) { Intent browserIntent1 = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); startActivity(browserIntent1); } ///////////////////// INTENT HELPER //////////////////////////////////////////////////////////// /** * Try to extract the bus stop ID from a URi * * @param uri The URL * @return bus stop ID or null */ public static String getBusStopIDFromUri(Uri uri) { String busStopID; // everithing catches fire when passing null to a switch. String host = uri.getHost(); if (host == null) { Log.e("ActivityMain", "Not an URL: " + uri); return null; } switch (host) { case "m.gtt.to.it": // http://m.gtt.to.it/m/it/arrivi.jsp?n=1254 busStopID = uri.getQueryParameter("n"); if (busStopID == null) { Log.e("ActivityMain", "Expected ?n from: " + uri); } break; case "www.gtt.to.it": case "gtt.to.it": // http://www.gtt.to.it/cms/percorari/arrivi?palina=1254 busStopID = uri.getQueryParameter("palina"); if (busStopID == null) { Log.e("ActivityMain", "Expected ?palina from: " + uri); } break; default: Log.e("ActivityMain", "Unexpected intent URL: " + uri); busStopID = null; } return busStopID; } }