From 9ceacc85f3ba13c21e8273e5d872c36546257ebc Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Tue, 10 Nov 2026 13:49:50 +0100 Subject: [PATCH 1/4] Rename .java to .kt Signed-off-by: alperozturk96 --- .../android/ui/activity/{FileActivity.java => FileActivity.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/src/main/java/com/owncloud/android/ui/activity/{FileActivity.java => FileActivity.kt} (100%) 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.kt similarity index 100% rename from app/src/main/java/com/owncloud/android/ui/activity/FileActivity.java rename to app/src/main/java/com/owncloud/android/ui/activity/FileActivity.kt From cc0e54767955bdec7227f0206cf1750c5efff921 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Tue, 10 Nov 2026 13:49:51 +0100 Subject: [PATCH 2/4] refactor(file-activity): convert to kotlin Signed-off-by: alperozturk96 --- .../nextcloud/client/FileDisplayActivityIT.kt | 4 +- .../client/editimage/EditImageActivity.kt | 14 +- .../activity/ContactsPreferenceActivity.java | 7 + .../android/ui/activity/FileActivity.kt | 1446 +++++++---------- .../ui/activity/FileDisplayActivity.kt | 32 +- .../ui/activity/FolderPickerActivity.kt | 5 +- .../ui/activity/ManageAccountsActivity.kt | 7 +- .../android/ui/dialog/SendShareDialog.kt | 5 +- .../ui/fragment/ExtendedListFragment.kt | 2 +- .../ui/fragment/FileDetailSharingFragment.kt | 19 +- .../android/ui/fragment/OCFileListFragment.kt | 2 +- .../fragment/albums/AlbumOperationListener.kt | 2 +- .../ui/helpers/FileOperationsHelper.kt | 8 +- .../ui/preview/media/PreviewMediaActivity.kt | 6 +- 14 files changed, 671 insertions(+), 888 deletions(-) diff --git a/app/src/androidTest/java/com/nextcloud/client/FileDisplayActivityIT.kt b/app/src/androidTest/java/com/nextcloud/client/FileDisplayActivityIT.kt index 00d7165d1380..8dbffad7e74c 100644 --- a/app/src/androidTest/java/com/nextcloud/client/FileDisplayActivityIT.kt +++ b/app/src/androidTest/java/com/nextcloud/client/FileDisplayActivityIT.kt @@ -139,7 +139,7 @@ class FileDisplayActivityIT : AbstractOnServerIT() { val test = storageManager.getFileByPath("/test/") sut.file = test sut.startSyncFolderOperation(test, false) - assertEquals(storageManager.getFileByPath("/test/"), sut.currentDir) + assertEquals(storageManager.getFileByPath("/test/"), sut.getCurrentDir()) EspressoIdlingResource.decrement() // open drawer @@ -150,7 +150,7 @@ class FileDisplayActivityIT : AbstractOnServerIT() { .perform(NavigationViewActions.navigateTo(R.id.nav_all_files)) // then should be in root again - assertEquals(storageManager.getFileByPath("/"), sut.currentDir) + assertEquals(storageManager.getFileByPath("/"), sut.getCurrentDir()) } } } diff --git a/app/src/main/java/com/nextcloud/client/editimage/EditImageActivity.kt b/app/src/main/java/com/nextcloud/client/editimage/EditImageActivity.kt index 9663c6fec9a3..2e41ece8ed16 100644 --- a/app/src/main/java/com/nextcloud/client/editimage/EditImageActivity.kt +++ b/app/src/main/java/com/nextcloud/client/editimage/EditImageActivity.kt @@ -43,7 +43,7 @@ class EditImageActivity : Injectable { private lateinit var binding: ActivityEditImageBinding - private lateinit var file: OCFile + private lateinit var imageFile: OCFile private lateinit var format: Bitmap.CompressFormat companion object { @@ -68,12 +68,12 @@ class EditImageActivity : binding = ActivityEditImageBinding.inflate(layoutInflater) setContentView(binding.root) - file = intent.extras?.getParcelableArgument(EXTRA_FILE, OCFile::class.java) + imageFile = intent.extras?.getParcelableArgument(EXTRA_FILE, OCFile::class.java) ?: throw IllegalArgumentException("Missing file argument") setSupportActionBar(binding.toolbar) supportActionBar?.apply { - title = file.fileName + title = imageFile.fileName setDisplayHomeAsUpEnabled(true) } @@ -91,7 +91,7 @@ class EditImageActivity : return } val resultUri = result.getUriFilePath(this, false) - val newFileName = file.fileName.substring(0, file.fileName.lastIndexOf('.')) + + val newFileName = imageFile.fileName.substring(0, imageFile.fileName.lastIndexOf('.')) + " " + getString(R.string.image_editor_file_edited_suffix) + resultUri?.substring(resultUri.lastIndexOf('.')) @@ -99,7 +99,7 @@ class EditImageActivity : val data = OCUploadLocalPathData( user = storageManager.user, localPaths = arrayOf(it), - remotePaths = arrayOf(file.parentRemotePath + File.separator + newFileName), + remotePaths = arrayOf(imageFile.parentRemotePath + File.separator + newFileName), createRemoteFolder = false, creationType = UploadFileOperation.CREATED_BY_USER, requiresWifi = false, @@ -171,10 +171,10 @@ class EditImageActivity : cropper.setOnSetImageUriCompleteListener(this) cropper.setOnCropImageCompleteListener(this) - cropper.setImageUriAsync(file.storageUri) + cropper.setImageUriAsync(imageFile.storageUri) // determine output file format - format = when (file.mimeType) { + format = when (imageFile.mimeType) { MimeType.PNG -> Bitmap.CompressFormat.PNG MimeType.WEBP -> { 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 15f27b5c2eb6..bb3f1b647ca9 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 @@ -17,6 +17,8 @@ import com.nextcloud.utils.extensions.IntentExtensionsKt; import com.owncloud.android.R; import com.owncloud.android.datamodel.OCFile; +import com.owncloud.android.lib.common.operations.RemoteOperation; +import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.ui.fragment.FileFragment; import com.owncloud.android.ui.fragment.contactsbackup.BackupFragment; import com.owncloud.android.ui.fragment.contactsbackup.BackupListFragment; @@ -129,6 +131,11 @@ public void onTransferStateChanged(OCFile file, boolean downloading, boolean upl // not needed } + @Override + public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) { + super.onRemoteOperationFinish(operation, result); + } + private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(true) { @Override public void handleOnBackPressed() { diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.kt index ac256cd2375c..8643ca7eb3f3 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.kt @@ -1,1019 +1,791 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2021-2026 TSI-mc - * SPDX-FileCopyrightText: 2022 Álvaro Brey - * SPDX-FileCopyrightText: 2017-2023 Tobias Kaminsky - * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-FileCopyrightText: 2018 Andy Scherzinger - * SPDX-FileCopyrightText: 2016 ownCloud Inc. - * SPDX-FileCopyrightText: 2015 María Asensio Valverde - * SPDX-FileCopyrightText: 2013 David A. Velasco - * SPDX-FileCopyrightText: 2011 Bartek Przybylski - * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later */ -package com.owncloud.android.ui.activity; - -import android.accounts.Account; -import android.accounts.AuthenticatorException; -import android.app.Activity; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; -import android.content.pm.PackageManager; -import android.net.Uri; -import android.os.Bundle; -import android.os.Handler; -import android.os.IBinder; -import android.text.TextUtils; - -import com.google.android.material.snackbar.Snackbar; -import com.nextcloud.client.account.User; -import com.nextcloud.client.account.UserAccountManager; -import com.nextcloud.client.jobs.BackgroundJobManager; -import com.nextcloud.client.jobs.upload.FileUploadHelper; -import com.nextcloud.client.network.ConnectivityService; -import com.nextcloud.client.network.NetworkChangeListener; -import com.nextcloud.client.player.ui.audio.AudioPlayerActivity; -import com.nextcloud.client.utils.IntentUtil; -import com.nextcloud.utils.EditorUtils; -import com.nextcloud.utils.SnackbarUtil; -import com.nextcloud.utils.extensions.ActivityExtensionsKt; -import com.nextcloud.utils.extensions.BundleExtensionsKt; -import com.nextcloud.utils.extensions.FileExtensionsKt; -import com.nextcloud.utils.extensions.IntentExtensionsKt; -import com.nextcloud.utils.mdm.MDMConfig; -import com.owncloud.android.MainApp; -import com.owncloud.android.R; -import com.owncloud.android.authentication.AuthenticatorActivity; -import com.owncloud.android.datamodel.ArbitraryDataProvider; -import com.owncloud.android.datamodel.ArbitraryDataProviderImpl; -import com.owncloud.android.datamodel.OCFile; -import com.owncloud.android.lib.common.OwnCloudAccount; -import com.owncloud.android.lib.common.network.CertificateCombinedException; -import com.owncloud.android.lib.common.operations.OnRemoteOperationListener; -import com.owncloud.android.lib.common.operations.RemoteOperation; -import com.owncloud.android.lib.common.operations.RemoteOperationResult; -import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; -import com.owncloud.android.lib.common.utils.Log_OC; -import com.owncloud.android.lib.resources.files.model.ServerFileInterface; -import com.owncloud.android.lib.resources.shares.OCShare; -import com.owncloud.android.lib.resources.shares.ShareType; -import com.owncloud.android.operations.CreateShareViaLinkOperation; -import com.owncloud.android.operations.CreateShareWithShareeOperation; -import com.owncloud.android.operations.GetSharesForFileOperation; -import com.owncloud.android.operations.SetFilesDownloadLimitOperation; -import com.owncloud.android.operations.SynchronizeFileOperation; -import com.owncloud.android.operations.SynchronizeFolderOperation; -import com.owncloud.android.operations.UnshareOperation; -import com.owncloud.android.operations.UpdateNoteForShareOperation; -import com.owncloud.android.operations.UpdateShareInfoOperation; -import com.owncloud.android.operations.UpdateSharePermissionsOperation; -import com.owncloud.android.operations.UpdateShareViaLinkOperation; -import com.owncloud.android.providers.UsersAndGroupsSearchConfig; -import com.owncloud.android.providers.UsersAndGroupsSearchProvider; -import com.owncloud.android.services.OperationsService; -import com.owncloud.android.services.OperationsService.OperationsServiceBinder; -import com.owncloud.android.ui.activity.drawer.DrawerActivity; -import com.owncloud.android.ui.asynctasks.CheckRemoteWipeTask; -import com.owncloud.android.ui.asynctasks.LoadingVersionNumberTask; -import com.owncloud.android.ui.dialog.ConfirmationDialogFragment; -import com.owncloud.android.ui.dialog.LoadingDialog; -import com.owncloud.android.ui.dialog.ShareLinkToDialog; -import com.owncloud.android.ui.dialog.SslUntrustedCertDialog; -import com.owncloud.android.ui.events.DialogEvent; -import com.owncloud.android.ui.events.DialogEventType; -import com.owncloud.android.ui.fragment.FileDetailFragment; -import com.owncloud.android.ui.fragment.FileDetailSharingFragment; -import com.owncloud.android.ui.fragment.OCFileListFragment; -import com.owncloud.android.ui.fragment.albums.AlbumItemsFragment; -import com.owncloud.android.ui.fragment.albums.AlbumsFragment; -import com.owncloud.android.ui.fragment.filesRepository.FilesRepository; -import com.owncloud.android.ui.fragment.filesRepository.RemoteFilesRepository; -import com.owncloud.android.ui.helpers.CredentialsUpdateHelper; -import com.owncloud.android.ui.helpers.FileOperationsHelper; -import com.owncloud.android.ui.navigation.model.ActionBarStyle; -import com.owncloud.android.ui.preview.media.PreviewMediaActivity; -import com.owncloud.android.utils.ClipboardUtil; -import com.owncloud.android.utils.ErrorMessageAdapter; -import com.owncloud.android.utils.FilesSyncHelper; -import com.owncloud.android.utils.theme.ViewThemeUtils; - -import org.greenrobot.eventbus.Subscribe; -import org.greenrobot.eventbus.ThreadMode; - -import java.lang.ref.WeakReference; -import java.util.ArrayList; - -import javax.inject.Inject; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.StringRes; -import androidx.appcompat.app.ActionBar; -import androidx.fragment.app.DialogFragment; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentManager; -import androidx.fragment.app.FragmentTransaction; - -import static com.owncloud.android.ui.activity.FileDisplayActivity.TAG_PUBLIC_LINK; - -/** - * Activity with common behaviour for activities handling {@link OCFile}s in ownCloud {@link Account}s . - */ -public abstract class FileActivity extends DrawerActivity - implements OnRemoteOperationListener, ComponentsGetter, SslUntrustedCertDialog.OnSslUntrustedCertListener, - LoadingVersionNumberTask.VersionDevInterface, FileDetailSharingFragment.OnEditShareListener, NetworkChangeListener { - - public static final String EXTRA_FILE = "com.owncloud.android.ui.activity.FILE"; - public static final String EXTRA_FILE_REMOTE_PATH = "com.owncloud.android.ui.activity.FILE_REMOTE_PATH"; - public static final String EXTRA_LIVE_PHOTO_FILE = "com.owncloud.android.ui.activity.LIVE.PHOTO.FILE"; - public static final String EXTRA_USER = "com.owncloud.android.ui.activity.USER"; - public static final String EXTRA_FROM_NOTIFICATION = "com.owncloud.android.ui.activity.FROM_NOTIFICATION"; - public static final String APP_OPENED_COUNT = "APP_OPENED_COUNT"; - public static final String EXTRA_SEARCH = "com.owncloud.android.ui.activity.SEARCH"; - public static final String EXTRA_SEARCH_QUERY = "com.owncloud.android.ui.activity.SEARCH_QUERY"; - - public static final String TAG = FileActivity.class.getSimpleName(); - - private static final String DIALOG_WAIT_TAG = "DIALOG_WAIT"; - - private static final String KEY_WAITING_FOR_OP_ID = "WAITING_FOR_OP_ID"; - private static final String KEY_ACTION_BAR_TITLE = "ACTION_BAR_TITLE"; - - public static final int REQUEST_CODE__UPDATE_CREDENTIALS = 0; - public static final int REQUEST_CODE__LAST_SHARED = REQUEST_CODE__UPDATE_CREDENTIALS; - - /* Dialog tags */ - private static final String DIALOG_UNTRUSTED_CERT = "DIALOG_UNTRUSTED_CERT"; - private static final String DIALOG_CERT_NOT_SAVED = "DIALOG_CERT_NOT_SAVED"; - - /** Main {@link OCFile} handled by the activity.*/ - private OCFile mFile; - - /** Flag to signal if the activity is launched by a notification */ - private boolean mFromNotification; - - /** Messages handler associated to the main thread and the life cycle of the activity */ - private Handler mHandler; - - private FileOperationsHelper mFileOperationsHelper; - - private ServiceConnection mOperationsServiceConnection; - - private OperationsServiceBinder mOperationsServiceBinder; - - private boolean mResumed; - - protected FileUploadHelper fileUploadHelper = FileUploadHelper.Companion.instance(); - protected boolean isFileDisplayActivityResumed = false; +package com.owncloud.android.ui.activity + +import android.accounts.Account +import android.accounts.AuthenticatorException +import android.app.Activity +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.content.ServiceConnection +import android.content.pm.PackageManager +import android.os.Bundle +import android.os.Handler +import android.os.IBinder +import android.os.Looper +import androidx.annotation.StringRes +import androidx.fragment.app.FragmentManager +import com.google.android.material.snackbar.Snackbar +import com.nextcloud.client.account.User +import com.nextcloud.client.account.UserAccountManager +import com.nextcloud.client.jobs.BackgroundJobManager +import com.nextcloud.client.jobs.upload.FileUploadHelper +import com.nextcloud.client.network.ConnectivityService +import com.nextcloud.client.network.NetworkChangeListener +import com.nextcloud.client.player.ui.audio.AudioPlayerActivity +import com.nextcloud.client.utils.IntentUtil +import com.nextcloud.utils.EditorUtils +import com.nextcloud.utils.SnackbarUtil +import com.nextcloud.utils.extensions.getParcelableArgument +import com.nextcloud.utils.extensions.isActive +import com.nextcloud.utils.extensions.logFileSize +import com.nextcloud.utils.mdm.MDMConfig +import com.owncloud.android.MainApp +import com.owncloud.android.R +import com.owncloud.android.datamodel.ArbitraryDataProvider +import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.lib.common.OwnCloudAccount +import com.owncloud.android.lib.common.accounts.AccountUtils +import com.owncloud.android.lib.common.network.CertificateCombinedException +import com.owncloud.android.lib.common.operations.OnRemoteOperationListener +import com.owncloud.android.lib.common.operations.RemoteOperation +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode +import com.owncloud.android.lib.common.utils.Log_OC +import com.owncloud.android.lib.resources.files.model.ServerFileInterface +import com.owncloud.android.lib.resources.shares.OCShare +import com.owncloud.android.lib.resources.shares.ShareType +import com.owncloud.android.operations.CreateShareViaLinkOperation +import com.owncloud.android.operations.CreateShareWithShareeOperation +import com.owncloud.android.operations.GetSharesForFileOperation +import com.owncloud.android.operations.SetFilesDownloadLimitOperation +import com.owncloud.android.operations.SynchronizeFileOperation +import com.owncloud.android.operations.SynchronizeFolderOperation +import com.owncloud.android.operations.UnshareOperation +import com.owncloud.android.operations.UpdateNoteForShareOperation +import com.owncloud.android.operations.UpdateShareInfoOperation +import com.owncloud.android.operations.UpdateSharePermissionsOperation +import com.owncloud.android.operations.UpdateShareViaLinkOperation +import com.owncloud.android.providers.UsersAndGroupsSearchConfig +import com.owncloud.android.providers.UsersAndGroupsSearchProvider +import com.owncloud.android.services.OperationsService +import com.owncloud.android.services.OperationsService.OperationsServiceBinder +import com.owncloud.android.ui.activity.drawer.DrawerActivity +import com.owncloud.android.ui.asynctasks.CheckRemoteWipeTask +import com.owncloud.android.ui.asynctasks.LoadingVersionNumberTask +import com.owncloud.android.ui.asynctasks.LoadingVersionNumberTask.VersionDevInterface +import com.owncloud.android.ui.dialog.ConfirmationDialogFragment +import com.owncloud.android.ui.dialog.LoadingDialog +import com.owncloud.android.ui.dialog.ShareLinkToDialog +import com.owncloud.android.ui.dialog.SslUntrustedCertDialog +import com.owncloud.android.ui.dialog.SslUntrustedCertDialog.OnSslUntrustedCertListener +import com.owncloud.android.ui.events.DialogEvent +import com.owncloud.android.ui.events.DialogEventType +import com.owncloud.android.ui.fragment.FileDetailFragment +import com.owncloud.android.ui.fragment.FileDetailSharingFragment +import com.owncloud.android.ui.fragment.FileDetailSharingFragment.OnEditShareListener +import com.owncloud.android.ui.fragment.OCFileListFragment +import com.owncloud.android.ui.fragment.albums.AlbumItemsFragment +import com.owncloud.android.ui.fragment.albums.AlbumsFragment +import com.owncloud.android.ui.fragment.filesRepository.FilesRepository +import com.owncloud.android.ui.fragment.filesRepository.RemoteFilesRepository +import com.owncloud.android.ui.helpers.CredentialsUpdateHelper +import com.owncloud.android.ui.helpers.FileOperationsHelper +import com.owncloud.android.ui.navigation.model.ActionBarStyle +import com.owncloud.android.ui.preview.media.PreviewMediaActivity +import com.owncloud.android.utils.ClipboardUtil +import com.owncloud.android.utils.ErrorMessageAdapter +import com.owncloud.android.utils.FilesSyncHelper +import com.owncloud.android.utils.MimeType +import org.greenrobot.eventbus.Subscribe +import org.greenrobot.eventbus.ThreadMode +import java.lang.ref.WeakReference +import javax.inject.Inject + +@Suppress("TooManyFunctions", "LargeClass") +abstract class FileActivity : + DrawerActivity(), + OnRemoteOperationListener, + ComponentsGetter, + OnSslUntrustedCertListener, + VersionDevInterface, + OnEditShareListener, + NetworkChangeListener { + + private var handledFile: OCFile? = null + + open var file: OCFile? + get() = handledFile + set(value) { + handledFile = value + } + + var isFromNotification = false + private set + + private val mainThreadHandler = Handler(Looper.getMainLooper()) + + open val handler: Handler + get() = mainThreadHandler + + private lateinit var operationsHelper: FileOperationsHelper + private var operationsServiceConnection: ServiceConnection? = null + private var operationsBinder: OperationsServiceBinder? = null + private var isActivityResumed = false + + protected val fileUploadHelper: FileUploadHelper = FileUploadHelper.instance() + protected var isFileDisplayActivityResumed = false @Inject - public UserAccountManager accountManager; + lateinit var accountManager: UserAccountManager - @Inject public ConnectivityService connectivityService; + @Inject + lateinit var connectivityService: ConnectivityService @Inject - protected BackgroundJobManager backgroundJobManager; + lateinit var backgroundJobManager: BackgroundJobManager @Inject - EditorUtils editorUtils; + lateinit var editorUtils: EditorUtils @Inject - UsersAndGroupsSearchConfig usersAndGroupsSearchConfig; + lateinit var usersAndGroupsSearchConfig: UsersAndGroupsSearchConfig @Inject - ArbitraryDataProvider arbitraryDataProvider; + lateinit var arbitraryDataProvider: ArbitraryDataProvider - private FilesRepository filesRepository; + lateinit var filesRepository: FilesRepository + private set - @Override - public void showFiles(boolean onDeviceOnly, boolean personalFiles) { - // must be specialized in subclasses - MainApp.showOnlyFilesOnDevice(onDeviceOnly); - MainApp.showOnlyPersonalFiles(personalFiles); - if (onDeviceOnly) { - showActionBar(ActionBarStyle.Plain); - } else { - showActionBar(ActionBarStyle.Search); - } - } - - /** - * Loads the ownCloud {@link Account} and main {@link OCFile} to be handled by the instance of - * the {@link FileActivity}. - * - * Grants that a valid ownCloud {@link Account} is associated to the instance, or that the user - * is requested to create a new one. - */ - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - usersAndGroupsSearchConfig.reset(); - mHandler = new Handler(); - mFileOperationsHelper = new FileOperationsHelper(this, getUserAccountManager(), connectivityService, editorUtils); - User user; + private val shareFileFragment: FileDetailSharingFragment? + get() { + val fragment = supportFragmentManager.findFragmentByTag(ShareActivity.TAG_SHARE_FRAGMENT) + ?: supportFragmentManager.findFragmentByTag(FileDisplayActivity.TAG_LIST_OF_FILES) - if (savedInstanceState != null) { - mFile = BundleExtensionsKt.getParcelableArgument(savedInstanceState, FileActivity.EXTRA_FILE, OCFile.class); - mFromNotification = savedInstanceState.getBoolean(FileActivity.EXTRA_FROM_NOTIFICATION); - mFileOperationsHelper.setOpIdWaitingFor( - savedInstanceState.getLong(KEY_WAITING_FOR_OP_ID, Long.MAX_VALUE) - ); - final ActionBar actionBar = getSupportActionBar(); - if (actionBar != null && !(this instanceof PreviewMediaActivity)) { - viewThemeUtils.files.themeActionBar(this, actionBar, savedInstanceState.getString(KEY_ACTION_BAR_TITLE)); + return when (fragment) { + is FileDetailSharingFragment -> fragment + is FileDetailFragment -> fragment.fileDetailSharingFragment + else -> null } - } else { - user = IntentExtensionsKt.getParcelableArgument(getIntent(), FileActivity.EXTRA_USER, User.class); - mFile = IntentExtensionsKt.getParcelableArgument(getIntent(), FileActivity.EXTRA_FILE, OCFile.class); - mFromNotification = getIntent().getBooleanExtra(FileActivity.EXTRA_FROM_NOTIFICATION, - false); + } - if (user != null) { - setUser(user); - } + private val fileDetailFragment: FileDetailFragment? + get() = supportFragmentManager.findFragmentByTag(FileDisplayActivity.TAG_LIST_OF_FILES) as? FileDetailFragment + + private val fileFromDetailFragment: OCFile? + get() { + val fragment = fileDetailFragment ?: return file + return fragment.file } - mOperationsServiceConnection = new OperationsServiceConnection(); - bindService(new Intent(this, OperationsService.class), mOperationsServiceConnection, - Context.BIND_AUTO_CREATE); - filesRepository = new RemoteFilesRepository(getClientRepository(), this); + override fun showFiles(onDeviceOnly: Boolean, onlyPersonalFiles: Boolean) { + MainApp.showOnlyFilesOnDevice(onDeviceOnly) + MainApp.showOnlyPersonalFiles(onlyPersonalFiles) + showActionBar(if (onDeviceOnly) ActionBarStyle.Plain else ActionBarStyle.Search) } - @Override - public void networkAndServerConnectionListener(boolean isNetworkAndServerAvailable) { - if (isNetworkAndServerAvailable) { - hideInfoBox(); + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + usersAndGroupsSearchConfig.reset() + operationsHelper = FileOperationsHelper(this, userAccountManager, connectivityService, editorUtils) - // No need to refresh the file list again since file display activity doing it. - if (!isFileDisplayActivityResumed) { - refreshList(); - } + if (savedInstanceState != null) { + restoreInstanceState(savedInstanceState) } else { - if (this instanceof AudioPlayerActivity) { - hideInfoBox(); - } else { - showInfoBox(R.string.offline_mode); - } + readIntentExtras() } - } - @Override - protected void onStart() { - super.onStart(); - connectivityService.addListener(this); - fetchExternalLinks(false); + bindOperationsService() + filesRepository = RemoteFilesRepository(clientRepository, this) } - @Override - protected void onStop() { - super.onStop(); - connectivityService.removeListener(this); - } + private fun restoreInstanceState(savedInstanceState: Bundle) { + handledFile = savedInstanceState.getParcelableArgument(EXTRA_FILE, OCFile::class.java) + isFromNotification = savedInstanceState.getBoolean(EXTRA_FROM_NOTIFICATION) + operationsHelper.opIdWaitingFor = savedInstanceState.getLong(KEY_WAITING_FOR_OP_ID, Long.MAX_VALUE) - @Override - protected void onResume() { - super.onResume(); - mResumed = true; - if (mOperationsServiceBinder != null) { - doOnResumeAndBound(); - } + val actionBar = supportActionBar ?: return + if (this is PreviewMediaActivity) return + val actionBarTitle = savedInstanceState.getString(KEY_ACTION_BAR_TITLE) ?: return + viewThemeUtils.files.themeActionBar(this, actionBar, actionBarTitle) } - @Override - protected void onPause() { - if (mOperationsServiceBinder != null) { - mOperationsServiceBinder.removeOperationListener(this); - } - mResumed = false; - super.onPause(); + private fun readIntentExtras() { + val user = intent.getParcelableArgument(EXTRA_USER, User::class.java) + handledFile = intent.getParcelableArgument(EXTRA_FILE, OCFile::class.java) + isFromNotification = intent.getBooleanExtra(EXTRA_FROM_NOTIFICATION, false) + user?.let { setUser(it) } } - @Override - protected void onDestroy() { - if (mOperationsServiceConnection != null) { - unbindService(mOperationsServiceConnection); - mOperationsServiceBinder = null; - } - - super.onDestroy(); + private fun bindOperationsService() { + val connection = OperationsServiceConnection() + operationsServiceConnection = connection + bindService(Intent(this, OperationsService::class.java), connection, BIND_AUTO_CREATE) } - @Override - protected void onSaveInstanceState(@NonNull Bundle outState) { - super.onSaveInstanceState(outState); - FileExtensionsKt.logFileSize(mFile, TAG); - outState.putParcelable(FileActivity.EXTRA_FILE, mFile); - outState.putBoolean(FileActivity.EXTRA_FROM_NOTIFICATION, mFromNotification); - outState.putLong(KEY_WAITING_FOR_OP_ID, mFileOperationsHelper.getOpIdWaitingFor()); - - final var actionBar = getSupportActionBar(); - - if(actionBar != null) { - final var actionBarTitle = actionBar.getTitle(); - if (actionBarTitle != null) { - outState.putString(KEY_ACTION_BAR_TITLE, actionBarTitle.toString()); + override fun networkAndServerConnectionListener(isNetworkAndServerAvailable: Boolean) { + when { + isNetworkAndServerAvailable -> { + hideInfoBox() + if (!isFileDisplayActivityResumed) { + refreshList() + } } - } - } - /** - * Getter for the main {@link OCFile} handled by the activity. - * - * @return Main {@link OCFile} handled by the activity. - */ - @Nullable - public OCFile getFile() { - return mFile; - } + this is AudioPlayerActivity -> hideInfoBox() - - /** - * Setter for the main {@link OCFile} handled by the activity. - * - * @param file Main {@link OCFile} to be handled by the activity. - */ - public void setFile(OCFile file) { - mFile = file; + else -> showInfoBox(R.string.offline_mode) + } } - /** - * @return Value of mFromNotification: True if the Activity is launched by a notification - */ - public boolean fromNotification() { - return mFromNotification; + override fun onStart() { + super.onStart() + connectivityService.addListener(this) + fetchExternalLinks(false) } - public OperationsServiceBinder getOperationsServiceBinder() { - return mOperationsServiceBinder; + override fun onStop() { + super.onStop() + connectivityService.removeListener(this) } - protected ServiceConnection newTransferenceServiceConnection() { - return null; + override fun onResume() { + super.onResume() + isActivityResumed = true + operationsBinder?.let { doOnResumeAndBound(it) } } - public OnRemoteOperationListener getRemoteOperationListener() { - return this; + override fun onPause() { + operationsBinder?.removeOperationListener(this) + isActivityResumed = false + super.onPause() } - public Handler getHandler() { - return mHandler; + override fun onDestroy() { + operationsServiceConnection?.let { + unbindService(it) + operationsBinder = null + } + super.onDestroy() } - public FileOperationsHelper getFileOperationsHelper() { - return mFileOperationsHelper; + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + handledFile.logFileSize(TAG) + outState.run { + putParcelable(EXTRA_FILE, handledFile) + putBoolean(EXTRA_FROM_NOTIFICATION, isFromNotification) + putLong(KEY_WAITING_FOR_OP_ID, operationsHelper.opIdWaitingFor) + supportActionBar?.title?.let { putString(KEY_ACTION_BAR_TITLE, it.toString()) } + } } - /** - * - * @param operation Operation performed. - * @param result Result of the removal. - */ - @Override - public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) { - Log_OC.d(TAG, "Received result of operation in FileActivity - common behaviour for all the " - + "FileActivities "); - - mFileOperationsHelper.setOpIdWaitingFor(Long.MAX_VALUE); + override fun getOperationsServiceBinder(): OperationsServiceBinder? = operationsBinder - dismissLoadingDialog(); + protected open fun newTransferenceServiceConnection(): ServiceConnection? = null - if (!result.isSuccess() && ( - result.getCode() == ResultCode.UNAUTHORIZED || - (result.isException() && result.getException() instanceof AuthenticatorException) - )) { - - requestCredentialsUpdate(); - - if (result.getCode() == ResultCode.UNAUTHORIZED) { - SnackbarUtil.show( - this, ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()) - ); - } + override fun getFileOperationsHelper(): FileOperationsHelper = operationsHelper - } else if (!result.isSuccess() && ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED == result.getCode()) { + override fun getFileUploaderHelper(): FileUploadHelper = fileUploadHelper - showUntrustedCertDialog(result); + fun getCurrentDir(): OCFile? { + val currentFile = file ?: return null + if (currentFile.isFolder) return currentFile + return storageManager?.getFileByPath(currentFile.parentRemotePath) + } - } else if (operation == null || operation instanceof SynchronizeFolderOperation) { - if (result.isSuccess()) { - updateFileFromDB(); + override fun onRemoteOperationFinish(operation: RemoteOperation<*>?, result: RemoteOperationResult<*>) { + Log_OC.d(TAG, "Received result of operation in FileActivity - common behaviour for all the FileActivities ") + operationsHelper.opIdWaitingFor = Long.MAX_VALUE + dismissLoadingDialog() + onCommonOperationFinish(operation, result) + onShareOperationFinish(operation, result) + } - } else if (result.getCode() != ResultCode.CANCELLED) { - SnackbarUtil.show( - this, ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()) - ); - } + private fun onCommonOperationFinish(operation: RemoteOperation<*>?, result: RemoteOperationResult<*>) { + when { + result.isAuthenticationFailure() -> onAuthenticationFailure(operation, result) + !result.isSuccess && result.code == ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED -> + showUntrustedCertDialog(result) + operation == null || operation is SynchronizeFolderOperation -> + onSynchronizeFolderOperationFinish(operation, result) + operation is SynchronizeFileOperation -> onSynchronizeFileOperationFinish(operation, result) + operation is GetSharesForFileOperation -> onGetSharesForFileOperationFinish(operation, result) + } + } - } else if (operation instanceof SynchronizeFileOperation) { - onSynchronizeFileOperationFinish((SynchronizeFileOperation) operation, result); + private fun onShareOperationFinish(operation: RemoteOperation<*>?, result: RemoteOperationResult<*>) { + when (operation) { + is CreateShareViaLinkOperation -> onCreateShareViaLinkOperationFinish(operation, result) + is CreateShareWithShareeOperation -> onUpdateShareInformation(result, R.string.sharee_add_failed) + is UpdateShareViaLinkOperation, + is UpdateShareInfoOperation, + is UpdateSharePermissionsOperation -> onUpdateShareInformation(result, R.string.updating_share_failed) + is SetFilesDownloadLimitOperation -> onUpdateShareInformation(result, R.string.set_download_limit_failed) + is UnshareOperation -> onUpdateShareInformation(result, R.string.unsharing_failed) + is UpdateNoteForShareOperation -> onUpdateNoteForShareOperationFinish(result) + } + } - } else if (operation instanceof GetSharesForFileOperation) { - if (result.isSuccess() || result.getCode() == ResultCode.SHARE_NOT_FOUND) { - updateFileFromDB(); + private fun RemoteOperationResult<*>.isAuthenticationFailure(): Boolean = + !isSuccess && (code == ResultCode.UNAUTHORIZED || (isException && exception is AuthenticatorException)) - } else { - SnackbarUtil.show(this, - ErrorMessageAdapter.getErrorCauseMessage(result, - operation, - getResources())); - } + private fun onAuthenticationFailure(operation: RemoteOperation<*>?, result: RemoteOperationResult<*>) { + requestCredentialsUpdate() + if (result.code == ResultCode.UNAUTHORIZED) { + showErrorCause(operation, result) } + } - if (operation instanceof CreateShareViaLinkOperation) { - onCreateShareViaLinkOperationFinish((CreateShareViaLinkOperation) operation, result); - } else if (operation instanceof CreateShareWithShareeOperation) { - onUpdateShareInformation(result, R.string.sharee_add_failed); - } else if (operation instanceof UpdateShareViaLinkOperation || operation instanceof UpdateShareInfoOperation) { - onUpdateShareInformation(result, R.string.updating_share_failed); - } else if (operation instanceof SetFilesDownloadLimitOperation) { - onUpdateShareInformation(result, R.string.set_download_limit_failed); - } else if (operation instanceof UpdateSharePermissionsOperation) { - onUpdateShareInformation(result, R.string.updating_share_failed); - } else if (operation instanceof UnshareOperation) { - onUpdateShareInformation(result, R.string.unsharing_failed); - } else if (operation instanceof UpdateNoteForShareOperation) { - onUpdateNoteForShareOperationFinish(result); + private fun onSynchronizeFolderOperationFinish(operation: RemoteOperation<*>?, result: RemoteOperationResult<*>) { + when { + result.isSuccess -> updateFileFromDB() + result.code != ResultCode.CANCELLED -> showErrorCause(operation, result) } } - /** - * Invalidates the credentials stored for the current OC account and requests new credentials to the user, - * navigating to {@link AuthenticatorActivity} - * - * Equivalent to call requestCredentialsUpdate(context, null); - * - */ - protected void requestCredentialsUpdate() { - requestCredentialsUpdate(null); + private fun onGetSharesForFileOperationFinish( + operation: GetSharesForFileOperation, + result: RemoteOperationResult<*> + ) { + if (result.isSuccess || result.code == ResultCode.SHARE_NOT_FOUND) { + updateFileFromDB() + } else { + showErrorCause(operation, result) + } } - /** - * Invalidates the credentials stored for the given OC account and requests new credentials to the user, - * navigating to {@link AuthenticatorActivity} - * - * @param account Stored OC account to request credentials update for. If null, current account will - * be used. - */ - protected void requestCredentialsUpdate(Account account) { - if (account == null) { - account = getAccount(); + private fun onSynchronizeFileOperationFinish( + operation: SynchronizeFileOperation, + result: RemoteOperationResult<*> + ) { + if (result.isSuccess) { + if (!operation.transferWasRequested) { + showErrorCause(operation, result) + } + supportInvalidateOptionsMenu() + return } - new CheckRemoteWipeTask(backgroundJobManager, account, new WeakReference<>(this)).execute(); + if (result.code != ResultCode.SYNC_CONFLICT) return + + val currentUser = user.orElseThrow { RuntimeException() } + val intent = ConflictsResolveActivity.createIntent( + operation.localFile, + currentUser, + NO_CONFLICT_UPLOAD_ID, + null, + this + ) + startActivity(intent) } - public void performCredentialsUpdate(Account account) { - final var credentialsUpdateHelper = new CredentialsUpdateHelper(this); - try { - credentialsUpdateHelper.invalidateCredentials(account); - startActivityForResult(credentialsUpdateHelper.createUpdateCredentialsIntent(account), - REQUEST_CODE__UPDATE_CREDENTIALS); - } catch (com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException e) { - SnackbarUtil.show(this, R.string.auth_account_does_not_exist); - } - } - - /** - * Show untrusted cert dialog - */ - public void showUntrustedCertDialog(RemoteOperationResult result) { - // Show a dialog with the certificate info - FragmentManager fm = getSupportFragmentManager(); - SslUntrustedCertDialog dialog = (SslUntrustedCertDialog) fm.findFragmentByTag(DIALOG_UNTRUSTED_CERT); - if(dialog == null) { - dialog = SslUntrustedCertDialog.newInstanceForFullSslError( - (CertificateCombinedException) result.getException()); - FragmentTransaction ft = fm.beginTransaction(); - dialog.show(ft, DIALOG_UNTRUSTED_CERT); - } - } - - private void onSynchronizeFileOperationFinish(SynchronizeFileOperation operation, - RemoteOperationResult result) { - OCFile syncedFile = operation.getLocalFile(); - if (!result.isSuccess()) { - if (result.getCode() == ResultCode.SYNC_CONFLICT) { - Intent intent = ConflictsResolveActivity.createIntent(syncedFile, - getUser().orElseThrow(RuntimeException::new), - -1, - null, - this); - startActivity(intent); - } + private fun showErrorCause(operation: RemoteOperation<*>?, result: RemoteOperationResult<*>) { + SnackbarUtil.show(this, ErrorMessageAdapter.getErrorCauseMessage(result, operation, resources)) + } - } else { - if (!operation.getTransferWasRequested()) { - SnackbarUtil.show(this, ErrorMessageAdapter.getErrorCauseMessage(result, - operation, getResources())); - } - supportInvalidateOptionsMenu(); - } + @JvmOverloads + protected fun requestCredentialsUpdate(account: Account? = null) { + val targetAccount = account ?: this.account + CheckRemoteWipeTask(backgroundJobManager, targetAccount, WeakReference(this)).execute() } - protected void updateFileFromDB(){ - OCFile file = getFile(); - if (file != null) { - file = getStorageManager().getFileByPath(file.getRemotePath()); - setFile(file); + fun performCredentialsUpdate(account: Account) { + val credentialsUpdateHelper = CredentialsUpdateHelper(this) + try { + credentialsUpdateHelper.invalidateCredentials(account) + startActivityForResult( + credentialsUpdateHelper.createUpdateCredentialsIntent(account), + REQUEST_CODE__UPDATE_CREDENTIALS + ) + } catch (e: AccountUtils.AccountNotFoundException) { + Log_OC.e(TAG, "Account not found while updating credentials", e) + SnackbarUtil.show(this, R.string.auth_account_does_not_exist) } } - /** - * Show loading dialog - */ - public void showLoadingDialog(String message) { - runOnUiThread(() -> { - if (!ActivityExtensionsKt.isActive(this)) { - Log_OC.w(TAG, "cannot show loading dialog, activity is finishing"); - return; - } + fun showUntrustedCertDialog(result: RemoteOperationResult<*>) { + val fragmentManager = supportFragmentManager + if (fragmentManager.findFragmentByTag(DIALOG_UNTRUSTED_CERT) != null) return - FragmentManager fragmentManager = getSupportFragmentManager(); - fragmentManager.executePendingTransactions(); - Fragment existingDialog = fragmentManager.findFragmentByTag(DIALOG_WAIT_TAG); - - if (existingDialog instanceof LoadingDialog loadingDialog) { - Log_OC.d(TAG, "dismiss previous loading dialog"); - - if (!fragmentManager.isStateSaved()) { - loadingDialog.dismiss(); - } else { - loadingDialog.dismissAllowingStateLoss(); - } - } + SslUntrustedCertDialog + .newInstanceForFullSslError(result.exception as CertificateCombinedException?) + .show(fragmentManager.beginTransaction(), DIALOG_UNTRUSTED_CERT) + } - // Show new dialog - if (!fragmentManager.isStateSaved()) { - Log_OC.d(TAG, "show loading dialog"); - LoadingDialog loadingDialogFragment = LoadingDialog.newInstance(message); - loadingDialogFragment.show(fragmentManager, DIALOG_WAIT_TAG); - } - }); + protected fun updateFileFromDB() { + val currentFile = file ?: return + file = storageManager.getFileByPath(currentFile.remotePath) } - /** - * Dismiss loading dialog - */ - public void dismissLoadingDialog() { - runOnUiThread(() -> { - if (!ActivityExtensionsKt.isActive(this)) { - Log_OC.w(TAG, "cannot dismiss loading dialog, activity is finishing"); - return; + fun showLoadingDialog(message: String?) { + runOnUiThread { + if (!isActive()) { + Log_OC.w(TAG, "cannot show loading dialog, activity is finishing") + return@runOnUiThread } - FragmentManager fragmentManager = getSupportFragmentManager(); - fragmentManager.executePendingTransactions(); - Fragment fragment = fragmentManager.findFragmentByTag(DIALOG_WAIT_TAG); + val fragmentManager = supportFragmentManager + fragmentManager.executePendingTransactions() + fragmentManager.dismissExistingLoadingDialog("dismiss previous loading dialog") - if (fragment instanceof LoadingDialog loadingDialogFragment) { - Log_OC.d(TAG, "dismiss loading dialog"); + if (fragmentManager.isStateSaved) return@runOnUiThread - // Avoid dismissing after state is saved - if (!fragmentManager.isStateSaved()) { - loadingDialogFragment.dismiss(); - } else { - // Dismiss allowing state loss if needed - loadingDialogFragment.dismissAllowingStateLoss(); - } - } - }); + Log_OC.d(TAG, "show loading dialog") + LoadingDialog.newInstance(message).show(fragmentManager, DIALOG_WAIT_TAG) + } } - private void doOnResumeAndBound() { - mOperationsServiceBinder.addOperationListener(this, mHandler); - long waitingForOpId = mFileOperationsHelper.getOpIdWaitingFor(); - if (waitingForOpId <= Integer.MAX_VALUE) { - boolean wait = mOperationsServiceBinder.dispatchResultIfFinished((int)waitingForOpId, - this); - if (!wait ) { - dismissLoadingDialog(); + fun dismissLoadingDialog() { + runOnUiThread { + if (!isActive()) { + Log_OC.w(TAG, "cannot dismiss loading dialog, activity is finishing") + return@runOnUiThread } + + val fragmentManager = supportFragmentManager + fragmentManager.executePendingTransactions() + fragmentManager.dismissExistingLoadingDialog("dismiss loading dialog") } } - /** - * Implements callback methods for service binding. Passed as a parameter to { - */ - private class OperationsServiceConnection implements ServiceConnection { + private fun FragmentManager.dismissExistingLoadingDialog(logMessage: String) { + val loadingDialog = findFragmentByTag(DIALOG_WAIT_TAG) as? LoadingDialog ?: return + Log_OC.d(TAG, logMessage) - @Override - public void onServiceConnected(ComponentName component, IBinder service) { - if (component.equals(new ComponentName(FileActivity.this, OperationsService.class))) { - Log_OC.d(TAG, "Operations service connected"); - mOperationsServiceBinder = (OperationsServiceBinder) service; - /*if (!mOperationsServiceBinder.isPerformingBlockingOperation()) { - dismissLoadingDialog(); - }*/ - if (mResumed) { - doOnResumeAndBound(); - } - - } else { - return; - } + if (isStateSaved) { + loadingDialog.dismissAllowingStateLoss() + } else { + loadingDialog.dismiss() } + } - @Override - public void onServiceDisconnected(ComponentName component) { - if (component.equals(new ComponentName(FileActivity.this, OperationsService.class))) { - Log_OC.d(TAG, "Operations service disconnected"); - mOperationsServiceBinder = null; - // TODO whatever could be waiting for the service is unbound - } + fun showSyncLoadingDialog(isFolder: Boolean) { + if (isFolder) return + showLoadingDialog(applicationContext.getString(R.string.wait_a_moment)) + } + + @Subscribe(threadMode = ThreadMode.MAIN) + fun handleSyncDialogEvent(event: DialogEvent) { + if (event.type == DialogEventType.SYNC) { + dismissLoadingDialog() } } - @Override - public FileUploadHelper getFileUploaderHelper() { - return fileUploadHelper; + private fun doOnResumeAndBound(binder: OperationsServiceBinder) { + binder.addOperationListener(this, mainThreadHandler) + + val waitingForOpId = operationsHelper.opIdWaitingFor + if (waitingForOpId > Int.MAX_VALUE) return + + val isOperationDispatchedOrPending = binder.dispatchResultIfFinished(waitingForOpId.toInt(), this) + if (!isOperationDispatchedOrPending) { + dismissLoadingDialog() + } } - @Nullable - public OCFile getCurrentDir() { - OCFile file = getFile(); - if (file != null) { - if (file.isFolder()) { - return file; - } else if (getStorageManager() != null) { - String parentPath = file.getParentRemotePath(); - return getStorageManager().getFileByPath(parentPath); + private inner class OperationsServiceConnection : ServiceConnection { + private val operationsServiceComponent = ComponentName(this@FileActivity, OperationsService::class.java) + + override fun onServiceConnected(component: ComponentName, service: IBinder) { + if (component != operationsServiceComponent) return + + Log_OC.d(TAG, "Operations service connected") + val binder = service as OperationsServiceBinder + operationsBinder = binder + if (isActivityResumed) { + doOnResumeAndBound(binder) } } - return null; - } - /* OnSslUntrustedCertListener methods */ + override fun onServiceDisconnected(component: ComponentName) { + if (component != operationsServiceComponent) return - @Override - public void onSavedCertificate() { - // Nothing to do in this context + Log_OC.d(TAG, "Operations service disconnected") + operationsBinder = null + } } - @Override - public void onFailedSavingCertificate() { - ConfirmationDialogFragment dialog = ConfirmationDialogFragment.newInstance( - R.string.ssl_validator_not_saved, new String[]{}, 0, R.string.common_ok, -1, -1 - ); - dialog.show(getSupportFragmentManager(), DIALOG_CERT_NOT_SAVED); + override fun onSavedCertificate() = Unit + + override fun onFailedSavingCertificate() { + ConfirmationDialogFragment.newInstance( + messageResId = R.string.ssl_validator_not_saved, + messageArguments = emptyArray(), + titleResId = NO_TITLE_RES, + positiveButtonTextId = R.string.common_ok, + negativeButtonTextId = NO_BUTTON_RES, + neutralButtonTextId = NO_BUTTON_RES + ).show(supportFragmentManager, DIALOG_CERT_NOT_SAVED) } - public void checkForNewDevVersionNecessary(Context context) { - if (getResources().getBoolean(R.bool.dev_version_direct_download_enabled)) { - ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProviderImpl(this); - int count = arbitraryDataProvider.getIntegerValue(FilesSyncHelper.GLOBAL, APP_OPENED_COUNT); + fun checkForNewDevVersionNecessary(context: Context) { + if (!resources.getBoolean(R.bool.dev_version_direct_download_enabled)) return - if (count > 10 || count == -1) { - checkForNewDevVersion(this, context); - } + val appOpenedCount = arbitraryDataProvider.getIntegerValue(FilesSyncHelper.GLOBAL, APP_OPENED_COUNT) + if (appOpenedCount > MIN_APP_OPENED_COUNT_FOR_DEV_VERSION_CHECK || appOpenedCount == UNKNOWN_VALUE) { + checkForNewDevVersion(this, context) } } - @Override - public void returnVersion(Integer latestVersion) { - showDevSnackbar(this, latestVersion, false, true); + override fun returnVersion(latestVersion: Int?) { + val version = latestVersion ?: return + showDevSnackbar(this, version, openDirectly = false, inBackground = true) } - public static void checkForNewDevVersion(LoadingVersionNumberTask.VersionDevInterface callback, Context context) { - String url = context.getString(R.string.dev_latest); - LoadingVersionNumberTask loadTask = new LoadingVersionNumberTask(callback); - loadTask.execute(url); + private fun onUpdateNoteForShareOperationFinish(result: RemoteOperationResult<*>) { + if (!result.isSuccess) { + SnackbarUtil.show(this, R.string.note_could_not_sent) + return + } + shareFileFragment?.onUpdateShareInformation(result) } - public static void showDevSnackbar(Activity activity, - Integer latestVersion, - boolean openDirectly, - boolean inBackground) { - int currentVersion = -1; - try { - currentVersion = activity.getPackageManager().getPackageInfo(activity.getPackageName(), 0).versionCode; - } catch (PackageManager.NameNotFoundException e) { - Log_OC.e(TAG, "Package not found", e); - } + private fun onUpdateShareInformation(result: RemoteOperationResult<*>, @StringRes defaultError: Int) { + val sharingFragment = shareFileFragment - if (latestVersion == -1 || currentVersion == -1) { - SnackbarUtil.show(activity, R.string.dev_version_no_information_available); + if (result.isSuccess) { + updateFileFromDB() + sharingFragment?.onUpdateShareInformation(result) + return } - if (latestVersion > currentVersion) { - String devApkLink = activity.getString(R.string.dev_link) + latestVersion + ".apk"; - if (openDirectly) { - IntentUtil.startLinkIntent(activity, devApkLink); - } else { - Snackbar.make(activity.findViewById(android.R.id.content), R.string.dev_version_new_version_available, - Snackbar.LENGTH_LONG) - .setAction(activity.getString(R.string.version_dev_download), v -> IntentUtil.startLinkIntent(activity, devApkLink)).show(); - } - } else { - if (!inBackground) { - SnackbarUtil.show(activity, R.string.dev_version_no_new_version_available); - } - } - } - public static void copyAndShareFileLink(FileActivity activity, - OCFile file, - String link, - final ViewThemeUtils viewThemeUtils) { - if (MDMConfig.INSTANCE.shareViaLink(activity) && MDMConfig.INSTANCE.clipBoardSupport(activity)) { - ClipboardUtil.copyToClipboard(activity, link, false); - Snackbar snackbar = Snackbar.make(activity.findViewById(android.R.id.content), R.string.clipboard_text_copied, - Snackbar.LENGTH_LONG) - .setAction(R.string.share, v -> showShareLinkDialog(activity, file, link)); - viewThemeUtils.material.themeSnackbar(snackbar); - snackbar.show(); + val message = result.message.takeUnless { it.isNullOrEmpty() } ?: getString(defaultError) + val sharingFragmentView = sharingFragment?.view + if (sharingFragmentView == null) { + SnackbarUtil.show(this, message) + return } + + Snackbar.make(sharingFragmentView, message, Snackbar.LENGTH_LONG).showThemed() } - public static void showShareLinkDialog(FileActivity activity, ServerFileInterface file, String link) { - // Create dialog to allow the user choose an app to send the link - Intent intentToShareLink = new Intent(Intent.ACTION_SEND); + fun refreshList() { + when { + isAlbumsFragment -> getFragment(AlbumsFragment.TAG, AlbumsFragment::class.java) + .ifPresent { it.refreshAlbums() } - intentToShareLink.putExtra(Intent.EXTRA_TEXT, link); - intentToShareLink.setType("text/plain"); + isAlbumItemsFragment -> getFragment(AlbumItemsFragment.TAG, AlbumItemsFragment::class.java) + .ifPresent { it.refreshData() } - String username; - try { - OwnCloudAccount oca = new OwnCloudAccount(activity.getAccount(), activity); - if (oca.getDisplayName() != null && !oca.getDisplayName().isEmpty()) { - username = oca.getDisplayName(); - } else { - username = com.owncloud.android.lib.common.accounts.AccountUtils - .getUsernameForAccount(activity.getAccount()); - } - } catch (Exception e) { - username = com.owncloud.android.lib.common.accounts.AccountUtils - .getUsernameForAccount(activity.getAccount()); + else -> refreshListOfFilesFragment() } + } - if (username != null) { - intentToShareLink.putExtra(Intent.EXTRA_SUBJECT, - activity.getString(R.string.subject_user_shared_with_you, - username, - file.getFileName())); - } else { - intentToShareLink.putExtra(Intent.EXTRA_SUBJECT, - activity.getString(R.string.subject_shared_with_you, - file.getFileName())); + private fun refreshListOfFilesFragment() { + when (val fragment = supportFragmentManager.findFragmentByTag(FileDisplayActivity.TAG_LIST_OF_FILES)) { + is OCFileListFragment -> fragment.onRefresh() + is FileDetailFragment -> fragment.goBackToOCFileListFragment() } + } - String[] packagesToExclude = new String[]{activity.getPackageName()}; - DialogFragment chooserDialog = ShareLinkToDialog.newInstance(intentToShareLink, packagesToExclude); - chooserDialog.show(activity.getSupportFragmentManager(), FileDisplayActivity.FTAG_CHOOSER_DIALOG); + private fun onCreateShareViaLinkOperationFinish( + operation: CreateShareViaLinkOperation, + result: RemoteOperationResult<*> + ) { + if (result.isSuccess) { + onShareViaLinkCreated(result) + } else { + onShareViaLinkFailed(operation, result) + } } - private void onUpdateNoteForShareOperationFinish(RemoteOperationResult result) { - FileDetailSharingFragment sharingFragment = getShareFileFragment(); + private fun onShareViaLinkCreated(result: RemoteOperationResult<*>) { + val sharingFragment = shareFileFragment + val fileListFragment = supportFragmentManager.findFragmentByTag(FileDisplayActivity.TAG_LIST_OF_FILES) + updateFileFromDB() - if (result.isSuccess()) { - if (sharingFragment != null) { - sharingFragment.onUpdateShareInformation(result); - } + val publicLinkShare = result.data + .filterIsInstance() + .firstOrNull { FileDisplayActivity.TAG_PUBLIC_LINK.equals(it.shareType?.name, ignoreCase = true) } + val sharedFile = publicLinkShare?.let { storageManager.getFileByEncryptedRemotePath(it.path) } + + if (publicLinkShare != null && sharedFile != null) { + copyAndShareFileLink(this, sharedFile, publicLinkShare.shareLink) + } + sharingFragment?.onUpdateShareInformation(result, sharedFile) + + if (fileListFragment !is OCFileListFragment || sharedFile == null) return + + if (fileListFragment.adapterFiles.contains(sharedFile)) { + fileListFragment.updateOCFile(sharedFile) } else { - SnackbarUtil.show(this, R.string.note_could_not_sent); + SnackbarUtil.show(this, R.string.file_activity_shared_file_cannot_be_updated) } } - private void onUpdateShareInformation(RemoteOperationResult result, @StringRes int defaultError) { - FileDetailSharingFragment sharingFragment = getShareFileFragment(); + private fun onShareViaLinkFailed(operation: CreateShareViaLinkOperation, result: RemoteOperationResult<*>) { + val sharingFragment = shareFileFragment + val isForbiddenWithoutPassword = + result.code == ResultCode.SHARE_FORBIDDEN && operation.password.isNullOrEmpty() - if (result.isSuccess()) { - updateFileFromDB(); - if (sharingFragment != null) { - sharingFragment.onUpdateShareInformation(result); - } - return; + if (!isForbiddenWithoutPassword) { + sharingFragment?.refreshSharesFromDB() + val errorMessage = ErrorMessageAdapter.getErrorCauseMessage(result, operation, resources) + Snackbar.make(findViewById(android.R.id.content), errorMessage, Snackbar.LENGTH_LONG).showThemed() + return } - final String message = TextUtils.isEmpty(result.getMessage()) ? getString(defaultError) : result.getMessage(); + val capability = capabilities.orElse(null) ?: return + if (!capability.filesSharingPublicEnabled.isUnknown) return + if (sharingFragment == null || !sharingFragment.isAdded) return - if (sharingFragment == null || sharingFragment.getView() == null) { - SnackbarUtil.show(this, message); - return; - } + sharingFragment.requestPasswordForShareViaLink(true, capability.filesSharingPublicAskForOptionalPassword.isTrue) + } - Snackbar snackbar = Snackbar.make(sharingFragment.getView(), message, Snackbar.LENGTH_LONG); - viewThemeUtils.material.themeSnackbar(snackbar); - snackbar.show(); + private fun Snackbar.showThemed() { + viewThemeUtils.material.themeSnackbar(this) + show() } - public void refreshList() { - if (isAlbumsFragment()) { - getFragment(AlbumsFragment.Companion.getTAG(), AlbumsFragment.class) - .ifPresent(AlbumsFragment::refreshAlbums); - return; + override fun onNewIntent(intent: Intent) { + super.onNewIntent(intent) + if (intent.action == UsersAndGroupsSearchProvider.ACTION_SHARE_WITH) { + onShareWithIntent(intent) } + } - if (isAlbumItemsFragment()) { - getFragment(AlbumItemsFragment.Companion.getTAG(), AlbumItemsFragment.class) - .ifPresent(AlbumItemsFragment::refreshData); - return; - } + private fun onShareWithIntent(intent: Intent) { + val data = intent.data ?: return + val remotePath = fileFromDetailFragment?.remotePath ?: return + val shareWith = data.toString().substringAfterLast('/') + val shareType = UsersAndGroupsSearchProvider.getShareType(data.authority) - final var fragment = - getSupportFragmentManager().findFragmentByTag(FileDisplayActivity.TAG_LIST_OF_FILES); + val isAlreadyShared = storageManager + .getSharesWithForAFile(remotePath, account.name) + .any { shareeKey(it.shareType, it.shareWith) == shareeKey(shareType, shareWith) } - if (fragment instanceof OCFileListFragment listFragment) { - listFragment.onRefresh(); - } else if (fragment instanceof FileDetailFragment detailFragment) { - detailFragment.goBackToOCFileListFragment(); + if (isAlreadyShared) { + SnackbarUtil.show(this, getString(R.string.sharee_already_added_to_file)) + } else { + doShareWith(shareWith, shareType) } } - private void onCreateShareViaLinkOperationFinish(CreateShareViaLinkOperation operation, - RemoteOperationResult result) { - FileDetailSharingFragment sharingFragment = getShareFileFragment(); - final Fragment fileListFragment = getSupportFragmentManager().findFragmentByTag(FileDisplayActivity.TAG_LIST_OF_FILES); + private fun shareeKey(shareType: ShareType?, shareWith: String?): String = "${shareType}_$shareWith" - if (result.isSuccess()) { - updateFileFromDB(); + protected open fun doShareWith(shareeName: String, shareType: ShareType) { + fileDetailFragment?.initiateSharingProcess(shareeName, shareType, usersAndGroupsSearchConfig.searchOnlyUsers) + } - // if share to user and share via link multiple ocshares are returned, - // therefore filtering for public_link - String link = ""; - OCFile file = null; - for (Object object : result.getData()) { - if (object instanceof OCShare shareLink) { - ShareType shareType = shareLink.getShareType(); + override fun editExistingShare(share: OCShare, screenTypePermission: Int, isReshareShown: Boolean) { + fileDetailFragment?.editExistingShare(share, screenTypePermission, isReshareShown) + } - if (shareType != null && TAG_PUBLIC_LINK.equalsIgnoreCase(shareType.name())) { - link = shareLink.getShareLink(); - file = getStorageManager().getFileByEncryptedRemotePath(shareLink.getPath()); - break; - } - } - } + override fun onShareProcessClosed() { + fileDetailFragment?.showHideFragmentView(false) + } - copyAndShareFileLink(this, file, link, viewThemeUtils); + companion object { + const val EXTRA_FILE = "com.owncloud.android.ui.activity.FILE" + const val EXTRA_FILE_REMOTE_PATH = "com.owncloud.android.ui.activity.FILE_REMOTE_PATH" + const val EXTRA_LIVE_PHOTO_FILE = "com.owncloud.android.ui.activity.LIVE.PHOTO.FILE" + const val EXTRA_USER = "com.owncloud.android.ui.activity.USER" + const val EXTRA_FROM_NOTIFICATION = "com.owncloud.android.ui.activity.FROM_NOTIFICATION" + const val APP_OPENED_COUNT = "APP_OPENED_COUNT" + const val EXTRA_SEARCH = "com.owncloud.android.ui.activity.SEARCH" + const val EXTRA_SEARCH_QUERY = "com.owncloud.android.ui.activity.SEARCH_QUERY" - if (sharingFragment != null) { - sharingFragment.onUpdateShareInformation(result, file); - } + @JvmField + val TAG: String = FileActivity::class.java.simpleName - if (fileListFragment instanceof OCFileListFragment ocFileListFragment && file != null) { - if (ocFileListFragment.getAdapterFiles().contains(file)) { - ocFileListFragment.updateOCFile(file); - } else { - SnackbarUtil.show(this, R.string.file_activity_shared_file_cannot_be_updated); - } - } - } else { - // Detect Failure (403) --> maybe needs password - String password = operation.getPassword(); - final var optionalCapabilities = getCapabilities(); - - if (result.getCode() == RemoteOperationResult.ResultCode.SHARE_FORBIDDEN && TextUtils.isEmpty(password)) { - if (optionalCapabilities.isPresent()) { - final var capabilities = optionalCapabilities.get(); - if (capabilities.getFilesSharingPublicEnabled().isUnknown()) { - // Was tried without password, but not sure that it's optional. - // Try with password before giving up; see also ShareFileFragment#OnShareViaLinkListener - if (sharingFragment != null && sharingFragment.isAdded()) { - // only if added to the view hierarchy - sharingFragment.requestPasswordForShareViaLink(true, capabilities.getFilesSharingPublicAskForOptionalPassword().isTrue()); - } - } - } - } else { - if (sharingFragment != null) { - sharingFragment.refreshSharesFromDB(); - } - Snackbar snackbar = Snackbar.make(findViewById(android.R.id.content), - ErrorMessageAdapter.getErrorCauseMessage(result, - operation, - getResources()), - Snackbar.LENGTH_LONG); - viewThemeUtils.material.themeSnackbar(snackbar); - snackbar.show(); - } - } - } + const val REQUEST_CODE__UPDATE_CREDENTIALS = 0 + const val REQUEST_CODE__LAST_SHARED = REQUEST_CODE__UPDATE_CREDENTIALS - /** - * Shortcut to get access to the {@link FileDetailSharingFragment} instance, if any - * - * @return A {@link FileDetailSharingFragment} instance, or null - */ - private @Nullable - @Deprecated - FileDetailSharingFragment getShareFileFragment() { - Fragment fragment = getSupportFragmentManager().findFragmentByTag(ShareActivity.TAG_SHARE_FRAGMENT); + private const val DIALOG_WAIT_TAG = "DIALOG_WAIT" + private const val DIALOG_UNTRUSTED_CERT = "DIALOG_UNTRUSTED_CERT" + private const val DIALOG_CERT_NOT_SAVED = "DIALOG_CERT_NOT_SAVED" - if (fragment == null) { - fragment = getSupportFragmentManager().findFragmentByTag(FileDisplayActivity.TAG_LIST_OF_FILES); - } + private const val KEY_WAITING_FOR_OP_ID = "WAITING_FOR_OP_ID" + private const val KEY_ACTION_BAR_TITLE = "ACTION_BAR_TITLE" - if (fragment instanceof FileDetailSharingFragment) { - return (FileDetailSharingFragment) fragment; - } else if (fragment instanceof FileDetailFragment fileDetailFragment) { - return fileDetailFragment.getFileDetailSharingFragment(); - } else { - return null; - } - } + private const val NO_CONFLICT_UPLOAD_ID = -1L + private const val NO_TITLE_RES = 0 + private const val NO_BUTTON_RES = -1 + private const val UNKNOWN_VALUE = -1 + private const val MIN_APP_OPENED_COUNT_FOR_DEV_VERSION_CHECK = 10 + private const val DEV_APK_EXTENSION = ".apk" - @Override - protected void onNewIntent(Intent intent) { - super.onNewIntent(intent); + @JvmStatic + fun checkForNewDevVersion(callback: VersionDevInterface?, context: Context) { + LoadingVersionNumberTask(callback).execute(context.getString(R.string.dev_latest)) + } - if (UsersAndGroupsSearchProvider.ACTION_SHARE_WITH.equals(intent.getAction())) { - Uri data = intent.getData(); - String dataString = intent.getDataString(); - String shareWith = dataString.substring(dataString.lastIndexOf('/') + 1); + @JvmStatic + fun showDevSnackbar(activity: Activity, latestVersion: Int, openDirectly: Boolean, inBackground: Boolean) { + val currentVersion = activity.installedVersionCode() - ArrayList existingSharees = new ArrayList<>(); - for (OCShare share : getStorageManager().getSharesWithForAFile(getFileFromDetailFragment().getRemotePath(), - getAccount().name)) { - existingSharees.add(share.getShareType() + "_" + share.getShareWith()); + if (latestVersion == UNKNOWN_VALUE || currentVersion == UNKNOWN_VALUE) { + SnackbarUtil.show(activity, R.string.dev_version_no_information_available) } - String dataAuthority = data.getAuthority(); - ShareType shareType = UsersAndGroupsSearchProvider.getShareType(dataAuthority); + if (latestVersion <= currentVersion) { + if (!inBackground) { + SnackbarUtil.show(activity, R.string.dev_version_no_new_version_available) + } + return + } - if (!existingSharees.contains(shareType + "_" + shareWith)) { - doShareWith(shareWith, shareType); - } else { - SnackbarUtil.show(this, getString(R.string.sharee_already_added_to_file)); + val devApkLink = activity.getString(R.string.dev_link) + latestVersion + DEV_APK_EXTENSION + if (openDirectly) { + IntentUtil.startLinkIntent(activity, devApkLink) + return } - } - } - /** - * returns the file that is selected for sharing, getFile() only returns the containing folder - */ - private OCFile getFileFromDetailFragment() { - FileDetailFragment fragment = getFileDetailFragment(); - if (fragment != null) { - return fragment.getFile(); + Snackbar + .make( + activity.findViewById(android.R.id.content), + R.string.dev_version_new_version_available, + Snackbar.LENGTH_LONG + ) + .setAction(activity.getString(R.string.version_dev_download)) { + IntentUtil.startLinkIntent(activity, devApkLink) + } + .show() } - return getFile(); - } - /** - * open the new sharing process fragment to create the share - * - * @param shareeName - * @param shareType - */ - protected void doShareWith(String shareeName, ShareType shareType) { - FileDetailFragment fragment = getFileDetailFragment(); - if (fragment != null) { - fragment.initiateSharingProcess(shareeName, - shareType, - usersAndGroupsSearchConfig.getSearchOnlyUsers()); + @Suppress("DEPRECATION") + private fun Activity.installedVersionCode(): Int = try { + packageManager.getPackageInfo(packageName, 0).versionCode + } catch (e: PackageManager.NameNotFoundException) { + Log_OC.e(TAG, "Package not found", e) + UNKNOWN_VALUE } - } - /** - * open the new sharing process to modify the created share - * @param share - * @param screenTypePermission - * @param isReshareShown - */ - @Override - public void editExistingShare(OCShare share, int screenTypePermission, boolean isReshareShown) { - FileDetailFragment fragment = getFileDetailFragment(); - if (fragment != null) { - fragment.editExistingShare(share, screenTypePermission, isReshareShown); - } - } + fun copyAndShareFileLink(activity: FileActivity, file: OCFile, link: String?) { + if (!MDMConfig.shareViaLink(activity) || !MDMConfig.clipBoardSupport(activity)) return - /** - * callback triggered on closing/finishing the sharing process - */ - @Override - public void onShareProcessClosed() { - FileDetailFragment fragment = getFileDetailFragment(); - if (fragment != null) { - fragment.showHideFragmentView(false); + ClipboardUtil.copyToClipboard(activity, link, false) + Snackbar + .make(activity.findViewById(android.R.id.content), R.string.clipboard_text_copied, Snackbar.LENGTH_LONG) + .setAction(R.string.share) { showShareLinkDialog(activity, file, link) } + .also { activity.viewThemeUtils.material.themeSnackbar(it) } + .show() } - } - private FileDetailFragment getFileDetailFragment() { - Fragment fragment = getSupportFragmentManager().findFragmentByTag(FileDisplayActivity.TAG_LIST_OF_FILES); - if (fragment instanceof FileDetailFragment) { - return (FileDetailFragment) fragment; - } - return null; - } + fun showShareLinkDialog(activity: FileActivity, file: ServerFileInterface, link: String?) { + val username = activity.sharingUsername() + val subject = if (username != null) { + activity.getString(R.string.subject_user_shared_with_you, username, file.fileName) + } else { + activity.getString(R.string.subject_shared_with_you, file.fileName) + } - public FilesRepository getFilesRepository() { - return filesRepository; - } + val intentToShareLink = Intent(Intent.ACTION_SEND).apply { + putExtra(Intent.EXTRA_TEXT, link) + type = MimeType.TEXT_PLAIN + putExtra(Intent.EXTRA_SUBJECT, subject) + } - public void showSyncLoadingDialog(boolean isFolder) { - if (isFolder) { - return; + ShareLinkToDialog + .newInstance(intentToShareLink, activity.packageName) + .show(activity.supportFragmentManager, FileDisplayActivity.FTAG_CHOOSER_DIALOG) } - showLoadingDialog(getApplicationContext().getString(R.string.wait_a_moment)); - } - - @Subscribe(threadMode = ThreadMode.MAIN) - public void handleSyncDialogEvent(DialogEvent event) { - if (event.getType() == DialogEventType.SYNC) { - dismissLoadingDialog(); + @Suppress("TooGenericExceptionCaught") + private fun FileActivity.sharingUsername(): String? { + val displayName = try { + OwnCloudAccount(account, this).displayName + } catch (e: Exception) { + Log_OC.e(TAG, "Could not read account display name", e) + null + } + return displayName.takeUnless { it.isNullOrEmpty() } ?: AccountUtils.getUsernameForAccount(account) } } } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt index 0385a900130a..d3ae4fa89eee 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt @@ -558,7 +558,7 @@ class FileDisplayActivity : return } - setFile(initialFile) + file = initialFile val accountName = currentUser.accountName switchAccountButton.tag = accountName @@ -644,12 +644,12 @@ class FileDisplayActivity : fragment.listDirectory(MainApp.isOnlyOnDevice) } fragment.registerFabListener() - updateActionBarTitleAndHomeButton(currentDir) + updateActionBarTitleAndHomeButton(getCurrentDir()) } else -> { fragment.listDirectory(startFile, false) - updateActionBarTitleAndHomeButton(currentDir ?: startFile) + updateActionBarTitleAndHomeButton(getCurrentDir() ?: startFile) } } } @@ -703,7 +703,7 @@ class FileDisplayActivity : when { ACTION_DETAILS.equals(action, ignoreCase = true) -> { val detailsFile = getFileFromIntent(intent) - setFile(detailsFile) + file = detailsFile showDetails(detailsFile) isShowingDetailsFromPreview = intent.getBooleanExtra(EXTRA_RETURN_TO_PREVIEW, false) } @@ -980,10 +980,12 @@ class FileDisplayActivity : override val menuItemId: Int get() = MainApp.menuItemId ?: listOfFilesFragment?.menuItemId ?: R.id.nav_all_files - override fun setFile(file: OCFile?) { - super.setFile(file) - file?.takeIf { it.isFolder }?.let { viewModel.setCurrentFolder(it.fileId) } - } + override var file: OCFile? + get() = super.file + set(value) { + super.file = value + value?.takeIf { it.isFolder }?.let { viewModel.setCurrentFolder(it.fileId) } + } private fun replaceLeftFragment(fragment: Fragment, showSortListGroup: Boolean = true) { isShowingDetailsFromPreview = false @@ -1120,7 +1122,7 @@ class FileDisplayActivity : fun performUnifiedSearch(query: String, listOfHiddenFiles: ArrayList?) { val unifiedSearchFragment = - UnifiedSearchFragment.newInstance(query, listOfHiddenFiles, currentDir?.decryptedRemotePath) + UnifiedSearchFragment.newInstance(query, listOfHiddenFiles, getCurrentDir()?.decryptedRemotePath) replaceLeftFragment(unifiedSearchFragment, showSortListGroup = false) } @@ -2071,7 +2073,7 @@ class FileDisplayActivity : configureToolbarForPreview(file) waitingToPreview = file downloadWaitingPreview() - setFile(file) + this.file = file } // endregion @@ -2137,7 +2139,7 @@ class FileDisplayActivity : } supportInvalidateOptionsMenu() - fetchRecommendedFilesIfNeeded(ignoreETag = true, currentDir) + fetchRecommendedFilesIfNeeded(ignoreETag = true, getCurrentDir()) } private fun updateFileListAfterRemoval(fragment: OCFileListFragment, removedFile: OCFile, onlyLocalCopy: Boolean) { @@ -2192,7 +2194,7 @@ class FileDisplayActivity : return } - val restoredFile = getFile() + val restoredFile = file // the new version is only downloaded when the old one was downloaded if (restoredFile?.isDown == true) { @@ -2238,10 +2240,10 @@ class FileDisplayActivity : (leftFragment as? FileFragment)?.let { refreshFileFragmentAfterRename(it, renamedFile, currentUser) } val parentDir = storageManager.getFileById(renamedFile.parentId) - val isRenamedInCurrentDir = parentDir != null && parentDir == currentDir + val isRenamedInCurrentDir = parentDir != null && parentDir == getCurrentDir() // the parent check fails in the Shared/Favorites root, so the root is always refreshed too - if (currentDir?.isRootDirectory == true || isRenamedInCurrentDir) { + if (getCurrentDir()?.isRootDirectory == true || isRenamedInCurrentDir) { val fileList = topFileListFragment // OCFileSearchTask may still run during rename, so use a single refresh path to avoid @@ -2254,7 +2256,7 @@ class FileDisplayActivity : } refreshGalleryFragmentIfNeeded() - fetchRecommendedFilesIfNeeded(ignoreETag = true, currentDir) + fetchRecommendedFilesIfNeeded(ignoreETag = true, getCurrentDir()) } private fun refreshFileFragmentAfterRename(fragment: FileFragment, renamedFile: OCFile, user: User) { 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 930366a7782f..f71f54bf079c 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 @@ -241,7 +241,7 @@ open class FolderPickerActivity : } override fun onSavedCertificate() { - startSyncFolderOperation(currentDir, false) + startSyncFolderOperation(getCurrentDir(), false) } private fun startSyncFolderOperation(folder: OCFile?, ignoreETag: Boolean) { @@ -450,7 +450,8 @@ open class FolderPickerActivity : } private fun getSelectedFolderPathTitle(): String? { - val atRoot = (currentDir == null || currentDir?.parentId == 0L) + val currentDir = getCurrentDir() + val atRoot = (currentDir == null || currentDir.parentId == 0L) return if (atRoot) captionText ?: "" else currentDir?.fileName } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.kt index 3660d64b59fe..5e48950b5887 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.kt @@ -52,7 +52,6 @@ import com.owncloud.android.ui.adapter.UserListAdapter import com.owncloud.android.ui.adapter.UserListItem import com.owncloud.android.ui.dialog.AccountRemovalDialog.Companion.newInstance import com.owncloud.android.ui.events.AccountRemovedEvent -import com.owncloud.android.ui.helpers.FileOperationsHelper import com.owncloud.android.ui.navigation.model.ActionBarStyle import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode @@ -69,7 +68,7 @@ class ManageAccountsActivity : UserListAdapter.ClickListener { private var recyclerView: RecyclerView? = null - private val handler = Handler() + override val handler = Handler() private var accountName: String? = null private var userListAdapter: UserListAdapter? = null private var originalUsers: Set? = null @@ -341,14 +340,10 @@ class ManageAccountsActivity : } } - override fun getHandler(): Handler = handler - override fun getOperationsServiceBinder(): OperationsServiceBinder? = null override fun getStorageManager(): FileDataStorageManager = super.getStorageManager() - override fun getFileOperationsHelper(): FileOperationsHelper? = null - @Suppress("DEPRECATION") @SuppressLint("NotifyDataSetChanged") private fun performAccountRemoval(user: User) { diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/SendShareDialog.kt b/app/src/main/java/com/owncloud/android/ui/dialog/SendShareDialog.kt index f0e7cb45db41..16894e49cc63 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/SendShareDialog.kt +++ b/app/src/main/java/com/owncloud/android/ui/dialog/SendShareDialog.kt @@ -144,8 +144,9 @@ class SendShareDialog : private fun shareByLink() { val fileOperationsHelper = (requireActivity() as FileActivity).fileOperationsHelper - if (file?.isSharedViaLink == true) { - fileOperationsHelper.getFileWithLink(file!!, viewThemeUtils) + val sharedFile = file + if (sharedFile?.isSharedViaLink == true) { + fileOperationsHelper.getFileWithLink(sharedFile) } else if (sharingPublicPasswordEnforced || sharingPublicAskForPassword) { // password enforced by server, request to the user before trying to create requestPasswordForShareViaLink() diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/ExtendedListFragment.kt b/app/src/main/java/com/owncloud/android/ui/fragment/ExtendedListFragment.kt index 5ed2c45e47a4..d4c3270b2986 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/ExtendedListFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/ExtendedListFragment.kt @@ -393,7 +393,7 @@ open class ExtendedListFragment : } private fun getEmptyListViewTextId(): Int { - val currentDir = getTypedActivity(FileDisplayActivity::class.java)?.currentDir + val currentDir = getTypedActivity(FileDisplayActivity::class.java)?.getCurrentDir() return if (currentDir?.canCreateFileAndFolder() == false) { R.string.extented_list_fragment_empty_list_message_no_write_permission } else { diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.kt b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.kt index 343811c31d9a..b2336578730c 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.kt @@ -550,11 +550,12 @@ class FileDetailSharingFragment : } if (publicShare.shareLink.isNullOrEmpty()) { - fileOperationsHelper?.getFileWithLink(file, viewThemeUtils) + fileOperationsHelper?.getFileWithLink(file) return } - FileActivity.showShareLinkDialog(fileActivity, file, publicShare.shareLink) + val activity = fileActivity ?: return + FileActivity.showShareLinkDialog(activity, file, publicShare.shareLink) } private fun refreshLegacyShareUiFromDb() { @@ -717,8 +718,10 @@ class FileDetailSharingFragment : } override fun sendLink(share: OCShare) { - if (file?.isSharedViaLink == true && !share.shareLink.isNullOrEmpty()) { - FileActivity.showShareLinkDialog(fileActivity, file, share.shareLink) + val currentFile = file + val activity = fileActivity + if (currentFile?.isSharedViaLink == true && !share.shareLink.isNullOrEmpty() && activity != null) { + FileActivity.showShareLinkDialog(activity, currentFile, share.shareLink) } else { showSendLinkTo(share) } @@ -736,7 +739,9 @@ class FileDetailSharingFragment : return } - file?.let { FileActivity.showShareLinkDialog(fileActivity, file, createInternalLink(user, it, capabilities)) } + val currentFile = file ?: return + val activity = fileActivity ?: return + FileActivity.showShareLinkDialog(activity, currentFile, createInternalLink(user, currentFile, capabilities)) } private fun OCCapability?.shouldAskForPublicLinkPassword(): Boolean = @@ -767,7 +772,7 @@ class FileDetailSharingFragment : } if (share.shareLink.isNullOrEmpty()) { - fileOperationsHelper?.getFileWithLink(file, viewThemeUtils) + fileOperationsHelper?.getFileWithLink(file) return } @@ -925,7 +930,7 @@ class FileDetailSharingFragment : // endregion interface OnEditShareListener { - fun editExistingShare(share: OCShare?, screenTypePermission: Int, isReshareShown: Boolean) + fun editExistingShare(share: OCShare, screenTypePermission: Int, isReshareShown: Boolean) fun onShareProcessClosed() } diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.kt b/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.kt index ba5b88d8ae13..365673c4d9f9 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.kt @@ -402,7 +402,7 @@ open class OCFileListFragment : } mSwitchGridViewButton?.setOnClickListener { toggleGridAndListLayout() } - (activity as? FileDisplayActivity)?.let { it.updateActionBarTitleAndHomeButton(it.currentDir) } + (activity as? FileDisplayActivity)?.let { it.updateActionBarTitleAndHomeButton(it.getCurrentDir()) } } private fun toggleGridAndListLayout() { diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/albums/AlbumOperationListener.kt b/app/src/main/java/com/owncloud/android/ui/fragment/albums/AlbumOperationListener.kt index 823b3c356fd9..833da879c81a 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/albums/AlbumOperationListener.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/albums/AlbumOperationListener.kt @@ -111,7 +111,7 @@ class AlbumOperationListener(private val activity: FileDisplayActivity) { private fun showUntrustedCertDialog(result: RemoteOperationResult<*>) { if (result.isSslRecoverableException) { activity.lastSslUntrustedServerResult = result - activity.showUntrustedCertDialog(activity.lastSslUntrustedServerResult) + activity.showUntrustedCertDialog(result) } } diff --git a/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.kt b/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.kt index 669150aab334..0e8b76cc2cb5 100755 --- a/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.kt +++ b/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.kt @@ -73,7 +73,6 @@ import com.owncloud.android.ui.events.SyncEventFinished import com.owncloud.android.utils.EncryptionUtils import com.owncloud.android.utils.FileStorageUtils import com.owncloud.android.utils.UriUtils -import com.owncloud.android.utils.theme.ViewThemeUtils import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.greenrobot.eventbus.EventBus @@ -387,12 +386,12 @@ class FileOperationsHelper( queueOperation(intent) } - fun getFileWithLink(file: OCFile, viewThemeUtils: ViewThemeUtils?) { + fun getFileWithLink(file: OCFile) { val shares = fileActivity.storageManager.getSharesByPathAndType(file.remotePath, ShareType.PUBLIC_LINK, "") when { shares.size == SINGLE_LINK_SIZE -> - FileActivity.copyAndShareFileLink(fileActivity, file, shares[0].shareLink, viewThemeUtils) + FileActivity.copyAndShareFileLink(fileActivity, file, shares[0].shareLink) fileActivity is FileDisplayActivity -> fileActivity.showDetails(file, SHARING_TAB) @@ -877,7 +876,8 @@ class FileOperationsHelper( } private fun queueOperation(intent: Intent) { - opIdWaitingFor = fileActivity.operationsServiceBinder.queueNewOperation(intent) + val binder = fileActivity.operationsServiceBinder ?: return + opIdWaitingFor = binder.queueNewOperation(intent) } private fun queueOperationAndShowWaitDialog(intent: Intent) { diff --git a/app/src/main/java/com/owncloud/android/ui/preview/media/PreviewMediaActivity.kt b/app/src/main/java/com/owncloud/android/ui/preview/media/PreviewMediaActivity.kt index e8c7df4eb155..0e40d9a31346 100644 --- a/app/src/main/java/com/owncloud/android/ui/preview/media/PreviewMediaActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/preview/media/PreviewMediaActivity.kt @@ -172,11 +172,11 @@ class PreviewMediaActivity : } } - override fun onNewIntent(intent: Intent?) { + override fun onNewIntent(intent: Intent) { super.onNewIntent(intent) // every later read goes through getIntent(), so the selection of the new intent would be lost otherwise - intent?.let { setIntent(it) } + setIntent(intent) initializeContent() } @@ -383,7 +383,7 @@ class PreviewMediaActivity : val isNewSelection = currentFile != file - setFile(currentFile) + file = currentFile showFile(currentFile, optionalUser.get(), isNewSelection) } From be30d0f8fd8d72c36088b6b35f7556686db4c0ef Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Tue, 10 Nov 2026 13:58:46 +0100 Subject: [PATCH 3/4] split into smaller classes Signed-off-by: alperozturk96 --- .../android/ui/activity/FileActivity.kt | 464 ++---------------- .../android/ui/activity/SettingsActivity.java | 5 +- .../fileactivity/DevVersionChecker.kt | 83 ++++ .../FileOperationResultHandler.kt | 95 ++++ .../fileactivity/LoadingDialogController.kt | 63 +++ .../OperationsServiceConnector.kt | 77 +++ .../activity/fileactivity/ShareLinkHelper.kt | 67 +++ .../ShareOperationResultHandler.kt | 150 ++++++ .../fileactivity/ShareWithIntentHandler.kt | 38 ++ .../ui/fragment/FileDetailSharingFragment.kt | 7 +- .../ui/helpers/FileOperationsHelper.kt | 3 +- 11 files changed, 617 insertions(+), 435 deletions(-) create mode 100644 app/src/main/java/com/owncloud/android/ui/activity/fileactivity/DevVersionChecker.kt create mode 100644 app/src/main/java/com/owncloud/android/ui/activity/fileactivity/FileOperationResultHandler.kt create mode 100644 app/src/main/java/com/owncloud/android/ui/activity/fileactivity/LoadingDialogController.kt create mode 100644 app/src/main/java/com/owncloud/android/ui/activity/fileactivity/OperationsServiceConnector.kt create mode 100644 app/src/main/java/com/owncloud/android/ui/activity/fileactivity/ShareLinkHelper.kt create mode 100644 app/src/main/java/com/owncloud/android/ui/activity/fileactivity/ShareOperationResultHandler.kt create mode 100644 app/src/main/java/com/owncloud/android/ui/activity/fileactivity/ShareWithIntentHandler.kt diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.kt index 8643ca7eb3f3..e5a24b5cd41a 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.kt @@ -7,77 +7,52 @@ package com.owncloud.android.ui.activity import android.accounts.Account -import android.accounts.AuthenticatorException -import android.app.Activity -import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.ServiceConnection -import android.content.pm.PackageManager import android.os.Bundle import android.os.Handler -import android.os.IBinder import android.os.Looper -import androidx.annotation.StringRes -import androidx.fragment.app.FragmentManager -import com.google.android.material.snackbar.Snackbar import com.nextcloud.client.account.User -import com.nextcloud.client.account.UserAccountManager import com.nextcloud.client.jobs.BackgroundJobManager import com.nextcloud.client.jobs.upload.FileUploadHelper import com.nextcloud.client.network.ConnectivityService import com.nextcloud.client.network.NetworkChangeListener import com.nextcloud.client.player.ui.audio.AudioPlayerActivity -import com.nextcloud.client.utils.IntentUtil import com.nextcloud.utils.EditorUtils import com.nextcloud.utils.SnackbarUtil import com.nextcloud.utils.extensions.getParcelableArgument -import com.nextcloud.utils.extensions.isActive import com.nextcloud.utils.extensions.logFileSize -import com.nextcloud.utils.mdm.MDMConfig import com.owncloud.android.MainApp import com.owncloud.android.R import com.owncloud.android.datamodel.ArbitraryDataProvider import com.owncloud.android.datamodel.OCFile -import com.owncloud.android.lib.common.OwnCloudAccount import com.owncloud.android.lib.common.accounts.AccountUtils import com.owncloud.android.lib.common.network.CertificateCombinedException import com.owncloud.android.lib.common.operations.OnRemoteOperationListener import com.owncloud.android.lib.common.operations.RemoteOperation import com.owncloud.android.lib.common.operations.RemoteOperationResult -import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode import com.owncloud.android.lib.common.utils.Log_OC -import com.owncloud.android.lib.resources.files.model.ServerFileInterface import com.owncloud.android.lib.resources.shares.OCShare import com.owncloud.android.lib.resources.shares.ShareType -import com.owncloud.android.operations.CreateShareViaLinkOperation -import com.owncloud.android.operations.CreateShareWithShareeOperation -import com.owncloud.android.operations.GetSharesForFileOperation -import com.owncloud.android.operations.SetFilesDownloadLimitOperation -import com.owncloud.android.operations.SynchronizeFileOperation -import com.owncloud.android.operations.SynchronizeFolderOperation -import com.owncloud.android.operations.UnshareOperation -import com.owncloud.android.operations.UpdateNoteForShareOperation -import com.owncloud.android.operations.UpdateShareInfoOperation -import com.owncloud.android.operations.UpdateSharePermissionsOperation -import com.owncloud.android.operations.UpdateShareViaLinkOperation import com.owncloud.android.providers.UsersAndGroupsSearchConfig import com.owncloud.android.providers.UsersAndGroupsSearchProvider -import com.owncloud.android.services.OperationsService import com.owncloud.android.services.OperationsService.OperationsServiceBinder import com.owncloud.android.ui.activity.drawer.DrawerActivity +import com.owncloud.android.ui.activity.fileactivity.DevVersionChecker +import com.owncloud.android.ui.activity.fileactivity.FileOperationResultHandler +import com.owncloud.android.ui.activity.fileactivity.LoadingDialogController +import com.owncloud.android.ui.activity.fileactivity.OperationsServiceConnector +import com.owncloud.android.ui.activity.fileactivity.ShareOperationResultHandler +import com.owncloud.android.ui.activity.fileactivity.ShareWithIntentHandler import com.owncloud.android.ui.asynctasks.CheckRemoteWipeTask -import com.owncloud.android.ui.asynctasks.LoadingVersionNumberTask import com.owncloud.android.ui.asynctasks.LoadingVersionNumberTask.VersionDevInterface import com.owncloud.android.ui.dialog.ConfirmationDialogFragment -import com.owncloud.android.ui.dialog.LoadingDialog -import com.owncloud.android.ui.dialog.ShareLinkToDialog import com.owncloud.android.ui.dialog.SslUntrustedCertDialog import com.owncloud.android.ui.dialog.SslUntrustedCertDialog.OnSslUntrustedCertListener import com.owncloud.android.ui.events.DialogEvent import com.owncloud.android.ui.events.DialogEventType import com.owncloud.android.ui.fragment.FileDetailFragment -import com.owncloud.android.ui.fragment.FileDetailSharingFragment import com.owncloud.android.ui.fragment.FileDetailSharingFragment.OnEditShareListener import com.owncloud.android.ui.fragment.OCFileListFragment import com.owncloud.android.ui.fragment.albums.AlbumItemsFragment @@ -88,16 +63,12 @@ import com.owncloud.android.ui.helpers.CredentialsUpdateHelper import com.owncloud.android.ui.helpers.FileOperationsHelper import com.owncloud.android.ui.navigation.model.ActionBarStyle import com.owncloud.android.ui.preview.media.PreviewMediaActivity -import com.owncloud.android.utils.ClipboardUtil -import com.owncloud.android.utils.ErrorMessageAdapter -import com.owncloud.android.utils.FilesSyncHelper -import com.owncloud.android.utils.MimeType import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode import java.lang.ref.WeakReference import javax.inject.Inject -@Suppress("TooManyFunctions", "LargeClass") +@Suppress("TooManyFunctions") abstract class FileActivity : DrawerActivity(), OnRemoteOperationListener, @@ -124,16 +95,15 @@ abstract class FileActivity : get() = mainThreadHandler private lateinit var operationsHelper: FileOperationsHelper - private var operationsServiceConnection: ServiceConnection? = null - private var operationsBinder: OperationsServiceBinder? = null - private var isActivityResumed = false + private val operationsServiceConnector = OperationsServiceConnector(this, this, ::doOnResumeAndBound) + private val loadingDialogController = LoadingDialogController(this) + private val operationResultHandler = FileOperationResultHandler(this) + private val shareOperationResultHandler = ShareOperationResultHandler(this) + private val shareWithIntentHandler = ShareWithIntentHandler(this, ::doShareWith) protected val fileUploadHelper: FileUploadHelper = FileUploadHelper.instance() protected var isFileDisplayActivityResumed = false - @Inject - lateinit var accountManager: UserAccountManager - @Inject lateinit var connectivityService: ConnectivityService @@ -152,18 +122,6 @@ abstract class FileActivity : lateinit var filesRepository: FilesRepository private set - private val shareFileFragment: FileDetailSharingFragment? - get() { - val fragment = supportFragmentManager.findFragmentByTag(ShareActivity.TAG_SHARE_FRAGMENT) - ?: supportFragmentManager.findFragmentByTag(FileDisplayActivity.TAG_LIST_OF_FILES) - - return when (fragment) { - is FileDetailSharingFragment -> fragment - is FileDetailFragment -> fragment.fileDetailSharingFragment - else -> null - } - } - private val fileDetailFragment: FileDetailFragment? get() = supportFragmentManager.findFragmentByTag(FileDisplayActivity.TAG_LIST_OF_FILES) as? FileDetailFragment @@ -190,7 +148,7 @@ abstract class FileActivity : readIntentExtras() } - bindOperationsService() + operationsServiceConnector.bind() filesRepository = RemoteFilesRepository(clientRepository, this) } @@ -212,12 +170,6 @@ abstract class FileActivity : user?.let { setUser(it) } } - private fun bindOperationsService() { - val connection = OperationsServiceConnection() - operationsServiceConnection = connection - bindService(Intent(this, OperationsService::class.java), connection, BIND_AUTO_CREATE) - } - override fun networkAndServerConnectionListener(isNetworkAndServerAvailable: Boolean) { when { isNetworkAndServerAvailable -> { @@ -246,21 +198,16 @@ abstract class FileActivity : override fun onResume() { super.onResume() - isActivityResumed = true - operationsBinder?.let { doOnResumeAndBound(it) } + operationsServiceConnector.onResume() } override fun onPause() { - operationsBinder?.removeOperationListener(this) - isActivityResumed = false + operationsServiceConnector.onPause() super.onPause() } override fun onDestroy() { - operationsServiceConnection?.let { - unbindService(it) - operationsBinder = null - } + operationsServiceConnector.unbind() super.onDestroy() } @@ -275,7 +222,7 @@ abstract class FileActivity : } } - override fun getOperationsServiceBinder(): OperationsServiceBinder? = operationsBinder + override fun getOperationsServiceBinder(): OperationsServiceBinder? = operationsServiceConnector.binder protected open fun newTransferenceServiceConnection(): ServiceConnection? = null @@ -293,94 +240,24 @@ abstract class FileActivity : Log_OC.d(TAG, "Received result of operation in FileActivity - common behaviour for all the FileActivities ") operationsHelper.opIdWaitingFor = Long.MAX_VALUE dismissLoadingDialog() - onCommonOperationFinish(operation, result) - onShareOperationFinish(operation, result) - } - - private fun onCommonOperationFinish(operation: RemoteOperation<*>?, result: RemoteOperationResult<*>) { - when { - result.isAuthenticationFailure() -> onAuthenticationFailure(operation, result) - !result.isSuccess && result.code == ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED -> - showUntrustedCertDialog(result) - operation == null || operation is SynchronizeFolderOperation -> - onSynchronizeFolderOperationFinish(operation, result) - operation is SynchronizeFileOperation -> onSynchronizeFileOperationFinish(operation, result) - operation is GetSharesForFileOperation -> onGetSharesForFileOperationFinish(operation, result) - } + operationResultHandler.onOperationFinish(operation, result) + shareOperationResultHandler.onOperationFinish(operation, result) } - private fun onShareOperationFinish(operation: RemoteOperation<*>?, result: RemoteOperationResult<*>) { - when (operation) { - is CreateShareViaLinkOperation -> onCreateShareViaLinkOperationFinish(operation, result) - is CreateShareWithShareeOperation -> onUpdateShareInformation(result, R.string.sharee_add_failed) - is UpdateShareViaLinkOperation, - is UpdateShareInfoOperation, - is UpdateSharePermissionsOperation -> onUpdateShareInformation(result, R.string.updating_share_failed) - is SetFilesDownloadLimitOperation -> onUpdateShareInformation(result, R.string.set_download_limit_failed) - is UnshareOperation -> onUpdateShareInformation(result, R.string.unsharing_failed) - is UpdateNoteForShareOperation -> onUpdateNoteForShareOperationFinish(result) - } - } - - private fun RemoteOperationResult<*>.isAuthenticationFailure(): Boolean = - !isSuccess && (code == ResultCode.UNAUTHORIZED || (isException && exception is AuthenticatorException)) - - private fun onAuthenticationFailure(operation: RemoteOperation<*>?, result: RemoteOperationResult<*>) { - requestCredentialsUpdate() - if (result.code == ResultCode.UNAUTHORIZED) { - showErrorCause(operation, result) - } - } - - private fun onSynchronizeFolderOperationFinish(operation: RemoteOperation<*>?, result: RemoteOperationResult<*>) { - when { - result.isSuccess -> updateFileFromDB() - result.code != ResultCode.CANCELLED -> showErrorCause(operation, result) - } - } + private fun doOnResumeAndBound(binder: OperationsServiceBinder) { + binder.addOperationListener(this, mainThreadHandler) - private fun onGetSharesForFileOperationFinish( - operation: GetSharesForFileOperation, - result: RemoteOperationResult<*> - ) { - if (result.isSuccess || result.code == ResultCode.SHARE_NOT_FOUND) { - updateFileFromDB() - } else { - showErrorCause(operation, result) - } - } + val waitingForOpId = operationsHelper.opIdWaitingFor + if (waitingForOpId > Int.MAX_VALUE) return - private fun onSynchronizeFileOperationFinish( - operation: SynchronizeFileOperation, - result: RemoteOperationResult<*> - ) { - if (result.isSuccess) { - if (!operation.transferWasRequested) { - showErrorCause(operation, result) - } - supportInvalidateOptionsMenu() - return + val isOperationDispatchedOrPending = binder.dispatchResultIfFinished(waitingForOpId.toInt(), this) + if (!isOperationDispatchedOrPending) { + dismissLoadingDialog() } - - if (result.code != ResultCode.SYNC_CONFLICT) return - - val currentUser = user.orElseThrow { RuntimeException() } - val intent = ConflictsResolveActivity.createIntent( - operation.localFile, - currentUser, - NO_CONFLICT_UPLOAD_ID, - null, - this - ) - startActivity(intent) - } - - private fun showErrorCause(operation: RemoteOperation<*>?, result: RemoteOperationResult<*>) { - SnackbarUtil.show(this, ErrorMessageAdapter.getErrorCauseMessage(result, operation, resources)) } @JvmOverloads - protected fun requestCredentialsUpdate(account: Account? = null) { + fun requestCredentialsUpdate(account: Account? = null) { val targetAccount = account ?: this.account CheckRemoteWipeTask(backgroundJobManager, targetAccount, WeakReference(this)).execute() } @@ -408,52 +285,14 @@ abstract class FileActivity : .show(fragmentManager.beginTransaction(), DIALOG_UNTRUSTED_CERT) } - protected fun updateFileFromDB() { + fun updateFileFromDB() { val currentFile = file ?: return file = storageManager.getFileByPath(currentFile.remotePath) } - fun showLoadingDialog(message: String?) { - runOnUiThread { - if (!isActive()) { - Log_OC.w(TAG, "cannot show loading dialog, activity is finishing") - return@runOnUiThread - } - - val fragmentManager = supportFragmentManager - fragmentManager.executePendingTransactions() - fragmentManager.dismissExistingLoadingDialog("dismiss previous loading dialog") - - if (fragmentManager.isStateSaved) return@runOnUiThread - - Log_OC.d(TAG, "show loading dialog") - LoadingDialog.newInstance(message).show(fragmentManager, DIALOG_WAIT_TAG) - } - } - - fun dismissLoadingDialog() { - runOnUiThread { - if (!isActive()) { - Log_OC.w(TAG, "cannot dismiss loading dialog, activity is finishing") - return@runOnUiThread - } - - val fragmentManager = supportFragmentManager - fragmentManager.executePendingTransactions() - fragmentManager.dismissExistingLoadingDialog("dismiss loading dialog") - } - } + fun showLoadingDialog(message: String?) = loadingDialogController.show(message) - private fun FragmentManager.dismissExistingLoadingDialog(logMessage: String) { - val loadingDialog = findFragmentByTag(DIALOG_WAIT_TAG) as? LoadingDialog ?: return - Log_OC.d(TAG, logMessage) - - if (isStateSaved) { - loadingDialog.dismissAllowingStateLoss() - } else { - loadingDialog.dismiss() - } - } + fun dismissLoadingDialog() = loadingDialogController.dismiss() fun showSyncLoadingDialog(isFolder: Boolean) { if (isFolder) return @@ -467,40 +306,6 @@ abstract class FileActivity : } } - private fun doOnResumeAndBound(binder: OperationsServiceBinder) { - binder.addOperationListener(this, mainThreadHandler) - - val waitingForOpId = operationsHelper.opIdWaitingFor - if (waitingForOpId > Int.MAX_VALUE) return - - val isOperationDispatchedOrPending = binder.dispatchResultIfFinished(waitingForOpId.toInt(), this) - if (!isOperationDispatchedOrPending) { - dismissLoadingDialog() - } - } - - private inner class OperationsServiceConnection : ServiceConnection { - private val operationsServiceComponent = ComponentName(this@FileActivity, OperationsService::class.java) - - override fun onServiceConnected(component: ComponentName, service: IBinder) { - if (component != operationsServiceComponent) return - - Log_OC.d(TAG, "Operations service connected") - val binder = service as OperationsServiceBinder - operationsBinder = binder - if (isActivityResumed) { - doOnResumeAndBound(binder) - } - } - - override fun onServiceDisconnected(component: ComponentName) { - if (component != operationsServiceComponent) return - - Log_OC.d(TAG, "Operations service disconnected") - operationsBinder = null - } - } - override fun onSavedCertificate() = Unit override fun onFailedSavingCertificate() { @@ -515,44 +320,14 @@ abstract class FileActivity : } fun checkForNewDevVersionNecessary(context: Context) { - if (!resources.getBoolean(R.bool.dev_version_direct_download_enabled)) return - - val appOpenedCount = arbitraryDataProvider.getIntegerValue(FilesSyncHelper.GLOBAL, APP_OPENED_COUNT) - if (appOpenedCount > MIN_APP_OPENED_COUNT_FOR_DEV_VERSION_CHECK || appOpenedCount == UNKNOWN_VALUE) { - checkForNewDevVersion(this, context) + if (DevVersionChecker.isCheckNecessary(resources, arbitraryDataProvider)) { + DevVersionChecker.checkForNewDevVersion(this, context) } } override fun returnVersion(latestVersion: Int?) { val version = latestVersion ?: return - showDevSnackbar(this, version, openDirectly = false, inBackground = true) - } - - private fun onUpdateNoteForShareOperationFinish(result: RemoteOperationResult<*>) { - if (!result.isSuccess) { - SnackbarUtil.show(this, R.string.note_could_not_sent) - return - } - shareFileFragment?.onUpdateShareInformation(result) - } - - private fun onUpdateShareInformation(result: RemoteOperationResult<*>, @StringRes defaultError: Int) { - val sharingFragment = shareFileFragment - - if (result.isSuccess) { - updateFileFromDB() - sharingFragment?.onUpdateShareInformation(result) - return - } - - val message = result.message.takeUnless { it.isNullOrEmpty() } ?: getString(defaultError) - val sharingFragmentView = sharingFragment?.view - if (sharingFragmentView == null) { - SnackbarUtil.show(this, message) - return - } - - Snackbar.make(sharingFragmentView, message, Snackbar.LENGTH_LONG).showThemed() + DevVersionChecker.showDevSnackbar(this, version, openDirectly = false, inBackground = true) } fun refreshList() { @@ -574,91 +349,14 @@ abstract class FileActivity : } } - private fun onCreateShareViaLinkOperationFinish( - operation: CreateShareViaLinkOperation, - result: RemoteOperationResult<*> - ) { - if (result.isSuccess) { - onShareViaLinkCreated(result) - } else { - onShareViaLinkFailed(operation, result) - } - } - - private fun onShareViaLinkCreated(result: RemoteOperationResult<*>) { - val sharingFragment = shareFileFragment - val fileListFragment = supportFragmentManager.findFragmentByTag(FileDisplayActivity.TAG_LIST_OF_FILES) - updateFileFromDB() - - val publicLinkShare = result.data - .filterIsInstance() - .firstOrNull { FileDisplayActivity.TAG_PUBLIC_LINK.equals(it.shareType?.name, ignoreCase = true) } - val sharedFile = publicLinkShare?.let { storageManager.getFileByEncryptedRemotePath(it.path) } - - if (publicLinkShare != null && sharedFile != null) { - copyAndShareFileLink(this, sharedFile, publicLinkShare.shareLink) - } - sharingFragment?.onUpdateShareInformation(result, sharedFile) - - if (fileListFragment !is OCFileListFragment || sharedFile == null) return - - if (fileListFragment.adapterFiles.contains(sharedFile)) { - fileListFragment.updateOCFile(sharedFile) - } else { - SnackbarUtil.show(this, R.string.file_activity_shared_file_cannot_be_updated) - } - } - - private fun onShareViaLinkFailed(operation: CreateShareViaLinkOperation, result: RemoteOperationResult<*>) { - val sharingFragment = shareFileFragment - val isForbiddenWithoutPassword = - result.code == ResultCode.SHARE_FORBIDDEN && operation.password.isNullOrEmpty() - - if (!isForbiddenWithoutPassword) { - sharingFragment?.refreshSharesFromDB() - val errorMessage = ErrorMessageAdapter.getErrorCauseMessage(result, operation, resources) - Snackbar.make(findViewById(android.R.id.content), errorMessage, Snackbar.LENGTH_LONG).showThemed() - return - } - - val capability = capabilities.orElse(null) ?: return - if (!capability.filesSharingPublicEnabled.isUnknown) return - if (sharingFragment == null || !sharingFragment.isAdded) return - - sharingFragment.requestPasswordForShareViaLink(true, capability.filesSharingPublicAskForOptionalPassword.isTrue) - } - - private fun Snackbar.showThemed() { - viewThemeUtils.material.themeSnackbar(this) - show() - } - override fun onNewIntent(intent: Intent) { super.onNewIntent(intent) - if (intent.action == UsersAndGroupsSearchProvider.ACTION_SHARE_WITH) { - onShareWithIntent(intent) - } - } + if (intent.action != UsersAndGroupsSearchProvider.ACTION_SHARE_WITH) return - private fun onShareWithIntent(intent: Intent) { - val data = intent.data ?: return val remotePath = fileFromDetailFragment?.remotePath ?: return - val shareWith = data.toString().substringAfterLast('/') - val shareType = UsersAndGroupsSearchProvider.getShareType(data.authority) - - val isAlreadyShared = storageManager - .getSharesWithForAFile(remotePath, account.name) - .any { shareeKey(it.shareType, it.shareWith) == shareeKey(shareType, shareWith) } - - if (isAlreadyShared) { - SnackbarUtil.show(this, getString(R.string.sharee_already_added_to_file)) - } else { - doShareWith(shareWith, shareType) - } + shareWithIntentHandler.handle(intent, remotePath) } - private fun shareeKey(shareType: ShareType?, shareWith: String?): String = "${shareType}_$shareWith" - protected open fun doShareWith(shareeName: String, shareType: ShareType) { fileDetailFragment?.initiateSharingProcess(shareeName, shareType, usersAndGroupsSearchConfig.searchOnlyUsers) } @@ -687,105 +385,13 @@ abstract class FileActivity : const val REQUEST_CODE__UPDATE_CREDENTIALS = 0 const val REQUEST_CODE__LAST_SHARED = REQUEST_CODE__UPDATE_CREDENTIALS - private const val DIALOG_WAIT_TAG = "DIALOG_WAIT" private const val DIALOG_UNTRUSTED_CERT = "DIALOG_UNTRUSTED_CERT" private const val DIALOG_CERT_NOT_SAVED = "DIALOG_CERT_NOT_SAVED" private const val KEY_WAITING_FOR_OP_ID = "WAITING_FOR_OP_ID" private const val KEY_ACTION_BAR_TITLE = "ACTION_BAR_TITLE" - private const val NO_CONFLICT_UPLOAD_ID = -1L private const val NO_TITLE_RES = 0 private const val NO_BUTTON_RES = -1 - private const val UNKNOWN_VALUE = -1 - private const val MIN_APP_OPENED_COUNT_FOR_DEV_VERSION_CHECK = 10 - private const val DEV_APK_EXTENSION = ".apk" - - @JvmStatic - fun checkForNewDevVersion(callback: VersionDevInterface?, context: Context) { - LoadingVersionNumberTask(callback).execute(context.getString(R.string.dev_latest)) - } - - @JvmStatic - fun showDevSnackbar(activity: Activity, latestVersion: Int, openDirectly: Boolean, inBackground: Boolean) { - val currentVersion = activity.installedVersionCode() - - if (latestVersion == UNKNOWN_VALUE || currentVersion == UNKNOWN_VALUE) { - SnackbarUtil.show(activity, R.string.dev_version_no_information_available) - } - - if (latestVersion <= currentVersion) { - if (!inBackground) { - SnackbarUtil.show(activity, R.string.dev_version_no_new_version_available) - } - return - } - - val devApkLink = activity.getString(R.string.dev_link) + latestVersion + DEV_APK_EXTENSION - if (openDirectly) { - IntentUtil.startLinkIntent(activity, devApkLink) - return - } - - Snackbar - .make( - activity.findViewById(android.R.id.content), - R.string.dev_version_new_version_available, - Snackbar.LENGTH_LONG - ) - .setAction(activity.getString(R.string.version_dev_download)) { - IntentUtil.startLinkIntent(activity, devApkLink) - } - .show() - } - - @Suppress("DEPRECATION") - private fun Activity.installedVersionCode(): Int = try { - packageManager.getPackageInfo(packageName, 0).versionCode - } catch (e: PackageManager.NameNotFoundException) { - Log_OC.e(TAG, "Package not found", e) - UNKNOWN_VALUE - } - - fun copyAndShareFileLink(activity: FileActivity, file: OCFile, link: String?) { - if (!MDMConfig.shareViaLink(activity) || !MDMConfig.clipBoardSupport(activity)) return - - ClipboardUtil.copyToClipboard(activity, link, false) - Snackbar - .make(activity.findViewById(android.R.id.content), R.string.clipboard_text_copied, Snackbar.LENGTH_LONG) - .setAction(R.string.share) { showShareLinkDialog(activity, file, link) } - .also { activity.viewThemeUtils.material.themeSnackbar(it) } - .show() - } - - fun showShareLinkDialog(activity: FileActivity, file: ServerFileInterface, link: String?) { - val username = activity.sharingUsername() - val subject = if (username != null) { - activity.getString(R.string.subject_user_shared_with_you, username, file.fileName) - } else { - activity.getString(R.string.subject_shared_with_you, file.fileName) - } - - val intentToShareLink = Intent(Intent.ACTION_SEND).apply { - putExtra(Intent.EXTRA_TEXT, link) - type = MimeType.TEXT_PLAIN - putExtra(Intent.EXTRA_SUBJECT, subject) - } - - ShareLinkToDialog - .newInstance(intentToShareLink, activity.packageName) - .show(activity.supportFragmentManager, FileDisplayActivity.FTAG_CHOOSER_DIALOG) - } - - @Suppress("TooGenericExceptionCaught") - private fun FileActivity.sharingUsername(): String? { - val displayName = try { - OwnCloudAccount(account, this).displayName - } catch (e: Exception) { - Log_OC.e(TAG, "Could not read account display name", e) - null - } - return displayName.takeUnless { it.isNullOrEmpty() } ?: AccountUtils.getUsernameForAccount(account) - } } } 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 0b8c51755f78..92a192af45c9 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 @@ -72,6 +72,7 @@ import com.owncloud.android.operations.e2e.E2EDeletionService; import com.owncloud.android.providers.documentstorage.DocumentsStorageProvider; import com.owncloud.android.ui.ThemeableSwitchPreference; +import com.owncloud.android.ui.activity.fileactivity.DevVersionChecker; import com.owncloud.android.ui.asynctasks.LoadingVersionNumberTask; import com.owncloud.android.ui.dialog.setupEncryption.SetupEncryptionDialogFragment; import com.owncloud.android.ui.helpers.FileOperationsHelper; @@ -241,7 +242,7 @@ private void setupDevCategory(PreferenceScreen preferenceScreen) { if (pDevLink != null) { if (getResources().getBoolean(R.bool.dev_version_direct_download_enabled)) { pDevLink.setOnPreferenceClickListener(preference -> { - FileActivity.checkForNewDevVersion(this, getApplicationContext()); + DevVersionChecker.checkForNewDevVersion(this, getApplicationContext()); return true; }); } else { @@ -1324,6 +1325,6 @@ public void onCancelMigration() { @Override public void returnVersion(Integer latestVersion) { - FileActivity.showDevSnackbar(this, latestVersion, true, false); + DevVersionChecker.showDevSnackbar(this, latestVersion, true, false); } } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/fileactivity/DevVersionChecker.kt b/app/src/main/java/com/owncloud/android/ui/activity/fileactivity/DevVersionChecker.kt new file mode 100644 index 000000000000..ca995f2114f5 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/activity/fileactivity/DevVersionChecker.kt @@ -0,0 +1,83 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +package com.owncloud.android.ui.activity.fileactivity + +import android.app.Activity +import android.content.Context +import android.content.pm.PackageManager +import android.content.res.Resources +import com.google.android.material.snackbar.Snackbar +import com.nextcloud.client.utils.IntentUtil +import com.nextcloud.utils.SnackbarUtil +import com.owncloud.android.R +import com.owncloud.android.datamodel.ArbitraryDataProvider +import com.owncloud.android.lib.common.utils.Log_OC +import com.owncloud.android.ui.activity.FileActivity +import com.owncloud.android.ui.asynctasks.LoadingVersionNumberTask +import com.owncloud.android.ui.asynctasks.LoadingVersionNumberTask.VersionDevInterface +import com.owncloud.android.utils.FilesSyncHelper + +object DevVersionChecker { + private const val TAG = "DevVersionChecker" + private const val UNKNOWN_VALUE = -1 + private const val MIN_APP_OPENED_COUNT_FOR_CHECK = 10 + private const val DEV_APK_EXTENSION = ".apk" + + fun isCheckNecessary(resources: Resources, arbitraryDataProvider: ArbitraryDataProvider): Boolean { + if (!resources.getBoolean(R.bool.dev_version_direct_download_enabled)) return false + + val appOpenedCount = + arbitraryDataProvider.getIntegerValue(FilesSyncHelper.GLOBAL, FileActivity.APP_OPENED_COUNT) + return appOpenedCount > MIN_APP_OPENED_COUNT_FOR_CHECK || appOpenedCount == UNKNOWN_VALUE + } + + @JvmStatic + fun checkForNewDevVersion(callback: VersionDevInterface?, context: Context) { + LoadingVersionNumberTask(callback).execute(context.getString(R.string.dev_latest)) + } + + @JvmStatic + fun showDevSnackbar(activity: Activity, latestVersion: Int, openDirectly: Boolean, inBackground: Boolean) { + val currentVersion = activity.installedVersionCode() + + if (latestVersion == UNKNOWN_VALUE || currentVersion == UNKNOWN_VALUE) { + SnackbarUtil.show(activity, R.string.dev_version_no_information_available) + } + + if (latestVersion <= currentVersion) { + if (!inBackground) { + SnackbarUtil.show(activity, R.string.dev_version_no_new_version_available) + } + return + } + + val devApkLink = activity.getString(R.string.dev_link) + latestVersion + DEV_APK_EXTENSION + if (openDirectly) { + IntentUtil.startLinkIntent(activity, devApkLink) + return + } + + Snackbar + .make( + activity.findViewById(android.R.id.content), + R.string.dev_version_new_version_available, + Snackbar.LENGTH_LONG + ) + .setAction(activity.getString(R.string.version_dev_download)) { + IntentUtil.startLinkIntent(activity, devApkLink) + } + .show() + } + + @Suppress("DEPRECATION") + private fun Activity.installedVersionCode(): Int = try { + packageManager.getPackageInfo(packageName, 0).versionCode + } catch (e: PackageManager.NameNotFoundException) { + Log_OC.e(TAG, "Package not found", e) + UNKNOWN_VALUE + } +} diff --git a/app/src/main/java/com/owncloud/android/ui/activity/fileactivity/FileOperationResultHandler.kt b/app/src/main/java/com/owncloud/android/ui/activity/fileactivity/FileOperationResultHandler.kt new file mode 100644 index 000000000000..2bcff2e187fc --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/activity/fileactivity/FileOperationResultHandler.kt @@ -0,0 +1,95 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +package com.owncloud.android.ui.activity.fileactivity + +import android.accounts.AuthenticatorException +import com.nextcloud.utils.SnackbarUtil +import com.owncloud.android.lib.common.operations.RemoteOperation +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode +import com.owncloud.android.operations.GetSharesForFileOperation +import com.owncloud.android.operations.SynchronizeFileOperation +import com.owncloud.android.operations.SynchronizeFolderOperation +import com.owncloud.android.ui.activity.ConflictsResolveActivity +import com.owncloud.android.ui.activity.FileActivity +import com.owncloud.android.utils.ErrorMessageAdapter + +class FileOperationResultHandler(private val activity: FileActivity) { + + fun onOperationFinish(operation: RemoteOperation<*>?, result: RemoteOperationResult<*>) { + when { + result.isAuthenticationFailure() -> onAuthenticationFailure(operation, result) + !result.isSuccess && result.code == ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED -> + activity.showUntrustedCertDialog(result) + operation == null || operation is SynchronizeFolderOperation -> + onSynchronizeFolderOperationFinish(operation, result) + operation is SynchronizeFileOperation -> onSynchronizeFileOperationFinish(operation, result) + operation is GetSharesForFileOperation -> onGetSharesForFileOperationFinish(operation, result) + } + } + + private fun RemoteOperationResult<*>.isAuthenticationFailure(): Boolean = + !isSuccess && (code == ResultCode.UNAUTHORIZED || (isException && exception is AuthenticatorException)) + + private fun onAuthenticationFailure(operation: RemoteOperation<*>?, result: RemoteOperationResult<*>) { + activity.requestCredentialsUpdate() + if (result.code == ResultCode.UNAUTHORIZED) { + showErrorCause(operation, result) + } + } + + private fun onSynchronizeFolderOperationFinish(operation: RemoteOperation<*>?, result: RemoteOperationResult<*>) { + when { + result.isSuccess -> activity.updateFileFromDB() + result.code != ResultCode.CANCELLED -> showErrorCause(operation, result) + } + } + + private fun onGetSharesForFileOperationFinish( + operation: GetSharesForFileOperation, + result: RemoteOperationResult<*> + ) { + if (result.isSuccess || result.code == ResultCode.SHARE_NOT_FOUND) { + activity.updateFileFromDB() + } else { + showErrorCause(operation, result) + } + } + + private fun onSynchronizeFileOperationFinish( + operation: SynchronizeFileOperation, + result: RemoteOperationResult<*> + ) { + if (result.isSuccess) { + if (!operation.transferWasRequested) { + showErrorCause(operation, result) + } + activity.supportInvalidateOptionsMenu() + return + } + + if (result.code != ResultCode.SYNC_CONFLICT) return + + val currentUser = activity.user.orElseThrow { RuntimeException() } + val intent = ConflictsResolveActivity.createIntent( + operation.localFile, + currentUser, + NO_CONFLICT_UPLOAD_ID, + null, + activity + ) + activity.startActivity(intent) + } + + private fun showErrorCause(operation: RemoteOperation<*>?, result: RemoteOperationResult<*>) { + SnackbarUtil.show(activity, ErrorMessageAdapter.getErrorCauseMessage(result, operation, activity.resources)) + } + + companion object { + private const val NO_CONFLICT_UPLOAD_ID = -1L + } +} diff --git a/app/src/main/java/com/owncloud/android/ui/activity/fileactivity/LoadingDialogController.kt b/app/src/main/java/com/owncloud/android/ui/activity/fileactivity/LoadingDialogController.kt new file mode 100644 index 000000000000..e120fa9be6ca --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/activity/fileactivity/LoadingDialogController.kt @@ -0,0 +1,63 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +package com.owncloud.android.ui.activity.fileactivity + +import androidx.appcompat.app.AppCompatActivity +import androidx.fragment.app.FragmentManager +import com.nextcloud.utils.extensions.isActive +import com.owncloud.android.lib.common.utils.Log_OC +import com.owncloud.android.ui.dialog.LoadingDialog + +class LoadingDialogController(private val activity: AppCompatActivity) { + + fun show(message: String?) { + activity.runOnUiThread { + if (!activity.isActive()) { + Log_OC.w(TAG, "cannot show loading dialog, activity is finishing") + return@runOnUiThread + } + + val fragmentManager = activity.supportFragmentManager + fragmentManager.executePendingTransactions() + fragmentManager.dismissExistingLoadingDialog("dismiss previous loading dialog") + + if (fragmentManager.isStateSaved) return@runOnUiThread + + Log_OC.d(TAG, "show loading dialog") + LoadingDialog.newInstance(message).show(fragmentManager, DIALOG_WAIT_TAG) + } + } + + fun dismiss() { + activity.runOnUiThread { + if (!activity.isActive()) { + Log_OC.w(TAG, "cannot dismiss loading dialog, activity is finishing") + return@runOnUiThread + } + + val fragmentManager = activity.supportFragmentManager + fragmentManager.executePendingTransactions() + fragmentManager.dismissExistingLoadingDialog("dismiss loading dialog") + } + } + + private fun FragmentManager.dismissExistingLoadingDialog(logMessage: String) { + val loadingDialog = findFragmentByTag(DIALOG_WAIT_TAG) as? LoadingDialog ?: return + Log_OC.d(TAG, logMessage) + + if (isStateSaved) { + loadingDialog.dismissAllowingStateLoss() + } else { + loadingDialog.dismiss() + } + } + + companion object { + private const val TAG = "LoadingDialogController" + private const val DIALOG_WAIT_TAG = "DIALOG_WAIT" + } +} diff --git a/app/src/main/java/com/owncloud/android/ui/activity/fileactivity/OperationsServiceConnector.kt b/app/src/main/java/com/owncloud/android/ui/activity/fileactivity/OperationsServiceConnector.kt new file mode 100644 index 000000000000..f2a550212420 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/activity/fileactivity/OperationsServiceConnector.kt @@ -0,0 +1,77 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +package com.owncloud.android.ui.activity.fileactivity + +import android.app.Activity +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.content.ServiceConnection +import android.os.IBinder +import com.owncloud.android.lib.common.operations.OnRemoteOperationListener +import com.owncloud.android.lib.common.utils.Log_OC +import com.owncloud.android.services.OperationsService +import com.owncloud.android.services.OperationsService.OperationsServiceBinder + +class OperationsServiceConnector( + private val activity: Activity, + private val listener: OnRemoteOperationListener, + private val onBoundWhileResumed: (OperationsServiceBinder) -> Unit +) : ServiceConnection { + + var binder: OperationsServiceBinder? = null + private set + + private var isBound = false + private var isActivityResumed = false + + fun bind() { + activity.bindService(Intent(activity, OperationsService::class.java), this, Context.BIND_AUTO_CREATE) + isBound = true + } + + fun unbind() { + if (!isBound) return + activity.unbindService(this) + binder = null + } + + fun onResume() { + isActivityResumed = true + binder?.let(onBoundWhileResumed) + } + + fun onPause() { + binder?.removeOperationListener(listener) + isActivityResumed = false + } + + override fun onServiceConnected(component: ComponentName, service: IBinder) { + if (!component.isOperationsService()) return + + Log_OC.d(TAG, "Operations service connected") + val connectedBinder = service as OperationsServiceBinder + binder = connectedBinder + if (isActivityResumed) { + onBoundWhileResumed(connectedBinder) + } + } + + override fun onServiceDisconnected(component: ComponentName) { + if (!component.isOperationsService()) return + + Log_OC.d(TAG, "Operations service disconnected") + binder = null + } + + private fun ComponentName.isOperationsService(): Boolean = + this == ComponentName(activity, OperationsService::class.java) + + companion object { + private const val TAG = "OperationsServiceConnector" + } +} diff --git a/app/src/main/java/com/owncloud/android/ui/activity/fileactivity/ShareLinkHelper.kt b/app/src/main/java/com/owncloud/android/ui/activity/fileactivity/ShareLinkHelper.kt new file mode 100644 index 000000000000..b214144a775c --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/activity/fileactivity/ShareLinkHelper.kt @@ -0,0 +1,67 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +package com.owncloud.android.ui.activity.fileactivity + +import android.content.Intent +import com.google.android.material.snackbar.Snackbar +import com.nextcloud.utils.mdm.MDMConfig +import com.owncloud.android.R +import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.lib.common.OwnCloudAccount +import com.owncloud.android.lib.common.accounts.AccountUtils +import com.owncloud.android.lib.common.utils.Log_OC +import com.owncloud.android.lib.resources.files.model.ServerFileInterface +import com.owncloud.android.ui.activity.FileActivity +import com.owncloud.android.ui.activity.FileDisplayActivity +import com.owncloud.android.ui.dialog.ShareLinkToDialog +import com.owncloud.android.utils.ClipboardUtil +import com.owncloud.android.utils.MimeType + +object ShareLinkHelper { + private const val TAG = "ShareLinkHelper" + + fun copyAndShareFileLink(activity: FileActivity, file: OCFile, link: String?) { + if (!MDMConfig.shareViaLink(activity) || !MDMConfig.clipBoardSupport(activity)) return + + ClipboardUtil.copyToClipboard(activity, link, false) + Snackbar + .make(activity.findViewById(android.R.id.content), R.string.clipboard_text_copied, Snackbar.LENGTH_LONG) + .setAction(R.string.share) { showShareLinkDialog(activity, file, link) } + .also { activity.viewThemeUtils.material.themeSnackbar(it) } + .show() + } + + fun showShareLinkDialog(activity: FileActivity, file: ServerFileInterface, link: String?) { + val username = activity.sharingUsername() + val subject = if (username != null) { + activity.getString(R.string.subject_user_shared_with_you, username, file.fileName) + } else { + activity.getString(R.string.subject_shared_with_you, file.fileName) + } + + val intentToShareLink = Intent(Intent.ACTION_SEND).apply { + putExtra(Intent.EXTRA_TEXT, link) + type = MimeType.TEXT_PLAIN + putExtra(Intent.EXTRA_SUBJECT, subject) + } + + ShareLinkToDialog + .newInstance(intentToShareLink, activity.packageName) + .show(activity.supportFragmentManager, FileDisplayActivity.FTAG_CHOOSER_DIALOG) + } + + @Suppress("TooGenericExceptionCaught") + private fun FileActivity.sharingUsername(): String? { + val displayName = try { + OwnCloudAccount(account, this).displayName + } catch (e: Exception) { + Log_OC.e(TAG, "Could not read account display name", e) + null + } + return displayName.takeUnless { it.isNullOrEmpty() } ?: AccountUtils.getUsernameForAccount(account) + } +} diff --git a/app/src/main/java/com/owncloud/android/ui/activity/fileactivity/ShareOperationResultHandler.kt b/app/src/main/java/com/owncloud/android/ui/activity/fileactivity/ShareOperationResultHandler.kt new file mode 100644 index 000000000000..ab513462ae3c --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/activity/fileactivity/ShareOperationResultHandler.kt @@ -0,0 +1,150 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +package com.owncloud.android.ui.activity.fileactivity + +import androidx.annotation.StringRes +import com.google.android.material.snackbar.Snackbar +import com.nextcloud.utils.SnackbarUtil +import com.owncloud.android.R +import com.owncloud.android.lib.common.operations.RemoteOperation +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode +import com.owncloud.android.lib.resources.shares.OCShare +import com.owncloud.android.operations.CreateShareViaLinkOperation +import com.owncloud.android.operations.CreateShareWithShareeOperation +import com.owncloud.android.operations.SetFilesDownloadLimitOperation +import com.owncloud.android.operations.UnshareOperation +import com.owncloud.android.operations.UpdateNoteForShareOperation +import com.owncloud.android.operations.UpdateShareInfoOperation +import com.owncloud.android.operations.UpdateSharePermissionsOperation +import com.owncloud.android.operations.UpdateShareViaLinkOperation +import com.owncloud.android.ui.activity.FileActivity +import com.owncloud.android.ui.activity.FileDisplayActivity +import com.owncloud.android.ui.activity.ShareActivity +import com.owncloud.android.ui.fragment.FileDetailFragment +import com.owncloud.android.ui.fragment.FileDetailSharingFragment +import com.owncloud.android.ui.fragment.OCFileListFragment +import com.owncloud.android.utils.ErrorMessageAdapter + +class ShareOperationResultHandler(private val activity: FileActivity) { + + private val fragmentManager + get() = activity.supportFragmentManager + + private val sharingFragment: FileDetailSharingFragment? + get() { + val fragment = fragmentManager.findFragmentByTag(ShareActivity.TAG_SHARE_FRAGMENT) + ?: fragmentManager.findFragmentByTag(FileDisplayActivity.TAG_LIST_OF_FILES) + + return when (fragment) { + is FileDetailSharingFragment -> fragment + is FileDetailFragment -> fragment.fileDetailSharingFragment + else -> null + } + } + + fun onOperationFinish(operation: RemoteOperation<*>?, result: RemoteOperationResult<*>) { + when (operation) { + is CreateShareViaLinkOperation -> onCreateShareViaLinkOperationFinish(operation, result) + is CreateShareWithShareeOperation -> onUpdateShareInformation(result, R.string.sharee_add_failed) + is UpdateShareViaLinkOperation, + is UpdateShareInfoOperation, + is UpdateSharePermissionsOperation -> onUpdateShareInformation(result, R.string.updating_share_failed) + is SetFilesDownloadLimitOperation -> onUpdateShareInformation(result, R.string.set_download_limit_failed) + is UnshareOperation -> onUpdateShareInformation(result, R.string.unsharing_failed) + is UpdateNoteForShareOperation -> onUpdateNoteForShareOperationFinish(result) + } + } + + private fun onUpdateNoteForShareOperationFinish(result: RemoteOperationResult<*>) { + if (!result.isSuccess) { + SnackbarUtil.show(activity, R.string.note_could_not_sent) + return + } + sharingFragment?.onUpdateShareInformation(result) + } + + private fun onUpdateShareInformation(result: RemoteOperationResult<*>, @StringRes defaultError: Int) { + val sharingFragment = sharingFragment + + if (result.isSuccess) { + activity.updateFileFromDB() + sharingFragment?.onUpdateShareInformation(result) + return + } + + val message = result.message.takeUnless { it.isNullOrEmpty() } ?: activity.getString(defaultError) + val sharingFragmentView = sharingFragment?.view + if (sharingFragmentView == null) { + SnackbarUtil.show(activity, message) + return + } + + Snackbar.make(sharingFragmentView, message, Snackbar.LENGTH_LONG).showThemed() + } + + private fun onCreateShareViaLinkOperationFinish( + operation: CreateShareViaLinkOperation, + result: RemoteOperationResult<*> + ) { + if (result.isSuccess) { + onShareViaLinkCreated(result) + } else { + onShareViaLinkFailed(operation, result) + } + } + + private fun onShareViaLinkCreated(result: RemoteOperationResult<*>) { + val sharingFragment = sharingFragment + val fileListFragment = fragmentManager.findFragmentByTag(FileDisplayActivity.TAG_LIST_OF_FILES) + activity.updateFileFromDB() + + val publicLinkShare = result.data + .filterIsInstance() + .firstOrNull { FileDisplayActivity.TAG_PUBLIC_LINK.equals(it.shareType?.name, ignoreCase = true) } + val sharedFile = publicLinkShare?.let { activity.storageManager.getFileByEncryptedRemotePath(it.path) } + + if (publicLinkShare != null && sharedFile != null) { + ShareLinkHelper.copyAndShareFileLink(activity, sharedFile, publicLinkShare.shareLink) + } + sharingFragment?.onUpdateShareInformation(result, sharedFile) + + if (fileListFragment !is OCFileListFragment || sharedFile == null) return + + if (fileListFragment.adapterFiles.contains(sharedFile)) { + fileListFragment.updateOCFile(sharedFile) + } else { + SnackbarUtil.show(activity, R.string.file_activity_shared_file_cannot_be_updated) + } + } + + private fun onShareViaLinkFailed(operation: CreateShareViaLinkOperation, result: RemoteOperationResult<*>) { + val sharingFragment = sharingFragment + val isForbiddenWithoutPassword = + result.code == ResultCode.SHARE_FORBIDDEN && operation.password.isNullOrEmpty() + + if (!isForbiddenWithoutPassword) { + sharingFragment?.refreshSharesFromDB() + val errorMessage = ErrorMessageAdapter.getErrorCauseMessage(result, operation, activity.resources) + Snackbar + .make(activity.findViewById(android.R.id.content), errorMessage, Snackbar.LENGTH_LONG) + .showThemed() + return + } + + val capability = activity.capabilities.orElse(null) ?: return + if (!capability.filesSharingPublicEnabled.isUnknown) return + if (sharingFragment == null || !sharingFragment.isAdded) return + + sharingFragment.requestPasswordForShareViaLink(true, capability.filesSharingPublicAskForOptionalPassword.isTrue) + } + + private fun Snackbar.showThemed() { + activity.viewThemeUtils.material.themeSnackbar(this) + show() + } +} diff --git a/app/src/main/java/com/owncloud/android/ui/activity/fileactivity/ShareWithIntentHandler.kt b/app/src/main/java/com/owncloud/android/ui/activity/fileactivity/ShareWithIntentHandler.kt new file mode 100644 index 000000000000..a88021f87798 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/activity/fileactivity/ShareWithIntentHandler.kt @@ -0,0 +1,38 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +package com.owncloud.android.ui.activity.fileactivity + +import android.content.Intent +import com.nextcloud.utils.SnackbarUtil +import com.owncloud.android.R +import com.owncloud.android.lib.resources.shares.ShareType +import com.owncloud.android.providers.UsersAndGroupsSearchProvider +import com.owncloud.android.ui.activity.FileActivity + +class ShareWithIntentHandler( + private val activity: FileActivity, + private val onNewSharee: (shareeName: String, shareType: ShareType) -> Unit +) { + + fun handle(intent: Intent, remotePath: String) { + val data = intent.data ?: return + val shareWith = data.toString().substringAfterLast('/') + val shareType = UsersAndGroupsSearchProvider.getShareType(data.authority) + + val isAlreadyShared = activity.storageManager + .getSharesWithForAFile(remotePath, activity.account.name) + .any { shareeKey(it.shareType, it.shareWith) == shareeKey(shareType, shareWith) } + + if (isAlreadyShared) { + SnackbarUtil.show(activity, activity.getString(R.string.sharee_already_added_to_file)) + } else { + onNewSharee(shareWith, shareType) + } + } + + private fun shareeKey(shareType: ShareType?, shareWith: String?): String = "${shareType}_$shareWith" +} diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.kt b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.kt index b2336578730c..c1b1f6ccada5 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.kt @@ -63,6 +63,7 @@ import com.owncloud.android.lib.resources.status.OCCapability import com.owncloud.android.operations.RefreshFolderOperation import com.owncloud.android.providers.UsersAndGroupsSearchConfig import com.owncloud.android.ui.activity.FileActivity +import com.owncloud.android.ui.activity.fileactivity.ShareLinkHelper import com.owncloud.android.ui.adapter.ShareeListAdapter import com.owncloud.android.ui.adapter.ShareeListAdapterListener import com.owncloud.android.ui.asynctasks.RetrieveHoverCardAsyncTask @@ -555,7 +556,7 @@ class FileDetailSharingFragment : } val activity = fileActivity ?: return - FileActivity.showShareLinkDialog(activity, file, publicShare.shareLink) + ShareLinkHelper.showShareLinkDialog(activity, file, publicShare.shareLink) } private fun refreshLegacyShareUiFromDb() { @@ -721,7 +722,7 @@ class FileDetailSharingFragment : val currentFile = file val activity = fileActivity if (currentFile?.isSharedViaLink == true && !share.shareLink.isNullOrEmpty() && activity != null) { - FileActivity.showShareLinkDialog(activity, currentFile, share.shareLink) + ShareLinkHelper.showShareLinkDialog(activity, currentFile, share.shareLink) } else { showSendLinkTo(share) } @@ -741,7 +742,7 @@ class FileDetailSharingFragment : val currentFile = file ?: return val activity = fileActivity ?: return - FileActivity.showShareLinkDialog(activity, currentFile, createInternalLink(user, currentFile, capabilities)) + ShareLinkHelper.showShareLinkDialog(activity, currentFile, createInternalLink(user, currentFile, capabilities)) } private fun OCCapability?.shouldAskForPublicLinkPassword(): Boolean = diff --git a/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.kt b/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.kt index 0e8b76cc2cb5..915c20c180cc 100755 --- a/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.kt +++ b/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.kt @@ -65,6 +65,7 @@ import com.owncloud.android.ui.activity.FileDisplayActivity import com.owncloud.android.ui.activity.RichDocumentsEditorWebView import com.owncloud.android.ui.activity.ShareActivity import com.owncloud.android.ui.activity.TextEditorWebView +import com.owncloud.android.ui.activity.fileactivity.ShareLinkHelper import com.owncloud.android.ui.dialog.SendFilesDialog import com.owncloud.android.ui.dialog.SendShareDialog import com.owncloud.android.ui.events.FavoriteEvent @@ -391,7 +392,7 @@ class FileOperationsHelper( when { shares.size == SINGLE_LINK_SIZE -> - FileActivity.copyAndShareFileLink(fileActivity, file, shares[0].shareLink) + ShareLinkHelper.copyAndShareFileLink(fileActivity, file, shares[0].shareLink) fileActivity is FileDisplayActivity -> fileActivity.showDetails(file, SHARING_TAB) From 50bce00a1cc77a8bb1629d1a356cfbb45a8c3e09 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Tue, 10 Nov 2026 14:07:05 +0100 Subject: [PATCH 4/4] fix formatting Signed-off-by: alperozturk96 --- .../ui/activity/fileactivity/FileOperationResultHandler.kt | 4 ++++ .../ui/activity/fileactivity/ShareOperationResultHandler.kt | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/app/src/main/java/com/owncloud/android/ui/activity/fileactivity/FileOperationResultHandler.kt b/app/src/main/java/com/owncloud/android/ui/activity/fileactivity/FileOperationResultHandler.kt index 2bcff2e187fc..b75611566f71 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/fileactivity/FileOperationResultHandler.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/fileactivity/FileOperationResultHandler.kt @@ -23,11 +23,15 @@ class FileOperationResultHandler(private val activity: FileActivity) { fun onOperationFinish(operation: RemoteOperation<*>?, result: RemoteOperationResult<*>) { when { result.isAuthenticationFailure() -> onAuthenticationFailure(operation, result) + !result.isSuccess && result.code == ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED -> activity.showUntrustedCertDialog(result) + operation == null || operation is SynchronizeFolderOperation -> onSynchronizeFolderOperationFinish(operation, result) + operation is SynchronizeFileOperation -> onSynchronizeFileOperationFinish(operation, result) + operation is GetSharesForFileOperation -> onGetSharesForFileOperationFinish(operation, result) } } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/fileactivity/ShareOperationResultHandler.kt b/app/src/main/java/com/owncloud/android/ui/activity/fileactivity/ShareOperationResultHandler.kt index ab513462ae3c..b03a19d80124 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/fileactivity/ShareOperationResultHandler.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/fileactivity/ShareOperationResultHandler.kt @@ -50,12 +50,17 @@ class ShareOperationResultHandler(private val activity: FileActivity) { fun onOperationFinish(operation: RemoteOperation<*>?, result: RemoteOperationResult<*>) { when (operation) { is CreateShareViaLinkOperation -> onCreateShareViaLinkOperationFinish(operation, result) + is CreateShareWithShareeOperation -> onUpdateShareInformation(result, R.string.sharee_add_failed) + is UpdateShareViaLinkOperation, is UpdateShareInfoOperation, is UpdateSharePermissionsOperation -> onUpdateShareInformation(result, R.string.updating_share_failed) + is SetFilesDownloadLimitOperation -> onUpdateShareInformation(result, R.string.set_download_limit_failed) + is UnshareOperation -> onUpdateShareInformation(result, R.string.unsharing_failed) + is UpdateNoteForShareOperation -> onUpdateNoteForShareOperationFinish(result) } }