Merge pull request #5491 from nextcloud/conflictDialog
Enhance conflict dialog
|
@ -73,6 +73,7 @@
|
||||||
</XML>
|
</XML>
|
||||||
<codeStyleSettings language="JAVA">
|
<codeStyleSettings language="JAVA">
|
||||||
<option name="ALIGN_MULTILINE_PARAMETERS_IN_CALLS" value="true" />
|
<option name="ALIGN_MULTILINE_PARAMETERS_IN_CALLS" value="true" />
|
||||||
|
<option name="ALIGN_MULTILINE_METHOD_BRACKETS" value="true" />
|
||||||
<option name="WRAP_COMMENTS" value="true" />
|
<option name="WRAP_COMMENTS" value="true" />
|
||||||
<option name="IF_BRACE_FORCE" value="3" />
|
<option name="IF_BRACE_FORCE" value="3" />
|
||||||
<option name="DOWHILE_BRACE_FORCE" value="3" />
|
<option name="DOWHILE_BRACE_FORCE" value="3" />
|
||||||
|
@ -192,5 +193,8 @@
|
||||||
</rules>
|
</rules>
|
||||||
</arrangement>
|
</arrangement>
|
||||||
</codeStyleSettings>
|
</codeStyleSettings>
|
||||||
|
<codeStyleSettings language="kotlin">
|
||||||
|
<option name="ALIGN_MULTILINE_METHOD_BRACKETS" value="true" />
|
||||||
|
</codeStyleSettings>
|
||||||
</code_scheme>
|
</code_scheme>
|
||||||
</component>
|
</component>
|
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 24 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 12 KiB |
|
@ -6,7 +6,7 @@ if ( [[ $(grep NC_TEST_SERVER_BASEURL ~/.gradle/gradle.properties | grep -v "#
|
||||||
fi
|
fi
|
||||||
|
|
||||||
## emulator
|
## emulator
|
||||||
if ( [[ $(emulator -list-avds | grep uiComparison -c) -eq 0 ]] ); then
|
if ( [[ ! $(emulator -list-avds | grep uiComparison -c) -eq 0 ]] ); then
|
||||||
avdmanager delete avd -n uiComparison
|
avdmanager delete avd -n uiComparison
|
||||||
(sleep 5; echo "no") | avdmanager create avd -n uiComparison -c 100M -k "system-images;android-27;google_apis;x86" --abi "google_apis/x86"
|
(sleep 5; echo "no") | avdmanager create avd -n uiComparison -c 100M -k "system-images;android-27;google_apis;x86" --abi "google_apis/x86"
|
||||||
fi
|
fi
|
||||||
|
|
BIN
src/androidTest/assets/image.jpg
Normal file
After Width: | Height: | Size: 73 KiB |
|
@ -0,0 +1,383 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Nextcloud Android client application
|
||||||
|
*
|
||||||
|
* @author Tobias Kaminsky
|
||||||
|
* Copyright (C) 2020 Tobias Kaminsky
|
||||||
|
* Copyright (C) 2020 Nextcloud GmbH
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.owncloud.android.files.services
|
||||||
|
|
||||||
|
import com.evernote.android.job.JobRequest
|
||||||
|
import com.nextcloud.client.account.UserAccountManager
|
||||||
|
import com.nextcloud.client.account.UserAccountManagerImpl
|
||||||
|
import com.nextcloud.client.device.PowerManagementService
|
||||||
|
import com.nextcloud.client.network.ConnectivityService
|
||||||
|
import com.owncloud.android.AbstractIT
|
||||||
|
import com.owncloud.android.datamodel.OCFile
|
||||||
|
import com.owncloud.android.datamodel.UploadsStorageManager
|
||||||
|
import com.owncloud.android.db.OCUpload
|
||||||
|
import com.owncloud.android.lib.resources.files.ReadFileRemoteOperation
|
||||||
|
import com.owncloud.android.lib.resources.files.model.RemoteFile
|
||||||
|
import com.owncloud.android.operations.UploadFileOperation
|
||||||
|
import com.owncloud.android.utils.FileStorageUtils.getSavePath
|
||||||
|
import junit.framework.Assert.assertEquals
|
||||||
|
import junit.framework.Assert.assertFalse
|
||||||
|
import junit.framework.Assert.assertTrue
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class FileUploaderIT : AbstractIT() {
|
||||||
|
val SHORT_WAIT: Long = 5000
|
||||||
|
val LONG_WAIT: Long = 20000
|
||||||
|
|
||||||
|
var uploadsStorageManager: UploadsStorageManager? = null
|
||||||
|
|
||||||
|
val connectivityServiceMock: ConnectivityService = object : ConnectivityService {
|
||||||
|
override fun isInternetWalled(): Boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isOnlineWithWifi(): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getActiveNetworkType(): JobRequest.NetworkType {
|
||||||
|
return JobRequest.NetworkType.ANY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val powerManagementServiceMock: PowerManagementService = object : PowerManagementService {
|
||||||
|
override val isPowerSavingEnabled: Boolean
|
||||||
|
get() = false
|
||||||
|
|
||||||
|
override val isPowerSavingExclusionAvailable: Boolean
|
||||||
|
get() = false
|
||||||
|
|
||||||
|
override val isBatteryCharging: Boolean
|
||||||
|
get() = false
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
val contentResolver = targetContext.contentResolver
|
||||||
|
val accountManager: UserAccountManager = UserAccountManagerImpl.fromContext(targetContext)
|
||||||
|
uploadsStorageManager = UploadsStorageManager(accountManager, contentResolver)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* uploads a file, overwrites it with an empty one, check if overwritten
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
fun testKeepLocalAndOverwriteRemote() {
|
||||||
|
val file = File(getSavePath(account.name) + "/chunkedFile.txt")
|
||||||
|
val ocUpload = OCUpload(file.absolutePath, "/testFile.txt", account.name)
|
||||||
|
|
||||||
|
assertTrue(UploadFileOperation(
|
||||||
|
uploadsStorageManager,
|
||||||
|
connectivityServiceMock,
|
||||||
|
powerManagementServiceMock,
|
||||||
|
account,
|
||||||
|
null,
|
||||||
|
ocUpload,
|
||||||
|
FileUploader.NameCollisionPolicy.DEFAULT,
|
||||||
|
FileUploader.LOCAL_BEHAVIOUR_COPY,
|
||||||
|
targetContext,
|
||||||
|
false,
|
||||||
|
false)
|
||||||
|
.setRemoteFolderToBeCreated()
|
||||||
|
.execute(client, storageManager).isSuccess)
|
||||||
|
|
||||||
|
val result = ReadFileRemoteOperation("/testFile.txt").execute(client)
|
||||||
|
assertTrue(result.isSuccess)
|
||||||
|
|
||||||
|
assertEquals(file.length(), (result.data[0] as RemoteFile).length)
|
||||||
|
|
||||||
|
val ocUpload2 = OCUpload(getSavePath(account.name) + "/empty.txt", "/testFile.txt", account.name)
|
||||||
|
|
||||||
|
assertTrue(UploadFileOperation(
|
||||||
|
uploadsStorageManager,
|
||||||
|
connectivityServiceMock,
|
||||||
|
powerManagementServiceMock,
|
||||||
|
account,
|
||||||
|
null,
|
||||||
|
ocUpload2,
|
||||||
|
FileUploader.NameCollisionPolicy.OVERWRITE,
|
||||||
|
FileUploader.LOCAL_BEHAVIOUR_COPY,
|
||||||
|
targetContext,
|
||||||
|
false,
|
||||||
|
false)
|
||||||
|
.execute(client, storageManager).isSuccess)
|
||||||
|
|
||||||
|
val result2 = ReadFileRemoteOperation("/testFile.txt").execute(client)
|
||||||
|
assertTrue(result2.isSuccess)
|
||||||
|
|
||||||
|
assertEquals(0, (result2.data[0] as RemoteFile).length)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* uploads a file, overwrites it with an empty one, check if overwritten
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
fun testKeepLocalAndOverwriteRemoteStatic() {
|
||||||
|
val file = File(getSavePath(account.name) + "/chunkedFile.txt")
|
||||||
|
|
||||||
|
FileUploader.uploadNewFile(
|
||||||
|
targetContext,
|
||||||
|
account,
|
||||||
|
file.absolutePath,
|
||||||
|
"/testFile.txt",
|
||||||
|
FileUploader.LOCAL_BEHAVIOUR_COPY,
|
||||||
|
null,
|
||||||
|
true,
|
||||||
|
UploadFileOperation.CREATED_BY_USER,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
FileUploader.NameCollisionPolicy.DEFAULT)
|
||||||
|
|
||||||
|
Thread.sleep(LONG_WAIT)
|
||||||
|
|
||||||
|
val result = ReadFileRemoteOperation("/testFile.txt").execute(client)
|
||||||
|
assertTrue(result.isSuccess)
|
||||||
|
|
||||||
|
assertEquals(file.length(), (result.data[0] as RemoteFile).length)
|
||||||
|
|
||||||
|
val ocFile2 = OCFile("/testFile.txt")
|
||||||
|
ocFile2.setStoragePath(getSavePath(account.name) + "/empty.txt")
|
||||||
|
|
||||||
|
FileUploader.uploadUpdateFile(
|
||||||
|
targetContext,
|
||||||
|
account,
|
||||||
|
ocFile2,
|
||||||
|
FileUploader.LOCAL_BEHAVIOUR_COPY,
|
||||||
|
FileUploader.NameCollisionPolicy.OVERWRITE)
|
||||||
|
|
||||||
|
Thread.sleep(SHORT_WAIT)
|
||||||
|
|
||||||
|
val result2 = ReadFileRemoteOperation("/testFile.txt").execute(client)
|
||||||
|
assertTrue(result2.isSuccess)
|
||||||
|
|
||||||
|
assertEquals(0, (result2.data[0] as RemoteFile).length)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* uploads a file, uploads another one with automatically (2) added, check
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
fun testKeepBoth() {
|
||||||
|
var renameListenerWasTriggered = false
|
||||||
|
|
||||||
|
val file = File(getSavePath(account.name) + "/chunkedFile.txt")
|
||||||
|
val ocUpload = OCUpload(file.absolutePath, "/testFile.txt", account.name)
|
||||||
|
|
||||||
|
assertTrue(UploadFileOperation(
|
||||||
|
uploadsStorageManager,
|
||||||
|
connectivityServiceMock,
|
||||||
|
powerManagementServiceMock,
|
||||||
|
account,
|
||||||
|
null,
|
||||||
|
ocUpload,
|
||||||
|
FileUploader.NameCollisionPolicy.DEFAULT,
|
||||||
|
FileUploader.LOCAL_BEHAVIOUR_COPY,
|
||||||
|
targetContext,
|
||||||
|
false,
|
||||||
|
false)
|
||||||
|
.setRemoteFolderToBeCreated()
|
||||||
|
.execute(client, storageManager).isSuccess)
|
||||||
|
|
||||||
|
val result = ReadFileRemoteOperation("/testFile.txt").execute(client)
|
||||||
|
assertTrue(result.isSuccess)
|
||||||
|
|
||||||
|
assertEquals(file.length(), (result.data[0] as RemoteFile).length)
|
||||||
|
|
||||||
|
val file2 = File(getSavePath(account.name) + "/empty.txt")
|
||||||
|
val ocUpload2 = OCUpload(file2.absolutePath, "/testFile.txt", account.name)
|
||||||
|
|
||||||
|
assertTrue(UploadFileOperation(
|
||||||
|
uploadsStorageManager,
|
||||||
|
connectivityServiceMock,
|
||||||
|
powerManagementServiceMock,
|
||||||
|
account,
|
||||||
|
null,
|
||||||
|
ocUpload2,
|
||||||
|
FileUploader.NameCollisionPolicy.RENAME,
|
||||||
|
FileUploader.LOCAL_BEHAVIOUR_COPY,
|
||||||
|
targetContext,
|
||||||
|
false,
|
||||||
|
false)
|
||||||
|
.addRenameUploadListener {
|
||||||
|
renameListenerWasTriggered = true
|
||||||
|
}
|
||||||
|
.execute(client, storageManager).isSuccess)
|
||||||
|
|
||||||
|
val result2 = ReadFileRemoteOperation("/testFile.txt").execute(client)
|
||||||
|
assertTrue(result2.isSuccess)
|
||||||
|
|
||||||
|
assertEquals(file.length(), (result2.data[0] as RemoteFile).length)
|
||||||
|
|
||||||
|
val result3 = ReadFileRemoteOperation("/testFile (2).txt").execute(client)
|
||||||
|
assertTrue(result3.isSuccess)
|
||||||
|
|
||||||
|
assertEquals(file2.length(), (result3.data[0] as RemoteFile).length)
|
||||||
|
assertTrue(renameListenerWasTriggered)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* uploads a file, uploads another one with automatically (2) added, check
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
fun testKeepBothStatic() {
|
||||||
|
val file = File(getSavePath(account.name) + "/chunkedFile.txt")
|
||||||
|
|
||||||
|
FileUploader.uploadNewFile(
|
||||||
|
targetContext,
|
||||||
|
account,
|
||||||
|
file.absolutePath,
|
||||||
|
"/testFile.txt",
|
||||||
|
FileUploader.LOCAL_BEHAVIOUR_COPY,
|
||||||
|
null,
|
||||||
|
true,
|
||||||
|
UploadFileOperation.CREATED_BY_USER,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
FileUploader.NameCollisionPolicy.DEFAULT)
|
||||||
|
|
||||||
|
Thread.sleep(LONG_WAIT)
|
||||||
|
|
||||||
|
val result = ReadFileRemoteOperation("/testFile.txt").execute(client)
|
||||||
|
assertTrue(result.isSuccess)
|
||||||
|
|
||||||
|
assertEquals(file.length(), (result.data[0] as RemoteFile).length)
|
||||||
|
|
||||||
|
val ocFile2 = OCFile("/testFile.txt")
|
||||||
|
ocFile2.setStoragePath(getSavePath(account.name) + "/empty.txt")
|
||||||
|
|
||||||
|
FileUploader.uploadUpdateFile(
|
||||||
|
targetContext,
|
||||||
|
account,
|
||||||
|
ocFile2,
|
||||||
|
FileUploader.LOCAL_BEHAVIOUR_COPY,
|
||||||
|
FileUploader.NameCollisionPolicy.RENAME)
|
||||||
|
|
||||||
|
Thread.sleep(SHORT_WAIT)
|
||||||
|
|
||||||
|
val result2 = ReadFileRemoteOperation("/testFile.txt").execute(client)
|
||||||
|
assertTrue(result2.isSuccess)
|
||||||
|
|
||||||
|
assertEquals(file.length(), (result2.data[0] as RemoteFile).length)
|
||||||
|
|
||||||
|
val result3 = ReadFileRemoteOperation("/testFile (2).txt").execute(client)
|
||||||
|
assertTrue(result3.isSuccess)
|
||||||
|
|
||||||
|
assertEquals(ocFile2.fileLength, (result3.data[0] as RemoteFile).length)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* uploads a file with "keep server" option set, so do nothing
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
fun testKeepServer() {
|
||||||
|
val file = File(getSavePath(account.name) + "/chunkedFile.txt")
|
||||||
|
val ocUpload = OCUpload(file.absolutePath, "/testFile.txt", account.name)
|
||||||
|
|
||||||
|
assertTrue(UploadFileOperation(
|
||||||
|
uploadsStorageManager,
|
||||||
|
connectivityServiceMock,
|
||||||
|
powerManagementServiceMock,
|
||||||
|
account,
|
||||||
|
null,
|
||||||
|
ocUpload,
|
||||||
|
FileUploader.NameCollisionPolicy.DEFAULT,
|
||||||
|
FileUploader.LOCAL_BEHAVIOUR_COPY,
|
||||||
|
targetContext,
|
||||||
|
false,
|
||||||
|
false)
|
||||||
|
.setRemoteFolderToBeCreated()
|
||||||
|
.execute(client, storageManager).isSuccess)
|
||||||
|
|
||||||
|
val result = ReadFileRemoteOperation("/testFile.txt").execute(client)
|
||||||
|
assertTrue(result.isSuccess)
|
||||||
|
|
||||||
|
assertEquals(file.length(), (result.data[0] as RemoteFile).length)
|
||||||
|
|
||||||
|
val ocUpload2 = OCUpload(getSavePath(account.name) + "/empty.txt", "/testFile.txt", account.name)
|
||||||
|
|
||||||
|
assertFalse(UploadFileOperation(
|
||||||
|
uploadsStorageManager,
|
||||||
|
connectivityServiceMock,
|
||||||
|
powerManagementServiceMock,
|
||||||
|
account,
|
||||||
|
null,
|
||||||
|
ocUpload2,
|
||||||
|
FileUploader.NameCollisionPolicy.CANCEL,
|
||||||
|
FileUploader.LOCAL_BEHAVIOUR_COPY,
|
||||||
|
targetContext,
|
||||||
|
false,
|
||||||
|
false)
|
||||||
|
.execute(client, storageManager).isSuccess)
|
||||||
|
|
||||||
|
val result2 = ReadFileRemoteOperation("/testFile.txt").execute(client)
|
||||||
|
assertTrue(result2.isSuccess)
|
||||||
|
|
||||||
|
assertEquals(file.length(), (result2.data[0] as RemoteFile).length)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* uploads a file with "keep server" option set, so do nothing
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
fun testKeepServerStatic() {
|
||||||
|
val file = File(getSavePath(account.name) + "/chunkedFile.txt")
|
||||||
|
|
||||||
|
FileUploader.uploadNewFile(
|
||||||
|
targetContext,
|
||||||
|
account,
|
||||||
|
file.absolutePath,
|
||||||
|
"/testFile.txt",
|
||||||
|
FileUploader.LOCAL_BEHAVIOUR_COPY,
|
||||||
|
null,
|
||||||
|
true,
|
||||||
|
UploadFileOperation.CREATED_BY_USER,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
FileUploader.NameCollisionPolicy.DEFAULT)
|
||||||
|
|
||||||
|
Thread.sleep(LONG_WAIT)
|
||||||
|
|
||||||
|
val result = ReadFileRemoteOperation("/testFile.txt").execute(client)
|
||||||
|
assertTrue(result.isSuccess)
|
||||||
|
|
||||||
|
assertEquals(file.length(), (result.data[0] as RemoteFile).length)
|
||||||
|
|
||||||
|
val ocFile2 = OCFile("/testFile.txt")
|
||||||
|
ocFile2.setStoragePath(getSavePath(account.name) + "/empty.txt")
|
||||||
|
|
||||||
|
FileUploader.uploadUpdateFile(
|
||||||
|
targetContext,
|
||||||
|
account,
|
||||||
|
ocFile2,
|
||||||
|
FileUploader.LOCAL_BEHAVIOUR_COPY,
|
||||||
|
FileUploader.NameCollisionPolicy.CANCEL)
|
||||||
|
|
||||||
|
Thread.sleep(SHORT_WAIT)
|
||||||
|
|
||||||
|
val result2 = ReadFileRemoteOperation("/testFile.txt").execute(client)
|
||||||
|
assertTrue(result2.isSuccess)
|
||||||
|
|
||||||
|
assertEquals(file.length(), (result2.data[0] as RemoteFile).length)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,290 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Nextcloud Android client application
|
||||||
|
*
|
||||||
|
* @author Tobias Kaminsky
|
||||||
|
* Copyright (C) 2020 Tobias Kaminsky
|
||||||
|
* Copyright (C) 2020 Nextcloud GmbH
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.owncloud.android.ui.activity;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
|
||||||
|
import com.facebook.testing.screenshot.Screenshot;
|
||||||
|
import com.nextcloud.client.account.UserAccountManagerImpl;
|
||||||
|
import com.owncloud.android.AbstractIT;
|
||||||
|
import com.owncloud.android.R;
|
||||||
|
import com.owncloud.android.datamodel.FileDataStorageManager;
|
||||||
|
import com.owncloud.android.datamodel.OCFile;
|
||||||
|
import com.owncloud.android.db.OCUpload;
|
||||||
|
import com.owncloud.android.lib.resources.files.UploadFileRemoteOperation;
|
||||||
|
import com.owncloud.android.operations.RefreshFolderOperation;
|
||||||
|
import com.owncloud.android.ui.dialog.ConflictsResolveDialog;
|
||||||
|
import com.owncloud.android.utils.FileStorageUtils;
|
||||||
|
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import androidx.fragment.app.DialogFragment;
|
||||||
|
import androidx.test.espresso.intent.rule.IntentsTestRule;
|
||||||
|
|
||||||
|
import static androidx.test.espresso.Espresso.onView;
|
||||||
|
import static androidx.test.espresso.action.ViewActions.click;
|
||||||
|
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||||
|
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
||||||
|
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
|
||||||
|
import static junit.framework.TestCase.assertTrue;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
public class ConflictsResolveActivityIT extends AbstractIT {
|
||||||
|
@Rule public IntentsTestRule<ConflictsResolveActivity> activityRule =
|
||||||
|
new IntentsTestRule<>(ConflictsResolveActivity.class, true, false);
|
||||||
|
private boolean returnCode;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void screenshotTextFiles() throws InterruptedException {
|
||||||
|
OCUpload newUpload = new OCUpload(FileStorageUtils.getSavePath(account.name) + "/nonEmpty.txt",
|
||||||
|
"/newFile.txt",
|
||||||
|
account.name);
|
||||||
|
|
||||||
|
OCFile existingFile = new OCFile("/newFile.txt");
|
||||||
|
existingFile.setFileLength(1024000);
|
||||||
|
existingFile.setModificationTimestamp(1582019340);
|
||||||
|
|
||||||
|
FileDataStorageManager storageManager = new FileDataStorageManager(account, targetContext.getContentResolver());
|
||||||
|
storageManager.saveNewFile(existingFile);
|
||||||
|
|
||||||
|
Intent intent = new Intent(targetContext, ConflictsResolveActivity.class);
|
||||||
|
intent.putExtra(ConflictsResolveActivity.EXTRA_FILE, existingFile);
|
||||||
|
intent.putExtra(ConflictsResolveActivity.EXTRA_CONFLICT_UPLOAD, newUpload);
|
||||||
|
|
||||||
|
ConflictsResolveActivity sut = activityRule.launchActivity(intent);
|
||||||
|
|
||||||
|
ConflictsResolveDialog dialog = ConflictsResolveDialog.newInstance(existingFile,
|
||||||
|
newUpload,
|
||||||
|
UserAccountManagerImpl
|
||||||
|
.fromContext(targetContext)
|
||||||
|
.getUser()
|
||||||
|
);
|
||||||
|
dialog.showDialog(sut);
|
||||||
|
|
||||||
|
getInstrumentation().waitForIdleSync();
|
||||||
|
|
||||||
|
Thread.sleep(2000);
|
||||||
|
|
||||||
|
Screenshot.snap(dialog.getDialog().getWindow().getDecorView()).record();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void screenshotImages() throws InterruptedException, IOException {
|
||||||
|
FileDataStorageManager storageManager = new FileDataStorageManager(account,
|
||||||
|
targetContext.getContentResolver());
|
||||||
|
|
||||||
|
OCUpload newUpload = new OCUpload(FileStorageUtils.getSavePath(account.name) + "/nonEmpty.txt",
|
||||||
|
"/newFile.txt", account.name);
|
||||||
|
|
||||||
|
File image = getFile("image.jpg");
|
||||||
|
|
||||||
|
assertTrue(new UploadFileRemoteOperation(image.getAbsolutePath(),
|
||||||
|
"/image.jpg",
|
||||||
|
"image/jpg",
|
||||||
|
"10000000").execute(client).isSuccess());
|
||||||
|
|
||||||
|
assertTrue(new RefreshFolderOperation(storageManager.getFileByPath("/"),
|
||||||
|
System.currentTimeMillis(),
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
storageManager,
|
||||||
|
account,
|
||||||
|
targetContext
|
||||||
|
).execute(client).isSuccess());
|
||||||
|
|
||||||
|
OCFile existingFile = storageManager.getFileByPath("/image.jpg");
|
||||||
|
|
||||||
|
Intent intent = new Intent(targetContext, ConflictsResolveActivity.class);
|
||||||
|
intent.putExtra(ConflictsResolveActivity.EXTRA_FILE, existingFile);
|
||||||
|
intent.putExtra(ConflictsResolveActivity.EXTRA_CONFLICT_UPLOAD, newUpload);
|
||||||
|
|
||||||
|
ConflictsResolveActivity sut = activityRule.launchActivity(intent);
|
||||||
|
|
||||||
|
ConflictsResolveDialog.OnConflictDecisionMadeListener listener = decision -> {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
ConflictsResolveDialog dialog = ConflictsResolveDialog.newInstance(existingFile,
|
||||||
|
newUpload,
|
||||||
|
UserAccountManagerImpl
|
||||||
|
.fromContext(targetContext)
|
||||||
|
.getUser()
|
||||||
|
);
|
||||||
|
dialog.showDialog(sut);
|
||||||
|
dialog.listener = listener;
|
||||||
|
|
||||||
|
getInstrumentation().waitForIdleSync();
|
||||||
|
Thread.sleep(2000);
|
||||||
|
|
||||||
|
Screenshot.snap(dialog.getDialog().getWindow().getDecorView()).record();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void cancel() throws InterruptedException {
|
||||||
|
returnCode = false;
|
||||||
|
|
||||||
|
OCUpload newUpload = new OCUpload(FileStorageUtils.getSavePath(account.name) + "/nonEmpty.txt",
|
||||||
|
"/newFile.txt",
|
||||||
|
account.name);
|
||||||
|
OCFile existingFile = new OCFile("/newFile.txt");
|
||||||
|
existingFile.setFileLength(1024000);
|
||||||
|
existingFile.setModificationTimestamp(1582019340);
|
||||||
|
|
||||||
|
FileDataStorageManager storageManager = new FileDataStorageManager(account, targetContext.getContentResolver());
|
||||||
|
storageManager.saveNewFile(existingFile);
|
||||||
|
|
||||||
|
Intent intent = new Intent(targetContext, ConflictsResolveActivity.class);
|
||||||
|
intent.putExtra(ConflictsResolveActivity.EXTRA_FILE, existingFile);
|
||||||
|
intent.putExtra(ConflictsResolveActivity.EXTRA_CONFLICT_UPLOAD, newUpload);
|
||||||
|
|
||||||
|
ConflictsResolveActivity sut = activityRule.launchActivity(intent);
|
||||||
|
|
||||||
|
sut.listener = decision -> {
|
||||||
|
assertEquals(decision, ConflictsResolveDialog.Decision.CANCEL);
|
||||||
|
returnCode = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
getInstrumentation().waitForIdleSync();
|
||||||
|
Thread.sleep(2000);
|
||||||
|
|
||||||
|
onView(withText("Cancel")).perform(click());
|
||||||
|
|
||||||
|
assertTrue(returnCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void keepExisting() {
|
||||||
|
returnCode = false;
|
||||||
|
|
||||||
|
OCUpload newUpload = new OCUpload(FileStorageUtils.getSavePath(account.name) + "/nonEmpty.txt",
|
||||||
|
"/newFile.txt",
|
||||||
|
account.name);
|
||||||
|
OCFile existingFile = new OCFile("/newFile.txt");
|
||||||
|
existingFile.setFileLength(1024000);
|
||||||
|
existingFile.setModificationTimestamp(1582019340);
|
||||||
|
|
||||||
|
FileDataStorageManager storageManager = new FileDataStorageManager(account, targetContext.getContentResolver());
|
||||||
|
storageManager.saveNewFile(existingFile);
|
||||||
|
|
||||||
|
Intent intent = new Intent(targetContext, ConflictsResolveActivity.class);
|
||||||
|
intent.putExtra(ConflictsResolveActivity.EXTRA_FILE, existingFile);
|
||||||
|
intent.putExtra(ConflictsResolveActivity.EXTRA_CONFLICT_UPLOAD, newUpload);
|
||||||
|
|
||||||
|
ConflictsResolveActivity sut = activityRule.launchActivity(intent);
|
||||||
|
|
||||||
|
sut.listener = decision -> {
|
||||||
|
assertEquals(decision, ConflictsResolveDialog.Decision.KEEP_SERVER);
|
||||||
|
returnCode = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
getInstrumentation().waitForIdleSync();
|
||||||
|
|
||||||
|
onView(withId(R.id.existing_checkbox)).perform(click());
|
||||||
|
|
||||||
|
DialogFragment dialog = (DialogFragment) sut.getSupportFragmentManager().findFragmentByTag("conflictDialog");
|
||||||
|
Screenshot.snap(dialog.getDialog().getWindow().getDecorView()).record();
|
||||||
|
|
||||||
|
onView(withText("OK")).perform(click());
|
||||||
|
|
||||||
|
assertTrue(returnCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void keepNew() {
|
||||||
|
returnCode = false;
|
||||||
|
|
||||||
|
OCUpload newUpload = new OCUpload(FileStorageUtils.getSavePath(account.name) + "/nonEmpty.txt",
|
||||||
|
"/newFile.txt",
|
||||||
|
account.name);
|
||||||
|
OCFile existingFile = new OCFile("/newFile.txt");
|
||||||
|
existingFile.setFileLength(1024000);
|
||||||
|
existingFile.setModificationTimestamp(1582019340);
|
||||||
|
|
||||||
|
FileDataStorageManager storageManager = new FileDataStorageManager(account, targetContext.getContentResolver());
|
||||||
|
storageManager.saveNewFile(existingFile);
|
||||||
|
|
||||||
|
Intent intent = new Intent(targetContext, ConflictsResolveActivity.class);
|
||||||
|
intent.putExtra(ConflictsResolveActivity.EXTRA_FILE, existingFile);
|
||||||
|
intent.putExtra(ConflictsResolveActivity.EXTRA_CONFLICT_UPLOAD, newUpload);
|
||||||
|
|
||||||
|
ConflictsResolveActivity sut = activityRule.launchActivity(intent);
|
||||||
|
|
||||||
|
sut.listener = decision -> {
|
||||||
|
assertEquals(decision, ConflictsResolveDialog.Decision.KEEP_SERVER);
|
||||||
|
returnCode = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
getInstrumentation().waitForIdleSync();
|
||||||
|
|
||||||
|
onView(withId(R.id.new_checkbox)).perform(click());
|
||||||
|
|
||||||
|
DialogFragment dialog = (DialogFragment) sut.getSupportFragmentManager().findFragmentByTag("conflictDialog");
|
||||||
|
Screenshot.snap(dialog.getDialog().getWindow().getDecorView()).record();
|
||||||
|
|
||||||
|
onView(withText("OK")).perform(click());
|
||||||
|
|
||||||
|
assertTrue(returnCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void keepBoth() {
|
||||||
|
returnCode = false;
|
||||||
|
|
||||||
|
OCUpload newUpload = new OCUpload(FileStorageUtils.getSavePath(account.name) + "/nonEmpty.txt",
|
||||||
|
"/newFile.txt",
|
||||||
|
account.name);
|
||||||
|
OCFile existingFile = new OCFile("/newFile.txt");
|
||||||
|
existingFile.setFileLength(1024000);
|
||||||
|
existingFile.setModificationTimestamp(1582019340);
|
||||||
|
|
||||||
|
FileDataStorageManager storageManager = new FileDataStorageManager(account, targetContext.getContentResolver());
|
||||||
|
storageManager.saveNewFile(existingFile);
|
||||||
|
|
||||||
|
Intent intent = new Intent(targetContext, ConflictsResolveActivity.class);
|
||||||
|
intent.putExtra(ConflictsResolveActivity.EXTRA_FILE, existingFile);
|
||||||
|
intent.putExtra(ConflictsResolveActivity.EXTRA_CONFLICT_UPLOAD, newUpload);
|
||||||
|
|
||||||
|
ConflictsResolveActivity sut = activityRule.launchActivity(intent);
|
||||||
|
|
||||||
|
sut.listener = decision -> {
|
||||||
|
assertEquals(decision, ConflictsResolveDialog.Decision.KEEP_SERVER);
|
||||||
|
returnCode = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
getInstrumentation().waitForIdleSync();
|
||||||
|
|
||||||
|
onView(withId(R.id.existing_checkbox)).perform(click());
|
||||||
|
onView(withId(R.id.new_checkbox)).perform(click());
|
||||||
|
|
||||||
|
DialogFragment dialog = (DialogFragment) sut.getSupportFragmentManager().findFragmentByTag("conflictDialog");
|
||||||
|
Screenshot.snap(dialog.getDialog().getWindow().getDecorView()).record();
|
||||||
|
|
||||||
|
onView(withText("OK")).perform(click());
|
||||||
|
|
||||||
|
assertTrue(returnCode);
|
||||||
|
}
|
||||||
|
}
|
|
@ -160,6 +160,7 @@ public class OCFile implements Parcelable, Comparable<OCFile>, ServerFileInterfa
|
||||||
ownerDisplayName = source.readString();
|
ownerDisplayName = source.readString();
|
||||||
mountType = (WebdavEntry.MountType) source.readSerializable();
|
mountType = (WebdavEntry.MountType) source.readSerializable();
|
||||||
richWorkspace = source.readString();
|
richWorkspace = source.readString();
|
||||||
|
previewAvailable = source.readInt() == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -193,6 +194,7 @@ public class OCFile implements Parcelable, Comparable<OCFile>, ServerFileInterfa
|
||||||
dest.writeString(ownerDisplayName);
|
dest.writeString(ownerDisplayName);
|
||||||
dest.writeSerializable(mountType);
|
dest.writeSerializable(mountType);
|
||||||
dest.writeString(richWorkspace);
|
dest.writeString(richWorkspace);
|
||||||
|
dest.writeInt(previewAvailable ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getDecryptedRemotePath() {
|
public String getDecryptedRemotePath() {
|
||||||
|
|
|
@ -285,8 +285,10 @@ public class UploadFileOperation extends SyncOperation {
|
||||||
return mLocalBehaviour;
|
return mLocalBehaviour;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRemoteFolderToBeCreated() {
|
public UploadFileOperation setRemoteFolderToBeCreated() {
|
||||||
mRemoteFolderToBeCreated = true;
|
mRemoteFolderToBeCreated = true;
|
||||||
|
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean wasRenamed() {
|
public boolean wasRenamed() {
|
||||||
|
@ -348,8 +350,10 @@ public class UploadFileOperation extends SyncOperation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addRenameUploadListener(OnRenameListener listener) {
|
public UploadFileOperation addRenameUploadListener(OnRenameListener listener) {
|
||||||
mRenameUploadListener = listener;
|
mRenameUploadListener = listener;
|
||||||
|
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Context getContext() {
|
public Context getContext() {
|
||||||
|
@ -946,7 +950,9 @@ public class UploadFileOperation extends SyncOperation {
|
||||||
mWasRenamed = true;
|
mWasRenamed = true;
|
||||||
createNewOCFile(mRemotePath);
|
createNewOCFile(mRemotePath);
|
||||||
Log_OC.d(TAG, "File renamed as " + mRemotePath);
|
Log_OC.d(TAG, "File renamed as " + mRemotePath);
|
||||||
|
if (mRenameUploadListener != null) {
|
||||||
mRenameUploadListener.onRenameUpload();
|
mRenameUploadListener.onRenameUpload();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case OVERWRITE:
|
case OVERWRITE:
|
||||||
Log_OC.d(TAG, "Overwriting file");
|
Log_OC.d(TAG, "Overwriting file");
|
||||||
|
|
|
@ -23,7 +23,10 @@ package com.owncloud.android.ui.activity;
|
||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.nextcloud.client.account.User;
|
||||||
|
import com.nextcloud.java.util.Optional;
|
||||||
import com.owncloud.android.datamodel.OCFile;
|
import com.owncloud.android.datamodel.OCFile;
|
||||||
import com.owncloud.android.datamodel.UploadsStorageManager;
|
import com.owncloud.android.datamodel.UploadsStorageManager;
|
||||||
import com.owncloud.android.db.OCUpload;
|
import com.owncloud.android.db.OCUpload;
|
||||||
|
@ -36,6 +39,9 @@ import com.owncloud.android.ui.dialog.ConflictsResolveDialog.OnConflictDecisionM
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.fragment.app.FragmentTransaction;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper activity which will be launched if keep-in-sync file will be modified by external
|
* Wrapper activity which will be launched if keep-in-sync file will be modified by external
|
||||||
|
@ -57,6 +63,7 @@ public class ConflictsResolveActivity extends FileActivity implements OnConflict
|
||||||
|
|
||||||
private OCUpload conflictUpload;
|
private OCUpload conflictUpload;
|
||||||
private int localBehaviour = FileUploader.LOCAL_BEHAVIOUR_FORGET;
|
private int localBehaviour = FileUploader.LOCAL_BEHAVIOUR_FORGET;
|
||||||
|
protected OnConflictDecisionMadeListener listener;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
@ -73,20 +80,19 @@ public class ConflictsResolveActivity extends FileActivity implements OnConflict
|
||||||
if (conflictUpload != null) {
|
if (conflictUpload != null) {
|
||||||
localBehaviour = conflictUpload.getLocalAction();
|
localBehaviour = conflictUpload.getLocalAction();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
listener = new OnConflictDecisionMadeListener() {
|
||||||
@Override
|
@Override
|
||||||
public void conflictDecisionMade(Decision decision) {
|
public void conflictDecisionMade(Decision decision) {
|
||||||
if (decision == Decision.CANCEL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
OCFile file = getFile();
|
OCFile file = getFile();
|
||||||
|
|
||||||
switch (decision) {
|
switch (decision) {
|
||||||
|
case CANCEL:
|
||||||
|
// nothing to do
|
||||||
|
break;
|
||||||
case KEEP_LOCAL: // Upload
|
case KEEP_LOCAL: // Upload
|
||||||
FileUploader.uploadUpdateFile(
|
FileUploader.uploadUpdateFile(
|
||||||
this,
|
getBaseContext(),
|
||||||
getAccount(),
|
getAccount(),
|
||||||
file,
|
file,
|
||||||
localBehaviour,
|
localBehaviour,
|
||||||
|
@ -99,7 +105,7 @@ public class ConflictsResolveActivity extends FileActivity implements OnConflict
|
||||||
break;
|
break;
|
||||||
case KEEP_BOTH: // Upload
|
case KEEP_BOTH: // Upload
|
||||||
FileUploader.uploadUpdateFile(
|
FileUploader.uploadUpdateFile(
|
||||||
this,
|
getBaseContext(),
|
||||||
getAccount(),
|
getAccount(),
|
||||||
file,
|
file,
|
||||||
localBehaviour,
|
localBehaviour,
|
||||||
|
@ -111,9 +117,9 @@ public class ConflictsResolveActivity extends FileActivity implements OnConflict
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case KEEP_SERVER: // Download
|
case KEEP_SERVER: // Download
|
||||||
if (!this.shouldDeleteLocal()) {
|
if (!shouldDeleteLocal()) {
|
||||||
// Overwrite local file
|
// Overwrite local file
|
||||||
Intent intent = new Intent(this, FileDownloader.class);
|
Intent intent = new Intent(getBaseContext(), FileDownloader.class);
|
||||||
intent.putExtra(FileDownloader.EXTRA_ACCOUNT, getAccount());
|
intent.putExtra(FileDownloader.EXTRA_ACCOUNT, getAccount());
|
||||||
intent.putExtra(FileDownloader.EXTRA_FILE, file);
|
intent.putExtra(FileDownloader.EXTRA_FILE, file);
|
||||||
if (conflictUpload != null) {
|
if (conflictUpload != null) {
|
||||||
|
@ -126,26 +132,57 @@ public class ConflictsResolveActivity extends FileActivity implements OnConflict
|
||||||
|
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onSaveInstanceState(Bundle outState) {
|
||||||
|
super.onSaveInstanceState(outState);
|
||||||
|
|
||||||
|
outState.putParcelable(EXTRA_CONFLICT_UPLOAD, conflictUpload);
|
||||||
|
outState.putInt(EXTRA_LOCAL_BEHAVIOUR, localBehaviour);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void conflictDecisionMade(Decision decision) {
|
||||||
|
listener.conflictDecisionMade(decision);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onStart() {
|
protected void onStart() {
|
||||||
super.onStart();
|
super.onStart();
|
||||||
if (getAccount() != null) {
|
if (getAccount() == null) {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
OCFile file = getFile();
|
OCFile file = getFile();
|
||||||
if (getFile() == null) {
|
if (getFile() == null) {
|
||||||
Log_OC.e(TAG, "No file received");
|
Log_OC.e(TAG, "No file received");
|
||||||
finish();
|
finish();
|
||||||
} else {
|
}
|
||||||
// Check whether the file is contained in the current Account
|
|
||||||
if (getStorageManager().fileExists(file.getRemotePath())) {
|
Optional<User> userOptional = getUser();
|
||||||
ConflictsResolveDialog dialog = new ConflictsResolveDialog(this, !this.shouldDeleteLocal());
|
|
||||||
dialog.showDialog(this);
|
if (!userOptional.isPresent()) {
|
||||||
} else {
|
Toast.makeText(this, "Error creating conflict dialog!", Toast.LENGTH_LONG).show();
|
||||||
// Account was changed to a different one - just finish
|
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check whether the file is contained in the current Account
|
||||||
|
Fragment prev = getSupportFragmentManager().findFragmentByTag("conflictDialog");
|
||||||
|
|
||||||
|
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
|
||||||
|
if (prev != null) {
|
||||||
|
fragmentTransaction.remove(prev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (getStorageManager().fileExists(file.getRemotePath())) {
|
||||||
|
ConflictsResolveDialog dialog = ConflictsResolveDialog.newInstance(getFile(),
|
||||||
|
conflictUpload,
|
||||||
|
userOptional.get());
|
||||||
|
dialog.show(fragmentTransaction, "conflictDialog");
|
||||||
} else {
|
} else {
|
||||||
|
// Account was changed to a different one - just finish
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -160,7 +160,7 @@ public class LocalFileListAdapter extends RecyclerView.Adapter<RecyclerView.View
|
||||||
}
|
}
|
||||||
|
|
||||||
gridViewHolder.thumbnail.setTag(file.hashCode());
|
gridViewHolder.thumbnail.setTag(file.hashCode());
|
||||||
setThumbnail(file, gridViewHolder.thumbnail);
|
setThumbnail(file, gridViewHolder.thumbnail, mContext);
|
||||||
|
|
||||||
if (file.isDirectory()) {
|
if (file.isDirectory()) {
|
||||||
gridViewHolder.checkbox.setVisibility(View.GONE);
|
gridViewHolder.checkbox.setVisibility(View.GONE);
|
||||||
|
@ -203,16 +203,17 @@ public class LocalFileListAdapter extends RecyclerView.Adapter<RecyclerView.View
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setThumbnail(File file, ImageView thumbnailView) {
|
public static void setThumbnail(File file, ImageView thumbnailView, Context context) {
|
||||||
if (file.isDirectory()) {
|
if (file.isDirectory()) {
|
||||||
thumbnailView.setImageDrawable(MimeTypeUtil.getDefaultFolderIcon(mContext));
|
thumbnailView.setImageDrawable(MimeTypeUtil.getDefaultFolderIcon(context));
|
||||||
} else {
|
} else {
|
||||||
thumbnailView.setImageResource(R.drawable.file);
|
thumbnailView.setImageResource(R.drawable.file);
|
||||||
|
|
||||||
/* Cancellation needs do be checked and done before changing the drawable in fileIcon, or
|
/* Cancellation needs do be checked and done before changing the drawable in fileIcon, or
|
||||||
* {@link ThumbnailsCacheManager#cancelPotentialThumbnailWork} will NEVER cancel any task.
|
* {@link ThumbnailsCacheManager#cancelPotentialThumbnailWork} will NEVER cancel any task.
|
||||||
*/
|
*/
|
||||||
boolean allowedToCreateNewThumbnail = ThumbnailsCacheManager.cancelPotentialThumbnailWork(file, thumbnailView);
|
boolean allowedToCreateNewThumbnail = ThumbnailsCacheManager.cancelPotentialThumbnailWork(file,
|
||||||
|
thumbnailView);
|
||||||
|
|
||||||
|
|
||||||
// get Thumbnail if file is image
|
// get Thumbnail if file is image
|
||||||
|
@ -236,7 +237,7 @@ public class LocalFileListAdapter extends RecyclerView.Adapter<RecyclerView.View
|
||||||
}
|
}
|
||||||
final ThumbnailsCacheManager.AsyncThumbnailDrawable asyncDrawable =
|
final ThumbnailsCacheManager.AsyncThumbnailDrawable asyncDrawable =
|
||||||
new ThumbnailsCacheManager.AsyncThumbnailDrawable(
|
new ThumbnailsCacheManager.AsyncThumbnailDrawable(
|
||||||
mContext.getResources(),
|
context.getResources(),
|
||||||
thumbnail,
|
thumbnail,
|
||||||
task
|
task
|
||||||
);
|
);
|
||||||
|
@ -247,7 +248,7 @@ public class LocalFileListAdapter extends RecyclerView.Adapter<RecyclerView.View
|
||||||
} // else, already being generated, don't restart it
|
} // else, already being generated, don't restart it
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
thumbnailView.setImageDrawable(MimeTypeUtil.getFileTypeIcon(null, file.getName(), mContext));
|
thumbnailView.setImageDrawable(MimeTypeUtil.getFileTypeIcon(null, file.getName(), context));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ package com.owncloud.android.ui.adapter;
|
||||||
import android.accounts.AccountManager;
|
import android.accounts.AccountManager;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
|
@ -353,7 +354,13 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
|
||||||
boolean gridImage = MimeTypeUtil.isImage(file) || MimeTypeUtil.isVideo(file);
|
boolean gridImage = MimeTypeUtil.isImage(file) || MimeTypeUtil.isVideo(file);
|
||||||
|
|
||||||
gridViewHolder.thumbnail.setTag(file.getFileId());
|
gridViewHolder.thumbnail.setTag(file.getFileId());
|
||||||
setThumbnail(file, gridViewHolder.thumbnail);
|
setThumbnail(file,
|
||||||
|
gridViewHolder.thumbnail,
|
||||||
|
user,
|
||||||
|
mStorageManager,
|
||||||
|
asyncTasks,
|
||||||
|
gridView,
|
||||||
|
activity);
|
||||||
|
|
||||||
if (highlightedItem != null && file.getFileId() == highlightedItem.getFileId()) {
|
if (highlightedItem != null && file.getFileId() == highlightedItem.getFileId()) {
|
||||||
gridViewHolder.itemLayout.setBackgroundColor(activity.getResources()
|
gridViewHolder.itemLayout.setBackgroundColor(activity.getResources()
|
||||||
|
@ -585,12 +592,18 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setThumbnail(OCFile file, ImageView thumbnailView) {
|
public static void setThumbnail(OCFile file,
|
||||||
|
ImageView thumbnailView,
|
||||||
|
User user,
|
||||||
|
FileDataStorageManager storageManager,
|
||||||
|
List<ThumbnailsCacheManager.ThumbnailGenerationTask> asyncTasks,
|
||||||
|
boolean gridView,
|
||||||
|
Context context) {
|
||||||
if (file.isFolder()) {
|
if (file.isFolder()) {
|
||||||
thumbnailView.setImageDrawable(MimeTypeUtil
|
thumbnailView.setImageDrawable(MimeTypeUtil
|
||||||
.getFolderTypeIcon(file.isSharedWithMe() || file.isSharedWithSharee(),
|
.getFolderTypeIcon(file.isSharedWithMe() || file.isSharedWithSharee(),
|
||||||
file.isSharedViaLink(), file.isEncrypted(),
|
file.isSharedViaLink(), file.isEncrypted(),
|
||||||
file.getMountType(), activity));
|
file.getMountType(), context));
|
||||||
} else {
|
} else {
|
||||||
if (file.getRemoteId() != null && file.isPreviewAvailable()) {
|
if (file.getRemoteId() != null && file.isPreviewAvailable()) {
|
||||||
// Thumbnail in cache?
|
// Thumbnail in cache?
|
||||||
|
@ -615,7 +628,7 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
|
||||||
try {
|
try {
|
||||||
final ThumbnailsCacheManager.ThumbnailGenerationTask task =
|
final ThumbnailsCacheManager.ThumbnailGenerationTask task =
|
||||||
new ThumbnailsCacheManager.ThumbnailGenerationTask(thumbnailView,
|
new ThumbnailsCacheManager.ThumbnailGenerationTask(thumbnailView,
|
||||||
mStorageManager,
|
storageManager,
|
||||||
user.toPlatformAccount(),
|
user.toPlatformAccount(),
|
||||||
asyncTasks,
|
asyncTasks,
|
||||||
!gridView);
|
!gridView);
|
||||||
|
@ -625,10 +638,10 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
|
||||||
MimeTypeUtil.getFileTypeIcon(file.getMimeType(),
|
MimeTypeUtil.getFileTypeIcon(file.getMimeType(),
|
||||||
file.getFileName(),
|
file.getFileName(),
|
||||||
user.toPlatformAccount(),
|
user.toPlatformAccount(),
|
||||||
activity));
|
context));
|
||||||
}
|
}
|
||||||
final ThumbnailsCacheManager.AsyncThumbnailDrawable asyncDrawable =
|
final ThumbnailsCacheManager.AsyncThumbnailDrawable asyncDrawable =
|
||||||
new ThumbnailsCacheManager.AsyncThumbnailDrawable(activity.getResources(),
|
new ThumbnailsCacheManager.AsyncThumbnailDrawable(context.getResources(),
|
||||||
thumbnail, task);
|
thumbnail, task);
|
||||||
thumbnailView.setImageDrawable(asyncDrawable);
|
thumbnailView.setImageDrawable(asyncDrawable);
|
||||||
asyncTasks.add(task);
|
asyncTasks.add(task);
|
||||||
|
@ -641,13 +654,13 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("image/png".equalsIgnoreCase(file.getMimeType())) {
|
if ("image/png".equalsIgnoreCase(file.getMimeType())) {
|
||||||
thumbnailView.setBackgroundColor(activity.getResources().getColor(R.color.bg_default));
|
thumbnailView.setBackgroundColor(context.getResources().getColor(R.color.bg_default));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
thumbnailView.setImageDrawable(MimeTypeUtil.getFileTypeIcon(file.getMimeType(),
|
thumbnailView.setImageDrawable(MimeTypeUtil.getFileTypeIcon(file.getMimeType(),
|
||||||
file.getFileName(),
|
file.getFileName(),
|
||||||
user.toPlatformAccount(),
|
user.toPlatformAccount(),
|
||||||
activity));
|
context));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,12 +22,34 @@
|
||||||
package com.owncloud.android.ui.dialog;
|
package com.owncloud.android.ui.dialog;
|
||||||
|
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.CheckBox;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.nextcloud.client.account.User;
|
||||||
import com.owncloud.android.R;
|
import com.owncloud.android.R;
|
||||||
|
import com.owncloud.android.datamodel.FileDataStorageManager;
|
||||||
|
import com.owncloud.android.datamodel.OCFile;
|
||||||
|
import com.owncloud.android.datamodel.ThumbnailsCacheManager;
|
||||||
|
import com.owncloud.android.db.OCUpload;
|
||||||
|
import com.owncloud.android.lib.common.utils.Log_OC;
|
||||||
|
import com.owncloud.android.ui.adapter.LocalFileListAdapter;
|
||||||
|
import com.owncloud.android.ui.adapter.OCFileListAdapter;
|
||||||
|
import com.owncloud.android.utils.DisplayUtils;
|
||||||
|
import com.owncloud.android.utils.ThemeUtils;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.fragment.app.DialogFragment;
|
import androidx.fragment.app.DialogFragment;
|
||||||
|
@ -40,6 +62,16 @@ import androidx.fragment.app.FragmentTransaction;
|
||||||
*/
|
*/
|
||||||
public class ConflictsResolveDialog extends DialogFragment {
|
public class ConflictsResolveDialog extends DialogFragment {
|
||||||
|
|
||||||
|
private OCFile existingFile;
|
||||||
|
private File newFile;
|
||||||
|
public OnConflictDecisionMadeListener listener;
|
||||||
|
private User user;
|
||||||
|
private List<ThumbnailsCacheManager.ThumbnailGenerationTask> asyncTasks = new ArrayList<>();
|
||||||
|
|
||||||
|
private static final String KEY_NEW_FILE = "file";
|
||||||
|
private static final String KEY_EXISTING_FILE = "ocfile";
|
||||||
|
private static final String KEY_USER = "user";
|
||||||
|
|
||||||
public enum Decision {
|
public enum Decision {
|
||||||
CANCEL,
|
CANCEL,
|
||||||
KEEP_BOTH,
|
KEEP_BOTH,
|
||||||
|
@ -47,42 +79,133 @@ public class ConflictsResolveDialog extends DialogFragment {
|
||||||
KEEP_SERVER,
|
KEEP_SERVER,
|
||||||
}
|
}
|
||||||
|
|
||||||
private final OnConflictDecisionMadeListener listener;
|
public static ConflictsResolveDialog newInstance(OCFile existingFile, OCUpload conflictUpload, User user) {
|
||||||
private final boolean canKeepServer;
|
ConflictsResolveDialog dialog = new ConflictsResolveDialog();
|
||||||
|
|
||||||
public ConflictsResolveDialog(OnConflictDecisionMadeListener listener, boolean canKeepServer) {
|
Bundle args = new Bundle();
|
||||||
this.listener = listener;
|
args.putParcelable(KEY_EXISTING_FILE, existingFile);
|
||||||
this.canKeepServer = canKeepServer;
|
args.putSerializable(KEY_NEW_FILE, new File(conflictUpload.getLocalPath()));
|
||||||
|
args.putParcelable(KEY_USER, user);
|
||||||
|
dialog.setArguments(args);
|
||||||
|
|
||||||
|
return dialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(@NonNull Context context) {
|
||||||
|
super.onAttach(context);
|
||||||
|
|
||||||
|
try {
|
||||||
|
listener = (OnConflictDecisionMadeListener) context;
|
||||||
|
} catch (ClassCastException e) {
|
||||||
|
throw new ClassCastException("Activity of this dialog must implement OnConflictDecisionMadeListener");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
|
||||||
|
AlertDialog alertDialog = (AlertDialog) getDialog();
|
||||||
|
|
||||||
|
if (alertDialog == null) {
|
||||||
|
Toast.makeText(getContext(), "Failed to create conflict dialog", Toast.LENGTH_LONG).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int color = ThemeUtils.primaryAccentColor(getContext());
|
||||||
|
alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setTextColor(color);
|
||||||
|
alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE).setTextColor(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
if (savedInstanceState != null) {
|
||||||
|
existingFile = savedInstanceState.getParcelable(KEY_EXISTING_FILE);
|
||||||
|
newFile = (File) savedInstanceState.getSerializable(KEY_NEW_FILE);
|
||||||
|
user = savedInstanceState.getParcelable(KEY_USER);
|
||||||
|
} else if (getArguments() != null) {
|
||||||
|
existingFile = getArguments().getParcelable(KEY_EXISTING_FILE);
|
||||||
|
newFile = (File) getArguments().getSerializable(KEY_NEW_FILE);
|
||||||
|
user = getArguments().getParcelable(KEY_USER);
|
||||||
|
} else {
|
||||||
|
Toast.makeText(getContext(), "Failed to create conflict dialog", Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSaveInstanceState(@NonNull Bundle outState) {
|
||||||
|
super.onSaveInstanceState(outState);
|
||||||
|
|
||||||
|
outState.putParcelable(KEY_EXISTING_FILE, existingFile);
|
||||||
|
outState.putSerializable(KEY_NEW_FILE, newFile);
|
||||||
|
outState.putParcelable(KEY_USER, user);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(requireActivity(), R.style.Theme_ownCloud_Dialog)
|
// Inflate the layout for the dialog
|
||||||
.setIcon(R.drawable.ic_warning)
|
LayoutInflater inflater = getActivity().getLayoutInflater();
|
||||||
.setTitle(R.string.conflict_title)
|
View view = inflater.inflate(R.layout.conflict_resolve_dialog, null);
|
||||||
.setMessage(getString(R.string.conflict_message))
|
int accentColor = ThemeUtils.primaryAccentColor(getContext());
|
||||||
.setPositiveButton(R.string.conflict_use_local_version,
|
|
||||||
(dialog, which) -> {
|
|
||||||
if (listener != null) {
|
|
||||||
listener.conflictDecisionMade(Decision.KEEP_LOCAL);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.setNeutralButton(R.string.conflict_keep_both,
|
|
||||||
(dialog, which) -> {
|
|
||||||
if (listener != null) {
|
|
||||||
listener.conflictDecisionMade(Decision.KEEP_BOTH);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (this.canKeepServer) {
|
// Build the dialog
|
||||||
builder.setNegativeButton(R.string.conflict_use_server_version,
|
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||||
(dialog, which) -> {
|
builder.setView(view)
|
||||||
|
.setPositiveButton(R.string.common_ok, (dialog, which) -> {
|
||||||
if (listener != null) {
|
if (listener != null) {
|
||||||
|
CheckBox newFile = view.findViewById(R.id.new_checkbox);
|
||||||
|
CheckBox existingFile = view.findViewById(R.id.existing_checkbox);
|
||||||
|
|
||||||
|
if (newFile.isSelected() && existingFile.isSelected()) {
|
||||||
|
listener.conflictDecisionMade(Decision.KEEP_BOTH);
|
||||||
|
} else if (newFile.isSelected()) {
|
||||||
|
listener.conflictDecisionMade(Decision.KEEP_LOCAL);
|
||||||
|
} else {
|
||||||
listener.conflictDecisionMade(Decision.KEEP_SERVER);
|
listener.conflictDecisionMade(Decision.KEEP_SERVER);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.setNegativeButton(R.string.common_cancel, (dialog, which) -> {
|
||||||
|
if (listener != null) {
|
||||||
|
listener.conflictDecisionMade(Decision.CANCEL);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.setTitle(ThemeUtils.getColoredTitle(getResources().getString(R.string.conflict_message_headline),
|
||||||
|
accentColor));
|
||||||
|
|
||||||
|
// set info for new file
|
||||||
|
TextView newSize = view.findViewById(R.id.new_size);
|
||||||
|
newSize.setText(DisplayUtils.bytesToHumanReadable(newFile.length()));
|
||||||
|
|
||||||
|
TextView newTimestamp = view.findViewById(R.id.new_timestamp);
|
||||||
|
newTimestamp.setText(DisplayUtils.getRelativeTimestamp(getContext(), newFile.lastModified()));
|
||||||
|
|
||||||
|
ImageView newThumbnail = view.findViewById(R.id.new_thumbnail);
|
||||||
|
newThumbnail.setTag(newFile.hashCode());
|
||||||
|
LocalFileListAdapter.setThumbnail(newFile, newThumbnail, getContext());
|
||||||
|
|
||||||
|
// set info for existing file
|
||||||
|
TextView existingSize = view.findViewById(R.id.existing_size);
|
||||||
|
existingSize.setText(DisplayUtils.bytesToHumanReadable(existingFile.getFileLength()));
|
||||||
|
|
||||||
|
TextView existingTimestamp = view.findViewById(R.id.existing_timestamp);
|
||||||
|
existingTimestamp.setText(DisplayUtils.getRelativeTimestamp(getContext(),
|
||||||
|
existingFile.getModificationTimestamp()));
|
||||||
|
|
||||||
|
ImageView existingThumbnail = view.findViewById(R.id.existing_thumbnail);
|
||||||
|
existingThumbnail.setTag(existingFile.getFileId());
|
||||||
|
OCFileListAdapter.setThumbnail(existingFile,
|
||||||
|
view.findViewById(R.id.existing_thumbnail),
|
||||||
|
user,
|
||||||
|
new FileDataStorageManager(user.toPlatformAccount(),
|
||||||
|
requireContext().getContentResolver()),
|
||||||
|
asyncTasks,
|
||||||
|
false,
|
||||||
|
getContext());
|
||||||
|
|
||||||
return builder.create();
|
return builder.create();
|
||||||
}
|
}
|
||||||
|
@ -99,7 +222,7 @@ public class ConflictsResolveDialog extends DialogFragment {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCancel(DialogInterface dialog) {
|
public void onCancel(@NonNull DialogInterface dialog) {
|
||||||
if (listener != null) {
|
if (listener != null) {
|
||||||
listener.conflictDecisionMade(Decision.CANCEL);
|
listener.conflictDecisionMade(Decision.CANCEL);
|
||||||
}
|
}
|
||||||
|
@ -108,4 +231,21 @@ public class ConflictsResolveDialog extends DialogFragment {
|
||||||
public interface OnConflictDecisionMadeListener {
|
public interface OnConflictDecisionMadeListener {
|
||||||
void conflictDecisionMade(Decision decision);
|
void conflictDecisionMade(Decision decision);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStop() {
|
||||||
|
super.onStop();
|
||||||
|
|
||||||
|
for (ThumbnailsCacheManager.ThumbnailGenerationTask task : asyncTasks) {
|
||||||
|
if (task != null) {
|
||||||
|
task.cancel(true);
|
||||||
|
if (task.getGetMethod() != null) {
|
||||||
|
Log_OC.d(this, "cancel: abort get method directly");
|
||||||
|
task.getGetMethod().abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
asyncTasks.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
110
src/main/res/layout/conflict_resolve_dialog.xml
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?><!--
|
||||||
|
~
|
||||||
|
~ Nextcloud Android client application
|
||||||
|
~
|
||||||
|
~ @author Tobias Kaminsky
|
||||||
|
~ Copyright (C) 2020 Tobias Kaminsky
|
||||||
|
~ Copyright (C) 2020 Nextcloud GmbH
|
||||||
|
~
|
||||||
|
~ This program is free software: you can redistribute it and/or modify
|
||||||
|
~ it under the terms of the GNU Affero General Public License as published by
|
||||||
|
~ the Free Software Foundation, either version 3 of the License, or
|
||||||
|
~ (at your option) any later version.
|
||||||
|
~
|
||||||
|
~ This program is distributed in the hope that it will be useful,
|
||||||
|
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
~ GNU Affero General Public License for more details.
|
||||||
|
~
|
||||||
|
~ You should have received a copy of the GNU Affero General Public License
|
||||||
|
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="clip_horizontal"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="@dimen/standard_padding">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/conflict_message_description" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:baselineAligned="false">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/newFileContainer"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
android:id="@+id/new_checkbox"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/conflict_new_file" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/new_thumbnail"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="80dp"
|
||||||
|
android:layout_margin="@dimen/standard_half_margin"
|
||||||
|
android:src="@drawable/file_image"
|
||||||
|
android:contentDescription="@string/thumbnail_for_new_file_desc" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/new_timestamp"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
tools:text="12. Dec 2020 - 23:10:20" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/new_size"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
tools:text="5 Mb" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/existingFileContainer"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
android:id="@+id/existing_checkbox"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/conflict_already_existing_file" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/existing_thumbnail"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="80dp"
|
||||||
|
android:layout_margin="@dimen/standard_half_margin"
|
||||||
|
android:src="@drawable/file_image"
|
||||||
|
android:contentDescription="@string/thumbnail_for_existing_file_description" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/existing_timestamp"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
tools:text="10. Dec 2020 - 10:10:10" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/existing_size"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
tools:text="3 Mb" />
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
|
@ -327,11 +327,6 @@
|
||||||
<string name="instant_upload_existing">Also upload existing files</string>
|
<string name="instant_upload_existing">Also upload existing files</string>
|
||||||
<string name="instant_upload_path">/InstantUpload</string>
|
<string name="instant_upload_path">/InstantUpload</string>
|
||||||
<string name="auto_upload_path">/AutoUpload</string>
|
<string name="auto_upload_path">/AutoUpload</string>
|
||||||
<string name="conflict_title">File conflict</string>
|
|
||||||
<string name="conflict_message">Which files do you want to keep? If you select both versions, the local file will have a number appended to its name.</string>
|
|
||||||
<string name="conflict_keep_both">Keep both</string>
|
|
||||||
<string name="conflict_use_local_version">local version</string>
|
|
||||||
<string name="conflict_use_server_version">server version</string>
|
|
||||||
|
|
||||||
<string name="preview_sorry">Sorry</string>
|
<string name="preview_sorry">Sorry</string>
|
||||||
<string name="preview_image_description">Image preview</string>
|
<string name="preview_image_description">Image preview</string>
|
||||||
|
@ -924,6 +919,11 @@
|
||||||
<string name="sync_not_enough_space_dialog_action_free_space">Free up space</string>
|
<string name="sync_not_enough_space_dialog_action_free_space">Free up space</string>
|
||||||
<string name="sync_not_enough_space_dialog_placeholder">%1$s is %2$s, but there is only %3$s available on device.</string>
|
<string name="sync_not_enough_space_dialog_placeholder">%1$s is %2$s, but there is only %3$s available on device.</string>
|
||||||
<string name="sync_not_enough_space_dialog_title">Not enough space</string>
|
<string name="sync_not_enough_space_dialog_title">Not enough space</string>
|
||||||
|
<string name="conflict_message_headline">Which files do you want to keep?</string>
|
||||||
|
<string name="conflict_message_description">If you select both versions, the local file will have a number appended to its name.</string>
|
||||||
|
<string name="conflict_new_file">New file</string>
|
||||||
|
<string name="conflict_already_existing_file">Already existing file</string>
|
||||||
|
<string name="thumbnail_for_new_file_desc">Thumbnail for new file</string>
|
||||||
|
<string name="thumbnail_for_existing_file_description">Thumbnail for existing file</string>
|
||||||
<string name="invalid_url">Invalid URL</string>
|
<string name="invalid_url">Invalid URL</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|