소스 검색

Fix syncing

Signed-off-by: Mario Danic <mario@lovelyhq.com>
Mario Danic 7 년 전
부모
커밋
8593b10c6d

+ 1 - 0
src/main/java/com/owncloud/android/operations/SynchronizeFileOperation.java

@@ -247,6 +247,7 @@ public class SynchronizeFileOperation extends SyncOperation {
                         // service when the upload finishes
                     } else {
                         // TODO CHECK: is this really useful in some point in the code?
+                        mServerFile.setFavorite(mLocalFile.getIsFavorite());
                         mServerFile.setAvailableOffline(mLocalFile.isAvailableOffline());
                         mServerFile.setLastSyncDateForData(mLocalFile.getLastSyncDateForData());
                         mServerFile.setStoragePath(mLocalFile.getStoragePath());

+ 28 - 2
src/main/java/com/owncloud/android/providers/DocumentsStorageProvider.java

@@ -23,6 +23,7 @@ package com.owncloud.android.providers;
 import android.accounts.Account;
 import android.annotation.TargetApi;
 import android.content.ContentResolver;
+import android.content.Context;
 import android.content.Intent;
 import android.content.res.AssetFileDescriptor;
 import android.database.Cursor;
@@ -36,6 +37,11 @@ import com.owncloud.android.authentication.AccountUtils;
 import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.files.services.FileDownloader;
+import com.owncloud.android.lib.common.operations.RemoteOperationResult;
+import com.owncloud.android.operations.SynchronizeFileOperation;
+import com.owncloud.android.ui.activity.ConflictsResolveActivity;
+import com.owncloud.android.utils.FileStorageUtils;
+
 import org.nextcloud.providers.cursors.FileCursor;
 import org.nextcloud.providers.cursors.RootCursor;
 
@@ -103,12 +109,15 @@ public class DocumentsStorageProvider extends DocumentsProvider {
 
         OCFile file = mCurrentStorageManager.getFileById(docId);
 
+        Account account = mCurrentStorageManager.getAccount();
+        Context context = getContext();
+
         if (!file.isDown()) {
 
             Intent i = new Intent(getContext(), FileDownloader.class);
-            i.putExtra(FileDownloader.EXTRA_ACCOUNT, mCurrentStorageManager.getAccount());
+            i.putExtra(FileDownloader.EXTRA_ACCOUNT, account);
             i.putExtra(FileDownloader.EXTRA_FILE, file);
-            getContext().startService(i);
+            context.startService(i);
 
             do {
                 if (!waitOrGetCancelled(cancellationSignal)) {
@@ -117,6 +126,23 @@ public class DocumentsStorageProvider extends DocumentsProvider {
                 file = mCurrentStorageManager.getFileById(docId);
 
             } while (!file.isDown());
+        } else {
+            FileDataStorageManager storageManager =
+                    new FileDataStorageManager(account, context.getContentResolver());
+            SynchronizeFileOperation sfo =
+                    new SynchronizeFileOperation(file, null, account, true, context);
+            RemoteOperationResult result = sfo.execute(storageManager, context);
+            if (result.getCode() == RemoteOperationResult.ResultCode.SYNC_CONFLICT) {
+                // ISSUE 5: if the user is not running the app (this is a service!),
+                // this can be very intrusive; a notification should be preferred
+                Intent i = new Intent(context, ConflictsResolveActivity.class);
+                i.setFlags(i.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
+                i.putExtra(ConflictsResolveActivity.EXTRA_FILE, file);
+                i.putExtra(ConflictsResolveActivity.EXTRA_ACCOUNT, account);
+                context.startActivity(i);
+            } else {
+                FileStorageUtils.checkIfFileFinishedSaving(file);
+            }
         }
 
         return ParcelFileDescriptor.open(new File(file.getStoragePath()), ParcelFileDescriptor.parseMode(mode));

+ 77 - 28
src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java

@@ -87,6 +87,7 @@ import com.owncloud.android.operations.UploadFileOperation;
 import com.owncloud.android.services.observer.FileObserverService;
 import com.owncloud.android.syncadapter.FileSyncAdapter;
 import com.owncloud.android.ui.dialog.SortingOrderDialogFragment;
+import com.owncloud.android.ui.events.SyncEventFinished;
 import com.owncloud.android.ui.events.TokenPushEvent;
 import com.owncloud.android.ui.fragment.ExtendedListFragment;
 import com.owncloud.android.ui.fragment.FileDetailFragment;
@@ -94,6 +95,7 @@ import com.owncloud.android.ui.fragment.FileFragment;
 import com.owncloud.android.ui.fragment.OCFileListFragment;
 import com.owncloud.android.ui.fragment.TaskRetainerFragment;
 import com.owncloud.android.ui.fragment.contactsbackup.ContactListFragment;
+import com.owncloud.android.ui.helpers.FileOperationsHelper;
 import com.owncloud.android.ui.helpers.UriUploader;
 import com.owncloud.android.ui.preview.PreviewImageActivity;
 import com.owncloud.android.ui.preview.PreviewImageFragment;
@@ -160,6 +162,8 @@ public class FileDisplayActivity extends HookActivity
     private static final String TAG_LIST_OF_FILES = "LIST_OF_FILES";
     public static final String TAG_SECOND_FRAGMENT = "SECOND_FRAGMENT";
 
+    public static final String TEXT_PREVIEW = "TEXT_PREVIEW";
+
     private OCFile mWaitingToPreview;
 
     private boolean mSyncInProgress = false;
@@ -465,7 +469,7 @@ public class FileDisplayActivity extends HookActivity
                 if (file.isDown() && MimeTypeUtil.isVCard(file.getMimetype())) {
                     startContactListFragment(file);
                 } else if (file.isDown() && PreviewTextFragment.canBePreviewed(file)) {
-                    startTextPreview(file);
+                    startTextPreview(file, false);
                 }
             }
 
@@ -630,13 +634,13 @@ public class FileDisplayActivity extends HookActivity
                                 mWaitingToPreview.getFileId());   // update the file from database,
                         // for the local storage path
                         if (PreviewMediaFragment.canBePreviewed(mWaitingToPreview)) {
-                            startMediaPreview(mWaitingToPreview, 0, true);
+                            startMediaPreview(mWaitingToPreview, 0, true, true);
                             detailsFragmentChanged = true;
                         } else if (MimeTypeUtil.isVCard(mWaitingToPreview.getMimetype())) {
                             startContactListFragment(mWaitingToPreview);
                             detailsFragmentChanged = true;
                         } else if (PreviewTextFragment.canBePreviewed(mWaitingToPreview)) {
-                            startTextPreview(mWaitingToPreview);
+                            startTextPreview(mWaitingToPreview, true);
                             detailsFragmentChanged = true;
                         } else {
                             getFileOperationsHelper().openFile(mWaitingToPreview);
@@ -1043,7 +1047,7 @@ public class FileDisplayActivity extends HookActivity
             if ((getIntent().getAction() != null && getIntent().getAction().equalsIgnoreCase(ACTION_DETAILS)) && !mDualPane) {
                 getIntent().setAction(null);
                 getIntent().putExtra(EXTRA_FILE, (OCFile) null);
-                startImagePreview(getFile());
+                startImagePreview(getFile(), false);
             }
 
             OCFileListFragment listOfFiles = getListOfFilesFragment();
@@ -1381,9 +1385,9 @@ public class FileDisplayActivity extends HookActivity
                     if (uploadWasFine) {
                         OCFile ocFile = getFile();
                         if (PreviewImageFragment.canBePreviewed(ocFile)) {
-                            startImagePreview(getFile());
+                            startImagePreview(getFile(), true);
                         } else if (PreviewTextFragment.canBePreviewed(ocFile)) {
-                            startTextPreview(ocFile);
+                            startTextPreview(ocFile, true);
                         }
                         // TODO what about other kind of previews?
                     }
@@ -1804,7 +1808,7 @@ public class FileDisplayActivity extends HookActivity
                     ((PreviewMediaFragment) details).updateFile(renamedFile);
                     if (PreviewMediaFragment.canBePreviewed(renamedFile)) {
                         int position = ((PreviewMediaFragment) details).getPosition();
-                        startMediaPreview(renamedFile, position, true);
+                        startMediaPreview(renamedFile, position, true, true);
                     } else {
                         getFileOperationsHelper().openFile(renamedFile);
                     }
@@ -1812,7 +1816,7 @@ public class FileDisplayActivity extends HookActivity
                         renamedFile.equals(details.getFile())) {
                     ((PreviewTextFragment) details).updateFile(renamedFile);
                     if (PreviewTextFragment.canBePreviewed(renamedFile)) {
-                        startTextPreview(renamedFile);
+                        startTextPreview(renamedFile, true);
                     } else {
                         getFileOperationsHelper().openFile(renamedFile);
                     }
@@ -2007,11 +2011,16 @@ public class FileDisplayActivity extends HookActivity
      *
      * @param file Image {@link OCFile} to show.
      */
-    public void startImagePreview(OCFile file) {
+    public void startImagePreview(OCFile file, boolean showPreview) {
         Intent showDetailsIntent = new Intent(this, PreviewImageActivity.class);
         showDetailsIntent.putExtra(EXTRA_FILE, file);
         showDetailsIntent.putExtra(EXTRA_ACCOUNT, getAccount());
-        startActivity(showDetailsIntent);
+        if (showPreview && file.isDown() && !file.isDownloading()) {
+            startActivity(showDetailsIntent);
+        } else {
+            FileOperationsHelper fileOperationsHelper = new FileOperationsHelper(this);
+            fileOperationsHelper.startSyncForFileAndIntent(file, showDetailsIntent);
+        }
     }
 
     /**
@@ -2019,13 +2028,18 @@ public class FileDisplayActivity extends HookActivity
      *
      * @param file Image {@link OCFile} to show.
      */
-    public void startImagePreview(OCFile file, VirtualFolderType type) {
+    public void startImagePreview(OCFile file, VirtualFolderType type, boolean showPreview) {
         Intent showDetailsIntent = new Intent(this, PreviewImageActivity.class);
         showDetailsIntent.putExtra(PreviewImageActivity.EXTRA_FILE, file);
         showDetailsIntent.putExtra(EXTRA_ACCOUNT, getAccount());
         showDetailsIntent.putExtra(PreviewImageActivity.EXTRA_VIRTUAL_TYPE, type);
-        startActivity(showDetailsIntent);
 
+        if (showPreview && file.isDown() && !file.isDownloading()) {
+            startActivity(showDetailsIntent);
+        } else {
+            FileOperationsHelper fileOperationsHelper = new FileOperationsHelper(this);
+            fileOperationsHelper.startSyncForFileAndIntent(file, showDetailsIntent);
+        }
     }
 
     /**
@@ -2037,12 +2051,21 @@ public class FileDisplayActivity extends HookActivity
      * @param autoplay              When 'true', the playback will start without user
      *                              interactions.
      */
-    public void startMediaPreview(OCFile file, int startPlaybackPosition, boolean autoplay) {
-        Fragment mediaFragment = PreviewMediaFragment.newInstance(file, getAccount(), startPlaybackPosition, autoplay);
-        setSecondFragment(mediaFragment);
-        updateFragmentsVisibility(true);
-        updateActionBarTitleAndHomeButton(file);
-        setFile(file);
+    public void startMediaPreview(OCFile file, int startPlaybackPosition, boolean autoplay, boolean showPreview) {
+        if (showPreview && file.isDown() && !file.isDownloading()) {
+            Fragment mediaFragment = PreviewMediaFragment.newInstance(file, getAccount(), startPlaybackPosition, autoplay);
+            setSecondFragment(mediaFragment);
+            updateFragmentsVisibility(true);
+            updateActionBarTitleAndHomeButton(file);
+            setFile(file);
+        } else {
+            Intent previewIntent = new Intent();
+            previewIntent.putExtra(EXTRA_FILE, file);
+            previewIntent.putExtra(PreviewVideoActivity.EXTRA_START_POSITION, startPlaybackPosition);
+            previewIntent.putExtra(PreviewVideoActivity.EXTRA_AUTOPLAY, autoplay);
+            FileOperationsHelper fileOperationsHelper = new FileOperationsHelper(this);
+            fileOperationsHelper.startSyncForFileAndIntent(file, previewIntent);
+        }
     }
 
     /**
@@ -2050,16 +2073,24 @@ public class FileDisplayActivity extends HookActivity
      *
      * @param file Text {@link OCFile} to preview.
      */
-    public void startTextPreview(OCFile file) {
-        Bundle args = new Bundle();
-        args.putParcelable(EXTRA_FILE, file);
-        args.putParcelable(EXTRA_ACCOUNT, getAccount());
-        Fragment textPreviewFragment = Fragment.instantiate(getApplicationContext(),
-                PreviewTextFragment.class.getName(), args);
-        setSecondFragment(textPreviewFragment);
-        updateFragmentsVisibility(true);
-        updateActionBarTitleAndHomeButton(file);
-        setFile(file);
+    public void startTextPreview(OCFile file, boolean showPreview) {
+        if (showPreview && file.isDown() && !file.isDownloading()) {
+            Bundle args = new Bundle();
+            args.putParcelable(EXTRA_FILE, file);
+            args.putParcelable(EXTRA_ACCOUNT, getAccount());
+            Fragment textPreviewFragment = Fragment.instantiate(getApplicationContext(),
+                    PreviewTextFragment.class.getName(), args);
+            setSecondFragment(textPreviewFragment);
+            updateFragmentsVisibility(true);
+            updateActionBarTitleAndHomeButton(file);
+            setFile(file);
+        } else {
+            Intent previewIntent = new Intent();
+            previewIntent.putExtra(EXTRA_FILE, file);
+            previewIntent.putExtra(TEXT_PREVIEW, true);
+            FileOperationsHelper fileOperationsHelper = new FileOperationsHelper(this);
+            fileOperationsHelper.startSyncForFileAndIntent(file, previewIntent);
+        }
     }
 
     public void startContactListFragment(OCFile file) {
@@ -2164,6 +2195,24 @@ public class FileDisplayActivity extends HookActivity
         getListOfFilesFragment().refreshDirectory();
     }
 
+    @Subscribe(threadMode = ThreadMode.MAIN)
+    public void onMessageEvent(SyncEventFinished event) {
+        Bundle bundle = event.getIntent().getExtras();
+        if (event.getIntent().getBooleanExtra(TEXT_PREVIEW, false)) {
+            startTextPreview((OCFile) bundle.get(EXTRA_FILE), true);
+        } else if (bundle.containsKey(PreviewVideoActivity.EXTRA_START_POSITION)) {
+            startMediaPreview((OCFile)bundle.get(EXTRA_FILE),
+                    (int)bundle.get(PreviewVideoActivity.EXTRA_START_POSITION),
+                    (boolean)bundle.get(PreviewVideoActivity.EXTRA_AUTOPLAY), true);
+        } else if (bundle.containsKey(PreviewImageActivity.EXTRA_VIRTUAL_TYPE)) {
+            startImagePreview((OCFile)bundle.get(EXTRA_FILE),
+                    (VirtualFolderType)bundle.get(PreviewImageActivity.EXTRA_VIRTUAL_TYPE),
+                    true);
+        } else {
+            startImagePreview((OCFile)bundle.get(EXTRA_FILE),true);
+        }
+    }
+
     @Subscribe(threadMode = ThreadMode.BACKGROUND)
     public void onMessageEvent(TokenPushEvent event) {
         PushUtils.pushRegistrationToServer();

+ 44 - 0
src/main/java/com/owncloud/android/ui/events/SyncEventFinished.java

@@ -0,0 +1,44 @@
+/**
+ * Nextcloud Android client application
+ *
+ * @author Mario Danic
+ * Copyright (C) 2017 Mario Danic
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.owncloud.android.ui.events;
+
+import android.content.Intent;
+
+import org.parceler.Parcel;
+
+@Parcel
+public class SyncEventFinished {
+    public Intent intent;
+
+    public SyncEventFinished(Intent intent) {
+        this.intent = intent;
+    }
+
+    public SyncEventFinished() {
+    }
+
+    public Intent getIntent() {
+        return intent;
+    }
+
+    public void setIntent(Intent intent) {
+        this.intent = intent;
+    }
+}

+ 5 - 4
src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java

@@ -901,18 +901,19 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
                                 type = VirtualFolderType.NONE;
                                 break;
                         }
-                        ((FileDisplayActivity) mContainerActivity).startImagePreview(file, type);
+                        ((FileDisplayActivity) mContainerActivity).startImagePreview(file, type, false);
                     } else {
-                        ((FileDisplayActivity) mContainerActivity).startImagePreview(file);
+                        ((FileDisplayActivity) mContainerActivity).startImagePreview(file, false);
                     }
                 } else if (file.isDown() && MimeTypeUtil.isVCard(file)) {
                     ((FileDisplayActivity) mContainerActivity).startContactListFragment(file);
                 } else if (PreviewTextFragment.canBePreviewed(file)) {
-                    ((FileDisplayActivity) mContainerActivity).startTextPreview(file);
+                    ((FileDisplayActivity) mContainerActivity).startTextPreview(file, false);
                 } else if (file.isDown()) {
                     if (PreviewMediaFragment.canBePreviewed(file)) {
                         // media preview
-                        ((FileDisplayActivity) mContainerActivity).startMediaPreview(file, 0, true);
+                        ((FileDisplayActivity) mContainerActivity).startMediaPreview(file, 0,
+                                true, false);
                     } else {
                         mContainerActivity.getFileOperationsHelper().openFile(file);
                     }

+ 110 - 44
src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java

@@ -37,18 +37,24 @@ import android.widget.Toast;
 import com.owncloud.android.MainApp;
 import com.owncloud.android.R;
 import com.owncloud.android.authentication.AccountUtils;
+import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
 import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
+import com.owncloud.android.lib.common.operations.RemoteOperationResult;
 import com.owncloud.android.lib.common.utils.Log_OC;
 import com.owncloud.android.lib.resources.shares.OCShare;
 import com.owncloud.android.lib.resources.shares.ShareType;
 import com.owncloud.android.lib.resources.status.OwnCloudVersion;
+import com.owncloud.android.operations.SynchronizeFileOperation;
 import com.owncloud.android.services.OperationsService;
 import com.owncloud.android.services.observer.FileObserverService;
+import com.owncloud.android.ui.activity.ConflictsResolveActivity;
 import com.owncloud.android.ui.activity.FileActivity;
 import com.owncloud.android.ui.activity.ShareActivity;
 import com.owncloud.android.ui.events.FavoriteEvent;
+import com.owncloud.android.ui.events.SyncEventFinished;
+import com.owncloud.android.utils.FileStorageUtils;
 import com.owncloud.android.utils.DisplayUtils;
 
 import org.greenrobot.eventbus.EventBus;
@@ -70,14 +76,11 @@ import java.util.regex.Pattern;
 public class FileOperationsHelper {
 
     private static final String TAG = FileOperationsHelper.class.getSimpleName();
-
-    private FileActivity mFileActivity = null;
-
-    /// Identifier of operation in progress which result shouldn't be lost 
-    private long mWaitingForOpId = Long.MAX_VALUE;
-
     private static final Pattern mPatternUrl = Pattern.compile("^URL=(.+)$");
     private static final Pattern mPatternString = Pattern.compile("<string>(.+)</string>");
+    private FileActivity mFileActivity = null;
+    /// Identifier of operation in progress which result shouldn't be lost
+    private long mWaitingForOpId = Long.MAX_VALUE;
 
     public FileOperationsHelper(FileActivity fileActivity) {
         mFileActivity = fileActivity;
@@ -144,6 +147,35 @@ public class FileOperationsHelper {
         return new Intent(Intent.ACTION_VIEW, Uri.parse(url));
     }
 
+
+    public void startSyncForFileAndIntent(OCFile file, Intent intent) {
+        mFileActivity.showLoadingDialog(mFileActivity.getResources().getString(R.string.sync_in_progress));
+        new Thread(new Runnable() {
+            @Override
+            public void run() {
+                Account account = AccountUtils.getCurrentOwnCloudAccount(mFileActivity);
+                FileDataStorageManager storageManager =
+                        new FileDataStorageManager(account, mFileActivity.getContentResolver());
+                SynchronizeFileOperation sfo =
+                        new SynchronizeFileOperation(file, null, account, true, mFileActivity);
+                RemoteOperationResult result = sfo.execute(storageManager, mFileActivity);
+                if (result.getCode() == RemoteOperationResult.ResultCode.SYNC_CONFLICT) {
+                    // ISSUE 5: if the user is not running the app (this is a service!),
+                    // this can be very intrusive; a notification should be preferred
+                    Intent i = new Intent(mFileActivity, ConflictsResolveActivity.class);
+                    i.setFlags(i.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
+                    i.putExtra(ConflictsResolveActivity.EXTRA_FILE, file);
+                    i.putExtra(ConflictsResolveActivity.EXTRA_ACCOUNT, account);
+                    mFileActivity.startActivity(i);
+                } else {
+                    FileStorageUtils.checkIfFileFinishedSaving(file);
+                    EventBus.getDefault().post(new SyncEventFinished(intent));
+                }
+                mFileActivity.dismissLoadingDialog();
+            }
+        }).start();
+
+    }
     public void openFile(OCFile file) {
         if (file != null) {
             String storagePath = file.getStoragePath();
@@ -179,15 +211,48 @@ public class FileOperationsHelper {
             List<ResolveInfo> launchables = mFileActivity.getPackageManager().
                     queryIntentActivities(openFileWithIntent, PackageManager.GET_RESOLVED_FILTER);
 
-            if (launchables != null && launchables.size() > 0) {
-                try {
-                    mFileActivity.startActivity(openFileWithIntent);
-                } catch (ActivityNotFoundException anfe) {
-                    DisplayUtils.showSnackMessage(mFileActivity, R.string.file_list_no_app_for_file_type);
+            mFileActivity.showLoadingDialog(mFileActivity.getResources().getString(R.string.sync_in_progress));
+            Intent finalOpenFileWithIntent = openFileWithIntent;
+            new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    Account account = AccountUtils.getCurrentOwnCloudAccount(mFileActivity);
+                    FileDataStorageManager storageManager =
+                            new FileDataStorageManager(account, mFileActivity.getContentResolver());
+                    // a fresh object is needed; many things could have occurred to the file
+                    // since it was registered to observe again, assuming that local files
+                    // are linked to a remote file AT MOST, SOMETHING TO BE DONE;
+                    SynchronizeFileOperation sfo =
+                            new SynchronizeFileOperation(file, null, account, true, mFileActivity);
+                    RemoteOperationResult result = sfo.execute(storageManager, mFileActivity);
+                    mFileActivity.dismissLoadingDialog();
+                    if (result.getCode() == RemoteOperationResult.ResultCode.SYNC_CONFLICT) {
+                        // ISSUE 5: if the user is not running the app (this is a service!),
+                        // this can be very intrusive; a notification should be preferred
+                        Intent i = new Intent(mFileActivity, ConflictsResolveActivity.class);
+                        i.setFlags(i.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
+                        i.putExtra(ConflictsResolveActivity.EXTRA_FILE, file);
+                        i.putExtra(ConflictsResolveActivity.EXTRA_ACCOUNT, account);
+                        mFileActivity.startActivity(i);
+                    } else {
+                        if (launchables != null && launchables.size() > 0) {
+                            try {
+                                mFileActivity.startActivity(
+                                        Intent.createChooser(
+                                                finalOpenFileWithIntent,
+                                                mFileActivity.getString(R.string.actionbar_open_with)
+                                        )
+                                );
+                            } catch (ActivityNotFoundException anfe) {
+                                DisplayUtils.showSnackMessage(mFileActivity, R.string.file_list_no_app_for_file_type);
+                            }
+                        } else {
+                            DisplayUtils.showSnackMessage(mFileActivity, R.string.file_list_no_app_for_file_type);
+                        }
+                    }
+
                 }
-            } else {
-                DisplayUtils.showSnackMessage(mFileActivity, R.string.file_list_no_app_for_file_type);
-            }
+            }).start();
 
         } else {
             Log_OC.e(TAG, "Trying to open a NULL OCFile");
@@ -197,8 +262,8 @@ public class FileOperationsHelper {
     /**
      * Helper method to share a file via a public link. Starts a request to do it in {@link OperationsService}
      *
-     * @param file          The file to share.
-     * @param password      Optional password to protect the public share.
+     * @param file     The file to share.
+     * @param password Optional password to protect the public share.
      */
     public void shareFileViaLink(OCFile file, String password) {
         if (isSharedSupported()) {
@@ -248,10 +313,10 @@ public class FileOperationsHelper {
     /**
      * Helper method to share a file with a known sharee. Starts a request to do it in {@link OperationsService}
      *
-     * @param file          The file to share.
-     * @param shareeName    Name (user name or group name) of the target sharee.
-     * @param shareType     The share type determines the sharee type.
-     * @param permissions   Permissions to grant to sharee on the shared file.
+     * @param file        The file to share.
+     * @param shareeName  Name (user name or group name) of the target sharee.
+     * @param shareType   The share type determines the sharee type.
+     * @param permissions Permissions to grant to sharee on the shared file.
      */
     public void shareFileWithSharee(OCFile file, String shareeName, ShareType shareType, int permissions) {
         if (file != null) {
@@ -290,7 +355,7 @@ public class FileOperationsHelper {
      * Helper method to unshare a file publicly shared via link.
      * Starts a request to do it in {@link OperationsService}
      *
-     * @param file      The file to unshare.
+     * @param file The file to unshare.
      */
     public void unshareFileViaLink(OCFile file) {
 
@@ -337,7 +402,7 @@ public class FileOperationsHelper {
     /**
      * Show an instance of {@link ShareType} for sharing or unsharing the {@link OCFile} received as parameter.
      *
-     * @param file  File to share or unshare.
+     * @param file File to share or unshare.
      */
     public void showShareFile(OCFile file) {
         Intent intent = new Intent(mFileActivity, ShareActivity.class);
@@ -351,9 +416,9 @@ public class FileOperationsHelper {
      * Updates a public share on a file to set its password.
      * Starts a request to do it in {@link OperationsService}
      *
-     * @param file          File which public share will be protected with a password.
-     * @param password      Password to set for the public link; null or empty string to clear
-     *                      the current password
+     * @param file     File which public share will be protected with a password.
+     * @param password Password to set for the public link; null or empty string to clear
+     *                 the current password
      */
     public void setPasswordToShareViaLink(OCFile file, String password) {
         // Set password updating share
@@ -374,9 +439,9 @@ public class FileOperationsHelper {
      * Updates a public share on a file to set its expiration date.
      * Starts a request to do it in {@link OperationsService}
      *
-     * @param file                      File which public share will be constrained with an expiration date.
-     * @param expirationTimeInMillis    Expiration date to set. A negative value clears the current expiration
-     *                                  date, leaving the link unrestricted. Zero makes no change.
+     * @param file                   File which public share will be constrained with an expiration date.
+     * @param expirationTimeInMillis Expiration date to set. A negative value clears the current expiration
+     *                               date, leaving the link unrestricted. Zero makes no change.
      */
     public void setExpirationDateToShareViaLink(OCFile file, long expirationTimeInMillis) {
         Intent updateShareIntent = new Intent(mFileActivity, OperationsService.class);
@@ -395,8 +460,8 @@ public class FileOperationsHelper {
      * Updates a share on a file to set its access permissions.
      * Starts a request to do it in {@link OperationsService}
      *
-     * @param share                     {@link OCShare} instance which permissions will be updated.
-     * @param permissions               New permissions to set. A value <= 0 makes no update.
+     * @param share       {@link OCShare} instance which permissions will be updated.
+     * @param permissions New permissions to set. A value <= 0 makes no update.
      */
     public void setPermissionsToShare(OCShare share, int permissions) {
         Intent updateShareIntent = new Intent(mFileActivity, OperationsService.class);
@@ -414,8 +479,8 @@ public class FileOperationsHelper {
      * Updates a public share on a folder to set its editing permission.
      * Starts a request to do it in {@link OperationsService}
      *
-     * @param folder                     Folder which editing permission of his public share will be modified.
-     * @param uploadPermission          New state of the permission for editing the folder shared via link.
+     * @param folder           Folder which editing permission of his public share will be modified.
+     * @param uploadPermission New state of the permission for editing the folder shared via link.
      */
     public void setUploadPermissionsToShare(OCFile folder, boolean uploadPermission) {
         Intent updateShareIntent = new Intent(mFileActivity, OperationsService.class);
@@ -433,8 +498,8 @@ public class FileOperationsHelper {
      * Updates a public share on a folder to set its hide file listing permission.
      * Starts a request to do it in {@link OperationsService}
      *
-     * @param share                    {@link OCShare} instance which permissions will be updated.
-     * @param hideFileListing          New state of the permission for editing the folder shared via link.
+     * @param share           {@link OCShare} instance which permissions will be updated.
+     * @param hideFileListing New state of the permission for editing the folder shared via link.
      */
     public void setHideFileListingPermissionsToShare(OCShare share, boolean hideFileListing) {
         Intent updateShareIntent = new Intent(mFileActivity, OperationsService.class);
@@ -548,7 +613,7 @@ public class FileOperationsHelper {
     /**
      * Request the synchronization of a file or folder with the OC server, including its contents.
      *
-     * @param file          The file or folder to synchronize
+     * @param file The file or folder to synchronize
      */
     public void syncFile(OCFile file) {
         if (!file.isFolder()) {
@@ -645,9 +710,9 @@ public class FileOperationsHelper {
     /**
      * Start operations to delete one or several files
      *
-     * @param files             Files to delete
-     * @param onlyLocalCopy     When 'true' only local copy of the files is removed; otherwise files are also deleted
-     *                          in the server.
+     * @param files         Files to delete
+     * @param onlyLocalCopy When 'true' only local copy of the files is removed; otherwise files are also deleted
+     *                      in the server.
      */
     public void removeFiles(Collection<OCFile> files, boolean onlyLocalCopy) {
         for (OCFile file : files) {
@@ -678,6 +743,7 @@ public class FileOperationsHelper {
 
     /**
      * Cancel the transference in downloads (files/folders) and file uploads
+     *
      * @param file OCFile
      */
     public void cancelTransference(OCFile file) {
@@ -704,8 +770,8 @@ public class FileOperationsHelper {
     /**
      * Start operations to move one or several files
      *
-     * @param files            Files to move
-     * @param targetFolder     Folder where the files while be moved into
+     * @param files        Files to move
+     * @param targetFolder Folder where the files while be moved into
      */
     public void moveFiles(Collection<OCFile> files, OCFile targetFolder) {
         for (OCFile file : files) {
@@ -722,8 +788,8 @@ public class FileOperationsHelper {
     /**
      * Start operations to copy one or several files
      *
-     * @param files            Files to copy
-     * @param targetFolder     Folder where the files while be copied into
+     * @param files        Files to copy
+     * @param targetFolder Folder where the files while be copied into
      */
     public void copyFiles(Collection<OCFile> files, OCFile targetFolder) {
         for (OCFile file : files) {
@@ -747,7 +813,7 @@ public class FileOperationsHelper {
     }
 
     /**
-     *  @return 'True' if the server doesn't need to check forbidden characters
+     * @return 'True' if the server doesn't need to check forbidden characters
      */
     public boolean isVersionWithForbiddenCharacters() {
         if (mFileActivity.getAccount() != null) {
@@ -761,7 +827,7 @@ public class FileOperationsHelper {
     /**
      * Starts a check of the currently stored credentials for the given account.
      *
-     * @param account       OC account which credentials will be checked.
+     * @param account OC account which credentials will be checked.
      */
     public void checkCurrentCredentials(Account account) {
         Intent service = new Intent(mFileActivity, OperationsService.class);

+ 17 - 0
src/main/java/com/owncloud/android/utils/FileStorageUtils.java

@@ -24,6 +24,7 @@ import android.content.Context;
 import android.content.SharedPreferences;
 import android.net.Uri;
 import android.preference.PreferenceManager;
+import android.util.Log;
 import android.webkit.MimeTypeMap;
 
 import com.owncloud.android.MainApp;
@@ -591,4 +592,20 @@ public class FileStorageUtils {
         file.delete();
     }
 
+    public static boolean checkIfFileFinishedSaving(OCFile file) {
+        long lastModified = 0;
+        long lastSize = 0;
+        File realFile = new File(file.getStoragePath());
+        while ((realFile.lastModified() != lastModified) && (realFile.length() != lastSize)) {
+            lastModified = realFile.lastModified();
+            lastSize = realFile.length();
+            try {
+                Thread.sleep(1000);
+            } catch (InterruptedException e) {
+                Log.d(TAG, "Failed to sleep for a bit");
+            }
+        }
+
+        return true;
+    }
 }

+ 2 - 0
src/main/res/values/strings.xml

@@ -708,6 +708,8 @@
     <string name="push_notifications_not_implemented">Push notifications disabled due to dependencies on proprietary Google Play services.</string>
     <string name="push_notifications_old_login">No push notifications due to outdated login session. Please consider re-adding your account.</string>
     <string name="push_notifications_temp_error">Push notifications currently not available.</string>
+
+    <string name="sync_in_progress">Please wait while we fetch the newest version of the file.</string>
     <string name="date_unknown">Unknown</string>
 
     <string name="resized_image_not_possible">No resized image available. Download full image?</string>