diff --git a/.github/workflows/detekt.yml b/.github/workflows/detekt.yml new file mode 100644 index 00000000000..79445c53c7c --- /dev/null +++ b/.github/workflows/detekt.yml @@ -0,0 +1,128 @@ +# This is a basic workflow that is manually triggered + +name: Detekt + +# Controls when the action will run. Workflow runs when manually triggered using the UI +# or API. +on: + workflow_dispatch: + # Inputs the workflow accepts. + inputs: + name: + # Friendly description to be shown in the UI instead of 'name' + description: 'Detekt' + # Default value if no value is explicitly provided + default: 'World' + # Input has to be provided for the workflow to run + required: true + # The data type of the input + type: string + + push: + branches: + - feature/detekt + - feature/run_actions_parallel + pull_request: + branches: + - "master" + paths: + - ".github/workflows/detekt.yml" + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + # This workflow contains a single job called "greet" + owncloudApp: + # The type of runner that the job will run on + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + + # Configure Gradle for optimal use in GitHub Actions, including caching of downloaded dependencies. + # See: https://github.com/gradle/actions/blob/main/setup-gradle/README.md + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + - name: module owncloudApp + run: ./gradlew detekt + + owncloudDomain: + # The type of runner that the job will run on + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + + # Configure Gradle for optimal use in GitHub Actions, including caching of downloaded dependencies. + # See: https://github.com/gradle/actions/blob/main/setup-gradle/README.md + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + - name: module owncloudDomain + run: ./gradlew owncloudDomain:detekt + + owncloudData: + # The type of runner that the job will run on + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + + # Configure Gradle for optimal use in GitHub Actions, including caching of downloaded dependencies. + # See: https://github.com/gradle/actions/blob/main/setup-gradle/README.md + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + - name: module owncloudData + run: ./gradlew owncloudData:detekt + + owncloudComLibrary: + # The type of runner that the job will run on + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + + # Configure Gradle for optimal use in GitHub Actions, including caching of downloaded dependencies. + # See: https://github.com/gradle/actions/blob/main/setup-gradle/README.md + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + - name: module owncloudComLibrary + run: ./gradlew owncloudComLibrary:detekt + + + owncloudTestUtil: + # The type of runner that the job will run on + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + + # Configure Gradle for optimal use in GitHub Actions, including caching of downloaded dependencies. + # See: https://github.com/gradle/actions/blob/main/setup-gradle/README.md + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + - name: module owncloudTestUtil + run: ./gradlew owncloudTestUtil:detekt diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index 0a8ab42d20d..af4fbad11b6 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -2,6 +2,7 @@ - \ No newline at end of file + diff --git a/build.gradle b/build.gradle index 6fc0ba10acc..86ee228dcee 100644 --- a/build.gradle +++ b/build.gradle @@ -21,6 +21,7 @@ buildscript { plugins { alias libs.plugins.sonarqube alias libs.plugins.ksp apply false + alias libs.plugins.detekt } allprojects { @@ -37,6 +38,7 @@ subprojects { apply plugin: "com.google.devtools.ksp" apply plugin: "org.jlleitschuh.gradle.ktlint" apply plugin: "org.sonarqube" + apply plugin: "io.gitlab.arturbosch.detekt" } sonarqube { diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml new file mode 100644 index 00000000000..cb449f47faf --- /dev/null +++ b/config/detekt/detekt.yml @@ -0,0 +1,926 @@ +build: + maxIssues: 0 + excludeCorrectable: false + weights: + # complexity: 2 + # LongParameterList: 1 + # style: 1 + # comments: 1 + +config: + validation: true + warningsAsErrors: false + # when writing own rules with new properties, exclude the property path e.g.: 'my_rule_set,.*>.*>[my_property]' + excludes: '' + +processors: + active: true + exclude: + - 'DetektProgressListener' + # - 'KtFileCountProcessor' + # - 'PackageCountProcessor' + # - 'ClassCountProcessor' + # - 'FunctionCountProcessor' + # - 'PropertyCountProcessor' + # - 'ProjectComplexityProcessor' + # - 'ProjectCognitiveComplexityProcessor' + # - 'ProjectLLOCProcessor' + # - 'ProjectCLOCProcessor' + # - 'ProjectLOCProcessor' + # - 'ProjectSLOCProcessor' + # - 'LicenseHeaderLoaderExtension' + +console-reports: + active: true + exclude: + - 'ProjectStatisticsReport' + - 'ComplexityReport' + - 'NotificationReport' + - 'FindingsReport' + - 'FileBasedFindingsReport' + # - 'LiteFindingsReport' + +output-reports: + active: true + exclude: + # - 'TxtOutputReport' + # - 'XmlOutputReport' + # - 'HtmlOutputReport' + +comments: + active: false + AbsentOrWrongFileLicense: + active: false + licenseTemplateFile: 'license.template' + licenseTemplateIsRegex: false + CommentOverPrivateFunction: + active: false + CommentOverPrivateProperty: + active: false + DeprecatedBlockTag: + active: false + EndOfSentenceFormat: + active: false + endOfSentenceFormat: '([.?!][ \t\n\r\f<])|([.?!:]$)' + KDocReferencesNonPublicProperty: + active: false + OutdatedDocumentation: + active: false + matchTypeParameters: true + matchDeclarationsOrder: true + allowParamOnConstructorProperties: false + UndocumentedPublicClass: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + searchInNestedClass: true + searchInInnerClass: true + searchInInnerObject: true + searchInInnerInterface: true + UndocumentedPublicFunction: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + UndocumentedPublicProperty: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + +complexity: + active: true + CognitiveComplexMethod: + active: true + threshold: 20 + ComplexCondition: + active: true + threshold: 5 + ComplexInterface: + active: false + threshold: 10 + includeStaticDeclarations: false + includePrivateDeclarations: false + CyclomaticComplexMethod: + active: true + threshold: 15 + ignoreSingleWhenExpression: false + ignoreSimpleWhenEntries: false + ignoreNestingFunctions: false + nestingFunctions: + - 'also' + - 'apply' + - 'forEach' + - 'isNotNull' + - 'ifNull' + - 'let' + - 'run' + - 'use' + - 'with' + LabeledExpression: + active: false + ignoredLabels: [] + LargeClass: + active: false + threshold: 600 + LongMethod: + active: true + threshold: 100 + LongParameterList: + active: false + functionThreshold: 6 + constructorThreshold: 7 + ignoreDefaultParameters: false + ignoreDataClasses: true + ignoreAnnotatedParameter: [] + MethodOverloading: + active: true + threshold: 6 + NamedArguments: + active: false + threshold: 3 + ignoreArgumentsMatchingNames: false + NestedBlockDepth: + active: true + threshold: 5 + NestedScopeFunctions: + active: false + threshold: 1 + functions: ['kotlin.apply', 'kotlin.run', 'kotlin.with', 'kotlin.let', 'kotlin.also'] + ReplaceSafeCallChainWithRun: + active: false + StringLiteralDuplication: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + threshold: 3 + ignoreAnnotation: true + excludeStringsWithLessThan5Characters: true + ignoreStringsRegex: '$^' + TooManyFunctions: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + thresholdInFiles: 11 + thresholdInClasses: 11 + thresholdInInterfaces: 11 + thresholdInObjects: 11 + thresholdInEnums: 11 + ignoreDeprecated: false + ignorePrivate: false + ignoreOverridden: false + +coroutines: + active: true + GlobalCoroutineUsage: + active: true + InjectDispatcher: + active: false + dispatcherNames: + - 'IO' + - 'Default' + - 'Unconfined' + RedundantSuspendModifier: + active: true + SleepInsteadOfDelay: + active: true + SuspendFunWithCoroutineScopeReceiver: + active: true + SuspendFunWithFlowReturnType: + active: true + +empty-blocks: + active: true + EmptyCatchBlock: + active: true + allowedExceptionNameRegex: '_|(ignore|expected).*' + EmptyClassBlock: + active: true + EmptyDefaultConstructor: + active: true + EmptyDoWhileBlock: + active: true + EmptyElseBlock: + active: true + EmptyFinallyBlock: + active: true + EmptyForBlock: + active: true + EmptyFunctionBlock: + active: true + ignoreOverridden: true + EmptyIfBlock: + active: true + EmptyInitBlock: + active: true + EmptyKtFile: + active: true + EmptySecondaryConstructor: + active: true + EmptyTryBlock: + active: true + EmptyWhenBlock: + active: true + EmptyWhileBlock: + active: true + +exceptions: + active: true + ExceptionRaisedInUnexpectedLocation: + active: false + methodNames: + - 'equals' + - 'finalize' + - 'hashCode' + - 'toString' + InstanceOfCheckForException: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + NotImplementedDeclaration: + active: true + ObjectExtendsThrowable: + active: true + PrintStackTrace: + active: true + RethrowCaughtException: + active: true + ReturnFromFinally: + active: true + ignoreLabeled: false + SwallowedException: + active: true + ignoredExceptionTypes: + - 'InterruptedException' + - 'MalformedURLException' + - 'NumberFormatException' + - 'ParseException' + allowedExceptionNameRegex: '_|(ignore|expected).*' + ThrowingExceptionFromFinally: + active: true + ThrowingExceptionInMain: + active: false + ThrowingExceptionsWithoutMessageOrCause: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + exceptions: + - 'ArrayIndexOutOfBoundsException' + - 'Exception' + - 'IllegalArgumentException' + - 'IllegalMonitorStateException' + - 'IllegalStateException' + - 'IndexOutOfBoundsException' + - 'NullPointerException' + - 'RuntimeException' + - 'Throwable' + ThrowingNewInstanceOfSameException: + active: true + TooGenericExceptionCaught: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + exceptionNames: + - 'ArrayIndexOutOfBoundsException' + - 'Error' + - 'Exception' + - 'IllegalMonitorStateException' + - 'IndexOutOfBoundsException' + - 'NullPointerException' + - 'RuntimeException' + - 'Throwable' + allowedExceptionNameRegex: '_|(ignore|expected).*' + TooGenericExceptionThrown: + active: true + exceptionNames: + - 'Error' + - 'Exception' + - 'RuntimeException' + - 'Throwable' + +formatting: + active: false + AnnotationOnSeparateLine: + active: true + indentSize: 4 + AnnotationSpacing: + active: true + ArgumentListWrapping: + active: true + indentSize: 4 + maxLineLength: 120 + BlockCommentInitialStarAlignment: + active: true + ChainWrapping: + active: true + indentSize: 4 + ClassName: + active: false + CommentSpacing: + active: true + CommentWrapping: + active: true + indentSize: 4 + ContentReceiverMapping: + active: false + maxLineLength: 120 + indentSize: 4 + DiscouragedCommentLocation: + active: false + EnumEntryNameCase: + active: true + EnumWrapping: + active: false + intentSize: 4 + Filename: + active: true + FinalNewline: + active: true + insertFinalNewLine: true + FunKeywordSpacing: + active: true + FunctionName: + active: false + FunctionReturnTypeSpacing: + active: true + maxLineLength: 120 + FunctionSignature: + active: false + forceMultilineWhenParameterCountGreaterOrEqualThan: 2147483647 + functionBodyExpressionWrapping: 'default' + maxLineLength: 120 + indentSize: 4 + FunctionStartOfBodySpacing: + active: true + FunctionTypeReferenceSpacing: + active: true + IfElseBracing: + active: false + IfElseWrapping: + active: false + indentSize: 4 + ImportOrdering: + active: true + layout: '*,java.**,javax.**,kotlin.**,^' + Indentation: + active: true + indentSize: 4 + KdocWrapping: + active: true + indentSize: 4 + MaximumLineLength: + active: true + maxLineLength: 120 + ignoreBackTickedIdentifier: false + ModifierListSpacing: + active: true + MultiLineIfElse: + active: true + indentSize: 4 + MultilineExpressionWrapping: + active: false + indentSize: 4 + NoBlankLineBeforeRbrace: + active: true + NoBlankLineInList: + active: false + NoBlankLinesInChainedMethodCalls: + active: true + NoConsecutiveBlankLines: + active: true + NoConsecutiveComments: + active: false + NoEmptyClassBody: + active: true + NoEmptyFirstLineInClassBody: + active: false + indentSize: 4 + NoEmptyFirstLineInMethodBlock: + active: true + NoLineBreakAfterElse: + active: true + NoLineBreakBeforeAssignment: + active: true + NoMultipleSpaces: + active: true + NoSemicolons: + active: true + NoSingleLineBlockComment: + active: false + indentSize: 4 + NoTrailingSpaces: + active: true + NoUnitReturn: + active: true + NoUnusedImports: + active: true + NoWildcardImports: + active: true + packagesToUseImportOnDemandProperty: 'java.util.*,kotlinx.android.synthetic.**' + NullableTypeSpacing: + active: true + PackageName: + active: true + ParameterListSpacing: + active: false + ParameterListWrapping: + active: true + maxLineLength: 120 + indentSize: 4 + ParameterWrapping: + active: true + indentSize: 4 + MaxLineLength: 120 + PropertyName: + active: false + PropertyWrapping: + active: true + indentSize: 4 + maxLineLength: 120 + SpacingAroundAngleBrackets: + active: true + SpacingAroundColon: + active: true + SpacingAroundComma: + active: true + SpacingAroundCurly: + active: true + SpacingAroundDot: + active: true + SpacingAroundDoubleColon: + active: true + SpacingAroundKeyword: + active: true + SpacingAroundOperators: + active: true + SpacingAroundParens: + active: true + SpacingAroundRangeOperator: + active: true + SpacingAroundUnaryOperator: + active: true + SpacingBetweenDeclarationsWithAnnotations: + active: true + SpacingBetweenDeclarationsWithComments: + active: true + SpacingBetweenFunctionNameAndOpeningParenthesis: + active: true + StringTemplate: + active: true + StringTemplateIndent: + active: false + indentSize: 4 + TrailingCommaOnCallSite: + active: false + useTrailingCommaOnCallSite: true + TrailingCommaOnDeclarationSite: + active: false + useTrailingCommaOnDeclarationSite: true + TryCatchFinallySpacing: + active: false + indentSize: 4 + TypeArgumentListSpacing: + active: false + indentSize: 4 + TypeParameterListSpacing: + active: false + indentSize: 4 + UnnecessaryParenthesesBeforeTrailingLambda: + active: true + Wrapping: + active: true + indentSize: 4 + maxLineLength: 120 + +libraries: + active: false + ForbiddenPublicDataClass: + active: true + ignorePackages: ['*.internal', '*.internal.*'] + LibraryCodeMustSpecifyReturnType: + active: true + allowOmitUnit: false + LibraryEntitiesShouldNotBePublic: + active: true + +naming: + active: true + BooleanPropertyNaming: + active: false + allowedPattern: '^(is|has|are)' + ClassNaming: + active: true + classPattern: '[A-Z][a-zA-Z0-9]*' + ConstructorParameterNaming: + active: true + parameterPattern: '[a-z][A-Za-z0-9]*' + privateParameterPattern: '[a-z][A-Za-z0-9]*' + excludeClassPattern: '$^' + EnumNaming: + active: true + enumEntryPattern: '[A-Z][_a-zA-Z0-9]*' + ForbiddenClassName: + active: false + forbiddenName: [] + FunctionMaxLength: + active: false + maximumFunctionNameLength: 30 + FunctionMinLength: + active: false + minimumFunctionNameLength: 3 + FunctionNaming: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + functionPattern: '[a-z][a-zA-Z0-9]*' + excludeClassPattern: '$^' + FunctionParameterNaming: + active: true + parameterPattern: '[a-z][A-Za-z0-9]*' + excludeClassPattern: '$^' + InvalidPackageDeclaration: + active: true + rootPackage: '' + requireRootInDeclaration: false + LambdaParameterNaming: + active: true + parameterPattern: '[a-z][A-Za-z0-9]*|_' + MatchingDeclarationName: + active: true + mustBeFirst: false + MemberNameEqualsClassName: + active: true + ignoreOverridden: true + NoNameShadowing: + active: true + NonBooleanPropertyPrefixedWithIs: + active: false + ObjectPropertyNaming: + active: true + constantPattern: '[A-Za-z][_A-Za-z0-9]*' + propertyPattern: '[A-Za-z][_A-Za-z0-9]*' + privatePropertyPattern: '(_)?[A-Za-z][_A-Za-z0-9]*' + PackageNaming: + active: true + packagePattern: '[a-z]+(\.[a-z][A-Za-z0-9]*)*' + TopLevelPropertyNaming: + active: true + constantPattern: '[A-Z][_A-Z0-9]*' + propertyPattern: '[A-Za-z][_A-Za-z0-9]*' + privatePropertyPattern: '_?[A-Za-z][_A-Za-z0-9]*' + VariableMaxLength: + active: false + maximumVariableNameLength: 64 + VariableMinLength: + active: false + minimumVariableNameLength: 1 + VariableNaming: + active: true + variablePattern: '[a-z][A-Za-z0-9]*' + privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*' + excludeClassPattern: '$^' + +performance: + active: true + ArrayPrimitive: + active: true + CouldBeSequence: + active: false + threshold: 3 + ForEachOnRange: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + SpreadOperator: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + UnnecessaryPartOfBinaryExpression: + active: true + UnnecessaryTemporaryInstantiation: + active: true + +potential-bugs: + active: true + AvoidReferentialEquality: + active: true + forbiddenTypePatterns: + - 'kotlin.String' + CastNullableToNonNullableType: + active: true + CastToNullableType: + active: true + Deprecation: + active: false + DontDowncastCollectionTypes: + active: true + DoubleMutabilityForCollection: + active: true + mutableTypes: + - 'kotlin.collections.MutableList' + - 'kotlin.collections.MutableMap' + - 'kotlin.collections.MutableSet' + - 'java.util.ArrayList' + - 'java.util.LinkedHashSet' + - 'java.util.HashSet' + - 'java.util.LinkedHashMap' + - 'java.util.HashMap' + ElseCaseInsteadOfExhaustiveWhen: + active: true + ignoredSubjectTypes: [] + EqualsAlwaysReturnsTrueOrFalse: + active: true + EqualsWithHashCodeExist: + active: true + ExitOutsideMain: + active: true + ExplicitGarbageCollectionCall: + active: true + HasPlatformType: + active: true + IgnoredReturnValue: + active: false + restrictToConfig: true + returnValueAnnotations: + - '*.CheckResult' + - '*.CheckReturnValue' + ignoreReturnValueAnnotations: + - '*.CanIgnoreReturnValue' + ignoreFunctionCall: [] + ImplicitDefaultLocale: + active: true + ImplicitUnitReturnType: + active: true + allowExplicitReturnType: false + InvalidRange: + active: true + IteratorHasNextCallsNextMethod: + active: true + IteratorNotThrowingNoSuchElementException: + active: true + LateinitUsage: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + ignoreOnClassesPattern: '' + MapGetWithNotNullAssertionOperator: + active: true + MissingPackageDeclaration: + active: true + NullCheckOnMutableProperty: + active: false + NullableToStringCall: + active: true + PropertyUsedBeforeDeclaration: + active: true + UnconditionalJumpStatementInLoop: + active: true + UnnecessaryNotNullCheck: + active: true + UnnecessaryNotNullOperator: + active: true + UnnecessarySafeCall: + active: true + UnreachableCatchBlock: + active: true + UnreachableCode: + active: true + UnsafeCallOnNullableType: + active: false + UnsafeCast: + active: true + UnusedUnaryOperator: + active: true + UselessPostfixExpression: + active: true + WrongEqualsTypeParameter: + active: true + +style: + active: true + AlsoCouldBeApply: + active: true + BracesOnIfStatements: + active: true + singleLine: 'consistent' + multiLine: 'consistent' + BracesOnWhenStatements: + active: true + singleLine: 'consistent' + multiLine: 'consistent' + CanBeNonNullable: + active: true + CascadingCallWrapping: + active: false + includeElvis: true + ClassOrdering: + active: true + CollapsibleIfStatements: + active: true + DataClassContainsFunctions: + active: false + allowOperators: false + conversionFunctionPrefix: ['to'] + DataClassShouldBeImmutable: + active: false + DestructuringDeclarationWithTooManyEntries: + active: false + maxDestructuringEntries: 3 + DoubleNegativeLambda: + active: true + negativeFunctions: ['takeUnless', 'none'] + negativeFunctionNameParts: ['not', 'non'] + EqualsNullCall: + active: true + EqualsOnSignatureLine: + active: true + ExplicitCollectionElementAccessMethod: + active: true + ExplicitItLambdaParameter: + active: true + ExpressionBodySyntax: + active: true + includeLineWrapping: true + ForbiddenAnnotation: + active: false + annotations: ['java.lang.SuppressWarnings', 'java.lang.Deprecated', 'java.lang.annotation.Documented', 'java.lang.annotation.Target', 'java.lang.annotation.Retention', 'java.lang.annotation.Repeatable', 'java.lang.annotation.Inherited'] + ForbiddenComment: + active: true + allowedPatterns: '' + comments: ['FIXME:', 'STOPSHIP:', 'TODO:'] + ForbiddenImport: + active: false + imports: [] + forbiddenPatterns: '' + ForbiddenMethodCall: + active: true + methods: ['kotlin.io.print', 'kotlin.io.println'] + ForbiddenSuppress: + active: false + rules: [] + ForbiddenVoid: + active: true + ignoreOverridden: false + ignoreUsageInGenerics: false + FunctionOnlyReturningConstant: + active: true + ignoreOverridableFunction: true + ignoreActualFunction: true + excludedFunctions: [] + LoopWithTooManyJumpStatements: + active: true + maxJumpCount: 1 + MagicNumber: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + ignoreNumbers: + - '-1' + - '0' + - '1' + - '2' + ignoreHashCodeFunction: true + ignorePropertyDeclaration: false + ignoreLocalVariableDeclaration: false + ignoreConstantDeclaration: true + ignoreCompanionObjectPropertyDeclaration: true + ignoreAnnotation: false + ignoreNamedArgument: true + ignoreEnums: false + ignoreRanges: false + ignoreExtensionFunctions: true + MandatoryBracesLoops: + active: true + MaxLineLength: + active: true + maxLineLength: 150 + excludePackageStatements: true + excludeImportStatements: true + excludeCommentStatements: false + MayBeConst: + active: true + ModifierOrder: + active: true + MultilineLambdaItParameter: + active: false + MultilineRawStringIndentation: + active: false + indentSize: 4 + trimmingMethods: ['trimIndent', 'trimMargin'] + NestedClassesVisibility: + active: true + NewLineAtEndOfFile: + active: true + NoTabs: + active: false + NullableBooleanCheck: + active: true + ObjectLiteralToLambda: + active: true + OptionalAbstractKeyword: + active: true + OptionalUnit: + active: true + PreferToOverPairSyntax: + active: true + ProtectedMemberInFinalClass: + active: true + RedundantExplicitType: + active: true + RedundantHigherOrderMapUsage: + active: true + RedundantVisibilityModifierRule: + active: true + ReturnCount: + active: true + max: 2 + excludedFunctions: ['equals'] + excludeLabeled: false + excludeReturnFromLambda: true + excludeGuardClauses: false + SafeCast: + active: true + SerialVersionUIDInSerializableClass: + active: true + SpacingBetweenPackageAndImports: + active: true + StringShouldBeRawString: + active: false + maxEscapedCharacterCount: 2 + ignoredCharacters: [] + ThrowsCount: + active: false + max: 2 + excludeGuardClauses: false + TrailingWhitespace: + active: true + TrimMultilineRawString: + active: false + trimmingMethods: ['trimIndent', 'trimMargin'] + UnderscoresInNumericLiterals: + active: false + acceptableLength: 4 + allowNonStandardGrouping: false + UnnecessaryAbstractClass: + active: true + UnnecessaryAnnotationUseSiteTarget: + active: false + UnnecessaryApply: + active: true + UnnecessaryBackticks: + active: false + UnnecessaryBracesAroundTrailingLambda: + active: true + UnnecessaryFilter: + active: true + UnnecessaryInheritance: + active: true + UnnecessaryInnerClass: + active: true + UnnecessaryLet: + active: true + UnnecessaryParentheses: + active: false + allowForUnclearPrecedence: true + UntilInsteadOfRangeTo: + active: true + UnusedImports: + active: true + UnusedParameter: + active: true + allowedNames: 'ignored|expected' + UnusedPrivateClass: + active: true + UnusedPrivateMember: + active: true + allowedNames: '(_|ignored|expected|serialVersionUID)' + UnusedPrivateProperty: + active: true + allowedNames: '_|ignored|expected|serialVersionUID' + UseAnyOrNoneInsteadOfFind: + active: true + UseArrayLiteralsInAnnotations: + active: true + UseCheckNotNull: + active: false + UseCheckOrError: + active: false + UseDataClass: + active: true + allowVars: true + UseEmptyCounterpart: + active: true + UseIfEmptyOrIfBlank: + active: true + UseIfInsteadOfWhen: + active: true + ignoreWhenContainingVariableDeclaration: false + UseIsNullOrEmpty: + active: true + UseLet: + active: true + UseOrEmpty: + active: true + UseRequire: + active: false + UseRequireNotNull: + active: false + UseSumOfInsteadOfFlatMapSize: + active: false + UselessCallOnNotNull: + active: true + UtilityClassWithPublicConstructor: + active: true + VarCouldBeVal: + active: true + ignoreLateinitVar: false + WildcardImport: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + excludeImports: + - 'java.util.*' diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 841312da68b..16106794a60 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -22,6 +22,7 @@ androidxTestMonitor = "1.6.1" androidxTestUiAutomator ="2.2.0" androidxWork = "2.8.1" coil = "2.2.2" +detekt = "1.23.3" dexopener = "2.0.5" disklrucache = "2.0.2" media3 ="1.1.1" @@ -80,6 +81,8 @@ androidx-test-runner = { group = "androidx.test", name = "runner", version.ref = androidx-test-uiautomator = { group = "androidx.test.uiautomator", name = "uiautomator", version.ref = "androidxTestUiAutomator" } androidx-work-runtime-ktx = { group = "androidx.work", name = "work-runtime-ktx", version.ref = "androidxWork" } coil = { group = "io.coil-kt", name = "coil", version.ref = "coil" } +detekt-formatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref = "detekt" } +detekt-libraries = { module = "io.gitlab.arturbosch.detekt:detekt-rules-libraries", version.ref = "detekt" } dexopener = { group = "com.github.tmurakami", name = "dexopener", version.ref = "dexopener" } disklrucache = { group = "com.jakewharton", name = "disklrucache", version.ref = "disklrucache" } media3-exoplayer = { group = "androidx.media3", name = "media3-exoplayer", version.ref = "media3" } @@ -124,3 +127,4 @@ markwon = ["markwon-core", "markwon-ext-tables", "markwon-ext-strikethrough", "m kotlin = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } sonarqube = { id = "org.sonarqube", version.ref = "sonarqube" } +detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } diff --git a/owncloudApp/build.gradle b/owncloudApp/build.gradle index 45cf212a379..1d419095ec7 100644 --- a/owncloudApp/build.gradle +++ b/owncloudApp/build.gradle @@ -85,6 +85,10 @@ dependencies { debugImplementation libs.androidx.fragment.testing debugImplementation libs.androidx.test.monitor debugImplementation libs.stetho + + // Detekt + detektPlugins libs.detekt.formatting + detektPlugins libs.detekt.libraries } android { diff --git a/owncloudApp/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.kt b/owncloudApp/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.kt index 398586f1590..dca870423ec 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.kt @@ -52,14 +52,15 @@ class FileDataStorageManager( getFileByPathAndAccount(remotePath, account.name, spaceId) } - private fun getFileByPathAndAccount(remotePath: String, accountName: String, spaceId: String? = null): OCFile? = runBlocking(CoroutinesDispatcherProvider().io) { - val getFileByRemotePathUseCase: GetFileByRemotePathUseCase by inject() - - val result = withContext(CoroutineScope(CoroutinesDispatcherProvider().io).coroutineContext) { - getFileByRemotePathUseCase(GetFileByRemotePathUseCase.Params(accountName, remotePath, spaceId)) - }.getDataOrNull() - result - } + private fun getFileByPathAndAccount(remotePath: String, accountName: String, spaceId: String? = null): OCFile? = + runBlocking(CoroutinesDispatcherProvider().io) { + val getFileByRemotePathUseCase: GetFileByRemotePathUseCase by inject() + + val result = withContext(CoroutineScope(CoroutinesDispatcherProvider().io).coroutineContext) { + getFileByRemotePathUseCase(GetFileByRemotePathUseCase.Params(accountName, remotePath, spaceId)) + }.getDataOrNull() + result + } fun getRootPersonalFolder() = runBlocking(CoroutinesDispatcherProvider().io) { val getPersonalRootFolderForAccountUseCase: GetPersonalRootFolderForAccountUseCase by inject() diff --git a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/ViewModelModule.kt b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/ViewModelModule.kt index 0dbc8bd07ff..835b724a67f 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/ViewModelModule.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/ViewModelModule.kt @@ -91,12 +91,14 @@ val viewModelModule = module { ShareViewModel(filePath, accountName, get(), get(), get(), get(), get(), get(), get(), get(), get(), get()) } viewModel { (initialFolderToDisplay: OCFile, fileListOption: FileListOption) -> - MainFileListViewModel(get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), initialFolderToDisplay, fileListOption) + MainFileListViewModel(get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), + initialFolderToDisplay, fileListOption) } viewModel { (ocFile: OCFile) -> ConflictsResolveViewModel(get(), get(), get(), get(), get(), ocFile) } viewModel { AuthenticationViewModel(get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get()) } viewModel { MigrationViewModel(MainApp.dataFolder, get(), get(), get(), get(), get(), get(), get()) } - viewModel { TransfersViewModel(get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get()) } + viewModel { TransfersViewModel(get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), + get()) } viewModel { ReceiveExternalFilesViewModel(get(), get(), get(), get()) } viewModel { (accountName: String, showPersonalSpace: Boolean) -> SpacesListViewModel(get(), get(), get(), get(), get(), accountName, showPersonalSpace) diff --git a/owncloudApp/src/main/java/com/owncloud/android/extensions/FragmentActivityExt.kt b/owncloudApp/src/main/java/com/owncloud/android/extensions/FragmentActivityExt.kt index a063c429968..15f5aca1183 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/extensions/FragmentActivityExt.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/extensions/FragmentActivityExt.kt @@ -18,7 +18,7 @@ * along with this program. If not, see //www.gnu.org/licenses/>. */ -package com.owncloud.android.ui.utils +package com.owncloud.android.extensions import androidx.fragment.app.DialogFragment import androidx.fragment.app.FragmentActivity diff --git a/owncloudApp/src/main/java/com/owncloud/android/extensions/ThrowableExt.kt b/owncloudApp/src/main/java/com/owncloud/android/extensions/ThrowableExt.kt index 5dd0282e5f6..c2feb639f63 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/extensions/ThrowableExt.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/extensions/ThrowableExt.kt @@ -106,11 +106,10 @@ fun Throwable.parseError( else -> resources.getString(R.string.common_error_unknown) } - return when { - showJustReason -> { - reason - } - else -> "$genericErrorMessage ${resources.getString(R.string.error_reason)} ${reason.lowercase(Locale.getDefault())}" + return if (showJustReason) { + reason + } else { + "$genericErrorMessage ${resources.getString(R.string.error_reason)} ${reason.lowercase(Locale.getDefault())}" } } } diff --git a/owncloudApp/src/main/java/com/owncloud/android/extensions/WorkManagerExt.kt b/owncloudApp/src/main/java/com/owncloud/android/extensions/WorkManagerExt.kt index e4685b24327..6217218b233 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/extensions/WorkManagerExt.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/extensions/WorkManagerExt.kt @@ -62,16 +62,6 @@ fun WorkManager.getRunningWorkInfosLiveData(tags: List): LiveData) { - when (uiResult.error) { - is NoNetworkConnectionException -> binding.webfingerStatusText.run { + if (uiResult.error is NoNetworkConnectionException) { + binding.webfingerStatusText.run { text = getString(R.string.error_no_network_connection) setCompoundDrawablesWithIntrinsicBounds(R.drawable.no_network, 0, 0, 0) } - - else -> binding.webfingerStatusText.run { + } else { + binding.webfingerStatusText.run { text = uiResult.getThrowableOrNull()?.parseError("", resources, true) setCompoundDrawablesWithIntrinsicBounds(R.drawable.common_error, 0, 0, 0) } @@ -548,7 +548,8 @@ class LoginActivity : AppCompatActivity(), SslUntrustedCertDialog.OnSslUntrusted codeChallenge = authenticationViewModel.codeChallenge, state = authenticationViewModel.oidcState, username = username, - sendLoginHintAndUser = mdmProvider.getBrandingBoolean(mdmKey = CONFIGURATION_SEND_LOGIN_HINT_AND_USER, booleanKey = R.bool.send_login_hint_and_user), + sendLoginHintAndUser = mdmProvider.getBrandingBoolean(mdmKey = CONFIGURATION_SEND_LOGIN_HINT_AND_USER, + booleanKey = R.bool.send_login_hint_and_user), ) try { @@ -585,7 +586,8 @@ class LoginActivity : AppCompatActivity(), SslUntrustedCertDialog.OnSslUntrusted val authorizationError = intent.data?.getQueryParameter("error") val authorizationErrorDescription = intent.data?.getQueryParameter("error_description") - Timber.e("OAuth request to get authorization code failed. Error: [$authorizationError]. Error description: [$authorizationErrorDescription]") + Timber.e("OAuth request to get authorization code failed. Error: [$authorizationError]." + + " Error description: [$authorizationErrorDescription]") val authorizationException = if (authorizationError == "access_denied") UnauthorizedException() else Throwable() updateOAuthStatusIconAndText(authorizationException) @@ -614,17 +616,15 @@ class LoginActivity : AppCompatActivity(), SslUntrustedCertDialog.OnSslUntrusted var clientId: String? = null var clientSecret: String? = null - when (val serverInfo = authenticationViewModel.serverInfo.value?.peekContent()?.getStoredData()) { - is ServerInfo.OIDCServer -> { - tokenEndPoint = serverInfo.oidcServerConfiguration.tokenEndpoint - if (serverInfo.oidcServerConfiguration.isTokenEndpointAuthMethodSupportedClientSecretPost()) { - clientId = clientRegistrationInfo?.clientId ?: contextProvider.getString(R.string.oauth2_client_id) - clientSecret = clientRegistrationInfo?.clientSecret ?: contextProvider.getString(R.string.oauth2_client_secret) - } - } - else -> { - tokenEndPoint = "$serverBaseUrl${File.separator}${contextProvider.getString(R.string.oauth2_url_endpoint_access)}" + val serverInfo = authenticationViewModel.serverInfo.value?.peekContent()?.getStoredData() + if (serverInfo is ServerInfo.OIDCServer) { + tokenEndPoint = serverInfo.oidcServerConfiguration.tokenEndpoint + if (serverInfo.oidcServerConfiguration.isTokenEndpointAuthMethodSupportedClientSecretPost()) { + clientId = clientRegistrationInfo?.clientId ?: contextProvider.getString(R.string.oauth2_client_id) + clientSecret = clientRegistrationInfo?.clientSecret ?: contextProvider.getString(R.string.oauth2_client_secret) } + } else { + tokenEndPoint = "$serverBaseUrl${File.separator}${contextProvider.getString(R.string.oauth2_url_endpoint_access)}" } val scope = resources.getString(R.string.oauth2_openid_scope) diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/common/UIResult.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/common/UIResult.kt index 8e8c8f19774..c94df13a9ed 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/common/UIResult.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/common/UIResult.kt @@ -37,9 +37,10 @@ sealed class UIResult { } fun getThrowableOrNull(): Throwable? = - when (this) { - is Error -> error - else -> null + if (this is Error) { + error + } else { + null } } diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/conflicts/ConflictsResolveActivity.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/conflicts/ConflictsResolveActivity.kt index 67380a633be..af746e819bf 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/conflicts/ConflictsResolveActivity.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/conflicts/ConflictsResolveActivity.kt @@ -50,7 +50,8 @@ class ConflictsResolveActivity : AppCompatActivity(), ConflictsResolveDialogFrag lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { conflictsResolveViewModel.currentFile.collectLatest { updatedOCFile -> - Timber.d("File ${updatedOCFile?.remotePath} from ${updatedOCFile?.owner} needs to fix a conflict with etag in conflict ${updatedOCFile?.etagInConflict}") + Timber.d("File ${updatedOCFile?.remotePath} from ${updatedOCFile?.owner} needs to fix a conflict with etag" + + " in conflict ${updatedOCFile?.etagInConflict}") // Finish if the file does not exists or if the file is not in conflict anymore. updatedOCFile?.etagInConflict ?: finish() } diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/documentsprovider/DocumentsStorageProvider.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/documentsprovider/DocumentsStorageProvider.kt index bf47d996b6d..9c46544f655 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/documentsprovider/DocumentsStorageProvider.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/documentsprovider/DocumentsStorageProvider.kt @@ -161,7 +161,6 @@ class DocumentsStorageProvider : DocumentsProvider() { context?.let { NotificationUtils.notifyConflict( fileInConflict = ocFile, - account = AccountUtils.getOwnCloudAccountByName(it, ocFile.owner), context = it ) } diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/files/details/FileDetailsFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/files/details/FileDetailsFragment.kt index f1973a9f407..3af7d5892c2 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/files/details/FileDetailsFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/files/details/FileDetailsFragment.kt @@ -149,7 +149,8 @@ class FileDetailsFragment : FileFragment() { // Mimetypes not supported via open in web, send 500 if (uiResult.error is InstanceNotConfiguredException) { val message = - getString(R.string.open_in_web_error_generic) + " " + getString(R.string.error_reason) + " " + getString(R.string.open_in_web_error_not_supported) + getString(R.string.open_in_web_error_generic) + " " + getString(R.string.error_reason) + + " " + getString(R.string.open_in_web_error_not_supported) this.showMessageInSnackbar(message, Snackbar.LENGTH_LONG) } else if (uiResult.error is TooEarlyException) { this.showMessageInSnackbar(getString(R.string.open_in_web_error_too_early), Snackbar.LENGTH_LONG) diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/FileListAdapter.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/FileListAdapter.kt index cba5cdf369e..00f668f74fc 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/FileListAdapter.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/FileListAdapter.kt @@ -188,11 +188,7 @@ class FileListAdapter( val fileIcon = holder.itemView.findViewById(R.id.thumbnail).apply { tag = file.id } - val thumbnail: Bitmap? = if (file.remoteId != null) { - ThumbnailsCacheManager.getBitmapFromDiskCache(file.remoteId) - } else { - null - } + val thumbnail: Bitmap? = file.remoteId?.let { ThumbnailsCacheManager.getBitmapFromDiskCache(file.remoteId) } holder.itemView.findViewById(R.id.ListItemLayout)?.apply { contentDescription = "LinearLayout-$name" @@ -207,71 +203,7 @@ class FileListAdapter( holder.itemView.findViewById(R.id.shared_via_users_icon).isVisible = file.sharedWithSharee == true || file.isSharedWithMe - when (viewType) { - ViewType.LIST_ITEM.ordinal -> { - val view = holder as ListViewHolder - view.binding.let { - it.fileListConstraintLayout.filterTouchesWhenObscured = PreferenceUtils.shouldDisallowTouchesWithOtherVisibleWindows(context) - it.Filename.text = file.fileName - it.fileListSize.text = DisplayUtils.bytesToHumanReadable(file.length, context, true) - it.fileListLastMod.text = DisplayUtils.getRelativeTimestamp(context, file.modificationTimestamp) - it.threeDotMenu.isVisible = getCheckedItems().isEmpty() - it.threeDotMenu.contentDescription = context.getString(R.string.content_description_file_operations, file.fileName) - if (fileListOption.isAvailableOffline() || (fileListOption.isSharedByLink() && fileWithSyncInfo.space == null)) { - it.spacePathLine.path.apply { - text = file.getParentRemotePath() - isVisible = true - } - fileWithSyncInfo.space?.let { space -> - it.spacePathLine.spaceIcon.isVisible = true - it.spacePathLine.spaceName.isVisible = true - if (space.isPersonal) { - it.spacePathLine.spaceIcon.setImageResource(R.drawable.ic_folder) - it.spacePathLine.spaceName.setText(R.string.bottom_nav_personal) - } else { - it.spacePathLine.spaceName.text = space.name - } - } - } else { - it.spacePathLine.path.isVisible = false - it.spacePathLine.spaceIcon.isVisible = false - it.spacePathLine.spaceName.isVisible = false - } - it.threeDotMenu.setOnClickListener { - listener.onThreeDotButtonClick(fileWithSyncInfo = fileWithSyncInfo) - } - } - } - - ViewType.GRID_ITEM.ordinal -> { - // Filename - val view = holder as GridViewHolder - view.binding.Filename.text = file.fileName - } - - ViewType.GRID_IMAGE.ordinal -> { - val view = holder as GridImageViewHolder - val layoutParams = fileIcon.layoutParams as ViewGroup.MarginLayoutParams - - if (thumbnail == null) { - view.binding.Filename.text = file.fileName - // Reset layout params values default - manageGridLayoutParams( - layoutParams = layoutParams, - marginVertical = 0, - height = context.resources.getDimensionPixelSize(R.dimen.item_file_grid_height), - width = context.resources.getDimensionPixelSize(R.dimen.item_file_grid_width), - ) - } else { - manageGridLayoutParams( - layoutParams = layoutParams, - marginVertical = context.resources.getDimensionPixelSize(R.dimen.item_file_image_grid_margin), - height = ViewGroup.LayoutParams.MATCH_PARENT, - width = ViewGroup.LayoutParams.MATCH_PARENT, - ) - } - } - } + setSpecificViewHolder(viewType, holder, fileWithSyncInfo, thumbnail) setIconPinAccordingToFilesLocalState(holder.itemView.findViewById(R.id.localFileIndicator), fileWithSyncInfo) @@ -311,18 +243,16 @@ class FileListAdapter( if (thumbnail != null) { fileIcon.setImageBitmap(thumbnail) } - if (file.needsToUpdateThumbnail) { + if (file.needsToUpdateThumbnail && ThumbnailsCacheManager.cancelPotentialThumbnailWork(file, fileIcon)) { // generate new Thumbnail - if (ThumbnailsCacheManager.cancelPotentialThumbnailWork(file, fileIcon)) { - val task = ThumbnailsCacheManager.ThumbnailGenerationTask(fileIcon, account) - val asyncDrawable = ThumbnailsCacheManager.AsyncThumbnailDrawable(context.resources, thumbnail, task) + val task = ThumbnailsCacheManager.ThumbnailGenerationTask(fileIcon, account) + val asyncDrawable = ThumbnailsCacheManager.AsyncThumbnailDrawable(context.resources, thumbnail, task) - // If drawable is not visible, do not update it. - if (asyncDrawable.minimumHeight > 0 && asyncDrawable.minimumWidth > 0) { - fileIcon.setImageDrawable(asyncDrawable) - } - task.execute(file) + // If drawable is not visible, do not update it. + if (asyncDrawable.minimumHeight > 0 && asyncDrawable.minimumWidth > 0) { + fileIcon.setImageDrawable(asyncDrawable) } + task.execute(file) } if (file.mimeType == "image/png") { @@ -342,6 +272,77 @@ class FileListAdapter( } } + private fun setSpecificViewHolder(viewType: Int, holder: RecyclerView.ViewHolder, fileWithSyncInfo: OCFileWithSyncInfo, thumbnail: Bitmap?) { + val file = fileWithSyncInfo.file + + when (viewType) { + ViewType.LIST_ITEM.ordinal -> { + val view = holder as ListViewHolder + view.binding.let { + it.fileListConstraintLayout.filterTouchesWhenObscured = PreferenceUtils.shouldDisallowTouchesWithOtherVisibleWindows(context) + it.Filename.text = file.fileName + it.fileListSize.text = DisplayUtils.bytesToHumanReadable(file.length, context, true) + it.fileListLastMod.text = DisplayUtils.getRelativeTimestamp(context, file.modificationTimestamp) + it.threeDotMenu.isVisible = getCheckedItems().isEmpty() + it.threeDotMenu.contentDescription = context.getString(R.string.content_description_file_operations, file.fileName) + if (fileListOption.isAvailableOffline() || (fileListOption.isSharedByLink() && fileWithSyncInfo.space == null)) { + it.spacePathLine.path.apply { + text = file.getParentRemotePath() + isVisible = true + } + fileWithSyncInfo.space?.let { space -> + it.spacePathLine.spaceIcon.isVisible = true + it.spacePathLine.spaceName.isVisible = true + if (space.isPersonal) { + it.spacePathLine.spaceIcon.setImageResource(R.drawable.ic_folder) + it.spacePathLine.spaceName.setText(R.string.bottom_nav_personal) + } else { + it.spacePathLine.spaceName.text = space.name + } + } + } else { + it.spacePathLine.path.isVisible = false + it.spacePathLine.spaceIcon.isVisible = false + it.spacePathLine.spaceName.isVisible = false + } + it.threeDotMenu.setOnClickListener { + listener.onThreeDotButtonClick(fileWithSyncInfo = fileWithSyncInfo) + } + } + } + + ViewType.GRID_ITEM.ordinal -> { + // Filename + val view = holder as GridViewHolder + view.binding.Filename.text = file.fileName + } + + ViewType.GRID_IMAGE.ordinal -> { + val view = holder as GridImageViewHolder + val fileIcon = holder.itemView.findViewById(R.id.thumbnail) + val layoutParams = fileIcon.layoutParams as ViewGroup.MarginLayoutParams + + if (thumbnail == null) { + view.binding.Filename.text = file.fileName + // Reset layout params values default + manageGridLayoutParams( + layoutParams = layoutParams, + marginVertical = 0, + height = context.resources.getDimensionPixelSize(R.dimen.item_file_grid_height), + width = context.resources.getDimensionPixelSize(R.dimen.item_file_grid_width), + ) + } else { + manageGridLayoutParams( + layoutParams = layoutParams, + marginVertical = context.resources.getDimensionPixelSize(R.dimen.item_file_image_grid_margin), + height = ViewGroup.LayoutParams.MATCH_PARENT, + width = ViewGroup.LayoutParams.MATCH_PARENT, + ) + } + } + } + } + private fun manageGridLayoutParams(layoutParams: ViewGroup.MarginLayoutParams, marginVertical: Int, height: Int, width: Int) { val marginHorizontal = context.resources.getDimensionPixelSize(R.dimen.item_file_image_grid_margin) layoutParams.setMargins(marginHorizontal, marginVertical, marginHorizontal, marginVertical) diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainEmptyListFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainEmptyListFragment.kt index 01a3595105a..237c363fa4f 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainEmptyListFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainEmptyListFragment.kt @@ -29,7 +29,6 @@ import android.widget.TextView import com.owncloud.android.R import com.owncloud.android.databinding.MainEmptyListFragmentBinding - class MainEmptyListFragment : Fragment() { private var _binding: MainEmptyListFragmentBinding? = null diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainFileListFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainFileListFragment.kt index ee9abe69faf..e3144741639 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainFileListFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainFileListFragment.kt @@ -314,6 +314,50 @@ class MainFileListFragment : Fragment(), private fun subscribeToViewModels() { /* MainFileListViewModel observables */ // Observe the current folder displayed + observeCurrentFolderDisplayed() + + // Observe the current space to update the toolbar + // We can't rely exclusively on the [currentFolderDisplayed] because sometimes retrieving the space takes more time + observeSpace() + + // Observe the list of app registries that allow creating new files + observeAppRegistryToCreateFiles() + + // Observe the open in web action to trigger browser + observeOpenInWebFlow() + + // Observe the menu filtered options in multiselection + observeMenuOptions() + + // Observe the app registry in multiselection + observeAppRegistryMimeType() + + // Observe the menu filtered options for a single file + observeMenuOptionsSingleFile() + + // Observe the app registry for a single file + observeAppRegistryMimeTypeSingleFile() + + // Observe the file list UI state + observeFileListUiState() + + /* FileOperationsViewModel observables */ + // Observe the refresh folder operation + observeRefreshFolder() + + // Observe the create file with app provider operation + observeCreateFileWithAppProvider() + + // Observe the check if file is local and not available offline operation + observeCheckIfFileIsLocalAndNotAvailableOffline() + + /* TransfersViewModel observables */ + // Observe transfers + observeTransfers() + + } + + private fun observeCurrentFolderDisplayed() { collectLatestLifecycleFlow(mainFileListViewModel.currentFolderDisplayed) { currentFolderDisplayed: OCFile -> fileActions?.onCurrentFolderUpdated(currentFolderDisplayed, mainFileListViewModel.getSpace()) val fileListOption = mainFileListViewModel.fileListOption.value @@ -336,22 +380,23 @@ class MainFileListFragment : Fragment(), numberOfUploadsRefreshed = 0 hideRefreshFab() } + } - // Observe the current space to update the toolbar - // We can't rely exclusively on the [currentFolderDisplayed] because sometimes retrieving the space takes more time + private fun observeSpace() { collectLatestLifecycleFlow(mainFileListViewModel.space) { currentSpace: OCSpace? -> currentSpace?.let { fileActions?.onCurrentFolderUpdated(mainFileListViewModel.getFile(), currentSpace) } } - - // Observe the list of app registries that allow creating new files + } + private fun observeAppRegistryToCreateFiles() { collectLatestLifecycleFlow(mainFileListViewModel.appRegistryToCreateFiles) { listAppRegistry -> binding.fabNewfile.isVisible = listAppRegistry.isNotEmpty() registerFabNewFileListener(listAppRegistry) } + } - // Observe the open in web action to trigger browser + private fun observeOpenInWebFlow() { collectLatestLifecycleFlow(mainFileListViewModel.openInWebFlow) { if (it != null) { val uiResult = it.peekContent() @@ -366,7 +411,8 @@ class MainFileListFragment : Fragment(), // Mimetypes not supported via open in web, send 500 if (uiResult.error is InstanceNotConfiguredException) { val message = - getString(R.string.open_in_web_error_generic) + " " + getString(R.string.error_reason) + " " + getString(R.string.open_in_web_error_not_supported) + getString(R.string.open_in_web_error_generic) + " " + getString(R.string.error_reason) + + " " + getString(R.string.open_in_web_error_not_supported) this.showMessageInSnackbar(message, Snackbar.LENGTH_LONG) } else if (uiResult.error is TooEarlyException) { this.showMessageInSnackbar(getString(R.string.open_in_web_error_too_early), Snackbar.LENGTH_LONG) @@ -381,8 +427,9 @@ class MainFileListFragment : Fragment(), currentDefaultApplication = null } } + } - // Observe the menu filtered options in multiselection + private fun observeMenuOptions() { collectLatestLifecycleFlow(mainFileListViewModel.menuOptions) { menuOptions -> val hasWritePermission = if (checkedFiles.size == 1) { checkedFiles.first().hasWritePermission @@ -391,16 +438,18 @@ class MainFileListFragment : Fragment(), } menu?.filterMenuOptions(menuOptions, hasWritePermission) } + } - // Observe the app registry in multiselection + private fun observeAppRegistryMimeType() { collectLatestLifecycleFlow(mainFileListViewModel.appRegistryMimeType) { appRegistryMimeType -> val appProviders = appRegistryMimeType?.appProviders menu?.let { openInWebProviders = addOpenInWebMenuOptions(it, openInWebProviders, appProviders) } } + } - // Observe the menu filtered options for a single file + private fun observeMenuOptionsSingleFile() { collectLatestLifecycleFlow(mainFileListViewModel.menuOptionsSingleFile) { menuOptions -> fileSingleFile?.let { file -> val fileOptionsBottomSheetSingleFile = layoutInflater.inflate(R.layout.file_options_bottom_sheet_fragment, null) @@ -427,21 +476,19 @@ class MainFileListFragment : Fragment(), if (thumbnail != null) { thumbnailBottomSheet.setImageBitmap(thumbnail) } - if (file.needsToUpdateThumbnail) { + if (file.needsToUpdateThumbnail && ThumbnailsCacheManager.cancelPotentialThumbnailWork(file, thumbnailBottomSheet)) { // generate new Thumbnail - if (ThumbnailsCacheManager.cancelPotentialThumbnailWork(file, thumbnailBottomSheet)) { - val task = ThumbnailsCacheManager.ThumbnailGenerationTask( - thumbnailBottomSheet, - AccountUtils.getCurrentOwnCloudAccount(requireContext()) - ) - val asyncDrawable = ThumbnailsCacheManager.AsyncThumbnailDrawable(resources, thumbnail, task) + val task = ThumbnailsCacheManager.ThumbnailGenerationTask( + thumbnailBottomSheet, + AccountUtils.getCurrentOwnCloudAccount(requireContext()) + ) + val asyncDrawable = ThumbnailsCacheManager.AsyncThumbnailDrawable(resources, thumbnail, task) - // If drawable is not visible, do not update it. - if (asyncDrawable.minimumHeight > 0 && asyncDrawable.minimumWidth > 0) { - thumbnailBottomSheet.setImageDrawable(asyncDrawable) - } - task.execute(file) + // If drawable is not visible, do not update it. + if (asyncDrawable.minimumHeight > 0 && asyncDrawable.minimumWidth > 0) { + thumbnailBottomSheet.setImageDrawable(asyncDrawable) } + task.execute(file) } if (file.mimeType == "image/png") { @@ -461,119 +508,124 @@ class MainFileListFragment : Fragment(), fileOptionsBottomSheetSingleFileLayout = fileOptionsBottomSheetSingleFile.findViewById(R.id.file_options_bottom_sheet_layout) menuOptions.forEach { menuOption -> - val fileOptionItemView = BottomSheetFragmentItemView(requireContext()) - fileOptionItemView.apply { - title = if (menuOption.toResId() == R.id.action_open_file_with && !file.hasWritePermission) { - getString(R.string.actionbar_open_with_read_only) - } else { - getString(menuOption.toStringResId()) + setMenuOption(menuOption, file, dialog) + } + // Disable drag gesture + fileOptionsBottomSheetSingleFileBehavior.addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() { + override fun onStateChanged(bottomSheet: View, newState: Int) { + if (newState == BottomSheetBehavior.STATE_DRAGGING) { + fileOptionsBottomSheetSingleFileBehavior.state = BottomSheetBehavior.STATE_EXPANDED } - itemIcon = ResourcesCompat.getDrawable(resources, menuOption.toDrawableResId(), null) - setOnClickListener { - when (menuOption) { - FileMenuOption.SELECT_ALL -> { - // Not applicable here - } + } - FileMenuOption.SELECT_INVERSE -> { - // Not applicable here - } + override fun onSlide(bottomSheet: View, slideOffset: Float) {} + }) + dialog.setOnShowListener { fileOptionsBottomSheetSingleFileBehavior.peekHeight = fileOptionsBottomSheetSingleFile.measuredHeight } + dialog.show() + mainFileListViewModel.getAppRegistryForMimeType(file.mimeType, isMultiselection = false) + } + } + } - FileMenuOption.DOWNLOAD, FileMenuOption.SYNC -> { - syncFiles(listOf(file)) - } + private fun setMenuOption(menuOption: FileMenuOption, file: OCFile, dialog: BottomSheetDialog) { + val fileOptionItemView = BottomSheetFragmentItemView(requireContext()) + fileOptionItemView.apply { + title = if (menuOption.toResId() == R.id.action_open_file_with && !file.hasWritePermission) { + getString(R.string.actionbar_open_with_read_only) + } else { + getString(menuOption.toStringResId()) + } + itemIcon = ResourcesCompat.getDrawable(resources, menuOption.toDrawableResId(), null) + setOnClickListener { + when (menuOption) { + FileMenuOption.SELECT_ALL -> { + // Not applicable here + } - FileMenuOption.RENAME -> { - val dialogRename = RenameFileDialogFragment.newInstance(file) - dialogRename.show(requireActivity().supportFragmentManager, FRAGMENT_TAG_RENAME_FILE) - } + FileMenuOption.SELECT_INVERSE -> { + // Not applicable here + } - FileMenuOption.MOVE -> { - val action = Intent(activity, FolderPickerActivity::class.java) - action.putParcelableArrayListExtra(FolderPickerActivity.EXTRA_FILES, arrayListOf(file)) - action.putExtra(FolderPickerActivity.EXTRA_PICKER_MODE, FolderPickerActivity.PickerMode.MOVE) - requireActivity().startActivityForResult(action, FileDisplayActivity.REQUEST_CODE__MOVE_FILES) - } + FileMenuOption.DOWNLOAD, FileMenuOption.SYNC -> { + syncFiles(listOf(file)) + } - FileMenuOption.COPY -> { - val action = Intent(activity, FolderPickerActivity::class.java) - action.putParcelableArrayListExtra(FolderPickerActivity.EXTRA_FILES, arrayListOf(file)) - action.putExtra(FolderPickerActivity.EXTRA_PICKER_MODE, FolderPickerActivity.PickerMode.COPY) - requireActivity().startActivityForResult(action, FileDisplayActivity.REQUEST_CODE__COPY_FILES) - } + FileMenuOption.RENAME -> { + val dialogRename = RenameFileDialogFragment.newInstance(file) + dialogRename.show(requireActivity().supportFragmentManager, FRAGMENT_TAG_RENAME_FILE) + } - FileMenuOption.REMOVE -> { - filesToRemove = listOf(file) - fileOperationsViewModel.showRemoveDialog(filesToRemove) - } + FileMenuOption.MOVE -> { + val action = Intent(activity, FolderPickerActivity::class.java) + action.putParcelableArrayListExtra(FolderPickerActivity.EXTRA_FILES, arrayListOf(file)) + action.putExtra(FolderPickerActivity.EXTRA_PICKER_MODE, FolderPickerActivity.PickerMode.MOVE) + requireActivity().startActivityForResult(action, FileDisplayActivity.REQUEST_CODE__MOVE_FILES) + } - FileMenuOption.OPEN_WITH -> { - fileActions?.openFile(file) - } + FileMenuOption.COPY -> { + val action = Intent(activity, FolderPickerActivity::class.java) + action.putParcelableArrayListExtra(FolderPickerActivity.EXTRA_FILES, arrayListOf(file)) + action.putExtra(FolderPickerActivity.EXTRA_PICKER_MODE, FolderPickerActivity.PickerMode.COPY) + requireActivity().startActivityForResult(action, FileDisplayActivity.REQUEST_CODE__COPY_FILES) + } - FileMenuOption.CANCEL_SYNC -> { - fileActions?.cancelFileTransference(arrayListOf(file)) - } + FileMenuOption.REMOVE -> { + filesToRemove = listOf(file) + fileOperationsViewModel.showRemoveDialog(filesToRemove) + } - FileMenuOption.SHARE -> { - fileActions?.onShareFileClicked(file) - } + FileMenuOption.OPEN_WITH -> { + fileActions?.openFile(file) + } - FileMenuOption.DETAILS -> { - fileActions?.showDetails(file) - } + FileMenuOption.CANCEL_SYNC -> { + fileActions?.cancelFileTransference(arrayListOf(file)) + } - FileMenuOption.SEND -> { - if (!file.isAvailableLocally) { // Download the file - Timber.d("${file.remotePath} : File must be downloaded") - fileActions?.initDownloadForSending(file) - } else { - fileActions?.sendDownloadedFile(file) - } - } + FileMenuOption.SHARE -> { + fileActions?.onShareFileClicked(file) + } - FileMenuOption.SET_AV_OFFLINE -> { - fileOperationsViewModel.performOperation(FileOperation.SetFilesAsAvailableOffline(listOf(file))) - if (file.isFolder) { - fileOperationsViewModel.performOperation( - FileOperation.SynchronizeFolderOperation( - folderToSync = file, - accountName = file.owner, - isActionSetFolderAvailableOfflineOrSynchronize = true, - ) - ) - } else { - fileOperationsViewModel.performOperation(FileOperation.SynchronizeFileOperation(file, file.owner)) - } - } + FileMenuOption.DETAILS -> { + fileActions?.showDetails(file) + } - FileMenuOption.UNSET_AV_OFFLINE -> { - fileOperationsViewModel.performOperation(FileOperation.UnsetFilesAsAvailableOffline(listOf(file))) - } - } - dialog.hide() - dialog.dismiss() + FileMenuOption.SEND -> { + if (!file.isAvailableLocally) { // Download the file + Timber.d("${file.remotePath} : File must be downloaded") + fileActions?.initDownloadForSending(file) + } else { + fileActions?.sendDownloadedFile(file) } } - fileOptionsBottomSheetSingleFileLayout!!.addView(fileOptionItemView) - } - // Disable drag gesture - fileOptionsBottomSheetSingleFileBehavior.addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() { - override fun onStateChanged(bottomSheet: View, newState: Int) { - if (newState == BottomSheetBehavior.STATE_DRAGGING) { - fileOptionsBottomSheetSingleFileBehavior.state = BottomSheetBehavior.STATE_EXPANDED + + FileMenuOption.SET_AV_OFFLINE -> { + fileOperationsViewModel.performOperation(FileOperation.SetFilesAsAvailableOffline(listOf(file))) + if (file.isFolder) { + fileOperationsViewModel.performOperation( + FileOperation.SynchronizeFolderOperation( + folderToSync = file, + accountName = file.owner, + isActionSetFolderAvailableOfflineOrSynchronize = true, + ) + ) + } else { + fileOperationsViewModel.performOperation(FileOperation.SynchronizeFileOperation(file, file.owner)) } } - override fun onSlide(bottomSheet: View, slideOffset: Float) {} - }) - dialog.setOnShowListener { fileOptionsBottomSheetSingleFileBehavior.peekHeight = fileOptionsBottomSheetSingleFile.measuredHeight } - dialog.show() - mainFileListViewModel.getAppRegistryForMimeType(file.mimeType, isMultiselection = false) + FileMenuOption.UNSET_AV_OFFLINE -> { + fileOperationsViewModel.performOperation(FileOperation.UnsetFilesAsAvailableOffline(listOf(file))) + } + } + dialog.hide() + dialog.dismiss() } } + fileOptionsBottomSheetSingleFileLayout!!.addView(fileOptionItemView) + } - // Observe the app registry for a single file + private fun observeAppRegistryMimeTypeSingleFile() { collectLatestLifecycleFlow(mainFileListViewModel.appRegistryMimeTypeSingleFile) { appRegistryMimeType -> fileSingleFile?.let { file -> val appProviders = appRegistryMimeType?.appProviders @@ -599,8 +651,19 @@ class MainFileListFragment : Fragment(), } fileSingleFile = null } + } - // Observe the file list UI state + private suspend fun getDrawableFromUrl(context: Context, url: String): Drawable? { + return withContext(Dispatchers.IO) { + Glide.with(context) + .load(url) + .fitCenter() + .submit() + .get() + } + } + + private fun observeFileListUiState() { collectLatestLifecycleFlow(mainFileListViewModel.fileListUiState) { fileListUiState -> if (fileListUiState !is MainFileListViewModel.FileListUiState.Success) return@collectLatestLifecycleFlow @@ -635,9 +698,9 @@ class MainFileListFragment : Fragment(), actionMode?.invalidate() } + } - /* FileOperationsViewModel observables */ - // Observe the refresh folder operation + private fun observeRefreshFolder() { fileOperationsViewModel.refreshFolderLiveData.observe(viewLifecycleOwner) { binding.syncProgressBar.isIndeterminate = it.peekContent().isLoading binding.swipeRefreshMainFileList.isRefreshing = it.peekContent().isLoading @@ -647,8 +710,9 @@ class MainFileListFragment : Fragment(), hideRefreshFab() } + } - // Observe the create file with app provider operation + private fun observeCreateFileWithAppProvider() { collectLatestLifecycleFlow(fileOperationsViewModel.createFileWithAppProviderFlow) { val uiResult = it?.peekContent() if (uiResult is UIResult.Error) { @@ -665,7 +729,9 @@ class MainFileListFragment : Fragment(), } } } + } + private fun observeCheckIfFileIsLocalAndNotAvailableOffline() { collectLatestLifecycleFlow(fileOperationsViewModel.checkIfFileIsLocalAndNotAvailableOfflineSharedFlow) { val fileActivity = (requireActivity() as FileActivity) when (it) { @@ -681,20 +747,6 @@ class MainFileListFragment : Fragment(), } } } - - /* TransfersViewModel observables */ - observeTransfers() - - } - - private suspend fun getDrawableFromUrl(context: Context, url: String): Drawable? { - return withContext(Dispatchers.IO) { - Glide.with(context) - .load(url) - .fitCenter() - .submit() - .get() - } } private fun observeTransfers() { @@ -826,7 +878,8 @@ class MainFileListFragment : Fragment(), * @param newFileListOption new file list option to enable. */ private fun showOrHideFab(newFileListOption: FileListOption, currentFolder: OCFile) { - if (!newFileListOption.isAllFiles() || isPickingAFolder() || (!currentFolder.hasAddFilePermission && !currentFolder.hasAddSubdirectoriesPermission)) { + if (!newFileListOption.isAllFiles() || isPickingAFolder() || + (!currentFolder.hasAddFilePermission && !currentFolder.hasAddSubdirectoriesPermission)) { toggleFabVisibility(false) } else { toggleFabVisibility(true) @@ -872,7 +925,8 @@ class MainFileListFragment : Fragment(), fabNewfile.isFocusable = isFabExpanded() fabNewshortcut.isFocusable = isFabExpanded() if (fabMain.isExpanded) { - binding.fabMain.findViewById(com.getbase.floatingactionbutton.R.id.fab_expand_menu_button).contentDescription = getString(R.string.content_description_add_new_content_expanded) + binding.fabMain.findViewById(com.getbase.floatingactionbutton.R.id.fab_expand_menu_button) + .contentDescription = getString(R.string.content_description_add_new_content_expanded) } else { setFabMainContentDescription() } @@ -1145,91 +1199,102 @@ class MainFileListFragment : Fragment(), */ @SuppressLint("UseRequireInsteadOfGet") private fun onFileActionChosen(menuId: Int?): Boolean { + var handled: Boolean val checkedFilesWithSyncInfo = fileListAdapter.getCheckedItems() as ArrayList if (checkedFilesWithSyncInfo.isEmpty()) { return false } else if (checkedFilesWithSyncInfo.size == 1) { - /// action only possible on a single file + /// Action possible on a single file val singleFile = checkedFilesWithSyncInfo.first().file + handled = onSingleFileActionChosen(menuId, singleFile) + } - openInWebProviders.forEach { (openInWebProviderName, menuItemId) -> - if (menuItemId == menuId) { - mainFileListViewModel.openInWeb(singleFile.remoteId!!, openInWebProviderName) - return true - } + /// Actions possible on a batch of files + val checkedFiles = checkedFilesWithSyncInfo.map { it.file } as ArrayList + handled = onCheckedFilesActionChosen(menuId, checkedFiles) + return handled + } + + private fun onSingleFileActionChosen(menuId: Int?, singleFile: OCFile): Boolean { + openInWebProviders.forEach { (openInWebProviderName, menuItemId) -> + if (menuItemId == menuId) { + mainFileListViewModel.openInWeb(singleFile.remoteId!!, openInWebProviderName) + return true } + } - when (menuId) { - R.id.action_share_file -> { - fileActions?.onShareFileClicked(singleFile) - fileListAdapter.clearSelection() - updateActionModeAfterTogglingSelected() - return true - } + when (menuId) { + R.id.action_share_file -> { + fileActions?.onShareFileClicked(singleFile) + fileListAdapter.clearSelection() + updateActionModeAfterTogglingSelected() + return true + } - R.id.action_open_file_with -> { - fileActions?.openFile(singleFile) - fileListAdapter.clearSelection() - updateActionModeAfterTogglingSelected() - return true - } + R.id.action_open_file_with -> { + fileActions?.openFile(singleFile) + fileListAdapter.clearSelection() + updateActionModeAfterTogglingSelected() + return true + } - R.id.action_rename_file -> { - val dialog = RenameFileDialogFragment.newInstance(singleFile) - dialog.show(requireActivity().supportFragmentManager, FRAGMENT_TAG_RENAME_FILE) - fileListAdapter.clearSelection() - updateActionModeAfterTogglingSelected() - return true - } + R.id.action_rename_file -> { + val dialog = RenameFileDialogFragment.newInstance(singleFile) + dialog.show(requireActivity().supportFragmentManager, FRAGMENT_TAG_RENAME_FILE) + fileListAdapter.clearSelection() + updateActionModeAfterTogglingSelected() + return true + } - R.id.action_see_details -> { - fileListAdapter.clearSelection() - updateActionModeAfterTogglingSelected() - fileActions?.showDetails(singleFile) - return true - } + R.id.action_see_details -> { + fileListAdapter.clearSelection() + updateActionModeAfterTogglingSelected() + fileActions?.showDetails(singleFile) + return true + } - R.id.action_sync_file -> { - syncFiles(listOf(singleFile)) - return true - } + R.id.action_sync_file -> { + syncFiles(listOf(singleFile)) + return true + } - R.id.action_send_file -> { - //Obtain the file - if (!singleFile.isAvailableLocally) { // Download the file - Timber.d("%s : File must be downloaded", singleFile.remotePath) - fileActions?.initDownloadForSending(singleFile) - } else { - fileActions?.sendDownloadedFile(singleFile) - } - return true + R.id.action_send_file -> { + //Obtain the file + if (!singleFile.isAvailableLocally) { // Download the file + Timber.d("%s : File must be downloaded", singleFile.remotePath) + fileActions?.initDownloadForSending(singleFile) + } else { + fileActions?.sendDownloadedFile(singleFile) } + return true + } - R.id.action_set_available_offline -> { - fileOperationsViewModel.performOperation(FileOperation.SetFilesAsAvailableOffline(listOf(singleFile))) - if (singleFile.isFolder) { - fileOperationsViewModel.performOperation( - FileOperation.SynchronizeFolderOperation( - folderToSync = singleFile, - accountName = singleFile.owner, - isActionSetFolderAvailableOfflineOrSynchronize = true, - ) + R.id.action_set_available_offline -> { + fileOperationsViewModel.performOperation(FileOperation.SetFilesAsAvailableOffline(listOf(singleFile))) + if (singleFile.isFolder) { + fileOperationsViewModel.performOperation( + FileOperation.SynchronizeFolderOperation( + folderToSync = singleFile, + accountName = singleFile.owner, + isActionSetFolderAvailableOfflineOrSynchronize = true, ) - } else { - fileOperationsViewModel.performOperation(FileOperation.SynchronizeFileOperation(singleFile, singleFile.owner)) - } - return true + ) + } else { + fileOperationsViewModel.performOperation(FileOperation.SynchronizeFileOperation(singleFile, singleFile.owner)) } + return true + } - R.id.action_unset_available_offline -> { - fileOperationsViewModel.performOperation(FileOperation.UnsetFilesAsAvailableOffline(listOf(singleFile))) - } + R.id.action_unset_available_offline -> { + fileOperationsViewModel.performOperation(FileOperation.UnsetFilesAsAvailableOffline(listOf(singleFile))) + return true } } + return false + } - /// Actions possible on a batch of files - val checkedFiles = checkedFilesWithSyncInfo.map { it.file } as ArrayList + private fun onCheckedFilesActionChosen(menuId: Int?, checkedFiles: ArrayList): Boolean { when (menuId) { R.id.file_action_select_all -> { fileListAdapter.selectAll() @@ -1301,7 +1366,6 @@ class MainFileListFragment : Fragment(), return true } } - return false } @@ -1442,16 +1506,23 @@ class MainFileListFragment : Fragment(), if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val roleAccessibilityDescription = getString(R.string.button_role_accessibility) menu?.apply { - findItem(R.id.file_action_select_all)?.contentDescription = "${getString(R.string.actionbar_select_all)} $roleAccessibilityDescription" - findItem(R.id.action_select_inverse)?.contentDescription = "${getString(R.string.actionbar_select_inverse)} $roleAccessibilityDescription" - findItem(R.id.action_open_file_with)?.contentDescription = "${getString(R.string.actionbar_open_with)} $roleAccessibilityDescription" + findItem(R.id.file_action_select_all)?.contentDescription = + "${getString(R.string.actionbar_select_all)} $roleAccessibilityDescription" + findItem(R.id.action_select_inverse)?.contentDescription = + "${getString(R.string.actionbar_select_inverse)} $roleAccessibilityDescription" + findItem(R.id.action_open_file_with)?.contentDescription = + "${getString(R.string.actionbar_open_with)} $roleAccessibilityDescription" findItem(R.id.action_rename_file)?.contentDescription = "${getString(R.string.common_rename)} $roleAccessibilityDescription" findItem(R.id.action_move)?.contentDescription = "${getString(R.string.actionbar_move)} $roleAccessibilityDescription" findItem(R.id.action_copy)?.contentDescription = "${getString(R.string.copy)} $roleAccessibilityDescription" - findItem(R.id.action_send_file)?.contentDescription = "${getString(R.string.actionbar_send_file)} $roleAccessibilityDescription" - findItem(R.id.action_set_available_offline)?.contentDescription = "${getString(R.string.set_available_offline)} $roleAccessibilityDescription" - findItem(R.id.action_unset_available_offline)?.contentDescription = "${getString(R.string.unset_available_offline)} $roleAccessibilityDescription" - findItem(R.id.action_see_details)?.contentDescription = "${getString(R.string.actionbar_see_details)} $roleAccessibilityDescription" + findItem(R.id.action_send_file)?.contentDescription = + "${getString(R.string.actionbar_send_file)} $roleAccessibilityDescription" + findItem(R.id.action_set_available_offline)?.contentDescription = + "${getString(R.string.set_available_offline)} $roleAccessibilityDescription" + findItem(R.id.action_unset_available_offline)?.contentDescription = + "${getString(R.string.unset_available_offline)} $roleAccessibilityDescription" + findItem(R.id.action_see_details)?.contentDescription = + "${getString(R.string.actionbar_see_details)} $roleAccessibilityDescription" findItem(R.id.action_remove_file)?.contentDescription = "${getString(R.string.common_remove)} $roleAccessibilityDescription" } } @@ -1538,7 +1609,6 @@ class MainFileListFragment : Fragment(), private const val DIALOG_CREATE_FOLDER = "DIALOG_CREATE_FOLDER" private const val DIALOG_CREATE_SHORTCUT = "DIALOG_CREATE_SHORTCUT" - private const val TAG_SECOND_FRAGMENT = "SECOND_FRAGMENT" private const val FILE_DOCXF_EXTENSION = "docxf" @JvmStatic diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainFileListViewModel.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainFileListViewModel.kt index 62246558373..37bdfd2ba31 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainFileListViewModel.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainFileListViewModel.kt @@ -226,9 +226,10 @@ class MainFileListViewModel( FileListOption.SHARED_BY_LINK -> { val fileById = fileByIdResult.getDataOrNull() - parentDir = if (fileById != null && (!fileById.sharedByLink || fileById.sharedWithSharee != true) && fileById.spaceId == null) { - getFileByRemotePathUseCase(GetFileByRemotePathUseCase.Params(fileById.owner, ROOT_PATH)).getDataOrNull() - } else fileById + parentDir = + if (fileById != null && (!fileById.sharedByLink || fileById.sharedWithSharee != true) && fileById.spaceId == null) { + getFileByRemotePathUseCase(GetFileByRemotePathUseCase.Params(fileById.owner, ROOT_PATH)).getDataOrNull() + } else fileById } FileListOption.AV_OFFLINE -> { diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/files/operations/FileOperationsViewModel.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/files/operations/FileOperationsViewModel.kt index 7a0781c5a19..7b894b52850 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/files/operations/FileOperationsViewModel.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/files/operations/FileOperationsViewModel.kt @@ -275,7 +275,8 @@ class FileOperationsViewModel( remotePath = fileOperation.folderToRefresh.remotePath, accountName = fileOperation.folderToRefresh.owner, spaceId = fileOperation.folderToRefresh.spaceId, - syncMode = if (fileOperation.shouldSyncContents) SynchronizeFolderUseCase.SyncFolderMode.SYNC_CONTENTS else SynchronizeFolderUseCase.SyncFolderMode.REFRESH_FOLDER + syncMode = if (fileOperation.shouldSyncContents) SynchronizeFolderUseCase.SyncFolderMode.SYNC_CONTENTS + else SynchronizeFolderUseCase.SyncFolderMode.REFRESH_FOLDER ) ) } diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/files/renamefile/RenameFileDialogFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/files/renamefile/RenameFileDialogFragment.kt index 1a284c06b94..b8ff0d8a7f9 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/files/renamefile/RenameFileDialogFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/files/renamefile/RenameFileDialogFragment.kt @@ -34,7 +34,6 @@ import com.google.android.material.textfield.TextInputLayout import com.owncloud.android.R import com.owncloud.android.domain.files.model.OCFile import com.owncloud.android.extensions.avoidScreenshotsIfNeeded -import com.owncloud.android.extensions.showMessageInSnackbar import com.owncloud.android.presentation.files.operations.FileOperation import com.owncloud.android.presentation.files.operations.FileOperationsViewModel import com.owncloud.android.utils.PreferenceUtils @@ -141,17 +140,6 @@ class RenameFileDialogFragment : DialogFragment(), DialogInterface.OnClickListen } } - /** - * Show a temporary message in a Snackbar bound to the content view of the parent Activity - * - * @param messageResource Message to show. - */ - private fun showSnackMessage(messageResource: Int) { - showMessageInSnackbar( - message = getString(messageResource) - ) - } - companion object { const val FRAGMENT_TAG_RENAME_FILE = "RENAME_FILE_FRAGMENT" private const val ARG_TARGET_FILE = "TARGET_FILE" diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/security/SecurityEnforced.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/security/SecurityEnforced.kt index b8af17c0159..5561888737a 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/security/SecurityEnforced.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/security/SecurityEnforced.kt @@ -29,9 +29,10 @@ enum class LockType { companion object { fun parseFromInteger(value: Int): LockType { - return when (value) { - 0 -> PASSCODE - else -> PATTERN + return if (value == 0) { + PASSCODE + } else { + PATTERN } } } diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/security/passcode/PassCodeActivity.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/security/passcode/PassCodeActivity.kt index 5dcda2ba161..ea1e6589dd7 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/security/passcode/PassCodeActivity.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/security/passcode/PassCodeActivity.kt @@ -215,9 +215,10 @@ class PassCodeActivity : AppCompatActivity(), NumberKeyboardListener, EnableBiom } PasscodeAction.REMOVE -> { - when (status.type) { - PasscodeType.OK -> actionRemoveOk() - else -> actionRemoveError() + if (status.type == PasscodeType.OK) { + actionRemoveOk() + } else { + actionRemoveError() } } diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsFragment.kt index 9d1f39b2b45..a2e4ceab694 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsFragment.kt @@ -103,7 +103,8 @@ class SettingsVideoUploadsFragment : PreferenceFragmentCompat() { prefVideoUploadsSourcePath = findPreference(PREF__CAMERA_VIDEO_UPLOADS_SOURCE) prefVideoUploadsLastSync = findPreference(PreferenceManager.PREF__CAMERA_VIDEO_UPLOADS_LAST_SYNC) prefVideoUploadsBehaviour = findPreference(PREF__CAMERA_VIDEO_UPLOADS_BEHAVIOUR)?.apply { - entries = listOf(getString(R.string.pref_behaviour_entries_keep_file), getString(R.string.pref_behaviour_entries_remove_original_file)).toTypedArray() + entries = listOf(getString(R.string.pref_behaviour_entries_keep_file), + getString(R.string.pref_behaviour_entries_remove_original_file)).toTypedArray() entryValues = listOf(UploadBehavior.COPY.name, UploadBehavior.MOVE.name).toTypedArray() } prefVideoUploadsAccount = findPreference(PREF__CAMERA_VIDEO_UPLOADS_ACCOUNT_NAME) diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/privacypolicy/PrivacyPolicyActivity.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/privacypolicy/PrivacyPolicyActivity.kt index 509f9b192f6..fc74f139ddd 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/privacypolicy/PrivacyPolicyActivity.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/privacypolicy/PrivacyPolicyActivity.kt @@ -91,9 +91,10 @@ class PrivacyPolicyActivity : AppCompatActivity() { override fun onOptionsItemSelected(item: MenuItem): Boolean { var retval = true - when (item.itemId) { - android.R.id.home -> finish() - else -> retval = super.onOptionsItemSelected(item) + if (item.itemId == android.R.id.home) { + finish() + } else { + retval = super.onOptionsItemSelected(item) } return retval } diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/security/SettingsSecurityFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/security/SettingsSecurityFragment.kt index 0e3cbab8d17..6a84ed1e116 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/security/SettingsSecurityFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/security/SettingsSecurityFragment.kt @@ -107,7 +107,6 @@ class SettingsSecurityFragment : PreferenceFragmentCompat() { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { setPreferencesFromResource(R.xml.settings_security, rootKey) - screenSecurity = findPreference(SCREEN_SECURITY) prefPasscode = findPreference(PassCodeActivity.PREFERENCE_SET_PASSCODE) prefPattern = findPreference(PatternActivity.PREFERENCE_SET_PATTERN) @@ -172,9 +171,8 @@ class SettingsSecurityFragment : PreferenceFragmentCompat() { if (!BiometricManager.isHardwareDetected()) { // Biometric not supported screenSecurity?.removePreferenceFromScreen(prefBiometric) } else { - if (prefPasscode?.isChecked == false && prefPattern?.isChecked == false) { // Disable biometric lock if Passcode or Pattern locks are disabled - disableBiometric() - } + // Disable biometric lock if Passcode or Pattern locks are disabled + if (prefPasscode?.isChecked == false && prefPattern?.isChecked == false) { disableBiometric() } prefBiometric?.setOnPreferenceChangeListener { _: Preference?, newValue: Any -> val incomingValue = newValue as Boolean @@ -190,9 +188,7 @@ class SettingsSecurityFragment : PreferenceFragmentCompat() { } // Lock application - if (prefPasscode?.isChecked == false && prefPattern?.isChecked == false) { - prefLockApplication?.isEnabled = false - } + if (prefPasscode?.isChecked == false && prefPattern?.isChecked == false) { prefLockApplication?.isEnabled = false } // Lock access from document provider prefLockAccessDocumentProvider?.setOnPreferenceChangeListener { _: Preference?, newValue: Any -> diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/PublicLinkPasswordUtils.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/PublicLinkPasswordUtils.kt index c322c5d5a9c..c71ab1ab24e 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/PublicLinkPasswordUtils.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/PublicLinkPasswordUtils.kt @@ -25,7 +25,8 @@ import java.security.SecureRandom private val charsetLowercase = ('a'..'z').toList() private val charsetUppercase = ('A'..'Z').toList() private val charsetDigits = ('0'..'9').toList() -private val charsetSpecial = listOf('!', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_', '`', '{', '|', '}', '~') +private val charsetSpecial = listOf('!', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '@', + '[', '\\', ']', '^', '_', '`', '{', '|', '}', '~') fun generatePassword( minChars: Int?, @@ -52,22 +53,22 @@ fun generatePassword( val passwordChars = mutableListOf() // Include the minimum number of digits established by the policy - for (i in 1..minDigits) { + repeat(minDigits) { passwordChars.add(charsetDigits[secureRandom.nextInt(charsetDigits.size)]) } // Include the minimum number of lowercase chars established by the policy - for (i in 1..minLowercaseCharacters) { + repeat(minLowercaseCharacters) { passwordChars.add(charsetLowercase[secureRandom.nextInt(charsetLowercase.size)]) } // Include the minimum number of uppercase chars established by the policy - for (i in 1..minUppercaseCharacters) { + repeat(minUppercaseCharacters) { passwordChars.add(charsetUppercase[secureRandom.nextInt(charsetUppercase.size)]) } // Include the minimum number of special chars established by the policy - for (i in 1..minSpecialCharacters) { + repeat(minSpecialCharacters) { passwordChars.add(charsetSpecial[secureRandom.nextInt(charsetSpecial.size)]) } diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/ShareActivity.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/ShareActivity.kt index 530ba94b22f..f66cdd5fdb5 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/ShareActivity.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/ShareActivity.kt @@ -47,7 +47,7 @@ import com.owncloud.android.presentation.sharing.sharees.SearchShareesFragment import com.owncloud.android.presentation.sharing.sharees.UsersAndGroupsSearchProvider import com.owncloud.android.presentation.sharing.shares.PublicShareDialogFragment import com.owncloud.android.ui.activity.FileActivity -import com.owncloud.android.ui.utils.showDialogFragment +import com.owncloud.android.extensions.showDialogFragment import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.core.parameter.parametersOf import timber.log.Timber @@ -294,11 +294,10 @@ class ShareActivity : FileActivity(), ShareFragmentListener { override fun onOptionsItemSelected(item: MenuItem): Boolean { var retval = true - when (item.itemId) { - android.R.id.home -> if (!supportFragmentManager.popBackStackImmediate()) { - finish() - } - else -> retval = super.onOptionsItemSelected(item) + if (item.itemId == android.R.id.home && !supportFragmentManager.popBackStackImmediate()) { + finish() + } else { + retval = super.onOptionsItemSelected(item) } return retval } @@ -317,14 +316,13 @@ class ShareActivity : FileActivity(), ShareFragmentListener { } override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean { - return when (keyCode) { - KeyEvent.KEYCODE_DPAD_DOWN -> { - if (findViewById(R.id.owncloud_app_bar).hasFocus()) { - findViewById(R.id.share_fragment_container).requestFocus() - } - true + return if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) { + if (findViewById(R.id.owncloud_app_bar).hasFocus()) { + findViewById(R.id.share_fragment_container).requestFocus() } - else -> super.onKeyUp(keyCode, event) + true + } else { + super.onKeyUp(keyCode, event) } } diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/ShareFileFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/ShareFileFragment.kt index 30048f56084..5fe32a69785 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/ShareFileFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/ShareFileFragment.kt @@ -257,7 +257,7 @@ class ShareFileFragment : Fragment(), ShareUserListAdapter.ShareUserAdapterListe showOrHidePrivateLink() // Hide share features sections that are not enabled - hideSectionsDisabledInBuildTime(view) + hideSectionsDisabledInBuildTime() binding.addUserButton.setOnClickListener { // Show Search Fragment @@ -537,7 +537,7 @@ class ShareFileFragment : Fragment(), ShareUserListAdapter.ShareUserAdapterListe * Hide share features sections that are not enabled * */ - private fun hideSectionsDisabledInBuildTime(view: View) { + private fun hideSectionsDisabledInBuildTime() { val shareViaLinkAllowed = requireActivity().resources.getBoolean(R.bool.share_via_link_feature) val shareWithUsersAllowed = requireActivity().resources.getBoolean(R.bool.share_with_users_feature) val shareWarningAllowed = requireActivity().resources.getBoolean(R.bool.warning_sharing_public_link) diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/ShareFragmentListener.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/ShareFragmentListener.kt index 7f52b90220e..ee921df5f10 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/ShareFragmentListener.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/ShareFragmentListener.kt @@ -34,7 +34,7 @@ import com.owncloud.android.domain.sharing.shares.model.OCShare * contained in that activity. * * - * See the Android Training lesson [Communicating with Other Fragments](http://developer.android.com/training/basics/fragments/communicating.html) for more information. + * See the Android docs [Communicating with fragments](https://developer.android.com/guide/fragments/communicate) for more information. */ interface ShareFragmentListener { fun copyOrSendPrivateLink(file: OCFile) diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/sharees/UsersAndGroupsSearchProvider.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/sharees/UsersAndGroupsSearchProvider.kt index a5a3d9d3011..014e393771d 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/sharees/UsersAndGroupsSearchProvider.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/sharees/UsersAndGroupsSearchProvider.kt @@ -112,9 +112,10 @@ class UsersAndGroupsSearchProvider : ContentProvider() { sortOrder: String? ): Cursor? { Timber.d("query received in thread ${Thread.currentThread().name}") - return when (uriMatcher.match(uri)) { - SEARCH -> searchForUsersOrGroups(uri) - else -> null + return if (uriMatcher.match(uri) == SEARCH) { + searchForUsersOrGroups(uri) + } else { + null } } @@ -167,15 +168,9 @@ class UsersAndGroupsSearchProvider : ContentProvider() { var dataUri: Uri? = null var count = 0 - val userBaseUri = Uri.Builder().scheme(CONTENT).authority( - suggestAuthority!! + DATA_USER_SUFFIX - ).build() - val groupBaseUri = Uri.Builder().scheme(CONTENT).authority( - suggestAuthority!! + DATA_GROUP_SUFFIX - ).build() - val remoteBaseUri = Uri.Builder().scheme(CONTENT).authority( - suggestAuthority!! + DATA_REMOTE_SUFFIX - ).build() + val userBaseUri = Uri.Builder().scheme(CONTENT).authority(suggestAuthority!! + DATA_USER_SUFFIX).build() + val groupBaseUri = Uri.Builder().scheme(CONTENT).authority(suggestAuthority!! + DATA_GROUP_SUFFIX).build() + val remoteBaseUri = Uri.Builder().scheme(CONTENT).authority(suggestAuthority!! + DATA_REMOTE_SUFFIX).build() val federatedShareAllowed = capabilities?.filesSharingFederationOutgoing?.isTrue ?: false @@ -184,9 +179,7 @@ class UsersAndGroupsSearchProvider : ContentProvider() { val fullName = AccountManager.get(context).getUserData(account, KEY_DISPLAY_NAME) while (namesIt.hasNext()) { item = namesIt.next() - if (item.label == userName || item.label == fullName && item.shareType == ShareType.USER) { - continue - } + if (item.label == userName || item.label == fullName && item.shareType == ShareType.USER) { continue } var userName = item.label val type = item.shareType val shareWith = item.shareWith @@ -212,15 +205,10 @@ class UsersAndGroupsSearchProvider : ContentProvider() { ShareType.FEDERATED -> { if (federatedShareAllowed) { icon = R.drawable.ic_user - displayName = if (userName == shareWith) { - context?.getString(R.string.share_remote_clarification, userName) + displayName = if (userName == shareWith) { context?.getString(R.string.share_remote_clarification, userName) } else { - val uriSplitted = - shareWith.split("@".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() - context?.getString( - R.string.share_known_remote_clarification, userName, - uriSplitted[uriSplitted.size - 1] - ) + val uriSplitted = shareWith.split("@".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() + context?.getString(R.string.share_known_remote_clarification, userName, uriSplitted[uriSplitted.size - 1]) } dataUri = Uri.withAppendedPath(remoteBaseUri, shareWith) } @@ -296,7 +284,6 @@ class UsersAndGroupsSearchProvider : ContentProvider() { private const val SEARCH = 1 - private const val DEFAULT_MIN_CHARACTERS_TO_SEARCH = 4 private const val RESULTS_PER_PAGE = 30 private const val REQUESTED_PAGE = 1 diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/shares/PublicShareDialogFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/shares/PublicShareDialogFragment.kt index 5ee96c8656d..daffcdb5573 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/shares/PublicShareDialogFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/sharing/shares/PublicShareDialogFragment.kt @@ -518,7 +518,8 @@ class PublicShareDialogFragment : DialogFragment() { val x = event.x.toInt() val y = event.y.toInt() val bounds = rightDrawable.bounds - if (x >= view.right - bounds.width() - fuzz && x <= view.right - view.paddingRight + fuzz && y >= view.paddingTop - fuzz && y <= view.height - view.paddingBottom + fuzz) { + if (x >= view.right - bounds.width() - fuzz && x <= view.right - view.paddingRight + fuzz && y >= view.paddingTop - fuzz && + y <= view.height - view.paddingBottom + fuzz) { return onDrawableTouch(event) } @@ -836,7 +837,9 @@ class PublicShareDialogFragment : DialogFragment() { // - Upload only is supported by the server version // - Upload only capability is set // - Allow editing capability is set - if (!(isSharedFolder && serverVersion?.isPublicSharingWriteOnlySupported == true && capabilities?.filesSharingPublicSupportsUploadOnly == CapabilityBooleanType.TRUE && capabilities?.filesSharingPublicUpload == CapabilityBooleanType.TRUE)) { + if (!(isSharedFolder && serverVersion?.isPublicSharingWriteOnlySupported == true && + capabilities?.filesSharingPublicSupportsUploadOnly == CapabilityBooleanType.TRUE && + capabilities?.filesSharingPublicUpload == CapabilityBooleanType.TRUE)) { binding.shareViaLinkEditPermissionGroup.isVisible = false } @@ -867,7 +870,12 @@ class PublicShareDialogFragment : DialogFragment() { } // Set password label when opening the dialog - if (binding.shareViaLinkEditPermissionReadOnly.isChecked && capabilities?.filesSharingPublicPasswordEnforcedReadOnly == CapabilityBooleanType.TRUE || binding.shareViaLinkEditPermissionReadAndWrite.isChecked && capabilities?.filesSharingPublicPasswordEnforcedReadWrite == CapabilityBooleanType.TRUE || binding.shareViaLinkEditPermissionUploadFiles.isChecked && capabilities?.filesSharingPublicPasswordEnforcedUploadOnly == CapabilityBooleanType.TRUE) { + if (binding.shareViaLinkEditPermissionReadOnly.isChecked && + capabilities?.filesSharingPublicPasswordEnforcedReadOnly == CapabilityBooleanType.TRUE || + binding.shareViaLinkEditPermissionReadAndWrite.isChecked && + capabilities?.filesSharingPublicPasswordEnforcedReadWrite == CapabilityBooleanType.TRUE || + binding.shareViaLinkEditPermissionUploadFiles.isChecked && + capabilities?.filesSharingPublicPasswordEnforcedUploadOnly == CapabilityBooleanType.TRUE) { setPasswordEnforced() } @@ -898,7 +906,9 @@ class PublicShareDialogFragment : DialogFragment() { // When there's no password enforced for capability val hasPasswordEnforcedFor = - capabilities?.filesSharingPublicPasswordEnforcedReadOnly == CapabilityBooleanType.TRUE || capabilities?.filesSharingPublicPasswordEnforcedReadWrite == CapabilityBooleanType.TRUE || capabilities?.filesSharingPublicPasswordEnforcedUploadOnly == CapabilityBooleanType.TRUE + capabilities?.filesSharingPublicPasswordEnforcedReadOnly == CapabilityBooleanType.TRUE || + capabilities?.filesSharingPublicPasswordEnforcedReadWrite == CapabilityBooleanType.TRUE || + capabilities?.filesSharingPublicPasswordEnforcedUploadOnly == CapabilityBooleanType.TRUE // hide password switch if password is enforced to prevent it is removed if (!hasPasswordEnforcedFor && capabilities?.filesSharingPublicPasswordEnforced == CapabilityBooleanType.TRUE) { diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListViewModel.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListViewModel.kt index 5207e16617b..8f78b4945bf 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListViewModel.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListViewModel.kt @@ -21,7 +21,6 @@ package com.owncloud.android.presentation.spaces - import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.owncloud.android.domain.UseCaseResult diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/transfers/TransfersAdapter.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/transfers/TransfersAdapter.kt index 0c163464b0a..cd50af79117 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/transfers/TransfersAdapter.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/transfers/TransfersAdapter.kt @@ -77,137 +77,11 @@ class TransfersAdapter( when (holder) { is TransferItemViewHolder -> { val transferItem = getItem(position) as TransferItem - holder.binding.apply { - val remoteFile = File(transferItem.transfer.remotePath) - - var fileName = remoteFile.name - if (fileName.isEmpty()) { - fileName = File.separator - } - uploadName.text = fileName - - transferItem.space?.let { - spacePathLine.spaceName.isVisible = true - spacePathLine.spaceIcon.isVisible = true - if (it.isPersonal) { - spacePathLine.spaceIcon.setImageResource(R.drawable.ic_folder) - spacePathLine.spaceName.setText(R.string.bottom_nav_personal) - } else { - spacePathLine.spaceName.text = it.name - } - } - - remoteFile.parent?.let { - spacePathLine.path.text = if (it.endsWith("${OCFile.PATH_SEPARATOR}")) it else "$it${OCFile.PATH_SEPARATOR}" - } - - uploadFileSize.text = DisplayUtils.bytesToHumanReadable(transferItem.transfer.fileSize, holder.itemView.context, true) - - uploadDate.isVisible = - transferItem.transfer.transferEndTimestamp != null && transferItem.transfer.status != TransferStatus.TRANSFER_FAILED - transferItem.transfer.transferEndTimestamp?.let { - val dateString = DisplayUtils.getRelativeDateTimeString( - holder.itemView.context, - it, - DateUtils.SECOND_IN_MILLIS, - DateUtils.WEEK_IN_MILLIS, - 0 - ) - uploadDate.text = ", $dateString" - } - - try { - val account = AccountUtils.getOwnCloudAccountByName(holder.itemView.context, transferItem.transfer.accountName) - val oca = OwnCloudAccount(account, holder.itemView.context) - val accountName = oca.displayName + " @ " + - DisplayUtils.convertIdn(account.name.substring(account.name.lastIndexOf("@") + 1), false) - uploadAccount.text = accountName - } catch (e: Exception) { - Timber.w("Couldn't get display name for account, using old style") - uploadAccount.text = transferItem.transfer.accountName - } - - uploadStatus.isVisible = transferItem.transfer.status != TransferStatus.TRANSFER_SUCCEEDED - uploadStatus.text = " — " + holder.itemView.context.getString(transferItem.transfer.statusToStringRes()) - - Glide.with(holder.itemView) - .load(transferItem.transfer.localPath) - .diskCacheStrategy(DiskCacheStrategy.ALL) - .placeholder( - MimetypeIconUtil.getFileTypeIconId( - MimetypeIconUtil.getBestMimeTypeByFilename(transferItem.transfer.localPath), - fileName - ) - ) - .into(thumbnail) - - uploadRightButton.isVisible = transferItem.transfer.status != TransferStatus.TRANSFER_SUCCEEDED - - uploadProgressBar.isVisible = transferItem.transfer.status == TransferStatus.TRANSFER_IN_PROGRESS - - holder.itemView.setOnClickListener(null) - - when (transferItem.transfer.status) { - TransferStatus.TRANSFER_IN_PROGRESS, TransferStatus.TRANSFER_QUEUED -> { - uploadRightButton.apply { - setImageResource(R.drawable.ic_action_cancel_grey) - setOnClickListener { - cancel(transferItem.transfer) - } - } - } - TransferStatus.TRANSFER_FAILED -> { - uploadRightButton.apply { - setImageResource(R.drawable.ic_action_delete_grey) - setOnClickListener { - cancel(transferItem.transfer) - } - } - holder.itemView.setOnClickListener { - retry(transferItem.transfer) - } - holder.binding.ListItemLayout.isClickable = true - holder.binding.ListItemLayout.isFocusable = true - } - TransferStatus.TRANSFER_SUCCEEDED -> { - // Nothing to do - } - } - } + onBindTransferItemViewHolder(holder, transferItem) } is HeaderItemViewHolder -> { val headerItem = getItem(position) as HeaderItem - holder.binding.apply { - uploadListGroupName.text = holder.itemView.context.getString(headerTitleStringRes(headerItem.status)) - - val stringResFileCount = - if (headerItem.numberTransfers == 1) R.string.uploads_view_group_file_count_single else R.string.uploads_view_group_file_count - val fileCountText: String = String.format(holder.itemView.context.getString(stringResFileCount), headerItem.numberTransfers) - textViewFileCount.text = fileCountText - - uploadListGroupButtonClear.isVisible = headerItem.status == TransferStatus.TRANSFER_FAILED || - headerItem.status == TransferStatus.TRANSFER_SUCCEEDED - uploadListGroupButtonRetry.isVisible = headerItem.status == TransferStatus.TRANSFER_FAILED - - when (headerItem.status) { - TransferStatus.TRANSFER_FAILED -> { - uploadListGroupButtonClear.setOnClickListener { - clearFailed() - } - uploadListGroupButtonRetry.setOnClickListener { - retryFailed() - } - } - TransferStatus.TRANSFER_SUCCEEDED -> { - uploadListGroupButtonClear.setOnClickListener { - clearSuccessful() - } - } - TransferStatus.TRANSFER_QUEUED, TransferStatus.TRANSFER_IN_PROGRESS -> { - // Nothing to do - } - } - } + onBindHeaderItemViewHolder(holder, headerItem) } } @@ -226,6 +100,142 @@ class TransfersAdapter( } + private fun onBindTransferItemViewHolder(holder: TransferItemViewHolder, transferItem: TransferItem) { + holder.binding.apply { + val remoteFile = File(transferItem.transfer.remotePath) + + var fileName = remoteFile.name + if (fileName.isEmpty()) { + fileName = File.separator + } + uploadName.text = fileName + + transferItem.space?.let { + spacePathLine.spaceName.isVisible = true + spacePathLine.spaceIcon.isVisible = true + if (it.isPersonal) { + spacePathLine.spaceIcon.setImageResource(R.drawable.ic_folder) + spacePathLine.spaceName.setText(R.string.bottom_nav_personal) + } else { + spacePathLine.spaceName.text = it.name + } + } + + remoteFile.parent?.let { + spacePathLine.path.text = if (it.endsWith("${OCFile.PATH_SEPARATOR}")) it else "$it${OCFile.PATH_SEPARATOR}" + } + + uploadFileSize.text = DisplayUtils.bytesToHumanReadable(transferItem.transfer.fileSize, holder.itemView.context, true) + + uploadDate.isVisible = + transferItem.transfer.transferEndTimestamp != null && transferItem.transfer.status != TransferStatus.TRANSFER_FAILED + transferItem.transfer.transferEndTimestamp?.let { + val dateString = DisplayUtils.getRelativeDateTimeString( + holder.itemView.context, + it, + DateUtils.SECOND_IN_MILLIS, + DateUtils.WEEK_IN_MILLIS, + 0 + ) + uploadDate.text = ", $dateString" + } + + try { + val account = AccountUtils.getOwnCloudAccountByName(holder.itemView.context, transferItem.transfer.accountName) + val oca = OwnCloudAccount(account, holder.itemView.context) + val accountName = oca.displayName + " @ " + + DisplayUtils.convertIdn(account.name.substring(account.name.lastIndexOf("@") + 1), false) + uploadAccount.text = accountName + } catch (e: Exception) { + Timber.w("Couldn't get display name for account, using old style") + uploadAccount.text = transferItem.transfer.accountName + } + + uploadStatus.isVisible = transferItem.transfer.status != TransferStatus.TRANSFER_SUCCEEDED + uploadStatus.text = " — " + holder.itemView.context.getString(transferItem.transfer.statusToStringRes()) + + Glide.with(holder.itemView) + .load(transferItem.transfer.localPath) + .diskCacheStrategy(DiskCacheStrategy.ALL) + .placeholder( + MimetypeIconUtil.getFileTypeIconId( + MimetypeIconUtil.getBestMimeTypeByFilename(transferItem.transfer.localPath), + fileName + ) + ) + .into(thumbnail) + + uploadRightButton.isVisible = transferItem.transfer.status != TransferStatus.TRANSFER_SUCCEEDED + + uploadProgressBar.isVisible = transferItem.transfer.status == TransferStatus.TRANSFER_IN_PROGRESS + + holder.itemView.setOnClickListener(null) + + when (transferItem.transfer.status) { + TransferStatus.TRANSFER_IN_PROGRESS, TransferStatus.TRANSFER_QUEUED -> { + uploadRightButton.apply { + setImageResource(R.drawable.ic_action_cancel_grey) + setOnClickListener { + cancel(transferItem.transfer) + } + } + } + + TransferStatus.TRANSFER_FAILED -> { + uploadRightButton.apply { + setImageResource(R.drawable.ic_action_delete_grey) + setOnClickListener { + cancel(transferItem.transfer) + } + } + holder.itemView.setOnClickListener { + retry(transferItem.transfer) + } + holder.binding.ListItemLayout.isClickable = true + holder.binding.ListItemLayout.isFocusable = true + } + + TransferStatus.TRANSFER_SUCCEEDED -> { + // Nothing to do + } + } + } + } + + private fun onBindHeaderItemViewHolder(holder: HeaderItemViewHolder, headerItem: HeaderItem) { + holder.binding.apply { + uploadListGroupName.text = holder.itemView.context.getString(headerTitleStringRes(headerItem.status)) + + val stringResFileCount = + if (headerItem.numberTransfers == 1) R.string.uploads_view_group_file_count_single else R.string.uploads_view_group_file_count + val fileCountText: String = String.format(holder.itemView.context.getString(stringResFileCount), headerItem.numberTransfers) + textViewFileCount.text = fileCountText + + uploadListGroupButtonClear.isVisible = headerItem.status == TransferStatus.TRANSFER_FAILED || + headerItem.status == TransferStatus.TRANSFER_SUCCEEDED + uploadListGroupButtonRetry.isVisible = headerItem.status == TransferStatus.TRANSFER_FAILED + + when (headerItem.status) { + TransferStatus.TRANSFER_FAILED -> { + uploadListGroupButtonClear.setOnClickListener { + clearFailed() + } + uploadListGroupButtonRetry.setOnClickListener { + retryFailed() + } + } + TransferStatus.TRANSFER_SUCCEEDED -> { + uploadListGroupButtonClear.setOnClickListener { + clearSuccessful() + } + } + TransferStatus.TRANSFER_QUEUED, TransferStatus.TRANSFER_IN_PROGRESS -> { + // Nothing to do + } + } + } + } + private fun headerTitleStringRes(status: TransferStatus): Int { return when (status) { TransferStatus.TRANSFER_IN_PROGRESS -> R.string.uploads_view_group_current_uploads diff --git a/owncloudApp/src/main/java/com/owncloud/android/providers/FileContentProvider.kt b/owncloudApp/src/main/java/com/owncloud/android/providers/FileContentProvider.kt index f00709fa4f8..db8409ebdba 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/providers/FileContentProvider.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/providers/FileContentProvider.kt @@ -482,6 +482,7 @@ class FileContentProvider(val executors: Executors = Executors()) : ContentProvi createCameraUploadsSyncTable(db) } + @Suppress("LongMethod") override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { Timber.i("SQL : Entering in onUpgrade") var upgraded = false diff --git a/owncloudApp/src/main/java/com/owncloud/android/providers/LogsProvider.kt b/owncloudApp/src/main/java/com/owncloud/android/providers/LogsProvider.kt index 3a8818ebaa1..25a21bb84e8 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/providers/LogsProvider.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/providers/LogsProvider.kt @@ -58,7 +58,8 @@ class LogsProvider( private fun initHttpLogs() { val httpLogsEnabled: Boolean = sharedPreferencesProvider.getBoolean(PREFERENCE_LOG_HTTP, false) LogInterceptor.httpLogsEnabled = httpLogsEnabled - val redactAuthHeader = mdmProvider.getBrandingBoolean(mdmKey = CONFIGURATION_REDACT_AUTH_HEADER_LOGS, booleanKey = R.bool.redact_auth_header_logs) + val redactAuthHeader = + mdmProvider.getBrandingBoolean(mdmKey = CONFIGURATION_REDACT_AUTH_HEADER_LOGS, booleanKey = R.bool.redact_auth_header_logs) LogInterceptor.redactAuthHeader = redactAuthHeader } diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.kt b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.kt index 10a17618690..3fd03b8d7f7 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.kt @@ -57,6 +57,7 @@ import com.google.android.material.navigation.NavigationView import com.owncloud.android.R import com.owncloud.android.domain.capabilities.model.OCCapability import com.owncloud.android.domain.files.model.FileListOption +import com.owncloud.android.domain.user.model.UserQuota import com.owncloud.android.domain.user.model.UserQuotaState import com.owncloud.android.domain.utils.Event import com.owncloud.android.extensions.goToUrl @@ -311,105 +312,7 @@ abstract class DrawerActivity : ToolbarActivity() { when (val uiResult = event.peekContent()) { is UIResult.Success -> { uiResult.data?.let { userQuota -> - when { - userQuota.available == -4L -> { // Light users (oCIS) - getAccountQuotaText()?.text = getString(R.string.drawer_unavailable_used_storage) - getAccountQuotaBar()?.isVisible = false - getAccountQuotaStatusText()?.isVisible = false - } - - userQuota.available < 0 -> { // Pending, unknown or unlimited free storage - getAccountQuotaBar()?.apply { - isVisible = true - progress = 0 - progressTintList = ColorStateList.valueOf(resources.getColor(R.color.color_accent)) - } - getAccountQuotaText()?.text = String.format( - getString(R.string.drawer_unavailable_free_storage), - DisplayUtils.bytesToHumanReadable(userQuota.used, this, true) - ) - getAccountQuotaStatusText()?.visibility = View.GONE - } - - userQuota.available == 0L -> { // Exceeded storage. The value is over 100%. - getAccountQuotaBar()?.apply { - isVisible = true - progress = 100 - progressTintList = ColorStateList.valueOf(resources.getColor(R.color.quota_exceeded)) - } - - if (userQuota.state == UserQuotaState.EXCEEDED) { // oCIS - getAccountQuotaText()?.apply { - text = String.format( - getString(R.string.drawer_quota), - DisplayUtils.bytesToHumanReadable(userQuota.used, context, true), - DisplayUtils.bytesToHumanReadable(userQuota.getTotal(), context, true), - userQuota.getRelative() - ) - } - getAccountQuotaStatusText()?.apply { - visibility = View.VISIBLE - text = getString(R.string.drawer_exceeded_quota) - } - } else { // oC10 - getAccountQuotaText()?.text = getString(R.string.drawer_exceeded_quota) - getAccountQuotaStatusText()?.visibility = View.GONE - } - } - - else -> { // Limited storage. Value under 100% - if (userQuota.state == UserQuotaState.NEARING) { // Nearing storage. Value between 75% and 90% - getAccountQuotaBar()?.apply { - isVisible = true - progress = userQuota.getRelative().toInt() - progressTintList = ColorStateList.valueOf(resources.getColor(R.color.color_accent)) - } - getAccountQuotaText()?.apply { - text = String.format( - getString(R.string.drawer_quota), - DisplayUtils.bytesToHumanReadable(userQuota.used, context, true), - DisplayUtils.bytesToHumanReadable(userQuota.getTotal(), context, true), - userQuota.getRelative() - ) - } - getAccountQuotaStatusText()?.apply { - visibility = View.VISIBLE - text = getString(R.string.drawer_nearing_quota) - } - } else if (userQuota.state == UserQuotaState.CRITICAL || userQuota.state == UserQuotaState.EXCEEDED) { // Critical storage. Value over 90% - getAccountQuotaBar()?.apply { - isVisible = true - progress = userQuota.getRelative().toInt() - progressTintList = ColorStateList.valueOf(resources.getColor(R.color.quota_exceeded)) - } - getAccountQuotaText()?.apply { - text = String.format( - getString(R.string.drawer_quota), - DisplayUtils.bytesToHumanReadable(userQuota.used, context, true), - DisplayUtils.bytesToHumanReadable(userQuota.getTotal(), context, true), - userQuota.getRelative() - ) - } - getAccountQuotaStatusText()?.apply { - visibility = View.VISIBLE - text = getString(R.string.drawer_critical_quota) - } - } else { // Normal storage. Value under 75% - getAccountQuotaBar()?.apply { - progress = userQuota.getRelative().toInt() - isVisible = true - progressTintList = ColorStateList.valueOf(resources.getColor(R.color.color_accent)) - } - getAccountQuotaText()?.text = String.format( - getString(R.string.drawer_quota), - DisplayUtils.bytesToHumanReadable(userQuota.used, this, true), - DisplayUtils.bytesToHumanReadable(userQuota.getTotal(), this, true), - userQuota.getRelative() - ) - getAccountQuotaStatusText()?.visibility = View.GONE - } - } - } + onUpdateQuotaIsSuccessful(userQuota) } } is UIResult.Loading -> getAccountQuotaText()?.text = getString(R.string.drawer_loading_quota) @@ -418,6 +321,106 @@ abstract class DrawerActivity : ToolbarActivity() { } } + private fun onUpdateQuotaIsSuccessful(userQuota: UserQuota) { + when { + userQuota.available == -4L -> { // Light users (oCIS) + getAccountQuotaText()?.text = getString(R.string.drawer_unavailable_used_storage) + getAccountQuotaBar()?.isVisible = false + getAccountQuotaStatusText()?.isVisible = false + } + userQuota.available < 0 -> { // Pending, unknown or unlimited free storage + getAccountQuotaBar()?.apply { + isVisible = true + progress = 0 + progressTintList = ColorStateList.valueOf(resources.getColor(R.color.color_accent)) + } + getAccountQuotaText()?.text = String.format( + getString(R.string.drawer_unavailable_free_storage), + DisplayUtils.bytesToHumanReadable(userQuota.used, this, true) + ) + getAccountQuotaStatusText()?.visibility = View.GONE + } + userQuota.available == 0L -> { // Exceeded storage. The value is over 100%. + getAccountQuotaBar()?.apply { + isVisible = true + progress = 100 + progressTintList = ColorStateList.valueOf(resources.getColor(R.color.quota_exceeded)) + } + + if (userQuota.state == UserQuotaState.EXCEEDED) { // oCIS + getAccountQuotaText()?.apply { + text = String.format( + getString(R.string.drawer_quota), + DisplayUtils.bytesToHumanReadable(userQuota.used, context, true), + DisplayUtils.bytesToHumanReadable(userQuota.getTotal(), context, true), + userQuota.getRelative() + ) + } + getAccountQuotaStatusText()?.apply { + visibility = View.VISIBLE + text = getString(R.string.drawer_exceeded_quota) + } + } else { // oC10 + getAccountQuotaText()?.text = getString(R.string.drawer_exceeded_quota) + getAccountQuotaStatusText()?.visibility = View.GONE + } + } + else -> { // Limited storage. Value under 100% + if (userQuota.state == UserQuotaState.NEARING) { // Nearing storage. Value between 75% and 90% + getAccountQuotaBar()?.apply { + isVisible = true + progress = userQuota.getRelative().toInt() + progressTintList = ColorStateList.valueOf(resources.getColor(R.color.color_accent)) + } + getAccountQuotaText()?.apply { + text = String.format( + getString(R.string.drawer_quota), + DisplayUtils.bytesToHumanReadable(userQuota.used, context, true), + DisplayUtils.bytesToHumanReadable(userQuota.getTotal(), context, true), + userQuota.getRelative() + ) + } + getAccountQuotaStatusText()?.apply { + visibility = View.VISIBLE + text = getString(R.string.drawer_nearing_quota) + } + } else if (userQuota.state == UserQuotaState.CRITICAL || + userQuota.state == UserQuotaState.EXCEEDED) { // Critical storage. Value over 90% + getAccountQuotaBar()?.apply { + isVisible = true + progress = userQuota.getRelative().toInt() + progressTintList = ColorStateList.valueOf(resources.getColor(R.color.quota_exceeded)) + } + getAccountQuotaText()?.apply { + text = String.format( + getString(R.string.drawer_quota), + DisplayUtils.bytesToHumanReadable(userQuota.used, context, true), + DisplayUtils.bytesToHumanReadable(userQuota.getTotal(), context, true), + userQuota.getRelative() + ) + } + getAccountQuotaStatusText()?.apply { + visibility = View.VISIBLE + text = getString(R.string.drawer_critical_quota) + } + } else { // Normal storage. Value under 75% + getAccountQuotaBar()?.apply { + progress = userQuota.getRelative().toInt() + isVisible = true + progressTintList = ColorStateList.valueOf(resources.getColor(R.color.color_accent)) + } + getAccountQuotaText()?.text = String.format( + getString(R.string.drawer_quota), + DisplayUtils.bytesToHumanReadable(userQuota.used, this, true), + DisplayUtils.bytesToHumanReadable(userQuota.getTotal(), this, true), + userQuota.getRelative() + ) + getAccountQuotaStatusText()?.visibility = View.GONE + } + } + } + } + override fun setupRootToolbar(title: String, isSearchEnabled: Boolean, isAvatarRequested: Boolean) { super.setupRootToolbar( title = title, @@ -496,7 +499,8 @@ abstract class DrawerActivity : ToolbarActivity() { findItem(R.id.nav_settings)?.contentDescription = "${getString(R.string.actionbar_settings)} $roleAccessibilityDescription" findItem(R.id.drawer_menu_feedback)?.contentDescription = "${getString(R.string.drawer_feedback)} $roleAccessibilityDescription" findItem(R.id.drawer_menu_help)?.contentDescription = "${getString(R.string.prefs_help)} $roleAccessibilityDescription" - findItem(R.id.drawer_menu_privacy_policy)?.contentDescription = "${getString(R.string.prefs_privacy_policy)} $roleAccessibilityDescription" + findItem(R.id.drawer_menu_privacy_policy)?.contentDescription = + "${getString(R.string.prefs_privacy_policy)} $roleAccessibilityDescription" } } } @@ -608,7 +612,6 @@ abstract class DrawerActivity : ToolbarActivity() { const val TALK_MOBILE_URL = "https://talk.owncloud.com/channel/mobile" const val GITHUB_URL = "https://github.com/owncloud/android/issues/new/choose" const val SURVEY_URL = "https://owncloud.com/android-app-feedback" - private const val KEY_IS_ACCOUNT_CHOOSER_ACTIVE = "IS_ACCOUNT_CHOOSER_ACTIVE" private const val KEY_CHECKED_MENU_ITEM = "CHECKED_MENU_ITEM" } } diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileActivity.java b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileActivity.java index ed21851c708..eb7ddb57479 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileActivity.java +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileActivity.java @@ -256,7 +256,7 @@ public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationRe if (result.getCode() == ResultCode.UNAUTHORIZED) { showSnackMessage( - ErrorMessageAdapter.Companion.getResultMessage(result, operation, getResources()) + ErrorMessageAdapter.Companion.getResultMessage(result, getResources()) ); } diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt index 74319110fa8..0ace414a562 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt @@ -62,7 +62,6 @@ import com.owncloud.android.MainApp import com.owncloud.android.R import com.owncloud.android.data.providers.SharedPreferencesProvider import com.owncloud.android.databinding.ActivityMainBinding -import com.owncloud.android.domain.automaticuploads.model.UploadBehavior import com.owncloud.android.domain.capabilities.model.OCCapability import com.owncloud.android.domain.exceptions.AccountNotFoundException import com.owncloud.android.domain.exceptions.DeepLinkException @@ -371,11 +370,6 @@ class FileDisplayActivity : FileActivity(), if (!stateWasRecovered) { Timber.d("Initializing Fragments in onAccountChanged..") initFragmentsWithFile() - file?.isFolder?.let { isFolder -> - if (isFolder) { - startSyncFolderOperation(file, false) - } - } val syncProfileOperation = SyncProfileOperation(account) syncProfileOperation.syncUserProfile() val workManagerProvider = WorkManagerProvider(context = baseContext) @@ -604,7 +598,7 @@ class FileDisplayActivity : FileActivity(), // Handle calls form internal activities. if (requestCode == REQUEST_CODE__SELECT_CONTENT_FROM_APPS && (resultCode == RESULT_OK || resultCode == RESULT_OK_AND_MOVE)) { - requestUploadOfContentFromApps(data, resultCode) + requestUploadOfContentFromApps(data) } else if (requestCode == REQUEST_CODE__UPLOAD_FROM_CAMERA) { if (resultCode == RESULT_OK || resultCode == RESULT_OK_AND_MOVE) { @@ -618,7 +612,7 @@ class FileDisplayActivity : FileActivity(), capturedFilePaths: Array ) { if (hasEnoughSpace) { - requestUploadOfFilesFromFileSystem(capturedFilePaths, UploadBehavior.MOVE.toLegacyLocalBehavior()) + requestUploadOfFilesFromFileSystem(capturedFilePaths) } } }) @@ -640,7 +634,7 @@ class FileDisplayActivity : FileActivity(), } } - private fun requestUploadOfFilesFromFileSystem(filePaths: Array?, behaviour: Int) { + private fun requestUploadOfFilesFromFileSystem(filePaths: Array?) { if (filePaths != null) { val remotePaths = arrayOfNulls(filePaths.size) val remotePathBase = currentDir?.remotePath @@ -661,7 +655,7 @@ class FileDisplayActivity : FileActivity(), } } - private fun requestUploadOfContentFromApps(contentIntent: Intent?, resultCode: Int) { + private fun requestUploadOfContentFromApps(contentIntent: Intent?) { val streamsToUpload = ArrayList() if (contentIntent!!.clipData != null && contentIntent.clipData!!.itemCount > 0) { @@ -749,7 +743,8 @@ class FileDisplayActivity : FileActivity(), // If secondFragment was shown, we need to navigate to the parent of the displayed file // Need a cleanup val folderIdToDisplay = - if (fileListOption == FileListOption.AV_OFFLINE) storageManager.getRootPersonalFolder()!!.id!! else secondFragment!!.file!!.parentId!! + if (fileListOption == FileListOption.AV_OFFLINE) storageManager.getRootPersonalFolder()!!.id!! + else secondFragment!!.file!!.parentId!! mainFileListFragment?.navigateToFolderId(folderIdToDisplay) cleanSecondFragment() updateToolbar(mainFileListFragment?.getCurrentFile()) @@ -861,14 +856,8 @@ class FileDisplayActivity : FileActivity(), syncInProgress = true } else { - var currentFile: OCFile? = if (file == null) - null - else - storageManager.getFileByPath(file.remotePath, file.spaceId) - val currentDir = if (currentDir == null) - null - else - storageManager.getFileByPath(currentDir!!.remotePath, currentDir.spaceId) + var currentFile: OCFile? = file?.let { storageManager.getFileByPath(file.remotePath, file.spaceId) } + val currentDir = currentDir?.let { storageManager.getFileByPath(currentDir!!.remotePath, currentDir.spaceId) } if (currentDir == null) { // current folder was removed from the server @@ -925,7 +914,6 @@ class FileDisplayActivity : FileActivity(), val root = storageManager.getRootPersonalFolder() listOfFiles.navigateToFolder(root!!) file = root - startSyncFolderOperation(root, false) } cleanSecondFragment() } @@ -962,7 +950,8 @@ class FileDisplayActivity : FileActivity(), private fun updateToolbar(chosenFileFromParam: OCFile?, space: OCSpace? = null) { val chosenFile = chosenFileFromParam ?: file // If no file is passed, current file decides - // If we come from a preview activity (image or video), not updating toolbar when initializing this activity or it will show the root folder one + // If we come from a preview activity (image or video), not updating toolbar when initializing this activity + // or it will show the root folder one if (intent.action == ACTION_DETAILS && chosenFile?.remotePath == OCFile.ROOT_PATH && secondFragment is FileDetailsFragment) return if (chosenFile == null || (chosenFile.remotePath == OCFile.ROOT_PATH && (space == null || !space.isProject))) { @@ -1520,29 +1509,6 @@ class FileDisplayActivity : FileActivity(), } } - override fun onSavedCertificate() { - startSyncFolderOperation(currentDir, false) - } - - /** - * Starts an operation to refresh the requested folder. - * - * - * The operation is run in a new background thread created on the fly. - * - * - * The refresh updates is a "light sync": properties of regular files in folder are updated (including - * associated shares), but not their contents. Only the contents of files marked to be kept-in-sync are - * synchronized too. - * - * @param folder Folder to refresh. - * @param ignoreETag If 'true', the data from the server will be fetched and synced even if the eTag - * didn't change. - */ - fun startSyncFolderOperation(folder: OCFile?, ignoreETag: Boolean) { - // TODO: SYNC FOLDER - } - private fun requestForDownload(file: OCFile) { val downloadFileUseCase: DownloadFileUseCase by inject() @@ -1921,7 +1887,7 @@ class FileDisplayActivity : FileActivity(), } override fun uploadShortcutFileFromApp(shortcutFilePath: Array) { - requestUploadOfFilesFromFileSystem(shortcutFilePath, UploadBehavior.MOVE.toLegacyLocalBehavior()) + requestUploadOfFilesFromFileSystem(shortcutFilePath) } override fun uploadFromFileSystem() { @@ -2010,14 +1976,13 @@ class FileDisplayActivity : FileActivity(), } override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean { - return when (keyCode) { - KeyEvent.KEYCODE_DPAD_DOWN -> { - if (findViewById(R.id.owncloud_app_bar).hasFocus()) { - findViewById(R.id.left_fragment_container).requestFocus() - } - true + return if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) { + if (findViewById(R.id.owncloud_app_bar).hasFocus()) { + findViewById(R.id.left_fragment_container).requestFocus() } - else -> super.onKeyUp(keyCode, event) + true + } else { + super.onKeyUp(keyCode, event) } } @@ -2031,7 +1996,6 @@ class FileDisplayActivity : FileActivity(), private const val KEY_WAITING_TO_SEND = "WAITING_TO_SEND" private const val KEY_UPLOAD_HELPER = "FILE_UPLOAD_HELPER" private const val KEY_FILE_LIST_OPTION = "FILE_LIST_OPTION" - private const val MAX_URL_LENGTH = 90 const val MIMETYPE_TEXT_URI_LIST = "text/uri-list" const val KEY_DEEP_LINK_ACCOUNTS_CHECKED = "DEEP_LINK_ACCOUNTS_CHECKED" diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/errorhandling/ErrorMessageAdapter.kt b/owncloudApp/src/main/java/com/owncloud/android/ui/errorhandling/ErrorMessageAdapter.kt index ac06ea4c962..90242cb1f1f 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/errorhandling/ErrorMessageAdapter.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/errorhandling/ErrorMessageAdapter.kt @@ -156,7 +156,6 @@ class ErrorMessageAdapter { */ fun getResultMessage( result: RemoteOperationResult<*>, - operation: RemoteOperation<*>?, resources: Resources ): String { val formatter = Formatter(resources) @@ -184,7 +183,7 @@ class ErrorMessageAdapter { ResultCode.CONFLICT -> formatter.format(R.string.move_file_error) ResultCode.INVALID_COPY_INTO_DESCENDANT -> formatter.format(R.string.copy_file_invalid_into_descendent) - else -> getCommonMessageForResult(operation, result, resources) + else -> getCommonMessageForResult(result, resources) } } @@ -197,7 +196,6 @@ class ErrorMessageAdapter { * @return User message corresponding to 'result'. */ private fun getCommonMessageForResult( - operation: RemoteOperation<*>?, result: RemoteOperationResult<*>, res: Resources ): String { @@ -226,7 +224,7 @@ class ErrorMessageAdapter { ResultCode.ACCOUNT_NOT_THE_SAME -> formatter.format(R.string.auth_account_not_the_same) ResultCode.OK_REDIRECT_TO_NON_SECURE_CONNECTION -> formatter.format(R.string.auth_redirect_non_secure_connection_title) else -> if (result.httpPhrase != null && result.httpPhrase.isNotEmpty()) - result.httpPhrase else getGenericErrorMessageForOperation(operation, result, res) + result.httpPhrase else getGenericErrorMessageForOperation(result, res) } } @@ -238,7 +236,6 @@ class ErrorMessageAdapter { * @return User message corresponding to a generic error of 'operation'. */ private fun getGenericErrorMessageForOperation( - operation: RemoteOperation<*>?, result: RemoteOperationResult<*>, res: Resources ): String { diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewAudioFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewAudioFragment.kt index 2087cfffb49..a02d50d26d0 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewAudioFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewAudioFragment.kt @@ -259,7 +259,8 @@ class PreviewAudioFragment : FileFragment() { private fun setRolesAccessibilityToMenuItems(menu: Menu) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - menu.findItem(R.id.action_see_details)?.contentDescription = "${getString(R.string.actionbar_see_details)} ${getString(R.string.button_role_accessibility)}" + menu.findItem(R.id.action_see_details)?.contentDescription = + "${getString(R.string.actionbar_see_details)} ${getString(R.string.button_role_accessibility)}" } } diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewFormatTextFragmentStateAdapter.kt b/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewFormatTextFragmentStateAdapter.kt index 4c7f44b3fa7..27fd5f69b72 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewFormatTextFragmentStateAdapter.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewFormatTextFragmentStateAdapter.kt @@ -47,9 +47,10 @@ class PreviewFormatTextFragmentStateAdapter( override fun getItemCount(): Int = 2 override fun createFragment(position: Int): Fragment { - return when (position) { - 0 -> PreviewFormatTextFragment.newInstance(text, mimeType) - else -> PreviewFormatTextFragment.newInstance(text) + return if (position == 0) { + PreviewFormatTextFragment.newInstance(text, mimeType) + } else { + PreviewFormatTextFragment.newInstance(text) } } diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt b/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt index 06fe6316acb..e8c1a7bb3ec 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt @@ -253,16 +253,15 @@ class PreviewImageActivity : FileActivity(), } override fun onOptionsItemSelected(item: MenuItem): Boolean { - return when (item.itemId) { - android.R.id.home -> { - if (isDrawerOpen()) { - closeDrawer() - } else { - backToDisplayActivity() - } - true + return if (item.itemId == android.R.id.home) { + if (isDrawerOpen()) { + closeDrawer() + } else { + backToDisplayActivity() } - else -> super.onOptionsItemSelected(item) + true + } else { + super.onOptionsItemSelected(item) } } @@ -411,12 +410,11 @@ class PreviewImageActivity : FileActivity(), } override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean { - return when (keyCode) { - KeyEvent.KEYCODE_TAB -> { - showSystemUI(fullScreenAnchorView) - true - } - else -> super.onKeyUp(keyCode, event) + return if (keyCode == KeyEvent.KEYCODE_TAB) { + showSystemUI(fullScreenAnchorView) + true + } else { + super.onKeyUp(keyCode, event) } } diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.kt index 1e1e2bdd7b8..a3e0bdd3dfc 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.kt @@ -206,7 +206,8 @@ class PreviewImageFragment : FileFragment() { private fun setRolesAccessibilityToMenuItems(menu: Menu) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - menu.findItem(R.id.action_see_details)?.contentDescription = "${getString(R.string.actionbar_see_details)} ${getString(R.string.button_role_accessibility)}" + menu.findItem(R.id.action_see_details)?.contentDescription = + "${getString(R.string.actionbar_see_details)} ${getString(R.string.button_role_accessibility)}" } } diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewTextFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewTextFragment.kt index 18421282bc6..b56c0ea2bf3 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewTextFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewTextFragment.kt @@ -247,7 +247,8 @@ class PreviewTextFragment : FileFragment() { private fun setRolesAccessibilityToMenuItems(menu: Menu) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - menu.findItem(R.id.action_see_details)?.contentDescription = "${getString(R.string.actionbar_see_details)} ${getString(R.string.button_role_accessibility)}" + menu.findItem(R.id.action_see_details)?.contentDescription = + "${getString(R.string.actionbar_see_details)} ${getString(R.string.button_role_accessibility)}" } } diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewVideoActivity.kt b/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewVideoActivity.kt index 0cd8482b328..072ef4d7955 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewVideoActivity.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewVideoActivity.kt @@ -354,11 +354,15 @@ class PreviewVideoActivity : FileActivity(), Player.Listener, OnPrepareVideoPlay if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val roleAccessibilityDescription = getString(R.string.button_role_accessibility) menu.apply { - menu.findItem(R.id.action_open_file_with)?.contentDescription = "${getString(R.string.actionbar_open_with)} $roleAccessibilityDescription" + menu.findItem(R.id.action_open_file_with)?.contentDescription = + "${getString(R.string.actionbar_open_with)} $roleAccessibilityDescription" menu.findItem(R.id.action_send_file)?.contentDescription = "${getString(R.string.actionbar_send_file)} $roleAccessibilityDescription" - menu.findItem(R.id.action_set_available_offline)?.contentDescription = "${getString(R.string.set_available_offline)} $roleAccessibilityDescription" - menu.findItem(R.id.action_unset_available_offline)?.contentDescription = "${getString(R.string.unset_available_offline)} $roleAccessibilityDescription" - menu.findItem(R.id.action_see_details)?.contentDescription = "${getString(R.string.actionbar_see_details)} $roleAccessibilityDescription" + menu.findItem(R.id.action_set_available_offline)?.contentDescription = + "${getString(R.string.set_available_offline)} $roleAccessibilityDescription" + menu.findItem(R.id.action_unset_available_offline)?.contentDescription = + "${getString(R.string.unset_available_offline)} $roleAccessibilityDescription" + menu.findItem(R.id.action_see_details)?.contentDescription = + "${getString(R.string.actionbar_see_details)} $roleAccessibilityDescription" menu.findItem(R.id.action_remove_file)?.contentDescription = "${getString(R.string.common_remove)} $roleAccessibilityDescription" } } diff --git a/owncloudApp/src/main/java/com/owncloud/android/usecases/synchronization/SynchronizeFileUseCase.kt b/owncloudApp/src/main/java/com/owncloud/android/usecases/synchronization/SynchronizeFileUseCase.kt index b87f24c3590..1a1181b3e62 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/usecases/synchronization/SynchronizeFileUseCase.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/usecases/synchronization/SynchronizeFileUseCase.kt @@ -73,7 +73,8 @@ class SynchronizeFileUseCase( // 3. Check if file has changed locally val changedLocally = fileToSynchronize.localModificationTimestamp > fileToSynchronize.lastSyncDateForData!! - Timber.i("Local file modification timestamp :${fileToSynchronize.localModificationTimestamp} and last sync date for data :${fileToSynchronize.lastSyncDateForData}") + Timber.i("Local file modification timestamp :${fileToSynchronize.localModificationTimestamp}" + + " and last sync date for data :${fileToSynchronize.lastSyncDateForData}") Timber.i("So it has changed locally: $changedLocally") // 4. Check if file has changed remotely @@ -101,7 +102,7 @@ class SynchronizeFileUseCase( } else if (changedLocally) { // 5.3 File has change ONLY locally -> upload new version Timber.i("File ${fileToSynchronize.fileName} has changed locally. Let's upload the new version") - val uuid = requestForUpload(accountName, fileToSynchronize, fileToSynchronize.etag!!) + val uuid = requestForUpload(accountName, fileToSynchronize) return SyncType.UploadEnqueued(uuid) } else { // 5.4 File has not change locally not remotely -> do nothing @@ -120,7 +121,7 @@ class SynchronizeFileUseCase( ) } - private fun requestForUpload(accountName: String, ocFile: OCFile, etagInConflict: String): UUID? { + private fun requestForUpload(accountName: String, ocFile: OCFile): UUID? { return uploadFileInConflictUseCase( UploadFileInConflictUseCase.Params( accountName = accountName, diff --git a/owncloudApp/src/main/java/com/owncloud/android/utils/NotificationUtils.kt b/owncloudApp/src/main/java/com/owncloud/android/utils/NotificationUtils.kt index b7872b282a8..74d72d666dc 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/utils/NotificationUtils.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/utils/NotificationUtils.kt @@ -157,7 +157,7 @@ object NotificationUtils { * @param account account which the file in conflict belongs to */ @JvmStatic - fun notifyConflict(fileInConflict: OCFile, account: Account?, context: Context) { + fun notifyConflict(fileInConflict: OCFile, context: Context) { val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager val notificationBuilder = newNotificationBuilder(context, FILE_SYNC_CONFLICT_NOTIFICATION_CHANNEL_ID) notificationBuilder diff --git a/owncloudApp/src/main/java/com/owncloud/android/workers/DownloadFileWorker.kt b/owncloudApp/src/main/java/com/owncloud/android/workers/DownloadFileWorker.kt index b8fdb94208d..e6ac9b0497f 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/workers/DownloadFileWorker.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/workers/DownloadFileWorker.kt @@ -53,11 +53,7 @@ import com.owncloud.android.presentation.authentication.EXTRA_ACCOUNT import com.owncloud.android.presentation.authentication.EXTRA_ACTION import com.owncloud.android.presentation.authentication.LoginActivity import com.owncloud.android.presentation.transfers.TransferOperation.Download -import com.owncloud.android.ui.activity.FileActivity -import com.owncloud.android.ui.activity.FileDisplayActivity import com.owncloud.android.ui.errorhandling.ErrorMessageAdapter -import com.owncloud.android.ui.preview.PreviewImageActivity -import com.owncloud.android.ui.preview.PreviewImageFragment.Companion.canBePreviewed import com.owncloud.android.utils.DOWNLOAD_NOTIFICATION_CHANNEL_ID import com.owncloud.android.utils.DOWNLOAD_NOTIFICATION_ID_DEFAULT import com.owncloud.android.utils.FileStorageUtils @@ -309,27 +305,6 @@ class DownloadFileWorker( ) } - private fun composePendingIntentToPreviewFile(): PendingIntent { - /// includes a pending intent in the notification showing the details view of the file - val showDetailsIntent: Intent = - if (canBePreviewed(ocFile)) { - Intent(appContext, PreviewImageActivity::class.java) - } else { - Intent(appContext, FileDisplayActivity::class.java) - }.apply { - putExtra(FileActivity.EXTRA_FILE, ocFile) - putExtra(FileActivity.EXTRA_ACCOUNT, account) - flags = Intent.FLAG_ACTIVITY_CLEAR_TOP - } - - return PendingIntent.getActivity( - appContext, - System.currentTimeMillis().toInt(), - showDetailsIntent, - PendingIntent.FLAG_IMMUTABLE - ) - } - private fun getClientForThisDownload(): OwnCloudClient = SingleSessionManager.getDefaultSingleton() .getClientFor(OwnCloudAccount(AccountUtils.getOwnCloudAccountByName(appContext, account.name), appContext), appContext) diff --git a/owncloudApp/src/main/java/com/owncloud/android/workers/UploadFileFromContentUriWorker.kt b/owncloudApp/src/main/java/com/owncloud/android/workers/UploadFileFromContentUriWorker.kt index a655a2c97ce..363cdec24a4 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/workers/UploadFileFromContentUriWorker.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/workers/UploadFileFromContentUriWorker.kt @@ -180,10 +180,8 @@ class UploadFileFromContentUriWorker( private fun copyFileToLocalStorage() { val cacheFile = File(cachePath) val cacheDir = cacheFile.parentFile - if (cacheDir != null) { - if (!cacheDir.exists()) { - cacheDir.mkdirs() - } + if (cacheDir != null && !cacheDir.exists()) { + cacheDir.mkdirs() } cacheFile.createNewFile() diff --git a/owncloudApp/src/test/java/com/owncloud/android/presentation/sharing/PublicLinkPasswordUtilsTest.kt b/owncloudApp/src/test/java/com/owncloud/android/presentation/sharing/PublicLinkPasswordUtilsTest.kt index f81abe73df5..bf1ba92319d 100644 --- a/owncloudApp/src/test/java/com/owncloud/android/presentation/sharing/PublicLinkPasswordUtilsTest.kt +++ b/owncloudApp/src/test/java/com/owncloud/android/presentation/sharing/PublicLinkPasswordUtilsTest.kt @@ -28,7 +28,8 @@ class PublicLinkPasswordUtilsTest { private val charsetLowercase = ('a'..'z').toList() private val charsetUppercase = ('A'..'Z').toList() private val charsetDigits = ('0'..'9').toList() - private val charsetSpecial = listOf('!', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_', '`', '{', '|', '}', '~') + private val charsetSpecial = listOf('!', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '@', + '[', '\\', ']', '^', '_', '`', '{', '|', '}', '~') private val minCharacters = 10 private val maxCharacters = 100 @@ -39,7 +40,7 @@ class PublicLinkPasswordUtilsTest { @Test fun `generatePassword creates password fulfilling all policies`() { - for (i in 1..1000) { + repeat(1000) { val password = generatePassword( minChars = minCharacters, maxChars = maxCharacters, diff --git a/owncloudComLibrary/build.gradle b/owncloudComLibrary/build.gradle index 990895e4ff7..b67c6464539 100644 --- a/owncloudComLibrary/build.gradle +++ b/owncloudComLibrary/build.gradle @@ -26,6 +26,10 @@ dependencies { testImplementation 'junit:junit:4.13.2' testImplementation 'org.robolectric:robolectric:4.10' debugImplementation 'com.facebook.stetho:stetho-okhttp3:1.6.0' + + // Detekt + detektPlugins libs.detekt.formatting + detektPlugins libs.detekt.libraries } android { diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/logging/LogInterceptor.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/logging/LogInterceptor.kt index 862846f521f..ecce97d7ea7 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/logging/LogInterceptor.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/logging/LogInterceptor.kt @@ -136,14 +136,7 @@ class LogInterceptor : Interceptor { LogResponse( Response( headers = logHeaders(response.headers), - body = if (responseBody == null) { - null - } else { - Body( - data = responseBody, - length = bodyLength, - ) - }, + body = responseBody?.let { Body(data = responseBody, length = bodyLength) }, info = ResponseInfo( id = requestId, method = request.method, diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/utils/OCFileLoggingTree.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/utils/OCFileLoggingTree.kt index 03f854c874b..03b95ef0b60 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/utils/OCFileLoggingTree.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/utils/OCFileLoggingTree.kt @@ -48,9 +48,8 @@ class OCFileLoggingTree( init { externalCacheDir.let { - if (!it.exists()) { - if (!it.mkdirs()) - Log.e(LOG_TAG, "couldn't create ${it.absoluteFile}") + if (!it.exists() && !it.mkdirs()) { + Log.e(LOG_TAG, "couldn't create ${it.absoluteFile}") } var fileNameTimestamp = SimpleDateFormat(LOG_FILE_TIME_FORMAT, Locale.getDefault()).format(Date()) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/appregistry/CreateRemoteFileWithAppProviderOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/appregistry/CreateRemoteFileWithAppProviderOperation.kt index e4f85a32fa1..9428b5c8c3e 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/appregistry/CreateRemoteFileWithAppProviderOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/appregistry/CreateRemoteFileWithAppProviderOperation.kt @@ -61,7 +61,8 @@ class CreateRemoteFileWithAppProviderOperation( } val status = client.executeHttpMethod(postMethod) - Timber.d("Create file $filename with app provider in folder with ID $parentContainerId - $status${if (!isSuccess(status)) "(FAIL)" else ""}") + Timber.d("Create file $filename with app provider in folder with ID $parentContainerId" + + " - $status${if (!isSuccess(status)) "(FAIL)" else ""}") if (isSuccess(status)) RemoteOperationResult(ResultCode.OK).apply { val moshi = Moshi.Builder().build() diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/responses/CapabilityResponse.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/responses/CapabilityResponse.kt index 06b14ca62b0..9c659127120 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/responses/CapabilityResponse.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/responses/CapabilityResponse.kt @@ -50,10 +50,14 @@ data class CapabilityResponse( filesSharingApiEnabled = CapabilityBooleanType.fromBooleanValue(capabilities?.fileSharingCapabilities?.fileSharingApiEnabled), filesSharingResharing = CapabilityBooleanType.fromBooleanValue(capabilities?.fileSharingCapabilities?.fileSharingReSharing), filesSharingPublicEnabled = CapabilityBooleanType.fromBooleanValue(capabilities?.fileSharingCapabilities?.fileSharingPublic?.enabled), - filesSharingPublicUpload = CapabilityBooleanType.fromBooleanValue(capabilities?.fileSharingCapabilities?.fileSharingPublic?.fileSharingPublicUpload), - filesSharingPublicSupportsUploadOnly = CapabilityBooleanType.fromBooleanValue(capabilities?.fileSharingCapabilities?.fileSharingPublic?.fileSharingPublicUploadOnly), - filesSharingPublicMultiple = CapabilityBooleanType.fromBooleanValue(capabilities?.fileSharingCapabilities?.fileSharingPublic?.fileSharingPublicMultiple), - filesSharingPublicPasswordEnforced = CapabilityBooleanType.fromBooleanValue(capabilities?.fileSharingCapabilities?.fileSharingPublic?.fileSharingPublicPassword?.enforced), + filesSharingPublicUpload = + CapabilityBooleanType.fromBooleanValue(capabilities?.fileSharingCapabilities?.fileSharingPublic?.fileSharingPublicUpload), + filesSharingPublicSupportsUploadOnly = + CapabilityBooleanType.fromBooleanValue(capabilities?.fileSharingCapabilities?.fileSharingPublic?.fileSharingPublicUploadOnly), + filesSharingPublicMultiple = + CapabilityBooleanType.fromBooleanValue(capabilities?.fileSharingCapabilities?.fileSharingPublic?.fileSharingPublicMultiple), + filesSharingPublicPasswordEnforced = + CapabilityBooleanType.fromBooleanValue(capabilities?.fileSharingCapabilities?.fileSharingPublic?.fileSharingPublicPassword?.enforced), filesSharingPublicPasswordEnforcedReadOnly = CapabilityBooleanType.fromBooleanValue( capabilities?.fileSharingCapabilities?.fileSharingPublic?.fileSharingPublicPassword?.enforcedFor?.enforcedReadOnly ), @@ -63,7 +67,8 @@ data class CapabilityResponse( filesSharingPublicPasswordEnforcedUploadOnly = CapabilityBooleanType.fromBooleanValue( capabilities?.fileSharingCapabilities?.fileSharingPublic?.fileSharingPublicPassword?.enforcedFor?.enforcedUploadOnly ), - filesSharingPublicExpireDateEnabled = CapabilityBooleanType.fromBooleanValue(capabilities?.fileSharingCapabilities?.fileSharingPublic?.fileSharingPublicExpireDate?.enabled), + filesSharingPublicExpireDateEnabled = + CapabilityBooleanType.fromBooleanValue(capabilities?.fileSharingCapabilities?.fileSharingPublic?.fileSharingPublicExpireDate?.enabled), filesSharingPublicExpireDateDays = capabilities?.fileSharingCapabilities?.fileSharingPublic?.fileSharingPublicExpireDate?.days ?: 0, filesSharingPublicExpireDateEnforced = CapabilityBooleanType.fromBooleanValue( @@ -75,9 +80,12 @@ data class CapabilityResponse( filesPrivateLinks = capabilities?.fileCapabilities?.privateLinks?.let { CapabilityBooleanType.fromBooleanValue(it) } ?: CapabilityBooleanType.UNKNOWN, filesAppProviders = capabilities?.fileCapabilities?.appProviders?.map { it.toAppProviders() }, - filesSharingFederationIncoming = CapabilityBooleanType.fromBooleanValue(capabilities?.fileSharingCapabilities?.fileSharingFederation?.incoming), - filesSharingFederationOutgoing = CapabilityBooleanType.fromBooleanValue(capabilities?.fileSharingCapabilities?.fileSharingFederation?.outgoing), - filesSharingUserProfilePicture = CapabilityBooleanType.fromBooleanValue(capabilities?.fileSharingCapabilities?.fileSharingUser?.profilePicture), + filesSharingFederationIncoming = + CapabilityBooleanType.fromBooleanValue(capabilities?.fileSharingCapabilities?.fileSharingFederation?.incoming), + filesSharingFederationOutgoing = + CapabilityBooleanType.fromBooleanValue(capabilities?.fileSharingCapabilities?.fileSharingFederation?.outgoing), + filesSharingUserProfilePicture = + CapabilityBooleanType.fromBooleanValue(capabilities?.fileSharingCapabilities?.fileSharingUser?.profilePicture), spaces = capabilities?.spacesCapabilities?.toSpaces(), passwordPolicy = capabilities?.passwordPolicyCapabilities?.toPasswordPolicy(), ) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/services/ServerInfoService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/services/ServerInfoService.kt index b1eb2871e39..e960f62cd06 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/services/ServerInfoService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/services/ServerInfoService.kt @@ -23,7 +23,6 @@ */ package com.owncloud.android.lib.resources.status.services - import com.owncloud.android.lib.common.OwnCloudClient import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.resources.status.RemoteServerInfo diff --git a/owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/webfinger/responses/WebFingerResponseTest.kt b/owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/webfinger/responses/WebFingerResponseTest.kt index 4b275cd73b7..7b14da998ee 100644 --- a/owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/webfinger/responses/WebFingerResponseTest.kt +++ b/owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/webfinger/responses/WebFingerResponseTest.kt @@ -41,9 +41,7 @@ class WebFingerResponseTest { companion object { private const val RESOURCES_PATH = "src/test/responses/com.owncloud.android.lib.resources.webfinger.responses" - private const val EXAMPLE_RESPONSE_JSON = "$RESOURCES_PATH/simple_response.json" private const val TOO_MUCH_INFORMATION_JSON = "$RESOURCES_PATH/to_much_information_response.json" private const val BROKEN_JSON = "$RESOURCES_PATH/broken_response.json" - private const val NOT_CONTAINING_RELEVANT_INFORMATION_JSON = "$RESOURCES_PATH/not_containing_relevant_info_response.json" } } diff --git a/owncloudData/build.gradle b/owncloudData/build.gradle index 0e62c821d84..7f50823a276 100644 --- a/owncloudData/build.gradle +++ b/owncloudData/build.gradle @@ -89,4 +89,8 @@ dependencies { androidTestImplementation libs.androidx.test.runner androidTestImplementation libs.kotlinx.coroutines.test androidTestImplementation(libs.mockk.android) { exclude module: "objenesis" } + + // Detekt + detektPlugins libs.detekt.formatting + detektPlugins libs.detekt.libraries } diff --git a/owncloudData/src/main/java/com/owncloud/android/data/capabilities/db/OCCapabilityEntity.kt b/owncloudData/src/main/java/com/owncloud/android/data/capabilities/db/OCCapabilityEntity.kt index 5eca4788088..d44bf2324de 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/capabilities/db/OCCapabilityEntity.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/capabilities/db/OCCapabilityEntity.kt @@ -132,7 +132,7 @@ data class OCCapabilityEntity( @PrimaryKey(autoGenerate = true) var id: Int = 0 companion object { - fun fromCursor(cursor: Cursor): OCCapabilityEntity = cursor.use { it -> + fun fromCursor(cursor: Cursor): OCCapabilityEntity = cursor.use { OCCapabilityEntity( it.getString(it.getColumnIndexOrThrow(CAPABILITIES_ACCOUNT_NAME)), it.getInt(it.getColumnIndexOrThrow(CAPABILITIES_VERSION_MAJOR)), diff --git a/owncloudData/src/main/java/com/owncloud/android/data/files/repository/OCFileRepository.kt b/owncloudData/src/main/java/com/owncloud/android/data/files/repository/OCFileRepository.kt index 0ada7453f64..29421665a4f 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/files/repository/OCFileRepository.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/files/repository/OCFileRepository.kt @@ -396,7 +396,8 @@ class OCFileRepository( // DO NOT update etag till contents are synced. etag = localChildToSync.etag needsToUpdateThumbnail = - (!remoteChild.isFolder && remoteChild.modificationTimestamp != localChildToSync.modificationTimestamp) || localChildToSync.needsToUpdateThumbnail + (!remoteChild.isFolder && remoteChild.modificationTimestamp != localChildToSync.modificationTimestamp) || + localChildToSync.needsToUpdateThumbnail // Probably not needed, if the child was already in the database, the av offline status should be also there if (remoteFolder.isAvailableOffline) { availableOfflineStatus = AVAILABLE_OFFLINE_PARENT @@ -584,7 +585,8 @@ class OCFileRepository( // 1. Remove folder content recursively folderContent.forEach { file -> - if (!(onlyFromLocalStorage && file.isAvailableOffline)) { // The condition will not be met when onlyFromLocalStorage is true and the file is of type available offline + // The condition will not be met when onlyFromLocalStorage is true and the file is of type available offline + if (!(onlyFromLocalStorage && file.isAvailableOffline)) { if (file.isFolder) { deleteLocalFolderRecursively(ocFile = file, onlyFromLocalStorage = onlyFromLocalStorage) } else { diff --git a/owncloudData/src/main/java/com/owncloud/android/data/migrations/Migration_28.kt b/owncloudData/src/main/java/com/owncloud/android/data/migrations/Migration_28.kt index f69a271d7fe..6f0bb2c0143 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/migrations/Migration_28.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/migrations/Migration_28.kt @@ -26,11 +26,33 @@ val MIGRATION_27_28 = object : Migration(27, 28) { override fun migrate(database: SupportSQLiteDatabase) { database.run { execSQL( - "CREATE TABLE IF NOT EXISTS `${ProviderTableMeta.CAPABILITIES_TABLE_NAME}2` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `account` TEXT, `version_mayor` INTEGER NOT NULL, `version_minor` INTEGER NOT NULL, `version_micro` INTEGER NOT NULL, `version_string` TEXT, `version_edition` TEXT, `core_pollinterval` INTEGER NOT NULL, `sharing_api_enabled` INTEGER NOT NULL DEFAULT -1, `search_min_length` INTEGER, `sharing_public_enabled` INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced` INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced_read_only` INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced_read_write` INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced_public_only` INTEGER NOT NULL DEFAULT -1, `sharing_public_expire_date_enabled` INTEGER NOT NULL DEFAULT -1, `sharing_public_expire_date_days` INTEGER NOT NULL, `sharing_public_expire_date_enforced` INTEGER NOT NULL DEFAULT -1, `sharing_public_send_mail` INTEGER NOT NULL DEFAULT -1, `sharing_public_upload` INTEGER NOT NULL DEFAULT -1, `sharing_public_multiple` INTEGER NOT NULL DEFAULT -1, `supports_upload_only` INTEGER NOT NULL DEFAULT -1, `sharing_user_send_mail` INTEGER NOT NULL DEFAULT -1, `sharing_resharing` INTEGER NOT NULL DEFAULT -1, `sharing_federation_outgoing` INTEGER NOT NULL DEFAULT -1, `sharing_federation_incoming` INTEGER NOT NULL DEFAULT -1, `files_bigfilechunking` INTEGER NOT NULL DEFAULT -1, `files_undelete` INTEGER NOT NULL DEFAULT -1, `files_versioning` INTEGER NOT NULL DEFAULT -1)" + "CREATE TABLE IF NOT EXISTS `${ProviderTableMeta.CAPABILITIES_TABLE_NAME}2` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," + + " `account` TEXT, `version_mayor` INTEGER NOT NULL, `version_minor` INTEGER NOT NULL, `version_micro` INTEGER NOT NULL," + + " `version_string` TEXT, `version_edition` TEXT, `core_pollinterval` INTEGER NOT NULL, `sharing_api_enabled`" + + " INTEGER NOT NULL DEFAULT -1, `search_min_length` INTEGER, `sharing_public_enabled` INTEGER NOT NULL DEFAULT -1," + + " `sharing_public_password_enforced` INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced_read_only`" + + " INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced_read_write` INTEGER NOT NULL DEFAULT -1," + + " `sharing_public_password_enforced_public_only` INTEGER NOT NULL DEFAULT -1, `sharing_public_expire_date_enabled`" + + " INTEGER NOT NULL DEFAULT -1, `sharing_public_expire_date_days` INTEGER NOT NULL, `sharing_public_expire_date_enforced`" + + " INTEGER NOT NULL DEFAULT -1, `sharing_public_send_mail` INTEGER NOT NULL DEFAULT -1, `sharing_public_upload` INTEGER" + + " NOT NULL DEFAULT -1, `sharing_public_multiple` INTEGER NOT NULL DEFAULT -1, `supports_upload_only` INTEGER NOT NULL" + + " DEFAULT -1, `sharing_user_send_mail` INTEGER NOT NULL DEFAULT -1, `sharing_resharing` INTEGER NOT NULL DEFAULT -1," + + " `sharing_federation_outgoing` INTEGER NOT NULL DEFAULT -1, `sharing_federation_incoming` INTEGER NOT NULL DEFAULT -1," + + " `files_bigfilechunking` INTEGER NOT NULL DEFAULT -1, `files_undelete` INTEGER NOT NULL DEFAULT -1, `files_versioning`" + + " INTEGER NOT NULL DEFAULT -1)" ) execSQL("ALTER TABLE ${ProviderTableMeta.CAPABILITIES_TABLE_NAME} ADD COLUMN search_min_length INTEGER") execSQL( - "INSERT INTO `${ProviderTableMeta.CAPABILITIES_TABLE_NAME}2` SELECT id, account, version_mayor, version_minor, version_micro, version_string, version_edition,core_pollinterval, IFNULL(sharing_api_enabled, -1), search_min_length, IFNULL(sharing_public_enabled, -1), IFNULL(sharing_public_password_enforced, -1),IFNULL(sharing_public_password_enforced_read_only, -1),IFNULL(sharing_public_password_enforced_read_write, -1),IFNULL(sharing_public_password_enforced_public_only, -1),IFNULL(sharing_public_expire_date_enabled, -1),IFNULL(sharing_public_expire_date_days, 0),IFNULL(sharing_public_expire_date_enforced, -1),IFNULL(sharing_public_send_mail, -1),IFNULL(sharing_public_upload, -1),IFNULL(sharing_public_multiple, -1), IFNULL(supports_upload_only, -1), IFNULL(sharing_user_send_mail, -1),IFNULL(sharing_resharing, -1),IFNULL(sharing_federation_outgoing, -1),IFNULL(sharing_federation_incoming, -1),IFNULL(files_bigfilechunking, -1),IFNULL(files_undelete, -1),IFNULL(files_versioning, -1) FROM ${ProviderTableMeta.CAPABILITIES_TABLE_NAME}" + "INSERT INTO `${ProviderTableMeta.CAPABILITIES_TABLE_NAME}2` SELECT id, account, version_mayor, version_minor, version_micro," + + " version_string, version_edition,core_pollinterval, IFNULL(sharing_api_enabled, -1), search_min_length," + + " IFNULL(sharing_public_enabled, -1), IFNULL(sharing_public_password_enforced, -1)," + + "IFNULL(sharing_public_password_enforced_read_only, -1),IFNULL(sharing_public_password_enforced_read_write, -1)," + + "IFNULL(sharing_public_password_enforced_public_only, -1),IFNULL(sharing_public_expire_date_enabled, -1)," + + "IFNULL(sharing_public_expire_date_days, 0),IFNULL(sharing_public_expire_date_enforced, -1)," + + "IFNULL(sharing_public_send_mail, -1),IFNULL(sharing_public_upload, -1),IFNULL(sharing_public_multiple, -1)," + + " IFNULL(supports_upload_only, -1), IFNULL(sharing_user_send_mail, -1),IFNULL(sharing_resharing, -1)," + + "IFNULL(sharing_federation_outgoing, -1),IFNULL(sharing_federation_incoming, -1),IFNULL(files_bigfilechunking, -1)," + + "IFNULL(files_undelete, -1),IFNULL(files_versioning, -1) FROM ${ProviderTableMeta.CAPABILITIES_TABLE_NAME}" ) execSQL("DROP TABLE ${ProviderTableMeta.CAPABILITIES_TABLE_NAME}") execSQL("ALTER TABLE ${ProviderTableMeta.CAPABILITIES_TABLE_NAME}2 RENAME TO ${ProviderTableMeta.CAPABILITIES_TABLE_NAME}") diff --git a/owncloudData/src/main/java/com/owncloud/android/data/migrations/Migration_29.kt b/owncloudData/src/main/java/com/owncloud/android/data/migrations/Migration_29.kt index 500d651d60e..e4e42d20f57 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/migrations/Migration_29.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/migrations/Migration_29.kt @@ -27,13 +27,40 @@ val MIGRATION_28_29 = object : Migration(28, 29) { database.run { execSQL("DROP TABLE ${ProviderTableMeta.OCSHARES_TABLE_NAME}") execSQL( - "CREATE TABLE IF NOT EXISTS `${ProviderTableMeta.OCSHARES_TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `file_source` TEXT NOT NULL, `item_source` TEXT NOT NULL, `share_type` INTEGER NOT NULL, `shate_with` TEXT, `path` TEXT NOT NULL, `permissions` INTEGER NOT NULL, `shared_date` INTEGER NOT NULL, `expiration_date` INTEGER NOT NULL, `token` TEXT, `shared_with_display_name` TEXT, `share_with_additional_info` TEXT, `is_directory` INTEGER NOT NULL, `user_id` INTEGER NOT NULL, `id_remote_shared` INTEGER NOT NULL, `owner_share` TEXT NOT NULL, `name` TEXT, `url` TEXT)" + "CREATE TABLE IF NOT EXISTS `${ProviderTableMeta.OCSHARES_TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," + + " `file_source` TEXT NOT NULL, `item_source` TEXT NOT NULL, `share_type` INTEGER NOT NULL, `shate_with` TEXT," + + " `path` TEXT NOT NULL, `permissions` INTEGER NOT NULL, `shared_date` INTEGER NOT NULL, `expiration_date` INTEGER" + + " NOT NULL, `token` TEXT, `shared_with_display_name` TEXT, `share_with_additional_info` TEXT, `is_directory` INTEGER" + + " NOT NULL, `user_id` INTEGER NOT NULL, `id_remote_shared` INTEGER NOT NULL, `owner_share` TEXT NOT NULL," + + " `name` TEXT, `url` TEXT)" ) execSQL( - "CREATE TABLE IF NOT EXISTS `${ProviderTableMeta.CAPABILITIES_TABLE_NAME}2` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `account` TEXT, `version_mayor` INTEGER NOT NULL, `version_minor` INTEGER NOT NULL, `version_micro` INTEGER NOT NULL, `version_string` TEXT, `version_edition` TEXT, `core_pollinterval` INTEGER NOT NULL, `sharing_api_enabled` INTEGER NOT NULL DEFAULT -1, `sharing_public_enabled` INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced` INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced_read_only` INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced_read_write` INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced_public_only` INTEGER NOT NULL DEFAULT -1, `sharing_public_expire_date_enabled` INTEGER NOT NULL DEFAULT -1, `sharing_public_expire_date_days` INTEGER NOT NULL, `sharing_public_expire_date_enforced` INTEGER NOT NULL DEFAULT -1, `sharing_public_send_mail` INTEGER NOT NULL DEFAULT -1, `sharing_public_upload` INTEGER NOT NULL DEFAULT -1, `sharing_public_multiple` INTEGER NOT NULL DEFAULT -1, `supports_upload_only` INTEGER NOT NULL DEFAULT -1, `sharing_user_send_mail` INTEGER NOT NULL DEFAULT -1, `sharing_resharing` INTEGER NOT NULL DEFAULT -1, `sharing_federation_outgoing` INTEGER NOT NULL DEFAULT -1, `sharing_federation_incoming` INTEGER NOT NULL DEFAULT -1, `files_bigfilechunking` INTEGER NOT NULL DEFAULT -1, `files_undelete` INTEGER NOT NULL DEFAULT -1, `files_versioning` INTEGER NOT NULL DEFAULT -1)" + "CREATE TABLE IF NOT EXISTS `${ProviderTableMeta.CAPABILITIES_TABLE_NAME}2` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," + + " `account` TEXT, `version_mayor` INTEGER NOT NULL, `version_minor` INTEGER NOT NULL, `version_micro` INTEGER NOT NULL," + + " `version_string` TEXT, `version_edition` TEXT, `core_pollinterval` INTEGER NOT NULL, `sharing_api_enabled` INTEGER NOT" + + " NULL DEFAULT -1, `sharing_public_enabled` INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced` INTEGER NOT" + + " NULL DEFAULT -1, `sharing_public_password_enforced_read_only` INTEGER NOT NULL DEFAULT -1," + + " `sharing_public_password_enforced_read_write` INTEGER NOT NULL DEFAULT -1," + + " `sharing_public_password_enforced_public_only` INTEGER NOT NULL DEFAULT -1, `sharing_public_expire_date_enabled`" + + " INTEGER NOT NULL DEFAULT -1, `sharing_public_expire_date_days` INTEGER NOT NULL, `sharing_public_expire_date_enforced`" + + " INTEGER NOT NULL DEFAULT -1, `sharing_public_send_mail` INTEGER NOT NULL DEFAULT -1, `sharing_public_upload` INTEGER NOT" + + " NULL DEFAULT -1, `sharing_public_multiple` INTEGER NOT NULL DEFAULT -1, `supports_upload_only` INTEGER NOT NULL DEFAULT" + + " -1, `sharing_user_send_mail` INTEGER NOT NULL DEFAULT -1, `sharing_resharing` INTEGER NOT NULL DEFAULT -1," + + " `sharing_federation_outgoing` INTEGER NOT NULL DEFAULT -1, `sharing_federation_incoming` INTEGER NOT NULL DEFAULT -1," + + " `files_bigfilechunking` INTEGER NOT NULL DEFAULT -1, `files_undelete` INTEGER NOT NULL DEFAULT -1, `files_versioning`" + + " INTEGER NOT NULL DEFAULT -1)" ) execSQL( - "INSERT INTO `${ProviderTableMeta.CAPABILITIES_TABLE_NAME}2` SELECT id, account, version_mayor, version_minor, version_micro, version_string, version_edition, core_pollinterval, IFNULL(sharing_api_enabled, -1), IFNULL(sharing_public_enabled, -1), IFNULL(sharing_public_password_enforced, -1),IFNULL(sharing_public_password_enforced_read_only, -1),IFNULL(sharing_public_password_enforced_read_write, -1),IFNULL(sharing_public_password_enforced_public_only, -1),IFNULL(sharing_public_expire_date_enabled, -1),IFNULL(sharing_public_expire_date_days, 0),IFNULL(sharing_public_expire_date_enforced, -1),IFNULL(sharing_public_send_mail, -1),IFNULL(sharing_public_upload, -1),IFNULL(sharing_public_multiple, -1), IFNULL(supports_upload_only, -1), IFNULL(sharing_user_send_mail, -1),IFNULL(sharing_resharing, -1),IFNULL(sharing_federation_outgoing, -1),IFNULL(sharing_federation_incoming, -1),IFNULL(files_bigfilechunking, -1),IFNULL(files_undelete, -1),IFNULL(files_versioning, -1) FROM ${ProviderTableMeta.CAPABILITIES_TABLE_NAME}" + "INSERT INTO `${ProviderTableMeta.CAPABILITIES_TABLE_NAME}2` SELECT id, account, version_mayor, version_minor, version_micro," + + " version_string, version_edition, core_pollinterval, IFNULL(sharing_api_enabled, -1), IFNULL(sharing_public_enabled, -1)," + + " IFNULL(sharing_public_password_enforced, -1),IFNULL(sharing_public_password_enforced_read_only, -1)," + + "IFNULL(sharing_public_password_enforced_read_write, -1),IFNULL(sharing_public_password_enforced_public_only, -1)," + + "IFNULL(sharing_public_expire_date_enabled, -1),IFNULL(sharing_public_expire_date_days, 0)," + + "IFNULL(sharing_public_expire_date_enforced, -1),IFNULL(sharing_public_send_mail, -1),IFNULL(sharing_public_upload, -1)," + + "IFNULL(sharing_public_multiple, -1), IFNULL(supports_upload_only, -1), IFNULL(sharing_user_send_mail, -1)," + + "IFNULL(sharing_resharing, -1),IFNULL(sharing_federation_outgoing, -1),IFNULL(sharing_federation_incoming, -1)," + + "IFNULL(files_bigfilechunking, -1),IFNULL(files_undelete, -1),IFNULL(files_versioning, -1) " + + "FROM ${ProviderTableMeta.CAPABILITIES_TABLE_NAME}" ) execSQL("DROP TABLE ${ProviderTableMeta.CAPABILITIES_TABLE_NAME}") execSQL("ALTER TABLE ${ProviderTableMeta.CAPABILITIES_TABLE_NAME}2 RENAME TO ${ProviderTableMeta.CAPABILITIES_TABLE_NAME}") diff --git a/owncloudData/src/main/java/com/owncloud/android/data/migrations/Migration_30.kt b/owncloudData/src/main/java/com/owncloud/android/data/migrations/Migration_30.kt index 8cf2ccf6d82..2d700cdec94 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/migrations/Migration_30.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/migrations/Migration_30.kt @@ -27,13 +27,35 @@ val MIGRATION_29_30 = object : Migration(29, 30) { override fun migrate(database: SupportSQLiteDatabase) { database.run { execSQL( - "CREATE TABLE IF NOT EXISTS `${ProviderMeta.ProviderTableMeta.CAPABILITIES_TABLE_NAME}2` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `account` TEXT, `version_mayor` INTEGER NOT NULL, `version_minor` INTEGER NOT NULL, `version_micro` INTEGER NOT NULL, `version_string` TEXT, `version_edition` TEXT, `core_pollinterval` INTEGER NOT NULL, `dav_chunking_version` TEXT NOT NULL DEFAULT '',`sharing_api_enabled` INTEGER NOT NULL DEFAULT -1, `sharing_public_enabled` INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced` INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced_read_only` INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced_read_write` INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced_public_only` INTEGER NOT NULL DEFAULT -1, `sharing_public_expire_date_enabled` INTEGER NOT NULL DEFAULT -1, `sharing_public_expire_date_days` INTEGER NOT NULL, `sharing_public_expire_date_enforced` INTEGER NOT NULL DEFAULT -1, `sharing_public_upload` INTEGER NOT NULL DEFAULT -1, `sharing_public_multiple` INTEGER NOT NULL DEFAULT -1, `supports_upload_only` INTEGER NOT NULL DEFAULT -1, `sharing_resharing` INTEGER NOT NULL DEFAULT -1, `sharing_federation_outgoing` INTEGER NOT NULL DEFAULT -1, `sharing_federation_incoming` INTEGER NOT NULL DEFAULT -1, `files_bigfilechunking` INTEGER NOT NULL DEFAULT -1, `files_undelete` INTEGER NOT NULL DEFAULT -1, `files_versioning` INTEGER NOT NULL DEFAULT -1)" + "CREATE TABLE IF NOT EXISTS `${ProviderMeta.ProviderTableMeta.CAPABILITIES_TABLE_NAME}2` (`id` INTEGER PRIMARY KEY AUTOINCREMENT" + + " NOT NULL, `account` TEXT, `version_mayor` INTEGER NOT NULL, `version_minor` INTEGER NOT NULL, `version_micro` INTEGER" + + " NOT NULL, `version_string` TEXT, `version_edition` TEXT, `core_pollinterval` INTEGER NOT NULL, `dav_chunking_version`" + + " TEXT NOT NULL DEFAULT '',`sharing_api_enabled` INTEGER NOT NULL DEFAULT -1, `sharing_public_enabled` INTEGER NOT NULL" + + " DEFAULT -1, `sharing_public_password_enforced` INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced_read_only`" + + " INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced_read_write` INTEGER NOT NULL DEFAULT -1," + + " `sharing_public_password_enforced_public_only` INTEGER NOT NULL DEFAULT -1, `sharing_public_expire_date_enabled` INTEGER" + + " NOT NULL DEFAULT -1, `sharing_public_expire_date_days` INTEGER NOT NULL, `sharing_public_expire_date_enforced` INTEGER" + + " NOT NULL DEFAULT -1, `sharing_public_upload` INTEGER NOT NULL DEFAULT -1, `sharing_public_multiple` INTEGER NOT NULL" + + " DEFAULT -1, `supports_upload_only` INTEGER NOT NULL DEFAULT -1, `sharing_resharing` INTEGER NOT NULL DEFAULT -1," + + " `sharing_federation_outgoing` INTEGER NOT NULL DEFAULT -1, `sharing_federation_incoming` INTEGER NOT NULL DEFAULT -1," + + " `files_bigfilechunking` INTEGER NOT NULL DEFAULT -1, `files_undelete` INTEGER NOT NULL DEFAULT -1, `files_versioning`" + + " INTEGER NOT NULL DEFAULT -1)" ) execSQL( - "INSERT INTO `${ProviderMeta.ProviderTableMeta.CAPABILITIES_TABLE_NAME}2` SELECT id, account, version_mayor, version_minor, version_micro, version_string, version_edition, core_pollinterval, '', IFNULL(sharing_api_enabled, -1), IFNULL(sharing_public_enabled, -1), IFNULL(sharing_public_password_enforced, -1),IFNULL(sharing_public_password_enforced_read_only, -1),IFNULL(sharing_public_password_enforced_read_write, -1),IFNULL(sharing_public_password_enforced_public_only, -1),IFNULL(sharing_public_expire_date_enabled, -1),IFNULL(sharing_public_expire_date_days, 0),IFNULL(sharing_public_expire_date_enforced, -1), IFNULL(sharing_public_upload, -1),IFNULL(sharing_public_multiple, -1), IFNULL(supports_upload_only, -1), IFNULL(sharing_resharing, -1),IFNULL(sharing_federation_outgoing, -1),IFNULL(sharing_federation_incoming, -1),IFNULL(files_bigfilechunking, -1),IFNULL(files_undelete, -1),IFNULL(files_versioning, -1) FROM ${ProviderMeta.ProviderTableMeta.CAPABILITIES_TABLE_NAME}" + "INSERT INTO `${ProviderMeta.ProviderTableMeta.CAPABILITIES_TABLE_NAME}2` SELECT id, account, version_mayor, version_minor," + + " version_micro, version_string, version_edition, core_pollinterval, '', IFNULL(sharing_api_enabled, -1)," + + " IFNULL(sharing_public_enabled, -1), IFNULL(sharing_public_password_enforced, -1)," + + "IFNULL(sharing_public_password_enforced_read_only, -1),IFNULL(sharing_public_password_enforced_read_write, -1)," + + "IFNULL(sharing_public_password_enforced_public_only, -1),IFNULL(sharing_public_expire_date_enabled, -1)," + + "IFNULL(sharing_public_expire_date_days, 0),IFNULL(sharing_public_expire_date_enforced, -1)," + + " IFNULL(sharing_public_upload, -1),IFNULL(sharing_public_multiple, -1), IFNULL(supports_upload_only, -1)," + + " IFNULL(sharing_resharing, -1),IFNULL(sharing_federation_outgoing, -1),IFNULL(sharing_federation_incoming, -1)," + + "IFNULL(files_bigfilechunking, -1),IFNULL(files_undelete, -1),IFNULL(files_versioning, -1) " + + "FROM ${ProviderMeta.ProviderTableMeta.CAPABILITIES_TABLE_NAME}" ) execSQL("DROP TABLE ${ProviderMeta.ProviderTableMeta.CAPABILITIES_TABLE_NAME}") - execSQL("ALTER TABLE ${ProviderMeta.ProviderTableMeta.CAPABILITIES_TABLE_NAME}2 RENAME TO ${ProviderMeta.ProviderTableMeta.CAPABILITIES_TABLE_NAME}") + execSQL("ALTER TABLE ${ProviderMeta.ProviderTableMeta.CAPABILITIES_TABLE_NAME}2 RENAME TO" + + " ${ProviderMeta.ProviderTableMeta.CAPABILITIES_TABLE_NAME}") } } diff --git a/owncloudData/src/main/java/com/owncloud/android/data/migrations/Migration_32.kt b/owncloudData/src/main/java/com/owncloud/android/data/migrations/Migration_32.kt index da58db549f2..fb9cde4f05d 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/migrations/Migration_32.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/migrations/Migration_32.kt @@ -24,6 +24,7 @@ import androidx.sqlite.db.SupportSQLiteDatabase val MIGRATION_31_32 = object : Migration(31, 32) { override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL("CREATE TABLE IF NOT EXISTS `user_quotas` (`accountName` TEXT NOT NULL, `used` INTEGER NOT NULL, `available` INTEGER NOT NULL, PRIMARY KEY(`accountName`))") + database.execSQL("CREATE TABLE IF NOT EXISTS `user_quotas` (`accountName` TEXT NOT NULL, `used` INTEGER NOT NULL," + + " `available` INTEGER NOT NULL, PRIMARY KEY(`accountName`))") } } diff --git a/owncloudData/src/main/java/com/owncloud/android/data/migrations/Migration_33.kt b/owncloudData/src/main/java/com/owncloud/android/data/migrations/Migration_33.kt index 13ac1365bb0..c14dfa68146 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/migrations/Migration_33.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/migrations/Migration_33.kt @@ -31,7 +31,11 @@ val MIGRATION_32_33 = object : Migration(32, 33) { try { // 1. Create new OCShares table - database.execSQL("CREATE TABLE IF NOT EXISTS `${OCSHARES_TABLE_NAME}2` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `share_type` INTEGER NOT NULL, `share_with` TEXT, `path` TEXT NOT NULL, `permissions` INTEGER NOT NULL, `shared_date` INTEGER NOT NULL, `expiration_date` INTEGER NOT NULL, `token` TEXT, `shared_with_display_name` TEXT, `share_with_additional_info` TEXT, `is_directory` INTEGER NOT NULL, `id_remote_shared` TEXT NOT NULL, `owner_share` TEXT NOT NULL, `name` TEXT, `url` TEXT)") + database.execSQL("CREATE TABLE IF NOT EXISTS `${OCSHARES_TABLE_NAME}2` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," + + " `share_type` INTEGER NOT NULL, `share_with` TEXT, `path` TEXT NOT NULL, `permissions` INTEGER NOT NULL, `shared_date`" + + " INTEGER NOT NULL, `expiration_date` INTEGER NOT NULL, `token` TEXT, `shared_with_display_name` TEXT," + + " `share_with_additional_info` TEXT, `is_directory` INTEGER NOT NULL, `id_remote_shared` TEXT NOT NULL, `owner_share`" + + " TEXT NOT NULL, `name` TEXT, `url` TEXT)") // 2. Get old OCShares and insert them into the new table val cursor = database.query("SELECT * FROM $OCSHARES_TABLE_NAME") diff --git a/owncloudData/src/main/java/com/owncloud/android/data/migrations/Migration_34.kt b/owncloudData/src/main/java/com/owncloud/android/data/migrations/Migration_34.kt index f58b2d83e4a..1c3500eceea 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/migrations/Migration_34.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/migrations/Migration_34.kt @@ -31,7 +31,9 @@ import java.io.File val MIGRATION_33_34 = object : Migration(33, 34) { override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL("CREATE TABLE IF NOT EXISTS `$FOLDER_BACKUP_TABLE_NAME` (`accountName` TEXT NOT NULL, `behavior` TEXT NOT NULL, `sourcePath` TEXT NOT NULL, `uploadPath` TEXT NOT NULL, `wifiOnly` INTEGER NOT NULL, `name` TEXT NOT NULL, `lastSyncTimestamp` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)") + database.execSQL("CREATE TABLE IF NOT EXISTS `$FOLDER_BACKUP_TABLE_NAME` (`accountName` TEXT NOT NULL, `behavior` TEXT NOT NULL," + + " `sourcePath` TEXT NOT NULL, `uploadPath` TEXT NOT NULL, `wifiOnly` INTEGER NOT NULL, `name` TEXT NOT NULL," + + " `lastSyncTimestamp` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)") } } diff --git a/owncloudData/src/main/java/com/owncloud/android/data/migrations/Migration_38.kt b/owncloudData/src/main/java/com/owncloud/android/data/migrations/Migration_38.kt index e52800ade85..87039dedef4 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/migrations/Migration_38.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/migrations/Migration_38.kt @@ -26,7 +26,15 @@ import androidx.sqlite.db.SupportSQLiteDatabase val MIGRATION_37_38 = object : Migration(37, 38) { override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL("CREATE TABLE IF NOT EXISTS `files` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `parentId` INTEGER, `owner` TEXT NOT NULL, `remotePath` TEXT NOT NULL, `remoteId` TEXT, `length` INTEGER NOT NULL, `creationTimestamp` INTEGER, `modificationTimestamp` INTEGER NOT NULL, `mimeType` TEXT NOT NULL, `etag` TEXT, `permissions` TEXT, `privateLink` TEXT, `storagePath` TEXT, `name` TEXT, `treeEtag` TEXT, `keepInSync` INTEGER, `lastSyncDateForData` INTEGER, `fileShareViaLink` INTEGER, `lastSyncDateForProperties` INTEGER, `needsToUpdateThumbnail` INTEGER NOT NULL, `modifiedAtLastSyncForData` INTEGER, `etagInConflict` TEXT, `fileIsDownloading` INTEGER, `sharedWithSharee` INTEGER, `sharedByLink` INTEGER NOT NULL)") - database.execSQL("CREATE TABLE IF NOT EXISTS `transfers` (`localPath` TEXT NOT NULL, `remotePath` TEXT NOT NULL, `accountName` TEXT NOT NULL, `fileSize` INTEGER NOT NULL, `status` INTEGER NOT NULL, `localBehaviour` INTEGER NOT NULL, `forceOverwrite` INTEGER NOT NULL, `transferEndTimestamp` INTEGER, `lastResult` INTEGER, `createdBy` INTEGER NOT NULL, `transferId` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)") + database.execSQL("CREATE TABLE IF NOT EXISTS `files` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `parentId` INTEGER," + + " `owner` TEXT NOT NULL, `remotePath` TEXT NOT NULL, `remoteId` TEXT, `length` INTEGER NOT NULL, `creationTimestamp` INTEGER," + + " `modificationTimestamp` INTEGER NOT NULL, `mimeType` TEXT NOT NULL, `etag` TEXT, `permissions` TEXT, `privateLink` TEXT," + + " `storagePath` TEXT, `name` TEXT, `treeEtag` TEXT, `keepInSync` INTEGER, `lastSyncDateForData` INTEGER, `fileShareViaLink`" + + " INTEGER, `lastSyncDateForProperties` INTEGER, `needsToUpdateThumbnail` INTEGER NOT NULL, `modifiedAtLastSyncForData` INTEGER," + + " `etagInConflict` TEXT, `fileIsDownloading` INTEGER, `sharedWithSharee` INTEGER, `sharedByLink` INTEGER NOT NULL)") + database.execSQL("CREATE TABLE IF NOT EXISTS `transfers` (`localPath` TEXT NOT NULL, `remotePath` TEXT NOT NULL, `accountName`" + + " TEXT NOT NULL, `fileSize` INTEGER NOT NULL, `status` INTEGER NOT NULL, `localBehaviour` INTEGER NOT NULL, `forceOverwrite`" + + " INTEGER NOT NULL, `transferEndTimestamp` INTEGER, `lastResult` INTEGER, `createdBy` INTEGER NOT NULL, `transferId` TEXT," + + " `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)") } } diff --git a/owncloudData/src/test/java/com/owncloud/android/data/files/datasources/implementation/OCLocalFileDataSourceTest.kt b/owncloudData/src/test/java/com/owncloud/android/data/files/datasources/implementation/OCLocalFileDataSourceTest.kt index 55c6766443a..45d50196eb3 100644 --- a/owncloudData/src/test/java/com/owncloud/android/data/files/datasources/implementation/OCLocalFileDataSourceTest.kt +++ b/owncloudData/src/test/java/com/owncloud/android/data/files/datasources/implementation/OCLocalFileDataSourceTest.kt @@ -296,7 +296,9 @@ class OCLocalFileDataSourceTest { @Test fun `getSearchAvailableOfflineFolderContent returns a list of OCFile`() { - every { fileDao.getSearchAvailableOfflineFolderContent(OC_FILE_AVAILABLE_OFFLINE_ENTITY.parentId!!, "test") } returns listOf(OC_FILE_AVAILABLE_OFFLINE_ENTITY) + every { + fileDao.getSearchAvailableOfflineFolderContent(OC_FILE_AVAILABLE_OFFLINE_ENTITY.parentId!!, "test") + } returns listOf(OC_FILE_AVAILABLE_OFFLINE_ENTITY) val result = ocLocalFileDataSource.getSearchAvailableOfflineFolderContent(OC_FILE_AVAILABLE_OFFLINE_ENTITY.parentId!!, "test") @@ -384,7 +386,9 @@ class OCLocalFileDataSourceTest { @Test fun `getSharedByLinkWithSyncInfoForAccountAsFlow returns a Flow with a list of OCFileWithSyncInfo`() = runTest { - val fileAndFileSyncEntitySharedByLink = OC_FILE_AND_FILE_SYNC.copy(file = OC_FILE_ENTITY.copy(sharedByLink = true).apply { this.id = OC_FILE_ENTITY.id }) + val fileAndFileSyncEntitySharedByLink = OC_FILE_AND_FILE_SYNC.copy(file = OC_FILE_ENTITY.copy(sharedByLink = true).apply { + this.id = OC_FILE_ENTITY.id + }) val fileWithSyncInfoSharedByLink = OC_FILE_WITH_SYNC_INFO_AND_SPACE.copy(file = OC_FILE.copy(sharedByLink = true)) @@ -411,14 +415,18 @@ class OCLocalFileDataSourceTest { @Test fun `getFilesWithSyncInfoAvailableOfflineFromAccountAsFlow returns a Flow with a list of OCFileWithSyncInfo`() = runTest { val fileAndFileSyncEntityAvailableOffline = OC_FILE_AND_FILE_SYNC.copy( - file = OC_FILE_ENTITY.copy(availableOfflineStatus = AvailableOfflineStatus.AVAILABLE_OFFLINE.ordinal).apply { this.id = OC_FILE_ENTITY.id } + file = OC_FILE_ENTITY.copy(availableOfflineStatus = AvailableOfflineStatus.AVAILABLE_OFFLINE.ordinal).apply { + this.id = OC_FILE_ENTITY.id + } ) val fileWithSyncInfoAvailableOffline = OC_FILE_WITH_SYNC_INFO_AND_SPACE.copy( file = OC_FILE.copy(availableOfflineStatus = AvailableOfflineStatus.AVAILABLE_OFFLINE) ) - every { fileDao.getFilesWithSyncInfoAvailableOfflineFromAccountAsFlow(OC_ACCOUNT_NAME) } returns flowOf(listOf(fileAndFileSyncEntityAvailableOffline)) + every { + fileDao.getFilesWithSyncInfoAvailableOfflineFromAccountAsFlow(OC_ACCOUNT_NAME) + } returns flowOf(listOf(fileAndFileSyncEntityAvailableOffline)) val result = ocLocalFileDataSource.getFilesWithSyncInfoAvailableOfflineFromAccountAsFlow(OC_ACCOUNT_NAME).first() @@ -428,15 +436,16 @@ class OCLocalFileDataSourceTest { } @Test - fun `getFilesWithSyncInfoAvailableOfflineFromAccountAsFlow returns a Flow with an empty list when DAO returns a Flow with an empty list`() = runTest { - every { fileDao.getFilesWithSyncInfoAvailableOfflineFromAccountAsFlow(OC_ACCOUNT_NAME) } returns flowOf(emptyList()) + fun `getFilesWithSyncInfoAvailableOfflineFromAccountAsFlow returns a Flow with an empty list when DAO returns a Flow with an empty list`() = + runTest { + every { fileDao.getFilesWithSyncInfoAvailableOfflineFromAccountAsFlow(OC_ACCOUNT_NAME) } returns flowOf(emptyList()) - val result = ocLocalFileDataSource.getFilesWithSyncInfoAvailableOfflineFromAccountAsFlow(OC_ACCOUNT_NAME).first() + val result = ocLocalFileDataSource.getFilesWithSyncInfoAvailableOfflineFromAccountAsFlow(OC_ACCOUNT_NAME).first() - assertEquals(emptyList(), result) + assertEquals(emptyList(), result) - verify(exactly = 1) { fileDao.getFilesWithSyncInfoAvailableOfflineFromAccountAsFlow(OC_ACCOUNT_NAME) } - } + verify(exactly = 1) { fileDao.getFilesWithSyncInfoAvailableOfflineFromAccountAsFlow(OC_ACCOUNT_NAME) } + } @Test fun `getFilesAvailableOfflineFromAccount returns a list of OCFile`() { diff --git a/owncloudData/src/test/java/com/owncloud/android/data/files/repository/OCFileRepositoryTest.kt b/owncloudData/src/test/java/com/owncloud/android/data/files/repository/OCFileRepositoryTest.kt index f63a53c41aa..70929cc9cb6 100644 --- a/owncloudData/src/test/java/com/owncloud/android/data/files/repository/OCFileRepositoryTest.kt +++ b/owncloudData/src/test/java/com/owncloud/android/data/files/repository/OCFileRepositoryTest.kt @@ -131,6 +131,7 @@ class OCFileRepositoryTest { } } + @Suppress("MaxLineLength") @Test fun `copyFile returns a list with the OCFile in conflict (the copied OCFile) when replace parameter is empty and expected path already exists`() { every { @@ -341,6 +342,7 @@ class OCFileRepositoryTest { } } + @Suppress("MaxLineLength") @Test fun `copyFile removes target folder locally and throws a ConflictException when replace parameter is empty and expected path doesn't exist but target folder doesn't exist anymore`() { every { @@ -398,6 +400,7 @@ class OCFileRepositoryTest { } } + @Suppress("MaxLineLength") @Test fun `copyFile removes source file locally and throws a FileNotFoundException when replace parameter is empty and expected path doesn't exist but source file doesn't exist anymore`() { every { @@ -548,7 +551,11 @@ class OCFileRepositoryTest { ) } returns OC_FILE_WITH_SPACE_ID - val ocFile = ocFileRepository.getFileByRemotePath(OC_FILE_WITH_SPACE_ID.remotePath, OC_FOLDER_WITH_SPACE_ID.owner, OC_FOLDER_WITH_SPACE_ID.spaceId) + val ocFile = ocFileRepository.getFileByRemotePath( + OC_FILE_WITH_SPACE_ID.remotePath, + OC_FOLDER_WITH_SPACE_ID.owner, + OC_FOLDER_WITH_SPACE_ID.spaceId + ) assertEquals(OC_FILE_WITH_SPACE_ID, ocFile) verify(exactly = 1) { @@ -570,7 +577,11 @@ class OCFileRepositoryTest { ) } returns null - val ocFile = ocFileRepository.getFileByRemotePath(OC_FILE_WITH_SPACE_ID.remotePath, OC_FOLDER_WITH_SPACE_ID.owner, OC_FOLDER_WITH_SPACE_ID.spaceId) + val ocFile = ocFileRepository.getFileByRemotePath( + OC_FILE_WITH_SPACE_ID.remotePath, + OC_FOLDER_WITH_SPACE_ID.owner, + OC_FOLDER_WITH_SPACE_ID.spaceId + ) assertNull(ocFile) verify(exactly = 1) { @@ -1093,6 +1104,7 @@ class OCFileRepositoryTest { } } + @Suppress("MaxLineLength") @Test fun `moveFile returns an empty list with no OCFiles in conflict when replace parameter is empty, expected path doesn't exist and file has a conflict`() { every { @@ -1271,6 +1283,7 @@ class OCFileRepositoryTest { } } + @Suppress("MaxLineLength") @Test fun `moveFile removes target folder locally and throws a ConflictException when replace parameter is empty and expected path doesn't exist but target folder doesn't exist anymore`() { every { @@ -1334,6 +1347,7 @@ class OCFileRepositoryTest { } } + @Suppress("MaxLineLength") @Test fun `moveFile removes source file locally and throws a FileNotFoundException when replace parameter is empty and expected path doesn't exist but source file doesn't exist anymore`() { every { @@ -1449,7 +1463,12 @@ class OCFileRepositoryTest { ) } returns emptyList() - val listOfFiles = ocFileRepository.refreshFolder(OC_PARENT_FOLDER_WITH_SPACE_ID.remotePath, OC_PARENT_FOLDER_WITH_SPACE_ID.owner, OC_PARENT_FOLDER_WITH_SPACE_ID.spaceId, false) + val listOfFiles = ocFileRepository.refreshFolder( + OC_PARENT_FOLDER_WITH_SPACE_ID.remotePath, + OC_PARENT_FOLDER_WITH_SPACE_ID.owner, + OC_PARENT_FOLDER_WITH_SPACE_ID.spaceId, + false + ) assertEquals(emptyList(), listOfFiles) verify(exactly = 1) { @@ -1505,7 +1524,12 @@ class OCFileRepositoryTest { ) } returns emptyList() - val listOfFiles = ocFileRepository.refreshFolder(OC_PARENT_FOLDER_WITH_SPACE_ID.remotePath, OC_PARENT_FOLDER_WITH_SPACE_ID.owner, OC_PARENT_FOLDER_WITH_SPACE_ID.spaceId, false) + val listOfFiles = ocFileRepository.refreshFolder( + OC_PARENT_FOLDER_WITH_SPACE_ID.remotePath, + OC_PARENT_FOLDER_WITH_SPACE_ID.owner, + OC_PARENT_FOLDER_WITH_SPACE_ID.spaceId, + false + ) assertEquals(emptyList(), listOfFiles) verify(exactly = 1) { @@ -1533,6 +1557,7 @@ class OCFileRepositoryTest { } } + @Suppress("MaxLineLength") @Test fun `refreshFolder returns a list with the OCFile that changed when folder and its content already exists in database but needs to be updated`() { val ocParentFolderWithoutSpaceId = OC_PARENT_FOLDER_WITH_SPACE_ID.copy(spaceId = null) @@ -1563,7 +1588,12 @@ class OCFileRepositoryTest { ) } returns listOf(OC_FILE_WITH_SPACE_ID) - val listOfFiles = ocFileRepository.refreshFolder(OC_PARENT_FOLDER_WITH_SPACE_ID.remotePath, OC_PARENT_FOLDER_WITH_SPACE_ID.owner, OC_PARENT_FOLDER_WITH_SPACE_ID.spaceId, true) + val listOfFiles = ocFileRepository.refreshFolder( + OC_PARENT_FOLDER_WITH_SPACE_ID.remotePath, + OC_PARENT_FOLDER_WITH_SPACE_ID.owner, + OC_PARENT_FOLDER_WITH_SPACE_ID.spaceId, + true + ) assertEquals(listOf(OC_FILE_WITH_SPACE_ID), listOfFiles) verify(exactly = 1) { @@ -1591,6 +1621,7 @@ class OCFileRepositoryTest { } } + @Suppress("MaxLineLength") @Test fun `refreshFolder returns an empty list of OCFiles when folder and its content already exists in database updated and the action is not set folder available offline or synchronize`() { val ocParentFolderWithoutSpaceId = OC_PARENT_FOLDER_WITH_SPACE_ID.copy(spaceId = null) @@ -1621,7 +1652,12 @@ class OCFileRepositoryTest { ) } returns emptyList() - val listOfFiles = ocFileRepository.refreshFolder(OC_PARENT_FOLDER_WITH_SPACE_ID.remotePath, OC_PARENT_FOLDER_WITH_SPACE_ID.owner, OC_PARENT_FOLDER_WITH_SPACE_ID.spaceId, false) + val listOfFiles = ocFileRepository.refreshFolder( + OC_PARENT_FOLDER_WITH_SPACE_ID.remotePath, + OC_PARENT_FOLDER_WITH_SPACE_ID.owner, + OC_PARENT_FOLDER_WITH_SPACE_ID.spaceId, + false + ) assertEquals(emptyList(), listOfFiles) verify(exactly = 1) { @@ -1649,11 +1685,13 @@ class OCFileRepositoryTest { } } + @Suppress("MaxLineLength") @Test fun `refreshFolder returns an empty list of OCFiles when folder and its content already exists in database but there are additional files in conflict in local to be removed`() { val ocParentFolderWithoutSpaceId = OC_PARENT_FOLDER_WITH_SPACE_ID.copy(spaceId = null) val ocFileWithoutSpaceId = OC_FILE_WITH_SPACE_ID.copy(spaceId = null) - val additionalOcFile = OC_FILE_WITH_SPACE_ID.copy(id = 300, remotePath = "/Folder/image3.jpt", remoteId = "00000003oci9p7er2hox2", privateLink = "http://server.url/f/70", etagInConflict = etagInConflict) + val additionalOcFile = OC_FILE_WITH_SPACE_ID.copy(id = 300, remotePath = "/Folder/image3.jpt", + remoteId = "00000003oci9p7er2hox2", privateLink = "http://server.url/f/70", etagInConflict = etagInConflict) every { remoteFileDataSource.refreshFolder( remotePath = OC_PARENT_FOLDER_WITH_SPACE_ID.remotePath, @@ -1683,7 +1721,11 @@ class OCFileRepositoryTest { ) } returns emptyList() - val listOfFiles = ocFileRepository.refreshFolder(OC_PARENT_FOLDER_WITH_SPACE_ID.remotePath, OC_PARENT_FOLDER_WITH_SPACE_ID.owner, OC_PARENT_FOLDER_WITH_SPACE_ID.spaceId, false) + val listOfFiles = ocFileRepository.refreshFolder( + OC_PARENT_FOLDER_WITH_SPACE_ID.remotePath, + OC_PARENT_FOLDER_WITH_SPACE_ID.owner, + OC_PARENT_FOLDER_WITH_SPACE_ID.spaceId, + false) assertEquals(emptyList(), listOfFiles) verify(exactly = 1) { diff --git a/owncloudData/src/test/java/com/owncloud/android/data/transfers/datasources/implementation/OCLocalTransferDataSourceTest.kt b/owncloudData/src/test/java/com/owncloud/android/data/transfers/datasources/implementation/OCLocalTransferDataSourceTest.kt index df3bcca4496..a1e36cdc435 100644 --- a/owncloudData/src/test/java/com/owncloud/android/data/transfers/datasources/implementation/OCLocalTransferDataSourceTest.kt +++ b/owncloudData/src/test/java/com/owncloud/android/data/transfers/datasources/implementation/OCLocalTransferDataSourceTest.kt @@ -108,7 +108,12 @@ class OCLocalTransferDataSourceTest { ocLocalTransferDataSource.updateTransferWhenFinished(OC_TRANSFER.id!!, TransferStatus.TRANSFER_SUCCEEDED, timestamp, TransferResult.UPLOADED) verify(exactly = 1) { - transferDao.updateTransferWhenFinished(OC_TRANSFER.id!!, TransferStatus.TRANSFER_SUCCEEDED.value, timestamp, TransferResult.UPLOADED.value) + transferDao.updateTransferWhenFinished( + OC_TRANSFER.id!!, + TransferStatus.TRANSFER_SUCCEEDED.value, + timestamp, + TransferResult.UPLOADED.value + ) } } diff --git a/owncloudDomain/build.gradle b/owncloudDomain/build.gradle index 93d50153250..e547d68bcd2 100644 --- a/owncloudDomain/build.gradle +++ b/owncloudDomain/build.gradle @@ -45,4 +45,8 @@ dependencies { testImplementation libs.junit4 testImplementation libs.kotlinx.coroutines.test testImplementation libs.mockk + + // Detekt + detektPlugins libs.detekt.formatting + detektPlugins libs.detekt.libraries } diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/UseCaseResult.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/UseCaseResult.kt index 3b836ccfe17..575bc6f7b26 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/UseCaseResult.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/UseCaseResult.kt @@ -27,14 +27,16 @@ sealed class UseCaseResult { val isError get() = this is Error fun getDataOrNull(): T? = - when (this) { - is Success -> data - else -> null + if (this is Success) { + data + } else { + null } fun getThrowableOrNull(): Throwable? = - when (this) { - is Error -> throwable - else -> null + if (this is Error) { + throwable + } else { + null } } diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/GetFilesAvailableOfflineFromAccountAsStreamUseCase.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/GetFilesAvailableOfflineFromAccountAsStreamUseCase.kt index a100b186325..a6e71d852af 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/GetFilesAvailableOfflineFromAccountAsStreamUseCase.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/GetFilesAvailableOfflineFromAccountAsStreamUseCase.kt @@ -26,7 +26,8 @@ class GetFilesAvailableOfflineFromAccountAsStreamUseCase( private val fileRepository: FileRepository ) : BaseUseCase>, GetFilesAvailableOfflineFromAccountAsStreamUseCase.Params>() { - override fun run(params: Params): Flow> = fileRepository.getFilesWithSyncInfoAvailableOfflineFromAccountAsFlow(params.owner) + override fun run(params: Params): Flow> = + fileRepository.getFilesWithSyncInfoAvailableOfflineFromAccountAsFlow(params.owner) data class Params( val owner: String diff --git a/owncloudDomain/src/test/java/com/owncloud/android/domain/server/usecases/GetServerInfoAsyncUseCaseTest.kt b/owncloudDomain/src/test/java/com/owncloud/android/domain/server/usecases/GetServerInfoAsyncUseCaseTest.kt index f39c7977b7d..c266e4e4ff7 100644 --- a/owncloudDomain/src/test/java/com/owncloud/android/domain/server/usecases/GetServerInfoAsyncUseCaseTest.kt +++ b/owncloudDomain/src/test/java/com/owncloud/android/domain/server/usecases/GetServerInfoAsyncUseCaseTest.kt @@ -78,6 +78,7 @@ class GetServerInfoAsyncUseCaseTest { verify(exactly = 1) { repository.getServerInfo(useCaseParams.serverPath, false, false) } } + @Suppress("MaxLineLength") @Test fun `Should throw SSLErrorException when secureConnectionEnforced is true and ServerInfoRepository returns ServerInfo with isSecureConnection returning false`() { every { repository.getServerInfo(useCaseParams.serverPath, false, false) } returns OC_INSECURE_SERVER_INFO_BASIC_AUTH @@ -90,6 +91,7 @@ class GetServerInfoAsyncUseCaseTest { verify(exactly = 1) { repository.getServerInfo(useCaseParams.serverPath, false, false) } } + @Suppress("MaxLineLength") @Test fun `Should work correctly when secureConnectionEnforced is true and ServerInfoRepository returns ServerInfo with isSecureConnection returning true`() { every { repository.getServerInfo(useCaseParams.serverPath, false, false) } returns OC_SECURE_SERVER_INFO_BASIC_AUTH diff --git a/owncloudTestUtil/build.gradle b/owncloudTestUtil/build.gradle index 9e03fd7fc45..52a902d188f 100644 --- a/owncloudTestUtil/build.gradle +++ b/owncloudTestUtil/build.gradle @@ -33,4 +33,8 @@ dependencies { implementation project(':owncloudComLibrary') implementation libs.kotlin.stdlib implementation libs.androidx.lifecycle.livedata.ktx + + // Detekt + detektPlugins libs.detekt.formatting + detektPlugins libs.detekt.libraries } diff --git a/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCSpace.kt b/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCSpace.kt index 26ff75cd45a..36c0d6ccbae 100644 --- a/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCSpace.kt +++ b/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCSpace.kt @@ -55,7 +55,8 @@ val OC_SPACE_SPECIAL_README = SpaceSpecial( specialFolder = SpaceSpecialFolder( name = "readme" ), - webDavUrl = "https://server.com/dav/spaces/8871f4f3-fc6f-4a66-8bed-62f175f76f38$0aa0e03c-ec36-498c-bb9f-857315568199%210aa0e03c-ec36-498c-bb9f-857315568199/.space/readme.md" + webDavUrl = "https://server.com/dav/spaces/8871f4f3-fc6f-4a66-8bed-62f175f76f38$" + + "0aa0e03c-ec36-498c-bb9f-857315568199%210aa0e03c-ec36-498c-bb9f-857315568199/.space/readme.md" ) val OC_SPACE_SPECIAL_IMAGE = SpaceSpecial( @@ -70,7 +71,8 @@ val OC_SPACE_SPECIAL_IMAGE = SpaceSpecial( specialFolder = SpaceSpecialFolder( name = "image" ), - webDavUrl = "https://server.com/dav/spaces/8871f4f3-fc6f-4a66-8bed-62f175f76f38$0aa0e03c-ec36-498c-bb9f-857315568199%210aa0e03c-ec36-498c-bb9f-857315568199/.space/image.jpg" + webDavUrl = "https://server.com/dav/spaces/8871f4f3-fc6f-4a66-8bed-62f175f76f38$" + + "0aa0e03c-ec36-498c-bb9f-857315568199%210aa0e03c-ec36-498c-bb9f-857315568199/.space/image.jpg" ) val OC_SPACE_PROJECT_WITH_IMAGE = OCSpace(