// Copyright 2009 Google Inc. // Copyright 2011 NPR // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package org.npr.android.news; import java.util.HashMap; import java.util.Map; import org.npr.android.util.DisplayUtils; import org.npr.android.util.FavoriteStationEntry; import org.npr.android.util.FavoriteStationsProvider; import org.npr.android.util.FavoriteStationsRepository; import org.npr.android.util.StationCache; import org.npr.api.ApiConstants; import org.npr.api.Station; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.database.Cursor; import android.graphics.Color; import android.location.Location; import android.location.LocationManager; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.util.Log; import android.view.KeyEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.view.inputmethod.InputMethodManager; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.ImageButton; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; public class StationListActivity extends TitleActivity implements OnItemClickListener, AdapterView.OnItemLongClickListener, OnClickListener, View.OnKeyListener { private static final String LOG_TAG = StationListActivity.class.getName(); private EditText searchTermField; private InputMethodManager inputMethodManager; public enum Mode { allStations, favoriteStations, locateStations, liveStreams, nearestStations } private Mode mode = Mode.allStations; private String query; private FavoriteStationsRepository favoriteStationsRepository; private int selectedPosition; private final String presets[] = new String[]{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "None"}; @Override public void onLowMemory() { super.onLowMemory(); listAdapter.clear(); StationCache.clear(); } private final Handler handler = new Handler() { @Override public void handleMessage(Message msg) { if (msg.what == 0) { listAdapter.showData(); TextView emptyText = (TextView) findViewById(R.id.Empty); if (listAdapter.getCount() == 0) { emptyText.setText(getEmptyText()); emptyText.setVisibility(View.VISIBLE); } else { emptyText.setVisibility(View.GONE); } } else { Toast.makeText(StationListActivity.this, getResources().getText(R.string.msg_check_connection), Toast.LENGTH_LONG) .show(); } stopIndeterminateProgressIndicator(); } }; private StationListAdapter listAdapter; private Thread listInitThread; private void initializeList() { startIndeterminateProgressIndicator(); listInitThread.start(); } @Override protected void onCreate(Bundle savedInstanceState) { String queryUrl = parseIntent(getIntent()); super.onCreate(savedInstanceState); ViewGroup container = (ViewGroup) findViewById(R.id.Content); ViewGroup.inflate(this, R.layout.station_list, container); final ListView lv = (ListView) findViewById(R.id.ListView01); favoriteStationsRepository = new FavoriteStationsRepository(getContentResolver()); listAdapter = new StationListAdapter(this, (mode == Mode.favoriteStations)); lv.setAdapter(listAdapter); lv.setOnItemClickListener(StationListActivity.this); lv.setOnItemLongClickListener(StationListActivity.this); if (mode == Mode.favoriteStations) { TextView presetInstructions = (TextView) findViewById(R.id.preset_instructions); if (favoriteStationsRepository.getItemCount() > 0) { presetInstructions.setVisibility(View.VISIBLE); } else { presetInstructions.setVisibility(View.GONE); } } else { loadFromQuery(queryUrl); } Button locateButton = (Button) findViewById(R.id.StationSearchButton); Button nearestButton = (Button) findViewById(R.id.StationNearestButton); searchTermField = (EditText) findViewById(R.id.StationSearchTerm); searchTermField.setOnKeyListener(this); ImageButton searchButton = (ImageButton) findViewById(R.id.search_go_button); searchButton.setOnClickListener(this); if (mode == Mode.locateStations) { locateButton .setBackgroundResource(R.drawable.button_background_pressed); locateButton.setTextColor(Color.WHITE); nearestButton.setOnClickListener(this); searchTermField.setVisibility(View.VISIBLE); } else { final View buttonBar = findViewById(R.id.ButtonBar); if (mode == Mode.nearestStations) { nearestButton .setBackgroundResource(R.drawable.button_background_pressed); nearestButton.setTextColor(Color.WHITE); locateButton.setOnClickListener(this); searchTermField.setVisibility(View.GONE); buttonBar.getLayoutParams().height = DisplayUtils.convertToDIP(this, 50); } else { buttonBar.setVisibility(View.GONE); } } inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); } @Override public void onResume() { super.onResume(); if (mode == Mode.favoriteStations) { loadFromFavorites(); } SharedPreferences preferences = getSharedPreferences("StationSearch", 0); searchTermField.setText(preferences.getString("lastSearch", "")); } private void loadFromFavorites() { listInitThread = new Thread(new Runnable() { public void run() { Cursor cursor = getApplicationContext().getContentResolver().query(FavoriteStationsProvider.CONTENT_URI, null, null, null, null); listAdapter.initializeList(cursor); cursor.close(); handler.sendEmptyMessage(0); } }); initializeList(); query = "Favorites"; } /** * Loads the list adapter and the list from the provided query. * * @param queryUrl The URL to fetch the data from. */ private void loadFromQuery(String queryUrl) { if (queryUrl != null) { final String url = queryUrl; listInitThread = new Thread(new Runnable() { public void run() { int res = listAdapter.initializeList(url); handler.sendEmptyMessage(res); } }); initializeList(); } } /** * Parses the intent for the query URL and sets the list's mode as a * side-effect. Note that this must be called before super.onCreate to * ensure that the title is properly selected. * * @param intent The Intent the activity was started with or the intent * returned to the activity as a result. * @return The URL to fetch the list data from. */ private String parseIntent(Intent intent) { String queryUrl = null; if (intent != null) { query = intent.getStringExtra(Constants.EXTRA_QUERY_TERM); queryUrl = intent.getStringExtra(Constants .EXTRA_LIVE_STREAM_RSS_URL); if (queryUrl != null) { mode = Mode.liveStreams; } else { mode = (Mode) intent.getSerializableExtra( Constants.EXTRA_STATION_LIST_MODE ); if (mode == Mode.nearestStations) { Map<String, String> params = new HashMap<String, String>(); query = populateLocalStationParams(params); queryUrl = ApiConstants.instance().createUrl( ApiConstants.STATIONS_PATH, params ); } else { queryUrl = intent.getStringExtra(Constants.EXTRA_QUERY_URL); } } } return queryUrl; } @Override public boolean onKey(View view, int i, KeyEvent keyEvent) { switch (keyEvent.getKeyCode()) { case KeyEvent.KEYCODE_SEARCH: case KeyEvent.KEYCODE_ENTER: search(); return true; } return false; } private void search() { if (inputMethodManager != null) { inputMethodManager.hideSoftInputFromWindow( searchTermField.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); } query = this.searchTermField.getText().toString().trim(); Map<String, String> params = new HashMap<String, String>(); if (query.length() == 4) { // Assume call letters params.put(ApiConstants.PARAM_CALL_LETTERS, query); } else if (query.length() == 5) { // Assume zip code params.put(ApiConstants.PARAM_ZIP, query); } else if (query.contains(",") || query.contains(" ")) { // Assume city/state String parts[] = query.split(",| "); String state = parts[parts.length - 1]; StringBuilder builder = new StringBuilder(parts[0]); for (int i = 1, length = parts.length; i < length - 1; i++) { builder.append(" ").append(parts[i]); } String city = builder.toString(); params.put(ApiConstants.PARAM_CITY, city); params.put(ApiConstants.PARAM_STATE, state); } else { Toast.makeText(this, R.string.msg_station_search_help, Toast.LENGTH_LONG).show(); return; } SharedPreferences.Editor editor = getSharedPreferences("StationSearch", 0).edit(); editor.putString("lastSearch", query); editor.commit(); loadFromQuery(ApiConstants.instance().createUrl( ApiConstants.STATIONS_PATH, params )); } @Override public void onClick(View v) { super.onClick(v); switch (v.getId()) { case R.id.StationSearchButton: Intent i1 = new Intent(this, StationListActivity.class) .putExtra(Constants.EXTRA_STATION_LIST_MODE, Mode.locateStations); startActivity(i1); break; case R.id.StationNearestButton: Intent i2 = new Intent(this, StationListActivity.class) .putExtra(Constants.EXTRA_STATION_LIST_MODE, Mode.nearestStations); startActivity(i2); break; case R.id.search_go_button: search(); } } @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Station station = (Station) parent.getItemAtPosition(position); Intent i = new Intent(this, StationDetailsActivity.class).putExtra( Constants.EXTRA_STATION_ID, station.getId()); startActivityWithoutAnimation(i); } private void removePreset() { Station station = listAdapter.getItem(selectedPosition); favoriteStationsRepository.removePreset(station.getId()); listAdapter.notifyDataSetChanged(); } private void setPreset(int presetNumberSelected) { favoriteStationsRepository.setPreset( listAdapter.getItem(selectedPosition), Integer.toString(presetNumberSelected + 1)); listAdapter.notifyDataSetChanged(); } @Override public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { if (mode != Mode.favoriteStations) { return false; } selectedPosition = position; int presetNumber = 10; FavoriteStationEntry favoriteStationEntry = favoriteStationsRepository.getFavoriteStationForStationId( listAdapter.getItem(position).getId()); if (favoriteStationEntry != null && favoriteStationEntry.preset != null && favoriteStationEntry.preset.length() > 0) { presetNumber = Integer.parseInt(favoriteStationEntry.preset) - 1; } AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle("Pick a preset"); builder.setSingleChoiceItems(presets, presetNumber, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int item) { if (item == 10) { removePreset(); } else { setPreset(item); } dialog.dismiss(); } }); AlertDialog alert = builder.create(); alert.show(); return true; } private String populateLocalStationParams(Map<String, String> params) { String query = null; Log.d(LOG_TAG, "finding local stations"); LocationManager lm = (LocationManager) getSystemService(Context.LOCATION_SERVICE); Location location = lm.getLastKnownLocation(LocationManager.NETWORK_PROVIDER); if (location != null) { double lat = location.getLatitude(); double lon = location.getLongitude(); params.put(ApiConstants.PARAM_LAT, Double.toString(lat)); params.put(ApiConstants.PARAM_LON, Double.toString(lon)); query = String.format("%f,%f", lat, lon); } return query; } @Override public CharSequence getMainTitle() { int titleId; switch (mode) { case locateStations: case nearestStations: titleId = R.string.msg_main_subactivity_locate_stations; break; case liveStreams: titleId = R.string.msg_main_subactivity_live_streams; break; case favoriteStations: titleId = R.string.msg_main_subactivity_favorite_stations; break; default: titleId = R.string.msg_main_subactivity_all_stations; } // TODO: If this is all stations within a state, append " - [state name]" return getString(titleId); } public CharSequence getEmptyText() { int helpId; switch (mode) { case liveStreams: helpId = R.string.msg_no_live_streams; break; case favoriteStations: helpId = R.string.msg_no_favorites; break; case nearestStations: helpId = R.string.msg_stations_found_nearby; break; default: helpId = R.string.msg_stations_found; } return getString(helpId); } }