From 72d9d63acb800adefdd32e63196e0434ec0f7d96 Mon Sep 17 00:00:00 2001 From: "David A. Velasco" Date: Thu, 30 Jan 2014 15:02:50 +0100 Subject: [PATCH 1/8] Added service to run operations and created SyncOperations as common class for operations that work both both with remote and local storage --- owncloud-android-library | 2 +- .../operations/GetSharesOperation.java | 17 +- .../operations/common/SyncOperation.java | 129 ++++++++ .../android/services/OperationsService.java | 284 ++++++++++++++++++ .../ui/activity/FileDisplayActivity.java | 5 +- 5 files changed, 423 insertions(+), 14 deletions(-) create mode 100644 src/com/owncloud/android/operations/common/SyncOperation.java create mode 100644 src/com/owncloud/android/services/OperationsService.java diff --git a/owncloud-android-library b/owncloud-android-library index 7126c73e63..27232c3aa4 160000 --- a/owncloud-android-library +++ b/owncloud-android-library @@ -1 +1 @@ -Subproject commit 7126c73e6324ca8d3615fc4757191848e03d5644 +Subproject commit 27232c3aa430951cde0d05c1846250eaf6385e4a diff --git a/src/com/owncloud/android/operations/GetSharesOperation.java b/src/com/owncloud/android/operations/GetSharesOperation.java index 3b879c0651..8949a8ed4f 100644 --- a/src/com/owncloud/android/operations/GetSharesOperation.java +++ b/src/com/owncloud/android/operations/GetSharesOperation.java @@ -28,6 +28,7 @@ import com.owncloud.android.lib.operations.common.OCShare; import com.owncloud.android.lib.operations.common.ShareType; import com.owncloud.android.lib.operations.remote.GetRemoteSharesOperation; import com.owncloud.android.lib.utils.FileUtils; +import com.owncloud.android.operations.common.SyncOperation; import com.owncloud.android.utils.Log_OC; /** @@ -35,19 +36,13 @@ import com.owncloud.android.utils.Log_OC; * Save the data in Database * * @author masensio + * @author David A. Velasco */ -public class GetSharesOperation extends RemoteOperation { +public class GetSharesOperation extends SyncOperation { private static final String TAG = GetSharesOperation.class.getSimpleName(); - protected FileDataStorageManager mStorageManager; - - - public GetSharesOperation(FileDataStorageManager storageManager) { - mStorageManager = storageManager; - } - @Override protected RemoteOperationResult run(OwnCloudClient client) { GetRemoteSharesOperation operation = new GetRemoteSharesOperation(); @@ -72,7 +67,7 @@ public class GetSharesOperation extends RemoteOperation { if (shares.size() > 0) { // Save share file - mStorageManager.saveShares(shares); + getStorageManager().saveShares(shares); ArrayList sharedFiles = new ArrayList(); @@ -84,7 +79,7 @@ public class GetSharesOperation extends RemoteOperation { } // Update OCFile with data from share: ShareByLink żand publicLink? - OCFile file = mStorageManager.getFileByPath(path); + OCFile file = getStorageManager().getFileByPath(path); if (file != null) { if (share.getShareType().equals(ShareType.PUBLIC_LINK)) { file.setShareByLink(true); @@ -94,7 +89,7 @@ public class GetSharesOperation extends RemoteOperation { } if (sharedFiles.size() > 0) { - mStorageManager.updateSharedFiles(sharedFiles); + getStorageManager().updateSharedFiles(sharedFiles); } } } diff --git a/src/com/owncloud/android/operations/common/SyncOperation.java b/src/com/owncloud/android/operations/common/SyncOperation.java new file mode 100644 index 0000000000..7a8e356a37 --- /dev/null +++ b/src/com/owncloud/android/operations/common/SyncOperation.java @@ -0,0 +1,129 @@ +/* ownCloud Android client application + * Copyright (C) 2012-2014 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.operations.common; + +import com.owncloud.android.datamodel.FileDataStorageManager; +import com.owncloud.android.lib.network.OwnCloudClient; +import com.owncloud.android.lib.operations.common.OnRemoteOperationListener; +import com.owncloud.android.lib.operations.common.RemoteOperation; +import com.owncloud.android.lib.operations.common.RemoteOperationResult; + +import android.app.Activity; +import android.content.Context; +import android.os.Handler; + + +/** + * Operation which execution involves both interactions with an ownCloud server and + * with local data in the device. + * + * Provides methods to execute the operation both synchronously or asynchronously. + * + * @author David A. Velasco + */ +public abstract class SyncOperation extends RemoteOperation { + + //private static final String TAG = SyncOperation.class.getSimpleName(); + + private FileDataStorageManager mStorageManager; + + public FileDataStorageManager getStorageManager() { + return mStorageManager; + } + + + /** + * Synchronously executes the operation on the received ownCloud account. + * + * Do not call this method from the main thread. + * + * This method should be used whenever an ownCloud account is available, instead of {@link #execute(OwnCloudClient)}. + * + * @param account ownCloud account in remote ownCloud server to reach during the execution of the operation. + * @param context Android context for the component calling the method. + * @return Result of the operation. + */ + public RemoteOperationResult execute(FileDataStorageManager storageManager, Context context) { + if (storageManager == null) { + throw new IllegalArgumentException("Trying to execute a sync operation with a NULL storage manager"); + } + if (storageManager.getAccount() == null) { + throw new IllegalArgumentException("Trying to execute a sync operation with a storage manager for a NULL account"); + } + mStorageManager = storageManager; + return super.execute(mStorageManager.getAccount(), context); + } + + + /** + * Synchronously executes the remote operation + * + * Do not call this method from the main thread. + * + * @param client Client object to reach an ownCloud server during the execution of the operation. + * @return Result of the operation. + */ + public RemoteOperationResult execute(OwnCloudClient client, FileDataStorageManager storageManager) { + if (storageManager == null) + throw new IllegalArgumentException("Trying to execute a sync operation with a NULL storage manager"); + mStorageManager = storageManager; + return super.execute(client); + } + + + /** + * Asynchronously executes the remote operation + * + * This method should be used whenever an ownCloud account is available, instead of {@link #execute(OwnCloudClient)}. + * + * @param account ownCloud account in remote ownCloud server to reach during the execution of the operation. + * @param context Android context for the component calling the method. + * @param listener Listener to be notified about the execution of the operation. + * @param listenerHandler Handler associated to the thread where the methods of the listener objects must be called. + * @return Thread were the remote operation is executed. + */ + public Thread execute(FileDataStorageManager storageManager, Context context, OnRemoteOperationListener listener, Handler listenerHandler, Activity callerActivity) { + if (storageManager == null) { + throw new IllegalArgumentException("Trying to execute a sync operation with a NULL storage manager"); + } + if (storageManager.getAccount() == null) { + throw new IllegalArgumentException("Trying to execute a sync operation with a storage manager for a NULL account"); + } + mStorageManager = storageManager; + return super.execute(storageManager.getAccount(), context, listener, listenerHandler, callerActivity); + } + + + /** + * Asynchronously executes the remote operation + * + * @param client Client object to reach an ownCloud server during the execution of the operation. + * @param listener Listener to be notified about the execution of the operation. + * @param listenerHandler Handler associated to the thread where the methods of the listener objects must be called. + * @return Thread were the remote operation is executed. + */ + public Thread execute(OwnCloudClient client, FileDataStorageManager storageManager, OnRemoteOperationListener listener, Handler listenerHandler) { + if (storageManager == null) { + throw new IllegalArgumentException("Trying to execute a sync operation with a NULL storage manager"); + } + mStorageManager = storageManager; + return super.execute(client, listener, listenerHandler); + } + + +} diff --git a/src/com/owncloud/android/services/OperationsService.java b/src/com/owncloud/android/services/OperationsService.java new file mode 100644 index 0000000000..b7290c8be6 --- /dev/null +++ b/src/com/owncloud/android/services/OperationsService.java @@ -0,0 +1,284 @@ +/* ownCloud Android client application + * 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.services; + +import java.io.IOException; +import java.util.concurrent.ConcurrentLinkedQueue; + +import com.owncloud.android.datamodel.FileDataStorageManager; + +import com.owncloud.android.lib.network.OwnCloudClientFactory; +import com.owncloud.android.lib.network.OwnCloudClient; +import com.owncloud.android.operations.GetSharesOperation; +import com.owncloud.android.operations.common.SyncOperation; +import com.owncloud.android.lib.operations.common.RemoteOperation; +import com.owncloud.android.lib.operations.common.RemoteOperationResult; +import com.owncloud.android.utils.Log_OC; + +import android.accounts.Account; +import android.accounts.AccountsException; +import android.app.Service; +import android.content.Intent; +import android.net.Uri; +import android.os.Binder; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Process; +import android.support.v4.content.LocalBroadcastManager; +import android.util.Pair; + +public class OperationsService extends Service { + + private static final String TAG = OperationsService.class.getSimpleName(); + + public static final String EXTRA_ACCOUNT = "ACCOUNT"; + public static final String EXTRA_SERVER_URL = "SERVER_URL"; + public static final String EXTRA_RESULT = "RESULT"; + private static final String ACTION_OPERATION_ADDED = OperationsService.class.getName() + ".OPERATION_ADDED"; + private static final String ACTION_OPERATION_FINISHED = OperationsService.class.getName() + ".OPERATION_FINISHED"; + + private ConcurrentLinkedQueue> mPendingOperations = new ConcurrentLinkedQueue>(); + + private static class Target { + public Uri mServerUrl = null; + public Account mAccount = null; + public Target(Account account, Uri serverUrl) { + mAccount = account; + mServerUrl = serverUrl; + } + } + + private Looper mServiceLooper; + private ServiceHandler mServiceHandler; + private IBinder mBinder; + private OwnCloudClient mOwnCloudClient = null; + private Target mLastTarget = null; + private FileDataStorageManager mStorageManager; + private RemoteOperation mCurrentOperation = null; + + + /** + * Service initialization + */ + @Override + public void onCreate() { + super.onCreate(); + HandlerThread thread = new HandlerThread("Operations service thread", Process.THREAD_PRIORITY_BACKGROUND); + thread.start(); + mServiceLooper = thread.getLooper(); + mServiceHandler = new ServiceHandler(mServiceLooper, this); + mBinder = new OperationsServiceBinder(); + } + + /** + * Entry point to add a new operation to the queue of operations. + * + * New operations are added calling to startService(), resulting in a call to this method. + * This ensures the service will keep on working although the caller activity goes away. + * + * IMPORTANT: the only operations performed here right now is {@link GetSharedFilesOperation}. The class + * is taking advantage of it due to time constraints. + */ + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + if (!intent.hasExtra(EXTRA_ACCOUNT) && !intent.hasExtra(EXTRA_SERVER_URL)) { + Log_OC.e(TAG, "Not enough information provided in intent"); + return START_NOT_STICKY; + } + try { + Account account = intent.getParcelableExtra(EXTRA_ACCOUNT); + String serverUrl = intent.getStringExtra(EXTRA_SERVER_URL); + Target target = new Target(account, (serverUrl == null) ? null : Uri.parse(serverUrl)); + GetSharesOperation operation = new GetSharesOperation(); + mPendingOperations.add(new Pair(target, operation)); + sendBroadcastNewOperation(target, operation); + + Message msg = mServiceHandler.obtainMessage(); + msg.arg1 = startId; + mServiceHandler.sendMessage(msg); + + } catch (IllegalArgumentException e) { + Log_OC.e(TAG, "Bad information provided in intent: " + e.getMessage()); + return START_NOT_STICKY; + } + + return START_NOT_STICKY; + } + + + /** + * Provides a binder object that clients can use to perform actions on the queue of operations, + * except the addition of new operations. + */ + @Override + public IBinder onBind(Intent intent) { + return mBinder; + } + + + /** + * Called when ALL the bound clients were unbound. + */ + @Override + public boolean onUnbind(Intent intent) { + //((OperationsServiceBinder)mBinder).clearListeners(); + return false; // not accepting rebinding (default behaviour) + } + + + /** + * Binder to let client components to perform actions on the queue of operations. + * + * It provides by itself the available operations. + */ + public class OperationsServiceBinder extends Binder { + // TODO + } + + + /** + * Operations worker. Performs the pending operations in the order they were requested. + * + * Created with the Looper of a new thread, started in {@link OperationsService#onCreate()}. + */ + private static class ServiceHandler extends Handler { + // don't make it a final class, and don't remove the static ; lint will warn about a possible memory leak + OperationsService mService; + public ServiceHandler(Looper looper, OperationsService service) { + super(looper); + if (service == null) { + throw new IllegalArgumentException("Received invalid NULL in parameter 'service'"); + } + mService = service; + } + + @Override + public void handleMessage(Message msg) { + mService.nextOperation(); + mService.stopSelf(msg.arg1); + } + } + + + /** + * Performs the next operation in the queue + */ + private void nextOperation() { + + Pair next = null; + synchronized(mPendingOperations) { + next = mPendingOperations.peek(); + } + + if (next != null) { + + mCurrentOperation = next.second; + RemoteOperationResult result = null; + try { + /// prepare client object to send the request to the ownCloud server + if (mLastTarget == null || !mLastTarget.equals(next.first)) { + mLastTarget = next.first; + if (mLastTarget.mAccount != null) { + mOwnCloudClient = OwnCloudClientFactory.createOwnCloudClient(mLastTarget.mAccount, getApplicationContext()); + mStorageManager = new FileDataStorageManager(mLastTarget.mAccount, getContentResolver()); + } else { + mOwnCloudClient = OwnCloudClientFactory.createOwnCloudClient(mLastTarget.mServerUrl, getApplicationContext(), true); // this is not good enough + mStorageManager = null; + } + } + + /// perform the operation + if (mCurrentOperation instanceof SyncOperation) { + result = ((SyncOperation)mCurrentOperation).execute(mOwnCloudClient, mStorageManager); + } else { + result = mCurrentOperation.execute(mOwnCloudClient); + } + + } catch (AccountsException e) { + if (mLastTarget.mAccount == null) { + Log_OC.e(TAG, "Error while trying to get autorization for a NULL account", e); + } else { + Log_OC.e(TAG, "Error while trying to get autorization for " + mLastTarget.mAccount.name, e); + } + result = new RemoteOperationResult(e); + + } catch (IOException e) { + if (mLastTarget.mAccount == null) { + Log_OC.e(TAG, "Error while trying to get autorization for a NULL account", e); + } else { + Log_OC.e(TAG, "Error while trying to get autorization for " + mLastTarget.mAccount.name, e); + } + result = new RemoteOperationResult(e); + + } finally { + synchronized(mPendingOperations) { + mPendingOperations.poll(); + } + } + + sendBroadcastOperationFinished(mLastTarget, mCurrentOperation, result); + } + } + + + /** + * Sends a LOCAL broadcast when a new operation is added to the queue. + * + * Local broadcasts are only delivered to activities in the same process. + * + * @param target Account or URL pointing to an OC server. + * @param operation Added operation. + */ + private void sendBroadcastNewOperation(Target target, RemoteOperation operation) { + Intent intent = new Intent(ACTION_OPERATION_ADDED); + if (target.mAccount != null) { + intent.putExtra(EXTRA_ACCOUNT, target.mAccount); + } else { + intent.putExtra(EXTRA_SERVER_URL, target.mServerUrl); + } + LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(this); + lbm.sendBroadcast(intent); + } + + + /** + * Sends a LOCAL broadcast when an operations finishes in order to the interested activities can update their view + * + * Local broadcasts are only delivered to activities in the same process. + * + * @param target Account or URL pointing to an OC server. + * @param operation Finished operation. + * @param result Result of the operation. + */ + private void sendBroadcastOperationFinished(Target target, RemoteOperation operation, RemoteOperationResult result) { + Intent intent = new Intent(ACTION_OPERATION_FINISHED); + intent.putExtra(EXTRA_RESULT, result); + if (target.mAccount != null) { + intent.putExtra(EXTRA_ACCOUNT, target.mAccount); + } else { + intent.putExtra(EXTRA_SERVER_URL, target.mServerUrl); + } + LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(this); + lbm.sendBroadcast(intent); + } + + +} diff --git a/src/com/owncloud/android/ui/activity/FileDisplayActivity.java b/src/com/owncloud/android/ui/activity/FileDisplayActivity.java index 0eb1966cf4..db4e2628cc 100644 --- a/src/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/src/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -80,6 +80,7 @@ import com.owncloud.android.operations.RemoveFileOperation; import com.owncloud.android.operations.RenameFileOperation; import com.owncloud.android.operations.SynchronizeFileOperation; import com.owncloud.android.operations.SynchronizeFolderOperation; +import com.owncloud.android.operations.common.SyncOperation; import com.owncloud.android.syncadapter.FileSyncService; import com.owncloud.android.ui.dialog.EditNameDialog; import com.owncloud.android.ui.dialog.EditNameDialog.EditNameDialogListener; @@ -1531,8 +1532,8 @@ OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNa private void startGetShares() { // Get shared files/folders - RemoteOperation getShares = new GetSharesOperation(mStorageManager); - getShares.execute(getAccount(), this, this, mHandler, this); + SyncOperation getShares = new GetSharesOperation(); + getShares.execute(mStorageManager, this, this, mHandler, this); mRefreshSharesInProgress = true; setSupportProgressBarIndeterminateVisibility(true); From 871d5ea05d56693a5526a5a7af43b884b0cdff05 Mon Sep 17 00:00:00 2001 From: "David A. Velasco" Date: Thu, 30 Jan 2014 17:58:35 +0100 Subject: [PATCH 2/8] Moved execution of GetSharesOperation to OperationsService --- AndroidManifest.xml | 1 + .../operations/GetSharesOperation.java | 2 - .../android/services/OperationsService.java | 7 +- .../ui/activity/FileDisplayActivity.java | 91 +++++++++++-------- 4 files changed, 61 insertions(+), 40 deletions(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index c1202e631f..ca11c81bf2 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -149,6 +149,7 @@ + diff --git a/src/com/owncloud/android/operations/GetSharesOperation.java b/src/com/owncloud/android/operations/GetSharesOperation.java index 8949a8ed4f..64942c09cc 100644 --- a/src/com/owncloud/android/operations/GetSharesOperation.java +++ b/src/com/owncloud/android/operations/GetSharesOperation.java @@ -19,10 +19,8 @@ package com.owncloud.android.operations; import java.util.ArrayList; -import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.lib.network.OwnCloudClient; -import com.owncloud.android.lib.operations.common.RemoteOperation; import com.owncloud.android.lib.operations.common.RemoteOperationResult; import com.owncloud.android.lib.operations.common.OCShare; import com.owncloud.android.lib.operations.common.ShareType; diff --git a/src/com/owncloud/android/services/OperationsService.java b/src/com/owncloud/android/services/OperationsService.java index b7290c8be6..316657c4e7 100644 --- a/src/com/owncloud/android/services/OperationsService.java +++ b/src/com/owncloud/android/services/OperationsService.java @@ -52,8 +52,9 @@ public class OperationsService extends Service { public static final String EXTRA_ACCOUNT = "ACCOUNT"; public static final String EXTRA_SERVER_URL = "SERVER_URL"; public static final String EXTRA_RESULT = "RESULT"; - private static final String ACTION_OPERATION_ADDED = OperationsService.class.getName() + ".OPERATION_ADDED"; - private static final String ACTION_OPERATION_FINISHED = OperationsService.class.getName() + ".OPERATION_FINISHED"; + + public static final String ACTION_OPERATION_ADDED = OperationsService.class.getName() + ".OPERATION_ADDED"; + public static final String ACTION_OPERATION_FINISHED = OperationsService.class.getName() + ".OPERATION_FINISHED"; private ConcurrentLinkedQueue> mPendingOperations = new ConcurrentLinkedQueue>(); @@ -259,6 +260,8 @@ public class OperationsService extends Service { } + // TODO - maybe add a notification for real start of operations + /** * Sends a LOCAL broadcast when an operations finishes in order to the interested activities can update their view * diff --git a/src/com/owncloud/android/ui/activity/FileDisplayActivity.java b/src/com/owncloud/android/ui/activity/FileDisplayActivity.java index db4e2628cc..5a324a5426 100644 --- a/src/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/src/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -45,6 +45,7 @@ import android.provider.MediaStore; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; +import android.support.v4.content.LocalBroadcastManager; import android.util.Log; import android.view.View; import android.view.ViewGroup; @@ -69,8 +70,6 @@ import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder; import com.owncloud.android.files.services.FileUploader.FileUploaderBinder; import com.owncloud.android.operations.CreateFolderOperation; -import com.owncloud.android.operations.GetSharesOperation; - import com.owncloud.android.lib.operations.common.OnRemoteOperationListener; import com.owncloud.android.lib.operations.common.RemoteOperation; import com.owncloud.android.lib.operations.common.RemoteOperationResult; @@ -80,7 +79,7 @@ import com.owncloud.android.operations.RemoveFileOperation; import com.owncloud.android.operations.RenameFileOperation; import com.owncloud.android.operations.SynchronizeFileOperation; import com.owncloud.android.operations.SynchronizeFolderOperation; -import com.owncloud.android.operations.common.SyncOperation; +import com.owncloud.android.services.OperationsService; import com.owncloud.android.syncadapter.FileSyncService; import com.owncloud.android.ui.dialog.EditNameDialog; import com.owncloud.android.ui.dialog.EditNameDialog.EditNameDialogListener; @@ -115,6 +114,7 @@ OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNa private SyncBroadcastReceiver mSyncBroadcastReceiver; private UploadFinishReceiver mUploadFinishReceiver; private DownloadFinishReceiver mDownloadFinishReceiver; + private OperationsServiceReceiver mOperationsServiceReceiver; private FileDownloaderBinder mDownloaderBinder = null; private FileUploaderBinder mUploaderBinder = null; private ServiceConnection mDownloadConnection = null, mUploadConnection = null; @@ -704,6 +704,12 @@ OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNa downloadIntentFilter.addAction(FileDownloader.getDownloadFinishMessage()); mDownloadFinishReceiver = new DownloadFinishReceiver(); registerReceiver(mDownloadFinishReceiver, downloadIntentFilter); + + // Listen for messages from the OperationsService + IntentFilter operationsIntentFilter = new IntentFilter(OperationsService.ACTION_OPERATION_ADDED); + operationsIntentFilter.addAction(OperationsService.ACTION_OPERATION_FINISHED); + mOperationsServiceReceiver = new OperationsServiceReceiver(); + LocalBroadcastManager.getInstance(this).registerReceiver(mOperationsServiceReceiver, operationsIntentFilter); Log_OC.d(TAG, "onResume() end"); } @@ -725,7 +731,10 @@ OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNa unregisterReceiver(mDownloadFinishReceiver); mDownloadFinishReceiver = null; } - + if (mOperationsServiceReceiver != null) { + LocalBroadcastManager.getInstance(this).unregisterReceiver(mOperationsServiceReceiver); + mOperationsServiceReceiver = null; + } Log_OC.d(TAG, "onPause() end"); } @@ -1034,6 +1043,45 @@ OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNa return (accountName != null && getAccount() != null && accountName.equals(getAccount().name)); } } + + + /** + * Class waiting for broadcast events from the {@link OperationsService}. + * + * Updates the list of files when a get for shares is finished; at this moment the refresh of shares is the only + * operation performed in {@link OperationsService}. + * + * In the future will handle the progress or finalization of all the operations performed in {@link OperationsService}, + * probably all the operations associated to the app model. + */ + private class OperationsServiceReceiver extends BroadcastReceiver { + + @Override + public void onReceive(Context context, Intent intent) { + if (OperationsService.ACTION_OPERATION_ADDED.equals(intent.getAction())) { + + } else if (OperationsService.ACTION_OPERATION_FINISHED.equals(intent.getAction())) { + mRefreshSharesInProgress = false; + + Account account = intent.getParcelableExtra(OperationsService.EXTRA_ACCOUNT); + RemoteOperationResult getSharesResult = (RemoteOperationResult)intent.getSerializableExtra(OperationsService.EXTRA_RESULT); + if (getAccount() != null && account.name.equals(getAccount().name) + && mStorageManager != null + ) { + refeshListOfFilesFragment(); + } + if ((getSharesResult != null) && + RemoteOperationResult.ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED.equals(getSharesResult.getCode())) { + mLastSslUntrustedServerResult = getSharesResult; + showDialog(DIALOG_SSL_VALIDATOR); + } + + setSupportProgressBarIndeterminateVisibility(mRefreshSharesInProgress || mSyncInProgress); + } + + } + + } /** @@ -1298,29 +1346,10 @@ OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNa } else if (operation instanceof CreateFolderOperation) { onCreateFolderOperationFinish((CreateFolderOperation)operation, result); - - } else if (operation instanceof GetSharesOperation) { - onGetSharesOperationFinish((GetSharesOperation) operation, result); } } - /** Updates the data about shared files - * - * @param operation Get Shared Files - * @param result Result of the operation - */ - private void onGetSharesOperationFinish(GetSharesOperation operation, RemoteOperationResult result) { - // Refresh the filelist with the information - refeshListOfFilesFragment(); - - mRefreshSharesInProgress = false; - - if (!mSyncInProgress) { - setSupportProgressBarIndeterminateVisibility(false); - } - } - /** * Updates the view associated to the activity after the finish of an operation trying to remove a * file. @@ -1532,23 +1561,13 @@ OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNa private void startGetShares() { // Get shared files/folders - SyncOperation getShares = new GetSharesOperation(); - getShares.execute(mStorageManager, this, this, mHandler, this); + Intent intent = new Intent(this, OperationsService.class); + intent.putExtra(OperationsService.EXTRA_ACCOUNT, getAccount()); + startService(intent); mRefreshSharesInProgress = true; setSupportProgressBarIndeterminateVisibility(true); } -// public void enableDisableViewGroup(ViewGroup viewGroup, boolean enabled) { -// int childCount = viewGroup.getChildCount(); -// for (int i = 0; i < childCount; i++) { -// View view = viewGroup.getChildAt(i); -// view.setEnabled(enabled); -// view.setClickable(!enabled); -// if (view instanceof ViewGroup) { -// enableDisableViewGroup((ViewGroup) view, enabled); -// } -// } -// } } From c9b639477ecdf7603731ec2684bf8d9bbc0e0aa7 Mon Sep 17 00:00:00 2001 From: "David A. Velasco" Date: Thu, 30 Jan 2014 18:29:23 +0100 Subject: [PATCH 3/8] Updated link to ownCloud library --- owncloud-android-library | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/owncloud-android-library b/owncloud-android-library index 27232c3aa4..8ab453d630 160000 --- a/owncloud-android-library +++ b/owncloud-android-library @@ -1 +1 @@ -Subproject commit 27232c3aa430951cde0d05c1846250eaf6385e4a +Subproject commit 8ab453d630458fc6cdcd1bf68493d82365f1c6d3 From d88dc5266037834f9baed8ea524f479785f31cb5 Mon Sep 17 00:00:00 2001 From: masensio Date: Fri, 31 Jan 2014 14:33:54 +0100 Subject: [PATCH 4/8] OC-2678: Save the new Share resource in the Shares database --- owncloud-android-library | 2 +- .../operations/CreateShareOperation.java | 40 ++++++++++++++++--- .../ui/activity/FileDisplayActivity.java | 4 +- .../ui/fragment/FileDetailFragment.java | 3 -- .../ui/fragment/OCFileListFragment.java | 2 +- 5 files changed, 38 insertions(+), 13 deletions(-) diff --git a/owncloud-android-library b/owncloud-android-library index 8ab453d630..280701ea8d 160000 --- a/owncloud-android-library +++ b/owncloud-android-library @@ -1 +1 @@ -Subproject commit 8ab453d630458fc6cdcd1bf68493d82365f1c6d3 +Subproject commit 280701ea8d864f553609f6cdce32e686b74087c6 diff --git a/src/com/owncloud/android/operations/CreateShareOperation.java b/src/com/owncloud/android/operations/CreateShareOperation.java index 1582f8f0f9..7659d16d5f 100644 --- a/src/com/owncloud/android/operations/CreateShareOperation.java +++ b/src/com/owncloud/android/operations/CreateShareOperation.java @@ -25,14 +25,18 @@ package com.owncloud.android.operations; */ import com.owncloud.android.datamodel.FileDataStorageManager; +import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.lib.network.OwnCloudClient; -import com.owncloud.android.lib.operations.common.RemoteOperation; +import com.owncloud.android.lib.operations.common.OCShare; import com.owncloud.android.lib.operations.common.RemoteOperationResult; import com.owncloud.android.lib.operations.common.ShareType; import com.owncloud.android.lib.operations.remote.CreateShareRemoteOperation; +import com.owncloud.android.lib.utils.FileUtils; +import com.owncloud.android.operations.common.SyncOperation; +import com.owncloud.android.utils.Log_OC; + +public class CreateShareOperation extends SyncOperation { -public class CreateShareOperation extends RemoteOperation { - private static final String TAG = CreateShareOperation.class.getSimpleName(); protected FileDataStorageManager mStorageManager; @@ -43,7 +47,7 @@ public class CreateShareOperation extends RemoteOperation { private boolean mPublicUpload; private String mPassword; private int mPermissions; - + /** * Constructor * @param path Full path of the file/folder being shared. Mandatory argument @@ -79,11 +83,35 @@ public class CreateShareOperation extends RemoteOperation { RemoteOperationResult result = operation.execute(client); if (result.isSuccess()) { - // TODO - // Update DB with the response + if (result.getData().size() > 0) { + OCShare share = (OCShare) result.getData().get(0); + + // Update DB with the response + if (mPath.endsWith(FileUtils.PATH_SEPARATOR)) { + share.setPath(mPath.substring(0, mPath.length()-1)); + share.setIsDirectory(true); + + } else { + share.setPath(mPath); + share.setIsDirectory(false); + } + share.setPermissions(mPermissions); + + getStorageManager().saveShare(share); + + // Update OCFile with data from share: ShareByLink and publicLink + OCFile file = getStorageManager().getFileByPath(mPath); + if (file!=null) { + file.setPublicLink(share.getShareLink()); + getStorageManager().saveFile(file); + Log_OC.d(TAG, "Public Link = " + file.getPublicLink()); + + } + } } + return result; } diff --git a/src/com/owncloud/android/ui/activity/FileDisplayActivity.java b/src/com/owncloud/android/ui/activity/FileDisplayActivity.java index 676e90a2bd..1927d75ecc 100644 --- a/src/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/src/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -1570,10 +1570,10 @@ OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNa } - @Override + public void shareFileWithLink(OCFile file) { CreateShareOperation createShare = new CreateShareOperation(file.getRemotePath(), ShareType.PUBLIC_LINK, "", false, "", 1); - createShare.execute(getAccount(), this, this, mHandler, this); + createShare.execute(getStorageManager(), this, this, mHandler, this); } diff --git a/src/com/owncloud/android/ui/fragment/FileDetailFragment.java b/src/com/owncloud/android/ui/fragment/FileDetailFragment.java index 7bf052749a..5925e085b4 100644 --- a/src/com/owncloud/android/ui/fragment/FileDetailFragment.java +++ b/src/com/owncloud/android/ui/fragment/FileDetailFragment.java @@ -55,9 +55,6 @@ import com.owncloud.android.lib.operations.common.OnRemoteOperationListener; import com.owncloud.android.lib.operations.common.RemoteOperation; import com.owncloud.android.lib.operations.common.RemoteOperationResult; import com.owncloud.android.lib.operations.common.RemoteOperationResult.ResultCode; -import com.owncloud.android.lib.operations.common.ShareType; -import com.owncloud.android.lib.operations.remote.CreateShareRemoteOperation; -import com.owncloud.android.operations.CreateShareOperation; import com.owncloud.android.operations.RemoveFileOperation; import com.owncloud.android.operations.RenameFileOperation; import com.owncloud.android.operations.SynchronizeFileOperation; diff --git a/src/com/owncloud/android/ui/fragment/OCFileListFragment.java b/src/com/owncloud/android/ui/fragment/OCFileListFragment.java index 326c835d0d..ae7b7567c1 100644 --- a/src/com/owncloud/android/ui/fragment/OCFileListFragment.java +++ b/src/com/owncloud/android/ui/fragment/OCFileListFragment.java @@ -285,7 +285,7 @@ public class OCFileListFragment extends ExtendedListFragment implements EditName mTargetFile = (OCFile) mAdapter.getItem(info.position); switch (item.getItemId()) { case R.id.action_share_file: { - mContainerActivity.shareFileWithLink(getCurrentFile()); + mContainerActivity.shareFileWithLink(mTargetFile); return true; } case R.id.action_rename_file: { From 8bf072f71cfaf3021f02aeec8238f38114c55d80 Mon Sep 17 00:00:00 2001 From: masensio Date: Fri, 31 Jan 2014 15:20:40 +0100 Subject: [PATCH 5/8] OC-2677: Update library --- owncloud-android-library | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/owncloud-android-library b/owncloud-android-library index 280701ea8d..cc71eeaa21 160000 --- a/owncloud-android-library +++ b/owncloud-android-library @@ -1 +1 @@ -Subproject commit 280701ea8d864f553609f6cdce32e686b74087c6 +Subproject commit cc71eeaa21e9217947dfce213049e16d02629b0b From f7bb011b7e0a3a9801f1cc461ebdf16bd8db9112 Mon Sep 17 00:00:00 2001 From: masensio Date: Sat, 1 Feb 2014 14:35:47 +0100 Subject: [PATCH 6/8] OC-2677: Add GetSharesForFile operation --- owncloud-android-library | 2 +- .../datamodel/FileDataStorageManager.java | 33 ++++++++ .../operations/GetSharesForFileOperation.java | 79 +++++++++++++++++++ .../operations/GetSharesOperation.java | 36 +-------- 4 files changed, 114 insertions(+), 36 deletions(-) create mode 100644 src/com/owncloud/android/operations/GetSharesForFileOperation.java diff --git a/owncloud-android-library b/owncloud-android-library index cc71eeaa21..781e738347 160000 --- a/owncloud-android-library +++ b/owncloud-android-library @@ -1 +1 @@ -Subproject commit cc71eeaa21e9217947dfce213049e16d02629b0b +Subproject commit 781e738347a87c2b24144e03f7fe8fe32603f2f3 diff --git a/src/com/owncloud/android/datamodel/FileDataStorageManager.java b/src/com/owncloud/android/datamodel/FileDataStorageManager.java index 0b35beaa8a..12390c3d45 100644 --- a/src/com/owncloud/android/datamodel/FileDataStorageManager.java +++ b/src/com/owncloud/android/datamodel/FileDataStorageManager.java @@ -29,6 +29,7 @@ import com.owncloud.android.MainApp; import com.owncloud.android.db.ProviderMeta.ProviderTableMeta; import com.owncloud.android.lib.operations.common.OCShare; import com.owncloud.android.lib.operations.common.ShareType; +import com.owncloud.android.lib.utils.FileUtils; import com.owncloud.android.utils.FileStorageUtils; import com.owncloud.android.utils.Log_OC; @@ -1098,4 +1099,36 @@ public class FileDataStorageManager { } } + + + public void saveSharesDB(ArrayList shares) { + + if (shares.size() > 0) { + // Save share file + saveShares(shares); + + ArrayList sharedFiles = new ArrayList(); + + for (OCShare share : shares) { + // Get the path + String path = share.getPath(); + if (share.isDirectory()) { + path = path + FileUtils.PATH_SEPARATOR; + } + + // Update OCFile with data from share: ShareByLink żand publicLink? + OCFile file = getFileByPath(path); + if (file != null) { + if (share.getShareType().equals(ShareType.PUBLIC_LINK)) { + file.setShareByLink(true); + sharedFiles.add(file); + } + } + } + + if (sharedFiles.size() > 0) { + updateSharedFiles(sharedFiles); + } + } + } } diff --git a/src/com/owncloud/android/operations/GetSharesForFileOperation.java b/src/com/owncloud/android/operations/GetSharesForFileOperation.java new file mode 100644 index 0000000000..fd2dab743c --- /dev/null +++ b/src/com/owncloud/android/operations/GetSharesForFileOperation.java @@ -0,0 +1,79 @@ +/* ownCloud Android client application + * 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.operations; + +import java.util.ArrayList; + +import com.owncloud.android.lib.network.OwnCloudClient; +import com.owncloud.android.lib.operations.common.OCShare; +import com.owncloud.android.lib.operations.common.RemoteOperationResult; +import com.owncloud.android.lib.operations.remote.GetSharesForFileRemoteOperation; +import com.owncloud.android.operations.common.SyncOperation; +import com.owncloud.android.utils.Log_OC; + +/** + * Provide a list shares for a specific file. + * + * @author masensio + * + */ +public class GetSharesForFileOperation extends SyncOperation { + + private static final String TAG = GetSharesForFileOperation.class.getSimpleName(); + + private String mPath; + private boolean mReshares; + private boolean mSubfiles; + + /** + * Constructor + * + * @param path Path to file or folder + * @param reshares If set to ‘false’ (default), only shares from the current user are returned + * If set to ‘true’, all shares from the given file are returned + * @param subfiles If set to ‘false’ (default), lists only the folder being shared + * If set to ‘true’, all shared files within the folder are returned. + */ + public GetSharesForFileOperation(String path, boolean reshares, boolean subfiles) { + mPath = path; + mReshares = reshares; + mSubfiles = subfiles; + } + + @Override + protected RemoteOperationResult run(OwnCloudClient client) { + GetSharesForFileRemoteOperation operation = new GetSharesForFileRemoteOperation(mPath, mReshares, mSubfiles); + RemoteOperationResult result = operation.execute(client); + + if (result.isSuccess()) { + + // Update DB with the response + Log_OC.d(TAG, "File = " + mPath + " Share list size " + result.getData().size()); + ArrayList shares = new ArrayList(); + for(Object obj: result.getData()) { + shares.add((OCShare) obj); + } + + getStorageManager().saveSharesDB(shares); + } + + return result; + } + +} diff --git a/src/com/owncloud/android/operations/GetSharesOperation.java b/src/com/owncloud/android/operations/GetSharesOperation.java index 64942c09cc..8f7a2ae88e 100644 --- a/src/com/owncloud/android/operations/GetSharesOperation.java +++ b/src/com/owncloud/android/operations/GetSharesOperation.java @@ -19,13 +19,10 @@ package com.owncloud.android.operations; import java.util.ArrayList; -import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.lib.network.OwnCloudClient; import com.owncloud.android.lib.operations.common.RemoteOperationResult; import com.owncloud.android.lib.operations.common.OCShare; -import com.owncloud.android.lib.operations.common.ShareType; import com.owncloud.android.lib.operations.remote.GetRemoteSharesOperation; -import com.owncloud.android.lib.utils.FileUtils; import com.owncloud.android.operations.common.SyncOperation; import com.owncloud.android.utils.Log_OC; @@ -55,41 +52,10 @@ public class GetSharesOperation extends SyncOperation { shares.add((OCShare) obj); } - saveSharesDB(shares); + getStorageManager().saveSharesDB(shares); } return result; } - private void saveSharesDB(ArrayList shares) { - - if (shares.size() > 0) { - // Save share file - getStorageManager().saveShares(shares); - - ArrayList sharedFiles = new ArrayList(); - - for (OCShare share : shares) { - // Get the path - String path = share.getPath(); - if (share.isDirectory()) { - path = path + FileUtils.PATH_SEPARATOR; - } - - // Update OCFile with data from share: ShareByLink żand publicLink? - OCFile file = getStorageManager().getFileByPath(path); - if (file != null) { - if (share.getShareType().equals(ShareType.PUBLIC_LINK)) { - file.setShareByLink(true); - sharedFiles.add(file); - } - } - } - - if (sharedFiles.size() > 0) { - getStorageManager().updateSharedFiles(sharedFiles); - } - } - } - } From 0f7d8d84534f9d2f5928f2867c5e5b2bdc4ff1c0 Mon Sep 17 00:00:00 2001 From: masensio Date: Sat, 1 Feb 2014 19:38:36 +0100 Subject: [PATCH 7/8] OC-2771: Add pop-up message if the server doesn't support Share API and Share Link option is selected --- res/values/strings.xml | 2 ++ .../android/ui/activity/FileDisplayActivity.java | 16 ++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 7ae67dc191..b4fff2210d 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -244,4 +244,6 @@ do nothing you are not online for instant upload Failure Message: Please check your server configuration,maybe your quota is exceeded. + + Sorry, sharing is not enabled on your server. Please contact your administrator. diff --git a/src/com/owncloud/android/ui/activity/FileDisplayActivity.java b/src/com/owncloud/android/ui/activity/FileDisplayActivity.java index 1927d75ecc..e26843b121 100644 --- a/src/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/src/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -75,7 +75,6 @@ import com.owncloud.android.lib.operations.common.RemoteOperation; import com.owncloud.android.lib.operations.common.RemoteOperationResult; import com.owncloud.android.lib.operations.common.ShareType; import com.owncloud.android.lib.operations.common.RemoteOperationResult.ResultCode; - import com.owncloud.android.operations.CreateShareOperation; import com.owncloud.android.operations.RemoveFileOperation; import com.owncloud.android.operations.RenameFileOperation; @@ -1347,6 +1346,7 @@ OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNa } else if (operation instanceof CreateFolderOperation) { onCreateFolderOperationFinish((CreateFolderOperation)operation, result); } + } @@ -1572,9 +1572,21 @@ OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNa public void shareFileWithLink(OCFile file) { + + if (isSharedSupported()) { + // Create the Share CreateShareOperation createShare = new CreateShareOperation(file.getRemotePath(), ShareType.PUBLIC_LINK, "", false, "", 1); createShare.execute(getStorageManager(), this, this, mHandler, this); - + + // TODO + // Get the link --> when the operation is finished + + } else { + // Show a Message + Toast t = Toast.makeText(this, getString(R.string.share_link_no_support_share_api), Toast.LENGTH_LONG); + t.show(); + + } } } From 261d8a5167571ffbaa447cce8df24f32a53faf7d Mon Sep 17 00:00:00 2001 From: masensio Date: Mon, 3 Feb 2014 09:26:36 +0100 Subject: [PATCH 8/8] OC-2772:Display a message if the user selects to share a file or folder that does not exist on the server --- owncloud-android-library | 2 +- res/values/strings.xml | 1 + .../android/ui/activity/FileDisplayActivity.java | 14 ++++++++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/owncloud-android-library b/owncloud-android-library index 781e738347..09b1c3c15a 160000 --- a/owncloud-android-library +++ b/owncloud-android-library @@ -1 +1 @@ -Subproject commit 781e738347a87c2b24144e03f7fe8fe32603f2f3 +Subproject commit 09b1c3c15aa2644635796c93ee918f8b0a20ff7c diff --git a/res/values/strings.xml b/res/values/strings.xml index b4fff2210d..7d941c1abc 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -246,4 +246,5 @@ Please check your server configuration,maybe your quota is exceeded. Sorry, sharing is not enabled on your server. Please contact your administrator. + Unable to share this file or folder. Please, make sure it exists diff --git a/src/com/owncloud/android/ui/activity/FileDisplayActivity.java b/src/com/owncloud/android/ui/activity/FileDisplayActivity.java index e26843b121..9ae9dfedd9 100644 --- a/src/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/src/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -1345,11 +1345,25 @@ OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNa } else if (operation instanceof CreateFolderOperation) { onCreateFolderOperationFinish((CreateFolderOperation)operation, result); + + } else if (operation instanceof CreateShareOperation) { + onCreateShareOperation((CreateShareOperation) operation, result); } } + private void onCreateShareOperation(CreateShareOperation operation, RemoteOperationResult result) { + if (result.getCode() == ResultCode.FILE_NOT_FOUND) { + // Show a Message + Toast t = Toast.makeText(this, getString(R.string.share_link_file_no_exist), Toast.LENGTH_LONG); + t.show(); + } + + refeshListOfFilesFragment(); + + } + /** * Updates the view associated to the activity after the finish of an operation trying to remove a * file.