X-Git-Url: http://git.linex4red.de/pub/Android/ownCloud.git/blobdiff_plain/d6ccd4c9600822b08141ecaa7d19aed614ca2ed5..bcc972d63d3ed1d76b51a8a03a27e809983dc756:/src/com/owncloud/android/syncadapter/FileSyncAdapter.java?ds=sidebyside diff --git a/src/com/owncloud/android/syncadapter/FileSyncAdapter.java b/src/com/owncloud/android/syncadapter/FileSyncAdapter.java index 7f4cf61b..058cfbc4 100644 --- a/src/com/owncloud/android/syncadapter/FileSyncAdapter.java +++ b/src/com/owncloud/android/syncadapter/FileSyncAdapter.java @@ -1,388 +1,470 @@ -/* ownCloud Android client application - * Copyright (C) 2011 Bartek Przybylski - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ - -package com.owncloud.android.syncadapter; - -import java.io.IOException; -import java.util.List; -import java.util.Vector; - -import org.apache.http.HttpStatus; -import org.apache.jackrabbit.webdav.DavException; -import org.apache.jackrabbit.webdav.MultiStatus; -import org.apache.jackrabbit.webdav.client.methods.PropFindMethod; -import org.json.JSONObject; - -import com.owncloud.android.AccountUtils; -import com.owncloud.android.R; -import com.owncloud.android.authenticator.AccountAuthenticator; -import com.owncloud.android.datamodel.FileDataStorageManager; -import com.owncloud.android.datamodel.OCFile; -import com.owncloud.android.files.services.FileDownloader; -import com.owncloud.android.utils.OwnCloudVersion; - -import android.accounts.Account; -import android.app.Notification; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.content.ContentProviderClient; -import android.content.ContentResolver; -import android.content.Context; -import android.content.Intent; -import android.content.SyncResult; -import android.os.Bundle; -import android.util.Log; -import eu.alefzero.webdav.WebdavEntry; -import eu.alefzero.webdav.WebdavUtils; - -/** - * SyncAdapter implementation for syncing sample SyncAdapter contacts to the - * platform ContactOperations provider. - * - * @author Bartek Przybylski - */ -public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter { - - private final static String TAG = "FileSyncAdapter"; - - /* Commented code for ugly performance tests - private final static int MAX_DELAYS = 100; - private static long[] mResponseDelays = new long[MAX_DELAYS]; - private static long[] mSaveDelays = new long[MAX_DELAYS]; - private int mDelaysIndex = 0; - private int mDelaysCount = 0; - */ - - private long mCurrentSyncTime; - private boolean mCancellation; - private boolean mIsManualSync; - private boolean mRightSync; - - public FileSyncAdapter(Context context, boolean autoInitialize) { - super(context, autoInitialize); - } - - @Override - public synchronized void onPerformSync(Account account, Bundle extras, - String authority, ContentProviderClient provider, - SyncResult syncResult) { - - mCancellation = false; - mIsManualSync = extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false); - mRightSync = true; - - this.setAccount(account); - this.setContentProvider(provider); - this.setStorageManager(new FileDataStorageManager(account, getContentProvider())); - - /* Commented code for ugly performance tests - mDelaysIndex = 0; - mDelaysCount = 0; - */ - - Log.d(TAG, "syncing owncloud account " + account.name); - - sendStickyBroadcast(true, null); // message to signal the start to the UI - - updateOCVersion(); - - String uri = getUri().toString(); - PropFindMethod query = null; - try { - mCurrentSyncTime = System.currentTimeMillis(); - query = new PropFindMethod(uri + "/"); - int status = getClient().executeMethod(query); - if (status != HttpStatus.SC_UNAUTHORIZED) { - MultiStatus resp = query.getResponseBodyAsMultiStatus(); - - if (resp.getResponses().length > 0) { - WebdavEntry we = new WebdavEntry(resp.getResponses()[0], getUri().getPath()); - OCFile file = fillOCFile(we); - file.setParentId(0); - getStorageManager().saveFile(file); - if (!mCancellation) { - fetchData(uri, syncResult, file.getFileId()); - } - } - - } else { - syncResult.stats.numAuthExceptions++; - } - } catch (IOException e) { - syncResult.stats.numIoExceptions++; - logException(e, uri + "/"); - - } catch (DavException e) { - syncResult.stats.numParseExceptions++; - logException(e, uri + "/"); - - } catch (Exception e) { - // TODO something smart with syncresult - logException(e, uri + "/"); - mRightSync = false; - - } finally { - if (query != null) - query.releaseConnection(); // let the connection available for other methods - mRightSync &= (syncResult.stats.numIoExceptions == 0 && syncResult.stats.numAuthExceptions == 0 && syncResult.stats.numParseExceptions == 0); - if (!mRightSync && mIsManualSync) { - /// don't let the system synchronization manager retries MANUAL synchronizations - // (be careful: "MANUAL" currently includes the synchronization requested when a new account is created and when the user changes the current account) - syncResult.tooManyRetries = true; - - /// notify the user about the failure of MANUAL synchronization - Notification notification = new Notification(R.drawable.icon, getContext().getString(R.string.sync_fail_ticker), System.currentTimeMillis()); - notification.flags |= Notification.FLAG_AUTO_CANCEL; - // TODO put something smart in the contentIntent below - notification.contentIntent = PendingIntent.getActivity(getContext().getApplicationContext(), 0, new Intent(), PendingIntent.FLAG_UPDATE_CURRENT); - notification.setLatestEventInfo(getContext().getApplicationContext(), - getContext().getString(R.string.sync_fail_ticker), - String.format(getContext().getString(R.string.sync_fail_content), account.name), - notification.contentIntent); - ((NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE)).notify(R.string.sync_fail_ticker, notification); - } - sendStickyBroadcast(false, null); // message to signal the end to the UI - } - - /* Commented code for ugly performance tests - long sum = 0, mean = 0, max = 0, min = Long.MAX_VALUE; - for (int i=0; i children = null; - try { - Log.d(TAG, "fetching " + uri); - - // remote request - query = new PropFindMethod(uri); - /* Commented code for ugly performance tests - long responseDelay = System.currentTimeMillis(); - */ - int status = getClient().executeMethod(query); - /* Commented code for ugly performance tests - responseDelay = System.currentTimeMillis() - responseDelay; - Log.e(TAG, "syncing: RESPONSE TIME for " + uri + " contents, " + responseDelay + "ms"); - */ - if (status != HttpStatus.SC_UNAUTHORIZED) { - MultiStatus resp = query.getResponseBodyAsMultiStatus(); - - // insertion or update of files - List updatedFiles = new Vector(resp.getResponses().length - 1); - for (int i = 1; i < resp.getResponses().length; ++i) { - WebdavEntry we = new WebdavEntry(resp.getResponses()[i], getUri().getPath()); - OCFile file = fillOCFile(we); - file.setParentId(parentId); - if (getStorageManager().getFileByPath(file.getRemotePath()) != null && - getStorageManager().getFileByPath(file.getRemotePath()).keepInSync() && - file.getModificationTimestamp() > getStorageManager().getFileByPath(file.getRemotePath()) - .getModificationTimestamp()) { - Intent intent = new Intent(this.getContext(), FileDownloader.class); - intent.putExtra(FileDownloader.EXTRA_ACCOUNT, getAccount()); - intent.putExtra(FileDownloader.EXTRA_FILE, file); - file.setKeepInSync(true); - getContext().startService(intent); - } - if (getStorageManager().getFileByPath(file.getRemotePath()) != null) - file.setKeepInSync(getStorageManager().getFileByPath(file.getRemotePath()).keepInSync()); - - // Log.v(TAG, "adding file: " + file); - updatedFiles.add(file); - } - /* Commented code for ugly performance tests - long saveDelay = System.currentTimeMillis(); - */ - getStorageManager().saveFiles(updatedFiles); // all "at once" ; trying to get a best performance in database update - /* Commented code for ugly performance tests - saveDelay = System.currentTimeMillis() - saveDelay; - Log.e(TAG, "syncing: SAVE TIME for " + uri + " contents, " + mSaveDelays[mDelaysIndex] + "ms"); - */ - - // removal of obsolete files - children = getStorageManager().getDirectoryContent( - getStorageManager().getFileById(parentId)); - OCFile file; - String currentSavePath = FileDownloader.getSavePath(getAccount().name); - for (int i=0; i < children.size(); ) { - file = children.get(i); - if (file.getLastSyncDate() != mCurrentSyncTime) { - Log.v(TAG, "removing file: " + file); - getStorageManager().removeFile(file, (file.isDown() && file.getStoragePath().startsWith(currentSavePath))); - children.remove(i); - } else { - i++; - } - } - - } else { - syncResult.stats.numAuthExceptions++; - } - } catch (IOException e) { - syncResult.stats.numIoExceptions++; - logException(e, uri); - - } catch (DavException e) { - syncResult.stats.numParseExceptions++; - logException(e, uri); - - } catch (Exception e) { - // TODO something smart with syncresult - mRightSync = false; - logException(e, uri); - - } finally { - if (query != null) - query.releaseConnection(); // let the connection available for other methods - - // synchronized folder -> notice to UI - sendStickyBroadcast(true, getStorageManager().getFileById(parentId).getRemotePath()); - } - - - fetchChildren(children, syncResult); - if (mCancellation) Log.d(TAG, "Leaving " + uri + " because cancelation request"); - - - /* Commented code for ugly performance tests - mResponseDelays[mDelaysIndex] = responseDelay; - mSaveDelays[mDelaysIndex] = saveDelay; - mDelaysCount++; - mDelaysIndex++; - if (mDelaysIndex >= MAX_DELAYS) - mDelaysIndex = 0; - */ - - } - - /** - * Synchronize data of folders in the list of received files - * - * @param files Files to recursively fetch - * @param syncResult Updated object to provide results to the Synchronization Manager - */ - private void fetchChildren(Vector files, SyncResult syncResult) { - for (int i=0; i < files.size() && !mCancellation; i++) { - OCFile newFile = files.get(i); - if (newFile.getMimetype().equals("DIR")) { - fetchData(getUri().toString() + WebdavUtils.encodePath(newFile.getRemotePath()), syncResult, newFile.getFileId()); - } - } - } - - - private OCFile fillOCFile(WebdavEntry we) { - OCFile file = new OCFile(we.decodedPath()); - file.setCreationTimestamp(we.createTimestamp()); - file.setFileLength(we.contentLength()); - file.setMimetype(we.contentType()); - file.setModificationTimestamp(we.modifiedTimesamp()); - file.setLastSyncDate(mCurrentSyncTime); - return file; - } - - - private void sendStickyBroadcast(boolean inProgress, String dirRemotePath) { - Intent i = new Intent(FileSyncService.SYNC_MESSAGE); - i.putExtra(FileSyncService.IN_PROGRESS, inProgress); - i.putExtra(FileSyncService.ACCOUNT_NAME, getAccount().name); - if (dirRemotePath != null) { - i.putExtra(FileSyncService.SYNC_FOLDER_REMOTE_PATH, dirRemotePath); - } - getContext().sendStickyBroadcast(i); - } - - /** - * Called by system SyncManager when a synchronization is required to be cancelled. - * - * Sets the mCancellation flag to 'true'. THe synchronization will be stopped when before a new folder is fetched. Data of the last folder - * fetched will be still saved in the database. See onPerformSync implementation. - */ - @Override - public void onSyncCanceled() { - Log.d(TAG, "Synchronization of " + getAccount().name + " has been requested to cancel"); - mCancellation = true; - super.onSyncCanceled(); - } - - - /** - * Logs an exception triggered in a synchronization request. - * - * @param e Caught exception. - * @param uri Uri to the remote directory that was fetched when the synchronization failed - */ - private void logException(Exception e, String uri) { - if (e instanceof IOException) { - Log.e(TAG, "Unrecovered transport exception while synchronizing " + uri + " at " + getAccount().name, e); - - } else if (e instanceof DavException) { - Log.e(TAG, "Unexpected WebDAV exception while synchronizing " + uri + " at " + getAccount().name, e); - - } else { - Log.e(TAG, "Unexpected exception while synchronizing " + uri + " at " + getAccount().name, e); - } - } - - private void updateOCVersion() { - String statUrl = getAccountManager().getUserData(getAccount(), AccountAuthenticator.KEY_OC_BASE_URL); - statUrl += AccountUtils.STATUS_PATH; - - try { - String result = getClient().getResultAsString(statUrl); - if (result != null) { - try { - JSONObject json = new JSONObject(result); - if (json != null && json.getString("version") != null) { - OwnCloudVersion ocver = new OwnCloudVersion(json.getString("version")); - if (ocver.isVersionValid()) { - getAccountManager().setUserData(getAccount(), AccountAuthenticator.KEY_OC_VERSION, ocver.toString()); - Log.d(TAG, "Got new OC version " + ocver.toString()); - } else { - Log.w(TAG, "Invalid version number received from server: " + json.getString("version")); - } - } - } catch (Throwable e) { - Log.w(TAG, "Couldn't parse version response", e); - } - } else { - Log.w(TAG, "Problem while getting ocversion from server"); - } - } catch (Exception e) { - Log.e(TAG, "Problem getting response from server", e); - } - } -} +/* ownCloud Android client application + * Copyright (C) 2011 Bartek Przybylski + * Copyright (C) 2012-2013 ownCloud Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.owncloud.android.syncadapter; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.jackrabbit.webdav.DavException; + +import com.owncloud.android.R; +import com.owncloud.android.authentication.AuthenticatorActivity; +import com.owncloud.android.datamodel.FileDataStorageManager; +import com.owncloud.android.datamodel.OCFile; +import com.owncloud.android.oc_framework.operations.RemoteOperationResult; +import com.owncloud.android.operations.SynchronizeFolderOperation; +import com.owncloud.android.operations.UpdateOCVersionOperation; +import com.owncloud.android.oc_framework.operations.RemoteOperationResult.ResultCode; +import com.owncloud.android.ui.activity.ErrorsWhileCopyingHandlerActivity; +import com.owncloud.android.utils.DisplayUtils; +import com.owncloud.android.utils.Log_OC; + + +import android.accounts.Account; +import android.accounts.AccountsException; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.AbstractThreadedSyncAdapter; +import android.content.ContentProviderClient; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.SyncResult; +import android.os.Bundle; + +/** + * Implementation of {@link AbstractThreadedSyncAdapter} responsible for synchronizing + * ownCloud files. + * + * Performs a full synchronization of the account recieved in {@link #onPerformSync(Account, Bundle, String, ContentProviderClient, SyncResult)}. + * + * @author Bartek Przybylski + * @author David A. Velasco + */ +public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter { + + private final static String TAG = FileSyncAdapter.class.getSimpleName(); + + /** Maximum number of failed folder synchronizations that are supported before finishing the synchronization operation */ + private static final int MAX_FAILED_RESULTS = 3; + + + /** Time stamp for the current synchronization process, used to distinguish fresh data */ + private long mCurrentSyncTime; + + /** Flag made 'true' when a request to cancel the synchronization is received */ + private boolean mCancellation; + + /** When 'true' the process was requested by the user through the user interface; when 'false', it was requested automatically by the system */ + private boolean mIsManualSync; + + /** Counter for failed operations in the synchronization process */ + private int mFailedResultsCounter; + + /** Result of the last failed operation */ + private RemoteOperationResult mLastFailedResult; + + /** Counter of conflicts found between local and remote files */ + private int mConflictsFound; + + /** Counter of failed operations in synchronization of kept-in-sync files */ + private int mFailsInFavouritesFound; + + /** Map of remote and local paths to files that where locally stored in a location out of the ownCloud folder and couldn't be copied automatically into it */ + private Map mForgottenLocalFiles; + + /** {@link SyncResult} instance to return to the system when the synchronization finish */ + private SyncResult mSyncResult; + + + /** + * Creates a {@link FileSyncAdapter} + * + * {@inheritDoc} + */ + public FileSyncAdapter(Context context, boolean autoInitialize) { + super(context, autoInitialize); + } + + + /** + * Creates a {@link FileSyncAdapter} + * + * {@inheritDoc} + */ + public FileSyncAdapter(Context context, boolean autoInitialize, boolean allowParallelSyncs) { + super(context, autoInitialize, allowParallelSyncs); + } + + + /** + * {@inheritDoc} + */ + @Override + public synchronized void onPerformSync(Account account, Bundle extras, + String authority, ContentProviderClient providerClient, + SyncResult syncResult) { + + mCancellation = false; + mIsManualSync = extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false); + mFailedResultsCounter = 0; + mLastFailedResult = null; + mConflictsFound = 0; + mFailsInFavouritesFound = 0; + mForgottenLocalFiles = new HashMap(); + mSyncResult = syncResult; + mSyncResult.fullSyncRequested = false; + mSyncResult.delayUntil = 60*60*24; // avoid too many automatic synchronizations + + this.setAccount(account); + this.setContentProviderClient(providerClient); + this.setStorageManager(new FileDataStorageManager(account, providerClient)); + try { + this.initClientForCurrentAccount(); + } catch (IOException e) { + /// the account is unknown for the Synchronization Manager, unreachable this context, or can not be authenticated; don't try this again + mSyncResult.tooManyRetries = true; + notifyFailedSynchronization(); + return; + } catch (AccountsException e) { + /// the account is unknown for the Synchronization Manager, unreachable this context, or can not be authenticated; don't try this again + mSyncResult.tooManyRetries = true; + notifyFailedSynchronization(); + return; + } + + Log_OC.d(TAG, "Synchronization of ownCloud account " + account.name + " starting"); + sendStickyBroadcast(true, null, null); // message to signal the start of the synchronization to the UI + + try { + updateOCVersion(); + mCurrentSyncTime = System.currentTimeMillis(); + if (!mCancellation) { + synchronizeFolder(getStorageManager().getFileByPath(OCFile.ROOT_PATH)); + + } else { + Log_OC.d(TAG, "Leaving synchronization before synchronizing the root folder because cancelation request"); + } + + + } finally { + // it's important making this although very unexpected errors occur; that's the reason for the finally + + if (mFailedResultsCounter > 0 && mIsManualSync) { + /// don't let the system synchronization manager retries MANUAL synchronizations + // (be careful: "MANUAL" currently includes the synchronization requested when a new account is created and when the user changes the current account) + mSyncResult.tooManyRetries = true; + + /// notify the user about the failure of MANUAL synchronization + notifyFailedSynchronization(); + } + if (mConflictsFound > 0 || mFailsInFavouritesFound > 0) { + notifyFailsInFavourites(); + } + if (mForgottenLocalFiles.size() > 0) { + notifyForgottenLocalFiles(); + } + sendStickyBroadcast(false, null, mLastFailedResult); // message to signal the end to the UI + } + + } + + /** + * Called by system SyncManager when a synchronization is required to be cancelled. + * + * Sets the mCancellation flag to 'true'. THe synchronization will be stopped later, + * before a new folder is fetched. Data of the last folder synchronized will be still + * locally saved. + * + * See {@link #onPerformSync(Account, Bundle, String, ContentProviderClient, SyncResult)} + * and {@link #synchronizeFolder(String, long)}. + */ + @Override + public void onSyncCanceled() { + Log_OC.d(TAG, "Synchronization of " + getAccount().name + " has been requested to cancel"); + mCancellation = true; + super.onSyncCanceled(); + } + + + /** + * Updates the locally stored version value of the ownCloud server + */ + private void updateOCVersion() { + UpdateOCVersionOperation update = new UpdateOCVersionOperation(getAccount(), getContext()); + RemoteOperationResult result = update.execute(getClient()); + if (!result.isSuccess()) { + mLastFailedResult = result; + } + } + + + /** + * Synchronizes the list of files contained in a folder identified with its remote path. + * + * Fetches the list and properties of the files contained in the given folder, including their + * properties, and updates the local database with them. + * + * Enters in the child folders to synchronize their contents also, following a recursive + * depth first strategy. + * + * @param folder Folder to synchronize. + */ + private void synchronizeFolder(OCFile folder) { + + if (mFailedResultsCounter > MAX_FAILED_RESULTS || isFinisher(mLastFailedResult)) + return; + + /* + OCFile folder, + long currentSyncTime, + boolean updateFolderProperties, + boolean syncFullAccount, + DataStorageManager dataStorageManager, + Account account, + Context context ) { + + } + */ + // folder synchronization + SynchronizeFolderOperation synchFolderOp = new SynchronizeFolderOperation( folder, + mCurrentSyncTime, + true, + getStorageManager(), + getAccount(), + getContext() + ); + RemoteOperationResult result = synchFolderOp.execute(getClient()); + + + // synchronized folder -> notice to UI - ALWAYS, although !result.isSuccess + sendStickyBroadcast(true, folder.getRemotePath(), null); + + // check the result of synchronizing the folder + if (result.isSuccess() || result.getCode() == ResultCode.SYNC_CONFLICT) { + + if (result.getCode() == ResultCode.SYNC_CONFLICT) { + mConflictsFound += synchFolderOp.getConflictsFound(); + mFailsInFavouritesFound += synchFolderOp.getFailsInFavouritesFound(); + } + if (synchFolderOp.getForgottenLocalFiles().size() > 0) { + mForgottenLocalFiles.putAll(synchFolderOp.getForgottenLocalFiles()); + } + if (result.isSuccess()) { + // synchronize children folders + List children = synchFolderOp.getChildren(); + fetchChildren(folder, children, synchFolderOp.getRemoteFolderChanged()); // beware of the 'hidden' recursion here! + } + + } else { + // in failures, the statistics for the global result are updated + if (result.getCode() == RemoteOperationResult.ResultCode.UNAUTHORIZED || + ( result.isIdPRedirection() && + getClient().getCredentials() == null )) { + //MainApp.getAuthTokenTypeSamlSessionCookie().equals(getClient().getAuthTokenType()))) { + mSyncResult.stats.numAuthExceptions++; + + } else if (result.getException() instanceof DavException) { + mSyncResult.stats.numParseExceptions++; + + } else if (result.getException() instanceof IOException) { + mSyncResult.stats.numIoExceptions++; + } + mFailedResultsCounter++; + mLastFailedResult = result; + } + + } + + /** + * Checks if a failed result should terminate the synchronization process immediately, according to + * OUR OWN POLICY + * + * @param failedResult Remote operation result to check. + * @return 'True' if the result should immediately finish the synchronization + */ + private boolean isFinisher(RemoteOperationResult failedResult) { + if (failedResult != null) { + RemoteOperationResult.ResultCode code = failedResult.getCode(); + return (code.equals(RemoteOperationResult.ResultCode.SSL_ERROR) || + code.equals(RemoteOperationResult.ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED) || + code.equals(RemoteOperationResult.ResultCode.BAD_OC_VERSION) || + code.equals(RemoteOperationResult.ResultCode.INSTANCE_NOT_CONFIGURED)); + } + return false; + } + + /** + * Triggers the synchronization of any folder contained in the list of received files. + * + * @param files Files to recursively synchronize. + */ + private void fetchChildren(OCFile parent, List files, boolean parentEtagChanged) { + int i; + OCFile newFile = null; + //String etag = null; + //boolean syncDown = false; + for (i=0; i < files.size() && !mCancellation; i++) { + newFile = files.get(i); + if (newFile.isFolder()) { + /* + etag = newFile.getEtag(); + syncDown = (parentEtagChanged || etag == null || etag.length() == 0); + if(syncDown) { */ + synchronizeFolder(newFile); + // update the size of the parent folder again after recursive synchronization + //getStorageManager().updateFolderSize(parent.getFileId()); + sendStickyBroadcast(true, parent.getRemotePath(), null); // notify again to refresh size in UI + //} + } + } + + if (mCancellation && i 0) { + Notification notification = new Notification(DisplayUtils.getSeasonalIconId(), getContext().getString(R.string.sync_fail_in_favourites_ticker), System.currentTimeMillis()); + notification.flags |= Notification.FLAG_AUTO_CANCEL; + // TODO put something smart in the contentIntent below + notification.contentIntent = PendingIntent.getActivity(getContext().getApplicationContext(), (int)System.currentTimeMillis(), new Intent(), 0); + notification.setLatestEventInfo(getContext().getApplicationContext(), + getContext().getString(R.string.sync_fail_in_favourites_ticker), + String.format(getContext().getString(R.string.sync_fail_in_favourites_content), mFailedResultsCounter + mConflictsFound, mConflictsFound), + notification.contentIntent); + ((NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE)).notify(R.string.sync_fail_in_favourites_ticker, notification); + + } else { + Notification notification = new Notification(DisplayUtils.getSeasonalIconId(), getContext().getString(R.string.sync_conflicts_in_favourites_ticker), System.currentTimeMillis()); + notification.flags |= Notification.FLAG_AUTO_CANCEL; + // TODO put something smart in the contentIntent below + notification.contentIntent = PendingIntent.getActivity(getContext().getApplicationContext(), (int)System.currentTimeMillis(), new Intent(), 0); + notification.setLatestEventInfo(getContext().getApplicationContext(), + getContext().getString(R.string.sync_conflicts_in_favourites_ticker), + String.format(getContext().getString(R.string.sync_conflicts_in_favourites_content), mConflictsFound), + notification.contentIntent); + ((NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE)).notify(R.string.sync_conflicts_in_favourites_ticker, notification); + } + } + + /** + * Notifies the user about local copies of files out of the ownCloud local directory that were 'forgotten' because + * copying them inside the ownCloud local directory was not possible. + * + * We don't want links to files out of the ownCloud local directory (foreign files) anymore. It's easy to have + * synchronization problems if a local file is linked to more than one remote file. + * + * We won't consider a synchronization as failed when foreign files can not be copied to the ownCloud local directory. + */ + private void notifyForgottenLocalFiles() { + Notification notification = new Notification(DisplayUtils.getSeasonalIconId(), getContext().getString(R.string.sync_foreign_files_forgotten_ticker), System.currentTimeMillis()); + notification.flags |= Notification.FLAG_AUTO_CANCEL; + + /// includes a pending intent in the notification showing a more detailed explanation + Intent explanationIntent = new Intent(getContext(), ErrorsWhileCopyingHandlerActivity.class); + explanationIntent.putExtra(ErrorsWhileCopyingHandlerActivity.EXTRA_ACCOUNT, getAccount()); + ArrayList remotePaths = new ArrayList(); + ArrayList localPaths = new ArrayList(); + remotePaths.addAll(mForgottenLocalFiles.keySet()); + localPaths.addAll(mForgottenLocalFiles.values()); + explanationIntent.putExtra(ErrorsWhileCopyingHandlerActivity.EXTRA_LOCAL_PATHS, localPaths); + explanationIntent.putExtra(ErrorsWhileCopyingHandlerActivity.EXTRA_REMOTE_PATHS, remotePaths); + explanationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + + notification.contentIntent = PendingIntent.getActivity(getContext().getApplicationContext(), (int)System.currentTimeMillis(), explanationIntent, 0); + notification.setLatestEventInfo(getContext().getApplicationContext(), + getContext().getString(R.string.sync_foreign_files_forgotten_ticker), + String.format(getContext().getString(R.string.sync_foreign_files_forgotten_content), mForgottenLocalFiles.size(), getContext().getString(R.string.app_name)), + notification.contentIntent); + ((NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE)).notify(R.string.sync_foreign_files_forgotten_ticker, notification); + + } + + +}