From c729823e757b6abe967d9c64d4318585c5aea455 Mon Sep 17 00:00:00 2001 From: A117870935 Date: Wed, 26 May 2021 09:53:43 +0530 Subject: [PATCH] Implemented MC code conflict dialog. --- .../ConflictsResolveConsentDialogIT.kt | 217 ++++++++++++++++ .../nextcloud/client/di/ComponentsModule.java | 4 + .../conflict/ConflictsResolveConsentDialog.kt | 242 ++++++++++++++++++ .../ui/activity/ConflictsResolveActivity.kt | 28 +- .../res/color/dialog_positive_btn_color.xml | 5 + .../conflict_resolve_consent_dialog.xml | 57 +++++ .../conflict_resolve_consent_dialog.xml | 56 ++++ .../values-de/nmc_conflict_dialog_strings.xml | 19 ++ app/src/main/res/values-night/colors.xml | 64 +++++ app/src/main/res/values/colors.xml | 89 +++++++ app/src/main/res/values/dimens.xml | 31 +++ .../values/nmc_conflict_dialog_strings.xml | 20 ++ app/src/main/res/values/styles.xml | 13 + 13 files changed, 837 insertions(+), 8 deletions(-) create mode 100644 app/src/androidTest/java/com/nmc/android/ui/conflict/ConflictsResolveConsentDialogIT.kt create mode 100644 app/src/main/java/com/nmc/android/ui/conflict/ConflictsResolveConsentDialog.kt create mode 100644 app/src/main/res/color/dialog_positive_btn_color.xml create mode 100644 app/src/main/res/layout-land/conflict_resolve_consent_dialog.xml create mode 100644 app/src/main/res/layout/conflict_resolve_consent_dialog.xml create mode 100644 app/src/main/res/values-de/nmc_conflict_dialog_strings.xml create mode 100644 app/src/main/res/values/dimens.xml create mode 100644 app/src/main/res/values/nmc_conflict_dialog_strings.xml 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 + + + + + + +