/** {@inheritDoc} */
 @Override
 protected void onNewIntent(final Intent intent) {
   super.onNewIntent(intent);
   {
     Debug.print(tag, "new intent", intent);
   }
 }
 /** {@inheritDoc} */
 @Override
 protected void onRestart() {
   super.onRestart();
   {
     Debug.print(tag, "restart");
   }
 }
 /**
  * Initialize and return this.database object.
  *
  * @return SQLiteDatabase properly initialized.
  * @throws IOException on error.
  */
 @Override
 protected SQLiteDatabase getDatabase() throws IOException {
   Debug.enter();
   final SQLiteDatabase database = DatabaseSession.getDatabase(Android.App.INSTANCE);
   Debug.leave();
   return database;
 }
 /**
  * Initialize and return path, relative from the "home" directory.
  *
  * @param path inside $HOME
  * @return File($HOME, path) properly initialized
  * @throws IOException on error
  */
 @Override
 protected File getHome(final String path) throws IOException {
   // @see http://en.wikipedia.org/wiki/Double-checked_locking. The use of local variable
   // 'directory' seems
   // unnecessary. It will make the code 25% faster for some Java VM, and it won't hurt for others
   File directory = home;
   if (directory != null) {
     Debug.print("home", directory.getAbsolutePath());
     return ((path == null) || (path.length() <= 0)) ? directory : new File(directory, path);
   }
   Debug.enter();
   synchronized (LOCK) {
     directory = home;
     if (directory != null) {
       Debug.print("lost getHome singleton race");
     } else {
       // initialize home
       directory = new File(Android.App.DATA_DIRECTORY_NAME);
       // next will be very slow
       restoreMissingFiles(directory);
       // finally, home
       home = directory;
     }
   }
   Debug.leave(directory.getAbsolutePath());
   return ((path == null) || (path.length() <= 0)) ? directory : new File(directory, path);
 }
  /**
   * Load {@link Bookmark} instance, creating it if it does not exist.
   *
   * @param title the {@link Bookmark}'s title to load.
   * @return the {@link Bookmark} for the given <code>title</code>, or null if <code>title</code> is
   *     null.
   */
  protected Bookmark loadBookmark(final String title) {
    Debug.enter(title);

    Bookmark bookmark = null;
    final String titleValue = (title != null) ? title.trim() : "";
    if (0 < titleValue.length()) {
      synchronized (AbstractProfileCRUD.LOCK) {
        String basename = AbstractUserProfileStorage.getBasename(titleValue);
        final boolean adding = (basename == null);

        if (adding) {
          basename = AbstractUserProfileStorage.getBasename();
        }

        final SharedPreferences prefs = AbstractProfile.getSharedPreferences(basename);
        bookmark = readBookmark(prefs, basename, titleValue);

        if (adding) {
          writeBookmark(prefs, bookmark);
        }
      }
    }

    Debug.leave("loaded", titleValue, (bookmark != null) ? bookmark.title : null);
    return bookmark;
  }
 /** {@inheritDoc} */
 @Override
 public void flash(final String message) {
   Debug.enter(tag, message);
   if (message != null) {
     Toast.makeText(this, message, Toast.LENGTH_LONG).show();
   }
   Debug.leave(tag);
 }
 /** {@inheritDoc} */
 @Override
 public boolean isTerminated() {
   Debug.enter(tag);
   boolean terminated = false;
   if (wasDestroyed || isFinishing()) {
     Debug.print(tag, "activity terminated");
     terminated = wasDestroyed = true;
   }
   Debug.leave(tag, terminated);
   return terminated;
 }
  /**
   * Save {@link Bookmark} instance.
   *
   * @param bookmark to save.
   * @return true on success, false otherwise.
   */
  protected boolean saveBookmark(final Bookmark bookmark) {
    Debug.enter();

    boolean success = false;
    if (bookmark != null && bookmark.title != null) {
      synchronized (AbstractProfileCRUD.LOCK) {
        final SharedPreferences prefs = bookmark.getSharedPreferences();
        success = writeBookmark(prefs, bookmark);
      }
    }

    Debug.leave("saved?", success, (bookmark != null) ? bookmark.title : null);
    return success;
  }
 /** {@inheritDoc} */
 @Override
 public void onConfigurationChanged(final Configuration newConfig) {
   super.onConfigurationChanged(newConfig);
   {
     Debug.enter();
     try {
       Debug.print(tag, "configurationChanged", newConfig.locale);
       Android.App.setLocale(newConfig.locale);
     } catch (final IOException ex) {
       Debug.error(ex, tag, "unable to set new locale", newConfig.locale);
     }
     Debug.leave();
   }
 }
 /** {@inheritDoc} */
 @Override
 protected void onStop() {
   Debug.print(tag, "stop");
   {
     super.onStop();
   }
 }
 /** {@inheritDoc} */
 @Override
 protected void onPause() {
   Debug.print(tag, "pause");
   {
     super.onPause();
   }
 }
 /** {@inheritDoc} */
 @Override
 protected void onRestoreInstanceState(final Bundle savedInstanceState) {
   super.onRestoreInstanceState(savedInstanceState);
   {
     Debug.print(tag, "restoreInstanceState");
   }
 }
 /**
  * Called when the application is created. Override this method to initialize our application
  * singleton and to create and initialize any application state variables or shared resources.
  */
 @Override
 public void onCreate() {
   super.onCreate();
   {
     Debug.print("create");
   }
 }
 /** {@inheritDoc} */
 @Override
 protected void onSaveInstanceState(final Bundle instanceState) {
   {
     Debug.print(tag, "saveInstanceState");
   }
   super.onSaveInstanceState(instanceState);
 }
 /**
  * Called when a background process have already been terminated and the current foreground
  * applications are still low on memory. Override this method to clear caches or release
  * unnecessary resources.
  */
 @Override
 public void onLowMemory() {
   super.onLowMemory();
   {
     Debug.print("lowMemory");
     DatabaseSession.close(); // to free buffers
   }
 }
 /**
  * Called when the application object is terminated. There is no guarantee of this method being
  * called.
  */
 @Override
 public void onTerminate() {
   {
     Debug.print("terminate");
     DatabaseSession.close();
   }
   super.onTerminate();
 }
 /** {@inheritDoc} */
 @Override
 protected void onDestroy() {
   {
     Debug.print(tag, "destroy");
     wasDestroyed = true;
   }
   super.onDestroy();
 }
 /** {@inheritDoc} */
 @Override
 protected void onResume() {
   super.onResume();
   {
     Debug.print(tag, "resume");
     if (controller != null) {
       controller.register(this);
     }
   }
 }
 /** {@inheritDoc} */
 @Override
 public void onCreate(final Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   {
     Debug.print(tag, "create");
     final Window window = getWindow();
     // increase the color range a lot
     window.setFormat(PixelFormat.RGBA_8888);
     // activate dithering for all your activity
     window.addFlags(WindowManager.LayoutParams.FLAG_DITHER);
   }
 }
 /**
  * Setup missing application files into the "home directory".
  *
  * @param targetDir
  * @throws IOException
  * @return true on success
  */
 private boolean restoreMissingFiles(final File targetDir) throws IOException {
   Debug.enter();
   boolean success = false;
   final SharedPreferences prefs = Settings.INSTANCE.getSharedPreferences();
   final String oldTarball = prefs.getString(SettingsStorage.FIELD_TARBALL, null);
   final String newTarball = getString(R.string.build_tarball);
   final boolean keepOldFiles = newTarball.equals(oldTarball);
   try {
     // extract the tar-ball to recover any missing file, if any
     final TarArchive tarball = new TarArchive(new GZIPInputStream(tarballFile()));
     tarball.keepOldFiles = keepOldFiles;
     tarball.extract(targetDir);
     tarball.close();
     // once the restore is completed successfully,
     // we need to update the shared preference, if necessary
     if (!keepOldFiles) {
       final SharedPreferences.Editor edit = prefs.edit();
       edit.putString(SettingsStorage.FIELD_TARBALL, newTarball);
       success = edit.commit();
     } else {
       success = true;
     }
   } catch (final Resources.NotFoundException ex) {
     // this exception usually would happen from the unit-tests
     Debug.error(ex, "tarball resources not found", keepOldFiles);
     // not really a fatal error if keepOldFiles is true
     if (!keepOldFiles) {
       Debug.leave();
       throw new IOException("Tarball resources missing");
     }
   } catch (final TarArchive.TarHeaderException ex) {
     Debug.error(ex, "invalid tarball header", keepOldFiles);
     Debug.leave();
     throw new IOException("Invalid tarball format");
   } catch (final IOException ex) {
     Debug.error(ex, "unable to install tarball", keepOldFiles);
     // not really a fatal error if keepOldFiles is true
     if (!keepOldFiles) {
       Debug.leave();
       throw ex;
     }
   }
   Debug.leave(success);
   return success;
 }
 /** {@inheritDoc} */
 @Override
 public void onControllerUpdated() {
   Debug.print(tag, "model/view/controller updated");
 }
 /**
  * Setter - set {@link AbstractController}.
  *
  * @param controller to setup.
  */
 protected void setController(final AbstractController controller) {
   Debug.enter(tag, controller);
   this.controller = controller;
   Debug.leave(tag);
 }
/**
 * {@link Activity} base class. All activities in the final application should be derived from this
 * class.
 *
 * @see "http://code.google.com/p/android-family-browser/"
 * @author <a href="mailto:[email protected]">David A Chaves</a>
 */
public abstract class AbstractActivity extends Activity implements Controllable {
  /** Internal name, used to distinguish which instance is being used in log messages. */
  protected String tag = Debug.getUniqueTag(AbstractActivity.class);

  /** Handler for posting actions. */
  protected final Handler handler = new Handler();

  /** Has {@link AbstractActivity#onDestroy} been called? */
  private volatile boolean wasDestroyed;

  /** The Activity controller. */
  private AbstractController controller;

  // -------
  // Setters
  // -------

  /**
   * Setter - set {@link AbstractController}.
   *
   * @param controller to setup.
   */
  protected void setController(final AbstractController controller) {
    Debug.enter(tag, controller);
    this.controller = controller;
    Debug.leave(tag);
  }

  // --------------------
  // Controller interface
  // --------------------

  /** {@inheritDoc} */
  @Override
  public boolean isTerminated() {
    Debug.enter(tag);
    boolean terminated = false;
    if (wasDestroyed || isFinishing()) {
      Debug.print(tag, "activity terminated");
      terminated = wasDestroyed = true;
    }
    Debug.leave(tag, terminated);
    return terminated;
  }

  /** {@inheritDoc} */
  @Override
  public void onControllerUpdated() {
    Debug.print(tag, "model/view/controller updated");
  }

  /** {@inheritDoc} */
  @Override
  public void flash(final String message) {
    Debug.enter(tag, message);
    if (message != null) {
      Toast.makeText(this, message, Toast.LENGTH_LONG).show();
    }
    Debug.leave(tag);
  }

  // ----------
  // Life Cycle
  // ----------

  /** {@inheritDoc} */
  @Override
  public void onCreate(final Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    {
      Debug.print(tag, "create");
      final Window window = getWindow();
      // increase the color range a lot
      window.setFormat(PixelFormat.RGBA_8888);
      // activate dithering for all your activity
      window.addFlags(WindowManager.LayoutParams.FLAG_DITHER);
    }
  }

  /** {@inheritDoc} */
  @Override
  protected void onStart() {
    super.onStart();
    {
      Debug.print(tag, "start");
    }
  }

  /** {@inheritDoc} */
  @Override
  protected void onResume() {
    super.onResume();
    {
      Debug.print(tag, "resume");
      if (controller != null) {
        controller.register(this);
      }
    }
  }

  /** {@inheritDoc} */
  @Override
  protected void onPause() {
    Debug.print(tag, "pause");
    {
      super.onPause();
    }
  }

  /** {@inheritDoc} */
  @Override
  protected void onStop() {
    Debug.print(tag, "stop");
    {
      super.onStop();
    }
  }

  /** {@inheritDoc} */
  @Override
  protected void onRestart() {
    super.onRestart();
    {
      Debug.print(tag, "restart");
    }
  }

  /** {@inheritDoc} */
  @Override
  protected void onDestroy() {
    {
      Debug.print(tag, "destroy");
      wasDestroyed = true;
    }
    super.onDestroy();
  }

  // ----------
  // Call backs
  // ----------

  /** {@inheritDoc} */
  @Override
  protected void onRestoreInstanceState(final Bundle savedInstanceState) {
    super.onRestoreInstanceState(savedInstanceState);
    {
      Debug.print(tag, "restoreInstanceState");
    }
  }

  /** {@inheritDoc} */
  @Override
  protected void onSaveInstanceState(final Bundle instanceState) {
    {
      Debug.print(tag, "saveInstanceState");
    }
    super.onSaveInstanceState(instanceState);
  }

  /** {@inheritDoc} */
  @Override
  public void onConfigurationChanged(final Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    {
      Debug.enter();
      try {
        Debug.print(tag, "configurationChanged", newConfig.locale);
        Android.App.setLocale(newConfig.locale);
      } catch (final IOException ex) {
        Debug.error(ex, tag, "unable to set new locale", newConfig.locale);
      }
      Debug.leave();
    }
  }

  /** {@inheritDoc} */
  @Override
  protected void onNewIntent(final Intent intent) {
    super.onNewIntent(intent);
    {
      Debug.print(tag, "new intent", intent);
    }
  }
}
 /**
  * Change language for this application only (not system-wide).
  *
  * @param locale the new {@link Locale} instance.
  * @throws IOException on error.
  */
 @Override
 protected void setLocale(final Locale locale) throws IOException {
   Debug.enter(locale);
   DatabaseSession.setLocale(Android.App.INSTANCE, locale);
   Debug.leave(locale);
 }