Merge remote-tracking branch 'remotes/upstream/material_buttons' into beta
[pub/Android/ownCloud.git] / src / com / owncloud / android / ui / activity / StorageMigrationActivity.java
index c5dc59b..c0b4522 100644 (file)
  */
 package com.owncloud.android.ui.activity;
 
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.os.Bundle;
 import android.support.v7.app.AppCompatActivity;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import com.owncloud.android.MainApp;
+import com.owncloud.android.R;
+import com.owncloud.android.datamodel.FileDataStorageManager;
+import com.owncloud.android.lib.common.utils.Log_OC;
+import com.owncloud.android.utils.FileStorageUtils;
+
+import java.io.File;
 
 /**
  * Created by Bartosz Przybylski on 07.11.2015.
  */
 public class StorageMigrationActivity extends AppCompatActivity {
+       private static final String TAG = StorageMigrationActivity.class.getName();
        public static final String KEY_MIGRATION_TARGET_DIR = "MIGRATION_TARGET";
        public static final String KEY_MIGRATION_SOURCE_DIR = "MIGRATION_SOURCE";
+
+       private ProgressBar mProgressBar;
+       private Button mFinishButton;
+       private TextView mFeedbackText;
+
+       @Override
+       public void onCreate(Bundle savedInstanceState) {
+               super.onCreate(savedInstanceState);
+
+               setContentView(R.layout.migration_layout);
+               mProgressBar = (ProgressBar)findViewById(R.id.migrationProgress);
+               mFinishButton = (Button)findViewById(R.id.finishButton);
+               mFeedbackText = (TextView)findViewById(R.id.migrationText);
+
+               mProgressBar.setProgress(0);
+               mFinishButton.setVisibility(View.INVISIBLE);
+               mFeedbackText.setText(R.string.file_migration_preparing);
+
+               mFinishButton.setOnClickListener(new View.OnClickListener() {
+                       @Override
+                       public void onClick(View view) {
+                               setResult(RESULT_CANCELED);
+                               finish();
+                       }
+               });
+
+               String source = getIntent().getStringExtra(KEY_MIGRATION_SOURCE_DIR);
+               String destination = getIntent().getStringExtra(KEY_MIGRATION_TARGET_DIR);
+
+               if (source == null || destination == null) {
+                       Log_OC.e(TAG, "source or destination is null");
+                       finish();
+               }
+
+               new FileMigrationTask().execute(source, destination);
+       }
+
+       private class FileMigrationTask extends AsyncTask<String, Integer, Integer> {
+
+               private String mStorageTarget;
+               private String mStorageSource;
+               private int mProgress;
+
+               private static final int mProgressCopyUpperBound = 98;
+
+               private class MigrationException extends Exception {
+                       private int mResId;
+                       /*
+                        * @param resId resource identifier to use for displaying error
+                        */
+                       MigrationException(int resId) {
+                               super();
+                               this.mResId = resId;
+                       }
+
+                       int getResId() { return mResId; }
+               }
+
+               @Override
+               protected Integer doInBackground(String... args) {
+
+                       mStorageSource = args[0];
+                       mStorageTarget = args[1];
+                       mProgress = 0;
+
+                       publishProgress(mProgress++, R.string.file_migration_preparing);
+
+                       Context context = StorageMigrationActivity.this;
+                       String ocAuthority = context.getString(R.string.authority);
+
+                       Account[] ocAccounts = AccountManager.get(context).getAccountsByType(MainApp.getAccountType());
+                       boolean[] oldAutoSync = new boolean[ocAccounts.length];
+
+                       Log_OC.stopLogging();
+
+                       try {
+                               publishProgress(mProgress++, R.string.file_migration_checking_destination);
+
+                               checkDestinationAvailability();
+
+                               publishProgress(mProgress++, R.string.file_migration_saving_accounts_configuration);
+                               saveAccountsSyncStatus(ocAuthority, ocAccounts, oldAutoSync);
+
+                               publishProgress(mProgress++, R.string.file_migration_waiting_for_unfinished_sync);
+                               stopAccountsSyncing(ocAuthority, ocAccounts);
+                               waitForUnfinishedSynchronizations(ocAuthority, ocAccounts);
+
+                               publishProgress(mProgress++, R.string.file_migration_migrating);
+                               copyFiles();
+
+                               publishProgress(mProgress++, R.string.file_migration_updating_index);
+                               updateIndex(context);
+
+                               publishProgress(mProgress++, R.string.file_migration_cleaning);
+                               cleanup();
+
+                       } catch (MigrationException e) {
+                               rollback();
+                               Log_OC.startLogging(mStorageSource);
+                               return e.getResId();
+                       } finally {
+                               publishProgress(mProgress++, R.string.file_migration_restoring_accounts_configuration);
+                               restoreAccountsSyncStatus(ocAuthority, ocAccounts, oldAutoSync);
+                       }
+
+                       Log_OC.startLogging(mStorageTarget);
+                       publishProgress(mProgress++, R.string.file_migration_ok_finished);
+
+                       return 0;
+               }
+
+               @Override
+               protected void onProgressUpdate(Integer... progress) {
+                       mProgressBar.setProgress(progress[0]);
+                       if (progress.length > 1)
+                               mFeedbackText.setText(progress[1]);
+               }
+
+               @Override
+               protected void onPostExecute(Integer code) {
+                       mFinishButton.setVisibility(View.VISIBLE);
+                       if (code != 0) {
+                               mFeedbackText.setText(code);
+                       } else {
+                               mFeedbackText.setText(R.string.file_migration_ok_finished);
+                               mFinishButton.setOnClickListener(new View.OnClickListener() {
+                                       @Override
+                                       public void onClick(View view) {
+                                               Intent resultIntent = new Intent();
+                                               resultIntent.putExtra(KEY_MIGRATION_TARGET_DIR, mStorageTarget);
+                                               setResult(RESULT_OK, resultIntent);
+                                               finish();
+                                       }
+                               });
+                       }
+               }
+
+               void checkDestinationAvailability() throws MigrationException {
+                       File srcFile = new File(mStorageSource);
+                       File dstFile = new File(mStorageTarget);
+
+                       if (!dstFile.canRead() || !srcFile.canRead())
+                               throw new MigrationException(R.string.file_migration_failed_not_readable);
+
+                       if (!dstFile.canWrite() || !srcFile.canWrite())
+                               throw new MigrationException(R.string.file_migration_failed_not_writable);
+
+                       if (new File(dstFile, MainApp.getDataFolder()).exists())
+                               throw new MigrationException(R.string.file_migration_failed_dir_already_exists);
+
+                       if (dstFile.getFreeSpace() < FileStorageUtils.getFolderSize(new File(srcFile, MainApp.getDataFolder())))
+                               throw new MigrationException(R.string.file_migration_failed_not_enough_space);
+               }
+
+               void copyFiles() throws MigrationException {
+                       File srcFile = new File(mStorageSource + File.separator + MainApp.getDataFolder());
+                       File dstFile = new File(mStorageTarget + File.separator + MainApp.getDataFolder());
+
+                       copyDirs(srcFile, dstFile);
+                       mProgress = Math.max(mProgress, mProgressCopyUpperBound);
+                       publishProgress(mProgress);
+               }
+
+               void copyDirs(File src, File dst) throws MigrationException {
+                       if (!dst.mkdirs())
+                               throw new MigrationException(R.string.file_migration_failed_while_coping);
+
+                       for (File f : src.listFiles()) {
+
+                               mProgress = Math.min(mProgress+1, mProgressCopyUpperBound);
+                               publishProgress(mProgress);
+
+                               if (f.isDirectory())
+                                       copyDirs(f, new File(dst, f.getName()));
+                               else if (!FileStorageUtils.copyFile(f, new File(dst, f.getName())))
+                                       throw new MigrationException(R.string.file_migration_failed_while_coping);
+                       }
+
+               }
+
+               void updateIndex(Context context) throws MigrationException {
+                       FileDataStorageManager manager = new FileDataStorageManager(null, context.getContentResolver());
+
+                       try {
+                               manager.migrateStoredFiles(mStorageSource, mStorageTarget);
+                       } catch (Exception e) {
+                               throw new MigrationException(R.string.file_migration_failed_while_updating_index);
+                       }
+               }
+
+               void cleanup() {
+                       File srcFile = new File(mStorageSource + File.separator + MainApp.getDataFolder());
+                       if (!srcFile.delete())
+                               Log_OC.w(TAG, "Migration cleanup step failed");
+               }
+
+               void rollback() {
+                       File dstFile = new File(mStorageTarget + File.separator + MainApp.getDataFolder());
+                       if (dstFile.exists())
+                               if (!dstFile.delete())
+                                       Log_OC.w(TAG, "Rollback step failed");
+               }
+
+               void saveAccountsSyncStatus(String authority, Account accounts[], boolean syncs[]) {
+                       for (int i = 0; i < accounts.length; ++i)
+                               syncs[i] = ContentResolver.getSyncAutomatically(accounts[i], authority);
+               }
+
+               void stopAccountsSyncing(String authority, Account accounts[]) {
+                       for (int i = 0; i < accounts.length; ++i)
+                               ContentResolver.setSyncAutomatically(accounts[i], authority, false);
+               }
+
+               void waitForUnfinishedSynchronizations(String authority, Account accounts[]) {
+                       for (int i = 0; i < accounts.length; ++i)
+                               while (ContentResolver.isSyncActive(accounts[i], authority))
+                                       try {
+                                               Thread.sleep(1000);
+                                       } catch (InterruptedException e) {
+                                               Log_OC.w(TAG, "Thread interrupted while waiting for account to end syncing");
+                                               Thread.currentThread().interrupt();
+                                       }
+               }
+
+               void restoreAccountsSyncStatus(String authority, Account accounts[], boolean oldSync[]) {
+                       for (int i = 0; i < accounts.length; ++i)
+                               ContentResolver.setSyncAutomatically(accounts[i], authority, oldSync[i]);
+               }
+
+       }
 }