mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-22 01:40:07 +01:00
Implement new workflow for scoped storage backup selection.
This commit is contained in:
committed by
Greyson Parrelli
parent
9a1c869efe
commit
ee3d7a9a35
@@ -0,0 +1,259 @@
|
||||
package org.thoughtcrime.securesms.preferences;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.text.HtmlCompat;
|
||||
import androidx.documentfile.provider.DocumentFile;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.backup.BackupDialog;
|
||||
import org.thoughtcrime.securesms.backup.FullBackupBase;
|
||||
import org.thoughtcrime.securesms.database.NoExternalStorageException;
|
||||
import org.thoughtcrime.securesms.jobs.LocalBackupJob;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||
import org.thoughtcrime.securesms.util.BackupUtil;
|
||||
import org.thoughtcrime.securesms.util.StorageUtil;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
|
||||
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 View create;
|
||||
private View folder;
|
||||
private View verify;
|
||||
private TextView toggle;
|
||||
private TextView info;
|
||||
private TextView summary;
|
||||
private TextView folderName;
|
||||
private ProgressBar progress;
|
||||
private TextView progressSummary;
|
||||
|
||||
@Override
|
||||
public @Nullable View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_backups, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
create = view.findViewById(R.id.fragment_backup_create);
|
||||
folder = view.findViewById(R.id.fragment_backup_folder);
|
||||
verify = view.findViewById(R.id.fragment_backup_verify);
|
||||
toggle = view.findViewById(R.id.fragment_backup_toggle);
|
||||
info = view.findViewById(R.id.fragment_backup_info);
|
||||
summary = view.findViewById(R.id.fragment_backup_create_summary);
|
||||
folderName = view.findViewById(R.id.fragment_backup_folder_name);
|
||||
progress = view.findViewById(R.id.fragment_backup_progress);
|
||||
progressSummary = view.findViewById(R.id.fragment_backup_progress_summary);
|
||||
|
||||
toggle.setOnClickListener(unused -> onToggleClicked());
|
||||
create.setOnClickListener(unused -> onCreateClicked());
|
||||
verify.setOnClickListener(unused -> BackupDialog.showVerifyBackupPassphraseDialog(requireContext()));
|
||||
|
||||
EventBus.getDefault().register(this);
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
((AppCompatActivity) getActivity()).getSupportActionBar().setTitle(R.string.BackupsPreferenceFragment__chat_backups);
|
||||
|
||||
setBackupStatus();
|
||||
setBackupSummary();
|
||||
setInfo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
EventBus.getDefault().unregister(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
|
||||
if (requestCode == CHOOSE_BACKUPS_LOCATION_REQUEST_CODE && resultCode == Activity.RESULT_OK && data != null && data.getData() != null) {
|
||||
|
||||
DocumentFile backupDirectory = DocumentFile.fromTreeUri(requireContext(), data.getData());
|
||||
if (backupDirectory == null || !backupDirectory.isDirectory()) {
|
||||
Log.w(TAG, "Could not open backup directory.");
|
||||
return;
|
||||
}
|
||||
|
||||
BackupDialog.showEnableBackupDialog(requireContext(),
|
||||
data,
|
||||
backupDirectory.getName(),
|
||||
this::setBackupsEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEvent(FullBackupBase.BackupEvent event) {
|
||||
if (event.getType() == FullBackupBase.BackupEvent.Type.PROGRESS) {
|
||||
create.setEnabled(false);
|
||||
summary.setText(getString(R.string.BackupsPreferenceFragment__in_progress));
|
||||
progress.setVisibility(View.VISIBLE);
|
||||
progressSummary.setVisibility(View.VISIBLE);
|
||||
progressSummary.setText(getString(R.string.BackupsPreferenceFragment__d_so_far, event.getCount()));
|
||||
} else if (event.getType() == FullBackupBase.BackupEvent.Type.FINISHED) {
|
||||
create.setEnabled(true);
|
||||
progress.setVisibility(View.GONE);
|
||||
progressSummary.setVisibility(View.GONE);
|
||||
setBackupSummary();
|
||||
}
|
||||
}
|
||||
|
||||
private void setBackupStatus() {
|
||||
if (TextSecurePreferences.isBackupEnabled(requireContext())) {
|
||||
if (BackupUtil.canUserAccessBackupDirectory(requireContext())) {
|
||||
setBackupsEnabled();
|
||||
} else {
|
||||
Log.w(TAG, "Cannot access backup directory. Disabling backups.");
|
||||
|
||||
BackupUtil.disableBackups(requireContext());
|
||||
setBackupsDisabled();
|
||||
}
|
||||
} else {
|
||||
setBackupsDisabled();
|
||||
}
|
||||
}
|
||||
|
||||
private void setBackupSummary() {
|
||||
summary.setText(getString(R.string.BackupsPreferenceFragment__last_backup, BackupUtil.getLastBackupTime(requireContext(), Locale.getDefault())));
|
||||
}
|
||||
|
||||
private void setBackupFolderName() {
|
||||
folder.setVisibility(View.GONE);
|
||||
|
||||
if (BackupUtil.canUserAccessBackupDirectory(requireContext())) {
|
||||
if (BackupUtil.isUserSelectionRequired(requireContext()) &&
|
||||
BackupUtil.canUserAccessBackupDirectory(requireContext()))
|
||||
{
|
||||
Uri backupUri = Objects.requireNonNull(SignalStore.settings().getSignalBackupDirectory());
|
||||
DocumentFile backupFile = Objects.requireNonNull(DocumentFile.fromTreeUri(requireContext(), backupUri));
|
||||
|
||||
if (backupFile.getName() != null) {
|
||||
folder.setVisibility(View.VISIBLE);
|
||||
folderName.setText(backupFile.getName());
|
||||
}
|
||||
} else if (StorageUtil.canWriteInSignalStorageDir()) {
|
||||
try {
|
||||
folder.setVisibility(View.VISIBLE);
|
||||
folderName.setText(StorageUtil.getBackupDirectory().getPath());
|
||||
} catch (NoExternalStorageException e) {
|
||||
Log.w(TAG, "Could not display folder name.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setInfo() {
|
||||
String link = String.format("<a href=\"%s\">%s</a>", getString(R.string.backup_support_url), getString(R.string.BackupsPreferenceFragment__learn_more));
|
||||
String infoText = getString(R.string.BackupsPreferenceFragment__to_restore_a_backup, link);
|
||||
|
||||
info.setText(HtmlCompat.fromHtml(infoText, 0));
|
||||
info.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
}
|
||||
|
||||
private void onToggleClicked() {
|
||||
if (BackupUtil.isUserSelectionRequired(requireContext())) {
|
||||
onToggleClickedApi29();
|
||||
} else {
|
||||
onToggleClickedLegacy();
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(29)
|
||||
private void onToggleClickedApi29() {
|
||||
if (!TextSecurePreferences.isBackupEnabled(requireContext())) {
|
||||
BackupDialog.showChooseBackupLocationDialog(this, CHOOSE_BACKUPS_LOCATION_REQUEST_CODE);
|
||||
} else {
|
||||
BackupDialog.showDisableBackupDialog(requireContext(), this::setBackupsDisabled);
|
||||
}
|
||||
}
|
||||
|
||||
private void onToggleClickedLegacy() {
|
||||
Permissions.with(this)
|
||||
.request(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
.ifNecessary()
|
||||
.onAllGranted(() -> {
|
||||
if (!TextSecurePreferences.isBackupEnabled(requireContext())) {
|
||||
BackupDialog.showEnableBackupDialog(requireContext(), null, null, this::setBackupsEnabled);
|
||||
} else {
|
||||
BackupDialog.showDisableBackupDialog(requireContext(), this::setBackupsDisabled);
|
||||
}
|
||||
})
|
||||
.withPermanentDenialDialog(getString(R.string.BackupsPreferenceFragment_signal_requires_external_storage_permission_in_order_to_create_backups))
|
||||
.execute();
|
||||
}
|
||||
|
||||
private void onCreateClicked() {
|
||||
if (BackupUtil.isUserSelectionRequired(requireContext())) {
|
||||
onCreateClickedApi29();
|
||||
} else {
|
||||
onCreateClickedLegacy();
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(29)
|
||||
private void onCreateClickedApi29() {
|
||||
Log.i(TAG, "Queing backup...");
|
||||
LocalBackupJob.enqueue(true);
|
||||
}
|
||||
|
||||
private void onCreateClickedLegacy() {
|
||||
Permissions.with(this)
|
||||
.request(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
.ifNecessary()
|
||||
.onAllGranted(() -> {
|
||||
Log.i(TAG, "Queuing backup...");
|
||||
LocalBackupJob.enqueue(true);
|
||||
})
|
||||
.withPermanentDenialDialog(getString(R.string.BackupsPreferenceFragment_signal_requires_external_storage_permission_in_order_to_create_backups))
|
||||
.execute();
|
||||
}
|
||||
|
||||
private void setBackupsEnabled() {
|
||||
toggle.setText(R.string.BackupsPreferenceFragment__turn_off);
|
||||
create.setVisibility(View.VISIBLE);
|
||||
verify.setVisibility(View.VISIBLE);
|
||||
setBackupFolderName();
|
||||
}
|
||||
|
||||
private void setBackupsDisabled() {
|
||||
toggle.setText(R.string.BackupsPreferenceFragment__turn_on);
|
||||
create.setVisibility(View.GONE);
|
||||
folder.setVisibility(View.GONE);
|
||||
verify.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
@@ -1,33 +1,25 @@
|
||||
package org.thoughtcrime.securesms.preferences;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.app.ActivityOptionsCompat;
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.backup.BackupDialog;
|
||||
import org.thoughtcrime.securesms.backup.FullBackupBase.BackupEvent;
|
||||
import org.thoughtcrime.securesms.components.SwitchPreferenceCompat;
|
||||
import org.thoughtcrime.securesms.jobs.LocalBackupJob;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||
import org.thoughtcrime.securesms.preferences.widgets.ProgressPreference;
|
||||
import org.thoughtcrime.securesms.util.BackupUtil;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
public class ChatsPreferenceFragment extends ListSummaryPreferenceFragment {
|
||||
@@ -46,16 +38,12 @@ public class ChatsPreferenceFragment extends ListSummaryPreferenceFragment {
|
||||
findPreference(TextSecurePreferences.MESSAGE_BODY_TEXT_SIZE_PREF)
|
||||
.setOnPreferenceChangeListener(new ListSummaryListener());
|
||||
|
||||
findPreference(TextSecurePreferences.BACKUP_ENABLED)
|
||||
.setOnPreferenceClickListener(new BackupClickListener());
|
||||
findPreference(TextSecurePreferences.BACKUP_NOW)
|
||||
.setOnPreferenceClickListener(new BackupCreateListener());
|
||||
findPreference(TextSecurePreferences.BACKUP_PASSPHRASE_VERIFY)
|
||||
.setOnPreferenceClickListener(new BackupVerifyListener());
|
||||
findPreference(TextSecurePreferences.BACKUP).setOnPreferenceClickListener(unused -> {
|
||||
goToBackupsPreferenceFragment();
|
||||
return true;
|
||||
});
|
||||
|
||||
initializeListSummary((ListPreference) findPreference(TextSecurePreferences.MESSAGE_BODY_TEXT_SIZE_PREF));
|
||||
|
||||
EventBus.getDefault().register(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -68,7 +56,6 @@ public class ChatsPreferenceFragment extends ListSummaryPreferenceFragment {
|
||||
super.onResume();
|
||||
((ApplicationPreferencesActivity)getActivity()).getSupportActionBar().setTitle(R.string.preferences__chats);
|
||||
setMediaDownloadSummaries();
|
||||
setBackupSummary();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -82,24 +69,8 @@ public class ChatsPreferenceFragment extends ListSummaryPreferenceFragment {
|
||||
Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEvent(BackupEvent event) {
|
||||
ProgressPreference preference = (ProgressPreference)findPreference(TextSecurePreferences.BACKUP_NOW);
|
||||
|
||||
if (event.getType() == BackupEvent.Type.PROGRESS) {
|
||||
preference.setEnabled(false);
|
||||
preference.setSummary(getString(R.string.ChatsPreferenceFragment_in_progress));
|
||||
preference.setProgress(event.getCount());
|
||||
} else if (event.getType() == BackupEvent.Type.FINISHED) {
|
||||
preference.setEnabled(true);
|
||||
preference.setProgressVisible(false);
|
||||
setBackupSummary();
|
||||
}
|
||||
}
|
||||
|
||||
private void setBackupSummary() {
|
||||
findPreference(TextSecurePreferences.BACKUP_NOW)
|
||||
.setSummary(String.format(getString(R.string.ChatsPreferenceFragment_last_backup_s), BackupUtil.getLastBackupTime(getContext(), Locale.getDefault())));
|
||||
private void goToBackupsPreferenceFragment() {
|
||||
((ApplicationPreferencesActivity) requireActivity()).pushFragment(new BackupsPreferenceFragment());
|
||||
}
|
||||
|
||||
private void setMediaDownloadSummaries() {
|
||||
@@ -124,51 +95,6 @@ public class ChatsPreferenceFragment extends ListSummaryPreferenceFragment {
|
||||
: TextUtils.join(", ", outValues);
|
||||
}
|
||||
|
||||
private class BackupClickListener implements Preference.OnPreferenceClickListener {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
Permissions.with(ChatsPreferenceFragment.this)
|
||||
.request(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
.ifNecessary()
|
||||
.onAllGranted(() -> {
|
||||
if (!((SwitchPreferenceCompat)preference).isChecked()) {
|
||||
BackupDialog.showEnableBackupDialog(getActivity(), (SwitchPreferenceCompat)preference);
|
||||
} else {
|
||||
BackupDialog.showDisableBackupDialog(getActivity(), (SwitchPreferenceCompat)preference);
|
||||
}
|
||||
})
|
||||
.withPermanentDenialDialog(getString(R.string.ChatsPreferenceFragment_signal_requires_external_storage_permission_in_order_to_create_backups))
|
||||
.execute();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private class BackupCreateListener implements Preference.OnPreferenceClickListener {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
Permissions.with(ChatsPreferenceFragment.this)
|
||||
.request(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
.ifNecessary()
|
||||
.onAllGranted(() -> {
|
||||
Log.i(TAG, "Starting backup from user");
|
||||
LocalBackupJob.enqueue(true);
|
||||
})
|
||||
.withPermanentDenialDialog(getString(R.string.ChatsPreferenceFragment_signal_requires_external_storage_permission_in_order_to_create_backups))
|
||||
.execute();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private class BackupVerifyListener implements Preference.OnPreferenceClickListener {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
BackupDialog.showVerifyBackupPassphraseDialog(requireContext());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private class MediaDownloadChangeListener implements Preference.OnPreferenceChangeListener {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
|
||||
Reference in New Issue
Block a user