Преглед на файлове

Merge pull request #3464 from nextcloud/chunkedUpload

Chunked upload
Tobias Kaminsky преди 6 години
родител
ревизия
ba81710bc3

+ 1 - 0
src/androidTest/java/com/owncloud/android/AbstractIT.java

@@ -52,6 +52,7 @@ public abstract class AbstractIT {
                         Integer.toString(com.owncloud.android.authentication.AccountUtils.ACCOUNT_VERSION));
                 accountManager.setUserData(temp, AccountUtils.Constants.KEY_OC_VERSION, "14.0.0.0");
                 accountManager.setUserData(temp, AccountUtils.Constants.KEY_OC_BASE_URL, "http://" + baseUrl);
+                accountManager.setUserData(temp, AccountUtils.Constants.KEY_USER_ID, username);
             }
 
             account = com.owncloud.android.authentication.AccountUtils.getOwnCloudAccountByName(context,

+ 145 - 1
src/main/java/com/owncloud/android/authentication/AccountUtils.java

@@ -23,12 +23,20 @@ import android.accounts.Account;
 import android.accounts.AccountManager;
 import android.content.Context;
 import android.content.SharedPreferences;
+import android.net.Uri;
 import android.preference.PreferenceManager;
 
 import com.owncloud.android.MainApp;
 import com.owncloud.android.datamodel.ArbitraryDataProvider;
+import com.owncloud.android.lib.common.OwnCloudAccount;
+import com.owncloud.android.lib.common.OwnCloudClient;
+import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
+import com.owncloud.android.lib.common.UserInfo;
 import com.owncloud.android.lib.common.accounts.AccountUtils.Constants;
+import com.owncloud.android.lib.common.operations.RemoteOperationResult;
+import com.owncloud.android.lib.common.utils.Log_OC;
 import com.owncloud.android.lib.resources.status.OwnCloudVersion;
+import com.owncloud.android.lib.resources.users.GetRemoteUserInfoOperation;
 import com.owncloud.android.ui.activity.ManageAccountsActivity;
 
 import androidx.annotation.NonNull;
@@ -38,10 +46,11 @@ import androidx.annotation.Nullable;
  * Helper class for dealing with accounts.
  */
 public final class AccountUtils {
+    private static final String TAG = AccountUtils.class.getSimpleName();
     private static final String PREF_SELECT_OC_ACCOUNT = "select_oc_account";
 
     public static final int ACCOUNT_VERSION = 1;
-    //public static final int ACCOUNT_VERSION_WITH_PROPER_ID = 2;
+    public static final int ACCOUNT_VERSION_WITH_PROPER_ID = 2;
     public static final String ACCOUNT_USES_STANDARD_PASSWORD = "ACCOUNT_USES_STANDARD_PASSWORD";
 
     private AccountUtils() {
@@ -200,4 +209,139 @@ public final class AccountUtils {
     public static boolean hasSearchSupport(Account account) {
         return getServerVersion(account).isSearchSupported();
     }
+
+    /**
+     * Update the accounts in AccountManager to meet the current version of accounts expected by the app, if needed.
+     * <p>
+     * Introduced to handle a change in the structure of stored account names needed to allow different OC servers in
+     * the same domain, but not in the same path.
+     *
+     * @param context Used to access the AccountManager.
+     */
+    public static void updateAccountVersion(Context context) {
+        Account currentAccount = AccountUtils.getCurrentOwnCloudAccount(context);
+        AccountManager accountMgr = AccountManager.get(context);
+
+        if (currentAccount != null) {
+            String currentAccountVersion = accountMgr.getUserData(currentAccount, Constants.KEY_OC_ACCOUNT_VERSION);
+
+            if (!String.valueOf(ACCOUNT_VERSION_WITH_PROPER_ID).equalsIgnoreCase(currentAccountVersion)) {
+                Log_OC.i(TAG, "Upgrading accounts to account version #" + ACCOUNT_VERSION_WITH_PROPER_ID);
+
+                Account[] ocAccounts = accountMgr.getAccountsByType(MainApp.getAccountType(context));
+                String serverUrl;
+                String username;
+                String displayName;
+                String newAccountName;
+                Account newAccount;
+                GetRemoteUserInfoOperation remoteUserNameOperation = new GetRemoteUserInfoOperation();
+
+                for (Account account : ocAccounts) {
+                    // build new account name
+                    serverUrl = accountMgr.getUserData(account, Constants.KEY_OC_BASE_URL);
+
+                    // update user name
+                    try {
+                        OwnCloudAccount ocAccount = new OwnCloudAccount(account, context);
+                        OwnCloudClient client = OwnCloudClientManagerFactory.getDefaultSingleton()
+                            .getClientFor(ocAccount, context);
+
+                        RemoteOperationResult result = remoteUserNameOperation.execute(client);
+
+                        if (result.isSuccess()) {
+                            UserInfo userInfo = (UserInfo) result.getData().get(0);
+                            username = userInfo.id;
+                            displayName = userInfo.displayName;
+                        } else {
+                            // skip account, try it next time
+                            Log_OC.e(TAG, "Error while getting username for account: " + account.name);
+                            continue;
+                        }
+                    } catch (Exception e) {
+                        Log_OC.e(TAG, "Error while getting username: " + e.getMessage());
+                        continue;
+                    }
+
+                    newAccountName = com.owncloud.android.lib.common.accounts.AccountUtils.
+                        buildAccountName(Uri.parse(serverUrl), username);
+
+                    // migrate to a new account, if needed
+                    if (!newAccountName.equals(account.name)) {
+                        newAccount = migrateAccount(context, currentAccount, accountMgr, serverUrl, newAccountName,
+                                                    account);
+
+                    } else {
+                        // servers which base URL is in the root of their domain need no change
+                        Log_OC.d(TAG, account.name + " needs no upgrade ");
+                        newAccount = account;
+                    }
+
+                    accountMgr.setUserData(newAccount, Constants.KEY_DISPLAY_NAME, displayName);
+                    accountMgr.setUserData(newAccount, Constants.KEY_USER_ID, username);
+
+                    // at least, upgrade account version
+                    Log_OC.d(TAG, "Setting version " + ACCOUNT_VERSION_WITH_PROPER_ID + " to " + newAccountName);
+                    accountMgr.setUserData(newAccount, Constants.KEY_OC_ACCOUNT_VERSION,
+                                           Integer.toString(ACCOUNT_VERSION_WITH_PROPER_ID));
+                }
+            }
+        }
+    }
+
+    @NonNull
+    private static Account migrateAccount(Context context, Account currentAccount, AccountManager accountMgr,
+                                          String serverUrl, String newAccountName, Account account) {
+
+        Log_OC.d(TAG, "Upgrading " + account.name + " to " + newAccountName);
+
+        // create the new account
+        Account newAccount = new Account(newAccountName, MainApp.getAccountType(context));
+        String password = accountMgr.getPassword(account);
+        accountMgr.addAccountExplicitly(newAccount, (password != null) ? password : "", null);
+
+        // copy base URL
+        accountMgr.setUserData(newAccount, Constants.KEY_OC_BASE_URL, serverUrl);
+
+        // copy server version
+        accountMgr.setUserData(
+            newAccount,
+            Constants.KEY_OC_VERSION,
+            accountMgr.getUserData(account, Constants.KEY_OC_VERSION)
+        );
+
+        // copy cookies
+        accountMgr.setUserData(
+            newAccount,
+            Constants.KEY_COOKIES,
+            accountMgr.getUserData(account, Constants.KEY_COOKIES)
+        );
+
+        // copy type of authentication
+        final String isSamlStr = accountMgr.getUserData(account, Constants.KEY_SUPPORTS_SAML_WEB_SSO);
+        if (Boolean.parseBoolean(isSamlStr)) {
+            accountMgr.setUserData(newAccount, Constants.KEY_SUPPORTS_SAML_WEB_SSO, "TRUE");
+        }
+
+        final String isOauthStr = accountMgr.getUserData(account, Constants.KEY_SUPPORTS_OAUTH2);
+        if (Boolean.parseBoolean(isOauthStr)) {
+            accountMgr.setUserData(newAccount, Constants.KEY_SUPPORTS_OAUTH2, "TRUE");
+        }
+
+        /* TODO - study if it's possible to run this method in a background thread to copy the authToken
+        if (isOAuth || isSaml) {
+            accountMgr.setAuthToken(newAccount, mAuthTokenType, mAuthToken);
+        }
+        */
+
+        // don't forget the account saved in preferences as the current one
+        if (currentAccount.name.equals(account.name)) {
+            AccountUtils.setCurrentOwnCloudAccount(context, newAccountName);
+        }
+
+        // remove the old account
+        accountMgr.removeAccount(account, null, null);
+
+        // will assume it succeeds, not a big deal otherwise
+        return newAccount;
+    }
 }

+ 9 - 6
src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java

@@ -1963,10 +1963,6 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
                 }
             }
 
-            // include account version with the new account
-            mAccountMgr.setUserData(mAccount, Constants.KEY_OC_ACCOUNT_VERSION,
-                    Integer.toString(AccountUtils.ACCOUNT_VERSION));
-
             /// add the new account as default in preferences, if there is none already
             Account defaultAccount = AccountUtils.getCurrentOwnCloudAccount(this);
             if (defaultAccount == null) {
@@ -1995,11 +1991,18 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
                     UserInfo userInfo = (UserInfo) authResult.getData().get(0);
                     mAccountMgr.setUserData(mAccount, Constants.KEY_DISPLAY_NAME, userInfo.getDisplayName());
                     mAccountMgr.setUserData(mAccount, Constants.KEY_USER_ID, userInfo.getId());
+                    mAccountMgr.setUserData(mAccount, Constants.KEY_OC_ACCOUNT_VERSION,
+                                            Integer.toString(AccountUtils.ACCOUNT_VERSION_WITH_PROPER_ID));
+
                 } catch (ClassCastException c) {
-                    Log_OC.w(TAG, "Couldn't get display name for " + username);
+                    mAccountMgr.setUserData(mAccount, Constants.KEY_OC_ACCOUNT_VERSION,
+                                            Integer.toString(AccountUtils.ACCOUNT_VERSION));
+                    Log_OC.w(TAG, "Couldn't get display name and user id for " + username);
                 }
             } else {
-                Log_OC.w(TAG, "Couldn't get display name for " + username);
+                mAccountMgr.setUserData(mAccount, Constants.KEY_OC_ACCOUNT_VERSION,
+                                        Integer.toString(AccountUtils.ACCOUNT_VERSION));
+                Log_OC.w(TAG, "Couldn't get display name and user id for " + username);
             }
 
             if (isSaml) {

+ 34 - 19
src/main/java/com/owncloud/android/operations/UploadFileOperation.java

@@ -20,6 +20,7 @@
 package com.owncloud.android.operations;
 
 import android.accounts.Account;
+import android.accounts.AccountManager;
 import android.annotation.SuppressLint;
 import android.content.Context;
 import android.net.Uri;
@@ -39,8 +40,9 @@ import com.owncloud.android.datamodel.UploadsStorageManager;
 import com.owncloud.android.db.OCUpload;
 import com.owncloud.android.files.services.FileUploader;
 import com.owncloud.android.lib.common.OwnCloudClient;
+import com.owncloud.android.lib.common.accounts.AccountUtils;
 import com.owncloud.android.lib.common.network.OnDatatransferProgressListener;
-import com.owncloud.android.lib.common.network.ProgressiveDataTransferer;
+import com.owncloud.android.lib.common.network.ProgressiveDataTransfer;
 import com.owncloud.android.lib.common.operations.OperationCancelledException;
 import com.owncloud.android.lib.common.operations.RemoteOperation;
 import com.owncloud.android.lib.common.operations.RemoteOperationResult;
@@ -128,7 +130,7 @@ public class UploadFileOperation extends SyncOperation {
      * Local path to file which is to be uploaded (before any possible renaming or moving).
      */
     private String mOriginalStoragePath;
-    private Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<>();
+    private final Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<>();
     private OnRenameListener mRenameUploadListener;
 
     private final AtomicBoolean mCancellationRequested = new AtomicBoolean(false);
@@ -138,7 +140,7 @@ public class UploadFileOperation extends SyncOperation {
 
     private UploadFileRemoteOperation mUploadOperation;
 
-    protected RequestEntity mEntity;
+    private RequestEntity mEntity;
 
     private Account mAccount;
     private OCUpload mUpload;
@@ -317,10 +319,10 @@ public class UploadFileOperation extends SyncOperation {
             mDataTransferListeners.add(listener);
         }
         if (mEntity != null) {
-            ((ProgressiveDataTransferer) mEntity).addDatatransferProgressListener(listener);
+            ((ProgressiveDataTransfer) mEntity).addDataTransferProgressListener(listener);
         }
         if (mUploadOperation != null) {
-            mUploadOperation.addDatatransferProgressListener(listener);
+            mUploadOperation.addDataTransferProgressListener(listener);
         }
     }
 
@@ -329,10 +331,10 @@ public class UploadFileOperation extends SyncOperation {
             mDataTransferListeners.remove(listener);
         }
         if (mEntity != null) {
-            ((ProgressiveDataTransferer) mEntity).removeDatatransferProgressListener(listener);
+            ((ProgressiveDataTransfer) mEntity).removeDataTransferProgressListener(listener);
         }
         if (mUploadOperation != null) {
-            mUploadOperation.removeDatatransferProgressListener(listener);
+            mUploadOperation.removeDataTransferProgressListener(listener);
         }
     }
 
@@ -575,10 +577,17 @@ public class UploadFileOperation extends SyncOperation {
             }
 
             /// perform the upload
-            if (size > ChunkedFileUploadRemoteOperation.CHUNK_SIZE) {
-                mUploadOperation = new ChunkedFileUploadRemoteOperation(mContext, encryptedTempFile.getAbsolutePath(),
-                        mFile.getParentRemotePath() + encryptedFileName, mFile.getMimeType(),
-                        mFile.getEtagInConflict(), timeStamp);
+            if (size > ChunkedFileUploadRemoteOperation.CHUNK_SIZE_MOBILE) {
+
+                String userId = AccountManager.get(getContext()).getUserData(getAccount(),
+                                                                             AccountUtils.Constants.KEY_USER_ID);
+
+                boolean onWifiConnection = ConnectivityUtils.isOnlineWithWifi(mContext);
+
+                mUploadOperation = new ChunkedFileUploadRemoteOperation(encryptedTempFile.getAbsolutePath(),
+                                                                        mFile.getParentRemotePath() + encryptedFileName,
+                                                                        mFile.getMimeType(), mFile.getEtagInConflict(),
+                                                                        timeStamp, userId, onWifiConnection);
             } else {
                 mUploadOperation = new UploadFileRemoteOperation(encryptedTempFile.getAbsolutePath(),
                         mFile.getParentRemotePath() + encryptedFileName, mFile.getMimeType(),
@@ -586,7 +595,7 @@ public class UploadFileOperation extends SyncOperation {
             }
 
             for (OnDatatransferProgressListener mDataTransferListener : mDataTransferListeners) {
-                mUploadOperation.addDatatransferProgressListener(mDataTransferListener);
+                mUploadOperation.addDataTransferProgressListener(mDataTransferListener);
             }
 
             if (mCancellationRequested.get()) {
@@ -818,16 +827,23 @@ public class UploadFileOperation extends SyncOperation {
             }
 
             // perform the upload
-            if (size > ChunkedFileUploadRemoteOperation.CHUNK_SIZE) {
-                mUploadOperation = new ChunkedFileUploadRemoteOperation(mContext, mFile.getStoragePath(),
-                        mFile.getRemotePath(), mFile.getMimeType(), mFile.getEtagInConflict(), timeStamp);
+            if (size > ChunkedFileUploadRemoteOperation.CHUNK_SIZE_MOBILE) {
+                String userId = AccountManager.get(getContext()).getUserData(getAccount(),
+                                                                             AccountUtils.Constants.KEY_USER_ID);
+
+                boolean onWifiConnection = ConnectivityUtils.isOnlineWithWifi(mContext);
+
+                mUploadOperation = new ChunkedFileUploadRemoteOperation(mFile.getStoragePath(),
+                                                                        mFile.getRemotePath(), mFile.getMimeType(),
+                                                                        mFile.getEtagInConflict(),
+                                                                        timeStamp, userId, onWifiConnection);
             } else {
                 mUploadOperation = new UploadFileRemoteOperation(mFile.getStoragePath(),
                         mFile.getRemotePath(), mFile.getMimeType(), mFile.getEtagInConflict(), timeStamp);
             }
 
             for (OnDatatransferProgressListener mDataTransferListener : mDataTransferListeners) {
-                mUploadOperation.addDatatransferProgressListener(mDataTransferListener);
+                mUploadOperation.addDataTransferProgressListener(mDataTransferListener);
             }
 
             if (mCancellationRequested.get()) {
@@ -998,7 +1014,7 @@ public class UploadFileOperation extends SyncOperation {
      * will be uploaded.
      */
     private RemoteOperationResult grantFolderExistence(String pathToGrant, OwnCloudClient client) {
-        RemoteOperation operation = new ExistenceCheckRemoteOperation(pathToGrant, mContext, false);
+        RemoteOperation operation = new ExistenceCheckRemoteOperation(pathToGrant, false);
         RemoteOperationResult result = operation.execute(client, true);
         if (!result.isSuccess() && result.getCode() == ResultCode.FILE_NOT_FOUND && mRemoteFolderToBeCreated) {
             SyncOperation syncOp = new CreateFolderOperation(pathToGrant, true);
@@ -1117,8 +1133,7 @@ public class UploadFileOperation extends SyncOperation {
 
             return false;
         } else {
-            ExistenceCheckRemoteOperation existsOperation = new ExistenceCheckRemoteOperation(remotePath, mContext,
-                    false);
+            ExistenceCheckRemoteOperation existsOperation = new ExistenceCheckRemoteOperation(remotePath, false);
             RemoteOperationResult result = existsOperation.execute(client);
             return result.isSuccess();
         }

+ 9 - 1
src/main/java/com/owncloud/android/ui/activity/FileActivity.java

@@ -39,6 +39,7 @@ import android.view.View;
 import com.google.android.material.snackbar.Snackbar;
 import com.owncloud.android.MainApp;
 import com.owncloud.android.R;
+import com.owncloud.android.authentication.AccountUtils;
 import com.owncloud.android.authentication.AuthenticatorActivity;
 import com.owncloud.android.datamodel.ArbitraryDataProvider;
 import com.owncloud.android.datamodel.OCFile;
@@ -153,7 +154,8 @@ public abstract class FileActivity extends DrawerActivity
         mHandler = new Handler();
         mFileOperationsHelper = new FileOperationsHelper(this);
         Account account = null;
-        if(savedInstanceState != null) {
+
+        if (savedInstanceState != null) {
             mFile = savedInstanceState.getParcelable(FileActivity.EXTRA_FILE);
             mFromNotification = savedInstanceState.getBoolean(FileActivity.EXTRA_FROM_NOTIFICATION);
             mFileOperationsHelper.setOpIdWaitingFor(
@@ -167,6 +169,12 @@ public abstract class FileActivity extends DrawerActivity
                     false);
         }
 
+        Thread t = new Thread(() -> {
+            // best place, before any access to AccountManager or database
+            AccountUtils.updateAccountVersion(this);
+        });
+        t.start();
+
         setAccount(account, savedInstanceState != null);
 
         mOperationsServiceConnection = new OperationsServiceConnection();

+ 4 - 4
src/main/java/com/owncloud/android/utils/ConnectivityUtils.java

@@ -75,7 +75,7 @@ public final class ConnectivityUtils {
 
                     if (serverVersion.compareTo(OwnCloudVersion.nextcloud_13) > 0) {
                         return !(status == HttpStatus.SC_NO_CONTENT &&
-                                (get.getResponseContentLength() == -1 || get.getResponseContentLength() == 0));
+                            (get.getResponseContentLength() == -1 || get.getResponseContentLength() == 0));
                     } else {
                         if (status == HttpStatus.SC_OK) {
                             try {
@@ -99,14 +99,14 @@ public final class ConnectivityUtils {
             } catch (OperationCanceledException | AuthenticatorException e) {
                 Log_OC.e(TAG, e.getMessage());
             }
-        } else if (!Device.getNetworkType(context).equals(JobRequest.NetworkType.ANY)) {
-            return false;
+        } else {
+            return Device.getNetworkType(context).equals(JobRequest.NetworkType.ANY);
         }
 
         return true;
     }
 
-    private static boolean isOnlineWithWifi(Context context) {
+    public static boolean isOnlineWithWifi(Context context) {
         try {
             ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
             NetworkInfo activeNetwork = cm.getActiveNetworkInfo();