Skip to content

Commit c2c2983

Browse files
PhilLabalperozturk96
authored andcommitted
Verify file name validity during user input.
The automated test only tests this partially, because the only invalid name was an emtpy name. All other characters which are typically unsuitable for a filename, like the directory separator '/' are not properly rejected during the test run, because the FileNameValidator is based on the user capabilities, which are null because this is a sever-less test. Signed-off-by: Philipp Hasper <vcs@hasper.info>
1 parent 680784b commit c2c2983

4 files changed

Lines changed: 94 additions & 2 deletions

File tree

app/src/androidTest/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivityIT.kt

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import com.owncloud.android.AbstractIT
3131
import com.owncloud.android.R
3232
import com.owncloud.android.datamodel.OCFile
3333
import com.owncloud.android.utils.ScreenshotTest
34+
import org.hamcrest.Matchers.not
3435
import org.junit.Rule
3536
import org.junit.Test
3637
import org.junit.rules.TestRule
@@ -75,7 +76,7 @@ class ReceiveExternalFilesActivityIT : AbstractIT() {
7576
val imageFile = getDummyFile("image.jpg")
7677
val intent = createSendIntent(imageFile)
7778

78-
// Create folders with the necessary permissions
79+
// Create folders with the necessary permissions and another test file
7980
val mainFolder = OCFile("/folder/").apply {
8081
permissions = OCFile.PERMISSION_CAN_CREATE_FILE_AND_FOLDER
8182
setFolder()
@@ -86,6 +87,9 @@ class ReceiveExternalFilesActivityIT : AbstractIT() {
8687
setFolder()
8788
fileDataStorageManager.saveNewFile(this)
8889
}
90+
val otherFile = OCFile("${mainFolder.remotePath}Other Image File.jpg").apply {
91+
fileDataStorageManager.saveNewFile(this)
92+
}
8993

9094
// Store the folder in preferences, so the activity starts from there.
9195
@Suppress("DEPRECATION")
@@ -117,6 +121,9 @@ class ReceiveExternalFilesActivityIT : AbstractIT() {
117121
.perform(ViewActions.pressKey(KeyEvent.KEYCODE_TAB))
118122
.perform(ViewActions.click())
119123
.check(matches(withSelectedText(secondFileName.removeFileExtension())))
124+
onView(withText(R.string.uploader_btn_upload_text))
125+
.check(matches(isDisplayed()))
126+
.check(matches(isEnabled()))
120127

121128
// Set a file name without file extension
122129
val thirdFileName = "No extension"
@@ -128,6 +135,35 @@ class ReceiveExternalFilesActivityIT : AbstractIT() {
128135
.perform(ViewActions.pressKey(KeyEvent.KEYCODE_TAB))
129136
.perform(ViewActions.click())
130137
.check(matches(withSelectedText(thirdFileName)))
138+
onView(withText(R.string.uploader_btn_upload_text))
139+
.check(matches(isDisplayed()))
140+
.check(matches(isEnabled()))
141+
142+
// Test an invalid filename. Note: as the user is null, the capabilities are also null, so the name checker
143+
// will not reject any special characters like '/'. So we only test empty and an existing file name
144+
onView(withId(R.id.user_input))
145+
.perform(ViewActions.clearText())
146+
.check(matches(withText("")))
147+
onView(withText(R.string.uploader_btn_upload_text))
148+
.check(matches(isDisplayed()))
149+
.check(matches(not(isEnabled())))
150+
onView(withId(R.id.user_input))
151+
.perform(ViewActions.click())
152+
.perform(ViewActions.typeTextIntoFocusedView(otherFile.fileName))
153+
.check(matches(withText(otherFile.fileName)))
154+
onView(withText(R.string.uploader_btn_upload_text))
155+
.check(matches(isDisplayed()))
156+
.check(matches(not(isEnabled())))
157+
158+
val fourthFileName = "New file name.jpg"
159+
onView(withId(R.id.user_input))
160+
.perform(ViewActions.click())
161+
.perform(ViewActions.clearText())
162+
.perform(ViewActions.typeTextIntoFocusedView(fourthFileName))
163+
.check(matches(withText(fourthFileName)))
164+
onView(withText(R.string.uploader_btn_upload_text))
165+
.check(matches(isDisplayed()))
166+
.check(matches(isEnabled()))
131167
}
132168
}
133169
}

app/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@
3030
import android.os.Handler;
3131
import android.os.Looper;
3232
import android.os.Parcelable;
33+
import android.text.Editable;
3334
import android.text.TextUtils;
35+
import android.text.TextWatcher;
3436
import android.text.format.DateFormat;
3537
import android.view.LayoutInflater;
3638
import android.view.Menu;
@@ -89,6 +91,8 @@
8991
import com.owncloud.android.utils.MimeType;
9092
import com.owncloud.android.utils.theme.ViewThemeUtils;
9193

94+
import org.apache.commons.io.FilenameUtils;
95+
9296
import java.io.File;
9397
import java.io.FileWriter;
9498
import java.io.IOException;
@@ -129,7 +133,7 @@
129133
public class ReceiveExternalFilesActivity extends FileActivity
130134
implements View.OnClickListener, CopyAndUploadContentUrisTask.OnCopyTmpFilesTaskListener,
131135
SortingOrderDialogFragment.OnSortingOrderListener, Injectable, AccountChooserInterface,
132-
ReceiveExternalFilesAdapter.OnItemClickListener {
136+
ReceiveExternalFilesAdapter.OnItemClickListener, TextWatcher {
133137

134138
private static final String TAG = ReceiveExternalFilesActivity.class.getSimpleName();
135139

@@ -330,6 +334,53 @@ public void selectFile(OCFile file) {
330334
}
331335
}
332336

337+
@Override
338+
public void afterTextChanged(Editable editable) {}
339+
340+
@Override
341+
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
342+
343+
// TODO: this is copy-paste from RenameFileDialogFragment.kt
344+
@Override
345+
public void onTextChanged(CharSequence s, int start, int before, int count) {
346+
final var newFileName = Objects.requireNonNullElse(binding.userInput.getText(), "").toString();
347+
final var positiveButton = binding.uploaderChooseFolder;
348+
349+
final var existingFiles = receiveExternalFilesAdapter.getFileNames();
350+
final var errorMessage = FileNameValidator.INSTANCE.checkFileName(newFileName, getCapabilities(), this, existingFiles);
351+
352+
if (FileNameValidator.INSTANCE.isFileHidden(newFileName)) {
353+
binding.userInputContainer.setError(getText(R.string.hidden_file_name_warning));
354+
positiveButton.setEnabled(true);
355+
} else if (errorMessage != null) {
356+
binding.userInputContainer.setError(errorMessage);
357+
positiveButton.setEnabled(false);
358+
} else if (checkExtensionRenamed(newFileName)) {
359+
binding.userInputContainer.setError(getText(R.string.warn_rename_extension));
360+
positiveButton.setEnabled(true);
361+
} else if (binding.userInputContainer.getError() != null) {
362+
binding.userInputContainer.setError(null);
363+
// Called to remove extra padding
364+
binding.userInputContainer.setErrorEnabled(false);
365+
positiveButton.setEnabled(true);
366+
}
367+
}
368+
369+
private boolean checkExtensionRenamed(@NonNull String newFileName) {
370+
if (mStreamsToUpload == null || mStreamsToUpload.size() != 1) {
371+
return false;
372+
}
373+
final String previousFileName = getDisplayNameForUri((Uri) mStreamsToUpload.get(0), getActivity());
374+
if (previousFileName == null) {
375+
return false;
376+
}
377+
378+
var previousExtension = FilenameUtils.getExtension(previousFileName);
379+
var newExtension = FilenameUtils.getExtension(newFileName);
380+
381+
return !Objects.equals(previousExtension, newExtension);
382+
}
383+
333384
public static class DialogNoAccount extends DialogFragment {
334385
private final ViewThemeUtils viewThemeUtils;
335386

@@ -860,6 +911,7 @@ private void setupFileNameInputField() {
860911

861912
binding.userInput.setVisibility(View.VISIBLE);
862913
binding.userInput.setText(fileName);
914+
binding.userInput.addTextChangedListener(this);
863915
mFileDisplayNameTransformer = uri ->
864916
Objects.requireNonNullElse(binding.userInput.getText(), fileName).toString();
865917

app/src/main/java/com/owncloud/android/ui/adapter/ReceiveExternalFilesAdapter.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import com.owncloud.android.datamodel.ThumbnailsCacheManager.ThumbnailGeneration
2626
import com.owncloud.android.utils.DisplayUtils
2727
import com.owncloud.android.utils.MimeTypeUtil
2828
import com.owncloud.android.utils.theme.ViewThemeUtils
29+
import java.util.Objects
2930

3031
@Suppress("LongParameterList")
3132
class ReceiveExternalFilesAdapter(
@@ -159,4 +160,6 @@ class ReceiveExternalFilesAdapter(
159160
}
160161

161162
override fun getItemCount() = filteredFiles.size
163+
164+
fun getFileNames(): Set<String> = files.map { it.fileName }.toSet()
162165
}

app/src/main/res/layout/receive_external_files.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
android:background="@color/list_divider_background" />
4949

5050
<com.google.android.material.textfield.TextInputLayout
51+
android:id="@+id/user_input_container"
5152
android:layout_width="match_parent"
5253
android:layout_height="wrap_content"
5354
android:background="@color/bg_default"

0 commit comments

Comments
 (0)