Adding cancellation to uploads (WIP)
[pub/Android/ownCloud.git] / src / com / owncloud / android / syncadapter / FileSyncAdapter.java
index f7bfa43..e55d618 100644 (file)
@@ -22,18 +22,22 @@ import java.io.IOException;
 import java.util.List;\r
 import java.util.Vector;\r
 \r
+import org.apache.http.HttpStatus;\r
 import org.apache.jackrabbit.webdav.DavException;\r
 import org.apache.jackrabbit.webdav.MultiStatus;\r
 import org.apache.jackrabbit.webdav.client.methods.PropFindMethod;\r
 \r
+import com.owncloud.android.R;\r
 import com.owncloud.android.datamodel.FileDataStorageManager;\r
 import com.owncloud.android.datamodel.OCFile;\r
 import com.owncloud.android.files.services.FileDownloader;\r
 \r
 import android.accounts.Account;\r
-import android.accounts.AuthenticatorException;\r
-import android.accounts.OperationCanceledException;\r
+import android.app.Notification;\r
+import android.app.NotificationManager;\r
+import android.app.PendingIntent;\r
 import android.content.ContentProviderClient;\r
+import android.content.ContentResolver;\r
 import android.content.Context;\r
 import android.content.Intent;\r
 import android.content.SyncResult;\r
@@ -62,7 +66,8 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter {
     \r
     private long mCurrentSyncTime;\r
     private boolean mCancellation;\r
-    private Account mAccount;\r
+    private boolean mIsManualSync;\r
+    private boolean mRightSync;\r
     \r
     public FileSyncAdapter(Context context, boolean autoInitialize) {\r
         super(context, autoInitialize);\r
@@ -74,12 +79,12 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter {
             SyncResult syncResult) {\r
 \r
         mCancellation = false;\r
-        mAccount = account;\r
+        mIsManualSync = extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false);\r
+        mRightSync = true;\r
         \r
-        this.setAccount(mAccount);\r
+        this.setAccount(account);\r
         this.setContentProvider(provider);\r
-        this.setStorageManager(new FileDataStorageManager(mAccount,\r
-                getContentProvider()));\r
+        this.setStorageManager(new FileDataStorageManager(account, getContentProvider()));\r
         \r
         /*  Commented code for ugly performance tests\r
         mDelaysIndex = 0;\r
@@ -87,42 +92,66 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter {
         */\r
         \r
         \r
-        Log.d(TAG, "syncing owncloud account " + mAccount.name);\r
+        Log.d(TAG, "syncing owncloud account " + account.name);\r
 \r
         sendStickyBroadcast(true, null);  // message to signal the start to the UI\r
 \r
-        PropFindMethod query;\r
+        String uri = getUri().toString();\r
+        PropFindMethod query = null;\r
         try {\r
             mCurrentSyncTime = System.currentTimeMillis();\r
-            query = new PropFindMethod(getUri().toString() + "/");\r
-            getClient().executeMethod(query);\r
-            MultiStatus resp = null;\r
-            resp = query.getResponseBodyAsMultiStatus();\r
+            query = new PropFindMethod(uri + "/");\r
+            int status = getClient().executeMethod(query);\r
+            if (status != HttpStatus.SC_UNAUTHORIZED) {\r
+                MultiStatus resp = query.getResponseBodyAsMultiStatus();\r
 \r
-            if (resp.getResponses().length > 0) {\r
-                WebdavEntry we = new WebdavEntry(resp.getResponses()[0], getUri().getPath());\r
-                OCFile file = fillOCFile(we);\r
-                file.setParentId(0);\r
-                getStorageManager().saveFile(file);\r
-                if (!mCancellation) {\r
-                    fetchData(getUri().toString(), syncResult, file.getFileId());\r
+                if (resp.getResponses().length > 0) {\r
+                    WebdavEntry we = new WebdavEntry(resp.getResponses()[0], getUri().getPath());\r
+                    OCFile file = fillOCFile(we);\r
+                    file.setParentId(0);\r
+                    getStorageManager().saveFile(file);\r
+                    if (!mCancellation) {\r
+                        fetchData(uri, syncResult, file.getFileId());\r
+                    }\r
                 }\r
+                \r
+            } else {\r
+                syncResult.stats.numAuthExceptions++;\r
             }\r
-        } catch (OperationCanceledException e) {\r
-            e.printStackTrace();\r
-        } catch (AuthenticatorException e) {\r
-            syncResult.stats.numAuthExceptions++;\r
-            e.printStackTrace();\r
         } catch (IOException e) {\r
             syncResult.stats.numIoExceptions++;\r
-            e.printStackTrace();\r
+            logException(e, uri + "/");\r
+            \r
         } catch (DavException e) {\r
-            syncResult.stats.numIoExceptions++;\r
-            e.printStackTrace();\r
-        } catch (Throwable t) {\r
-            // TODO update syncResult\r
-            Log.e(TAG, "problem while synchronizing owncloud account " + account.name, t);\r
-            t.printStackTrace();\r
+            syncResult.stats.numParseExceptions++;\r
+            logException(e, uri + "/");\r
+            \r
+        } catch (Exception e) {\r
+            // TODO something smart with syncresult\r
+            logException(e, uri + "/");\r
+            mRightSync = false;\r
+            \r
+        } finally {\r
+            if (query != null)\r
+                query.releaseConnection();  // let the connection available for other methods\r
+            mRightSync &= (syncResult.stats.numIoExceptions == 0 && syncResult.stats.numAuthExceptions == 0 && syncResult.stats.numParseExceptions == 0);\r
+            if (!mRightSync && mIsManualSync) {\r
+                /// don't let the system synchronization manager retries MANUAL synchronizations\r
+                //      (be careful: "MANUAL" currently includes the synchronization requested when a new account is created and when the user changes the current account)\r
+                syncResult.tooManyRetries = true;\r
+                \r
+                /// notify the user about the failure of MANUAL synchronization\r
+                Notification notification = new Notification(R.drawable.icon, getContext().getString(R.string.sync_fail_ticker), System.currentTimeMillis());\r
+                notification.flags |= Notification.FLAG_AUTO_CANCEL;\r
+                // TODO put something smart in the contentIntent below\r
+                notification.contentIntent = PendingIntent.getActivity(getContext().getApplicationContext(), 0, new Intent(), PendingIntent.FLAG_UPDATE_CURRENT);\r
+                notification.setLatestEventInfo(getContext().getApplicationContext(), \r
+                                                getContext().getString(R.string.sync_fail_ticker), \r
+                                                String.format(getContext().getString(R.string.sync_fail_content), account.name), \r
+                                                notification.contentIntent);\r
+                ((NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE)).notify(R.string.sync_fail_ticker, notification);\r
+            }\r
+            sendStickyBroadcast(false, null);        // message to signal the end to the UI\r
         }\r
         \r
         /*  Commented code for ugly performance tests\r
@@ -146,114 +175,118 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter {
         Log.e(TAG, "SYNC STATS - folders measured: " + mDelaysCount);\r
         */\r
         \r
-        sendStickyBroadcast(false, null);        \r
     }\r
 \r
     private void fetchData(String uri, SyncResult syncResult, long parentId) {\r
+        PropFindMethod query = null;\r
         try {\r
             Log.d(TAG, "fetching " + uri);\r
             \r
             // remote request \r
-            PropFindMethod query = new PropFindMethod(uri);\r
+            query = new PropFindMethod(uri);\r
             /*  Commented code for ugly performance tests\r
             long responseDelay = System.currentTimeMillis();\r
             */\r
-            getClient().executeMethod(query);\r
+            int status = getClient().executeMethod(query);\r
             /*  Commented code for ugly performance tests\r
             responseDelay = System.currentTimeMillis() - responseDelay;\r
             Log.e(TAG, "syncing: RESPONSE TIME for " + uri + " contents, " + responseDelay + "ms");\r
             */\r
-            MultiStatus resp = null;\r
-            resp = query.getResponseBodyAsMultiStatus();\r
+            if (status != HttpStatus.SC_UNAUTHORIZED) {\r
+                MultiStatus resp = query.getResponseBodyAsMultiStatus();\r
             \r
-            // insertion or update of files\r
-            List<OCFile> updatedFiles = new Vector<OCFile>(resp.getResponses().length - 1);\r
-            for (int i = 1; i < resp.getResponses().length; ++i) {\r
-                WebdavEntry we = new WebdavEntry(resp.getResponses()[i], getUri().getPath());\r
-                OCFile file = fillOCFile(we);\r
-                file.setParentId(parentId);\r
-                if (getStorageManager().getFileByPath(file.getRemotePath()) != null &&\r
-                    getStorageManager().getFileByPath(file.getRemotePath()).keepInSync() &&\r
-                    file.getModificationTimestamp() > getStorageManager().getFileByPath(file.getRemotePath())\r
+                // insertion or update of files\r
+                List<OCFile> updatedFiles = new Vector<OCFile>(resp.getResponses().length - 1);\r
+                for (int i = 1; i < resp.getResponses().length; ++i) {\r
+                    WebdavEntry we = new WebdavEntry(resp.getResponses()[i], getUri().getPath());\r
+                    OCFile file = fillOCFile(we);\r
+                    file.setParentId(parentId);\r
+                    if (getStorageManager().getFileByPath(file.getRemotePath()) != null &&\r
+                            getStorageManager().getFileByPath(file.getRemotePath()).keepInSync() &&\r
+                            file.getModificationTimestamp() > getStorageManager().getFileByPath(file.getRemotePath())\r
                                                                          .getModificationTimestamp()) {\r
-                    Intent intent = new Intent(this.getContext(), FileDownloader.class);\r
-                    intent.putExtra(FileDownloader.EXTRA_ACCOUNT, getAccount());\r
-                    intent.putExtra(FileDownloader.EXTRA_FILE_PATH, file.getRemotePath());\r
-                    intent.putExtra(FileDownloader.EXTRA_REMOTE_PATH, file.getRemotePath());\r
-                    intent.putExtra(FileDownloader.EXTRA_FILE_SIZE, file.getFileLength());\r
-                    file.setKeepInSync(true);\r
-                    getContext().startService(intent);\r
-                }\r
-                if (getStorageManager().getFileByPath(file.getRemotePath()) != null)\r
-                    file.setKeepInSync(getStorageManager().getFileByPath(file.getRemotePath()).keepInSync());\r
+                        Intent intent = new Intent(this.getContext(), FileDownloader.class);\r
+                        intent.putExtra(FileDownloader.EXTRA_ACCOUNT, getAccount());\r
+                        intent.putExtra(FileDownloader.EXTRA_FILE, file);\r
+                        /*intent.putExtra(FileDownloader.EXTRA_FILE_PATH, file.getRemotePath());\r
+                        intent.putExtra(FileDownloader.EXTRA_REMOTE_PATH, file.getRemotePath());\r
+                        intent.putExtra(FileDownloader.EXTRA_FILE_SIZE, file.getFileLength());*/\r
+                        file.setKeepInSync(true);\r
+                        getContext().startService(intent);\r
+                    }\r
+                    if (getStorageManager().getFileByPath(file.getRemotePath()) != null)\r
+                        file.setKeepInSync(getStorageManager().getFileByPath(file.getRemotePath()).keepInSync());\r
                 \r
-                //Log.v(TAG, "adding file: " + file);\r
-                updatedFiles.add(file);\r
-                if (parentId == 0)\r
-                    parentId = file.getFileId();\r
-            }\r
-            /*  Commented code for ugly performance tests\r
-            long saveDelay = System.currentTimeMillis();\r
-            */            \r
-            getStorageManager().saveFiles(updatedFiles);    // all "at once" ; trying to get a best performance in database update\r
-            /*  Commented code for ugly performance tests\r
-            saveDelay = System.currentTimeMillis() - saveDelay;\r
-            Log.e(TAG, "syncing: SAVE TIME for " + uri + " contents, " + mSaveDelays[mDelaysIndex] + "ms");\r
-            */\r
+                    // Log.v(TAG, "adding file: " + file);\r
+                    updatedFiles.add(file);\r
+                    if (parentId == 0)\r
+                        parentId = file.getFileId();\r
+                }\r
+                /*  Commented code for ugly performance tests\r
+                long saveDelay = System.currentTimeMillis();\r
+                 */            \r
+                getStorageManager().saveFiles(updatedFiles);    // all "at once" ; trying to get a best performance in database update\r
+                /*  Commented code for ugly performance tests\r
+                saveDelay = System.currentTimeMillis() - saveDelay;\r
+                Log.e(TAG, "syncing: SAVE TIME for " + uri + " contents, " + mSaveDelays[mDelaysIndex] + "ms");\r
+                 */\r
             \r
-            // removal of obsolete files\r
-            Vector<OCFile> files = getStorageManager().getDirectoryContent(\r
-                    getStorageManager().getFileById(parentId));\r
-            OCFile file;\r
-            for (int i=0; i < files.size(); ) {\r
-                file = files.get(i);\r
-                if (file.getLastSyncDate() != mCurrentSyncTime) {\r
-                    Log.v(TAG, "removing file: " + file);\r
-                    getStorageManager().removeFile(file);\r
-                    files.remove(i);\r
-                } else {\r
-                    i++;\r
+                // removal of obsolete files\r
+                Vector<OCFile> files = getStorageManager().getDirectoryContent(\r
+                        getStorageManager().getFileById(parentId));\r
+                OCFile file;\r
+                String currentSavePath = FileDownloader.getSavePath(getAccount().name);\r
+                for (int i=0; i < files.size(); ) {\r
+                    file = files.get(i);\r
+                    if (file.getLastSyncDate() != mCurrentSyncTime) {\r
+                        Log.v(TAG, "removing file: " + file);\r
+                        getStorageManager().removeFile(file, (file.isDown() && file.getStoragePath().startsWith(currentSavePath)));\r
+                        files.remove(i);\r
+                    } else {\r
+                        i++;\r
+                    }\r
                 }\r
-            }\r
             \r
-            // synchronized folder -> notice to UI\r
-            sendStickyBroadcast(true, getStorageManager().getFileById(parentId).getRemotePath());\r
-\r
-            // recursive fetch\r
-            for (int i=0; i < files.size() && !mCancellation; i++) {\r
-                OCFile newFile = files.get(i);\r
-                if (newFile.getMimetype().equals("DIR")) {\r
-                    fetchData(getUri().toString() + WebdavUtils.encodePath(newFile.getRemotePath()), syncResult, newFile.getFileId());\r
+                // recursive fetch\r
+                for (int i=0; i < files.size() && !mCancellation; i++) {\r
+                    OCFile newFile = files.get(i);\r
+                    if (newFile.getMimetype().equals("DIR")) {\r
+                        fetchData(getUri().toString() + WebdavUtils.encodePath(newFile.getRemotePath()), syncResult, newFile.getFileId());\r
+                    }\r
                 }\r
-            }\r
-            if (mCancellation) Log.d(TAG, "Leaving " + uri + " because cancelation request");\r
+                if (mCancellation) Log.d(TAG, "Leaving " + uri + " because cancelation request");\r
                 \r
-            /*  Commented code for ugly performance tests\r
-            mResponseDelays[mDelaysIndex] = responseDelay;\r
-            mSaveDelays[mDelaysIndex] = saveDelay;\r
-            mDelaysCount++;\r
-            mDelaysIndex++;\r
-            if (mDelaysIndex >= MAX_DELAYS)\r
-                mDelaysIndex = 0;\r
-             */\r
-            \r
+                /*  Commented code for ugly performance tests\r
+                mResponseDelays[mDelaysIndex] = responseDelay;\r
+                mSaveDelays[mDelaysIndex] = saveDelay;\r
+                mDelaysCount++;\r
+                mDelaysIndex++;\r
+                if (mDelaysIndex >= MAX_DELAYS)\r
+                    mDelaysIndex = 0;\r
+                 */\r
 \r
-\r
-        } catch (OperationCanceledException e) {\r
-            e.printStackTrace();\r
-        } catch (AuthenticatorException e) {\r
-            syncResult.stats.numAuthExceptions++;\r
-            e.printStackTrace();\r
+            } else {\r
+                syncResult.stats.numAuthExceptions++;\r
+            }\r
         } catch (IOException e) {\r
             syncResult.stats.numIoExceptions++;\r
-            e.printStackTrace();\r
+            logException(e, uri);\r
+            \r
         } catch (DavException e) {\r
-            syncResult.stats.numIoExceptions++;\r
-            e.printStackTrace();\r
-        } catch (Throwable t) {\r
-            // TODO update syncResult\r
-            Log.e(TAG, "problem while synchronizing owncloud account " + mAccount.name, t);\r
-            t.printStackTrace();\r
+            syncResult.stats.numParseExceptions++;\r
+            logException(e, uri);\r
+            \r
+        } catch (Exception e) {\r
+            // TODO something smart with syncresult\r
+            mRightSync = false;\r
+            logException(e, uri);\r
+\r
+        } finally {\r
+            if (query != null)\r
+                query.releaseConnection();  // let the connection available for other methods\r
+\r
+            // synchronized folder -> notice to UI\r
+            sendStickyBroadcast(true, getStorageManager().getFileById(parentId).getRemotePath());\r
         }\r
     }\r
 \r
@@ -286,9 +319,29 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter {
      */\r
     @Override\r
     public void onSyncCanceled() {\r
-        Log.d(TAG, "Synchronization of " + mAccount.name + " has been requested to cancell");\r
+        Log.d(TAG, "Synchronization of " + getAccount().name + " has been requested to cancel");\r
         mCancellation = true;\r
         super.onSyncCanceled();\r
     }\r
 \r
+    \r
+    /**\r
+     * Logs an exception triggered in a synchronization request. \r
+     * \r
+     * @param e       Caught exception.\r
+     * @param uri     Uri to the remote directory that was fetched when the synchronization failed \r
+     */\r
+    private void logException(Exception e, String uri) {\r
+        if (e instanceof IOException) {\r
+            Log.e(TAG, "Unrecovered transport exception while synchronizing " + uri + " at " + getAccount().name, e);\r
+\r
+        } else if (e instanceof DavException) {\r
+            Log.e(TAG, "Unexpected WebDAV exception while synchronizing " + uri + " at " + getAccount().name, e);\r
+\r
+        } else {\r
+            Log.e(TAG, "Unexpected exception while synchronizing " + uri  + " at " + getAccount().name, e);\r
+        }\r
+    }\r
+\r
+    \r
 }\r