Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make backup folder changeable #12154

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import android.content.ActivityNotFoundException;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
Expand Down Expand Up @@ -33,8 +34,11 @@
import org.thoughtcrime.securesms.util.BackupUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.movedocumentfiles.Callback;
import org.thoughtcrime.securesms.util.text.AfterTextChanged;

import java.util.Objects;

public class BackupDialog {

private static final String TAG = Log.tag(BackupDialog.class);
Expand Down Expand Up @@ -110,6 +114,65 @@ public static void showEnableBackupDialog(@NonNull Context context,

}

@RequiresApi(29)
public static void startChooseFolderActivity(@NonNull Fragment fragment, int requestCode) {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);

if (Build.VERSION.SDK_INT >= 26) {
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, SignalStore.settings().getLatestSignalBackupDirectory());
}

intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION |
Intent.FLAG_GRANT_WRITE_URI_PERMISSION |
Intent.FLAG_GRANT_READ_URI_PERMISSION);

try {
fragment.startActivityForResult(intent, requestCode);
} catch (ActivityNotFoundException e) {
Toast.makeText(fragment.requireContext(), R.string.BackupDialog_no_file_picker_available, Toast.LENGTH_LONG)
.show();
}
}

@RequiresApi(29)
public static void showChangeBackupFolderDialog(@NonNull Context context,
@Nullable Intent backupDirectorySelectionIntent,
@NonNull Runnable runnable) {
android.app.AlertDialog dialog = new android.app.AlertDialog.Builder(context)
.setTitle(R.string.backup_change_folder_dialog__title)
.setView(R.layout.backup_change_folder_dialog)
.setPositiveButton(R.string.backup_change_folder_dialog__change_folder, null)
.setNegativeButton(android.R.string.cancel, null)
.create();
dialog.setOnShowListener(created -> {
Button button = ((android.app.AlertDialog) created).getButton(android.app.AlertDialog.BUTTON_POSITIVE);
button.setOnClickListener(v -> {
if (backupDirectorySelectionIntent != null && backupDirectorySelectionIntent.getData() != null) {
Uri backupDirectoryUri = backupDirectorySelectionIntent.getData();
int takeFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION |
Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
ContentResolver contentResolver = context.getContentResolver();
Uri oldBackupDirectoryUri = Objects.requireNonNull(SignalStore.settings().getSignalBackupDirectory());
SignalStore.settings().setSignalBackupDirectory(backupDirectoryUri);
contentResolver.takePersistableUriPermission(backupDirectoryUri, takeFlags);

TextSecurePreferences.setNextBackupTime(context, 0);
LocalBackupListener.schedule(context);
Callback callback = new Callback() {
@Override public void onFinish() {
contentResolver.releasePersistableUriPermission(oldBackupDirectoryUri, takeFlags);
}
};
BackupUtil.moveAllBackupsToNewDirectoryAsync(context.getApplicationContext(), oldBackupDirectoryUri, backupDirectoryUri, callback);
runnable.run();
created.dismiss();
}
});
});

dialog.show();
}

@RequiresApi(29)
public static void showChooseBackupLocationDialog(@NonNull Fragment fragment, int requestCode) {
new MaterialAlertDialogBuilder(fragment.requireContext())
Expand All @@ -119,23 +182,7 @@ public static void showChooseBackupLocationDialog(@NonNull Fragment fragment, in
dialog.dismiss();
})
.setPositiveButton(R.string.BackupDialog_choose_folder, ((dialog, which) -> {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);

if (Build.VERSION.SDK_INT >= 26) {
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, SignalStore.settings().getLatestSignalBackupDirectory());
}

intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION |
Intent.FLAG_GRANT_WRITE_URI_PERMISSION |
Intent.FLAG_GRANT_READ_URI_PERMISSION);

try {
fragment.startActivityForResult(intent, requestCode);
} catch (ActivityNotFoundException e) {
Toast.makeText(fragment.requireContext(), R.string.BackupDialog_no_file_picker_available, Toast.LENGTH_LONG)
.show();
}

startChooseFolderActivity(fragment, requestCode);
dialog.dismiss();
}))
.create()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public class BackupsPreferenceFragment extends Fragment {
private static final String TAG = Log.tag(BackupsPreferenceFragment.class);

private static final short CHOOSE_BACKUPS_LOCATION_REQUEST_CODE = 26212;
private static final short CHANGE_BACKUPS_LOCATION_REQUEST_CODE = 26213;

private View create;
private View folder;
Expand Down Expand Up @@ -116,6 +117,23 @@ public void onActivityResult(int requestCode, int resultCode, @Nullable Intent d
data,
StorageUtil.getDisplayPath(requireContext(), data.getData()),
this::setBackupsEnabled);
} else if (Build.VERSION.SDK_INT >= 29 &&
requestCode == CHANGE_BACKUPS_LOCATION_REQUEST_CODE &&
resultCode == Activity.RESULT_OK &&
data != null &&
data.getData() != null)
{
Uri oldBackupFolder = Objects.requireNonNull(SignalStore.settings().getSignalBackupDirectory());
Uri newBackupFolder = data.getData();
if (oldBackupFolder.equals(newBackupFolder)) {
return;
}
BackupDialog.showChangeBackupFolderDialog(requireContext(),
data,
() -> {
setBackupFolderName();
setBackupSummary();
});
}
}

Expand Down Expand Up @@ -175,6 +193,9 @@ private void setBackupFolderName() {
Uri backupUri = Objects.requireNonNull(SignalStore.settings().getSignalBackupDirectory());

folder.setVisibility(View.VISIBLE);
folder.setOnClickListener(v -> {
BackupDialog.startChooseFolderActivity(this, CHANGE_BACKUPS_LOCATION_REQUEST_CODE);
});
folderName.setText(StorageUtil.getDisplayPath(requireContext(), backupUri));
} else if (StorageUtil.canWriteInSignalStorageDir()) {
try {
Expand Down
51 changes: 44 additions & 7 deletions app/src/main/java/org/thoughtcrime/securesms/util/BackupUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;

import androidx.annotation.NonNull;
Expand All @@ -21,6 +22,7 @@
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.util.movedocumentfiles.Callback;

import java.io.File;
import java.security.SecureRandom;
Expand All @@ -31,6 +33,8 @@
import java.util.Locale;
import java.util.Objects;

import static org.thoughtcrime.securesms.util.movedocumentfiles.Util.moveDocumentFiles;

public class BackupUtil {

private static final String TAG = Log.tag(BackupUtil.class);
Expand Down Expand Up @@ -135,13 +139,7 @@ private static List<BackupInfo> getAllBackupsNewestFirst() throws NoExternalStor
}

@RequiresApi(29)
private static List<BackupInfo> getAllBackupsNewestFirstApi29() {
Uri backupDirectoryUri = SignalStore.settings().getSignalBackupDirectory();
if (backupDirectoryUri == null) {
Log.i(TAG, "Backup directory is not set. Returning an empty list.");
return Collections.emptyList();
}

public static List<BackupInfo> getAllBackupsNewestFirstFromDirectoryApi29(Uri backupDirectoryUri) {
DocumentFile backupDirectory = DocumentFile.fromTreeUri(ApplicationDependencies.getApplication(), backupDirectoryUri);
if (backupDirectory == null || !backupDirectory.exists() || !backupDirectory.canRead()) {
Log.w(TAG, "Backup directory is inaccessible. Returning an empty list.");
Expand All @@ -166,6 +164,45 @@ private static List<BackupInfo> getAllBackupsNewestFirstApi29() {
return backups;
}

@RequiresApi(29)
public static void deleteAllBackupsInDirectory(Uri backupDirectoryUri) {
List<BackupInfo> backups = getAllBackupsNewestFirstFromDirectoryApi29(backupDirectoryUri);
for (BackupInfo backupInfo : backups) {
backupInfo.delete();
}
}

@RequiresApi(29)
public static void moveAllBackupsToNewDirectoryAsync(Context context, Uri oldDirectoryUri, Uri targetDirectoryUri, Callback callback) {
new AsyncTask<Void, Void, Void>() {
@Override protected Void doInBackground(Void... voids) {
moveAllBackupsToNewDirectory(context, oldDirectoryUri, targetDirectoryUri, callback);
return null;
}
}.execute();
}

@RequiresApi(29)
public static void moveAllBackupsToNewDirectory(Context context, Uri oldDirectoryUri, Uri targetDirectoryUri, Callback callback) {
List<BackupInfo> backups = getAllBackupsNewestFirstFromDirectoryApi29(oldDirectoryUri);
DocumentFile[] documentFiles = new DocumentFile[backups.size()];
for(int i = 0; i < backups.size(); ++i) {
documentFiles[i] = DocumentFile.fromSingleUri(context, backups.get(i).getUri());
}
DocumentFile targetDirectory = DocumentFile.fromTreeUri(context, targetDirectoryUri);
moveDocumentFiles(context.getContentResolver(), documentFiles, targetDirectory, callback);
}

@RequiresApi(29)
private static List<BackupInfo> getAllBackupsNewestFirstApi29() {
Uri backupDirectoryUri = SignalStore.settings().getSignalBackupDirectory();
if (backupDirectoryUri == null) {
Log.i(TAG, "Backup directory is not set. Returning an empty list.");
return Collections.emptyList();
}
return getAllBackupsNewestFirstFromDirectoryApi29(backupDirectoryUri);
}

public static @Nullable BackupInfo getBackupInfoFromSingleUri(@NonNull Context context, @NonNull Uri singleUri) throws BackupFileException {
DocumentFile documentFile = Objects.requireNonNull(DocumentFile.fromSingleUri(context, singleUri));

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.thoughtcrime.securesms.util.movedocumentfiles;

public class Callback {
private long totalLength;
public void setTotalLength(long totalLength) {
this.totalLength = totalLength;
}
public long getTotalLength() {
return totalLength;
}
public void onSuccess() { }
public void onProgress(long bytesCopied) { }
public void onPartialSuccess() { }
public void onError(ErrorType errorType) { }
public void onFinish() { }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.thoughtcrime.securesms.util.movedocumentfiles;

public enum ErrorType {
TARGET_DIRECTORY_NOT_WRITABLE,
GENERIC
}
Loading