From b6eb89b4a6d2c58fb0171c541b9cd8762bf517fd Mon Sep 17 00:00:00 2001 From: Stefan Niedermann Date: Sat, 11 Apr 2020 17:43:58 +0200 Subject: #317 Adjust theme for each account Switches & min contrast ratio for main content area Signed-off-by: Stefan Niedermann --- .../it/niedermann/nextcloud/deck/Application.java | 6 +- .../nextcloud/deck/ui/BrandedActivity.java | 162 -------------------- .../nextcloud/deck/ui/BrandedDialogFragment.java | 44 ------ .../niedermann/nextcloud/deck/ui/MainActivity.java | 1 + .../nextcloud/deck/ui/about/AboutActivity.java | 2 +- .../deck/ui/about/AboutFragmentLicenseTab.java | 3 +- .../deck/ui/board/EditBoardDialogFragment.java | 4 +- .../accesscontrol/AccessControlDialogFragment.java | 4 +- .../nextcloud/deck/ui/branding/Branded.java | 7 + .../deck/ui/branding/BrandedActivity.java | 167 +++++++++++++++++++++ .../deck/ui/branding/BrandedDialogFragment.java | 44 ++++++ .../deck/ui/branding/BrandedSwitchPreference.java | 102 +++++++++++++ .../nextcloud/deck/ui/card/EditActivity.java | 2 +- .../card/attachments/CardAttachmentsFragment.java | 5 +- .../comments/CardCommentsEditDialogFragment.java | 4 +- .../ui/card/comments/CardCommentsFragment.java | 7 +- .../deck/ui/card/details/CardDetailsFragment.java | 6 +- .../ui/preparecreate/PrepareCreateActivity.java | 2 +- .../deck/ui/settings/SettingsActivity.java | 2 +- .../deck/ui/settings/SettingsFragment.java | 33 +++- .../deck/ui/stack/EditStackDialogFragment.java | 4 +- .../niedermann/nextcloud/deck/util/ColorUtil.java | 15 +- app/src/main/res/xml/settings.xml | 4 +- 23 files changed, 391 insertions(+), 239 deletions(-) delete mode 100644 app/src/main/java/it/niedermann/nextcloud/deck/ui/BrandedActivity.java delete mode 100644 app/src/main/java/it/niedermann/nextcloud/deck/ui/BrandedDialogFragment.java create mode 100644 app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/Branded.java create mode 100644 app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedActivity.java create mode 100644 app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedDialogFragment.java create mode 100644 app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedSwitchPreference.java (limited to 'app') diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/Application.java b/app/src/main/java/it/niedermann/nextcloud/deck/Application.java index 1f3bc2e7b..61a715f38 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/Application.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/Application.java @@ -10,6 +10,8 @@ import androidx.preference.PreferenceManager; import java.util.ArrayList; import java.util.List; +import it.niedermann.nextcloud.deck.ui.branding.Branded; + import static androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO; import static androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES; import static androidx.appcompat.app.AppCompatDelegate.setDefaultNightMode; @@ -23,10 +25,6 @@ public class Application extends android.app.Application { private static boolean enableBrand; - public interface Branded { - void applyBrand(@ColorInt int mainColor, @ColorInt int textColor); - } - @NonNull private static List brandedComponents = new ArrayList<>(); diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/BrandedActivity.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/BrandedActivity.java deleted file mode 100644 index 76b704f6c..000000000 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/BrandedActivity.java +++ /dev/null @@ -1,162 +0,0 @@ -package it.niedermann.nextcloud.deck.ui; - -import android.content.Context; -import android.content.res.ColorStateList; -import android.graphics.Color; -import android.graphics.ColorFilter; -import android.graphics.PorterDuff; -import android.graphics.drawable.Drawable; -import android.view.Menu; -import android.view.View; -import android.view.Window; -import android.widget.EditText; - -import androidx.annotation.CallSuper; -import androidx.annotation.ColorInt; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AppCompatActivity; -import androidx.appcompat.widget.Toolbar; -import androidx.core.graphics.drawable.DrawableCompat; - -import com.google.android.material.floatingactionbutton.FloatingActionButton; -import com.google.android.material.tabs.TabLayout; - -import it.niedermann.nextcloud.deck.Application; - -import static android.os.Build.VERSION.SDK_INT; -import static android.os.Build.VERSION_CODES.LOLLIPOP; -import static android.os.Build.VERSION_CODES.M; -import static it.niedermann.nextcloud.deck.util.ColorUtil.isColorDark; - -public abstract class BrandedActivity extends AppCompatActivity implements Application.Branded { - - /** - * Member variable needed for onCreateOptionsMenu()-callback - */ - @Nullable - @ColorInt - private Integer textColor = null; - - @Override - protected void onResume() { - super.onResume(); - Application.registerBrandedComponent(this, this); - } - - @Override - protected void onPause() { - super.onPause(); - Application.deregisterBrandedComponent(this); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - Application.deregisterBrandedComponent(this); - } - - @CallSuper - @Override - public void applyBrand(@ColorInt int mainColor, @ColorInt int textColor) { - this.textColor = textColor; - if (SDK_INT >= LOLLIPOP) { // Set status bar color - final Window window = getWindow(); - window.setStatusBarColor(mainColor); - if (SDK_INT >= M) { // Set icon and text color of status bar - final View decorView = window.getDecorView(); - if (isColorDark(mainColor)) { - int flags = decorView.getSystemUiVisibility(); - flags &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; - decorView.setSystemUiVisibility(flags); - } else { - decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); - } - } - } - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - if (this.textColor != null) { - for (int i = 0; i < menu.size(); i++) { - Drawable drawable = menu.getItem(i).getIcon(); - if (drawable != null) { - drawable = DrawableCompat.wrap(drawable); - DrawableCompat.setTint(drawable, this.textColor); - menu.getItem(i).setIcon(drawable); - } - } - } - return super.onCreateOptionsMenu(menu); - } - - protected static void applyBrandToPrimaryToolbar(@ColorInt int mainColor, @ColorInt int textColor, @NonNull Toolbar toolbar) { - toolbar.setBackgroundColor(mainColor); - toolbar.setTitleTextColor(textColor); - final Drawable overflowDrawable = toolbar.getOverflowIcon(); - if (overflowDrawable != null) { - overflowDrawable.setColorFilter(textColor, PorterDuff.Mode.SRC_ATOP); - toolbar.setOverflowIcon(overflowDrawable); - } - - final Drawable navigationDrawable = toolbar.getNavigationIcon(); - if (navigationDrawable != null) { - navigationDrawable.setColorFilter(textColor, PorterDuff.Mode.SRC_ATOP); - toolbar.setNavigationIcon(navigationDrawable); - } - } - - protected static void applyBrandToPrimaryTabLayout(@ColorInt int mainColor, @ColorInt int textColor, @NonNull TabLayout tabLayout) { - tabLayout.setBackgroundColor(mainColor); - tabLayout.setTabTextColors(textColor, textColor); - tabLayout.setTabIconTint(ColorStateList.valueOf(textColor)); - tabLayout.setSelectedTabIndicatorColor(textColor); - } - - public static void applyBrandToFAB(@ColorInt int mainColor, @ColorInt int textColor, @NonNull FloatingActionButton fab) { - fab.setSupportBackgroundTintList(ColorStateList.valueOf(mainColor)); - fab.setColorFilter(textColor); - } - - public static void applyBrandToEditText(@ColorInt int mainColor, @ColorInt int textColor, @NonNull EditText editText) { - final Drawable background = editText.getBackground(); - final ColorFilter oldColorFilter = DrawableCompat.getColorFilter(background); - final View.OnFocusChangeListener oldOnFocusChangeListener = editText.getOnFocusChangeListener(); - - @ColorInt final int finalMainColor = getColorDependingOnTheme(editText.getContext(), mainColor); - - final boolean isFocused = editText.isFocused(); - if (isFocused) { - editText.clearFocus(); - } - editText.setOnFocusChangeListener((v, hasFocus) -> { - if (hasFocus) { - background.setColorFilter(finalMainColor, PorterDuff.Mode.SRC_ATOP); - } else { - background.setColorFilter(oldColorFilter); - } - if (oldOnFocusChangeListener != null) { - oldOnFocusChangeListener.onFocusChange(v, hasFocus); - } - }); - if (isFocused) { - editText.requestFocus(); - } - } - - /** - * Since we may collide with dark theme in this area, we have to make sure that the color is visible depending on the background - */ - @ColorInt - public static int getColorDependingOnTheme(@NonNull Context context, @ColorInt int mainColor) { - final boolean isDarkTheme = Application.getAppTheme(context); - if (isDarkTheme && mainColor == Color.BLACK) { - return Color.WHITE; - } else if (!isDarkTheme && mainColor == Color.WHITE) { - return Color.BLACK; - } else { - return mainColor; - } - } -} diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/BrandedDialogFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/BrandedDialogFragment.java deleted file mode 100644 index 940787bf1..000000000 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/BrandedDialogFragment.java +++ /dev/null @@ -1,44 +0,0 @@ -package it.niedermann.nextcloud.deck.ui; - -import android.app.Dialog; -import android.content.DialogInterface; -import android.widget.Button; - -import androidx.annotation.CallSuper; -import androidx.appcompat.app.AlertDialog; -import androidx.fragment.app.DialogFragment; - -import it.niedermann.nextcloud.deck.Application; - -public class BrandedDialogFragment extends DialogFragment implements Application.Branded { - - @Override - public void onResume() { - super.onResume(); - Application.registerBrandedComponent(requireContext(), this); - } - - @Override - public void onPause() { - Application.deregisterBrandedComponent(this); - super.onPause(); - } - - @CallSuper - @Override - public void applyBrand(int mainColor, int textColor) { - Dialog dialog = requireDialog(); - if (dialog instanceof AlertDialog) { - AlertDialog alertDialog = (AlertDialog) dialog; - Button[] buttons = new Button[3]; - buttons[0] = alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); - buttons[1] = alertDialog.getButton(DialogInterface.BUTTON_NEGATIVE); - buttons[2] = alertDialog.getButton(DialogInterface.BUTTON_NEUTRAL); - for (Button button : buttons) { - if (button != null) { - button.setTextColor(BrandedActivity.getColorDependingOnTheme(button.getContext(), mainColor)); - } - } - } - } -} diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/MainActivity.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/MainActivity.java index 4aa2c5a78..0458bc4cc 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/MainActivity.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/MainActivity.java @@ -71,6 +71,7 @@ import it.niedermann.nextcloud.deck.persistence.sync.adapters.db.util.WrappedLiv import it.niedermann.nextcloud.deck.ui.about.AboutActivity; import it.niedermann.nextcloud.deck.ui.board.EditBoardDialogFragment; import it.niedermann.nextcloud.deck.ui.board.EditBoardListener; +import it.niedermann.nextcloud.deck.ui.branding.BrandedActivity; import it.niedermann.nextcloud.deck.ui.card.CardAdapter; import it.niedermann.nextcloud.deck.ui.card.EditActivity; import it.niedermann.nextcloud.deck.ui.exception.ExceptionHandler; diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/about/AboutActivity.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/about/AboutActivity.java index cfd735cc0..27dddabab 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/about/AboutActivity.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/about/AboutActivity.java @@ -15,7 +15,7 @@ import it.niedermann.nextcloud.deck.Application; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.ActivityAboutBinding; import it.niedermann.nextcloud.deck.model.Account; -import it.niedermann.nextcloud.deck.ui.BrandedActivity; +import it.niedermann.nextcloud.deck.ui.branding.BrandedActivity; import it.niedermann.nextcloud.deck.ui.exception.ExceptionHandler; import static it.niedermann.nextcloud.deck.ui.card.CardAdapter.BUNDLE_KEY_ACCOUNT; diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/about/AboutFragmentLicenseTab.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/about/AboutFragmentLicenseTab.java index 124b5d038..7a8a8f868 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/about/AboutFragmentLicenseTab.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/about/AboutFragmentLicenseTab.java @@ -13,10 +13,11 @@ import androidx.fragment.app.Fragment; import it.niedermann.nextcloud.deck.Application; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.FragmentAboutLicenseTabBinding; +import it.niedermann.nextcloud.deck.ui.branding.Branded; import static it.niedermann.nextcloud.deck.util.SpannableUtil.setTextWithURL; -public class AboutFragmentLicenseTab extends Fragment implements Application.Branded { +public class AboutFragmentLicenseTab extends Fragment implements Branded { private FragmentAboutLicenseTabBinding binding; diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/EditBoardDialogFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/EditBoardDialogFragment.java index 6ef540c3e..03b008f19 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/EditBoardDialogFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/EditBoardDialogFragment.java @@ -12,8 +12,8 @@ import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.DialogBoardCreateBinding; import it.niedermann.nextcloud.deck.model.full.FullBoard; import it.niedermann.nextcloud.deck.persistence.sync.SyncManager; -import it.niedermann.nextcloud.deck.ui.BrandedActivity; -import it.niedermann.nextcloud.deck.ui.BrandedDialogFragment; +import it.niedermann.nextcloud.deck.ui.branding.BrandedActivity; +import it.niedermann.nextcloud.deck.ui.branding.BrandedDialogFragment; import static it.niedermann.nextcloud.deck.Application.NO_BOARD_ID; diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/accesscontrol/AccessControlDialogFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/accesscontrol/AccessControlDialogFragment.java index 6617e6842..b525d686e 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/accesscontrol/AccessControlDialogFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/accesscontrol/AccessControlDialogFragment.java @@ -19,8 +19,8 @@ import it.niedermann.nextcloud.deck.model.AccessControl; import it.niedermann.nextcloud.deck.model.User; import it.niedermann.nextcloud.deck.model.full.FullBoard; import it.niedermann.nextcloud.deck.persistence.sync.SyncManager; -import it.niedermann.nextcloud.deck.ui.BrandedActivity; -import it.niedermann.nextcloud.deck.ui.BrandedDialogFragment; +import it.niedermann.nextcloud.deck.ui.branding.BrandedActivity; +import it.niedermann.nextcloud.deck.ui.branding.BrandedDialogFragment; import it.niedermann.nextcloud.deck.ui.card.UserAutoCompleteAdapter; public class AccessControlDialogFragment extends BrandedDialogFragment implements AccessControlChangedListener, AdapterView.OnItemClickListener { diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/Branded.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/Branded.java new file mode 100644 index 000000000..9d9d2d3db --- /dev/null +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/Branded.java @@ -0,0 +1,7 @@ +package it.niedermann.nextcloud.deck.ui.branding; + +import androidx.annotation.ColorInt; + +public interface Branded { + void applyBrand(@ColorInt int mainColor, @ColorInt int textColor); + } \ No newline at end of file diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedActivity.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedActivity.java new file mode 100644 index 000000000..817fd3ca2 --- /dev/null +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedActivity.java @@ -0,0 +1,167 @@ +package it.niedermann.nextcloud.deck.ui.branding; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.graphics.Color; +import android.graphics.ColorFilter; +import android.graphics.PorterDuff; +import android.graphics.drawable.Drawable; +import android.view.Menu; +import android.view.View; +import android.view.Window; +import android.widget.EditText; + +import androidx.annotation.CallSuper; +import androidx.annotation.ColorInt; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.Toolbar; +import androidx.core.graphics.drawable.DrawableCompat; + +import com.google.android.material.floatingactionbutton.FloatingActionButton; +import com.google.android.material.tabs.TabLayout; + +import it.niedermann.nextcloud.deck.Application; +import it.niedermann.nextcloud.deck.DeckLog; + +import static android.os.Build.VERSION.SDK_INT; +import static android.os.Build.VERSION_CODES.LOLLIPOP; +import static android.os.Build.VERSION_CODES.M; +import static it.niedermann.nextcloud.deck.util.ColorUtil.getContrastRatio; +import static it.niedermann.nextcloud.deck.util.ColorUtil.isColorDark; + +public abstract class BrandedActivity extends AppCompatActivity implements Branded { + + /** + * Member variable needed for onCreateOptionsMenu()-callback + */ + @Nullable + @ColorInt + private Integer textColor = null; + + @Override + protected void onResume() { + super.onResume(); + Application.registerBrandedComponent(this, this); + } + + @Override + protected void onPause() { + super.onPause(); + Application.deregisterBrandedComponent(this); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + Application.deregisterBrandedComponent(this); + } + + @CallSuper + @Override + public void applyBrand(@ColorInt int mainColor, @ColorInt int textColor) { + this.textColor = textColor; + if (SDK_INT >= LOLLIPOP) { // Set status bar color + final Window window = getWindow(); + window.setStatusBarColor(mainColor); + if (SDK_INT >= M) { // Set icon and text color of status bar + final View decorView = window.getDecorView(); + if (isColorDark(mainColor)) { + int flags = decorView.getSystemUiVisibility(); + flags &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; + decorView.setSystemUiVisibility(flags); + } else { + decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); + } + } + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + if (this.textColor != null) { + for (int i = 0; i < menu.size(); i++) { + Drawable drawable = menu.getItem(i).getIcon(); + if (drawable != null) { + drawable = DrawableCompat.wrap(drawable); + DrawableCompat.setTint(drawable, this.textColor); + menu.getItem(i).setIcon(drawable); + } + } + } + return super.onCreateOptionsMenu(menu); + } + + protected static void applyBrandToPrimaryToolbar(@ColorInt int mainColor, @ColorInt int textColor, @NonNull Toolbar toolbar) { + toolbar.setBackgroundColor(mainColor); + toolbar.setTitleTextColor(textColor); + final Drawable overflowDrawable = toolbar.getOverflowIcon(); + if (overflowDrawable != null) { + overflowDrawable.setColorFilter(textColor, PorterDuff.Mode.SRC_ATOP); + toolbar.setOverflowIcon(overflowDrawable); + } + + final Drawable navigationDrawable = toolbar.getNavigationIcon(); + if (navigationDrawable != null) { + navigationDrawable.setColorFilter(textColor, PorterDuff.Mode.SRC_ATOP); + toolbar.setNavigationIcon(navigationDrawable); + } + } + + protected static void applyBrandToPrimaryTabLayout(@ColorInt int mainColor, @ColorInt int textColor, @NonNull TabLayout tabLayout) { + tabLayout.setBackgroundColor(mainColor); + tabLayout.setTabTextColors(textColor, textColor); + tabLayout.setTabIconTint(ColorStateList.valueOf(textColor)); + tabLayout.setSelectedTabIndicatorColor(textColor); + } + + public static void applyBrandToFAB(@ColorInt int mainColor, @ColorInt int textColor, @NonNull FloatingActionButton fab) { + fab.setSupportBackgroundTintList(ColorStateList.valueOf(mainColor)); + fab.setColorFilter(textColor); + } + + public static void applyBrandToEditText(@ColorInt int mainColor, @ColorInt int textColor, @NonNull EditText editText) { + final Drawable background = editText.getBackground(); + final ColorFilter oldColorFilter = DrawableCompat.getColorFilter(background); + final View.OnFocusChangeListener oldOnFocusChangeListener = editText.getOnFocusChangeListener(); + + @ColorInt final int finalMainColor = getColorDependingOnTheme(editText.getContext(), mainColor); + + final boolean isFocused = editText.isFocused(); + if (isFocused) { + editText.clearFocus(); + } + editText.setOnFocusChangeListener((v, hasFocus) -> { + if (hasFocus) { + background.setColorFilter(finalMainColor, PorterDuff.Mode.SRC_ATOP); + } else { + background.setColorFilter(oldColorFilter); + } + if (oldOnFocusChangeListener != null) { + oldOnFocusChangeListener.onFocusChange(v, hasFocus); + } + }); + if (isFocused) { + editText.requestFocus(); + } + } + + /** + * Since we may collide with dark theme in this area, we have to make sure that the color is visible depending on the background + */ + @ColorInt + public static int getColorDependingOnTheme(@NonNull Context context, @ColorInt int mainColor) { + final boolean isDarkTheme = Application.getAppTheme(context); + if (isDarkTheme && getContrastRatio(mainColor, Color.BLACK) < 1.5f) { + DeckLog.info("[BRAND] Contrast ration between " + String.format("#%06X", (0xFFFFFF & mainColor)) + " and BLACK is " + getContrastRatio(mainColor, Color.BLACK) + ". Falling back to WHITE"); + return Color.WHITE; + } else if (!isDarkTheme && getContrastRatio(mainColor, Color.WHITE) < 1.5f) { + DeckLog.info("[BRAND] Contrast ration between " + String.format("#%06X", (0xFFFFFF & mainColor)) + " and WHITE is " + getContrastRatio(mainColor, Color.WHITE) + ". Falling back to BLACK"); + return Color.BLACK; + } else { + DeckLog.info("[BRAND] Contrast should be fine."); + return mainColor; + } + } +} diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedDialogFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedDialogFragment.java new file mode 100644 index 000000000..27263ab43 --- /dev/null +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedDialogFragment.java @@ -0,0 +1,44 @@ +package it.niedermann.nextcloud.deck.ui.branding; + +import android.app.Dialog; +import android.content.DialogInterface; +import android.widget.Button; + +import androidx.annotation.CallSuper; +import androidx.appcompat.app.AlertDialog; +import androidx.fragment.app.DialogFragment; + +import it.niedermann.nextcloud.deck.Application; + +public class BrandedDialogFragment extends DialogFragment implements Branded { + + @Override + public void onResume() { + super.onResume(); + Application.registerBrandedComponent(requireContext(), this); + } + + @Override + public void onPause() { + Application.deregisterBrandedComponent(this); + super.onPause(); + } + + @CallSuper + @Override + public void applyBrand(int mainColor, int textColor) { + Dialog dialog = requireDialog(); + if (dialog instanceof AlertDialog) { + AlertDialog alertDialog = (AlertDialog) dialog; + Button[] buttons = new Button[3]; + buttons[0] = alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); + buttons[1] = alertDialog.getButton(DialogInterface.BUTTON_NEGATIVE); + buttons[2] = alertDialog.getButton(DialogInterface.BUTTON_NEUTRAL); + for (Button button : buttons) { + if (button != null) { + button.setTextColor(BrandedActivity.getColorDependingOnTheme(button.getContext(), mainColor)); + } + } + } + } +} diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedSwitchPreference.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedSwitchPreference.java new file mode 100644 index 000000000..451cef7c9 --- /dev/null +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedSwitchPreference.java @@ -0,0 +1,102 @@ +package it.niedermann.nextcloud.deck.ui.branding; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Switch; + +import androidx.annotation.ColorInt; +import androidx.annotation.Nullable; +import androidx.core.graphics.drawable.DrawableCompat; +import androidx.preference.PreferenceViewHolder; +import androidx.preference.SwitchPreference; + +import static android.os.Build.VERSION.SDK_INT; +import static android.os.Build.VERSION_CODES.JELLY_BEAN; +import static it.niedermann.nextcloud.deck.ui.branding.BrandedActivity.getColorDependingOnTheme; + +public class BrandedSwitchPreference extends SwitchPreference implements Branded { + + @ColorInt + private Integer mainColor = null; + + @ColorInt + private Integer textColor = null; + + @Nullable + private Switch switchView; + + public BrandedSwitchPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + public BrandedSwitchPreference(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public BrandedSwitchPreference(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public BrandedSwitchPreference(Context context) { + super(context); + } + + @Override + public void onBindViewHolder(PreferenceViewHolder holder) { + super.onBindViewHolder(holder); + + if (holder.itemView instanceof ViewGroup) { + switchView = findSwitchWidget(holder.itemView); + if (mainColor != null && textColor != null) { + applyBrand(); + } + } + } + + @Override + public void applyBrand(@ColorInt int mainColor, @ColorInt int textColor) { + this.mainColor = mainColor; + this.textColor = textColor; + // onBindViewHolder is called after applyBrand, therefore we have to store the given values and apply them later. + applyBrand(); + } + + private void applyBrand() { + if (switchView != null && SDK_INT >= JELLY_BEAN) { + final int finalMainColor = getColorDependingOnTheme(getContext(), mainColor); + // int trackColor = Color.argb(77, Color.red(finalMainColor), Color.green(finalMainColor), Color.blue(finalMainColor)); + DrawableCompat.setTintList(switchView.getThumbDrawable(), ColorStateList.valueOf(finalMainColor)); + DrawableCompat.setTintList(switchView.getTrackDrawable(), ColorStateList.valueOf(finalMainColor)); + } + } + + /** + * Recursively go through view tree until we find an android.widget.Switch + * + * @param view Root view to start searching + * @return A Switch class or null + * @see Source + */ + private Switch findSwitchWidget(View view) { + if (view instanceof Switch) { + return (Switch) view; + } + if (view instanceof ViewGroup) { + ViewGroup viewGroup = (ViewGroup) view; + for (int i = 0; i < viewGroup.getChildCount(); i++) { + View child = viewGroup.getChildAt(i); + if (child instanceof ViewGroup) { + Switch result = findSwitchWidget(child); + if (result != null) return result; + } + if (child instanceof Switch) { + return (Switch) child; + } + } + } + return null; + } +} diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/EditActivity.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/EditActivity.java index b3070db4e..5a0a02bee 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/EditActivity.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/EditActivity.java @@ -29,7 +29,7 @@ import it.niedermann.nextcloud.deck.model.full.FullCard; import it.niedermann.nextcloud.deck.model.ocs.Version; import it.niedermann.nextcloud.deck.model.ocs.comment.DeckComment; import it.niedermann.nextcloud.deck.persistence.sync.SyncManager; -import it.niedermann.nextcloud.deck.ui.BrandedActivity; +import it.niedermann.nextcloud.deck.ui.branding.BrandedActivity; import it.niedermann.nextcloud.deck.ui.card.attachments.NewCardAttachmentHandler; import it.niedermann.nextcloud.deck.ui.card.comments.CommentAddedListener; import it.niedermann.nextcloud.deck.ui.card.comments.CommentDeletedListener; diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/CardAttachmentsFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/CardAttachmentsFragment.java index 2395e7df0..fa7a319ee 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/CardAttachmentsFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/CardAttachmentsFragment.java @@ -31,9 +31,10 @@ import it.niedermann.nextcloud.deck.databinding.FragmentCardEditTabAttachmentsBi import it.niedermann.nextcloud.deck.model.Account; import it.niedermann.nextcloud.deck.model.Attachment; import it.niedermann.nextcloud.deck.persistence.sync.SyncManager; +import it.niedermann.nextcloud.deck.ui.branding.Branded; import it.niedermann.nextcloud.deck.util.FileUtils; -import static it.niedermann.nextcloud.deck.ui.BrandedActivity.applyBrandToFAB; +import static it.niedermann.nextcloud.deck.ui.branding.BrandedActivity.applyBrandToFAB; import static it.niedermann.nextcloud.deck.ui.card.CardAdapter.BUNDLE_KEY_ACCOUNT_ID; import static it.niedermann.nextcloud.deck.ui.card.CardAdapter.BUNDLE_KEY_BOARD_ID; import static it.niedermann.nextcloud.deck.ui.card.CardAdapter.BUNDLE_KEY_CAN_EDIT; @@ -42,7 +43,7 @@ import static it.niedermann.nextcloud.deck.ui.card.CardAdapter.NO_LOCAL_ID; import static it.niedermann.nextcloud.deck.ui.card.attachments.CardAttachmentAdapter.VIEW_TYPE_DEFAULT; import static it.niedermann.nextcloud.deck.ui.card.attachments.CardAttachmentAdapter.VIEW_TYPE_IMAGE; -public class CardAttachmentsFragment extends Fragment implements AttachmentDeletedListener, AttachmentClickedListener, Application.Branded { +public class CardAttachmentsFragment extends Fragment implements AttachmentDeletedListener, AttachmentClickedListener, Branded { private FragmentCardEditTabAttachmentsBinding binding; private static final int REQUEST_CODE_ADD_ATTACHMENT = 1; diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/CardCommentsEditDialogFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/CardCommentsEditDialogFragment.java index 37bdb70b1..4dbc22603 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/CardCommentsEditDialogFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/CardCommentsEditDialogFragment.java @@ -17,8 +17,8 @@ import java.util.Objects; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.DialogAddCommentBinding; -import it.niedermann.nextcloud.deck.ui.BrandedActivity; -import it.niedermann.nextcloud.deck.ui.BrandedDialogFragment; +import it.niedermann.nextcloud.deck.ui.branding.BrandedActivity; +import it.niedermann.nextcloud.deck.ui.branding.BrandedDialogFragment; public class CardCommentsEditDialogFragment extends BrandedDialogFragment { private static final String BUNDLE_KEY_COMMENT_ID = "commentId"; diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/CardCommentsFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/CardCommentsFragment.java index cbb6e53f3..b202c97b0 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/CardCommentsFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/CardCommentsFragment.java @@ -17,16 +17,17 @@ import it.niedermann.nextcloud.deck.Application; import it.niedermann.nextcloud.deck.databinding.FragmentCardEditTabCommentsBinding; import it.niedermann.nextcloud.deck.model.ocs.comment.DeckComment; import it.niedermann.nextcloud.deck.persistence.sync.SyncManager; +import it.niedermann.nextcloud.deck.ui.branding.Branded; import static android.view.View.GONE; import static android.view.View.VISIBLE; -import static it.niedermann.nextcloud.deck.ui.BrandedActivity.applyBrandToEditText; -import static it.niedermann.nextcloud.deck.ui.BrandedActivity.applyBrandToFAB; +import static it.niedermann.nextcloud.deck.ui.branding.BrandedActivity.applyBrandToEditText; +import static it.niedermann.nextcloud.deck.ui.branding.BrandedActivity.applyBrandToFAB; import static it.niedermann.nextcloud.deck.ui.card.CardAdapter.BUNDLE_KEY_ACCOUNT_ID; import static it.niedermann.nextcloud.deck.ui.card.CardAdapter.BUNDLE_KEY_CAN_EDIT; import static it.niedermann.nextcloud.deck.ui.card.CardAdapter.BUNDLE_KEY_LOCAL_ID; -public class CardCommentsFragment extends Fragment implements Application.Branded, CommentEditedListener { +public class CardCommentsFragment extends Fragment implements Branded, CommentEditedListener { private FragmentCardEditTabCommentsBinding binding; private SyncManager syncManager; diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/details/CardDetailsFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/details/CardDetailsFragment.java index 1d168ea5a..df580bd46 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/details/CardDetailsFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/details/CardDetailsFragment.java @@ -47,6 +47,7 @@ import it.niedermann.nextcloud.deck.model.Label; import it.niedermann.nextcloud.deck.model.User; import it.niedermann.nextcloud.deck.model.full.FullCard; import it.niedermann.nextcloud.deck.persistence.sync.SyncManager; +import it.niedermann.nextcloud.deck.ui.branding.Branded; import it.niedermann.nextcloud.deck.ui.card.LabelAutoCompleteAdapter; import it.niedermann.nextcloud.deck.ui.card.UserAutoCompleteAdapter; import it.niedermann.nextcloud.deck.util.ColorUtil; @@ -56,15 +57,14 @@ import it.niedermann.nextcloud.deck.util.ViewUtil; import static android.app.DatePickerDialog.OnDateSetListener; import static it.niedermann.nextcloud.deck.persistence.sync.adapters.db.util.LiveDataHelper.observeOnce; -import static it.niedermann.nextcloud.deck.ui.BrandedActivity.applyBrandToEditText; +import static it.niedermann.nextcloud.deck.ui.branding.BrandedActivity.applyBrandToEditText; import static it.niedermann.nextcloud.deck.ui.card.CardAdapter.BUNDLE_KEY_ACCOUNT_ID; import static it.niedermann.nextcloud.deck.ui.card.CardAdapter.BUNDLE_KEY_BOARD_ID; import static it.niedermann.nextcloud.deck.ui.card.CardAdapter.BUNDLE_KEY_CAN_EDIT; import static it.niedermann.nextcloud.deck.ui.card.CardAdapter.BUNDLE_KEY_LOCAL_ID; import static it.niedermann.nextcloud.deck.ui.card.CardAdapter.NO_LOCAL_ID; -public class CardDetailsFragment extends Fragment implements Application.Branded, OnDateSetListener, - OnTimeSetListener { +public class CardDetailsFragment extends Fragment implements Branded, OnDateSetListener, OnTimeSetListener { private FragmentCardEditTabDetailsBinding binding; diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/preparecreate/PrepareCreateActivity.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/preparecreate/PrepareCreateActivity.java index 941cd0f31..03f1b6d92 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/preparecreate/PrepareCreateActivity.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/preparecreate/PrepareCreateActivity.java @@ -18,8 +18,8 @@ import it.niedermann.nextcloud.deck.model.Account; import it.niedermann.nextcloud.deck.model.Board; import it.niedermann.nextcloud.deck.model.full.FullStack; import it.niedermann.nextcloud.deck.persistence.sync.SyncManager; -import it.niedermann.nextcloud.deck.ui.BrandedActivity; import it.niedermann.nextcloud.deck.ui.ImportAccountActivity; +import it.niedermann.nextcloud.deck.ui.branding.BrandedActivity; import it.niedermann.nextcloud.deck.ui.card.EditActivity; import it.niedermann.nextcloud.deck.ui.exception.ExceptionHandler; diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/settings/SettingsActivity.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/settings/SettingsActivity.java index 0eaa7f1d4..32a9a1849 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/settings/SettingsActivity.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/settings/SettingsActivity.java @@ -7,7 +7,7 @@ import androidx.annotation.Nullable; import it.niedermann.nextcloud.deck.Application; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.ActivitySettingsBinding; -import it.niedermann.nextcloud.deck.ui.BrandedActivity; +import it.niedermann.nextcloud.deck.ui.branding.BrandedActivity; import it.niedermann.nextcloud.deck.ui.exception.ExceptionHandler; public class SettingsActivity extends BrandedActivity { diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/settings/SettingsFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/settings/SettingsFragment.java index cabdd1021..5ca6aab99 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/settings/SettingsFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/settings/SettingsFragment.java @@ -2,24 +2,31 @@ package it.niedermann.nextcloud.deck.ui.settings; import android.app.Activity; import android.os.Bundle; +import android.view.View; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.preference.ListPreference; import androidx.preference.Preference; import androidx.preference.PreferenceFragmentCompat; -import androidx.preference.SwitchPreference; import it.niedermann.nextcloud.deck.Application; import it.niedermann.nextcloud.deck.DeckLog; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.persistence.sync.SyncWorker; +import it.niedermann.nextcloud.deck.ui.branding.Branded; +import it.niedermann.nextcloud.deck.ui.branding.BrandedSwitchPreference; -public class SettingsFragment extends PreferenceFragmentCompat { +public class SettingsFragment extends PreferenceFragmentCompat implements Branded { + + private BrandedSwitchPreference wifiOnlyPref; + private BrandedSwitchPreference themePref; @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { setPreferencesFromResource(R.xml.settings, rootKey); - final SwitchPreference wifiOnlyPref = findPreference(getString(R.string.pref_key_wifi_only)); + wifiOnlyPref = findPreference(getString(R.string.pref_key_wifi_only)); if (wifiOnlyPref != null) { wifiOnlyPref.setOnPreferenceChangeListener((Preference preference, Object newValue) -> { Boolean syncOnWifiOnly = (Boolean) newValue; @@ -30,7 +37,7 @@ public class SettingsFragment extends PreferenceFragmentCompat { DeckLog.error("Could not find preference with key: \"" + getString(R.string.pref_key_wifi_only) + "\""); } - final SwitchPreference themePref = findPreference(getString(R.string.pref_key_dark_theme)); + themePref = findPreference(getString(R.string.pref_key_dark_theme)); if (themePref != null) { themePref.setOnPreferenceChangeListener((Preference preference, Object newValue) -> { Boolean darkTheme = (Boolean) newValue; @@ -54,4 +61,22 @@ public class SettingsFragment extends PreferenceFragmentCompat { DeckLog.error("Could not find preference with key: \"" + getString(R.string.pref_key_background_sync) + "\""); } } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + Application.registerBrandedComponent(requireContext(), this); + } + + @Override + public void onDestroy() { + Application.deregisterBrandedComponent(this); + super.onDestroy(); + } + + @Override + public void applyBrand(int mainColor, int textColor) { + wifiOnlyPref.applyBrand(mainColor, textColor); + themePref.applyBrand(mainColor, textColor); + } } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/stack/EditStackDialogFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/stack/EditStackDialogFragment.java index 4f8e70109..c0cd6ef0e 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/stack/EditStackDialogFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/stack/EditStackDialogFragment.java @@ -17,8 +17,8 @@ import java.util.Objects; import it.niedermann.nextcloud.deck.Application; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.DialogStackCreateBinding; -import it.niedermann.nextcloud.deck.ui.BrandedActivity; -import it.niedermann.nextcloud.deck.ui.BrandedDialogFragment; +import it.niedermann.nextcloud.deck.ui.branding.BrandedActivity; +import it.niedermann.nextcloud.deck.ui.branding.BrandedDialogFragment; import static it.niedermann.nextcloud.deck.Application.NO_STACK_ID; diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/util/ColorUtil.java b/app/src/main/java/it/niedermann/nextcloud/deck/util/ColorUtil.java index b085c1f21..c76cc1dd6 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/util/ColorUtil.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/util/ColorUtil.java @@ -22,11 +22,22 @@ public final class ColorUtil { } public static boolean isColorDark(@ColorInt int color) { + return getBrightness(color) < 200; + } + + public static int getBrightness(@ColorInt int color) { int[] rgb = {Color.red(color), Color.green(color), Color.blue(color)}; - int brightness = (int) Math.sqrt(rgb[0] * rgb[0] * .241 + rgb[1] + return (int) Math.sqrt(rgb[0] * rgb[0] * .241 + rgb[1] * rgb[1] * .691 + rgb[2] * rgb[2] * .068); + } + + public static double getContrastRatio(@ColorInt int colorOne, @ColorInt int colorTwo) { + int brightnessOne = getBrightness(colorOne); + int brightnessTwo = getBrightness(colorTwo); - return brightness < 200; + return (brightnessOne > brightnessTwo) + ? (double) brightnessOne / (double) brightnessTwo + : (double) brightnessTwo / (double) brightnessOne; } } diff --git a/app/src/main/res/xml/settings.xml b/app/src/main/res/xml/settings.xml index 6a24dce9e..69249ec38 100644 --- a/app/src/main/res/xml/settings.xml +++ b/app/src/main/res/xml/settings.xml @@ -1,7 +1,7 @@ - -