Java tutorial
/********************************************************************************** * Bismillahir Rahmanir Rahim, ALLAHU AKBAR * * The MIT License (MIT) * * * * Copyright (c) 2014 Mohammed Sazid-Al-Rashid * * * * Permission is hereby granted, free of charge, to any person obtaining a copy * * of this software and associated documentation files (the "Software"), to deal * * in the Software without restriction, including without limitation the rights * * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * * copies of the Software, and to permit persons to whom the Software is * * furnished to do so, subject to the following conditions: * * * * The above copyright notice and this permission notice shall be included in all * * copies or substantial portions of the Software. * * * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * * SOFTWARE. * **********************************************************************************/ package com.mohammedsazidalrashid.android.sunshine; import android.app.Fragment; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.preference.PreferenceManager; import android.support.v4.widget.SwipeRefreshLayout; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.AbsListView; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.Toast; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; /** * Created by sazid on 12/19/2014. */ public class ForecastFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener { public static final String EXTRA_FORECAST = null; private final String LOG_TAG = ForecastFragment.class.getSimpleName(); private ArrayAdapter<String> mForecastAdapater; private SwipeRefreshLayout mSwipeRefreshLayout; public ForecastFragment() { } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setHasOptionsMenu(true); } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { inflater.inflate(R.menu.forecastfragment, menu); } @Override public void onRefresh() { updateWeather(); } @Override public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); if (id == R.id.action_refresh) { // updateWeather(); mSwipeRefreshLayout.post(new Runnable() { @Override public void run() { updateWeather(); mSwipeRefreshLayout.setRefreshing(true); } }); Toast.makeText(getActivity(), "TIP: Swipe down to refresh :)", Toast.LENGTH_SHORT).show(); return true; } return super.onOptionsItemSelected(item); } private void updateWeather() { FetchWeatherTask weatherTask = new FetchWeatherTask(); String location = PreferenceManager.getDefaultSharedPreferences(getActivity()) .getString(getString(R.string.pref_location_key), getString(R.string.pref_location_default)); weatherTask.execute(location); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { final View rootView = inflater.inflate(R.layout.fragment_forecast, container, false); mSwipeRefreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.swipe_refresh_layout); mSwipeRefreshLayout.setOnRefreshListener(this); mSwipeRefreshLayout.setColorSchemeResources(R.color.light_blue_500, R.color.orange_500, R.color.teal500, R.color.green_500); mForecastAdapater = new ArrayAdapter<>(getActivity(), R.layout.list_item_forecast, R.id.list_item_forecast_textview, new ArrayList<String>()); final ListView listviewForecast = (ListView) rootView.findViewById(R.id.listview_forecast); listviewForecast.setAdapter(mForecastAdapater); listviewForecast.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) { String forecast = mForecastAdapater.getItem(position); MainActivity.bundleForFragments.putString(EXTRA_FORECAST, forecast); getActivity().getFragmentManager().beginTransaction().addToBackStack(null) .setCustomAnimations(R.animator.enter_anim, R.animator.exit_anim, R.animator.enter_anim_reverse, R.animator.exit_anim_reverse) .replace(R.id.container, new DetailsFragment()).commit(); } }); listviewForecast.setOnScrollListener(new AbsListView.OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, int scrollState) { } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { int topRowVerticalPosition = (listviewForecast == null || listviewForecast.getChildCount() == 0) ? 0 : listviewForecast.getChildAt(0).getTop(); mSwipeRefreshLayout.setEnabled(topRowVerticalPosition >= 0); } }); return rootView; } @Override public void onStart() { super.onStart(); mSwipeRefreshLayout.post(new Runnable() { @Override public void run() { updateWeather(); mSwipeRefreshLayout.setRefreshing(true); } }); } private String getReadableDateString(long time) { // The API returns a unix timestamp (in secs) which should be converted into millis Date date = new Date(time * 1000); // http://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html SimpleDateFormat format = new SimpleDateFormat("E, MMM d"); return format.format(date).toString(); } /** * Prepare the weather high/lows for presentation */ private String formatHighLows(double high, double low) { // For presentation, assume the user don't care about tenth of a degree long roundedHigh = Math.round(high); long roundedLow = Math.round(low); String preferred_unit = PreferenceManager.getDefaultSharedPreferences(getActivity()) .getString(getString(R.string.pref_units_key), getString(R.string.pref_units_default)); // (C * 9/5) + 32 = F if (preferred_unit == getString(R.string.pref_units_imperial)) { roundedHigh = Math.round((high * 9 / 5) + 32); roundedLow = Math.round((low * 9 / 5) + 32); } return roundedHigh + "/" + roundedLow; } /** * Take the String representing the complete forecast in JSON format and pull out the * data we need to construct the Strings needed for the wireframes. */ private String[] getWeatherDataFromJson(String forecastJsonStr, int numDays) throws JSONException { // These are the names of the JSON objects that need to be extracted final String OWM_LIST = "list"; final String OWM_WEATHER = "weather"; final String OWM_TEMPERATURE = "temp"; final String OWM_MAX = "max"; final String OWM_MIN = "min"; final String OWM_DATETIME = "dt"; final String OWM_DESCRIPTION = "description"; JSONObject forecastJson = new JSONObject(forecastJsonStr); JSONArray weatherArray = forecastJson.getJSONArray(OWM_LIST); String[] resultStrs = new String[numDays]; for (int i = 0; i < weatherArray.length(); i++) { // For now, using the format Day - description - hi/low String day; String description; String highAndLow; // Get the JSON object representing the day JSONObject dayForecast = weatherArray.getJSONObject(i); long dateTime = dayForecast.getLong(OWM_DATETIME); day = getReadableDateString(dateTime); // description is in a child array, which is 1 element long description = dayForecast.getJSONArray(OWM_WEATHER).getJSONObject(0).getString(OWM_DESCRIPTION); // Temperatures are in a child object called "temp" JSONObject temperatureObject = dayForecast.getJSONObject(OWM_TEMPERATURE); double high = temperatureObject.getDouble(OWM_MAX); double low = temperatureObject.getDouble(OWM_MIN); highAndLow = formatHighLows(high, low); resultStrs[i] = day + " - " + description + " - " + highAndLow; } return resultStrs; } private class FetchWeatherTask extends AsyncTask<String, Void, String[]> { protected String[] doInBackground(String... params) { // If there's no zip code, city, etc there's nothing to look up if (params.length == 0) { return null; } // These two need to be declared outside of the try/catch so that they can be closed // in the finally block HttpURLConnection urlConnection = null; BufferedReader reader = null; // Will contain the raw JSON response as String String forecastJsonStr = null; String format = "json"; String units = "metric"; int numDays = 7; try { // Construct the url final String FORECAST_BASE_URL = "http://api.openweathermap.org/data/2.5/forecast/daily?"; final String QUERY_PARAM = "q"; final String FORMAT_PARAM = "mode"; final String UNITS_PARAM = "units"; final String DAYS_PARAM = "cnt"; Uri builtUri = Uri.parse(FORECAST_BASE_URL).buildUpon().appendQueryParameter(QUERY_PARAM, params[0]) .appendQueryParameter(FORMAT_PARAM, format).appendQueryParameter(UNITS_PARAM, units) .appendQueryParameter(DAYS_PARAM, Integer.toString(numDays)).build(); // Log.v(LOG_TAG, "Built URI: " + builtUri.toString()); URL url = new URL(builtUri.toString()); // Open the connection, set its request method and connect to the remote system urlConnection = (HttpURLConnection) url.openConnection(); urlConnection.setRequestMethod("GET"); urlConnection.connect(); // Read the input stream into a String StringBuffer buffer = new StringBuffer(); InputStream inputStream = urlConnection.getInputStream(); if (inputStream == null) { // Nothing to do return null; } else { reader = new BufferedReader(new InputStreamReader(inputStream)); } String line; while ((line = reader.readLine()) != null) { // Adding "\n" for debugging purposes/readability buffer.append(line).append("\n"); } if (buffer.length() == 0) { return null; } forecastJsonStr = buffer.toString(); // Log.v(LOG_TAG, "JSON: " + forecastJsonStr); } catch (IOException | NullPointerException exception) { Log.e(LOG_TAG, "Error ", exception); } finally { if (urlConnection != null) { urlConnection.disconnect(); } if (reader != null) { try { reader.close(); } catch (IOException exception) { Log.e(LOG_TAG, "Error ", exception); } } } try { return getWeatherDataFromJson(forecastJsonStr, numDays); } catch (JSONException e) { Log.e(LOG_TAG, e.getMessage(), e); e.printStackTrace(); } return null; } @Override protected void onPostExecute(String[] weatherForecast) { if (weatherForecast != null) { mForecastAdapater.clear(); mForecastAdapater.addAll(weatherForecast); } mSwipeRefreshLayout.setRefreshing(false); } } }