/**
* This file is part of ElasticDroid.
*
* ElasticDroid 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.
* ElasticDroid 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 ElasticDroid. If not, see <http://www.gnu.org/licenses/>.
*
* Authored by Siddhu Warrier on 14 Nov 2010
*/
package org.elasticdroid;
import static org.elasticdroid.utils.ResultConstants.RESULT_ERROR;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import org.elasticdroid.db.ElasticDroidDB;
import org.elasticdroid.model.EC2DashboardModel;
import org.elasticdroid.model.EC2InstancesModel;
import org.elasticdroid.model.RetrieveRegionModel;
import org.elasticdroid.tpl.GenericListActivity;
import org.elasticdroid.utils.DialogConstants;
import org.elasticdroid.utils.AWSConstants.InstanceStateConstants;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.text.Html;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Spinner;
import android.widget.Toast;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemSelectedListener;
import com.amazonaws.AmazonClientException;
import com.amazonaws.AmazonServiceException;
/**
* Class to display an EC2 Dashboard. Extends {@link GenericListActivity}, as
* the principal element is a ListView. It also implements
* {@link OnItemClickListener}
*
* The EC2 Dashboard shall display a ListView that contains the following
* information:
* <ul>
* <li>Number of instance groups.</li>
* <li>Number of running instances.</li>
* <li>Number of stopped instances.</li>
* <li>AMIs (query AMIs, view AMIs registered by you).</li>
* <li>Number of Elastic IPs.</li>
* <li>Number of security groups.</li>
* <li>Number of private keys.</li>
*
* for the selected region
* </ul>
*
* @author Siddhu Warrier
*
* 14 Nov 2010
*/
public class EC2DashboardView extends GenericListActivity implements OnItemSelectedListener {
/**
* AWS login details: username, access key, secret access key. Can be IAM
* username or AWS email address Not using ArrayList<String> cuz AsyncTask
* excepts String... as argument.
* */
private HashMap<String, String> connectionData;
/**
* Flag set to true in onCreate
*/
boolean onCreateExecuted;
/**
* The data received from the model
*/
private HashMap<String, Integer> dashboardData;
/**
* Reference to EC2DashboardModel object which does the credential checks
* and stores user details in DB
*/
private EC2DashboardModel ec2DashboardModel;
/**
* Reference to RetrieveRegionModel object
*/
private RetrieveRegionModel retrieveRegionModel;
/** Dialog box for credential verification errors */
private AlertDialog alertDialogBox;
/**
* set to show if alert dialog displayed. Used to decide whether to restore
* progress dialog when screen rotated.
*/
private boolean alertDialogDisplayed;
/** message displayed in {@link #alertDialogBox alertDialogBox}. */
private String alertDialogMessage;
/** the available AWS regions */
private HashMap<String, String> regionData;
/**
* the default region set by the user. Default: not set; set randomly first
* time by Vuew
*/
private String selectedRegion;
/**
* boolean to indicate if an error that occurred is sufficiently serious to
* have the activity killed.
*/
private boolean killActivityOnError;
/**
* hack
*/
private boolean defaultRegionChanged;
/**
* Log tag
*/
private static final String TAG = "org.elasticdroid.EC2DashboardView";
/**
* Called when the Activity is first started, or recreated (for instance
* when user turns screen around) Suppressing warnings when deserialising
* object and casting to Hashmap<String, String>
*
* @param savedInstanceState
* the saved instance state which contains information written in
* onSaveInstanceState when the activity is destroyed and
* recreated.
*/
@SuppressWarnings("unchecked")
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); // call superclass onCreate
// this class is called from LoginView, and is passed an argument in the
// intent.Retrieve it.
Intent intent = this.getIntent();
try {
this.connectionData = (HashMap<String, String>) intent
.getSerializableExtra("org.elasticdroid.LoginView.connectionData");
}
// the possible exceptions are NullPointerException: the Hashmap was not
// found, or
// ClassCastException: the argument passed is not Hashmap<String,
// String>. In either case,
// just print out the error and exit. This is very inelegant, but this
// is a programmer's bug
catch (Exception exception) {
Log.e(this.getClass().getName(), exception.getMessage());
finish(); // kill the application, and off to bed.
}
// restore model object if one was saved. This happens when the activity
// is destroyed
// when the model is executing in a background thread.
Object retained = getLastNonConfigurationInstance();
// redundant if condition. Nothing else is retained. Can be removed if
// performance is
// an issue.
if (retained instanceof EC2DashboardModel) {
Log.i(this.getClass().getName(),
"Reclaiming previous background task.");
// restore the reference to the Object.
ec2DashboardModel = (EC2DashboardModel) retained;
// when saving instance state, we told the model that the activity
// it was referring to
// no longer existed. Let the model know htat a new activity has
// taken its place.
// the model will then call the new activity's
ec2DashboardModel.setActivity(this); // tell loginModel that this is
// the new recreated
// activity
}
else if (retained instanceof RetrieveRegionModel) {
Log.i(this.getClass().getName(),
"Reclaiming previous background RetrieveRegionModel task.");
retrieveRegionModel = (RetrieveRegionModel) retained;
retrieveRegionModel.setActivity(this);
}
else {
//onResume can also be executed on the press of the home button
//in whic case we have not lost our object references. Therefore,
//disable your progress bar Only IF there are no model objects.
if ((retrieveRegionModel == null) && (ec2DashboardModel == null)) {
Log.v(this.getClass().getName(),
"No model object, or model finished before activity "
+ "was recreated.");
// if the progress dialog is being displayed, kill it.
if (progressDialogDisplayed) {
progressDialogDisplayed = false;
removeDialog(DialogConstants.PROGRESS_DIALOG.ordinal());
}
}
}
// create and initialise the alert dialog
alertDialogBox = new AlertDialog.Builder(this).create(); // create alert dialog box
alertDialogBox.setCancelable(false);
alertDialogBox.setButton(
this.getString(R.string.loginview_alertdialogbox_button),
new DialogInterface.OnClickListener() {
// click listener on the alert box - unlock orientation when
// clicked.
// this is to prevent orientation changing when alert box
// locked.
public void onClick(DialogInterface arg0, int arg1) {
alertDialogDisplayed = false;
alertDialogBox.dismiss(); // dismiss dialog.
// if an error occurs that is serious enough return the
// user to the login
// screen. THis happens due to exceptions caused by
// programming errors and
// exceptions caused due to invalid credentials.
if (killActivityOnError) {
EC2DashboardView.this.finish();
Intent loginIntent = new Intent();
loginIntent.setClassName("org.elasticdroid",
"org.elasticdroid.LoginView");
startActivity(loginIntent);
}
}
});
setContentView(R.layout.ec2dashboard);
// set the title to the username of the person currently logged in.
this.setTitle(connectionData.get("username"));
onCreateExecuted = true;
}
/**
* Restore state of the activity on destroy/stop This method is called when
* the activity is restarted.
*
* Restores:
* <ul>
* <li>boolean to indicate if alert dialog box is displayed.</li>
* <li>alert dialog box string.</li>
* <li>boolean to indicate if progress dialog is displayed.</li>
* <li>List of regions available. This doesn't have to be queried everytime,
* as AWS are unlikely to add new regions in the lifetime of this app. This
* improves responsiveness.</li>
* </ul>
*
* It restores
*/
@SuppressWarnings("unchecked")
@Override
public void onRestoreInstanceState(Bundle stateToRestore) {
super.onRestoreInstanceState(stateToRestore);
// restore dialog data
alertDialogDisplayed = stateToRestore.getBoolean("alertDialogDisplayed");
Log.v(this.getClass().getName(), "alertDialogDisplayed = "
+ alertDialogDisplayed);
alertDialogMessage = stateToRestore.getString("alertDialogMessage");
//restore progress dialog
progressDialogDisplayed = stateToRestore.getBoolean("progressDialogDisplayed");
Log.v(this.getClass().getName(),
"onRestoreInstanceState:progress dialog displayed="
+ progressDialogDisplayed);
// restore dashboard data if any.
Log.v(this.getClass().getName(), "Restoring dashboardData...");
try {
dashboardData = (HashMap<String, Integer>) stateToRestore
.getSerializable("dashboardData");
} catch (Exception exception) {
// no data in the restore state. So just ignore.
Log.e(this.getClass().getName(), "I should never come here:"
+ exception);
finish();
}
// restore region data
Log.v(this.getClass().getName(), "Restoring regionData...");
try {
regionData = (HashMap<String, String>) stateToRestore
.getSerializable("regionData");
} catch (Exception exception) {
// no data in the restore state. So just ignore.
regionData = null;
//onResume will get this data later
}
// restore default region
selectedRegion = stateToRestore.getString("selectedRegion");
Log.v(this.getClass().getName(),
"onRestoreInstanceState:selectedRegion="
+ selectedRegion);
}
/**
* Resume operation of Activity. This method performs the following tasks:
* <ul>
* <li>Gets a reference to the model (
* {@link org.elasticdroid.model.EC2DashboardModel Async Task and points it
* to the activity again.Only if the Activity was destroyed and recreated.
* </li> <li>Kills progress dialog if the model finished executing before
* the activity came back up. Only if the Activity was destroyed and
* recreated.</li> <li>Restores region data, or queries model to get region
* data if none.</li> <li>Re-displays alert dialog box if there was an alert
* dialog box when activity destroyed. Only if the Activity was destroyed
* and recreated.</li>
* </ul>
*/
@Override
public void onResume() {
super.onResume(); // call base class method
// do not execute any of this if the default region has been changed.
// this is a bit of a hack but has been figured out to be the best way
// to ensure that data is loaded properly when the default region is
// changed.
// that is, we switch the user automatically to the new default region.
// Too tired to explain
// but trust me, leave this in here!
if (defaultRegionChanged) {
defaultRegionChanged = false;
return;
}
// restore alert dialog box if any.
if (alertDialogDisplayed) {
alertDialogBox.setMessage(alertDialogMessage);
alertDialogBox.show();
} else {
//check if we need to get region data.
//if we have neither region data, nor a running retrieveregionmodel
//execute retrieve region model
if (regionData == null) {
//execute retrieveregionmodel unless not executing already
if (retrieveRegionModel == null) {
executeRetrieveRegionModel();
}
}
else {//we have region data
//populate the region spinner
//firstExecute is set only when onCreate is executed,
if (onCreateExecuted) {
onCreateExecuted = false;
populateRegionSpinner();
}
//execute
}
if ((regionData == null) && (retrieveRegionModel == null)) {
if (onCreateExecuted) {
onCreateExecuted = false;
executeRetrieveRegionModel();
}
}
else if ( (regionData != null) && (retrieveRegionModel == null) ) {
if (onCreateExecuted) {
onCreateExecuted = false;
populateRegionSpinner();
}
}
}
}
/**
* Save state of the activity on destroy/stop
*/
@Override
public void onSaveInstanceState(Bundle saveState) {
// if a dialog is displayed when this happens, dismiss it
if (alertDialogDisplayed) {
alertDialogBox.dismiss();
}
saveState.putBoolean("alertDialogDisplayed", alertDialogDisplayed);
saveState.putString("alertDialogMessage", alertDialogMessage);
Log.v(this.getClass().getName(),
"OnSaveInstanceState: progressDialogDisplayed="
+ progressDialogDisplayed);
saveState.putBoolean("progressDialogDisplayed", progressDialogDisplayed);
// save the data only if the model is not presently executing
if ((dashboardData != null) && (ec2DashboardModel == null)) {
Log.v(this.getClass().getName(), "Saving dashboardData...");
saveState.putSerializable("dashboardData", dashboardData);
}
// similarly write the region data so we do not have to query AWS every
// time
// the user twists his screen around
if (regionData != null) {
saveState.putSerializable("regionData", regionData);
}
// save the default region so that we do not have to query the DB every
// time
// the user twists his screen around
saveState.putString("selectedRegion", selectedRegion);
// NOTE: THe loginData is int he intent, so not to worry about that.
// call the superclass (Activity)'s save state method
super.onSaveInstanceState(saveState);
}
/**
* Save reference to {@link org.elasticdroid.model.EC2DashboardModel Async
* Task when object is destroyed (for instance when screen rotated).
*
* This has to be done as the Async Task is running in the background.
*/
@Override
public Object onRetainNonConfigurationInstance() {
Log.v(this.getClass().getName(), "Object about to destroyed...");
//we know only one model can execute at a given time.
// if the model is being executed when the onDestroy method is called.
if (ec2DashboardModel != null) {
Log.v(this.getClass().getName() + ".onRetainNonConfigInstance()", "Saving " +
"ec2DashboardModel");
ec2DashboardModel.setActivityNull();
return ec2DashboardModel;
}
else if (retrieveRegionModel != null) {
Log.v(this.getClass().getName() + ".onRetainNonConfigInstance()", "Saving " +
"retrieveRegionModel");
retrieveRegionModel.setActivityNull();
return retrieveRegionModel;
}
//both are null, return null
return null;
}
/**
* Execute the model to retrieve the list of regions. The model
* runs in a different thread and calls processModelResults when done.
*/
private void executeRetrieveRegionModel() {
retrieveRegionModel = new RetrieveRegionModel(this);
retrieveRegionModel.execute(new HashMap<?,?>[]{connectionData});
}
/**
* Execute the model to retrieve EC2 data for the selected region. The model
* runs in a different thread and calls processModelResults when done.
*/
private void executeEC2DashboardModel() {
ec2DashboardModel = new EC2DashboardModel(this);
connectionData.put("endpoint", regionData.get(selectedRegion));
ec2DashboardModel.execute(new HashMap<?, ?>[] { connectionData });
}
/**
* Process the results returned by the model,
*
* @see org.elasticdroid.tpl.GenericActivity#processModelResults(java.lang.Object)
*/
@SuppressWarnings("unchecked")
@Override
public void processModelResults(Object result) {
Log.v(this.getClass().getName(), "Processing model results...");
// dismiss the progress dialog if displayed. Check redundant
if (progressDialogDisplayed) {
progressDialogDisplayed = false;
removeDialog(DialogConstants.PROGRESS_DIALOG.ordinal());
}
if (result == null) {
Toast.makeText(this, Html.fromHtml(this.getString(R.string.cancelled)), Toast.
LENGTH_LONG).show();
return; //do not execute the rest of this method.
}
//check which model returned. Only one model can be executing at a time.
//model to retrieve regions was executed.
if (retrieveRegionModel != null) {
//set reference to model to null
retrieveRegionModel = null;
if (result instanceof HashMap<?, ?>) {
try {
regionData = (HashMap<String, String>) result;
} catch (ClassCastException exception) {
Log.e(this.getClass().getName(),
"Result returned from model should be HashMap:"
+ exception.getMessage());
finish();//kill activity
}
populateRegionSpinner();
}
else if (result instanceof AmazonServiceException) {
// if a server error
if (((AmazonServiceException) result).getErrorCode()
.startsWith("5")) {
alertDialogMessage = this.getString(R.string.loginview_server_err_dlg);
} else {
alertDialogMessage = this.getString(R.string.loginview_invalid_keys_dlg);
}
alertDialogDisplayed = true;
killActivityOnError = false;//do not kill activity on server error
//allow user to retry.
}
else if (result instanceof AmazonClientException) {
alertDialogMessage = this
.getString(R.string.loginview_no_connxn_dlg);
alertDialogDisplayed = true;
killActivityOnError = false;//do not kill activity on connectivity error. allow client
//to retry.
}
else if (result instanceof IllegalArgumentException) {
alertDialogMessage = this
.getString(R.string.ec2dashview_illegal_arg_exception);
alertDialogDisplayed = true;
killActivityOnError = true;
}
}
//it was the dashboard model
else {
//set model reference to null
ec2DashboardModel = null;
// if the result is an instance of Hashtable<?,?> populate listview
if (result instanceof HashMap<?, ?>) {
try {
dashboardData = (HashMap<String, Integer>) result;
} catch (ClassCastException exception) {
Log.e(this.getClass().getName(),
"Result returned from model should be HashMap:"
+ exception.getMessage());
finish();//kill activity
}
// populate ListView
populateEC2Dashboard();
}
else if (result instanceof AmazonServiceException) {
// if a server error
if (((AmazonServiceException) result).getErrorCode()
.startsWith("5")) {
alertDialogMessage = this.getString(R.string.loginview_server_err_dlg);
} else {
alertDialogMessage = this.getString(R.string.loginview_invalid_keys_dlg);
}
alertDialogDisplayed = true;
killActivityOnError = false;//do not kill activity on server error
//allow user to retry.
}
else if (result instanceof AmazonClientException) {
alertDialogMessage = this
.getString(R.string.loginview_no_connxn_dlg);
alertDialogDisplayed = true;
killActivityOnError = false;//do not kill activity on connectivity error. allow client
//to retry.
}
else if (result instanceof IllegalArgumentException) {
alertDialogMessage = this
.getString(R.string.ec2dashview_illegal_arg_exception);
alertDialogDisplayed = true;
killActivityOnError = true;
}
}
// display the alert dialog if the user set the displayed var to true
if (alertDialogDisplayed) {
alertDialogBox.setMessage(alertDialogMessage);
alertDialogBox.show();// show error
}
}
/**
* Private method to repopulate spinners. gets region data if absent
*/
private void populateRegionSpinner() {
String defaultRegion = null;
ElasticDroidDB elasticDroidDb = new ElasticDroidDB(this);
//get the default region
try {
defaultRegion = elasticDroidDb.getDefaultRegion(connectionData.
get("username"));
} catch (SQLException e) {
Log.e(this.getClass().getName() + ".populateRegionSpinner", "SQL Exception.Exiting: "+
e.getMessage());
}
//if there is no default region, set the first item in regionData as the default region
if (defaultRegion == null) {
//this is still O(1). Gingerbread didn't like my unsafe class casts.
for (String region : regionData.keySet()) {
defaultRegion = region;
break;
}
elasticDroidDb.setDefaultRegion(connectionData.get("username"), defaultRegion);
}
//if we dont have an already selected region, set it to default region
if (selectedRegion == null) {
selectedRegion = defaultRegion;
}
// populate the android spinner with region data
ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<String>(this,
R.layout.customspinneritem, (String[]) regionData.keySet()
.toArray(new String[regionData.keySet().size()]));
spinnerAdapter.setDropDownViewResource(R.layout.customspinnerdropdownitem);
// get the spinner and set the adapter as ITS adapter
Spinner regionSpinner = ((Spinner) findViewById(R.id.ec2DashboardRegionSpinner));
regionSpinner.setAdapter(spinnerAdapter);
// listen to changes.
regionSpinner.setOnItemSelectedListener(this);
// set the spinner to the selected region (which is the default region
// if nothing has been
// selected by the user
// for this, we need to iterate through the regions and find the
// position of the selected region
// i am sure that AWS won't have more than 2^15 regions in the near
// future. So i'll be an a**h**e
// and set the position to be a byte. Ha!
Log.i(this.getClass().getName(), "OnResume(): Selected region: "
+ selectedRegion);
short selectedRegionPosition = 0;
for (String regionName : regionData.keySet()) {
if (regionName.equals(selectedRegion)) {
break;
}
selectedRegionPosition++;
}
// just-in-case error check
if (selectedRegionPosition == regionData.keySet().size()) {
Log.e(this.getClass().getName(),
"Could not find selected region in regions list. WTF!");
finish();
}
// set the selection.
Log.i(this.getClass().getName(),
"onResume: found selected region in list at pos: "
+ selectedRegionPosition);
regionSpinner.setSelection(selectedRegionPosition);
//if we have data, don't re-execute
if ((dashboardData != null) && (ec2DashboardModel == null)) {
// if there is no model, just repopulate
populateEC2Dashboard();
} else if (ec2DashboardModel == null) {
Log.v(this.getClass().getName(), "Starting model...");
executeEC2DashboardModel();
}
//third condition: dashboard model already executing
}
/**
* Populates the list view with EC2 dashboard data for the selected region.
* This method is called by {@link EC2DashboardView.processModelResults}.
*/
private void populateEC2Dashboard() {
ArrayList<String> dashboardItems = new ArrayList<String>();
//get the instance groups count and add it to the dashboard items
int instanceGroupCount = 0;
try{
instanceGroupCount = getInstanceGroupCount();
} catch (SQLException exception) {
Log.e(this.getClass().getName(),
"Couldn't query for the group instances count.");
finish();
}
dashboardItems.add(this
.getString(R.string.ec2dashview_instancegroups)
+ instanceGroupCount);
// add entries to dashboard items
dashboardItems.add(this
.getString(R.string.ec2dashview_runninginstances)
+ dashboardData.get("runningInstances"));
dashboardItems.add(this
.getString(R.string.ec2dashview_stoppedinstances)
+ dashboardData.get("stoppedInstances"));
dashboardItems.add(this.getString(R.string.ec2dashview_elasticip)
+ dashboardData.get("elasticIp"));
dashboardItems.add(this.getString(R.string.ec2dashview_securitygroups)
+ dashboardData.get("securityGroups"));
dashboardItems.add(this.getString(R.string.ec2dashview_keypairs)
+ dashboardData.get("keyPairs"));
// add the dashboard items to the list adapter to display.
setListAdapter(new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1,
(String[]) dashboardItems.toArray(new String[dashboardItems
.size()])));
}
/**
* Private method that queries the db for the count of instance groups
*
* @return the count of instance groups
* @throws SQLException
*/
private int getInstanceGroupCount() throws SQLException {
return new ElasticDroidDB(this)
.instanceGroupCount(connectionData.get("username"), selectedRegion);
}
/**
* Overriden listen method to capture clicks on the list item. Allows users
* to view lists of:
* <ul>
* <li>Running instances</li>
* <li><i>stopped instances (TBI)</i></li>
* <li><i>Elastic IPs (TBI)</i></li>
* <li><i>Security Groups (TBI)</i></li>
* <li><i>Keypairs (TBI)</i></li>
* </ul>
*
* TBI= to be implemented
*/
@Override
protected void onListItemClick(ListView list, View v, int position, long id) {
String selectedItem = list.getItemAtPosition(position).toString();
Log.v(this.getClass().getName() + "onListItemClick:",
"Selected list item: " + selectedItem);
selectedItem = selectedItem.split(":")[0] + ": "; // split the item out
// to remove the
// number
// and get the text
Intent displayListIntent = new Intent();
if (selectedItem.equals(this.getString(
R.string.ec2dashview_instancegroups))) {
displayListIntent.setClassName("org.elasticdroid",
"org.elasticdroid.EC2DisplayInstanceGroupsView");
displayListIntent.putExtra("selectedRegion", selectedRegion); // selected region
displayListIntent.putExtra(
"org.elasticdroid.EC2DashboardView.connectionData",
connectionData); // aws connection info
Log.v(TAG + ".onListItemClick:",
"Will now show list of instance groups with connection data " + connectionData);
}
else if (selectedItem.equals(this.getString(
R.string.ec2dashview_runninginstances))) {
displayListIntent.setClassName("org.elasticdroid",
"org.elasticdroid.EC2DisplayInstancesView");
displayListIntent.putExtra(
"org.elasticdroid.EC2DashboardView.connectionData",
connectionData); // aws connection info
Log.v(TAG + ".onListItemClick:",
"Will now show list of running " + "instances.");
displayListIntent.putExtra("listType",
InstanceStateConstants.RUNNING);
displayListIntent.putExtra("selectedRegion", selectedRegion); // selected region
}
else if (selectedItem.equals(this.getString(R.string.ec2dashview_stoppedinstances))) {
displayListIntent.setClassName("org.elasticdroid",
"org.elasticdroid.EC2DisplayInstancesView");
displayListIntent.putExtra("org.elasticdroid.EC2DashboardView.connectionData",
connectionData); // aws connection info
Log.v(this.getClass().getName() + ".onListItemClick:",
"Will now show list of stopped " + "instances.");
displayListIntent.putExtra("listType", InstanceStateConstants.STOPPED);
displayListIntent.putExtra("selectedRegion", selectedRegion); // selected region
}
else if (selectedItem.equals(this.getString(R.string.ec2dashview_elasticip))) {
Log.v(TAG, "Selected item: " + selectedItem);
displayListIntent.setClassName("org.elasticdroid", "org.elasticdroid.ElasticIPsView");
displayListIntent.putExtra("org.elasticdroid.EC2DashboardView.connectionData",
connectionData); // aws connection info
displayListIntent.putExtra("selectedRegion", selectedRegion); // selected region
} else if (selectedItem.equals(this.getString(R.string.ec2dashview_securitygroups))) {
Log.v(TAG, "Selected item: " + selectedItem);
displayListIntent.setClassName("org.elasticdroid", "org.elasticdroid.SecurityGroupsView");
displayListIntent.putExtra("org.elasticdroid.EC2DashboardView.connectionData",
connectionData); // aws connection info
displayListIntent.putExtra("selectedRegion", selectedRegion); // selected region
}
else {
return;
}
startActivityForResult(displayListIntent, 0); //second arg ignored.
}
/**
* Method to handle the selection of an item in the region spinner box. If a
* different region is selected, it calls the model to get data for the
* selected region. Implementation for interface @link
* {@link OnItemSelectedListener}.
*/
@Override
public void onItemSelected(AdapterView<?> selectedItem, View view, int pos,
long id) {
// has selection changed
String selectedItemText = ((Spinner) findViewById(R.id.ec2DashboardRegionSpinner))
.getItemAtPosition(pos).toString();
Log.v(this.getClass().getName(), "Selected region: " + selectedRegion
+ ", Selected item text: " + selectedItemText);
if (!selectedRegion.equals(selectedItemText)) {
Log.v(this.getClass().getName(), "Region selected: "
+ selectedRegion);
selectedRegion = ((Spinner) findViewById(R.id.ec2DashboardRegionSpinner))
.getItemAtPosition(pos).toString();
// repopulate ListView with data for this region
executeEC2DashboardModel();
}
}
/**
* Empty method called if the region spinner box is invoked and nothing is
* selected. Nothing to do here.
*/
@Override
public void onNothingSelected(AdapterView<?> arg0) {
}
/**
* Overridden method to display the menu on press of the menu key
*
* Inflates and displays dashboard menu.
*/
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.dashboard_menu, menu);
return true;
}
/**
* Overriden method to handle selection of menu item
*/
@Override
public boolean onOptionsItemSelected(MenuItem selectedItem) {
switch (selectedItem.getItemId()) {
case R.id.dashboard_menuitem_another_user:
finish(); // kill this activity and start login activity
Intent loginIntent = new Intent();
loginIntent.setClassName("org.elasticdroid",
"org.elasticdroid.LoginView");
startActivity(loginIntent);
return true;
case R.id.dashboard_menuitem_about:
Intent aboutIntent = new Intent(this, AboutView.class);
startActivity(aboutIntent);
return true;
case R.id.dashboard_menuitem_refresh:
//get region data if necessary
if (regionData == null) {
executeRetrieveRegionModel();
//this will execute EC2DashboardModel automagically.
} else {
executeEC2DashboardModel();
}
return true;
case R.id.dashboard_menuitem_default_region:
Intent setDefaultRegionIntent = new Intent();
setDefaultRegionIntent.setClassName("org.elasticdroid",
"org.elasticdroid.DefaultRegionView");
setDefaultRegionIntent.putExtra("regionData",
new ArrayList<String>(regionData.keySet()));
setDefaultRegionIntent.putExtra("username",
connectionData.get("username"));
startActivityForResult(setDefaultRegionIntent, 0); // the second arg
// is ignored.
return true;
default:
return super.onOptionsItemSelected(selectedItem);
}
}
/**
* Called when the default region setter returns.
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.v(this.getClass().getName() + "onActivityResult()", "result intent from : " +
data.resolveType(this));
Log.v(this.getClass().getName() + "onActivityResult()", "test: " +
EC2DisplayInstancesView.class.toString());
Log.v(this.getClass().getName(), "Subactivity returned with result: "
+ resultCode);
//check which class returned the result intent
if (data.getType().equals(EC2DisplayInstancesView.class.getName())) {
switch (resultCode) {
case RESULT_ERROR:
Log.e(this.getClass().getName()+"onActivityResult", data.getStringExtra("EXCEPTION_MSG"));
finish(); //kill the app off.
break;
case RESULT_OK:
Log.v(this.getClass().getName(), "EC2DisplayInstancesView returned successfully.");
if (data.getBooleanExtra("forceRefresh", false)) {
Log.v(this.getClass().getName(), "Force dashboard refresh");
//get region data if necessary
if (regionData == null) {
executeRetrieveRegionModel();
//this will execute EC2DashboardModel automagically.
} else {
executeEC2DashboardModel();
}
}
}
}
else if (data.getType().equals(EC2DisplayInstanceGroupsView.class.getName())) {
// RC
// it should only refresh the security groups count
// i'll go ahead and refresh everything
//TODO it should only refresh in case something changed
defaultRegionChanged = true;
}
else {
switch (resultCode) {
case RESULT_OK:
selectedRegion = data.getStringExtra("defaultRegion");
defaultRegionChanged = true;
Log.v(this.getClass().getName(), "Region selected: "
+ selectedRegion);
Spinner regionSpinner = ((Spinner) findViewById(R.id.ec2DashboardRegionSpinner));
String selectedItemText = regionSpinner.getSelectedItem()
.toString();
Log.v(this.getClass().getName(), "New default region: "
+ selectedRegion + ", currently selected item text: "
+ selectedItemText);
// if new region selected, populate it.
if (!selectedRegion.equals(selectedItemText)) {
short selectedRegionPosition = 0;
for (String regionName : regionData.keySet()) {
if (regionName.equals(selectedRegion)) {
break;
}
selectedRegionPosition++;
}
// just-in-case error check
if (selectedRegionPosition == regionData.keySet().size()) {
Log.e(this.getClass().getName(),
"Could not find selected region in regions list. WTF!");
finish();
}
// set the selection to be the new region...
regionSpinner.setSelection(selectedRegionPosition);
// repopulate ListView with data for this region
executeEC2DashboardModel();
}
break;
case RESULT_CANCELED:
defaultRegionChanged = true; // yes, default region didn't change
// but you're just saying,
// "dont bother reloading the dashboard, mate"
// to EC2Dashboard View. See onResume to see what this bool does.
}
}
}
/**
* Handle cancel of progress dialog
* @see android.content.DialogInterface.OnCancelListener#onCancel(android.content.
* DialogInterface)
*/
@Override
public void onCancel(DialogInterface dialog) {
//this cannot be called UNLESS the user has the model running.
//i.e. the prog bar is visible
progressDialogDisplayed = false;
if (retrieveRegionModel != null) {
retrieveRegionModel.cancel(true);
}
else if (ec2DashboardModel != null) {
ec2DashboardModel.cancel(true);
}
}
}