provide progress bar for import backup. fixes #3809
This commit is contained in:
		
							parent
							
								
									71a56002fe
								
							
						
					
					
						commit
						9ab0fbe48c
					
				@ -1,5 +1,6 @@
 | 
				
			|||||||
package eu.siacs.conversations.services;
 | 
					package eu.siacs.conversations.services;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.app.Notification;
 | 
				
			||||||
import android.app.NotificationManager;
 | 
					import android.app.NotificationManager;
 | 
				
			||||||
import android.app.PendingIntent;
 | 
					import android.app.PendingIntent;
 | 
				
			||||||
import android.app.Service;
 | 
					import android.app.Service;
 | 
				
			||||||
@ -10,9 +11,21 @@ import android.database.sqlite.SQLiteDatabase;
 | 
				
			|||||||
import android.net.Uri;
 | 
					import android.net.Uri;
 | 
				
			||||||
import android.os.Binder;
 | 
					import android.os.Binder;
 | 
				
			||||||
import android.os.IBinder;
 | 
					import android.os.IBinder;
 | 
				
			||||||
 | 
					import android.provider.OpenableColumns;
 | 
				
			||||||
import android.support.v4.app.NotificationCompat;
 | 
					import android.support.v4.app.NotificationCompat;
 | 
				
			||||||
 | 
					import android.support.v4.app.NotificationManagerCompat;
 | 
				
			||||||
import android.util.Log;
 | 
					import android.util.Log;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.common.base.Charsets;
 | 
				
			||||||
 | 
					import com.google.common.io.CountingInputStream;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.bouncycastle.crypto.engines.AESEngine;
 | 
				
			||||||
 | 
					import org.bouncycastle.crypto.io.CipherInputStream;
 | 
				
			||||||
 | 
					import org.bouncycastle.crypto.modes.AEADBlockCipher;
 | 
				
			||||||
 | 
					import org.bouncycastle.crypto.modes.GCMBlockCipher;
 | 
				
			||||||
 | 
					import org.bouncycastle.crypto.params.AEADParameters;
 | 
				
			||||||
 | 
					import org.bouncycastle.crypto.params.KeyParameter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.io.BufferedReader;
 | 
					import java.io.BufferedReader;
 | 
				
			||||||
import java.io.DataInputStream;
 | 
					import java.io.DataInputStream;
 | 
				
			||||||
import java.io.File;
 | 
					import java.io.File;
 | 
				
			||||||
@ -33,10 +46,6 @@ import java.util.zip.GZIPInputStream;
 | 
				
			|||||||
import java.util.zip.ZipException;
 | 
					import java.util.zip.ZipException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import javax.crypto.BadPaddingException;
 | 
					import javax.crypto.BadPaddingException;
 | 
				
			||||||
import javax.crypto.Cipher;
 | 
					 | 
				
			||||||
import javax.crypto.CipherInputStream;
 | 
					 | 
				
			||||||
import javax.crypto.spec.IvParameterSpec;
 | 
					 | 
				
			||||||
import javax.crypto.spec.SecretKeySpec;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import eu.siacs.conversations.Config;
 | 
					import eu.siacs.conversations.Config;
 | 
				
			||||||
import eu.siacs.conversations.R;
 | 
					import eu.siacs.conversations.R;
 | 
				
			||||||
@ -44,14 +53,9 @@ import eu.siacs.conversations.persistance.DatabaseBackend;
 | 
				
			|||||||
import eu.siacs.conversations.persistance.FileBackend;
 | 
					import eu.siacs.conversations.persistance.FileBackend;
 | 
				
			||||||
import eu.siacs.conversations.ui.ManageAccountActivity;
 | 
					import eu.siacs.conversations.ui.ManageAccountActivity;
 | 
				
			||||||
import eu.siacs.conversations.utils.BackupFileHeader;
 | 
					import eu.siacs.conversations.utils.BackupFileHeader;
 | 
				
			||||||
import eu.siacs.conversations.utils.Compatibility;
 | 
					 | 
				
			||||||
import eu.siacs.conversations.utils.SerialSingleThreadExecutor;
 | 
					import eu.siacs.conversations.utils.SerialSingleThreadExecutor;
 | 
				
			||||||
import eu.siacs.conversations.xmpp.Jid;
 | 
					import eu.siacs.conversations.xmpp.Jid;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import static eu.siacs.conversations.services.ExportBackupService.CIPHERMODE;
 | 
					 | 
				
			||||||
import static eu.siacs.conversations.services.ExportBackupService.KEYTYPE;
 | 
					 | 
				
			||||||
import static eu.siacs.conversations.services.ExportBackupService.PROVIDER;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public class ImportBackupService extends Service {
 | 
					public class ImportBackupService extends Service {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static final int NOTIFICATION_ID = 21;
 | 
					    private static final int NOTIFICATION_ID = 21;
 | 
				
			||||||
@ -117,9 +121,9 @@ public class ImportBackupService extends Service {
 | 
				
			|||||||
        return running.get();
 | 
					        return running.get();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public void loadBackupFiles(OnBackupFilesLoaded onBackupFilesLoaded) {
 | 
					    public void loadBackupFiles(final OnBackupFilesLoaded onBackupFilesLoaded) {
 | 
				
			||||||
        executor.execute(() -> {
 | 
					        executor.execute(() -> {
 | 
				
			||||||
            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) {
 | 
					            for (String app : apps) {
 | 
				
			||||||
@ -128,7 +132,12 @@ public class ImportBackupService extends Service {
 | 
				
			|||||||
                    Log.d(Config.LOGTAG, "directory not found: " + directory.getAbsolutePath());
 | 
					                    Log.d(Config.LOGTAG, "directory not found: " + directory.getAbsolutePath());
 | 
				
			||||||
                    continue;
 | 
					                    continue;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                for (File file : directory.listFiles()) {
 | 
					                final File[] files = directory.listFiles();
 | 
				
			||||||
 | 
					                if (files == null) {
 | 
				
			||||||
 | 
					                    onBackupFilesLoaded.onBackupFilesLoaded(backupFiles);
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                for (final File file : files) {
 | 
				
			||||||
                    if (file.isFile() && file.getName().endsWith(".ceb")) {
 | 
					                    if (file.isFile() && file.getName().endsWith(".ceb")) {
 | 
				
			||||||
                        try {
 | 
					                        try {
 | 
				
			||||||
                            final BackupFile backupFile = BackupFile.read(file);
 | 
					                            final BackupFile backupFile = BackupFile.read(file);
 | 
				
			||||||
@ -149,24 +158,67 @@ public class ImportBackupService extends Service {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void startForegroundService() {
 | 
					    private void startForegroundService() {
 | 
				
			||||||
 | 
					        startForeground(NOTIFICATION_ID, createImportBackupNotification(1, 0));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void updateImportBackupNotification(final long total, final long current) {
 | 
				
			||||||
 | 
					        final int max;
 | 
				
			||||||
 | 
					        final int progress;
 | 
				
			||||||
 | 
					        if (total == 0) {
 | 
				
			||||||
 | 
					            max = 1;
 | 
				
			||||||
 | 
					            progress = 0;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            max = 100;
 | 
				
			||||||
 | 
					            progress = (int) (current * 100 / total);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        final NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            notificationManager.notify(NOTIFICATION_ID, createImportBackupNotification(max, progress));
 | 
				
			||||||
 | 
					        } catch (final RuntimeException e) {
 | 
				
			||||||
 | 
					            Log.d(Config.LOGTAG, "unable to make notification", e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private Notification createImportBackupNotification(final int max, final int progress) {
 | 
				
			||||||
        NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getBaseContext(), "backup");
 | 
					        NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getBaseContext(), "backup");
 | 
				
			||||||
        mBuilder.setContentTitle(getString(R.string.restoring_backup))
 | 
					        mBuilder.setContentTitle(getString(R.string.restoring_backup))
 | 
				
			||||||
                .setSmallIcon(R.drawable.ic_unarchive_white_24dp)
 | 
					                .setSmallIcon(R.drawable.ic_unarchive_white_24dp)
 | 
				
			||||||
                .setProgress(1, 0, true);
 | 
					                .setProgress(max, progress, max == 1 && progress == 0);
 | 
				
			||||||
        startForeground(NOTIFICATION_ID, mBuilder.build());
 | 
					        return mBuilder.build();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private boolean importBackup(Uri uri, String password) {
 | 
					    private boolean importBackup(final Uri uri, final String password) {
 | 
				
			||||||
        Log.d(Config.LOGTAG, "importing backup from " + uri);
 | 
					        Log.d(Config.LOGTAG, "importing backup from " + uri);
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            SQLiteDatabase db = mDatabaseBackend.getWritableDatabase();
 | 
					            final SQLiteDatabase db = mDatabaseBackend.getWritableDatabase();
 | 
				
			||||||
            final InputStream inputStream;
 | 
					            final InputStream inputStream;
 | 
				
			||||||
            if ("file".equals(uri.getScheme())) {
 | 
					            final String path = uri.getPath();
 | 
				
			||||||
                inputStream = new FileInputStream(new File(uri.getPath()));
 | 
					            final long fileSize;
 | 
				
			||||||
 | 
					            if ("file".equals(uri.getScheme()) && path != null) {
 | 
				
			||||||
 | 
					                final File file = new File(path);
 | 
				
			||||||
 | 
					                inputStream = new FileInputStream(file);
 | 
				
			||||||
 | 
					                fileSize = file.length();
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
 | 
					                final Cursor returnCursor = getContentResolver().query(uri, null, null, null, null);
 | 
				
			||||||
 | 
					                if (returnCursor == null) {
 | 
				
			||||||
 | 
					                    fileSize = 0;
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    returnCursor.moveToFirst();
 | 
				
			||||||
 | 
					                    fileSize = returnCursor.getLong(returnCursor.getColumnIndex(OpenableColumns.SIZE));
 | 
				
			||||||
 | 
					                    returnCursor.close();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
                inputStream = getContentResolver().openInputStream(uri);
 | 
					                inputStream = getContentResolver().openInputStream(uri);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            final DataInputStream dataInputStream = new DataInputStream(inputStream);
 | 
					            if (inputStream == null) {
 | 
				
			||||||
 | 
					                synchronized (mOnBackupProcessedListeners) {
 | 
				
			||||||
 | 
					                    for (final OnBackupProcessed l : mOnBackupProcessedListeners) {
 | 
				
			||||||
 | 
					                        l.onBackupRestoreFailed();
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                return false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            final CountingInputStream countingInputStream = new CountingInputStream(inputStream);
 | 
				
			||||||
 | 
					            final DataInputStream dataInputStream = new DataInputStream(countingInputStream);
 | 
				
			||||||
            final BackupFileHeader backupFileHeader = BackupFileHeader.read(dataInputStream);
 | 
					            final BackupFileHeader backupFileHeader = BackupFileHeader.read(dataInputStream);
 | 
				
			||||||
            Log.d(Config.LOGTAG, backupFileHeader.toString());
 | 
					            Log.d(Config.LOGTAG, backupFileHeader.toString());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -179,15 +231,14 @@ public class ImportBackupService extends Service {
 | 
				
			|||||||
                return false;
 | 
					                return false;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            final Cipher cipher = Compatibility.twentyEight() ? Cipher.getInstance(CIPHERMODE) : Cipher.getInstance(CIPHERMODE, PROVIDER);
 | 
					            final byte[] key = ExportBackupService.getKey(password, backupFileHeader.getSalt());
 | 
				
			||||||
            byte[] key = ExportBackupService.getKey(password, backupFileHeader.getSalt());
 | 
					 | 
				
			||||||
            SecretKeySpec keySpec = new SecretKeySpec(key, KEYTYPE);
 | 
					 | 
				
			||||||
            IvParameterSpec ivSpec = new IvParameterSpec(backupFileHeader.getIv());
 | 
					 | 
				
			||||||
            cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
 | 
					 | 
				
			||||||
            CipherInputStream cipherInputStream = new CipherInputStream(inputStream, cipher);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            GZIPInputStream gzipInputStream = new GZIPInputStream(cipherInputStream);
 | 
					            AEADBlockCipher cipher = new GCMBlockCipher(new AESEngine());
 | 
				
			||||||
            BufferedReader reader = new BufferedReader(new InputStreamReader(gzipInputStream, "UTF-8"));
 | 
					            cipher.init(false, new AEADParameters(new KeyParameter(key), 128, backupFileHeader.getIv()));
 | 
				
			||||||
 | 
					            final CipherInputStream cipherInputStream = new CipherInputStream(countingInputStream, cipher);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            final GZIPInputStream gzipInputStream = new GZIPInputStream(cipherInputStream);
 | 
				
			||||||
 | 
					            BufferedReader reader = new BufferedReader(new InputStreamReader(gzipInputStream, Charsets.UTF_8));
 | 
				
			||||||
            String line;
 | 
					            String line;
 | 
				
			||||||
            StringBuilder multiLineQuery = null;
 | 
					            StringBuilder multiLineQuery = null;
 | 
				
			||||||
            while ((line = reader.readLine()) != null) {
 | 
					            while ((line = reader.readLine()) != null) {
 | 
				
			||||||
@ -198,10 +249,12 @@ public class ImportBackupService extends Service {
 | 
				
			|||||||
                    if (count % 2 == 1) {
 | 
					                    if (count % 2 == 1) {
 | 
				
			||||||
                        db.execSQL(multiLineQuery.toString());
 | 
					                        db.execSQL(multiLineQuery.toString());
 | 
				
			||||||
                        multiLineQuery = null;
 | 
					                        multiLineQuery = null;
 | 
				
			||||||
 | 
					                        updateImportBackupNotification(fileSize, countingInputStream.getCount());
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    if (count % 2 == 0) {
 | 
					                    if (count % 2 == 0) {
 | 
				
			||||||
                        db.execSQL(line);
 | 
					                        db.execSQL(line);
 | 
				
			||||||
 | 
					                        updateImportBackupNotification(fileSize, countingInputStream.getCount());
 | 
				
			||||||
                    } else {
 | 
					                    } else {
 | 
				
			||||||
                        multiLineQuery = new StringBuilder(line);
 | 
					                        multiLineQuery = new StringBuilder(line);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
@ -220,7 +273,7 @@ public class ImportBackupService extends Service {
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            return true;
 | 
					            return true;
 | 
				
			||||||
        } catch (Exception e) {
 | 
					        } catch (final Exception e) {
 | 
				
			||||||
            Throwable throwable = e.getCause();
 | 
					            Throwable throwable = e.getCause();
 | 
				
			||||||
            final boolean reasonWasCrypto = throwable instanceof BadPaddingException || e instanceof ZipException;
 | 
					            final boolean reasonWasCrypto = throwable instanceof BadPaddingException || e instanceof ZipException;
 | 
				
			||||||
            synchronized (mOnBackupProcessedListeners) {
 | 
					            synchronized (mOnBackupProcessedListeners) {
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user