diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 6ba265a284592e3672280c4bdf7c0aa14a25109d..23340a40daf3f2dd49bb207dbe0b5aebb97d7a0f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,4 +1,4 @@ -image: "registry.gitlab.e.foundation:5000/e/apps/docker-android-apps-cicd:latest" +image: "registry.gitlab.e.foundation:5000/e/apps/docker-android-apps-cicd:legacy" stages: - build @@ -22,4 +22,3 @@ build: paths: - app/build/outputs/apk/ expire_in: 4 weeks - diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 0d1e0f00b9396114083b74981077a569fae1e6d0..975ed48ec892b0d6cfeb8d3ba7665739d5d12ada 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -18,15 +18,19 @@ http://www.gnu.org/licenses/gpl.html + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/java/foundation/e/drive/jobs/ScannerJob.java b/app/src/main/java/foundation/e/drive/jobs/ScannerJob.java index 878f554b882119cefd4795e8131d2660ddcba33a..762c7cd758af64e7842d67184b20c0ce4e4bf7bb 100644 --- a/app/src/main/java/foundation/e/drive/jobs/ScannerJob.java +++ b/app/src/main/java/foundation/e/drive/jobs/ScannerJob.java @@ -13,7 +13,10 @@ import android.app.job.JobService; import android.content.Intent; import android.content.IntentFilter; import android.util.Log; + +import foundation.e.drive.receivers.ConnectivityReceiver; import foundation.e.drive.receivers.ScreenOffReceiver; +import foundation.e.drive.services.InitializerService; import foundation.e.drive.services.ObserverService; import foundation.e.drive.utils.CommonUtils; @@ -35,7 +38,13 @@ public class ScannerJob extends JobService { Intent observerServiceIntent = new Intent(this, ObserverService.class); this.startService(observerServiceIntent); jobFinished(params, false); + + if(!ConnectivityReceiver.isConnected()){ + InitializerService.fileObserverFlag=false; + } return true; + + } /** diff --git a/app/src/main/java/foundation/e/drive/models/FileObserver.java b/app/src/main/java/foundation/e/drive/models/FileObserver.java new file mode 100644 index 0000000000000000000000000000000000000000..9b5e76fc2cc3509c1fd0d9e5e3dc4d2df3a20d50 --- /dev/null +++ b/app/src/main/java/foundation/e/drive/models/FileObserver.java @@ -0,0 +1,33 @@ +package foundation.e.drive.models; + +import java.io.File; +import java.io.Serializable; +import java.util.List; + +public class FileObserver implements Serializable { + + private List files; + + //private List syncedFileStatesList; + + public FileObserver(List files) { + this.files = files; + + } + + public List getFiles() { + return files; + } + + public void setFiles(List files) { + this.files = files; + } + +// public List getSyncedFileStatesList() { +// return syncedFileStatesList; +// } +// +// public void setSyncedFileStatesList(List syncedFileStatesList) { +// this.syncedFileStatesList = syncedFileStatesList; +// } +} diff --git a/app/src/main/java/foundation/e/drive/models/SyncedFileState.java b/app/src/main/java/foundation/e/drive/models/SyncedFileState.java index 42c76a102482a4681845082e752f1edf57211f3d..3b9255be143e3b637c1686beff0ce6d5b7d4ee8e 100644 --- a/app/src/main/java/foundation/e/drive/models/SyncedFileState.java +++ b/app/src/main/java/foundation/e/drive/models/SyncedFileState.java @@ -11,12 +11,14 @@ package foundation.e.drive.models; import android.os.Parcel; import android.os.Parcelable; +import java.io.Serializable; + /** * @author Vincent Bourgmayer * Describe a file state which will be Synchronized (= Synced) or which has already been synced one times */ -public class SyncedFileState implements Parcelable { +public class SyncedFileState implements Parcelable, Serializable { protected SyncedFileState(){}; //@ToRemove. Test Only. It's to allow to make a mock SyncedFileState Class in test. diff --git a/app/src/main/java/foundation/e/drive/operations/DownloadFileRemoteOperation.java b/app/src/main/java/foundation/e/drive/operations/DownloadFileRemoteOperation.java index dce9dff620ad4ab620450b046dcd50eb94fb49e5..c8d8a211d5e5865c504f9002ed670b84cb20ec0a 100644 --- a/app/src/main/java/foundation/e/drive/operations/DownloadFileRemoteOperation.java +++ b/app/src/main/java/foundation/e/drive/operations/DownloadFileRemoteOperation.java @@ -24,6 +24,8 @@ */ package foundation.e.drive.operations; +import android.util.Log; + import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.network.WebdavUtils; import com.owncloud.android.lib.common.operations.OperationCancelledException; @@ -40,6 +42,8 @@ import java.io.IOException; import java.util.Date; import java.util.concurrent.atomic.AtomicBoolean; +import foundation.e.drive.services.InitializerService; + /** * Remote operation performing the download of a remote file in the ownCloud server. * @@ -77,6 +81,7 @@ class DownloadFileRemoteOperation extends RemoteOperation { tmpFile.getParentFile().mkdirs(); int status = downloadFile(client, tmpFile); result = new RemoteOperationResult(isSuccess(status), mGet); + Log_OC.i(TAG, "Download of " + mRemotePath + " to " + getTmpPath() + ": " + result.getLogMessage()); @@ -120,6 +125,9 @@ class DownloadFileRemoteOperation extends RemoteOperation { fos.write(bytes, 0, readResult); transferred += readResult; } + // Log.e(TAG, "...DownLoad ...."+targetFile.getName()); + InitializerService.remoteDownloadFile.add(targetFile); + // Check if the file is completed // if transfer-encoding: chunked we cannot check if the file is complete Header transferEncodingHeader = mGet.getResponseHeader("Transfer-Encoding"); diff --git a/app/src/main/java/foundation/e/drive/operations/UploadFileOperation.java b/app/src/main/java/foundation/e/drive/operations/UploadFileOperation.java index 88276a6b9703b773b4bdaf40e115b3b11e4e59e0..44495f7782ba3add57ecf1c6eeb9fd87bb055e95 100644 --- a/app/src/main/java/foundation/e/drive/operations/UploadFileOperation.java +++ b/app/src/main/java/foundation/e/drive/operations/UploadFileOperation.java @@ -23,8 +23,13 @@ import com.owncloud.android.lib.resources.users.GetRemoteUserInfoOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; import java.io.File; import java.util.ArrayList; +import java.util.Iterator; + import foundation.e.drive.database.DbHelper; +import foundation.e.drive.models.FileObserver; import foundation.e.drive.models.SyncedFileState; +import foundation.e.drive.services.FileObserverService; +import foundation.e.drive.services.InitializerService; import foundation.e.drive.utils.CommonUtils; /** @@ -136,6 +141,13 @@ public class UploadFileOperation extends RemoteOperation implements ComparableOp //if upload is a success if( uploadResult.isSuccess() ){ + + try { + FileObserverService.files.remove(file); + }catch (Exception ex){ + ex.printStackTrace(); + } + Object data = uploadResult.getSingleData(); if(data != null){ mSyncedState.setLastETAG((String) data); diff --git a/app/src/main/java/foundation/e/drive/receivers/ConnectivityReceiver.java b/app/src/main/java/foundation/e/drive/receivers/ConnectivityReceiver.java new file mode 100644 index 0000000000000000000000000000000000000000..d6c7b25f07bfeecc9becdc97c9f78a7d46037394 --- /dev/null +++ b/app/src/main/java/foundation/e/drive/receivers/ConnectivityReceiver.java @@ -0,0 +1,92 @@ +package foundation.e.drive.receivers; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.os.Bundle; +import android.os.Handler; +import android.util.Log; + +import java.io.IOException; + +import foundation.e.drive.services.FileObserverService; +import foundation.e.drive.services.InitializerService; +import foundation.e.drive.services.MyApplication; + +public class ConnectivityReceiver + extends BroadcastReceiver { + private final static String TAG = ConnectivityReceiver.class.getSimpleName(); + public static ConnectivityReceiverListener connectivityReceiverListener; + + public ConnectivityReceiver() { + super(); + } + + public boolean isConnected; + + public static boolean isConnected() { + + + try { + String command = "ping -c 1 ecloud.global"; + return Runtime.getRuntime().exec(command).waitFor() == 0; + }catch (Exception ex){ + ex.printStackTrace(); + } + return false; + } + + + + @Override + public void onReceive(final Context context, Intent arg1) { + ConnectivityManager cm = (ConnectivityManager) context + .getSystemService(Context.CONNECTIVITY_SERVICE); + final NetworkInfo activeNetwork = cm.getActiveNetworkInfo(); + + + final Handler handler = new Handler(); + handler.postDelayed(new Runnable() { + @Override + public void run() { + isConnected = activeNetwork != null + && activeNetwork.isConnectedOrConnecting(); + + Log.e(TAG, "connectivityReceiverListener..." + isConnected()); + if (connectivityReceiverListener != null) { + connectivityReceiverListener.onNetworkConnectionChanged(isConnected); + } + + if (isConnected()) { + Intent observersServiceIntent = new Intent(context, foundation.e.drive.services.ObserverService.class); + if (InitializerService.schedulerFlag) { + + context.startService(observersServiceIntent); + + } else if (InitializerService.fileObserverFlag) { + // + Bundle mBundle = new Bundle(); + mBundle.putBoolean("isFileObserverService", true); + observersServiceIntent.putExtras(mBundle); + context.startService(observersServiceIntent); + } + + InitializerService.schedulerFlag = false; + InitializerService.fileObserverFlag = false; + } + } + }, 20000); + + //if (connectivityReceiverListener != null) { + Log.e(TAG, "ConnectivityReceiver onNetworkConnectionChanged...." + isConnected); + //connectivityReceiverListener.onNetworkConnectionChanged(isConnected); + //} + + } + + public interface ConnectivityReceiverListener { + void onNetworkConnectionChanged(boolean isConnected); + } +} \ No newline at end of file diff --git a/app/src/main/java/foundation/e/drive/receivers/ScreenOffReceiver.java b/app/src/main/java/foundation/e/drive/receivers/ScreenOffReceiver.java index 27d489598d6fb2b579ddd671612ac7a87c96541f..a294c1b50537d22d02c54e7a4840b6ad2e86861c 100644 --- a/app/src/main/java/foundation/e/drive/receivers/ScreenOffReceiver.java +++ b/app/src/main/java/foundation/e/drive/receivers/ScreenOffReceiver.java @@ -11,7 +11,11 @@ package foundation.e.drive.receivers; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.os.Bundle; import android.util.Log; + +import foundation.e.drive.services.FileObserverService; +import foundation.e.drive.services.InitializerService; import foundation.e.drive.services.ObserverService; import foundation.e.drive.utils.CommonUtils; @@ -38,14 +42,6 @@ public class ScreenOffReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Log.i(TAG, "onReceive"); - String intentAction = intent.getAction(); - if(intentAction == null){ - Log.e(TAG, "intent Action is null"); - } else if ( intent.getAction().equals(Intent.ACTION_SCREEN_OFF) - && CommonUtils.haveNetworkConnexion( context ) ) { - Log.d(TAG, "onReceive: ACTION_SCREEN_OFF"); - Intent cloudIntent = new Intent(context, ObserverService.class); - context.startService(cloudIntent); - } + } } diff --git a/app/src/main/java/foundation/e/drive/services/FileObserverService.java b/app/src/main/java/foundation/e/drive/services/FileObserverService.java new file mode 100644 index 0000000000000000000000000000000000000000..a54fe622becb193ad8e70fc42ed935e6076aedf2 --- /dev/null +++ b/app/src/main/java/foundation/e/drive/services/FileObserverService.java @@ -0,0 +1,189 @@ +package foundation.e.drive.services; + +import android.app.ActivityManager; +import android.app.Service; +import android.content.Intent; +import android.os.AsyncTask; +import android.os.Build; +import android.os.Bundle; +import android.os.Environment; + +import android.os.FileObserver; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.support.annotation.Nullable; +import android.util.Log; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import foundation.e.drive.receivers.ConnectivityReceiver; +import foundation.e.drive.utils.CommonUtils; +import foundation.e.drive.utils.RecursiveFileObserver; + + + +public class FileObserverService extends Service { + + private final static String TAG = FileObserverService.class.getSimpleName(); + RecursiveFileObserver mFileObserver = null; + private int observerFlag=-1; + public static List files=new CopyOnWriteArrayList<>(); + private boolean remoteFileFlag; + + final Handler handler = new Handler(); + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + Log.d("obService", "started"); + super.onStartCommand(intent, flags, startId); + startTask(); + return START_STICKY; + } + + @Override + public void onCreate() { + super.onCreate(); + + } + + private void startTask() { + + String path = Environment.getExternalStorageDirectory().getAbsolutePath(); + Log.d("obService", path); + + + + mFileObserver = new RecursiveFileObserver(getApplicationContext(), path, new RecursiveFileObserver.EventListener() { + @Override + public void onEvent(int event, File file) { + + if(event==FileObserver.CLOSE_WRITE){ + +// Log.e(TAG, "...CLOSE_WRITE ..." + event+"...file ..." + file); +// } +// +// +// //Modify =2, create =256, delete =512, movedTo =128 +// if(event== FileObserver.CREATE || +// event==FileObserver.MODIFY || +// event== FileObserver.DELETE || +// event ==FileObserver.MOVED_TO){ + + Log.i(TAG, "...Event ..." + event+"...file ..." + file); + + remoteFileFlag=false; + if(!file.isDirectory() ){ + + for(File remoteFile:InitializerService.remoteDownloadFile){ + if(remoteFile.getName().equals(file.getName())){ + remoteFileFlag=true; + break; + } + } + + + + + if(!files.contains(file) && !remoteFileFlag){ + files.add(file); + } + else { + return; + } + if(!ConnectivityReceiver.isConnected()){ + InitializerService.fileObserverFlag=true; + } + try + { + if(observerFlag == -1){ + new AsyncTaskRunner().execute(""); + } + + + } + catch (Exception e) + { + e.printStackTrace(); + } + } + } + } + }); + + mFileObserver.startWatching(); + } + + @Nullable + @Override + public IBinder onBind(Intent intent) { + return null; + } + + @Override + public void onDestroy() { + Log.d("TAG", "............OnDestroy"); + + super.onDestroy(); + } + + + @Override + public void onTaskRemoved(Intent rootIntent) { + + Log.d(TAG, "............onTaskRemoved"); + super.onTaskRemoved(rootIntent); + Intent intent = new Intent(this, InitializerService.class); + this.startActivity(intent); + + } + + private class AsyncTaskRunner extends AsyncTask{ + + @Override + protected String doInBackground(String... strings) { + observerFlag=1; + return null; + } + + @Override + protected void onPostExecute(String s) { + super.onPostExecute(s); + + handler.postDelayed(new Runnable() { + @Override + public void run() { + // Do something after 5s = 5000ms + try { + Log.d(TAG, "calling observerServiceIntent via fileObserver.."); + Intent observersServiceIntent = new Intent(getApplicationContext(), foundation.e.drive.services.ObserverService.class); + Bundle mBundle = new Bundle(); + mBundle.putBoolean("isFileObserverService", true); + if(files.size()!=0) { + mBundle.putByteArray("fileObserverObject", CommonUtils.convertToBytes(new foundation.e.drive.models.FileObserver(files))); + } + + observersServiceIntent.putExtras(mBundle); + startService(observersServiceIntent); + }catch (Exception exception){ + exception.printStackTrace(); + } + observerFlag=-1; + } + }, 5000); + + + + + } + + } + + + + +} \ No newline at end of file diff --git a/app/src/main/java/foundation/e/drive/services/InitializerService.java b/app/src/main/java/foundation/e/drive/services/InitializerService.java index 607edf9d813afa30631a4ad6c3cc7bad87daf2b6..ff049c246e7eec82317aeba5498bec2f176a0289 100644 --- a/app/src/main/java/foundation/e/drive/services/InitializerService.java +++ b/app/src/main/java/foundation/e/drive/services/InitializerService.java @@ -15,6 +15,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; +import android.net.ConnectivityManager; import android.os.Build; import android.os.Environment; import android.os.Handler; @@ -27,12 +28,15 @@ import com.owncloud.android.lib.common.operations.OnRemoteOperationListener; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; +import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; import foundation.e.drive.models.SyncedFolder; import foundation.e.drive.operations.CreateInitialFolderRemoteOperation; +import foundation.e.drive.receivers.ConnectivityReceiver; import foundation.e.drive.receivers.ScreenOffReceiver; import foundation.e.drive.utils.AppConstants; import foundation.e.drive.utils.CommonUtils; @@ -47,7 +51,8 @@ import static foundation.e.drive.utils.AppConstants.SETTINGS_SYNCABLE_CATEGORIES /** * @author Vincent Bourgmayer */ -public class InitializerService extends Service implements OnRemoteOperationListener { +public class InitializerService extends Service + implements OnRemoteOperationListener, ConnectivityReceiver.ConnectivityReceiverListener { final private String TAG = InitializerService.class.getSimpleName(); //Complex properties private int existingRemoteFolderCounter; //@dev-only; Temporarily used to know if all remotePath exist @@ -56,6 +61,11 @@ public class InitializerService extends Service implements OnRemoteOperationList private Handler mHandler; private Account mAccount; private int restartFolderCreationCounter =0; + private ConnectivityReceiver connectivityReceiver; + + public static List remoteDownloadFile=new CopyOnWriteArrayList<>(); + public static boolean schedulerFlag=false; + public static boolean fileObserverFlag=false; @Override public void onCreate() { @@ -63,8 +73,16 @@ public class InitializerService extends Service implements OnRemoteOperationList super.onCreate(); this.existingRemoteFolderCounter = 0; //JobUtils.scheduleInitializerJob(getApplicationContext()); + connectivityReceiver = new ConnectivityReceiver(); + registerConnectivityReceiver(); + } + private void registerConnectivityReceiver() { + + registerReceiver(connectivityReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)); + + } @Override public int onStartCommand( Intent intent, int flags, int startId ) { Log.i(TAG, "onStartCommand(...)"); @@ -284,7 +302,7 @@ public class InitializerService extends Service implements OnRemoteOperationList .apply(); //all folder have been created - //JobUtils.stopScheduledJob(appContext, JobUtils.InitializerJobId); + JobUtils.stopScheduledJob(appContext, JobUtils.InitializerJobId); JobUtils.scheduleScannerJob(appContext); Log.d(TAG, "RegisterReceiver: screenOffReceiver"); @@ -296,6 +314,8 @@ public class InitializerService extends Service implements OnRemoteOperationList Intent observersServiceIntent = new Intent(getApplicationContext(), foundation.e.drive.services.ObserverService.class); startService(observersServiceIntent); + //start FileObserverService + startService(new Intent(this, FileObserverService.class)); stopSelf(); @@ -309,6 +329,14 @@ public class InitializerService extends Service implements OnRemoteOperationList this.mCloudClient = null; if(this.mSyncedFolders != null) this.mSyncedFolders.clear(); this.mSyncedFolders = null; + unregisterConnectivityReceiver(); + } + protected void unregisterConnectivityReceiver() { + try { + unregisterReceiver(connectivityReceiver); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } } @Nullable @@ -316,4 +344,12 @@ public class InitializerService extends Service implements OnRemoteOperationList public IBinder onBind(Intent intent) { return null; } + + @Override + public void onNetworkConnectionChanged(boolean isConnected) { + + //just for testing code for now + Log.e(TAG, "onNetworkConnectionChanged...." + isConnected); + + } } \ No newline at end of file diff --git a/app/src/main/java/foundation/e/drive/services/MyApplication.java b/app/src/main/java/foundation/e/drive/services/MyApplication.java new file mode 100644 index 0000000000000000000000000000000000000000..79ae6d83d3d151f591cdc38464ac92dec0636d5a --- /dev/null +++ b/app/src/main/java/foundation/e/drive/services/MyApplication.java @@ -0,0 +1,25 @@ +package foundation.e.drive.services; + +import android.app.Application; + +import foundation.e.drive.receivers.ConnectivityReceiver; + +public class MyApplication extends Application { + + private static MyApplication mInstance; + + @Override + public void onCreate() { + super.onCreate(); + + mInstance = this; + } + + public static synchronized MyApplication getInstance() { + return mInstance; + } + + public void setConnectivityListener(ConnectivityReceiver.ConnectivityReceiverListener listener) { + ConnectivityReceiver.connectivityReceiverListener = listener; + } +} \ No newline at end of file diff --git a/app/src/main/java/foundation/e/drive/services/ObserverService.java b/app/src/main/java/foundation/e/drive/services/ObserverService.java index 0cadf83c6381f3af6a8f861c12b9b59a78716d9b..d9f19de1d444248b7dbfadcf176370a0e10f842d 100644 --- a/app/src/main/java/foundation/e/drive/services/ObserverService.java +++ b/app/src/main/java/foundation/e/drive/services/ObserverService.java @@ -15,18 +15,21 @@ import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageInfo; +import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Parcelable; import android.provider.MediaStore; import android.support.annotation.Nullable; import android.util.Log; + import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.operations.OnRemoteOperationListener; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.resources.files.FileUtils; import com.owncloud.android.lib.resources.files.model.RemoteFile; + import java.io.File; import java.io.FileFilter; import java.io.FileOutputStream; @@ -40,12 +43,14 @@ import foundation.e.drive.database.DbHelper; import foundation.e.drive.fileFilters.CrashlogsFileFilter; import foundation.e.drive.fileFilters.FileFilterFactory; import foundation.e.drive.fileFilters.OnlyFileFilter; +import foundation.e.drive.models.FileObserver; import foundation.e.drive.models.SyncedFolder; import foundation.e.drive.models.SyncedFileState; import foundation.e.drive.operations.DownloadFileOperation; import foundation.e.drive.operations.ListFileRemoteOperation; import foundation.e.drive.operations.RemoveFileOperation; import foundation.e.drive.operations.UploadFileOperation; +import foundation.e.drive.receivers.ConnectivityReceiver; import foundation.e.drive.utils.AppConstants; import foundation.e.drive.utils.CommonUtils; import foundation.e.drive.utils.DavClientProvider; @@ -60,7 +65,7 @@ import static foundation.e.drive.utils.AppConstants.INITIALIZATION_HAS_BEEN_DONE * @author Vincent Bourgmayer * This service look for remote or looale file to synchronize */ -public class ObserverService extends Service implements OnRemoteOperationListener{ +public class ObserverService extends Service implements OnRemoteOperationListener { private final static String TAG = ObserverService.class.getSimpleName(); private final static int INTERSYNC_MINIMUM_DELAY = 900000; // min delay between two sync in ms. @@ -69,18 +74,31 @@ public class ObserverService extends Service implements OnRemoteOperationListene private int initialFolderCounter; private Account mAccount; private HashMap operationsForIntent; + private Boolean isFileObserverService = false; + private FileObserver fileObserverObject; + // private foundation.e.drive.models.FileObserver fileObserverObject; /* Lifecycle Methods */ @Override - public void onDestroy(){ + public void onDestroy() { Log.i(TAG, "onDestroy()"); super.onDestroy(); this.mSyncedFolders = null; } - + @Override public int onStartCommand(Intent intent, int flags, int startId) { - Log.i(TAG, "onStartCommand("+startId+")"); + Log.i(TAG, "onStartCommand(" + startId + ")"); + + try { + Bundle bundle = intent.getExtras(); + if (null != bundle) { + isFileObserverService = bundle.getBoolean("isFileObserverService"); + fileObserverObject = (foundation.e.drive.models.FileObserver) CommonUtils.convertFromBytes(bundle.getByteArray("fileObserverObject")); + } + } catch (Exception ex) { + ex.printStackTrace(); + } CommonUtils.setServiceUnCaughtExceptionHandler(this); @@ -91,14 +109,14 @@ public class ObserverService extends Service implements OnRemoteOperationListene initialFolderCounter = prefs.getInt(AppConstants.INITIALFOLDERS_NUMBER, 0); // Check if account is invalid - if(this.mAccount == null){ + if (this.mAccount == null) { Log.w(TAG, "No account registered"); JobUtils.stopScheduledJob(this, JobUtils.ScannerJobId); //If no account return super.onStartCommand(intent, flags, startId); } //check if user have disable eDrive's sync in account's settings - if(!CommonUtils.isMediaSyncEnabled(mAccount) && !CommonUtils.isSettingsSyncEnabled(mAccount) ){ + if (!CommonUtils.isMediaSyncEnabled(mAccount) && !CommonUtils.isSettingsSyncEnabled(mAccount)) { Log.w(TAG, "eDrive syncing has been disabled in /e/ account's settings"); return super.onStartCommand(intent, flags, startId); } @@ -108,19 +126,26 @@ public class ObserverService extends Service implements OnRemoteOperationListene Log.w(TAG, "Initialization hasn't been done"); Intent initializerIntent = new Intent(this, InitializerService.class); startService(initializerIntent); - return super.onStartCommand( intent, flags, startId ); + return super.onStartCommand(intent, flags, startId); } //Check this service isn't already working - if(isWorking){ + if (isWorking) { Log.w(TAG, "ObserverService is already working"); - return super.onStartCommand(intent,flags,startId); + getApplicationContext().stopService(new Intent(getApplicationContext(), ObserverService.class)); + // return super.onStartCommand(intent, flags, startId); + startService(new Intent(this, ObserverService.class)); } //check OperationManagerService isn't working - if(prefs.getBoolean(AppConstants.KEY_OMS_IS_WORKING, false)){ + if (prefs.getBoolean(AppConstants.KEY_OMS_IS_WORKING, false)) { Log.w(TAG, "OperationManagerService is still performing some operation"); - return super.onStartCommand(intent,flags, startId); + + getApplicationContext().stopService(new Intent(getApplicationContext(), OperationManagerService.class)); + if (operationsForIntent != null && !operationsForIntent.isEmpty()) { + startOperationManagerService(); + } + } //Check a minimum delay has been respected between two start. @@ -128,27 +153,28 @@ public class ObserverService extends Service implements OnRemoteOperationListene long currentTime = System.currentTimeMillis(); //if time diff between current sync and last sync is higher or equal to delay minimum between two sync - if( (currentTime - lastSyncTime ) < INTERSYNC_MINIMUM_DELAY ){ + /*if( (currentTime - lastSyncTime ) < INTERSYNC_MINIMUM_DELAY ){ Log.w(TAG, "Delay between now and last call is too short"); return super.onStartCommand( intent, flags, startId ); - } + }*/ //check for the case where intent has been launched by initializerService - if (!CommonUtils.haveNetworkConnexion(this)) { + if (!ConnectivityReceiver.isConnected()) { Log.w(TAG, "There is no Internet connexion."); - return super.onStartCommand( intent, flags, startId ); + return super.onStartCommand(intent, flags, startId); } this.operationsForIntent = new HashMap<>(); begin(); - return super.onStartCommand( intent, flags, startId ); + return super.onStartCommand(intent, flags, startId); } /* Common methods */ + /** * Start to bind this service to OperationManagerService or start scan if binding is already set. * Method to factorise code that is called from different place */ - private void begin(){ + private void begin() { Log.i(TAG, "begin()"); this.isWorking = true; clearCachedFile(); @@ -160,7 +186,7 @@ public class ObserverService extends Service implements OnRemoteOperationListene * This method remove all the crash-logs file * in external dir that are 10 days or more old. */ - private void deleteOldestCrashlogs(){ + private void deleteOldestCrashlogs() { Log.i(TAG, "deleteOldestCrashLogs()"); File[] fileToRemove = getExternalFilesDir(ServiceExceptionHandler.CRASH_LOG_FOLDER) .listFiles(new CrashlogsFileFilter()); @@ -170,48 +196,51 @@ public class ObserverService extends Service implements OnRemoteOperationListene try { file.delete(); ++counter; - }catch (SecurityException e){ + } catch (SecurityException e) { e.printStackTrace(); } } - Log.d(TAG, counter+" old crashlogs file.s deleted"); + Log.d(TAG, counter + " old crashlogs file.s deleted"); } /** * Clear cached file unused: * remove each cached file which isn't in OperationManagerService.lockedSyncedFileState(); + * * @TODO rewrite this method! */ - private void clearCachedFile(){ + private void clearCachedFile() { Log.i(TAG, "clearCachedFile()"); //Load subfiles into external cache file - File[] fileArray = this.getApplicationContext().getExternalCacheDir().listFiles(new OnlyFileFilter() ); - if(fileArray != null) { + File[] fileArray = this.getApplicationContext().getExternalCacheDir().listFiles(new OnlyFileFilter()); + if (fileArray != null) { boolean toRemove; for (int i = -1, size = fileArray.length; ++i < size; ) { toRemove = true; if (toRemove) { boolean deleteResult = fileArray[i].delete(); - Log.v(TAG+"_handleCachedFile()", "Deletion of cached file: " + deleteResult); + Log.v(TAG + "_handleCachedFile()", "Deletion of cached file: " + deleteResult); } } - }else{ - Log.e(TAG+"_handleCachedFile()", "Array of cached file is null"); + } else { + Log.e(TAG + "_handleCachedFile()", "Array of cached file is null"); } } /** * Start scanning job * Load operation from DB + * * @param remote if true looks for remote change. If false, look for local change. **/ private void startScan(boolean remote) { - Log.i(TAG, "startScan("+remote+")"); + Log.i(TAG, "startScan(" + remote + ")"); + this.mSyncedFolders = loadSyncedFolders(); - if(mSyncedFolders.isEmpty() ){ + if (mSyncedFolders.isEmpty()) { Log.w(TAG, "List of synced folders is empty"); this.stopSelf(); return; @@ -219,7 +248,7 @@ public class ObserverService extends Service implements OnRemoteOperationListene //Display content of SyncedFolderList StringBuilder logFolderList = new StringBuilder("SyncedFolder: libelle, localFolder, lastmodified, scanLocal, id :"); - for(SyncedFolder sf : mSyncedFolders){ + for (SyncedFolder sf : mSyncedFolders) { logFolderList.append("\n").append(sf.getLibelle()).append(", ").append(sf.getLocalFolder()).append(", ").append(sf.getLastModified()).append(", ").append(sf.isScanLocal()).append(", ").append(sf.getId()); } Log.d(TAG, logFolderList.toString()); @@ -231,52 +260,71 @@ public class ObserverService extends Service implements OnRemoteOperationListene Log.d(TAG, "Going to scan remote files"); ListFileRemoteOperation loadOperation = new ListFileRemoteOperation(this.mSyncedFolders, this, this.initialFolderCounter); loadOperation.execute(client, this, new Handler()); - } catch (IllegalArgumentException e){ - Log.e(TAG, e.toString() ); + } catch (IllegalArgumentException e) { + Log.e(TAG, e.toString()); } - }else{ + } else { Log.w(TAG, "OwnCloudClient is null"); return; } } else { - scanLocalFiles(); + + if (isFileObserverService && null != fileObserverObject) { + Log.i(TAG, "calling observer service from event event with true and files list data"); + // List files = fileObserverObject.getFiles(); + DbHelper.updateSyncedFolders(mSyncedFolders, this); //@ToDo: maybe do this when all contents will be synced. + + List syncedFileStates = DbHelper.getSyncedFileStatesByFolders(this, + getIdsFromFolderToScan()); + + if (!syncedFileStates.isEmpty() || !fileObserverObject.getFiles().isEmpty()) { + handleLocalFiles(fileObserverObject.getFiles(), syncedFileStates); + } + // handleLocalFiles(fileObserverObject.getFiles(), fileObserverObject.getSyncedFileStatesList()); + + } else { + Log.i(TAG, "calling observer service with false or files list data null "); + if(null!=FileObserverService.files){ + FileObserverService.files.removeAll(FileObserverService.files); + } + scanLocalFiles(); + } + } } /** * Get list of synced folder depending of if media and setting sync are enabled. + * * @return */ - private List loadSyncedFolders(){ + private List loadSyncedFolders() { boolean mediaSyncEnabled = CommonUtils.isMediaSyncEnabled(mAccount); boolean settingsSyncedEnabled = CommonUtils.isSettingsSyncEnabled(mAccount); - if(mediaSyncEnabled && settingsSyncedEnabled){ + if (mediaSyncEnabled && settingsSyncedEnabled) { return DbHelper.getAllSyncedFolders(this); - }else if(mediaSyncEnabled){ + } else if (mediaSyncEnabled) { return DbHelper.getSyncedFolderList(this, true); - }else if(settingsSyncedEnabled){ + } else if (settingsSyncedEnabled) { return DbHelper.getSyncedFolderList(this, false); - }else{ + } else { return new ArrayList(); } - } - - - - + } /** * Handle end of remote Operation + * * @param operation The RemoteOperation which ends and call this methods - * @param result The result of the remote Operation + * @param result The result of the remote Operation */ @Override - public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result ) { - Log.i( TAG, "onRemoteOperationFinish()" ); - if( operation instanceof ListFileRemoteOperation) { + public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) { + Log.i(TAG, "onRemoteOperationFinish()"); + if (operation instanceof ListFileRemoteOperation) { if (result.isSuccess()) { List resultDatas = result.getData(); if (resultDatas != null) { @@ -296,18 +344,23 @@ public class ObserverService extends Service implements OnRemoteOperationListene } this.startScan(false); - Log.v(TAG, "operationsForIntent contains "+ operationsForIntent.size() ); + Log.v(TAG, "operationsForIntent contains " + operationsForIntent.size()); //After everything has been scanned. Send Intent to OperationmanagerService with data in bundle - if(operationsForIntent != null && !operationsForIntent.isEmpty()) { - Intent OMSIntent = new Intent(this, OperationManagerService.class); - for(Map.Entry entry: operationsForIntent.entrySet()){ - OMSIntent.putExtra(entry.getKey()+"", entry.getValue()); - } + if (operationsForIntent != null && !operationsForIntent.isEmpty()) { +// Intent OMSIntent = new Intent(this, OperationManagerService.class); +// for (Map.Entry entry : operationsForIntent.entrySet()) { +// OMSIntent.putExtra(entry.getKey() + "", entry.getValue()); +// } +// +// OMSIntent.putExtra("account", mAccount); +// startService(OMSIntent); + + startOperationManagerService(); - OMSIntent.putExtra("account", mAccount); - startService(OMSIntent); - }else{ + + + } else { Log.w(TAG, "There is no file to sync."); getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE) .edit() @@ -321,34 +374,48 @@ public class ObserverService extends Service implements OnRemoteOperationListene } } + + /** * Method to get Id of SyncedFolder to scan + * * @return List id of SyncedFolder to scan */ - private List getIdsFromFolderToScan(){ + private List getIdsFromFolderToScan() { List result = new ArrayList<>(); - for(int i = -1, size = this.mSyncedFolders.size(); ++i < size;){ + for (int i = -1, size = this.mSyncedFolders.size(); ++i < size; ) { SyncedFolder syncedFolder = this.mSyncedFolders.get(i); - if(syncedFolder.isToSync() ){ - result.add( (long) syncedFolder.getId() ); + if (syncedFolder.isToSync()) { + result.add((long) syncedFolder.getId()); } } return result; } + private void startOperationManagerService() { + Intent OMSIntent = new Intent(this, OperationManagerService.class); + for (Map.Entry entry : operationsForIntent.entrySet()) { + OMSIntent.putExtra(entry.getKey() + "", entry.getValue()); + } + + OMSIntent.putExtra("account", mAccount); + startService(OMSIntent); + } + /* methods related to Server Scanning */ /** * decide what to do with remote files and decide - * @param remoteFiles Remote Files to inspect + * + * @param remoteFiles Remote Files to inspect * @param syncedFileStates SyncedFileState to inspect */ - private void handleRemoteFiles(List remoteFiles, List syncedFileStates ){ + private void handleRemoteFiles(List remoteFiles, List syncedFileStates) { Log.i(TAG, "handleRemoteFiles()"); Log.d(TAG, "start to loop through remoteFiles"); ListIterator syncedFileListIterator; - for( int i =-1, size = remoteFiles.size(); ++i < size; ){ + for (int i = -1, size = remoteFiles.size(); ++i < size; ) { final RemoteFile remoteFile = (RemoteFile) remoteFiles.get(i); final String remoteFilePath = remoteFile.getRemotePath(); @@ -357,29 +424,29 @@ public class ObserverService extends Service implements OnRemoteOperationListene boolean correspondant_found = false; syncedFileListIterator = syncedFileStates.listIterator(); //reset listiterator - Log.d( TAG, "start to loop through syncedFileList for: "+remoteFilePath); + Log.d(TAG, "start to loop through syncedFileList for: " + remoteFilePath); - while( syncedFileListIterator.hasNext() ){ + while (syncedFileListIterator.hasNext()) { SyncedFileState syncedFileState = syncedFileListIterator.next(); //ignore hidden file from db - if(syncedFileState.isMediaType() && syncedFileState.getName().startsWith(".")){ + if (syncedFileState.isMediaType() && syncedFileState.getName().startsWith(".")) { syncedFileListIterator.remove(); continue; } - Log.v(TAG, "syncedFileState.getRemotePath() :"+syncedFileState.getRemotePath() ); + Log.v(TAG, "syncedFileState.getRemotePath() :" + syncedFileState.getRemotePath()); //If syncedFileState match the remote file if (remoteFilePath.equals(syncedFileState.getRemotePath())) { - Log.d(TAG, "correspondant found for "+remoteFilePath ); + Log.d(TAG, "correspondant found for " + remoteFilePath); correspondant_found = true; if (syncedFileState.isLastEtagStored() //there is an etag stored - && (!remoteFile.getEtag().equals(syncedFileState.getLastETAG()) //If etag has changed - || syncedFileState.getLocalLastModified() == 0L)) { //File hasn't been downloaded + && (!remoteFile.getEtag().equals(syncedFileState.getLastETAG()) //If etag has changed + || syncedFileState.getLocalLastModified() == 0L)) { //File hasn't been downloaded - Log.v(TAG, "etag and localLastModified are valids for "+remoteFilePath ); + Log.v(TAG, "etag and localLastModified are valids for " + remoteFilePath); //compare size with local file if (remoteFile.getLength() == new File(syncedFileState.getLocalPath()).length()) { //length is 0 is file doesn't exist @@ -388,8 +455,8 @@ public class ObserverService extends Service implements OnRemoteOperationListene syncedFileState.setLastETAG(remoteFile.getEtag()); int affectedRows = DbHelper.manageSyncedFileStateDB(syncedFileState, "UPDATE", this); Log.v(TAG, affectedRows + " syncedFileState.s row in DB has been updated."); - }else { - Log.i(TAG, "Add download operation for file "+syncedFileState.getId()); + } else { + Log.i(TAG, "Add download operation for file " + syncedFileState.getId()); DownloadFileOperation downloadFileOperation = new DownloadFileOperation(remoteFile, syncedFileState); this.operationsForIntent.put(syncedFileState.getId(), downloadFileOperation); } @@ -399,7 +466,7 @@ public class ObserverService extends Service implements OnRemoteOperationListene } } - if( correspondant_found )continue; + if (correspondant_found) continue; //If we get here, RemoteFile is a new file to download Log.v(TAG, "SyncedFileState corresponding to remoteFile not found."); @@ -408,8 +475,8 @@ public class ObserverService extends Service implements OnRemoteOperationListene final String parentOfKnownPath = remoteFilePath.substring(0, remoteFilePath.lastIndexOf(FileUtils.PATH_SEPARATOR) + 1); //look for parent folder in SyncedFolders - for (int j = -1, mSyncedFolderSize = this.mSyncedFolders.size(); ++j < mSyncedFolderSize;) { - if ( mSyncedFolders.get(j).getRemoteFolder().equals( parentOfKnownPath ) ) { //We have found the parent folder + for (int j = -1, mSyncedFolderSize = this.mSyncedFolders.size(); ++j < mSyncedFolderSize; ) { + if (mSyncedFolders.get(j).getRemoteFolder().equals(parentOfKnownPath)) { //We have found the parent folder final SyncedFolder parentFolder = mSyncedFolders.get(j); String fileName = CommonUtils.getFileNameFromPath(remoteFilePath); //get remote file's name @@ -422,7 +489,7 @@ public class ObserverService extends Service implements OnRemoteOperationListene int storedId = DbHelper.manageSyncedFileStateDB(newRemoteFile, "INSERT", this); if (storedId > 0) { newRemoteFile.setId(storedId); - Log.i(TAG, "Add download operation for new file "+storedId); + Log.i(TAG, "Add download operation for new file " + storedId); //Create Download operation and add it into Bundle this.operationsForIntent.put(storedId, new DownloadFileOperation(remoteFile, newRemoteFile)); @@ -439,52 +506,53 @@ public class ObserverService extends Service implements OnRemoteOperationListene } //At this step, we finished to handle each remote file and we may still have synced file but without remote equivalent. // In most cases, we consider those files as remotly removed files. So we start to delete those local file. - Log.v( TAG, "Start to handle remotly missing file" ); - handleRemoteRemainingSyncedFileState( syncedFileStates ); + Log.v(TAG, "Start to handle remotly missing file"); + handleRemoteRemainingSyncedFileState(syncedFileStates); } /** * Handle the list of syncedFileState which don't have remoteFile anymore. + * * @param syncedFileStates SyncedFileState for which no remote equivalent has been found */ - private void handleRemoteRemainingSyncedFileState(List syncedFileStates){ + private void handleRemoteRemainingSyncedFileState(List syncedFileStates) { - Log.i( TAG, "handleRemoteRemainingSyncedFileState()" ); + Log.i(TAG, "handleRemoteRemainingSyncedFileState()"); //Loop through remaining file state - for(int i = -1, size = syncedFileStates.size(); ++i < size; ){ + for (int i = -1, size = syncedFileStates.size(); ++i < size; ) { SyncedFileState syncedFileState = syncedFileStates.get(i); - if( !CommonUtils.isThisSyncAllowed( mAccount, syncedFileState.isMediaType() ) ){ - Log.d(TAG, "Sync of current file: "+syncedFileState.getName()+" isn't allowed"); + if (!CommonUtils.isThisSyncAllowed(mAccount, syncedFileState.isMediaType())) { + Log.d(TAG, "Sync of current file: " + syncedFileState.getName() + " isn't allowed"); continue; } //Check that file has already been synced fully - if( syncedFileState.isLastEtagStored() && syncedFileState.getLocalLastModified() > 0L) { + if (syncedFileState.isLastEtagStored() && syncedFileState.getLocalLastModified() > 0L) { //Get local file - File file = new File( syncedFileStates.get(i).getLocalPath() ); + File file = new File(syncedFileStates.get(i).getLocalPath()); //Try to remove local file boolean fileExists = file.exists(); - if( fileExists) { - Log.d(TAG, file.getName()+" exists *1"); + if (fileExists) { + Log.d(TAG, file.getName() + " exists *1"); //delete file int rowAffected = getContentResolver().delete(MediaStore.Files.getContentUri("external"), MediaStore.Files.FileColumns.DATA + "=?", new String[]{CommonUtils.getLocalPath(file)}); - Log.d(TAG, "deleted rows by mediastore : "+rowAffected); + Log.d(TAG, "deleted rows by mediastore : " + rowAffected); //sometimes (it seems to be relative to file's type) mediastore don't remove local file from storage fileExists = !file.delete(); } //if it succeed, remove syncedFileState in DB - if(! fileExists ) { + if (!fileExists) { //It means that file has been correctly deleted from device. So update DB. if (DbHelper.manageSyncedFileStateDB(syncedFileState, "DELETE", this) <= 0) Log.e(TAG, "SyncedFileState row hasn't been deleted from DB"); - }else - Log.w(TAG, "local file:"+file.getName()+" still exist and can't be remove"); + } else + Log.w(TAG, "local file:" + file.getName() + " still exist and can't be remove"); } } } @@ -496,74 +564,74 @@ public class ObserverService extends Service implements OnRemoteOperationListene * Generate a .txt file containing list of all installed packages with their version name * I.e : " com.android.my_example_package,7.1.2 " */ - private void generateAppListFile(){ + private void generateAppListFile() { Log.i(TAG, "generateAppListFile()"); List packagesInfo = getPackageManager().getInstalledPackages(0); StringBuilder fileContents = new StringBuilder(); - for( int i =-1, size = packagesInfo.size(); ++i < size; ){ + for (int i = -1, size = packagesInfo.size(); ++i < size; ) { PackageInfo currentPackage = packagesInfo.get(i); - fileContents.append( currentPackage.packageName).append(",").append(currentPackage.versionName).append("\n"); + fileContents.append(currentPackage.packageName).append(",").append(currentPackage.versionName).append("\n"); } try { FileOutputStream tmp = openFileOutput(AppConstants.APPLICATIONS_LIST_FILE_NAME_TMP, Context.MODE_PRIVATE); tmp.write(fileContents.toString().getBytes()); tmp.close(); - String filesdir = getFilesDir().getCanonicalPath()+PATH_SEPARATOR; - File tmp_file = new File(filesdir+AppConstants.APPLICATIONS_LIST_FILE_NAME_TMP); - File real_file = new File(filesdir+AppConstants.APPLICATIONS_LIST_FILE_NAME); + String filesdir = getFilesDir().getCanonicalPath() + PATH_SEPARATOR; + File tmp_file = new File(filesdir + AppConstants.APPLICATIONS_LIST_FILE_NAME_TMP); + File real_file = new File(filesdir + AppConstants.APPLICATIONS_LIST_FILE_NAME); - if ( tmp_file.length() != real_file.length() ) { + if (tmp_file.length() != real_file.length()) { //only use tmp file if content has changed tmp_file.renameTo(real_file); } else { tmp_file.delete(); } } catch (Exception e) { - Log.e(TAG, "Can't save file with package list: "+e.toString()); + Log.e(TAG, "Can't save file with package list: " + e.toString()); } } /** * Prepare the list of files and SyncedFileState for synchronisation */ - private void scanLocalFiles(){ - Log.i( TAG, "scanLocalFiles()" ); + private void scanLocalFiles() { + List fileList = new ArrayList<>(); - List folderIdList= new ArrayList<>(); + List folderIdList = new ArrayList<>(); boolean contentToSyncFound = false; //Regenere list of application's package - if(CommonUtils.isSettingsSyncEnabled(mAccount)) generateAppListFile(); + if (CommonUtils.isSettingsSyncEnabled(mAccount)) generateAppListFile(); - ListIterator iterator = mSyncedFolders.listIterator() ; + ListIterator iterator = mSyncedFolders.listIterator(); //Loop through folders - while(iterator.hasNext() ){ + while (iterator.hasNext()) { SyncedFolder syncedFolder = iterator.next(); - Log.d(TAG, "SyncedFolder :"+syncedFolder.getLibelle()+", "+syncedFolder.getLocalFolder()+", "+syncedFolder.getLastModified()+", "+syncedFolder.isScanLocal()+", "+syncedFolder.getId() ); + Log.d(TAG, "SyncedFolder :" + syncedFolder.getLibelle() + ", " + syncedFolder.getLocalFolder() + ", " + syncedFolder.getLastModified() + ", " + syncedFolder.isScanLocal() + ", " + syncedFolder.getId()); //Check it's not a hidden file - if(syncedFolder.isMediaType() && CommonUtils.getFileNameFromPath(syncedFolder.getLocalFolder()).startsWith(".")){ + if (syncedFolder.isMediaType() && CommonUtils.getFileNameFromPath(syncedFolder.getLocalFolder()).startsWith(".")) { iterator.remove(); continue; } //Check it can be scann from local - if(!syncedFolder.isScanLocal()){ + if (!syncedFolder.isScanLocal()) { iterator.remove(); continue; } //Check if it's a new folder - if ( syncedFolder.getId() == -1) { + if (syncedFolder.getId() == -1) { Log.v(TAG, "This is a new folder, we must register it"); //persist new syncedFolder int syncedFolder_id = (int) DbHelper.insertSyncedFolder(syncedFolder, this); //It will return -1 if there is an error, like an already existing folder with same value if (syncedFolder_id > 0) { Log.v(TAG, "Folder has been registered in DB"); syncedFolder.setId(syncedFolder_id); - }else { + } else { Log.w(TAG, "syncedFolder " + syncedFolder.getLocalFolder() + " remove iterator because it hasn't been registered in DB or already stored"); iterator.remove(); continue; @@ -571,38 +639,38 @@ public class ObserverService extends Service implements OnRemoteOperationListene } //Get local folder corresponding File localFolder = new File(syncedFolder.getLocalFolder()); //Obtention du fichier local - Log.d(TAG, "Local Folder (last modified / exists): "+localFolder.lastModified()+", "+localFolder.exists() ); + Log.d(TAG, "Local Folder (last modified / exists): " + localFolder.lastModified() + ", " + localFolder.exists()); //Check if local folder exists - if(!localFolder.exists()){ + if (!localFolder.exists()) { Log.v(TAG, "local folder doesn't exist anymore . So content has change"); contentToSyncFound = true; - folderIdList.add( (long) syncedFolder.getId() ); + folderIdList.add((long) syncedFolder.getId()); continue; } boolean folderHasChange = false; //consider by default that file hadn't change //Check if folder had change - if(localFolder.lastModified() > syncedFolder.getLastModified() ) { //compare last modified date + if (localFolder.lastModified() > syncedFolder.getLastModified()) { //compare last modified date Log.v(TAG, "local folder has changed"); - syncedFolder.setLastModified( localFolder.lastModified() ); //@Todo: it would be better to set it after all it's content has been synced + syncedFolder.setLastModified(localFolder.lastModified()); //@Todo: it would be better to set it after all it's content has been synced contentToSyncFound = true; //at least one dir has changed folderHasChange = true; //The folder has change - folderIdList.add( (long) syncedFolder.getId() ); //add id into list of modified folder + folderIdList.add((long) syncedFolder.getId()); //add id into list of modified folder } //Get sub files - final FileFilter filter = FileFilterFactory.getFileFilter( (syncedFolder.isMediaType()) ? "media" : syncedFolder.getLibelle() ); + final FileFilter filter = FileFilterFactory.getFileFilter((syncedFolder.isMediaType()) ? "media" : syncedFolder.getLibelle()); File[] subElements = localFolder.listFiles(filter); //hiden media files are being ignored Log.v(TAG, "loop through subfiles"); - for (int i = -1, subEltSize = (subElements != null)? subElements.length: 0; ++i < subEltSize; ) { + for (int i = -1, subEltSize = (subElements != null) ? subElements.length : 0; ++i < subEltSize; ) { File subElt = subElements[i]; - if(subElt == null) continue; + if (subElt == null) continue; if (subElt.isDirectory()) { //if its a subfolder add it to syncedFolder list //if a subfolder is found, add it to syncedFolder list - Log.v(TAG, "subfile "+subElt.getAbsolutePath()+" is a directory."); + Log.v(TAG, "subfile " + subElt.getAbsolutePath() + " is a directory."); SyncedFolder subSyncedFolder = new SyncedFolder(syncedFolder, subElt.getName() + FileUtils.PATH_SEPARATOR, 0L, "");//Need to put 0 into to be handled on next iterati iterator.add(subSyncedFolder); iterator.previous(); @@ -617,12 +685,13 @@ public class ObserverService extends Service implements OnRemoteOperationListene } //end of iterator loop - if(contentToSyncFound) { + if (contentToSyncFound) { DbHelper.updateSyncedFolders(mSyncedFolders, this); //@ToDo: maybe do this when all contents will be synced. + List syncedFileStates = DbHelper.getSyncedFileStatesByFolders(this, folderIdList); - if(!syncedFileStates.isEmpty() || !fileList.isEmpty() ) { + if (!syncedFileStates.isEmpty() || !fileList.isEmpty()) { handleLocalFiles(fileList, syncedFileStates); } } @@ -633,49 +702,50 @@ public class ObserverService extends Service implements OnRemoteOperationListene * If file has already be synced and modified since last synced then update (= upload) * if file has never been synced then upload * if file has already be on server once(=> SyncedFIleState.etag is valid) and not local file exist then remove syncedFile - * @param localFileList list of local file to scan + * + * @param localFileList list of local file to scan * @param syncedFileStates List of SyncedFileState to scan */ - private void handleLocalFiles(List localFileList, List syncedFileStates ){ - Log.i(TAG, "handleLocalFiles()"); + private void handleLocalFiles(List localFileList, List syncedFileStates) { Log.d(TAG, "Loop through local file list"); Log.v(TAG, "format: filePath, exist, lastModified) :"); + Log.e("TAG", "ObserverService..handleLocalFiles...."); //Loop through local files - for(int i =-1, localFilesSize = localFileList.size(); ++i < localFilesSize;){ + for (int i = -1, localFilesSize = localFileList.size(); ++i < localFilesSize; ) { File localFile = localFileList.get(i); - String filePath = CommonUtils.getLocalPath( localFile ); + String filePath = CommonUtils.getLocalPath(localFile); boolean correspondant_found = false; - Log.v(TAG, "Current file is "+filePath+", "+localFile.exists()+", "+localFile.lastModified() ); + Log.v(TAG, "Current file is " + filePath + ", " + localFile.exists() + ", " + localFile.lastModified()); ListIterator syncedFileListIterator = syncedFileStates.listIterator(); Log.d(TAG, "Loop through syncedFileStates "); Log.v(TAG, "format: (Path, Id, last Modified)"); - while( syncedFileListIterator.hasNext() ) { + while (syncedFileListIterator.hasNext()) { SyncedFileState syncedFileState = syncedFileListIterator.next(); //Ignore hidden media file store in DB - if(syncedFileState.isMediaType() && syncedFileState.getName().startsWith(".")){ + if (syncedFileState.isMediaType() && syncedFileState.getName().startsWith(".")) { syncedFileListIterator.remove(); continue; } - Log.v(TAG, syncedFileState.getLocalPath()+", "+syncedFileState.getId()+", "+syncedFileState.getLocalLastModified()); + Log.v(TAG, syncedFileState.getLocalPath() + ", " + syncedFileState.getId() + ", " + syncedFileState.getLocalLastModified()); //if syncedFileState correspond to local file - if( syncedFileState.getLocalPath().equals( filePath ) ){ + if (syncedFileState.getLocalPath().equals(filePath)) { correspondant_found = true; //If no etag is stored in sfs, the file hasn't been sync up to server. then do upload - if( syncedFileState.getLocalLastModified() < localFile.lastModified() || !syncedFileState.isLastEtagStored()){ - Log.d(TAG+"_handleLocalFiles()", syncedFileState.getName()+" file has been modified or never sync" ); + if (syncedFileState.getLocalLastModified() < localFile.lastModified() || !syncedFileState.isLastEtagStored()) { + Log.d(TAG + "_handleLocalFiles()", syncedFileState.getName() + " file has been modified or never sync"); boolean checkEtag = false; //Look for folder to know if the folder can be scan remotly. - for(int folderIndex =-1, size = mSyncedFolders.size();++folderIndex < size;) { + for (int folderIndex = -1, size = mSyncedFolders.size(); ++folderIndex < size; ) { final SyncedFolder syncedFolder = mSyncedFolders.get(folderIndex); if (syncedFolder.getId() == syncedFileState.getSyncedFolderId()) { @@ -684,8 +754,8 @@ public class ObserverService extends Service implements OnRemoteOperationListene break; } } - Log.i(TAG, "Add upload operation for file "+syncedFileState.getId()); - UploadFileOperation uploadFileOperation = new UploadFileOperation(syncedFileState, checkEtag ); + Log.i(TAG, "Add upload operation for file " + syncedFileState.getId()); + UploadFileOperation uploadFileOperation = new UploadFileOperation(syncedFileState, checkEtag); this.operationsForIntent.put(syncedFileState.getId(), uploadFileOperation); } // No need to reloop on it. @@ -693,7 +763,7 @@ public class ObserverService extends Service implements OnRemoteOperationListene break; } } - if( correspondant_found ) continue; + if (correspondant_found) continue; //if no correspondance, then it is a new file Log.v(TAG, "this is a new file to sync"); @@ -702,20 +772,20 @@ public class ObserverService extends Service implements OnRemoteOperationListene final String parentPath = filePath.substring(0, filePath.lastIndexOf(FileUtils.PATH_SEPARATOR) + 1); //look into synced folders if folder path exist - for(SyncedFolder syncedFolder : mSyncedFolders){ - if(syncedFolder.getLocalFolder().equals(parentPath)){ + for (SyncedFolder syncedFolder : mSyncedFolders) { + if (syncedFolder.getLocalFolder().equals(parentPath)) { //create the syncedFile State SyncedFileState newSyncedFileState = new SyncedFileState(-1, localFile.getName(), filePath, syncedFolder.getRemoteFolder() + localFile.getName(), "", 0, syncedFolder.getId(), syncedFolder.isMediaType()); //Store it in DB int storedId = DbHelper.manageSyncedFileStateDB(newSyncedFileState, "INSERT", this); - if(storedId > 0){ - newSyncedFileState.setId( storedId ); - Log.i(TAG, "Add upload operation for new file "+storedId); + if (storedId > 0) { + newSyncedFileState.setId(storedId); + Log.i(TAG, "Add upload operation for new file " + storedId); //create UploadOperation and add it into bundle UploadFileOperation uploadOperation = new UploadFileOperation(newSyncedFileState, syncedFolder.isScanRemote()); this.operationsForIntent.put(storedId, uploadOperation); - }else{ + } else { Log.w(TAG, "The new file to synced cannot be store in DB. Ignore it"); } break; @@ -723,25 +793,26 @@ public class ObserverService extends Service implements OnRemoteOperationListene }//end of loop over folder }//end of loop over local files //Handle remaining file - handleLocalRemainingSyncedFileState( syncedFileStates ); + handleLocalRemainingSyncedFileState(syncedFileStates); } /** * manage rest of the list of syncedFilesState to look for remote file to delete + * * @param syncedFileStates List of SyncedFileState for which no local equivalent has been found */ - private void handleLocalRemainingSyncedFileState(List syncedFileStates){ + private void handleLocalRemainingSyncedFileState(List syncedFileStates) { Log.i(TAG, "handleLocalRemainingSyncedFileState(...)"); //Loop through remaining SyncedFileState - for(SyncedFileState fileState : syncedFileStates){ - if(fileState.isLastEtagStored() && fileState.getLocalLastModified() > 0L){ + for (SyncedFileState fileState : syncedFileStates) { + if (fileState.isLastEtagStored() && fileState.getLocalLastModified() > 0L) { //try to get File File file = new File(fileState.getLocalPath()); - Log.v(TAG, "File : "+file.getAbsolutePath()+","+file.exists()); - if(file.exists()){ + Log.v(TAG, "File : " + file.getAbsolutePath() + "," + file.exists()); + if (file.exists()) { Log.w(TAG, "The file still exist. There is a problem!"); - }else{ - Log.i(TAG, "Add remove operation for file "+fileState.getId()); + } else { + Log.i(TAG, "Add remove operation for file " + fileState.getId()); RemoveFileOperation removeOperation = new RemoveFileOperation(fileState); this.operationsForIntent.put(fileState.getId(), removeOperation); } diff --git a/app/src/main/java/foundation/e/drive/services/OperationManagerService.java b/app/src/main/java/foundation/e/drive/services/OperationManagerService.java index 7054df66db0bd165d717dd49c69309ba94625c32..c0613c7c5fc9440a98c5d7a83f00a3dff0ad8d03 100644 --- a/app/src/main/java/foundation/e/drive/services/OperationManagerService.java +++ b/app/src/main/java/foundation/e/drive/services/OperationManagerService.java @@ -15,12 +15,14 @@ import android.app.PendingIntent; import android.app.Service; import android.content.Context; import android.content.Intent; +import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Parcelable; import android.support.annotation.Nullable; +import android.support.v4.app.NotificationCompat; import android.util.Log; import com.owncloud.android.lib.common.OwnCloudClient; @@ -32,6 +34,7 @@ import java.lang.ref.WeakReference; import java.util.Hashtable; import java.util.concurrent.ConcurrentLinkedDeque; +import foundation.e.drive.R; import foundation.e.drive.database.DbHelper; import foundation.e.drive.operations.ComparableOperation; import foundation.e.drive.operations.DownloadFileOperation; @@ -44,6 +47,7 @@ import foundation.e.drive.utils.ServiceExceptionHandler; /** * @author Vincent Bourgmayer + * @author Narinder Rana * Service to do upload, remove and download operation. */ public class OperationManagerService extends Service implements OnRemoteOperationListener{ @@ -187,18 +191,9 @@ public class OperationManagerService extends Service implements OnRemoteOperatio //Case specific to UploadFileOperation Log.w(TAG, "Quota_EXCEEDED"); - NotificationManager nM = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE); + addNotification(); - Notification notif = new Notification.Builder(this, AppConstants.notificationChannelID) - .setContentIntent(PendingIntent.getActivity(getApplicationContext(), - 0, - new Intent(Intent.ACTION_VIEW, mClient.getBaseUri()), - 0)) - .setContentText("Your drive lacks of space. Tap to check " + mClient.getBaseUri()) - .setSmallIcon(android.R.drawable.stat_sys_warning) - .build(); - nM.notify(1,notif ); break; case FILE_NOT_FOUND: //Case specific to DownloadFileOperation @@ -213,6 +208,25 @@ public class OperationManagerService extends Service implements OnRemoteOperatio } //Close else } + /** + * send notification to inform user that he lacks space on ecloud + * Improvement idea: + * - add translatable message & title + * - make message & title to be a parameter of the method, so we could reuse the function somewhere + * - else with different notification. File conflict for example. + **/ + private void addNotification() { + NotificationCompat.Builder builder = + new NotificationCompat.Builder(this) + .setSmallIcon(android.R.drawable.stat_sys_warning) + .setContentTitle("Drive Quota Exceeded") + .setContentText("Your drive lacks of space. Tap to check "+ mClient.getBaseUri()); + // Add as notification + NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + manager.notify(0, builder.build()); + } + + /** * Handler for the class */ diff --git a/app/src/main/java/foundation/e/drive/utils/CommonUtils.java b/app/src/main/java/foundation/e/drive/utils/CommonUtils.java index 2d1c16e2075583f573ec17e3d5845d66938b3a93..98a9841bc5c894ba87c2e51e23e9297a9ee1a805 100644 --- a/app/src/main/java/foundation/e/drive/utils/CommonUtils.java +++ b/app/src/main/java/foundation/e/drive/utils/CommonUtils.java @@ -30,7 +30,12 @@ import com.owncloud.android.lib.common.OwnCloudClientManagerFactory; import com.owncloud.android.lib.common.accounts.AccountUtils; import com.owncloud.android.lib.resources.files.FileUtils; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import foundation.e.drive.receivers.ScreenOffReceiver; @@ -291,4 +296,26 @@ public abstract class CommonUtils { + "\n File can be read?: " + f.canRead() + "\n File can be written?: " + f.canWrite(); } + + /* + this function convert object to bytes + */ + public static byte[] convertToBytes(Object object) throws IOException { + try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutputStream out = new ObjectOutputStream(bos)) { + out.writeObject(object); + return bos.toByteArray(); + } + } + + /* + this function convert bytes to Object + */ + public static Object convertFromBytes(byte[] bytes) throws IOException, ClassNotFoundException { + try (ByteArrayInputStream bis = new ByteArrayInputStream(bytes); + ObjectInputStream in = new ObjectInputStream(bis)) { + return in.readObject(); + } + } + } \ No newline at end of file diff --git a/app/src/main/java/foundation/e/drive/utils/JobUtils.java b/app/src/main/java/foundation/e/drive/utils/JobUtils.java index 790427eb103bdd02a10a0b93fe760a1a2bc4c435..a83aedb8a515625c16fa300da0562751a3c45241 100644 --- a/app/src/main/java/foundation/e/drive/utils/JobUtils.java +++ b/app/src/main/java/foundation/e/drive/utils/JobUtils.java @@ -24,7 +24,7 @@ import foundation.e.drive.jobs.ScannerJob; public abstract class JobUtils { final private static String TAG = JobUtils.class.getSimpleName(); //Tag for log public final static int ScannerJobId = 3310; - //public final static int InitializerJobId = 3311; + public final static int InitializerJobId = 3311; /** * Start the scheduledJob for observing remote's folder @@ -32,11 +32,12 @@ public abstract class JobUtils { */ public static void scheduleScannerJob(Context context){ Log.i(TAG, "scheduleJob"); + /* I. Start periodic checkup */ ComponentName jobService = new ComponentName( context, ScannerJob.class ); JobInfo job = new JobInfo.Builder(ScannerJobId, jobService ) - .setPeriodic(1860000, 30000) //31git minutes and 30 secondes + .setPeriodic(7200000 , 30000) //2 hr .setPersisted(true) .setRequiredNetworkType( JobInfo.NETWORK_TYPE_ANY ) .build(); diff --git a/app/src/main/java/foundation/e/drive/utils/RecursiveFileObserver.java b/app/src/main/java/foundation/e/drive/utils/RecursiveFileObserver.java new file mode 100644 index 0000000000000000000000000000000000000000..c58afcbfad012c643657400c3dfdc1b0ba467bd9 --- /dev/null +++ b/app/src/main/java/foundation/e/drive/utils/RecursiveFileObserver.java @@ -0,0 +1,156 @@ +package foundation.e.drive.utils; + + +import java.io.File; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Stack; + +import android.content.Context; +import android.os.FileObserver; +import android.util.Log; + +import foundation.e.drive.database.DbHelper; +import foundation.e.drive.models.SyncedFolder; + +public class RecursiveFileObserver extends FileObserver { + + private final Map mObservers = new HashMap<>(); + + private final Context applicationContext; + private String mPath; + private int mMask; + private EventListener mListener; + + public interface EventListener { + void onEvent(int event, File file); + } + + public RecursiveFileObserver(Context applicationContext, String path, EventListener listener) { + this(applicationContext, path, ALL_EVENTS, listener); + + } + + public RecursiveFileObserver(Context applicationContext, String path, int mask, EventListener listener) { + super(path, mask); + mPath = path; + mMask = mask | FileObserver.CREATE | FileObserver.DELETE_SELF; + mListener = listener; + this.applicationContext=applicationContext; + } + + private void startWatching(String path) { + synchronized (mObservers) { + FileObserver observer = mObservers.remove(path); + if (observer != null) { + observer.stopWatching(); + } + observer = new SingleFileObserver(path, mMask); + observer.startWatching(); + mObservers.put(path, observer); + } + } + + @Override + public void startWatching() { + Stack stack = new Stack<>(); + //stack.push(mPath); + List mSyncedFolders =DbHelper.getAllSyncedFolders(applicationContext); + if(!mSyncedFolders.isEmpty()){ + for(SyncedFolder syncedFolder:mSyncedFolders){ + stack.push(syncedFolder.getLocalFolder()); + stack.push(syncedFolder.getRemoteFolder()); + } + } + + // Recursively watch all child directories + while (!stack.empty()) { + String parent = stack.pop(); + startWatching(parent); + + File path = new File(parent); + File[] files = path.listFiles(); + if (files != null) { + for (File file : files) { + if (watch(file)) { + stack.push(file.getAbsolutePath()); + } + } + } + } + } + + private boolean watch(File file) { + return file.isDirectory() && !file.getName().equals(".") && !file.getName().equals(".."); + } + + private void stopWatching(String path) { + synchronized (mObservers) { + FileObserver observer = mObservers.remove(path); + if (observer != null) { + observer.stopWatching(); + } + } + } + + @Override + public void stopWatching() { + synchronized (mObservers) { + for (FileObserver observer : mObservers.values()) { + observer.stopWatching(); + } + mObservers.clear(); + } + } + + @Override + public void onEvent(int event, String path) { + File file; + if (path == null) { + file = new File(mPath); + } else { + file = new File(mPath, path); + } + notify(event, file); + } + + private void notify(int event, File file) { + if (mListener != null) { + mListener.onEvent(event & FileObserver.ALL_EVENTS, file); + } + } + + private class SingleFileObserver extends FileObserver { + private String filePath; + + public SingleFileObserver(String path, int mask) { + super(path, mask); + filePath = path; + } + + @Override + public void onEvent(int event, String path) { + File file; + if (path == null) { + file = new File(filePath); + } else { + file = new File(filePath, path); + } + + switch (event & FileObserver.ALL_EVENTS) { + case DELETE_SELF: + RecursiveFileObserver.this.stopWatching(filePath); + break; + case CREATE: + if (watch(file)) { + RecursiveFileObserver.this.startWatching(file.getAbsolutePath()); + } + break; + } + + RecursiveFileObserver.this.notify(event, file); + } + } + +} \ No newline at end of file diff --git a/app/src/main/res/xml/provider_paths.xml b/app/src/main/res/xml/provider_paths.xml new file mode 100644 index 0000000000000000000000000000000000000000..ffa74ab5621d088e0a3b7e0fc94e0f0a7455a434 --- /dev/null +++ b/app/src/main/res/xml/provider_paths.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 13372aef5e24af05341d49695ee84e5f9b594659..1948b9074f1016d15d505d185bc3f73deb82d8c8 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 54f9e46602f2b5e51d1294b2d243fc60ca32c500..d2c45a4b26036a7b633459a2d8f9a0041850eea2 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Mon Aug 06 14:01:55 CEST 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.8-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 index 9d82f78915133e1c35a6ea51252590fb38efac2f..cccdd3d517fc5249beaefa600691cf150f2fa3e6 --- a/gradlew +++ b/gradlew @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh ############################################################################## ## @@ -6,20 +6,38 @@ ## ############################################################################## -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" -warn ( ) { +warn () { echo "$*" } -die ( ) { +die () { echo echo "$*" echo @@ -30,6 +48,7 @@ die ( ) { cygwin=false msys=false darwin=false +nonstop=false case "`uname`" in CYGWIN* ) cygwin=true @@ -40,26 +59,11 @@ case "`uname`" in MINGW* ) msys=true ;; + NONSTOP* ) + nonstop=true + ;; esac -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -85,7 +89,7 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then @@ -150,11 +154,19 @@ if $cygwin ; then esac fi -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " } -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 8a0b282aa6885fb573c106b3551f7275c5f17e8e..f9553162f122c71b34635112e717c3e733b5b212 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -8,14 +8,14 @@ @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome @@ -46,10 +46,9 @@ echo location of your Java installation. goto fail :init -@rem Get command-line arguments, handling Windowz variants +@rem Get command-line arguments, handling Windows variants if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args :win9xME_args @rem Slurp the command line arguments. @@ -60,11 +59,6 @@ set _SKIP=2 if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ :execute @rem Setup the command line