diff --git a/app/src/androidTest/java/com/nmc/android/FileMenuFilterIT.kt b/app/src/androidTest/java/com/nmc/android/FileMenuFilterIT.kt
new file mode 100644
index 000000000000..58dc7c8386db
--- /dev/null
+++ b/app/src/androidTest/java/com/nmc/android/FileMenuFilterIT.kt
@@ -0,0 +1,152 @@
+/*
+ * Nextcloud Android client application
+ *
+ * @author Álvaro Brey Vilas
+ * Copyright (C) 2022 Álvaro Brey Vilas
+ * Copyright (C) 2022 Nextcloud GmbH
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.nmc.android
+
+import androidx.test.core.app.launchActivity
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.nextcloud.client.account.User
+import com.nextcloud.client.jobs.download.FileDownloadWorker
+import com.nextcloud.client.jobs.upload.FileUploadHelper
+import com.nextcloud.test.TestActivity
+import com.nextcloud.utils.EditorUtils
+import com.owncloud.android.AbstractIT
+import com.owncloud.android.R
+import com.owncloud.android.datamodel.ArbitraryDataProvider
+import com.owncloud.android.datamodel.FileDataStorageManager
+import com.owncloud.android.datamodel.OCFile
+import com.owncloud.android.files.FileMenuFilter
+import com.owncloud.android.lib.resources.status.CapabilityBooleanType
+import com.owncloud.android.lib.resources.status.OCCapability
+import com.owncloud.android.services.OperationsService
+import com.owncloud.android.ui.activity.ComponentsGetter
+import com.owncloud.android.utils.MimeType
+import io.mockk.MockKAnnotations
+import io.mockk.every
+import io.mockk.impl.annotations.MockK
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.security.SecureRandom
+
+@RunWith(AndroidJUnit4::class)
+class FileMenuFilterIT : AbstractIT() {
+
+ @MockK
+ private lateinit var mockComponentsGetter: ComponentsGetter
+
+ @MockK
+ private lateinit var mockStorageManager: FileDataStorageManager
+
+ @MockK
+ private lateinit var mockFileUploaderBinder: FileUploadHelper
+
+ @MockK
+ private lateinit var mockOperationsServiceBinder: OperationsService.OperationsServiceBinder
+
+ @MockK
+ private lateinit var mockFileDownloadProgressListener: FileDownloadWorker.FileDownloadProgressListener
+
+ @MockK
+ private lateinit var mockArbitraryDataProvider: ArbitraryDataProvider
+
+ private lateinit var editorUtils: EditorUtils
+
+ @Before
+ fun setup() {
+ MockKAnnotations.init(this)
+ every { mockFileUploaderBinder.isUploading(any(), any()) } returns false
+ every { mockComponentsGetter.fileUploaderHelper } returns mockFileUploaderBinder
+ every { mockFileDownloadProgressListener.isDownloading(any(), any()) } returns false
+ every { mockComponentsGetter.fileDownloadProgressListener } returns mockFileDownloadProgressListener
+ every { mockOperationsServiceBinder.isSynchronizing(any(), any()) } returns false
+ every { mockComponentsGetter.operationsServiceBinder } returns mockOperationsServiceBinder
+ every { mockStorageManager.getFileById(any()) } returns OCFile("/")
+ every { mockStorageManager.getFolderContent(any(), any()) } returns ArrayList()
+ every { mockArbitraryDataProvider.getValue(any(), any()) } returns ""
+ editorUtils = EditorUtils(mockArbitraryDataProvider)
+ }
+
+ @Test
+ fun hide_shareAndFavouriteMenu_encryptedFolder() {
+ val capability = OCCapability().apply {
+ endToEndEncryption = CapabilityBooleanType.TRUE
+ }
+
+ val encryptedFolder = OCFile("/encryptedFolder/").apply {
+ isEncrypted = true
+ mimeType = MimeType.DIRECTORY
+ fileLength = SecureRandom().nextLong()
+ }
+
+ configureCapability(capability)
+
+ launchActivity().use {
+ it.onActivity { activity ->
+ val filterFactory =
+ FileMenuFilter.Factory(mockStorageManager, activity, editorUtils)
+
+ val sut = filterFactory.newInstance(encryptedFolder, mockComponentsGetter, true, user)
+ val toHide = sut.getToHide(false)
+
+ // encrypted folder
+ assertTrue(toHide.contains(R.id.action_see_details))
+ assertTrue(toHide.contains(R.id.action_favorite))
+ assertTrue(toHide.contains(R.id.action_unset_favorite))
+ }
+ }
+ }
+
+ @Test
+ fun show_shareAndFavouriteMenu_normalFolder() {
+ val capability = OCCapability().apply {
+ endToEndEncryption = CapabilityBooleanType.TRUE
+ }
+
+ val normalFolder = OCFile("/folder/").apply {
+ mimeType = MimeType.DIRECTORY
+ fileLength = SecureRandom().nextLong()
+ }
+
+ configureCapability(capability)
+
+ launchActivity().use {
+ it.onActivity { activity ->
+ val filterFactory =
+ FileMenuFilter.Factory(mockStorageManager, activity, editorUtils)
+
+ val sut = filterFactory.newInstance(normalFolder, mockComponentsGetter, true, user)
+ val toHide = sut.getToHide(false)
+
+ // normal folder
+ assertFalse(toHide.contains(R.id.action_see_details))
+ assertFalse(toHide.contains(R.id.action_favorite))
+ assertTrue(toHide.contains(R.id.action_unset_favorite))
+ }
+ }
+ }
+
+ private fun configureCapability(capability: OCCapability) {
+ every { mockStorageManager.getCapability(any()) } returns capability
+ every { mockStorageManager.getCapability(any()) } returns capability
+ }
+}
diff --git a/app/src/androidTest/java/com/nmc/android/SetupEncryptionDialogFragmentIT.kt b/app/src/androidTest/java/com/nmc/android/SetupEncryptionDialogFragmentIT.kt
new file mode 100644
index 000000000000..91cd80a457ae
--- /dev/null
+++ b/app/src/androidTest/java/com/nmc/android/SetupEncryptionDialogFragmentIT.kt
@@ -0,0 +1,56 @@
+package com.nmc.android
+
+import androidx.test.espresso.Espresso.onView
+import androidx.test.espresso.assertion.ViewAssertions.matches
+import androidx.test.espresso.intent.rule.IntentsTestRule
+import androidx.test.espresso.matcher.ViewMatchers.withHint
+import androidx.test.espresso.matcher.ViewMatchers.withId
+import androidx.test.internal.runner.junit4.statement.UiThreadStatement
+import com.nextcloud.test.TestActivity
+import com.owncloud.android.AbstractIT
+import com.owncloud.android.ui.dialog.SetupEncryptionDialogFragment
+import org.junit.Rule
+import org.junit.Test
+import com.owncloud.android.R
+
+class SetupEncryptionDialogFragmentIT : AbstractIT() {
+
+ @get:Rule
+ val testActivityRule = IntentsTestRule(TestActivity::class.java, true, false)
+
+ @Test
+ fun validatePassphraseInputHint() {
+ val activity = testActivityRule.launchActivity(null)
+
+ val sut = SetupEncryptionDialogFragment.newInstance(user, 0)
+
+ sut.show(activity.supportFragmentManager, "1")
+
+ val keyWords = arrayListOf(
+ "ability",
+ "able",
+ "about",
+ "above",
+ "absent",
+ "absorb",
+ "abstract",
+ "absurd",
+ "abuse",
+ "access",
+ "accident",
+ "account",
+ "accuse"
+ )
+
+ shortSleep()
+
+ UiThreadStatement.runOnUiThread {
+ sut.setMnemonic(keyWords)
+ sut.showMnemonicInfo()
+ }
+
+ waitForIdleSync()
+
+ onView(withId(R.id.encryption_passwordInput)).check(matches(withHint("Passphrase…")))
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/owncloud/android/datamodel/OCFile.java b/app/src/main/java/com/owncloud/android/datamodel/OCFile.java
index 48108dc91f45..98f404a70577 100644
--- a/app/src/main/java/com/owncloud/android/datamodel/OCFile.java
+++ b/app/src/main/java/com/owncloud/android/datamodel/OCFile.java
@@ -620,6 +620,10 @@ public boolean isHidden() {
}
/**
+ * The unique fileId for the file within the instance This only works if we have 12 digits for instanceId RemoteId
+ * is a combination of localId + instanceId If a file has remoteId: 4174305739oc97a8ddfc96, in this 4174305739 is
+ * localId & oc97a8ddfc96 is instanceId which is of 12 digits
+ *
* unique fileId for the file within the instance
*/
@SuppressFBWarnings("STT")
@@ -627,7 +631,8 @@ public long getLocalId() {
if (localId > 0) {
return localId;
} else if (remoteId != null && remoteId.length() > 8) {
- return Long.parseLong(remoteId.substring(0, 8).replaceAll("^0*", ""));
+ //NMC Customization --> for long remote id's
+ return Long.parseLong(remoteId.substring(0, remoteId.length() - 12).replaceAll("^0*", ""));
} else {
return -1;
}
diff --git a/app/src/main/java/com/owncloud/android/files/FileMenuFilter.java b/app/src/main/java/com/owncloud/android/files/FileMenuFilter.java
index 38e855f69a13..6f397e5ca849 100644
--- a/app/src/main/java/com/owncloud/android/files/FileMenuFilter.java
+++ b/app/src/main/java/com/owncloud/android/files/FileMenuFilter.java
@@ -175,7 +175,7 @@ private List filter(boolean inSingleFileFragment) {
private void filterShareFile(List toHide, OCCapability capability) {
- if (!isSingleSelection() || containsEncryptedFile() || hasEncryptedParent() ||
+ if (!isSingleSelection() || containsEncryptedFile() || hasEncryptedParent() || containsEncryptedFolder() ||
(!isShareViaLinkAllowed() && !isShareWithUsersAllowed()) ||
!isShareApiEnabled(capability) || !files.iterator().next().canReshare()) {
toHide.add(R.id.action_send_share_file);
@@ -191,19 +191,21 @@ private void filterSendFiles(List toHide, boolean inSingleFileFragment)
}
private void filterDetails(Collection toHide) {
- if (!isSingleSelection()) {
+ if (!isSingleSelection() || containsEncryptedFolder() || containsEncryptedFile()) {
toHide.add(R.id.action_see_details);
}
}
private void filterFavorite(List toHide, boolean synchronizing) {
- if (files.isEmpty() || synchronizing || allFavorites()) {
+ if (files.isEmpty() || synchronizing || allFavorites() || containsEncryptedFile()
+ || containsEncryptedFolder()) {
toHide.add(R.id.action_favorite);
}
}
private void filterUnfavorite(List toHide, boolean synchronizing) {
- if (files.isEmpty() || synchronizing || allNotFavorites()) {
+ if (files.isEmpty() || synchronizing || allNotFavorites() || containsEncryptedFile()
+ || containsEncryptedFolder()) {
toHide.add(R.id.action_unset_favorite);
}
}
@@ -289,8 +291,8 @@ private boolean isRichDocumentEditingSupported(OCCapability capability, String m
}
private void filterSync(List toHide, boolean synchronizing) {
- if (files.isEmpty() || (!anyFileDown() && !containsFolder()) || synchronizing || containsEncryptedFile()
- || containsEncryptedFolder()) {
+ //NMC Customization --> show sync option for e2ee folder
+ if (files.isEmpty() || (!anyFileDown() && !containsFolder()) || synchronizing) {
toHide.add(R.id.action_sync_file);
}
}
@@ -332,8 +334,10 @@ private void filterSelectAll(List toHide, boolean inSingleFileFragment)
}
private void filterRemove(List toHide, boolean synchronizing) {
- if (files.isEmpty() || synchronizing || containsLockedFile()
- || containsEncryptedFolder() || isFolderAndContainsEncryptedFile()) {
+ if ((files.isEmpty() || synchronizing || containsLockedFile()
+ || containsEncryptedFolder() || isFolderAndContainsEncryptedFile())
+ //show delete option for encrypted sub-folder
+ && !hasEncryptedParent()) {
toHide.add(R.id.action_remove_file);
}
}
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 0961de2671b8..7791327a0fe0 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
@@ -60,6 +60,8 @@ open class FolderPickerActivity :
private var mSyncBroadcastReceiver: SyncBroadcastReceiver? = null
private var mSyncInProgress = false
private var mSearchOnlyFolders = false
+ private var mShowOnlyFolder = false
+ private var mHideEncryptedFolder = false
var isDoNotEnterEncryptedFolder = false
private set
@@ -113,6 +115,9 @@ open class FolderPickerActivity :
}
private fun setupAction() {
+ mShowOnlyFolder = intent.getBooleanExtra(EXTRA_SHOW_ONLY_FOLDER, false)
+ mHideEncryptedFolder = intent.getBooleanExtra(EXTRA_HIDE_ENCRYPTED_FOLDER, false)
+
action = intent.getStringExtra(EXTRA_ACTION)
if (action != null && action == CHOOSE_LOCATION) {
@@ -358,7 +363,7 @@ open class FolderPickerActivity :
}
private fun refreshListOfFilesFragment(fromSearch: Boolean) {
- listOfFilesFragment?.listDirectory(false, fromSearch)
+ listOfFilesFragment?.listDirectoryFolder(false, fromSearch, mShowOnlyFolder, mHideEncryptedFolder)
}
fun browseToRoot() {
@@ -660,6 +665,12 @@ open class FolderPickerActivity :
@JvmField
val EXTRA_ACTION = FolderPickerActivity::class.java.canonicalName?.plus(".EXTRA_ACTION")
+ @JvmField
+ val EXTRA_SHOW_ONLY_FOLDER = FolderPickerActivity::class.java.canonicalName?.plus(".EXTRA_SHOW_ONLY_FOLDER")
+
+ @JvmField
+ val EXTRA_HIDE_ENCRYPTED_FOLDER = FolderPickerActivity::class.java.canonicalName?.plus(".EXTRA_HIDE_ENCRYPTED_FOLDER")
+
const val MOVE_OR_COPY = "MOVE_OR_COPY"
const val CHOOSE_LOCATION = "CHOOSE_LOCATION"
private val TAG = FolderPickerActivity::class.java.simpleName
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 bb6793eb7867..dd34998cc186 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
@@ -417,10 +417,14 @@ private void setupE2EPreference(PreferenceCategory preferenceCategoryMore) {
Preference preference = findPreference("setup_e2e");
if (preference != null) {
+
+ if (!CapabilityUtils.getCapability(this).getEndToEndEncryption().isTrue()) {
+ preferenceCategoryMore.removePreference(preference);
+ return;
+ }
+
if (FileOperationsHelper.isEndToEndEncryptionSetup(this, user) ||
- CapabilityUtils.getCapability(this).getEndToEndEncryptionKeysExist().isTrue() ||
- CapabilityUtils.getCapability(this).getEndToEndEncryptionKeysExist().isUnknown()
- ) {
+ CapabilityUtils.getCapability(this).getEndToEndEncryptionKeysExist().isTrue()) {
preferenceCategoryMore.removePreference(preference);
} else {
preference.setOnPreferenceClickListener(p -> {
@@ -493,19 +497,16 @@ private void removeE2E(PreferenceCategory preferenceCategoryMore) {
preferenceCategoryMore.removePreference(preference);
} else {
preference.setOnPreferenceClickListener(p -> {
- AlertDialog.Builder builder = new AlertDialog.Builder(this, R.style.FallbackTheming_Dialog);
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
AlertDialog alertDialog = builder.setTitle(R.string.prefs_e2e_mnemonic)
.setMessage(getString(R.string.remove_e2e_message))
.setCancelable(true)
.setNegativeButton(R.string.common_cancel, ((dialog, i) -> dialog.dismiss()))
.setPositiveButton(R.string.confirm_removal, (dialog, which) -> {
EncryptionUtils.removeE2E(arbitraryDataProvider, user);
- preferenceCategoryMore.removePreference(preference);
- Preference pMnemonic = findPreference("mnemonic");
- if (pMnemonic != null) {
- preferenceCategoryMore.removePreference(pMnemonic);
- }
+ //restart to show the preferences correctly
+ restartSettingsActivity();
dialog.dismiss();
})
@@ -961,13 +962,18 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
} else if (requestCode == ACTION_SHOW_MNEMONIC && resultCode == RESULT_OK) {
handleMnemonicRequest(data);
} else if (requestCode == ACTION_E2E && data != null && data.getBooleanExtra(SetupEncryptionDialogFragment.SUCCESS, false)) {
- Intent i = new Intent(this, SettingsActivity.class);
- i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- i.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
- startActivity(i);
+ //restart to show the preferences correctly
+ restartSettingsActivity();
}
}
+ private void restartSettingsActivity() {
+ Intent i = new Intent(this, SettingsActivity.class);
+ i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ i.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
+ startActivity(i);
+ }
+
@VisibleForTesting
public void handleMnemonicRequest(Intent data) {
if (data == null) {
@@ -980,8 +986,8 @@ public void handleMnemonicRequest(Intent data) {
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProviderImpl(this);
String mnemonic = arbitraryDataProvider.getValue(user.getAccountName(), EncryptionUtils.MNEMONIC).trim();
- AlertDialog.Builder builder = new AlertDialog.Builder(this, R.style.FallbackTheming_Dialog);
- AlertDialog alertDialog = builder.setTitle(R.string.prefs_e2e_mnemonic)
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ AlertDialog alertDialog = builder.setTitle(R.string.dialog_e2e_mnemonic_title)
.setMessage(mnemonic)
.setNegativeButton(R.string.common_cancel, (dialog, i) -> dialog.dismiss())
.setNeutralButton(R.string.common_copy, (dialog, i) ->
@@ -990,7 +996,6 @@ public void handleMnemonicRequest(Intent data) {
.create();
alertDialog.show();
- viewThemeUtils.platform.colorTextButtons(alertDialog.getButton(AlertDialog.BUTTON_POSITIVE));
}
}
}
diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java
index cd99bf1a2dc7..2b227f4c49fe 100644
--- a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java
+++ b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java
@@ -65,6 +65,7 @@
import com.owncloud.android.utils.DisplayUtils;
import com.owncloud.android.utils.FileSortOrder;
import com.owncloud.android.utils.FileStorageUtils;
+import com.owncloud.android.utils.MimeType;
import com.owncloud.android.utils.MimeTypeUtil;
import com.owncloud.android.utils.theme.CapabilityUtils;
import com.owncloud.android.utils.theme.ViewThemeUtils;
@@ -757,6 +758,65 @@ public void swapDirectory(
notifyDataSetChanged();
}
+ /**
+ * method will only called if only folders has to show
+ */
+ public void showOnlyFolder(
+ User account,
+ OCFile directory,
+ FileDataStorageManager updatedStorageManager,
+ boolean onlyOnDevice, String limitToMimeType,
+ boolean hideEncryptedFolder
+ ) {
+ this.onlyOnDevice = onlyOnDevice;
+
+ if (updatedStorageManager != null && !updatedStorageManager.equals(mStorageManager)) {
+ mStorageManager = updatedStorageManager;
+ ocFileListDelegate.setShowShareAvatar(mStorageManager
+ .getCapability(user.getAccountName())
+ .getVersion()
+ .isShareesOnDavSupported());
+ this.user = account;
+ }
+
+ if (mStorageManager != null) {
+
+ List allFiles = mStorageManager.getFolderContent(directory, onlyOnDevice);
+ mFiles.clear();
+
+ for (int i = 0; i < allFiles.size(); i++) {
+ OCFile ocFile = allFiles.get(i);
+
+ //if e2ee folder has to hide then ignore if OCFile is encrypted
+ if (hideEncryptedFolder && ocFile.isEncrypted()) {
+ continue;
+ }
+
+ if (ocFile.getMimeType().equals(MimeType.DIRECTORY)) {
+ mFiles.add(allFiles.get(i));
+ }
+ }
+
+ if (!preferences.isShowHiddenFilesEnabled()) {
+ mFiles = filterHiddenFiles(mFiles);
+ }
+ if (!limitToMimeType.isEmpty()) {
+ mFiles = filterByMimeType(mFiles, limitToMimeType);
+ }
+ FileSortOrder sortOrder = preferences.getSortOrderByFolder(directory);
+ mFiles = sortOrder.sortCloudFiles(mFiles);
+ mFilesAll.clear();
+ mFilesAll.addAll(mFiles);
+
+ currentDirectory = directory;
+ } else {
+ mFiles.clear();
+ mFilesAll.clear();
+ }
+
+ new Handler(Looper.getMainLooper()).post(this::notifyDataSetChanged);
+ }
+
public void setData(List