/*************************************************************************************** * Copyright (c) 2016 Houssam Salem <houssam.salem.au@gmail.com> * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 3 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see <http://www.gnu.org/licenses/>. * ****************************************************************************************/ package com.ichi2.libanki.importer; import com.google.gson.stream.JsonReader; import com.ichi2.anki.BackupManager; import com.ichi2.anki.R; import com.ichi2.libanki.Collection; import com.ichi2.libanki.Storage; import com.ichi2.libanki.Utils; import java.io.BufferedInputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.zip.ZipFile; import timber.log.Timber; public class AnkiPackageImporter extends Anki2Importer { private ZipFile mZip; private Map<String, String> mNameToNum; public AnkiPackageImporter(Collection col, String file) { super(col, file); } @Override public void run() { publishProgress(0, 0, 0); File tempDir = new File(new File(mCol.getPath()).getParent(), "tmpzip"); Collection tmpCol; try { // We extract the zip contents into a temporary directory and do a little more // validation than the desktop client to ensure the extracted collection is an apkg. try { // extract the deck from the zip file mZip = new ZipFile(new File(mFile), ZipFile.OPEN_READ); Utils.unzipFiles(mZip, tempDir.getAbsolutePath(), new String[]{"collection.anki2", "media"}, null); } catch (IOException e) { Timber.e(e, "Failed to unzip apkg."); mLog.add(getRes().getString(R.string.import_log_no_apkg)); return; } String colpath = new File(tempDir, "collection.anki2").getAbsolutePath(); if (!(new File(colpath)).exists()) { mLog.add(getRes().getString(R.string.import_log_no_apkg)); return; } tmpCol = Storage.Collection(mContext, colpath); try { if (!tmpCol.validCollection()) { mLog.add(getRes().getString(R.string.import_log_no_apkg)); return; } } finally { if (tmpCol != null) { tmpCol.close(); } } mFile = colpath; // we need the media dict in advance, and we'll need a map of fname -> // number to use during the import File mediaMapFile = new File(tempDir, "media"); mNameToNum = new HashMap<>(); // We need the opposite mapping in AnkiDroid since our extraction method requires it. Map<String, String> numToName = new HashMap<>(); try { JsonReader jr = new JsonReader(new FileReader(mediaMapFile)); jr.beginObject(); String name; String num; while (jr.hasNext()) { num = jr.nextName(); name = jr.nextString(); mNameToNum.put(name, num); numToName.put(num, name); } jr.endObject(); jr.close(); } catch (FileNotFoundException e) { Timber.e("Apkg did not contain a media dict. No media will be imported."); } catch (IOException e) { Timber.e("Malformed media dict. Media import will be incomplete."); } // run anki2 importer super.run(); // import static media for (Map.Entry<String, String> entry : mNameToNum.entrySet()) { String file = entry.getKey(); String c = entry.getValue(); if (!file.startsWith("_") && !file.startsWith("latex-")) { continue; } File path = new File(mCol.getMedia().dir(), Utils.nfcNormalized(file)); if (!path.exists()) { try { Utils.unzipFiles(mZip, mCol.getMedia().dir(), new String[]{c}, numToName); } catch (IOException e) { Timber.e("Failed to extract static media file. Ignoring."); } } } } finally { // Clean up our temporary files if (tempDir.exists()) { BackupManager.removeDir(tempDir); } } publishProgress(100, 100, 100); } @Override protected BufferedInputStream _srcMediaData(String fname) { if (mNameToNum.containsKey(fname)) { try { return new BufferedInputStream(mZip.getInputStream(mZip.getEntry(mNameToNum.get(fname)))); } catch (IOException e) { Timber.e("Could not extract media file " + fname + "from zip file."); } } return null; } }