瀏覽代碼

Simplify usage of ConflictResolveDialog

Signed-off-by: alperozturk <alper_ozturk@proton.me>
alperozturk 9 月之前
父節點
當前提交
ae5064d9a5

+ 2 - 1
app/src/androidTest/java/com/owncloud/android/ui/activity/ConflictsResolveActivityIT.java

@@ -62,7 +62,8 @@ public class ConflictsResolveActivityIT extends AbstractIT {
 
         ConflictsResolveActivity sut = activityRule.launchActivity(intent);
 
-        ConflictsResolveDialog dialog = ConflictsResolveDialog.newInstance(existingFile,
+        ConflictsResolveDialog dialog = ConflictsResolveDialog.newInstance(targetContext,
+                                                                           existingFile,
                                                                            newFile,
                                                                            UserAccountManagerImpl
                                                                                .fromContext(targetContext)

+ 12 - 4
app/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.kt

@@ -223,11 +223,18 @@ class ConflictsResolveActivity : FileActivity(), OnConflictDecisionMadeListener
 
         offlineOperationPath?.let { path ->
             newFile?.let { ocFile ->
+                val offlineOperation = fileDataStorageManager.offlineOperationDao.getByPath(path)
+
+                if (offlineOperation == null) {
+                    showErrorAndFinish()
+                    return
+                }
+
                 val (ft, user) = prepareDialog()
                 val dialog = ConflictsResolveDialog.newInstance(
-                    ocFile,
-                    user,
-                    path
+                    this,
+                    offlineOperation,
+                    ocFile
                 )
                 dialog.show(ft, "conflictDialog")
                 return
@@ -283,8 +290,9 @@ class ConflictsResolveActivity : FileActivity(), OnConflictDecisionMadeListener
 
         if (existingFile != null && storageManager.fileExists(remotePath) && newFile != null) {
             val dialog = ConflictsResolveDialog.newInstance(
-                existingFile,
+                this,
                 newFile!!,
+                existingFile,
                 user
             )
             dialog.show(ft, "conflictDialog")

+ 149 - 116
app/src/main/java/com/owncloud/android/ui/dialog/ConflictsResolveDialog.kt

@@ -1,11 +1,8 @@
 /*
  * Nextcloud - Android Client
  *
- * SPDX-FileCopyrightText: 2023 Alper Ozturk <alper.ozturk@nextcloud.com>
- * SPDX-FileCopyrightText: 2020-2022 Tobias Kaminsky <tobias@kaminsky.me>
- * SPDX-FileCopyrightText: 2015 ownCloud Inc.
- * SPDX-FileCopyrightText: 2012 Bartosz Przybylski <bart.p.pl@gmail.com>
- * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only)
+ * SPDX-FileCopyrightText: 2024 Alper Ozturk <alper.ozturk@nextcloud.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
  */
 package com.owncloud.android.ui.dialog
 
@@ -25,7 +22,6 @@ 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.nextcloud.utils.extensions.logFileSize
 import com.owncloud.android.R
 import com.owncloud.android.databinding.ConflictResolveDialogBinding
 import com.owncloud.android.datamodel.FileDataStorageManager
@@ -34,6 +30,8 @@ import com.owncloud.android.datamodel.SyncedFolderProvider
 import com.owncloud.android.datamodel.ThumbnailsCacheManager.ThumbnailGenerationTask
 import com.owncloud.android.lib.common.utils.Log_OC
 import com.owncloud.android.ui.adapter.LocalFileListAdapter
+import com.owncloud.android.ui.dialog.parcel.ConflictDialogData
+import com.owncloud.android.ui.dialog.parcel.ConflictFileData
 import com.owncloud.android.utils.DisplayUtils
 import com.owncloud.android.utils.MimeTypeUtil
 import com.owncloud.android.utils.theme.ViewThemeUtils
@@ -46,14 +44,14 @@ import javax.inject.Inject
 class ConflictsResolveDialog : DialogFragment(), Injectable {
     private lateinit var binding: ConflictResolveDialogBinding
 
-    private var existingFile: OCFile? = null
-    private var newFile: File? = null
     var listener: OnConflictDecisionMadeListener? = null
-    private var user: User? = null
     private val asyncTasks: MutableList<ThumbnailGenerationTask> = ArrayList()
     private var positiveButton: MaterialButton? = null
-    private var offlineOperation: OfflineOperationEntity? = null
-    private var serverFile: OCFile? = null
+
+    private var data: ConflictDialogData? = null
+    private var user: User? = null
+    private var leftDataFile: File? = null
+    private var rightDataFile: OCFile? = null
 
     @Inject
     lateinit var viewThemeUtils: ViewThemeUtils
@@ -109,25 +107,15 @@ class ConflictsResolveDialog : DialogFragment(), Injectable {
         super.onCreate(savedInstanceState)
 
         if (savedInstanceState != null) {
-            existingFile = savedInstanceState.getParcelableArgument(KEY_EXISTING_FILE, OCFile::class.java)
-            newFile = savedInstanceState.getSerializableArgument(KEY_NEW_FILE, File::class.java)
-            user = savedInstanceState.getParcelableArgument(KEY_USER, User::class.java)
-
-            val offlineOperationPath = savedInstanceState.getString(KEY_OFFLINE_OPERATION_PATH) ?: return
-            offlineOperation = fileDataStorageManager.offlineOperationDao.getByPath(offlineOperationPath)
-
-            val serverFileRemoteId = savedInstanceState.getString(KEY_OFFLINE_SERVER_FILE_REMOTE_ID) ?: return
-            serverFile = fileDataStorageManager.getFileByRemoteId(serverFileRemoteId)
+            data = savedInstanceState.getParcelableArgument(CONFLICT_DATA, ConflictDialogData::class.java)
+            leftDataFile = savedInstanceState.getSerializableArgument(LEFT_FILE, File::class.java)
+            rightDataFile = savedInstanceState.getParcelableArgument(RIGHT_FILE, OCFile::class.java)
+            user = savedInstanceState.getParcelableArgument(USER, User::class.java)
         } else if (arguments != null) {
-            existingFile = arguments.getParcelableArgument(KEY_EXISTING_FILE, OCFile::class.java)
-            newFile = arguments.getSerializableArgument(KEY_NEW_FILE, File::class.java)
-            user = arguments.getParcelableArgument(KEY_USER, User::class.java)
-
-            val offlineOperationPath = arguments?.getString(KEY_OFFLINE_OPERATION_PATH) ?: return
-            offlineOperation = fileDataStorageManager.offlineOperationDao.getByPath(offlineOperationPath)
-
-            val serverFileRemoteId = arguments?.getString(KEY_OFFLINE_SERVER_FILE_REMOTE_ID) ?: return
-            serverFile = fileDataStorageManager.getFileByRemoteId(serverFileRemoteId)
+            data = arguments.getParcelableArgument(CONFLICT_DATA, ConflictDialogData::class.java)
+            leftDataFile = arguments.getSerializableArgument(LEFT_FILE, File::class.java)
+            rightDataFile = arguments.getParcelableArgument(RIGHT_FILE, OCFile::class.java)
+            user = arguments.getParcelableArgument(USER, User::class.java)
         } else {
             Toast.makeText(context, "Failed to create conflict dialog", Toast.LENGTH_LONG).show()
         }
@@ -135,14 +123,8 @@ class ConflictsResolveDialog : DialogFragment(), Injectable {
 
     override fun onSaveInstanceState(outState: Bundle) {
         super.onSaveInstanceState(outState)
-
-        existingFile.logFileSize(TAG)
-        newFile.logFileSize(TAG)
-
         outState.run {
-            putParcelable(KEY_EXISTING_FILE, existingFile)
-            putSerializable(KEY_NEW_FILE, newFile)
-            putParcelable(KEY_USER, user)
+            putParcelable(CONFLICT_DATA, data)
         }
     }
 
@@ -157,13 +139,13 @@ class ConflictsResolveDialog : DialogFragment(), Injectable {
             .setPositiveButton(R.string.common_ok) { _: DialogInterface?, _: Int ->
                 val decision = when {
                     binding.leftCheckbox.isChecked && binding.rightCheckbox.isChecked ->
-                        if (offlineOperation != null && serverFile != null) Decision.KEEP_BOTH_FOLDER else Decision.KEEP_BOTH
+                        if (data?.folderName == null) Decision.KEEP_BOTH_FOLDER else Decision.KEEP_BOTH
 
                     binding.leftCheckbox.isChecked ->
-                        if (offlineOperation != null && serverFile != null) Decision.KEEP_OFFLINE_FOLDER else Decision.KEEP_LOCAL
+                        if (data?.folderName == null) Decision.KEEP_OFFLINE_FOLDER else Decision.KEEP_LOCAL
 
                     binding.rightCheckbox.isChecked ->
-                        if (offlineOperation != null && serverFile != null) Decision.KEEP_SERVER_FOLDER else Decision.KEEP_SERVER
+                        if (data?.folderName == null) Decision.KEEP_SERVER_FOLDER else Decision.KEEP_SERVER
 
                     else -> null
                 }
@@ -173,15 +155,9 @@ class ConflictsResolveDialog : DialogFragment(), Injectable {
             .setNegativeButton(R.string.common_cancel) { _: DialogInterface?, _: Int ->
                 listener?.conflictDecisionMade(Decision.CANCEL)
             }
+            .setTitle(data?.folderName)
 
-        if (existingFile != null && newFile != null) {
-            builder.setTitle(String.format(getString(R.string.conflict_file_headline), existingFile?.fileName))
-            setupUI()
-        } else if (offlineOperation != null && serverFile != null) {
-            builder.setTitle(getString(R.string.conflict_folder_headline))
-            setupUIForFolderConflict()
-        }
-
+        setupUI()
         setOnClickListeners()
 
         viewThemeUtils.dialog.colorMaterialAlertDialogBackground(binding.rightFileContainer.context, builder)
@@ -189,64 +165,62 @@ class ConflictsResolveDialog : DialogFragment(), Injectable {
         return builder.create()
     }
 
-    private fun setupUIForFolderConflict() {
+    private fun setupUI() {
         binding.run {
-            folderName.visibility = View.GONE
-            title.visibility = View.GONE
-            description.text = getString(R.string.conflict_message_description_for_folder)
-            leftCheckbox.text = getString(R.string.prefs_synced_folders_local_path_title)
-            rightCheckbox.text = getString(R.string.prefs_synced_folders_remote_path_title)
-
-            val folderIcon = MimeTypeUtil.getDefaultFolderIcon(requireContext(), viewThemeUtils)
-            leftThumbnail.setImageDrawable(folderIcon)
-            leftTimestamp.text =
-                DisplayUtils.getRelativeTimestamp(requireContext(), offlineOperation?.createdAt?.times(1000L) ?: 0)
-            leftFileSize.text = DisplayUtils.bytesToHumanReadable(0)
-
-            rightThumbnail.setImageDrawable(folderIcon)
-            rightTimestamp.text =
-                DisplayUtils.getRelativeTimestamp(requireContext(), serverFile?.modificationTimestamp ?: 0)
-            rightFileSize.text = DisplayUtils.bytesToHumanReadable(serverFile?.fileLength ?: 0)
+            data?.let {
+                val (leftData, rightData) = it.checkboxData
+
+                folderName.visibility = if (it.folderName == null) {
+                    View.GONE
+                } else {
+                    View.VISIBLE
+                }
+                folderName.text = it.folderName
+
+                title.visibility = if (it.title == null) {
+                    View.GONE
+                } else {
+                    View.VISIBLE
+                }
+                title.text = it.title
+
+                description.text = it.description
+
+                leftCheckbox.text = leftData.title
+                leftTimestamp.text = leftData.timestamp
+                leftFileSize.text = leftData.fileSize
+
+                rightCheckbox.text = rightData.title
+                rightTimestamp.text = rightData.timestamp
+                rightFileSize.text = rightData.fileSize
+
+                if (leftDataFile != null && rightDataFile != null && user != null) {
+                    setThumbnailsForFileConflicts()
+                } else {
+                    val folderIcon = MimeTypeUtil.getDefaultFolderIcon(requireContext(), viewThemeUtils)
+                    leftThumbnail.setImageDrawable(folderIcon)
+                    rightThumbnail.setImageDrawable(folderIcon)
+                }
+            }
         }
     }
 
-    private fun setupUI() {
-        val parentFile = existingFile?.remotePath?.let { File(it).parentFile }
-        if (parentFile != null) {
-            binding.folderName.text = String.format(getString(R.string.in_folder), parentFile.absolutePath)
-        } else {
-            binding.folderName.visibility = View.GONE
-        }
+    private fun setThumbnailsForFileConflicts() {
+        binding.leftThumbnail.tag = leftDataFile.hashCode()
+        binding.rightThumbnail.tag = rightDataFile.hashCode()
 
-        // set info for new file
-        binding.leftFileSize.text = newFile?.length()?.let { DisplayUtils.bytesToHumanReadable(it) }
-        binding.leftTimestamp.text = newFile?.lastModified()?.let { DisplayUtils.getRelativeTimestamp(context, it) }
-        binding.leftThumbnail.tag = newFile?.hashCode()
         LocalFileListAdapter.setThumbnail(
-            newFile,
+            leftDataFile,
             binding.leftThumbnail,
             context,
             viewThemeUtils
         )
 
-        // set info for existing file
-        binding.rightFileSize.text = existingFile?.fileLength?.let { DisplayUtils.bytesToHumanReadable(it) }
-        binding.rightTimestamp.text = existingFile?.modificationTimestamp?.let {
-            DisplayUtils.getRelativeTimestamp(
-                context,
-                it
-            )
-        }
-
-        binding.rightThumbnail.tag = existingFile?.fileId
         DisplayUtils.setThumbnail(
-            existingFile,
+            rightDataFile,
             binding.rightThumbnail,
             user,
-            FileDataStorageManager(
-                user,
-                requireContext().contentResolver
-            ),
+            fileDataStorageManager,
             asyncTasks,
             false,
             context,
@@ -260,20 +234,20 @@ class ConflictsResolveDialog : DialogFragment(), Injectable {
     private fun setOnClickListeners() {
         binding.run {
             val checkBoxClickListener = View.OnClickListener {
-                positiveButton?.isEnabled = leftCheckbox.isChecked || rightCheckbox.isChecked
+                positiveButton?.isEnabled = (leftCheckbox.isChecked || rightCheckbox.isChecked)
             }
 
             leftCheckbox.setOnClickListener(checkBoxClickListener)
             rightCheckbox.setOnClickListener(checkBoxClickListener)
 
             leftFileContainer.setOnClickListener {
-                leftCheckbox.isChecked = !leftCheckbox.isChecked
-                positiveButton?.isEnabled = leftCheckbox.isChecked || rightCheckbox.isChecked
+                leftCheckbox.toggle()
+                positiveButton?.isEnabled = (leftCheckbox.isChecked || rightCheckbox.isChecked)
             }
 
             rightFileContainer.setOnClickListener {
-                rightCheckbox.isChecked = !rightCheckbox.isChecked
-                positiveButton?.isEnabled = leftCheckbox.isChecked || rightCheckbox.isChecked
+                rightCheckbox.toggle()
+                positiveButton?.isEnabled = (leftCheckbox.isChecked || rightCheckbox.isChecked)
             }
         }
     }
@@ -300,33 +274,35 @@ class ConflictsResolveDialog : DialogFragment(), Injectable {
     override fun onStop() {
         super.onStop()
 
-        for (task in asyncTasks) {
-            task.cancel(true)
+        asyncTasks.forEach {
+            it.cancel(true)
             Log_OC.d(this, "cancel: abort get method directly")
-            task.getMethod?.abort()
+            it.getMethod?.abort()
         }
 
         asyncTasks.clear()
     }
 
     companion object {
-        private const val TAG = "ConflictsResolveDialog"
-        private const val KEY_NEW_FILE = "file"
-        private const val KEY_EXISTING_FILE = "ocfile"
-        private const val KEY_USER = "user"
-
-        private const val KEY_OFFLINE_SERVER_FILE_REMOTE_ID = "KEY_OFFLINE_SERVER_FILE_REMOTE_ID"
-        private const val KEY_OFFLINE_OPERATION_PATH = "KEY_OFFLINE_OPERATION_PATH"
+        private const val CONFLICT_DATA = "CONFLICT_DATA"
+        private const val LEFT_FILE = "KEY_LEFT_FILE"
+        private const val RIGHT_FILE = "KEY_RIGHT_FILE"
+        private const val USER = "user"
 
         @JvmStatic
-        fun newInstance(existingFile: OCFile?, newFile: OCFile, user: User?): ConflictsResolveDialog {
-            val file = File(newFile.storagePath)
-            file.logFileSize(TAG)
+        fun newInstance(
+            context: Context,
+            leftFile: OCFile,
+            rightFile: OCFile?,
+            user: User?
+        ): ConflictsResolveDialog {
+            val file = File(leftFile.storagePath)
 
             val bundle = Bundle().apply {
-                putParcelable(KEY_EXISTING_FILE, existingFile)
-                putSerializable(KEY_NEW_FILE, file)
-                putParcelable(KEY_USER, user)
+                putParcelable(CONFLICT_DATA, getConflictDataForFileConflict(file, rightFile, context))
+                putSerializable(LEFT_FILE, file)
+                putParcelable(RIGHT_FILE, rightFile)
+                putParcelable(USER, user)
             }
 
             return ConflictsResolveDialog().apply {
@@ -335,16 +311,73 @@ class ConflictsResolveDialog : DialogFragment(), Injectable {
         }
 
         @JvmStatic
-        fun newInstance(ocFile: OCFile, user: User, operationPath: String): ConflictsResolveDialog {
+        fun newInstance(
+            context: Context,
+            offlineOperation: OfflineOperationEntity,
+            rightFile: OCFile?,
+        ): ConflictsResolveDialog {
+            val conflictData = getConflictDataForFolderConflict(offlineOperation, rightFile, context)
+
             val bundle = Bundle().apply {
-                putString(KEY_OFFLINE_SERVER_FILE_REMOTE_ID, ocFile.remoteId)
-                putString(KEY_OFFLINE_OPERATION_PATH, operationPath)
-                putParcelable(KEY_USER, user)
+                putParcelable(CONFLICT_DATA, conflictData)
+                putParcelable(RIGHT_FILE, rightFile)
             }
 
             return ConflictsResolveDialog().apply {
                 arguments = bundle
             }
         }
+
+        @JvmStatic
+        fun getConflictDataForFolderConflict(
+            offlineOperation: OfflineOperationEntity,
+            rightFile: OCFile?,
+            context: Context
+        ): ConflictDialogData {
+            val folderName = null
+
+            val leftTitle = context.getString(R.string.prefs_synced_folders_local_path_title)
+            val leftTimestamp =
+                DisplayUtils.getRelativeTimestamp(context, offlineOperation.createdAt?.times(1000L) ?: 0)
+            val leftFileSize = DisplayUtils.bytesToHumanReadable(0)
+            val leftCheckBoxData = ConflictFileData(leftTitle, leftTimestamp.toString(), leftFileSize)
+
+            val rightTitle = context.getString(R.string.prefs_synced_folders_remote_path_title)
+            val rightTimestamp = DisplayUtils.getRelativeTimestamp(context, rightFile?.modificationTimestamp ?: 0)
+            val rightFileSize = DisplayUtils.bytesToHumanReadable(rightFile?.fileLength ?: 0)
+            val rightCheckBoxData = ConflictFileData(rightTitle, rightTimestamp.toString(), rightFileSize)
+
+            val title = context.getString(R.string.conflict_folder_headline)
+            val description = context.getString(R.string.conflict_message_description_for_folder)
+            return ConflictDialogData(folderName, title, description, Pair(leftCheckBoxData, rightCheckBoxData))
+        }
+
+        @JvmStatic
+        fun getConflictDataForFileConflict(
+            file: File,
+            rightFile: OCFile?,
+            context: Context
+        ): ConflictDialogData {
+            val parentFile = rightFile?.remotePath?.let { File(it).parentFile }
+            val folderName = if (parentFile != null) {
+                String.format(context.getString(R.string.in_folder), parentFile.absolutePath)
+            } else {
+                null
+            }
+
+            val leftTitle = context.getString(R.string.conflict_local_file)
+            val leftTimestamp = DisplayUtils.getRelativeTimestamp(context, file.lastModified())
+            val leftFileSize = DisplayUtils.bytesToHumanReadable(file.length())
+            val leftCheckBoxData = ConflictFileData(leftTitle, leftTimestamp.toString(), leftFileSize)
+
+            val rightTitle = context.getString(R.string.conflict_server_file)
+            val rightTimestamp = DisplayUtils.getRelativeTimestamp(context, rightFile?.modificationTimestamp ?: 0)
+            val rightFileSize = DisplayUtils.bytesToHumanReadable(rightFile?.fileLength ?: 0)
+            val rightCheckBoxData = ConflictFileData(rightTitle, rightTimestamp.toString(), rightFileSize)
+
+            val title = context.getString(R.string.choose_which_file)
+            val description = context.getString(R.string.conflict_message_description)
+            return ConflictDialogData(folderName, title, description, Pair(leftCheckBoxData, rightCheckBoxData))
+        }
     }
 }

+ 77 - 0
app/src/main/java/com/owncloud/android/ui/dialog/parcel/ConflictDialogData.kt

@@ -0,0 +1,77 @@
+/*
+ * Nextcloud - Android Client
+ *
+ * SPDX-FileCopyrightText: 2024 Alper Ozturk <alper.ozturk@nextcloud.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+package com.owncloud.android.ui.dialog.parcel
+
+import android.os.Parcel
+import android.os.Parcelable
+
+data class ConflictDialogData(
+    val folderName: String?,
+    val title: String?,
+    val description: String,
+    val checkboxData: Pair<ConflictFileData, ConflictFileData>
+) : Parcelable {
+    constructor(parcel: Parcel) : this(
+        parcel.readString() ?: "",
+        parcel.readString() ?: "",
+        parcel.readString() ?: "",
+        checkboxData = Pair(
+            parcel.readParcelableCompat(ConflictFileData::class.java.classLoader) ?: ConflictFileData("", "", ""),
+            parcel.readParcelableCompat(ConflictFileData::class.java.classLoader) ?: ConflictFileData("", "", "")
+        )
+    )
+
+    override fun writeToParcel(parcel: Parcel, flags: Int) {
+        parcel.writeString(folderName)
+        parcel.writeString(title)
+        parcel.writeString(description)
+        parcel.writeParcelable(checkboxData.first, flags)
+        parcel.writeParcelable(checkboxData.second, flags)
+    }
+
+    override fun describeContents(): Int = 0
+
+    companion object CREATOR : Parcelable.Creator<ConflictDialogData> {
+        override fun createFromParcel(parcel: Parcel): ConflictDialogData = ConflictDialogData(parcel)
+        override fun newArray(size: Int): Array<ConflictDialogData?> = arrayOfNulls(size)
+    }
+}
+
+data class ConflictFileData(
+    val title: String,
+    val timestamp: String,
+    val fileSize: String,
+) : Parcelable {
+    constructor(parcel: Parcel) : this(
+        parcel.readString() ?: "",
+        parcel.readString() ?: "",
+        parcel.readString() ?: ""
+    )
+
+    override fun writeToParcel(parcel: Parcel, flags: Int) {
+        parcel.writeString(title)
+        parcel.writeString(timestamp)
+        parcel.writeString(fileSize)
+    }
+
+    override fun describeContents(): Int = 0
+
+    companion object CREATOR : Parcelable.Creator<ConflictFileData> {
+        override fun createFromParcel(parcel: Parcel): ConflictFileData = ConflictFileData(parcel)
+        override fun newArray(size: Int): Array<ConflictFileData?> = arrayOfNulls(size)
+    }
+}
+
+inline fun <reified T : Parcelable> Parcel.readParcelableCompat(classLoader: ClassLoader?): T? {
+    return if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.TIRAMISU) {
+        readParcelable(classLoader, T::class.java)
+    } else {
+        @Suppress("DEPRECATION")
+        readParcelable(classLoader)
+    }
+}

+ 3 - 0
app/src/main/res/layout/conflict_resolve_dialog.xml

@@ -25,6 +25,7 @@
 
     <TextView
         android:id="@+id/title"
+        android:layout_marginTop="@dimen/standard_margin"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:text="@string/choose_which_file"
@@ -32,11 +33,13 @@
 
     <TextView
         android:id="@+id/description"
+        android:layout_marginTop="@dimen/standard_margin"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:text="@string/conflict_message_description" />
 
     <LinearLayout
+        android:layout_marginTop="@dimen/standard_margin"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:orientation="horizontal"