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
;
42 import java
.io
.FileInputStream
;
43 import java
.io
.FileOutputStream
;
44 import java
.io
.IOException
;
45 import java
.io
.InputStream
;
46 import java
.io
.OutputStream
;
49 * Created by Bartosz Przybylski on 07.11.2015.
51 public class StorageMigrationActivity
extends AppCompatActivity
{
52 private static final String TAG
= StorageMigrationActivity
.class.getName();
53 public static final String KEY_MIGRATION_TARGET_DIR
= "MIGRATION_TARGET";
54 public static final String KEY_MIGRATION_SOURCE_DIR
= "MIGRATION_SOURCE";
56 private ProgressBar mProgressBar
;
57 private Button mFinishButton
;
58 private TextView mFeedbackText
;
61 public void onCreate(Bundle savedInstanceState
) {
62 super.onCreate(savedInstanceState
);
64 setContentView(R
.layout
.migration_layout
);
65 mProgressBar
= (ProgressBar
)findViewById(R
.id
.migrationProgress
);
66 mFinishButton
= (Button
)findViewById(R
.id
.finishButton
);
67 mFeedbackText
= (TextView
)findViewById(R
.id
.migrationText
);
69 mProgressBar
.setProgress(0);
70 mFinishButton
.setVisibility(View
.INVISIBLE
);
71 mFeedbackText
.setText(R
.string
.file_migration_preparing
);
73 mFinishButton
.setOnClickListener(new View
.OnClickListener() {
75 public void onClick(View view
) {
76 setResult(RESULT_CANCELED
);
81 String source
= getIntent().getStringExtra(KEY_MIGRATION_SOURCE_DIR
);
82 String destination
= getIntent().getStringExtra(KEY_MIGRATION_TARGET_DIR
);
84 if (source
== null
|| destination
== null
) {
85 Log_OC
.e(TAG
, "source or destination is null");
89 new FileMigrationTask().execute(source
, destination
);
92 private class FileMigrationTask
extends AsyncTask
<String
, Integer
, Integer
> {
94 private String mStorageTarget
;
95 private String mStorageSource
;
97 private class MigrationException
extends Exception
{
100 * @param resId resource identifier to use for displaying error
102 MigrationException(int resId
) {
107 int getResId() { return mResId
; }
111 protected Integer
doInBackground(String
... args
) {
113 mStorageSource
= args
[0];
114 mStorageTarget
= args
[1];
117 publishProgress(progress
++, R
.string
.file_migration_preparing
);
119 Context context
= StorageMigrationActivity
.this;
120 String ocAuthority
= context
.getString(R
.string
.authority
);
122 Account
[] ocAccounts
= AccountManager
.get(context
).getAccountsByType(MainApp
.getAccountType());
123 boolean[] oldAutoSync
= new boolean[ocAccounts
.length
];
126 publishProgress(progress
++, R
.string
.file_migration_checking_destination
);
128 checkDestinationAvailability();
130 publishProgress(progress
++, R
.string
.file_migration_saving_accounts_configuration
);
131 saveAccountsSyncStatus(ocAuthority
, ocAccounts
, oldAutoSync
);
133 publishProgress(progress
++, R
.string
.file_migration_waiting_for_unfinished_sync
);
134 stopAccountsSyncing(ocAuthority
, ocAccounts
);
135 waitForUnfinishedSynchronizations(ocAuthority
, ocAccounts
);
137 publishProgress(progress
++, R
.string
.file_migration_migrating
);
140 publishProgress(progress
++, R
.string
.file_migration_updating_index
);
141 updateIndex(context
);
143 publishProgress(progress
++, R
.string
.file_migration_cleaning
);
146 } catch (MigrationException e
) {
149 publishProgress(progress
++, R
.string
.file_migration_restoring_accounts_configuration
);
150 restoreAccountsSyncStatus(ocAuthority
, ocAccounts
, oldAutoSync
);
153 publishProgress(progress
++, R
.string
.file_migration_ok_finished
);
159 protected void onProgressUpdate(Integer
... progress
) {
160 mProgressBar
.setProgress(progress
[0]);
161 if (progress
.length
> 1)
162 mFeedbackText
.setText(progress
[1]);
166 protected void onPostExecute(Integer code
) {
167 mFinishButton
.setVisibility(View
.VISIBLE
);
169 mFeedbackText
.setText(code
);
171 mFeedbackText
.setText(R
.string
.file_migration_ok_finished
);
172 mFinishButton
.setOnClickListener(new View
.OnClickListener() {
174 public void onClick(View view
) {
175 Intent resultIntent
= new Intent();
176 resultIntent
.putExtra(KEY_MIGRATION_TARGET_DIR
, mStorageTarget
);
177 setResult(RESULT_OK
, resultIntent
);
184 void checkDestinationAvailability() throws MigrationException
{
185 File srcFile
= new File(mStorageSource
);
186 File dstFile
= new File(mStorageTarget
);
188 if (!dstFile
.canRead() || !srcFile
.canRead())
189 throw new MigrationException(R
.string
.file_migration_failed_not_readable
);
191 if (!dstFile
.canWrite() || !srcFile
.canWrite())
192 throw new MigrationException(R
.string
.file_migration_failed_not_writable
);
194 if (new File(dstFile
, MainApp
.getDataFolder()).exists())
195 throw new MigrationException(R
.string
.file_migration_failed_dir_already_exists
);
197 if (dstFile
.getFreeSpace() < calculateUsedSpace(new File(srcFile
, MainApp
.getDataFolder())))
198 throw new MigrationException(R
.string
.file_migration_failed_not_enough_space
);
201 void copyFiles() throws MigrationException
{
202 File srcFile
= new File(mStorageSource
+ File
.separator
+ MainApp
.getDataFolder());
203 File dstFile
= new File(mStorageTarget
+ File
.separator
+ MainApp
.getDataFolder());
205 copyDirs(srcFile
, dstFile
);
208 private boolean copyFile(File src
, File target
) {
211 InputStream
in = null
;
212 OutputStream out
= null
;
215 in = new FileInputStream(src
);
216 out
= new FileOutputStream(target
);
217 byte[] buf
= new byte[1024];
219 while ((len
= in.read(buf
)) > 0) {
220 out
.write(buf
, 0, len
);
222 } catch (IOException ex
) {
225 if (in != null
) try {
227 } catch (IOException e
) {
228 e
.printStackTrace(System
.err
);
230 if (out
!= null
) try {
232 } catch (IOException e
) {
233 e
.printStackTrace(System
.err
);
240 void copyDirs(File src
, File dst
) throws MigrationException
{
242 throw new MigrationException(R
.string
.file_migration_failed_while_coping
);
244 for (File f
: src
.listFiles()) {
246 copyDirs(f
, new File(dst
, f
.getName()));
247 else if (!copyFile(f
, new File(dst
, f
.getName())))
248 throw new MigrationException(R
.string
.file_migration_failed_while_coping
);
253 void updateIndex(Context context
) throws MigrationException
{
254 FileDataStorageManager manager
= new FileDataStorageManager(null
, context
.getContentResolver());
257 manager
.migrateStoredFiles(mStorageSource
, mStorageTarget
);
258 } catch (Exception e
) {
259 throw new MigrationException(R
.string
.file_migration_failed_while_updating_index
);
268 long calculateUsedSpace(File dir
) {
271 for (File f
: dir
.listFiles()) {
273 result
+= calculateUsedSpace(f
);
275 result
+= f
.length();
281 void saveAccountsSyncStatus(String authority
, Account accounts
[], boolean syncs
[]) {
282 for (int i
= 0; i
< accounts
.length
; ++i
) {
283 syncs
[i
] = ContentResolver
.getSyncAutomatically(accounts
[i
], authority
);
287 void stopAccountsSyncing(String authority
, Account accounts
[]) {
288 for (int i
= 0; i
< accounts
.length
; ++i
)
289 ContentResolver
.setSyncAutomatically(accounts
[i
], authority
, false
);
292 void waitForUnfinishedSynchronizations(String authority
, Account accounts
[]) {
293 for (int i
= 0; i
< accounts
.length
; ++i
)
294 while (ContentResolver
.isSyncActive(accounts
[i
], authority
))
297 } catch (InterruptedException e
) {
298 Log_OC
.w(TAG
, "Thread interrupted while waiting for account to end syncing");
303 void restoreAccountsSyncStatus(String authority
, Account accounts
[], boolean oldSync
[]) {
304 for (int i
= 0; i
< accounts
.length
; ++i
)
305 ContentResolver
.setSyncAutomatically(accounts
[i
], authority
, oldSync
[i
]);