diff --git a/app/src/main/java/org/shadowice/flocke/andotp/Activities/BackupActivity.java b/app/src/main/java/org/shadowice/flocke/andotp/Activities/BackupActivity.java index 45c19f20..ec154f3b 100644 --- a/app/src/main/java/org/shadowice/flocke/andotp/Activities/BackupActivity.java +++ b/app/src/main/java/org/shadowice/flocke/andotp/Activities/BackupActivity.java @@ -36,11 +36,16 @@ import android.text.TextUtils; import android.util.Log; import android.view.View; import android.view.ViewStub; -import android.widget.LinearLayout; -import android.widget.Switch; +import android.widget.AdapterView; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.ProgressBar; +import android.widget.Spinner; import android.widget.TextView; import android.widget.Toast; +import com.google.android.material.switchmaterial.SwitchMaterial; + import org.openintents.openpgp.OpenPgpError; import org.openintents.openpgp.OpenPgpSignatureResult; import org.openintents.openpgp.util.OpenPgpApi; @@ -48,6 +53,7 @@ import org.openintents.openpgp.util.OpenPgpServiceConnection; import org.shadowice.flocke.andotp.Database.Entry; import org.shadowice.flocke.andotp.Dialogs.PasswordEntryDialog; import org.shadowice.flocke.andotp.R; +import org.shadowice.flocke.andotp.Tasks.BackupTask; import org.shadowice.flocke.andotp.Utilities.BackupHelper; import org.shadowice.flocke.andotp.Utilities.Constants; import org.shadowice.flocke.andotp.Utilities.DatabaseHelper; @@ -67,9 +73,11 @@ import java.util.concurrent.Executors; import javax.crypto.SecretKey; -public class BackupActivity extends BaseActivity { +public class BackupActivity extends BaseActivity + implements BackupTask.BackupCallback { private final static String TAG = BackupActivity.class.getSimpleName(); + private Constants.BackupType backupType = Constants.BackupType.ENCRYPTED; private SecretKey encryptionKey = null; private OpenPgpServiceConnection pgpServiceConnection; @@ -78,7 +86,14 @@ public class BackupActivity extends BaseActivity { private Uri encryptTargetFile; private Uri decryptSourceFile; - private Switch replace; + private Button btnBackup; + private Button btnRestore; + private TextView txtBackupLabel; + private TextView txtBackupWarning; + private SwitchMaterial swReplace; + private CheckBox chkOldFormat; + private ProgressBar progressBackup; + private ProgressBar progressRestore; private boolean reload = false; @@ -100,96 +115,119 @@ public class BackupActivity extends BaseActivity { byte[] keyMaterial = callingIntent.getByteArrayExtra(Constants.EXTRA_BACKUP_ENCRYPTION_KEY); encryptionKey = EncryptionHelper.generateSymmetricKey(keyMaterial); - // Plain-text + Spinner spBackupType = v.findViewById(R.id.backupType); + btnBackup = v.findViewById(R.id.buttonBackup); + btnRestore = v.findViewById(R.id.buttonRestore); + txtBackupLabel = v.findViewById(R.id.backupLabel); + txtBackupWarning = v.findViewById(R.id.backupErrorLabel); + swReplace = v.findViewById(R.id.backup_replace); + chkOldFormat = v.findViewById(R.id.restoreOldCrypt); + progressBackup = v.findViewById(R.id.progressBarBackup); + progressRestore = v.findViewById(R.id.progressBarRestore); - LinearLayout backupPlain = v.findViewById(R.id.button_backup_plain); - LinearLayout restorePlain = v.findViewById(R.id.button_restore_plain); + setupBackupType(settings.getDefaultBackupType()); + spBackupType.setSelection(backupType.ordinal()); - backupPlain.setOnClickListener(new View.OnClickListener() { + spBackupType.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override - public void onClick(View view) { - backupPlainWithWarning(); + public void onItemSelected(AdapterView adapterView, View view, int i, long l) { + Constants.BackupType type = Constants.BackupType.values()[i]; + setupBackupType(type); } + + @Override + public void onNothingSelected(AdapterView adapterView) { } }); - restorePlain.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - showOpenFileSelector(Constants.INTENT_BACKUP_OPEN_DOCUMENT_PLAIN); - } - }); - - // Password - - TextView cryptSetup = v.findViewById(R.id.msg_crypt_setup); - LinearLayout backupCrypt = v.findViewById(R.id.button_backup_crypt); - LinearLayout restoreCrypt = v.findViewById(R.id.button_restore_crypt); - LinearLayout restoreCryptOld = v.findViewById(R.id.button_restore_crypt_old); - - if (settings.getBackupPasswordEnc().isEmpty()) { - cryptSetup.setVisibility(View.VISIBLE); - } else { - cryptSetup.setVisibility(View.GONE); - } - - backupCrypt.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - showSaveFileSelector(Constants.BACKUP_MIMETYPE_CRYPT, Constants.BackupType.ENCRYPTED, Constants.INTENT_BACKUP_SAVE_DOCUMENT_CRYPT); - } - }); - - restoreCrypt.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - showOpenFileSelector(Constants.INTENT_BACKUP_OPEN_DOCUMENT_CRYPT); - } - }); - - restoreCryptOld.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - showOpenFileSelector(Constants.INTENT_BACKUP_OPEN_DOCUMENT_CRYPT_OLD); - } - }); - - // OpenPGP - - String PGPProvider = settings.getOpenPGPProvider(); - pgpEncryptionUserIDs = settings.getOpenPGPEncryptionUserIDs(); - - TextView setupPGP = v.findViewById(R.id.msg_openpgp_setup); - LinearLayout backupPGP = v.findViewById(R.id.button_backup_openpgp); - LinearLayout restorePGP = v.findViewById(R.id.button_restore_openpgp); - - if (TextUtils.isEmpty(PGPProvider)) { - setupPGP.setVisibility(View.VISIBLE); - backupPGP.setVisibility(View.GONE); - restorePGP.setVisibility(View.GONE); - } else if (TextUtils.isEmpty(pgpEncryptionUserIDs)){ - setupPGP.setVisibility(View.VISIBLE); - setupPGP.setText(R.string.backup_desc_openpgp_keyid); - backupPGP.setVisibility(View.GONE); - } else { - pgpServiceConnection = new OpenPgpServiceConnection(BackupActivity.this.getApplicationContext(), PGPProvider); - pgpServiceConnection.bindToService(); - - backupPGP.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { + btnBackup.setOnClickListener(view -> { + switch (backupType) { + case PLAIN_TEXT: + backupPlainWithWarning(); + break; + case ENCRYPTED: + showSaveFileSelector(Constants.BACKUP_MIMETYPE_CRYPT, Constants.BackupType.ENCRYPTED, Constants.INTENT_BACKUP_SAVE_DOCUMENT_CRYPT); + break; + case OPEN_PGP: showSaveFileSelector(Constants.BACKUP_MIMETYPE_PGP, Constants.BackupType.OPEN_PGP, Constants.INTENT_BACKUP_SAVE_DOCUMENT_PGP); - } - }); + break; + } + }); - restorePGP.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { + btnRestore.setOnClickListener(view -> { + switch (backupType) { + case PLAIN_TEXT: + showOpenFileSelector(Constants.INTENT_BACKUP_OPEN_DOCUMENT_PLAIN); + break; + case ENCRYPTED: + if (chkOldFormat.isChecked()) + showOpenFileSelector(Constants.INTENT_BACKUP_OPEN_DOCUMENT_CRYPT_OLD); + else + showOpenFileSelector(Constants.INTENT_BACKUP_OPEN_DOCUMENT_CRYPT); + break; + case OPEN_PGP: showOpenFileSelector(Constants.INTENT_BACKUP_OPEN_DOCUMENT_PGP); + break; + } + }); + } + + private void setupBackupType(Constants.BackupType type) { + switch (type) { + case PLAIN_TEXT: + txtBackupLabel.setText(R.string.backup_label_warning_plain); + + chkOldFormat.setVisibility(View.GONE); + txtBackupWarning.setVisibility(View.GONE); + + btnBackup.setEnabled(true); + btnRestore.setEnabled(true); + + break; + case ENCRYPTED: + txtBackupLabel.setText(R.string.backup_label_crypt); + + chkOldFormat.setVisibility(View.VISIBLE); + txtBackupWarning.setVisibility(View.GONE); + + btnBackup.setEnabled(true); + btnRestore.setEnabled(true); + + break; + case OPEN_PGP: + txtBackupLabel.setText(R.string.backup_label_pgp); + + chkOldFormat.setVisibility(View.GONE); + + String PGPProvider = settings.getOpenPGPProvider(); + pgpEncryptionUserIDs = settings.getOpenPGPEncryptionUserIDs(); + + if (TextUtils.isEmpty(PGPProvider)) { + txtBackupWarning.setText(R.string.backup_desc_openpgp_provider); + txtBackupWarning.setVisibility(View.VISIBLE); + + btnBackup.setEnabled(false); + btnRestore.setEnabled(false); + } else if (TextUtils.isEmpty(pgpEncryptionUserIDs)){ + txtBackupWarning.setText(R.string.backup_desc_openpgp_keyid); + txtBackupWarning.setVisibility(View.VISIBLE); + + btnBackup.setEnabled(false); + btnRestore.setEnabled(false); + } else { + txtBackupWarning.setVisibility(View.GONE); + + btnBackup.setEnabled(true); + btnRestore.setEnabled(true); + + pgpServiceConnection = new OpenPgpServiceConnection(BackupActivity.this.getApplicationContext(), PGPProvider); + pgpServiceConnection.bindToService(); } - }); + + break; } - replace = v.findViewById(R.id.backup_replace); + backupType = type; + settings.setDefaultBackupType(type); } // End with a result @@ -221,6 +259,38 @@ public class BackupActivity extends BaseActivity { pgpServiceConnection.unbindFromService(); } + @Override + public void onBackupFinished() { + hideProgress(); + } + + @Override + public void onBackupFailed() { + hideProgress(); + } + + private void showProgress(boolean restore) { + btnBackup.setEnabled(false); + btnRestore.setEnabled(false); + chkOldFormat.setEnabled(false); + swReplace.setEnabled(false); + + if (restore) + progressRestore.setVisibility(View.VISIBLE); + else + progressBackup.setVisibility(View.VISIBLE); + } + + private void hideProgress() { + btnBackup.setEnabled(true); + btnRestore.setEnabled(true); + chkOldFormat.setEnabled(true); + swReplace.setEnabled(true); + + progressRestore.setVisibility(View.GONE); + progressBackup.setVisibility(View.GONE); + } + // Get the result from external activities @Override protected void onActivityResult(int requestCode, int resultCode, Intent intent) { @@ -293,26 +363,11 @@ public class BackupActivity extends BaseActivity { } else { if (settings.isBackupLocationSet()) { if (intentId == Constants.INTENT_BACKUP_SAVE_DOCUMENT_PLAIN) { - BackupHelper.BackupFile plainBackupFile = BackupHelper.backupFile(this, settings.getBackupLocation(), Constants.BackupType.PLAIN_TEXT); - - if (plainBackupFile.file != null) - doBackupPlain(plainBackupFile.file.getUri()); - else - Toast.makeText(this, plainBackupFile.errorMessage, Toast.LENGTH_LONG).show(); + doBackupPlain(null); } else if (intentId == Constants.INTENT_BACKUP_SAVE_DOCUMENT_CRYPT) { - BackupHelper.BackupFile cryptBackupFile = BackupHelper.backupFile(this, settings.getBackupLocation(), Constants.BackupType.ENCRYPTED); - - if (cryptBackupFile.file != null) - doBackupCrypt(cryptBackupFile.file.getUri()); - else - Toast.makeText(this, cryptBackupFile.errorMessage, Toast.LENGTH_LONG).show(); + doBackupCrypt(null); } else if (intentId == Constants.INTENT_BACKUP_SAVE_DOCUMENT_PGP) { - BackupHelper.BackupFile pgpBackupFile = BackupHelper.backupFile(this, settings.getBackupLocation(), Constants.BackupType.OPEN_PGP); - - if (pgpBackupFile.file != null) - backupEncryptedWithPGP(pgpBackupFile.file.getUri(), null); - else - Toast.makeText(this, pgpBackupFile.errorMessage, Toast.LENGTH_LONG).show(); + backupEncryptedWithPGP(null, null); } } else { Toast.makeText(this, R.string.backup_toast_no_location, Toast.LENGTH_LONG).show(); @@ -324,7 +379,7 @@ public class BackupActivity extends BaseActivity { ArrayList entries = DatabaseHelper.stringToEntries(text); if (entries.size() > 0) { - if (! replace.isChecked()) { + if (! swReplace.isChecked()) { ArrayList currentEntries = DatabaseHelper.loadDatabase(this, encryptionKey); entries.removeAll(currentEntries); @@ -360,14 +415,17 @@ public class BackupActivity extends BaseActivity { ArrayList entries = DatabaseHelper.loadDatabase(this, encryptionKey); ExecutorService executor = Executors.newSingleThreadExecutor(); - BackupHelper.SaveStringToFile runnable = new BackupHelper.SaveStringToFile(this, uri, DatabaseHelper.entriesToString(entries)); - executor.execute(runnable); + BackupTask backupTask = new BackupTask(this, Constants.BackupType.PLAIN_TEXT, DatabaseHelper.entriesToString(entries), this); + + if (uri != null) + backupTask.setUri(uri); + + showProgress(false); + executor.execute(backupTask); } else { Toast.makeText(this, R.string.backup_toast_storage_not_accessible, Toast.LENGTH_LONG).show(); } - - finishWithResult(); } private void backupPlainWithWarning() { @@ -466,12 +524,11 @@ public class BackupActivity extends BaseActivity { private void doBackupCryptWithPassword(Uri uri, String password) { if (Tools.isExternalStorageWritable()) { - BackupHelper.backupToFileAsync(this, uri, password, encryptionKey, false); + showProgress(false); + BackupHelper.backupToFileAsync(this, uri, password, encryptionKey, this, false); } else { Toast.makeText(this, R.string.backup_toast_storage_not_accessible, Toast.LENGTH_LONG).show(); } - - finishWithResult(); } /* OpenPGP backup functions */ @@ -492,14 +549,17 @@ public class BackupActivity extends BaseActivity { private void doBackupEncrypted(Uri uri, String data) { if (Tools.isExternalStorageWritable()) { ExecutorService executor = Executors.newSingleThreadExecutor(); - BackupHelper.SaveStringToFile runnable = new BackupHelper.SaveStringToFile(this, uri, data); - executor.execute(runnable); + BackupTask backupTask = new BackupTask(this, Constants.BackupType.OPEN_PGP, data, this); + + if (uri != null) + backupTask.setUri(uri); + + showProgress(false); + executor.execute(backupTask); } else { Toast.makeText(this, R.string.backup_toast_storage_not_accessible, Toast.LENGTH_LONG).show(); } - - finishWithResult(); } private void backupEncryptedWithPGP(Uri uri, Intent encryptIntent) { diff --git a/app/src/main/java/org/shadowice/flocke/andotp/Tasks/BackupTask.java b/app/src/main/java/org/shadowice/flocke/andotp/Tasks/BackupTask.java new file mode 100644 index 00000000..9a5c7705 --- /dev/null +++ b/app/src/main/java/org/shadowice/flocke/andotp/Tasks/BackupTask.java @@ -0,0 +1,124 @@ +package org.shadowice.flocke.andotp.Tasks; + +import android.content.Context; +import android.net.Uri; +import android.os.Handler; +import android.os.Looper; +import android.widget.Toast; + +import org.shadowice.flocke.andotp.R; +import org.shadowice.flocke.andotp.Utilities.BackupHelper; +import org.shadowice.flocke.andotp.Utilities.Constants; +import org.shadowice.flocke.andotp.Utilities.Settings; +import org.shadowice.flocke.andotp.Utilities.StorageAccessHelper; + +public class BackupTask implements Runnable { + final private Context context; + final private String payload; + final private Constants.BackupType type; + final private BackupCallback callback; + + final private Handler handler; + + private boolean silent = false; + private Uri uri = null; + private String password = null; + + public BackupTask(Context context, Constants.BackupType type, String payload, BackupCallback callback) { + this.context = context; + this.payload = payload; + this.type = type; + this.callback = callback; + + this.handler = new Handler(Looper.getMainLooper()); + } + + public void setUri(Uri uri) { + this.uri = uri; + } + + public void setPassword(String password) { + this.password = password; + } + + public void setSilent(boolean silent) { + this.silent = silent; + } + + @Override + public void run() { + boolean success; + + if (uri == null) + uri = getTargetUri(); + + if (uri == null) + return; + + if (type == Constants.BackupType.PLAIN_TEXT) { + success = StorageAccessHelper.saveFile(context, uri, payload); + } else if (type == Constants.BackupType.ENCRYPTED) { + success = BackupHelper.backupToFile(context, uri, password, payload); + } else if (type == Constants.BackupType.OPEN_PGP) { + success = StorageAccessHelper.saveFile(context, uri, payload); + } else { + success = false; + } + + if (success) + onFinished(); + else + onFailed(); + } + + private Uri getTargetUri() { + Settings settings = new Settings(context); + + BackupHelper.BackupFile backupFile = BackupHelper.backupFile(context, settings.getBackupLocation(), type); + + if (backupFile.file != null) { + return backupFile.file.getUri(); + } else { + showToast(context, backupFile.errorMessage); + return null; + } + } + + private void showToast(Context context, int msgId) { + if (!this.silent) { + this.handler.post(new Runnable() { + @Override + public void run() { + Toast.makeText(context, msgId, Toast.LENGTH_LONG).show(); + } + }); + } + } + + private void onFinished() { + this.handler.post(new Runnable() { + @Override + public void run() { + Toast.makeText(context, R.string.backup_toast_export_success, Toast.LENGTH_LONG).show(); + if (callback != null) + callback.onBackupFinished(); + } + }); + } + + private void onFailed() { + this.handler.post(new Runnable() { + @Override + public void run() { + Toast.makeText(context, R.string.backup_toast_export_failed, Toast.LENGTH_LONG).show(); + if (callback != null) + callback.onBackupFailed(); + } + }); + } + + public interface BackupCallback { + void onBackupFinished(); + void onBackupFailed(); + } +} diff --git a/app/src/main/java/org/shadowice/flocke/andotp/Utilities/BackupHelper.java b/app/src/main/java/org/shadowice/flocke/andotp/Utilities/BackupHelper.java index da4fffe7..f1839ee9 100644 --- a/app/src/main/java/org/shadowice/flocke/andotp/Utilities/BackupHelper.java +++ b/app/src/main/java/org/shadowice/flocke/andotp/Utilities/BackupHelper.java @@ -2,14 +2,12 @@ package org.shadowice.flocke.andotp.Utilities; import android.content.Context; import android.net.Uri; -import android.os.Handler; -import android.os.Looper; -import android.widget.Toast; import androidx.documentfile.provider.DocumentFile; import org.shadowice.flocke.andotp.Database.Entry; import org.shadowice.flocke.andotp.R; +import org.shadowice.flocke.andotp.Tasks.BackupTask; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; @@ -106,25 +104,31 @@ public class BackupHelper { return Constants.BackupType.UNAVAILABLE; } - public static void backupToFileAsync(Context context, Uri uri, String password, SecretKey encryptionKey, boolean silent) { + public static void backupToFileAsync(Context context, Uri uri, String password, SecretKey encryptionKey, BackupTask.BackupCallback callback, boolean silent) { ArrayList entries = DatabaseHelper.loadDatabase(context, encryptionKey); ExecutorService executor = Executors.newSingleThreadExecutor(); - BackupCrypt runnable = new BackupCrypt(context, uri, password, entries, silent); - executor.execute(runnable); + BackupTask backupTask = new BackupTask(context, Constants.BackupType.ENCRYPTED, DatabaseHelper.entriesToString(entries), callback); + + if (uri != null) + backupTask.setUri(uri); + + backupTask.setPassword(password); + backupTask.setSilent(silent); + + executor.execute(backupTask); } public static boolean backupToFile(Context context, Uri uri, String password, SecretKey encryptionKey) { ArrayList entries = DatabaseHelper.loadDatabase(context, encryptionKey); - return backupToFile(context, uri, password, entries); - } - - public static boolean backupToFile(Context context, Uri uri, String password, ArrayList entries) - { - String plain = DatabaseHelper.entriesToString(entries); + return backupToFile(context, uri, password, plain); + } + + public static boolean backupToFile(Context context, Uri uri, String password, String plain) + { boolean success = true; try { @@ -149,73 +153,4 @@ public class BackupHelper { return success; } - - public static class BackupCrypt implements Runnable { - final private Context context; - final private Uri uri; - final private String password; - final private ArrayList entries; - final private boolean silent; - - public BackupCrypt(Context context, Uri uri, String password, ArrayList entries, boolean silent) { - this.context = context; - this.uri = uri; - this.password = password; - this.entries = entries; - this.silent = silent; - } - - @Override - public void run() { - boolean success = backupToFile(context, uri, password, entries); - - if (!silent) { - if (success) { - postMessage(R.string.backup_toast_export_success); - } else { - postMessage(R.string.backup_toast_export_failed); - } - } - } - - void postMessage(int msgId) { - new Handler(Looper.getMainLooper()).post(new Runnable() { - @Override - public void run() { - Toast.makeText(context, msgId, Toast.LENGTH_LONG).show(); - } - }); - } - } - - public static class SaveStringToFile implements Runnable { - final private Context context; - final private Uri uri; - final private String data; - - public SaveStringToFile(Context context, Uri uri, String data) { - this.context = context; - this.uri = uri; - this.data = data; - } - - @Override - public void run() { - boolean success = StorageAccessHelper.saveFile(context, uri, data); - - if (success) - postMessage(R.string.backup_toast_export_success); - else - postMessage(R.string.backup_toast_export_failed); - } - - void postMessage(int msgId) { - new Handler(Looper.getMainLooper()).post(new Runnable() { - @Override - public void run() { - Toast.makeText(context, msgId, Toast.LENGTH_LONG).show(); - } - }); - } - } } diff --git a/app/src/main/java/org/shadowice/flocke/andotp/Utilities/Settings.java b/app/src/main/java/org/shadowice/flocke/andotp/Utilities/Settings.java index b9bc1f38..c8853c3e 100644 --- a/app/src/main/java/org/shadowice/flocke/andotp/Utilities/Settings.java +++ b/app/src/main/java/org/shadowice/flocke/andotp/Utilities/Settings.java @@ -605,4 +605,13 @@ public class Settings { public boolean getBlockAutofill() { return getBoolean(R.string.settings_key_block_autofill, false); } + + public void setDefaultBackupType(Constants.BackupType type) { + setString(R.string.settings_key_backup_default_type, type.name().toLowerCase(Locale.ENGLISH)); + } + + public Constants.BackupType getDefaultBackupType() { + String defaultType = getString(R.string.settings_key_backup_default_type, Constants.BackupType.ENCRYPTED.name()); + return Constants.BackupType.valueOf(defaultType.toUpperCase(Locale.ENGLISH)); + } } diff --git a/app/src/main/res/layout/content_backup.xml b/app/src/main/res/layout/content_backup.xml index 76489466..a8fa7663 100644 --- a/app/src/main/res/layout/content_backup.xml +++ b/app/src/main/res/layout/content_backup.xml @@ -2,222 +2,78 @@ + app:layout_behavior="@string/appbar_scrolling_view_behavior"> - - + android:layout_height="wrap_content" + android:layout_marginHorizontal="@dimen/activity_margin" + android:layout_marginTop="@dimen/activity_margin_large" + android:gravity="center_horizontal"> + android:orientation="horizontal" + android:layout_width="wrap_content" + android:layout_height="wrap_content"> + android:textColor="@color/colorAccent" + android:textStyle="bold" + android:textAppearance="@style/TextAppearance.AppCompat.Widget.TextView.SpinnerItem" + android:text="@string/backup_label_type"/> - - - - - - - - - + android:entries="@array/backup_list_type_names" /> + android:layout_marginTop="@dimen/activity_margin" + android:padding="@dimen/activity_margin" + android:text="@string/backup_label_warning_plain"/> + android:textColor="?attr/colorExpiring" + android:text="@string/backup_desc_openpgp_provider" + android:visibility="gone"/> - + android:text="@string/backup_button_backup"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + android:layout_marginTop="@dimen/activity_margin" + android:indeterminate="true" + style="?android:attr/progressBarStyle" + android:visibility="gone"/> @@ -242,7 +98,7 @@ - + + +