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

it.tidalwave.bluebill.mobile.observation.ui.spi.DefaultObservationsViewController Maven / Gradle / Ivy

The newest version!
/***********************************************************************************************************************
 *
 * blueBill Mobile - Android - open source birding
 * Copyright (C) 2009-2011 by Tidalwave s.a.s. (http://www.tidalwave.it)
 *
 ***********************************************************************************************************************
 *
 * 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.
 *
 ***********************************************************************************************************************
 *
 * WWW: http://bluebill.tidalwave.it/mobile
 * SCM: https://java.net/hg/bluebill-mobile~android-src
 *
 **********************************************************************************************************************/
package it.tidalwave.bluebill.mobile.observation.ui.spi;

import it.tidalwave.bluebill.mobile.observation.ui.ObservationsView;
import it.tidalwave.bluebill.mobile.observation.ui.ObservationsViewController;
import it.tidalwave.bluebill.mobile.observation.ui.ReportUserNotificationWithFeedback;
import javax.annotation.Nonnull;
import javax.inject.Provider;
import java.util.Date;
import java.util.Calendar;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.io.File;
import java.io.IOException;
import it.tidalwave.util.thread.ThreadAssertions;
import it.tidalwave.util.ui.UserNotificationWithFeedback;
import it.tidalwave.netbeans.util.Locator;
import it.tidalwave.observation.Observation;
import it.tidalwave.observation.Observation.Builder;
import it.tidalwave.observation.ObservationItem;
import it.tidalwave.observation.ObservationManager;
import it.tidalwave.observation.ObservationSet;
import it.tidalwave.mobile.util.DateUpdater;
import it.tidalwave.mobile.io.FileSystem;
import it.tidalwave.mobile.io.MasterFileSystem;
import it.tidalwave.mobile.location.LocationFinder;
import it.tidalwave.semantic.document.Document;
import it.tidalwave.semantic.document.DocumentWriter;
import it.tidalwave.observation.bluebill.ObservationClipboard;
import it.tidalwave.bluebill.mobile.preferences.SharingPreferences;
import it.tidalwave.bluebill.taxonomy.mobile.Taxon;
import lombok.extern.slf4j.Slf4j;
import static org.openide.util.NbBundle.*;
import static it.tidalwave.util.thread.ThreadType.*;
import static it.tidalwave.util.ui.UserNotification.notification;
import static it.tidalwave.util.ui.UserNotificationWithFeedback.notificationWithFeedback;
import static it.tidalwave.role.PlainTextRenderable.PlainTextRenderable;
import static it.tidalwave.bluebill.taxonomy.mobile.Taxon.Taxon;
import static it.tidalwave.semantic.document.Sendable.Sendable;

/***********************************************************************************************************************
 *
 * @stereotype Controller
 * 
 * @author  Fabrizio Giudici
 * @version $Id$
 *
 **********************************************************************************************************************/
@Slf4j
public class DefaultObservationsViewController implements ObservationsViewController
  {
    private static final Class _ = DefaultObservationsViewController.class;

    @Nonnull
    private final ObservationClipboard observationClipboard = Locator.find(ObservationClipboard.class);

    @Nonnull
    private final LocationFinder locationFinder = Locator.find(LocationFinder.class);

    @Nonnull
    private final ObservationManager observationManager = Locator.find(ObservationManager.class);
    
    @Nonnull
    private final ObservationSet observationSet = observationManager.findOrCreateObservationSetById(null);
    
    @Nonnull
    private final Provider sharingPreferences = Locator.createProviderFor(SharingPreferences.class);
    
    @Nonnull
    private final Provider masterFileSystem = Locator.createProviderFor(MasterFileSystem.class);

    @Nonnull
    private final ObservationsView view;

    /*******************************************************************************************************************
     *
     *
     *
     ******************************************************************************************************************/
    public DefaultObservationsViewController (final @Nonnull ObservationsView view)
      {
        this.view = view;
      }

    /*******************************************************************************************************************
     *
     * {@inheritDoc}
     *
     ******************************************************************************************************************/
    public void startNewObservationSequence()
      {
        ThreadAssertions.assertThread(UI);
        log.info("startNewObservationSequence()");
        observationClipboard.clear();
        final Builder builder = observationClipboard.getBuilder();
        observationClipboard.setBuilder(builder.at(Calendar.getInstance().getTime()));
        locationFinder.start(); // search needs time, and we pre-start it
      }

    /*******************************************************************************************************************
     *
     * {@inheritDoc}
     *
     ******************************************************************************************************************/
    public void browseToFactSheet()
      {
        ThreadAssertions.assertThread(UI);
        log.info("browseToFactSheet()");
        // nothing to do by default
      }

    /*******************************************************************************************************************
     *
     * {@inheritDoc}
     *
     ******************************************************************************************************************/
    public void commitNewObservation()
      {
        ThreadAssertions.assertThread(UI);
        log.info("commitNewObservation()");
        
        final Observation.Builder builder = observationClipboard.getBuilder();

        if (builder.getItemCount() > 0)
          {
            builder.build();
            view.highlightLatestObservation();
            view.notifyNewObservationCommitted(notification().withText(_, "addedNewObservation"));
          }

        observationClipboard.clear();
      }

    /*******************************************************************************************************************
     *
     * {@inheritDoc}
     *
     ******************************************************************************************************************/
    public void cancelNewObservation()
      {
        ThreadAssertions.assertThread(UI);
        log.info("cancelNewObservation()");
        locationFinder.stop(); // we pre-started it
        view.notifyNewObservationCancelled(notification().withText(_, "addedNewObservationCancelled"));
      }

    /*******************************************************************************************************************
     *
     * {@inheritDoc}
     *
     ******************************************************************************************************************/
    public void updateObservationDate (final @Nonnull Observation observation, final @Nonnull DateUpdater dateUpdater) 
      {
        ThreadAssertions.assertThread(UI);
        final Date date = dateUpdater.update(observation.getDate()); 
        observation.change().at(date).apply();
        view.notifyObservationDateTimeUpdated(notification().withText(_, "observationDateTimeUpdated"));
      }
    
    /*******************************************************************************************************************
     *
     * {@inheritDoc}
     *
     ******************************************************************************************************************/
    public void exportObservations()
      {
        ThreadAssertions.assertThread(UI);
        log.info("exportObservations()");
        view.askForExportOptions(new ReportUserNotificationWithFeedback(getMessage(_, "exportOptions"))                 
          {
            @Override
            protected void onConfirmInBackground()
              {
                log.info("exportObservations({})", this);

                progressListener.start();
                final String reportBaseName = getReportBaseName();
                final Document report = getReportFactory().createReport(observationSet, reportBaseName, progressListener);
                progressListener.setProgressName(getMessage(_, "exportSaving"));
                
                final FileSystem fileSystem = masterFileSystem.get().getExternalFileSystem();
                final String reportExportFolderName = getMessage(_, "exportFolder");
                
                try
                  {
                    final File reportContainerFolder = fileSystem.getFile(reportExportFolderName);
                    final File reportFolder = new File(reportContainerFolder, reportBaseName);
                    log.info(">>>> saving report to {}", reportFolder.getAbsolutePath());
                                                
                    if (!reportFolder.mkdirs() && !reportFolder.exists())
                      {
                        throw new IOException("Cannot create " + reportFolder.getAbsolutePath());   
                      }
                    
                    report.accept(new DocumentWriter(reportBaseName, reportFolder));
                    
                    // FIXME: this is brittle and based on a hidden behaviour of ExternalFileSystem.
                    final File storageRoot = reportContainerFolder.getParentFile().getParentFile();
                    final String path = reportFolder.getAbsolutePath().substring(storageRoot.getAbsolutePath().length());
                    view.notifyExportCompleted(notificationWithFeedback().withCaption(_, "export")
                                                                         .withText(_, "exportCompleted", path));
                  }
                catch (Throwable t)
                  {
                    // FIXME: now that we have Exception reporting, why we just don't have this throwing exception?
                    log.error("exportObservations()", t);
                    view.notifyExportFailed(notificationWithFeedback().withCaption(_, "export")
                                                                      .withText(_, "exportFailed"));
                    throw new RuntimeException(t);
                  }
                finally
                  {
                    progressListener.stop();
                  }            
              }
          });
      }

    /*******************************************************************************************************************
     *
     * {@inheritDoc}
     *
     ******************************************************************************************************************/
    public void shareObservations()
      {
        ThreadAssertions.assertThread(UI);
        log.info("shareObservations()");
        view.askForShareOptions(new ReportUserNotificationWithFeedback(getMessage(_, "shareOptions"))
          {
            @Override
            protected void onConfirmInBackground()
              throws Exception
              {                
                try 
                  {
                    log.info("shareObservations({})", this);
                    Document report = null;

                    try
                      {
                        progressListener.start();
                        final String reportBaseName = getReportBaseName();
                        report = getReportFactory().createReport(observationSet, reportBaseName, progressListener);
                      }
                    finally
                      {
                        progressListener.stop(); // before sending the report
                      }
                    
                    report.as(Sendable).sendTo(view, sharingPreferences.get().getDefaultRecipients());
//                    view.showLightNotification(NbBundle.getMessage(C, "shareCompleted"));
                    // TODO: a light notification that the operation completed, but we must get notified about the send activity.
                  }
                catch (Throwable t) 
                  {
                    // FIXME: now that we have Exception reporting, why we just don't have this throwing exception?
                    log.error("shareObservations()", t);
                    view.notifyExportFailed(notificationWithFeedback().withCaption(_, "share")
                                                                      .withText(_, "shareFailed"));
                    throw new RuntimeException(t);
                  }
              }
          });
      }
    
    /*******************************************************************************************************************
     *
     * {@inheritDoc}
     *
     ******************************************************************************************************************/
    public void deleteAllObservations()
      {
        ThreadAssertions.assertThread(UI);
        log.info("deleteAllObservations()");

        view.confirmToDeleteAllObservations(notificationWithFeedback().withCaption(_, "confirm") 
                                                                      .withText(_, "confirmEraseAllObservations")
                                                                      .withFeedback(new UserNotificationWithFeedback.Feedback()
          {
            @Override
            public void onConfirm() 
              {
                observationSet.clear();
                view.notifyAllObservationsDeleted(notification().withText(_, "allObservationsErased"));
              }
          }));
      }

    /*******************************************************************************************************************
     *
     * {@inheritDoc}
     *
     ******************************************************************************************************************/
    public void deleteObservationItem (final @Nonnull ObservationItem observationItem)
      {
        ThreadAssertions.assertThread(UI);
        log.info("deleteObservationItem({})", observationItem);

        final Taxon taxon = observationItem.getObservable().as(Taxon);
        
        view.confirmToDeleteAnObservationItem(notificationWithFeedback().withCaption(_, "confirm")
                                                                        .withText(_, "confirmDeleteObservationItem", taxon.as(PlainTextRenderable).render())
                                                                        .withFeedback(new UserNotificationWithFeedback.Feedback()
          {
            @Override
            public void onConfirm() 
              {
                observationItem.getObservation().change().without(observationItem).apply();
                view.notifyObservationItemDeleted(notification().withText(_, "itemDeleted"));
              }
          }));
      }

    /*******************************************************************************************************************
     *
     *
     ******************************************************************************************************************/
    @Nonnull
    private static String getReportBaseName()
      {
        final DateFormat df = new SimpleDateFormat("'blueBill-Observations-'yyyy-MM-dd_HHmm");
        return df.format(new Date());
      }
  }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy