Loading app/src/main/java/foundation/e/drive/models/SyncWrapper.java 0 → 100644 +85 −0 Original line number Diff line number Diff line /* * Copyright © Vincent Bourgmayer (/e/ foundation). * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Public License v3.0 * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/gpl.html */ package foundation.e.drive.models; import android.content.Context; import com.owncloud.android.lib.common.operations.RemoteOperation; import foundation.e.drive.operations.DownloadFileOperation; import foundation.e.drive.operations.UploadFileOperation; /** * This class encapsulates data, for SynchronizationSerivce, about a thread which run a RemoteOperation * * @author Vincent Bourgmayer */ public class SyncWrapper { private final SyncRequest request; private final RemoteOperation remoteOperation; private boolean isRunning; /** * Build an instance of SyncThreadHolder for a file transfer * @param request SyncRequest at origin of the file transfer * @param context Application context, used to create RemoteOperation to run */ public SyncWrapper(SyncRequest request, Context context) { this.request = request; remoteOperation = createRemoteOperation(request, context); isRunning = true; } public RemoteOperation getRemoteOperation() { return remoteOperation; } public boolean isRunning() { return isRunning; } public synchronized void setRunning(boolean running) { isRunning = running; } /** * Create the RemoteOperation (to perform file transfer) based on SyncRequest * @param request syncRequest for the file * @param context App context to be passed to RemoteOperation's contructor * @return RemoteOperation for Upload/Download request or null */ private static RemoteOperation createRemoteOperation(SyncRequest request, Context context) { final RemoteOperation operation; switch (request.getOperationType()) { case UPLOAD: final SyncedFileState sfs = request.getSyncedFileState(); operation = new UploadFileOperation(sfs, context); break; case DOWNLOAD: final DownloadRequest downloadRequest = (DownloadRequest) request; operation = new DownloadFileOperation(downloadRequest.getRemoteFile(), downloadRequest.getSyncedFileState(), context); break; case REMOTE_DELETE: default: operation = null; break; } return operation; } @Override public boolean equals(Object obj) { if (obj instanceof SyncRequest) { final SyncRequest objRequest = (SyncRequest) obj; return (objRequest.equals(this.request) && objRequest.getOperationType() == this.request.getOperationType()); } return super.equals(obj); } } app/src/main/java/foundation/e/drive/services/SynchronizationService.java +57 −49 Original line number Diff line number Diff line Loading @@ -30,13 +30,13 @@ import com.owncloud.android.lib.common.operations.RemoteOperationResult; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collection; import java.util.Hashtable; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedDeque; import foundation.e.drive.database.DbHelper; import foundation.e.drive.models.DownloadRequest; import foundation.e.drive.models.SyncRequest; import foundation.e.drive.models.SyncedFileState; import foundation.e.drive.models.SyncWrapper; import foundation.e.drive.operations.DownloadFileOperation; import foundation.e.drive.operations.RemoveFileOperation; import foundation.e.drive.operations.UploadFileOperation; Loading @@ -52,10 +52,10 @@ public class SynchronizationService extends Service implements OnRemoteOperation private final SynchronizationBinder binder = new SynchronizationBinder(); private ConcurrentLinkedDeque<SyncRequest> syncRequestQueue; private Hashtable<RemoteOperation, Integer> startedOperations; //Operations which are running private ConcurrentHashMap<Integer, SyncWrapper> startedSync; //Integer is thread index (1 to workerAmount) private Account account; private final int workerAmount = 4; private boolean[] threadWorkingState; //State of the threads; true mean the thread is working private Thread[] threadPool; private OwnCloudClient client; private OperationHandler handler; Loading @@ -76,9 +76,8 @@ public class SynchronizationService extends Service implements OnRemoteOperation } syncRequestQueue = new ConcurrentLinkedDeque<>(); startedOperations = new Hashtable<>(); startedSync = new ConcurrentHashMap<>(); threadPool = new Thread[workerAmount]; threadWorkingState = new boolean[workerAmount]; client = DavClientProvider.getInstance().getClientInstance(account, getApplicationContext()); handler = new OperationHandler(this); Loading @@ -97,11 +96,20 @@ public class SynchronizationService extends Service implements OnRemoteOperation } public void queueOperation(SyncRequest request){ for (SyncWrapper syncWrapper : startedSync.values()) { if (syncWrapper.equals(request)) { return; } } syncRequestQueue.remove(request); syncRequestQueue.add(request); } public void queueOperations(Collection<SyncRequest> requests){ for (SyncWrapper syncWrapper : startedSync.values()) { requests.removeIf(syncRequest -> syncWrapper.equals(syncRequest)); } syncRequestQueue.removeAll(requests); syncRequestQueue.addAll(requests); } Loading @@ -113,37 +121,55 @@ public class SynchronizationService extends Service implements OnRemoteOperation } } private void startWorker(int threadIndex){ final boolean meteredNetworkAllowed = CommonUtils.isMeteredNetworkAllowed(account); if (threadWorkingState[threadIndex] || !CommonUtils.haveNetworkConnection(getApplicationContext(), meteredNetworkAllowed)) { return; } private void startWorker(Integer threadIndex){ if (!canStart(threadIndex)) return; final SyncRequest request = this.syncRequestQueue.poll(); //return null if empty if (request == null) return; final RemoteOperation operation = this.createRemoteOperation(request); final SyncWrapper syncWrapper = new SyncWrapper(request, getApplicationContext()); final RemoteOperation operation = syncWrapper.getRemoteOperation(); if (operation != null) { Log.v(TAG, " an operation has been poll from queue"); if (CommonUtils.isThisSyncAllowed(account, request.getSyncedFileState().isMediaType())) { CommonUtils.createNotificationChannel(this); startedOperations.put(operation, threadIndex); Log.v(TAG, " starts " + request.getSyncedFileState().getName() + " " + request.getOperationType().name() + " on thread " + threadIndex); threadPool[threadIndex] = operation.execute(client, this, handler); threadWorkingState[threadIndex] = true; startedSync.put(threadIndex, syncWrapper); } } } /** * Check if conditions are met to run a new file transfer * @param threadIndex index of thread on which we want to perform the transfer * @return false if nogo */ private boolean canStart(int threadIndex) { final boolean meteredNetworkAllowed = CommonUtils.isMeteredNetworkAllowed(account); final SyncWrapper syncWrapper = startedSync.get(threadIndex); if ( (syncWrapper != null && syncWrapper.isRunning()) || !CommonUtils.haveNetworkConnection(getApplicationContext(), meteredNetworkAllowed)) { return false; } return true; } @Override public void onRemoteOperationFinish(RemoteOperation callerOperation, RemoteOperationResult result) { Log.d(TAG, "onRemoteOperationFinish()"); Integer threadIndex = this.startedOperations.remove(callerOperation); if (threadIndex != null) { this.threadWorkingState[threadIndex] = false; this.startWorker(threadIndex); for (Map.Entry<Integer, SyncWrapper> keyValue : startedSync.entrySet()) { if (keyValue.getValue().getRemoteOperation().equals(callerOperation)) { keyValue.getValue().setRunning(false); startWorker(keyValue.getKey()); } } if (callerOperation instanceof RemoveFileOperation){ Loading Loading @@ -209,26 +235,6 @@ public class SynchronizationService extends Service implements OnRemoteOperation } } private RemoteOperation createRemoteOperation(SyncRequest request){ RemoteOperation operation; switch (request.getOperationType()){ case UPLOAD: final SyncedFileState sfs = request.getSyncedFileState(); operation = new UploadFileOperation(sfs, getApplicationContext()); break; case DOWNLOAD: final DownloadRequest downloadRequest = (DownloadRequest) request; operation = new DownloadFileOperation(downloadRequest.getRemoteFile(), downloadRequest.getSyncedFileState(), getApplicationContext()); break; case REMOTE_DELETE: default: operation = null; break; } return operation; } /** * Handler for the class */ Loading @@ -244,10 +250,12 @@ public class SynchronizationService extends Service implements OnRemoteOperation @Override public void handleMessage(Message msg) { Log.i(TAG, "handler.handleMessage()"); Bundle data = msg.getData(); serviceWeakRef.get() .threadWorkingState[data.getInt("thread index")] = data.getBoolean("mThreadWorkingState"); final Bundle data = msg.getData(); if (data == null || !data.containsKey("thread index") || !data.containsKey("mThreadWorkingState")) { return ; } final SyncWrapper syncWrapper = serviceWeakRef.get().startedSync.get(data.getInt("thread index")); if (syncWrapper != null) syncWrapper.setRunning(data.getBoolean("mThreadWorkingState")); } } Loading Loading
app/src/main/java/foundation/e/drive/models/SyncWrapper.java 0 → 100644 +85 −0 Original line number Diff line number Diff line /* * Copyright © Vincent Bourgmayer (/e/ foundation). * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Public License v3.0 * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/gpl.html */ package foundation.e.drive.models; import android.content.Context; import com.owncloud.android.lib.common.operations.RemoteOperation; import foundation.e.drive.operations.DownloadFileOperation; import foundation.e.drive.operations.UploadFileOperation; /** * This class encapsulates data, for SynchronizationSerivce, about a thread which run a RemoteOperation * * @author Vincent Bourgmayer */ public class SyncWrapper { private final SyncRequest request; private final RemoteOperation remoteOperation; private boolean isRunning; /** * Build an instance of SyncThreadHolder for a file transfer * @param request SyncRequest at origin of the file transfer * @param context Application context, used to create RemoteOperation to run */ public SyncWrapper(SyncRequest request, Context context) { this.request = request; remoteOperation = createRemoteOperation(request, context); isRunning = true; } public RemoteOperation getRemoteOperation() { return remoteOperation; } public boolean isRunning() { return isRunning; } public synchronized void setRunning(boolean running) { isRunning = running; } /** * Create the RemoteOperation (to perform file transfer) based on SyncRequest * @param request syncRequest for the file * @param context App context to be passed to RemoteOperation's contructor * @return RemoteOperation for Upload/Download request or null */ private static RemoteOperation createRemoteOperation(SyncRequest request, Context context) { final RemoteOperation operation; switch (request.getOperationType()) { case UPLOAD: final SyncedFileState sfs = request.getSyncedFileState(); operation = new UploadFileOperation(sfs, context); break; case DOWNLOAD: final DownloadRequest downloadRequest = (DownloadRequest) request; operation = new DownloadFileOperation(downloadRequest.getRemoteFile(), downloadRequest.getSyncedFileState(), context); break; case REMOTE_DELETE: default: operation = null; break; } return operation; } @Override public boolean equals(Object obj) { if (obj instanceof SyncRequest) { final SyncRequest objRequest = (SyncRequest) obj; return (objRequest.equals(this.request) && objRequest.getOperationType() == this.request.getOperationType()); } return super.equals(obj); } }
app/src/main/java/foundation/e/drive/services/SynchronizationService.java +57 −49 Original line number Diff line number Diff line Loading @@ -30,13 +30,13 @@ import com.owncloud.android.lib.common.operations.RemoteOperationResult; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collection; import java.util.Hashtable; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedDeque; import foundation.e.drive.database.DbHelper; import foundation.e.drive.models.DownloadRequest; import foundation.e.drive.models.SyncRequest; import foundation.e.drive.models.SyncedFileState; import foundation.e.drive.models.SyncWrapper; import foundation.e.drive.operations.DownloadFileOperation; import foundation.e.drive.operations.RemoveFileOperation; import foundation.e.drive.operations.UploadFileOperation; Loading @@ -52,10 +52,10 @@ public class SynchronizationService extends Service implements OnRemoteOperation private final SynchronizationBinder binder = new SynchronizationBinder(); private ConcurrentLinkedDeque<SyncRequest> syncRequestQueue; private Hashtable<RemoteOperation, Integer> startedOperations; //Operations which are running private ConcurrentHashMap<Integer, SyncWrapper> startedSync; //Integer is thread index (1 to workerAmount) private Account account; private final int workerAmount = 4; private boolean[] threadWorkingState; //State of the threads; true mean the thread is working private Thread[] threadPool; private OwnCloudClient client; private OperationHandler handler; Loading @@ -76,9 +76,8 @@ public class SynchronizationService extends Service implements OnRemoteOperation } syncRequestQueue = new ConcurrentLinkedDeque<>(); startedOperations = new Hashtable<>(); startedSync = new ConcurrentHashMap<>(); threadPool = new Thread[workerAmount]; threadWorkingState = new boolean[workerAmount]; client = DavClientProvider.getInstance().getClientInstance(account, getApplicationContext()); handler = new OperationHandler(this); Loading @@ -97,11 +96,20 @@ public class SynchronizationService extends Service implements OnRemoteOperation } public void queueOperation(SyncRequest request){ for (SyncWrapper syncWrapper : startedSync.values()) { if (syncWrapper.equals(request)) { return; } } syncRequestQueue.remove(request); syncRequestQueue.add(request); } public void queueOperations(Collection<SyncRequest> requests){ for (SyncWrapper syncWrapper : startedSync.values()) { requests.removeIf(syncRequest -> syncWrapper.equals(syncRequest)); } syncRequestQueue.removeAll(requests); syncRequestQueue.addAll(requests); } Loading @@ -113,37 +121,55 @@ public class SynchronizationService extends Service implements OnRemoteOperation } } private void startWorker(int threadIndex){ final boolean meteredNetworkAllowed = CommonUtils.isMeteredNetworkAllowed(account); if (threadWorkingState[threadIndex] || !CommonUtils.haveNetworkConnection(getApplicationContext(), meteredNetworkAllowed)) { return; } private void startWorker(Integer threadIndex){ if (!canStart(threadIndex)) return; final SyncRequest request = this.syncRequestQueue.poll(); //return null if empty if (request == null) return; final RemoteOperation operation = this.createRemoteOperation(request); final SyncWrapper syncWrapper = new SyncWrapper(request, getApplicationContext()); final RemoteOperation operation = syncWrapper.getRemoteOperation(); if (operation != null) { Log.v(TAG, " an operation has been poll from queue"); if (CommonUtils.isThisSyncAllowed(account, request.getSyncedFileState().isMediaType())) { CommonUtils.createNotificationChannel(this); startedOperations.put(operation, threadIndex); Log.v(TAG, " starts " + request.getSyncedFileState().getName() + " " + request.getOperationType().name() + " on thread " + threadIndex); threadPool[threadIndex] = operation.execute(client, this, handler); threadWorkingState[threadIndex] = true; startedSync.put(threadIndex, syncWrapper); } } } /** * Check if conditions are met to run a new file transfer * @param threadIndex index of thread on which we want to perform the transfer * @return false if nogo */ private boolean canStart(int threadIndex) { final boolean meteredNetworkAllowed = CommonUtils.isMeteredNetworkAllowed(account); final SyncWrapper syncWrapper = startedSync.get(threadIndex); if ( (syncWrapper != null && syncWrapper.isRunning()) || !CommonUtils.haveNetworkConnection(getApplicationContext(), meteredNetworkAllowed)) { return false; } return true; } @Override public void onRemoteOperationFinish(RemoteOperation callerOperation, RemoteOperationResult result) { Log.d(TAG, "onRemoteOperationFinish()"); Integer threadIndex = this.startedOperations.remove(callerOperation); if (threadIndex != null) { this.threadWorkingState[threadIndex] = false; this.startWorker(threadIndex); for (Map.Entry<Integer, SyncWrapper> keyValue : startedSync.entrySet()) { if (keyValue.getValue().getRemoteOperation().equals(callerOperation)) { keyValue.getValue().setRunning(false); startWorker(keyValue.getKey()); } } if (callerOperation instanceof RemoveFileOperation){ Loading Loading @@ -209,26 +235,6 @@ public class SynchronizationService extends Service implements OnRemoteOperation } } private RemoteOperation createRemoteOperation(SyncRequest request){ RemoteOperation operation; switch (request.getOperationType()){ case UPLOAD: final SyncedFileState sfs = request.getSyncedFileState(); operation = new UploadFileOperation(sfs, getApplicationContext()); break; case DOWNLOAD: final DownloadRequest downloadRequest = (DownloadRequest) request; operation = new DownloadFileOperation(downloadRequest.getRemoteFile(), downloadRequest.getSyncedFileState(), getApplicationContext()); break; case REMOTE_DELETE: default: operation = null; break; } return operation; } /** * Handler for the class */ Loading @@ -244,10 +250,12 @@ public class SynchronizationService extends Service implements OnRemoteOperation @Override public void handleMessage(Message msg) { Log.i(TAG, "handler.handleMessage()"); Bundle data = msg.getData(); serviceWeakRef.get() .threadWorkingState[data.getInt("thread index")] = data.getBoolean("mThreadWorkingState"); final Bundle data = msg.getData(); if (data == null || !data.containsKey("thread index") || !data.containsKey("mThreadWorkingState")) { return ; } final SyncWrapper syncWrapper = serviceWeakRef.get().startedSync.get(data.getInt("thread index")); if (syncWrapper != null) syncWrapper.setRunning(data.getBoolean("mThreadWorkingState")); } } Loading