diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 5e607ef71d83..4e44dcd5a3ae 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -32,7 +32,7 @@ jobs: with: swap-size-gb: 10 - name: Initialize CodeQL - uses: github/codeql-action/init@74483a38d39275f33fcff5f35b679b5ca4a26a99 # v2.22.5 + uses: github/codeql-action/init@66b90a5db151a8042fa97405c6cf843bbe433f7b # v2.22.7 with: languages: ${{ matrix.language }} - name: Set up JDK 17 @@ -46,4 +46,4 @@ jobs: echo "org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError" > "$HOME/.gradle/gradle.properties" ./gradlew assembleDebug - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@74483a38d39275f33fcff5f35b679b5ca4a26a99 # v2.22.5 + uses: github/codeql-action/analyze@66b90a5db151a8042fa97405c6cf843bbe433f7b # v2.22.7 diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index e6664f0039ef..3559d339b79d 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -37,6 +37,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@74483a38d39275f33fcff5f35b679b5ca4a26a99 # v2.22.5 + uses: github/codeql-action/upload-sarif@66b90a5db151a8042fa97405c6cf843bbe433f7b # v2.22.7 with: sarif_file: results.sarif diff --git a/app/build.gradle b/app/build.gradle index 1227b2cf78ad..df3e9016b7f3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -84,8 +84,8 @@ android { defaultConfig { minSdkVersion 24 - targetSdkVersion 33 - compileSdk 33 + targetSdkVersion 34 + compileSdk 34 buildConfigField 'boolean', 'CI', ciBuild.toString() buildConfigField 'boolean', 'RUNTIME_PERF_ANALYSIS', perfAnalysis.toString() @@ -246,7 +246,7 @@ dependencies { implementation 'org.apache.jackrabbit:jackrabbit-webdav:2.13.5' // remove after entire switch to lib v2 implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation 'androidx.legacy:legacy-support-v4:1.0.0' - implementation 'com.google.android.material:material:1.9.0' + implementation 'com.google.android.material:material:1.10.0' implementation 'com.jakewharton:disklrucache:2.0.2' implementation "androidx.appcompat:appcompat:$appCompatVersion" implementation 'androidx.webkit:webkit:1.7.0' diff --git a/app/src/androidTest/java/com/nextcloud/client/ActivitiesActivityIT.kt b/app/src/androidTest/java/com/nextcloud/client/ActivitiesActivityIT.kt index 9cec5f9039f1..616fc1bc9be5 100644 --- a/app/src/androidTest/java/com/nextcloud/client/ActivitiesActivityIT.kt +++ b/app/src/androidTest/java/com/nextcloud/client/ActivitiesActivityIT.kt @@ -174,7 +174,7 @@ class ActivitiesActivityIT : AbstractIT() { sut.dismissSnackbar() } - shortSleep() + longSleep() waitForIdleSync() screenshot(sut) diff --git a/app/src/huawei/java/com/owncloud/android/ui/activity/HuaweiCommunityActivity.kt b/app/src/huawei/java/com/owncloud/android/ui/activity/HuaweiCommunityActivity.kt index bc3bf0e17f79..03c453e8ca9b 100644 --- a/app/src/huawei/java/com/owncloud/android/ui/activity/HuaweiCommunityActivity.kt +++ b/app/src/huawei/java/com/owncloud/android/ui/activity/HuaweiCommunityActivity.kt @@ -20,14 +20,15 @@ */ package com.owncloud.android.ui.activity +import android.os.Bundle import android.view.View /** * Activity providing information about ways to participate in the app's development. */ class HuaweiCommunityActivity : CommunityActivity() { - override fun setupContent() { - super.setupContent() + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) binding.communityReleaseCandidatePlaystore.visibility = View.GONE } } diff --git a/app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt b/app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt index 676da368baa0..556acc642266 100644 --- a/app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt +++ b/app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt @@ -92,7 +92,7 @@ abstract class NextcloudDatabase : RoomDatabase() { INSTANCE = Room .databaseBuilder(context, NextcloudDatabase::class.java, ProviderMeta.DB_NAME) .allowMainThreadQueries() - .addLegacyMigrations(clock) + .addLegacyMigrations(clock, context) .addMigrations(RoomMigration()) .addMigrations(Migration67to68()) .addMigrations(Migration70to71()) diff --git a/app/src/main/java/com/nextcloud/client/database/migrations/LegacyMigration.kt b/app/src/main/java/com/nextcloud/client/database/migrations/LegacyMigration.kt index abd77b7c734f..d8fe46585220 100644 --- a/app/src/main/java/com/nextcloud/client/database/migrations/LegacyMigration.kt +++ b/app/src/main/java/com/nextcloud/client/database/migrations/LegacyMigration.kt @@ -22,6 +22,7 @@ package com.nextcloud.client.database.migrations +import android.content.Context import androidx.room.RoomDatabase import androidx.room.migration.Migration import androidx.sqlite.db.SupportSQLiteDatabase @@ -36,12 +37,13 @@ private const val MIN_SUPPORTED_DB_VERSION = 24 class LegacyMigration( private val from: Int, private val to: Int, - private val clock: Clock + private val clock: Clock, + private val context: Context ) : Migration(from, to) { override fun migrate(database: SupportSQLiteDatabase) { - LegacyMigrationHelper(clock) - .onUpgrade(database, from, to) + LegacyMigrationHelper(clock, context) + .tryUpgrade(database, from, to) } } @@ -52,10 +54,11 @@ class LegacyMigration( */ @Suppress("ForEachOnRange") fun RoomDatabase.Builder.addLegacyMigrations( - clock: Clock + clock: Clock, + context: Context ): RoomDatabase.Builder { (MIN_SUPPORTED_DB_VERSION until NextcloudDatabase.FIRST_ROOM_DB_VERSION - 1) - .map { from -> LegacyMigration(from, from + 1, clock) } + .map { from -> LegacyMigration(from, from + 1, clock, context) } .forEach { migration -> this.addMigrations(migration) } return this } diff --git a/app/src/main/java/com/nextcloud/client/database/migrations/LegacyMigrationHelper.java b/app/src/main/java/com/nextcloud/client/database/migrations/LegacyMigrationHelper.java index a5b8b1098c5a..af248c2c207e 100644 --- a/app/src/main/java/com/nextcloud/client/database/migrations/LegacyMigrationHelper.java +++ b/app/src/main/java/com/nextcloud/client/database/migrations/LegacyMigrationHelper.java @@ -22,6 +22,7 @@ package com.nextcloud.client.database.migrations; +import android.app.ActivityManager; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteException; @@ -31,11 +32,11 @@ import com.owncloud.android.db.ProviderMeta; import com.owncloud.android.files.services.NameCollisionPolicy; import com.owncloud.android.lib.common.utils.Log_OC; -import com.owncloud.android.providers.FileContentProvider; import java.util.Locale; import androidx.sqlite.db.SupportSQLiteDatabase; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; public class LegacyMigrationHelper { @@ -52,12 +53,29 @@ public class LegacyMigrationHelper { private static final String UPGRADE_VERSION_MSG = "OUT of the ADD in onUpgrade; oldVersion == %d, newVersion == %d"; private final Clock clock; + private final Context context; - public LegacyMigrationHelper(Clock clock) { + public LegacyMigrationHelper(Clock clock, Context context) { this.clock = clock; + this.context = context; } - public void onUpgrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) { + public void tryUpgrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) { + try { + upgrade(db, oldVersion, newVersion); + } catch (Throwable t) { + Log_OC.i(TAG, "Migration upgrade failed due to " + t); + clearStorage(); + } + } + + @SuppressFBWarnings("RV_RETURN_VALUE_IGNORED_BAD_PRACTICE") + private void clearStorage() { + context.getCacheDir().delete(); + ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE)).clearApplicationUserData(); + } + + private void upgrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) { Log_OC.i(TAG, "Entering in onUpgrade"); boolean upgraded = false; diff --git a/app/src/main/java/com/nextcloud/utils/extensions/TextViewExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/TextViewExtensions.kt new file mode 100644 index 000000000000..52ad55653b13 --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/extensions/TextViewExtensions.kt @@ -0,0 +1,32 @@ +/* + * Nextcloud Android client application + * + * @author Alper Ozturk + * Copyright (C) 2023 Alper Ozturk + * Copyright (C) 2023 Nextcloud GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.utils.extensions + +import android.text.method.LinkMovementMethod +import android.widget.TextView +import androidx.core.text.HtmlCompat + +@Suppress("NewLineAtEndOfFile") +fun TextView.setHtmlContent(value: String) { + movementMethod = LinkMovementMethod.getInstance() + text = HtmlCompat.fromHtml(value, HtmlCompat.FROM_HTML_MODE_LEGACY) +} diff --git a/app/src/main/java/com/owncloud/android/ui/activity/CommunityActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/CommunityActivity.java deleted file mode 100644 index 05662f63b7e9..000000000000 --- a/app/src/main/java/com/owncloud/android/ui/activity/CommunityActivity.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Nextcloud Android client application - * - * @author Andy Scherzinger - * @author Tobias Kaminsky - * Copyright (C) 2016 Andy Scherzinger - * Copyright (C) 2016 Nextcloud - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU AFFERO GENERAL PUBLIC LICENSE for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with this program. If not, see . - */ -package com.owncloud.android.ui.activity; - -import android.os.Bundle; -import android.text.Html; -import android.text.method.LinkMovementMethod; -import android.view.MenuItem; -import android.widget.TextView; - -import com.google.android.material.button.MaterialButton; -import com.owncloud.android.R; -import com.owncloud.android.databinding.CommunityLayoutBinding; -import com.owncloud.android.utils.DisplayUtils; - -/** - * Activity providing information about ways to participate in the app's development. - */ -public class CommunityActivity extends DrawerActivity { - - protected CommunityLayoutBinding binding; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - binding = CommunityLayoutBinding.inflate(getLayoutInflater()); - setContentView(binding.getRoot()); - - // setup toolbar - setupToolbar(); - - updateActionBarTitleAndHomeButtonByString(getString(R.string.drawer_community)); - - // setup drawer - setupDrawer(R.id.nav_community); - - setupContent(); - } - - protected void setupContent() { - binding.communityReleaseCandidateText.setMovementMethod(LinkMovementMethod.getInstance()); - - TextView contributeForumView = binding.communityContributeForumText; - contributeForumView.setMovementMethod(LinkMovementMethod.getInstance()); - contributeForumView.setText(Html.fromHtml(getString(R.string.community_contribute_forum_text) + " " + - getString(R.string.community_contribute_forum_text_link, - viewThemeUtils - .files - .primaryColorToHexString(this), - getString(R.string.help_link), - getString(R.string.community_contribute_forum_forum)))); - - TextView contributeTranslationView = binding.communityContributeTranslateText; - contributeTranslationView.setMovementMethod(LinkMovementMethod.getInstance()); - contributeTranslationView.setText(Html.fromHtml( - getString(R.string.community_contribute_translate_link, - viewThemeUtils.files.primaryColorToHexString(this), - getString(R.string.translation_link), - getString(R.string.community_contribute_translate_translate)) + " " + - getString(R.string.community_contribute_translate_text))); - - TextView contributeGithubView = binding.communityContributeGithubText; - contributeGithubView.setMovementMethod(LinkMovementMethod.getInstance()); - contributeGithubView.setText(Html.fromHtml( - getString(R.string.community_contribute_github_text, - getString(R.string.community_contribute_github_text_link, - viewThemeUtils.files.primaryColorToHexString(this), - getString(R.string.contributing_link))))); - - MaterialButton reportButton = binding.communityTestingReport; - viewThemeUtils.material.colorMaterialButtonPrimaryFilled(reportButton); - reportButton.setOnClickListener(v -> DisplayUtils.startLinkIntent(this, R.string.report_issue_empty_link)); - - binding.communityBetaFdroid.setOnClickListener( - l -> DisplayUtils.startLinkIntent(this, R.string.fdroid_beta_link)); - - binding.communityReleaseCandidateFdroid.setOnClickListener( - l -> DisplayUtils.startLinkIntent(this, R.string.fdroid_link)); - - binding.communityReleaseCandidatePlaystore.setOnClickListener( - l -> DisplayUtils.startLinkIntent(this, R.string.play_store_register_beta)); - - binding.communityBetaApk.setOnClickListener( - l -> DisplayUtils.startLinkIntent(this, R.string.beta_apk_link)); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - boolean retval = true; - if (item.getItemId() == android.R.id.home) { - if (isDrawerOpen()) { - closeDrawer(); - } else { - openDrawer(); - } - } else { - retval = super.onOptionsItemSelected(item); - } - return retval; - } - - @Override - protected void onResume() { - super.onResume(); - - setDrawerMenuItemChecked(R.id.nav_community); - } -} 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 new file mode 100644 index 000000000000..28dc94c67f10 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/activity/CommunityActivity.kt @@ -0,0 +1,146 @@ +/* + * Nextcloud Android client application + * + * @author Andy Scherzinger + * @author Tobias Kaminsky + * Copyright (C) 2016 Andy Scherzinger + * Copyright (C) 2016 Nextcloud + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this program. If not, see . + */ +package com.owncloud.android.ui.activity + +import android.os.Bundle +import android.text.method.LinkMovementMethod +import android.view.MenuItem +import com.nextcloud.utils.extensions.setHtmlContent +import com.owncloud.android.R +import com.owncloud.android.databinding.CommunityLayoutBinding +import com.owncloud.android.utils.DisplayUtils + +/** + * Activity providing information about ways to participate in the app's development. + */ +open class CommunityActivity : DrawerActivity() { + lateinit var binding: CommunityLayoutBinding + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = CommunityLayoutBinding.inflate(layoutInflater) + setContentView(binding.root) + + setupToolbar() + updateActionBarTitleAndHomeButtonByString(getString(R.string.drawer_community)) + + setupDrawer(R.id.nav_community) + binding.communityReleaseCandidateText.movementMethod = LinkMovementMethod.getInstance() + setupContributeForumView() + setupContributeTranslationView() + setupContributeGithubView() + setupReportButton() + setOnClickListeners() + } + + private fun setupContributeForumView() { + val htmlContent = getString(R.string.community_contribute_forum_text) + " " + + getString( + R.string.community_contribute_forum_text_link, + viewThemeUtils.files + .primaryColorToHexString(this), + getString(R.string.help_link), + getString(R.string.community_contribute_forum_forum) + ) + binding.communityContributeForumText.setHtmlContent(htmlContent) + } + + private fun setupContributeTranslationView() { + val htmlContent = getString( + R.string.community_contribute_translate_link, + viewThemeUtils.files.primaryColorToHexString(this), + getString(R.string.translation_link), + getString(R.string.community_contribute_translate_translate) + ) + " " + + getString(R.string.community_contribute_translate_text) + binding.communityContributeTranslateText.setHtmlContent(htmlContent) + } + + private fun setupContributeGithubView() { + val htmlContent = getString( + R.string.community_contribute_github_text, + getString( + R.string.community_contribute_github_text_link, + viewThemeUtils.files.primaryColorToHexString(this), + getString(R.string.contributing_link) + ) + ) + binding.communityContributeGithubText.setHtmlContent(htmlContent) + } + + private fun setupReportButton() { + val reportButton = binding.communityTestingReport + viewThemeUtils.material.colorMaterialButtonPrimaryFilled(reportButton) + reportButton.setOnClickListener { + DisplayUtils.startLinkIntent( + this, + R.string.report_issue_empty_link + ) + } + } + + private fun setOnClickListeners() { + binding.communityBetaFdroid.setOnClickListener { + DisplayUtils.startLinkIntent( + this, + R.string.fdroid_beta_link + ) + } + binding.communityReleaseCandidateFdroid.setOnClickListener { + DisplayUtils.startLinkIntent( + this, + R.string.fdroid_link + ) + } + binding.communityReleaseCandidatePlaystore.setOnClickListener { + DisplayUtils.startLinkIntent( + this, + R.string.play_store_register_beta + ) + } + binding.communityBetaApk.setOnClickListener { + DisplayUtils.startLinkIntent( + this, + R.string.beta_apk_link + ) + } + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + var retval = true + if (item.itemId == android.R.id.home) { + if (isDrawerOpen) { + closeDrawer() + } else { + openDrawer() + } + } else { + retval = super.onOptionsItemSelected(item) + } + return retval + } + + override fun onResume() { + super.onResume() + setDrawerMenuItemChecked(R.id.nav_community) + } +} diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ContactsPreferenceActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/ContactsPreferenceActivity.java index 074de51a1069..a819c25fbdff 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/ContactsPreferenceActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/ContactsPreferenceActivity.java @@ -36,6 +36,7 @@ import javax.inject.Inject; +import androidx.activity.OnBackPressedCallback; import androidx.drawerlayout.widget.DrawerLayout; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; @@ -115,6 +116,8 @@ protected void onCreate(Bundle savedInstanceState) { } transaction.commit(); } + + getOnBackPressedDispatcher().addCallback(this, onBackPressedCallback); } @Override @@ -137,12 +140,14 @@ public void onTransferStateChanged(OCFile file, boolean downloading, boolean upl // not needed } - @Override - public void onBackPressed() { - if (getSupportFragmentManager().findFragmentByTag(BackupListFragment.TAG) != null) { - getSupportFragmentManager().popBackStack(BACKUP_TO_LIST, FragmentManager.POP_BACK_STACK_INCLUSIVE); - } else { - finish(); + private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(true) { + @Override + public void handleOnBackPressed() { + if (getSupportFragmentManager().findFragmentByTag(BackupListFragment.TAG) != null) { + getSupportFragmentManager().popBackStack(BACKUP_TO_LIST, FragmentManager.POP_BACK_STACK_INCLUSIVE); + } else { + finish(); + } } - } + }; } 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 cb7013cd1ce2..16478cd5b628 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 @@ -2507,8 +2507,7 @@ private void openFile(User user, String fileId) { setUser(user); if (fileId == null) { - dismissLoadingDialog(); - DisplayUtils.showSnackMessage(this, getString(R.string.error_retrieving_file)); + onFileRequestError(null); return; } @@ -2529,8 +2528,7 @@ private void openFileByPath(User user, String filepath) { setUser(user); if (filepath == null) { - dismissLoadingDialog(); - DisplayUtils.showSnackMessage(this, getString(R.string.error_retrieving_file)); + onFileRequestError(null); return; } @@ -2544,8 +2542,7 @@ private void openFileByPath(User user, String filepath) { try { client = clientFactory.create(user); } catch (ClientFactory.CreationException e) { - dismissLoadingDialog(); - DisplayUtils.showSnackMessage(this, getString(R.string.error_retrieving_file)); + onFileRequestError(null); return; } @@ -2554,9 +2551,17 @@ private void openFileByPath(User user, String filepath) { client, storageManager, user); - asyncRunner.postQuickTask(getRemoteFileTask, this::onFileRequestResult, null); + asyncRunner.postQuickTask(getRemoteFileTask, this::onFileRequestResult, this::onFileRequestError); + } + + private Unit onFileRequestError(Throwable throwable) { + dismissLoadingDialog(); + DisplayUtils.showSnackMessage(this, getString(R.string.error_retrieving_file)); + Log_OC.e(TAG, "Requesting file from remote failed!", throwable); + return null; } + private Unit onFileRequestResult(GetRemoteFileTask.Result result) { dismissLoadingDialog(); diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FilePickerActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/FilePickerActivity.kt similarity index 55% rename from app/src/main/java/com/owncloud/android/ui/activity/FilePickerActivity.java rename to app/src/main/java/com/owncloud/android/ui/activity/FilePickerActivity.kt index 029bb13a82ec..7ed93b2e2be2 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FilePickerActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/FilePickerActivity.kt @@ -18,40 +18,29 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +package com.owncloud.android.ui.activity -package com.owncloud.android.ui.activity; - -import android.os.Bundle; -import android.view.View; - -import com.owncloud.android.R; -import com.owncloud.android.ui.fragment.OCFileListFragment; - -import androidx.fragment.app.FragmentTransaction; +import android.os.Bundle +import com.owncloud.android.R +import com.owncloud.android.ui.fragment.OCFileListFragment /** * File picker of remote files */ -public class FilePickerActivity extends FolderPickerActivity { - - @Override - public void onClick(View v) { - super.onClick(v); - } +class FilePickerActivity : FolderPickerActivity() { - @Override - protected void createFragments() { - OCFileListFragment listOfFiles = new OCFileListFragment(); - Bundle args = new Bundle(); - args.putBoolean(OCFileListFragment.ARG_ONLY_FOLDERS_CLICKABLE, true); - args.putBoolean(OCFileListFragment.ARG_HIDE_FAB, true); - args.putBoolean(OCFileListFragment.ARG_HIDE_ITEM_OPTIONS, true); - args.putBoolean(OCFileListFragment.ARG_SEARCH_ONLY_FOLDER, false); - args.putBoolean(OCFileListFragment.ARG_FILE_SELECTABLE, true); - args.putString(OCFileListFragment.ARG_MIMETYPE, getIntent().getStringExtra(OCFileListFragment.ARG_MIMETYPE)); - listOfFiles.setArguments(args); - FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - transaction.add(R.id.fragment_container, listOfFiles, TAG_LIST_OF_FOLDERS); - transaction.commit(); + override fun createFragments() { + val listOfFiles = OCFileListFragment() + val args = Bundle() + args.putBoolean(OCFileListFragment.ARG_ONLY_FOLDERS_CLICKABLE, true) + args.putBoolean(OCFileListFragment.ARG_HIDE_FAB, true) + args.putBoolean(OCFileListFragment.ARG_HIDE_ITEM_OPTIONS, true) + args.putBoolean(OCFileListFragment.ARG_SEARCH_ONLY_FOLDER, false) + args.putBoolean(OCFileListFragment.ARG_FILE_SELECTABLE, true) + args.putString(OCFileListFragment.ARG_MIMETYPE, intent.getStringExtra(OCFileListFragment.ARG_MIMETYPE)) + listOfFiles.arguments = args + val transaction = supportFragmentManager.beginTransaction() + transaction.add(R.id.fragment_container, listOfFiles, TAG_LIST_OF_FOLDERS) + transaction.commit() } } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FolderPickerActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/FolderPickerActivity.kt index 6e829d547b1e..fced4e7cda99 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FolderPickerActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/FolderPickerActivity.kt @@ -32,6 +32,7 @@ import android.view.ActionMode import android.view.Menu import android.view.MenuItem import android.view.View +import androidx.activity.OnBackPressedCallback import androidx.localbroadcastmanager.content.LocalBroadcastManager import com.google.android.material.button.MaterialButton import com.nextcloud.client.di.Injectable @@ -125,9 +126,33 @@ open class FolderPickerActivity : // sets message for empty list of folders setBackgroundText() + + handleOnBackPressed() + Log_OC.d(TAG, "onCreate() end") } + private fun handleOnBackPressed() { + onBackPressedDispatcher.addCallback( + this, + object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + val listOfFiles = listOfFilesFragment + if (listOfFiles != null) { + // should never be null, indeed + val levelsUp = listOfFiles.onBrowseUp() + if (levelsUp == 0) { + finish() + return + } + file = listOfFiles.currentFile + updateUiElements() + } + } + } + ) + } + override fun onActionModeStarted(mode: ActionMode) { super.onActionModeStarted(mode) if (account != null) { @@ -321,20 +346,6 @@ open class FolderPickerActivity : } } - override fun onBackPressed() { - val listOfFiles = listOfFilesFragment - if (listOfFiles != null) { - // should never be null, indeed - val levelsUp = listOfFiles.onBrowseUp() - if (levelsUp == 0) { - finish() - return - } - file = listOfFiles.currentFile - updateUiElements() - } - } - private fun updateUiElements() { toggleChooseEnabled() updateNavigationElementsInActionBar() @@ -605,6 +616,7 @@ open class FolderPickerActivity : const val MOVE_OR_COPY = "MOVE_OR_COPY" const val CHOOSE_LOCATION = "CHOOSE_LOCATION" private val TAG = FolderPickerActivity::class.java.simpleName - protected const val TAG_LIST_OF_FOLDERS = "LIST_OF_FOLDERS" + + const val TAG_LIST_OF_FOLDERS = "LIST_OF_FOLDERS" } } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/NotificationsActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/NotificationsActivity.java deleted file mode 100644 index 21bc674acc86..000000000000 --- a/app/src/main/java/com/owncloud/android/ui/activity/NotificationsActivity.java +++ /dev/null @@ -1,373 +0,0 @@ -/* - * Nextcloud Android client application - * - * @author Andy Scherzinger - * @author Mario Danic - * @author Chris Narkiewicz - * Copyright (C) 2017 Andy Scherzinger - * Copyright (C) 2017 Mario Danic - * Copyright (C) 2020 Chris Narkiewicz - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.owncloud.android.ui.activity; - -import android.os.Bundle; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; - -import com.google.android.material.snackbar.Snackbar; -import com.nextcloud.client.account.User; -import com.nextcloud.client.account.UserAccountManager; -import com.nextcloud.client.jobs.NotificationWork; -import com.nextcloud.client.network.ClientFactory; -import com.nextcloud.java.util.Optional; -import com.owncloud.android.R; -import com.owncloud.android.databinding.NotificationsLayoutBinding; -import com.owncloud.android.datamodel.ArbitraryDataProvider; -import com.owncloud.android.datamodel.ArbitraryDataProviderImpl; -import com.owncloud.android.lib.common.OwnCloudClient; -import com.owncloud.android.lib.common.operations.RemoteOperationResult; -import com.owncloud.android.lib.common.utils.Log_OC; -import com.owncloud.android.lib.resources.notifications.GetNotificationsRemoteOperation; -import com.owncloud.android.lib.resources.notifications.models.Notification; -import com.owncloud.android.ui.adapter.NotificationListAdapter; -import com.owncloud.android.ui.asynctasks.DeleteAllNotificationsTask; -import com.owncloud.android.ui.notifications.NotificationsContract; -import com.owncloud.android.utils.DisplayUtils; -import com.owncloud.android.utils.PushUtils; - -import java.util.List; - -import javax.inject.Inject; - -import androidx.annotation.VisibleForTesting; -import androidx.recyclerview.widget.LinearLayoutManager; - -/** - * Activity displaying all server side stored notification items. - */ -public class NotificationsActivity extends DrawerActivity implements NotificationsContract.View { - - private static final String TAG = NotificationsActivity.class.getSimpleName(); - - private NotificationsLayoutBinding binding; - private NotificationListAdapter adapter; - private Snackbar snackbar; - private OwnCloudClient client; - private Optional optionalUser; - - @Inject ClientFactory clientFactory; - - @Override - protected void onCreate(Bundle savedInstanceState) { - Log_OC.v(TAG, "onCreate() start"); - super.onCreate(savedInstanceState); - - binding = NotificationsLayoutBinding.inflate(getLayoutInflater()); - setContentView(binding.getRoot()); - - optionalUser = getUser(); - - // use account from intent (opened via android notification can have a different account than current one) - if (getIntent() != null && getIntent().getExtras() != null) { - String accountName = getIntent().getExtras().getString(NotificationWork.KEY_NOTIFICATION_ACCOUNT); - if (accountName != null && optionalUser.isPresent()) { - User user = optionalUser.get(); - if (user.getAccountName().equalsIgnoreCase(accountName)) { - accountManager.setCurrentOwnCloudAccount(accountName); - setUser(getUserAccountManager().getUser()); - optionalUser = getUser(); - } - } - } - - // setup toolbar - setupToolbar(); - - updateActionBarTitleAndHomeButtonByString(getString(R.string.drawer_item_notifications)); - - viewThemeUtils.androidx.themeSwipeRefreshLayout(binding.swipeContainingList); - viewThemeUtils.androidx.themeSwipeRefreshLayout(binding.swipeContainingEmpty); - - // setup drawer - setupDrawer(R.id.nav_notifications); - - if (!optionalUser.isPresent()) { - // show error - runOnUiThread(() -> setEmptyContent( - getString(R.string.notifications_no_results_headline), - getString(R.string.account_not_found)) - ); - return; - } - - binding.swipeContainingList.setOnRefreshListener(() -> { - setLoadingMessage(); - binding.swipeContainingList.setRefreshing(true); - fetchAndSetData(); - }); - - binding.swipeContainingEmpty.setOnRefreshListener(() -> { - setLoadingMessageEmpty(); - fetchAndSetData(); - }); - - setupPushWarning(); - setupContent(); - } - - private void setupPushWarning() { - if (!getResources().getBoolean(R.bool.show_push_warning)) { - return; - } - if (snackbar != null) { - if (!snackbar.isShown()) { - snackbar.show(); - } - } else { - String pushUrl = getResources().getString(R.string.push_server_url); - - if (pushUrl.isEmpty()) { - snackbar = Snackbar.make(binding.emptyList.emptyListView, - R.string.push_notifications_not_implemented, - Snackbar.LENGTH_INDEFINITE); - } else { - final ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProviderImpl(this); - final String accountName = optionalUser.isPresent() ? optionalUser.get().getAccountName() : ""; - final boolean usesOldLogin = arbitraryDataProvider.getBooleanValue(accountName, - UserAccountManager.ACCOUNT_USES_STANDARD_PASSWORD); - - if (usesOldLogin) { - snackbar = Snackbar.make(binding.emptyList.emptyListView, - R.string.push_notifications_old_login, - Snackbar.LENGTH_INDEFINITE); - } else { - String pushValue = arbitraryDataProvider.getValue(accountName, PushUtils.KEY_PUSH); - - if (pushValue == null || pushValue.isEmpty()) { - snackbar = Snackbar.make(binding.emptyList.emptyListView, - R.string.push_notifications_temp_error, - Snackbar.LENGTH_INDEFINITE); - } - } - } - - if (snackbar != null && !snackbar.isShown()) { - snackbar.show(); - } - } - } - - @Override - public void openDrawer() { - super.openDrawer(); - - if (snackbar != null && snackbar.isShown()) { - snackbar.dismiss(); - } - } - - @Override - public void closeDrawer() { - super.closeDrawer(); - - setupPushWarning(); - } - - /** - * sets up the UI elements and loads all notification items. - */ - private void setupContent() { - binding.emptyList.emptyListIcon.setImageResource(R.drawable.ic_notification); - setLoadingMessageEmpty(); - - LinearLayoutManager layoutManager = new LinearLayoutManager(this); - - binding.list.setLayoutManager(layoutManager); - - fetchAndSetData(); - } - - @VisibleForTesting - public void populateList(List notifications) { - initializeAdapter(); - adapter.setNotificationItems(notifications); - binding.loadingContent.setVisibility(View.GONE); - - if (notifications.size() > 0) { - binding.swipeContainingEmpty.setVisibility(View.GONE); - binding.swipeContainingList.setVisibility(View.VISIBLE); - } else { - setEmptyContent( - getString(R.string.notifications_no_results_headline), - getString(R.string.notifications_no_results_message) - ); - binding.swipeContainingList.setVisibility(View.GONE); - binding.swipeContainingEmpty.setVisibility(View.VISIBLE); - } - } - - private void fetchAndSetData() { - Thread t = new Thread(() -> { - initializeAdapter(); - - GetNotificationsRemoteOperation getRemoteNotificationOperation = new GetNotificationsRemoteOperation(); - final RemoteOperationResult> result = getRemoteNotificationOperation.execute(client); - - if (result.isSuccess() && result.getResultData() != null) { - runOnUiThread(() -> populateList(result.getResultData())); - } else { - Log_OC.d(TAG, result.getLogMessage()); - // show error - runOnUiThread(() -> setEmptyContent(getString(R.string.notifications_no_results_headline), result.getLogMessage())); - } - - hideRefreshLayoutLoader(); - }); - - t.start(); - } - - private void initializeClient() { - if (client == null && optionalUser.isPresent()) { - try { - User user = optionalUser.get(); - client = clientFactory.create(user); - } catch (ClientFactory.CreationException e) { - Log_OC.e(TAG, "Error initializing client", e); - } - } - } - - private void initializeAdapter() { - initializeClient(); - if (adapter == null) { - adapter = new NotificationListAdapter(client, this, viewThemeUtils); - binding.list.setAdapter(adapter); - } - } - - private void hideRefreshLayoutLoader() { - runOnUiThread(() -> { - binding.swipeContainingList.setRefreshing(false); - binding.swipeContainingEmpty.setRefreshing(false); - }); - } - - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.activity_notifications, menu); - - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - boolean retval = true; - - int itemId = item.getItemId(); - if (itemId == android.R.id.home) { - if (isDrawerOpen()) { - closeDrawer(); - } else { - openDrawer(); - } - } else if (itemId == R.id.action_empty_notifications) { - new DeleteAllNotificationsTask(client, this).execute(); - } else { - retval = super.onOptionsItemSelected(item); - } - - return retval; - } - - private void setLoadingMessage() { - binding.swipeContainingEmpty.setVisibility(View.GONE); - } - - @VisibleForTesting - public void setLoadingMessageEmpty() { - binding.swipeContainingList.setVisibility(View.GONE); - binding.emptyList.emptyListView.setVisibility(View.GONE); - binding.loadingContent.setVisibility(View.VISIBLE); - } - - @VisibleForTesting - public void setEmptyContent(String headline, String message) { - binding.swipeContainingList.setVisibility(View.GONE); - binding.loadingContent.setVisibility(View.GONE); - binding.swipeContainingEmpty.setVisibility(View.VISIBLE); - binding.emptyList.emptyListView.setVisibility(View.VISIBLE); - - binding.emptyList.emptyListViewHeadline.setText(headline); - binding.emptyList.emptyListViewText.setText(message); - binding.emptyList.emptyListIcon.setImageResource(R.drawable.ic_notification); - - binding.emptyList.emptyListViewText.setVisibility(View.VISIBLE); - binding.emptyList.emptyListIcon.setVisibility(View.VISIBLE); - } - - @Override - protected void onResume() { - super.onResume(); - setDrawerMenuItemChecked(R.id.nav_notifications); - } - - @Override - public void onRemovedNotification(boolean isSuccess) { - if (!isSuccess) { - DisplayUtils.showSnackMessage(this, getString(R.string.remove_notification_failed)); - fetchAndSetData(); - } - } - - @Override - public void removeNotification(NotificationListAdapter.NotificationViewHolder holder) { - adapter.removeNotification(holder); - - if (adapter.getItemCount() == 0) { - setEmptyContent(getString(R.string.notifications_no_results_headline), getString(R.string.notifications_no_results_message)); - binding.swipeContainingList.setVisibility(View.GONE); - binding.loadingContent.setVisibility(View.GONE); - binding.swipeContainingEmpty.setVisibility(View.VISIBLE); - } - } - - @Override - public void onRemovedAllNotifications(boolean isSuccess) { - if (isSuccess) { - adapter.removeAllNotifications(); - setEmptyContent(getString(R.string.notifications_no_results_headline), getString(R.string.notifications_no_results_message)); - binding.loadingContent.setVisibility(View.GONE); - binding.swipeContainingList.setVisibility(View.GONE); - binding.swipeContainingEmpty.setVisibility(View.VISIBLE); - } else { - DisplayUtils.showSnackMessage(this, getString(R.string.clear_notifications_failed)); - } - } - - @Override - public void onActionCallback(boolean isSuccess, - Notification notification, - NotificationListAdapter.NotificationViewHolder holder) { - if (isSuccess) { - adapter.removeNotification(holder); - } else { - adapter.setButtons(holder, notification); - DisplayUtils.showSnackMessage(this, getString(R.string.notification_action_failed)); - } - } -} diff --git a/app/src/main/java/com/owncloud/android/ui/activity/NotificationsActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/NotificationsActivity.kt new file mode 100644 index 000000000000..f0e5ec2cd874 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/activity/NotificationsActivity.kt @@ -0,0 +1,376 @@ +/* + * Nextcloud Android client application + * + * @author Andy Scherzinger + * @author Mario Danic + * @author Chris Narkiewicz + * Copyright (C) 2017 Andy Scherzinger + * Copyright (C) 2017 Mario Danic + * Copyright (C) 2020 Chris Narkiewicz + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package com.owncloud.android.ui.activity + +import android.os.Bundle +import android.view.Menu +import android.view.MenuItem +import android.view.View +import androidx.annotation.VisibleForTesting +import androidx.recyclerview.widget.LinearLayoutManager +import com.google.android.material.snackbar.Snackbar +import com.nextcloud.client.account.User +import com.nextcloud.client.account.UserAccountManager +import com.nextcloud.client.jobs.NotificationWork +import com.nextcloud.client.network.ClientFactory.CreationException +import com.nextcloud.java.util.Optional +import com.owncloud.android.R +import com.owncloud.android.databinding.NotificationsLayoutBinding +import com.owncloud.android.datamodel.ArbitraryDataProvider +import com.owncloud.android.datamodel.ArbitraryDataProviderImpl +import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.utils.Log_OC +import com.owncloud.android.lib.resources.notifications.GetNotificationsRemoteOperation +import com.owncloud.android.lib.resources.notifications.models.Notification +import com.owncloud.android.ui.adapter.NotificationListAdapter +import com.owncloud.android.ui.adapter.NotificationListAdapter.NotificationViewHolder +import com.owncloud.android.ui.asynctasks.DeleteAllNotificationsTask +import com.owncloud.android.ui.notifications.NotificationsContract +import com.owncloud.android.utils.DisplayUtils +import com.owncloud.android.utils.PushUtils + +/** + * Activity displaying all server side stored notification items. + */ +class NotificationsActivity : DrawerActivity(), NotificationsContract.View { + + private lateinit var binding: NotificationsLayoutBinding + + private var adapter: NotificationListAdapter? = null + private var snackbar: Snackbar? = null + private var client: OwnCloudClient? = null + private var optionalUser: Optional? = null + + override fun onCreate(savedInstanceState: Bundle?) { + Log_OC.v(TAG, "onCreate() start") + + super.onCreate(savedInstanceState) + + binding = NotificationsLayoutBinding.inflate(layoutInflater) + setContentView(binding.root) + + optionalUser = user + + intent?.let { + it.extras?.let { bundle -> + setupUser(bundle) + } + } + + setupToolbar() + updateActionBarTitleAndHomeButtonByString(getString(R.string.drawer_item_notifications)) + setupDrawer(R.id.nav_notifications) + + if (optionalUser?.isPresent == false) { + showError() + } + + setupContainingList() + setupPushWarning() + setupContent() + } + + private fun setupContainingList() { + viewThemeUtils.androidx.themeSwipeRefreshLayout(binding.swipeContainingList) + viewThemeUtils.androidx.themeSwipeRefreshLayout(binding.swipeContainingEmpty) + binding.swipeContainingList.setOnRefreshListener { + setLoadingMessage() + binding.swipeContainingList.isRefreshing = true + fetchAndSetData() + } + binding.swipeContainingEmpty.setOnRefreshListener { + setLoadingMessageEmpty() + fetchAndSetData() + } + } + + private fun setupUser(bundle: Bundle) { + val accountName = bundle.getString(NotificationWork.KEY_NOTIFICATION_ACCOUNT) + + if (accountName != null && optionalUser?.isPresent == true) { + val user = optionalUser?.get() + if (user?.accountName.equals(accountName, ignoreCase = true)) { + accountManager.setCurrentOwnCloudAccount(accountName) + setUser(userAccountManager.user) + optionalUser = getUser() + } + } + } + + private fun showError() { + runOnUiThread { + setEmptyContent( + getString(R.string.notifications_no_results_headline), + getString(R.string.account_not_found) + ) + } + return + } + + private fun setupPushWarning() { + if (!resources.getBoolean(R.bool.show_push_warning)) { + return + } + + if (snackbar != null) { + if (snackbar?.isShown == false) { + snackbar?.show() + } + } else { + val pushUrl = resources.getString(R.string.push_server_url) + if (pushUrl.isEmpty()) { + snackbar = Snackbar.make( + binding.emptyList.emptyListView, + R.string.push_notifications_not_implemented, + Snackbar.LENGTH_INDEFINITE + ) + } else { + val arbitraryDataProvider: ArbitraryDataProvider = ArbitraryDataProviderImpl(this) + val accountName: String = if (optionalUser?.isPresent == true) { + optionalUser?.get()?.accountName ?: "" + } else { + "" + } + val usesOldLogin = arbitraryDataProvider.getBooleanValue( + accountName, + UserAccountManager.ACCOUNT_USES_STANDARD_PASSWORD + ) + + if (usesOldLogin) { + snackbar = Snackbar.make( + binding.emptyList.emptyListView, + R.string.push_notifications_old_login, + Snackbar.LENGTH_INDEFINITE + ) + } else { + val pushValue = arbitraryDataProvider.getValue(accountName, PushUtils.KEY_PUSH) + if (pushValue.isEmpty()) { + snackbar = Snackbar.make( + binding.emptyList.emptyListView, + R.string.push_notifications_temp_error, + Snackbar.LENGTH_INDEFINITE + ) + } + } + } + + if (snackbar != null && snackbar?.isShown == false) { + snackbar?.show() + } + } + } + + override fun openDrawer() { + super.openDrawer() + if (snackbar != null && snackbar?.isShown == true) { + snackbar?.dismiss() + } + } + + override fun closeDrawer() { + super.closeDrawer() + setupPushWarning() + } + + /** + * sets up the UI elements and loads all notification items. + */ + private fun setupContent() { + binding.emptyList.emptyListIcon.setImageResource(R.drawable.ic_notification) + setLoadingMessageEmpty() + val layoutManager = LinearLayoutManager(this) + binding.list.layoutManager = layoutManager + fetchAndSetData() + } + + @VisibleForTesting + fun populateList(notifications: List?) { + initializeAdapter() + adapter?.setNotificationItems(notifications) + binding.loadingContent.visibility = View.GONE + + if (notifications?.isNotEmpty() == true) { + binding.swipeContainingEmpty.visibility = View.GONE + binding.swipeContainingList.visibility = View.VISIBLE + } else { + setEmptyContent( + getString(R.string.notifications_no_results_headline), + getString(R.string.notifications_no_results_message) + ) + binding.swipeContainingList.visibility = View.GONE + binding.swipeContainingEmpty.visibility = View.VISIBLE + } + } + + private fun fetchAndSetData() { + val t = Thread { + initializeAdapter() + val getRemoteNotificationOperation = GetNotificationsRemoteOperation() + val result = getRemoteNotificationOperation.execute(client) + if (result.isSuccess && result.resultData != null) { + runOnUiThread { populateList(result.resultData) } + } else { + Log_OC.d(TAG, result.logMessage) + // show error + runOnUiThread { + setEmptyContent( + getString(R.string.notifications_no_results_headline), + result.logMessage + ) + } + } + hideRefreshLayoutLoader() + } + t.start() + } + + private fun initializeClient() { + if (client == null && optionalUser?.isPresent == true) { + try { + val user = optionalUser?.get() + client = clientFactory.create(user) + } catch (e: CreationException) { + Log_OC.e(TAG, "Error initializing client", e) + } + } + } + + private fun initializeAdapter() { + initializeClient() + if (adapter == null) { + adapter = NotificationListAdapter(client, this, viewThemeUtils) + binding.list.adapter = adapter + } + } + + private fun hideRefreshLayoutLoader() { + runOnUiThread { + binding.swipeContainingList.isRefreshing = false + binding.swipeContainingEmpty.isRefreshing = false + } + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + menuInflater.inflate(R.menu.activity_notifications, menu) + return true + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + var retval = true + val itemId = item.itemId + if (itemId == android.R.id.home) { + if (isDrawerOpen) { + closeDrawer() + } else { + openDrawer() + } + } else if (itemId == R.id.action_empty_notifications) { + DeleteAllNotificationsTask(client, this).execute() + } else { + retval = super.onOptionsItemSelected(item) + } + return retval + } + + private fun setLoadingMessage() { + binding.swipeContainingEmpty.visibility = View.GONE + } + + @VisibleForTesting + fun setLoadingMessageEmpty() { + binding.swipeContainingList.visibility = View.GONE + binding.emptyList.emptyListView.visibility = View.GONE + binding.loadingContent.visibility = View.VISIBLE + } + + @VisibleForTesting + fun setEmptyContent(headline: String?, message: String?) { + binding.swipeContainingList.visibility = View.GONE + binding.loadingContent.visibility = View.GONE + binding.swipeContainingEmpty.visibility = View.VISIBLE + binding.emptyList.emptyListView.visibility = View.VISIBLE + binding.emptyList.emptyListViewHeadline.text = headline + binding.emptyList.emptyListViewText.text = message + binding.emptyList.emptyListIcon.setImageResource(R.drawable.ic_notification) + binding.emptyList.emptyListViewText.visibility = View.VISIBLE + binding.emptyList.emptyListIcon.visibility = View.VISIBLE + } + + override fun onResume() { + super.onResume() + setDrawerMenuItemChecked(R.id.nav_notifications) + } + + override fun onRemovedNotification(isSuccess: Boolean) { + if (!isSuccess) { + DisplayUtils.showSnackMessage(this, getString(R.string.remove_notification_failed)) + fetchAndSetData() + } + } + + override fun removeNotification(holder: NotificationViewHolder) { + adapter?.removeNotification(holder) + if (adapter?.itemCount == 0) { + setEmptyContent( + getString(R.string.notifications_no_results_headline), + getString(R.string.notifications_no_results_message) + ) + binding.swipeContainingList.visibility = View.GONE + binding.loadingContent.visibility = View.GONE + binding.swipeContainingEmpty.visibility = View.VISIBLE + } + } + + override fun onRemovedAllNotifications(isSuccess: Boolean) { + if (isSuccess) { + adapter?.removeAllNotifications() + setEmptyContent( + getString(R.string.notifications_no_results_headline), + getString(R.string.notifications_no_results_message) + ) + binding.loadingContent.visibility = View.GONE + binding.swipeContainingList.visibility = View.GONE + binding.swipeContainingEmpty.visibility = View.VISIBLE + } else { + DisplayUtils.showSnackMessage(this, getString(R.string.clear_notifications_failed)) + } + } + + override fun onActionCallback( + isSuccess: Boolean, + notification: Notification, + holder: NotificationViewHolder + ) { + if (isSuccess) { + adapter?.removeNotification(holder) + } else { + adapter?.setButtons(holder, notification) + DisplayUtils.showSnackMessage(this, getString(R.string.notification_action_failed)) + } + } + + companion object { + private val TAG = NotificationsActivity::class.java.simpleName + } +} diff --git a/app/src/main/java/com/owncloud/android/ui/activity/UploadFilesActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/UploadFilesActivity.java index bff39ae57e0e..44942b1b3a63 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/UploadFilesActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/UploadFilesActivity.java @@ -61,6 +61,7 @@ import javax.inject.Inject; +import androidx.activity.OnBackPressedCallback; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import androidx.appcompat.app.ActionBar; @@ -258,6 +259,8 @@ public void onNothingSelected(AdapterView parent) { checkWritableFolder(mCurrentDir); + getOnBackPressedDispatcher().addCallback(this, onBackPressedCallback); + Log_OC.d(TAG, "onCreate() end"); } @@ -369,43 +372,45 @@ private boolean isSearchOpen() { } } - @Override - public void onBackPressed() { - if (isSearchOpen() && mSearchView != null) { - mSearchView.setQuery("", false); - mFileListFragment.onClose(); - mSearchView.onActionViewCollapsed(); - setDrawerIndicatorEnabled(isDrawerIndicatorAvailable()); - } else { - if (mDirectories.getCount() <= SINGLE_DIR) { - finish(); - return; - } + private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(true) { + @Override + public void handleOnBackPressed() { + if (isSearchOpen() && mSearchView != null) { + mSearchView.setQuery("", false); + mFileListFragment.onClose(); + mSearchView.onActionViewCollapsed(); + setDrawerIndicatorEnabled(isDrawerIndicatorAvailable()); + } else { + if (mDirectories.getCount() <= SINGLE_DIR) { + finish(); + return; + } - File parentFolder = mCurrentDir.getParentFile(); - if (!parentFolder.canRead()) { - checkLocalStoragePathPickerPermission(); - return; - } + File parentFolder = mCurrentDir.getParentFile(); + if (!parentFolder.canRead()) { + checkLocalStoragePathPickerPermission(); + return; + } - popDirname(); - mFileListFragment.onNavigateUp(); - mCurrentDir = mFileListFragment.getCurrentDirectory(); - checkWritableFolder(mCurrentDir); + popDirname(); + mFileListFragment.onNavigateUp(); + mCurrentDir = mFileListFragment.getCurrentDirectory(); + checkWritableFolder(mCurrentDir); - if (mCurrentDir.getParentFile() == null) { - ActionBar actionBar = getSupportActionBar(); - if (actionBar != null) { - actionBar.setDisplayHomeAsUpEnabled(false); + if (mCurrentDir.getParentFile() == null) { + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + actionBar.setDisplayHomeAsUpEnabled(false); + } } - } - // invalidate checked state when navigating directories - if (!mLocalFolderPickerMode) { - setSelectAllMenuItem(mOptionsMenu.findItem(R.id.action_select_all), false); + // invalidate checked state when navigating directories + if (!mLocalFolderPickerMode) { + setSelectAllMenuItem(mOptionsMenu.findItem(R.id.action_select_all), false); + } } } - } + }; @Override protected void onSaveInstanceState(@NonNull Bundle outState) { 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 0298ef49bc0e..821d44ac3c7a 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 @@ -62,6 +62,8 @@ import com.owncloud.android.operations.RefreshFolderOperation; import com.owncloud.android.ui.activity.ConflictsResolveActivity; import com.owncloud.android.ui.activity.FileActivity; +import com.owncloud.android.ui.activity.FileDisplayActivity; +import com.owncloud.android.ui.preview.PreviewImageFragment; import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.MimeTypeUtil; import com.owncloud.android.utils.theme.ViewThemeUtils; @@ -346,12 +348,15 @@ public void onBindViewHolder(SectionedViewHolder holder, int section, int relati itemViewHolder.binding.uploadRightButton.setOnClickListener(v -> removeUpload(item)); } itemViewHolder.binding.uploadRightButton.setVisibility(View.VISIBLE); - } else { // UploadStatus.UPLOAD_SUCCESS + } else { // UploadStatus.UPLOAD_SUCCEEDED itemViewHolder.binding.uploadRightButton.setVisibility(View.INVISIBLE); } itemViewHolder.binding.uploadListItemLayout.setOnClickListener(null); + // Set icon or thumbnail + itemViewHolder.binding.thumbnail.setImageResource(R.drawable.file); + // click on item if (item.getUploadStatus() == UploadStatus.UPLOAD_FAILED) { final UploadResult uploadResult = item.getLastResult(); @@ -381,12 +386,15 @@ public void onBindViewHolder(SectionedViewHolder holder, int section, int relati ); } }); - } else { - itemViewHolder.binding.uploadListItemLayout.setOnClickListener(v -> onUploadItemClick(item)); + } else if (item.getUploadStatus() == UploadStatus.UPLOAD_SUCCEEDED){ + itemViewHolder.binding.uploadListItemLayout.setOnClickListener(v -> onUploadedItemClick(item)); } - // Set icon or thumbnail - itemViewHolder.binding.thumbnail.setImageResource(R.drawable.file); + + // click on thumbnail to open locally + if (item.getUploadStatus() != UploadStatus.UPLOAD_SUCCEEDED){ + itemViewHolder.binding.thumbnail.setOnClickListener(v -> onUploadingItemClick(item)); + } /* * Cancellation needs do be checked and done before changing the drawable in fileIcon, or @@ -738,7 +746,10 @@ public final void loadUploadItemsFromDb() { notifyDataSetChanged(); } - private void onUploadItemClick(OCUpload file) { + /** + * Open local file. + */ + private void onUploadingItemClick(OCUpload file) { File f = new File(file.getLocalPath()); if (!f.exists()) { DisplayUtils.showSnackMessage(parentActivity, R.string.local_file_not_found_message); @@ -747,6 +758,30 @@ private void onUploadItemClick(OCUpload file) { } } + /** + * Open remote file. + */ + private void onUploadedItemClick(OCUpload upload) { + final OCFile file = parentActivity.getStorageManager().getFileByEncryptedRemotePath(upload.getRemotePath()); + if (file == null){ + DisplayUtils.showSnackMessage(parentActivity, R.string.error_retrieving_file); + Log_OC.i(TAG, "Could not find uploaded file on remote."); + return; + } + + if (PreviewImageFragment.canBePreviewed(file)){ + //show image preview and stay in uploads tab + Intent intent = FileDisplayActivity.openFileIntent(parentActivity, parentActivity.getUser().get(), file); + parentActivity.startActivity(intent); + }else{ + Intent intent = new Intent(parentActivity, FileDisplayActivity.class); + intent.setAction(Intent.ACTION_VIEW); + intent.putExtra(FileDisplayActivity.KEY_FILE_PATH, upload.getRemotePath()); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + parentActivity.startActivity(intent); + } + } + /** * Open file with app associates with its MIME type. If MIME type unknown, show list with all apps. diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index d74e02132488..3874b9b1c127 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -325,7 +325,6 @@ حذف خطأ في استرداد النشاطات للملف حدث خطأ في تحميل التفاصيل - تنزيل \u0020 الملف حفظ قم برفع بعض المحتوى أو زامن مع أجهزتك diff --git a/app/src/main/res/values-cs-rCZ/strings.xml b/app/src/main/res/values-cs-rCZ/strings.xml index 0aa6761dc318..179ba7581d29 100644 --- a/app/src/main/res/values-cs-rCZ/strings.xml +++ b/app/src/main/res/values-cs-rCZ/strings.xml @@ -325,7 +325,6 @@ Smazat Při načítání aktivit u souboru došlo k chybě Nepodařilo se načíst podrobnosti. - Stahování \u0020 Soubor Ponechat Nahrajte nějaký obsah, nebo synchronizujte s vašimi zařízeními. diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 5c024cac6f24..cf4e85d7e5d1 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -325,7 +325,6 @@ Slet Fejl ved indlæsning af aktiviteter for fil Fejl ved indlæsning af detaljer - Downloader \u0020 Fil Behold Upload indhold eller synkronisér med dine enheder. diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index e6c62482f210..fe479e5e4852 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -325,7 +325,6 @@ Löschen Fehler beim Abrufen der Aktivitäten für die Datei Fehler beim Laden der Details - Herunterladen \u0020 Datei Behalten Laden Sie Inhalt hoch oder synchronisieren Sie mit Ihren Geräten. diff --git a/app/src/main/res/values-es-rCL/strings.xml b/app/src/main/res/values-es-rCL/strings.xml index d2e5257b43f5..a8ab730dfe97 100644 --- a/app/src/main/res/values-es-rCL/strings.xml +++ b/app/src/main/res/values-es-rCL/strings.xml @@ -239,6 +239,7 @@ Crear No hay carpetas aquí Seleccionar + Mover No se te permite %s para copiar este archivo para crear este archivo diff --git a/app/src/main/res/values-es-rSV/strings.xml b/app/src/main/res/values-es-rSV/strings.xml index 9652d8bf0e11..c4dbc29b1b1d 100644 --- a/app/src/main/res/values-es-rSV/strings.xml +++ b/app/src/main/res/values-es-rSV/strings.xml @@ -540,6 +540,7 @@ Aguarda un momento… Verificando credenciales almacenadas Copiando el archivo desde almacenamiento privado + Actualizar Imagen de qué es nuevo Omitir Nuevo en %1$s diff --git a/app/src/main/res/values-et-rEE/strings.xml b/app/src/main/res/values-et-rEE/strings.xml index 845e7deb5e81..ddf8681eea42 100644 --- a/app/src/main/res/values-et-rEE/strings.xml +++ b/app/src/main/res/values-et-rEE/strings.xml @@ -525,6 +525,7 @@ Lae alla Oota üks hetk… Faili kopeerimine privaatsest salvestusalast + Uuenda Jäta vahele Mis on su staatus? Saada kiri diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index d88f97b2deb7..0cb6b3e19c1e 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -393,6 +393,7 @@ Ez dago karpetarik hemen Aukeratu Aukeratu helburuko karpeta + Kopiatu Mugitu Ez daukazu baimenik %s fitxategi hau kopiatzeko @@ -943,6 +944,7 @@ Itxaron momentu bat… Gordetako nortasun-datuak konprobatzen Fitxategia biltegiratze pribatutik kopiatzen + Eguneratu Zer da irudi berria Salto egin Berria %1$s-n diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index bf89788c8ad4..ef26443935f6 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -393,6 +393,7 @@ هیچ پوشه ای اینجا وجود ندارد انتخاب کردن پوشهٔ هدف را انتخاب کنید + رونوشت انتقال شما مجاز نیستید%s کپی این فایل @@ -943,6 +944,7 @@ یک لحظه صبر کنید... بررسی اعتبارنامه‌های ذخیره شده کپی کردن فایل از حافظه خصوصی + به‌روز رسانی چه تصویر جدیدی است رد شدن جدید در %1$s diff --git a/app/src/main/res/values-fi-rFI/strings.xml b/app/src/main/res/values-fi-rFI/strings.xml index 0352263ba128..d516221c7bc0 100644 --- a/app/src/main/res/values-fi-rFI/strings.xml +++ b/app/src/main/res/values-fi-rFI/strings.xml @@ -381,6 +381,7 @@ Ei kansioita täällä Valitse Valitse kohdekansio + Kopioi Siirrä Sinulla ei ole oikeutta %s kopioida tämä tiedosto @@ -906,6 +907,7 @@ GNU yleinen lisenssi, versio 2 Odota hetki… Tarkistetaan tallennettuja tilitietoja Kopioidaan tiedostoa yksityisestä tallennustilasta + Päivitä Mitä uutta -kuva Ohita Uutta versiossa %1$s diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 0c0202b9fcaf..03e2d5ca0eb2 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -327,7 +327,6 @@ Attention, la suppression est irréversible. Supprimer Erreur lors de la récupération de l’activité du fichier Impossible de charger les détails - Téléchargement de \u0020 Fichier Conserver Déposez du contenu ou synchronisez vos appareils. diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index 5117e01527ce..0c6f74392c0f 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -325,7 +325,6 @@ Eliminar Produciuse un erro ao recuperar actividades para o ficheiro Produciuse un fallo ao cargar os detalles - Descargando \u0020 Ficheiro Conservar Envíe algún contido ou sincronice cos seus dispositivos. diff --git a/app/src/main/res/values-hu-rHU/strings.xml b/app/src/main/res/values-hu-rHU/strings.xml index a68e13ba8ad4..41bd2a80b1a8 100644 --- a/app/src/main/res/values-hu-rHU/strings.xml +++ b/app/src/main/res/values-hu-rHU/strings.xml @@ -325,7 +325,6 @@ Törlés Hiba a fájl tevékenységeinek lekérésekor A részletek betöltése sikertelen - Letöltés \u0020 Fájl Megtartás Töltsön fel új tartalmat vagy szinkronizáljon az eszközeivel diff --git a/app/src/main/res/values-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml index e33b3fbc7606..1671fe22e9a4 100644 --- a/app/src/main/res/values-nb-rNO/strings.xml +++ b/app/src/main/res/values-nb-rNO/strings.xml @@ -18,6 +18,7 @@ Kopier Ny mappe Flytt + Flytt eller kopier Åpne med Søk Detaljer @@ -425,6 +426,13 @@ Roter mot klokken Roter med klokken Kan ikke endre bildet. + Fildetaljer + Betingelser for å ta bilder + ƒ/%s + ISO %s + %s MP + %s mm + %s s i mappen %1$s Last også opp eksisterende filer Bare last opp under lading @@ -505,6 +513,7 @@ Ingen kalender finnes Ingen program for å behandle e-post adresse Ingen gjenstander + Ingen app tilgjengelig for å håndtere kart Kun én konto er tillatt Ingen app tilgjengelig for å håndtere PDF Ingen program for å sende valgt fil @@ -591,7 +600,9 @@ Avtrykk Opprinnelig fil vil bli… Opprinnelig fil vil bli… + Lagre i undermapper basert på dato Bruk undermapper + Alternativer for undermappe Legg til ende-til-ende -kryptering på denne klienten Lisens Appsikkerhet @@ -781,6 +792,8 @@ Intern strøming ikke mulig Vennligst last ned media i stedet, eller bruk ekstern app. Streng modus: ingen HTTP-tilkobling tillatt! + År/Måned/Dag + År/Måned År \"%1$s\" er blitt delt med deg %1$s delte \"%2$s\" med deg @@ -932,7 +945,9 @@ Vent et øyeblikk… Sjekker lagrede påloggingsdetaljer Kopierer fil fra privat lager + Vennligst oppdater Android systemets WebView-app for pålogging Oppdater + Oppdater Android systemets WebView Hva er nytt-bilde Hopp over Nytt i %1$s diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index e6198a5a30bf..bce95d1a0843 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -18,6 +18,7 @@ Kopiuj Nowy katalog Przenieś + Przenieś lub kopiuj Otwórz za pomocą Wyszukaj Szczegóły @@ -944,7 +945,9 @@ Proszę czekać… Sprawdzanie danych Kopiowanie pliku z prywatnego magazynu + Aby się zalogować, zaktualizuj aplikację WebView systemu Android Aktualizuj + Zaktualizuj WebView systemu Android Jaki jest nowy obraz Pomiń Co nowego w %1$s diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index c406d92a0234..09b3d3537913 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -325,7 +325,6 @@ Удалить Ошибка получения истории событий, связанных с файлом Не удалось получить подробные сведения - Скачивание \u0020 Файл Сохранить Добавьте что-нибудь или синхронизируйте со своими устройствами! diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index 36ad0cb6c782..295eeef96f77 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -325,7 +325,6 @@ Обриши Грешка при добављању активности за фајл Грешка при учитавању детаља - Преузима се \u0020 Фајл Задржи Отпремите неки садржај или синхронизујте са вашим уређајима. diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 23cb5966995a..84ab136db76d 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -325,7 +325,6 @@ Ta bort Fel vid hämtning av aktiviteter för fil Kunde inte läsa in detaljer - Laddar ner \u0020 Fil Behåll Ladda upp något eller synkronisera med dina enheter diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 3384b39a1c82..cc435ed7d9d9 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -325,7 +325,6 @@ Sil Dosya işlemleri alınırken sorun çıktı Ayrıntılar yüklenemedi - İndiriliyor \u0020 Dosya Tut Bazı içerikler yükleyin ya da aygıtlarınızla eşitleyin. diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 8ed39ee92810..c66b1fde903f 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -323,7 +323,6 @@ Вилучити Помилка з отриманням дії для файлу Не вдалося завантажити подробиці - Звантаження \u0020 Файл Зберегти Додати дані або синхронізувати з вашими пристроями. diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 8a2916edbac9..46513658720c 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -325,7 +325,6 @@ 删除 获取文件动态时出错 加载详情失败 - 下载中 \u0020 文件 保留 上传一些内容或与您的设备同步。 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 52f42a306548..8f94353c5cb3 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -325,7 +325,6 @@ 刪除 取得檔案活動時發生錯誤 載入詳細資訊失敗 - 正在下載 \u0020 檔案 保留 上傳一些內容或與您的裝置同步。 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d552f8c69bc1..4c293c2b934c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -162,7 +162,6 @@ Fetching server version… Waiting to upload %1$s (%2$d) - Downloading \u0020 Downloading… %1$d%% Downloading %2$s Downloaded diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index c8383be19632..ec14ded2e15b 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -70,7 +70,7 @@ @color/text_color @color/text_color @color/hwSecurityRed - @style/Widget.MaterialComponents.TextInputEditText.OutlinedBox + @style/Widget.Material3.TextInputEditText.OutlinedBox - @@ -363,7 +363,7 @@ @@ -371,9 +371,9 @@ @color/white @color/white @color/hwSecurityRed - @style/TextAppearance.MaterialComponents.Subtitle1 - @style/TextAppearance.MaterialComponents.Caption - @style/Widget.MaterialComponents.TextInputEditText.OutlinedBox + @style/TextAppearance.Material3.BodyLarge + @style/TextAppearance.Material3.BodySmall + @style/Widget.Material3.TextInputEditText.OutlinedBox - - diff --git a/app/src/test/java/com/nextcloud/client/core/LocalConnectionTest.kt b/app/src/test/java/com/nextcloud/client/core/LocalConnectionTest.kt index 5a9380573ed0..f1f77f43e845 100644 --- a/app/src/test/java/com/nextcloud/client/core/LocalConnectionTest.kt +++ b/app/src/test/java/com/nextcloud/client/core/LocalConnectionTest.kt @@ -65,7 +65,7 @@ class LocalConnectionTest { // THEN // no binding is performed - verify(exactly = 0) { context.bindService(any(), any(), any()) } + verify(exactly = 0) { context.bindService(any(), any(), Context.BIND_AUTO_CREATE) } } @Test @@ -76,12 +76,12 @@ class LocalConnectionTest { // WHEN // bind requested - every { context.bindService(mockIntent, any(), any()) } returns true + every { context.bindService(mockIntent!!, any(), Context.BIND_AUTO_CREATE) } returns true connection.bind() // THEN // service bound - verify { context.bindService(mockIntent, any(), any()) } + verify { context.bindService(mockIntent!!, any(), Context.BIND_AUTO_CREATE) } } @Test diff --git a/gradle.properties b/gradle.properties index 85bc2ba7da37..5b47fafb59fc 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,5 +9,10 @@ android.nonTransitiveRClass=false android.nonFinalResIds=false #android.debug.obsoleteApi=true -# Minimum max heap space to get reliable builds -org.gradle.jvmargs=-Xmx1g + +# JVM arguments to optimize heap usage, enable heap dump on out-of-memory errors, and set the file encoding +org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=1g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 +kotlin.daemon.jvmargs=-Xmx4096m +org.gradle.caching=true +org.gradle.parallel=true +org.gradle.configureondemand=true diff --git a/scripts/analysis/lint-results.txt b/scripts/analysis/lint-results.txt index b8212f767a28..17a46e780670 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: 75 warnings + Lint Report: 74 warnings