@Override
    protected StatsAdapter doInBackground(Boolean... refresh) {
      // do we need to refresh current
      if (refresh[0]) {
        // make sure to create a valid "current" stat
        StatsProvider.getInstance(StatsActivity.this).setCurrentReference(m_iSorting);
      }
      // super.doInBackground(params);
      m_listViewAdapter = null;
      try {
        Log.i(
            TAG,
            "LoadStatData: refreshing display for stats " + m_refFromName + " to " + m_refToName);
        m_listViewAdapter =
            new StatsAdapter(
                StatsActivity.this,
                StatsProvider.getInstance(StatsActivity.this)
                    .getStatList(m_iStat, m_refFromName, m_iSorting, m_refToName));
      } catch (BatteryInfoUnavailableException e) {
        // Log.e(TAG, e.getMessage(), e.fillInStackTrace());
        Log.e(TAG, "Exception: " + Log.getStackTraceString(e));
        m_exception = e;

      } catch (Exception e) {
        // Log.e(TAG, e.getMessage(), e.fillInStackTrace());
        Log.e(TAG, "Exception: " + Log.getStackTraceString(e));
        m_exception = e;
      }

      // StatsActivity.this.setListAdapter(m_listViewAdapter);
      // getStatList();
      return m_listViewAdapter;
    }
    //		@Override
    protected void onPostExecute(StatsAdapter o) {
      //			super.onPostExecute(o);
      // update hourglass
      try {
        if (m_progressDialog != null) {
          m_progressDialog.dismiss(); // hide();
          m_progressDialog = null;
        }
      } catch (Exception e) {
        // nop
      } finally {
        m_progressDialog = null;
      }

      if (m_exception != null) {
        if (m_exception instanceof BatteryInfoUnavailableException) {
          Toast.makeText(
                  StatsActivity.this,
                  "BatteryInfo Service could not be contacted.",
                  Toast.LENGTH_LONG)
              .show();

        } else {
          Toast.makeText(
                  StatsActivity.this,
                  "An unknown error occured while retrieving stats.",
                  Toast.LENGTH_LONG)
              .show();
        }
      }
      TextView tvSince = (TextView) findViewById(R.id.TextViewSince);
      Reference myReferenceFrom =
          ReferenceStore.getReferenceByName(m_refFromName, StatsActivity.this);
      Reference myReferenceTo = ReferenceStore.getReferenceByName(m_refToName, StatsActivity.this);

      long sinceMs =
          StatsProvider.getInstance(StatsActivity.this).getSince(myReferenceFrom, myReferenceTo);

      if (sinceMs != -1) {
        String sinceText = DateUtils.formatDuration(sinceMs);

        SharedPreferences sharedPrefs =
            PreferenceManager.getDefaultSharedPreferences(StatsActivity.this);
        boolean bShowBatteryLevels = sharedPrefs.getBoolean("show_batt", true);
        if (bShowBatteryLevels) {
          sinceText +=
              " "
                  + StatsProvider.getInstance(StatsActivity.this)
                      .getBatteryLevelFromTo(myReferenceFrom, myReferenceTo);
        }
        tvSince.setText(sinceText);
        Log.i(TAG, "Since " + sinceText);
      } else {
        tvSince.setText("n/a");
        Log.i(TAG, "Since: n/a ");
      }

      StatsActivity.this.setListAdapter(o);
    }
  /**
   * In order to refresh the ListView we need to re-create the Adapter (should be the case but
   * notifyDataSetChanged doesn't work so we recreate and set a new one)
   */
  private void setListViewAdapter() throws Exception {
    // make sure we only instanciate when the reference does not exist
    if (m_listViewAdapter == null) {
      m_listViewAdapter =
          new StatsAdapter(
              this,
              StatsProvider.getInstance(this)
                  .getStatList(m_iStat, m_refFromName, m_iSorting, m_refToName));

      setListAdapter(m_listViewAdapter);
    }
  }
  @Override
  public void onHandleIntent(Intent intent) {
    Log.i(TAG, "Called at " + DateUtils.now());
    try {
      Wakelock.aquireWakelock(this);
      // Store the "custom
      StatsProvider.getInstance(this).setCurrentReference(0);
      // Build the intent to update the widget
      Intent intentRefreshWidgets = new Intent(LargeWidgetProvider.WIDGET_UPDATE);
      this.sendBroadcast(intentRefreshWidgets);

      Intent i =
          new Intent(ReferenceStore.REF_UPDATED)
              .putExtra(Reference.EXTRA_REF_NAME, Reference.CURRENT_REF_FILENAME);
      this.sendBroadcast(i);

    } catch (Exception e) {
      Log.e(TAG, "An error occured: " + e.getMessage());
    } finally {
      Wakelock.releaseWakelock();
    }

    stopSelf();
  }
  /**
   * Take the change of selection from the spinners into account and refresh the ListView with the
   * right data
   */
  public void onItemSelected(AdapterView<?> parent, View v, int position, long id) {
    // this method is fired even if nothing has changed so we nee to find that out
    SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);

    boolean bChanged = false;

    // id is in the order of the spinners, 0 is stat, 1 is stat_type
    if (parent == (Spinner) findViewById(R.id.spinnerStatType)) {
      // detect if something changed
      String newStat = (String) ((ReferencesAdapter) parent.getAdapter()).getItemName(position);
      if ((m_refFromName != null) && (!m_refFromName.equals(newStat))) {
        Log.i(TAG, "Spinner from changed from " + m_refFromName + " to " + newStat);
        m_refFromName = newStat;
        bChanged = true;
        // we need to update the second spinner
        m_spinnerToAdapter.filterToSpinner(newStat, this);
        m_spinnerToAdapter.notifyDataSetChanged();

        // select the right element
        Spinner spinnerStatSampleEnd = (Spinner) findViewById(R.id.spinnerStatSampleEnd);
        if (spinnerStatSampleEnd.isShown()) {
          spinnerStatSampleEnd.setSelection(m_spinnerToAdapter.getPosition(m_refToName));
        } else {
          spinnerStatSampleEnd.setSelection(
              m_spinnerToAdapter.getPosition(Reference.CURRENT_REF_FILENAME));
        }

      } else {
        return;
      }

    } else if (parent == (Spinner) findViewById(R.id.spinnerStatSampleEnd)) {
      String newStat = (String) ((ReferencesAdapter) parent.getAdapter()).getItemName(position);
      if ((m_refFromName != null) && (!m_refToName.equals(newStat))) {
        Log.i(TAG, "Spinner to changed from " + m_refToName + " to " + newStat);
        m_refToName = newStat;
        bChanged = true;
      } else {
        return;
      }

    } else if (parent == (Spinner) findViewById(R.id.spinnerStat)) {
      int iNewStat = position;
      if (m_iStat != iNewStat) {
        m_iStat = iNewStat;
        bChanged = true;
      } else {
        return;
      }

      // inform the user when he tries to use functions requiring root and he doesn't have root
      // enabled
      boolean rootEnabled = sharedPrefs.getBoolean("root_features", false);

      if (!rootEnabled) {
        if ((m_iStat == 4) || (m_iStat == 3)) {
          Toast.makeText(
                  this,
                  "This function requires root access. Check \"Advanced\" preferences",
                  Toast.LENGTH_LONG)
              .show();
        }
      }

    } else {
      Log.e(TAG, "ProcessStatsActivity.onItemSelected error. ID could not be resolved");
      Toast.makeText(this, "Error: could not resolve what changed", Toast.LENGTH_SHORT).show();
    }

    Reference myReferenceFrom = ReferenceStore.getReferenceByName(m_refFromName, this);
    Reference myReferenceTo = ReferenceStore.getReferenceByName(m_refToName, this);

    TextView tvSince = (TextView) findViewById(R.id.TextViewSince);
    //        long sinceMs = getSince();
    long sinceMs = StatsProvider.getInstance(this).getSince(myReferenceFrom, myReferenceTo);

    if (sinceMs != -1) {
      String sinceText = DateUtils.formatDuration(sinceMs);
      boolean bShowBatteryLevels = sharedPrefs.getBoolean("show_batt", true);
      if (bShowBatteryLevels) {

        sinceText +=
            " "
                + StatsProvider.getInstance(this)
                    .getBatteryLevelFromTo(myReferenceFrom, myReferenceTo);
      }
      tvSince.setText(sinceText);
      Log.i(TAG, "Since " + sinceText);
    } else {
      tvSince.setText("n/a ");
      Log.i(TAG, "Since: n/a ");
    }
    // @todo fix this: this method is called twice
    // m_listViewAdapter.notifyDataSetChanged();
    if (bChanged) {
      GoogleAnalytics.getInstance(this)
          .trackStats(
              this,
              GoogleAnalytics.ACTIVITY_STATS,
              m_iStat,
              m_refFromName,
              m_refToName,
              m_iSorting);
      // new LoadStatData().execute(this);
      // as the source changed fetch the data
      doRefresh(false);
    }
  }
  /** @see android.app.Activity#onCreate(Bundle@SuppressWarnings("rawtypes") */
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    Log.i(TAG, "OnCreated called");
    super.onCreate(savedInstanceState);
    setContentView(R.layout.stats);
    SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);

    // set debugging
    if (sharedPrefs.getBoolean("debug_logging", false)) {
      LogSettings.DEBUG = true;
      CommonLogSettings.DEBUG = true;
    } else {
      LogSettings.DEBUG = false;
      CommonLogSettings.DEBUG = false;
    }

    // Check if the stats are accessible and warn if not
    BatteryStatsProxy stats = BatteryStatsProxy.getInstance(this);

    if (stats.initFailed()) {
      Toast.makeText(
              this,
              "The 'batteryinfo' service could not be accessed. If this error persists after a reboot please contact the dev and provide your ROM/Kernel versions.",
              Toast.LENGTH_SHORT)
          .show();
    }

    ///////////////////////////////////////////////
    // check if we have a new release
    ///////////////////////////////////////////////
    // if yes do some migration (if required) and show release notes
    String strLastRelease = sharedPrefs.getString("last_release", "0");

    String strCurrentRelease = "";
    try {
      PackageInfo pinfo = getPackageManager().getPackageInfo(getPackageName(), 0);

      strCurrentRelease = Integer.toString(pinfo.versionCode);
    } catch (Exception e) {
      // nop strCurrentRelease is set to ""
    }

    if (strLastRelease.equals("0")) {
      // show the initial run screen
      FirstLaunch.app_launched(this);

    } else if (!strLastRelease.equals(strCurrentRelease)) {
      // save the current release to properties so that the dialog won't be shown till next version
      SharedPreferences.Editor updater = sharedPrefs.edit();
      updater.putString("last_release", strCurrentRelease);
      updater.commit();

      boolean migrated = false;

      //////////////////////////////////////////////////////////////////////////
      // Fix for bad migration to 1.12
      //////////////////////////////////////////////////////////////////////////
      if (!sharedPrefs.getString("default_stat_type", "0").startsWith("ref_")) {
        Log.i(
            TAG,
            "Migrating default_stat_type, value was "
                + sharedPrefs.getString("default_stat_type", "0"));
        SharedPreferences.Editor editor = sharedPrefs.edit();
        editor.putString("default_stat_type", Reference.UNPLUGGED_REF_FILENAME);
        editor.commit();
        migrated = true;
      }
      if (!sharedPrefs.getString("small_widget_default_stat_type", "0").startsWith("ref_")) {
        Log.i(
            TAG,
            "Migrating small_widget_default_stat_type, value was "
                + sharedPrefs.getString("small_widget_default_stat_type", "0"));
        SharedPreferences.Editor editor = sharedPrefs.edit();
        editor.putString("small_widget_default_stat_type", Reference.UNPLUGGED_REF_FILENAME);
        editor.commit();
        migrated = true;
      }
      if (!sharedPrefs.getString("widget_fallback_stat_type", "0").startsWith("ref_")) {
        Log.i(
            TAG,
            "Migrating widget_fallback_stat_type, value was "
                + sharedPrefs.getString("widget_fallback_stat_type", "0"));
        SharedPreferences.Editor editor = sharedPrefs.edit();
        editor.putString("widget_fallback_stat_type", Reference.BOOT_REF_FILENAME);
        editor.commit();
        migrated = true;
      }
      if (!sharedPrefs.getString("large_widget_default_stat_type", "0").startsWith("ref_")) {
        Log.i(
            TAG,
            "Migrating large_widget_default_stat_type, value was "
                + sharedPrefs.getString("large_widget_default_stat_type", "0"));
        SharedPreferences.Editor editor = sharedPrefs.edit();
        editor.putString("large_widget_default_stat_type", Reference.UNPLUGGED_REF_FILENAME);
        editor.commit();
        migrated = true;
      }

      if (migrated) {
        Log.i(TAG, "Some preferences were migrated");
        Toast.makeText(this, "Upgrading data.", Toast.LENGTH_SHORT).show();
      }
      if (strCurrentRelease.equals("38")) {
        // we have changed the serialized format: delete reference and re-create umplugged and boot
        Toast.makeText(this, "Deleting and re-creating references", Toast.LENGTH_SHORT).show();
        ReferenceStore.deleteAllRefs(this);
        Intent i = new Intent(this, WriteBootReferenceService.class);
        this.startService(i);
        i = new Intent(this, WriteUnpluggedReferenceService.class);
        this.startService(i);
      }

      // show the readme
      Intent intentReleaseNotes = new Intent(this, ReadmeActivity.class);
      intentReleaseNotes.putExtra("filename", "readme.html");
      this.startActivity(intentReleaseNotes);

    } else {
      // can't do this at the same time as the popup dialog would be masked by the readme
      ///////////////////////////////////////////////
      // check if we have shown the opt-out from analytics
      ///////////////////////////////////////////////
      boolean bWarningShown = sharedPrefs.getBoolean("analytics_opt_out", false);
      boolean bAnalyticsEnabled = sharedPrefs.getBoolean("use_analytics", true);
      if (bAnalyticsEnabled && !bWarningShown) {
        // prepare the alert box
        AlertDialog.Builder alertbox = new AlertDialog.Builder(this);

        // set the message to display
        alertbox.setMessage(
            "BetterBatteryStats makes use of Google Analytics to collect usage statitics. If you disagree or do not want to participate you can opt-out by disabling \"Google Analytics\" in the \"Advanced Preferences\"");

        // add a neutral button to the alert box and assign a click listener
        alertbox.setNeutralButton(
            "Ok",
            new DialogInterface.OnClickListener() {

              // click listener on the alert box
              public void onClick(DialogInterface arg0, int arg1) {
                // opt out info was displayed
                SharedPreferences prefs =
                    PreferenceManager.getDefaultSharedPreferences(StatsActivity.this);
                SharedPreferences.Editor editor = prefs.edit();
                editor.putBoolean("analytics_opt_out", true);
                editor.commit();
              }
            });

        // show it
        alertbox.show();

      } else {
        // show "rate" dialog
        // for testing: AppRater.showRateDialog(this, null);
        AppRater.app_launched(this);
      }
    }

    ///////////////////////////////////////////////
    // retrieve default selections for spinners
    // if none were passed
    ///////////////////////////////////////////////

    m_iStat = Integer.valueOf(sharedPrefs.getString("default_stat", "0"));
    m_refFromName = sharedPrefs.getString("default_stat_type", Reference.UNPLUGGED_REF_FILENAME);

    if (!ReferenceStore.hasReferenceByName(m_refFromName, this)) {
      if (sharedPrefs.getBoolean("fallback_to_since_boot", false)) {
        m_refFromName = Reference.BOOT_REF_FILENAME;
        Toast.makeText(this, "Fallback to 'Since Boot'", Toast.LENGTH_SHORT).show();
      }
    }

    try {
      // recover any saved state
      if ((savedInstanceState != null) && (!savedInstanceState.isEmpty())) {
        m_iStat = (Integer) savedInstanceState.getSerializable("stat");
        m_refFromName = (String) savedInstanceState.getSerializable("stattypeFrom");
        m_refToName = (String) savedInstanceState.getSerializable("stattypeTo");
      }
    } catch (Exception e) {
      m_iStat = Integer.valueOf(sharedPrefs.getString("default_stat", "0"));
      m_refFromName = sharedPrefs.getString("default_stat_type", Reference.UNPLUGGED_REF_FILENAME);

      Log.e(TAG, "Exception: " + e.getMessage());
      DataStorage.LogToFile(LOGFILE, "Exception in onCreate restoring Bundle");
      DataStorage.LogToFile(LOGFILE, e.getMessage());
      DataStorage.LogToFile(LOGFILE, e.getStackTrace());

      Toast.makeText(
              this, "An error occured while recovering the previous state", Toast.LENGTH_SHORT)
          .show();
    }

    // Handle the case the Activity was called from an intent with paramaters
    Bundle extras = getIntent().getExtras();
    if (extras != null) {
      // Override if some values were passed to the intent
      m_iStat = extras.getInt(StatsActivity.STAT);
      m_refFromName = extras.getString(StatsActivity.STAT_TYPE_FROM);
      m_refToName = extras.getString(StatsActivity.STAT_TYPE_TO);
      boolean bCalledFromNotification = extras.getBoolean(StatsActivity.FROM_NOTIFICATION, false);

      // Clear the notifications that was clicked to call the activity
      if (bCalledFromNotification) {
        NotificationManager nM =
            (NotificationManager) getSystemService(Service.NOTIFICATION_SERVICE);
        nM.cancel(EventWatcherService.NOTFICATION_ID);
      }
    }

    // Display the reference of the stat
    TextView tvSince = (TextView) findViewById(R.id.TextViewSince);
    if (tvSince != null) {
      Reference myReferenceFrom = ReferenceStore.getReferenceByName(m_refFromName, this);
      Reference myReferenceTo = ReferenceStore.getReferenceByName(m_refToName, this);

      long sinceMs = StatsProvider.getInstance(this).getSince(myReferenceFrom, myReferenceTo);
      if (sinceMs != -1) {
        String sinceText = DateUtils.formatDuration(sinceMs);
        boolean bShowBatteryLevels = sharedPrefs.getBoolean("show_batt", true);
        if (bShowBatteryLevels) {
          sinceText +=
              " "
                  + StatsProvider.getInstance(this)
                      .getBatteryLevelFromTo(myReferenceFrom, myReferenceTo);
        }
        tvSince.setText(sinceText);
        Log.i(TAG, "Since " + sinceText);
      } else {
        tvSince.setText("n/a");
        Log.i(TAG, "Since: n/a ");
      }
    }

    // Spinner for selecting the stat
    Spinner spinnerStat = (Spinner) findViewById(R.id.spinnerStat);

    ArrayAdapter spinnerStatAdapter =
        ArrayAdapter.createFromResource(this, R.array.stats, android.R.layout.simple_spinner_item);
    spinnerStatAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);

    spinnerStat.setAdapter(spinnerStatAdapter);
    // setSelection MUST be called after setAdapter
    spinnerStat.setSelection(m_iStat);
    spinnerStat.setOnItemSelectedListener(this);

    ///////////////////////////////////////////////
    // Spinner for Selecting the Stat type
    ///////////////////////////////////////////////
    Spinner spinnerStatType = (Spinner) findViewById(R.id.spinnerStatType);
    m_spinnerFromAdapter = new ReferencesAdapter(this, android.R.layout.simple_spinner_item);
    m_spinnerFromAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    spinnerStatType.setAdapter(m_spinnerFromAdapter);

    try {
      this.setListViewAdapter();
    } catch (BatteryInfoUnavailableException e) {
      //			Log.e(TAG, e.getMessage(), e.fillInStackTrace());
      Log.e(TAG, "Exception: " + Log.getStackTraceString(e));
      Toast.makeText(this, "BatteryInfo Service could not be contacted.", Toast.LENGTH_LONG).show();

    } catch (Exception e) {
      // Log.e(TAG, e.getMessage(), e.fillInStackTrace());
      Log.e(TAG, "Exception: " + Log.getStackTraceString(e));
      Toast.makeText(
              this, "An unhandled error occured. Please check your logcat", Toast.LENGTH_LONG)
          .show();
    }
    // setSelection MUST be called after setAdapter
    spinnerStatType.setSelection(m_spinnerFromAdapter.getPosition(m_refFromName));
    spinnerStatType.setOnItemSelectedListener(this);

    ///////////////////////////////////////////////
    // Spinner for Selecting the end sample
    ///////////////////////////////////////////////
    Spinner spinnerStatSampleEnd = (Spinner) findViewById(R.id.spinnerStatSampleEnd);
    m_spinnerToAdapter = new ReferencesAdapter(this, android.R.layout.simple_spinner_item);
    m_spinnerToAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);

    boolean bShowSpinner = sharedPrefs.getBoolean("show_to_ref", true);
    if (bShowSpinner) {
      spinnerStatSampleEnd.setVisibility(View.VISIBLE);
      spinnerStatSampleEnd.setAdapter(m_spinnerToAdapter);
      // setSelection must be called after setAdapter
      if ((m_refToName != null) && !m_refToName.equals("")) {
        int pos = m_spinnerToAdapter.getPosition(m_refToName);
        spinnerStatSampleEnd.setSelection(pos);

      } else {
        spinnerStatSampleEnd.setSelection(
            m_spinnerToAdapter.getPosition(Reference.CURRENT_REF_FILENAME));
      }

    } else {
      spinnerStatSampleEnd.setVisibility(View.GONE);
      spinnerStatSampleEnd.setAdapter(m_spinnerToAdapter);
      // setSelection must be called after setAdapter
      spinnerStatSampleEnd.setSelection(
          m_spinnerToAdapter.getPosition(Reference.CURRENT_REF_FILENAME));
    }

    spinnerStatSampleEnd.setOnItemSelectedListener(this);

    ///////////////////////////////////////////////
    // sorting
    ///////////////////////////////////////////////
    String strOrderBy = sharedPrefs.getString("default_orderby", "0");
    try {
      m_iSorting = Integer.valueOf(strOrderBy);
    } catch (Exception e) {
      // handle error here
      m_iSorting = 0;
    }
    GoogleAnalytics.getInstance(this)
        .trackStats(
            this, GoogleAnalytics.ACTIVITY_STATS, m_iStat, m_refFromName, m_refToName, m_iSorting);

    // Set up a listener whenever a key changes
    PreferenceManager.getDefaultSharedPreferences(this)
        .registerOnSharedPreferenceChangeListener(this);

    // log reference store
    ReferenceStore.logReferences(this);
  }