use new storage location for backup and recordings

This commit is contained in:
Daniel Gultsch 2022-02-22 15:14:00 +01:00
parent 60617618b8
commit 8abacd23e8
6 changed files with 60 additions and 94 deletions

View file

@ -128,16 +128,19 @@ public class ImportBackupService extends Service {
final List<Jid> accounts = mDatabaseBackend.getAccountJids(false); final List<Jid> accounts = mDatabaseBackend.getAccountJids(false);
final ArrayList<BackupFile> backupFiles = new ArrayList<>(); final ArrayList<BackupFile> backupFiles = new ArrayList<>();
final Set<String> apps = new HashSet<>(Arrays.asList("Conversations", "Quicksy", getString(R.string.app_name))); final Set<String> apps = new HashSet<>(Arrays.asList("Conversations", "Quicksy", getString(R.string.app_name)));
for (String app : apps) { final List<File> directories = new ArrayList<>();
final File directory = new File(FileBackend.getBackupDirectory(app)); for (final String app : apps) {
directories.add(FileBackend.getLegacyBackupDirectory(app));
}
directories.add(FileBackend.getBackupDirectory(this));
for (final File directory : directories) {
if (!directory.exists() || !directory.isDirectory()) { if (!directory.exists() || !directory.isDirectory()) {
Log.d(Config.LOGTAG, "directory not found: " + directory.getAbsolutePath()); Log.d(Config.LOGTAG, "directory not found: " + directory.getAbsolutePath());
continue; continue;
} }
final File[] files = directory.listFiles(); final File[] files = directory.listFiles();
if (files == null) { if (files == null) {
onBackupFilesLoaded.onBackupFilesLoaded(backupFiles); continue;
return;
} }
for (final File file : files) { for (final File file : files) {
if (file.isFile() && file.getName().endsWith(".ceb")) { if (file.isFile() && file.getName().endsWith(".ceb")) {

View file

@ -64,7 +64,6 @@ import eu.siacs.conversations.entities.DownloadableFile;
import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.services.AttachFileToConversationRunnable; import eu.siacs.conversations.services.AttachFileToConversationRunnable;
import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.ui.RecordingActivity;
import eu.siacs.conversations.ui.util.Attachment; import eu.siacs.conversations.ui.util.Attachment;
import eu.siacs.conversations.utils.Compatibility; import eu.siacs.conversations.utils.Compatibility;
import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.utils.CryptoHelper;
@ -88,19 +87,6 @@ public class FileBackend {
this.mXmppConnectionService = service; this.mXmppConnectionService = service;
} }
private static boolean isInDirectoryThatShouldNotBeScanned(Context context, File file) {
return isInDirectoryThatShouldNotBeScanned(context, file.getAbsolutePath());
}
public static boolean isInDirectoryThatShouldNotBeScanned(Context context, String path) {
for (String type : new String[] {RecordingActivity.STORAGE_DIRECTORY_TYPE_NAME, "Files"}) {
if (path.startsWith(getLegacyStorageLocation(context, type).getAbsolutePath())) {
return true;
}
}
return false;
}
public static long getFileSize(Context context, Uri uri) { public static long getFileSize(Context context, Uri uri) {
try { try {
final Cursor cursor = context.getContentResolver().query(uri, null, null, null, null); final Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
@ -156,34 +142,18 @@ public class FileBackend {
return true; return true;
} }
public static File getLegacyStorageLocation(Context context, final String type) { public static File getBackupDirectory(final Context context) {
if (Config.ONLY_INTERNAL_STORAGE) { final File conversationsDownloadDirectory =
return new File(context.getFilesDir(), type); new File(
} else { Environment.getExternalStoragePublicDirectory(
final File appDirectory = Environment.DIRECTORY_DOWNLOADS),
new File( context.getString(R.string.app_name));
Environment.getExternalStorageDirectory(), return new File(conversationsDownloadDirectory, "Backup");
context.getString(R.string.app_name));
final File appMediaDirectory = new File(appDirectory, "Media");
final String locationName =
String.format("%s %s", context.getString(R.string.app_name), type);
return new File(appMediaDirectory, locationName);
}
} }
private static String getAppMediaDirectory(Context context) { public static File getLegacyBackupDirectory(final String app) {
return Environment.getExternalStorageDirectory().getAbsolutePath() final File appDirectory = new File(Environment.getExternalStorageDirectory(), app);
+ "/" return new File(appDirectory, "Backup");
+ context.getString(R.string.app_name)
+ "/Media/";
}
public static String getBackupDirectory(Context context) {
return getBackupDirectory(context.getString(R.string.app_name));
}
public static String getBackupDirectory(String app) {
return Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + app + "/Backup/";
} }
private static Bitmap rotate(final Bitmap bitmap, final int degree) { private static Bitmap rotate(final Bitmap bitmap, final int degree) {
@ -521,38 +491,26 @@ public class FileBackend {
} }
public void updateMediaScanner(File file, final Runnable callback) { public void updateMediaScanner(File file, final Runnable callback) {
if (!isInDirectoryThatShouldNotBeScanned(mXmppConnectionService, file)) { MediaScannerConnection.scanFile(
MediaScannerConnection.scanFile( mXmppConnectionService,
mXmppConnectionService, new String[] {file.getAbsolutePath()},
new String[] {file.getAbsolutePath()}, null,
null, new MediaScannerConnection.MediaScannerConnectionClient() {
new MediaScannerConnection.MediaScannerConnectionClient() { @Override
@Override public void onMediaScannerConnected() {}
public void onMediaScannerConnected() {}
@Override @Override
public void onScanCompleted(String path, Uri uri) { public void onScanCompleted(String path, Uri uri) {
if (callback != null && file.getAbsolutePath().equals(path)) { if (callback != null && file.getAbsolutePath().equals(path)) {
callback.run();
} else {
Log.d(Config.LOGTAG, "media scanner scanned wrong file");
if (callback != null) {
callback.run(); callback.run();
} else {
Log.d(Config.LOGTAG, "media scanner scanned wrong file");
if (callback != null) {
callback.run();
}
} }
} }
}); }
return; });
/*Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
intent.setData(Uri.fromFile(file));
mXmppConnectionService.sendBroadcast(intent);*/
} else if (file.getAbsolutePath()
.startsWith(getAppMediaDirectory(mXmppConnectionService))) {
createNoMedia(file.getParentFile());
}
if (callback != null) {
callback.run();
}
} }
public boolean deleteFile(Message message) { public boolean deleteFile(Message message) {
@ -629,9 +587,20 @@ public class FileBackend {
return attachments; return attachments;
} }
// TODO remove static method. use direct instance access
private File getLegacyStorageLocation(final String type) { private File getLegacyStorageLocation(final String type) {
return getLegacyStorageLocation(mXmppConnectionService, type); if (Config.ONLY_INTERNAL_STORAGE) {
return new File(mXmppConnectionService.getFilesDir(), type);
} else {
final File appDirectory =
new File(
Environment.getExternalStorageDirectory(),
mXmppConnectionService.getString(R.string.app_name));
final File appMediaDirectory = new File(appDirectory, "Media");
final String locationName =
String.format(
"%s %s", mXmppConnectionService.getString(R.string.app_name), type);
return new File(appMediaDirectory, locationName);
}
} }
private Bitmap resize(final Bitmap originalBitmap, int size) throws IOException { private Bitmap resize(final Bitmap originalBitmap, int size) throws IOException {

View file

@ -291,7 +291,7 @@ public class ExportBackupService extends Service {
secureRandom.nextBytes(salt); secureRandom.nextBytes(salt);
final BackupFileHeader backupFileHeader = new BackupFileHeader(getString(R.string.app_name), account.getJid(), System.currentTimeMillis(), IV, salt); final BackupFileHeader backupFileHeader = new BackupFileHeader(getString(R.string.app_name), account.getJid(), System.currentTimeMillis(), IV, salt);
final Progress progress = new Progress(mBuilder, max, count); final Progress progress = new Progress(mBuilder, max, count);
final File file = new File(FileBackend.getBackupDirectory(this) + account.getJid().asBareJid().toEscapedString() + ".ceb"); final File file = new File(FileBackend.getBackupDirectory(this), account.getJid().asBareJid().toEscapedString() + ".ceb");
files.add(file); files.add(file);
final File directory = file.getParentFile(); final File directory = file.getParentFile();
if (directory != null && directory.mkdirs()) { if (directory != null && directory.mkdirs()) {
@ -335,7 +335,7 @@ public class ExportBackupService extends Service {
} }
private void notifySuccess(final List<File> files) { private void notifySuccess(final List<File> files) {
final String path = FileBackend.getBackupDirectory(this); final String path = FileBackend.getBackupDirectory(this).getAbsolutePath();
PendingIntent openFolderIntent = null; PendingIntent openFolderIntent = null;
@ -363,7 +363,7 @@ public class ExportBackupService extends Service {
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getBaseContext(), "backup"); NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getBaseContext(), "backup");
mBuilder.setContentTitle(getString(R.string.notification_backup_created_title)) mBuilder.setContentTitle(getString(R.string.notification_backup_created_title))
.setContentText(getString(R.string.notification_backup_created_subtitle, path)) .setContentText(getString(R.string.notification_backup_created_subtitle, path))
.setStyle(new NotificationCompat.BigTextStyle().bigText(getString(R.string.notification_backup_created_subtitle, FileBackend.getBackupDirectory(this)))) .setStyle(new NotificationCompat.BigTextStyle().bigText(getString(R.string.notification_backup_created_subtitle, FileBackend.getBackupDirectory(this).getAbsolutePath())))
.setAutoCancel(true) .setAutoCancel(true)
.setContentIntent(openFolderIntent) .setContentIntent(openFolderIntent)
.setSmallIcon(R.drawable.ic_archive_white_24dp); .setSmallIcon(R.drawable.ic_archive_white_24dp);

View file

@ -1183,8 +1183,8 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
cancelTransmission.setVisible(true); cancelTransmission.setVisible(true);
} }
if (m.isFileOrImage() && !deleted && !cancelable) { if (m.isFileOrImage() && !deleted && !cancelable) {
String path = m.getRelativeFilePath(); final String path = m.getRelativeFilePath();
if (path == null || !path.startsWith("/") || FileBackend.isInDirectoryThatShouldNotBeScanned(getActivity(), path)) { if (path == null || !path.startsWith("/")) {
deleteFile.setVisible(true); deleteFile.setVisible(true);
deleteFile.setTitle(activity.getString(R.string.delete_x_file, UIHelper.getFileDescriptionString(activity, m))); deleteFile.setTitle(activity.getString(R.string.delete_x_file, UIHelper.getFileDescriptionString(activity, m)));
} }

View file

@ -6,6 +6,7 @@ import android.content.Intent;
import android.media.MediaRecorder; import android.media.MediaRecorder;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Environment;
import android.os.FileObserver; import android.os.FileObserver;
import android.os.Handler; import android.os.Handler;
import android.os.SystemClock; import android.os.SystemClock;
@ -153,28 +154,21 @@ public class RecordingActivity extends Activity implements View.OnClickListener
} }
} }
private static File generateOutputFilename(Context context) { private File generateOutputFilename() {
final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.US); final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.US);
final String filename = "RECORDING_" + dateFormat.format(new Date()) + ".m4a"; final String filename = "RECORDING_" + dateFormat.format(new Date()) + ".m4a";
return new File(FileBackend.getLegacyStorageLocation(context, STORAGE_DIRECTORY_TYPE_NAME), filename); //TODO once we target 31 use DIRECTORY_RECORDINGS
final File parentDirectory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
final File conversationsDirectory = new File(parentDirectory, getString(R.string.app_name));
return new File(conversationsDirectory, filename);
} }
private void setupOutputFile() { private void setupOutputFile() {
mOutputFile = generateOutputFilename(this); mOutputFile = generateOutputFilename();
File parentDirectory = mOutputFile.getParentFile(); final File parentDirectory = mOutputFile.getParentFile();
if (parentDirectory.mkdirs()) { if (parentDirectory.mkdirs()) {
Log.d(Config.LOGTAG, "created " + parentDirectory.getAbsolutePath()); Log.d(Config.LOGTAG, "created " + parentDirectory.getAbsolutePath());
} }
File noMedia = new File(parentDirectory, ".nomedia");
if (!noMedia.exists()) {
try {
if (noMedia.createNewFile()) {
Log.d(Config.LOGTAG, "created nomedia file in " + parentDirectory.getAbsolutePath());
}
} catch (IOException e) {
Log.d(Config.LOGTAG, "unable to create nomedia file in " + parentDirectory.getAbsolutePath(), e);
}
}
setupFileObserver(parentDirectory); setupFileObserver(parentDirectory);
} }

View file

@ -224,7 +224,7 @@ public class SettingsActivity extends XmppActivity implements
final Preference createBackupPreference = mSettingsFragment.findPreference("create_backup"); final Preference createBackupPreference = mSettingsFragment.findPreference("create_backup");
if (createBackupPreference != null) { if (createBackupPreference != null) {
createBackupPreference.setSummary(getString(R.string.pref_create_backup_summary, FileBackend.getBackupDirectory(this))); createBackupPreference.setSummary(getString(R.string.pref_create_backup_summary, FileBackend.getBackupDirectory(this).getAbsolutePath()));
createBackupPreference.setOnPreferenceClickListener(preference -> { createBackupPreference.setOnPreferenceClickListener(preference -> {
if (hasStoragePermission(REQUEST_CREATE_BACKUP)) { if (hasStoragePermission(REQUEST_CREATE_BACKUP)) {
createBackup(); createBackup();