diff --git a/app/src/androidTest/java/com/nmc/android/ui/conflict/ConflictsResolveConsentDialogIT.kt b/app/src/androidTest/java/com/nmc/android/ui/conflict/ConflictsResolveConsentDialogIT.kt
new file mode 100644
index 000000000000..fef0a204fb65
--- /dev/null
+++ b/app/src/androidTest/java/com/nmc/android/ui/conflict/ConflictsResolveConsentDialogIT.kt
@@ -0,0 +1,217 @@
+/*
+ *
+ * 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 .
+ */
+package com.nmc.android.ui.conflict
+
+import android.content.Intent
+import androidx.test.espresso.Espresso
+import androidx.test.espresso.action.ViewActions
+import androidx.test.espresso.intent.rule.IntentsTestRule
+import androidx.test.espresso.matcher.ViewMatchers
+import androidx.test.platform.app.InstrumentationRegistry
+import com.nextcloud.client.account.UserAccountManagerImpl
+import com.nmc.android.ui.conflict.ConflictsResolveConsentDialog.Companion.newInstance
+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.ui.activity.ConflictsResolveActivity
+import com.owncloud.android.ui.activity.FileActivity
+import com.owncloud.android.ui.dialog.ConflictsResolveDialog.Decision
+import com.owncloud.android.ui.dialog.ConflictsResolveDialog.OnConflictDecisionMadeListener
+import com.owncloud.android.utils.FileStorageUtils
+import junit.framework.TestCase
+import org.junit.After
+import org.junit.Assert
+import org.junit.Rule
+import org.junit.Test
+
+class ConflictsResolveConsentDialogIT : AbstractIT() {
+ @get:Rule
+ val activityRule = IntentsTestRule(ConflictsResolveActivity::class.java, true, false)
+
+ private var returnCode = false
+
+ @Test
+ fun replaceWithNewFile() {
+ returnCode = false
+
+ val newUpload = OCUpload(
+ FileStorageUtils.getSavePath(user.accountName) + "/nonEmpty.txt",
+ "/newFile.txt",
+ user.accountName
+ )
+
+ val existingFile = OCFile("/newFile.txt")
+ existingFile.fileLength = 1024000
+ existingFile.modificationTimestamp = 1582019340
+ existingFile.remoteId = "00000123abc"
+
+ val newFile = OCFile("/newFile.txt")
+ newFile.fileLength = 56000
+ newFile.modificationTimestamp = 1522019340
+ newFile.storagePath = FileStorageUtils.getSavePath(user.accountName) + "/nonEmpty.txt"
+
+ val storageManager = FileDataStorageManager(user, targetContext.contentResolver)
+ storageManager.saveNewFile(existingFile)
+
+ val intent = Intent(targetContext, ConflictsResolveActivity::class.java)
+ intent.putExtra(FileActivity.EXTRA_FILE, newFile)
+ intent.putExtra(ConflictsResolveActivity.EXTRA_EXISTING_FILE, existingFile)
+ intent.putExtra(ConflictsResolveActivity.EXTRA_CONFLICT_UPLOAD_ID, newUpload.uploadId)
+ intent.putExtra(ConflictsResolveActivity.EXTRA_LAUNCHED_FROM_TEST, true)
+
+ val sut = activityRule.launchActivity(intent)
+
+ val dialog = newInstance(
+ targetContext,
+ existingFile,
+ newFile,
+ UserAccountManagerImpl
+ .fromContext(targetContext)
+ .user
+ )
+ dialog.showDialog(sut)
+
+ sut.listener = OnConflictDecisionMadeListener { decision: Decision? ->
+ Assert.assertEquals(decision, Decision.KEEP_LOCAL)
+ returnCode = true
+ }
+
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+
+ Espresso.onView(ViewMatchers.withId(R.id.replace_btn)).perform(ViewActions.click())
+
+ TestCase.assertTrue(returnCode)
+ }
+
+ @Test
+ fun keepBothFiles() {
+ returnCode = false
+
+ val newUpload = OCUpload(
+ FileStorageUtils.getSavePath(user.accountName) + "/nonEmpty.txt",
+ "/newFile.txt",
+ user.accountName
+ )
+
+ val existingFile = OCFile("/newFile.txt")
+ existingFile.fileLength = 1024000
+ existingFile.modificationTimestamp = 1582019340
+
+ val newFile = OCFile("/newFile.txt")
+ newFile.fileLength = 56000
+ newFile.modificationTimestamp = 1522019340
+ newFile.storagePath = FileStorageUtils.getSavePath(user.accountName) + "/nonEmpty.txt"
+
+ val storageManager = FileDataStorageManager(user, targetContext.contentResolver)
+ storageManager.saveNewFile(existingFile)
+
+ val intent = Intent(targetContext, ConflictsResolveActivity::class.java)
+ intent.putExtra(FileActivity.EXTRA_FILE, newFile)
+ intent.putExtra(ConflictsResolveActivity.EXTRA_EXISTING_FILE, existingFile)
+ intent.putExtra(FileActivity.EXTRA_USER, user)
+ intent.putExtra(ConflictsResolveActivity.EXTRA_CONFLICT_UPLOAD_ID, newUpload.uploadId)
+ intent.putExtra(ConflictsResolveActivity.EXTRA_LAUNCHED_FROM_TEST, true)
+
+ val sut = activityRule.launchActivity(intent)
+
+ val dialog = newInstance(
+ targetContext,
+ existingFile,
+ newFile,
+ UserAccountManagerImpl
+ .fromContext(targetContext)
+ .user
+ )
+ dialog.showDialog(sut)
+
+ sut.listener = OnConflictDecisionMadeListener { decision: Decision? ->
+ Assert.assertEquals(decision, Decision.KEEP_BOTH)
+ returnCode = true
+ }
+
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+
+ Espresso.onView(ViewMatchers.withId(R.id.keep_both_btn)).perform(ViewActions.click())
+
+ TestCase.assertTrue(returnCode)
+ }
+
+ @Test
+ fun keepExistingFile() {
+ returnCode = false
+
+ val newUpload = OCUpload(
+ FileStorageUtils.getSavePath(user.accountName) + "/nonEmpty.txt",
+ "/newFile.txt",
+ user.accountName
+ )
+
+ val existingFile = OCFile("/newFile.txt")
+ existingFile.fileLength = 1024000
+ existingFile.modificationTimestamp = 1582019340
+
+ val newFile = OCFile("/newFile.txt")
+ newFile.fileLength = 56000
+ newFile.modificationTimestamp = 1522019340
+ newFile.storagePath = FileStorageUtils.getSavePath(user.accountName) + "/nonEmpty.txt"
+
+ val storageManager = FileDataStorageManager(user, targetContext.contentResolver)
+ storageManager.saveNewFile(existingFile)
+
+ val intent = Intent(targetContext, ConflictsResolveActivity::class.java)
+ intent.putExtra(FileActivity.EXTRA_FILE, newFile)
+ intent.putExtra(ConflictsResolveActivity.EXTRA_EXISTING_FILE, existingFile)
+ intent.putExtra(FileActivity.EXTRA_USER, user)
+ intent.putExtra(ConflictsResolveActivity.EXTRA_CONFLICT_UPLOAD_ID, newUpload.uploadId)
+ intent.putExtra(ConflictsResolveActivity.EXTRA_LAUNCHED_FROM_TEST, true)
+
+ val sut = activityRule.launchActivity(intent)
+
+ val dialog = newInstance(
+ targetContext,
+ existingFile,
+ newFile,
+ UserAccountManagerImpl
+ .fromContext(targetContext)
+ .user
+ )
+ dialog.showDialog(sut)
+
+ sut.listener = OnConflictDecisionMadeListener { decision: Decision? ->
+ Assert.assertEquals(decision, Decision.KEEP_SERVER)
+ returnCode = true
+ }
+
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+
+ Espresso.onView(ViewMatchers.withId(R.id.cancel_keep_existing_btn)).perform(ViewActions.click())
+
+ TestCase.assertTrue(returnCode)
+ }
+
+ @After
+ override fun after() {
+ storageManager.deleteAllFiles()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/nextcloud/client/di/ComponentsModule.java b/app/src/main/java/com/nextcloud/client/di/ComponentsModule.java
index 8225e60be251..307acabc1e6c 100644
--- a/app/src/main/java/com/nextcloud/client/di/ComponentsModule.java
+++ b/app/src/main/java/com/nextcloud/client/di/ComponentsModule.java
@@ -30,6 +30,7 @@
import com.nextcloud.ui.SetStatusDialogFragment;
import com.nextcloud.ui.composeActivity.ComposeActivity;
import com.nextcloud.ui.fileactions.FileActionsBottomSheet;
+import com.nmc.android.ui.conflict.ConflictsResolveConsentDialog;
import com.nmc.android.ui.LauncherActivity;
import com.owncloud.android.MainApp;
import com.owncloud.android.authentication.AuthenticatorActivity;
@@ -371,6 +372,9 @@ abstract class ComponentsModule {
@ContributesAndroidInjector
abstract ConflictsResolveDialog conflictsResolveDialog();
+ @ContributesAndroidInjector
+ abstract ConflictsResolveConsentDialog conflictsResolveConsentDialog();
+
@ContributesAndroidInjector
abstract CreateFolderDialogFragment createFolderDialogFragment();
diff --git a/app/src/main/java/com/nmc/android/ui/conflict/ConflictsResolveConsentDialog.kt b/app/src/main/java/com/nmc/android/ui/conflict/ConflictsResolveConsentDialog.kt
new file mode 100644
index 000000000000..56c99d211c81
--- /dev/null
+++ b/app/src/main/java/com/nmc/android/ui/conflict/ConflictsResolveConsentDialog.kt
@@ -0,0 +1,242 @@
+/*
+ * ownCloud Android client application
+ *
+ * @author Bartek Przybylski
+ * Copyright (C) 2012 Bartek Przybylski
+ * Copyright (C) 2015 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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.ui.conflict
+
+import android.app.Dialog
+import android.content.Context
+import android.content.DialogInterface
+import android.os.Bundle
+import android.widget.Toast
+import androidx.appcompat.app.AlertDialog
+import androidx.appcompat.app.AppCompatActivity
+import androidx.fragment.app.DialogFragment
+import com.nextcloud.client.account.User
+import com.nextcloud.client.database.entity.OfflineOperationEntity
+import com.nextcloud.client.di.Injectable
+import com.nextcloud.utils.extensions.getParcelableArgument
+import com.nextcloud.utils.extensions.getSerializableArgument
+import com.owncloud.android.R
+import com.owncloud.android.databinding.ConflictResolveConsentDialogBinding
+import com.owncloud.android.datamodel.FileDataStorageManager
+import com.owncloud.android.datamodel.OCFile
+import com.owncloud.android.ui.dialog.ConflictsResolveDialog
+import com.owncloud.android.ui.dialog.ConflictsResolveDialog.OnConflictDecisionMadeListener
+import com.owncloud.android.ui.dialog.parcel.ConflictDialogData
+import com.owncloud.android.ui.dialog.parcel.ConflictFileData
+import com.owncloud.android.utils.DisplayUtils
+import java.io.File
+import javax.inject.Inject
+
+/**
+ * Dialog which will be displayed to user upon keep-in-sync file conflict.
+ */
+class ConflictsResolveConsentDialog : DialogFragment(), Injectable {
+ private lateinit var binding: ConflictResolveConsentDialogBinding
+
+ private var existingFile: OCFile? = null
+ private var newFile: File? = null
+ private var listener: OnConflictDecisionMadeListener? = null
+ private var data: ConflictDialogData? = null
+ private var user: User? = null
+
+ @Inject
+ lateinit var fileDataStorageManager: FileDataStorageManager
+
+ override fun onAttach(context: Context) {
+ super.onAttach(context)
+ try {
+ listener = context as OnConflictDecisionMadeListener
+ } catch (e: ClassCastException) {
+ throw ClassCastException("Activity of this dialog must implement OnConflictDecisionMadeListener")
+ }
+ }
+
+ override fun onStart() {
+ super.onStart()
+
+ val alertDialog = dialog as AlertDialog?
+
+ if (alertDialog == null) {
+ Toast.makeText(context, "Failed to create conflict dialog", Toast.LENGTH_LONG).show()
+ return
+ }
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ val bundle = savedInstanceState ?: arguments
+
+ if (bundle != null) {
+ data = bundle.getParcelableArgument(ARG_CONFLICT_DATA, ConflictDialogData::class.java)
+ newFile = bundle.getSerializableArgument(ARG_NEW_FILE, File::class.java)
+ existingFile = bundle.getParcelableArgument(ARG_EXISTING_FILE, OCFile::class.java)
+ user = bundle.getParcelableArgument(ARG_USER, User::class.java)
+ } else {
+ Toast.makeText(context, "Failed to create conflict dialog", Toast.LENGTH_LONG).show()
+ }
+ }
+
+ override fun onSaveInstanceState(outState: Bundle) {
+ super.onSaveInstanceState(outState)
+ outState.run {
+ putParcelable(ARG_CONFLICT_DATA, data)
+ }
+ }
+
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+ binding = ConflictResolveConsentDialogBinding.inflate(requireActivity().layoutInflater)
+
+ // TODO: 26-05-2021 change replace and keep both button text for multiple files
+ binding.replaceBtn.setOnClickListener {
+ listener?.conflictDecisionMade(ConflictsResolveDialog.Decision.KEEP_LOCAL)
+ }
+
+ binding.keepBothBtn.setOnClickListener {
+ listener?.conflictDecisionMade(ConflictsResolveDialog.Decision.KEEP_BOTH)
+ }
+
+ binding.moreDetailsBtn.setOnClickListener { }
+
+ binding.cancelKeepExistingBtn.setOnClickListener {
+ listener?.conflictDecisionMade(ConflictsResolveDialog.Decision.KEEP_SERVER)
+ }
+
+ // Build the dialog
+ // TODO: 26-05-2021 Handle multiple dialog message
+ val dialogMessage = String.format(
+ getString(R.string.conflict_dialog_message),
+ fileDataStorageManager.getFileByEncryptedRemotePath(existingFile?.remotePath).fileName
+ )
+ val builder = AlertDialog.Builder(requireActivity())
+ builder.setView(binding.root) // TODO: 26-05-2021 handle multiple dialog title
+ .setTitle(getString(R.string.conflict_dialog_title))
+ .setMessage(dialogMessage)
+
+
+ return builder.create()
+ }
+
+ fun showDialog(activity: AppCompatActivity) {
+ val prev = activity.supportFragmentManager.findFragmentByTag("dialog")
+ activity.supportFragmentManager.beginTransaction().run {
+ if (prev != null) {
+ this.remove(prev)
+ }
+ addToBackStack(null)
+ show(this, "dialog")
+ }
+ }
+
+ override fun onCancel(dialog: DialogInterface) {
+ listener?.conflictDecisionMade(ConflictsResolveDialog.Decision.CANCEL)
+ }
+
+ companion object {
+ private const val ARG_CONFLICT_DATA = "CONFLICT_DATA"
+ private const val ARG_NEW_FILE = "NEW_FILE"
+ private const val ARG_EXISTING_FILE = "EXISTING_FILE"
+ private const val ARG_USER = "USER"
+
+ @JvmStatic
+ fun newInstance(
+ context: Context,
+ existingFile: OCFile,
+ newFile: OCFile,
+ user: User?
+ ): ConflictsResolveConsentDialog {
+ val file = File(newFile.storagePath)
+ val conflictData = getFileConflictData(file, existingFile, context)
+
+ val bundle = Bundle().apply {
+ putParcelable(ARG_CONFLICT_DATA, conflictData)
+ putSerializable(ARG_NEW_FILE, file)
+ putParcelable(ARG_EXISTING_FILE, existingFile)
+ putParcelable(ARG_USER, user)
+ }
+
+ return ConflictsResolveConsentDialog().apply {
+ arguments = bundle
+ }
+ }
+
+ @JvmStatic
+ fun newInstance(
+ context: Context,
+ offlineOperation: OfflineOperationEntity,
+ existingFile: OCFile
+ ): ConflictsResolveConsentDialog {
+ val conflictData = getFolderConflictData(offlineOperation, existingFile, context)
+
+ val bundle = Bundle().apply {
+ putParcelable(ARG_CONFLICT_DATA, conflictData)
+ putParcelable(ARG_EXISTING_FILE, existingFile)
+ }
+
+ return ConflictsResolveConsentDialog().apply {
+ arguments = bundle
+ }
+ }
+
+ @Suppress("MagicNumber")
+ @JvmStatic
+ private fun getFolderConflictData(
+ offlineOperation: OfflineOperationEntity,
+ existingFile: OCFile,
+ context: Context
+ ): ConflictDialogData {
+ val newFile = context.getString(R.string.prefs_synced_folders_local_path_title)
+ val newFileTimestamp =
+ DisplayUtils.getRelativeTimestamp(context, offlineOperation.createdAt?.times(1000L) ?: 0)
+ val newFileSize = DisplayUtils.bytesToHumanReadable(0)
+ val newFileData = ConflictFileData(newFile, newFileTimestamp.toString(), newFileSize)
+
+ val existingFileTitle = context.getString(R.string.prefs_synced_folders_remote_path_title)
+ val existingFileTimestamp = DisplayUtils.getRelativeTimestamp(context, existingFile.modificationTimestamp)
+ val existingFileSize = DisplayUtils.bytesToHumanReadable(existingFile.fileLength)
+ val existingFileData = ConflictFileData(existingFileTitle, existingFileTimestamp.toString(), existingFileSize)
+
+ val title = context.getString(R.string.conflict_folder_headline)
+ val description = context.getString(R.string.conflict_message_description_for_folder)
+ return ConflictDialogData(null, title, description, Pair(newFileData, existingFileData))
+ }
+
+ @JvmStatic
+ private fun getFileConflictData(newFile: File, existingFile: OCFile, context: Context): ConflictDialogData {
+ // TODO Path needs to be set it correctly for encrypted folders
+ val title = existingFile.decryptedRemotePath
+
+ val newFileTitle = context.getString(R.string.conflict_local_file)
+ val newFileTimestamp = DisplayUtils.getRelativeTimestamp(context, newFile.lastModified())
+ val newFileSize = DisplayUtils.bytesToHumanReadable(newFile.length())
+ val newFileData = ConflictFileData(newFileTitle, newFileTimestamp.toString(), newFileSize)
+
+ val existingFileTitle = context.getString(R.string.conflict_server_file)
+ val existingFileTimestamp = DisplayUtils.getRelativeTimestamp(context, existingFile.modificationTimestamp)
+ val existingFileSize = DisplayUtils.bytesToHumanReadable(existingFile.fileLength)
+ val existingFileData = ConflictFileData(existingFileTitle, existingFileTimestamp.toString(), existingFileSize)
+
+ val headline = context.getString(R.string.choose_which_file)
+ val description = context.getString(R.string.conflict_message_description)
+ return ConflictDialogData(title, headline, description, Pair(newFileData, existingFileData))
+ }
+ }
+}
diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.kt
index 755dd53555d6..c525da4dfd46 100644
--- a/app/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.kt
+++ b/app/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.kt
@@ -27,6 +27,7 @@ import com.nextcloud.client.jobs.upload.UploadNotificationManager
import com.nextcloud.model.HTTPStatusCodes
import com.nextcloud.utils.extensions.getParcelableArgument
import com.nextcloud.utils.extensions.logFileSize
+import com.nmc.android.ui.conflict.ConflictsResolveConsentDialog
import com.owncloud.android.R
import com.owncloud.android.datamodel.OCFile
import com.owncloud.android.datamodel.UploadsStorageManager
@@ -35,7 +36,6 @@ import com.owncloud.android.files.services.NameCollisionPolicy
import com.owncloud.android.lib.common.utils.Log_OC
import com.owncloud.android.lib.resources.files.ReadFileRemoteOperation
import com.owncloud.android.lib.resources.files.model.RemoteFile
-import com.owncloud.android.ui.dialog.ConflictsResolveDialog
import com.owncloud.android.ui.dialog.ConflictsResolveDialog.Decision
import com.owncloud.android.ui.dialog.ConflictsResolveDialog.OnConflictDecisionMadeListener
import com.owncloud.android.utils.FileStorageUtils
@@ -180,7 +180,9 @@ class ConflictsResolveActivity : FileActivity(), OnConflictDecisionMadeListener
}
private fun keepServer(file: OCFile?, upload: OCUpload?) {
- if (!shouldDeleteLocal()) {
+ if (newFile?.isEncrypted == true) {
+ // NMC-2361 fix
+ } else if (!shouldDeleteLocal()) {
// Overwrite local file
file?.let {
FileDownloadHelper.instance().downloadFile(
@@ -241,7 +243,7 @@ class ConflictsResolveActivity : FileActivity(), OnConflictDecisionMadeListener
}
val (ft, _) = prepareDialog()
- val dialog = ConflictsResolveDialog.newInstance(
+ val dialog = ConflictsResolveConsentDialog.newInstance(
this,
offlineOperation,
ocFile
@@ -300,10 +302,10 @@ class ConflictsResolveActivity : FileActivity(), OnConflictDecisionMadeListener
val (ft, user) = prepareDialog()
if (existingFile != null && storageManager.fileExists(remotePath) && newFile != null) {
- val dialog = ConflictsResolveDialog.newInstance(
+ val dialog = ConflictsResolveConsentDialog.newInstance(
this,
- newFile!!,
existingFile!!,
+ newFile!!,
user
)
dialog.show(ft, "conflictDialog")
@@ -316,9 +318,15 @@ class ConflictsResolveActivity : FileActivity(), OnConflictDecisionMadeListener
private fun showErrorAndFinish(code: Int? = null) {
val message = parseErrorMessage(code)
- lifecycleScope.launch(Dispatchers.Main) {
- Toast.makeText(this@ConflictsResolveActivity, message, Toast.LENGTH_LONG).show()
- finish()
+ // NMC Customization
+ // if activity is launched from test case then don't finish the activity as it is required to show the dialog
+ // but during normal app run activity should finish during error so we have to pass it false or don't pass
+ // anything
+ if (!intent.getBooleanExtra(EXTRA_LAUNCHED_FROM_TEST, false)) {
+ lifecycleScope.launch(Dispatchers.Main) {
+ Toast.makeText(this@ConflictsResolveActivity, message, Toast.LENGTH_LONG).show()
+ finish()
+ }
}
}
@@ -350,6 +358,10 @@ class ConflictsResolveActivity : FileActivity(), OnConflictDecisionMadeListener
const val EXTRA_EXISTING_FILE = "EXISTING_FILE"
private const val EXTRA_OFFLINE_OPERATION_PATH = "EXTRA_OFFLINE_OPERATION_PATH"
+ /**
+ * variable to tell activity that it has been launched from test class
+ */
+ const val EXTRA_LAUNCHED_FROM_TEST = "LAUNCHED_FROM_TEST"
private val TAG = ConflictsResolveActivity::class.java.simpleName
@JvmStatic
diff --git a/app/src/main/res/color/dialog_positive_btn_color.xml b/app/src/main/res/color/dialog_positive_btn_color.xml
new file mode 100644
index 000000000000..5913e0da1a1d
--- /dev/null
+++ b/app/src/main/res/color/dialog_positive_btn_color.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout-land/conflict_resolve_consent_dialog.xml b/app/src/main/res/layout-land/conflict_resolve_consent_dialog.xml
new file mode 100644
index 000000000000..325749554862
--- /dev/null
+++ b/app/src/main/res/layout-land/conflict_resolve_consent_dialog.xml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/conflict_resolve_consent_dialog.xml b/app/src/main/res/layout/conflict_resolve_consent_dialog.xml
new file mode 100644
index 000000000000..f0ac8de88111
--- /dev/null
+++ b/app/src/main/res/layout/conflict_resolve_consent_dialog.xml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values-de/nmc_conflict_dialog_strings.xml b/app/src/main/res/values-de/nmc_conflict_dialog_strings.xml
new file mode 100644
index 000000000000..a919012dfb20
--- /dev/null
+++ b/app/src/main/res/values-de/nmc_conflict_dialog_strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Ersetzen
+ Alle ersetzen
+ Beide behalten
+ Beide Versionen für alle behalten
+ Mehr Details
+ Abbrechen und bestehende Datei behalten
+ Dateikonflikt
+ %d Dateikonflikte
+ %1$s ist im Zielordner bereits vorhanden. Möchten Sie die bestehende Datei behalten oder überschreiben?
+ Die Dateien sind im Zielordner bereits vorhanden. Möchten Sie die bestehenden Dateien behalten oder überschreiben?
+
\ No newline at end of file
diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml
index 1ce3f0da4f73..2f79b79712ed 100644
--- a/app/src/main/res/values-night/colors.xml
+++ b/app/src/main/res/values-night/colors.xml
@@ -36,4 +36,68 @@
#1E1E1E
@android:color/white
+
+
+ #FFFFFF
+ @color/grey_30
+ @color/grey_30
+ #CCCCCC
+ @color/grey_70
+ @color/grey_80
+ #2D2D2D
+ @color/grey_70
+ @color/grey_70
+
+
+ @color/grey_80
+ @color/grey_0
+
+
+ @color/grey_80
+ @color/grey_0
+
+
+ @color/grey_60
+ @color/grey_0
+ @color/grey_0
+ @color/grey_30
+ #FFFFFF
+ @color/grey_30
+ @color/grey_80
+ #FFFFFF
+
+
+ @color/grey_80
+ @color/grey_30
+ @color/grey_0
+
+
+ @color/grey_80
+ @color/grey_0
+ @color/grey_80
+
+
+ @color/grey_70
+ @color/grey_60
+
+
+ @color/grey_70
+ @color/grey_70
+
+
+ #FFFFFF
+ @color/grey_30
+ @color/grey_0
+ @color/grey_0
+ @color/grey_0
+ @color/grey_0
+ @color/grey_60
+ @color/grey_0
+ #FFFFFF
+
+
+ #121212
+ @color/grey_0
+ @color/grey_80
+ @color/grey_80
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 89ed00a08bf2..46992e0a67c6 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -75,4 +75,93 @@
@android:color/white
#666666
#A5A5A5
+
+
+ #191919
+ @color/primary
+ #191919
+ #191919
+ @color/grey_30
+ @android:color/white
+ #FFFFFF
+ @color/grey_0
+ #CCCCCC
+ #77c4ff
+ #B3FFFFFF
+ @color/grey_10
+
+
+ #101010
+ #F2F2F2
+ #E5E5E5
+ #B2B2B2
+ #666666
+ #4C4C4C
+ #333333
+
+
+ @color/design_snackbar_background_color
+ @color/white
+
+
+ #FFFFFF
+ #191919
+
+
+ @color/grey_0
+ #191919
+ @color/primary
+ #191919
+ @color/primary
+ @color/grey_30
+ @color/white
+ #191919
+
+
+ #FFFFFF
+ #191919
+ #191919
+
+
+ #FFFFFF
+ #191919
+ #FFFFFF
+
+
+ @color/primary
+ #F399C7
+ #FFFFFF
+ @color/grey_30
+ @color/grey_10
+ @color/grey_0
+
+
+ @color/primary
+ @color/grey_30
+ @color/grey_30
+ #CCCCCC
+
+
+ #191919
+ @color/grey_30
+ #191919
+ #191919
+ #191919
+ #191919
+ @color/grey_30
+ #191919
+ #000000
+ #191919
+ #F6E5EB
+ #C16F81
+ #0D39DF
+ #0099ff
+
+
+ @color/grey_0
+ #191919
+ @color/grey_0
+ @color/grey_30
+ #77b6bb
+ #5077b6bb
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
new file mode 100644
index 000000000000..cc9e25255a10
--- /dev/null
+++ b/app/src/main/res/values/dimens.xml
@@ -0,0 +1,31 @@
+
+
+ 4dp
+ 16dp
+ 24dp
+ 6dp
+ 18sp
+ 15sp
+ 15dp
+ 56dp
+ 86dp
+ 80dp
+ 11sp
+ 30dp
+ 55dp
+ 258dp
+ 17sp
+ 20dp
+ 160dp
+ 50dp
+ 150dp
+ 55dp
+ 48dp
+ 48dp
+ 24dp
+ 26dp
+ 20sp
+ 145dp
+ 1dp
+ 13sp
+
\ No newline at end of file
diff --git a/app/src/main/res/values/nmc_conflict_dialog_strings.xml b/app/src/main/res/values/nmc_conflict_dialog_strings.xml
new file mode 100644
index 000000000000..78c8e470dd17
--- /dev/null
+++ b/app/src/main/res/values/nmc_conflict_dialog_strings.xml
@@ -0,0 +1,20 @@
+
+
+
+ Replace
+ Replace all
+ Keep both
+ Keep both for all
+ More details
+ Cancel and keep existing
+ File conflict
+ %d File conflicts
+ %1$s already exists in this location. Do you want to replace it with the
+ file you are moving?
+ The files already exist in this location. Do you want to replace them with the files you are moving?
+
\ No newline at end of file
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 4c1563085156..d888b4c602b6 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -431,6 +431,19 @@
- ?android:attr/colorBackground
+
+
+
+
+
+
+