io.v.syncslides.lib.DeckImporter.java Source code

Java tutorial

Introduction

Here is the source code for io.v.syncslides.lib.DeckImporter.java

Source

// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package io.v.syncslides.lib;

import android.content.ContentResolver;
import android.support.v4.provider.DocumentFile;

import com.google.common.base.Charsets;
import com.google.common.io.ByteStreams;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;

import io.v.syncslides.db.DB;
import io.v.syncslides.model.Deck;
import io.v.syncslides.model.DeckImpl;
import io.v.syncslides.model.Slide;
import io.v.syncslides.model.SlideImpl;
import io.v.v23.verror.VException;

import static io.v.v23.VFutures.sync;

/**
 * Imports a slide deck from the given (local) folder directly into the DB.
 *
 * The folder must contain a JSON metadata file 'deck.json' with the following format:
 * {
 *     "Title" : "<title>",
 *     "Thumb" : "<filename>,
 *     "Slides" : [
 *          {
 *              "Thumb" : "<thumb_filename1>",
 *              "Image" : "<image_filename1>",
 *              "Note" : "<note1>"
 *          },
 *          {
 *              "Thumb" : "<thumb_filename2>",
 *              "Image" : "<image_filename2>",
 *              "Note" : "<note2>"
 *          },
 *
 *          ...
 *     ]
 * }
 *
 * All the filenames must be local to the given folder.
 */
public class DeckImporter {

    private static final String DECK_JSON = "deck.json";
    private static final String TITLE = "Title";
    private static final String THUMB = "Thumb";
    private static final String SLIDES = "Slides";
    private static final String IMAGE = "Image";
    private static final String NOTE = "Note";

    private final ListeningExecutorService mExecutorService;
    private ContentResolver mContentResolver;
    private DB mDB;

    public DeckImporter(ContentResolver contentResolver, DB db) {
        mExecutorService = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor());
        mContentResolver = contentResolver;
        mDB = db;
    }

    public ListenableFuture<Void> importDeck(final DocumentFile dir) {
        return mExecutorService.submit(() -> importDeckImpl(dir));
    }

    private Void importDeckImpl(DocumentFile dir) throws ImportException {
        if (!dir.isDirectory()) {
            throw new ImportException("Must import from a directory, got: " + dir);
        }
        // Read the deck metadata file.
        DocumentFile metadataFile = dir.findFile(DECK_JSON);
        if (metadataFile == null) {
            throw new ImportException("Couldn't find deck metadata file 'deck.json'");
        }
        JSONObject metadata = null;
        try {
            String data = new String(
                    ByteStreams.toByteArray(mContentResolver.openInputStream(metadataFile.getUri())),
                    Charsets.UTF_8);
            metadata = new JSONObject(data);
        } catch (FileNotFoundException e) {
            throw new ImportException("Couldn't open deck metadata file", e);
        } catch (IOException e) {
            throw new ImportException("Couldn't read data from deck metadata file", e);
        } catch (JSONException e) {
            throw new ImportException("Couldn't parse deck metadata", e);
        }

        try {
            String id = UUID.randomUUID().toString();
            String title = metadata.getString(TITLE);
            byte[] thumbData = readImage(dir, metadata.getString(THUMB));
            Deck deck = new DeckImpl(title, thumbData, id);
            Slide[] slides = readSlides(dir, metadata);
            sync(mDB.importDeck(deck, slides));
        } catch (JSONException e) {
            throw new ImportException("Invalid format for deck metadata", e);
        } catch (IOException e) {
            throw new ImportException("Error interpreting deck metadata", e);
        } catch (VException e) {
            throw new ImportException("Error importing deck", e);
        }
        return null;
    }

    // TODO(kash): Lazily read the slide images so we don't need to have them all
    // in memory simultaneously.
    private Slide[] readSlides(DocumentFile dir, JSONObject metadata) throws JSONException, IOException {
        if (!metadata.has(SLIDES)) {
            return new Slide[0];
        }
        JSONArray slides = metadata.getJSONArray(SLIDES);
        Slide[] ret = new Slide[slides.length()];
        for (int i = 0; i < slides.length(); ++i) {
            JSONObject slide = slides.getJSONObject(i);
            byte[] thumbData = readImage(dir, slide.getString(THUMB));
            byte[] imageData = thumbData;
            if (slide.has(IMAGE)) {
                imageData = readImage(dir, slide.getString(IMAGE));
            }
            String note = slide.getString(NOTE);
            // Temporarily use the thumbnail image's filename as the slide's unique ID.  SyncbaseDB
            // will generate a real ID on its own, so this is just a placeholder.
            ret[i] = new SlideImpl(slide.getString(THUMB), thumbData, imageData, note);
        }
        return ret;
    }

    private byte[] readImage(DocumentFile dir, String fileName) throws IOException {
        DocumentFile file = dir.findFile(fileName);
        if (file == null) {
            throw new FileNotFoundException("Image file doesn't exist: " + fileName);
        }
        return ByteStreams.toByteArray(mContentResolver.openInputStream(file.getUri()));
    }
}