diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 84906f84fb76..e4f29e553267 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:noble@sha256:562456a05a0dbd62a671c1854868862a4687bf979a96d48ae8e766642cd911e8 +FROM ubuntu:noble@sha256:3f85b7caad41a95462cf5b787d8a04604c8262cdcdf9a472b8c52ef83375fe15 ARG DEBIAN_FRONTEND=noninteractive ENV ANDROID_HOME=/usr/lib/android-sdk diff --git a/.github/workflows/analysis.yml b/.github/workflows/analysis.yml index 47b48ecad89f..5e5a2077e025 100644 --- a/.github/workflows/analysis.yml +++ b/.github/workflows/analysis.yml @@ -44,7 +44,7 @@ jobs: echo "repo=${{ github.event.pull_request.head.repo.full_name }}" } >> "$GITHUB_OUTPUT" fi - - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: repository: ${{ steps.get-vars.outputs.repo }} ref: ${{ steps.get-vars.outputs.branch }} diff --git a/.github/workflows/assembleFlavors.yml b/.github/workflows/assembleFlavors.yml index 4e638204441f..5dcda08bfd0d 100644 --- a/.github/workflows/assembleFlavors.yml +++ b/.github/workflows/assembleFlavors.yml @@ -22,7 +22,7 @@ jobs: matrix: flavor: [ Generic, Gplay, Huawei ] steps: - - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 - name: set up JDK 17 uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1 with: diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 1cd9dcc07811..95afec2c3512 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -22,7 +22,7 @@ jobs: matrix: task: [ detekt, spotlessKotlinCheck ] steps: - - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 - name: Set up JDK 17 uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1 with: diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 2564164fb4da..f08f8eb7ee32 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -33,13 +33,13 @@ jobs: language: [ 'java' ] steps: - name: Checkout repository - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - name: Set Swap Space uses: pierotofy/set-swap-space@49819abfb41bd9b44fb781159c033dba90353a7c # v1.0 with: swap-size-gb: 10 - name: Initialize CodeQL - uses: github/codeql-action/init@d39d31e687223d841ef683f52467bd88e9b21c14 # v3.25.3 + uses: github/codeql-action/init@f079b8493333aace61c81488f8bd40919487bd9f # v3.25.7 with: languages: ${{ matrix.language }} - name: Set up JDK 17 @@ -53,4 +53,4 @@ jobs: echo "org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError" > "$HOME/.gradle/gradle.properties" ./gradlew assembleDebug - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@d39d31e687223d841ef683f52467bd88e9b21c14 # v3.25.3 + uses: github/codeql-action/analyze@f079b8493333aace61c81488f8bd40919487bd9f # v3.25.7 diff --git a/.github/workflows/command-rebase.yml b/.github/workflows/command-rebase.yml index c2ae95dd49ae..ccfeb1f9a1e0 100644 --- a/.github/workflows/command-rebase.yml +++ b/.github/workflows/command-rebase.yml @@ -34,7 +34,7 @@ jobs: reaction-type: "+1" - name: Checkout the latest code - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 with: fetch-depth: 0 token: ${{ secrets.COMMAND_BOT_PAT }} diff --git a/.github/workflows/detectWrongSettings.yml b/.github/workflows/detectWrongSettings.yml index 1cf0e4e2257d..75f59c4fc4b0 100644 --- a/.github/workflows/detectWrongSettings.yml +++ b/.github/workflows/detectWrongSettings.yml @@ -20,7 +20,7 @@ jobs: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 - name: Set up JDK 17 uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1 with: diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml index b72bf62069be..aba8a0e7940b 100644 --- a/.github/workflows/gradle-wrapper-validation.yml +++ b/.github/workflows/gradle-wrapper-validation.yml @@ -25,5 +25,5 @@ jobs: name: "Validation" runs-on: ubuntu-latest steps: - - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - uses: gradle/wrapper-validation-action@216d1ad2b3710bf005dc39237337b9673fd8fcd5 # v3.3.2 diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml index 11b1457afb5c..c8eb858204ab 100644 --- a/.github/workflows/qa.yml +++ b/.github/workflows/qa.yml @@ -23,7 +23,7 @@ jobs: - name: Check if secrets are available run: echo "::set-output name=ok::${{ secrets.KS_PASS != '' }}" id: check-secrets - - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 if: ${{ steps.check-secrets.outputs.ok == 'true' }} - name: set up JDK 17 uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1 diff --git a/.github/workflows/reuse.yml b/.github/workflows/reuse.yml index 57231dcf6cec..fa62e61e7406 100644 --- a/.github/workflows/reuse.yml +++ b/.github/workflows/reuse.yml @@ -9,12 +9,12 @@ name: REUSE Compliance Check -on: [pull_request] +on: pull_request jobs: - test: + reuse-compliance-check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 - name: REUSE Compliance Check uses: fsfe/reuse-action@a46482ca367aef4454a87620aa37c2be4b2f8106 # v3.0.0 diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 4507aadd13bc..497616d34928 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -29,12 +29,12 @@ jobs: steps: - name: "Checkout code" - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: persist-credentials: false - name: "Run analysis" - uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1 + uses: ossf/scorecard-action@dc50aa9510b46c811795eb24b2f1ba02a914e534 # v2.3.3 with: results_file: results.sarif results_format: sarif @@ -42,6 +42,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@d39d31e687223d841ef683f52467bd88e9b21c14 # v3.25.3 + uses: github/codeql-action/upload-sarif@f079b8493333aace61c81488f8bd40919487bd9f # v3.25.7 with: sarif_file: results.sarif diff --git a/.github/workflows/screenShotTest.yml b/.github/workflows/screenShotTest.yml index caabdcae9ae5..f1faa2e4a936 100644 --- a/.github/workflows/screenShotTest.yml +++ b/.github/workflows/screenShotTest.yml @@ -25,7 +25,7 @@ jobs: color: [ blue ] api-level: [ 27 ] steps: - - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 - name: Gradle cache uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 0cc139d3b5ec..6bf699b1c396 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -21,7 +21,7 @@ jobs: test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 - name: Set up JDK 17 uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1 with: @@ -42,7 +42,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: scripts/uploadReport.sh "${{ secrets.LOG_USERNAME }}" "${{ secrets.LOG_PASSWORD }}" ${{github.event.number}} "test" "Unit" ${{github.event.number}} - name: Upload coverage to codecov - uses: codecov/codecov-action@84508663e988701840491b86de86b666e8a86bed # v4.3.0 + uses: codecov/codecov-action@5ecb98a3c6b747ed38dc09f787459979aebb39be # v4.3.1 with: token: ${{ secrets.CODECOV_TOKEN }} flags: unit diff --git a/Gemfile.lock b/Gemfile.lock index 234c43e5425c..1a85fa6eaed2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -10,17 +10,17 @@ GEM artifactory (3.0.17) atomos (0.1.3) aws-eventstream (1.3.0) - aws-partitions (1.913.0) - aws-sdk-core (3.191.6) + aws-partitions (1.937.0) + aws-sdk-core (3.196.1) aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.651.0) aws-sigv4 (~> 1.8) jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.78.0) - aws-sdk-core (~> 3, >= 3.191.0) + aws-sdk-kms (1.82.0) + aws-sdk-core (~> 3, >= 3.193.0) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.146.1) - aws-sdk-core (~> 3, >= 3.191.0) + aws-sdk-s3 (1.151.0) + aws-sdk-core (~> 3, >= 3.194.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.8) aws-sigv4 (1.8.0) @@ -110,7 +110,7 @@ GEM xcodeproj (>= 1.13.0, < 2.0.0) xcpretty (~> 0.3.0) xcpretty-travis-formatter (>= 0.0.3, < 2.0.0) - fastlane-plugin-huawei_appgallery_connect (1.0.28) + fastlane-plugin-huawei_appgallery_connect (1.0.30) cgi gh_inspector (1.1.3) google-apis-androidpublisher_v3 (0.54.0) @@ -160,11 +160,11 @@ GEM mini_magick (4.12.0) mini_mime (1.1.5) multi_json (1.15.0) - multipart-post (2.4.0) + multipart-post (2.4.1) nanaimo (0.3.0) naturally (2.2.1) nkf (0.2.0) - optparse (0.4.0) + optparse (0.5.0) os (1.1.4) plist (3.7.1) public_suffix (5.0.5) @@ -174,7 +174,8 @@ GEM trailblazer-option (>= 0.1.1, < 0.2.0) uber (< 0.2.0) retriable (3.1.2) - rexml (3.2.6) + rexml (3.2.8) + strscan (>= 3.0.9) rouge (2.0.7) ruby2_keywords (0.0.5) rubyzip (2.3.2) @@ -187,6 +188,7 @@ GEM simctl (1.6.10) CFPropertyList naturally + strscan (3.1.0) terminal-notifier (2.0.0) terminal-table (3.0.2) unicode-display_width (>= 1.1.1, < 3) diff --git a/app/build.gradle b/app/build.gradle index e7191e83333d..e5472ec611f3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2024 Tobias Kaminsky * SPDX-FileCopyrightText: 2024 Andy Scherzinger * SPDX-FileCopyrightText: 2022 Álvaro Brey Vilas @@ -16,7 +16,7 @@ import org.gradle.internal.jvm.Jvm buildscript { dependencies { classpath "com.android.tools.build:gradle:$androidPluginVersion" - classpath 'com.github.spotbugs.snom:spotbugs-gradle-plugin:6.0.13' + classpath 'com.github.spotbugs.snom:spotbugs-gradle-plugin:6.0.14' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.23.6" classpath "commons-httpclient:commons-httpclient:3.1@jar" // remove after entire switch to lib v2 @@ -28,14 +28,15 @@ buildscript { } plugins { + id "org.jetbrains.kotlin.plugin.compose" version "2.0.0" id "com.diffplug.spotless" version "6.20.0" - id 'com.google.devtools.ksp' version '1.9.23-1.0.20' apply false + id "org.jetbrains.kotlin.kapt" version "2.0.0" + id 'com.google.devtools.ksp' version '2.0.0-1.0.21' apply false } apply plugin: 'com.android.application' apply plugin: 'kotlin-android' -apply plugin: 'kotlin-kapt' apply plugin: 'kotlin-parcelize' apply plugin: 'checkstyle' apply plugin: 'pmd' @@ -262,10 +263,6 @@ android { // Adds exported schema location as test app assets. androidTest.assets.srcDirs += files("$projectDir/schemas".toString()) } - - composeOptions { - kotlinCompilerExtensionVersion = "1.5.12" - } } dependencies { @@ -276,7 +273,7 @@ dependencies { } // Jetpack Compose - implementation(platform("androidx.compose:compose-bom:2024.04.01")) + implementation(platform("androidx.compose:compose-bom:2024.05.00")) implementation("androidx.compose.ui:ui") implementation("androidx.compose.ui:ui-graphics") implementation("androidx.compose.material3:material3") @@ -292,19 +289,19 @@ dependencies { implementation 'com.google.android.material:material:1.11.0' implementation 'com.jakewharton:disklrucache:2.0.2' implementation "androidx.appcompat:appcompat:$appCompatVersion" - implementation 'androidx.webkit:webkit:1.10.0' + implementation 'androidx.webkit:webkit:1.11.0' implementation 'androidx.cardview:cardview:1.0.0' implementation 'androidx.exifinterface:exifinterface:1.3.7' implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0" implementation "androidx.work:work-runtime:$workRuntime" implementation "androidx.work:work-runtime-ktx:$workRuntime" - implementation "androidx.fragment:fragment-ktx:1.6.2" + implementation "androidx.fragment:fragment-ktx:1.7.1" implementation 'com.github.albfernandez:juniversalchardet:2.0.3' // need this version for Android <7 compileOnly 'com.google.code.findbugs:annotations:3.0.1u2' implementation 'commons-io:commons-io:2.16.1' implementation 'org.greenrobot:eventbus:3.3.1' implementation 'com.googlecode.ez-vcard:ez-vcard:0.12.1' - implementation 'org.lukhnos:nnio:0.3' + implementation 'org.lukhnos:nnio:0.3.1' implementation 'org.bouncycastle:bcpkix-jdk18on:1.78.1' implementation 'com.google.code.gson:gson:2.10.1' implementation 'com.github.nextcloud-deps:sectioned-recyclerview:0.6.1' diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.activity.UploadFilesActivityIT_noneSelected.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.activity.UploadFilesActivityIT_noneSelected.png index fe2bf2c55203..3e5fbdf6e4d8 100644 Binary files a/app/screenshots/gplay/debug/com.owncloud.android.ui.activity.UploadFilesActivityIT_noneSelected.png and b/app/screenshots/gplay/debug/com.owncloud.android.ui.activity.UploadFilesActivityIT_noneSelected.png differ diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testBottomSheet.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testBottomSheet.png index b85fe31782e3..0b64a27ec73e 100644 Binary files a/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testBottomSheet.png and b/app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testBottomSheet.png differ diff --git a/app/src/androidTest/java/com/nextcloud/client/assistant/AssistantRepositoryTests.kt b/app/src/androidTest/java/com/nextcloud/client/assistant/AssistantRepositoryTests.kt index 6e0a9c7ed5fb..aa3eefb8c2a3 100644 --- a/app/src/androidTest/java/com/nextcloud/client/assistant/AssistantRepositoryTests.kt +++ b/app/src/androidTest/java/com/nextcloud/client/assistant/AssistantRepositoryTests.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2024 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/androidTest/java/com/nextcloud/extensions/BundleExtensionTests.kt b/app/src/androidTest/java/com/nextcloud/extensions/BundleExtensionTests.kt index 2b0fb9353e9b..853a0e4a17cc 100644 --- a/app/src/androidTest/java/com/nextcloud/extensions/BundleExtensionTests.kt +++ b/app/src/androidTest/java/com/nextcloud/extensions/BundleExtensionTests.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2024 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/androidTest/java/com/nextcloud/extensions/IntentExtensionTests.kt b/app/src/androidTest/java/com/nextcloud/extensions/IntentExtensionTests.kt index 8653b863422e..6fa385bdef63 100644 --- a/app/src/androidTest/java/com/nextcloud/extensions/IntentExtensionTests.kt +++ b/app/src/androidTest/java/com/nextcloud/extensions/IntentExtensionTests.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2024 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/androidTest/java/com/nextcloud/test/model/TestModels.kt b/app/src/androidTest/java/com/nextcloud/test/model/TestModels.kt index 1a1741806b28..4c99499a3aa6 100644 --- a/app/src/androidTest/java/com/nextcloud/test/model/TestModels.kt +++ b/app/src/androidTest/java/com/nextcloud/test/model/TestModels.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2024 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/androidTest/java/com/nextcloud/utils/AppConfigManagerTests.kt b/app/src/androidTest/java/com/nextcloud/utils/AppConfigManagerTests.kt new file mode 100644 index 000000000000..8392ae549fd4 --- /dev/null +++ b/app/src/androidTest/java/com/nextcloud/utils/AppConfigManagerTests.kt @@ -0,0 +1,86 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils + +import android.os.Bundle +import com.owncloud.android.AbstractIT +import com.owncloud.android.lib.common.OwnCloudClientManagerFactory +import com.owncloud.android.utils.appConfig.AppConfigKeys +import com.owncloud.android.utils.appConfig.AppConfigManager +import org.junit.AfterClass +import org.junit.Test + +class AppConfigManagerTests : AbstractIT() { + + private val testBaseUrl = "nextcloud.cloud.cloud" + private val testProxyHost = "nextcloud.cloud.cloud.com" + + @Suppress("MagicNumber") + private val testProxyPort = 8800 + + @Test + fun testSetProxyConfigWhenGivenClientBrandedPlusAndCorrectBundleDataProxyConfigurationShouldSet() { + val proxySetting = Bundle().apply { + putString(AppConfigKeys.ProxyHost.key, testProxyHost) + putInt(AppConfigKeys.ProxyPort.key, testProxyPort) + } + + AppConfigManager(targetContext, proxySetting).run { + setProxyConfig(true) + } + + val proxyHost = OwnCloudClientManagerFactory.getProxyHost() + val proxyPort = OwnCloudClientManagerFactory.getProxyPort() + + assert(proxyHost.equals(testProxyHost)) + assert(proxyPort == testProxyPort) + } + + @Test + fun testSetProxyConfigWhenGivenClientNotBrandedPlusAndCorrectBundleDataProxyConfigurationShouldNotSet() { + val proxySetting = Bundle().apply { + putString(AppConfigKeys.ProxyHost.key, testProxyHost) + putInt(AppConfigKeys.ProxyPort.key, testProxyPort) + } + + AppConfigManager(targetContext, proxySetting).run { + setProxyConfig(false) + } + + val proxyHost = OwnCloudClientManagerFactory.getProxyHost() + val proxyPort = OwnCloudClientManagerFactory.getProxyPort() + + assert(proxyHost.equals("")) + assert(proxyPort == -1) + } + + @Test + fun testGetBaseUrlConfigWhenGivenClientBrandedPlusAndCorrectBundleDataBaseUrlConfigurationShouldSet() { + val baseUrlConfig = Bundle().apply { + putString(AppConfigKeys.BaseUrl.key, testBaseUrl) + } + val sut = AppConfigManager(targetContext, baseUrlConfig) + assert(!sut.getBaseUrl(true).isNullOrEmpty()) + } + + @Test + fun testGetBaseUrlConfigWhenGivenClientBrandedPlusAndBrokenBundleDataBaseUrlConfigurationShouldNotSet() { + val baseUrlConfig = Bundle() + val sut = AppConfigManager(targetContext, baseUrlConfig) + assert(sut.getBaseUrl(true).isNullOrEmpty()) + } + + companion object { + @JvmStatic + @AfterClass + fun tearDown() { + OwnCloudClientManagerFactory.setProxyHost("") + OwnCloudClientManagerFactory.setProxyPort(-1) + } + } +} diff --git a/app/src/androidTest/java/com/owncloud/android/EncryptionIT.kt b/app/src/androidTest/java/com/owncloud/android/EncryptionIT.kt index a02b57fb57b0..ec53f46b1b65 100644 --- a/app/src/androidTest/java/com/owncloud/android/EncryptionIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/EncryptionIT.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Your Name + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/androidTest/java/com/owncloud/android/datamodel/OCFileIconTests.kt b/app/src/androidTest/java/com/owncloud/android/datamodel/OCFileIconTests.kt index 729d5d69ba9f..59167c69d76f 100644 --- a/app/src/androidTest/java/com/owncloud/android/datamodel/OCFileIconTests.kt +++ b/app/src/androidTest/java/com/owncloud/android/datamodel/OCFileIconTests.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/androidTest/java/com/owncloud/android/utils/DrawableUtilTests.kt b/app/src/androidTest/java/com/owncloud/android/utils/DrawableUtilTests.kt index 02a1e7d4c9f8..5958ff4ef682 100644 --- a/app/src/androidTest/java/com/owncloud/android/utils/DrawableUtilTests.kt +++ b/app/src/androidTest/java/com/owncloud/android/utils/DrawableUtilTests.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 131cd3aa78b3..af9fd5fae0f3 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,6 +2,7 @@ @@ -126,6 +127,10 @@ android:usesCleartextTraffic="true" tools:ignore="UnusedAttribute" tools:replace="android:allowBackup"> + + + @@ -227,6 +232,8 @@ + + + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2024 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt index 0f170b264e75..8295cc5146e0 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2024 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/nextcloud/client/assistant/component/AddTaskAlertDialog.kt b/app/src/main/java/com/nextcloud/client/assistant/component/AddTaskAlertDialog.kt index 3ab71ef3f9ac..9dfdae04650b 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/component/AddTaskAlertDialog.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/component/AddTaskAlertDialog.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2024 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/nextcloud/client/assistant/component/CenterText.kt b/app/src/main/java/com/nextcloud/client/assistant/component/CenterText.kt index a21f8aea9f9f..f6a23f2f7ee2 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/component/CenterText.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/component/CenterText.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2024 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/nextcloud/client/assistant/extensions/TaskExtensions.kt b/app/src/main/java/com/nextcloud/client/assistant/extensions/TaskExtensions.kt index f2ff257800bd..4df63f69a902 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/extensions/TaskExtensions.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/extensions/TaskExtensions.kt @@ -3,7 +3,7 @@ * * SPDX-FileCopyrightText: 2023-2024 Nextcloud GmbH and Nextcloud * contributors - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-License-Identifier: MIT */ diff --git a/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantMockRepository.kt b/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantMockRepository.kt index ec03df526a4a..4d9a87a3e544 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantMockRepository.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantMockRepository.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2024 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepository.kt b/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepository.kt index 162fc3e9baf1..bdd1f2412615 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepository.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepository.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2024 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepositoryType.kt b/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepositoryType.kt index f96679802f1e..58acaf4d09fc 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepositoryType.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepositoryType.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2024 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/nextcloud/client/assistant/task/TaskStatus.kt b/app/src/main/java/com/nextcloud/client/assistant/task/TaskStatus.kt index 5f45dac4e128..f3de443b4202 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/task/TaskStatus.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/task/TaskStatus.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Your Name + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/nextcloud/client/assistant/task/TaskView.kt b/app/src/main/java/com/nextcloud/client/assistant/task/TaskView.kt index d52b907a9345..acdc298aa165 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/task/TaskView.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/task/TaskView.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Your Name + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.assistant.task diff --git a/app/src/main/java/com/nextcloud/client/assistant/taskDetail/TaskDetailBottomSheet.kt b/app/src/main/java/com/nextcloud/client/assistant/taskDetail/TaskDetailBottomSheet.kt index 0fac596f6eb7..d73fae93717d 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/taskDetail/TaskDetailBottomSheet.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/taskDetail/TaskDetailBottomSheet.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Your Name + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/nextcloud/client/assistant/taskTypes/TaskTypesRow.kt b/app/src/main/java/com/nextcloud/client/assistant/taskTypes/TaskTypesRow.kt index faf8a29f3bca..db7e2ac2ebb1 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/taskTypes/TaskTypesRow.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/taskTypes/TaskTypesRow.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2024 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/nextcloud/client/files/DeepLinkConstants.kt b/app/src/main/java/com/nextcloud/client/files/DeepLinkConstants.kt new file mode 100644 index 000000000000..cc4fbdc4966c --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/files/DeepLinkConstants.kt @@ -0,0 +1,35 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 TSI-mc + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.client.files + +import com.owncloud.android.R + +enum class DeepLinkConstants(val route: String, val navId: Int) { + OPEN_FILES("openFiles", R.id.nav_all_files), + OPEN_FAVORITES("openFavorites", R.id.nav_favorites), + OPEN_MEDIA("openMedia", R.id.nav_gallery), + OPEN_SHARED("openShared", R.id.nav_shared), + OPEN_OFFLINE("openOffline", R.id.nav_on_device), + OPEN_NOTIFICATIONS("openNotifications", R.id.nav_notifications), + OPEN_DELETED("openDeleted", R.id.nav_trashbin), + OPEN_SETTINGS("openSettings", R.id.nav_settings), + + // Special case, handled separately + OPEN_AUTO_UPLOAD("openAutoUpload", -1), + OPEN_EXTERNAL_URL("openUrl", -1), + ACTION_CREATE_NEW("createNew", -1), + ACTION_APP_UPDATE("checkAppUpdate", -1); + + companion object { + fun fromPath(path: String?): DeepLinkConstants? { + return entries.find { it.route == path } + } + + val navigationPaths = entries.map { it.route } + } +} diff --git a/app/src/main/java/com/nextcloud/client/files/DeepLinkHandler.kt b/app/src/main/java/com/nextcloud/client/files/DeepLinkHandler.kt index a621abcded41..3b1209780ac2 100644 --- a/app/src/main/java/com/nextcloud/client/files/DeepLinkHandler.kt +++ b/app/src/main/java/com/nextcloud/client/files/DeepLinkHandler.kt @@ -33,6 +33,9 @@ class DeepLinkHandler( val BASE_URL_GROUP_INDEX = 1 val INDEX_PATH_GROUP_INDEX = 2 val FILE_ID_GROUP_INDEX = 3 + + fun isDeepLinkTypeIsNavigation(deepLinkUrl: String): Boolean = + DeepLinkConstants.navigationPaths.any { deepLinkUrl.endsWith(it) } } /** diff --git a/app/src/main/java/com/nextcloud/client/files/Registry.kt b/app/src/main/java/com/nextcloud/client/files/Registry.kt index 3c589b790bfd..5c6b719c13be 100644 --- a/app/src/main/java/com/nextcloud/client/files/Registry.kt +++ b/app/src/main/java/com/nextcloud/client/files/Registry.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/nextcloud/client/files/Request.kt b/app/src/main/java/com/nextcloud/client/files/Request.kt index cb871ff5f53a..5d3dcc71571f 100644 --- a/app/src/main/java/com/nextcloud/client/files/Request.kt +++ b/app/src/main/java/com/nextcloud/client/files/Request.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManager.kt b/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManager.kt index a4f825ad2492..ae6d35a5c39a 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManager.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManager.kt @@ -119,9 +119,13 @@ interface BackgroundJobManager { fun startImmediateFilesExportJob(files: Collection): LiveData - fun schedulePeriodicFilesSyncJob() + fun schedulePeriodicFilesSyncJob(syncedFolderID: Long) + /** + * Immediately start File Sync job for given syncFolderID. + */ fun startImmediateFilesSyncJob( + syncedFolderID: Long, overridePowerSaving: Boolean = false, changedFiles: Array = arrayOf() ) @@ -163,4 +167,5 @@ interface BackgroundJobManager { fun cancelAllJobs() fun schedulePeriodicHealthStatus() fun startHealthStatus() + fun bothFilesSyncJobsRunning(syncedFolderID: Long): Boolean } diff --git a/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt b/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt index c2117074987b..5c04ad9141b7 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt @@ -28,6 +28,7 @@ import com.nextcloud.client.documentscan.GeneratePdfFromImagesWork import com.nextcloud.client.jobs.download.FileDownloadWorker import com.nextcloud.client.jobs.upload.FileUploadWorker import com.nextcloud.client.preferences.AppPreferences +import com.nextcloud.utils.extensions.isWorkRunning import com.nextcloud.utils.extensions.isWorkScheduled import com.owncloud.android.datamodel.OCFile import com.owncloud.android.operations.DownloadType @@ -403,32 +404,55 @@ internal class BackgroundJobManagerImpl( workManager.cancelJob(JOB_PERIODIC_CALENDAR_BACKUP, user) } - override fun schedulePeriodicFilesSyncJob() { + override fun bothFilesSyncJobsRunning(syncedFolderID: Long): Boolean { + return workManager.isWorkRunning(JOB_PERIODIC_FILES_SYNC + "_" + syncedFolderID) && + workManager.isWorkRunning(JOB_IMMEDIATE_FILES_SYNC + "_" + syncedFolderID) + } + + override fun schedulePeriodicFilesSyncJob( + syncedFolderID: Long + ) { + val arguments = Data.Builder() + .putLong(FilesSyncWork.SYNCED_FOLDER_ID, syncedFolderID) + .build() + val request = periodicRequestBuilder( jobClass = FilesSyncWork::class, - jobName = JOB_PERIODIC_FILES_SYNC, + jobName = JOB_PERIODIC_FILES_SYNC + "_" + syncedFolderID, intervalMins = DEFAULT_PERIODIC_JOB_INTERVAL_MINUTES - ).build() - workManager.enqueueUniquePeriodicWork(JOB_PERIODIC_FILES_SYNC, ExistingPeriodicWorkPolicy.REPLACE, request) + ) + .setInputData(arguments) + .build() + workManager.enqueueUniquePeriodicWork( + JOB_PERIODIC_FILES_SYNC + "_" + syncedFolderID, + ExistingPeriodicWorkPolicy.REPLACE, + request + ) } override fun startImmediateFilesSyncJob( + syncedFolderID: Long, overridePowerSaving: Boolean, changedFiles: Array ) { val arguments = Data.Builder() .putBoolean(FilesSyncWork.OVERRIDE_POWER_SAVING, overridePowerSaving) .putStringArray(FilesSyncWork.CHANGED_FILES, changedFiles) + .putLong(FilesSyncWork.SYNCED_FOLDER_ID, syncedFolderID) .build() val request = oneTimeRequestBuilder( jobClass = FilesSyncWork::class, - jobName = JOB_IMMEDIATE_FILES_SYNC + jobName = JOB_IMMEDIATE_FILES_SYNC + "_" + syncedFolderID ) .setInputData(arguments) .build() - workManager.enqueueUniqueWork(JOB_IMMEDIATE_FILES_SYNC, ExistingWorkPolicy.APPEND, request) + workManager.enqueueUniqueWork( + JOB_IMMEDIATE_FILES_SYNC + "_" + syncedFolderID, + ExistingWorkPolicy.APPEND, + request + ) } override fun scheduleOfflineSync() { diff --git a/app/src/main/java/com/nextcloud/client/jobs/ContentObserverWork.kt b/app/src/main/java/com/nextcloud/client/jobs/ContentObserverWork.kt index 6939f858aaa8..25724d40351b 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/ContentObserverWork.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/ContentObserverWork.kt @@ -12,6 +12,7 @@ import androidx.work.WorkerParameters import com.nextcloud.client.device.PowerManagementService import com.owncloud.android.datamodel.SyncedFolderProvider import com.owncloud.android.lib.common.utils.Log_OC +import com.owncloud.android.utils.FilesSyncHelper /** * This work is triggered when OS detects change in media folders. @@ -23,7 +24,7 @@ import com.owncloud.android.lib.common.utils.Log_OC class ContentObserverWork( appContext: Context, private val params: WorkerParameters, - private val syncerFolderProvider: SyncedFolderProvider, + private val syncedFolderProvider: SyncedFolderProvider, private val powerManagementService: PowerManagementService, private val backgroundJobManager: BackgroundJobManager ) : Worker(appContext, params) { @@ -48,13 +49,17 @@ class ContentObserverWork( } private fun checkAndStartFileSyncJob() { - val syncFolders = syncerFolderProvider.countEnabledSyncedFolders() > 0 - if (!powerManagementService.isPowerSavingEnabled && syncFolders) { + if (!powerManagementService.isPowerSavingEnabled && syncedFolderProvider.countEnabledSyncedFolders() > 0) { val changedFiles = mutableListOf() for (uri in params.triggeredContentUris) { changedFiles.add(uri.toString()) } - backgroundJobManager.startImmediateFilesSyncJob(false, changedFiles.toTypedArray()) + FilesSyncHelper.startFilesSyncForAllFolders( + syncedFolderProvider, + backgroundJobManager, + false, + changedFiles.toTypedArray() + ) } } diff --git a/app/src/main/java/com/nextcloud/client/jobs/FilesSyncWork.kt b/app/src/main/java/com/nextcloud/client/jobs/FilesSyncWork.kt index 88990164c5d7..c3bbfd4c2b5b 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/FilesSyncWork.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/FilesSyncWork.kt @@ -11,11 +11,8 @@ package com.nextcloud.client.jobs import android.content.ContentResolver import android.content.Context import android.content.res.Resources -import android.os.Build import android.text.TextUtils -import androidx.core.app.NotificationCompat import androidx.exifinterface.media.ExifInterface -import androidx.work.ForegroundInfo import androidx.work.Worker import androidx.work.WorkerParameters import com.nextcloud.client.account.UserAccountManager @@ -28,7 +25,6 @@ import com.owncloud.android.R import com.owncloud.android.datamodel.ArbitraryDataProvider import com.owncloud.android.datamodel.ArbitraryDataProviderImpl import com.owncloud.android.datamodel.FilesystemDataProvider -import com.owncloud.android.datamodel.ForegroundServiceType import com.owncloud.android.datamodel.MediaFolderType import com.owncloud.android.datamodel.SyncedFolder import com.owncloud.android.datamodel.SyncedFolderProvider @@ -36,7 +32,6 @@ import com.owncloud.android.datamodel.UploadsStorageManager import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.operations.UploadFileOperation import com.owncloud.android.ui.activity.SettingsActivity -import com.owncloud.android.ui.notifications.NotificationUtils import com.owncloud.android.utils.FileStorageUtils import com.owncloud.android.utils.FilesSyncHelper import com.owncloud.android.utils.MimeType @@ -64,48 +59,32 @@ class FilesSyncWork( const val TAG = "FilesSyncJob" const val OVERRIDE_POWER_SAVING = "overridePowerSaving" const val CHANGED_FILES = "changedFiles" - const val FOREGROUND_SERVICE_ID = 414 + const val SYNCED_FOLDER_ID = "syncedFolderId" } - @Suppress("MagicNumber") - private fun updateForegroundWorker(progressPercent: Int, useForegroundWorker: Boolean) { - if (!useForegroundWorker) { - return - } - - // update throughout worker execution to give use feedback how far worker is - val notification = NotificationCompat.Builder(context, NotificationUtils.NOTIFICATION_CHANNEL_FILE_SYNC) - .setTicker(context.getString(R.string.autoupload_worker_foreground_info)) - .setContentText(context.getString(R.string.autoupload_worker_foreground_info)) - .setSmallIcon(R.drawable.notification_icon) - .setContentTitle(context.getString(R.string.autoupload_worker_foreground_info)) - .setOngoing(true) - .setProgress(100, progressPercent, false) - .build() - val foregroundInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - ForegroundInfo(FOREGROUND_SERVICE_ID, notification, ForegroundServiceType.DataSync.getId()) - } else { - ForegroundInfo(FOREGROUND_SERVICE_ID, notification) - } - - setForegroundAsync(foregroundInfo) - } + private lateinit var syncedFolder: SyncedFolder @Suppress("MagicNumber") override fun doWork(): Result { - backgroundJobManager.logStartOfWorker(BackgroundJobManagerImpl.formatClassTag(this::class)) - Log_OC.d(TAG, "File-sync worker started") + val syncFolderId = inputData.getLong(SYNCED_FOLDER_ID, -1) + val changedFiles = inputData.getStringArray(CHANGED_FILES) - val overridePowerSaving = inputData.getBoolean(OVERRIDE_POWER_SAVING, false) - // If we are in power save mode, better to postpone upload - if (powerManagementService.isPowerSavingEnabled && !overridePowerSaving) { + backgroundJobManager.logStartOfWorker(BackgroundJobManagerImpl.formatClassTag(this::class) + "_" + syncFolderId) + Log_OC.d(TAG, "File-sync worker started for folder ID: $syncFolderId") + + if (canExitEarly(changedFiles, syncFolderId)) { val result = Result.success() - backgroundJobManager.logEndOfWorker(BackgroundJobManagerImpl.formatClassTag(this::class), result) + backgroundJobManager.logEndOfWorker( + BackgroundJobManagerImpl.formatClassTag(this::class) + + "_" + syncFolderId, + result + ) return result } + val resources = context.resources val lightVersion = resources.getBoolean(R.bool.syncedFolder_light) - FilesSyncHelper.restartJobsIfNeeded( + FilesSyncHelper.restartUploadsIfNeeded( uploadsStorageManager, userAccountManager, connectivityService, @@ -113,10 +92,13 @@ class FilesSyncWork( ) // Get changed files from ContentObserverWork (only images and videos) or by scanning filesystem - val changedFiles = inputData.getStringArray(CHANGED_FILES) - Log_OC.d(TAG, "File-sync worker changed files from observer: " + changedFiles.contentToString()) + Log_OC.d( + TAG, + "File-sync worker (${syncedFolder.remotePath}) changed files from observer: " + + changedFiles.contentToString() + ) collectChangedFiles(changedFiles) - Log_OC.d(TAG, "File-sync worker finished checking files.") + Log_OC.d(TAG, "File-sync worker (${syncedFolder.remotePath}) finished checking files.") // Create all the providers we'll need val filesystemDataProvider = FilesystemDataProvider(contentResolver) @@ -124,41 +106,83 @@ class FilesSyncWork( val dateFormat = SimpleDateFormat("yyyy:MM:dd HH:mm:ss", currentLocale) dateFormat.timeZone = TimeZone.getTimeZone(TimeZone.getDefault().id) - // start upload of changed / new files - val syncedFolders = syncedFolderProvider.syncedFolders - for ((index, syncedFolder) in syncedFolders.withIndex()) { - updateForegroundWorker( - (50 + (index.toDouble() / syncedFolders.size.toDouble()) * 50).toInt(), - changedFiles.isNullOrEmpty() - ) - if (syncedFolder.isEnabled) { - syncFolder( - context, - resources, - lightVersion, - filesystemDataProvider, - currentLocale, - dateFormat, - syncedFolder - ) - } - } - Log_OC.d(TAG, "File-sync worker finished") + syncFolder( + context, + resources, + lightVersion, + filesystemDataProvider, + currentLocale, + dateFormat, + syncedFolder + ) + + Log_OC.d(TAG, "File-sync worker (${syncedFolder.remotePath}) finished") val result = Result.success() - backgroundJobManager.logEndOfWorker(BackgroundJobManagerImpl.formatClassTag(this::class), result) + backgroundJobManager.logEndOfWorker( + BackgroundJobManagerImpl.formatClassTag(this::class) + + "_" + syncFolderId, + result + ) return result } + private fun setSyncedFolder(syncedFolderID: Long): Boolean { + val syncedFolderTmp = syncedFolderProvider.getSyncedFolderByID(syncedFolderID) + if (syncedFolderTmp == null || !syncedFolderTmp.isEnabled || !syncedFolderTmp.isExisting) { + return false + } + syncedFolder = syncedFolderTmp + return true + } + + @Suppress("ReturnCount") + private fun canExitEarly(changedFiles: Array?, syncedFolderID: Long): Boolean { + // If we are in power save mode better to postpone scan and upload + val overridePowerSaving = inputData.getBoolean(OVERRIDE_POWER_SAVING, false) + if ((powerManagementService.isPowerSavingEnabled && !overridePowerSaving)) { + return true + } + + if (syncedFolderID < 0) { + Log_OC.d(TAG, "File-sync kill worker since no valid syncedFolderID provided!") + return true + } + + // or sync worker already running and no changed files to be processed + val alreadyRunning = backgroundJobManager.bothFilesSyncJobsRunning(syncedFolderID) + if (alreadyRunning && changedFiles.isNullOrEmpty()) { + Log_OC.d( + TAG, + "File-sync kill worker since another instance of the worker " + + "($syncedFolderID) seems to be running already!" + ) + return true + } + + if (!setSyncedFolder(syncedFolderID)) { + Log_OC.d(TAG, "File-sync kill worker since syncedFolder ($syncedFolderID) is not enabled!") + return true + } + + if (syncedFolder.isChargingOnly && + !powerManagementService.battery.isCharging && + !powerManagementService.battery.isFull + ) { + Log_OC.d(TAG, "File-sync kill worker since phone is not charging (${syncedFolder.localPath})!") + return true + } + + return false + } + @Suppress("MagicNumber") private fun collectChangedFiles(changedFiles: Array?) { if (!changedFiles.isNullOrEmpty()) { - FilesSyncHelper.insertChangedEntries(syncedFolderProvider, changedFiles) + FilesSyncHelper.insertChangedEntries(syncedFolder, changedFiles) } else { - // Check every file in every synced folder for changes and update - // filesystemDataProvider database (potentially needs a long time so use foreground worker) - updateForegroundWorker(5, true) - FilesSyncHelper.insertAllDBEntries(syncedFolderProvider) - updateForegroundWorker(50, true) + // Check every file in synced folder for changes and update + // filesystemDataProvider database (potentially needs a long time) + FilesSyncHelper.insertAllDBEntries(syncedFolder, powerManagementService) } } diff --git a/app/src/main/java/com/nextcloud/client/jobs/download/DownloadNotificationManager.kt b/app/src/main/java/com/nextcloud/client/jobs/download/DownloadNotificationManager.kt index debcd53a311d..30762f8dd0d2 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/download/DownloadNotificationManager.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/download/DownloadNotificationManager.kt @@ -1,77 +1,49 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.jobs.download -import android.app.Notification -import android.app.NotificationManager import android.app.PendingIntent import android.content.Context import android.content.Intent -import android.graphics.BitmapFactory -import android.os.Build -import android.os.Handler -import android.os.Looper -import androidx.core.app.NotificationCompat +import com.nextcloud.client.jobs.notification.WorkerNotificationManager import com.owncloud.android.R -import com.owncloud.android.lib.resources.files.FileUtils import com.owncloud.android.operations.DownloadFileOperation -import com.owncloud.android.ui.notifications.NotificationUtils import com.owncloud.android.utils.theme.ViewThemeUtils import java.io.File import java.security.SecureRandom @Suppress("TooManyFunctions") class DownloadNotificationManager( - private val id: Int, + id: Int, private val context: Context, - private val viewThemeUtils: ViewThemeUtils -) { - private var notification: Notification - private var notificationBuilder: NotificationCompat.Builder - private val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - - init { - notificationBuilder = NotificationUtils.newNotificationBuilder(context, viewThemeUtils).apply { - setContentTitle(context.getString(R.string.downloader_download_in_progress_ticker)) - setTicker(context.getString(R.string.downloader_download_in_progress_ticker)) - setSmallIcon(R.drawable.notification_icon) - setLargeIcon(BitmapFactory.decodeResource(context.resources, R.drawable.notification_icon)) - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - setChannelId(NotificationUtils.NOTIFICATION_CHANNEL_DOWNLOAD) - } - } - - notification = notificationBuilder.build() - } + viewThemeUtils: ViewThemeUtils +) : WorkerNotificationManager(id, context, viewThemeUtils, R.string.downloader_download_in_progress_ticker) { @Suppress("MagicNumber") - fun prepareForStart(operation: DownloadFileOperation) { - notificationBuilder = NotificationUtils.newNotificationBuilder(context, viewThemeUtils).apply { - setSmallIcon(R.drawable.notification_icon) - setOngoing(true) - setProgress(100, 0, operation.size < 0) - setContentText( - String.format( - context.getString(R.string.downloader_download_in_progress), 0, - File(operation.savePath).name - ) + fun prepareForStart(operation: DownloadFileOperation, currentDownloadIndex: Int, totalDownloadSize: Int) { + currentOperationTitle = if (totalDownloadSize > 1) { + String.format( + context.getString(R.string.downloader_notification_manager_download_text), + currentDownloadIndex, + totalDownloadSize, + File(operation.savePath).name ) + } else { + File(operation.savePath).name + } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - setChannelId(NotificationUtils.NOTIFICATION_CHANNEL_DOWNLOAD) - } - - notificationManager.notify( - id, - this.build() - ) + notificationBuilder.run { + setContentTitle(currentOperationTitle) + setOngoing(false) + setProgress(100, 0, operation.size < 0) } + + showNotification() } fun prepareForResult() { @@ -82,23 +54,14 @@ class DownloadNotificationManager( } @Suppress("MagicNumber") - fun updateDownloadProgress(filePath: String, percent: Int, totalToTransfer: Long) { - notificationBuilder.run { - setProgress(100, percent, totalToTransfer < 0) - val fileName: String = filePath.substring(filePath.lastIndexOf(FileUtils.PATH_SEPARATOR) + 1) - val text = - String.format(context.getString(R.string.downloader_download_in_progress), percent, fileName) - val title = - context.getString(R.string.downloader_download_in_progress_ticker) - updateNotificationText(title, text) - } + fun updateDownloadProgress(percent: Int, totalToTransfer: Long) { + setProgress(percent, R.string.downloader_notification_manager_in_progress_text, totalToTransfer < 0) + showNotification() } @Suppress("MagicNumber") fun dismissNotification() { - Handler(Looper.getMainLooper()).postDelayed({ - notificationManager.cancel(id) - }, 2000) + dismissNotification(2000) } fun showNewNotification(text: String) { @@ -106,24 +69,12 @@ class DownloadNotificationManager( notificationBuilder.run { setProgress(0, 0, false) - setContentTitle(null) - setContentText(text) + setContentTitle(text) setOngoing(false) notificationManager.notify(notifyId, this.build()) } } - private fun updateNotificationText(title: String?, text: String) { - notificationBuilder.run { - title?.let { - setContentTitle(title) - } - - setContentText(text) - notificationManager.notify(id, this.build()) - } - } - fun setContentIntent(intent: Intent, flag: Int) { notificationBuilder.setContentIntent( PendingIntent.getActivity( @@ -134,12 +85,4 @@ class DownloadNotificationManager( ) ) } - - fun getId(): Int { - return id - } - - fun getNotification(): Notification { - return notificationBuilder.build() - } } diff --git a/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadError.kt b/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadError.kt index 56aadce8e596..c69eea3e9416 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadError.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadError.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadHelper.kt b/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadHelper.kt index 37401f3823f9..fb42ecbe2b9c 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadHelper.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadHelper.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadIntents.kt b/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadIntents.kt index 5d41f0469fa4..bbedd2805855 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadIntents.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadIntents.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadWorker.kt b/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadWorker.kt index 7265eb765abc..025283ca6e37 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadWorker.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/download/FileDownloadWorker.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ @@ -126,8 +126,8 @@ class FileDownloadWorker( ) setForegroundAsync(foregroundInfo) - requestDownloads.forEach { - downloadFile(it) + requestDownloads.forEachIndexed { currentDownloadIndex, requestedDownload -> + downloadFile(requestedDownload, currentDownloadIndex, requestDownloads.size) } downloadError?.let { @@ -254,7 +254,7 @@ class FileDownloadWorker( } @Suppress("TooGenericExceptionCaught", "DEPRECATION") - private fun downloadFile(downloadKey: String) { + private fun downloadFile(downloadKey: String, currentDownloadIndex: Int, totalDownloadSize: Int) { currentDownload = pendingDownloads.get(downloadKey) if (currentDownload == null) { @@ -270,7 +270,13 @@ class FileDownloadWorker( return } - notifyDownloadStart(currentDownload!!) + lastPercent = 0 + + notificationManager.run { + prepareForStart(currentDownload!!, currentDownloadIndex + 1, totalDownloadSize) + setContentIntent(intents.detailsIntent(currentDownload!!), PendingIntent.FLAG_IMMUTABLE) + } + var downloadResult: RemoteOperationResult<*>? = null try { val ocAccount = getOCAccountForDownload() @@ -291,15 +297,6 @@ class FileDownloadWorker( } } - private fun notifyDownloadStart(download: DownloadFileOperation) { - lastPercent = 0 - - notificationManager.run { - prepareForStart(download) - setContentIntent(intents.detailsIntent(download), PendingIntent.FLAG_IMMUTABLE) - } - } - @Suppress("DEPRECATION") private fun getOCAccountForDownload(): OwnCloudAccount { val currentDownloadAccount = currentDownload?.user?.toPlatformAccount() @@ -353,6 +350,7 @@ class FileDownloadWorker( private fun checkDownloadError(result: RemoteOperationResult<*>) { if (result.isSuccess || downloadError != null) { + notificationManager.dismissNotification() return } @@ -368,6 +366,7 @@ class FileDownloadWorker( FileDownloadError.Cancelled -> { context.getString(R.string.downloader_file_download_cancelled) } + FileDownloadError.Failed -> { context.getString(R.string.downloader_file_download_failed) } @@ -407,6 +406,10 @@ class FileDownloadWorker( } } + @Suppress("MagicNumber") + private val minProgressUpdateInterval = 750 + private var lastUpdateTime = 0L + @Suppress("MagicNumber") override fun onTransferProgress( progressRate: Long, @@ -415,11 +418,13 @@ class FileDownloadWorker( filePath: String ) { val percent: Int = (100.0 * totalTransferredSoFar.toDouble() / totalToTransfer.toDouble()).toInt() + val currentTime = System.currentTimeMillis() - if (percent != lastPercent) { + if (percent != lastPercent && (currentTime - lastUpdateTime) >= minProgressUpdateInterval) { notificationManager.run { - updateDownloadProgress(filePath, percent, totalToTransfer) + updateDownloadProgress(percent, totalToTransfer) } + lastUpdateTime = currentTime } lastPercent = percent diff --git a/app/src/main/java/com/nextcloud/client/jobs/notification/WorkerNotificationManager.kt b/app/src/main/java/com/nextcloud/client/jobs/notification/WorkerNotificationManager.kt new file mode 100644 index 000000000000..1f9a9ee03a25 --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/jobs/notification/WorkerNotificationManager.kt @@ -0,0 +1,76 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.client.jobs.notification + +import android.app.Notification +import android.app.NotificationManager +import android.content.Context +import android.graphics.BitmapFactory +import android.os.Build +import android.os.Handler +import android.os.Looper +import androidx.core.app.NotificationCompat +import com.owncloud.android.R +import com.owncloud.android.ui.notifications.NotificationUtils +import com.owncloud.android.utils.theme.ViewThemeUtils + +open class WorkerNotificationManager( + private val id: Int, + private val context: Context, + viewThemeUtils: ViewThemeUtils, + private val tickerId: Int +) { + var currentOperationTitle: String? = null + + val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + + var notificationBuilder: NotificationCompat.Builder = + NotificationUtils.newNotificationBuilder(context, "WorkerNotificationManager", viewThemeUtils).apply { + setTicker(context.getString(tickerId)) + setSmallIcon(R.drawable.notification_icon) + setLargeIcon(BitmapFactory.decodeResource(context.resources, R.drawable.notification_icon)) + setStyle(NotificationCompat.BigTextStyle()) + setPriority(NotificationCompat.PRIORITY_LOW) + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + setChannelId(NotificationUtils.NOTIFICATION_CHANNEL_DOWNLOAD) + } + } + + fun showNotification() { + notificationManager.notify(id, notificationBuilder.build()) + } + + @Suppress("MagicNumber") + fun setProgress(percent: Int, progressTextId: Int, indeterminate: Boolean) { + val progressText = String.format( + context.getString(progressTextId), + percent + ) + + notificationBuilder.run { + setProgress(100, percent, indeterminate) + setContentTitle(currentOperationTitle) + setContentText(progressText) + } + } + + fun dismissNotification(delay: Long = 0) { + Handler(Looper.getMainLooper()).postDelayed({ + notificationManager.cancel(id) + }, delay) + } + + fun getId(): Int { + return id + } + + fun getNotification(): Notification { + return notificationBuilder.build() + } +} diff --git a/app/src/main/java/com/nextcloud/client/jobs/transfer/FileTransferService.kt b/app/src/main/java/com/nextcloud/client/jobs/transfer/FileTransferService.kt index 1e779b879399..28c051cbba44 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/transfer/FileTransferService.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/transfer/FileTransferService.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/nextcloud/client/jobs/transfer/Transfer.kt b/app/src/main/java/com/nextcloud/client/jobs/transfer/Transfer.kt index d4fd95d1a69d..b3ecdae4b0b6 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/transfer/Transfer.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/transfer/Transfer.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/nextcloud/client/jobs/transfer/TransferManager.kt b/app/src/main/java/com/nextcloud/client/jobs/transfer/TransferManager.kt index e6f4664713f2..aaac9bc603ca 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/transfer/TransferManager.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/transfer/TransferManager.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/nextcloud/client/jobs/transfer/TransferManagerConnection.kt b/app/src/main/java/com/nextcloud/client/jobs/transfer/TransferManagerConnection.kt index 7c1cc03c6ad4..75a37253b8f8 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/transfer/TransferManagerConnection.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/transfer/TransferManagerConnection.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/nextcloud/client/jobs/transfer/TransferManagerImpl.kt b/app/src/main/java/com/nextcloud/client/jobs/transfer/TransferManagerImpl.kt index 28d7f205591a..680d011445b4 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/transfer/TransferManagerImpl.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/transfer/TransferManagerImpl.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadHelper.kt b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadHelper.kt index 878d3fa9248a..67ba4834dfa3 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadHelper.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadHelper.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadWorker.kt b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadWorker.kt index f0913b8fdffa..3329a92f2a71 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadWorker.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadWorker.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ @@ -56,7 +56,6 @@ class FileUploadWorker( val TAG: String = FileUploadWorker::class.java.simpleName const val NOTIFICATION_ERROR_ID: Int = 413 - private const val MAX_PROGRESS: Int = 100 const val ACCOUNT = "data_account" var currentUploadFileOperation: UploadFileOperation? = null @@ -101,9 +100,10 @@ class FileUploadWorker( backgroundJobManager.logStartOfWorker(BackgroundJobManagerImpl.formatClassTag(this::class)) val result = retrievePagesBySortingUploadsByID() backgroundJobManager.logEndOfWorker(BackgroundJobManagerImpl.formatClassTag(this::class), result) + notificationManager.dismissNotification() result } catch (t: Throwable) { - Log_OC.e(TAG, "Error caught at FileUploadWorker " + t.localizedMessage) + Log_OC.e(TAG, "Error caught at FileUploadWorker $t") Result.failure() } } @@ -113,7 +113,7 @@ class FileUploadWorker( setIdleWorkerState() currentUploadFileOperation?.cancel(null) - notificationManager.dismissWorkerNotifications() + notificationManager.dismissNotification() super.onStopped() } @@ -129,11 +129,12 @@ class FileUploadWorker( @Suppress("ReturnCount") private fun retrievePagesBySortingUploadsByID(): Result { val accountName = inputData.getString(ACCOUNT) ?: return Result.failure() - var currentPage = uploadsStorageManager.getCurrentAndPendingUploadsForAccountPageAscById(-1, accountName) + var uploadsPerPage = uploadsStorageManager.getCurrentAndPendingUploadsForAccountPageAscById(-1, accountName) + val totalUploadSize = uploadsStorageManager.getTotalUploadSize(accountName) - notificationManager.dismissWorkerNotifications() + Log_OC.d(TAG, "Total upload size: $totalUploadSize") - while (currentPage.isNotEmpty() && !isStopped) { + while (uploadsPerPage.isNotEmpty() && !isStopped) { if (preferences.isGlobalUploadPaused) { Log_OC.d(TAG, "Upload is paused, skip uploading files!") notificationManager.notifyPaused( @@ -142,10 +143,10 @@ class FileUploadWorker( return Result.success() } - Log_OC.d(TAG, "Handling ${currentPage.size} uploads for account $accountName") - val lastId = currentPage.last().uploadId - uploadFiles(currentPage, accountName) - currentPage = + Log_OC.d(TAG, "Handling ${uploadsPerPage.size} uploads for account $accountName") + val lastId = uploadsPerPage.last().uploadId + uploadFiles(totalUploadSize, uploadsPerPage, accountName) + uploadsPerPage = uploadsStorageManager.getCurrentAndPendingUploadsForAccountPageAscById(lastId, accountName) } @@ -157,31 +158,43 @@ class FileUploadWorker( return Result.success() } - private fun uploadFiles(uploads: List, accountName: String) { + private fun uploadFiles(totalUploadSize: Int, uploadsPerPage: List, accountName: String) { val user = userAccountManager.getUser(accountName) - setWorkerState(user.get(), uploads) + setWorkerState(user.get(), uploadsPerPage) - for (upload in uploads) { - if (isStopped) { - break - } + run uploads@{ + uploadsPerPage.forEachIndexed { currentUploadIndex, upload -> + if (isStopped) { + return@uploads + } - if (user.isPresent) { - val uploadFileOperation = createUploadFileOperation(upload, user.get()) + if (user.isPresent) { + val uploadFileOperation = createUploadFileOperation(upload, user.get()) - currentUploadFileOperation = uploadFileOperation - val result = upload(uploadFileOperation, user.get()) - currentUploadFileOperation = null + currentUploadFileOperation = uploadFileOperation - fileUploaderDelegate.sendBroadcastUploadFinished( - uploadFileOperation, - result, - uploadFileOperation.oldFile?.storagePath, - context, - localBroadcastManager - ) - } else { - uploadsStorageManager.removeUpload(upload.uploadId) + notificationManager.prepareForStart( + uploadFileOperation, + cancelPendingIntent = intents.startIntent(uploadFileOperation), + startIntent = intents.notificationStartIntent(uploadFileOperation), + currentUploadIndex = currentUploadIndex + 1, + totalUploadSize = totalUploadSize + ) + + val result = upload(uploadFileOperation, user.get()) + + currentUploadFileOperation = null + + fileUploaderDelegate.sendBroadcastUploadFinished( + uploadFileOperation, + result, + uploadFileOperation.oldFile?.storagePath, + context, + localBroadcastManager + ) + } else { + uploadsStorageManager.removeUpload(upload.uploadId) + } } } } @@ -210,12 +223,6 @@ class FileUploadWorker( private fun upload(uploadFileOperation: UploadFileOperation, user: User): RemoteOperationResult { lateinit var result: RemoteOperationResult - notificationManager.prepareForStart( - uploadFileOperation, - cancelPendingIntent = intents.startIntent(uploadFileOperation), - intents.notificationStartIntent(uploadFileOperation) - ) - try { val storageManager = uploadFileOperation.storageManager val ocAccount = OwnCloudAccount(user.toPlatformAccount(), context) @@ -240,7 +247,6 @@ class FileUploadWorker( if (!isStopped || !result.isCancelled) { uploadsStorageManager.updateDatabaseUploadResult(result, uploadFileOperation) notifyUploadResult(uploadFileOperation, result) - notificationManager.dismissWorkerNotifications() } } @@ -303,31 +309,41 @@ class FileUploadWorker( null } - notifyForFailedResult(uploadResult.code, conflictResolveIntent, credentialIntent, errorMessage) - showNewNotification(uploadFileOperation) + notifyForFailedResult( + uploadFileOperation, + uploadResult.code, + conflictResolveIntent, + credentialIntent, + errorMessage + ) } } + @Suppress("MagicNumber") + private val minProgressUpdateInterval = 750 + private var lastUpdateTime = 0L + + @Suppress("MagicNumber") override fun onTransferProgress( progressRate: Long, totalTransferredSoFar: Long, totalToTransfer: Long, fileAbsoluteName: String ) { - val percent = (MAX_PROGRESS * totalTransferredSoFar.toDouble() / totalToTransfer.toDouble()).toInt() + val percent = (100.0 * totalTransferredSoFar.toDouble() / totalToTransfer.toDouble()).toInt() + val currentTime = System.currentTimeMillis() - if (percent != lastPercent) { + if (percent != lastPercent && (currentTime - lastUpdateTime) >= minProgressUpdateInterval) { notificationManager.run { val accountName = currentUploadFileOperation?.user?.accountName val remotePath = currentUploadFileOperation?.remotePath - val filename = currentUploadFileOperation?.fileName ?: "" - updateUploadProgress(filename, percent, currentUploadFileOperation) + updateUploadProgress(percent, currentUploadFileOperation) if (accountName != null && remotePath != null) { - val key: String = - FileUploadHelper.buildRemoteName(accountName, remotePath) + val key: String = FileUploadHelper.buildRemoteName(accountName, remotePath) val boundListener = FileUploadHelper.mBoundListeners[key] + val filename = currentUploadFileOperation?.fileName ?: "" boundListener?.onTransferProgress( progressRate, @@ -339,6 +355,7 @@ class FileUploadWorker( dismissOldErrorNotification(currentUploadFileOperation) } + lastUpdateTime = currentTime } lastPercent = percent diff --git a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploaderDelegate.kt b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploaderDelegate.kt index 68e4319f537b..2cff0b44df4c 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploaderDelegate.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploaderDelegate.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploaderIntents.kt b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploaderIntents.kt index 1089eed6231f..99f6e4f20b2a 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploaderIntents.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploaderIntents.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/nextcloud/client/jobs/upload/UploadNotificationManager.kt b/app/src/main/java/com/nextcloud/client/jobs/upload/UploadNotificationManager.kt index 6e293617aae1..1912070c3b3c 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/upload/UploadNotificationManager.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/upload/UploadNotificationManager.kt @@ -1,65 +1,57 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.client.jobs.upload -import android.app.Notification -import android.app.NotificationManager import android.app.PendingIntent import android.content.Context -import android.graphics.BitmapFactory -import android.os.Build -import androidx.core.app.NotificationCompat +import com.nextcloud.client.jobs.notification.WorkerNotificationManager import com.owncloud.android.R import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.operations.UploadFileOperation import com.owncloud.android.ui.notifications.NotificationUtils import com.owncloud.android.utils.theme.ViewThemeUtils -class UploadNotificationManager(private val context: Context, viewThemeUtils: ViewThemeUtils) { +class UploadNotificationManager(private val context: Context, viewThemeUtils: ViewThemeUtils) : + WorkerNotificationManager(ID, context, viewThemeUtils, R.string.foreground_service_upload) { + companion object { private const val ID = 411 } - private var notification: Notification? = null - private var notificationBuilder: NotificationCompat.Builder = - NotificationUtils.newNotificationBuilder(context, viewThemeUtils).apply { - setContentTitle(context.getString(R.string.foreground_service_upload)) - setSmallIcon(R.drawable.notification_icon) - setLargeIcon(BitmapFactory.decodeResource(context.resources, R.drawable.notification_icon)) - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - setChannelId(NotificationUtils.NOTIFICATION_CHANNEL_UPLOAD) - } - } - private val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - - init { - notification = notificationBuilder.build() - } - @Suppress("MagicNumber") fun prepareForStart( uploadFileOperation: UploadFileOperation, cancelPendingIntent: PendingIntent, - startIntent: PendingIntent + startIntent: PendingIntent, + currentUploadIndex: Int, + totalUploadSize: Int ) { - notificationBuilder.run { - setContentTitle(context.getString(R.string.uploader_upload_in_progress_ticker)) - setContentText( - String.format( - context.getString(R.string.uploader_upload_in_progress), - 0, - uploadFileOperation.fileName - ) + currentOperationTitle = if (totalUploadSize > 1) { + String.format( + context.getString(R.string.upload_notification_manager_start_text), + currentUploadIndex, + totalUploadSize, + uploadFileOperation.fileName ) - setTicker(context.getString(R.string.foreground_service_upload)) + } else { + uploadFileOperation.fileName + } + + val progressText = String.format( + context.getString(R.string.upload_notification_manager_upload_in_progress_text), + 0 + ) + + notificationBuilder.run { setProgress(100, 0, false) - setOngoing(true) + setContentTitle(currentOperationTitle) + setContentText(progressText) + setOngoing(false) clearActions() addAction( @@ -76,13 +68,21 @@ class UploadNotificationManager(private val context: Context, viewThemeUtils: Vi } } + @Suppress("MagicNumber") + fun updateUploadProgress(percent: Int, currentOperation: UploadFileOperation?) { + setProgress(percent, R.string.upload_notification_manager_upload_in_progress_text, false) + showNotification() + dismissOldErrorNotification(currentOperation) + } + fun notifyForFailedResult( + uploadFileOperation: UploadFileOperation, resultCode: RemoteOperationResult.ResultCode, conflictsResolveIntent: PendingIntent?, credentialIntent: PendingIntent?, errorMessage: String ) { - val textId = resultTitle(resultCode) + val textId = getFailedResultTitleId(resultCode) notificationBuilder.run { setTicker(context.getString(textId)) @@ -106,9 +106,11 @@ class UploadNotificationManager(private val context: Context, viewThemeUtils: Vi setContentText(errorMessage) } + + showNewNotification(uploadFileOperation) } - private fun resultTitle(resultCode: RemoteOperationResult.ResultCode): Int { + private fun getFailedResultTitleId(resultCode: RemoteOperationResult.ResultCode): Int { val needsToUpdateCredentials = (resultCode == RemoteOperationResult.ResultCode.UNAUTHORIZED) return if (needsToUpdateCredentials) { @@ -128,7 +130,7 @@ class UploadNotificationManager(private val context: Context, viewThemeUtils: Vi ) } - fun showNewNotification(operation: UploadFileOperation) { + private fun showNewNotification(operation: UploadFileOperation) { notificationManager.notify( NotificationUtils.createUploadNotificationTag(operation.file), FileUploadWorker.NOTIFICATION_ERROR_ID, @@ -136,22 +138,6 @@ class UploadNotificationManager(private val context: Context, viewThemeUtils: Vi ) } - private fun showNotification() { - notificationManager.notify(ID, notificationBuilder.build()) - } - - @Suppress("MagicNumber") - fun updateUploadProgress(filename: String, percent: Int, currentOperation: UploadFileOperation?) { - notificationBuilder.run { - setProgress(100, percent, false) - val text = String.format(context.getString(R.string.uploader_upload_in_progress), percent, filename) - setContentText(text) - - showNotification() - dismissOldErrorNotification(currentOperation) - } - } - fun dismissOldErrorNotification(operation: UploadFileOperation?) { if (operation == null) { return @@ -171,15 +157,11 @@ class UploadNotificationManager(private val context: Context, viewThemeUtils: Vi ) } - fun dismissWorkerNotifications() { - notificationManager.cancel(ID) - } - fun notifyPaused(intent: PendingIntent) { - notificationBuilder.apply { + notificationBuilder.run { setContentTitle(context.getString(R.string.upload_global_pause_title)) setTicker(context.getString(R.string.upload_global_pause_title)) - setOngoing(true) + setOngoing(false) setAutoCancel(false) setProgress(0, 0, false) clearActions() diff --git a/app/src/main/java/com/nextcloud/client/jobs/upload/UploadTask.kt b/app/src/main/java/com/nextcloud/client/jobs/upload/UploadTask.kt index 04a4a1da4113..99025ca78a90 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/upload/UploadTask.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/upload/UploadTask.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/nextcloud/client/jobs/upload/UploadTrigger.kt b/app/src/main/java/com/nextcloud/client/jobs/upload/UploadTrigger.kt index 322d42f7a4fb..a378f5980f7e 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/upload/UploadTrigger.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/upload/UploadTrigger.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/nextcloud/client/onboarding/FirstRunActivity.kt b/app/src/main/java/com/nextcloud/client/onboarding/FirstRunActivity.kt index dde6320f1a00..d919ed52af6e 100644 --- a/app/src/main/java/com/nextcloud/client/onboarding/FirstRunActivity.kt +++ b/app/src/main/java/com/nextcloud/client/onboarding/FirstRunActivity.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2018 Tobias Kaminsky * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/nextcloud/client/onboarding/WhatsNewActivity.kt b/app/src/main/java/com/nextcloud/client/onboarding/WhatsNewActivity.kt index 6ad506207fd6..669587658bb0 100644 --- a/app/src/main/java/com/nextcloud/client/onboarding/WhatsNewActivity.kt +++ b/app/src/main/java/com/nextcloud/client/onboarding/WhatsNewActivity.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Álvaro Brey * SPDX-FileCopyrightText: 2017 Tobias Kaminsky * SPDX-FileCopyrightText: 2016 Andy Scherzinger diff --git a/app/src/main/java/com/nextcloud/client/widget/DashboardWidgetConfigurationActivity.kt b/app/src/main/java/com/nextcloud/client/widget/DashboardWidgetConfigurationActivity.kt index 2747ac4f9157..2b54c8a2683c 100644 --- a/app/src/main/java/com/nextcloud/client/widget/DashboardWidgetConfigurationActivity.kt +++ b/app/src/main/java/com/nextcloud/client/widget/DashboardWidgetConfigurationActivity.kt @@ -61,7 +61,7 @@ class DashboardWidgetConfigurationActivity : @Inject lateinit var widgetUpdater: DashboardWidgetUpdater - var appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + private var appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID public override fun onCreate(bundle: Bundle?) { super.onCreate(bundle) @@ -117,7 +117,7 @@ class DashboardWidgetConfigurationActivity : // Find the widget id from the intent. appWidgetId = intent?.extras?.getInt( - AppWidgetManager.EXTRA_APPWIDGET_ID, + EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID ) ?: AppWidgetManager.INVALID_APPWIDGET_ID @@ -153,11 +153,9 @@ class DashboardWidgetConfigurationActivity : visibility = View.VISIBLE } binding.emptyView.emptyListViewText.apply { - setText( - String.format( - getString(R.string.widgets_not_available), - getString(R.string.app_name) - ) + text = String.format( + getString(R.string.widgets_not_available), + getString(R.string.app_name) ) visibility = View.VISIBLE } diff --git a/app/src/main/java/com/nextcloud/client/widget/DashboardWidgetProvider.kt b/app/src/main/java/com/nextcloud/client/widget/DashboardWidgetProvider.kt index e4d9dc90ae12..18fc2074a2c1 100644 --- a/app/src/main/java/com/nextcloud/client/widget/DashboardWidgetProvider.kt +++ b/app/src/main/java/com/nextcloud/client/widget/DashboardWidgetProvider.kt @@ -45,9 +45,12 @@ class DashboardWidgetProvider : AppWidgetProvider() { AndroidInjection.inject(this, context) if (intent?.action == OPEN_INTENT) { - val clickIntent = Intent(Intent.ACTION_VIEW, intent.data) - clickIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - context?.startActivity(clickIntent) + context?.let { + val clickIntent = Intent(Intent.ACTION_VIEW, intent.data).apply { + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + } + context.startActivity(clickIntent) + } } } diff --git a/app/src/main/java/com/nextcloud/client/widget/DashboardWidgetService.kt b/app/src/main/java/com/nextcloud/client/widget/DashboardWidgetService.kt index 150c2b4bac1f..6486771a65f3 100644 --- a/app/src/main/java/com/nextcloud/client/widget/DashboardWidgetService.kt +++ b/app/src/main/java/com/nextcloud/client/widget/DashboardWidgetService.kt @@ -95,19 +95,20 @@ class StackRemoteViewsFactory( override fun onDataSetChanged() { CoroutineScope(Dispatchers.IO).launch { try { - if (widgetConfiguration.user.isPresent) { - val client = clientFactory.createNextcloudClient(widgetConfiguration.user.get()) - val result = DashboardGetWidgetItemsRemoteOperation(widgetConfiguration.widgetId, LIMIT_SIZE) - .execute(client) - widgetItems = if (result.isSuccess) { - result.resultData[widgetConfiguration.widgetId] ?: emptyList() - } else { - emptyList() - } - hasLoadMore = widgetConfiguration.moreButton != null && widgetItems.size == LIMIT_SIZE - } else { + if (!widgetConfiguration.user.isPresent) { Log_OC.w(TAG, "User not present for widget update") + return@launch + } + + val client = clientFactory.createNextcloudClient(widgetConfiguration.user.get()) + val result = DashboardGetWidgetItemsRemoteOperation(widgetConfiguration.widgetId, LIMIT_SIZE) + .execute(client) + widgetItems = if (result.isSuccess) { + result.resultData[widgetConfiguration.widgetId] ?: emptyList() + } else { + emptyList() } + hasLoadMore = widgetConfiguration.moreButton != null && widgetItems.size == LIMIT_SIZE } catch (e: ClientFactory.CreationException) { Log_OC.e(TAG, "Error updating widget", e) } diff --git a/app/src/main/java/com/nextcloud/client/widget/DashboardWidgetUpdater.kt b/app/src/main/java/com/nextcloud/client/widget/DashboardWidgetUpdater.kt index 9499d793b195..1634bf9a1bae 100644 --- a/app/src/main/java/com/nextcloud/client/widget/DashboardWidgetUpdater.kt +++ b/app/src/main/java/com/nextcloud/client/widget/DashboardWidgetUpdater.kt @@ -13,6 +13,7 @@ import android.content.Context import android.content.Intent import android.graphics.Bitmap import android.net.Uri +import android.os.Build import android.view.View import android.widget.RemoteViews import com.bumptech.glide.Glide @@ -63,64 +64,96 @@ class DashboardWidgetUpdater @Inject constructor( loadIcon(appWidgetId, iconUrl, this) } - appWidgetManager.updateAppWidget(appWidgetId, views) - appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.list) + appWidgetManager.run { + updateAppWidget(appWidgetId, views) + notifyAppWidgetViewDataChanged(appWidgetId, R.id.list) + } } private fun setPendingReload(remoteViews: RemoteViews, appWidgetId: Int) { - val intentUpdate = Intent(context, DashboardWidgetProvider::class.java) - intentUpdate.action = AppWidgetManager.ACTION_APPWIDGET_UPDATE - - val idArray = intArrayOf(appWidgetId) - intentUpdate.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, idArray) - - remoteViews.setOnClickPendingIntent( - R.id.reload, - PendingIntent.getBroadcast( - context, - appWidgetId, - intentUpdate, - PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT - ) - ) + val pendingIntent = getReloadPendingIntent(appWidgetId) + remoteViews.setOnClickPendingIntent(R.id.reload, pendingIntent) } private fun setPendingClick(remoteViews: RemoteViews) { - val flags = PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE + val intent = Intent().apply { + setPackage(context.packageName) + } - val clickIntent = PendingIntent.getActivity( + val pendingIntent = PendingIntent.getActivity( context, 0, - Intent(), - flags + intent, + pendingIntentFlags ) - remoteViews.setPendingIntentTemplate(R.id.list, clickIntent) + remoteViews.setPendingIntentTemplate(R.id.list, pendingIntent) } private fun setAddButton(addButton: DashboardButton?, appWidgetId: Int, remoteViews: RemoteViews) { - // create add button - if (addButton == null) { - remoteViews.setViewVisibility(R.id.create, View.GONE) - } else { - remoteViews.setViewVisibility(R.id.create, View.VISIBLE) - remoteViews.setContentDescription(R.id.create, addButton.text) - - val clickIntent = Intent(context, DashboardWidgetProvider::class.java) - clickIntent.action = DashboardWidgetProvider.OPEN_INTENT - clickIntent.data = Uri.parse(addButton.link) - - remoteViews.setOnClickPendingIntent( - R.id.create, - PendingIntent.getBroadcast( - context, - appWidgetId, - clickIntent, - PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT + remoteViews.run { + if (addButton == null) { + setViewVisibility(R.id.create, View.GONE) + } else { + setViewVisibility(R.id.create, View.VISIBLE) + setContentDescription(R.id.create, addButton.text) + + val pendingIntent = getAddPendingIntent(appWidgetId, addButton) + + setOnClickPendingIntent( + R.id.create, + pendingIntent ) - ) + } + } + } + + // region PendingIntents + private fun getReloadPendingIntent(appWidgetId: Int): PendingIntent { + val intent = Intent(context, DashboardWidgetProvider::class.java).apply { + setPackage(context.packageName) + action = AppWidgetManager.ACTION_APPWIDGET_UPDATE + + val idArray = intArrayOf(appWidgetId) + putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, idArray) + } + + return PendingIntent.getBroadcast( + context, + appWidgetId, + intent, + pendingIntentFlags + ) + } + + private fun getAddPendingIntent(appWidgetId: Int, addButton: DashboardButton): PendingIntent { + val intent = Intent(context, DashboardWidgetProvider::class.java).apply { + setPackage(context.packageName) + action = DashboardWidgetProvider.OPEN_INTENT + data = Uri.parse(addButton.link) + } + + return PendingIntent.getBroadcast( + context, + appWidgetId, + intent, + pendingIntentFlags + ) + } + + @Suppress("MagicNumber") + private val pendingIntentFlags: Int = when { + Build.VERSION.SDK_INT >= 34 -> { + PendingIntent.FLAG_UPDATE_CURRENT or + PendingIntent.FLAG_MUTABLE or + PendingIntent.FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT + } + + else -> { + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE } } + // endregion private fun loadIcon(appWidgetId: Int, iconUrl: String, remoteViews: RemoteViews) { val iconTarget = object : AppWidgetTarget(context, remoteViews, R.id.icon, appWidgetId) { diff --git a/app/src/main/java/com/nextcloud/model/HTTPStatusCodes.kt b/app/src/main/java/com/nextcloud/model/HTTPStatusCodes.kt index 08c9cfcf69a3..2c3c00326fa7 100644 --- a/app/src/main/java/com/nextcloud/model/HTTPStatusCodes.kt +++ b/app/src/main/java/com/nextcloud/model/HTTPStatusCodes.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/nextcloud/model/WorkerState.kt b/app/src/main/java/com/nextcloud/model/WorkerState.kt index 844a24fcc73d..ebca644172a7 100644 --- a/app/src/main/java/com/nextcloud/model/WorkerState.kt +++ b/app/src/main/java/com/nextcloud/model/WorkerState.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/nextcloud/model/WorkerStateLiveData.kt b/app/src/main/java/com/nextcloud/model/WorkerStateLiveData.kt index 2bb4ab612051..3af80f6553be 100644 --- a/app/src/main/java/com/nextcloud/model/WorkerStateLiveData.kt +++ b/app/src/main/java/com/nextcloud/model/WorkerStateLiveData.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt index d69570c6ddc0..e3eba394ea05 100644 --- a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt +++ b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2024 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeDestination.kt b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeDestination.kt index ba3fcb5bfcc2..10e80ad4ca7f 100644 --- a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeDestination.kt +++ b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeDestination.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2024 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/nextcloud/ui/composeComponents/alertDialog/SimpleAlertDialog.kt b/app/src/main/java/com/nextcloud/ui/composeComponents/alertDialog/SimpleAlertDialog.kt index 84f65d1c9ac0..a75bd87575af 100644 --- a/app/src/main/java/com/nextcloud/ui/composeComponents/alertDialog/SimpleAlertDialog.kt +++ b/app/src/main/java/com/nextcloud/ui/composeComponents/alertDialog/SimpleAlertDialog.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2024 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/nextcloud/ui/composeComponents/bottomSheet/MoreActionsBottomSheet.kt b/app/src/main/java/com/nextcloud/ui/composeComponents/bottomSheet/MoreActionsBottomSheet.kt index 1be5662b2f6f..408f6c6d334b 100644 --- a/app/src/main/java/com/nextcloud/ui/composeComponents/bottomSheet/MoreActionsBottomSheet.kt +++ b/app/src/main/java/com/nextcloud/ui/composeComponents/bottomSheet/MoreActionsBottomSheet.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.nextcloud.ui.composeComponents.bottomSheet diff --git a/app/src/main/java/com/nextcloud/utils/ForegroundServiceHelper.kt b/app/src/main/java/com/nextcloud/utils/ForegroundServiceHelper.kt index 64e72cbc39a7..479227001ceb 100644 --- a/app/src/main/java/com/nextcloud/utils/ForegroundServiceHelper.kt +++ b/app/src/main/java/com/nextcloud/utils/ForegroundServiceHelper.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/nextcloud/utils/ShortcutUtil.kt b/app/src/main/java/com/nextcloud/utils/ShortcutUtil.kt index 7a4987191ff6..439d2b6c75eb 100644 --- a/app/src/main/java/com/nextcloud/utils/ShortcutUtil.kt +++ b/app/src/main/java/com/nextcloud/utils/ShortcutUtil.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2022 Felix Nüsse * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only diff --git a/app/src/main/java/com/nextcloud/utils/extensions/BundleExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/BundleExtensions.kt index 368812fc9055..228a76df1cd4 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/BundleExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/BundleExtensions.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/nextcloud/utils/extensions/ContextExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/ContextExtensions.kt index dd4e4abc4e5b..282d1b8ce834 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/ContextExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/ContextExtensions.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/nextcloud/utils/extensions/IntentExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/IntentExtensions.kt index 7bb213dcba66..b286d652959d 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/IntentExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/IntentExtensions.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/nextcloud/utils/extensions/StringExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/StringExtensions.kt index e447bd618bdb..fbe1acf16fcb 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/StringExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/StringExtensions.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Your Name + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/nextcloud/utils/extensions/TextViewExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/TextViewExtensions.kt index 0e9b60e84d9a..11e622f420e8 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/TextViewExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/TextViewExtensions.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/nextcloud/utils/extensions/ViewExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/ViewExtensions.kt index ced757476928..0871dca3af11 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/ViewExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/ViewExtensions.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/nextcloud/utils/extensions/WorkManagerExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/WorkManagerExtensions.kt index cbd059801677..9a3a8b69c7b2 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/WorkManagerExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/WorkManagerExtensions.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ @@ -13,23 +13,24 @@ import com.google.common.util.concurrent.ListenableFuture import com.owncloud.android.lib.common.utils.Log_OC import java.util.concurrent.ExecutionException -fun WorkManager.isWorkScheduled(tag: String): Boolean { - val statuses: ListenableFuture> = this.getWorkInfosByTag(tag) - var running = false +private const val TAG = "WorkManager" + +fun WorkManager.isWorkRunning(tag: String): Boolean = checkWork(tag, listOf(WorkInfo.State.RUNNING)) + +fun WorkManager.isWorkScheduled(tag: String): Boolean = + checkWork(tag, listOf(WorkInfo.State.RUNNING, WorkInfo.State.ENQUEUED)) + +private fun WorkManager.checkWork(tag: String, stateConditions: List): Boolean { + val statuses: ListenableFuture> = getWorkInfosByTag(tag) var workInfoList: List = emptyList() try { workInfoList = statuses.get() } catch (e: ExecutionException) { - Log_OC.d("Worker", "ExecutionException in isWorkScheduled: $e") + Log_OC.d(TAG, "ExecutionException in checkWork: $e") } catch (e: InterruptedException) { - Log_OC.d("Worker", "InterruptedException in isWorkScheduled: $e") - } - - for (workInfo in workInfoList) { - val state = workInfo.state - running = running || (state == WorkInfo.State.RUNNING || state == WorkInfo.State.ENQUEUED) + Log_OC.d(TAG, "InterruptedException in checkWork: $e") } - return running + return workInfoList.any { workInfo -> stateConditions.contains(workInfo.state) } } diff --git a/app/src/main/java/com/nmc/android/ui/LauncherActivity.kt b/app/src/main/java/com/nmc/android/ui/LauncherActivity.kt index 1eb58f295024..b4c1272a76ee 100644 --- a/app/src/main/java/com/nmc/android/ui/LauncherActivity.kt +++ b/app/src/main/java/com/nmc/android/ui/LauncherActivity.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Andy Scherzinger * SPDX-FileCopyrightText: 2023 TSI-mc * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only diff --git a/app/src/main/java/com/owncloud/android/MainApp.java b/app/src/main/java/com/owncloud/android/MainApp.java index f59dd23dc6ac..f8088eaae4ee 100644 --- a/app/src/main/java/com/owncloud/android/MainApp.java +++ b/app/src/main/java/com/owncloud/android/MainApp.java @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 TSI-mc * SPDX-FileCopyrightText: 2022-2023 Álvaro Brey * SPDX-FileCopyrightText: 2016-2020 Tobias Kaminsky @@ -21,9 +21,12 @@ import android.app.Application; import android.app.NotificationChannel; import android.app.NotificationManager; +import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; +import android.content.RestrictionsManager; import android.content.pm.ActivityInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -55,6 +58,7 @@ import com.nextcloud.client.preferences.AppPreferences; import com.nextcloud.client.preferences.AppPreferencesImpl; import com.nextcloud.client.preferences.DarkMode; +import com.nextcloud.utils.extensions.ContextExtensionsKt; import com.nmc.android.ui.LauncherActivity; import com.owncloud.android.authentication.AuthenticatorActivity; import com.owncloud.android.authentication.PassCodeManager; @@ -63,6 +67,7 @@ import com.owncloud.android.datamodel.MediaFolder; import com.owncloud.android.datamodel.MediaFolderType; import com.owncloud.android.datamodel.MediaProvider; +import com.owncloud.android.datamodel.ReceiverFlag; import com.owncloud.android.datamodel.SyncedFolder; import com.owncloud.android.datamodel.SyncedFolderProvider; import com.owncloud.android.datamodel.ThumbnailsCacheManager; @@ -75,6 +80,7 @@ import com.owncloud.android.lib.resources.status.OwnCloudVersion; import com.owncloud.android.ui.activity.SyncedFoldersActivity; import com.owncloud.android.ui.notifications.NotificationUtils; +import com.owncloud.android.utils.appConfig.AppConfigManager; import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.FilesSyncHelper; import com.owncloud.android.utils.PermissionUtil; @@ -155,6 +161,9 @@ public class MainApp extends MultiDexApplication implements HasAndroidInjector { @Inject ConnectivityService connectivityService; + @Inject + SyncedFolderProvider syncedFolderProvider; + @Inject PowerManagementService powerManagementService; @Inject @@ -191,6 +200,8 @@ public class MainApp extends MultiDexApplication implements HasAndroidInjector { @SuppressWarnings("unused") private boolean mBound; + private AppConfigManager appConfigManager; + private static AppComponent appComponent; /** @@ -281,6 +292,7 @@ public static AppComponent getAppComponent() { return appComponent; } + @SuppressFBWarnings("ST") @Override public void onCreate() { @@ -314,11 +326,15 @@ public void onCreate() { OwnCloudClientManagerFactory.setUserAgent(getUserAgent()); - try { - OwnCloudClientManagerFactory.setProxyHost(getResources().getString(R.string.proxy_host)); - OwnCloudClientManagerFactory.setProxyPort(getResources().getInteger(R.integer.proxy_port)); - } catch (Resources.NotFoundException e) { - // no proxy set + if (isClientBrandedPlus()) { + RestrictionsManager restrictionsManager = (RestrictionsManager) getSystemService(Context.RESTRICTIONS_SERVICE); + appConfigManager = new AppConfigManager(this, restrictionsManager.getApplicationRestrictions()); + appConfigManager.setProxyConfig(isClientBrandedPlus()); + + // Listen app config changes + ContextExtensionsKt.registerBroadcastReceiver(this, restrictionsReceiver, restrictionsFilter, ReceiverFlag.NotExported); + } else { + setProxyForNonBrandedPlusClients(); } // initialise thumbnails cache on background thread @@ -346,7 +362,8 @@ public void onCreate() { backgroundJobManager, clock, viewThemeUtils, - walledCheckCache); + walledCheckCache, + syncedFolderProvider); initContactsBackup(accountManager, backgroundJobManager); notificationChannels(); @@ -364,9 +381,35 @@ public void onCreate() { } else if (event == Lifecycle.Event.ON_STOP) { passCodeManager.setCanAskPin(true); Log_OC.d(TAG, "APP IN BACKGROUND"); + } else if (event == Lifecycle.Event.ON_RESUME) { + if (appConfigManager == null) return; + appConfigManager.setProxyConfig(isClientBrandedPlus()); + Log_OC.d(TAG, "APP ON RESUME"); } }); + private void setProxyForNonBrandedPlusClients() { + try { + OwnCloudClientManagerFactory.setProxyHost(getResources().getString(R.string.proxy_host)); + OwnCloudClientManagerFactory.setProxyPort(getResources().getInteger(R.integer.proxy_port)); + } catch (Resources.NotFoundException e) { + Log_OC.d(TAG, "Error caught at setProxyForNonBrandedPlusClients: " + e); + } + } + + public static boolean isClientBrandedPlus() { + return (getAppContext().getResources().getBoolean(R.bool.is_branded_plus_client)); + } + + private final IntentFilter restrictionsFilter = new IntentFilter(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED); + + private final BroadcastReceiver restrictionsReceiver = new BroadcastReceiver() { + @Override public void onReceive(Context context, Intent intent) { + if (appConfigManager == null) return; + appConfigManager.setProxyConfig(isClientBrandedPlus()); + } + }; + private void registerGlobalPassCodeProtection() { registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() { @@ -547,7 +590,8 @@ public static void initSyncOperations( final BackgroundJobManager backgroundJobManager, final Clock clock, final ViewThemeUtils viewThemeUtils, - final WalledCheckCache walledCheckCache) { + final WalledCheckCache walledCheckCache, + final SyncedFolderProvider syncedFolderProvider) { updateToAutoUpload(); cleanOldEntries(clock); updateAutoUploadEntries(clock); @@ -561,12 +605,12 @@ public static void initSyncOperations( } if (!preferences.isAutoUploadInitialized()) { - backgroundJobManager.startImmediateFilesSyncJob(false, new String[]{}); + FilesSyncHelper.startFilesSyncForAllFolders(syncedFolderProvider, backgroundJobManager,false, new String[]{}); preferences.setAutoUploadInit(true); } - FilesSyncHelper.scheduleFilesSyncIfNeeded(mContext, backgroundJobManager); - FilesSyncHelper.restartJobsIfNeeded( + FilesSyncHelper.scheduleFilesSyncForAllFoldersIfNeeded(mContext, syncedFolderProvider, backgroundJobManager); + FilesSyncHelper.restartUploadsIfNeeded( uploadsStorageManager, accountManager, connectivityService, diff --git a/app/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java b/app/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java index 4f936057c4ec..6e24fffa8e8f 100644 --- a/app/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java +++ b/app/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java @@ -21,6 +21,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.RestrictionsManager; import android.content.ServiceConnection; import android.content.SharedPreferences; import android.content.pm.PackageManager; @@ -83,6 +84,7 @@ import com.owncloud.android.lib.common.OwnCloudCredentials; import com.owncloud.android.lib.common.OwnCloudCredentialsFactory; import com.owncloud.android.lib.common.UserInfo; +import com.owncloud.android.lib.common.accounts.AccountUtils; import com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException; import com.owncloud.android.lib.common.accounts.AccountUtils.Constants; import com.owncloud.android.lib.common.network.CertificateCombinedException; @@ -111,6 +113,7 @@ import com.owncloud.android.utils.ErrorMessageAdapter; import com.owncloud.android.utils.PermissionUtil; import com.owncloud.android.utils.WebViewUtil; +import com.owncloud.android.utils.appConfig.AppConfigManager; import com.owncloud.android.utils.theme.CapabilityUtils; import com.owncloud.android.utils.theme.ViewThemeUtils; @@ -140,6 +143,9 @@ import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleEventObserver; +import androidx.lifecycle.ProcessLifecycleOwner; import de.cotech.hw.fido.WebViewFidoBridge; import de.cotech.hw.fido.ui.FidoDialogOptions; import de.cotech.hw.fido2.WebViewWebauthnBridge; @@ -318,9 +324,18 @@ protected void onCreate(Bundle savedInstanceState) { mIsFirstAuthAttempt = savedInstanceState.getBoolean(KEY_AUTH_IS_FIRST_ATTEMPT_TAG); } - String webloginUrl = null; boolean webViewLoginMethod; - if (getIntent().getBooleanExtra(EXTRA_USE_PROVIDER_AS_WEBLOGIN, false)) { + String webloginUrl = null; + + if (MainApp.isClientBrandedPlus()) { + RestrictionsManager restrictionsManager = (RestrictionsManager) getSystemService(Context.RESTRICTIONS_SERVICE); + AppConfigManager appConfigManager = new AppConfigManager(this, restrictionsManager.getApplicationRestrictions()); + webloginUrl = appConfigManager.getBaseUrl(MainApp.isClientBrandedPlus()); + } + + if (webloginUrl != null) { + webViewLoginMethod = true; + } else if (getIntent().getBooleanExtra(EXTRA_USE_PROVIDER_AS_WEBLOGIN, false)) { webViewLoginMethod = true; webloginUrl = getString(R.string.provider_registration_server); } else { @@ -348,10 +363,18 @@ protected void onCreate(Bundle savedInstanceState) { } initServerPreFragment(savedInstanceState); + ProcessLifecycleOwner.get().getLifecycle().addObserver(lifecycleEventObserver); // webViewUtil.checkWebViewVersion(); } + private final LifecycleEventObserver lifecycleEventObserver = ((lifecycleOwner, event) -> { + if (event == Lifecycle.Event.ON_START && token != null) { + Log_OC.d(TAG, "Start poolLogin"); + poolLogin(clientFactory.createPlainClient()); + } + }); + private void deleteCookies() { try { CookieSyncManager.createInstance(this); @@ -391,7 +414,8 @@ private void anonymouslyPostLoginRequest(String url) { String loginUrl = login; runOnUiThread(() -> { Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(loginUrl)); - loginFlowResultLauncher.launch(intent); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(intent); }); token = jsonObject.getAsJsonObject("poll").get("token").getAsString(); @@ -400,9 +424,6 @@ private void anonymouslyPostLoginRequest(String url) { thread.start(); } - private final ActivityResultLauncher loginFlowResultLauncher = registerForActivityResult( - new ActivityResultContracts.StartActivityForResult(), result -> poolLogin(clientFactory.createPlainClient())); - private static String getWebLoginUserAgent() { return Build.MANUFACTURER.substring(0, 1).toUpperCase(Locale.getDefault()) + Build.MANUFACTURER.substring(1).toLowerCase(Locale.getDefault()) + " " + Build.MODEL + " (Android)"; @@ -816,6 +837,8 @@ protected void onDestroy() { mOperationsServiceBinder = null; } + Log_OC.d(TAG, "AuthenticatorActivity onDestroy called"); + super.onDestroy(); } @@ -1027,6 +1050,7 @@ private void initLoginInfoView() { cancelButton.setOnClickListener(v -> { loginFlowExecutorService.shutdown(); + ProcessLifecycleOwner.get().getLifecycle().removeObserver(lifecycleEventObserver); recreate(); }); } @@ -1375,7 +1399,7 @@ protected boolean createAccount(RemoteOperationResult authResult) { // can be anything: email, name, name with whitespaces String loginName = webViewUser; - String accountName = com.owncloud.android.lib.common.accounts.AccountUtils.buildAccountName(uri, loginName); + String accountName = AccountUtils.buildAccountName(uri, loginName); Account newAccount = new Account(accountName, accountType); if (accountManager.exists(newAccount)) { // fail - not a new account, but an existing one; disallow @@ -1480,7 +1504,7 @@ private void startQRScanner() { public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { - if (requestCode == PermissionUtil.PERMISSIONS_CAMERA) {// If request is cancelled, result arrays are empty. + if (requestCode == PERMISSIONS_CAMERA) {// If request is cancelled, result arrays are empty. if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // permission was granted startQRScanner(); @@ -1626,7 +1650,7 @@ public void onServiceDisconnected(ComponentName component) { private boolean isRedirectedToTheDefaultBrowser = false; private void poolLogin(PlainClient client) { - loginFlowExecutorService.scheduleAtFixedRate(() -> { + loginFlowExecutorService.scheduleWithFixedDelay(() -> { if (!isLoginProcessCompleted) { performLoginFlowV2(client); } @@ -1682,6 +1706,7 @@ private void completeLoginFlow(String response, int status) { checkOcServer(); loginFlowExecutorService.shutdown(); + ProcessLifecycleOwner.get().getLifecycle().removeObserver(lifecycleEventObserver); } /** diff --git a/app/src/main/java/com/owncloud/android/authentication/AuthenticatorAsyncTask.kt b/app/src/main/java/com/owncloud/android/authentication/AuthenticatorAsyncTask.kt index 0eda0f54ba88..9dc0e4d26d26 100644 --- a/app/src/main/java/com/owncloud/android/authentication/AuthenticatorAsyncTask.kt +++ b/app/src/main/java/com/owncloud/android/authentication/AuthenticatorAsyncTask.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2021 Tobias Kaminsky * SPDX-FileCopyrightText: 2015 ownCloud Inc. * SPDX-FileCopyrightText: 2013-2015 María Asensio Valverde diff --git a/app/src/main/java/com/owncloud/android/authentication/AuthenticatorUrlUtils.kt b/app/src/main/java/com/owncloud/android/authentication/AuthenticatorUrlUtils.kt index 76787a390a20..290fac2dda33 100644 --- a/app/src/main/java/com/owncloud/android/authentication/AuthenticatorUrlUtils.kt +++ b/app/src/main/java/com/owncloud/android/authentication/AuthenticatorUrlUtils.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2017 Andy Scherzinger * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/owncloud/android/authentication/DeepLinkLoginActivity.kt b/app/src/main/java/com/owncloud/android/authentication/DeepLinkLoginActivity.kt index b8f90d619678..141b64fafb71 100644 --- a/app/src/main/java/com/owncloud/android/authentication/DeepLinkLoginActivity.kt +++ b/app/src/main/java/com/owncloud/android/authentication/DeepLinkLoginActivity.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2018-2022 Tobias Kaminsky * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/owncloud/android/authentication/PassCodeManager.kt b/app/src/main/java/com/owncloud/android/authentication/PassCodeManager.kt index 47aeca544637..2ef2fa9f71dc 100644 --- a/app/src/main/java/com/owncloud/android/authentication/PassCodeManager.kt +++ b/app/src/main/java/com/owncloud/android/authentication/PassCodeManager.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2022-2023 Álvaro Brey * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java b/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java index 97931f7bc974..63be12074521 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java +++ b/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2022 TSI-mc * SPDX-FileCopyrightText: 2021 Chris Narkiewicz * SPDX-FileCopyrightText: 2018-2020 Tobias Kaminsky @@ -346,7 +346,7 @@ public static void clearTempEncryptedFolder(String accountName) { File tempEncryptedFolder = new File(FileStorageUtils.getTemporalEncryptedFolderPath(accountName)); if (!tempEncryptedFolder.exists()) { - Log_OC.d(TAG,"tempEncryptedFolder not exists"); + Log_OC.d(TAG,"tempEncryptedFolder does not exist"); return; } diff --git a/app/src/main/java/com/owncloud/android/datamodel/ForegroundServiceType.kt b/app/src/main/java/com/owncloud/android/datamodel/ForegroundServiceType.kt index 6911fc8a902c..9da4705f623c 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/ForegroundServiceType.kt +++ b/app/src/main/java/com/owncloud/android/datamodel/ForegroundServiceType.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/owncloud/android/datamodel/OCFile.java b/app/src/main/java/com/owncloud/android/datamodel/OCFile.java index 6d1551423657..a24770c0584a 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/OCFile.java +++ b/app/src/main/java/com/owncloud/android/datamodel/OCFile.java @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2022 Álvaro Brey Vilas * SPDX-FileCopyrightText: 2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2018 Andy Scherzinger diff --git a/app/src/main/java/com/owncloud/android/datamodel/ReceiverFlag.kt b/app/src/main/java/com/owncloud/android/datamodel/ReceiverFlag.kt index fdeb7be06c9e..ec6fc3414369 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/ReceiverFlag.kt +++ b/app/src/main/java/com/owncloud/android/datamodel/ReceiverFlag.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java b/app/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java index 2540af8928d8..4d1442183b21 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java +++ b/app/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java @@ -27,6 +27,8 @@ import java.util.List; import java.util.Observable; +import javax.annotation.Nullable; + import androidx.annotation.NonNull; import static com.owncloud.android.datamodel.OCFile.PATH_SEPARATOR; @@ -210,6 +212,29 @@ public SyncedFolder findByLocalPathAndAccount(String localPath, User user) { } + @Nullable + public SyncedFolder getSyncedFolderByID(Long syncedFolderID) { + SyncedFolder result = null; + Cursor cursor = mContentResolver.query( + ProviderMeta.ProviderTableMeta.CONTENT_URI_SYNCED_FOLDERS, + null, + ProviderMeta.ProviderTableMeta._ID + " =? ", + new String[]{syncedFolderID.toString()}, + null + ); + + if (cursor != null && cursor.getCount() == 1 && cursor.moveToFirst()) { + result = createSyncedFolderFromCursor(cursor); + } + + if (cursor != null) { + cursor.close(); + } + + return result; + + } + /** * Delete all synced folders for an account * diff --git a/app/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java b/app/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java index 799f61eedf84..6a4afa54bb39 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java +++ b/app/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java @@ -54,7 +54,11 @@ public class UploadsStorageManager extends Observable { private static final String TAG = UploadsStorageManager.class.getSimpleName(); + private static final String IS_EQUAL = "== ?"; + private static final String EQUAL = "=="; + private static final String OR = " OR "; private static final String AND = " AND "; + private static final String ANGLE_BRACKETS = "<>"; private static final int SINGLE_RESULT = 1; private static final long QUERY_PAGE_SIZE = 100; @@ -464,11 +468,45 @@ private OCUpload[] getUploads(@Nullable String selection, @Nullable String... se @NonNull private List getUploadPage(final long afterId, @Nullable String selection, @Nullable String... selectionArgs) { - return getUploadPage(afterId, true, selection, selectionArgs); + return getUploadPage(QUERY_PAGE_SIZE, afterId, true, selection, selectionArgs); + } + + private String getInProgressUploadsSelection() { + return "( " + ProviderTableMeta.UPLOADS_STATUS + EQUAL + UploadStatus.UPLOAD_IN_PROGRESS.value + + OR + ProviderTableMeta.UPLOADS_LAST_RESULT + + EQUAL + UploadResult.DELAYED_FOR_WIFI.getValue() + + OR + ProviderTableMeta.UPLOADS_LAST_RESULT + + EQUAL + UploadResult.LOCK_FAILED.getValue() + + OR + ProviderTableMeta.UPLOADS_LAST_RESULT + + EQUAL + UploadResult.DELAYED_FOR_CHARGING.getValue() + + OR + ProviderTableMeta.UPLOADS_LAST_RESULT + + EQUAL + UploadResult.DELAYED_IN_POWER_SAVE_MODE.getValue() + + " ) AND " + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + IS_EQUAL; + } + + public int getTotalUploadSize(@Nullable String... selectionArgs) { + final String selection = getInProgressUploadsSelection(); + int totalSize = 0; + + Cursor cursor = getDB().query( + ProviderTableMeta.CONTENT_URI_UPLOADS, + new String[]{"COUNT(*) AS count"}, + selection, + selectionArgs, + null); + + if (cursor != null) { + if (cursor.moveToFirst()) { + totalSize = cursor.getInt(cursor.getColumnIndexOrThrow("count")); + } + cursor.close(); + } + + return totalSize; } @NonNull - private List getUploadPage(final long afterId, final boolean descending, @Nullable String selection, @Nullable String... selectionArgs) { + private List getUploadPage(long limit, final long afterId, final boolean descending, @Nullable String selection, @Nullable String... selectionArgs) { List uploads = new ArrayList<>(); String pageSelection = selection; String[] pageSelectionArgs = selectionArgs; @@ -499,13 +537,20 @@ private List getUploadPage(final long afterId, final boolean descendin } else { Log_OC.d(TAG, String.format(Locale.ENGLISH, "QUERY: %s ROWID: %d", selection, afterId)); } + + String sortOrder; + if (limit > 0) { + sortOrder = String.format(Locale.ENGLISH, "_id " + sortDirection + " LIMIT %d", limit); + } else { + sortOrder = String.format(Locale.ENGLISH, "_id " + sortDirection); + } + Cursor c = getDB().query( ProviderTableMeta.CONTENT_URI_UPLOADS, null, pageSelection, pageSelectionArgs, - String.format(Locale.ENGLISH, "_id " + sortDirection + " LIMIT %d", QUERY_PAGE_SIZE) - ); + sortOrder); if (c != null) { if (c.moveToFirst()) { @@ -560,17 +605,8 @@ public OCUpload[] getCurrentAndPendingUploadsForCurrentAccount() { } public OCUpload[] getCurrentAndPendingUploadsForAccount(final @NonNull String accountName) { - return getUploads("( " + ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_IN_PROGRESS.value + - " OR " + ProviderTableMeta.UPLOADS_LAST_RESULT + - "==" + UploadResult.DELAYED_FOR_WIFI.getValue() + - " OR " + ProviderTableMeta.UPLOADS_LAST_RESULT + - "==" + UploadResult.LOCK_FAILED.getValue() + - " OR " + ProviderTableMeta.UPLOADS_LAST_RESULT + - "==" + UploadResult.DELAYED_FOR_CHARGING.getValue() + - " OR " + ProviderTableMeta.UPLOADS_LAST_RESULT + - "==" + UploadResult.DELAYED_IN_POWER_SAVE_MODE.getValue() + - " ) AND " + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "== ?", - accountName); + String inProgressUploadsSelection = getInProgressUploadsSelection(); + return getUploads(inProgressUploadsSelection, accountName); } /** @@ -579,76 +615,66 @@ public OCUpload[] getCurrentAndPendingUploadsForAccount(final @NonNull String ac * If afterId is -1, returns the first page */ public List getCurrentAndPendingUploadsForAccountPageAscById(final long afterId, final @NonNull String accountName) { - final String selection = "( " + ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_IN_PROGRESS.value + - " OR " + ProviderTableMeta.UPLOADS_LAST_RESULT + - "==" + UploadResult.DELAYED_FOR_WIFI.getValue() + - " OR " + ProviderTableMeta.UPLOADS_LAST_RESULT + - "==" + UploadResult.LOCK_FAILED.getValue() + - " OR " + ProviderTableMeta.UPLOADS_LAST_RESULT + - "==" + UploadResult.DELAYED_FOR_CHARGING.getValue() + - " OR " + ProviderTableMeta.UPLOADS_LAST_RESULT + - "==" + UploadResult.DELAYED_IN_POWER_SAVE_MODE.getValue() + - " ) AND " + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "== ?"; - return getUploadPage(afterId, false, selection, accountName); + final String selection = getInProgressUploadsSelection(); + return getUploadPage(QUERY_PAGE_SIZE, afterId, false, selection, accountName); } /** * Get all failed uploads. */ public OCUpload[] getFailedUploads() { - return getUploads("(" + ProviderTableMeta.UPLOADS_STATUS + "== ?" + - " OR " + ProviderTableMeta.UPLOADS_LAST_RESULT + - "==" + UploadResult.DELAYED_FOR_WIFI.getValue() + - " OR " + ProviderTableMeta.UPLOADS_LAST_RESULT + - "==" + UploadResult.LOCK_FAILED.getValue() + - " OR " + ProviderTableMeta.UPLOADS_LAST_RESULT + - "==" + UploadResult.DELAYED_FOR_CHARGING.getValue() + - " OR " + ProviderTableMeta.UPLOADS_LAST_RESULT + - "==" + UploadResult.DELAYED_IN_POWER_SAVE_MODE.getValue() + + return getUploads("(" + ProviderTableMeta.UPLOADS_STATUS + IS_EQUAL + + OR + ProviderTableMeta.UPLOADS_LAST_RESULT + + EQUAL + UploadResult.DELAYED_FOR_WIFI.getValue() + + OR + ProviderTableMeta.UPLOADS_LAST_RESULT + + EQUAL + UploadResult.LOCK_FAILED.getValue() + + OR + ProviderTableMeta.UPLOADS_LAST_RESULT + + EQUAL + UploadResult.DELAYED_FOR_CHARGING.getValue() + + OR + ProviderTableMeta.UPLOADS_LAST_RESULT + + EQUAL + UploadResult.DELAYED_IN_POWER_SAVE_MODE.getValue() + " ) AND " + ProviderTableMeta.UPLOADS_LAST_RESULT + "!= " + UploadResult.VIRUS_DETECTED.getValue() , String.valueOf(UploadStatus.UPLOAD_FAILED.value)); } public OCUpload[] getUploadsForAccount(final @NonNull String accountName) { - return getUploads(ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "== ?", accountName); + return getUploads(ProviderTableMeta.UPLOADS_ACCOUNT_NAME + IS_EQUAL, accountName); } public OCUpload[] getFinishedUploadsForCurrentAccount() { User user = currentAccountProvider.getUser(); - return getUploads(ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_SUCCEEDED.value + AND + - ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "== ?", user.getAccountName()); + return getUploads(ProviderTableMeta.UPLOADS_STATUS + EQUAL + UploadStatus.UPLOAD_SUCCEEDED.value + AND + + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + IS_EQUAL, user.getAccountName()); } public OCUpload[] getCancelledUploadsForCurrentAccount() { User user = currentAccountProvider.getUser(); - return getUploads(ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_CANCELLED.value + AND + - ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "== ?", user.getAccountName()); + return getUploads(ProviderTableMeta.UPLOADS_STATUS + EQUAL + UploadStatus.UPLOAD_CANCELLED.value + AND + + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + IS_EQUAL, user.getAccountName()); } /** * Get all uploads which where successfully completed. */ public OCUpload[] getFinishedUploads() { - - return getUploads(ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_SUCCEEDED.value, (String[]) null); + return getUploads(ProviderTableMeta.UPLOADS_STATUS + EQUAL + UploadStatus.UPLOAD_SUCCEEDED.value, (String[]) null); } public OCUpload[] getFailedButNotDelayedUploadsForCurrentAccount() { User user = currentAccountProvider.getUser(); - return getUploads(ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_FAILED.value + + return getUploads(ProviderTableMeta.UPLOADS_STATUS + EQUAL + UploadStatus.UPLOAD_FAILED.value + AND + ProviderTableMeta.UPLOADS_LAST_RESULT + - "<>" + UploadResult.DELAYED_FOR_WIFI.getValue() + + ANGLE_BRACKETS + UploadResult.DELAYED_FOR_WIFI.getValue() + AND + ProviderTableMeta.UPLOADS_LAST_RESULT + - "<>" + UploadResult.LOCK_FAILED.getValue() + + ANGLE_BRACKETS + UploadResult.LOCK_FAILED.getValue() + AND + ProviderTableMeta.UPLOADS_LAST_RESULT + - "<>" + UploadResult.DELAYED_FOR_CHARGING.getValue() + + ANGLE_BRACKETS + UploadResult.DELAYED_FOR_CHARGING.getValue() + AND + ProviderTableMeta.UPLOADS_LAST_RESULT + - "<>" + UploadResult.DELAYED_IN_POWER_SAVE_MODE.getValue() + - AND + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "== ?", + ANGLE_BRACKETS + UploadResult.DELAYED_IN_POWER_SAVE_MODE.getValue() + + AND + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + IS_EQUAL, user.getAccountName()); } @@ -659,14 +685,14 @@ public OCUpload[] getFailedButNotDelayedUploadsForCurrentAccount() { */ public OCUpload[] getFailedButNotDelayedUploads() { - return getUploads(ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_FAILED.value + AND + - ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.LOCK_FAILED.getValue() + + return getUploads(ProviderTableMeta.UPLOADS_STATUS + EQUAL + UploadStatus.UPLOAD_FAILED.value + AND + + ProviderTableMeta.UPLOADS_LAST_RESULT + ANGLE_BRACKETS + UploadResult.LOCK_FAILED.getValue() + AND + ProviderTableMeta.UPLOADS_LAST_RESULT + - "<>" + UploadResult.DELAYED_FOR_WIFI.getValue() + + ANGLE_BRACKETS + UploadResult.DELAYED_FOR_WIFI.getValue() + AND + ProviderTableMeta.UPLOADS_LAST_RESULT + - "<>" + UploadResult.DELAYED_FOR_CHARGING.getValue() + + ANGLE_BRACKETS + UploadResult.DELAYED_FOR_CHARGING.getValue() + AND + ProviderTableMeta.UPLOADS_LAST_RESULT + - "<>" + UploadResult.DELAYED_IN_POWER_SAVE_MODE.getValue(), + ANGLE_BRACKETS + UploadResult.DELAYED_IN_POWER_SAVE_MODE.getValue(), (String[]) null ); } @@ -679,16 +705,16 @@ public long clearFailedButNotDelayedUploads() { User user = currentAccountProvider.getUser(); final long deleted = getDB().delete( ProviderTableMeta.CONTENT_URI_UPLOADS, - ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_FAILED.value + + ProviderTableMeta.UPLOADS_STATUS + EQUAL + UploadStatus.UPLOAD_FAILED.value + AND + ProviderTableMeta.UPLOADS_LAST_RESULT + - "<>" + UploadResult.LOCK_FAILED.getValue() + + ANGLE_BRACKETS + UploadResult.LOCK_FAILED.getValue() + AND + ProviderTableMeta.UPLOADS_LAST_RESULT + - "<>" + UploadResult.DELAYED_FOR_WIFI.getValue() + + ANGLE_BRACKETS + UploadResult.DELAYED_FOR_WIFI.getValue() + AND + ProviderTableMeta.UPLOADS_LAST_RESULT + - "<>" + UploadResult.DELAYED_FOR_CHARGING.getValue() + + ANGLE_BRACKETS + UploadResult.DELAYED_FOR_CHARGING.getValue() + AND + ProviderTableMeta.UPLOADS_LAST_RESULT + - "<>" + UploadResult.DELAYED_IN_POWER_SAVE_MODE.getValue() + - AND + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "== ?", + ANGLE_BRACKETS + UploadResult.DELAYED_IN_POWER_SAVE_MODE.getValue() + + AND + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + IS_EQUAL, new String[]{user.getAccountName()} ); Log_OC.d(TAG, "delete all failed uploads but those delayed for Wifi"); @@ -702,8 +728,8 @@ public void clearCancelledUploadsForCurrentAccount() { User user = currentAccountProvider.getUser(); final long deleted = getDB().delete( ProviderTableMeta.CONTENT_URI_UPLOADS, - ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_CANCELLED.value + AND + - ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "== ?", new String[]{user.getAccountName()} + ProviderTableMeta.UPLOADS_STATUS + EQUAL + UploadStatus.UPLOAD_CANCELLED.value + AND + + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + IS_EQUAL, new String[]{user.getAccountName()} ); Log_OC.d(TAG, "delete all cancelled uploads"); @@ -716,8 +742,8 @@ public long clearSuccessfulUploads() { User user = currentAccountProvider.getUser(); final long deleted = getDB().delete( ProviderTableMeta.CONTENT_URI_UPLOADS, - ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_SUCCEEDED.value + AND + - ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "== ?", new String[]{user.getAccountName()} + ProviderTableMeta.UPLOADS_STATUS + EQUAL + UploadStatus.UPLOAD_SUCCEEDED.value + AND + + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + IS_EQUAL, new String[]{user.getAccountName()} ); Log_OC.d(TAG, "delete all successful uploads"); @@ -877,17 +903,13 @@ public enum UploadStatus { } public static UploadStatus fromValue(int value) { - switch (value) { - case 0: - return UPLOAD_IN_PROGRESS; - case 1: - return UPLOAD_FAILED; - case 2: - return UPLOAD_SUCCEEDED; - case 3: - return UPLOAD_CANCELLED; - } - return null; + return switch (value) { + case 0 -> UPLOAD_IN_PROGRESS; + case 1 -> UPLOAD_FAILED; + case 2 -> UPLOAD_SUCCEEDED; + case 3 -> UPLOAD_CANCELLED; + default -> null; + }; } public int getValue() { diff --git a/app/src/main/java/com/owncloud/android/files/BootupBroadcastReceiver.java b/app/src/main/java/com/owncloud/android/files/BootupBroadcastReceiver.java index 843d732150a7..381b32053cfa 100644 --- a/app/src/main/java/com/owncloud/android/files/BootupBroadcastReceiver.java +++ b/app/src/main/java/com/owncloud/android/files/BootupBroadcastReceiver.java @@ -23,6 +23,7 @@ import com.nextcloud.client.network.WalledCheckCache; import com.nextcloud.client.preferences.AppPreferences; import com.owncloud.android.MainApp; +import com.owncloud.android.datamodel.SyncedFolderProvider; import com.owncloud.android.datamodel.UploadsStorageManager; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.utils.theme.ViewThemeUtils; @@ -48,6 +49,7 @@ public class BootupBroadcastReceiver extends BroadcastReceiver { @Inject Clock clock; @Inject ViewThemeUtils viewThemeUtils; @Inject WalledCheckCache walledCheckCache; + @Inject SyncedFolderProvider syncedFolderProvider; /** * Receives broadcast intent reporting that the system was just boot up. * @@ -68,7 +70,9 @@ public void onReceive(Context context, Intent intent) { backgroundJobManager, clock, viewThemeUtils, - walledCheckCache); + walledCheckCache, + syncedFolderProvider + ); MainApp.initContactsBackup(accountManager, backgroundJobManager); } else { Log_OC.d(TAG, "Getting wrong intent: " + intent.getAction()); diff --git a/app/src/main/java/com/owncloud/android/files/FileMenuFilter.java b/app/src/main/java/com/owncloud/android/files/FileMenuFilter.java index 1ba1366153e5..ecb979542eef 100644 --- a/app/src/main/java/com/owncloud/android/files/FileMenuFilter.java +++ b/app/src/main/java/com/owncloud/android/files/FileMenuFilter.java @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2019-2023 Tobias Kaminsky * SPDX-FileCopyrightText: 2022 Álvaro Brey Vilas * SPDX-FileCopyrightText: 2020 Andy Scherzinger diff --git a/app/src/main/java/com/owncloud/android/files/services/IndexedForest.java b/app/src/main/java/com/owncloud/android/files/services/IndexedForest.java index e2d620b196b7..83bfc21a7106 100644 --- a/app/src/main/java/com/owncloud/android/files/services/IndexedForest.java +++ b/app/src/main/java/com/owncloud/android/files/services/IndexedForest.java @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2017-2018 Andy Scherzinger * SPDX-FileCopyrightText: 2016 ownCloud Inc. * SPDX-FileCopyrightText: 2015 David A. Velasco diff --git a/app/src/main/java/com/owncloud/android/media/MediaControlView.kt b/app/src/main/java/com/owncloud/android/media/MediaControlView.kt index a5f509b5f19c..1335d289ac1d 100644 --- a/app/src/main/java/com/owncloud/android/media/MediaControlView.kt +++ b/app/src/main/java/com/owncloud/android/media/MediaControlView.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2022 Álvaro Brey Vilas * SPDX-FileCopyrightText: 2018-2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2019 Chris Narkiewicz diff --git a/app/src/main/java/com/owncloud/android/operations/DownloadFileOperation.java b/app/src/main/java/com/owncloud/android/operations/DownloadFileOperation.java index 79ed00c592be..2658c3cdb486 100644 --- a/app/src/main/java/com/owncloud/android/operations/DownloadFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/DownloadFileOperation.java @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2020-2022 Tobias Kaminsky * SPDX-FileCopyrightText: 2019 Andy Scherzinger * SPDX-FileCopyrightText: 2015 ownCloud Inc. diff --git a/app/src/main/java/com/owncloud/android/operations/RemoveRemoteEncryptedFileOperation.kt b/app/src/main/java/com/owncloud/android/operations/RemoveRemoteEncryptedFileOperation.kt index 150447a922d4..5c85d0a5b20a 100644 --- a/app/src/main/java/com/owncloud/android/operations/RemoveRemoteEncryptedFileOperation.kt +++ b/app/src/main/java/com/owncloud/android/operations/RemoveRemoteEncryptedFileOperation.kt @@ -52,7 +52,7 @@ class RemoveRemoteEncryptedFileOperation internal constructor( @Deprecated("Deprecated in Java") @Suppress("TooGenericExceptionCaught") override fun run(client: OwnCloudClient): RemoteOperationResult { - val result: RemoteOperationResult + var result: RemoteOperationResult var delete: DeleteMethod? = null var token: String? = null val e2eVersion = CapabilityUtils.getCapability(context).endToEndEncryptionApiVersion diff --git a/app/src/main/java/com/owncloud/android/operations/SynchronizeFileOperation.java b/app/src/main/java/com/owncloud/android/operations/SynchronizeFileOperation.java index c914a04e3423..58ca7cd8432e 100644 --- a/app/src/main/java/com/owncloud/android/operations/SynchronizeFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/SynchronizeFileOperation.java @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2021 Chris Narkiewicz * SPDX-FileCopyrightText: 2021 Tobias Kaminsky * SPDX-FileCopyrightText: 2016-2018 Andy Scherzinger diff --git a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index f2fc4223fa22..a03a8f0b69f2 100644 --- a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -534,7 +534,7 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare updateMetadataForE2E(object, e2eData, clientData, e2eFiles, arbitraryDataProvider, encryptionUtilsV2, metadataExists); } } catch (FileNotFoundException e) { - Log_OC.d(TAG, mFile.getStoragePath() + " not exists anymore"); + Log_OC.d(TAG, mFile.getStoragePath() + " does not exist anymore"); result = new RemoteOperationResult(ResultCode.LOCAL_FILE_NOT_FOUND); } catch (OverlappingFileLockException e) { Log_OC.d(TAG, "Overlapping file lock exception"); @@ -917,7 +917,7 @@ private RemoteOperationResult checkConditions(File originalFile) { // check if the file continues existing before schedule the operation if (!originalFile.exists()) { - Log_OC.d(TAG, mOriginalStoragePath + " not exists anymore"); + Log_OC.d(TAG, mOriginalStoragePath + " does not exist anymore"); remoteOperationResult = new RemoteOperationResult(ResultCode.LOCAL_FILE_NOT_FOUND); } diff --git a/app/src/main/java/com/owncloud/android/operations/e2e/E2EClientData.kt b/app/src/main/java/com/owncloud/android/operations/e2e/E2EClientData.kt index 604f890031a0..892152a5163c 100644 --- a/app/src/main/java/com/owncloud/android/operations/e2e/E2EClientData.kt +++ b/app/src/main/java/com/owncloud/android/operations/e2e/E2EClientData.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Your Name + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-License-Identifier: AGPL-3.0-or-later */ diff --git a/app/src/main/java/com/owncloud/android/operations/e2e/E2EData.kt b/app/src/main/java/com/owncloud/android/operations/e2e/E2EData.kt index 2063708d74af..003d216a6646 100644 --- a/app/src/main/java/com/owncloud/android/operations/e2e/E2EData.kt +++ b/app/src/main/java/com/owncloud/android/operations/e2e/E2EData.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Your Name + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-License-Identifier: AGPL-3.0-or-later */ diff --git a/app/src/main/java/com/owncloud/android/operations/e2e/E2EFiles.kt b/app/src/main/java/com/owncloud/android/operations/e2e/E2EFiles.kt index b201775b2d3f..d67a0958857c 100644 --- a/app/src/main/java/com/owncloud/android/operations/e2e/E2EFiles.kt +++ b/app/src/main/java/com/owncloud/android/operations/e2e/E2EFiles.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Your Name + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-License-Identifier: AGPL-3.0-or-later */ diff --git a/app/src/main/java/com/owncloud/android/ui/activity/CommunityActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/CommunityActivity.kt index 6e507e358c30..b507103c18b2 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/CommunityActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/CommunityActivity.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2016 Andy Scherzinger * SPDX-FileCopyrightText: 2016 Tobias Kaminsky * SPDX-FileCopyrightText: 2016 Nextcloud diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ComponentsGetter.java b/app/src/main/java/com/owncloud/android/ui/activity/ComponentsGetter.java index e703ed33e1d6..7ce91df97af4 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/ComponentsGetter.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/ComponentsGetter.java @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2016 ownCloud Inc. * SPDX-FileCopyrightText: 2015 María Asensio Valverde * SPDX-FileCopyrightText: 2014 David A. Velasco diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.kt index a4f01558bf8e..f3a71e7a213c 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2024 Jonas Mayer - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2019 Alice Gaudon * SPDX-FileCopyrightText: 2012 Bartosz Przybylski * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) diff --git a/app/src/main/java/com/owncloud/android/ui/activity/CopyToClipboardActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/CopyToClipboardActivity.kt index 1a6349856409..2811b200791a 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/CopyToClipboardActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/CopyToClipboardActivity.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ package com.owncloud.android.ui.activity diff --git a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index a17e0cce1615..cbfd8f7f774d 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2021 TSI-mc + * SPDX-FileCopyrightText: 2021-2024 TSI-mc * SPDX-FileCopyrightText: 2020 Infomaniak Network SA * SPDX-FileCopyrightText: 2020 Chris Narkiewicz * SPDX-FileCopyrightText: 2017 Tobias Kaminsky @@ -51,6 +51,7 @@ import com.google.android.material.progressindicator.LinearProgressIndicator; import com.nextcloud.client.account.User; import com.nextcloud.client.di.Injectable; +import com.nextcloud.client.files.DeepLinkConstants; import com.nextcloud.client.network.ClientFactory; import com.nextcloud.client.onboarding.FirstRunActivity; import com.nextcloud.client.preferences.AppPreferences; @@ -113,6 +114,7 @@ import javax.inject.Inject; +import androidx.annotation.IdRes; import androidx.annotation.NonNull; import androidx.appcompat.app.ActionBarDrawerToggle; import androidx.core.content.ContextCompat; @@ -1272,4 +1274,42 @@ && getStorageManager() != null) { t.start(); } } + + protected void handleDeepLink(@NonNull Uri uri) { + String path = uri.getLastPathSegment(); + if (path == null) return; + + DeepLinkConstants deepLinkType = DeepLinkConstants.Companion.fromPath(path); + if (deepLinkType == null) { + DisplayUtils.showSnackMessage(this, getString(R.string.invalid_url)); + return; + } + + switch (deepLinkType) { + case OPEN_AUTO_UPLOAD: + startActivity(new Intent(this, SyncedFoldersActivity.class)); + break; + case OPEN_EXTERNAL_URL: + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(uri.getQueryParameter("url"))); + startActivity(intent); + break; + case ACTION_CREATE_NEW: + findViewById(R.id.fab_main).callOnClick(); + break; + case ACTION_APP_UPDATE: + openAppStore(getPackageName(), false); + break; + default: + handleNavItemClickEvent(deepLinkType.getNavId()); + break; + } + } + + private void handleNavItemClickEvent(@IdRes int menuItemId) { + if (mNavigationView == null) { + mNavigationView = findViewById(R.id.nav_view); + } + Menu navMenu = mNavigationView.getMenu(); + onNavigationItemClicked(navMenu.findItem(menuItemId)); + } } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.java index 765bd39b29ca..16fd7836c712 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.java @@ -520,17 +520,16 @@ protected void updateFileFromDB(){ * Show loading dialog */ public void showLoadingDialog(String message) { - // grant that only one waiting dialog is shown dismissLoadingDialog(); - // Construct dialog + Fragment frag = getSupportFragmentManager().findFragmentByTag(DIALOG_WAIT_TAG); if (frag == null) { Log_OC.d(TAG, "show loading dialog"); LoadingDialog loading = LoadingDialog.newInstance(message); FragmentManager fm = getSupportFragmentManager(); FragmentTransaction ft = fm.beginTransaction(); - loading.show(ft, DIALOG_WAIT_TAG); - fm.executePendingTransactions(); + ft.add(loading, DIALOG_WAIT_TAG); + ft.commitAllowingStateLoss(); } } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java index 7978ab48e9de..ca1879b5d451 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 TSI-mc + * SPDX-FileCopyrightText: 2023-2024 TSI-mc * SPDX-FileCopyrightText: 2023 Archontis E. Kostis * SPDX-FileCopyrightText: 2019 Chris Narkiewicz * SPDX-FileCopyrightText: 2018-2022 Tobias Kaminsky @@ -602,10 +602,13 @@ private void setLeftFragment(Fragment fragment, boolean showSortListGroup) { showSortListGroup(showSortListGroup); - FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - transaction.addToBackStack(null); - transaction.replace(R.id.left_fragment_container, fragment, TAG_LIST_OF_FILES); - transaction.commit(); + FragmentManager fragmentManager = getSupportFragmentManager(); + if (!isFinishing() && !fragmentManager.isDestroyed()) { + FragmentTransaction transaction = fragmentManager.beginTransaction(); + transaction.addToBackStack(null); + transaction.replace(R.id.left_fragment_container, fragment, TAG_LIST_OF_FILES); + transaction.commit(); + } } private OCFileListFragment getOCFileListFragmentFromFile() { @@ -2325,7 +2328,10 @@ public void setSearchQuery(String query) { } private void handleOpenFileViaIntent(Intent intent) { - showLoadingDialog(getString(R.string.retrieving_file)); + Uri deepLinkUri = getIntent().getData(); + if (deepLinkUri == null || !DeepLinkHandler.Companion.isDeepLinkTypeIsNavigation(deepLinkUri.toString())) { + showLoadingDialog(getString(R.string.retrieving_file)); + } String userName = intent.getStringExtra(KEY_ACCOUNT); String fileId = intent.getStringExtra(KEY_FILE_ID); @@ -2356,7 +2362,7 @@ private void openDeepLink(Uri uri) { DeepLinkHandler.Match match = linkHandler.parseDeepLink(uri); if (match == null) { dismissLoadingDialog(); - DisplayUtils.showSnackMessage(this, getString(R.string.invalid_url)); + handleDeepLink(uri); } else if (match.getUsers().isEmpty()) { dismissLoadingDialog(); DisplayUtils.showSnackMessage(this, getString(R.string.associated_account_not_found)); diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.java index 5c653353d7fa..aa4f032a6660 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.java @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2020 Chris Narkiewicz * SPDX-FileCopyrightText: 2020 Chawki Chouib * SPDX-FileCopyrightText: 2019 Tobias Kaminsky diff --git a/app/src/main/java/com/owncloud/android/ui/activity/PassCodeActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/PassCodeActivity.kt index c6ffcd9abbe4..cf14a992e4ee 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/PassCodeActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/PassCodeActivity.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2015 ownCloud Inc. * SPDX-FileCopyrightText: 2011 Bartek Przybylski * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) diff --git a/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java index f922cd0a4acf..5e68f2900edc 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 TSI-mc * SPDX-FileCopyrightText: 2022-2023 Álvaro Brey * SPDX-FileCopyrightText: 2017-2018 Tobias Kaminsky diff --git a/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt index debe1a6ca2d0..35769aa590ce 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt @@ -571,7 +571,7 @@ class SyncedFoldersActivity : } } if (syncedFolderDisplayItem.isEnabled) { - backgroundJobManager.startImmediateFilesSyncJob(overridePowerSaving = false) + backgroundJobManager.startImmediateFilesSyncJob(syncedFolderDisplayItem.id, overridePowerSaving = false) showBatteryOptimizationInfo() } } @@ -702,7 +702,7 @@ class SyncedFoldersActivity : // existing synced folder setup to be updated syncedFolderProvider.updateSyncFolder(item) if (item.isEnabled) { - backgroundJobManager.startImmediateFilesSyncJob(overridePowerSaving = false) + backgroundJobManager.startImmediateFilesSyncJob(item.id, overridePowerSaving = false) } else { val syncedFolderInitiatedKey = KEY_SYNCED_FOLDER_INITIATED_PREFIX + item.id val arbitraryDataProvider = @@ -719,7 +719,7 @@ class SyncedFoldersActivity : if (storedId != -1L) { item.id = storedId if (item.isEnabled) { - backgroundJobManager.startImmediateFilesSyncJob(overridePowerSaving = false) + backgroundJobManager.startImmediateFilesSyncJob(item.id, overridePowerSaving = false) } else { val syncedFolderInitiatedKey = KEY_SYNCED_FOLDER_INITIATED_PREFIX + item.id arbitraryDataProvider.deleteKeyForAccount("global", syncedFolderInitiatedKey) diff --git a/app/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java index 61768bb45985..7543267ad3ed 100755 --- a/app/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java @@ -34,6 +34,8 @@ import com.owncloud.android.R; import com.owncloud.android.databinding.UploadListLayoutBinding; import com.owncloud.android.datamodel.OCFile; +import com.owncloud.android.datamodel.SyncedFolder; +import com.owncloud.android.datamodel.SyncedFolderProvider; import com.owncloud.android.datamodel.UploadsStorageManager; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; @@ -84,6 +86,9 @@ public class UploadListActivity extends FileActivity { @Inject BackgroundJobManager backgroundJobManager; + @Inject + SyncedFolderProvider syncedFolderProvider; + @Inject LocalBroadcastManager localBroadcastManager; @@ -193,7 +198,10 @@ private void loadItems() { } private void refresh() { - backgroundJobManager.startImmediateFilesSyncJob(true,new String[]{}); + FilesSyncHelper.startFilesSyncForAllFolders(syncedFolderProvider, + backgroundJobManager, + true, + new String[]{}); if (uploadsStorageManager.getFailedUploads().length > 0) { new Thread(() -> { @@ -267,10 +275,10 @@ private void updateGlobalPauseIcon(MenuItem pauseMenuItem) { int iconId; String title; if (preferences.isGlobalUploadPaused()) { - iconId = R.drawable.ic_play; + iconId = R.drawable.ic_global_resume; title = getString(R.string.upload_action_global_upload_resume); } else { - iconId = R.drawable.ic_pause; + iconId = R.drawable.ic_global_pause; title = getString(R.string.upload_action_global_upload_pause); } @@ -316,10 +324,10 @@ public boolean onOptionsItemSelected(MenuItem item) { protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == FileActivity.REQUEST_CODE__UPDATE_CREDENTIALS && resultCode == RESULT_OK) { - FilesSyncHelper.restartJobsIfNeeded(uploadsStorageManager, - userAccountManager, - connectivityService, - powerManagementService); + FilesSyncHelper.restartUploadsIfNeeded(uploadsStorageManager, + userAccountManager, + connectivityService, + powerManagementService); } } @@ -339,10 +347,10 @@ public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationRe } else { // already updated -> just retry! - FilesSyncHelper.restartJobsIfNeeded(uploadsStorageManager, - userAccountManager, - connectivityService, - powerManagementService); + FilesSyncHelper.restartUploadsIfNeeded(uploadsStorageManager, + userAccountManager, + connectivityService, + powerManagementService); } } else { diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/ReceiveExternalFilesAdapter.kt b/app/src/main/java/com/owncloud/android/ui/adapter/ReceiveExternalFilesAdapter.kt index c463d792cd26..11bbd4bf86ff 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/ReceiveExternalFilesAdapter.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/ReceiveExternalFilesAdapter.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/ShareeListAdapter.java b/app/src/main/java/com/owncloud/android/ui/adapter/ShareeListAdapter.java index 61e85ced6b93..2e051086d91b 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/ShareeListAdapter.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/ShareeListAdapter.java @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2017-2018 Tobias Kaminsky * SPDX-FileCopyrightText: 2020 Chris Narkiewicz diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java b/app/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java index 51a83545b3f4..76b390518690 100755 --- a/app/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java @@ -102,7 +102,12 @@ public void onBindHeaderViewHolder(SectionedViewHolder holder, int section, bool group.getGroupName(), group.getGroupItemCount())); viewThemeUtils.platform.colorPrimaryTextViewElement(headerViewHolder.binding.uploadListTitle); - headerViewHolder.binding.uploadListTitle.setOnClickListener(v -> toggleSectionExpanded(section)); + headerViewHolder.binding.uploadListTitle.setOnClickListener(v -> { + toggleSectionExpanded(section); + headerViewHolder.binding.uploadListState.setImageResource(isSectionExpanded(section) ? + R.drawable.ic_expand_less : + R.drawable.ic_expand_more); + }); switch (group.type) { case CURRENT, FINISHED -> headerViewHolder.binding.uploadListAction.setImageResource(R.drawable.ic_close); diff --git a/app/src/main/java/com/owncloud/android/ui/components/CustomViewPager.java b/app/src/main/java/com/owncloud/android/ui/components/CustomViewPager.java deleted file mode 100644 index d3ce4a5f512b..000000000000 --- a/app/src/main/java/com/owncloud/android/ui/components/CustomViewPager.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Nextcloud - Android Client - * - * SPDX-FileCopyrightText: 2018 Tobias Kaminsky - * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only - */ -package com.owncloud.android.ui.components; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.util.AttributeSet; -import android.view.MotionEvent; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.viewpager.widget.ViewPager; - -public class CustomViewPager extends ViewPager { - public CustomViewPager(@NonNull Context context) { - super(context); - } - - public CustomViewPager(@NonNull Context context, @Nullable AttributeSet attrs) { - super(context, attrs); - } - - @SuppressLint("ClickableViewAccessibility") - @Override - public boolean onTouchEvent(MotionEvent ev) { - try { - return super.onTouchEvent(ev); - } catch (IllegalArgumentException ex) { - // no logging as this might flood log and we cannot do anything - } - return false; - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - try { - return super.onInterceptTouchEvent(ev); - } catch (IllegalArgumentException ex) { - // no logging as this might flood log and we cannot do anything - } - return false; - } -} diff --git a/app/src/main/java/com/owncloud/android/ui/components/PassCodeEditText.kt b/app/src/main/java/com/owncloud/android/ui/components/PassCodeEditText.kt index e48e55a79d96..ebbb9b1be022 100644 --- a/app/src/main/java/com/owncloud/android/ui/components/PassCodeEditText.kt +++ b/app/src/main/java/com/owncloud/android/ui/components/PassCodeEditText.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/ConfirmationDialogFragment.kt b/app/src/main/java/com/owncloud/android/ui/dialog/ConfirmationDialogFragment.kt index 4b1975c64269..8fcc9b52603e 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/ConfirmationDialogFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/dialog/ConfirmationDialogFragment.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2020-2022 Tobias Kaminsky * SPDX-FileCopyrightText: 2015 ownCloud Inc. * SPDX-FileCopyrightText: 2012 Bartosz Przybylski diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/ConflictsResolveDialog.java b/app/src/main/java/com/owncloud/android/ui/dialog/ConflictsResolveDialog.java index 1922af23c00a..9807350e81f4 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/ConflictsResolveDialog.java +++ b/app/src/main/java/com/owncloud/android/ui/dialog/ConflictsResolveDialog.java @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2020-2022 Tobias Kaminsky * SPDX-FileCopyrightText: 2015 ownCloud Inc. * SPDX-FileCopyrightText: 2012 Bartosz Przybylski diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/CreateFolderDialogFragment.kt b/app/src/main/java/com/owncloud/android/ui/dialog/CreateFolderDialogFragment.kt index 00feb5f87b6a..d2ebcfafe519 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/CreateFolderDialogFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/dialog/CreateFolderDialogFragment.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2018 Tobias Kaminsky * SPDX-FileCopyrightText: 2015 David A. Velasco diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/ExpirationDatePickerDialogFragment.kt b/app/src/main/java/com/owncloud/android/ui/dialog/ExpirationDatePickerDialogFragment.kt index 00748161dda3..b5baded8ed3b 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/ExpirationDatePickerDialogFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/dialog/ExpirationDatePickerDialogFragment.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2021 2018 TSI-mc * SPDX-FileCopyrightText: 2018 Tobias Kaminsky diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/IndeterminateProgressDialog.kt b/app/src/main/java/com/owncloud/android/ui/dialog/IndeterminateProgressDialog.kt index ff8b6c90aeb3..f5921a39a9e5 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/IndeterminateProgressDialog.kt +++ b/app/src/main/java/com/owncloud/android/ui/dialog/IndeterminateProgressDialog.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2015 David A. Velasco * SPDX-FileCopyrightText: 2015 ownCloud Inc. * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/LoadingDialog.kt b/app/src/main/java/com/owncloud/android/ui/dialog/LoadingDialog.kt index 2cbfad7f0fbd..ed179977f7af 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/LoadingDialog.kt +++ b/app/src/main/java/com/owncloud/android/ui/dialog/LoadingDialog.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2015 María Asensio Valverde * SPDX-FileCopyrightText: 2015 ownCloud Inc. * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/MultipleAccountsDialog.kt b/app/src/main/java/com/owncloud/android/ui/dialog/MultipleAccountsDialog.kt index 002bbe6f2e8a..0bb2043c4b0b 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/MultipleAccountsDialog.kt +++ b/app/src/main/java/com/owncloud/android/ui/dialog/MultipleAccountsDialog.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2020 Chris Narkiewicz * SPDX-FileCopyrightText: 2021 Andy Scherzinger * SPDX-FileCopyrightText: 2019 Tobias Kaminsky diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/RemoveFilesDialogFragment.java b/app/src/main/java/com/owncloud/android/ui/dialog/RemoveFilesDialogFragment.java index 921a1fd9fddd..62086d0441d4 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/RemoveFilesDialogFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/dialog/RemoveFilesDialogFragment.java @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2018 Andy Scherzinger * SPDX-FileCopyrightText: 2018 Jessie Chatham Spencer * SPDX-FileCopyrightText: 2016-2022 Tobias Kaminsky diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/RenameFileDialogFragment.java b/app/src/main/java/com/owncloud/android/ui/dialog/RenameFileDialogFragment.java index 9d34ef79af47..e2817f99227f 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/RenameFileDialogFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/dialog/RenameFileDialogFragment.java @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2022 Andy Scherzinger * SPDX-FileCopyrightText: 2017-2022 Tobias Kaminsky * SPDX-FileCopyrightText: 2014 ownCloud Inc. diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/SharePasswordDialogFragment.kt b/app/src/main/java/com/owncloud/android/ui/dialog/SharePasswordDialogFragment.kt index 8ab4aa0227e2..aada6af1ad77 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/SharePasswordDialogFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/dialog/SharePasswordDialogFragment.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2017-2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2018 Andy Scherzinger diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/FileFragment.java b/app/src/main/java/com/owncloud/android/ui/fragment/FileFragment.java index c730525b4cc2..8e575216b563 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/FileFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/FileFragment.java @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2021 TSI-mc * SPDX-FileCopyrightText: 2019 Chris Narkiewicz * SPDX-FileCopyrightText: 2017 Tobias Kaminsky diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragment.java b/app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragment.java index 950d74d6ada9..23132402aea9 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragment.java @@ -195,6 +195,7 @@ public void onConfigurationChanged(@NonNull Configuration newConfig) { showAllGalleryItems(); } + @Override public int getColumnsCount() { return columnSize; } diff --git a/app/src/main/java/com/owncloud/android/ui/helpers/UriUploader.kt b/app/src/main/java/com/owncloud/android/ui/helpers/UriUploader.kt index 36836cf85fe1..6429d625bd5c 100644 --- a/app/src/main/java/com/owncloud/android/ui/helpers/UriUploader.kt +++ b/app/src/main/java/com/owncloud/android/ui/helpers/UriUploader.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2018-2021 Tobias Kaminsky * SPDX-FileCopyrightText: 2022 Nextcloud GmbH diff --git a/app/src/main/java/com/owncloud/android/ui/notifications/NotificationUtils.java b/app/src/main/java/com/owncloud/android/ui/notifications/NotificationUtils.java index 2be4cf2b9e0b..184517f4a8c8 100644 --- a/app/src/main/java/com/owncloud/android/ui/notifications/NotificationUtils.java +++ b/app/src/main/java/com/owncloud/android/ui/notifications/NotificationUtils.java @@ -53,8 +53,8 @@ private NotificationUtils() { * @param context Context that will use the builder to create notifications * @return An instance of the regular {@link NotificationCompat.Builder}. */ - public static NotificationCompat.Builder newNotificationBuilder(Context context, final ViewThemeUtils viewThemeUtils) { - final NotificationCompat.Builder builder = new NotificationCompat.Builder(context); + public static NotificationCompat.Builder newNotificationBuilder(Context context, String channelId, final ViewThemeUtils viewThemeUtils) { + final NotificationCompat.Builder builder = new NotificationCompat.Builder(context, channelId); viewThemeUtils.androidx.themeNotificationCompatBuilder(context, builder); return builder; } diff --git a/app/src/main/java/com/owncloud/android/ui/preview/FileDownloadFragment.java b/app/src/main/java/com/owncloud/android/ui/preview/FileDownloadFragment.java index 5d43e8886ed4..83ea8b5beacb 100644 --- a/app/src/main/java/com/owncloud/android/ui/preview/FileDownloadFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/preview/FileDownloadFragment.java @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2022 Tobias Kaminsky * SPDX-FileCopyrightText: 2020 Chris Narkiewicz diff --git a/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.java b/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.java index 0121fd6d2dc3..f871ed78234f 100644 --- a/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.java @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020-2024 Andy Scherzinger - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2019 Tobias Kaminsky * SPDX-FileCopyrightText: 2019 Chris Narkiewicz @@ -59,7 +59,7 @@ import androidx.appcompat.app.ActionBar; import androidx.drawerlayout.widget.DrawerLayout; import androidx.localbroadcastmanager.content.LocalBroadcastManager; -import androidx.viewpager.widget.ViewPager; +import androidx.viewpager2.widget.ViewPager2; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; /** @@ -68,7 +68,6 @@ @SuppressWarnings("PMD.AvoidDuplicateLiterals") public class PreviewImageActivity extends FileActivity implements FileFragment.ContainerActivity, - ViewPager.OnPageChangeListener, OnRemoteOperationListener, Injectable { @@ -78,13 +77,12 @@ public class PreviewImageActivity extends FileActivity implements private static final String KEY_SYSTEM_VISIBLE = "TRUE"; private OCFile livePhotoFile; - private ViewPager viewPager; + private ViewPager2 viewPager; private PreviewImagePagerAdapter previewImagePagerAdapter; private int savedPosition; private boolean hasSavedPosition; private boolean requestWaitingForBinder; private DownloadFinishReceiver downloadFinishReceiver; - private UploadFinishReceiver uploadFinishReceiver; private View fullScreenAnchorView; private boolean isDownloadWorkStarted = false; @@ -158,7 +156,7 @@ private void initViewPager(User user) { if (virtualFolderType != null && virtualFolderType != VirtualFolderType.NONE) { VirtualFolderType type = (VirtualFolderType) virtualFolderType; - previewImagePagerAdapter = new PreviewImagePagerAdapter(getSupportFragmentManager(), + previewImagePagerAdapter = new PreviewImagePagerAdapter(this, type, user, getStorageManager()); @@ -168,11 +166,11 @@ private void initViewPager(User user) { if (parentFolder == null) { // should not be necessary - parentFolder = getStorageManager().getFileByPath(OCFile.ROOT_PATH); + parentFolder = getStorageManager().getFileByEncryptedRemotePath(OCFile.ROOT_PATH); } previewImagePagerAdapter = new PreviewImagePagerAdapter( - getSupportFragmentManager(), + this, livePhotoFile, parentFolder, user, @@ -185,11 +183,16 @@ private void initViewPager(User user) { viewPager = findViewById(R.id.fragmentPager); int position = hasSavedPosition ? savedPosition : previewImagePagerAdapter.getFilePosition(getFile()); - position = position >= 0 ? position : 0; + position = Math.max(position, 0); viewPager.setAdapter(previewImagePagerAdapter); - viewPager.addOnPageChangeListener(this); - viewPager.setCurrentItem(position); + viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() { + @Override + public void onPageSelected(int position) { + selectPage(position); + } + }); + viewPager.setCurrentItem(position, false); if (position == 0 && !getFile().isDown()) { // this is necessary because mViewPager.setCurrentItem(0) just after setting the @@ -247,7 +250,7 @@ public void onStart() { if (file != null) { /// Refresh the activity according to the Account and OCFile set setFile(file); // reset after getting it fresh from storageManager - getSupportActionBar().setTitle(getFile().getFileName()); + updateActionBarTitle(getFile().getFileName()); //if (!stateWasRecovered) { initViewPager(optionalUser.get()); //} @@ -271,15 +274,16 @@ public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationRe super.onRemoteOperationFinish(operation, result); if (operation instanceof RemoveFileOperation) { - // initialize the pager with the new file list - initViewPager(getUser().get()); - if (viewPager.getAdapter().getCount() > 0) { - // Trigger page reselection, to update the title - onPageSelected(viewPager.getCurrentItem()); - } else { - // Last file has been deleted, so finish the activity + int deletePosition = viewPager.getCurrentItem(); + int nextPosition = deletePosition > 0 ? deletePosition - 1 : 0; + + if (previewImagePagerAdapter.getItemCount() <= 1) { finish(); + return; } + + viewPager.setCurrentItem(nextPosition, true); + previewImagePagerAdapter.delete(deletePosition); } else if (operation instanceof SynchronizeFileOperation) { onSynchronizeFileOperationFinish(result); } @@ -301,7 +305,7 @@ private void observeWorkerState() { requestWaitingForBinder = false; Log_OC.d(TAG, "Simulating reselection of current page after connection " + "of download binder"); - onPageSelected(viewPager.getCurrentItem()); + selectPage(viewPager.getCurrentItem()); } } else { Log_OC.d(TAG, "Download worker stopped"); @@ -328,7 +332,7 @@ protected void onResume() { IntentFilter downloadIntentFilter = new IntentFilter(FileDownloadWorker.Companion.getDownloadFinishMessage()); localBroadcastManager.registerReceiver(downloadFinishReceiver, downloadIntentFilter); - uploadFinishReceiver = new UploadFinishReceiver(); + UploadFinishReceiver uploadFinishReceiver = new UploadFinishReceiver(); IntentFilter uploadIntentFilter = new IntentFilter(FileUploadWorker.Companion.getUploadFinishMessage()); localBroadcastManager.registerReceiver(uploadFinishReceiver, uploadIntentFilter); } @@ -384,8 +388,7 @@ public void requestForDownload(OCFile file, String downloadBehaviour) { * * @param position Position index of the new selected page */ - @Override - public void onPageSelected(int position) { + public void selectPage(int position) { savedPosition = position; hasSavedPosition = true; @@ -405,45 +408,21 @@ public void onPageSelected(int position) { } } - // Update ActionBar title if (currentFile != null) { - if (getSupportActionBar() != null) { - getSupportActionBar().setTitle(currentFile.getFileName()); - } + updateActionBarTitle(currentFile.getFileName()); setDrawerIndicatorEnabled(false); } } - /** - * Called when the scroll state changes. Useful for discovering when the user begins dragging, - * when the pager is automatically settling to the current page. when it is fully stopped/idle. - * - * @param state The new scroll state (SCROLL_STATE_IDLE, _DRAGGING, _SETTLING - */ - @Override - public void onPageScrollStateChanged(int state) { - // not used at the moment - } - - /** - * This method will be invoked when the current page is scrolled, either as part of a - * programmatically initiated smooth scroll or a user initiated touch scroll. - * - * @param position Position index of the first page currently being displayed. - * Page position+1 will be visible if positionOffset is - * nonzero. - * @param positionOffset Value from [0, 1) indicating the offset from the page - * at position. - * @param positionOffsetPixels Value in pixels indicating the offset from position. - */ - @Override - public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { - // not used at the moment + public void updateActionBarTitle(String title) { + if (getSupportActionBar() != null) { + getSupportActionBar().setTitle(title); + } } /** * Class waiting for broadcast events from the {@link FileDownloadWorker} service. - * + *

* Updates the UI when a download is started or finished, provided that it is relevant for the * folder displayed in the gallery. */ @@ -465,8 +444,9 @@ private void previewNewImage(Intent intent) { String accountName = intent.getStringExtra(FileDownloadWorker.EXTRA_ACCOUNT_NAME); String downloadedRemotePath = intent.getStringExtra(FileDownloadWorker.EXTRA_REMOTE_PATH); String downloadBehaviour = intent.getStringExtra(OCFileListFragment.DOWNLOAD_BEHAVIOUR); + if (getAccount().name.equals(accountName) && downloadedRemotePath != null) { - OCFile file = getStorageManager().getFileByPath(downloadedRemotePath); + OCFile file = getStorageManager().getFileByEncryptedRemotePath(downloadedRemotePath); boolean downloadWasFine = intent.getBooleanExtra(FileDownloadWorker.EXTRA_DOWNLOAD_RESULT, false); if (EditImageActivity.OPEN_IMAGE_EDITOR.equals(downloadBehaviour)) { @@ -476,16 +456,19 @@ private void previewNewImage(Intent intent) { if (position >= 0) { if (downloadWasFine) { previewImagePagerAdapter.updateFile(position, file); - } else { previewImagePagerAdapter.updateWithDownloadError(position); } - previewImagePagerAdapter.notifyDataSetChanged(); // will trigger the creation of new fragments + previewImagePagerAdapter.notifyItemChanged(position); } else if (downloadWasFine) { - initViewPager(getUser().get()); - int newPosition = previewImagePagerAdapter.getFilePosition(file); - if (newPosition >= 0) { - viewPager.setCurrentItem(newPosition); + Optional user = getUser(); + + if (user.isPresent()) { + initViewPager(user.get()); + int newPosition = previewImagePagerAdapter.getFilePosition(file); + if (newPosition >= 0) { + viewPager.setCurrentItem(newPosition); + } } } } @@ -502,19 +485,11 @@ public void toggleFullScreen() { if (visible) { hideSystemUI(fullScreenAnchorView); - // actionBar.hide(); // propagated through - // OnSystemUiVisibilityChangeListener() } else { showSystemUI(fullScreenAnchorView); - // actionBar.show(); // propagated through - // OnSystemUiVisibilityChangeListener() } } - public void switchToFullScreen() { - hideSystemUI(fullScreenAnchorView); - } - public void startImageEditor(OCFile file) { if (file.isDown()) { Intent editImageIntent = new Intent(this, EditImageActivity.class); diff --git a/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.java b/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.java index fd4a7f9a425a..d823dadab4e5 100644 --- a/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.java @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2020-2024 Andy Scherzinger - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2017-2020 Tobias Kaminsky * SPDX-FileCopyrightText: 2019 Chris Narkiewicz @@ -81,6 +81,7 @@ import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentStatePagerAdapter; import androidx.fragment.app.FragmentTransaction; +import androidx.viewpager2.adapter.FragmentStateAdapter; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import pl.droidsonroids.gif.GifDrawable; @@ -127,7 +128,7 @@ public class PreviewImageFragment extends FileFragment implements Injectable { * This method hides to client objects the need of doing the construction in two steps. * * @param imageFile An {@link OCFile} to preview as an image in the fragment - * @param ignoreFirstSavedState Flag to work around an unexpected behaviour of {@link FragmentStatePagerAdapter} ; + * @param ignoreFirstSavedState Flag to work around an unexpected behaviour of {@link FragmentStateAdapter} ; * TODO better solution */ public static PreviewImageFragment newInstance(@NonNull OCFile imageFile, @@ -232,8 +233,17 @@ public void onActivityCreated(Bundle savedInstanceState) { if (savedInstanceState != null) { if (!ignoreFirstSavedState) { OCFile file = BundleExtensionsKt.getParcelableArgument(savedInstanceState, EXTRA_FILE, OCFile.class); + if (file == null) { + return; + } + setFile(file); - binding.image.setScale(Math.min(binding.image.getMaximumScale(), savedInstanceState.getFloat(EXTRA_ZOOM))); + + try { + binding.image.setScale(Math.min(binding.image.getMaximumScale(), savedInstanceState.getFloat(EXTRA_ZOOM))); + } catch (IllegalArgumentException e) { + Log_OC.d(TAG, "Error caught at setScale: " + e); + } } else { ignoreFirstSavedState = false; } diff --git a/app/src/main/java/com/owncloud/android/ui/preview/PreviewImagePagerAdapter.kt b/app/src/main/java/com/owncloud/android/ui/preview/PreviewImagePagerAdapter.kt index 1d39872b8515..0132567a02ec 100644 --- a/app/src/main/java/com/owncloud/android/ui/preview/PreviewImagePagerAdapter.kt +++ b/app/src/main/java/com/owncloud/android/ui/preview/PreviewImagePagerAdapter.kt @@ -1,132 +1,124 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2018 Tobias Kaminsky * SPDX-FileCopyrightText: 2020 Chris Narkiewicz * SPDX-FileCopyrightText: 2015 ownCloud Inc. * SPDX-FileCopyrightText: 2013 David A. Velasco * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ -package com.owncloud.android.ui.preview; - -import android.util.SparseArray; -import android.view.ViewGroup; - -import com.nextcloud.client.account.User; -import com.nextcloud.client.preferences.AppPreferences; -import com.owncloud.android.datamodel.FileDataStorageManager; -import com.owncloud.android.datamodel.OCFile; -import com.owncloud.android.datamodel.VirtualFolderType; -import com.owncloud.android.ui.fragment.FileFragment; -import com.owncloud.android.utils.FileSortOrder; -import com.owncloud.android.utils.FileStorageUtils; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import javax.annotation.Nullable; - -import androidx.annotation.NonNull; -import androidx.annotation.OptIn; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentManager; -import androidx.fragment.app.FragmentStatePagerAdapter; -import androidx.media3.common.util.UnstableApi; +package com.owncloud.android.ui.preview + +import android.util.SparseArray +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentActivity +import androidx.viewpager2.adapter.FragmentStateAdapter +import com.nextcloud.client.account.User +import com.nextcloud.client.preferences.AppPreferences +import com.owncloud.android.datamodel.FileDataStorageManager +import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.datamodel.VirtualFolderType +import com.owncloud.android.ui.fragment.FileFragment +import com.owncloud.android.utils.FileStorageUtils /** * Adapter class that provides Fragment instances */ -public class PreviewImagePagerAdapter extends FragmentStatePagerAdapter { +class PreviewImagePagerAdapter : FragmentStateAdapter { - private OCFile selectedFile; - private List mImageFiles; - private User user; - private Set mObsoleteFragments; - private Set mObsoletePositions; - private Set mDownloadErrors; - private FileDataStorageManager mStorageManager; - private SparseArray mCachedFragments; + private var selectedFile: OCFile? = null + private var imageFiles: MutableList = mutableListOf() + private val user: User + private val mObsoleteFragments: MutableSet + private val mObsoletePositions: MutableSet + private val mDownloadErrors: MutableSet + private val mStorageManager: FileDataStorageManager + private val mCachedFragments: SparseArray /** * Constructor * - * @param fragmentManager {@link FragmentManager} instance that will handle the {@link Fragment}s provided by the - * adapter. + * @param fragmentActivity [FragmentActivity] instance that will handle the [Fragment]s provided by the + * adapter. * @param parentFolder Folder where images will be searched for. * @param storageManager Bridge to database. */ - public PreviewImagePagerAdapter(FragmentManager fragmentManager, - OCFile selectedFile, - OCFile parentFolder, - User user, - FileDataStorageManager storageManager, - boolean onlyOnDevice, - AppPreferences preferences) { - super(fragmentManager); - if (parentFolder == null) { - throw new IllegalArgumentException("NULL parent folder"); - } - if (storageManager == null) { - throw new IllegalArgumentException("NULL storage manager"); - } - - this.user = user; - this.selectedFile = selectedFile; - mStorageManager = storageManager; - mImageFiles = mStorageManager.getFolderImages(parentFolder, onlyOnDevice); - - FileSortOrder sortOrder = preferences.getSortOrderByFolder(parentFolder); - mImageFiles = sortOrder.sortCloudFiles(mImageFiles); - - mObsoleteFragments = new HashSet<>(); - mObsoletePositions = new HashSet<>(); - mDownloadErrors = new HashSet<>(); - mCachedFragments = new SparseArray<>(); + @Suppress("LongParameterList") + constructor( + fragmentActivity: FragmentActivity, + selectedFile: OCFile?, + parentFolder: OCFile?, + user: User, + storageManager: FileDataStorageManager, + onlyOnDevice: Boolean, + preferences: AppPreferences + ) : super(fragmentActivity) { + requireNotNull(parentFolder) { "NULL parent folder" } + + this.user = user + this.selectedFile = selectedFile + mStorageManager = storageManager + imageFiles = mStorageManager.getFolderImages(parentFolder, onlyOnDevice) + + val sortOrder = preferences.getSortOrderByFolder(parentFolder) + imageFiles = sortOrder.sortCloudFiles(imageFiles.toMutableList()).toMutableList() + + mObsoleteFragments = HashSet() + mObsoletePositions = HashSet() + mDownloadErrors = HashSet() + mCachedFragments = SparseArray() } /** * Constructor * - * @param fragmentManager {@link FragmentManager} instance that will handle the {@link Fragment}s provided by the - * adapter. + * @param fragmentActivity [FragmentActivity] instance that will handle the [Fragment]s provided by the + * adapter. * @param type Type of virtual folder, e.g. favorite or photos * @param storageManager Bridge to database. */ - public PreviewImagePagerAdapter(FragmentManager fragmentManager, - VirtualFolderType type, - User user, - FileDataStorageManager storageManager) { - super(fragmentManager); - - if (fragmentManager == null) { - throw new IllegalArgumentException("NULL FragmentManager instance"); - } - if (type == null) { - throw new IllegalArgumentException("NULL parent folder"); + constructor( + fragmentActivity: FragmentActivity, + type: VirtualFolderType?, + user: User, + storageManager: FileDataStorageManager + ) : super(fragmentActivity) { + requireNotNull(type) { "NULL parent folder" } + require(type != VirtualFolderType.NONE) { "NONE virtual folder type" } + + this.user = user + mStorageManager = storageManager + + if (type == VirtualFolderType.GALLERY) { + imageFiles = mStorageManager.allGalleryItems + imageFiles = FileStorageUtils.sortOcFolderDescDateModifiedWithoutFavoritesFirst(imageFiles) + } else { + imageFiles = mStorageManager.getVirtualFolderContent(type, true) } - if (type == VirtualFolderType.NONE) { - throw new IllegalArgumentException("NONE virtual folder type"); + + mObsoleteFragments = HashSet() + mObsoletePositions = HashSet() + mDownloadErrors = HashSet() + mCachedFragments = SparseArray() + } + + fun delete(position: Int) { + if (position < 0 || position >= imageFiles.size) { + return } - if (storageManager == null) { - throw new IllegalArgumentException("NULL storage manager"); + + mCachedFragments[position]?.let { + mObsoleteFragments.add(it) } - this.user = user; - mStorageManager = storageManager; + mObsoletePositions.add(position) - if (type == VirtualFolderType.GALLERY) { - mImageFiles = mStorageManager.getAllGalleryItems(); - mImageFiles = FileStorageUtils.sortOcFolderDescDateModifiedWithoutFavoritesFirst(mImageFiles); - } else { - mImageFiles = mStorageManager.getVirtualFolderContent(type, true); - } + imageFiles.removeAt(position) + mDownloadErrors.remove(position) + mCachedFragments.remove(position) - mObsoleteFragments = new HashSet<>(); - mObsoletePositions = new HashSet<>(); - mDownloadErrors = new HashSet<>(); - mCachedFragments = new SparseArray<>(); + notifyItemRemoved(position) } /** @@ -134,113 +126,82 @@ public class PreviewImagePagerAdapter extends FragmentStatePagerAdapter { * * @return OCFile desired image or null if position is not in adapter */ - @Nullable - public OCFile getFileAt(int position) { - try { - return mImageFiles.get(position); - } catch (IndexOutOfBoundsException exception) { - return null; + @Suppress("TooGenericExceptionCaught") + fun getFileAt(position: Int): OCFile? { + return try { + imageFiles[position] + } catch (exception: IndexOutOfBoundsException) { + null } } - private void addVideoOfLivePhoto(OCFile file) { - file.livePhotoVideo = selectedFile; + override fun getItemId(position: Int): Long { + return imageFiles[position].hashCode().toLong() + } + + private fun addVideoOfLivePhoto(file: OCFile) { + file.livePhotoVideo = selectedFile } - @NonNull - @OptIn(markerClass = UnstableApi.class) - public Fragment getItem(int i) { - OCFile file = getFileAt(i); - Fragment fragment; + fun getItem(i: Int): Fragment { + val file = getFileAt(i) + val fragment: Fragment if (file == null) { - fragment = PreviewImageErrorFragment.newInstance(); - } else if (file.isDown()) { - fragment = PreviewImageFragment.newInstance(file, mObsoletePositions.contains(i), false); + fragment = PreviewImageErrorFragment.newInstance() + } else if (file.isDown) { + fragment = PreviewImageFragment.newInstance(file, mObsoletePositions.contains(i), false) } else { - addVideoOfLivePhoto(file); + addVideoOfLivePhoto(file) if (mDownloadErrors.remove(i)) { - fragment = FileDownloadFragment.newInstance(file, user, true); - ((FileDownloadFragment) fragment).setError(true); + fragment = FileDownloadFragment.newInstance(file, user, true) + (fragment as FileDownloadFragment).setError(true) } else { - if (file.isEncrypted()) { - fragment = FileDownloadFragment.newInstance(file, user, mObsoletePositions.contains(i)); + fragment = if (file.isEncrypted) { + FileDownloadFragment.newInstance(file, user, mObsoletePositions.contains(i)) } else if (PreviewMediaFragment.canBePreviewed(file)) { - fragment = PreviewMediaFragment.newInstance(file, user, 0, false, file.livePhotoVideo != null); + PreviewMediaFragment.newInstance(file, user, 0, false, file.livePhotoVideo != null) } else { - fragment = PreviewImageFragment.newInstance(file, mObsoletePositions.contains(i), true); + PreviewImageFragment.newInstance(file, mObsoletePositions.contains(i), true) } } } - mObsoletePositions.remove(i); - return fragment; - } - - public int getFilePosition(OCFile file) { - return mImageFiles.indexOf(file); - } - - @Override - public int getCount() { - return mImageFiles.size(); + mObsoletePositions.remove(i) + return fragment } - @Override - public CharSequence getPageTitle(int position) { - OCFile file = getFileAt(position); - - if (file != null) { - return file.getFileName(); - } else { - return ""; - } + fun getFilePosition(file: OCFile): Int { + return imageFiles.indexOf(file) } - - public void updateFile(int position, OCFile file) { - FileFragment fragmentToUpdate = mCachedFragments.get(position); + fun updateFile(position: Int, file: OCFile) { + val fragmentToUpdate = mCachedFragments[position] if (fragmentToUpdate != null) { - mObsoleteFragments.add(fragmentToUpdate); + mObsoleteFragments.add(fragmentToUpdate) } - mObsoletePositions.add(position); - mImageFiles.set(position, file); + mObsoletePositions.add(position) + imageFiles[position] = file } - - public void updateWithDownloadError(int position) { - FileFragment fragmentToUpdate = mCachedFragments.get(position); + fun updateWithDownloadError(position: Int) { + val fragmentToUpdate = mCachedFragments[position] if (fragmentToUpdate != null) { - mObsoleteFragments.add(fragmentToUpdate); + mObsoleteFragments.add(fragmentToUpdate) } - mDownloadErrors.add(position); + mDownloadErrors.add(position) } - @Override - public int getItemPosition(@NonNull Object object) { - if (mObsoleteFragments.remove(object)) { - return POSITION_NONE; - } - return super.getItemPosition(object); - } - - @NonNull - @Override - public Object instantiateItem(@NonNull ViewGroup container, int position) { - Object fragment = super.instantiateItem(container, position); - mCachedFragments.put(position, (FileFragment) fragment); - return fragment; + fun pendingErrorAt(position: Int): Boolean { + return mDownloadErrors.contains(position) } - @Override - public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) { - mCachedFragments.remove(position); - super.destroyItem(container, position, object); + override fun createFragment(position: Int): Fragment { + return getItem(position) } - - public boolean pendingErrorAt(int position) { - return mDownloadErrors.contains(position); + override fun getItemCount(): Int { + return imageFiles.size } } diff --git a/app/src/main/java/com/owncloud/android/ui/preview/PreviewMediaActivity.kt b/app/src/main/java/com/owncloud/android/ui/preview/PreviewMediaActivity.kt index 791c88f73c35..c07da9c9e3c7 100644 --- a/app/src/main/java/com/owncloud/android/ui/preview/PreviewMediaActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/preview/PreviewMediaActivity.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2023 Parneet Singh - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 TSI-mc * SPDX-FileCopyrightText: 2020 Andy Scherzinger * SPDX-FileCopyrightText: 2019 Chris Narkiewicz @@ -541,7 +541,8 @@ class PreviewMediaActivity : override fun onRemoteOperationFinish(operation: RemoteOperation<*>?, result: RemoteOperationResult<*>?) { super.onRemoteOperationFinish(operation, result) if (operation is RemoveFileOperation) { - DisplayUtils.showSnackMessage(this, ErrorMessageAdapter.getErrorCauseMessage(result, operation, resources)) + val errorMessage = ErrorMessageAdapter.getErrorCauseMessage(result, operation, resources) + DisplayUtils.showSnackMessage(this, errorMessage) val removedFile = operation.file val fileAvailable: Boolean = storageManager.fileExists(removedFile.fileId) diff --git a/app/src/main/java/com/owncloud/android/ui/preview/PreviewMediaFragment.java b/app/src/main/java/com/owncloud/android/ui/preview/PreviewMediaFragment.java index 75b3f51d63a7..605359d921d9 100644 --- a/app/src/main/java/com/owncloud/android/ui/preview/PreviewMediaFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/preview/PreviewMediaFragment.java @@ -46,6 +46,7 @@ import com.nextcloud.common.NextcloudClient; import com.nextcloud.ui.fileactions.FileActionsBottomSheet; import com.nextcloud.utils.extensions.BundleExtensionsKt; +import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.databinding.FragmentPreviewMediaBinding; import com.owncloud.android.datamodel.OCFile; @@ -324,6 +325,15 @@ public void onSaveInstanceState(@NonNull Bundle outState) { public void onStart() { super.onStart(); Log_OC.v(TAG, "onStart"); + + @NonNull Context context; + if (getContext() != null) { + context = getContext(); + } else { + context = MainApp.getAppContext(); + } + + OCFile file = getFile(); if (file != null) { // bind to any existing player @@ -348,9 +358,9 @@ public void onStart() { try { nextcloudClient = clientFactory.createNextcloudClient(accountManager.getUser()); handler.post(() -> { - exoPlayer = NextcloudExoPlayer.createNextcloudExoplayer(requireContext(), nextcloudClient); + exoPlayer = NextcloudExoPlayer.createNextcloudExoplayer(context, nextcloudClient); - exoPlayer.addListener(new ExoplayerListener(requireContext(), binding.exoplayerView, exoPlayer, () -> { + exoPlayer.addListener(new ExoplayerListener(context, binding.exoplayerView, exoPlayer, () -> { goBackToLivePhoto(); return null; })); diff --git a/app/src/main/java/com/owncloud/android/ui/trashbin/RemoteTrashbinRepository.kt b/app/src/main/java/com/owncloud/android/ui/trashbin/RemoteTrashbinRepository.kt index 9b5af523066e..8399c8d02501 100644 --- a/app/src/main/java/com/owncloud/android/ui/trashbin/RemoteTrashbinRepository.kt +++ b/app/src/main/java/com/owncloud/android/ui/trashbin/RemoteTrashbinRepository.kt @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2023 TSI-mc - * SPDX-FileCopyrightText: Alper Ozturk + * SPDX-FileCopyrightText: Alper Ozturk * SPDX-FileCopyrightText: 2019 Chris Narkiewicz * SPDX-FileCopyrightText: 2018 Tobias Kaminsky * SPDX-FileCopyrightText: 2018 Nextcloud GmbH diff --git a/app/src/main/java/com/owncloud/android/ui/trashbin/TrashbinActivity.kt b/app/src/main/java/com/owncloud/android/ui/trashbin/TrashbinActivity.kt index 5803aa812ed2..1e25961b0435 100644 --- a/app/src/main/java/com/owncloud/android/ui/trashbin/TrashbinActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/trashbin/TrashbinActivity.kt @@ -150,9 +150,9 @@ class TrashbinActivity : recyclerView.setHasFooter(true) recyclerView.layoutManager = LinearLayoutManager(this) - viewThemeUtils.androidx.themeSwipeRefreshLayout(binding.swipeContainingList) + viewThemeUtils?.androidx?.themeSwipeRefreshLayout(binding.swipeContainingList) binding.swipeContainingList.setOnRefreshListener { loadFolder() } - viewThemeUtils.material.colorMaterialTextButton(findViewById(R.id.sort_button)) + viewThemeUtils?.material?.colorMaterialTextButton(findViewById(R.id.sort_button)) findViewById(R.id.sort_button).setOnClickListener { DisplayUtils.openSortingOrderDialogFragment( diff --git a/app/src/main/java/com/owncloud/android/ui/unifiedsearch/UnifiedSearchModel.kt b/app/src/main/java/com/owncloud/android/ui/unifiedsearch/UnifiedSearchModel.kt index 99de45efa535..8640305dd7a3 100644 --- a/app/src/main/java/com/owncloud/android/ui/unifiedsearch/UnifiedSearchModel.kt +++ b/app/src/main/java/com/owncloud/android/ui/unifiedsearch/UnifiedSearchModel.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2021 Álvaro Brey * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/owncloud/android/utils/DisplayUtils.java b/app/src/main/java/com/owncloud/android/utils/DisplayUtils.java index 543af6c2aa9d..66892aa05e4b 100644 --- a/app/src/main/java/com/owncloud/android/utils/DisplayUtils.java +++ b/app/src/main/java/com/owncloud/android/utils/DisplayUtils.java @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2023 ZetaTom * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2021 TSI-mc diff --git a/app/src/main/java/com/owncloud/android/utils/DrawableUtil.kt b/app/src/main/java/com/owncloud/android/utils/DrawableUtil.kt index 5e015911710a..c8aa15fb5ac6 100644 --- a/app/src/main/java/com/owncloud/android/utils/DrawableUtil.kt +++ b/app/src/main/java/com/owncloud/android/utils/DrawableUtil.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/owncloud/android/utils/DrawerMenuUtil.java b/app/src/main/java/com/owncloud/android/utils/DrawerMenuUtil.java index 0591b0d0c4d0..7292b1400edb 100644 --- a/app/src/main/java/com/owncloud/android/utils/DrawerMenuUtil.java +++ b/app/src/main/java/com/owncloud/android/utils/DrawerMenuUtil.java @@ -11,6 +11,7 @@ import android.view.Menu; import com.nextcloud.client.account.User; +import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.lib.resources.status.OCCapability; diff --git a/app/src/main/java/com/owncloud/android/utils/ErrorMessageAdapter.java b/app/src/main/java/com/owncloud/android/utils/ErrorMessageAdapter.java index 8574f2846bfb..6795feb714e6 100644 --- a/app/src/main/java/com/owncloud/android/utils/ErrorMessageAdapter.java +++ b/app/src/main/java/com/owncloud/android/utils/ErrorMessageAdapter.java @@ -293,9 +293,9 @@ String getMessageForRemoveFileOperation(RemoteOperationResult result, Resources } else { if (result.getCode() == ResultCode.FORBIDDEN) { - // Error --> No permissions - return String.format(res.getString(R.string.forbidden_permissions), - res.getString(R.string.forbidden_permissions_delete)); + return String.format(res.getString(R.string.forbidden_permissions), res.getString(R.string.forbidden_permissions_delete)); + } else if (result.getCode() == ResultCode.LOCKED) { + return res.getString(R.string.preview_media_unhandled_http_code_message); } } diff --git a/app/src/main/java/com/owncloud/android/utils/FileStorageUtils.java b/app/src/main/java/com/owncloud/android/utils/FileStorageUtils.java index db092ed68a06..a8b92caf3c7f 100644 --- a/app/src/main/java/com/owncloud/android/utils/FileStorageUtils.java +++ b/app/src/main/java/com/owncloud/android/utils/FileStorageUtils.java @@ -192,7 +192,7 @@ public static String getInstantUploadFilePath(File file, String relativeSubfolderPath = ""; if (parentFile == null) { - Log_OC.e("AutoUpload", "Parent folder does not exists!"); + Log_OC.e("AutoUpload", "Parent folder does not exist!"); } else { relativeSubfolderPath = parentFile.getAbsolutePath(); } diff --git a/app/src/main/java/com/owncloud/android/utils/FileUtil.java b/app/src/main/java/com/owncloud/android/utils/FileUtil.java index 09f64481d911..b378f564fe7d 100644 --- a/app/src/main/java/com/owncloud/android/utils/FileUtil.java +++ b/app/src/main/java/com/owncloud/android/utils/FileUtil.java @@ -12,11 +12,6 @@ import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.lib.resources.files.UploadFileRemoteOperation; -import org.lukhnos.nnio.file.FileVisitResult; -import org.lukhnos.nnio.file.FileVisitor; -import org.lukhnos.nnio.file.Path; -import org.lukhnos.nnio.file.impl.FileBasedPathImpl; - import java.io.File; import java.io.IOException; import java.nio.file.Files; @@ -68,19 +63,4 @@ Long getCreationTimestamp(File file) { return null; } } - - public static Path walkFileTree(Path start, FileVisitor visitor) throws IOException { - if (org.lukhnos.nnio.file.Files.isDirectory(start)) { - org.lukhnos.nnio.file.FileVisitResult preVisitDirectoryResult = visitor.preVisitDirectory(start, null); - if (preVisitDirectoryResult == FileVisitResult.CONTINUE) { - for (File child : start.toFile().listFiles()) { - walkFileTree(FileBasedPathImpl.get(child), visitor); - } - } - visitor.postVisitDirectory(start, null); - } else { - visitor.visitFile(start, new org.lukhnos.nnio.file.attribute.BasicFileAttributes(start.toFile())); - } - return start; - } } diff --git a/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java b/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java index 5f31b88a10b8..1987abfc3cdc 100644 --- a/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java +++ b/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java @@ -36,6 +36,7 @@ import org.lukhnos.nnio.file.Paths; import org.lukhnos.nnio.file.SimpleFileVisitor; import org.lukhnos.nnio.file.attribute.BasicFileAttributes; +import org.lukhnos.nnio.file.Files; import java.io.File; import java.io.IOException; @@ -63,7 +64,7 @@ private static void insertCustomFolderIntoDB(Path path, final long enabledTimestampMs = syncedFolder.getEnabledTimestampMs(); try { - FileUtil.walkFileTree(path, new SimpleFileVisitor<>() { + Files.walkFileTree(path, new SimpleFileVisitor<>() { @Override public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) { File file = path.toFile(); @@ -91,7 +92,7 @@ public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) { @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) { if (syncedFolder.isExcludeHidden() && dir.compareTo(Paths.get(syncedFolder.getLocalPath())) != 0 && dir.toFile().isHidden()) { - return null; + return FileVisitResult.SKIP_SUBTREE; } return FileVisitResult.CONTINUE; } @@ -145,28 +146,29 @@ private static void insertAllDBEntriesForSyncedFolder(SyncedFolder syncedFolder) } } - public static void insertAllDBEntries(SyncedFolderProvider syncedFolderProvider) { - for (SyncedFolder syncedFolder : syncedFolderProvider.getSyncedFolders()) { - if (syncedFolder.isEnabled()) { - insertAllDBEntriesForSyncedFolder(syncedFolder); - } + public static void insertAllDBEntries(SyncedFolder syncedFolder, + PowerManagementService powerManagementService) { + if (syncedFolder.isEnabled() && + !(syncedFolder.isChargingOnly() && + !powerManagementService.getBattery().isCharging() && + !powerManagementService.getBattery().isFull() + ) + ) { + insertAllDBEntriesForSyncedFolder(syncedFolder); } } - public static void insertChangedEntries(SyncedFolderProvider syncedFolderProvider, + public static void insertChangedEntries(SyncedFolder syncedFolder, String[] changedFiles) { final ContentResolver contentResolver = MainApp.getAppContext().getContentResolver(); final FilesystemDataProvider filesystemDataProvider = new FilesystemDataProvider(contentResolver); for (String changedFileURI : changedFiles){ String changedFile = getFileFromURI(changedFileURI); - for (SyncedFolder syncedFolder : syncedFolderProvider.getSyncedFolders()) { - if (syncedFolder.isEnabled() && syncedFolder.containsFile(changedFile)){ - File file = new File(changedFile); - filesystemDataProvider.storeOrUpdateFileValue(changedFile, - file.lastModified(),file.isDirectory(), - syncedFolder); - break; - } + if (syncedFolder.isEnabled() && syncedFolder.containsFile(changedFile)){ + File file = new File(changedFile); + filesystemDataProvider.storeOrUpdateFileValue(changedFile, + file.lastModified(),file.isDirectory(), + syncedFolder); } } } @@ -242,10 +244,10 @@ private static void insertContentIntoDB(Uri uri, SyncedFolder syncedFolder, } } - public static void restartJobsIfNeeded(final UploadsStorageManager uploadsStorageManager, - final UserAccountManager accountManager, - final ConnectivityService connectivityService, - final PowerManagementService powerManagementService) { + public static void restartUploadsIfNeeded(final UploadsStorageManager uploadsStorageManager, + final UserAccountManager accountManager, + final ConnectivityService connectivityService, + final PowerManagementService powerManagementService) { boolean accountExists; boolean whileChargingOnly = true; @@ -309,11 +311,23 @@ public static void restartJobsIfNeeded(final UploadsStorageManager uploadsStorag }).start(); } - public static void scheduleFilesSyncIfNeeded(Context context, BackgroundJobManager jobManager) { - jobManager.schedulePeriodicFilesSyncJob(); + public static void scheduleFilesSyncForAllFoldersIfNeeded(Context context, SyncedFolderProvider syncedFolderProvider, BackgroundJobManager jobManager) { + for (SyncedFolder syncedFolder : syncedFolderProvider.getSyncedFolders()) { + if (syncedFolder.isEnabled()) { + jobManager.schedulePeriodicFilesSyncJob(syncedFolder.getId()); + } + } if (context != null) { jobManager.scheduleContentObserverJob(); } } + + public static void startFilesSyncForAllFolders(SyncedFolderProvider syncedFolderProvider, BackgroundJobManager jobManager, boolean overridePowerSaving, String[] changedFiles) { + for (SyncedFolder syncedFolder : syncedFolderProvider.getSyncedFolders()) { + if (syncedFolder.isEnabled()) { + jobManager.startImmediateFilesSyncJob(syncedFolder.getId(),overridePowerSaving,changedFiles); + } + } + } } diff --git a/app/src/main/java/com/owncloud/android/utils/MimeTypeUtil.java b/app/src/main/java/com/owncloud/android/utils/MimeTypeUtil.java index 38e413439f55..5e11ec467c05 100644 --- a/app/src/main/java/com/owncloud/android/utils/MimeTypeUtil.java +++ b/app/src/main/java/com/owncloud/android/utils/MimeTypeUtil.java @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2023 TSI-mc - * SPDX-FileCopyrightText: 2023 alperozturk + * SPDX-FileCopyrightText: 2023 alperozturk * SPDX-FileCopyrightText: 2022 Álvaro Brey * SPDX-FileCopyrightText: 2018-2022 Tobias Kaminsky * SPDX-FileCopyrightText: 2016 Andy Scherzinger diff --git a/app/src/main/java/com/owncloud/android/utils/ReceiversHelper.java b/app/src/main/java/com/owncloud/android/utils/ReceiversHelper.java index 945b48280553..898200cfc8da 100644 --- a/app/src/main/java/com/owncloud/android/utils/ReceiversHelper.java +++ b/app/src/main/java/com/owncloud/android/utils/ReceiversHelper.java @@ -49,10 +49,10 @@ public void onReceive(Context context, Intent intent) { DNSCache.clear(); walledCheckCache.clear(); if (connectivityService.getConnectivity().isConnected()) { - FilesSyncHelper.restartJobsIfNeeded(uploadsStorageManager, - accountManager, - connectivityService, - powerManagementService); + FilesSyncHelper.restartUploadsIfNeeded(uploadsStorageManager, + accountManager, + connectivityService, + powerManagementService); } } }; @@ -76,10 +76,10 @@ public static void registerPowerChangeReceiver( @Override public void onReceive(Context context, Intent intent) { if (Intent.ACTION_POWER_CONNECTED.equals(intent.getAction())) { - FilesSyncHelper.restartJobsIfNeeded(uploadsStorageManager, - accountManager, - connectivityService, - powerManagementService); + FilesSyncHelper.restartUploadsIfNeeded(uploadsStorageManager, + accountManager, + connectivityService, + powerManagementService); } } }; @@ -102,10 +102,10 @@ public static void registerPowerSaveReceiver( @Override public void onReceive(Context context, Intent intent) { if (!powerManagementService.isPowerSavingEnabled()) { - FilesSyncHelper.restartJobsIfNeeded(uploadsStorageManager, - accountManager, - connectivityService, - powerManagementService); + FilesSyncHelper.restartUploadsIfNeeded(uploadsStorageManager, + accountManager, + connectivityService, + powerManagementService); } } }; diff --git a/app/src/main/java/com/owncloud/android/utils/WebViewUtil.kt b/app/src/main/java/com/owncloud/android/utils/WebViewUtil.kt index 6707f6ca7290..c3dbbd73e759 100644 --- a/app/src/main/java/com/owncloud/android/utils/WebViewUtil.kt +++ b/app/src/main/java/com/owncloud/android/utils/WebViewUtil.kt @@ -1,7 +1,7 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2023 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ diff --git a/app/src/main/java/com/owncloud/android/utils/appConfig/AppConfigKeys.kt b/app/src/main/java/com/owncloud/android/utils/appConfig/AppConfigKeys.kt new file mode 100644 index 000000000000..5df2a65a05d1 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/utils/appConfig/AppConfigKeys.kt @@ -0,0 +1,17 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.utils.appConfig + +/** + * These keys are connected to app_config.xml + */ +enum class AppConfigKeys(val key: String) { + BaseUrl("base_url"), + ProxyHost("proxy_host"), + ProxyPort("proxy_port") +} diff --git a/app/src/main/java/com/owncloud/android/utils/appConfig/AppConfigManager.kt b/app/src/main/java/com/owncloud/android/utils/appConfig/AppConfigManager.kt new file mode 100644 index 000000000000..a390a6aee128 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/utils/appConfig/AppConfigManager.kt @@ -0,0 +1,68 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.utils.appConfig + +import android.content.Context +import android.content.res.Resources +import android.os.Bundle +import android.text.TextUtils +import com.owncloud.android.R +import com.owncloud.android.lib.common.OwnCloudClientManagerFactory +import com.owncloud.android.lib.common.utils.Log_OC + +class AppConfigManager(private val context: Context, private val appRestrictions: Bundle) { + + private val tag = "AppConfigManager" + + fun setProxyConfig(isBrandedPlus: Boolean) { + if (!isBrandedPlus) { + Log_OC.d(tag, "Proxy configuration cannot be set. Client is not branded plus.") + return + } + + val host = if (appRestrictions.containsKey(AppConfigKeys.ProxyHost.key)) { + appRestrictions.getString(AppConfigKeys.ProxyHost.key) + } else { + context.getString(R.string.proxy_host) + } + + val port = if (appRestrictions.containsKey(AppConfigKeys.ProxyPort.key)) { + appRestrictions.getInt(AppConfigKeys.ProxyPort.key) + } else { + context.resources.getInteger(R.integer.proxy_port) + } + + if (TextUtils.isEmpty(host) || port == -1) { + Log_OC.d(tag, "Proxy configuration cannot be found") + return + } + + try { + OwnCloudClientManagerFactory.setProxyHost(host) + OwnCloudClientManagerFactory.setProxyPort(port) + + Log_OC.d(tag, "Proxy configuration successfully set") + } catch (e: Resources.NotFoundException) { + Log_OC.e(tag, "Proxy config cannot able to set due to: $e") + } + } + + fun getBaseUrl(isBrandedPlus: Boolean): String? { + if (!isBrandedPlus) { + Log_OC.d(tag, "Proxy configuration cannot be set. Client is not branded plus. Default url applied") + return null + } + + return if (appRestrictions.containsKey(AppConfigKeys.BaseUrl.key)) { + appRestrictions.getString(AppConfigKeys.BaseUrl.key) + } else { + Log_OC.d(tag, "BaseUrl configuration cannot be found, default url applied") + null + } + } +} diff --git a/app/src/main/res/drawable/file_pdf.xml b/app/src/main/res/drawable/file_pdf.xml index 8a39afb69257..af485a0487b2 100644 --- a/app/src/main/res/drawable/file_pdf.xml +++ b/app/src/main/res/drawable/file_pdf.xml @@ -11,7 +11,7 @@ android:viewportHeight="96"> diff --git a/app/src/main/res/drawable/ic_assistant.xml b/app/src/main/res/drawable/ic_assistant.xml index 152710dcfe8e..878d9e60fe70 100644 --- a/app/src/main/res/drawable/ic_assistant.xml +++ b/app/src/main/res/drawable/ic_assistant.xml @@ -1,7 +1,7 @@ diff --git a/app/src/main/res/drawable/ic_expand_less.xml b/app/src/main/res/drawable/ic_expand_less.xml new file mode 100644 index 000000000000..b175a7702956 --- /dev/null +++ b/app/src/main/res/drawable/ic_expand_less.xml @@ -0,0 +1,17 @@ + + + + diff --git a/app/src/main/res/drawable/ic_expand_more.xml b/app/src/main/res/drawable/ic_expand_more.xml index 91c193fce12a..20888ef41398 100644 --- a/app/src/main/res/drawable/ic_expand_more.xml +++ b/app/src/main/res/drawable/ic_expand_more.xml @@ -1,16 +1,17 @@ \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_global_pause.xml b/app/src/main/res/drawable/ic_global_pause.xml new file mode 100644 index 000000000000..41ac9509583a --- /dev/null +++ b/app/src/main/res/drawable/ic_global_pause.xml @@ -0,0 +1,17 @@ + + + + diff --git a/app/src/main/res/drawable/ic_global_resume.xml b/app/src/main/res/drawable/ic_global_resume.xml new file mode 100644 index 000000000000..559b4d2d7db7 --- /dev/null +++ b/app/src/main/res/drawable/ic_global_resume.xml @@ -0,0 +1,17 @@ + + + + diff --git a/app/src/main/res/layout/activity_compose.xml b/app/src/main/res/layout/activity_compose.xml index a429227b70f6..48a7b07773a1 100644 --- a/app/src/main/res/layout/activity_compose.xml +++ b/app/src/main/res/layout/activity_compose.xml @@ -2,7 +2,7 @@ diff --git a/app/src/main/res/layout/files_folder_picker.xml b/app/src/main/res/layout/files_folder_picker.xml index 14897afa9575..f1d4f53ebb5d 100644 --- a/app/src/main/res/layout/files_folder_picker.xml +++ b/app/src/main/res/layout/files_folder_picker.xml @@ -2,7 +2,7 @@ diff --git a/app/src/main/res/layout/grid_image.xml b/app/src/main/res/layout/grid_image.xml index 6799819a5ffd..69e064ac87f4 100644 --- a/app/src/main/res/layout/grid_image.xml +++ b/app/src/main/res/layout/grid_image.xml @@ -2,7 +2,7 @@ diff --git a/app/src/main/res/layout/preview_image_activity.xml b/app/src/main/res/layout/preview_image_activity.xml index a51e50a07841..35746487f5f1 100644 --- a/app/src/main/res/layout/preview_image_activity.xml +++ b/app/src/main/res/layout/preview_image_activity.xml @@ -10,16 +10,17 @@ ~ SPDX-FileCopyrightText: 2013 David A. Velasco ~ SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) --> - - + android:layout_height="match_parent" /> + ~ SPDX-FileCopyrightText: 2023 Alper Ozturk ~ SPDX-FileCopyrightText: 2020 Tobias Kaminsky ~ SPDX-FileCopyrightText: 2020 Andy Scherzinger ~ SPDX-FileCopyrightText: 2015 ownCloud Inc. @@ -15,7 +15,7 @@ android:id="@+id/top" android:layout_width="match_parent" android:layout_height="match_parent" - android:animateLayoutChanges="true"> + android:animateLayoutChanges="false"> + ~ SPDX-FileCopyrightText: 2023 Alper Ozturk ~ SPDX-FileCopyrightText: 2017 Andy Scherzinger ~ SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only --> diff --git a/app/src/main/res/layout/upload_list_header.xml b/app/src/main/res/layout/upload_list_header.xml index e8d39c5be3df..9b4a95e0ed87 100755 --- a/app/src/main/res/layout/upload_list_header.xml +++ b/app/src/main/res/layout/upload_list_header.xml @@ -2,6 +2,7 @@ diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 764a6aaa871b..5afb269d54d4 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -33,6 +33,7 @@ Voeg toe aan %1$s Geavanceerde instellingen Opnieuw delen toestaan + Proxy poort Toont één widget van dashboard Zoeken in %s Alle @@ -191,13 +192,11 @@ Kon URL niet ophalen Creëren Kan map niet aanmaken - Maak nieuw - Creëer nieuw document - Nieuwe map aanmaken - Creëer nieuwe presentatie - Creëren nieuw werkblad - Toevoegen mapinfo - maakt mapinfo + Nieuw + Nieuw document + Nieuwe map + Nieuwe presentatie + Nieuw werkblad Inloggegevens uitgeschakeld Dagelijkse back-up Te back-uppen data @@ -826,7 +825,7 @@ Ontgrendel bestand Er zijn ongelezen reacties Niet ingestelde versleuteling - Verwijderen uit favorieten + Verwijder uit favorieten Het beëindigen van het delen van dit bestand of deze map is mislukt. Kan delen niet beëindigen. Controleer of het bestand bestaat. om dit bestand niet meer te delen diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 23c92bd053ae..e4db05585561 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -34,6 +34,7 @@ Dodaj do %1$s Ustawienia zaawansowane Zezwalaj na udostępnianie dalej + Port proxy Pokazuje jeden widżet z pulpitu nawigacyjnego Szukaj w %s Wszystkie @@ -88,6 +89,8 @@ Nie znaleziono hosta %1$s nie wspiera wielu kont Nie udało się ustanowić połączenia + Anuluj logowanie + Dokończ proces logowania w przeglądarce zachowany w oryginalnym katalogu, jak jest tylko do odczytu Wysyłaj tylko przez Wi-Fi /AutoUpload @@ -199,13 +202,12 @@ Nie można pobrać adresu URL Utwórz Nie udało się utworzyć katalogu - Utwórz nowy - Utwórz nowy dokument - Utwórz nowy katalog - Utwórz nową prezentację - Utwórz nowy arkusz kalkulacyjny - Dodaj informacje o katalogu - tworzy informacje o katalogu + Nowy + Nowy dokument + Nowy katalog + Nowa prezentacja + Nowy arkusz kalkulacyjny + Dodaj opis folderu Poświadczenia wyłączone Codzienna kopia zapasowa Dane dla kopii zapasowej @@ -317,6 +319,7 @@ Błąd podczas uruchamiania aparatu Błąd podczas uruchamiania skanowania dokumentu Konta + Ilość uruchomień w ciągu 48h Utworzono Nazwa zadania Postęp @@ -399,6 +402,7 @@ Nie znaleziono pliku Nie można zsynchronizować pliku. Pokazana jest najnowsza dostępna wersja. Zmień nazwę + Ten sam plik już istnieje, nie wykryto konfliktu Wystąpił błąd podczas przywracania wersji pliku! Pomyślnie przywrócono wersję pliku. Szczegóły @@ -412,6 +416,9 @@ Nazwa pliku zawiera co najmniej jeden nieprawidłowy znak Nazwa pliku Przechowuj swoje dane bezpiecznie i pod swoją pełną kontrolą + Bezpieczna współpraca i wymiana plików + Łatwe w obsłudze poczta internetowa, kalendarz i kontakty + Udostępnianie ekranu, spotkania online i konferencje internetowe Katalog już istnieje Utwórz Brak katalogów @@ -433,6 +440,7 @@ Wszystkie pliki zostały przeniesione Prześlij dalej 4 godziny + Ta ikona wskazuje dostępność zdjęcia na żywo Nazwa spowoduje ukrycie pliku Nazwa Notatka @@ -598,13 +606,16 @@ Zmień nazwę nowej wersji Co zrobić, jeśli plik już istnieje? Dodaj konto + Synchronizacja kalendarza i kontaktów Brak w systemie F-Droid i Google Play Skonfiguruj DAVx5 (wcześniej znany jako DAVdroid) (v1.3.0 +) dla bieżącego konta + Skonfigurowano synchronizację kalendarza i kontaktów O aplikacji Szczegóły Deweloperskie Ogólne Więcej + Codzienna kopia zapasowa kalendarza i kontaktów Codzienne tworzenie kopii zapasowej kontaktów Nieoczekiwany błąd podczas konfigurowania DAVx5 (wcześniej znanego jako DAVdroid) Szyfrowanie end-to-end jest skonfigurowane! @@ -617,6 +628,8 @@ Stopka Oryginalny plik zostanie… Oryginalny plik zostanie… + Wyklucz ukryte pliki i foldery + Wyklucz ukryte Przechowuj w podkatalogach na podstawie daty Używaj podkatalogów Opcje podkatalogów @@ -648,6 +661,7 @@ Podgląd Brak lokalnego pliku do podglądu Nie można wyświetlić obrazu + Plik jest obecnie zablokowany przez innego użytkownika lub proces i dlatego nie można go usunąć. Spróbuj ponownie później. Przykro nam Prywatność Nowa nazwa @@ -696,8 +710,12 @@ Wszystkie Twoje konta w jednym miejscu Automatyczne wysyłanie + dla Twoich zdjęć i filmów + Kalendarz i kontakty Synchronizuj z DAVx5 Błąd podczas pobierania wyników wyszukiwania + Dla tego użytkownika nie skonfigurowano bezpiecznego udostępniania + Bezpieczne udostępnianie… Wybierz wszystko Ustaw katalog multimediów Wybierz jeden szablon @@ -712,6 +730,7 @@ Ustaw komunikat statusu Podczas konfigurowania szyfrowania typu end-to-end otrzymasz losowy mnemonik składający się z 12-stu słów, który będzie potrzebny do otwierania plików na innych urządzeniach. Będzie on przechowywany tylko na tym urządzeniu i można go ponownie wyświetlić na tym ekranie. Zapisz go w bezpiecznym miejscu! Udostępnij + Udostępnij i skopiuj odnośnik Udostępnianie %1$s Wygaśnie o %1$s @@ -731,6 +750,7 @@ Udostępnij link (%1$s) Ustaw datę wygaśnięcia Ustaw hasło + Ponowne udostępnianie nie jest dozwolone podczas bezpiecznego upuszczania plików Zabezpieczone hasłem Może modyfikować Upuść plik @@ -754,6 +774,7 @@ udostępniono za pomocą linku Udostępnione przez %1$s Udostępnienie nie powiodło się + Dodanie współudziału nie powiodło się. Ten plik lub folder został już udostępniony tej osobie lub grupie. Pokaż zdjęcia Pokaż filmy Zarejestruj się u usługodawcy @@ -796,6 +817,7 @@ Pełny dostęp Multimedia tylko do odczytu Obrazy + Oprogramowanie biurowe uruchamiane na własnym serwerze, które daje Ci pełną kontrolę nad swoimi danymi.\n\nCechy:\n* Łatwy, nowoczesny interfejs, dostosowany do wyglądu Twojego serwera\n* Wysyłanie plików na serwer Nextcloud\n* Udostępnianie ich innym\n* Synchronizacja ulubionych plików i katalogów\n* Przeszukiwanie wszystkich katalogów na serwerze\n* Automatyczne przesyłanie zdjęć i filmów zrobionych przez urządzenie\n* Otrzymywanie na bieżąco powiadomień\n* Obsługa wielu kont\n* Bezpieczny dostęp do danych za pomocą odcisku palca lub kodu PIN\n* Integracja z DAVx5 (wcześniej znany jako DAVdroid) dla łatwej konfiguracji kalendarza i synchronizacji kontaktów\n\nZgłaszaj problemy na https://github.com/nextcloud/android/issues i omów tę aplikację na https://help.nextcloud.com/c/clients/android\n\nNie znasz jeszcze Nextcloud? Nextcloud to prywatny serwer do synchronizacji plików i komunikacji. Jest darmowym oprogramowaniem, które możesz utrzymywać samodzielnie lub zapłacić firmie, która zrobi to za Ciebie. Dzięki temu posiadasz pełną kontrolę nad swoimi zdjęciami, kalendarzem, kontaktami, dokumentami i nad wszystkim innym.\n\nSprawdź Nextcloud na https://nextcloud.com Oprogramowanie biurowe uruchamiane na własnym hostingu, który zapewnia kontrolę.\nJest to oficjalna wersja deweloperska, zawierająca codzienną próbkę każdej nowej niesprawdzonej funkcji, która może powodować niestabilność i utratę danych. Aplikacja jest przeznaczona dla użytkowników chcących przetestować i zgłosić błędy w przypadku ich wystąpienia. Nie używaj jej do ciągłej pracy!\n\nZarówno oficjalna wersja dev jak i zwykła są dostępne na F-droid i mogą być instalowane równocześnie. Oprogramowanie biurowe uruchamiane na własnym hostingu, który zapewnia kontrolę Oprogramowanie biurowe uruchamiane na własnym hostingu, które zapewnia kontrolę (wersja deweloperska) @@ -809,6 +831,7 @@ Udostępniono Tobie \"%1$s\" %1$s udostępnił Tobie \"%2$s\" Tylko zdjęcia + Zdjęcia i filmy Tylko filmy Propozycja Znaleziono konflikty @@ -866,6 +889,8 @@ Nie można zaktualizować. Sprawdź, czy plik istnieje. do zaktualizowania tego udostępnienia Zmiana udostępniania nie powiodła się + Usuń anulowane przesyłanie + Wznów anulowane przesyłanie Wyczyść nieudane wysyłanie Ponów nieudane wysyłanie Wstrzymaj wszystkie przesyłania diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 1de92b5d710f..4d694bb0a3f3 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -34,6 +34,9 @@ Adicionar em %1$s Configurações avançadas Permitir recompartilhamento + URL base + Nome do host proxy + Porta do Proxy Mostra um widget do painel Pesquisar em %s Tudos @@ -88,6 +91,8 @@ Não foi possível encontrar o host %1$s não tem suporte a múltiplas contas Não foi possível estabelecer a conexão + Cancelar login + Conclua o processo de login no seu navegador mantido na pasta original, já que é somente leitura Enviar só por WiFi não medida /AutoEnvio @@ -172,6 +177,7 @@ Quer realmente excluir %1$s e seu conteúdo? Quer realmente excluir os itens selecionados e seus conteúdos? Somente local + A caixa de diálogo de resolução de conflitos não pode ser criada Arquivo conflitante %1$s Arquivo local Se você selecionar as duas versões, o arquivo local terá um número anexado ao seu nome. @@ -198,13 +204,13 @@ Não foi possível recuperar a URL Criar Não foi possível criar a pasta - Criar novo - Criar novo documento - Criar nova pasta - Criar nova apresentação - Criar nova planilha - Adicionar informação da pasta - cria informação da pasta + Novo + Novo documento + Nova pasta + Nova apresentação + Nova planilha + Adicionar descrição da pasta + Adiciona descrição da pasta Credenciais desabilitadas Backup diário Dados para backup @@ -399,6 +405,7 @@ Arquivo não encontrado Arquivo não pôde ser sincronizado. Mostrando a última versão disponível. Renomear + O mesmo arquivo já existe, nenhum conflito detectado Erro recuperando arquivo! Arquivo recuperado com sucesso. Detalhes @@ -657,6 +664,7 @@ Pré-visualização da imagem Não há arquivos locais para exibir Não foi possível exibir a imagem + O arquivo está atualmente bloqueado por outro usuário ou processo e, portanto, não pode ser excluído. Por favor, tente novamente mais tarde. Desculpe Privacidade Novo nome @@ -888,7 +896,7 @@ Retomar uploads cancelados Apagar envios com falha Repetir envios com falha - Alguns arquivos não existem, esses arquivos não podem ser retomados + Alguns arquivos não existem mais. Esses uploads não podem ser retomados. Pausar todos os uploads Retomar todos os uploads Não foi possível criar o arquivo local diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml index 0393ad0c6ef0..83a681226c68 100644 --- a/app/src/main/res/values-pt-rPT/strings.xml +++ b/app/src/main/res/values-pt-rPT/strings.xml @@ -174,13 +174,11 @@ Não foi possível obter o URL Criar Não foi possível criar a pasta - Criar novo - Criar novo documento - Criar nova pasta - Criar nova apresentação - Criar nova folha de cálculo - Adicionar informação da pasta - cria informação sobre a pasta + Novo + Novo documento + Nova pasta + Nova apresentação + Nova folha de calculo Credenciais desativadas Cópia de segurança diária Dados para cópia de segurança diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index a75583470fbd..bc1546566a41 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -13,6 +13,7 @@ Trimitere/Distribuire Afișare în grilă Afișare în listă + Restaurare contacte şi calendar Dosar nou Mută sau copiază Deschideţi cu @@ -29,14 +30,37 @@ Activitate Adaugă un alt link Adaugă un link public partajat nou + Adaugă o nouă încărcare securizată de fișier Adaugă la 1 %1$s Setări avansate Permite repartajarea + URL-ul de bază + Hostname-ul de proxy + Port proxy Arată un singur widget din panoul principal Caută in %s Toate + Tastează un text + Sigur doriți să ștergeți acest task? + Şterge task + Eșuat + Lista de taskuri se încarcă, așteptați + Nici un task disponibil. Selectați un tip de task pentru a crea unul nou. + Niciun task disponibil pentru tipul de task %s, puteți crea un task nou din dreapta jos. + În curs de desfășurare + Programat Terminat + A apărut o eroare la crearea taskului + Task creat cu succes + A apărut o eroare la ștergerea taskului + Task șters cu succes + Nu se poate prelua lista taskurilor, vă rugăm să verificați conexiunea la internet. + Şterge task + Nu se pot prelua tipurile taskurilor, vă rugăm să verificați conexiunea la internet. + Asistent Necunoscut + Intrare + Rezultat Contul asociat nu a fost găsit! Accesul a eșuat: %1$s Contul nu există încă pe dispozitiv @@ -67,6 +91,7 @@ Nu am putut găsi gazdă %1$s nu suporta conturi multiple Nu s-a putut stabili conexiunea + Anulați autentificarea păstrează în directorul original deoarece este disponibil doar pentru citire Se încarcă numai pe Wi-Fi nelimitat /AutoÎncărcare @@ -174,13 +199,8 @@ Nu a fost găsit URL-ul Crează Nu s-a putut crea directorul - Crează nou - Crează document nou - Creează director nou - Crează prezentare nouă - Crează foaie de calcul nouă - Adaugă informații director - crează informații director + Nou + Director nou Autentificare dezactivată Copie de rezervă zilnică Date pentru copia de rezervă @@ -217,6 +237,7 @@ Descărcarea a eșuat, autentificați-vă din nou Descărcarea a eșuat Fișierul nu mai este disponibil pe server + %1$d%% %2$s %1$d%% Se descarcă %2$s Se descarcă… %1$s descarcat @@ -227,6 +248,7 @@ Imagine de fundal a antetului sertarului Activități Toate fișierele + Asistent Favorite Media Acasă @@ -243,6 +265,7 @@ Încărcare automată Criptarea E2E nu este încă setată Nu este posibil fără conexiune la internet + Asistent Mai mult Notițe Discuție @@ -331,6 +354,7 @@ Nimic partajat încă Nu au fost găsite rezultate pentru căutarea ta director + LIVE Se încarcă… Nu este instalată nicio aplicație care să deschidă acest tip de fișiere. secunde în urmă @@ -405,6 +429,7 @@ Pictogramă pentru liste goale Pictogramă pentru widgetul panoului principal Pictogramă pentru alegerea widgetului + editat ƒ/%s ISO %s %s MP @@ -745,6 +770,8 @@ Transmisia internă nu este posibilă Vă rugăm descărcați fișierul media sau folosiți o aplicație externă. Mod strict: conexiunile HTTP nu sunt permise! + An/Lună/Zi + An/Lună An „%1$s” a fost partajat cu dumneavoastră %1$s a partajat fișierul \"%2$s\" cu tine @@ -796,7 +823,6 @@ Deblochează fișierul Există comentarii necitite Dezactivați criptarea - Ștergeți din favorite A apărut o eroare la eliminarea partajării acestui fișier sau folder. Imposibil de șters partajarea. Te rugăm să verifici dacă există fișierul. de a nu permite accesul la acest fisier @@ -851,6 +877,7 @@ Păstrați fișierul in dosarul sursă Ștergeți fișierul din dosarul sursă pentru a încărca în acest dosar + %1$d%% %2$s %1$d%% Se încarcă %2$s Încărcare… %1$s incarcat diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index e0d0b13d6bc7..2e7098c5bea7 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -34,6 +34,7 @@ Добавить в %1$s Дополнительные настройки Разрешить повторную публикацию + Порт прокси Показывает один виджет с главного экрана. Искать в %s Все @@ -199,13 +200,11 @@ Не удалось получить URL Создать Не удалось создать каталог - Создать - Создать документ - Создать папку - Создать презентацию - Создать таблицу - Добавить информацию о папке - Создание информации о папке + Новый + Новый документ + Новая папка + Новая презентация + Новая таблица Учётная запись отключена Ежедневное резервное копирование Данные для резервного копирования @@ -890,7 +889,6 @@ Продолжить отмененные загрузки Убрать неудавшиеся загрузки Повторить неудавшиеся загрузки - Некоторые файлы не существуют, эти файлы нельзя возобновить Приостановить все загрузки Возобновить все загрузки Невозможно создать локальный файл diff --git a/app/src/main/res/values-sc/strings.xml b/app/src/main/res/values-sc/strings.xml index 6fc476d640b0..9d4eb603afcc 100644 --- a/app/src/main/res/values-sc/strings.xml +++ b/app/src/main/res/values-sc/strings.xml @@ -31,6 +31,7 @@ Agiunghe a %1$s Cunfiguratziones avantzadas Permite sa re-cumpartzidura + Port serbidore intermèdiu Chirca in %s Totu Cumpletadu @@ -165,13 +166,11 @@ No at fatu a recuperare s\'URL Crea No at fatu a creare sa cartella - Crea·nde una noa - Crea archìviu nou - Crea cartella noa - Crea presentatzione noa - Crea fògiu eletrònicu nou - Agiunghe informatziones de sa cartella - creat informatziones de sa cartella + Nou + Documentu nou + Cartella noa + Presentatzione noa + Fògiu eletrònicu nou Credentziales disativadas Credentziales isballiadas Boga·nche su contu @@ -705,7 +704,7 @@ Isbloca su documentu Ddoe sunt cummentos non lèghidos Tzifradura non cunfigurada - Boga·nche·ddu dae is preferidos + Boga·nche dae preferidos Ddoe at àpidu un\'errore in s\'annullamentu de sa cumpartzidura de s\'archìviu o de sa cartella. No at fatu a annnullare sa cumpartzidura. Controlla si s\'archìviu esistit. de annullare sa cumpartzidura de custu documentu diff --git a/app/src/main/res/values-sk-rSK/strings.xml b/app/src/main/res/values-sk-rSK/strings.xml index fefecc1a83f6..cfbd7e6453f0 100644 --- a/app/src/main/res/values-sk-rSK/strings.xml +++ b/app/src/main/res/values-sk-rSK/strings.xml @@ -34,6 +34,9 @@ Pridaj do %1$s Rozšírené nastavenia Povoliť sprístupňovanie ďalej + Základná URL + Názov proxy servera + Brána proxy Zobrazí jeden widget z hlavného panela Hľadať v %s Všetko @@ -201,13 +204,11 @@ Nepodarilo sa načítať webovú adresu Vytvoriť Nemožno vytvoriť priečinok - Vytvoriť nový - Vytvoriť nový dokument - Vytvoriť nový priečinok - Vytvoriť novú prezentáciu - Vytvoriť novú tabuľku - Pridať informácie o priečinku - vytvorí informácie o priečinku + Nový + Nový dokument + Nový priečinok + Nová prezentácia + Nová tabuľka Prihlasovacie údaje zakázané Denná záloha Dáta pre zálohovanie @@ -891,7 +892,6 @@ Obnoviť zrušené nahrávania Odstrániť zlyhané nahrávania Zopakovať zlyhané nahrávania - Niektoré súbory neexistujú, tieto súbory nie je možné znova nahrať. Pozastaviť všetky nahrávania Obnoviť všetky nahrávania Nieje možné vytvoriť lokálny súbor diff --git a/app/src/main/res/values-sl/strings.xml b/app/src/main/res/values-sl/strings.xml index 5b72b578d41e..268d783630d9 100644 --- a/app/src/main/res/values-sl/strings.xml +++ b/app/src/main/res/values-sl/strings.xml @@ -33,6 +33,7 @@ Dodaj v %1$s Napredne nastavitve Dovoli nadaljnje omogočanje souporabe + Vrata posredniškega strežnika Pokaži en gradnik iz nadzorne plošče Poišči v %s Vse @@ -178,13 +179,11 @@ Naslova URL ni mogoče pridobiti Ustvari Ni mogoče ustvariti mape - Ustvari novo - Ustvari nov dokument - Ustvari novo mapo - Ustvari novo predstavitev - Ustvari novo preglednico - Dodaj podrobnosti mape - ustvari podrobnosti mape + Novo + Nov dokument + Nova mapa + Nova predstavitev + Nova preglednica Poverila so onemogočena Dnevna varnostna kopija Podatki za varnostno kopiranje diff --git a/app/src/main/res/values-sq/strings.xml b/app/src/main/res/values-sq/strings.xml index 50f7c9c863e6..53d05df3d2fb 100644 --- a/app/src/main/res/values-sq/strings.xml +++ b/app/src/main/res/values-sq/strings.xml @@ -149,13 +149,8 @@ Nuk mund të rikthej URL-në Krijo S’u krijua dot dosja - Krijo të ri - Krijo një dokument te ri - Krijo një dosje të re - Krijo një prezantim të ri - Krijo një spreadsheet të ri - Shto informacion për dosjen - Krijon informacionin e dosjeve + I ri + Dosje e re Kredencialet të pa aktivizuara Kredenciele jo të sakta Hiqe llogarinë @@ -622,7 +617,7 @@ Skedarët nuk mund të fshihen përgjithmonë! Ekzistojnë komente të palexuara Hiq enkriptimin - Hiqe nga të parapëlqyerat + Hiqeni nga të parapëlqyerat për zhbërje të ndarjes me të tjerët të kësaj kartele Mos ndarja dështoi Aksesi nëpërmjet një domaini jo të besuar. Ju lutem shikoni dokumentimin për info të mëtejshme. diff --git a/app/src/main/res/values-sr-rSP/strings.xml b/app/src/main/res/values-sr-rSP/strings.xml index e8db00c4d220..ab951cbd2351 100644 --- a/app/src/main/res/values-sr-rSP/strings.xml +++ b/app/src/main/res/values-sr-rSP/strings.xml @@ -139,10 +139,8 @@ Nije moguće preuzeti celu sliku Napravi Ne mogu da napravim fasciklu - Napravi novi dokument - Napravi novu fasciklu - Napravi novu prezentaciju - Napravi novu tabelu + Nov + Nova fascikla Akreditivi isključeni Neispravni akreditivi Ukloni nalog @@ -591,7 +589,6 @@ Fajl ne može da se obriše nepovratno! Postoje nepročitani komentari Isključi šifrovanje - Ukloni iz omiljenih da ukinete deljenje ovog fajla Uklanjanje deljenja nije uspelo Pristup kroz domen koji nije od poverenja. Pogledajte dokumentaciju za još informacija. diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index c6f5536c320a..4d50b2632a82 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -34,6 +34,9 @@ Додај у %1$s Напредне поставке Дозволи дељење даље + Базни URL + Хост име проксија + Прокси порт Приказује један виџет са контролне табле Тражи у %s Све @@ -201,13 +204,13 @@ Не могу да добавим УРЛ Направи Не могу да направим фасциклу - Направи нови - Направи нови документ - Направи нову фасциклу - Направи нову презентацију - Направи нову табелу - Додај информације о фасцикли - прави информације о фасцикли + Нов + Нови документ + Нова фасцикла + Нова презентација + Нова табела + Додај опис фолдера + Додаје опис фолдера Акредитиви искључени Дневна резервна копија Подаци који се чувају @@ -402,6 +405,7 @@ Фајл није нађен Фајл не може да се синхронизује. Приказујем последњу доступну верзију. Преименуј + Исти фајл већ постојим, није откривен конфликт Грешка при враћању верзије фајла! Успешно враћена верзија фајла. Детаљи @@ -660,6 +664,7 @@ Преглед слике Нема локалног фајла за преглед Не могу да прикажем слику + Фајл је тренутно закључао други корисник или процес, па не може да се обрише. Молимо вас покушајте касније. Извините Приватност Ново име @@ -877,7 +882,7 @@ Откључај фајл Постоје непрочитани коментари Искључи шифровање - Уклони из омиљених + Избаци из омиљених Десила се грешка при покушају укидања дељења фајла или фасцикле. Не могу да укинем дељење. Проверите да ли фајл постоји. да укинете дељење овог фајла @@ -891,7 +896,7 @@ Настави отказана отпремања Избриши неуспела отпремања Понови неуспела отпремања - Неки фајлови не постоје, неће моћи да се настави отпремање + Неки фајлови вишене постоје. Та отпремања неће моћи да се наставе. Паузирај сва отпремања Настави сва отпремања Не могу да направим локални фајл @@ -910,6 +915,7 @@ Избриши Нема доступних отпремања Отпремите нешто или укључите аутоматско отпремање. + Укљ./Искљ. развијање заглавља Разреши сукоб Локално складиште пуно Фајл није могао да се копира на локално складиште diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index ff83cad00522..c51b95718124 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -34,6 +34,9 @@ Lägg till %1$s Avancerade inställningar Tillåt dela vidare + Bas-URL + Proxy-värdnamn + Proxyport Visa en widget från dashboard Sök i %s Alla @@ -57,6 +60,7 @@ Assistent Okänd Inmatning + Utdata Associerat konto kunde inte hittas! Åtkomst misslyckades: %1$s Kontot har ännu inte lagts till på den här enheten @@ -87,6 +91,8 @@ Kunde inte hitta värden %1$s har inte stöd för multipla konton Kunde inte upprätta anslutning + Avbryt inloggning + Slutför inloggningsprocessen i din webbläsare behålls i originalmappen, eftersom den är skrivskyddad Ladda bara upp via obelastad Wi-Fi /AutomatiskUppladdning @@ -198,13 +204,13 @@ Kunde inte hämta URL Skapa Kunde inte skapa mapp - Skapa ny - Skapa nytt dokument - Skapa ny mapp - Skapa ny presentation - Skapa nytt kalkylblad - Lägg till mappinfo - skapar mappinfo + Ny + Nytt dokument + Ny mapp + Ny presentation + Nytt kalkylblad + Lägg till mappbeskrivning + Lägg till mappbeskrivning Inloggningsuppgifter inaktiverade Daglig säkerhetskopiering Data att säkerhetskopiera @@ -399,6 +405,7 @@ Filen hittades inte Filen kunde inte synkroniseras. Visar senaste tillgängliga versionen. Byt namn + Samma fil finns redan, ingen konflikt upptäckt Fel vid återställning av filversion! Lyckades återställa filversion. Detaljer @@ -658,6 +665,7 @@ Förhandsvisa bild Där finns ingen lokal fil att förhandsvisa Kan inte visa bild + Filen är för närvarande låst av en annan användare eller process och kan därför inte raderas. Försök igen senare. Tyvärr Privat Nytt namn @@ -889,7 +897,7 @@ Återuppta avbrutna uppladdningar Ta bort misslyckade uppladdningar Försök igen med misslyckade uppladdningar - Vissa filer finns inte. Dessa filer kan inte återupptas + Vissa filer finns inte längre. Dessa uppladdningar kan inte återupptas. Pausa alla uppladdningar Återuppta alla uppladdningar Kan inte skapa lokal fil @@ -908,6 +916,7 @@ Ta bort Inga uppladdningar tillgängliga Ladda upp något innehåll eller aktivera automatisk uppladdning. + Växla expansion av rubrik Lös konflikt Lokal lagring full Filen kunde inte kopieras till lokal lagringsplats diff --git a/app/src/main/res/values-th-rTH/strings.xml b/app/src/main/res/values-th-rTH/strings.xml index 1e3da07892b5..b61927779688 100644 --- a/app/src/main/res/values-th-rTH/strings.xml +++ b/app/src/main/res/values-th-rTH/strings.xml @@ -170,13 +170,8 @@ ไม่สามารถดึง URL สร้าง ไม่สามารถสร้างโฟลเดอร์ - สร้างใหม่ - สร้างเอกสารใหม่ - สร้างโฟลเดอร์ใหม่ - สร้างงานนำเสนอใหม่ - สร้างสเปรดชีตใหม่ - เพิ่มข้อมูลโฟลเดอร์ - สร้างข้อมูลโฟลเดอร์ + ใหม่ + โฟลเดอร์ใหม่ ข้อมูลประจำตัวถูกปิดใช้งาน สำรองข้อมูลรายวัน ข้อมูลที่จะสำรอง @@ -568,6 +563,7 @@ ไฟล์ที่ถูกลบ ไม่มีไฟล์ที่ถูกลบ ลบแบบถาวร + เอาออกจากรายการโปรด เพื่อเลิกแชร์ไฟล์นี้ เพื่ออัปเดตการแชร์นี้ ล้างการอัปโหลดที่ล้มเหลว diff --git a/app/src/main/res/values-tk/strings.xml b/app/src/main/res/values-tk/strings.xml index 962c6b34d48f..b4bc0c5e3ddc 100644 --- a/app/src/main/res/values-tk/strings.xml +++ b/app/src/main/res/values-tk/strings.xml @@ -157,13 +157,7 @@ URL alyp bolmady Dörediň Bukjany döredip bolmady - Täzesini dörediň - Täze resminama dörediň - Täze bukja dörediň - Täze prezentasiýany dörediň - Täze elektron tablisany dörediň - Bukja barada maglumat goşuň - bukja barada maglumatlar döredilýär + Täze papka döretmek Şahsyýet maglumatlary ýapyldy Nädogry şahsyýet maglumatlary Hasaby aýyryň @@ -669,7 +663,6 @@ Faýllary hemişelik pozup bolmaz! Okalmadyk teswirler bar Şifrlemäni açmak - Halanýanlardan aýyryň Bu faýly ýa-da bukjany paýlaşmaga synanyşanda ýalňyşlyk ýüze çykdy. Paýlaşyp bolmaýar. Faýlyň bardygyny ýa-da ýokdugyny barlaň. bu faýly paýlaşmak üçin diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index e0c3ef5096db..5a4e590d923c 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -34,6 +34,9 @@ %1$s üzerine ekle Gelişmiş ayarlar Yeniden paylaşılabilsin + Temel adres + Vekil sunucu adı + Vekil sunucu bağlantı noktası Panodan bir pano bileşeni görüntüler %s içinde arama Tümü @@ -201,13 +204,12 @@ Adres alınamadı Ekle Klasör oluşturulamadı - Yeni ekle + Yeni Yeni belge - Klasör ekle + Yeni klasör Yeni sunum Yeni çalışma sayfası - Klasör bilgileri ekle - klasör bilgilerini oluşturur + Klasör açıklaması ekle Kimlik doğrulama bilgileri kullanılmıyor Günlük yedek Yedeklenecek veriler @@ -402,6 +404,7 @@ Dosya bulunamadı Dosya eşitlenemedi. Kullanılabilen son sürüm görüntüleniyor. Yeniden adlandır + Aynı dosya zaten var. Herhangi bir çakışma bulunmadı Geri yüklenecek dosya sürümünü seçin! Dosya sürümü geri yüklendi. Ayrıntılar @@ -660,6 +663,7 @@ Görsel ön izleme Ön izlenebilecek bir yerel dosya yok Görsel görüntülenemedi + Dosya şu anda başka bir kullanıcı veya işlem tarafından kilitlenmiş olduğundan silinemez. Lütfen bir süre sonra yeniden deneyin. Ne yazık ki Gizlilik Yeni ad @@ -891,7 +895,6 @@ İptal edilen yüklemeler sürdürülsün Tamamlanamayan yüklemeler silinsin Tamamlanamayan yüklemeler yinelensin - Bazı dosyalar bulunamadı. Bu dosyalar sürdürülemez Tüm yüklemeleri duraklat Tüm yüklemeleri sürdür Yerel dosya oluşturulamadı diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 65c8f9c63aeb..9cc100f7bf5e 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -34,6 +34,9 @@ Додати до хмари %1$s Розширені Дозволяти передавати у спільний доступ іншим + Основний URL + Хост проксі-сервера + Порт проксі-сервера Показувати один віджет з панелі віджетів Пошук у %s Всі @@ -201,13 +204,13 @@ Неможливо отримати посилання Створити Неможливо створити каталог - Додати - Створити новий документ - Створити новий каталог - Створити нову презентацію - Створити нову таблицю - Додати інформацію про каталог - додавання інформації про каталог + Створити + Новий документ + Новий каталог + Нова презентація + Нова електронна таблиця + Додати опис каталогу + Додає опис каталогу Дані авторизації не активні Щоденне резервне збереження Дані користувача для резервного збереження @@ -399,6 +402,7 @@ Файл не знайдено Неможливо синхронізувати файл. Буде показано останню доступну версію. Перейменувати + Тотожній файл присутній. Не знайдено конфліктів Помилка під час відновлення версії файлу! Успішно відновлено версію файлу. Деталі @@ -650,6 +654,7 @@ Попередній перегляд зображення Відсутній файл на пристрої для попереднього показу Неможливо показати зображення + Цей файл неможливо вилучити, оскільки його заблоковано іншим користувачем або процесом. Спробуйте пізніше. Перепрошуємо Конфіденційність Нова назва @@ -719,7 +724,7 @@ Під час налаштування наскрізного шифрування ви отримаєте парольну фразу з 12 слів, за допомогою якої ви зможете відкривати ваші файлі на інших пристроях. Її буде збережено лише на цьому пристрої та буде показано знову на цьому екрані. Будь ласка, занотуйте цю фразу та збережіть у безпеченому місці! Спільний доступ Поділитися та копіювати посилання - Спільний доступ + Спільне %1$s Дійсне до %1$s Спільне %1$s @@ -880,7 +885,7 @@ Відновити раніше скасовані завантаження Очистити невдалі завантаження Повторити невдалі завантаження - Деякі файли відсутні, їхнє завантаження неможливо відновити + Окремі файли більше не доступні. Неможливо відновити ці завантаження. Призупинити всі завантаження Відновити всі завантаження Неможливо створити файл на пристрої @@ -899,6 +904,7 @@ Вилучити Ви ще нічого не завантажили Завантажте вміст або увімкніть автоматичне завантаження + Розгорнути/згорнути заголовок Розв\'язати конфлікт Локальне сховище заповнено Неможливо скопіювати файл до внутрішнього сховища diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 968796c73bf8..589f4c4d6413 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -171,12 +171,10 @@ Tạo Không thể tạo thư mục Tạo mới - Tạo tệp tài liệu mới - Tạo thư mục mới - Tạo tệp trình chiếu mới  - Tại bảng tính mới - Thêm thông tin thư mục - Tạo thông tin thư mục + Tài liệu mới + Tạo thư mục + Bản trình chiếu mới + Bảng tính mới Đã vô hiệu hóa thông tin đăng nhập Sao lưu hàng ngày Dữ liệu được sao lưu @@ -761,7 +759,6 @@ Mở khoá tập tin Có bình luận chưa đọc Bỏ đặt mã hóa - Xóa khỏi ưa thích Đã xảy ra lỗi khi cố gắng hủy chia sẻ tệp hoặc thư mục này. Không thể ngừng chia sẻ. Vui lòng kiểm tra xem tệp có tồn tại hay không. bỏ chia sẽ tập tin này diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index ff8fee2903eb..5ae41a2e93d5 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -34,18 +34,26 @@ 添加到%1$s 高级设置 允许二次共享 + 基础 URL + 代理主机名称 + 代理端口 显示仪表盘中的一个小工具 在 %s 中搜索 所有 输入一些文字 您确定要删除这些任务吗? + 删除任务 失败了 任务列表加载中,请稍等 已排程 已完成 任务已创建 任务已删除 + 删除任务 + 无法获取任务类型,请检查您的网络连接。 + 助手 未知 + 输入 输出结果 相关账号未找到! 访问已失败: %1$s @@ -77,6 +85,8 @@ 找不到主机 %1$s 不支持多个账号 无法建立连接 + 取消登录 + 请在浏览器中完成登录流程 保持为原始的文件夹,即使它是只读的 仅通过无流量限制的 Wi-Fi 上传 /自动上传 @@ -161,6 +171,7 @@ 确定删除%1$s及其相关内容? 确定删除选中项及其内容? 仅本地 + 无法创建冲突解决程序对话框 冲突文件 %1$s 本地文件 如果您选择两个版本,则本地文件的名称后面将附加一个数字。 @@ -188,12 +199,10 @@ 创建 无法创建文件夹 新建 - 新建文档 - 创建新文件夹 - 新建演示 - 新建表格 - 添加文件夹信息 - 创建文件夹信息 + 新文档 + 新建文件夹 + 新演示文稿 + 新表格 设备凭证已禁用 每日备份 要备份的数据 @@ -244,6 +253,7 @@ 抽屉标题的背景图片 动态 全部文件 + 助手 收藏 媒体文件 组文件夹 @@ -253,7 +263,7 @@ 个人文件 最近修改 已共享 - 已删除的文件 + 回收站 上传 登出 打开侧边栏 @@ -262,6 +272,7 @@ 自动上传 尚未安装端到端加密 无互联网连接 + 助手 更多 笔记 通话 @@ -386,6 +397,7 @@ 无法找到文件 文件无法同步。显示最新的可用版本。 重命名 + 相同的文件已存在,未检测到冲突 恢复文件版本时发生错误! 成功恢复文件版本。 详情 @@ -644,6 +656,7 @@ 图片预览 没有可预览的本地文件 无法显示图像 + 文件当前被其他用户或进程锁定,因此无法删除。请稍后再试。 抱歉 隐私 新名称 @@ -756,6 +769,7 @@ 通过链接共享 您已通过 %1$s 共享 新建共享失败 + 添加分享失败。此文件或文件夹已经与此人或群组共享。 显示照片 显示视频 与提供商签约 @@ -872,7 +886,7 @@ Nextcloud 是一个私有文件同步、共享和通信服务器。它是自由 新文件的缩略图 加载比预期时间要长 今天 - 已删除的文件 + 回收站 没有被删除的文件 您可以在此处恢复已删除的文件。 文件 %1$s 无法被删除! @@ -883,7 +897,7 @@ Nextcloud 是一个私有文件同步、共享和通信服务器。它是自由 解锁文件 有未读评论 不设置加密 - 从收藏列表移除 + 从收藏中移除 尝试取消共享此文件或文件夹时发生错误。 无法取消共享。请检查文件是否存在。 取消共享该文件 @@ -897,7 +911,6 @@ Nextcloud 是一个私有文件同步、共享和通信服务器。它是自由 继续已取消的上传 清除失败上传任务 重试失败上传任务 - 有些文件已经不存在,因此无法继续上传 暂停所有上传 继续所有上传 无法新建本地文件 diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index 69f0894b21b5..53fa27099a0a 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -34,6 +34,9 @@ 添加到 %1$s 進階設定 允許轉貼分享 + 基礎 URL + Proxy 伺服器名稱 + proxy代理伺服器連接埠 顯示儀表板中的一個小部件 %s內搜尋 全部 @@ -201,13 +204,13 @@ 不能取得網址 建立 無法新增資料夾 - 建立新的 - 新增文件 - 新增資料夾 - 新增投影片 - 新增資料表 - 新增資料夾資訊 - 建立資料夾資訊 + + 新文件 + 新資料夾 + 新簡報 + 新試算表 + 添加資料夾描述 + 添加資料夾描述 認證方式已取消 每日備份 要備份的數據 @@ -402,6 +405,7 @@ 找不到檔案 檔案不能同步,將顯示最新可用版本。 更名 + 相同的檔案已存在,未檢測到衝突 回復失敗! 成功地回復檔案版本 細節 @@ -654,12 +658,13 @@ 近端資料夾 遠端資料夾 佈景主題 - 暗色 - 亮色 + 深色 + 淺色 跟隨系統 圖像預覽 沒有可供預覽的近端檔案 無法顯示圖像 + 檔案目前已被其他用戶或進程鎖定,因此無法刪除。 請稍後再試。 很抱歉 私隱政策 新名稱 @@ -891,7 +896,7 @@ 繼續已取消的上傳 清除失敗的上傳 重試失敗的上傳 - 部份檔案不存在,這些檔案無法繼續 + 部份檔案不再存在。無法繼續這些上傳。 暫停所有上傳 繼續所有上傳 無法建立近端檔案 @@ -910,6 +915,7 @@ 刪除 沒有上傳的檔案 上傳一些內容或者啟動自動上傳。 + 切換標頭展開 解決抵觸 近端儲存空間已滿 無法複製檔案到近端資料夾 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index fe5f18d67535..40c475637685 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -34,6 +34,9 @@ 新增到 %1$s 進階設定 允許轉貼分享 + 基礎 Url + 代理伺服器主機名稱 + proxy代理伺服器連接埠 顯示儀表板中的一個小工具 %s內搜尋 全部 @@ -201,13 +204,13 @@ 不能取得網址 建立 無法新增資料夾 - 建立新的 - 新增文件 + 新增 + 新文字文件 新增資料夾 - 新增投影片 - 新增資料表 - 新增資料夾資訊 - 建立資料夾資訊 + 新簡報 + 新試算表 + 新增資料夾描述 + 新增資料夾描述 認證方式已取消 每日備份 要備份的資料 @@ -402,6 +405,7 @@ 找不到檔案 檔案無法同步,將顯示最新可用版本。 重新命名 + 已存在相同的檔案,未偵測到衝突 復原失敗! 成功地回復檔案版本 詳細資訊 @@ -660,6 +664,7 @@ 圖片預覽 沒有可供預覽的本機檔案 無法顯示圖片 + 檔案目前已被其他使用者或處理程序鎖定,因此無法刪除。請稍後再試。 很抱歉 隱私權 新名稱 @@ -877,7 +882,7 @@ 解鎖檔案 有未讀留言 取消加密 - 從最愛中移除 + 取消我的最愛 嘗試取消分享檔案或資料夾時發生錯誤 無法取消分享。請確認檔案是否存在。 取消分享此檔案 @@ -891,7 +896,7 @@ 繼續已取消的上傳 清除失敗的上傳 重試未成功的上傳項目 - 部份檔案不存在,這些檔案無法繼續 + 部份檔案不再存在。無法繼續這些上傳。 暫停所有上傳 繼續所有上傳 無法建立本機檔案 @@ -910,6 +915,7 @@ 刪除 沒有上傳的檔案 上傳一些內容或者啟用自動上傳。 + 切換標題展開 解決衝突 本機儲存空間已滿 無法複製檔案到本機儲存空間 diff --git a/app/src/main/res/values/setup.xml b/app/src/main/res/values/setup.xml index e6e4ba61b014..046976793481 100644 --- a/app/src/main/res/values/setup.xml +++ b/app/src/main/res/values/setup.xml @@ -43,6 +43,7 @@ true true false + false /.Contacts-Backup diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 80ed1b46b753..a9697e5e2d4f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -26,6 +26,10 @@ Assistant + Base Url + Proxy Host Name + Proxy Port + Unable to fetch task types, please check your internet connection. Unable to fetch task list, please check your internet connection. @@ -182,8 +186,11 @@ Active user Upload from… Folder name + + %1$d / %2$d - %3$s + %1$d%% + Uploading… - %1$d%% %2$s %1$d%% Uploading %2$s %1$s uploaded Upload failed @@ -212,8 +219,11 @@ Fetching server version… Waiting to upload %1$s (%2$d) + + %1$d / %2$d - %3$s + %1$d%% + Downloading… - %1$d%% %2$s %1$d%% Downloading %2$s Downloaded %1$s downloaded @@ -339,7 +349,7 @@ Please complete login process in your browser Add to favorites - Remove from favourites + Remove from favorites Set as encrypted Unset encryption Rename @@ -403,6 +413,8 @@ /InstantUpload /AutoUpload + File is currently locked by another user or process and therefore not deletable. Please try again later. + Sorry Image preview Unable to show image @@ -681,7 +693,6 @@ Hide folder Configure Configure folders - Preparing auto upload Test server connection @@ -799,7 +810,7 @@ Upload from camera Scan document from camera Upload content from other apps - Create new folder + New folder Virus detected. Upload cannot be completed! Tags Adding sharee failed @@ -865,9 +876,9 @@ Unread comments exist This icon indicates availability of live photo Failed to load document! - Create new document - Create new spreadsheet - Create new presentation + New document + New spreadsheet + New presentation Select template Filename Thumbnail @@ -893,12 +904,13 @@ Storage quota exceeded Server not available Delete entries + Toggle expansion of header Retry failed uploads Clear failed uploads Pause all uploads Resume all uploads Dismiss notification - Some files not exists those files cannot be resumed + Some files no longer exist. These uploads cannot be resumed. Resume cancelled uploads Clear cancelled uploads Upload was cancelled by user @@ -978,8 +990,8 @@ Share internal link Edit Failed to start editor - Add folder info - creates folder info + Add folder description + Adds folder description Retry to upload failed local files We couldnt locate the file on server. Another user may have deleted the file File not found. Are you sure that this file exists or has a previous conflict not been resolved? @@ -987,7 +999,7 @@ Pick which version to keep of %1$s Resolve conflict Delete - Create new + New %1$s %2$s Choose what to sync diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 7163263d6429..cefa8e7856d8 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -1,7 +1,7 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/test/java/com/nextcloud/client/jobs/ContentObserverWorkTest.kt b/app/src/test/java/com/nextcloud/client/jobs/ContentObserverWorkTest.kt index c2244a3f0c31..dd0b241a1960 100644 --- a/app/src/test/java/com/nextcloud/client/jobs/ContentObserverWorkTest.kt +++ b/app/src/test/java/com/nextcloud/client/jobs/ContentObserverWorkTest.kt @@ -46,7 +46,7 @@ class ContentObserverWorkTest { worker = ContentObserverWork( appContext = context, params = params, - syncerFolderProvider = folderProvider, + syncedFolderProvider = folderProvider, powerManagementService = powerManagementService, backgroundJobManager = backgroundJobManager ) diff --git a/appscan/build.gradle b/appscan/build.gradle index 3576c8dfb789..dba9ed55f985 100644 --- a/appscan/build.gradle +++ b/appscan/build.gradle @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2023 Álvaro Brey * SPDX-FileCopyrightText: 2023 Andy Scherzinger * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only diff --git a/build.gradle b/build.gradle index bfdb86dafa9d..8427106d3de9 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors - * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-FileCopyrightText: 2024 Alper Ozturk * SPDX-FileCopyrightText: 2023 Tobias Kaminsky * SPDX-FileCopyrightText: 2022 Álvaro Brey Vilas * SPDX-FileCopyrightText: 2016 Andy Scherzinger @@ -10,7 +10,7 @@ */ buildscript { ext { - androidLibraryVersion ="be87ba7948ba2067eaa6193b6c02f5e85b5bfdf1" + androidLibraryVersion ="689983463e1c3e169318af1a57f69797cea49be1" androidPluginVersion = '8.4.0' androidxMediaVersion = '1.3.1' androidxTestVersion = "1.5.0" @@ -21,7 +21,7 @@ buildscript { espressoVersion = "3.5.1" fidoVersion = "4.1.0-patch2" jacoco_version = '0.8.12' - kotlin_version = '1.9.23' + kotlin_version = '2.0.0' markwonVersion = "4.6.2" mockitoVersion = "4.11.0" mockitoKotlinVersion = "4.1.0" diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 619de200cb2a..5dac2fa0e5f5 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -135,7 +135,10 @@ - + + + + @@ -144,7 +147,10 @@ - + + + + @@ -169,12 +175,15 @@ + + @@ -226,7 +235,11 @@ - + + + + + @@ -772,6 +785,11 @@ + + + + + @@ -1004,6 +1022,9 @@ + + + @@ -1017,6 +1038,9 @@ + + + @@ -1589,6 +1613,11 @@ + + + + + @@ -1891,6 +1920,11 @@ + + + + + @@ -2885,6 +2919,9 @@ + + + @@ -5556,6 +5593,12 @@ + + + + + @@ -5709,6 +5752,17 @@ + + + + + + + + @@ -5717,6 +5771,14 @@ + + + + + + + + @@ -5733,6 +5795,22 @@ + + + + + + + + + + + + + + + + @@ -5749,6 +5827,14 @@ + + + + + + + + @@ -6406,6 +6492,11 @@ + + + + + @@ -6438,6 +6529,14 @@ + + + + + + + + @@ -7971,6 +8070,12 @@ + + + + + @@ -8595,6 +8700,11 @@ + + + + + @@ -8718,6 +8828,12 @@ + + + + + @@ -8733,6 +8849,12 @@ + + + + + @@ -8759,6 +8881,12 @@ + + + + + @@ -8878,6 +9006,8 @@ + @@ -8939,6 +9069,8 @@ + @@ -9081,6 +9213,11 @@ + + + + + @@ -9089,6 +9226,11 @@ + + + + + @@ -9097,6 +9239,11 @@ + + + + + @@ -9197,6 +9344,11 @@ + + + + + @@ -9229,6 +9381,11 @@ + + + + + @@ -9237,6 +9394,11 @@ + + + + + @@ -9312,6 +9474,11 @@ + + + + + @@ -9781,6 +9948,16 @@ + + + + + + + + + + @@ -10074,6 +10251,11 @@ + + + + + @@ -10174,6 +10356,14 @@ + + + + + + + + @@ -10296,6 +10486,11 @@ + + + + + @@ -10508,6 +10703,12 @@ + + + + + diff --git a/scripts/analysis/lint-results.txt b/scripts/analysis/lint-results.txt index fa64b20338d3..e53927d3c705 100644 --- a/scripts/analysis/lint-results.txt +++ b/scripts/analysis/lint-results.txt @@ -1,2 +1,2 @@ DO NOT TOUCH; GENERATED BY DRONE - Lint Report: 3 errors and 72 warnings + Lint Report: 3 errors and 70 warnings diff --git a/scripts/updateLibraryHash.sh b/scripts/updateLibraryHash.sh index 92a649a21b70..86c1a7a6778f 100755 --- a/scripts/updateLibraryHash.sh +++ b/scripts/updateLibraryHash.sh @@ -11,7 +11,7 @@ git fetch git checkout master git pull -[[ $latestCommit == "$currentCommit" ]] && exit # nothing to do +[[ $latestCommit == "$currentCommit" ]] && echo "Nothing to do. Commit is: $latestCommit" && exit # nothing to do git fetch git checkout -B update-library-"$(date +%F)" origin/master diff --git a/src/generic/fastlane/metadata/android/es-ES/full_description.txt b/src/generic/fastlane/metadata/android/es-ES/full_description.txt index e4a8751596cd..df1eb66c2e99 100644 --- a/src/generic/fastlane/metadata/android/es-ES/full_description.txt +++ b/src/generic/fastlane/metadata/android/es-ES/full_description.txt @@ -1,19 +1,19 @@ -La plataforma autoalojada de productividad que te entrega el control. +La plataforma de productividad auto hospedada que le mantiene en control. -Características: -* Interfaz sencilla y modernal, adaptada al tema de tu servidor -* Subida de archivos a tu servidor Nextcloud -*Compártelos con otros -*Mantén sincronizados tus archivos y carpetas favoritos -*Búsqueda en todas las carpetas de tu servidor -*Subida autoática para fotos y vídeos hechos con tu dispositivo -*Manténte informado con notificaciones -*Soporte de múltiples cuentas -* Acceso seguro a tus datos con huella dactilar o PIN -*Integración con DAVx5 (antes conocido como DAVdroid) para configurar fácilmente la sincronización de calendarios y contactos +Características; +* Interfaz moderna y fácil de usar adecuada al tema de su servidor +* Cargue archivos a su servidor Nextcloud +* Compártalos con otros +* Mantenga sus archivos y carpetas favoritas sincronizados +* Busque en todas las carpetas de su servidor +* Carga automática para fotos y videos capturados en su dispositivo +* Manténgase al día con notificaciones +* Soporte para múltiples cuentas +* Acceda a su información de forma segura usando su huella dactilar o PIN +* Integración con DAVx5 (anteriormente conocido como DAVdroid) para configurar fácilmente la sincronización del calendario y contactos -Por favor, informa de todos los problemas en https://github.com/nextcloud/android/issues y habla de esta app en https://help.nextcloud.com/c/clients/android +Por favor, reporte cualquier problema en https://github.com/nextcloud/android/issues y discuta acerca de esta aplicación en https://help.nextcloud.com/c/clients/android -¿Nuevo en Nextcloud? Nextcloud es un servidor privado de sincronización de archivos, para compartir y comunicarse. Es software libre y puedes alojarlo tú mismo o pagar a una compañía para que lo haga por ti. De esta forma, tú controlas tus fotos, tus datos de calendario y contactos, tus documentos y todo lo demás. +¿Es Ud. nuevo en Nextcloud? Nextcloud es un servidor privado para sincronizar y compartir archivos, y comunicación. Es un programa de uso libre, y lo puede hospedar Ud. mismo o pagar a una compañía para hacerlo por Ud. De esa manera, Ud. está en control de sus fotos, su calendario e información de sus contactos, sus documentos y todo lo demás. -Descubre Nextcloud en https://nextcloud.com \ No newline at end of file +Conozca Nextcloud en https://nextcloud.com \ No newline at end of file diff --git a/src/generic/fastlane/metadata/android/pl-PL/full_description.txt b/src/generic/fastlane/metadata/android/pl-PL/full_description.txt index 89b22a1222cc..520e1e48f0e5 100644 --- a/src/generic/fastlane/metadata/android/pl-PL/full_description.txt +++ b/src/generic/fastlane/metadata/android/pl-PL/full_description.txt @@ -1,6 +1,6 @@ -Oprogramowanie biurowe uruchamiane na własnym hostingu, który zapewnia kontrolę. +Oprogramowanie biurowe uruchamiane na własnym serwerze, które daje Ci pełną kontrolę nad swoimi danymi. -Funkcje: +Cechy: * Łatwy, nowoczesny interfejs, dostosowany do wyglądu Twojego serwera * Wysyłanie plików na serwer Nextcloud * Udostępnianie ich innym @@ -10,10 +10,10 @@ Funkcje: * Otrzymywanie na bieżąco powiadomień * Obsługa wielu kont * Bezpieczny dostęp do danych za pomocą odcisku palca lub kodu PIN -* Integracja z DAVx5 (wcześniej znany jako DAVdroid) dla łatwej konfiguracji kalendarza i synchronizacja kontaktów +* Integracja z DAVx5 (wcześniej znany jako DAVdroid) dla łatwej konfiguracji kalendarza i synchronizacji kontaktów -Zgłoś wszystkie problemy na https://github.com/nextcloud/android/issues i omów tę aplikację na https://help.nextcloud.com/c/clients/android +Zgłaszaj problemy na https://github.com/nextcloud/android/issues i omów tę aplikację na https://help.nextcloud.com/c/clients/android -Nie znasz jeszcze Nextcloud? Nextcloud to prywatny serwer do synchronizacji plików i komunikacji. Jest darmowym oprogramowaniem, które możesz hostować samodzielnie lub zapłacić firmie, która zrobi to za Ciebie. Dzięki temu posiadasz pełną kontrolę nad swoimi zdjęciami, kalendarzem, kontaktami, dokumentami i nad wszystkim innym. +Nie znasz jeszcze Nextcloud? Nextcloud to prywatny serwer do synchronizacji plików i komunikacji. Jest darmowym oprogramowaniem, które możesz utrzymywać samodzielnie lub zapłacić firmie, która zrobi to za Ciebie. Dzięki temu posiadasz pełną kontrolę nad swoimi zdjęciami, kalendarzem, kontaktami, dokumentami i nad wszystkim innym. Sprawdź Nextcloud na https://nextcloud.com \ No newline at end of file