//-------------------------------------------------------------------------------------------------
//
// Module Name: MainScreen.java
//
// General Description: main screen displaying weather forecast information
//
// Project: Weather Forecast - TP Android Introduction
//
//
//-------------------------------------------------------------------------------------------------
//
// TELECOM BRETAGNE
//
//
// Revision History:
// Modification
// Author (core ID) Date Description of Changes
// ------------------------- ------------ ---------------------------------------------------
// pcontrei 02/10/2010 Creation
//
//-------------------------------------------------------------------------------------------------
package fr.enstb.tp.android;
import java.io.IOException;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import data.CurrentWeatherData;
import data.ForecastWeathertData;
public class MainScreen extends Activity {
// constant values:
static final String TEMP_DEGREE = "°C";
static final String TEMP_FAHRENEIT = "F";
static final int D_DAY_PLUS_ONE = 1;
static final int D_DAY_PLUS_TWO = 2;
// Menu parameters:
static final int MENU_ID_SETTINGS = Menu.FIRST;
static final int DIALOG_ID_SETTINGS_TEMP_UNIT = 0;
static final String MENU_NAME_SETTINGS = "Parameters";
static final CharSequence[] CHOIX_UNITE = { "Celsius (°C)",
"Fahrenheit (F)" };
// internal processing variables:
private String mCountryName = "";
private String mCityName = "";
static final String FORECAST_NUM_OF_DAYS_STR = "3";
private boolean mIsCelsiusUnit = false; // to choose between C� and F
private ProgressDialog mUpdateForecastProgressDialog;
// UI widgets for current forecast:
private TextView mCurrentWeatherTitleTxt;
private TextView mDescriptionTxt;
private TextView mTemperatureTxt;
private TextView mCloudCoverTxt;
private ImageView mWeather0ImgView;
// UI widgets for forecast Day+1:
private TextView mWeather1TitleTxt;
private TextView mDescriptionDayAfterTxt;
private TextView mTempMaxTxt;
private TextView mTempMinTxt;
private TextView mRainPrecipitTxt;
private TextView mWindDirectionTxt;
private TextView mWindSpeedTxt;
private ImageView mWeather1ImgView;
// asynchronous return values:
private CurrentWeatherData mCurrentWeather;
private ForecastWeathertData[] mForecastWeatherTab;
// multi-threading parameters:
private Runnable mGetWeatherDataRunnable = new Runnable() {
@Override
public void run() {
// Auto-generated method stub
getWeatherDataRunProc();
Message msg = mProgressHandler.obtainMessage();
msg.arg1 = GET_WEATHER_DONE;
mProgressHandler.sendMessage(msg);
}
};
private static int GET_WEATHER_DONE = 1;
private Runnable mUpdateUiRunnable = new Runnable() {
@Override
public void run() {
// Auto-generated method stub
updateUiRunProc();
}
};
private Handler mProgressHandler = new Handler() {
public void handleMessage(Message msg) {
if (msg.arg1 == GET_WEATHER_DONE) {
stopProgressDialog();
}
}
};
boolean mAsynchProcStatus = false;
// =========================================================================================
// Ecran principal: �tape4 � lancement du thread & requ�te serveur
// ** Multi-threading **
// - getWeatherDataRunProc()
// - updateUiRunProc()
// - startProgressDialog()
// =========================================================================================
/**
* Performs the HTTP request on the forecast weather server. - Launch the
* JSON parser on server response - Launch the UI update:
* runOnUiThread(mUpdateUiRunnable); This method is expected to be run on a
* separate thread
*
* WARNING: - This is a blocking method - No UI access can be made from this
* method!!
*
* @see #mGetWeatherDataRunnable
* @see #mUpdateUiRunnable
* @see #parseCurrentWeather
* @see #parseForecastWeather
*/
private void getWeatherDataRunProc() {
try {
mAsynchProcStatus = false;
// building the URL
String urlString = Tools.buildUrlRequest(mCityName, mCountryName,
FORECAST_NUM_OF_DAYS_STR);
Log.d("MainScreen:mGetWeatherDataRunnable", "** URL=" + urlString);
// launch the server request
HttpClient myHttpClient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(urlString);
HttpResponse response = myHttpClient.execute(httpGet);
// test the HTTP status response
int httpStatus = response.getStatusLine().getStatusCode();
if (httpStatus == HttpStatus.SC_OK) {
String httpRespStr = EntityUtils.toString(response.getEntity());
Log.d("MainScreen:getWeatherDataRunProc", "** Status="
+ httpStatus + " JsonResp=" + httpRespStr);
//
// *******************************************************************
// // Ecran principal: �tape5 � parsing JSON & affichage de la
// m�t�o
// courante Code:2
// //
// *******************************************************************
// parse the current weather data
if ((mCurrentWeather = JsonParser
.parseCurrentWeather(httpRespStr)) != null) {
// Download the weather icon from its URL
String iconUrl = mCurrentWeather.getIconUrl();
if (iconUrl != null) {
Bitmap iconBitmap = Tools.downloadImageFromUrl(iconUrl);
mCurrentWeather.setIconBitmap(iconBitmap);
}
}
// parse the forecast weather data:
if ((mForecastWeatherTab = JsonParser
.parseForecastWeather(httpRespStr)) != null) {
// Download the weather icon from its URL for each forecast
// in mForecastWeatherTab
for (int i = 0; i < mForecastWeatherTab.length; i++) {
String iconUrl = mForecastWeatherTab[i].getIconUrl();
if (iconUrl != null) {
Bitmap iconBitmap = Tools
.downloadImageFromUrl(iconUrl);
mForecastWeatherTab[i].setIconBitmap(iconBitmap);
}
}
}
// set the final status: set to true only if parsing & HTTP are
// both OK
if ((mCurrentWeather != null) && (mForecastWeatherTab != null)) {
mAsynchProcStatus = true; // what could it be used
// for?..
}
runOnUiThread(mUpdateUiRunnable);
} else {
// bad response server
Log.d("MainScreen:mGetWeatherDataRunnable",
"** HTTP error - status=" + httpStatus);
}
} catch (ClientProtocolException e) {
Log.e("EXCEPT",
"** mGetWeatherDataRunnable() ClientProtocolException: "
+ e.getMessage());
} catch (IOException e) {
Log.e("EXCEPT",
"** mGetWeatherDataRunnable() IOException: "
+ e.getMessage());
} catch (Exception e) {
Log.e("EXCEPT",
"** mGetWeatherDataRunnable() Exception: " + e.getMessage());
}
}
/**
* Display the current weather data on the GUI - test the asynch status
* before displaying weather information - stops the progress dialog
*
* @see #getWeatherDataRunProc
*/
private void updateUiRunProc() {
if (!mAsynchProcStatus){
return;
}
// display observed weather for today (D day)
mCurrentWeatherTitleTxt.setText("Observation " + mCityName + " - "
+ mCurrentWeather.getObservationTime());
mDescriptionTxt.setText(mCurrentWeather.getWeatherDescription());
// display temperature C� vs F
mTemperatureTxt.setText(mIsCelsiusUnit ? mCurrentWeather
.getTempCelsius() + TEMP_DEGREE : mCurrentWeather
.getTempFarenheit() + TEMP_FAHRENEIT);
mCloudCoverTxt.setText(mCurrentWeather.getCloudCover());
mWeather0ImgView.setImageBitmap(mCurrentWeather.getIconBitmap());
// display weather forecast (D day + 1)
displayForecastByDay(D_DAY_PLUS_ONE);
}
/**
* Display the forecast weather details according to the forecast day number
* given in aDayNumber
*
* @param aDayNumber
* the day number for the weather forecast: D_DAY_PLUS_ONE..
* @see #updateUiRunProc()
* @see #D_DAY_PLUS_ONE
* @see #D_DAY_PLUS_TWO
*/
private void displayForecastByDay(int aDayNumber) {
// **************************************************************************
// Ecran principal: �tape7 � parsing JSON & affichage de la m�t�o � J+1
// Code:8
// **************************************************************************
ForecastWeathertData weatherDataDay = mForecastWeatherTab[aDayNumber];
mWeather1TitleTxt.setText("Forecast " + mCityName + " - "
+ weatherDataDay.getForecastDate());
mDescriptionDayAfterTxt.setText(weatherDataDay.getWeatherDescription());
// display temperature C� vs F
if (mIsCelsiusUnit == true) {
mTempMaxTxt.setText(weatherDataDay.getTempMaxCelsius()
+ TEMP_DEGREE);
mTempMinTxt.setText(weatherDataDay.getTempMinCelsius()
+ TEMP_DEGREE);
} else {
mTempMaxTxt.setText(weatherDataDay.getTempMaxFarenheit()
+ TEMP_FAHRENEIT);
mTempMinTxt.setText(weatherDataDay.getTempMinFarenheit()
+ TEMP_FAHRENEIT);
}
mRainPrecipitTxt.setText(weatherDataDay.getPrecipitationMm() + "mm");
mWindDirectionTxt.setText(weatherDataDay.getWindDirection());
mWindSpeedTxt.setText(weatherDataDay.getWindSpeedKmph() + "Km/h");
mWeather1ImgView.setImageBitmap(weatherDataDay.getIconBitmap());
}
/**
* Create a simple progress dialog
*/
private void startProgressDialog() {
// ***********************************************************
// Ecran principal: �tape6 � insertion d'une progress dialog (optionnel)
// Code:1-3
// ***********************************************************
mUpdateForecastProgressDialog = new ProgressDialog(this);
mUpdateForecastProgressDialog.setTitle("Forecast Update");
mUpdateForecastProgressDialog.setMessage("Loading - please wait...");
mUpdateForecastProgressDialog.setIndeterminate(true);
mUpdateForecastProgressDialog.setCancelable(false);
mUpdateForecastProgressDialog.show();
}
private void stopProgressDialog() {
mUpdateForecastProgressDialog.dismiss();
}
// =========================================================================================
// Ecran principal: �tape3 � cr�ation du menu & boite de dialogue
// ** MENU & DIALOGS **
// - onCreateOptionsMenu()
// - onMenuItemSelected()
// - onCreateDialog()
// =========================================================================================
/**
* Creates the option menu for the activity - only one menu is created:
* MENU_NAME_SETTINGS
*/
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Auto-generated method stub
super.onCreateOptionsMenu(menu);
// Toast.makeText(this, "onCreateOptionsMenu not yet emplemented!",
// Toast.LENGTH_LONG).show();
// ***********************************************************
// Ecran principal: �tape3 � cr�ation du menu & boite de dialogue Code:5
// ***********************************************************
// "Parameters" menu creation:
MenuItem theMenu = menu.add(Menu.NONE, MENU_ID_SETTINGS, Menu.NONE,
MENU_NAME_SETTINGS);
theMenu.setIcon(android.R.drawable.ic_menu_manage);
return true;
}
@Override
public boolean onMenuItemSelected(int featureId, MenuItem item) {
// Auto-generated method stub
// ***********************************************************
// Ecran principal: �tape3 � cr�ation du menu & boite de dialogue
// Code:6-7
// ***********************************************************
switch (item.getItemId()) {
case MENU_ID_SETTINGS:
// display the dialog box to choose the temperature unit (C or F)
showDialog(DIALOG_ID_SETTINGS_TEMP_UNIT);
break;
default:
// error case
break;
}
return super.onMenuItemSelected(featureId, item);
}
/**
* Display a single choice dialog box to choose the temperature unit (C� or
* F)
*/
@Override
protected Dialog onCreateDialog(int id) {
// Auto-generated method stub
// ***********************************************************
// Ecran principal: �tape3 � cr�ation du menu & boite de dialogue
// Code:1-4
// ***********************************************************
switch (id) {
case DIALOG_ID_SETTINGS_TEMP_UNIT:
return new AlertDialog.Builder(this)
.setIcon(android.R.drawable.ic_dialog_info)
// 0 for no icon
.setTitle("Temperature Unit")
.setSingleChoiceItems(CHOIX_UNITE, mIsCelsiusUnit ? 0 : 1,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,
int choiceItemId) {
String valueToWrite;
switch (choiceItemId) {
case 0:
mIsCelsiusUnit = true;
valueToWrite = Tools.CELCIUS_UNIT_STR;
break;
case 1:
mIsCelsiusUnit = false;
valueToWrite = Tools.FAHRENHEIT_UNIT_STR;
break;
default:
mIsCelsiusUnit = true;
valueToWrite = Tools.CELCIUS_UNIT_STR;
}
Tools.setPreferences(MainScreen.this,
Tools.PREF_TEMPERATURE_UNIT_ID,
valueToWrite);
}
})
.setPositiveButton("OK",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,
int whichButton) {
// Update the display according to the unit
// user choice
runOnUiThread(mUpdateUiRunnable);
}
})
.setNegativeButton("CANCEL",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,
int which) {
// nothing to do... just leave
}
}).create();
default:
break;
}
return super.onCreateDialog(id);
}
// =========================================================================================
// ** LIFE CYCLE **
// =========================================================================================
@Override
protected void onCreate(Bundle savedInstanceState) {
// Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.main_screen);
Log.d("MainScreen:onCreate()", "** Hello!");
// ***********************************************************
// Ecran principal: �tape 1 � r�cup�ration du login dans MainScreen
// Code:1-3
// ***********************************************************
// Is there any intent parameters sent by the calling activity?
Bundle extras;
String login = "";
if (null != (extras = getIntent().getExtras())) {
// extract the user login from the login screen
login = extras.getString(Login.INTENT_PARAM_LOGIN);
} else {
Toast.makeText(this, "MainScreen Error: login is missing..",
Toast.LENGTH_LONG).show();
}
// display the user login in the title
this.setTitle(this.getTitle().toString() + " by " + login);
// ***********************************************************
// Ecran principal: �tape2 � partie saisie & m�t�o observ�e Code:8-9
// ***********************************************************
mWeather0ImgView = (ImageView) findViewById(R.id.Desc0ImageView);
mCurrentWeatherTitleTxt = (TextView) findViewById(R.id.currentWeatherTitle);
mDescriptionTxt = (TextView) findViewById(R.id.DescriptionText);
mTemperatureTxt = (TextView) findViewById(R.id.TemperatureText);
mCloudCoverTxt = (TextView) findViewById(R.id.CloudCoverTxtView);
mRainPrecipitTxt = (TextView) findViewById(R.id.RainTextD1);
mWindSpeedTxt = (TextView) findViewById(R.id.WindSpeedTextD1);
mWindDirectionTxt = (TextView) findViewById(R.id.WindDirTextD1);
mTempMaxTxt = (TextView) findViewById(R.id.TempMaxTextD1);
mTempMinTxt = (TextView) findViewById(R.id.TempMinTextD1);
mWeather1TitleTxt = (TextView) findViewById(R.id.forecastD1WeatherTitle);
mWeather1ImgView = (ImageView) findViewById(R.id.Desc1ImageView);
mDescriptionDayAfterTxt = (TextView) findViewById(R.id.DescTextD1);
// read the preferences settings: Celsius or Fahrenheit??
String tempUnit = Tools.getPreferences(this,
Tools.PREF_TEMPERATURE_UNIT_ID);
Log.d("MainScreen:onCreate()", "** Temperature unit=" + tempUnit);
// ***********************************************************
// Ecran principal: �tape2 � partie saisie & m�t�o observ�e Code:10-14
// ***********************************************************
// set the forecast OK button handler
Button okButton = (Button) findViewById(R.id.searchButton);
okButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// get user inputs from GUI
mCountryName = ((EditText) findViewById(R.id.countryTxtEdit))
.getText().toString();
mCityName = ((EditText) findViewById(R.id.cityTxtEdit))
.getText().toString();
Log.d("MainScreen:onClick()", "** Country=" + mCountryName
+ " City=" + mCityName);
Log.d("MainScreen:onClick()", "** Start HTTP request!");
if (mCountryName.equals("") || mCityName.equals("")) {
Log.d("MainScreen:onClick()",
"** Bad input: country or city is missing!!");
return;
}
// ***********************************************************
// Ecran principal: �tape4 � lancement du thread & requ�te
// serveur Code:1
// ***********************************************************
// start the thread in charge of performing the server request
Thread serverThread = new Thread(mGetWeatherDataRunnable);
startProgressDialog();
serverThread.start();
}
});
}
@Override
protected void onDestroy() {
// Auto-generated method stub
super.onDestroy();
Log.d("MainScreen:onDestroy()", "** Hello!");
}
@Override
protected void onPause() {
// Auto-generated method stub
super.onPause();
Log.d("MainScreen:onPause()", "** Hello!");
}
@Override
protected void onRestart() {
// Auto-generated method stub
super.onRestart();
Log.d("MainScreen:onRestart()", "** Hello!");
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
// Auto-generated method stub
super.onRestoreInstanceState(savedInstanceState);
Log.d("MainScreen:onRestoreInstanceState()", "** Hello!");
}
@Override
protected void onResume() {
// Auto-generated method stub
super.onResume();
Log.d("MainScreen:onResume()", "** Hello!");
}
/**
* DESC: onSaveInstanceState() is called by Android if the Activity is being
* stopped and may be killed before it is resumed. It is the counterpart to
* the onCreate() method. The savedInstanceState Bundle passed in to
* onCreate() is the same Bundle that we construct as outState in this
* onSaveInstanceState() method.
*
* @see onCreate()
*/
@Override
protected void onSaveInstanceState(Bundle outState) {
// Auto-generated method stub
super.onSaveInstanceState(outState);
Log.d("MainScreen:onSaveInstanceState()", "** Hello!");
}
@Override
protected void onStart() {
// Auto-generated method stub
super.onStart();
Log.d("MainScreen:onStart()", "** Hello!");
}
@Override
protected void onStop() {
// Auto-generated method stub
super.onStop();
Log.d("MainScreen:onStop()", "** Hello!");
}
} // end class