2 * ownCloud Android client application
4 * @author Bartosz Przybylski
5 * Copyright (C) 2015 ownCloud Inc.
6 * Copyright (C) 2015 Bartosz Przybylski
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2,
10 * as published by the Free Software Foundation.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 package com
.owncloud
.android
.ui
.activity
;
23 import android
.accounts
.Account
;
24 import android
.accounts
.AccountManager
;
25 import android
.content
.ContentResolver
;
26 import android
.content
.Context
;
27 import android
.content
.Intent
;
28 import android
.os
.AsyncTask
;
29 import android
.os
.Bundle
;
30 import android
.support
.v7
.app
.AppCompatActivity
;
31 import android
.view
.View
;
32 import android
.widget
.Button
;
33 import android
.widget
.ProgressBar
;
34 import android
.widget
.TextView
;
36 import com
.owncloud
.android
.MainApp
;
37 import com
.owncloud
.android
.R
;
38 import com
.owncloud
.android
.datamodel
.FileDataStorageManager
;
39 import com
.owncloud
.android
.lib
.common
.utils
.Log_OC
;
40 import com
.owncloud
.android
.utils
.FileStorageUtils
;
45 * Created by Bartosz Przybylski on 07.11.2015.
47 public class StorageMigrationActivity
extends AppCompatActivity
{
48 private static final String TAG
= StorageMigrationActivity
.class.getName();
49 public static final String KEY_MIGRATION_TARGET_DIR
= "MIGRATION_TARGET";
50 public static final String KEY_MIGRATION_SOURCE_DIR
= "MIGRATION_SOURCE";
52 private ProgressBar mProgressBar
;
53 private Button mFinishButton
;
54 private TextView mFeedbackText
;
57 public void onCreate(Bundle savedInstanceState
) {
58 super.onCreate(savedInstanceState
);
60 setContentView(R
.layout
.migration_layout
);
61 mProgressBar
= (ProgressBar
)findViewById(R
.id
.migrationProgress
);
62 mFinishButton
= (Button
)findViewById(R
.id
.finishButton
);
63 mFeedbackText
= (TextView
)findViewById(R
.id
.migrationText
);
65 mProgressBar
.setProgress(0);
66 mFinishButton
.setVisibility(View
.INVISIBLE
);
67 mFeedbackText
.setText(R
.string
.file_migration_preparing
);
69 mFinishButton
.setOnClickListener(new View
.OnClickListener() {
71 public void onClick(View view
) {
72 setResult(RESULT_CANCELED
);
77 String source
= getIntent().getStringExtra(KEY_MIGRATION_SOURCE_DIR
);
78 String destination
= getIntent().getStringExtra(KEY_MIGRATION_TARGET_DIR
);
80 if (source
== null
|| destination
== null
) {
81 Log_OC
.e(TAG
, "source or destination is null");
85 new FileMigrationTask().execute(source
, destination
);
88 private class FileMigrationTask
extends AsyncTask
<String
, Integer
, Integer
> {
90 private String mStorageTarget
;
91 private String mStorageSource
;
92 private int mProgress
;
94 private static final int mProgressCopyUpperBound
= 98;
96 private class MigrationException
extends Exception
{
99 * @param resId resource identifier to use for displaying error
101 MigrationException(int resId
) {
106 int getResId() { return mResId
; }
110 protected Integer
doInBackground(String
... args
) {
112 mStorageSource
= args
[0];
113 mStorageTarget
= args
[1];
116 publishProgress(mProgress
++, R
.string
.file_migration_preparing
);
118 Context context
= StorageMigrationActivity
.this;
119 String ocAuthority
= context
.getString(R
.string
.authority
);
121 Account
[] ocAccounts
= AccountManager
.get(context
).getAccountsByType(MainApp
.getAccountType());
122 boolean[] oldAutoSync
= new boolean[ocAccounts
.length
];
124 Log_OC
.stopLogging();
127 publishProgress(mProgress
++, R
.string
.file_migration_checking_destination
);
129 checkDestinationAvailability();
131 publishProgress(mProgress
++, R
.string
.file_migration_saving_accounts_configuration
);
132 saveAccountsSyncStatus(ocAuthority
, ocAccounts
, oldAutoSync
);
134 publishProgress(mProgress
++, R
.string
.file_migration_waiting_for_unfinished_sync
);
135 stopAccountsSyncing(ocAuthority
, ocAccounts
);
136 waitForUnfinishedSynchronizations(ocAuthority
, ocAccounts
);
138 publishProgress(mProgress
++, R
.string
.file_migration_migrating
);
141 publishProgress(mProgress
++, R
.string
.file_migration_updating_index
);
142 updateIndex(context
);
144 publishProgress(mProgress
++, R
.string
.file_migration_cleaning
);
147 } catch (MigrationException e
) {
149 Log_OC
.startLogging(mStorageSource
);
152 publishProgress(mProgress
++, R
.string
.file_migration_restoring_accounts_configuration
);
153 restoreAccountsSyncStatus(ocAuthority
, ocAccounts
, oldAutoSync
);
156 Log_OC
.startLogging(mStorageTarget
);
157 publishProgress(mProgress
++, R
.string
.file_migration_ok_finished
);
163 protected void onProgressUpdate(Integer
... progress
) {
164 mProgressBar
.setProgress(progress
[0]);
165 if (progress
.length
> 1)
166 mFeedbackText
.setText(progress
[1]);
170 protected void onPostExecute(Integer code
) {
171 mFinishButton
.setVisibility(View
.VISIBLE
);
173 mFeedbackText
.setText(code
);
175 mFeedbackText
.setText(R
.string
.file_migration_ok_finished
);
176 mFinishButton
.setOnClickListener(new View
.OnClickListener() {
178 public void onClick(View view
) {
179 Intent resultIntent
= new Intent();
180 resultIntent
.putExtra(KEY_MIGRATION_TARGET_DIR
, mStorageTarget
);
181 setResult(RESULT_OK
, resultIntent
);
188 void checkDestinationAvailability() throws MigrationException
{
189 File srcFile
= new File(mStorageSource
);
190 File dstFile
= new File(mStorageTarget
);
192 if (!dstFile
.canRead() || !srcFile
.canRead())
193 throw new MigrationException(R
.string
.file_migration_failed_not_readable
);
195 if (!dstFile
.canWrite() || !srcFile
.canWrite())
196 throw new MigrationException(R
.string
.file_migration_failed_not_writable
);
198 if (new File(dstFile
, MainApp
.getDataFolder()).exists())
199 throw new MigrationException(R
.string
.file_migration_failed_dir_already_exists
);
201 if (dstFile
.getFreeSpace() < FileStorageUtils
.getFolderSize(new File(srcFile
, MainApp
.getDataFolder())))
202 throw new MigrationException(R
.string
.file_migration_failed_not_enough_space
);
205 void copyFiles() throws MigrationException
{
206 File srcFile
= new File(mStorageSource
+ File
.separator
+ MainApp
.getDataFolder());
207 File dstFile
= new File(mStorageTarget
+ File
.separator
+ MainApp
.getDataFolder());
209 copyDirs(srcFile
, dstFile
);
210 mProgress
= Math
.max(mProgress
, mProgressCopyUpperBound
);
211 publishProgress(mProgress
);
214 void copyDirs(File src
, File dst
) throws MigrationException
{
216 throw new MigrationException(R
.string
.file_migration_failed_while_coping
);
218 for (File f
: src
.listFiles()) {
220 mProgress
= Math
.min(mProgress
+1, mProgressCopyUpperBound
);
221 publishProgress(mProgress
);
224 copyDirs(f
, new File(dst
, f
.getName()));
225 else if (!FileStorageUtils
.copyFile(f
, new File(dst
, f
.getName())))
226 throw new MigrationException(R
.string
.file_migration_failed_while_coping
);
231 void updateIndex(Context context
) throws MigrationException
{
232 FileDataStorageManager manager
= new FileDataStorageManager(null
, context
.getContentResolver());
235 manager
.migrateStoredFiles(mStorageSource
, mStorageTarget
);
236 } catch (Exception e
) {
237 throw new MigrationException(R
.string
.file_migration_failed_while_updating_index
);
242 File srcFile
= new File(mStorageSource
+ File
.separator
+ MainApp
.getDataFolder());
243 if (!deleteRecursive(srcFile
))
244 Log_OC
.w(TAG
, "Migration cleanup step failed");
247 boolean deleteRecursive(File f
) {
250 for (File c
: f
.listFiles())
251 res
= deleteRecursive(c
) && res
;
252 return f
.delete() && res
;
257 File dstFile
= new File(mStorageTarget
+ File
.separator
+ MainApp
.getDataFolder());
258 if (dstFile
.exists())
259 if (!dstFile
.delete())
260 Log_OC
.w(TAG
, "Rollback step failed");
263 void saveAccountsSyncStatus(String authority
, Account accounts
[], boolean syncs
[]) {
264 for (int i
= 0; i
< accounts
.length
; ++i
)
265 syncs
[i
] = ContentResolver
.getSyncAutomatically(accounts
[i
], authority
);
268 void stopAccountsSyncing(String authority
, Account accounts
[]) {
269 for (int i
= 0; i
< accounts
.length
; ++i
)
270 ContentResolver
.setSyncAutomatically(accounts
[i
], authority
, false
);
273 void waitForUnfinishedSynchronizations(String authority
, Account accounts
[]) {
274 for (int i
= 0; i
< accounts
.length
; ++i
)
275 while (ContentResolver
.isSyncActive(accounts
[i
], authority
))
278 } catch (InterruptedException e
) {
279 Log_OC
.w(TAG
, "Thread interrupted while waiting for account to end syncing");
280 Thread
.currentThread().interrupt();
284 void restoreAccountsSyncStatus(String authority
, Account accounts
[], boolean oldSync
[]) {
285 for (int i
= 0; i
< accounts
.length
; ++i
)
286 ContentResolver
.setSyncAutomatically(accounts
[i
], authority
, oldSync
[i
]);