Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/**
* openCloud Android client application
*
* Copyright (C) 2026 openCloud GmbH.
*
* 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 <http://www.gnu.org/licenses/>.
*/

package eu.opencloud.android.ui

import android.content.res.Configuration
import android.view.LayoutInflater
import android.widget.EditText
import android.widget.ImageView
import android.widget.TextView
import androidx.appcompat.view.ContextThemeWrapper
import androidx.core.graphics.ColorUtils
import androidx.test.platform.app.InstrumentationRegistry
import com.andrognito.patternlockview.PatternLockView
import eu.opencloud.android.R
import eu.opencloud.android.presentation.files.createshortcut.CreateShortcutDialogFragment
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
import org.junit.Test

class DarkModeTextContrastTest {

@Test
fun fileDetailsTextIsReadableInBothModes() {
assertLayoutTextContrast(
R.layout.file_details_fragment,
listOf(R.id.fdname, R.id.fdType, R.id.fdSize, R.id.fdModified, R.id.fdCreated, R.id.fdLastSync, R.id.fdPath)
)
}

@Test
fun uploadHeadingIsReadableInBothModes() {
assertLayoutTextContrast(R.layout.upload_list_group, listOf(R.id.uploadListGroupName))
}

@Test
fun passwordActionsAreReadableInBothModes() {
assertLayoutTextContrast(R.layout.share_public_dialog, listOf(R.id.generatePasswordButton, R.id.copyPasswordButton))
}

@Test
fun shortcutActionsStayReadableAfterValidInput() {
InstrumentationRegistry.getInstrumentation().runOnMainSync {
for (nightMode in listOf(Configuration.UI_MODE_NIGHT_NO, Configuration.UI_MODE_NIGHT_YES)) {
val context = contextForMode(nightMode)
val fragment = CreateShortcutDialogFragment()
val view = fragment.onCreateView(LayoutInflater.from(context), null, null)
fragment.onViewCreated(view, null)
view.findViewById<EditText>(R.id.create_shortcut_dialog_name_file_value).setText("notes")
view.findViewById<EditText>(R.id.create_shortcut_dialog_url_value).setText("https://example.com")
val button = view.findViewById<TextView>(R.id.createButton)
assertTrue(button.isEnabled)
val contrast = ColorUtils.calculateContrast(button.currentTextColor, context.getColor(R.color.theme_color_background))
assertTrue("Create shortcut night=$nightMode contrast=$contrast", contrast >= 4.5)
}
}
}

@Test
fun fileDetailsSpaceIconHasReadableTint() {
InstrumentationRegistry.getInstrumentation().runOnMainSync {
for (nightMode in listOf(Configuration.UI_MODE_NIGHT_NO, Configuration.UI_MODE_NIGHT_YES)) {
val context = contextForMode(nightMode)
val view = LayoutInflater.from(context).inflate(R.layout.file_details_fragment, null)
val tint = view.findViewById<ImageView>(R.id.fdIconSpace).imageTintList
assertNotNull(tint)
val contrast = ColorUtils.calculateContrast(tint!!.defaultColor, context.getColor(R.color.theme_color_background))
assertTrue("Spaces icon contrast=$contrast", contrast >= 3.0)
}
}
}

@Test
fun patternDotsAreVisibleOnPhonesAndTablets() {
InstrumentationRegistry.getInstrumentation().runOnMainSync {
for (nightMode in listOf(Configuration.UI_MODE_NIGHT_NO, Configuration.UI_MODE_NIGHT_YES)) {
for (width in listOf(360, 800)) {
val context = contextForMode(nightMode, width)
val view = LayoutInflater.from(context).inflate(R.layout.activity_pattern_lock, null)
val pattern = view.findViewById<PatternLockView>(R.id.pattern_lock_view)
for (color in listOf(pattern.normalStateColor, pattern.correctStateColor, pattern.wrongStateColor)) {
val contrast = ColorUtils.calculateContrast(color, context.getColor(R.color.theme_color_background))
assertTrue("Pattern width=$width night=$nightMode contrast=$contrast", contrast >= 3.0)
}
}
}
}
}

private fun contextForMode(nightMode: Int, width: Int = 360): ContextThemeWrapper {
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
val configuration = Configuration(appContext.resources.configuration).apply {
uiMode = (uiMode and Configuration.UI_MODE_NIGHT_MASK.inv()) or nightMode
screenWidthDp = width
smallestScreenWidthDp = width
}
return ContextThemeWrapper(appContext.createConfigurationContext(configuration), R.style.Theme_openCloud)
}

private fun assertLayoutTextContrast(layout: Int, textIds: List<Int>) {
InstrumentationRegistry.getInstrumentation().runOnMainSync {
for (nightMode in listOf(Configuration.UI_MODE_NIGHT_NO, Configuration.UI_MODE_NIGHT_YES)) {
val context = contextForMode(nightMode)
val view = LayoutInflater.from(context).inflate(layout, null)
for (id in textIds) {
val text = view.findViewById<TextView>(id)
text.isEnabled = true
val background = context.getColor(R.color.theme_color_background)
val contrast = ColorUtils.calculateContrast(text.currentTextColor, background)
assertTrue("${context.resources.getResourceEntryName(id)} night=$nightMode contrast=$contrast", contrast >= 4.5)
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,8 @@ class CreateShortcutDialogFragment : DialogFragment() {
)
dialog?.dismiss()
}
setTextColor(resources.getColor(R.color.primary_button_background_color, null))
} else {
setOnClickListener(null)
setTextColor(resources.getColor(R.color.grey, null))
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -494,10 +494,6 @@ class PublicShareDialogFragment : DialogFragment() {
private fun enableCopyPasswordButton(enable: Boolean) {
binding.copyPasswordButton.apply {
isEnabled = enable
setTextColor(
if (enable) resources.getColor(R.color.primary_button_background_color, null)
else resources.getColor(R.color.grey, null)
)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,10 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusable="true"
app:correctStateColor="@color/primary"
app:correctStateColor="@color/theme_color_selected_foreground"
app:dotCount="3"
app:normalStateColor="@color/primary"
app:wrongStateColor="@color/primary">
app:normalStateColor="@color/theme_color_selected_foreground"
app:wrongStateColor="@color/theme_color_selected_foreground">

<requestFocus />

Expand Down
6 changes: 3 additions & 3 deletions opencloudApp/src/main/res/layout/activity_pattern_lock.xml
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,10 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusable="true"
app:correctStateColor="@color/primary"
app:correctStateColor="@color/theme_color_selected_foreground"
app:dotCount="3"
app:normalStateColor="@color/primary"
app:wrongStateColor="@color/primary">
app:normalStateColor="@color/theme_color_selected_foreground"
app:wrongStateColor="@color/theme_color_selected_foreground">

<requestFocus />

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/createButton"
android:enabled="false"
android:textColor="?attr/colorOnSurfaceVariant"
android:textColor="@color/dialog_button_text"
style="?android:attr/borderlessButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
Expand Down
15 changes: 8 additions & 7 deletions opencloudApp/src/main/res/layout/file_details_fragment.xml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
android:layout_marginBottom="30dp"
android:gravity="center"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textColor="@color/action_mode_status_bar_background"
android:textColor="?attr/colorOnSurface"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
Expand Down Expand Up @@ -139,7 +139,7 @@
android:gravity="end"
android:text=""
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
android:textColor="@color/action_mode_status_bar_background"
android:textColor="?attr/colorOnSurface"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/fdTypeLabel"
app:layout_constraintTop_toBottomOf="@id/divider"
Expand All @@ -163,7 +163,7 @@
android:gravity="end"
android:text=""
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
android:textColor="@color/action_mode_status_bar_background"
android:textColor="?attr/colorOnSurface"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/fdSizeLabel"
app:layout_constraintTop_toBottomOf="@id/fdType"
Expand Down Expand Up @@ -200,7 +200,7 @@
android:visibility="gone"
android:text=""
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
android:textColor="@color/action_mode_status_bar_background"
android:textColor="?attr/colorOnSurface"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/fdModifiedLabel"
app:layout_constraintTop_toBottomOf="@id/divider2"
Expand All @@ -227,7 +227,7 @@
android:gravity="end"
android:visibility="gone"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
android:textColor="@color/action_mode_status_bar_background"
android:textColor="?attr/colorOnSurface"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/fdCreatedLabel"
app:layout_constraintTop_toBottomOf="@id/fdModified"
Expand All @@ -254,7 +254,7 @@
android:gravity="end"
android:visibility="gone"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
android:textColor="@color/action_mode_status_bar_background"
android:textColor="?attr/colorOnSurface"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/fdLastSyncLabel"
app:layout_constraintTop_toBottomOf="@id/fdCreated"
Expand Down Expand Up @@ -288,7 +288,7 @@
android:layout_marginTop="12dp"
android:gravity="end"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
android:textColor="@color/action_mode_status_bar_background"
android:textColor="?attr/colorOnSurface"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/fdPathLabel"
app:layout_constraintTop_toBottomOf="@id/divider3"
Expand Down Expand Up @@ -329,6 +329,7 @@
android:layout_height="@dimen/file_indicator_pin_size_grid"
android:background="@android:color/transparent"
android:src="@drawable/ic_spaces"
android:tint="?attr/colorOnSurfaceVariant"
android:scaleType="fitCenter"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/fdSpace"
Expand Down
4 changes: 2 additions & 2 deletions opencloudApp/src/main/res/layout/share_public_dialog.xml
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@
android:layout_height="wrap_content"
android:text="@string/share_via_link_generate_password_button"
android:backgroundTint="@color/transparent"
android:textColor="@color/primary_button_background_color"
android:textColor="@color/dialog_button_text"
style="?android:attr/borderlessButtonStyle"/>

<androidx.appcompat.widget.AppCompatButton
Expand All @@ -137,7 +137,7 @@
android:layout_height="wrap_content"
android:text="@string/share_via_link_copy_password_button"
android:backgroundTint="@color/transparent"
android:textColor="?attr/colorOnSurfaceVariant"
android:textColor="@color/dialog_button_text"
android:enabled="false"
style="?android:attr/borderlessButtonStyle" />

Expand Down
2 changes: 1 addition & 1 deletion opencloudApp/src/main/res/layout/upload_list_group.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
android:ellipsize="middle"
android:paddingStart="@dimen/activity_horizontal_margin"
android:showDividers="none"
android:textColor="@color/color_accent"
android:textColor="?attr/colorOnSurfaceVariant"
app:layout_constraintBottom_toTopOf="@id/uploadListGroupButtonClear"
app:layout_constraintEnd_toStartOf="@id/textViewFileCount"
app:layout_constraintStart_toStartOf="parent"
Expand Down
Loading