diff options
Diffstat (limited to 'src/main/java/eu/siacs/conversations/persistance/FileBackend.java')
-rw-r--r-- | src/main/java/eu/siacs/conversations/persistance/FileBackend.java | 263 |
1 files changed, 263 insertions, 0 deletions
diff --git a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java new file mode 100644 index 00000000..6f99ef55 --- /dev/null +++ b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java @@ -0,0 +1,263 @@ +package eu.siacs.conversations.persistance; + +import android.content.Intent; +import android.graphics.Bitmap; +import android.net.Uri; +import android.os.Environment; +import android.util.Log; +import android.webkit.MimeTypeMap; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; + +import de.thedevstack.android.logcat.Logging; +import de.thedevstack.conversationsplus.ConversationsPlusApplication; +import de.thedevstack.conversationsplus.ConversationsPlusPreferences; +import de.thedevstack.conversationsplus.entities.FileParams; +import de.thedevstack.conversationsplus.exceptions.FileCopyException; +import de.thedevstack.conversationsplus.persistance.observers.FileDeletionObserver; +import de.thedevstack.conversationsplus.utils.StreamUtil; +import de.thedevstack.conversationsplus.utils.XmppConnectionServiceAccessor; + +import eu.siacs.conversations.Config; +import eu.siacs.conversations.R; +import eu.siacs.conversations.entities.DownloadableFile; +import eu.siacs.conversations.entities.Message; +import eu.siacs.conversations.services.XmppConnectionService; + +public class FileBackend { + private static final SimpleDateFormat imageDateFormat = new SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.US); + private static FileBackend INSTANCE; + + private FileDeletionObserver privateFilesDirectoryObserver; + private FileDeletionObserver privateFilesImageDirectoryObserver; + private FileDeletionObserver conversationsFilesDirectoryObserver; + private FileDeletionObserver conversationsImagesDirectoryObserver; + + public static void init() { + if (null == INSTANCE) { + INSTANCE = new FileBackend(); + } + INSTANCE.initFileObservers(); + INSTANCE.createNoMedia(); + } + + private void initFileObservers() { + this.privateFilesDirectoryObserver = new FileDeletionObserver(FileBackend.getPrivateFileDirectoryPath()); + this.privateFilesImageDirectoryObserver = new FileDeletionObserver(FileBackend.getConversationsFileDirectory()); + this.conversationsFilesDirectoryObserver = new FileDeletionObserver(FileBackend.getConversationsImageDirectory()); + this.conversationsImagesDirectoryObserver = new FileDeletionObserver(FileBackend.getPrivateImageDirectoryPath()); + + this.privateFilesDirectoryObserver.startWatching(); + this.privateFilesImageDirectoryObserver.startWatching(); + this.conversationsFilesDirectoryObserver.startWatching(); + this.conversationsImagesDirectoryObserver.startWatching(); + } + + private void createNoMedia() { + final File nomedia = new File(getConversationsFileDirectory()+".nomedia"); + if (!nomedia.exists()) { + try { + nomedia.createNewFile(); + } catch (Exception e) { + Log.d(Config.LOGTAG, "could not create nomedia file"); + } + } + } + + public static void onFileTransferFolderChanged() { + INSTANCE.conversationsFilesDirectoryObserver.stopWatching(); + INSTANCE.conversationsFilesDirectoryObserver = new FileDeletionObserver(FileBackend.getConversationsFileDirectory()); + INSTANCE.conversationsFilesDirectoryObserver.startWatching(); + INSTANCE.createNoMedia(); + } + + public static void onImageTransferFolderChanged() { + INSTANCE.conversationsImagesDirectoryObserver.stopWatching(); + INSTANCE.conversationsImagesDirectoryObserver = new FileDeletionObserver(FileBackend.getConversationsImageDirectory()); + INSTANCE.conversationsImagesDirectoryObserver.startWatching(); + } + + public static void updateMediaScanner(File file, XmppConnectionService xmppConnectionService) { + if (file.getAbsolutePath().startsWith(getConversationsImageDirectory())) { + Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); + intent.setData(Uri.fromFile(file)); + xmppConnectionService.sendBroadcast(intent); + } + } + + public static boolean deleteFile(Message message) { + File file = getFile(message); + if (file.delete()) { + updateMediaScanner(file, XmppConnectionServiceAccessor.xmppConnectionService); + return true; + } else { + return false; + } + } + + public static DownloadableFile getFile(Message message) { + return getFile(message, true); + } + + public static DownloadableFile getFile(Message message, boolean decrypted) { + return new DownloadableFile(getFilePath(message, decrypted)); + } + + protected static String getFilePath(Message message, String extension) { + String path = FileBackend.getFilePath(message, true); + if (!path.endsWith(extension)) { + path += "." + extension; + } + + return path; + } + + protected static String getFilePath(Message message, Uri uri) { + String mime = ConversationsPlusApplication.getInstance().getContentResolver().getType(uri); + String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mime); + + return getFilePath(message, extension); + } + + protected static String getFilePath(Message message, boolean decrypted) { + final boolean encrypted = !decrypted + && (message.getEncryption() == Message.ENCRYPTION_PGP + || message.getEncryption() == Message.ENCRYPTION_DECRYPTED); + FileParams fileParams = message.getFileParams(); + if (null == fileParams) { + fileParams = new FileParams(); + message.setFileParams(fileParams); + } + String path = fileParams.getPath(); + + if (null == path) { // File does not yet exist + path = message.getUuid(); + String mime = message.getMimeType(); + if (mime != null && mime.startsWith("image")) { // TODO: Check if this can be determined in a better way + path = getConversationsImageDirectory() + path; + } else { + path = getConversationsFileDirectory() + path; + } + String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mime); + path += "." + extension; + + fileParams.setPath(path); + } + if (encrypted) { + path += ".pgp"; + } + return path; + } + + public static String getConversationsFileDirectory() { + return Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + ConversationsPlusPreferences.fileTransferFolder() + File.separator; + } + + public static String getConversationsImageDirectory() { + return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getAbsolutePath() + File.separator + ConversationsPlusPreferences.imgTransferFolder() + File.separator; + } + + public static String getPrivateFileDirectoryPath() { + return ConversationsPlusApplication.getPrivateFilesDir().getAbsolutePath(); + } + + private static String getPrivateImageDirectoryPath() { + return FileBackend.getPrivateFileDirectoryPath() + File.separator + "Images" + File.separator; + } + + public static void copyFileToPrivateStorage(File file, Uri uri) throws FileCopyException { + file.getParentFile().mkdirs(); + OutputStream os = null; + InputStream is = null; + try { + file.createNewFile(); + os = new FileOutputStream(file); + is = StreamUtil.openInputStreamFromContentResolver(uri); + byte[] buffer = new byte[1024]; + int length; + while ((length = is.read(buffer)) > 0) { + os.write(buffer, 0, length); + } + os.flush(); + } catch(FileNotFoundException e) { + throw new FileCopyException(R.string.error_file_not_found); + } catch (IOException e) { + e.printStackTrace(); + throw new FileCopyException(R.string.error_io_exception); + } finally { + StreamUtil.close(os); + StreamUtil.close(is); + } + Logging.d(Config.LOGTAG, "output file name " + file); + } + + public static void copyFileToPrivateStorage(Message message, Uri uri) throws FileCopyException { + Log.d(Config.LOGTAG, "copy " + uri.toString() + " to private storage"); + String path = getFilePath(message, uri); + message.getFileParams().setPath(path); + message.setRelativeFilePath(path); // TODO: Remove + copyFileToPrivateStorage(getFile(message), uri); + } + + public static DownloadableFile compressImageAndCopyToPrivateStorage(Message message, Bitmap scaledBitmap) throws FileCopyException { + String path = getFilePath(message, "jpg"); + message.getFileParams().setPath(path); + message.setRelativeFilePath(path); // TODO: Remove + DownloadableFile file = getFile(message); + file.getParentFile().mkdirs(); + OutputStream os = null; + try { + file.createNewFile(); + os = new FileOutputStream(file); + + boolean success = scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 75, os); + if (!success) { + throw new FileCopyException(R.string.error_compressing_image); + } + os.flush(); + } catch (IOException e) { + throw new FileCopyException(R.string.error_io_exception, e); + } catch (SecurityException e) { + throw new FileCopyException(R.string.error_security_exception_during_image_copy); + } catch (NullPointerException e) { + throw new FileCopyException(R.string.error_io_exception); + } finally { + StreamUtil.close(os); + } + return file; + } + + public static Uri getTakePhotoUri() { + StringBuilder pathBuilder = new StringBuilder(); + pathBuilder.append(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)); + pathBuilder.append('/'); + pathBuilder.append("Camera"); + pathBuilder.append('/'); + pathBuilder.append("IMG_" + imageDateFormat.format(new Date()) + ".jpg"); + Uri uri = Uri.parse("file://" + pathBuilder.toString()); + File file = new File(uri.toString()); + file.getParentFile().mkdirs(); + return uri; + } + + public static Uri getJingleFileUri(Message message) { + File file = getFile(message); + return Uri.parse("file://" + file.getAbsolutePath()); + } + + public static boolean isFileAvailable(Message message) { + return getFile(message).exists(); + } + + private FileBackend() { + // Static helper class + } +} |