From 00eb62498cc5a7f77e8f80272ea729d2de45b57e Mon Sep 17 00:00:00 2001 From: Phillip9933 Date: Wed, 2 Sep 2026 20:25:33 +0900 Subject: [PATCH 01/23] feat: update base theme to DayNight for dark mode support --- opencloudApp/src/main/res/values/styles.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opencloudApp/src/main/res/values/styles.xml b/opencloudApp/src/main/res/values/styles.xml index cdf9a08a29..57c50e973b 100644 --- a/opencloudApp/src/main/res/values/styles.xml +++ b/opencloudApp/src/main/res/values/styles.xml @@ -25,7 +25,7 @@ - @@ -190,6 +190,7 @@ diff --git a/opencloudApp/src/main/res/xml/settings.xml b/opencloudApp/src/main/res/xml/settings.xml index 7f7af84e22..c0806bfeb8 100644 --- a/opencloudApp/src/main/res/xml/settings.xml +++ b/opencloudApp/src/main/res/xml/settings.xml @@ -21,7 +21,7 @@ diff --git a/opencloudApp/src/test/java/eu/opencloud/android/presentation/settings/AppearanceModeTest.kt b/opencloudApp/src/test/java/eu/opencloud/android/presentation/settings/AppearanceModeTest.kt index 249c2b9aaf..c809d8eb3d 100644 --- a/opencloudApp/src/test/java/eu/opencloud/android/presentation/settings/AppearanceModeTest.kt +++ b/opencloudApp/src/test/java/eu/opencloud/android/presentation/settings/AppearanceModeTest.kt @@ -44,4 +44,4 @@ class AppearanceModeTest { assertEquals("LIGHT", AppearanceMode.fromPreferenceValue("LIGHT").name) assertEquals("DARK", AppearanceMode.fromPreferenceValue("DARK").name) } -} \ No newline at end of file +} From 11a5b7de733d4e46f77bea9e289e347d955d4db6 Mon Sep 17 00:00:00 2001 From: phillip9933 <19641186+phillip9933@users.noreply.github.com> Date: Sat, 3 Oct 2026 00:46:23 +0900 Subject: [PATCH 22/23] fix: make space placeholders work in dark mode --- opencloudApp/src/main/res/drawable/ic_spaces_placeholder.xml | 2 +- opencloudApp/src/main/res/values-night/colors.xml | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/opencloudApp/src/main/res/drawable/ic_spaces_placeholder.xml b/opencloudApp/src/main/res/drawable/ic_spaces_placeholder.xml index 91975e389a..a46199225c 100644 --- a/opencloudApp/src/main/res/drawable/ic_spaces_placeholder.xml +++ b/opencloudApp/src/main/res/drawable/ic_spaces_placeholder.xml @@ -5,6 +5,6 @@ android:pivotY="12" android:scaleX="0.25" android:scaleY="0.25"> - + diff --git a/opencloudApp/src/main/res/values-night/colors.xml b/opencloudApp/src/main/res/values-night/colors.xml index 18db5eb8f2..1ec2022fac 100644 --- a/opencloudApp/src/main/res/values-night/colors.xml +++ b/opencloudApp/src/main/res/values-night/colors.xml @@ -9,4 +9,5 @@ #A5ABAF #FFFFFF #31515D - \ No newline at end of file + @color/theme_color_surface + From 4cfa8b8cd3a2e4c457b2a76b130a27d2f3625a27 Mon Sep 17 00:00:00 2001 From: phillip9933 <19641186+phillip9933@users.noreply.github.com> Date: Sat, 10 Oct 2026 23:56:16 +0900 Subject: [PATCH 23/23] fix: prevent selector inflation crashes and finish dark mode styling --- .../opencloud/android/ui/ListSelectorTest.kt | 110 ++++++++++++++++++ .../android/extensions/ActivityExt.kt | 2 +- .../common/BottomSheetFragmentItemView.kt | 2 +- .../ui/activity/FileDisplayActivity.kt | 4 +- .../src/main/res/drawable/list_selector.xml | 6 +- .../res/layout-sw600dp/whats_new_activity.xml | 2 +- .../src/main/res/layout/grid_item.xml | 4 +- .../src/main/res/values-night/colors.xml | 1 + opencloudApp/src/main/res/values/colors.xml | 1 + 9 files changed, 123 insertions(+), 9 deletions(-) create mode 100644 opencloudApp/src/androidTest/java/eu/opencloud/android/ui/ListSelectorTest.kt diff --git a/opencloudApp/src/androidTest/java/eu/opencloud/android/ui/ListSelectorTest.kt b/opencloudApp/src/androidTest/java/eu/opencloud/android/ui/ListSelectorTest.kt new file mode 100644 index 0000000000..278fc352c0 --- /dev/null +++ b/opencloudApp/src/androidTest/java/eu/opencloud/android/ui/ListSelectorTest.kt @@ -0,0 +1,110 @@ +/** + * 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 . + */ + +package eu.opencloud.android.ui + +import android.content.Context +import android.content.res.Configuration +import android.graphics.drawable.ColorDrawable +import android.graphics.drawable.StateListDrawable +import android.view.LayoutInflater +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 eu.opencloud.android.R +import eu.opencloud.android.presentation.common.BottomSheetFragmentItemView +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertTrue +import org.junit.Test + +class ListSelectorTest { + + @Test + fun selectorLoadsWithoutThemeInLightAndDarkModes() { + for (nightMode in listOf(Configuration.UI_MODE_NIGHT_NO, Configuration.UI_MODE_NIGHT_YES)) { + val context = contextForNightMode(nightMode) + val selector = context.resources.getDrawable(R.drawable.list_selector, null) as StateListDrawable + // Remove the transition so the current drawable can be checked immediately. + selector.setExitFadeDuration(0) + for (state in listOf(intArrayOf(), intArrayOf(android.R.attr.state_pressed), intArrayOf(android.R.attr.state_focused))) { + selector.state = state + val expectedColor = if (state.isEmpty()) R.color.theme_color_background else R.color.theme_color_selection + assertEquals(context.getColor(expectedColor), (selector.current as ColorDrawable).color) + } + } + } + + @Test + fun gridRowsInflateInLightAndDarkModes() { + assertRowInflates(R.layout.grid_item) + } + + @Test + fun uploadRowsInflateInLightAndDarkModes() { + assertRowInflates(R.layout.uploader_list_item_layout) + } + + @Test + fun selectedSortRowStaysReadableInLightAndDarkModes() { + InstrumentationRegistry.getInstrumentation().runOnMainSync { + for (nightMode in listOf(Configuration.UI_MODE_NIGHT_NO, Configuration.UI_MODE_NIGHT_YES)) { + val context = ContextThemeWrapper(contextForNightMode(nightMode), R.style.Theme_openCloud) + val sheet = LayoutInflater.from(context).inflate(R.layout.sort_bottom_sheet_fragment, null) + val row = sheet.findViewById(R.id.sort_by_name) + row.setSelected(R.drawable.ic_baseline_arrow_upward) + val textColor = row.findViewById(R.id.item_title).currentTextColor + assertTrue(ColorUtils.calculateContrast(textColor, context.getColor(R.color.theme_color_surface)) >= 4.5) + } + } + } + + @Test + fun gridSharingIndicatorsUseReadableTintsInLightAndDarkModes() { + InstrumentationRegistry.getInstrumentation().runOnMainSync { + for (nightMode in listOf(Configuration.UI_MODE_NIGHT_NO, Configuration.UI_MODE_NIGHT_YES)) { + val context = ContextThemeWrapper(contextForNightMode(nightMode), R.style.Theme_openCloud) + val row = LayoutInflater.from(context).inflate(R.layout.grid_item, null) + for (id in listOf(R.id.shared_via_users_icon, R.id.shared_by_link_icon)) { + val tint = row.findViewById(id).imageTintList + assertNotNull(tint) + assertTrue(ColorUtils.calculateContrast(tint!!.defaultColor, context.getColor(R.color.theme_color_surface)) >= 3.0) + } + } + } + } + + private fun assertRowInflates(layout: Int) { + InstrumentationRegistry.getInstrumentation().runOnMainSync { + for (nightMode in listOf(Configuration.UI_MODE_NIGHT_NO, Configuration.UI_MODE_NIGHT_YES)) { + val context = ContextThemeWrapper(contextForNightMode(nightMode), R.style.Theme_openCloud) + assertNotNull(LayoutInflater.from(context).inflate(layout, null).background) + } + } + } + + private fun contextForNightMode(nightMode: Int): Context { + val context = InstrumentationRegistry.getInstrumentation().targetContext + val configuration = Configuration(context.resources.configuration).apply { + uiMode = (uiMode and Configuration.UI_MODE_NIGHT_MASK.inv()) or nightMode + } + return context.createConfigurationContext(configuration) + } +} diff --git a/opencloudApp/src/main/java/eu/opencloud/android/extensions/ActivityExt.kt b/opencloudApp/src/main/java/eu/opencloud/android/extensions/ActivityExt.kt index 2c9a6171bf..38559d812a 100644 --- a/opencloudApp/src/main/java/eu/opencloud/android/extensions/ActivityExt.kt +++ b/opencloudApp/src/main/java/eu/opencloud/android/extensions/ActivityExt.kt @@ -368,7 +368,7 @@ fun Activity.openFeedbackDialog() { val getInContactDescriptionTextView = TextView(this).apply { text = spannableString - setTextColor(getColor(android.R.color.black)) + setTextColor(getColor(R.color.theme_color_on_surface)) setTextSize(TypedValue.COMPLEX_UNIT_SP, 16f) movementMethod = LinkMovementMethod.getInstance() } diff --git a/opencloudApp/src/main/java/eu/opencloud/android/presentation/common/BottomSheetFragmentItemView.kt b/opencloudApp/src/main/java/eu/opencloud/android/presentation/common/BottomSheetFragmentItemView.kt index 0ae8c0d762..5e61e25250 100644 --- a/opencloudApp/src/main/java/eu/opencloud/android/presentation/common/BottomSheetFragmentItemView.kt +++ b/opencloudApp/src/main/java/eu/opencloud/android/presentation/common/BottomSheetFragmentItemView.kt @@ -73,7 +73,7 @@ class BottomSheetFragmentItemView @JvmOverloads constructor( fun setSelected(iconAdditional: Int) { itemAdditionalIcon = ContextCompat.getDrawable(context, iconAdditional) - val selectedColor = ContextCompat.getColor(context, R.color.primary) + val selectedColor = ContextCompat.getColor(context, R.color.theme_color_selected_foreground) binding.itemIcon.setColorFilter(selectedColor) binding.itemTitle.setTextColor(selectedColor) binding.itemAdditionalIcon.setColorFilter(selectedColor) diff --git a/opencloudApp/src/main/java/eu/opencloud/android/ui/activity/FileDisplayActivity.kt b/opencloudApp/src/main/java/eu/opencloud/android/ui/activity/FileDisplayActivity.kt index a4c9c95f16..9515048ae1 100644 --- a/opencloudApp/src/main/java/eu/opencloud/android/ui/activity/FileDisplayActivity.kt +++ b/opencloudApp/src/main/java/eu/opencloud/android/ui/activity/FileDisplayActivity.kt @@ -1449,13 +1449,13 @@ class FileDisplayActivity : FileActivity(), val messageTextView = TextView(this).apply { text = message setPadding(0, 70, 0, 30) - setTextColor(ContextCompat.getColor(this@FileDisplayActivity, android.R.color.black)) + setTextColor(ContextCompat.getColor(this@FileDisplayActivity, R.color.theme_color_on_surface)) setTextSize(TypedValue.COMPLEX_UNIT_SP, 16f) } val urlTextView = TextView(this).apply { text = urlFormat - setTextColor(ContextCompat.getColor(this@FileDisplayActivity, android.R.color.black)) + setTextColor(ContextCompat.getColor(this@FileDisplayActivity, R.color.theme_color_on_surface)) setTextSize(TypedValue.COMPLEX_UNIT_SP, 16f) } val scrollView = ScrollView(this).apply { diff --git a/opencloudApp/src/main/res/drawable/list_selector.xml b/opencloudApp/src/main/res/drawable/list_selector.xml index 93fddea71a..f532a8bb09 100644 --- a/opencloudApp/src/main/res/drawable/list_selector.xml +++ b/opencloudApp/src/main/res/drawable/list_selector.xml @@ -20,8 +20,8 @@ - - - + + + diff --git a/opencloudApp/src/main/res/layout-sw600dp/whats_new_activity.xml b/opencloudApp/src/main/res/layout-sw600dp/whats_new_activity.xml index c7591ef0da..fe665a4105 100644 --- a/opencloudApp/src/main/res/layout-sw600dp/whats_new_activity.xml +++ b/opencloudApp/src/main/res/layout-sw600dp/whats_new_activity.xml @@ -36,7 +36,7 @@ android:layout_gravity="center_vertical|center_horizontal" android:layout_weight="1" android:text="@string/welcome_feature_skip_button" - android:textColor="@color/white" + android:textColor="?attr/colorOnPrimary" android:textSize="14sp" /> @@ -64,6 +65,7 @@ android:layout_width="15dp" android:layout_height="15dp" android:src="@drawable/ic_shared_by_link" + android:tint="?attr/colorOnSurfaceVariant" android:visibility="gone" tools:visibility="visible" /> diff --git a/opencloudApp/src/main/res/values-night/colors.xml b/opencloudApp/src/main/res/values-night/colors.xml index 1ec2022fac..37d5563faf 100644 --- a/opencloudApp/src/main/res/values-night/colors.xml +++ b/opencloudApp/src/main/res/values-night/colors.xml @@ -8,6 +8,7 @@ #E0E0E0 #A5ABAF #FFFFFF + #9AD6E6 #31515D @color/theme_color_surface diff --git a/opencloudApp/src/main/res/values/colors.xml b/opencloudApp/src/main/res/values/colors.xml index d4d2999a54..b024f60a98 100644 --- a/opencloudApp/src/main/res/values/colors.xml +++ b/opencloudApp/src/main/res/values/colors.xml @@ -31,6 +31,7 @@ #525757 #707575 #FFFFFF + @color/primary #D6E6EC