package model; import android.content.Context; import android.net.Uri; import android.os.AsyncTask; import android.util.Log; import org.unfoldingword.mobile.R; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.Enumeration; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; import de.greenrobot.common.io.FileUtils; import model.daoModels.AudioChapter; import model.daoModels.Book; import model.daoModels.Version; import model.parsers.MediaType; import utils.FileNameHelper; import utils.FileUtil; /** * Created by Fechner on 11/23/15. */ public class DataFileManager { public interface GetDownloadStateResponse{ void foundDownloadState(DownloadState state); } private static final String TAG = "DataFileManager"; private static final String TEMP_FILE_FOLDER_NAME = "sideload"; private static final int FILES_PER_TEXT = 2; private static final int FILES_PER_AUDIO = 1; private static final int FILES_PER_VIDEO = 2; public static void saveDataForBook(Context context, Book book, byte[] data, MediaType type){ saveDataForBook(context, book, data, type, book.getSourceUrl()); } public static void saveDataForBook(Context context, Book book, byte[] data, MediaType type, String url){ FileUtil.saveFile(getFileForDownload(context, type, book.getVersion(), FileNameHelper.getSaveFileName(context, book, type, url)), data); } public static void saveSignatureForBook(Context context, Book book, byte[] data, MediaType type){ saveSignatureForBook(context, book, data, type, book.getSignatureUrl()); } public static void saveSignatureForBook(Context context, Book book, byte[] data, MediaType type, String url){ FileUtil.saveFile(getFileForDownload(context, type, book.getVersion(), FileNameHelper.getSaveFileName(context, book, type, url)), data); } public static void getStateOfContent(final Context context, final Version version, final MediaType type, final GetDownloadStateResponse response){ new AsyncTask<Void, DownloadState, DownloadState>(){ @Override protected DownloadState doInBackground(Void... params) { File mediaFolder = getFileForDownload(context, type, version); if(!mediaFolder.exists()){ Log.d(TAG, "Media folder didn't exist"); return DownloadState.DOWNLOAD_STATE_NONE; } else { return verifyStateForContent(version, type, mediaFolder); } } @Override protected void onPostExecute(DownloadState downloadState) { super.onPostExecute(downloadState); response.foundDownloadState(downloadState); } }.execute(); } public static void getStateOfContent(final Context context, final List<Version> versions, final MediaType type, final GetDownloadStateResponse response){ new AsyncTask<Void, DownloadState, DownloadState>(){ @Override protected DownloadState doInBackground(Void... params) { // Log.d(TAG, "started checking of content state asynctask"); for(Version version : versions) { File mediaFolder = getFileForDownload(context, type, version); if (mediaFolder.exists() && verifyStateForContent(version, type, mediaFolder) == DownloadState.DOWNLOAD_STATE_DOWNLOADED) { // version had that type of media return DownloadState.DOWNLOAD_STATE_DOWNLOADED; } } return DownloadState.DOWNLOAD_STATE_NONE; } @Override protected void onPostExecute(DownloadState downloadState) { super.onPostExecute(downloadState); response.foundDownloadState(downloadState); } }.execute(); } public static Uri getUri(Context context, Book book, MediaType type, String fileUrl){ return Uri.fromFile(getFileForDownload(context, type, book.getVersion(), FileNameHelper.getSaveFileName(context, book, type, fileUrl))); } public static int getDownloadedBitrate(Context context, Version version, MediaType type){ File audioFolder = getFileForDownload(context, type, version); if(audioFolder.exists() && audioFolder.isDirectory()){ File[]files = audioFolder.listFiles(); for(File file : files){ String fileName = file.getName(); Pattern bitrateFinder = Pattern.compile("(\\d*)kbps"); Matcher matcher = bitrateFinder.matcher(fileName); while (matcher.find()) { String group = matcher.group(0); String bitrate = group.substring(0, group.indexOf("k")); if(isNumeric(bitrate)){ return Integer.parseInt(bitrate); } } } } return -1; } public static boolean isNumeric(String str) { try { int d = Integer.parseInt(str); } catch(NumberFormatException nfe) { return false; } return true; } public static boolean deleteContentForBook(Context context, Version version, MediaType type){ File desiredFolder = getFileForDownload(context, type, version); if(desiredFolder.exists()){ if(FileUtil.deleteContents(desiredFolder)) { return desiredFolder.delete(); } } return false; } private static DownloadState verifyStateForContent(Version version, MediaType type, File folder){ int expectedSize = getCountForMediaType(version, type); File[] files = folder.listFiles(); int numberOfFiles = files.length; if (expectedSize < 1) { // Log.d(TAG, "expected size is < 1"); return DownloadState.DOWNLOAD_STATE_NONE; } else if(expectedSize > numberOfFiles){ // Log.d(TAG, "expected size is " + expectedSize + " but number of files is " + numberOfFiles); return DownloadState.DOWNLOAD_STATE_DOWNLOADING; } else if (expectedSize == numberOfFiles) { // Log.d(TAG, "expected size is good!"); return DownloadState.DOWNLOAD_STATE_DOWNLOADED; } else{ Log.e(TAG, "error, file were larger than expected. that shouldn't happen"); return DownloadState.DOWNLOAD_STATE_ERROR; } } private static int getCountForMediaType(Version version, MediaType type){ switch (type){ case MEDIA_TYPE_TEXT:{ return version.getBooks().size() * FILES_PER_TEXT; } case MEDIA_TYPE_AUDIO:{ int finalCount = 0; for(Book book : version.getBooks()){ finalCount += book.getAudioBook().getAudioChapters().size(); } return finalCount; } case MEDIA_TYPE_VIDEO:{ return version.getBooks().size() * FILES_PER_VIDEO; } default: return -1; } } private static String getPath(Context context, MediaType mediaType, Version version){ return context.getFilesDir() + "/" + version.getUniqueSlug() + "/" + mediaType.getPathForType(); } private static File getFileForDownload(Context context, MediaType mediaType, Version version){ return new File(getPath(context, mediaType, version)); } private static File getFileForDownload(Context context, MediaType mediaType, Version version, String fileName){ return new File(getPath(context, mediaType, version), fileName); } // @Nullable // public static Uri createUriForSideLoad(Context context, Version version, List<MediaType> types, String fileName){ // // FileUtil.clearTemporaryFiles(context); // saveTempFileForSideLoading(context, version.getAsSideLoadJson(context).toString().getBytes(), FileNameHelper.getShareTextFileName(version)); // for(MediaType type : types){ // if(type == MediaType.MEDIA_TYPE_AUDIO){ // int bitRate = getDownloadedBitrate(context, version, MediaType.MEDIA_TYPE_AUDIO); // if(!saveAudioFilesForPreload(context, version, bitRate)){ // return null; // } // } // } // File outFile = new File(FileUtil.getTempStorageDir(context)); // // Uri compressedFile = compressFiles(version, FileUtil.getUriForTempDir(context, TEMP_FILE_FOLDER_NAME), Uri.fromFile(outFile)); // return compressedFile; // } public static Uri createUriForSideLoad(Context context, Map<Version, List<MediaType>> versions, String fileName) { FileUtil.clearTemporaryFiles(context); for(Map.Entry<Version, List<MediaType>> entry : versions.entrySet()) { for (MediaType type : entry.getValue()) { if (type == MediaType.MEDIA_TYPE_AUDIO) { int bitRate = getDownloadedBitrate(context, entry.getKey(), MediaType.MEDIA_TYPE_AUDIO); if (!saveAudioFilesForPreload(context, entry.getKey(), bitRate)) { return null; } } else if(type == MediaType.MEDIA_TYPE_TEXT) { if(saveTempFileForSideLoading(context, entry.getKey().getAsSideLoadJson(context).toString().getBytes(), FileNameHelper.getShareTextFileName(entry.getKey())) == null){ return null; } } } } File outFile = new File(FileUtil.getTempStorageDir(context)); Uri compressedFile = compressFiles(fileName, FileUtil.getUriForTempDir(context, TEMP_FILE_FOLDER_NAME), Uri.fromFile(outFile)); return compressedFile; } private static boolean saveAudioFilesForPreload(Context context, Version version, int bitRate){ boolean success = true; for(Book book : version.getBooks()){ for(AudioChapter chapter : book.getAudioBook().getAudioChapters()){ if(!saveAudioForSideLoad(context, book, chapter, bitRate)){ success = false; } // saveAudioSignatureForSideLoad(context, version, chapter, bitRate); } } return success; } private static Uri compressFiles(String fileName, Uri filesUri, Uri newFile){ File outFile = new File(newFile.getPath(), fileName); zipFolder(new File(filesUri.getPath()), outFile); return Uri.fromFile(outFile); } private static void zipFolder(File srcFile, File outZipPath) { try { FileOutputStream fos = new FileOutputStream(outZipPath); ZipOutputStream zos = new ZipOutputStream(fos); File[] files = srcFile.listFiles(); Log.d("", "Zip directory: " + srcFile.getName()); for (File file : files) { Log.d("", "Adding file: " + file.getName()); byte[] buffer = new byte[1024]; FileInputStream fis = new FileInputStream(file); zos.putNextEntry(new ZipEntry(file.getName())); int length; while ((length = fis.read(buffer)) > 0) { zos.write(buffer, 0, length); } zos.closeEntry(); fis.close(); } zos.close(); } catch (IOException ioe) { Log.e("", ioe.getMessage()); } } private static void unzipFiles(File archive, File newFile){ try { ZipFile zipfile = new ZipFile(archive); int entries = zipfile.size(); int total = 0; for (Enumeration<?> e = zipfile.entries(); e.hasMoreElements();) { ZipEntry entry = (ZipEntry) e.nextElement(); unzipEntry(zipfile, entry, newFile); } zipfile.close(); } catch (Exception e) { e.printStackTrace(); } } private static void unzipEntry(ZipFile zipfile, ZipEntry entry, File outputDir) throws IOException { if(!outputDir.exists()){ outputDir.mkdirs(); } // if (entry.isDirectory()) { // outputDir.mkdirs(); // createDir(new File(outputDir, entry.getName())); // return; // } File outputFile = new File(outputDir, entry.getName()); if (!outputFile.exists()) { outputFile.createNewFile(); } BufferedInputStream inputStream = new BufferedInputStream(zipfile .getInputStream(entry)); BufferedOutputStream outputStream = new BufferedOutputStream( new FileOutputStream(outputFile)); try{ copy(inputStream, outputStream); } finally{ outputStream.close(); inputStream.close(); } } public static int copy(BufferedInputStream in, BufferedOutputStream out) { byte[] buffer = new byte[1024]; int count = 0, n = 0; try { while ((n = in.read(buffer, 0, 1024)) != (-1)) { out.write(buffer, 0, n); count += n; } out.flush(); } catch (IOException e) { e.printStackTrace(); } finally { try { out.close(); } catch (IOException e) { e.printStackTrace(); } try { in.close(); } catch (IOException e) { e.printStackTrace(); } } return count; } public static Uri uncompressSideLoadedFiles(Context context, File currentFile){ FileUtil.clearTemporaryFiles(context); File outFile = new File(context.getFilesDir() + "/" + context.getString(R.string.app_name) + "/temp/sideload"); unzipFiles(currentFile, outFile); return Uri.fromFile(outFile); } private static boolean saveAudioForSideLoad(Context context, Book book, AudioChapter audioChapter, int bitRate){ File file = new File(getUri(context, book, MediaType.MEDIA_TYPE_AUDIO, audioChapter.getAudioUrl(bitRate)).getPath()); File newFile = new File(FileUtil.getUriForTempDir(context, TEMP_FILE_FOLDER_NAME).getPath()); newFile = new File(newFile, FileNameHelper.getShareAudioFileName(audioChapter, bitRate)); try { if(!newFile.exists()){ newFile.createNewFile(); } FileUtils.copyFile(file, newFile); return true; } catch (IOException e){ e.printStackTrace(); return false; } } private static void saveAudioSignatureForSideLoad(Context context, Version version, AudioChapter audioChapter, int bitRate){ Uri fileUri = getUri(context, audioChapter.getAudioBook().getBook(), MediaType.MEDIA_TYPE_AUDIO, audioChapter.getSignatureUrl(bitRate)); File newFile = new File(FileUtil.getUriForTempDir(context, TEMP_FILE_FOLDER_NAME).getPath()); FileUtil.copyFile(fileUri, Uri.fromFile(new File(newFile, FileNameHelper.getShareAudioSignatureFileName(audioChapter, bitRate)))); } private static Uri saveTempFileForSideLoading(Context context, byte[] file, String fileName) { return FileUtil.createTemporaryFile(context, file, TEMP_FILE_FOLDER_NAME, fileName); } }