content: package edu.berkeley.cs.amplab.carat.android;
  
  import java.io.IOException;
  import java.io.InputStream;
  import java.lang.reflect.Field;
  import java.util.ArrayList;
  import java.util.List;
  import java.util.Locale;
  import java.util.Properties;
  
  import org.json.JSONArray;
  import org.json.JSONException;
  import org.json.JSONObject;
  
  import android.annotation.SuppressLint;
  import android.content.Intent;
  import android.content.SharedPreferences;
  import android.content.res.Configuration;
  import android.os.AsyncTask;
  import android.os.Build;
  import android.os.Bundle;
  import android.os.Handler;
  import android.preference.PreferenceManager;
  import android.support.v4.app.ActionBarDrawerToggle;
  import android.support.v4.app.Fragment;
  import android.support.v4.app.FragmentManager;
  import android.support.v4.app.FragmentManager.BackStackEntry;
  import android.support.v4.app.FragmentTransaction;
  import android.support.v4.view.GravityCompat;
  import android.support.v4.widget.DrawerLayout;
  import android.support.v7.app.ActionBar;
  import android.support.v7.app.ActionBarActivity;
  import android.util.Log;
  import android.view.Gravity;
  import android.view.KeyEvent;
  import android.view.MenuItem;
  import android.view.View;
  import android.view.Window;
  import android.widget.AdapterView;
  import android.widget.ListView;
  import android.widget.Toast;
  
  import com.flurry.android.FlurryAgent;
  
  import edu.berkeley.cs.amplab.carat.android.fragments.AboutFragment;
  import edu.berkeley.cs.amplab.carat.android.fragments.BugsOrHogsFragment;
  import edu.berkeley.cs.amplab.carat.android.fragments.CaratSettingsFragment;
  import edu.berkeley.cs.amplab.carat.android.fragments.EnableInternetDialogFragment;
  import edu.berkeley.cs.amplab.carat.android.fragments.MyDeviceFragment;
  import edu.berkeley.cs.amplab.carat.android.fragments.SuggestionsFragment;
  import edu.berkeley.cs.amplab.carat.android.fragments.SummaryFragment;
  import edu.berkeley.cs.amplab.carat.android.sampling.SamplingLibrary;
  import edu.berkeley.cs.amplab.carat.android.subscreens.WebViewFragment;
  import edu.berkeley.cs.amplab.carat.android.utils.JsonParser;
  import edu.berkeley.cs.amplab.carat.android.utils.Tracker;
  
  /**
   * Carat Android App Main Activity. Is loaded right after CaratApplication.
   * Holds the Tabs that comprise the UI. Place code related to tab handling and
   * global Activity code here.
   * 
   * @author Eemil Lagerspetz
   * 
   */
  public class MainActivity extends ActionBarActivity {
  
  	private DrawerLayout mDrawerLayout;
  	private ListView mDrawerList;
  	private ActionBarDrawerToggle mDrawerToggle;
  
  	private CharSequence mTitle;
  	private String[] mDrawerItems;
  
  	// Log tag
  	// private static final String TAG = "MainActivity";
  
  	public static final String ACTION_BUGS = "bugs",
  							   ACTION_HOGS = "hogs";
  
  	// Key File
  	private static final String FLURRY_KEYFILE = "flurry.properties";
  	
  	private String fullVersion = null;
  	
  	private Tracker tracker = null;
  	
  	/**
  	 * Dynamic way of dealing with a list of fragments that you need to keep references for.
  	 */
  	private Fragment[] frags = new Fragment[CaratApplication.getTitles().length];
  	
  	private Bundle mArgs;
  
  	// public boolean updateSummaryFragment;
  	
  	// counts (general Carat statistics shown in the summary fragment)
  	public int mWellbehaved = Constants.VALUE_NOT_AVAILABLE,
  			mHogs = Constants.VALUE_NOT_AVAILABLE,
  			mBugs = Constants.VALUE_NOT_AVAILABLE ;
  	
  	@Override
  	protected void onCreate(Bundle savedInstanceState) {
  		super.onCreate(savedInstanceState);
  		CaratApplication.setMain(this);
  		tracker = Tracker.getInstance();
  		// track user clicks (taps)
  		tracker.trackUser("caratstarted");
  		
  		if (!CaratApplication.isInternetAvailable()) {
  			EnableInternetDialogFragment dialog = new EnableInternetDialogFragment();
  			dialog.show(getSupportFragmentManager(), "dialog");
  		}
  
  		/*
  		 * Activity.getWindow.requestFeature() should get invoked only before
  		 * setContentView(), otherwise it will cause an app crash The progress
  		 * bar doesn't get displayed when there is no update in progress
  		 */
  		getWindow().requestFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
  		getWindow().requestFeature(Window.FEATURE_PROGRESS);
  		
  		// Log.d(TAG, "about to set the layout");
  		setContentView(R.layout.activity_main);
  
  		ActionBar actionBar = getSupportActionBar();
  		setTitleNormal();
  		
  		// read and load the preferences specified in our xml preference file
  		PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
  		
  		// Log.d(TAG, "about to initialize fragments");
  		preInittializeFragments();
  		// Log.d(TAG, "done with fragment initialization");
  		
  		/*
  		 * Before using the field "fullVersion", first invoke setTitleNormal()
  		 * or setFullVersion() to set this field
  		 */
  		mDrawerItems = getResources().getStringArray(R.array.drawer_items);
  		
  		List<Item> items = new ArrayList<Item>();
  		
  //		items.add(new NavDrawerListHeader("Main"));
  		items.add(new ListItem(mDrawerItems[0]));
  		items.add(new ListItem(mDrawerItems[1]));
  		items.add(new ListItem(mDrawerItems[2]));
  		items.add(new ListItem(mDrawerItems[3]));
  		items.add(new ListItem(mDrawerItems[4]));
  		items.add(new NavDrawerListHeader(""));
  		items.add(new ListItem(mDrawerItems[5]));
  		items.add(new ListItem(mDrawerItems[6]));
  		
  		TextArrayAdapter adapter = new TextArrayAdapter(this, items);
  		
  		mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
  		mDrawerList = (ListView) findViewById(R.id.left_drawer);
  		// set a custom shadow that overlays the main content when the drawer opens
  		mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
  		// set up the drawer's list view with items and click listener
  		mDrawerList.setAdapter(adapter);
  //		mDrawerList.setAdapter(new ArrayAdapter<String>(this, R.layout.drawer_list_item, mDrawerItems));
  		mDrawerList.setOnItemClickListener(new DrawerItemClickListener());
  
  		// ActionBarDrawerToggle ties together the the proper interactions
  		// between the sliding drawer and the action bar app icon
  		mDrawerToggle = new ActionBarDrawerToggle(
  				this, /* host Activity */
  				mDrawerLayout, /* DrawerLayout object */
  				R.drawable.ic_drawer, /* nav drawer image to replace 'Up' caret */
  				R.string.drawer_open, /* "open drawer" description for accessibility */
  				R.string.drawer_close /* "close drawer" description for accessibility */
  				) {
  					public void onDrawerClosed(View view) {
  						//getSupportActionBar().setTitle(mTitle);
  					}
  
  					public void onDrawerOpened(View drawerView) {
  						getSupportActionBar().setTitle(mTitle);
  					}
  				};
  		mDrawerLayout.setDrawerListener(mDrawerToggle);
  
  		// Enable ActionBar app icon to behave as action to toggle navigation drawer
  		actionBar.setDisplayHomeAsUpEnabled(true);
  		actionBar.setHomeButtonEnabled(true);
  
  		if (savedInstanceState == null) {
  			selectItem(0);
  		}
  
  		setTitleNormal();
  		
  		// Uncomment the following to enable listening on local port 8080:
  		/*
  		 * try {
  		 *  HelloServer h = new HelloServer();
  		 * } catch (IOException e) {
  		 *  e.printStackTrace(); 
  		 * }
  		 */
  	}
  
  	
  	/* The click listener for ListView in the navigation drawer */
  	private class DrawerItemClickListener implements ListView.OnItemClickListener {
  		@Override
  		public void onItemClick(AdapterView<?> parent, View view, final int position, long id) {
  			// To remove the lag in closing the drawer, don't do a fragment transaction
  			// while the drawer is getting closed. 
  			// First close the drawer (takes about 300ms (transition/animation time)),  
  			// wait for it to get closed completely, then start replacing the fragment .
  			
  			// How to modify the navigation drawer closing transition time: 
  			// http://stackoverflow.com/questions/19460683/speed-up-navigation-drawer-animation-speed-on-closing
  			
  			// consider menu item headers (additional menu items) when selecting an item
  			// TODO: use a dynamic approach
  			final int newPosition = (position <= 4) ? position : position - 1;
  			
  			mDrawerLayout.closeDrawer(mDrawerList);
  	        new Handler().postDelayed(new Runnable() {
  	            @Override
  	            public void run() {
  	            	selectItem(newPosition);
  	            }
  	        }, 300); // wait 300ms before calling selectItem()
  		}
  	}
  
  	private void selectItem(int position) {
  		// update the main content by replacing fragments
  		replaceFragment(frags[position], mDrawerItems[position], true);
  
  		mDrawerList.setItemChecked(position, true);
  	}
  	
  	/**
  	 * 
  	 * @param index 0-based index of the navigation drawer entries (e.g. 3 for bugs fragment)
  	 * @return the tag of the fragment corresponding to the index
  	 */
  	public String getFragmentTag(int index) {
  		return mDrawerItems[index];
  	}
  
  	/**
  	 * Avoid displaying a white screen when the back button is pressed in the summary fragment.
  	 * When we are in the summary fragment, since there is only one fragment in the backstack,
  	 * the fragment manager will fail to pop another fragment from the backstack,
  	 * so only the framelayout (the parent/host widget for fragments (in our activity's layout)) is shown.
  	 * We need to check the number of fragments present in the backstack, and act accordingly
  	 */
  	@Override
  	public void onBackPressed() {
  		FragmentManager manager = getSupportFragmentManager();
  		
  		// If we will pop a top level screen, show drawer indicator again
  		int stackTop = manager.getBackStackEntryCount()-1;
  		
  		BackStackEntry entry = manager.getBackStackEntryAt(stackTop);
  		String name = entry.getName();
  		String[] titles = CaratApplication.getTitles();
  		boolean found = false;
  		for (String t: titles){
  			if (!found)
  				found = t.equals(name);
  		}
  		if (found){
  			// Restore menu
  			mDrawerToggle.setDrawerIndicatorEnabled(true);
  		}
  		if (stackTop > 0 ) {
  	        // If there are back-stack entries, replace the fragment (go to the fragment)
  	        manager.popBackStack();
  	    }else
  	        finish();
  	}
  	
  	@Override
  	public void setTitle(CharSequence title) {
  		getSupportActionBar().setTitle(title);
  	}
  
  	/**
  	 * When using the ActionBarDrawerToggle, you must call it during
  	 * onPostCreate() and onConfigurationChanged()...
  	 */
  	@Override
  	protected void onPostCreate(Bundle savedInstanceState) {
  		super.onPostCreate(savedInstanceState);
  		// Sync the toggle state after onRestoreInstanceState has occurred.
  		mDrawerToggle.syncState();
  	}
  
  	@Override
  	public void onConfigurationChanged(Configuration newConfig) {
  		super.onConfigurationChanged(newConfig);
  		// Pass any configuration change to the drawer toggle
  		mDrawerToggle.onConfigurationChanged(newConfig);
  	}
  
  	@Override
  	public boolean onOptionsItemSelected(MenuItem item) {
  		// Pass the event to ActionBarDrawerToggle, if it returns
  		// true, then it has handled the app icon touch event
  		
  		// In case we are at a sublevel, enable going back by clicking title.
  		if (item.getItemId() == android.R.id.home && !mDrawerToggle.isDrawerIndicatorEnabled()){
  			onBackPressed();
  			return true;
  		}
  		
  		if (mDrawerToggle.onOptionsItemSelected(item)) {
  			return true;
  		}
  		// Handle your other action bar items...
  
  		return super.onOptionsItemSelected(item);
  	}
  
  	public void setTitleNormal() {
  		setFullVersion();
  		if (CaratApplication.storage != null) {
  			long s = CaratApplication.storage.getSamplesReported();
  			Log.d("setTitleNormal", "number of samples reported=" + String.valueOf(s));
  			if (s > 0) {
  				mTitle = fullVersion + " - "+ s + " " + getString(R.string.samplesreported);
  			} else {
  				mTitle = fullVersion;
  			}
  		}
  		setTitle(mTitle);
  	}
  	
  	private void setFullVersion() {
  		fullVersion = getString(R.string.app_name) + " " + getString(R.string.version_name);
  	}
  	
  	public String getFullVersion()  {
  		return fullVersion;
  	}
  
  	public void setTitleUpdating(String what) {
  		setTitle(getString(R.string.updating) + " " + what);
  		// this is quick hack to update SummaryFragment.
  		if (what.equals(getString(R.string.finishing)))
  		    refreshSummaryFragment();
  	}
  
  	public void setTitleUpdatingFailed(String what) {
  		setTitle(getString(R.string.didntget) + " " + what);
  	}
  	
  	/**
  	 * Used in the system settings fragment and the summary fragment
  	 * @param intentString
  	 * @param thing
  	 */
  	public void safeStart(String intentString, String thing) {
          Intent intent = null;
          try {
              intent = new Intent(intentString);
              startActivity(intent);
          } catch (Throwable th) {
              // Log.e(TAG, "Could not start activity: " + intent, th);
              if (thing != null) {
                  Toast t = Toast.makeText(this, getString(R.string.opening) + thing + getString(R.string.notsupported),
                          Toast.LENGTH_SHORT);
                  t.show();
              }
          }
      }
  
  	@Override
  	protected void onStart() {
  		super.onStart();
  
  		String secretKey = null;
  		Properties properties = new Properties();
  		try {
  			InputStream raw = MainActivity.this.getAssets().open(FLURRY_KEYFILE);
  			if (raw != null) {
  				properties.load(raw);
  				if (properties.containsKey("secretkey"))
  					secretKey = properties.getProperty("secretkey", "secretkey");
  				// Log.d(TAG, "Set Flurry secret key.");
  			} else {
  			  // Log.e(TAG, "Could not open Flurry key file!");
  			}
  		} catch (IOException e) {
  			// Log.e(TAG, "Could not open Flurry key file: " + e.toString());
  		}
  		if (secretKey != null) {
  			FlurryAgent.onStartSession(getApplicationContext(), secretKey);
  		}
  		
  		/* To perform an action when our defaultSharedPreferences changes, we can do it in this listener
  		 * when you uncomment this, remember to uncomment the unregistering code in the onStop() method of this activity (right below)
  		 */
  		// PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(mOnSharedPreferenceChangeListener);
  	}
  
  	@Override
  	protected void onStop() {
  		super.onStop();
  		FlurryAgent.onEndSession(getApplicationContext());
  		// PreferenceManager.getDefaultSharedPreferences(this).unregisterOnSharedPreferenceChangeListener(mOnSharedPreferenceChangeListener);
  	}
  	
  	@Override
  	protected void onResume() {
  		// Log.i(TAG, "Resumed. Refreshing UI");
  		tracker.trackUser("caratresumed");
  
  		// if statistics data for the summary fragment is not already fetched,
  		// and the device has an Internet connection, fetch statistics and then refresh the summary fragment
  		if ( (! isStatsDataAvailable()) && CaratApplication.isInternetAvailable()) {
  			getStatsFromServer();
  		}
  				
  		/** 
  		 * This may take minutes, so refresh summary frag here again.
  		 */
  	    new Thread() {
  	            public void run() {
  	                ((CaratApplication) getApplication()).refreshUi();
  	                // This should only run if we are on that tab, so onResume of SummaryFragment should be enough.
  	                //refreshSummaryFragment();
  	            }}.start();
  		super.onResume();
  	}
  
  	public Fragment getVisibleFragment(){
  	    FragmentManager fragmentManager = MainActivity.this.getSupportFragmentManager();
  	    List<Fragment> fragments = fragmentManager.getFragments();
  	    for(Fragment fragment : fragments){
  	        if(fragment != null && fragment.isVisible())
  	            return fragment;
  	    }
  	    return null;
  	}
  	
  	@Override
  	protected void onPause() {
  		// Log.i(TAG, "Paused");
  		tracker.trackUser("caratpaused");
  		SamplingLibrary.resetRunningProcessInfo();
  		super.onPause();
  	}
  
  	@Override
  	public void finish() {
  		// Log.d(TAG, "Finishing up");
  		tracker.trackUser("caratstopped");
  		super.finish();
  	}
  
  	/**
  	 * must be called in onCreate() method of the activity, before calling selectItem() method
  	 * [before attaching the navigation drawer listener]
  	 * pre-initialize all fragments before committing a replace fragment transaction
  	 * may help for better smoothness when user selects a navigation drawer item
  	 */
  	private void preInittializeFragments() {        
  		getStatsFromServer();
  		
  		// after fetching the data needed by the summary fragment, initialize it
  		int idx = 0;
  		frags[idx] = new SummaryFragment();
  		idx++;
  		frags[idx] = new SuggestionsFragment();
  		idx++;
  		frags[idx] = new MyDeviceFragment();
  		idx++;
  		frags[idx] = new BugsOrHogsFragment();
  		mArgs = new Bundle();
  		mArgs.putBoolean("isBugs", true);
  		frags[idx].setArguments(mArgs);
  		idx++;
  		frags[idx] = new BugsOrHogsFragment();
  		mArgs = new Bundle();
  		mArgs.putBoolean("isBugs", false);
  		frags[idx].setArguments(mArgs);
  		idx++;
  		frags[idx] = new CaratSettingsFragment();
  		idx++;
  		frags[idx] = new AboutFragment();
  	}
  
  	/**
  	 * Before initializing the summary fragment, we need to fetch the the data it needs from our server,
  	 * in an asyncTask in a new thread
  	 */
  	@SuppressLint("NewApi")
  	private void getStatsFromServer() {
  		PrefetchData prefetchData = new PrefetchData();
  		// run this asyncTask in a new thread [from the thread pool] (run in parallel to other asyncTasks)
  		// (do not wait for them to finish, it takes a long time)
  		if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.HONEYCOMB)
  			prefetchData.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
  		 else
  			 prefetchData.execute();
  	}
  	
  	/*
  	 * shows the fragment using a fragment transaction (replaces the FrameLayout
  	 * (a placeholder in the main activity's layout file) with the passed-in fragment)
  	 * 
  	 * @param fragment the fragment that should be shown
  	 * @param tag a name for the fragment to be shown in the
  	 * fragment (task) stack
  	 */
  	public void replaceFragment(Fragment fragment, String tag, boolean showDrawerIndicator) {
  		// use a fragment tag, so that later on we can find the currently displayed fragment
  		final String FRAGMENT_TAG = tag;
  		// replace the fragment, using a fragment transaction
  		FragmentManager fragmentManager = getSupportFragmentManager();
  		FragmentTransaction transaction = fragmentManager.beginTransaction();
  		
  		transaction.replace(R.id.content_frame, fragment, FRAGMENT_TAG)
  					.addToBackStack(FRAGMENT_TAG)
  					.commit();
  		mDrawerToggle.setDrawerIndicatorEnabled(showDrawerIndicator);
  	}
  	
  	/**
  	 * used by other classes
  	 * @param fileName
  	 */
  	public void showHTMLFile(String fileName, String title, boolean showDrawerIndicator) {
  		WebViewFragment fragment = WebViewFragment.getInstance(fileName);
  		replaceFragment(fragment, title, showDrawerIndicator);
  	}
  
  	public boolean isStatsDataAvailable() {
  		if (isStatsDataLoaded()) {
  			// Log.i(TAG, "isStatsDataAvailable(), mWellbehaved=" + mWellbehaved + ", mHogs=" + mHogs + ", mBugs=" + mBugs);
  			return true;
  		} else {
  			return isStatsDataStoredInPref();
  		}
  	}
  
  	private boolean isStatsDataLoaded() {
  		// don't check for zero, check for something unlikely, e.g. -1 (use a constant for that value, use it consistently)
  		return mWellbehaved != Constants.VALUE_NOT_AVAILABLE && mHogs != Constants.VALUE_NOT_AVAILABLE  && mBugs != Constants.VALUE_NOT_AVAILABLE;
  	}
  
  	private boolean isStatsDataStoredInPref() {
  		// TODO: consider a data freshness timeout (e.g. two weeks)
  		int wellbehaved = CaratApplication.mPrefs.getInt(Constants.STATS_WELLBEHAVED_COUNT_PREFERENCE_KEY, Constants.VALUE_NOT_AVAILABLE);
  		int hogs = CaratApplication.mPrefs.getInt(Constants.STATS_HOGS_COUNT_PREFERENCE_KEY, Constants.VALUE_NOT_AVAILABLE);
  		int bugs = CaratApplication.mPrefs.getInt(Constants.STATS_BUGS_COUNT_PREFERENCE_KEY, Constants.VALUE_NOT_AVAILABLE);
  		if (wellbehaved != Constants.VALUE_NOT_AVAILABLE && hogs != Constants.VALUE_NOT_AVAILABLE  && bugs != Constants.VALUE_NOT_AVAILABLE) {
  			// Log.i(TAG, "isStatsDataAvailable(), wellbehaved (fetched from the pref)=" + wellbehaved);
  			mWellbehaved = wellbehaved;
  			mHogs = hogs;
  			mBugs = bugs;
  			return true;
  		} else {
  			return false;
  		}
  	}
  	
  	public void GoToWifiScreen() {
      	safeStart(android.provider.Settings.ACTION_WIFI_SETTINGS, getString(R.string.wifisettings));
      }
  	
  	public void refreshSummaryFragment() {
  		if (frags.length > 0)
  			((SummaryFragment) frags[0]).scheduleRefresh();
  	}
  	
  	public Fragment getMydeviceFragment() {
          return frags[2];
      }
  	
  	public Fragment getHogsFragment() {
  		return frags[4];
  	}
  
  	public void setHogsFragment(Fragment hogsFragment) {
  		frags[4] = hogsFragment;
  	}
  
  	public Fragment getBugsFragment() {
  		return frags[3];
  	}
  
  	public void setBugsFragment(Fragment bugsFragment) {
  		frags[3] = bugsFragment;
  	}
  	
  	/**
  	 * A listener that is triggered when a value changes in our defualtSharedPreferences.
  	 * Can be used to do an immediate action whenever one of our items in that hashtable (defualtSharedPreferences) changes.
  	 * Should be registered (in our main activity's onStart()) and unregistered (in onStop())  
  	 */
  	// private OnSharedPreferenceChangeListener mOnSharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
      //      @Override
      //      public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
      //          Toast.makeText(CaratApplication.getMainActivity(), String.valueOf(sharedPreferences.getBoolean(key, false)), Toast.LENGTH_SHORT).show();
      //      }
      //  };
  	
  	public class PrefetchData extends AsyncTask<Void, Void, Void> {
  
  		String serverResponseJson = null;
  		private final String TAG = "PrefetchData";
  		
  		@Override
  	    protected Void doInBackground(Void... arg0) {
  	    	// Log.d(TAG, "started doInBackground() method of the asyncTask");
  	        JsonParser jsonParser = new JsonParser();
  	        try {
  	        	if (CaratApplication.isInternetAvailable()) {
  	        		serverResponseJson = jsonParser
  	        				.getJSONFromUrl("http://carat.cs.helsinki.fi/statistics-data/stats.json");
  	        	}
  	        } catch (Exception e) {
  	        }
  	        
  	        if (serverResponseJson != null && serverResponseJson != "") {
  	            try {
  	                JSONArray jsonArray = new JSONObject(serverResponseJson).getJSONArray("android-apps");
  	                // Using Java reflections to set fields by passing their name to a method
  	                try {
  						setIntFieldsFromJson(jsonArray, 0, "mWellbehaved");
  						setIntFieldsFromJson(jsonArray, 1, "mHogs");
  						setIntFieldsFromJson(jsonArray, 2, "mBugs");
  						
  						if (CaratApplication.mPrefs != null) {
  							saveStatsToPref();
  						} else {
  							// Log.e(TAG, "The shared preference is null (not loaded yet. "
  							//		+ "Check CaratApplication's new thread for loading the sharedPref)");
  						}
  						
  						// Log.i(TAG, "received JSON: " + "mBugs: " + mWellbehaved 
  						//		+ ", mHogs: " + mHogs + ", mBugs: " + mBugs);
  					} catch (IllegalArgumentException e) {
  						Log.e(TAG, "IllegalArgumentException in setFieldsFromJson()");
  					} catch (IllegalAccessException e) {
  						Log.e(TAG, "IllegalAccessException in setFieldsFromJson()");
  					}
  	            } catch (JSONException e) {
  	            	// Log.e(TAG, e.getStackTrace().toString());
  	            }
  	        } else {
  	        	// Log.d(TAG, "server response JSON is null.");
  	        }
  	        return null;
  	    }
  
  		@SuppressLint("NewApi")
  		private void saveStatsToPref() {
  			SharedPreferences.Editor editor = CaratApplication.mPrefs.edit();
  			// the returned values (from setIntFieldsFromJson()
  			// might be -1 (Constants.VALUE_NOT_AVAILABLE). So
  			// when we are reading the following pref values, we
  			// should check that condition )
  			editor.putInt(Constants.STATS_WELLBEHAVED_COUNT_PREFERENCE_KEY, mWellbehaved);
  			editor.putInt(Constants.STATS_HOGS_COUNT_PREFERENCE_KEY, mHogs);
  			editor.putInt(Constants.STATS_BUGS_COUNT_PREFERENCE_KEY, mBugs);
  
  			if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
  				editor.apply(); // async (runs in parallel
  								// in a new shared thread (off the UI thread)
  			} else {
  				editor.commit();
  			}
  		}
  
  		@Override
  		protected void onPostExecute(Void result) {
  			// Log.d(TAG, "started the onPostExecute() of the asyncTask");
  			super.onPostExecute(result);
  			// SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
  			// sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
  			// Log.d(TAG, "asyncTask.onPstExecute(). mWellbehaved=" + mWellbehaved);
  			refreshSummaryFragment();
  		}
  	    
  	    /**
  	     * Using Java reflections to set fields by passing their name to a method
  	     * @param jsonArray the json array from which we want to extract different json objects
  	     * @param objIdx the index of the object in the json array
  	     * @param fieldName the name of the field in the current NESTED class (PrefetchData)
  	     */
  		private void setIntFieldsFromJson(JSONArray jsonArray, int objIdx, String fieldName) 
  				throws JSONException, IllegalArgumentException, IllegalAccessException {
  			// Class<? extends PrefetchData> currentClass = this.getClass();
  			Field field = null;
  			int res = Constants.VALUE_NOT_AVAILABLE;
  			
  			try {
  				// important: getField() can only get PUBLIC fields. 
  				// For private fields, use another method: getDeclaredField(fieldName)
  				field = /*currentClass.*/ CaratApplication.getMainActivity().getClass().getField(fieldName);
  			} catch(NoSuchFieldException e) {
  				// Log.e(TAG, "NoSuchFieldException when trying to get a reference to the field: " + fieldName);
  			}
  			
  			if (field != null) {
  				JSONObject jsonObject = null;
  				if (jsonArray != null ) {
  					jsonObject = jsonArray.getJSONObject(objIdx);
  					if (jsonObject != null && jsonObject.getString("value") != null && jsonObject.getString("value") != "") {
  						res = Integer.parseInt(jsonObject.getString("value"));
  						field.set(CaratApplication.getMainActivity()/*this*/, res);
  					} else { 
  						// Log.e(TAG, "json object (server response) is null: jsonArray(" + objIdx + ")=null (or ='')");
  					}
  				}
  			}
  			// if an exception occurs, the value of the field would be -1 (Constants.VALUE_NOT_AVAILABLE)
  		}
  	}
  	
  	/**
  	 * Handle physical menu button (e.g. Samsung devices).
  	 */
  	public boolean onKeyUp(int keyCode, KeyEvent event) {
          if (keyCode == KeyEvent.KEYCODE_MENU) {
              boolean drawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList);
              if (drawerOpen) {
                  mDrawerLayout.closeDrawers();
              } else {
              	// FIXME: Gravity.Start is not available in API Level 8, so hack below.
              	int grav = Gravity.TOP|Gravity.LEFT;
              	if (isRTL())
              		grav = Gravity.TOP|Gravity.RIGHT;
                  mDrawerLayout.openDrawer(grav);
              }
              return true;
          } else {
              return super.onKeyUp(keyCode, event);
          }
      }
  	
  	private static boolean isRTL() {
  	    return isRTL(Locale.getDefault());
  	}
  
  	private static boolean isRTL(Locale locale) {
  	    final int directionality = Character.getDirectionality(locale.getDisplayName().charAt(0));
  	    return directionality == Character.DIRECTIONALITY_RIGHT_TO_LEFT ||
  	           directionality == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC;
  	}
  }