blob: 4756cf4c07a3d4413a4c054e5c9281dad4f3be3b [file] [log] [blame]
// 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()));
}
}