Commit acefd595 authored by vince-bourgmayer's avatar vince-bourgmayer
Browse files

merge OS_integration to eelo-0.1

parent e9e30d5f
Pipeline #1310 passed with stage
in 2 minutes and 1 second
image: "registry.gitlab.e.foundation:5000/e/apps/docker-android-apps-cicd:latest"
stages:
- build
before_script:
- export GRADLE_USER_HOME=$(pwd)/.gradle
- chmod +x ./gradlew
cache:
key: ${CI_PROJECT_ID}
paths:
- .gradle/
build:
stage: build
script:
- ./gradlew build
artifacts:
paths:
- app/build/outputs/apk/
......@@ -24,7 +24,7 @@
</value>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
......
This diff is collapsed.
......@@ -6,7 +6,7 @@
#### Actu:
This branch is up-to-date (2018-08-20).
This branch is for the last run before the closed bêta in august's end.
#### TODO:
......
......@@ -38,6 +38,11 @@ android {
}
}
lintOptions {
abortOnError false
}
testOptions {
unitTests.returnDefaultValues = true
}
......@@ -56,10 +61,10 @@ dependencies {
androidTestImplementation 'com.android.support.test:rules:1.0.2'
androidTestImplementation 'com.android.support:support-annotations:27.1.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
compile 'com.github.nextcloud:android-library:1.0.40'
compile('com.crashlytics.sdk.android:crashlytics:2.9.2@aar') {
api 'com.github.nextcloud:android-library:1.1.0'
api('com.crashlytics.sdk.android:crashlytics:2.9.2@aar') {
transitive = true;
}
compile 'com.android.support:support-annotations:27.1.1'
api 'com.android.support:support-annotations:27.1.1'
}
\ No newline at end of file
}
......@@ -8,43 +8,55 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> <!-- needed for PersistedJob -->
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_eelo"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_eelo_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".ui.SetupActivity">
android:supportsRtl="true">
<provider
android:authorities="io.eelo.drive.providers.MediasSyncProvider"
android:name="io.eelo.drive.providers.MediasSyncProvider"
android:label="Pictures and videos"
android:enabled="true"
android:exported="true"/>
<provider
android:authorities="io.eelo.drive.providers.SettingsSyncProvider"
android:name="io.eelo.drive.providers.SettingsSyncProvider"
android:label="Application settings"
android:enabled="true"
android:exported="true"/>
<service android:name=".services.InitializerService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<action android:name="drive.services.initializerService" />
</intent-filter>
<intent-filter>
<action android:name="io.eelo.drive.LAUNCHER" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<!--<activity android:name=".ui.SettingsActivity" android:label="Settings">
<intent-filter>
<action android:name="android.intent.action.APPLICATION_PREFERENCES"/>
<category android:name="android.intent.category.PREFERENCE"/>
</intent-filter>
</activity>-->
</service>
<service android:name=".jobs.ScannerJob"
android:permission="android.permission.BIND_JOB_SERVICE" />
<service android:name=".services.ObserverService"/>
<service android:name=".services.OperationManagerService"></service>
<service android:name=".services.ObserverService"
android:enabled="true"
android:exported="true">
<!--<intent-filter>
<action android:name="drive.services.observerService" />
</intent-filter>-->
</service>
<service android:name=".services.OperationManagerService"/>
<receiver android:name=".receivers.BatteryStateReceiver" android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.BATTERY_LOW"/>
<action android:name="android.intent.action.BATTERY_OKAY"/>
</intent-filter>
</receiver>
<meta-data
android:name="preloaded_fonts"
android:resource="@array/preloaded_fonts" />
<receiver android:name=".receivers.PackageEventReceiver" android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.MY_PACKAGE_REPLACED"/>
</intent-filter>
</receiver>
<meta-data
android:name="io.fabric.ApiKey"
android:value="63e8fd2ffc15b7b187bd369a9c0535e9fe151e8a" />
......
......@@ -101,8 +101,9 @@ public final class DbHelper extends SQLiteOpenHelper {
Log.d(TAG, "DB Delete for "+syncedFileState.getName() );
result = dao.delete( syncedFileState.getId() );
}
dao.close();
}
dao.close();
return result;
}
......@@ -129,16 +130,14 @@ public final class DbHelper extends SQLiteOpenHelper {
* @param context calling context to get DAO
* @param ids List<String> id of SyncedFolders
* @return null if DB opening failed, either return the list of SyncedFileState
* @return empty list if DB opening failed, either return the list of SyncedFileState
*/
public static List<SyncedFileState> getSyncedFileStatesByFolders(Context context, List<Long> ids) {//Connect to DB
SyncedFileStateDAO dao = openSyncedFileStateDAO(context, false);
if(dao == null){
return null;
return new ArrayList<>();
}
List<SyncedFileState> result = dao.getBySyncedFolderID(ids);
dao.close();
return result;
}
......@@ -169,24 +168,18 @@ public final class DbHelper extends SQLiteOpenHelper {
/**
* Load SyncedFolder's from DB
* @param context app or service activity
* @param filter use either "SCANLOCAL" to get locally scan-able SyncedFolder either "SCANREMOTE"
* to get remotely scan-able SyncedFolder or any other value to get "ENABLED" SyncedFolder
* @return List<Folder> a list of SyncedFolder from DB
*/
public static List<SyncedFolder> getSyncedFolders(Context context, String filter) {
public static List<SyncedFolder> getSyncedFolders(Context context) {
SyncedFolderDAO dao = openSyncedFolderDAO(context, false);
List<SyncedFolder> mSyncedFolder = new ArrayList<SyncedFolder>();
if(dao == null){
return mSyncedFolder;
} else if( filter.equals(SyncedFolderDAO.KEY_FILTER_LOCAL) ){
mSyncedFolder = dao.getAllSyncedFolders(SyncedFolderContract.SCANLOCAL);
} else if(filter.equals( SyncedFolderDAO.KEY_FILTER_REMOTE ) ){
mSyncedFolder = dao.getAllSyncedFolders(SyncedFolderContract.SCANREMOTE);
}else {
mSyncedFolder = dao.getAllSyncedFolders(SyncedFolderContract.ENABLED);
dao.close();
return mSyncedFolder;
}
dao.close();
return mSyncedFolder;
}
/**
......@@ -198,7 +191,8 @@ public final class DbHelper extends SQLiteOpenHelper {
public static long insertSyncedFolder(SyncedFolder mSyncedFolder, Context context){
SyncedFolderDAO dao = openSyncedFolderDAO(context, true);
if(dao == null){
return -1;
Log.d(TAG+"_insertSyncedFolder", "dao is null");
return -2;
}
long id = dao.insert(mSyncedFolder);
dao.close();
......
......@@ -8,9 +8,9 @@ import android.provider.BaseColumns;
*/
class SyncedFileStateContract implements BaseColumns{
/** Table structure **/
static final String TABLE_NAME ="synced_file_state";
static final String FILE_NAME ="file_name";
static final String LOCAL_PATH ="local_path";
......
......@@ -117,35 +117,26 @@ import io.eelo.drive.models.SyncedFileState;
* @return List<SyncedFileState> List of SyncedFileState filtered on syncedFolder ID.
*/
List<SyncedFileState> getBySyncedFolderID(List<Long> syncedFolderids){
StringBuilder queryBuilder = new StringBuilder().append("Select ")
.append(SyncedFileStateContract._ID)
.append(", ")
.append(SyncedFileStateContract.FILE_NAME)
.append(", ")
.append(SyncedFileStateContract.LOCAL_PATH)
.append(", ")
.append(SyncedFileStateContract.REMOTE_PATH)
.append(", ")
.append(SyncedFileStateContract.LAST_ETAG)
.append(", ")
.append(SyncedFileStateContract.LOCAL_LAST_MODIFIED)
.append(", ")
.append(SyncedFileStateContract.SYNCEDFOLDER_ID)
.append(" FROM ")
.append(SyncedFileStateContract.TABLE_NAME)
.append(" WHERE ");
int idsSize = syncedFolderids.size();
for(int i =0; i <idsSize; ++i){
queryBuilder.append(SyncedFileStateContract.SYNCEDFOLDER_ID);
queryBuilder.append(" = ")
.append(syncedFolderids.get(i));
if(i < idsSize-1 ){
queryBuilder.append(" OR ");
}
}
String query = queryBuilder.toString();
String query = "Select "
+SyncedFileStateContract._ID+", "
+SyncedFileStateContract.FILE_NAME+", "
+SyncedFileStateContract.LOCAL_PATH+", "
+SyncedFileStateContract.REMOTE_PATH+", "
+SyncedFileStateContract.LAST_ETAG+", "
+SyncedFileStateContract.LOCAL_LAST_MODIFIED+", "
+SyncedFileStateContract.SYNCEDFOLDER_ID
+" FROM "
+SyncedFileStateContract.TABLE_NAME;
if(syncedFolderids.size() > 0) {
query+=" WHERE ";
for (int i = -1, idsSize = syncedFolderids.size(); ++i < idsSize; ) {
query += SyncedFileStateContract.SYNCEDFOLDER_ID + " = " + syncedFolderids.get(i);
if (i < idsSize - 1) {
query += " OR ";
}
}
}
Log.d(TAG+"_getBySyncedFolderID(...)", query);
Cursor cursor = mDB.rawQuery(query, null);
cursor.moveToFirst();
......
......@@ -22,21 +22,20 @@ public class ScannerJob extends JobService {
Log.d(TAG, "RegisterReceiver: screenOffReceiver");
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
ScreenOffReceiver screenOffReceiver = ScreenOffReceiver.getInstance();
getApplicationContext().registerReceiver(screenOffReceiver, filter);
getApplicationContext().registerReceiver(ScreenOffReceiver.getInstance(), filter);
Intent i = new Intent(this, ObserverService.class);
this.startService(i);
jobFinished(params, true);
Intent observerServiceIntent = new Intent(this, ObserverService.class);
this.startService(observerServiceIntent);
jobFinished(params, false);
return true;
}
@Override
public boolean onStopJob(JobParameters params) {
Log.i(TAG, "onStopJob");
Intent i = new Intent(this, ObserverService.class);
this.unregisterReceiver(ScreenOffReceiver.getInstance());
this.stopService(i);
Intent observerServiceIntent = new Intent(this, ObserverService.class);
this.stopService(observerServiceIntent);
return false;
}
}
......@@ -46,32 +46,31 @@ public class CreateInitialRemoteFolderOperation extends RemoteOperation {
Log.e(TAG, "Local folder doesn't exist, so create it");
folder.mkdirs();
}
ExistenceCheckRemoteOperation absenceCheckOperation = new ExistenceCheckRemoteOperation(mRemotePath, false);
RemoteOperationResult absenceCheckResult = absenceCheckOperation.execute(client, mUseNextcloudAgent);
if(!absenceCheckResult.isSuccess()){
/*ExistenceCheckRemoteOperation presenceCheckOperation = new ExistenceCheckRemoteOperation(mRemotePath, false);
RemoteOperationResult presenceCheckResult = presenceCheckOperation.execute(client, mUseNextcloudAgent);
if(!presenceCheckResult.isSuccess()){*/
CreateRemoteFolderOperation createFolderOperation = new CreateRemoteFolderOperation(mRemotePath, mCreateFullPath);
RemoteOperationResult createOperationResult = createFolderOperation.execute(client, true);
if(createOperationResult.isSuccess()){
ExistenceCheckRemoteOperation existenceCheckOperation = new ExistenceCheckRemoteOperation(mRemotePath, false);
if(createOperationResult.isSuccess() || createOperationResult.getCode() == RemoteOperationResult.ResultCode.FOLDER_ALREADY_EXISTS ){
/*ExistenceCheckRemoteOperation existenceCheckOperation = new ExistenceCheckRemoteOperation(mRemotePath, false);
RemoteOperationResult ExistenceCheckResult = existenceCheckOperation.execute(client, mUseNextcloudAgent);
if(ExistenceCheckResult.isSuccess()){
if(ExistenceCheckResult.isSuccess() ){*/
//Save in DB;
if(DbHelper.insertSyncedFolder(mSyncedFolder, mContext) >= 0 )
return createOperationResult;
else
return new RemoteOperationResult(RemoteOperationResult.ResultCode.CANCELLED);
}
return ExistenceCheckResult;
if(DbHelper.insertSyncedFolder(mSyncedFolder, mContext) >= 0 )
return createOperationResult;
else
return new RemoteOperationResult(RemoteOperationResult.ResultCode.CANCELLED);
/* }
return ExistenceCheckResult;*/
}
return createOperationResult;
} else {
/*} else {
Log.e(TAG, mRemotePath + " Already exist");
DbHelper.insertSyncedFolder(mSyncedFolder, mContext);
return absenceCheckResult;
}
return presenceCheckResult;
}*/
}
}
......@@ -8,7 +8,6 @@ import com.owncloud.android.lib.common.operations.RemoteOperation;
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
import com.owncloud.android.lib.resources.files.ReadRemoteFolderOperation;
import com.owncloud.android.lib.resources.files.RemoteFile;
import com.owncloud.android.lib.resources.files.RemoveRemoteFileOperation;
import java.io.File;
import java.util.ArrayList;
......@@ -35,8 +34,8 @@ public class ListRemoteFileOperation extends RemoteOperation {
}
/**
*
* @param ownCloudClient
* Execute l'operation
* @param ownCloudClient DAV client
* @return List containing remoteFolder followed by remote files
*/
@Override
......@@ -53,80 +52,83 @@ public class ListRemoteFileOperation extends RemoteOperation {
//Get CurrentSyncedFolder
SyncedFolder syncedFolder = mSyncedFolderIterator.next();
RemoteOperationResult result = null;
if(syncedFolder.isScanRemote()) {
//Create ReadRemoteOperation
ReadRemoteFolderOperation operation = new ReadRemoteFolderOperation(syncedFolder.getRemoteFolder());
result = operation.execute(ownCloudClient);
}
//If operation succeed
if ( result != null && result.isSuccess() ) {
int dataSize = result.getData().size();
RemoteFile directory = (RemoteFile) result.getData().get(0); //remote directory corresponding to syncedFolder
//Check if directory has change since last time
if( directory != null && !directory.getEtag().equals( syncedFolder.getLastEtag() ) ){
List<Object> remoteFiles = result.getData().subList( 1, dataSize ); //get sub files and sub folders
if(syncedFolder.getId() == -1) {
//persist new syncedFolder
int syncedFolderId = (int) DbHelper.insertSyncedFolder(syncedFolder, mContext);
if (syncedFolderId > 0) {
syncedFolder.setId(syncedFolderId);
}
}
if(syncedFolder.isScanRemote()) {
//loop through remote sub-element.
int remoteFilesSize = remoteFiles.size();
for(int i = 0; i < remoteFilesSize; ++i) {
RemoteFile remoteFile = (RemoteFile) remoteFiles.get(i);
Log.d(TAG, "remoteFile: "+remoteFile.getRemotePath());
if(remoteFile.getMimeType().equals("DIR") ) {
String suffixPath = remoteFile.getRemotePath().substring( syncedFolder.getRemoteFolder().length() );
//but is it already known as SyncedFolder?
SyncedFolder subSyncedFolder = new SyncedFolder(syncedFolder, suffixPath, 0L, "" ); //need to set empty etag to allow it to be scan
mSyncedFolderIterator.add(subSyncedFolder);
mSyncedFolderIterator.previous();
}else {
//If it's a file just add it to mRemoteFiles.
mRemoteFiles.add(remoteFile);
}
if(syncedFolder.getId() == -1) {
//persist new syncedFolder
int syncedFolderId = (int) DbHelper.insertSyncedFolder(syncedFolder, mContext);
if (syncedFolderId > 0) {
syncedFolder.setId(syncedFolderId);
}
syncedFolder.setLastEtag(directory.getEtag() ).setToSync(true);
atLeastOneDirAsChanged = true;
}else{
//If not, just go to next
syncedFolder.setToSync(false);
}
} else {
if(result == null)
Log.w(TAG, syncedFolder.toString() );
else{
Log.w(TAG, "Empty folder : http " + result.getHttpCode() + ", " + result.getLogMessage()+" => Ignored");
//If there is no remote file, then try to delete local one if empty. Finally remove Synced Folder from DB.
File localFolder = new File(syncedFolder.getLocalFolder());
if( localFolder.exists() && localFolder.listFiles().length == 0)
{
boolean deletion_result = localFolder.delete();
Log.d(TAG, "Removed local folder: "+deletion_result);
}
if( !localFolder.exists() ) {
if (syncedFolder.getId() > -1) { //does the synced folder has been persisted?
//remove it from DB
int deleteResult = DbHelper.deleteSyncedFolder(syncedFolder.getId(), mContext);
Log.d(TAG, "syncedFolder Id: "+syncedFolder.getId() + " deletion from db return " + deleteResult + " row affected");
if(syncedFolder.getId() > 0){
//Create ReadRemoteOperation
ReadRemoteFolderOperation operation = new ReadRemoteFolderOperation(syncedFolder.getRemoteFolder());
RemoteOperationResult result = operation.execute(ownCloudClient);
if(result.isSuccess() ){
//is success then data can't be null
int dataSize = result.getData().size();
if(dataSize > 1){ //There is at least one subfiles
RemoteFile directory = (RemoteFile) result.getData().get(0);
if(!directory.getEtag().equals(syncedFolder.getLastEtag() )){ //if etag differs
List<Object> remoteFiles = result.getData().subList( 1, dataSize ); //get list of subfiles
//loop through subelements
for (int i = -1, remoteFilesSize = remoteFiles.size(); ++i < remoteFilesSize; ){
RemoteFile remoteFile = (RemoteFile) remoteFiles.get(i);
if( remoteFile.getMimeType().equals("DIR") ) {
String suffixPath = remoteFile.getRemotePath().substring( syncedFolder.getRemoteFolder().length() );
//but is it already known as SyncedFolder?
SyncedFolder subSyncedFolder = new SyncedFolder(syncedFolder, suffixPath, 0L, "" ); //need to set empty etag to allow it to be scan
mSyncedFolderIterator.add(subSyncedFolder);
mSyncedFolderIterator.previous();
}else {
//If it's a file just add it to mRemoteFiles.
mRemoteFiles.add(remoteFile);
}
}
syncedFolder.setLastEtag(directory.getEtag() ).setToSync(true);
atLeastOneDirAsChanged = true;
}
}else if(dataSize == 1){ //Empty folder
syncedFolder.setLastEtag( ( (RemoteFile) result.getData().get(0) ).getEtag() ).setToSync(true);
}//Last else correspond to error 404 at readRemoteFolderOperation (see below)
}else{ //Result isn't a success
if(result.getHttpCode() == 404){
//If not, just go to next
syncedFolder.setToSync(false);
//If there is no remote file, then try to delete local one if empty. Finally remove Synced Folder from DB.
File localFolder = new File(syncedFolder.getLocalFolder());
if( localFolder.exists() && localFolder.listFiles().length == 0)
{
boolean deletion_result = localFolder.delete();
Log.d(TAG, "Removed local folder: "+deletion_result);
}
if( !localFolder.exists() ) {
if (syncedFolder.getId() > -1) { //does the synced folder has been persisted?
//remove it from DB
//@TODO remove syncedFileState bound to syncedFolderToDelete
int deleteResult = DbHelper.deleteSyncedFolder(syncedFolder.getId(), mContext);
Log.d(TAG, "syncedFolder Id: "+syncedFolder.getId() + " deletion from db return " + deleteResult + " row affected");
}
mSyncedFolderIterator.remove();
}
}
mSyncedFolderIterator.remove();
Log.w(TAG, "ReadRemoteFolderOperation failed : http " + result.getHttpCode() + ", " + result.getLogMessage()+" => Ignored");
}
}else{
mSyncedFolderIterator.remove();
Log.w(TAG, "syncedFolder "+syncedFolder.getRemoteFolder()+" doesn't have a valid ID");
}
}
}
} //Then its folder is not scanRemote
} //End of loop
finalResult = new RemoteOperationResult(RemoteOperationResult.ResultCode.OK);
if( atLeastOneDirAsChanged ) {
......
package io.eelo.drive.operations;
import android.content.Context;
import android.util.Log;
import android.content.Context;
import android.util.Log;
import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.common.operations.RemoteOperation;
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
import com.owncloud.android.lib.resources.files.CreateRemoteFolderOperation;
import com.owncloud.android.lib.resources.files.FileUtils;
import com.owncloud.android.lib.resources.files.ReadRemoteFileOperation;
import com.owncloud.android.lib.resources.files.RemoteFile;
import com.owncloud.android.lib.resources.files.UploadRemoteFileOperation;
import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.common.operations.RemoteOperation;
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
import com.owncloud.android.lib.resources.files.CreateRemoteFolderOperation;
import com.owncloud.android.lib.resources.files.FileUtils;
import com.owncloud.android.lib.resources.files.ReadRemoteFileOperation;
import com.owncloud.android.lib.resources.files.RemoteFile;
import com.owncloud.android.lib.resources.files.UploadRemoteFileOperation;
import java.io.File;
import java.util.List;
import java.io.File;
import java.util.List;
import io.eelo.drive.database.DbHelper;
import io.eelo.drive.models.SyncedFileState;
import io.eelo.drive.utils.CommonUtils;
import io.eelo.drive.database.DbHelper;
import io.eelo.drive.models.SyncedFileState;
import io.eelo.drive.utils.CommonUtils;
/**
* Created by Vincent on 09/05/2018.
*/
/**
* Created by Vincent on 09/05/2018.
* High level Operation which upload a local file to a remote cloud storage
*/
public class UploadFileOperation extends RemoteOperation {
......@@ -35,17 +34,22 @@ public class UploadFileOperation extends RemoteOperation {
private SyncedFileState mSyncedState;
private int restartCounter =0;