diff --git a/res/values/strings.xml b/res/values/strings.xml index c0a1d4cec..7a0f410bd 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1368,7 +1368,6 @@ TAP TO UNLOCK Reminder: About - Delta Chat The messenger with the broadest audience in the world. Free and independent. @@ -1406,11 +1405,16 @@ Delta Chat needs access to your contacts and media in order to connect with friends and send files Please enter a valid email address and a password + Import backup + Backup found at \"%1$s\".\n\nDo you want to import and use all data and settings from it? + No backups found.\n\nCopy the backup to \"%1$s\" and try again. Alternatively, press \"Start messaging\" to go with the normal setup process. + Email address Password Advanced icon Loading … + One moment … diff --git a/src/org/thoughtcrime/securesms/RegistrationActivity.java b/src/org/thoughtcrime/securesms/RegistrationActivity.java index ddb744233..277c475d7 100644 --- a/src/org/thoughtcrime/securesms/RegistrationActivity.java +++ b/src/org/thoughtcrime/securesms/RegistrationActivity.java @@ -2,6 +2,7 @@ package org.thoughtcrime.securesms; import android.Manifest; import android.annotation.SuppressLint; +import android.app.AlertDialog; import android.app.ProgressDialog; import android.content.DialogInterface; import android.content.Intent; @@ -22,6 +23,7 @@ import com.b44t.messenger.DcContext; import com.b44t.messenger.DcEventCenter; import com.dd.CircularProgressButton; +import org.thoughtcrime.securesms.connect.ApplicationDcContext; import org.thoughtcrime.securesms.connect.DcHelper; import org.thoughtcrime.securesms.permissions.Permissions; import org.thoughtcrime.securesms.util.Dialogs; @@ -222,6 +224,7 @@ public class RegistrationActivity extends BaseActionBarActivity implements DcEve // calling configure() results in // receiving multiple DC_EVENT_CONFIGURE_PROGRESS events + DcHelper.getContext(this).captureNextError(); DcHelper.getContext(this).configure(); } @@ -240,14 +243,24 @@ public class RegistrationActivity extends BaseActionBarActivity implements DcEve @Override public void handleEvent(int eventId, Object data1, Object data2) { if (eventId==DcContext.DC_EVENT_CONFIGURE_PROGRESS) { + ApplicationDcContext dcContext = DcHelper.getContext(this); long progress = (Long)data1; if (progress==0/*error/aborted*/) { + dcContext.endCaptureNextError(); progressDialog.dismiss(); + if (dcContext.hasCapturedError()) { + new AlertDialog.Builder(this) + .setMessage(dcContext.getCapturedError()) + .setPositiveButton(android.R.string.ok, null) + .show(); + } } else if (progress<1000/*progress in permille*/) { - progressDialog.setMessage("Loading ... " + progress); + int percent = (int)progress / 10; + progressDialog.setMessage(getResources().getString(R.string.one_moment)+String.format(" %d%%", percent)); } else if (progress==1000/*done*/) { + dcContext.endCaptureNextError(); progressDialog.dismiss(); Intent conversationList = new Intent(getApplicationContext(), ConversationListActivity.class); startActivity(conversationList); diff --git a/src/org/thoughtcrime/securesms/WelcomeActivity.java b/src/org/thoughtcrime/securesms/WelcomeActivity.java index 825d46c0c..ac36d1031 100644 --- a/src/org/thoughtcrime/securesms/WelcomeActivity.java +++ b/src/org/thoughtcrime/securesms/WelcomeActivity.java @@ -2,7 +2,10 @@ package org.thoughtcrime.securesms; import android.Manifest; import android.annotation.SuppressLint; +import android.app.AlertDialog; +import android.app.ProgressDialog; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; import android.support.annotation.NonNull; @@ -19,9 +22,12 @@ import android.widget.TextView; import com.b44t.messenger.DcContext; import com.b44t.messenger.DcEventCenter; +import org.thoughtcrime.securesms.connect.ApplicationDcContext; import org.thoughtcrime.securesms.connect.DcHelper; import org.thoughtcrime.securesms.permissions.Permissions; +import java.io.File; + /** * The welcome activity. Provides the user with useful information about the app. * It also allows the triggering of the backup import process. @@ -85,7 +91,10 @@ public class WelcomeActivity extends BaseActionBarActivity implements DcEventCen setContentView(R.layout.welcome_activity); initializeResources(); - DcHelper.getContext(this).eventCenter.addObserver(this, DcContext.DC_EVENT_CONFIGURE_PROGRESS); + + ApplicationDcContext dcContext = DcHelper.getContext(this); + dcContext.eventCenter.addObserver(this, DcContext.DC_EVENT_CONFIGURE_PROGRESS); + dcContext.eventCenter.addObserver(this, DcContext.DC_EVENT_IMEX_PROGRESS); } @Override @@ -108,8 +117,8 @@ public class WelcomeActivity extends BaseActionBarActivity implements DcEventCen View backupText = findViewById(R.id.backup_text); View backupImage = findViewById(R.id.backup_icon); skipButton.setOnClickListener((view) -> startRegistrationActivity()); - backupText.setOnClickListener((view) -> initializePermissions()); - backupImage.setOnClickListener((view) -> initializePermissions()); + backupText.setOnClickListener((view) -> startImportBackup()); + backupImage.setOnClickListener((view) -> startImportBackup()); } private void startRegistrationActivity() { @@ -119,20 +128,87 @@ public class WelcomeActivity extends BaseActionBarActivity implements DcEventCen } @SuppressLint("InlinedApi") - private void initializePermissions() { + private void startImportBackup() { Permissions.with(this) .request(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE) .ifNecessary() .withRationaleDialog("Delta Chat needs access to your files in order start the backup import", R.drawable.ic_folder_white_48dp) .onAllGranted(() -> { - //JavaBindings.loadBackup(); TODO load backup value via bindings + ApplicationDcContext dcContext = DcHelper.getContext(this); + File imexDir = dcContext.getImexDir(); + final String backupFile = dcContext.imexHasBackup(imexDir.getAbsolutePath()); + if (backupFile != null) { + new AlertDialog.Builder(this) + .setTitle(R.string.import_backup_title) + .setMessage(String.format(getResources().getString(R.string.import_backup_ask), backupFile)) + .setNegativeButton(android.R.string.cancel, null) + .setPositiveButton(android.R.string.ok, (dialog, which) -> { + startImport(backupFile); + }) + .show(); + } + else { + new AlertDialog.Builder(this) + .setTitle(R.string.import_backup_title) + .setMessage(String.format(getResources().getString(R.string.import_backup_no_backup_found), imexDir.getAbsolutePath())) + .setPositiveButton(android.R.string.ok, null) + .show(); + } }) .execute(); } + private ProgressDialog progressDialog = null; + private void startImport(final String backupFile) + { + ApplicationDcContext dcContext = DcHelper.getContext(this); + + if( progressDialog!=null ) { + progressDialog.dismiss(); + progressDialog = null; + } + + progressDialog = new ProgressDialog(this); + progressDialog.setMessage(getResources().getString(R.string.one_moment)); + progressDialog.setCanceledOnTouchOutside(false); + progressDialog.setCancelable(false); + progressDialog.setButton(DialogInterface.BUTTON_NEGATIVE, getResources().getString(android.R.string.cancel), (dialog, which) -> { + dcContext.stopOngoingProcess(); + }); + progressDialog.show(); + + dcContext.captureNextError(); + dcContext.imex(DcContext.DC_IMEX_IMPORT_BACKUP, backupFile); + } + public void handleEvent(int eventId, Object data1, Object data2) { - if (eventId== DcContext.DC_EVENT_CONFIGURE_PROGRESS) { + if (eventId== DcContext.DC_EVENT_IMEX_PROGRESS) { + ApplicationDcContext dcContext = DcHelper.getContext(this); + long progress = (Long)data1; + if (progress==0/*error/aborted*/) { + dcContext.endCaptureNextError(); + progressDialog.dismiss(); + if (dcContext.hasCapturedError()) { + new AlertDialog.Builder(this) + .setMessage(dcContext.getCapturedError()) + .setPositiveButton(android.R.string.ok, null) + .show(); + } + } + else if (progress<1000/*progress in permille*/) { + int percent = (int)progress / 10; + progressDialog.setMessage(getResources().getString(R.string.one_moment)+String.format(" %d%%", percent)); + } + else if (progress==1000/*done*/) { + dcContext.endCaptureNextError(); + progressDialog.dismiss(); + Intent conversationList = new Intent(getApplicationContext(), ConversationListActivity.class); + startActivity(conversationList); + finish(); + } + } + else if (eventId== DcContext.DC_EVENT_CONFIGURE_PROGRESS) { long progress = (Long)data1; if (progress==1000/*done*/) { finish(); // remove ourself from the activity stack (finishAffinity is available in API 16, we're targeting API 14) diff --git a/src/org/thoughtcrime/securesms/connect/ApplicationDcContext.java b/src/org/thoughtcrime/securesms/connect/ApplicationDcContext.java index afae790b3..60f951a06 100644 --- a/src/org/thoughtcrime/securesms/connect/ApplicationDcContext.java +++ b/src/org/thoughtcrime/securesms/connect/ApplicationDcContext.java @@ -17,6 +17,7 @@ package org.thoughtcrime.securesms.connect; import android.content.Context; +import android.os.Environment; import android.os.PowerManager; import android.util.Log; import android.widget.Toast; @@ -67,6 +68,15 @@ public class ApplicationDcContext extends DcContext { startThreads(); } + public File getImexDir() + { + // DIRECTORY_DOCUMENTS is only available since KitKat; + // as we also support Ice Cream Sandwich and Jellybean (2017: 11% in total), this is no option. + // Moreover, DIRECTORY_DOWNLOADS seems to be easier accessible by the user, + // eg. "Download Managers" are nearly always installed. + // CAVE: do not use DownloadManager to add the file as it is deleted on uninstall then ... + return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS); + } /*********************************************************************************************** * Working Threads @@ -182,10 +192,34 @@ public class ApplicationDcContext extends DcContext { public DcEventCenter eventCenter = new DcEventCenter(); - public final Object lastErrorLock = new Object(); - public int lastErrorCode = 0; - public String lastErrorString = ""; - public boolean showNextErrorAsToast = true; + private final Object lastErrorLock = new Object(); + private String lastErrorString = ""; + private boolean showNextErrorAsToast = true; + + public void captureNextError() { + synchronized (lastErrorLock) { + showNextErrorAsToast = false; + lastErrorString = ""; + } + } + + public boolean hasCapturedError() { + synchronized (lastErrorLock) { + return !lastErrorString.isEmpty(); + } + } + + public String getCapturedError() { + synchronized (lastErrorLock) { + return lastErrorString; + } + } + + public void endCaptureNextError() { + synchronized (lastErrorLock) { + showNextErrorAsToast = true; + } + } @Override public long handleEvent(final int event, long data1, long data2) { switch(event) { @@ -200,7 +234,6 @@ public class ApplicationDcContext extends DcContext { case DC_EVENT_ERROR: Log.e("DeltaChat", dataToString(data2)); synchronized (lastErrorLock) { - lastErrorCode = (int)data1; lastErrorString = dataToString(data2); } Util.runOnMain(new Runnable() {