diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 4165289771aacd3411143d44088b9858bc3e2890..5b8dbb3f575d5e92d09805f2ba0339e9f701cd1d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -14,6 +14,7 @@ stages: - gitlab_release before_script: + - export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64 - export GRADLE_USER_HOME=$(pwd)/.gradle - chmod +x ./gradlew @@ -46,8 +47,8 @@ build: exit 1 fi cd "release" - - unsigned_build=$(ls *.apk | grep "unsigned") - - cp $unsigned_build $UNSIGNED_APK + - unsigned_build=$(ls *.apk | grep "universal-release") + - mv $unsigned_build $UNSIGNED_APK artifacts: paths: - app/build/outputs/apk/ @@ -158,4 +159,4 @@ create-release: ./systemAppsUpdateInfo/scripts/create-release.sh \ "$APK_PATH" "$UNSIGNED_APK" "$COMMUNITY_APK" "$OFFICIAL_APK" allow_failure: true - \ No newline at end of file + diff --git a/app/build.gradle b/app/build.gradle index 91943658e681a304af30f2fe2fe87d0b434870aa..1635e9d24c25bc118560d2f6957c0d4edcfe1469 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,11 +1,11 @@ plugins { - id 'com.android.application' - id 'org.jetbrains.kotlin.android' + alias(libs.plugins.android.application) + alias(libs.plugins.kotlin.android) } def versionMajor = 1 -def versionMinor = 8 -def versionPatch = 1 +def versionMinor = 9 +def versionPatch = 0 def getTestProp(String propName) { def result = "" @@ -35,47 +35,54 @@ def getSentryDsn = { -> } android { - compileSdk 34 + namespace = 'foundation.e.drive' + compileSdk = 36 + defaultConfig { - applicationId "foundation.e.drive" - minSdk 26 - targetSdk 34 - versionCode versionMajor * 1000000 + versionMinor * 1000 + versionPatch - versionName "${versionMajor}.${versionMinor}.${versionPatch}" - setProperty("archivesBaseName", "eDrive-$versionName") - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + applicationId = "foundation.e.drive" + minSdk = 26 + targetSdk = 36 + versionCode = versionMajor * 1000000 + versionMinor * 1000 + versionPatch + versionName = "${versionMajor}.${versionMinor}.${versionPatch}" + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" manifestPlaceholders = [ 'appAuthRedirectScheme': applicationId, ] } + splits { + abi { + enable true + reset() + include 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' + universalApk true + } + } + signingConfigs { debugConfig { - storeFile file("../keystore/platform.keystore") - storePassword 'android' - keyAlias 'platform' - keyPassword 'android' + storeFile = file("../keystore/platform.keystore") + storePassword = 'android' + keyAlias = 'platform' + keyPassword = 'android' } } buildTypes { release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + minifyEnabled = false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + signingConfig = signingConfigs.debugConfig buildConfigField("String", "SENTRY_DSN", "\"${getSentryDsn()}\"") } debug { - signingConfig signingConfigs.debugConfig + signingConfig = signingConfigs.debugConfig buildConfigField("String", "SENTRY_DSN", "\"dummy\"") } } - kotlinOptions { - jvmTarget = "11" - } - testOptions { unitTests { returnDefaultValues = true @@ -87,50 +94,57 @@ android { } } } + buildFeatures { - viewBinding true + buildConfig = true + viewBinding = true } + compileOptions { - sourceCompatibility JavaVersion.VERSION_11 - targetCompatibility JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 + } + + kotlinOptions { + jvmTarget = "21" } } dependencies { - implementation("foundation.e:Nextcloud-Android-Library:2.0.3-u2.17-release") { + implementation fileTree(include: ['*.jar'], dir: 'libs') + + implementation(libs.nextcloud.android.library) { exclude group: 'com.gitlab.bitfireAT', module: 'dav4jvm' // Got from AccountManager exclude group: 'com.github.bitfireAT', module: 'dav4jvm' exclude group: 'org.ogce', module: 'xpp3' // unused in Android and brings wrong Junit version } - - implementation "commons-httpclient:commons-httpclient:3.1@jar" - implementation fileTree(include: ['*.jar'], dir: 'libs') - api 'androidx.annotation:annotation:1.7.0' - implementation 'androidx.appcompat:appcompat:1.6.1' - implementation "androidx.constraintlayout:constraintlayout:2.1.4" - implementation 'com.google.android.material:material:1.10.0' - implementation 'com.github.bumptech.glide:glide:4.15.1' - implementation 'com.github.bumptech.glide:annotations:4.15.1' - implementation 'androidx.core:core-ktx:1.12.0' - annotationProcessor 'com.github.bumptech.glide:compiler:4.15.1' - implementation "androidx.work:work-runtime:2.9.0" - implementation 'androidx.test:core:1.5.0' - implementation 'com.jakewharton.timber:timber:5.0.1' - implementation 'foundation.e:elib:0.0.1-alpha11' - implementation 'foundation.e.lib:telemetry:1.0.1-release' - - - androidTestImplementation 'androidx.test:runner:1.5.2' - androidTestImplementation 'androidx.test:rules:1.5.0' - androidTestImplementation 'androidx.annotation:annotation:1.7.0' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' - androidTestImplementation 'junit:junit:4.13.2' - - testImplementation 'androidx.test:runner:1.5.2' - testImplementation 'androidx.test:rules:1.5.0' - - testImplementation 'junit:junit:4.13.2' - testImplementation 'org.robolectric:robolectric:4.7.3' - testImplementation 'org.mockito:mockito-core:5.0.0' - testImplementation 'androidx.work:work-testing:2.9.0' + + annotationProcessor libs.compiler + + api libs.annotation + + implementation libs.appcompat + implementation libs.constraintlayout + implementation libs.core + implementation libs.core.ktx + implementation libs.elib + implementation libs.glide + implementation libs.glide.annotations + implementation libs.material + implementation libs.telemetry + implementation libs.timber + implementation libs.work.runtime + + androidTestImplementation libs.annotation + androidTestImplementation libs.espresso.core + androidTestImplementation libs.junit + androidTestImplementation libs.rules + androidTestImplementation libs.runner + + testImplementation libs.junit + testImplementation libs.mockito.core + testImplementation libs.robolectric + testImplementation libs.rules + testImplementation libs.runner + testImplementation libs.work.testing } diff --git a/app/detekt-baseline.xml b/app/detekt-baseline.xml index 1475cca0df99984abe70fba8f31f1ea21e62b134..179702b1b1cd0b22789cbe4d59d889563b7307b2 100644 --- a/app/detekt-baseline.xml +++ b/app/detekt-baseline.xml @@ -1,26 +1,89 @@ - + - EmptyDefaultConstructor:AccountAddedReceiver.kt$AccountAddedReceiver$() - EmptyElseBlock:SyncTask.kt$SyncTask$if (filePath.isEmpty()) return - MagicNumber:FileDiffUtils.kt$FileDiffUtils$1000 + AnnotationOnSeparateLine:SyncTask.kt$SyncTask$@Suppress("DEPRECATION") + ArgumentListWrapping:AccountAdder.kt$AccountAdder$( AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE ) + ArgumentListWrapping:AccountRemover.kt$AccountRemover$( AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE ) + ArgumentListWrapping:DownloadFileOperation.kt$DownloadFileOperation$("Local's size $tmpFileSize and remote's size $remoteFileSize " + "of ${remoteFile.remotePath} don't match") + ArgumentListWrapping:DownloadFileOperation.kt$DownloadFileOperation$(context, arrayOf(filePath), arrayOf(remoteFile.mimeType)) + ArgumentListWrapping:DownloadFileOperation.kt$DownloadFileOperation$(illegalArgumentException, "remoteFile's lastModifiedTimestamp : ${remoteFile.modifiedTimestamp}") + ArgumentListWrapping:WorkLauncher.kt$WorkLauncher$(FullScanWorker.UNIQUE_WORK_NAME, ExistingWorkPolicy.KEEP, request) + ArgumentListWrapping:WorkLauncher.kt$WorkLauncher$(FullScanWorker.UNIQUE_WORK_NAME, ExistingWorkPolicy.REPLACE, request) + ArgumentListWrapping:WorkLauncher.kt$WorkLauncher$(OneTimeWorkType.FULL_SCAN, null) + ArgumentListWrapping:WorkLauncher.kt$WorkLauncher$(OneTimeWorkType.LIST_APPS, null) + ArgumentListWrapping:WorkLauncher.kt$WorkLauncher$(OneTimeWorkType.USER_TRIGGERED_FULL_SCAN, null) + ArgumentListWrapping:WorkLauncher.kt$WorkLauncher$(PeriodicScanWorker.UNIQUE_WORK_NAME, ExistingPeriodicWorkPolicy.KEEP, request) + ArgumentListWrapping:WorkRequestFactory.kt$WorkRequestFactory$( PERIODIC_WORK_REPEAT_INTERVAL_IN_MIN, MINUTES) + ArgumentListWrapping:WorkRequestFactory.kt$WorkRequestFactory$( PERIODIC_WORK_REPEAT_INTERVAL_IN_MIN, MINUTES, PERIODIC_SCAN_FLEX_TIME_IN_MIN,MINUTES) + ChainWrapping:DownloadFileOperation.kt$DownloadFileOperation$&& + ChainWrapping:DownloadFileOperation.kt$DownloadFileOperation$|| + ChainWrapping:FileFilterFactory.kt$FileFilterFactory.<no name provided>$&& + ChainWrapping:FileFilterFactory.kt$FileFilterFactory.<no name provided>$|| + ChainWrapping:SyncTask.kt$SyncTask$|| + CommentSpacing:AccountRemover.kt$AccountRemover$//If removal failed, clear all data inside + CommentSpacing:DownloadFileOperation.kt$DownloadFileOperation$//already checked in "isFileUpToDate()" + CommentSpacing:FileEventHandler.kt$FileEventHandler$//File update + CommentSpacing:FileEventHandler.kt$FileEventHandler$//If already in DB + CommentSpacing:FileEventHandler.kt$FileEventHandler$//It's a directory update + CommentSpacing:FileEventHandler.kt$FileEventHandler$//New file discovered + CommentSpacing:FileEventHandler.kt$FileEventHandler$//look for parent + CommentSpacing:FileEventHandler.kt$FileEventHandler$//todo check if really relevant + CommentSpacing:FileEventHandler.kt$FileEventHandler$//todo: if file is already sync disabled, we should probably remove file from DB + CommentSpacing:FileUtils.kt$FileUtils$//todo why not simply always use getAbsolutePath? + CommentSpacing:StateMachine.kt$StateMachine$//todo use it to prevent apps to be blocked in one state + CommentSpacing:SyncProxy.kt$SyncProxy$//1h + CommentSpacing:SyncProxy.kt$SyncProxy$//2h + CommentSpacing:SyncProxy.kt$SyncProxy$//could we use channel instead ? + FinalNewline:FileDiffUtils.kt$foundation.e.drive.periodicScan.contentScanner.FileDiffUtils.kt + FinalNewline:FullScanWorker.kt$foundation.e.drive.periodicScan.FullScanWorker.kt + FinalNewline:RootSyncedFolderProvider.kt$foundation.e.drive.utils.RootSyncedFolderProvider.kt + FinalNewline:StateMachine.kt$foundation.e.drive.synchronization.StateMachine.kt + FinalNewline:SyncProxy.kt$foundation.e.drive.synchronization.SyncProxy.kt + FinalNewline:SyncTask.kt$foundation.e.drive.synchronization.SyncTask.kt + FinalNewline:SyncWorker.kt$foundation.e.drive.synchronization.SyncWorker.kt + FinalNewline:SyncedFileState.kt$foundation.e.drive.models.SyncedFileState.kt + FunctionReturnTypeSpacing:FullScanWorker.kt$FullScanWorker$private fun isFileSyncDisabled(account: Account) : Boolean + FunctionReturnTypeSpacing:SyncedFileState.kt$SyncedFileState$fun isLastEtagStored() : Boolean + ImportOrdering:AccountAdder.kt$import android.accounts.AccountManager import android.content.Context import android.content.SharedPreferences import foundation.e.drive.R import foundation.e.drive.utils.AppConstants import foundation.e.drive.utils.DavClientProvider import foundation.e.drive.work.WorkLauncher import timber.log.Timber import androidx.core.content.edit + ImportOrdering:AccountRemover.kt$import android.accounts.AccountManager import android.app.Application import android.app.NotificationManager import android.content.Context import android.content.SharedPreferences import androidx.work.WorkManager import foundation.e.drive.database.DbHelper import foundation.e.drive.database.FailedSyncPrefsManager import foundation.e.drive.synchronization.SyncProxy import foundation.e.drive.utils.AppConstants import foundation.e.drive.utils.AppConstants.INITIAL_FOLDER_NUMBER import foundation.e.drive.utils.AppConstants.SETUP_COMPLETED import foundation.e.drive.utils.DavClientProvider import timber.log.Timber import java.io.File import androidx.core.content.edit + ImportOrdering:DownloadFileOperation.kt$import android.content.Context import android.media.MediaScannerConnection 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.common.operations.RemoteOperationResult.ResultCode import com.owncloud.android.lib.resources.files.DownloadFileRemoteOperation import com.owncloud.android.lib.resources.files.model.RemoteFile import foundation.e.drive.database.DbHelper import foundation.e.drive.models.SyncedFileState import foundation.e.drive.utils.AppConstants.CORRUPTED_TIMESTAMP_IN_MILLISECOND import foundation.e.drive.utils.FileUtils import timber.log.Timber import java.io.File import java.util.stream.IntStream.range + ImportOrdering:FullScanWorker.kt$import android.accounts.Account import android.app.Application import android.content.Context import android.content.SharedPreferences import androidx.work.Worker import androidx.work.WorkerParameters import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.resources.files.model.RemoteFile import foundation.e.drive.account.AccountUtils import foundation.e.drive.database.DbHelper import foundation.e.drive.models.SyncRequest import foundation.e.drive.models.SyncedFolder import foundation.e.drive.periodicScan.contentScanner.LocalContentScanner import foundation.e.drive.periodicScan.contentScanner.LocalFileLister import foundation.e.drive.periodicScan.contentScanner.RemoteContentScanner import foundation.e.drive.synchronization.SyncProxy import foundation.e.drive.synchronization.SyncRequestCollector import foundation.e.drive.utils.AppConstants import foundation.e.drive.utils.AppConstants.KEY_LAST_SCAN_TIME import foundation.e.drive.utils.CommonUtils import foundation.e.drive.utils.DavClientProvider import timber.log.Timber import androidx.core.content.edit + ImportOrdering:RecoveryPreferences.kt$import android.content.Context import foundation.e.drive.recovery.RecoveryPreferences.RecoveryStatus.RecoveryCompleted import foundation.e.drive.recovery.RecoveryPreferences.RecoveryStatus.RecoveryNeeded import androidx.core.content.edit + ImportOrdering:SyncTask.kt$import android.accounts.Account import android.content.Context import com.owncloud.android.lib.common.OwnCloudClient import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.HOST_NOT_AVAILABLE import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.NO_NETWORK_CONNECTION import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.WRONG_CONNECTION import foundation.e.drive.database.DbHelper import foundation.e.drive.database.FailedSyncPrefsManager import foundation.e.drive.models.SyncRequest import foundation.e.drive.models.SyncRequest.Type.UPLOAD import foundation.e.drive.models.SyncRequest.Type.DISABLE_SYNCING import foundation.e.drive.models.SyncRequest.Type.DOWNLOAD import foundation.e.drive.models.SyncWrapper import foundation.e.drive.synchronization.tasks.UploadFileOperation import foundation.e.drive.utils.CommonUtils import timber.log.Timber import java.io.File + ImportOrdering:SyncWorker.kt$import android.accounts.Account import android.content.Context import androidx.work.Worker import androidx.work.WorkerParameters import com.owncloud.android.lib.common.OwnCloudClient import foundation.e.drive.EdriveApplication import foundation.e.drive.account.AccountUtils import foundation.e.drive.utils.DavClientProvider import timber.log.Timber import java.util.concurrent.Executors import java.util.concurrent.Future import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicInteger + Indentation:DirectoryObserver.kt$DirectoryObserver.Companion$ + Indentation:DownloadFileOperation.kt$DownloadFileOperation$ + Indentation:FileFilterFactory.kt$FileFilterFactory.<no name provided>$ + Indentation:FileUtils.kt$FileUtils$ + Indentation:SyncProxy.kt$SyncProxy$ + Indentation:SyncTask.kt$SyncTask$ + Indentation:SyncedFileState.kt$SyncedFileState$ MagicNumber:SyncProxy.kt$SyncProxy$1000 MagicNumber:SyncProxy.kt$SyncProxy$18000 MagicNumber:SyncProxy.kt$SyncProxy$3 MagicNumber:SyncProxy.kt$SyncProxy$3600 MagicNumber:SyncProxy.kt$SyncProxy$7200 MagicNumber:SyncWorker.kt$SyncWorker$30 - MaxLineLength:FullScanWorker.kt$FullScanWorker$if (isFileSyncDisabled(account)) return false MaxLineLength:FullScanWorker.kt$FullScanWorker$private MaxLineLength:RootSyncedFolderProvider.kt$RootSyncedFolderProvider$private MaxLineLength:RootSyncedFolderProvider.kt$RootSyncedFolderProvider$private val REMOTE_ROM_SETTINGS_PATH = PATH_SEPARATOR + "Devices" + PATH_SEPARATOR + BRAND + "_" + MODEL + "_" + Build.getSerial() + "/rom_settings/" - MaxLineLength:SyncProxy.kt$SyncProxy$private val syncRequestQueue: ConcurrentLinkedQueue<SyncRequest> = ConcurrentLinkedQueue() //could we use channel instead ? + MaxLineLength:SyncProxy.kt$SyncProxy$private val syncRequestQueue: ConcurrentLinkedQueue<SyncRequest> = ConcurrentLinkedQueue() //could we use channel instead ? MaxLineLength:SyncTask.kt$SyncTask$if - NewLineAtEndOfFile:AccountAddedReceiver.kt$foundation.e.drive.account.receivers.AccountAddedReceiver.kt - NewLineAtEndOfFile:AppConstants.kt$foundation.e.drive.utils.AppConstants.kt + MaximumLineLength:FullScanWorker.kt$FullScanWorker$ + MaximumLineLength:RootSyncedFolderProvider.kt$RootSyncedFolderProvider$ + MaximumLineLength:SyncProxy.kt$SyncProxy$ + MaximumLineLength:SyncTask.kt$SyncTask$ + ModifierListSpacing:FileUtils.kt$FileUtils$@JvmStatic + ModifierListSpacing:StateMachine.kt$StateMachine$@Synchronized + MultiLineIfElse:AccountUtils.kt$AccountUtils$null + MultiLineIfElse:AccountUtils.kt$AccountUtils$splitPremiumGroup[1] + MultiLineIfElse:FileDiffUtils.kt$FileDiffUtils$Action.Skip + MultiLineIfElse:FullScanWorker.kt$FullScanWorker$ArrayList() + MultiLineIfElse:FullScanWorker.kt$FullScanWorker$DbHelper.getAllSyncedFolders(context) + MultiLineIfElse:FullScanWorker.kt$FullScanWorker$DbHelper.getSyncedFolderList(context, false) + MultiLineIfElse:FullScanWorker.kt$FullScanWorker$DbHelper.getSyncedFolderList(context, true) NewLineAtEndOfFile:FileDiffUtils.kt$foundation.e.drive.periodicScan.contentScanner.FileDiffUtils.kt - NewLineAtEndOfFile:FileUtils.kt$foundation.e.drive.utils.FileUtils.kt NewLineAtEndOfFile:FullScanWorker.kt$foundation.e.drive.periodicScan.FullScanWorker.kt NewLineAtEndOfFile:RootSyncedFolderProvider.kt$foundation.e.drive.utils.RootSyncedFolderProvider.kt NewLineAtEndOfFile:StateMachine.kt$foundation.e.drive.synchronization.StateMachine.kt @@ -28,27 +91,75 @@ NewLineAtEndOfFile:SyncTask.kt$foundation.e.drive.synchronization.SyncTask.kt NewLineAtEndOfFile:SyncWorker.kt$foundation.e.drive.synchronization.SyncWorker.kt NewLineAtEndOfFile:SyncedFileState.kt$foundation.e.drive.models.SyncedFileState.kt - ReturnCount:AccountAddedReceiver.kt$AccountAddedReceiver$private fun canStart( accountName: String, accountType: String, prefs: SharedPreferences, context: Context ): Boolean + NoBlankLineBeforeRbrace:FileUtils.kt$FileUtils$ + NoBlankLineBeforeRbrace:FullScanWorker.kt$FullScanWorker$ + NoBlankLineBeforeRbrace:WorkRequestFactory.kt$WorkRequestFactory$ + NoConsecutiveBlankLines:FileDiffUtils.kt$FileDiffUtils$ + NoConsecutiveBlankLines:StateMachine.kt$StateMachine$ + NoConsecutiveBlankLines:SyncProxy.kt$SyncProxy$ + NoConsecutiveBlankLines:SyncTask.kt$SyncTask$ + NoConsecutiveBlankLines:SyncedFileState.kt$ + NoConsecutiveBlankLines:WorkLauncher.kt$WorkLauncher$ + NoConsecutiveBlankLines:WorkRequestFactory.kt$ + NoMultipleSpaces:DownloadFileOperation.kt$DownloadFileOperation$ + NoMultipleSpaces:FileEventHandler.kt$FileEventHandler$ + NoMultipleSpaces:RootSyncedFolderProvider.kt$RootSyncedFolderProvider$ + NoSemicolons:FileFilterFactory.kt$; + NoSemicolons:SyncProxy.kt$SyncProxy$; + NoTrailingSpaces:DownloadFileOperation.kt$DownloadFileOperation$ + NoTrailingSpaces:StateMachine.kt$StateMachine$ + ParameterListWrapping:DownloadFileOperation.kt$DownloadFileOperation$(private val remoteFile: RemoteFile, private val fileState: SyncedFileState, private val context: Context) + ParameterListWrapping:SyncedFileState.kt$SyncedFileState$(var id: Int, val name: String, val localPath: String, val remotePath: String, var lastEtag: String, var lastModified: Long, val syncedFolderId: Long, val isMediaType: Boolean, var scanScope: Int) ReturnCount:FileDiffUtils.kt$FileDiffUtils$@JvmStatic fun getActionForFileDiff(remoteFile: RemoteFile, fileState: SyncedFileState): Action ReturnCount:FullScanWorker.kt$FullScanWorker$override fun doWork(): Result ReturnCount:FullScanWorker.kt$FullScanWorker$private fun checkStartConditions(account: Account?, prefs : SharedPreferences, requestCollector: SyncRequestCollector): Boolean - ReturnCount:FullScanWorker.kt$FullScanWorker$private fun scanLocalFiles(syncedFolders: List<SyncedFolder>): HashMap<Int, SyncRequest> - ReturnCount:FullScanWorker.kt$FullScanWorker$private fun scanRemoteFiles(account: Account, syncedFolders: List<SyncedFolder>): HashMap<Int, SyncRequest> - ReturnCount:StateMachine.kt$StateMachine$private fun setPeriodicScanState(): Boolean - ReturnCount:SyncProxy.kt$SyncProxy$override fun onPeriodicScanStart(application: Application): Boolean - ReturnCount:SyncProxy.kt$SyncProxy$override fun queueSyncRequest(request: SyncRequest, context: Context): Boolean - ReturnCount:SyncProxy.kt$SyncProxy$override fun startSynchronization(context: Context) + ReturnCount:FullScanWorker.kt$FullScanWorker$private fun scanLocalFiles(syncedFolders: List<SyncedFolder>): HashMap<Int, SyncRequest> ReturnCount:SyncProxy.kt$SyncProxy$private fun skipBecauseOfPreviousFail(request: SyncRequest, context: Context): Boolean ReturnCount:SyncTask.kt$SyncTask$private fun canStart(): Boolean ReturnCount:SyncTask.kt$SyncTask$private fun runDownload(syncWrapper: SyncWrapper): Boolean ReturnCount:SyncTask.kt$SyncTask$private fun runUpload(syncWrapper: SyncWrapper): Boolean ReturnCount:SyncTask.kt$SyncTask$private fun updateFailureCounter(request: SyncRequest, success: Boolean) ReturnCount:SyncWorker.kt$SyncWorker$override fun doWork(): Result + SpacingAroundColon:DownloadFileOperation.kt$DownloadFileOperation$: + SpacingAroundColon:FullScanWorker.kt$FullScanWorker$: + SpacingAroundColon:RootSyncedFolderProvider.kt$RootSyncedFolderProvider$: + SpacingAroundColon:SyncProxy.kt$SyncProxy$: + SpacingAroundColon:SyncedFileState.kt$SyncedFileState$: + SpacingAroundComma:WorkRequestFactory.kt$WorkRequestFactory$, + SpacingAroundCurly:FullScanWorker.kt$FullScanWorker${ + SpacingAroundCurly:SyncTask.kt$SyncTask${ + SpacingAroundCurly:WorkRequestFactory.kt$PeriodicWorkType$} + SpacingAroundKeyword:DownloadFileOperation.kt$DownloadFileOperation$catch + SpacingAroundKeyword:FileUtils.kt$FileUtils$catch + SpacingAroundKeyword:FullScanWorker.kt$FullScanWorker$catch + SpacingAroundOperators:FullScanWorker.kt$FullScanWorker$?: + SpacingAroundOperators:SyncWorker.kt$SyncWorker$?: + SpacingAroundParens:DownloadFileOperation.kt$DownloadFileOperation$( + SpacingAroundParens:DownloadFileOperation.kt$DownloadFileOperation$) + SpacingAroundParens:FileUtils.kt$FileUtils$) + SpacingAroundParens:SyncedFileState.kt$SyncedFileState$( + SpacingBetweenDeclarationsWithAnnotations:FileDiffUtils.kt$FileDiffUtils$@JvmStatic fun getActionForFileDiff(remoteFile: RemoteFile, fileState: SyncedFileState): Action + SpacingBetweenDeclarationsWithComments:FileDiffUtils.kt$FileDiffUtils$/** * Define what to do of RemoteFile for which we know the Database equivalent * @param remoteFile RemoteFile * @param fileState SyncedFileState instance * @return Action from Enum */ + StringTemplate:SyncedFileState.kt$SyncedFileState$${id} + StringTemplate:SyncedFileState.kt$SyncedFileState$${isMediaType} + StringTemplate:SyncedFileState.kt$SyncedFileState$${lastEtag} + StringTemplate:SyncedFileState.kt$SyncedFileState$${lastModified} + StringTemplate:SyncedFileState.kt$SyncedFileState$${localPath} + StringTemplate:SyncedFileState.kt$SyncedFileState$${name} + StringTemplate:SyncedFileState.kt$SyncedFileState$${remotePath} + StringTemplate:SyncedFileState.kt$SyncedFileState$${scanScope} + StringTemplate:SyncedFileState.kt$SyncedFileState$${syncedFolderId} SwallowedException:FileUtils.kt$FileUtils$e: java.lang.NullPointerException TooGenericExceptionCaught:FullScanWorker.kt$FullScanWorker$exception: Exception TooGenericExceptionCaught:SyncWorker.kt$SyncWorker$exception: Exception TooManyFunctions:SyncProxy.kt$SyncProxy : SyncRequestCollectorSyncManager - WildcardImport:AccountAddedReceiver.kt$import foundation.e.drive.work.WorkRequestFactory.* - WildcardImport:SyncTask.kt$import foundation.e.drive.models.SyncRequest.Type.* + Wrapping:DirectoryObserver.kt$DirectoryObserver.Companion$( + Wrapping:DownloadFileOperation.kt$DownloadFileOperation$( + Wrapping:DownloadFileOperation.kt$DownloadFileOperation$-> + Wrapping:FileFilterFactory.kt$FileFilterFactory.<no name provided>$( + Wrapping:SyncTask.kt$SyncTask$( + Wrapping:SyncedFileState.kt$SyncedFileState$( + Wrapping:WorkLauncher.kt$WorkLauncher$( + Wrapping:WorkRequestFactory.kt$WorkRequestFactory$( diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a4b19720a9155c7fb9eab676ac66fbb47365f7f3..a3a1581e7a31b8425404d56b10a78e60ffad418c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,7 +1,6 @@ + xmlns:tools="http://schemas.android.com/tools"> diff --git a/app/src/main/java/foundation/e/drive/account/AccountAdder.kt b/app/src/main/java/foundation/e/drive/account/AccountAdder.kt index 0cbba53068d7bb02b3b1c3fa097be27e19c794d2..a40a32218f6adc6d387c52538a9ef730173aae81 100644 --- a/app/src/main/java/foundation/e/drive/account/AccountAdder.kt +++ b/app/src/main/java/foundation/e/drive/account/AccountAdder.kt @@ -26,6 +26,7 @@ import foundation.e.drive.utils.AppConstants import foundation.e.drive.utils.DavClientProvider import foundation.e.drive.work.WorkLauncher import timber.log.Timber +import androidx.core.content.edit class AccountAdder(private val context: Context) { @@ -48,9 +49,9 @@ class AccountAdder(private val context: Context) { } private fun updateAccountNameOnPreference(name: String) { - preferences.edit() - .putString(AccountManager.KEY_ACCOUNT_NAME, name) - .apply() + preferences.edit { + putString(AccountManager.KEY_ACCOUNT_NAME, name) + } } /** diff --git a/app/src/main/java/foundation/e/drive/account/AccountRemover.kt b/app/src/main/java/foundation/e/drive/account/AccountRemover.kt index fe6acc51c576be4469d5d8e9c5e3a32c1cf22315..9d8476b7c499e6a1e144ef99108c5da50949cb7a 100644 --- a/app/src/main/java/foundation/e/drive/account/AccountRemover.kt +++ b/app/src/main/java/foundation/e/drive/account/AccountRemover.kt @@ -33,6 +33,7 @@ import foundation.e.drive.utils.AppConstants.SETUP_COMPLETED import foundation.e.drive.utils.DavClientProvider import timber.log.Timber import java.io.File +import androidx.core.content.edit class AccountRemover( private val context: Context, @@ -74,12 +75,13 @@ class AccountRemover( private fun cleanSharedPreferences() { if (!context.applicationContext.deleteSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME)) { //If removal failed, clear all data inside - preferences.edit().remove(AccountManager.KEY_ACCOUNT_NAME) - .remove(AccountManager.KEY_ACCOUNT_TYPE) - .remove(SETUP_COMPLETED) - .remove(INITIAL_FOLDER_NUMBER) - .remove(AppConstants.KEY_LAST_SCAN_TIME) - .apply() + preferences.edit { + remove(AccountManager.KEY_ACCOUNT_NAME) + .remove(AccountManager.KEY_ACCOUNT_TYPE) + .remove(SETUP_COMPLETED) + .remove(INITIAL_FOLDER_NUMBER) + .remove(AppConstants.KEY_LAST_SCAN_TIME) + } } context.applicationContext.deleteSharedPreferences(FailedSyncPrefsManager.PREF_NAME) } diff --git a/app/src/main/java/foundation/e/drive/account/GetAliasOperation.java b/app/src/main/java/foundation/e/drive/account/GetAliasOperation.java index 637763869fd44c74d3e4a95d0c99cd84ac46085a..3003ea0fb9e1849370497589ad9483a45116fbbb 100644 --- a/app/src/main/java/foundation/e/drive/account/GetAliasOperation.java +++ b/app/src/main/java/foundation/e/drive/account/GetAliasOperation.java @@ -16,11 +16,11 @@ import com.nextcloud.operations.GetMethod; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; -import org.apache.commons.httpclient.HttpStatus; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import java.net.HttpURLConnection; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; @@ -43,7 +43,7 @@ public class GetAliasOperation extends RemoteOperation> { private final String mID; private boolean isSuccess(int status) { - return (status == HttpStatus.SC_OK); + return status == HttpURLConnection.HTTP_OK; } public GetAliasOperation(@NonNull String id) { diff --git a/app/src/main/java/foundation/e/drive/activity/AccountsActivity.java b/app/src/main/java/foundation/e/drive/activity/AccountsActivity.java index b995fcad0f5c36ebdc3218018f1f1238f8b036f1..c6f5cf45a0ffd3e82f35b6dba8f3a72196bfd12e 100644 --- a/app/src/main/java/foundation/e/drive/activity/AccountsActivity.java +++ b/app/src/main/java/foundation/e/drive/activity/AccountsActivity.java @@ -13,9 +13,6 @@ import static foundation.e.drive.utils.AppConstants.ACCOUNT_DATA_EMAIL; import static foundation.e.drive.utils.AppConstants.ACCOUNT_DATA_NAME; import static foundation.e.drive.utils.AppConstants.ACCOUNT_DATA_TOTAL_QUOTA_KEY; import static foundation.e.drive.utils.AppConstants.ACCOUNT_DATA_USED_QUOTA_KEY; -import static foundation.e.drive.widgets.EDriveWidget.buildIntent; -import static foundation.e.drive.widgets.EDriveWidget.convertIntoMB; -import static foundation.e.drive.widgets.EDriveWidget.dataForWeb; import android.accounts.Account; import android.accounts.AccountManager; @@ -61,7 +58,7 @@ public class AccountsActivity extends AppCompatActivity { binding.toolbar.setNavigationOnClickListener(v -> onBackPressed()); binding.settings.setOnClickListener(v -> { - final Intent settingsIntent = buildIntent("", "") + final Intent settingsIntent = EDriveWidget.buildIntent("", "") .setComponent(new ComponentName( ACCOUNT_MANAGER_PACKAGE_NAME, ACCOUNT_SETTINGS)); startActivity(settingsIntent); @@ -89,8 +86,8 @@ public class AccountsActivity extends AppCompatActivity { binding.name.setText(name); binding.email.setText(email); - binding.progress.setMax(convertIntoMB(totalQuota)); - binding.progress.setProgress(convertIntoMB(usedQuota)); + binding.progress.setMax(EDriveWidget.convertIntoMB(totalQuota)); + binding.progress.setProgress(EDriveWidget.convertIntoMB(usedQuota)); binding.progress.setVisibility(View.VISIBLE); String totalShownQuota = "?"; @@ -141,9 +138,8 @@ public class AccountsActivity extends AppCompatActivity { binding.upgrade.setVisibility(View.VISIBLE); binding.upgrade.setOnClickListener(v -> { - final Intent upgradeIntent = buildIntent(Intent.ACTION_VIEW, - String.format(EDriveWidget.WEBPAGE, email, token, - dataForWeb(totalQuota))); + final Intent upgradeIntent = EDriveWidget.buildIntent(Intent.ACTION_VIEW, + EDriveWidget.buildUpgradeUrl(email, token, totalQuota)); startActivity(upgradeIntent); }); diff --git a/app/src/main/java/foundation/e/drive/periodicScan/FullScanWorker.kt b/app/src/main/java/foundation/e/drive/periodicScan/FullScanWorker.kt index 194dea8d5751d999a41bd45f879e3cda09efefb9..d309cafb5aecf9c7d4ee6c8529b3c92f4d5f5db8 100644 --- a/app/src/main/java/foundation/e/drive/periodicScan/FullScanWorker.kt +++ b/app/src/main/java/foundation/e/drive/periodicScan/FullScanWorker.kt @@ -29,6 +29,7 @@ import foundation.e.drive.utils.AppConstants.KEY_LAST_SCAN_TIME import foundation.e.drive.utils.CommonUtils import foundation.e.drive.utils.DavClientProvider import timber.log.Timber +import androidx.core.content.edit class FullScanWorker(private val context: Context, private val workerParams: WorkerParameters) : Worker(context, workerParams) @@ -73,9 +74,9 @@ class FullScanWorker(private val context: Context, private val workerParams: Wor syncRequests.putAll(localSyncRequests) Timber.d("${localSyncRequests.size} request collected from device") - prefs.edit() - .putLong(KEY_LAST_SCAN_TIME, System.currentTimeMillis()) - .apply(); + prefs.edit { + putLong(KEY_LAST_SCAN_TIME, System.currentTimeMillis()) + } if (syncRequests.isEmpty()) { Timber.d("Nothing to sync") diff --git a/app/src/main/java/foundation/e/drive/recovery/RecoveryPreferences.kt b/app/src/main/java/foundation/e/drive/recovery/RecoveryPreferences.kt index 8824e7786599bd5419bdbf91c6bd0d80b993365f..4b95dab7327a7c2c9796f0a173b9ae4734cc05dd 100644 --- a/app/src/main/java/foundation/e/drive/recovery/RecoveryPreferences.kt +++ b/app/src/main/java/foundation/e/drive/recovery/RecoveryPreferences.kt @@ -20,6 +20,7 @@ package foundation.e.drive.recovery import android.content.Context import foundation.e.drive.recovery.RecoveryPreferences.RecoveryStatus.RecoveryCompleted import foundation.e.drive.recovery.RecoveryPreferences.RecoveryStatus.RecoveryNeeded +import androidx.core.content.edit object RecoveryPreferences { @@ -44,11 +45,11 @@ object RecoveryPreferences { context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE) when (status) { RecoveryCompleted -> { - preferences.edit().putBoolean(KEY_IS_DATA_ALREADY_RECOVERED, true).apply() + preferences.edit { putBoolean(KEY_IS_DATA_ALREADY_RECOVERED, true) } } RecoveryNeeded -> { - preferences.edit().putBoolean(KEY_IS_DATA_ALREADY_RECOVERED, false).apply() + preferences.edit { putBoolean(KEY_IS_DATA_ALREADY_RECOVERED, false) } } } } diff --git a/app/src/main/java/foundation/e/drive/widgets/EDriveWidget.java b/app/src/main/java/foundation/e/drive/widgets/EDriveWidget.java index 0e9558b449939dc89eaa508a4fb74e22b084c33a..38bc1b7bf3508945eed8ed80d21d557393e14ed9 100644 --- a/app/src/main/java/foundation/e/drive/widgets/EDriveWidget.java +++ b/app/src/main/java/foundation/e/drive/widgets/EDriveWidget.java @@ -50,9 +50,6 @@ import timber.log.Timber; * Implementation of App Widget functionality. */ public class EDriveWidget extends AppWidgetProvider { - - public static final String WEBPAGE = - "https://murena.com/ecloud-subscriptions/?username=%s&token=%s¤t-quota=%s&from=wp"; private static final String ADD_ACCOUNT_WEBPAGE = "https://murena.io/signup/e-email-invite"; private static final String ACCOUNT_PROVIDER_EELO = "e.foundation.webdav.eelo"; @@ -70,6 +67,17 @@ public class EDriveWidget extends AppWidgetProvider { private RemoteViews views; private Account account = null; + public static @NonNull String buildUpgradeUrl( + @NonNull String email, + @NonNull String token, + @NonNull String totalQuota) { + return "https://murena.com/ecloud-subscriptions/" + + "?username=" + email + + "&token=" + token + + "¤t-quota=" + dataForWeb(totalQuota) + + "&from=wp"; + } + @NonNull public static String dataForWeb(@NonNull String bytes) { long convertedBytes = 0; @@ -303,8 +311,8 @@ public class EDriveWidget extends AppWidgetProvider { views.setOnClickPendingIntent(R.id.settings, pendingIntentSettings); final PendingIntent pendingIntentUpgrade = PendingIntent.getActivity(context, 0, - buildIntent(Intent.ACTION_VIEW, String.format(WEBPAGE, email, token, - dataForWeb(totalQuota))), PendingIntent.FLAG_IMMUTABLE); + buildIntent(Intent.ACTION_VIEW, buildUpgradeUrl(email, token, + totalQuota)), PendingIntent.FLAG_IMMUTABLE); views.setOnClickPendingIntent(R.id.upgrade, pendingIntentUpgrade); } diff --git a/app/src/main/res/drawable/widget_preview_image.xml b/app/src/main/res/drawable/widget_preview_image.xml index 16319bb05883d1edb9a5e179c0c7c1a92b46fd27..68fa20fbabb6bbdcc014baa27bfd3f35fd4b462c 100644 --- a/app/src/main/res/drawable/widget_preview_image.xml +++ b/app/src/main/res/drawable/widget_preview_image.xml @@ -16,8 +16,8 @@ --> . --> - + \ No newline at end of file diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index efcd3e2742b161ee5f454b2e8bd43bec0c054530..f9ff2bd787e5bad8019b638f40964fc9da744831 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -36,6 +36,7 @@ Sincronizar el canal de trabajador %d archivo a sincronizar + %d archivos a sincronizar %d archivos a sincronizar Notificación sobre el contenido que está en sincronización diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index e8775c8a8d180d0b87e59fbba2c9717fa495d056..aa5f6abcdf0b8da9cda368ce652bd8571c0c3364 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -4,20 +4,20 @@ Paramètres additionnels du compte Paramètres %1$s copié dans le presse-papier - L\'espace de stockage cloud qui vous est alloué est rempli à 80%. - 99% de l\'espace de stockage cloud qui vous est alloué est utilisé. Merci de faire le nécessaire. - L\'espace de stockage cloud qui vous est alloué est rempli à 90%. + L’espace de stockage cloud qui vous est alloué est rempli à 80%. + 99% de l’espace de stockage cloud qui vous est alloué est utilisé. Merci de faire le nécessaire. + L’espace de stockage cloud qui vous est alloué est rempli à 90%. Votre stockage cloud est presque plein. Votre stockage cloud est plein. Canal de notification eDrive - Aucune connexion internet détectée, le widget se mettra à jour dès qu\'il détectera une connexion + Aucune connexion internet détectée, le widget se mettra à jour dès qu’il détectera une connexion Alias alias Masquer les alias copier •   Créer un compte - S\'identifier + S’identifier Synchronisez vos photos et vidéos \nde façon sécurisée avec le cloud Dernière synchronisation : %1$s diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 3322eb1d88dead167b187bec70b335e86e44fed7..8f4c50e193644b44ec00ca5f967d0561156aed55 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -1,7 +1,7 @@ /e/ Drive - Impostazioni aggiuntive per l\'account + Impostazioni aggiuntive per l’account Impostazioni Copiato %1$s negli appunti Hai riempito il tuo spazio di archiviazione cloud al 80%. diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 2da1bcc2abbee62d7fe57524b27d68817295ae0c..1dbd24d37756ffd138f1e2555a562435fdff67eb 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -7,8 +7,8 @@ Abonnement:  Upgraden Laatst gesynchroniseerd: %1$s - Synchroniseer je foto\'s en -\nvideo\'s met de cloud + Synchroniseer je foto’s en +\nvideo’s met de cloud Inloggen Account aanmaken •   @@ -27,7 +27,7 @@ Verberg aliassen %1$s gekopieerd naar klembord Geen internet verbinding gedetecteerd. Widget zal automatisch updaten bij een actieve internet verbinding - Afbeeldingen en video\'s + Afbeeldingen en video’s Applicatie instellingen Sta synchronisatie toe op netwerken met data limiet Mijn Account diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1ebbd1a25159a0a0efefca494dc111b8152bd573..b0cfba3ef09df138f644496c36b7985dbf1c9185 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,7 +1,7 @@ /e/ Drive e.foundation.webdav.eelo - Allow files\' sync on metered network + Allow files’ sync on metered network Pictures and videos Application settings @@ -24,19 +24,19 @@ Aliases No internet connection detected, widget will auto update upon active internet connection - eDrive\'s notification - eDrive\'s notification channel + eDrive’s notification + eDrive’s notification channel Your cloud storage is full. Your cloud storage is nearly full. 99% of your allotted cloud storage is used. Please take action. - You\'ve filled your allotted cloud storage up to 90%. - You\'ve filled your allotted cloud storage up to 80%. + You’ve filled your allotted cloud storage up to 90%. + You’ve filled your allotted cloud storage up to 80%. File synchronization %d file to sync %d files to sync - Sync worker\'s channel + Sync worker’s channel Notification about content being synchronized Copied %1$s to clipboard Settings diff --git a/app/src/main/res/values/style.xml b/app/src/main/res/values/style.xml index 52ae0a5179ff5855f073001adb05cb99e233a7aa..d8f30661be185e25b07af32e755e9c6427f56af4 100644 --- a/app/src/main/res/values/style.xml +++ b/app/src/main/res/values/style.xml @@ -13,6 +13,7 @@ @bool/isLight @bool/isLight @color/background + true