io.github.data4all.util.Gallery.java Source code

Java tutorial

Introduction

Here is the source code for io.github.data4all.util.Gallery.java

Source

/*
 * Copyright (c) 2014, 2015 Data4All
 * 
 * <p>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
 * 
 * <p>http://www.apache.org/licenses/LICENSE-2.0
 * 
 * <p>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.
 */
package io.github.data4all.util;

import io.github.data4all.Exceptions;
import io.github.data4all.logger.Log;
import io.github.data4all.model.DeviceOrientation;
import io.github.data4all.model.data.TransformationParamBean;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import org.apache.commons.io.IOUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.content.Context;
import android.graphics.Point;

/**
 * A gallery holds images for being tagged later.
 * 
 * @author tbrose
 */
public class Gallery {
    /**
     * 
     */
    private static final String JSON_DIMENSION = "dimension";

    /**
     * 
     */
    private static final String JSON_ORIENTATION = "orientation";

    /**
     * 
     */
    private static final String JSON_PARAMETER = "parameters";

    /**
     * The name of the gallery folder.
     */
    private static final String DIRECTORY_NAME = "gallery";

    /**
     * The ending of the image file itself.
     */
    private static final String ENDING_JPEG = ".jpeg";

    /**
     * The ending if the information file of an image.
     */
    private static final String ENDING_INFO = ".info";

    private static final String LOG_TAG = Gallery.class.getSimpleName();

    public static final String GALLERY_ID_EXTRA = Gallery.class.getName() + ":GALLERY_ID";

    /**
     * The working directory of this gallery.
     */
    private final File workingDirectory;

    /**
     * Constructs a gallery to read from and write to.
     * 
     * @param context
     *            The context of this gallery
     */
    public Gallery(Context context) {
        workingDirectory = new File(context.getFilesDir(), DIRECTORY_NAME);
    }

    /**
     * Saves the given image to this gallery with the parameters, the
     * orientation and the dimension linked to this image.
     * 
     * @param imageData
     *            The raw image data, cannot be {@code null}
     * @param parameters
     *            The camera-parameters, cannot be {@code null}
     * @param orientation
     *            The orientation of the device, cannot be {@code null}
     * @param dimension
     *            The screen dimension, cannot be {@code null}
     * @throws IOException
     *             If the data cannot be stored
     */
    public void addImage(final byte[] imageData, TransformationParamBean parameters, DeviceOrientation orientation,
            Point dimension) throws IOException {
        Log.d(LOG_TAG, "adding image");
        if (imageData == null) {
            throw Exceptions.nullArgument("imageData");
        } else if (parameters == null) {
            throw Exceptions.nullArgument(JSON_PARAMETER);
        } else if (orientation == null) {
            throw Exceptions.nullArgument("oriantation");
        } else if (dimension == null) {
            throw Exceptions.nullArgument(JSON_DIMENSION);
        } else {
            final long time = System.currentTimeMillis();
            final JSONObject json = encodeInformations(parameters, orientation, dimension);

            this.save(imageData, time, json);
            Log.d(LOG_TAG, "image added");
        }
    }

    private void save(final byte[] imageData, final long time, final JSONObject json) throws IOException {
        try {
            this.saveData(time, ENDING_JPEG, imageData);
        } catch (IOException e) {
            this.deleteData(time, ENDING_JPEG);
            throw e;
        }

        try {
            this.saveData(time, ENDING_INFO, json.toString().getBytes("UTF-8"));
        } catch (IOException e) {
            this.deleteData(time, ENDING_JPEG, ENDING_INFO);
            throw e;
        }
    }

    /**
     * Receives the file of the image at the given timestamp.
     * 
     * @param timestamp
     *            The timestamp of the image
     * @return The file object of the image
     * @throws FileNotFoundException
     *             If the image is not stored in this gallery
     */
    public File getImageFile(long timestamp) throws FileNotFoundException {
        final File result = this.buildPath(timestamp, ENDING_JPEG);
        if (result.exists()) {
            return result;
        } else {
            throw new FileNotFoundException("No image found for timestamp " + timestamp);
        }
    }

    /**
     * Receives the informations of the image at the given timestamp.
     * 
     * @param timestamp
     *            The timestamp of the image
     * @return The information object of the image
     * @throws IOException
     *             If an I/O error occurs
     * @throws FileNotFoundException
     *             If the image is not stored in this gallery
     */
    public Informations getImageInformations(long timestamp) throws IOException {
        final File result = this.buildPath(timestamp, ENDING_INFO);
        if (result.exists()) {
            final FileInputStream stream = new FileInputStream(result);
            final byte[] content = IOUtils.toByteArray(stream);
            IOUtils.closeQuietly(stream);
            try {
                final JSONObject jsonObject = new JSONObject(new String(content, "UTF-8"));
                return decodeInformations(jsonObject);
            } catch (JSONException e) {
                throw new IOException("Content cannot be parsed", e);
            }
        } else {
            throw new FileNotFoundException("No image found for timestamp " + timestamp);
        }
    }

    /**
     * Lists the timestamp of all images in this gallery.
     * 
     * @return An array with timestamps
     */
    public long[] getImages() {
        final File[] files = this.getImageFiles();
        final Long[] timestamps = new Long[files.length];
        for (int i = 0; i < files.length; i++) {
            final File f = files[i];
            timestamps[i] = Long.parseLong(f.getName().replace(ENDING_JPEG, ""));
        }
        final List<Long> asList = Arrays.asList(timestamps);
        Collections.sort(asList, new Comparator<Long>() {
            @Override
            public int compare(Long lhs, Long rhs) {
                return rhs.compareTo(lhs);
            }
        });
        final long[] result = new long[asList.size()];
        for (int i = 0; i < asList.size(); i++) {
            result[i] = asList.get(i);
        }
        return result;
    }

    /**
     * Lists the file-objects of all images in this gallery.
     * 
     * @return An array with files, never {@code null}
     */
    public File[] getImageFiles() {
        final List<File> images = new ArrayList<File>();
        final File[] files = workingDirectory.listFiles();
        if (files != null) {
            for (final File f : files) {
                if (f.getName().endsWith(ENDING_JPEG)) {
                    images.add(f);
                }
            }
        }
        return images.toArray(new File[images.size()]);
    }

    /**
     * Let this gallery forget about the given image. If the picture does not
     * exists, nothing will be deleted.<br/>
     * This operation cannot be undone!
     * 
     * @param timestamp
     *            The timestamp of the image
     */
    public void deleteImage(long timestamp) {
        this.deleteData(timestamp, ENDING_JPEG, ENDING_INFO);
    }

    /**
     * Add a file to the gallery folder.
     * 
     * @param timestamp
     *            The timestamp of the image
     * @param ending
     *            The file ending
     * @param content
     *            The content of the file
     * @throws IOException
     *             If an I/O error occurs
     */
    private void saveData(long timestamp, String ending, byte[] content) throws IOException {
        Log.d(LOG_TAG, "saving data " + timestamp + ending);
        if (this.checkOrCreateWorkingDirectory()) {
            FileOutputStream stream = null;
            try {
                final File file = this.buildPath(timestamp, ending);
                stream = new FileOutputStream(file);
                stream.write(content);
            } finally {
                IOUtils.closeQuietly(stream);
            }
        } else {
            throw new IOException("Gallery folder cannot be created.");
        }
    }

    /**
     * Removes a file from the gallery folder.
     * 
     * @param timestamp
     *            The timestamp of the image
     * @param endings
     *            The file ending
     */
    private void deleteData(long timestamp, String... endings) {
        for (String ending : endings) {
            final File file = this.buildPath(timestamp, ending);
            if (file.exists()) {
                file.delete();
            }
        }
    }

    /**
     * Builds the file object for the given time and ending.
     * 
     * @param timestamp
     *            The timestamp of the image
     * @param ending
     *            The file-ending
     * @return The full qualified file object for the gallery folder
     */
    private File buildPath(long timestamp, String ending) {
        return new File(workingDirectory, timestamp + ending);
    }

    /**
     * Encodes the given informations of an image into a JSON.
     * 
     * @param parameters
     *            The parameters of the image
     * @param orientation
     *            The orientation of the device
     * @param dimension
     *            The screen dimension of the device
     * @return A JSONObject holding the informations
     * @throws IOException
     *             If an JSON error occurs
     */
    private static JSONObject encodeInformations(TransformationParamBean parameters, DeviceOrientation orientation,
            Point dimension) throws IOException {
        final JSONObject json = new JSONObject();
        try {
            return json.put(JSON_PARAMETER, parameters.toJSON()).put(JSON_ORIENTATION, orientation.toJSON())
                    .put(JSON_DIMENSION, new JSONArray(Arrays.asList(dimension.x, dimension.y)));
        } catch (JSONException e) {
            throw new IOException(e);
        }
    }

    /**
     * Decodes the informations of an image from the given JSON.
     * 
     * @param json
     * @return
     * @throws JSONException
     */
    private static Informations decodeInformations(JSONObject json) throws JSONException {
        final TransformationParamBean parameters = TransformationParamBean
                .fromJSON(json.getJSONArray(JSON_PARAMETER));
        final DeviceOrientation oriantation = DeviceOrientation
                .fromJSON((JSONArray) json.getJSONArray(JSON_ORIENTATION));
        final JSONArray dimension = json.getJSONArray(JSON_DIMENSION);

        return new Informations(parameters, oriantation, new Point(dimension.getInt(0), dimension.getInt(1)));
    }

    /**
     * Creates the working directory if it currently does not exists.
     * 
     * @return If the working directory exists after the call of this method
     */
    private boolean checkOrCreateWorkingDirectory() {
        return workingDirectory.exists() || workingDirectory.mkdirs();
    }

    /**
     * This class holds the informations of the information file for return
     * purpose.
     * 
     * @author tbrose
     */
    public static final class Informations {
        private final TransformationParamBean parameters;
        private final DeviceOrientation orientation;
        private final Point dimension;

        private Informations(TransformationParamBean parameters, DeviceOrientation orientation, Point dimension) {
            this.parameters = parameters;
            this.orientation = orientation;
            this.dimension = dimension;
        }

        public TransformationParamBean getParameters() {
            return parameters;
        }

        public DeviceOrientation getOrientation() {
            return orientation;
        }

        public Point getDimension() {
            return dimension;
        }
    }
}