All Downloads are FREE. Search and download functionalities are using the official Maven repository.

it.tidalwave.bluebill.mobile.taxonomy.factsheet.sound.TaxonSoundFactSheetController Maven / Gradle / Ivy

/***********************************************************************************************************************
 *
 * blueBill Mobile - open source birdwatching
 * ==========================================
 *
 * Copyright (C) 2009, 2010 by Tidalwave s.a.s. (http://www.tidalwave.it)
 * http://bluebill.tidalwave.it/mobile/
 *
 ***********************************************************************************************************************
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 *
 ***********************************************************************************************************************
 *
 * $Id: TaxonSoundFactSheetController.java,v 7facb9eec0c2 2010/07/06 15:48:36 fabrizio $
 *
 **********************************************************************************************************************/
package it.tidalwave.bluebill.mobile.taxonomy.factsheet.sound;

import javax.annotation.CheckForNull;
import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
import javax.inject.Provider;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Arrays;
import java.util.List;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.Reader;
import org.openide.util.NbBundle;
import it.tidalwave.util.AsException;
import it.tidalwave.util.Identifiable;
import it.tidalwave.util.Removable;
import it.tidalwave.util.logging.Logger;
import it.tidalwave.netbeans.util.Locator;
import it.tidalwave.mobile.util.Downloadable;
import it.tidalwave.mobile.util.Downloadable.Status;
import it.tidalwave.mobile.io.MasterFileSystem;
import it.tidalwave.mobile.media.MediaPlayer;
import it.tidalwave.mobile.media.MediaPlayer.Controller;
import it.tidalwave.mobile.media.Media;
import it.tidalwave.semantic.io.json.RdfJsonUnmarshaller;
import it.tidalwave.bluebill.observation.Observation;
import it.tidalwave.bluebill.taxonomy.Taxon;
import it.tidalwave.bluebill.observation.ObservationSet;
import it.tidalwave.bluebill.mobile.network.NetworkingPreferences;
import static it.tidalwave.mobile.util.Downloadable.Status.*;

/***********************************************************************************************************************
 *
 * @author  Fabrizio Giudici
 * @version $Id: $
 *
 **********************************************************************************************************************/
public abstract class TaxonSoundFactSheetController
  {
    private static final String CLASS = TaxonSoundFactSheetController.class.getName();
    private static final Logger logger = Logger.getLogger(CLASS);

    private final static double MEGA = 1024.0 * 1024.0;
    /* package */ final static long MIN_FREE_SPACE = 20L * 1024 * 1024;
    private final static List WATCHED_PROPERTIES = Arrays.asList(Downloadable.PROP_STATUS, Downloadable.PROP_DOWNLOAD_PROGRESS);

    private enum Operation
      {
        DOWNLOAD
          {
            public void run (final @Nonnull Downloadable downloadable)
              {
                downloadable.download();
              }
          },
        REFRESH
          {
            public void run (final @Nonnull Downloadable downloadable)
              {
                downloadable.refresh();
              }
          };

        public abstract void run (@Nonnull Downloadable downloadable);
      }

    public static enum DefaultAction
      {
        DO_NOTHING
          {
            public void run (final @Nonnull TaxonSoundFactSheetController controller, final @Nonnegative int index)
              {
              }
          },
        DOWNLOAD
          {
            public void run (final @Nonnull TaxonSoundFactSheetController controller, final @Nonnegative int index)
              {
                controller.downloadMedia(index);
              }
          },
        PLAY
          {
            public void run (final @Nonnull TaxonSoundFactSheetController controller, final @Nonnegative int index)
              {
                controller.playMedia(index);
              }
          },
        STOP
          {
            public void run (final @Nonnull TaxonSoundFactSheetController controller, final @Nonnegative int index)
              {
                controller.stopPlayer();
              }
          };

        public abstract void run (@Nonnull TaxonSoundFactSheetController controller, @Nonnegative int index);
      }

    @Nonnull
    private final TaxonSoundFactSheetUI ui;

    @Nonnull
    protected final Taxon taxon;

    @CheckForNull
    /* package */ Controller controller;

    @Nonnull
    private final Provider mediaPlayer = Locator.createProviderFor(MediaPlayer.class);

    @Nonnull
    private final Provider masterFileSystem = Locator.createProviderFor(MasterFileSystem.class);

    private int playingIndex;

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    /* package */ final PropertyChangeListener mediaChangeListener = new PropertyChangeListener()
      {
        public void propertyChange (final @Nonnull PropertyChangeEvent event)
          {
            notifyStatusChanged();
          }
      };

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    /* package */ final PropertyChangeListener downloadablePropertyChangeListener = new PropertyChangeListener()
      {
        public void propertyChange (final @Nonnull PropertyChangeEvent event)
          {
//            logger.info("propertyChange(%s)", event);

            if (WATCHED_PROPERTIES.contains(event.getPropertyName()) && (event.getOldValue() != event.getNewValue()))
              {
                notifyStatusChanged();

                if (event.getPropertyName().equals(Downloadable.PROP_STATUS))
                  {
                    switch ((Status)event.getNewValue())
                      {
                        case DOWNLOADED:
                          final double freeSpace = masterFileSystem.get().getExternalFileSystem().getFreeSpace() / MEGA;
                          ui.notify(NbBundle.getMessage(TaxonSoundFactSheetController.class, "downloadComplete", freeSpace));
                          ((Downloadable)event.getSource()).removePropertyChangeListener(downloadablePropertyChangeListener);
                          break;

                        case BROKEN:
                          ui.notify(NbBundle.getMessage(TaxonSoundFactSheetController.class, "downloadFailed"));
                          ((Downloadable)event.getSource()).removePropertyChangeListener(downloadablePropertyChangeListener);
                          break;

                        case DOWNLOADING:
                        case NOT_DOWNLOADED:
                        case OBSOLETE:
                        case QUEUED:
                      }
                  }
              }
          }
      };

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    public TaxonSoundFactSheetController (final @Nonnull TaxonSoundFactSheetUI ui, final @Nonnull Taxon taxon)
      throws IOException
      {
        this.ui = ui;
        this.taxon = taxon;
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    public void disposePlayer()
      {
        if (controller != null)
          {
            controller.removePropertyChangeListener(mediaChangeListener);
            controller.dispose();
            controller = null;
          }
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    public boolean isDownloadable (final @Nonnegative int index)
      {
        return isStatus(index, NOT_DOWNLOADED, OBSOLETE, BROKEN);
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    public boolean isRefreshable (final @Nonnegative int index)
      {
        return isStatus(index, DOWNLOADED, OBSOLETE, BROKEN);
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    public boolean isDeletable (final @Nonnegative int index)
      {
        return isStatus(index, DOWNLOADED, OBSOLETE);
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    public boolean isPlayable (final @Nonnegative int index)
      {
        return isStatus(index, DOWNLOADED, OBSOLETE);
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    public boolean isPlaying (final @Nonnegative int index)
      {
        return isPlayable(index) && (controller != null) && controller.isPlaying() && (index == playingIndex);
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    public boolean hasWebPage (final @Nonnegative int index)
      {
        try
          {
            getObservation(index).as(Identifiable.class).getId();
            return true;
          }
        catch (AsException e)
          {
            return false;
          }
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    public void downloadMedia (final @Nonnegative int index)
      {
        if (isDownloadable(index))
          {
            downloadMediaWithConfirmation(index, Operation.DOWNLOAD);
          }
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    public void refreshMedia (final @Nonnegative int index)
      {
        if (isRefreshable(index))
          {
            downloadMediaWithConfirmation(index, Operation.REFRESH);
          }
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    public void deleteMedia (final @Nonnegative int index)
      {
        if (isDeletable(index))
          {
            final String title   = NbBundle.getMessage(TaxonSoundFactSheetController.class, "confirmDeletionTitle");
            final String message = NbBundle.getMessage(TaxonSoundFactSheetController.class, "confirmDeletionMessage");

            ui.getCommonUITasks().alertDialog(title, message, new Runnable()
              {
                public void run()
                  {
                    try
                      {
                        getMedia(index).as(Removable.class).remove();
                        final double freeSpace = masterFileSystem.get().getExternalFileSystem().getFreeSpace() / MEGA;
                        ui.notify(NbBundle.getMessage(TaxonSoundFactSheetController.class, "deletionCompleted", freeSpace));
                      }
                    catch (Exception e)
                      {
                        ui.notify(NbBundle.getMessage(TaxonSoundFactSheetController.class, "deletionFailed"));
                        logger.warning("Can't delete media %s", e);
                        logger.throwing("deleteMedia()", CLASS, e);
                      }

                    // Needed because at this point the PropertyChangeListener has been detached.
                    // Maybe it would be better to keep the listener attached, implement a dispose() method that
                    // detaches it at the end.
                    notifyStatusChanged();
                  }
              });
          }
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    public void playMedia (final @Nonnegative int index)
      {
        if (isPlayable(index))
          {
            try
              {
                disposePlayer();
                playingIndex = index;
                controller = mediaPlayer.get().play(getMedia(index));
                controller.addPropertyChangeListener(mediaChangeListener);
                ui.notifyPlayMedia(controller);
                notifyStatusChanged();
              }
            catch (Exception e)
              {
                ui.notify(NbBundle.getMessage(TaxonSoundFactSheetController.class, "playFailed"));
                logger.warning("Can't play sound %s", e);
                logger.throwing("downloadMedia()", CLASS, e);
              }
          }
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    public void stopPlayer()
      {
        disposePlayer();
        notifyStatusChanged();
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    public void goToPage (final @Nonnegative int index)
      {
        try
          {
            ui.openWebPage(getObservation(index).as(Identifiable.class).getId().stringValue());
          }
        catch (Exception e)
          {
            logger.warning("Can't open web page %s", e);
            logger.throwing("goToPage()", CLASS, e);
          }
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    @Nonnull
    public DefaultAction getDefaultAction (final @Nonnegative int index)
      {
        if (isPlaying(index))
          {
            return DefaultAction.STOP;
          }
        else if (isPlayable(index))
          {
            return DefaultAction.PLAY;
          }
        else if (isDownloadable(index))
          {
            return DefaultAction.DOWNLOAD;
          }
        else
          {
            return DefaultAction.DO_NOTHING;
          }
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    @Nonnull
    protected Media getMedia (final @Nonnegative int index)
      {
        return getObservation(index).as(Media.class);
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    @Nonnull
    protected abstract Observation getObservation (final @Nonnegative int index);

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    protected abstract void notifyStatusChanged();

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    @Nonnull
    protected ObservationSet loadObservationSet (final @Nonnull InputStream is)
      throws IOException
      {
        final Reader r = new InputStreamReader(is, "UTF-8");
        final RdfJsonUnmarshaller unmarshaller = new RdfJsonUnmarshaller();
        return unmarshaller.unmarshal(r, ObservationSet.class);
      }
    
    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    private void downloadMediaWithConfirmation (final @Nonnegative int index, final @Nonnull Operation operation)
      {
        final String title   = NbBundle.getMessage(TaxonSoundFactSheetController.class, "confirmDownloadTitle");
        final String message = NbBundle.getMessage(TaxonSoundFactSheetController.class, "confirmDownloadMessage");

        if (Locator.find(NetworkingPreferences.class).isNetworkConnectionAllowed())
          {
            doDownloadMedia(index, operation);
          }
        else
          {
            ui.getCommonUITasks().alertDialog(title, message, new Runnable()
              {
                public void run()
                  {
                    doDownloadMedia(index, operation);
                  }
              });
          }
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    /* package */ void doDownloadMedia (final @Nonnegative int index, final @Nonnull Operation operation)
      {
        if (!masterFileSystem.get().getExternalFileSystem().isWritable())
          {
            final String title   = NbBundle.getMessage(TaxonSoundFactSheetController.class, "downloadFailed");
            final String message = NbBundle.getMessage(TaxonSoundFactSheetController.class, "diskNotMountedMessage");
            ui.getCommonUITasks().alertDialog(title, message, null);
          }
        else if(masterFileSystem.get().getExternalFileSystem().getFreeSpace() <= MIN_FREE_SPACE)
          {
            final String title   = NbBundle.getMessage(TaxonSoundFactSheetController.class, "downloadFailed", MIN_FREE_SPACE / MEGA);
            final String message = NbBundle.getMessage(TaxonSoundFactSheetController.class, "diskFullMessage", MIN_FREE_SPACE / MEGA);
            ui.getCommonUITasks().alertDialog(title, message, null);
          }
        else
          {
            final Downloadable downloadable = getMedia(index).as(Downloadable.class);
            downloadable.addPropertyChangeListener(downloadablePropertyChangeListener);
            operation.run(downloadable);
            ping();
          }
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    private boolean isStatus (final @Nonnegative int index, final @Nonnull Status ... status)
      {
        final Downloadable downloadable = getMedia(index).as(Downloadable.class);
        return Arrays.asList(status).contains(downloadable.getStatus());
      }

    protected void ping()
      {
      }
  }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy