/* * Copyright 2015 Daniel Dittmar * * 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. * */ package dan.dit.whatsthat.image; import android.content.Context; import android.support.annotation.NonNull; import android.text.TextUtils; import android.util.Log; import android.util.Xml; import org.xmlpull.v1.XmlSerializer; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.StringWriter; import java.util.ArrayList; import java.util.Collections; import java.util.List; import dan.dit.whatsthat.preferences.User; import dan.dit.whatsthat.riddle.types.RiddleType; import dan.dit.whatsthat.solution.Solution; import dan.dit.whatsthat.storage.ImageTable; import dan.dit.whatsthat.util.general.IOUtil; import dan.dit.whatsthat.util.image.ExternalStorage; /** * I contrast to the {@see ImageXmlParser} this is the counter part class * that writes a list of images to a bundle that can be read the parser. * Created by daniel on 18.04.15. */ class ImageXmlWriter { public static final int RESULT_SUCCESS = -1; public static final int RESULT_NONE = 0; public static final int RESULT_ILLEGAL_ARGUMENT = 1; public static final int RESULT_EXTERNAL_STORAGE_PROBLEM = 2; public static final int RESULT_TARGET_BUNDLE_EXISTS = 3; public static final int RESULT_NO_TEMP_DIRECTORY = 4; public static final int RESULT_XML_WRITE_FAILED = 5; public static final int RESULT_XML_WRITE_SUCCESS = 6; public static final int RESULT_ZIP_FAILED = 7; private static final String BUILD_DIRECTORY_NAME = ".build"; private ImageXmlWriter() {} public static int writeBundle(Context context, List<BundleCreator.SelectedBitmap> imageBundle, String bundleOrigin, String bundleName) { if (context == null || imageBundle == null || TextUtils.isEmpty(bundleName)) { return RESULT_ILLEGAL_ARGUMENT; } File dir = BundleManager.ensureBundleDirectory(); if (dir == null) { return RESULT_EXTERNAL_STORAGE_PROBLEM; } File targetZip = BundleManager.makeBundleFile(dir, bundleOrigin, bundleName); if (targetZip.exists()) { return RESULT_TARGET_BUNDLE_EXISTS; } File tempDir = User.getTempDirectory(); if (tempDir == null) { return RESULT_NO_TEMP_DIRECTORY; } File targetDataXml = new File(tempDir, bundleName + ".xml"); FileOutputStream output = null; boolean success = false; List<Image> images = new ArrayList<>(imageBundle.size()); for (BundleCreator.SelectedBitmap bitmap : imageBundle) { images.add(bitmap.mImage); } try { output = new FileOutputStream(targetDataXml); success = writeXml(output, Collections.singletonList(images), Collections.singletonList(bundleOrigin), Collections.singletonList(0)); } catch (IOException ioe) { Log.e("Image", "Bundle XMLWRITE: Could not create file or save to file: " + ioe); } catch (IllegalStateException state) { Log.e("Image", "Bundle XMLWRITE: Illegal state for serializer: " + state); } catch (IllegalArgumentException arg) { Log.e("Image", "Bundle XMLWRITE: Illegal argument for serializer: " + arg); } finally { if (output != null) { try { output.close(); } catch (IOException ioe) { // final failure, ignore } } } int result = success ? RESULT_XML_WRITE_SUCCESS : RESULT_XML_WRITE_FAILED; if (success) { // now zip everything together List<File> toZip = new ArrayList<>(1 + imageBundle.size()); for (BundleCreator.SelectedBitmap bitmap : imageBundle) { toZip.add(bitmap.mPathInTemp); } toZip.add(targetDataXml); result = RESULT_ZIP_FAILED; try { if (IOUtil.zip(toZip, targetZip, new IOUtil.RelativePathExtractor() { @NonNull @Override public String getRelativePathOfFile(@NonNull File file) { String path = User.extractRelativePathInsideTempDirectory(file); return path != null ? path : file.getName(); } })) { result = RESULT_SUCCESS; User.clearTempDirectory(); } } catch (IOException e) { Log.e("Image", "Failed zipping files " + toZip + " into " + targetZip); } } return result; } /** * Writes a list of images to an xml file contained in the external storage directory under * WhatsThat/build, suffixed with the given bundle number. Does nothing if given bundle number is null * or empty or if there is no context given. * @param context The context. * @param imageBundles The list of bundles to write to xml. * @param bundleNumbers The number of the bundles, suffix of the file name if no outFile given. */ public static void writeBundle(Context context, List<List<Image>> imageBundles, List<Integer> bundleNumbers, String origin, File outFile) { if (context == null || imageBundles == null || imageBundles.isEmpty()) { return; } if (outFile == null) { String path = ExternalStorage.getExternalStoragePathIfMounted(BUILD_DIRECTORY_NAME); if (path == null) { return; } File dir = new File(path); if (dir.mkdirs() || dir.isDirectory()) { String fullName = path + "/imagedata" + (bundleNumbers != null && bundleNumbers.size() == 1 ? bundleNumbers.get(0) : "") + ".xml"; // Write to file. File file = new File(fullName); outFile = file; } } if (outFile != null) { FileOutputStream output = null; boolean success = false; try { output = new FileOutputStream(outFile); success = writeXml(output, imageBundles, Collections.singletonList(origin), bundleNumbers); } catch (IOException ioe) { Log.e("Image", "XMLWRITE: Could not create file or save to file: " + ioe); } catch (IllegalStateException state) { Log.e("Image", "XMLWRITE: Illegal state for serializer: " + state); } catch (IllegalArgumentException arg) { Log.e("Image", "XMLWRITE: Illegal argument for serializer: " + arg); } finally { if (output != null) { try { output.close(); } catch (IOException ioe) { // final failure, ignore } } } if (success) { Log.d("Image", "Bundle created for " + imageBundles.size() + " bundles, numbers " + bundleNumbers + " at " + outFile.getAbsolutePath()); } } else { Log.e("Image", "Failed writing bundle outFile/dir not available: " + outFile); } } private static boolean writeXml(FileOutputStream outputStream, List<List<Image>> imageBundles, List<String> bundleOrigins, List<Integer> bundleNumbers) throws IOException{ XmlSerializer serializer = Xml.newSerializer(); StringWriter writer = new StringWriter(); serializer.setOutput(writer); serializer.startDocument("UTF-8", true); serializer.startTag(ImageXmlParser.NAMESPACE, ImageXmlParser.TAG_ALL_BUNDLES_NAME); for (int i = 0; i < imageBundles.size(); i++) { String origin = bundleOrigins == null || bundleOrigins.isEmpty() ? null : i < bundleOrigins.size() ? bundleOrigins.get(i) : bundleOrigins.get(bundleOrigins.size() - 1); int number = bundleNumbers == null || bundleNumbers.isEmpty() ? 0 : i < bundleNumbers.size() ? bundleNumbers.get(i) : bundleNumbers.get(bundleNumbers.size() - 1); serializer.startTag(ImageXmlParser.NAMESPACE, ImageXmlParser.TAG_BUNDLE_NAME); if (origin != null) { serializer.attribute(ImageXmlParser.NAMESPACE, ImageTable.COLUMN_ORIGIN, origin); } serializer.attribute(ImageXmlParser.NAMESPACE, ImageXmlParser.BUNDLE_ATTRIBUTE_VERSION_NAME, String.valueOf(number)); for (Image image : imageBundles.get(i)) { serializer.startTag(ImageXmlParser.NAMESPACE, ImageXmlParser.TAG_IMAGE_NAME); String relativePath = image.getRelativePath(); if (!TextUtils.isEmpty(relativePath)) { writeText(serializer, ImageTable.COLUMN_SAVELOC, relativePath); } else { writeText(serializer, ImageTable.COLUMN_RESNAME, image.getName()); } writeText(serializer, ImageTable.COLUMN_ORIGIN, image.getOrigin()); writeText(serializer, ImageTable.COLUMN_HASH, image.getHash()); writeText(serializer, ImageTable.COLUMN_OBFUSCATION, String.valueOf(image.getObfuscation())); writeText(serializer, ImageTable.COLUMN_AVERAGE_COLOR, String.valueOf(image.getAverageColor())); writeSolutions(serializer, ImageTable.COLUMN_SOLUTIONS, image.getSolutions()); writeAuthor(serializer, ImageTable.COLUMN_AUTHOR, image.getAuthor()); writeTypes(serializer, ImageTable.COLUMN_RIDDLEPREFTYPES, image.getPreferredRiddleTypes()); writeTypes(serializer, ImageTable.COLUMN_RIDDLEREFUSEDTYPES, image.getRefusedRiddleTypes()); serializer.endTag(ImageXmlParser.NAMESPACE, ImageXmlParser.TAG_IMAGE_NAME); } serializer.endTag(ImageXmlParser.NAMESPACE, ImageXmlParser.TAG_BUNDLE_NAME); } serializer.endTag(ImageXmlParser.NAMESPACE, ImageXmlParser.TAG_ALL_BUNDLES_NAME); serializer.endDocument(); serializer.flush(); String dataWrite = writer.toString(); outputStream.write(dataWrite.getBytes()); outputStream.close(); return true; } private static void writeTypes(XmlSerializer serializer, String tag, List<RiddleType> types) throws IOException{ if (types != null) { serializer.startTag(ImageXmlParser.NAMESPACE, tag); for (RiddleType type : types) { writeText(serializer, ImageXmlParser.TAG_RIDDLE_TYPE_NAME, type.getFullName()); } serializer.endTag(ImageXmlParser.NAMESPACE, tag); } } private static void writeAuthor(XmlSerializer serializer, String tag, ImageAuthor author) throws IOException { serializer.startTag(ImageXmlParser.NAMESPACE, tag); writeText(serializer, ImageXmlParser.TAG_AUTHOR_NAME, author.getName()); writeText(serializer, ImageXmlParser.TAG_AUTHOR_SOURCE, author.getSource()); writeText(serializer, ImageXmlParser.TAG_AUTHOR_LICENSE, author.getLicense()); writeText(serializer, ImageXmlParser.TAG_AUTHOR_TITLE, author.getTitle()); writeText(serializer, ImageXmlParser.TAG_AUTHOR_EXTRAS, author.getExtras()); serializer.endTag(ImageXmlParser.NAMESPACE, tag); } private static void writeSolutions(XmlSerializer serializer, String tag, List<Solution> solutions) throws IOException { serializer.startTag(ImageXmlParser.NAMESPACE, tag); for (Solution sol : solutions) { writeSolution(serializer, ImageXmlParser.TAG_SOLUTION_NAME, sol); } serializer.endTag(ImageXmlParser.NAMESPACE, tag); } private static void writeSolution(XmlSerializer serializer, String tag, Solution sol) throws IOException { serializer.startTag(ImageXmlParser.NAMESPACE, tag); writeText(serializer, ImageXmlParser.TAG_SOLUTION_TONGUE_NAME, sol.getTongue().getShortcut()); for (String word : sol.getWords()) { writeText(serializer, ImageXmlParser.TAG_SOLUTION_WORD_NAME, word); } serializer.endTag(ImageXmlParser.NAMESPACE, tag); } private static void writeText(XmlSerializer serializer, String tag, String text) throws IOException { if (TextUtils.isEmpty(text)) { return; } serializer.startTag(ImageXmlParser.NAMESPACE, tag); serializer.text(text); serializer.endTag(ImageXmlParser.NAMESPACE, tag); } }