diff options
68 files changed, 772 insertions, 883 deletions
diff --git a/app/build.gradle b/app/build.gradle index 675ee7bcd..70f3363ec 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -100,6 +100,7 @@ dependencies { // Single-Sign-On implementation 'com.github.nextcloud:Android-SingleSignOn:0.6.1' + implementation 'com.github.nextcloud:android-common:0.4.0' implementation 'com.github.stefan-niedermann.nextcloud-commons:sso-glide:1.6.4' implementation 'com.github.stefan-niedermann.nextcloud-commons:exception:1.6.4' implementation('com.github.stefan-niedermann.nextcloud-commons:markdown:1.6.4') { diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/DeckApplication.java b/app/src/main/java/it/niedermann/nextcloud/deck/DeckApplication.java index 99c3d2aa6..9902e011d 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/DeckApplication.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/DeckApplication.java @@ -5,7 +5,6 @@ import static androidx.lifecycle.Transformations.distinctUntilChanged; import android.app.Application; import android.content.Context; -import android.content.res.Configuration; import android.os.StrictMode; import androidx.annotation.ColorInt; @@ -14,6 +13,7 @@ import androidx.core.content.ContextCompat; import androidx.lifecycle.LiveData; import androidx.preference.PreferenceManager; +import com.nextcloud.android.common.ui.util.PlatformThemeUtil; import com.nextcloud.android.sso.helper.SingleAccountHelper; import it.niedermann.android.sharedpreferences.SharedPreferenceIntLiveData; @@ -39,7 +39,7 @@ public class DeckApplication extends Application { PREF_KEY_THEME = getString(R.string.pref_key_dark_theme); PREF_KEY_DEBUGGING = getString(R.string.pref_key_debugging); - setAppTheme(getAppTheme(this)); + setAppTheme(getAppThemeSetting(this)); DeckLog.enablePersistentLogs(isPersistentLoggingEnabled(this)); final var sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); currentAccountColor$ = distinctUntilChanged(new SharedPreferenceIntLiveData(sharedPreferences, @@ -91,7 +91,7 @@ public class DeckApplication extends Application { setDefaultNightMode(setting); } - public static int getAppTheme(@NonNull Context context) { + public static int getAppThemeSetting(@NonNull Context context) { final var prefs = PreferenceManager.getDefaultSharedPreferences(context); String mode; try { @@ -102,20 +102,13 @@ public class DeckApplication extends Application { return Integer.parseInt(mode); } - public static boolean isDarkThemeActive(@NonNull Context context, int darkModeSetting) { + public static boolean isDarkTheme(@NonNull Context context) { + final var darkModeSetting = getAppThemeSetting(context); return darkModeSetting == Integer.parseInt(context.getString(R.string.pref_value_theme_system_default)) - ? isDarkThemeActive(context) + ? PlatformThemeUtil.isDarkMode(context) : darkModeSetting == Integer.parseInt(context.getString(R.string.pref_value_theme_dark)); } - public static boolean isDarkThemeActive(@NonNull Context context) { - return (context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES; - } - - public static boolean isDarkTheme(@NonNull Context context) { - return isDarkThemeActive(context, getAppTheme(context)); - } - // -------------------------------------- // Current account / board / stack states // -------------------------------------- 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 bc35f5692..db6815685 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 @@ -11,13 +11,8 @@ import static it.niedermann.nextcloud.deck.DeckApplication.saveCurrentAccount; import static it.niedermann.nextcloud.deck.DeckApplication.saveCurrentBoardId; import static it.niedermann.nextcloud.deck.DeckApplication.saveCurrentStackId; import static it.niedermann.nextcloud.deck.persistence.sync.adapters.db.util.LiveDataHelper.observeOnce; -import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.applyBrandToExtendedFAB; -import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.applyBrandToPrimaryTabLayout; -import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.clearBrandColors; -import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.getSecondaryForegroundColorDependingOnTheme; -import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.saveBrandColors; -import static it.niedermann.nextcloud.deck.util.DeckColorUtil.contrastRatioIsSufficient; -import static it.niedermann.nextcloud.deck.util.DeckColorUtil.contrastRatioIsSufficientBigAreas; +import static it.niedermann.nextcloud.deck.ui.theme.ThemeUtils.clearBrandColors; +import static it.niedermann.nextcloud.deck.ui.theme.ThemeUtils.saveBrandColors; import static it.niedermann.nextcloud.deck.util.DrawerMenuUtil.MENU_ID_ABOUT; import static it.niedermann.nextcloud.deck.util.DrawerMenuUtil.MENU_ID_ADD_BOARD; import static it.niedermann.nextcloud.deck.util.DrawerMenuUtil.MENU_ID_ARCHIVED_BOARDS; @@ -32,7 +27,6 @@ import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.database.sqlite.SQLiteConstraintException; -import android.graphics.Color; import android.net.ConnectivityManager; import android.net.Network; import android.net.NetworkRequest; @@ -87,6 +81,7 @@ import java.util.Objects; import it.niedermann.android.crosstabdnd.CrossTabDragAndDrop; import it.niedermann.android.tablayouthelper.TabLayoutHelper; import it.niedermann.android.tablayouthelper.TabTitleGenerator; +import it.niedermann.android.util.ColorUtil; import it.niedermann.nextcloud.deck.DeckApplication; import it.niedermann.nextcloud.deck.DeckLog; import it.niedermann.nextcloud.deck.R; @@ -112,8 +107,6 @@ import it.niedermann.nextcloud.deck.ui.board.ArchiveBoardListener; import it.niedermann.nextcloud.deck.ui.board.DeleteBoardListener; import it.niedermann.nextcloud.deck.ui.board.EditBoardDialogFragment; import it.niedermann.nextcloud.deck.ui.board.EditBoardListener; -import it.niedermann.nextcloud.deck.ui.branding.BrandedSnackbar; -import it.niedermann.nextcloud.deck.ui.branding.BrandingUtil; import it.niedermann.nextcloud.deck.ui.card.CardAdapter; import it.niedermann.nextcloud.deck.ui.card.CreateCardListener; import it.niedermann.nextcloud.deck.ui.card.NewCardDialog; @@ -130,6 +123,8 @@ import it.niedermann.nextcloud.deck.ui.stack.EditStackListener; import it.niedermann.nextcloud.deck.ui.stack.OnScrollListener; import it.niedermann.nextcloud.deck.ui.stack.StackAdapter; import it.niedermann.nextcloud.deck.ui.stack.StackFragment; +import it.niedermann.nextcloud.deck.ui.theme.ThemeUtils; +import it.niedermann.nextcloud.deck.ui.theme.ThemedSnackbar; import it.niedermann.nextcloud.deck.ui.upcomingcards.UpcomingCardsActivity; import it.niedermann.nextcloud.deck.util.CustomAppGlideModule; import it.niedermann.nextcloud.deck.util.DrawerMenuUtil; @@ -196,7 +191,7 @@ public class MainActivity extends AppCompatActivity implements DeleteStackListen Thread.setDefaultUncaughtExceptionHandler(new ExceptionHandler(this)); setTheme(R.style.AppTheme); - colorAccent = BrandingUtil.getAttribute(this, R.attr.colorAccent); + colorAccent = ContextCompat.getColor(this, R.color.accent); binding = ActivityMainBinding.inflate(getLayoutInflater()); headerBinding = NavHeaderMainBinding.bind(binding.navigationView.getHeaderView(0)); @@ -218,8 +213,8 @@ public class MainActivity extends AppCompatActivity implements DeleteStackListen binding.navigationView.setNavigationItemSelectedListener(this); sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); - DeckApplication.readCurrentAccountColor().observe(this, this::applyAccountBranding); - DeckApplication.readCurrentBoardColor().observe(this, this::applyBoardBranding); + DeckApplication.readCurrentAccountColor().observe(this, this::applyAccountTheme); + DeckApplication.readCurrentBoardColor().observe(this, this::applyBoardTheme); binding.filterText.addTextChangedListener(new TextWatcher() { @Override @@ -452,17 +447,25 @@ public class MainActivity extends AppCompatActivity implements DeleteStackListen binding.accountSwitcher.setOnClickListener((v) -> AccountSwitcherDialog.newInstance().show(getSupportFragmentManager(), AccountSwitcherDialog.class.getSimpleName())); } - private void applyBoardBranding(@ColorInt int mainColor) { - applyBrandToPrimaryTabLayout(mainColor, binding.stackTitles); - applyBrandToExtendedFAB(mainColor, binding.fab); - // TODO We assume, that the background of the spinner is always white - binding.swipeRefreshLayout.setColorSchemeColors(contrastRatioIsSufficient(Color.WHITE, mainColor) ? mainColor : DeckApplication.isDarkTheme(this) ? Color.DKGRAY : colorAccent); - DrawableCompat.setTint(binding.filterIndicator.getDrawable(), getSecondaryForegroundColorDependingOnTheme(this, mainColor)); + private void applyBoardTheme(@ColorInt int color) { + final var utils = ThemeUtils.of(color, this); + final var scheme = ThemeUtils.createScheme(color, this); + + utils.deck.themeTabLayout(binding.stackTitles); + utils.deck.themeExtendedFAB(binding.fab); + utils.androidx.themeSwipeRefreshLayout(binding.swipeRefreshLayout); + utils.platform.colorEditText(binding.filterText); + + DrawableCompat.setTint(binding.filterIndicator.getDrawable(), scheme.getOnPrimaryContainer()); } - private void applyAccountBranding(@ColorInt int accountColor) { + private void applyAccountTheme(@ColorInt int accountColor) { + final var utils = ThemeUtils.of(accountColor, this); + + utils.deck.colorNavigationView(binding.navigationView); + headerBinding.headerView.setBackgroundColor(accountColor); - @ColorInt final int headerTextColor = contrastRatioIsSufficientBigAreas(accountColor, Color.WHITE) ? Color.WHITE : Color.BLACK; + @ColorInt final int headerTextColor = ColorUtil.INSTANCE.getForegroundColorForBackgroundColor(accountColor); headerBinding.appName.setTextColor(headerTextColor); DrawableCompat.setTint(headerBinding.logo.getDrawable(), headerTextColor); DrawableCompat.setTint(headerBinding.copyDebugLogs.getDrawable(), headerTextColor); @@ -497,7 +500,7 @@ public class MainActivity extends AppCompatActivity implements DeleteStackListen @Override public void onError(Throwable error) { IResponseCallback.super.onError(error); - runOnUiThread(() -> BrandedSnackbar.make(binding.coordinatorLayout, Objects.requireNonNull(error.getLocalizedMessage()), Snackbar.LENGTH_LONG) + runOnUiThread(() -> ThemedSnackbar.make(binding.coordinatorLayout, Objects.requireNonNull(error.getLocalizedMessage()), Snackbar.LENGTH_LONG) .setAction(R.string.simple_more, v -> ExceptionDialogFragment.newInstance(error, mainViewModel.getCurrentAccount()).show(getSupportFragmentManager(), ExceptionDialogFragment.class.getSimpleName())) .setAnchorView(binding.fab) .show()); @@ -548,7 +551,7 @@ public class MainActivity extends AppCompatActivity implements DeleteStackListen @Override public void onError(Throwable throwable) { IResponseCallback.super.onError(throwable); - runOnUiThread(() -> BrandedSnackbar.make(binding.coordinatorLayout, R.string.synchronization_failed, Snackbar.LENGTH_LONG) + runOnUiThread(() -> ThemedSnackbar.make(binding.coordinatorLayout, R.string.synchronization_failed, Snackbar.LENGTH_LONG) .setAction(R.string.simple_more, v -> ExceptionDialogFragment.newInstance(throwable, mainViewModel.getCurrentAccount()).show(getSupportFragmentManager(), ExceptionDialogFragment.class.getSimpleName())) .setAnchorView(binding.fab) .show()); @@ -857,14 +860,14 @@ public class MainActivity extends AppCompatActivity implements DeleteStackListen if (!response.isMaintenanceEnabled()) { if (response.getDeckVersion().isSupported()) { runOnUiThread(() -> { - final var importSnackbar = BrandedSnackbar.make(binding.coordinatorLayout, R.string.account_is_getting_imported, Snackbar.LENGTH_INDEFINITE) + final var importSnackbar = ThemedSnackbar.make(binding.coordinatorLayout, R.string.account_is_getting_imported, Snackbar.LENGTH_INDEFINITE) .setAnchorView(binding.fab); importSnackbar.show(); importSyncManager.synchronize(new ResponseCallback<>(createdAccount) { @Override public void onResponse(Boolean syncSuccess) { importSnackbar.dismiss(); - runOnUiThread(() -> BrandedSnackbar.make(binding.coordinatorLayout, getString(R.string.account_imported), Snackbar.LENGTH_LONG) + runOnUiThread(() -> ThemedSnackbar.make(binding.coordinatorLayout, getString(R.string.account_imported), Snackbar.LENGTH_LONG) .setAnchorView(binding.fab) .setAction(R.string.simple_switch, (a) -> { createdAccount.setColor(response.getColor()); @@ -932,7 +935,7 @@ public class MainActivity extends AppCompatActivity implements DeleteStackListen IResponseCallback.super.onError(error); if (error instanceof SQLiteConstraintException) { DeckLog.warn("Account already added"); - BrandedSnackbar.make(binding.coordinatorLayout, R.string.account_already_added, Snackbar.LENGTH_LONG) + ThemedSnackbar.make(binding.coordinatorLayout, R.string.account_already_added, Snackbar.LENGTH_LONG) .setAnchorView(binding.fab) .show(); } else { @@ -1116,7 +1119,7 @@ public class MainActivity extends AppCompatActivity implements DeleteStackListen /** - * Displays a {@link BrandedSnackbar} for an exception of a failed sync, but only if the cause wasn't maintenance mode (this should be handled by a TextView instead of a snackbar). + * Displays a {@link ThemedSnackbar} for an exception of a failed sync, but only if the cause wasn't maintenance mode (this should be handled by a TextView instead of a snackbar). * * @param throwable the cause of the failed sync */ @@ -1125,7 +1128,7 @@ public class MainActivity extends AppCompatActivity implements DeleteStackListen if (!(throwable instanceof NextcloudHttpRequestFailedException) || ((NextcloudHttpRequestFailedException) throwable).getStatusCode() != HttpURLConnection.HTTP_UNAVAILABLE) { runOnUiThread(() -> { if (binding != null) { // Can be null in case the activity has been destroyed before the synchronization process has been finished - BrandedSnackbar.make(binding.coordinatorLayout, R.string.synchronization_failed, Snackbar.LENGTH_LONG) + ThemedSnackbar.make(binding.coordinatorLayout, R.string.synchronization_failed, Snackbar.LENGTH_LONG) .setAction(R.string.simple_more, v -> ExceptionDialogFragment.newInstance(throwable, mainViewModel.getCurrentAccount()).show(getSupportFragmentManager(), ExceptionDialogFragment.class.getSimpleName())) .setAnchorView(binding.fab) .show(); @@ -1159,7 +1162,7 @@ public class MainActivity extends AppCompatActivity implements DeleteStackListen .setMultiChoiceItems(animals, checkedItems, (dialog, which, isChecked) -> checkedItems[0] = isChecked) .setPositiveButton(R.string.simple_clone, (dialog, which) -> { binding.drawerLayout.closeDrawer(GravityCompat.START); - final var snackbar = BrandedSnackbar.make(binding.coordinatorLayout, getString(R.string.cloning_board, board.getTitle()), Snackbar.LENGTH_INDEFINITE) + final var snackbar = ThemedSnackbar.make(binding.coordinatorLayout, getString(R.string.cloning_board, board.getTitle()), Snackbar.LENGTH_INDEFINITE) .setAnchorView(binding.fab); snackbar.show(); mainViewModel.cloneBoard(board.getAccountId(), board.getLocalId(), board.getAccountId(), board.getColor(), checkedItems[0], new IResponseCallback<>() { @@ -1168,7 +1171,7 @@ public class MainActivity extends AppCompatActivity implements DeleteStackListen runOnUiThread(() -> { snackbar.dismiss(); setCurrentBoard(response.getBoard()); - BrandedSnackbar.make(binding.coordinatorLayout, getString(R.string.successfully_cloned_board, response.getBoard().getTitle()), Snackbar.LENGTH_LONG) + ThemedSnackbar.make(binding.coordinatorLayout, getString(R.string.successfully_cloned_board, response.getBoard().getTitle()), Snackbar.LENGTH_LONG) .setAction(R.string.edit, v -> EditBoardDialogFragment.newInstance(response.getLocalId()).show(getSupportFragmentManager(), EditBoardDialogFragment.class.getSimpleName())) .setAnchorView(binding.fab) .show(); diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/PickStackActivity.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/PickStackActivity.java index 1d54ec015..358f17bbf 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/PickStackActivity.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/PickStackActivity.java @@ -1,44 +1,35 @@ package it.niedermann.nextcloud.deck.ui; import static androidx.lifecycle.Transformations.switchMap; -import static it.niedermann.nextcloud.deck.DeckApplication.isDarkTheme; -import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.getSecondaryForegroundColorDependingOnTheme; -import static it.niedermann.nextcloud.deck.util.DeckColorUtil.contrastRatioIsSufficientBigAreas; -import android.content.res.ColorStateList; -import android.graphics.Color; import android.os.Bundle; import android.text.Editable; import android.text.TextWatcher; import android.view.View; -import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import androidx.core.content.ContextCompat; -import androidx.core.graphics.drawable.DrawableCompat; import androidx.lifecycle.ViewModelProvider; import java.util.List; -import it.niedermann.android.util.ColorUtil; -import it.niedermann.nextcloud.deck.DeckLog; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.api.IResponseCallback; import it.niedermann.nextcloud.deck.databinding.ActivityPickStackBinding; import it.niedermann.nextcloud.deck.model.Account; import it.niedermann.nextcloud.deck.model.Board; import it.niedermann.nextcloud.deck.model.Stack; -import it.niedermann.nextcloud.deck.ui.branding.Branded; -import it.niedermann.nextcloud.deck.ui.branding.BrandingUtil; import it.niedermann.nextcloud.deck.ui.exception.ExceptionDialogFragment; import it.niedermann.nextcloud.deck.ui.exception.ExceptionHandler; import it.niedermann.nextcloud.deck.ui.pickstack.PickStackFragment; import it.niedermann.nextcloud.deck.ui.pickstack.PickStackListener; import it.niedermann.nextcloud.deck.ui.pickstack.PickStackViewModel; +import it.niedermann.nextcloud.deck.ui.theme.ThemeUtils; +import it.niedermann.nextcloud.deck.ui.theme.Themed; -public abstract class PickStackActivity extends AppCompatActivity implements Branded, PickStackListener { +public abstract class PickStackActivity extends AppCompatActivity implements Themed, PickStackListener { private ActivityPickStackBinding binding; private PickStackViewModel viewModel; @@ -93,7 +84,7 @@ public abstract class PickStackActivity extends AppCompatActivity implements Bra } }); }); - viewModel.submitButtonEnabled().observe(this, (enabled) -> binding.submit.setEnabled(enabled)); + viewModel.submitButtonEnabled().observe(this, enabled -> binding.submit.setEnabled(enabled)); if (requireContent()) { viewModel.setContentIsSatisfied(false); binding.inputWrapper.setVisibility(View.VISIBLE); @@ -127,25 +118,19 @@ public abstract class PickStackActivity extends AppCompatActivity implements Bra @Override public void onStackPicked(@NonNull Account account, @Nullable Board board, @Nullable Stack stack) { viewModel.setSelected(account, board, stack); - applyBrand(board == null + applyTheme(board == null ? ContextCompat.getColor(this, R.color.accent) : board.getColor() ); } @Override - public void applyBrand(int mainColor) { - try { - @ColorInt final int finalMainColor = contrastRatioIsSufficientBigAreas(mainColor, ContextCompat.getColor(this, R.color.primary)) - ? mainColor - : isDarkTheme(this) ? Color.WHITE : Color.BLACK; - DrawableCompat.setTintList(binding.submit.getBackground(), ColorStateList.valueOf(finalMainColor)); - binding.submit.setTextColor(ColorUtil.INSTANCE.getForegroundColorForBackgroundColor(finalMainColor)); - binding.cancel.setTextColor(getSecondaryForegroundColorDependingOnTheme(this, mainColor)); - BrandingUtil.applyBrandToEditTextInputLayout(mainColor, binding.inputWrapper); - } catch (Throwable t) { - DeckLog.logError(t); - } + public void applyTheme(int color) { + final var utils = ThemeUtils.of(color, this); + + utils.material.colorMaterialButtonText(binding.cancel); + utils.material.colorMaterialButtonPrimaryFilled(binding.submit); + utils.material.colorTextInputLayout(binding.inputWrapper); } abstract protected void onSubmit(Account account, long boardLocalId, long stackId, @NonNull IResponseCallback<Void> callback); diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/PushNotificationActivity.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/PushNotificationActivity.java index f76187a04..3b2332e7e 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/PushNotificationActivity.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/PushNotificationActivity.java @@ -1,10 +1,7 @@ package it.niedermann.nextcloud.deck.ui; -import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.getSecondaryForegroundColorDependingOnTheme; - import android.annotation.SuppressLint; import android.content.Intent; -import android.graphics.PorterDuff; import android.net.Uri; import android.view.View; @@ -15,8 +12,8 @@ import androidx.lifecycle.ViewModelProvider; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.stream.Stream; -import it.niedermann.android.util.ColorUtil; import it.niedermann.nextcloud.deck.DeckLog; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.ActivityPushNotificationBinding; @@ -24,6 +21,7 @@ import it.niedermann.nextcloud.deck.model.Account; import it.niedermann.nextcloud.deck.ui.card.EditActivity; import it.niedermann.nextcloud.deck.ui.exception.ExceptionDialogFragment; import it.niedermann.nextcloud.deck.ui.exception.ExceptionHandler; +import it.niedermann.nextcloud.deck.ui.theme.ThemeUtils; /** * Warning: Do not move this class to another package or folder! @@ -54,7 +52,7 @@ public class PushNotificationActivity extends AppCompatActivity { setSupportActionBar(binding.toolbar); binding.progress.setIndeterminate(true); - viewModel.getAccount().observe(this, this::applyBrandToSubmitButton); + viewModel.getAccount().observe(this, this::applyThemeToSubmitButton); executor.submit(() -> viewModel.getCardInformation(intent.getExtras(), new PushNotificationViewModel.PushNotificationCallback() { @Override public void onResponse(@NonNull PushNotificationViewModel.CardInformation cardInformation) { @@ -123,16 +121,11 @@ public class PushNotificationActivity extends AppCompatActivity { // TODO implement Branded interface // TODO apply branding based on board color - public void applyBrandToSubmitButton(@ColorInt int mainColor) { - try { - binding.progress.getProgressDrawable().setColorFilter( - getSecondaryForegroundColorDependingOnTheme(this, mainColor), PorterDuff.Mode.SRC_IN); - binding.submit.setBackgroundColor(mainColor); - binding.submit.setTextColor(ColorUtil.INSTANCE.getForegroundColorForBackgroundColor(mainColor)); - binding.showError.setBackgroundColor(mainColor); - binding.showError.setTextColor(ColorUtil.INSTANCE.getForegroundColorForBackgroundColor(mainColor)); - } catch (Throwable t) { - DeckLog.logError(t); - } + public void applyThemeToSubmitButton(@ColorInt int color) { + final var utils = ThemeUtils.of(color, this); + + utils.platform.themeHorizontalProgressBar(binding.progress); + Stream.of(binding.submit, binding.showError) + .forEach(utils.material::colorMaterialButtonPrimaryFilled); } } 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 552e9b5fd..cfb43c382 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 @@ -1,7 +1,5 @@ package it.niedermann.nextcloud.deck.ui.about; -import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.applyBrandToPrimaryTabLayout; - import android.content.Context; import android.content.Intent; import android.os.Bundle; @@ -20,6 +18,7 @@ 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.exception.ExceptionHandler; +import it.niedermann.nextcloud.deck.ui.theme.ThemeUtils; public class AboutActivity extends AppCompatActivity { private static final String BUNDLE_KEY_ACCOUNT = "account"; @@ -39,7 +38,7 @@ public class AboutActivity extends AppCompatActivity { binding = ActivityAboutBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - DeckApplication.readCurrentAccountColor().observe(this, (mainColor) -> applyBrandToPrimaryTabLayout(mainColor, binding.tabLayout)); + DeckApplication.readCurrentAccountColor().observe(this, color -> ThemeUtils.of(color, this).deck.themeTabLayout(binding.tabLayout)); setSupportActionBar(binding.toolbar); binding.viewPager.setAdapter(new TabsPagerAdapter(this, (Account) getIntent().getSerializableExtra(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 39a30ec2e..dafc54bee 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 @@ -1,29 +1,22 @@ package it.niedermann.nextcloud.deck.ui.about; -import static it.niedermann.nextcloud.deck.DeckApplication.isDarkTheme; -import static it.niedermann.nextcloud.deck.util.DeckColorUtil.contrastRatioIsSufficientBigAreas; import static it.niedermann.nextcloud.deck.util.SpannableUtil.setTextWithURL; import android.content.Intent; -import android.content.res.ColorStateList; -import android.graphics.Color; import android.net.Uri; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.core.content.ContextCompat; -import androidx.core.graphics.drawable.DrawableCompat; import androidx.fragment.app.Fragment; -import it.niedermann.android.util.ColorUtil; import it.niedermann.nextcloud.deck.DeckApplication; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.FragmentAboutLicenseTabBinding; +import it.niedermann.nextcloud.deck.ui.theme.ThemeUtils; public class AboutFragmentLicenseTab extends Fragment { @@ -41,13 +34,8 @@ public class AboutFragmentLicenseTab extends Fragment { @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); - DeckApplication.readCurrentAccountColor().observe(getViewLifecycleOwner(), (mainColor) -> { - @ColorInt final int finalMainColor = contrastRatioIsSufficientBigAreas(mainColor, ContextCompat.getColor(requireContext(), R.color.primary)) - ? mainColor - : isDarkTheme(requireContext()) ? Color.WHITE : Color.BLACK; - DrawableCompat.setTintList(binding.aboutAppLicenseButton.getBackground(), ColorStateList.valueOf(finalMainColor)); - binding.aboutAppLicenseButton.setTextColor(ColorUtil.INSTANCE.getForegroundColorForBackgroundColor(finalMainColor)); - }); + DeckApplication.readCurrentAccountColor().observe(getViewLifecycleOwner(), color -> + ThemeUtils.of(color, requireContext()).material.colorMaterialButtonPrimaryFilled(binding.aboutAppLicenseButton)); } @Override diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/accountswitcher/AccountSwitcherDialog.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/accountswitcher/AccountSwitcherDialog.java index 12150641a..1f0098c4f 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/accountswitcher/AccountSwitcherDialog.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/accountswitcher/AccountSwitcherDialog.java @@ -70,7 +70,7 @@ public class AccountSwitcherDialog extends DialogFragment { adapter.setAccounts(accounts.stream().filter(account -> !Objects.equals(account.getId(), viewModel.getCurrentAccount().getId())).collect(Collectors.toList()))); - observeOnce(DeckApplication.readCurrentBoardColor(), requireActivity(), this::applyBrand); + observeOnce(DeckApplication.readCurrentBoardColor(), requireActivity(), this::applyTheme); binding.accountsList.setAdapter(adapter); @@ -108,7 +108,7 @@ public class AccountSwitcherDialog extends DialogFragment { return new AccountSwitcherDialog(); } - private void applyBrand(int mainColor) { -// applyBrandToLayerDrawable((LayerDrawable) binding.check.getDrawable(), R.id.area, mainColor); + private void applyTheme(int color) { +// applyThemeToLayerDrawable((LayerDrawable) binding.check.getDrawable(), R.id.area, mainColor); } } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/archivedcards/ArchivedCardsAdapter.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/archivedcards/ArchivedCardsAdapter.java index 45212f287..ea1ff4d89 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/archivedcards/ArchivedCardsAdapter.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/archivedcards/ArchivedCardsAdapter.java @@ -26,7 +26,7 @@ public class ArchivedCardsAdapter extends CardAdapter { @Override public void onBindViewHolder(@NonNull AbstractCardViewHolder viewHolder, int position) { - viewHolder.bind(cardList.get(position), mainViewModel.getCurrentAccount(), mainViewModel.getCurrentBoardRemoteId(), false, R.menu.archived_card_menu, this, counterMaxValue, mainColor); + viewHolder.bind(cardList.get(position), mainViewModel.getCurrentAccount(), mainViewModel.getCurrentBoardRemoteId(), false, R.menu.archived_card_menu, this, counterMaxValue, scheme); } @Override diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/DeleteBoardDialogFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/DeleteBoardDialogFragment.java index b81c6471e..94e658954 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/DeleteBoardDialogFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/DeleteBoardDialogFragment.java @@ -9,7 +9,7 @@ import androidx.fragment.app.DialogFragment; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.model.Board; -import it.niedermann.nextcloud.deck.ui.branding.DeleteAlertDialogBuilder; +import it.niedermann.nextcloud.deck.ui.theme.DeleteAlertDialogBuilder; public class DeleteBoardDialogFragment extends DialogFragment { 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 894f0d835..9e05fbd80 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 @@ -1,7 +1,5 @@ package it.niedermann.nextcloud.deck.ui.board; -import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.applyBrandToEditTextInputLayout; - import android.app.Dialog; import android.content.Context; import android.os.Bundle; @@ -24,6 +22,7 @@ import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.DialogTextColorInputBinding; import it.niedermann.nextcloud.deck.model.full.FullBoard; import it.niedermann.nextcloud.deck.ui.MainViewModel; +import it.niedermann.nextcloud.deck.ui.theme.ThemeUtils; public class EditBoardDialogFragment extends DialogFragment { @@ -64,14 +63,17 @@ public class EditBoardDialogFragment extends DialogFragment { this.editBoardListener.onUpdateBoard(fullBoard); }); final var viewModel = new ViewModelProvider(requireActivity()).get(MainViewModel.class); - viewModel.getFullBoardById(viewModel.getCurrentAccount().getId(), args.getLong(KEY_BOARD_ID)).observe(EditBoardDialogFragment.this, (FullBoard fb) -> { - if (fb.board != null) { - this.fullBoard = fb; - String title = this.fullBoard.getBoard().getTitle(); + + viewModel.getFullBoardById(viewModel.getCurrentAccount().getId(), args.getLong(KEY_BOARD_ID)).observe(EditBoardDialogFragment.this, fullBoard -> { + if (fullBoard.board != null) { + this.fullBoard = fullBoard; + final var utils = ThemeUtils.of(fullBoard.getBoard().getColor(), requireContext()); + + final String title = this.fullBoard.getBoard().getTitle(); binding.input.setText(title); binding.input.setSelection(title.length()); - applyBrandToEditTextInputLayout(fb.getBoard().getColor(), binding.inputWrapper); - binding.colorChooser.selectColor(fullBoard.getBoard().getColor()); + binding.colorChooser.selectColor(this.fullBoard.getBoard().getColor()); + utils.material.colorTextInputLayout(binding.inputWrapper); } }); } else { diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/accesscontrol/AccessControlAdapter.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/accesscontrol/AccessControlAdapter.java index e120c665c..c97a1e4ba 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/accesscontrol/AccessControlAdapter.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/accesscontrol/AccessControlAdapter.java @@ -1,17 +1,12 @@ package it.niedermann.nextcloud.deck.ui.board.accesscontrol; import android.content.Context; -import android.content.res.ColorStateList; -import android.graphics.Color; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import androidx.annotation.ColorInt; import androidx.annotation.NonNull; -import androidx.appcompat.widget.SwitchCompat; import androidx.core.content.ContextCompat; -import androidx.core.graphics.drawable.DrawableCompat; import androidx.recyclerview.widget.RecyclerView; import java.util.LinkedList; @@ -24,34 +19,34 @@ import it.niedermann.nextcloud.deck.databinding.ItemAccessControlOwnerBinding; import it.niedermann.nextcloud.deck.model.AccessControl; import it.niedermann.nextcloud.deck.model.Account; import it.niedermann.nextcloud.deck.model.enums.DBStatus; -import it.niedermann.nextcloud.deck.ui.branding.Branded; +import it.niedermann.nextcloud.deck.ui.theme.ThemeUtils; +import it.niedermann.nextcloud.deck.ui.theme.Themed; import it.niedermann.nextcloud.deck.util.ViewUtil; -import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.getSecondaryForegroundColorDependingOnTheme; - -public class AccessControlAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements Branded { +public class AccessControlAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements Themed { public static final long HEADER_ITEM_LOCAL_ID = -1L; private static final int TYPE_HEADER = 0; private static final int TYPE_ITEM = 1; - private int mainColor; + @NonNull + private ThemeUtils utils; @NonNull - private Account account; + private final Account account; @NonNull - private List<AccessControl> accessControls = new LinkedList<>(); + private final List<AccessControl> accessControls = new LinkedList<>(); @NonNull - private AccessControlChangedListener accessControlChangedListener; + private final AccessControlChangedListener accessControlChangedListener; @NonNull - private Context context; + private final Context context; private boolean hasManagePermission = false; AccessControlAdapter(@NonNull Account account, @NonNull AccessControlChangedListener accessControlChangedListener, @NonNull Context context) { this.account = account; this.accessControlChangedListener = accessControlChangedListener; this.context = context; - this.mainColor = ContextCompat.getColor(context, R.color.primary); + this.utils = ThemeUtils.of(ContextCompat.getColor(context, R.color.primary), context); setHasStableIds(true); } @@ -123,10 +118,10 @@ public class AccessControlAdapter extends RecyclerView.Adapter<RecyclerView.View }); if (hasManagePermission) { - brandSwitch(context, acHolder.binding.permissionEdit, mainColor); - brandSwitch(context, acHolder.binding.permissionManage, mainColor); + utils.androidx.colorSwitchCompat(acHolder.binding.permissionEdit); + utils.androidx.colorSwitchCompat(acHolder.binding.permissionManage); } - brandSwitch(context, acHolder.binding.permissionShare, mainColor); + utils.androidx.colorSwitchCompat(acHolder.binding.permissionShare); break; } } @@ -157,26 +152,8 @@ public class AccessControlAdapter extends RecyclerView.Adapter<RecyclerView.View } @Override - public void applyBrand(int mainColor) { - this.mainColor = getSecondaryForegroundColorDependingOnTheme(context, mainColor); + public void applyTheme(int color) { + this.utils = ThemeUtils.of(color, context); notifyDataSetChanged(); } - - /** - * Helper method to apply branding to a {@link SwitchCompat} - */ - private static void brandSwitch(@NonNull Context context, @NonNull SwitchCompat switchCompat, @ColorInt int mainColor) { - final int finalMainColor = getSecondaryForegroundColorDependingOnTheme(context, mainColor); - DrawableCompat.setTintList(switchCompat.getThumbDrawable(), new ColorStateList( - new int[][]{new int[]{android.R.attr.state_checked}, new int[]{}}, - new int[]{finalMainColor, ContextCompat.getColor(context, R.color.fg_secondary)} - )); - final int trackColor = ContextCompat.getColor(context, R.color.fg_secondary); - final int lightTrackColor = Color.argb(77, Color.red(trackColor), Color.green(trackColor), Color.blue(trackColor)); - final int lightTrackColorChecked = Color.argb(77, Color.red(finalMainColor), Color.green(finalMainColor), Color.blue(finalMainColor)); - DrawableCompat.setTintList(switchCompat.getTrackDrawable(), new ColorStateList( - new int[][]{new int[]{android.R.attr.state_checked}, new int[]{}}, - new int[]{lightTrackColorChecked, lightTrackColor} - )); - } } 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 74c0ad2a3..7aa0dc199 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 @@ -1,7 +1,6 @@ package it.niedermann.nextcloud.deck.ui.board.accesscontrol; import static it.niedermann.nextcloud.deck.ui.board.accesscontrol.AccessControlAdapter.HEADER_ITEM_LOCAL_ID; -import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.applyBrandToEditTextInputLayout; import android.app.Dialog; import android.content.Context; @@ -29,9 +28,10 @@ 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.MainViewModel; -import it.niedermann.nextcloud.deck.ui.branding.BrandedSnackbar; import it.niedermann.nextcloud.deck.ui.card.UserAutoCompleteAdapter; import it.niedermann.nextcloud.deck.ui.exception.ExceptionDialogFragment; +import it.niedermann.nextcloud.deck.ui.theme.ThemeUtils; +import it.niedermann.nextcloud.deck.ui.theme.ThemedSnackbar; public class AccessControlDialogFragment extends DialogFragment implements AccessControlChangedListener, OnItemClickListener { @@ -84,7 +84,7 @@ public class AccessControlDialogFragment extends DialogFragment implements Acces binding.people.setAdapter(userAutoCompleteAdapter); binding.people.setOnItemClickListener(this); }); - applyBrand(fullBoard.getBoard().getColor()); + applyTheme(fullBoard.getBoard().getColor()); } else { // Happens when someone revokes his own access → board gets deleted locally → LiveData fires, but no board // see https://github.com/stefan-niedermann/nextcloud-deck/issues/410 @@ -132,7 +132,7 @@ public class AccessControlDialogFragment extends DialogFragment implements Acces public void onError(Throwable throwable) { if (!SyncManager.ignoreExceptionOnVoidError(throwable)) { IResponseCallback.super.onError(throwable); - requireActivity().runOnUiThread(() -> BrandedSnackbar.make(requireView(), getString(R.string.error_revoking_ac, ac.getUser().getDisplayname()), Snackbar.LENGTH_LONG) + requireActivity().runOnUiThread(() -> ThemedSnackbar.make(requireView(), getString(R.string.error_revoking_ac, ac.getUser().getDisplayname()), Snackbar.LENGTH_LONG) .setAction(R.string.simple_more, v -> ExceptionDialogFragment.newInstance(throwable, viewModel.getCurrentAccount()).show(getChildFragmentManager(), ExceptionDialogFragment.class.getSimpleName())) .show()); } @@ -166,9 +166,12 @@ public class AccessControlDialogFragment extends DialogFragment implements Acces userAutoCompleteAdapter.exclude(user); } - public void applyBrand(@ColorInt int mainColor) { - applyBrandToEditTextInputLayout(mainColor, binding.peopleWrapper); - this.adapter.applyBrand(mainColor); + public void applyTheme(@ColorInt int color) { + final var utils = ThemeUtils.of(color, requireContext()); + + utils.material.colorTextInputLayout(binding.peopleWrapper); + + this.adapter.applyTheme(color); } public static DialogFragment newInstance(long boardLocalId) { diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/managelabels/EditLabelDialogFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/managelabels/EditLabelDialogFragment.java index 1b8c1e0b1..4eef71eb8 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/managelabels/EditLabelDialogFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/managelabels/EditLabelDialogFragment.java @@ -1,7 +1,5 @@ package it.niedermann.nextcloud.deck.ui.board.managelabels; -import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.applyBrandToEditTextInputLayout; - import android.app.Dialog; import android.content.Context; import android.os.Bundle; @@ -14,9 +12,10 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.DialogTextColorInputBinding; import it.niedermann.nextcloud.deck.model.Label; -import it.niedermann.nextcloud.deck.ui.branding.BrandedDialogFragment; +import it.niedermann.nextcloud.deck.ui.theme.ThemeUtils; +import it.niedermann.nextcloud.deck.ui.theme.ThemedDialogFragment; -public class EditLabelDialogFragment extends BrandedDialogFragment { +public class EditLabelDialogFragment extends ThemedDialogFragment { private DialogTextColorInputBinding binding; @@ -90,7 +89,9 @@ public class EditLabelDialogFragment extends BrandedDialogFragment { } @Override - public void applyBrand(int mainColor) { - applyBrandToEditTextInputLayout(mainColor, binding.inputWrapper); + public void applyTheme(int color) { + final var utils = ThemeUtils.of(color, requireContext()); + + utils.material.colorTextInputLayout(binding.inputWrapper); } }
\ No newline at end of file diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/managelabels/ManageLabelsAdapter.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/managelabels/ManageLabelsAdapter.java index d6b097d89..ca5126250 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/managelabels/ManageLabelsAdapter.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/managelabels/ManageLabelsAdapter.java @@ -5,35 +5,24 @@ import android.view.LayoutInflater; import android.view.ViewGroup; import androidx.annotation.NonNull; -import androidx.core.content.ContextCompat; import androidx.recyclerview.widget.RecyclerView; import java.util.LinkedList; import java.util.List; import java.util.NoSuchElementException; -import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.ItemManageLabelBinding; import it.niedermann.nextcloud.deck.model.Label; -import it.niedermann.nextcloud.deck.ui.branding.Branded; -import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.getSecondaryForegroundColorDependingOnTheme; - -public class ManageLabelsAdapter extends RecyclerView.Adapter<ManageLabelsViewHolder> implements Branded { - - private int mainColor; +public class ManageLabelsAdapter extends RecyclerView.Adapter<ManageLabelsViewHolder> { @NonNull - private ManageLabelListener listener; + private final ManageLabelListener listener; @NonNull - private List<Label> labels = new LinkedList<>(); - @NonNull - private Context context; + private final List<Label> labels = new LinkedList<>(); ManageLabelsAdapter(@NonNull ManageLabelListener listener, @NonNull Context context) { this.listener = listener; - this.context = context; - this.mainColor = ContextCompat.getColor(context, R.color.primary); setHasStableIds(true); } @@ -73,10 +62,4 @@ public class ManageLabelsAdapter extends RecyclerView.Adapter<ManageLabelsViewHo this.labels.addAll(labels); notifyDataSetChanged(); } - - @Override - public void applyBrand(int mainColor) { - this.mainColor = getSecondaryForegroundColorDependingOnTheme(context, mainColor); - notifyDataSetChanged(); - } } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/managelabels/ManageLabelsDialogFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/managelabels/ManageLabelsDialogFragment.java index 53cbe09b9..ca3547ae7 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/managelabels/ManageLabelsDialogFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/managelabels/ManageLabelsDialogFragment.java @@ -1,8 +1,5 @@ package it.niedermann.nextcloud.deck.ui.board.managelabels; -import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.applyBrandToEditTextInputLayout; -import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.applyBrandToFAB; - import android.app.Dialog; import android.content.Context; import android.database.sqlite.SQLiteConstraintException; @@ -27,10 +24,11 @@ import it.niedermann.nextcloud.deck.databinding.DialogBoardManageLabelsBinding; import it.niedermann.nextcloud.deck.model.Label; import it.niedermann.nextcloud.deck.persistence.sync.SyncManager; import it.niedermann.nextcloud.deck.ui.MainViewModel; -import it.niedermann.nextcloud.deck.ui.branding.BrandedDialogFragment; -import it.niedermann.nextcloud.deck.ui.branding.DeleteAlertDialogBuilder; +import it.niedermann.nextcloud.deck.ui.theme.DeleteAlertDialogBuilder; +import it.niedermann.nextcloud.deck.ui.theme.ThemeUtils; +import it.niedermann.nextcloud.deck.ui.theme.ThemedDialogFragment; -public class ManageLabelsDialogFragment extends BrandedDialogFragment implements ManageLabelListener, EditLabelListener { +public class ManageLabelsDialogFragment extends ThemedDialogFragment implements ManageLabelListener, EditLabelListener { private MainViewModel viewModel; private DialogBoardManageLabelsBinding binding; @@ -119,9 +117,11 @@ public class ManageLabelsDialogFragment extends BrandedDialogFragment implements } @Override - public void applyBrand(int mainColor) { - applyBrandToFAB(mainColor, binding.fab); - applyBrandToEditTextInputLayout(mainColor, binding.addLabelTitleWrapper); + public void applyTheme(int color) { + final var utils = ThemeUtils.of(color, requireContext()); + + utils.material.themeFAB(binding.fab); + utils.material.colorTextInputLayout(binding.addLabelTitleWrapper); } public static DialogFragment newInstance(long boardLocalId) { 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 deleted file mode 100644 index 99ad9c074..000000000 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/Branded.java +++ /dev/null @@ -1,9 +0,0 @@ -package it.niedermann.nextcloud.deck.ui.branding; - -import androidx.annotation.ColorInt; -import androidx.annotation.UiThread; - -public interface Branded { - @UiThread - void applyBrand(@ColorInt int mainColor); -}
\ No newline at end of file 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 deleted file mode 100644 index 4b35351b4..000000000 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedDialogFragment.java +++ /dev/null @@ -1,19 +0,0 @@ -package it.niedermann.nextcloud.deck.ui.branding; - -import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.readBrandMainColor; - -import androidx.annotation.Nullable; -import androidx.fragment.app.DialogFragment; - -public abstract class BrandedDialogFragment extends DialogFragment implements Branded { - - @Override - public void onStart() { - super.onStart(); - - @Nullable final var context = getContext(); - if (context != null) { - applyBrand(readBrandMainColor(context)); - } - } -} diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedPreferenceCategory.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedPreferenceCategory.java deleted file mode 100644 index eeac7579b..000000000 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedPreferenceCategory.java +++ /dev/null @@ -1,44 +0,0 @@ -package it.niedermann.nextcloud.deck.ui.branding; - -import static it.niedermann.nextcloud.deck.DeckApplication.readCurrentAccountColor; -import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.getSecondaryForegroundColorDependingOnTheme; - -import android.content.Context; -import android.util.AttributeSet; -import android.widget.TextView; - -import androidx.annotation.ColorInt; -import androidx.annotation.Nullable; -import androidx.preference.PreferenceCategory; -import androidx.preference.PreferenceViewHolder; - -public class BrandedPreferenceCategory extends PreferenceCategory { - - @Override - public void onBindViewHolder(PreferenceViewHolder holder) { - super.onBindViewHolder(holder); - - final var view = holder.itemView.findViewById(android.R.id.title); - @Nullable final Context context = getContext(); - if (context != null && view instanceof TextView) { - @ColorInt final int mainColor = getSecondaryForegroundColorDependingOnTheme(context, readCurrentAccountColor(context)); - ((TextView) view).setTextColor(mainColor); - } - } - - public BrandedPreferenceCategory(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - } - - public BrandedPreferenceCategory(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - public BrandedPreferenceCategory(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public BrandedPreferenceCategory(Context context) { - super(context); - } -} diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedSnackbar.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedSnackbar.java deleted file mode 100644 index adc4ea65f..000000000 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedSnackbar.java +++ /dev/null @@ -1,46 +0,0 @@ -package it.niedermann.nextcloud.deck.ui.branding; - -import static it.niedermann.nextcloud.deck.DeckApplication.isDarkTheme; -import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.getAttribute; -import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.readBrandMainColor; -import static it.niedermann.nextcloud.deck.util.DeckColorUtil.contrastRatioIsSufficient; - -import android.graphics.Color; -import android.view.View; - -import androidx.annotation.ColorInt; -import androidx.annotation.NonNull; -import androidx.annotation.StringRes; - -import com.google.android.material.snackbar.BaseTransientBottomBar; -import com.google.android.material.snackbar.Snackbar; - -import it.niedermann.nextcloud.deck.R; - -public class BrandedSnackbar { - - @NonNull - public static Snackbar make(@NonNull View view, @NonNull CharSequence text, @BaseTransientBottomBar.Duration int duration) { - final var snackbar = Snackbar.make(view, text, duration); - - @ColorInt final int backgroundColor = getAttribute(view.getContext(), R.attr.colorSurfaceInverse); - @ColorInt final int color = readBrandMainColor(view.getContext()); - - if (contrastRatioIsSufficient(backgroundColor, color)) { - snackbar.setActionTextColor(color); - } else { - if (isDarkTheme(view.getContext())) { - snackbar.setActionTextColor(Color.BLACK); - } else { - snackbar.setActionTextColor(Color.WHITE); - } - } - - return snackbar; - } - - @NonNull - public static Snackbar make(@NonNull View view, @StringRes int resId, @BaseTransientBottomBar.Duration int duration) { - return make(view, view.getResources().getText(resId), duration); - } -}
\ No newline at end of file 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 deleted file mode 100644 index 3c1558df5..000000000 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedSwitchPreference.java +++ /dev/null @@ -1,104 +0,0 @@ -package it.niedermann.nextcloud.deck.ui.branding; - -import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.getSecondaryForegroundColorDependingOnTheme; - -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 it.niedermann.nextcloud.deck.R; - -public class BrandedSwitchPreference extends SwitchPreference implements Branded { - - @ColorInt - private Integer mainColor = 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) { - applyBrand(); - } - } - } - - @Override - public void applyBrand(@ColorInt int mainColor) { - this.mainColor = mainColor; - // onBindViewHolder is called after applyBrand, therefore we have to store the given values and apply them later. - applyBrand(); - } - - private void applyBrand() { - if (switchView != null) { - final int finalMainColor = getSecondaryForegroundColorDependingOnTheme(getContext(), mainColor); - // int trackColor = Color.argb(77, Color.red(finalMainColor), Color.green(finalMainColor), Color.blue(finalMainColor)); - DrawableCompat.setTintList(switchView.getThumbDrawable(), new ColorStateList( - new int[][]{new int[]{android.R.attr.state_checked}, new int[]{}}, - new int[]{finalMainColor, getContext().getResources().getColor(R.color.fg_secondary)} - )); - DrawableCompat.setTintList(switchView.getTrackDrawable(), new ColorStateList( - new int[][]{new int[]{android.R.attr.state_checked}, new int[]{}}, - new int[]{finalMainColor, getContext().getResources().getColor(R.color.fg_secondary)} - )); - } - } - - /** - * 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 <a href="https://gist.github.com/marchold/45e22839eb94aa14dfb5">Source</a> - */ - 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++) { - final var child = viewGroup.getChildAt(i); - if (child instanceof ViewGroup) { - final var 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/branding/BrandingUtil.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandingUtil.java deleted file mode 100644 index ee6a9ccdc..000000000 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandingUtil.java +++ /dev/null @@ -1,119 +0,0 @@ -package it.niedermann.nextcloud.deck.ui.branding; - -import static it.niedermann.nextcloud.deck.DeckApplication.isDarkTheme; -import static it.niedermann.nextcloud.deck.util.DeckColorUtil.contrastRatioIsSufficient; -import static it.niedermann.nextcloud.deck.util.DeckColorUtil.contrastRatioIsSufficientBigAreas; - -import android.content.Context; -import android.content.res.ColorStateList; -import android.graphics.Color; -import android.util.TypedValue; -import android.view.MenuItem; - -import androidx.annotation.AttrRes; -import androidx.annotation.ColorInt; -import androidx.annotation.NonNull; -import androidx.core.content.ContextCompat; -import androidx.core.graphics.drawable.DrawableCompat; -import androidx.preference.PreferenceManager; - -import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton; -import com.google.android.material.floatingactionbutton.FloatingActionButton; -import com.google.android.material.tabs.TabLayout; -import com.google.android.material.textfield.TextInputLayout; - -import it.niedermann.android.util.ColorUtil; -import it.niedermann.nextcloud.deck.DeckLog; -import it.niedermann.nextcloud.deck.R; - -public abstract class BrandingUtil { - - private BrandingUtil() { - throw new UnsupportedOperationException("This class must not get instantiated"); - } - - @ColorInt - public static int readBrandMainColor(@NonNull Context context) { - final var sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext()); - DeckLog.log("--- Read:", context.getString(R.string.shared_preference_theme_main)); - return sharedPreferences.getInt(context.getString(R.string.shared_preference_theme_main), context.getApplicationContext().getResources().getColor(R.color.defaultBrand)); - } - - public static void saveBrandColors(@NonNull Context context, @ColorInt int mainColor) { - final var editor = PreferenceManager.getDefaultSharedPreferences(context).edit(); - DeckLog.log("--- Write:", context.getString(R.string.shared_preference_theme_main), "|", mainColor); - editor.putInt(context.getString(R.string.shared_preference_theme_main), mainColor); - editor.apply(); - } - - public static void clearBrandColors(@NonNull Context context) { - final var editor = PreferenceManager.getDefaultSharedPreferences(context).edit(); - DeckLog.log("--- Remove:", context.getString(R.string.shared_preference_theme_main)); - editor.remove(context.getString(R.string.shared_preference_theme_main)); - editor.apply(); - } - - /** - * 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 getSecondaryForegroundColorDependingOnTheme(@NonNull Context context, @ColorInt int mainColor) { - if (contrastRatioIsSufficient(mainColor, ContextCompat.getColor(context, R.color.primary))) { - return mainColor; - } - DeckLog.verbose("Contrast ratio between brand color", String.format("#%06X", (0xFFFFFF & mainColor)), "and primary theme background is too low. Falling back to WHITE/BLACK as brand color."); - return isDarkTheme(context) ? Color.WHITE : Color.BLACK; - } - - public static void applyBrandToFAB(@ColorInt int mainColor, @NonNull FloatingActionButton fab) { - final boolean contrastRatioIsSufficient = contrastRatioIsSufficientBigAreas(mainColor, ContextCompat.getColor(fab.getContext(), R.color.primary)); - fab.setSupportBackgroundTintList(ColorStateList.valueOf(contrastRatioIsSufficient - ? mainColor - : ContextCompat.getColor(fab.getContext(), R.color.accent))); - fab.setColorFilter(contrastRatioIsSufficient ? ColorUtil.INSTANCE.getForegroundColorForBackgroundColor(mainColor) : mainColor); - } - - public static void applyBrandToExtendedFAB(@ColorInt int mainColor, @NonNull ExtendedFloatingActionButton extendedFab) { - final boolean contrastRatioIsSufficient = contrastRatioIsSufficientBigAreas(mainColor, ContextCompat.getColor(extendedFab.getContext(), R.color.primary)); - @ColorInt final int color = contrastRatioIsSufficient - ? mainColor - : ContextCompat.getColor(extendedFab.getContext(), R.color.accent); - extendedFab.setTextColor(ColorUtil.INSTANCE.getForegroundColorForBackgroundColor(color)); - extendedFab.setIconTint(ColorStateList.valueOf(ColorUtil.INSTANCE.getForegroundColorForBackgroundColor(color))); - extendedFab.setBackgroundTintList(ColorStateList.valueOf(color)); - } - - public static void applyBrandToPrimaryTabLayout(@ColorInt int mainColor, @NonNull TabLayout tabLayout) { - @ColorInt final int finalMainColor = getSecondaryForegroundColorDependingOnTheme(tabLayout.getContext(), mainColor); - tabLayout.setBackgroundColor(ContextCompat.getColor(tabLayout.getContext(), R.color.primary)); - final boolean contrastRatioIsSufficient = ColorUtil.INSTANCE.getContrastRatio(mainColor, ContextCompat.getColor(tabLayout.getContext(), R.color.primary)) > 1.7d; - tabLayout.setSelectedTabIndicatorColor(contrastRatioIsSufficient ? mainColor : finalMainColor); - } - - public static void applyBrandToEditTextInputLayout(@ColorInt int color, @NonNull TextInputLayout til) { - final int colorPrimary = ContextCompat.getColor(til.getContext(), R.color.primary); - final int colorAccent = ContextCompat.getColor(til.getContext(), R.color.accent); - final var colorDanger = ColorStateList.valueOf(ContextCompat.getColor(til.getContext(), R.color.danger)); - til.setBoxStrokeColor(contrastRatioIsSufficientBigAreas(color, colorPrimary) ? color : colorAccent); - til.setHintTextColor(ColorStateList.valueOf(contrastRatioIsSufficient(color, colorPrimary) ? color : colorAccent)); - til.setErrorTextColor(colorDanger); - til.setBoxStrokeErrorColor(colorDanger); - til.setErrorIconTintList(colorDanger); - } - - public static void tintMenuIcon(@NonNull MenuItem menuItem, @ColorInt int color) { - var drawable = menuItem.getIcon(); - if (drawable != null) { - drawable = DrawableCompat.wrap(drawable); - DrawableCompat.setTint(drawable, color); - menuItem.setIcon(drawable); - } - } - - @ColorInt - public static int getAttribute(@NonNull Context context, @AttrRes int id) { - final var typedValue = new TypedValue(); - context.getTheme().resolveAttribute(id, typedValue, true); - return typedValue.data; - } -} diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/AbstractCardViewHolder.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/AbstractCardViewHolder.java index eb25d1270..6a54d16c6 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/AbstractCardViewHolder.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/AbstractCardViewHolder.java @@ -9,7 +9,6 @@ import android.widget.LinearLayout; import android.widget.TextView; import androidx.annotation.CallSuper; -import androidx.annotation.ColorInt; import androidx.annotation.MenuRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -36,6 +35,7 @@ import it.niedermann.nextcloud.deck.util.AttachmentUtil; import it.niedermann.nextcloud.deck.util.DateUtil; import it.niedermann.nextcloud.deck.util.MimeTypeUtil; import it.niedermann.nextcloud.deck.util.ViewUtil; +import scheme.Scheme; public abstract class AbstractCardViewHolder extends RecyclerView.ViewHolder { @@ -47,7 +47,7 @@ public abstract class AbstractCardViewHolder extends RecyclerView.ViewHolder { * Removes all {@link OnClickListener} and {@link OnLongClickListener} */ @CallSuper - public void bind(@NonNull FullCard fullCard, @NonNull Account account, @Nullable Long boardRemoteId, boolean hasEditPermission, @MenuRes int optionsMenu, @NonNull CardOptionsItemSelectedListener optionsItemsSelectedListener, @NonNull String counterMaxValue, @ColorInt int mainColor) { + public void bind(@NonNull FullCard fullCard, @NonNull Account account, @Nullable Long boardRemoteId, boolean hasEditPermission, @MenuRes int optionsMenu, @NonNull CardOptionsItemSelectedListener optionsItemsSelectedListener, @NonNull String counterMaxValue, @NonNull Scheme scheme) { final var context = itemView.getContext(); bindCardClickListener(null); @@ -56,7 +56,10 @@ public abstract class AbstractCardViewHolder extends RecyclerView.ViewHolder { getCardMenu().setVisibility(hasEditPermission ? View.VISIBLE : View.GONE); getCardTitle().setText(fullCard.getCard().getTitle().trim()); - DrawableCompat.setTint(getNotSyncedYet().getDrawable(), mainColor); + DrawableCompat.setTint(getNotSyncedYet().getDrawable(), scheme.getOnPrimaryContainer()); + // TODO should be discussed with UX + // utils.material.themeCardView(getCard()); + getNotSyncedYet().setVisibility(DBStatus.LOCAL_EDITED.equals(fullCard.getStatusEnum()) ? View.VISIBLE : View.GONE); if (fullCard.getCard().getDueDate() != null) { diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/CardAdapter.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/CardAdapter.java index 1c0567793..1d003ec78 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/CardAdapter.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/CardAdapter.java @@ -1,7 +1,6 @@ package it.niedermann.nextcloud.deck.ui.card; import static androidx.preference.PreferenceManager.getDefaultSharedPreferences; -import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.getSecondaryForegroundColorDependingOnTheme; import static it.niedermann.nextcloud.deck.util.MimeTypeUtil.TEXT_PLAIN; import android.app.Activity; @@ -12,7 +11,6 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.StringRes; @@ -39,12 +37,14 @@ import it.niedermann.nextcloud.deck.model.Stack; import it.niedermann.nextcloud.deck.model.full.FullCard; import it.niedermann.nextcloud.deck.persistence.sync.SyncManager; import it.niedermann.nextcloud.deck.ui.MainViewModel; -import it.niedermann.nextcloud.deck.ui.branding.Branded; import it.niedermann.nextcloud.deck.ui.exception.ExceptionDialogFragment; import it.niedermann.nextcloud.deck.ui.movecard.MoveCardDialogFragment; +import it.niedermann.nextcloud.deck.ui.theme.ThemeUtils; +import it.niedermann.nextcloud.deck.ui.theme.Themed; import it.niedermann.nextcloud.deck.util.CardUtil; +import scheme.Scheme; -public class CardAdapter extends RecyclerView.Adapter<AbstractCardViewHolder> implements DragAndDropAdapter<FullCard>, CardOptionsItemSelectedListener, Branded { +public class CardAdapter extends RecyclerView.Adapter<AbstractCardViewHolder> implements DragAndDropAdapter<FullCard>, CardOptionsItemSelectedListener, Themed { private final ExecutorService executor; private final boolean compactMode; @@ -61,8 +61,8 @@ public class CardAdapter extends RecyclerView.Adapter<AbstractCardViewHolder> im protected List<FullCard> cardList = new ArrayList<>(); @NonNull protected String counterMaxValue; - @ColorInt - protected int mainColor; + @NonNull + protected Scheme scheme; @StringRes private final int shareLinkRes; protected final int maxCoverImages; @@ -79,7 +79,7 @@ public class CardAdapter extends RecyclerView.Adapter<AbstractCardViewHolder> im this.stackId = stackId; this.mainViewModel = mainViewModel; this.selectCardListener = selectCardListener; - this.mainColor = ContextCompat.getColor(this.activity, R.color.defaultBrand); + this.scheme = ThemeUtils.createScheme(ContextCompat.getColor(this.activity, R.color.defaultBrand), this.activity); this.compactMode = getDefaultSharedPreferences(this.activity).getBoolean(this.activity.getString(R.string.pref_key_compact), false); this.maxCoverImages = PreferenceManager.getDefaultSharedPreferences(activity).getBoolean(activity.getString(R.string.pref_key_cover_images), true) ? activity.getResources().getInteger(R.integer.max_cover_images) @@ -124,7 +124,7 @@ public class CardAdapter extends RecyclerView.Adapter<AbstractCardViewHolder> im @Override public void onBindViewHolder(@NonNull AbstractCardViewHolder viewHolder, int position) { @NonNull final var fullCard = cardList.get(position); - viewHolder.bind(fullCard, mainViewModel.getCurrentAccount(), mainViewModel.getCurrentBoardRemoteId(), mainViewModel.currentBoardHasEditPermission(), R.menu.card_menu, this, counterMaxValue, mainColor); + viewHolder.bind(fullCard, mainViewModel.getCurrentAccount(), mainViewModel.getCurrentBoardRemoteId(), mainViewModel.currentBoardHasEditPermission(), R.menu.card_menu, this, counterMaxValue, scheme); // Only enable details view if there is no one waiting for selecting a card. viewHolder.bindCardClickListener((v) -> { @@ -184,8 +184,8 @@ public class CardAdapter extends RecyclerView.Adapter<AbstractCardViewHolder> im } @Override - public void applyBrand(int mainColor) { - this.mainColor = getSecondaryForegroundColorDependingOnTheme(activity, mainColor); + public void applyTheme(int color) { + this.scheme = ThemeUtils.createScheme(color, activity); notifyDataSetChanged(); } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/CompactCardViewHolder.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/CompactCardViewHolder.java index f019551fe..fd93035de 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/CompactCardViewHolder.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/CompactCardViewHolder.java @@ -6,7 +6,6 @@ import android.view.View.OnLongClickListener; import android.widget.ImageView; import android.widget.TextView; -import androidx.annotation.ColorInt; import androidx.annotation.MenuRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -19,6 +18,7 @@ import it.niedermann.nextcloud.deck.databinding.ItemCardCompactBinding; import it.niedermann.nextcloud.deck.model.Account; import it.niedermann.nextcloud.deck.model.Label; import it.niedermann.nextcloud.deck.model.full.FullCard; +import scheme.Scheme; public class CompactCardViewHolder extends AbstractCardViewHolder { private final ItemCardCompactBinding binding; @@ -35,8 +35,8 @@ public class CompactCardViewHolder extends AbstractCardViewHolder { * Removes all {@link OnClickListener} and {@link OnLongClickListener} */ @Override - public void bind(@NonNull FullCard fullCard, @NonNull Account account, @Nullable Long boardRemoteId, boolean hasEditPermission, @MenuRes int optionsMenu, @NonNull CardOptionsItemSelectedListener optionsItemsSelectedListener, @NonNull String counterMaxValue, @ColorInt int mainColor) { - super.bind(fullCard, account, boardRemoteId, hasEditPermission, optionsMenu, optionsItemsSelectedListener, counterMaxValue, mainColor); + public void bind(@NonNull FullCard fullCard, @NonNull Account account, @Nullable Long boardRemoteId, boolean hasEditPermission, @MenuRes int optionsMenu, @NonNull CardOptionsItemSelectedListener optionsItemsSelectedListener, @NonNull String counterMaxValue, @NonNull Scheme scheme) { + super.bind(fullCard, account, boardRemoteId, hasEditPermission, optionsMenu, optionsItemsSelectedListener, counterMaxValue, scheme); setupCoverImages(account, binding.coverImages, fullCard, Math.min(maxCoverImagesCount, 1)); diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/DefaultCardViewHolder.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/DefaultCardViewHolder.java index b7e6f911b..2641bf3ad 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/DefaultCardViewHolder.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/DefaultCardViewHolder.java @@ -7,7 +7,6 @@ import android.view.View.OnLongClickListener; import android.widget.ImageView; import android.widget.TextView; -import androidx.annotation.ColorInt; import androidx.annotation.MenuRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -19,6 +18,7 @@ import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.ItemCardDefaultBinding; import it.niedermann.nextcloud.deck.model.Account; import it.niedermann.nextcloud.deck.model.full.FullCard; +import scheme.Scheme; public class DefaultCardViewHolder extends AbstractCardViewHolder { private final ItemCardDefaultBinding binding; @@ -35,8 +35,8 @@ public class DefaultCardViewHolder extends AbstractCardViewHolder { * Removes all {@link OnClickListener} and {@link OnLongClickListener} */ @Override - public void bind(@NonNull FullCard fullCard, @NonNull Account account, @Nullable Long boardRemoteId, boolean hasEditPermission, @MenuRes int optionsMenu, @NonNull CardOptionsItemSelectedListener optionsItemsSelectedListener, @NonNull String counterMaxValue, @ColorInt int mainColor) { - super.bind(fullCard, account, boardRemoteId, hasEditPermission, optionsMenu, optionsItemsSelectedListener, counterMaxValue, mainColor); + public void bind(@NonNull FullCard fullCard, @NonNull Account account, @Nullable Long boardRemoteId, boolean hasEditPermission, @MenuRes int optionsMenu, @NonNull CardOptionsItemSelectedListener optionsItemsSelectedListener, @NonNull String counterMaxValue, @NonNull Scheme scheme) { + super.bind(fullCard, account, boardRemoteId, hasEditPermission, optionsMenu, optionsItemsSelectedListener, counterMaxValue, scheme); final var context = itemView.getContext(); 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 9fa8c3200..895da6bd1 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 @@ -1,8 +1,6 @@ package it.niedermann.nextcloud.deck.ui.card; import static it.niedermann.nextcloud.deck.persistence.sync.adapters.db.util.LiveDataHelper.observeOnce; -import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.applyBrandToPrimaryTabLayout; -import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.tintMenuIcon; import android.content.Context; import android.content.Intent; @@ -32,6 +30,7 @@ import it.niedermann.nextcloud.deck.model.full.FullCard; import it.niedermann.nextcloud.deck.model.ocs.Version; import it.niedermann.nextcloud.deck.ui.MainActivity; import it.niedermann.nextcloud.deck.ui.exception.ExceptionHandler; +import it.niedermann.nextcloud.deck.ui.theme.ThemeUtils; import it.niedermann.nextcloud.deck.util.CardUtil; public class EditActivity extends AppCompatActivity { @@ -86,7 +85,7 @@ public class EditActivity extends AppCompatActivity { setContentView(binding.getRoot()); setSupportActionBar(binding.toolbar); - viewModel.getBrandingColor().observe(this, this::applyBoardBranding); + viewModel.getBoardColor().observe(this, this::applyTheme); loadDataFromIntent(); } @@ -94,7 +93,7 @@ public class EditActivity extends AppCompatActivity { @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); - viewModel.setBrandingColor(ContextCompat.getColor(this, R.color.primary)); + viewModel.setBoardColor(ContextCompat.getColor(this, R.color.primary)); setIntent(intent); loadDataFromIntent(); } @@ -129,7 +128,7 @@ public class EditActivity extends AppCompatActivity { } observeOnce(viewModel.getFullBoardById(account.getId(), boardLocalId), EditActivity.this, (fullBoard -> { - viewModel.setBrandingColor(fullBoard.getBoard().getColor()); + viewModel.setBoardColor(fullBoard.getBoard().getColor()); viewModel.setCanEdit(fullBoard.getBoard().isPermissionEdit()); invalidateOptionsMenu(); observeOnce(viewModel.getFullCardWithProjectsByLocalId(account.getId(), cardLocalId), EditActivity.this, (fullCard) -> { @@ -155,9 +154,11 @@ public class EditActivity extends AppCompatActivity { public boolean onCreateOptionsMenu(@NonNull Menu menu) { if (viewModel.canEdit()) { getMenuInflater().inflate(R.menu.card_edit_menu, menu); - @ColorInt final int colorAccent = ContextCompat.getColor(this, R.color.accent); + @ColorInt final int color = ContextCompat.getColor(this, R.color.accent); + final var utils = ThemeUtils.of(color, this); + for (int i = 0; i < menu.size(); i++) { - tintMenuIcon(menu.getItem(i), colorAccent); + utils.platform.colorToolbarMenuIcon(this, menu.getItem(i)); } } else { menu.clear(); @@ -175,7 +176,7 @@ public class EditActivity extends AppCompatActivity { @Override public boolean onSupportNavigateUp() { - if(isTaskRoot()) { + if (isTaskRoot()) { Intent intent = new Intent(EditActivity.this, MainActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(intent); @@ -281,14 +282,18 @@ public class EditActivity extends AppCompatActivity { super.finish(); } - private void applyBoardBranding(int mainColor) { + private void applyTheme(int color) { final var navigationIcon = binding.toolbar.getNavigationIcon(); if (navigationIcon == null) { DeckLog.error("Expected navigationIcon to be present."); } else { DrawableCompat.setTint(binding.toolbar.getNavigationIcon(), ContextCompat.getColor(this, R.color.accent)); } - applyBrandToPrimaryTabLayout(mainColor, binding.tabLayout); + + final var utils = ThemeUtils.of(color, this); + + utils.platform.colorEditText(binding.title); + utils.deck.themeTabLayout(binding.tabLayout); } @NonNull diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/EditCardViewModel.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/EditCardViewModel.java index e97973ee0..f4c652f96 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/EditCardViewModel.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/EditCardViewModel.java @@ -45,14 +45,14 @@ public class EditCardViewModel extends AndroidViewModel { private boolean hasCommentsAbility = false; private boolean pendingSaveOperation = false; private boolean canEdit = false; - private final MutableLiveData<Integer> brandingColor$ = new MutableLiveData<>(); + private final MutableLiveData<Integer> boardColor$ = new MutableLiveData<>(); private final SharedPreferences sharedPreferences; private final MutableLiveData<Boolean> descriptionIsPreview = new MutableLiveData<>(false); public EditCardViewModel(@NonNull Application application) { super(application); this.syncManager = new SyncManager(application); - this.brandingColor$.setValue(ContextCompat.getColor(application, R.color.primary)); + this.boardColor$.setValue(ContextCompat.getColor(application, R.color.primary)); sharedPreferences = PreferenceManager.getDefaultSharedPreferences(application); } @@ -87,12 +87,12 @@ public class EditCardViewModel extends AndroidViewModel { .apply(); } - public LiveData<Integer> getBrandingColor() { - return distinctUntilChanged(this.brandingColor$); + public LiveData<Integer> getBoardColor() { + return distinctUntilChanged(this.boardColor$); } - public void setBrandingColor(@ColorInt int brandingColor) { - this.brandingColor$.setValue(brandingColor); + public void setBoardColor(@ColorInt int color) { + this.boardColor$.setValue(color); } /** diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/NewCardDialog.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/NewCardDialog.java index 2801c0d7a..fd6231029 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/NewCardDialog.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/NewCardDialog.java @@ -5,7 +5,6 @@ import static androidx.lifecycle.Transformations.distinctUntilChanged; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; -import android.content.res.ColorStateList; import android.os.Bundle; import android.text.Editable; import android.text.TextWatcher; @@ -31,9 +30,9 @@ import it.niedermann.nextcloud.deck.databinding.DialogNewCardBinding; import it.niedermann.nextcloud.deck.exceptions.OfflineException; import it.niedermann.nextcloud.deck.model.Account; import it.niedermann.nextcloud.deck.model.full.FullCard; -import it.niedermann.nextcloud.deck.ui.branding.BrandingUtil; import it.niedermann.nextcloud.deck.ui.exception.ExceptionDialogFragment; import it.niedermann.nextcloud.deck.ui.preparecreate.PrepareCreateViewModel; +import it.niedermann.nextcloud.deck.ui.theme.ThemeUtils; public class NewCardDialog extends DialogFragment implements DialogInterface.OnClickListener { @@ -96,8 +95,10 @@ public class NewCardDialog extends DialogFragment implements DialogInterface.OnC dialog.getButton(DialogInterface.BUTTON_NEGATIVE).setOnClickListener(v -> onClick(dialog, DialogInterface.BUTTON_NEGATIVE)); }); - BrandingUtil.applyBrandToEditTextInputLayout(color, binding.inputWrapper); - binding.progressCircular.setIndeterminateTintList(ColorStateList.valueOf(BrandingUtil.getSecondaryForegroundColorDependingOnTheme(requireContext(), color))); + final var utils = ThemeUtils.of(color, requireContext()); + + utils.material.colorTextInputLayout(binding.inputWrapper); + utils.platform.colorCircularProgressBar(binding.progressCircular); binding.input.addTextChangedListener(new TextWatcher() { @Override diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/assignee/CardAssigneeDialog.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/assignee/CardAssigneeDialog.java index 6a15a4f6c..9a46cec65 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/assignee/CardAssigneeDialog.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/assignee/CardAssigneeDialog.java @@ -20,9 +20,9 @@ import com.bumptech.glide.Glide; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.DialogPreviewBinding; import it.niedermann.nextcloud.deck.model.User; -import it.niedermann.nextcloud.deck.ui.branding.DeleteAlertDialogBuilder; import it.niedermann.nextcloud.deck.ui.card.EditCardViewModel; import it.niedermann.nextcloud.deck.ui.card.attachments.previewdialog.PreviewDialog; +import it.niedermann.nextcloud.deck.ui.theme.DeleteAlertDialogBuilder; /** * TODO maybe this can be merged with {@link PreviewDialog} diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/AttachmentViewHolder.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/AttachmentViewHolder.java index cb77ae89b..ddc35f76e 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/AttachmentViewHolder.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/AttachmentViewHolder.java @@ -17,7 +17,7 @@ import it.niedermann.nextcloud.deck.model.Account; import it.niedermann.nextcloud.deck.model.Attachment; import it.niedermann.nextcloud.deck.model.enums.DBStatus; import it.niedermann.nextcloud.deck.model.enums.EAttachmentType; -import it.niedermann.nextcloud.deck.ui.branding.BrandingUtil; +import it.niedermann.nextcloud.deck.ui.theme.ThemeUtils; import it.niedermann.nextcloud.deck.util.AttachmentUtil; public abstract class AttachmentViewHolder extends RecyclerView.ViewHolder { @@ -25,10 +25,12 @@ public abstract class AttachmentViewHolder extends RecyclerView.ViewHolder { super(itemView); } - public void bind(@NonNull Account account, @NonNull MenuInflater menuInflater, @NonNull FragmentManager fragmentManager, Long cardRemoteId, Attachment attachment, @Nullable View.OnClickListener onClickListener, @ColorInt int mainColor) { + public void bind(@NonNull Account account, @NonNull MenuInflater menuInflater, @NonNull FragmentManager fragmentManager, Long cardRemoteId, Attachment attachment, @Nullable View.OnClickListener onClickListener, @ColorInt int color) { final String attachmentUri = (attachment.getId() == null || cardRemoteId == null) ? attachment.getLocalPath() - : AttachmentUtil.getCopyDownloadUrl(account, cardRemoteId, attachment); setNotSyncedYetStatus(!DBStatus.LOCAL_EDITED.equals(attachment.getStatusEnum()), mainColor); + : AttachmentUtil.getCopyDownloadUrl(account, cardRemoteId, attachment); + + setNotSyncedYetStatus(!DBStatus.LOCAL_EDITED.equals(attachment.getStatusEnum()), color); itemView.setOnCreateContextMenuListener((menu, v, menuInfo) -> { menuInflater.inflate(R.menu.attachment_menu, menu); if(EAttachmentType.DECK_FILE.equals(attachment.getType())) { @@ -51,9 +53,11 @@ public abstract class AttachmentViewHolder extends RecyclerView.ViewHolder { abstract protected ImageView getPreview(); - protected void setNotSyncedYetStatus(boolean synced, @ColorInt int mainColor) { + protected void setNotSyncedYetStatus(boolean synced, @ColorInt int color) { final var notSyncedYet = getNotSyncedYetStatusIcon(); - DrawableCompat.setTint(notSyncedYet.getDrawable(), BrandingUtil.getSecondaryForegroundColorDependingOnTheme(notSyncedYet.getContext(), mainColor)); + final var scheme = ThemeUtils.createScheme(color, notSyncedYet.getContext()); + + DrawableCompat.setTint(notSyncedYet.getDrawable(), scheme.getOnPrimaryContainer()); notSyncedYet.setVisibility(synced ? View.GONE : View.VISIBLE); } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/CardAttachmentAdapter.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/CardAttachmentAdapter.java index 3b18515c3..57fe8d1e3 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/CardAttachmentAdapter.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/CardAttachmentAdapter.java @@ -28,11 +28,11 @@ import it.niedermann.nextcloud.deck.databinding.ItemAttachmentImageBinding; import it.niedermann.nextcloud.deck.model.Account; import it.niedermann.nextcloud.deck.model.Attachment; import it.niedermann.nextcloud.deck.ui.attachments.AttachmentsActivity; -import it.niedermann.nextcloud.deck.ui.branding.Branded; +import it.niedermann.nextcloud.deck.ui.theme.Themed; import it.niedermann.nextcloud.deck.util.MimeTypeUtil; @SuppressWarnings("WeakerAccess") -public class CardAttachmentAdapter extends RecyclerView.Adapter<AttachmentViewHolder> implements Branded { +public class CardAttachmentAdapter extends RecyclerView.Adapter<AttachmentViewHolder> implements Themed { public static final int VIEW_TYPE_DEFAULT = 2; public static final int VIEW_TYPE_IMAGE = 1; @@ -42,7 +42,7 @@ public class CardAttachmentAdapter extends RecyclerView.Adapter<AttachmentViewHo @NonNull private final MenuInflater menuInflater; @ColorInt - private int mainColor; + private int color; private final Account account; @Nullable private Long cardRemoteId = null; @@ -116,7 +116,7 @@ public class CardAttachmentAdapter extends RecyclerView.Adapter<AttachmentViewHo break; } } - holder.bind(account, menuInflater, fragmentManager, cardRemoteId, attachment, onClickListener, mainColor); + holder.bind(account, menuInflater, fragmentManager, cardRemoteId, attachment, onClickListener, color); } @Override @@ -167,8 +167,8 @@ public class CardAttachmentAdapter extends RecyclerView.Adapter<AttachmentViewHo } @Override - public void applyBrand(@ColorInt int mainColor) { - this.mainColor = mainColor; + public void applyTheme(@ColorInt int color) { + this.color = color; notifyDataSetChanged(); } } 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 01706f9b1..e29f53904 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 @@ -15,14 +15,12 @@ import static com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_ import static com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_HIDDEN; import static java.net.HttpURLConnection.HTTP_CONFLICT; import static it.niedermann.nextcloud.deck.persistence.sync.adapters.db.util.LiveDataHelper.observeOnce; -import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.applyBrandToFAB; 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; import static it.niedermann.nextcloud.deck.util.FilesUtil.copyContentUriToTempFile; import android.content.ContentResolver; import android.content.Intent; -import android.content.res.ColorStateList; import android.graphics.Bitmap; import android.net.Uri; import android.os.Bundle; @@ -49,6 +47,7 @@ import androidx.recyclerview.widget.RecyclerView; import com.google.android.material.bottomsheet.BottomSheetBehavior; import com.google.android.material.snackbar.Snackbar; +import com.nextcloud.android.common.ui.theme.utils.ColorRole; import com.nextcloud.android.sso.exceptions.NextcloudHttpRequestFailedException; import java.io.File; @@ -73,7 +72,6 @@ import it.niedermann.nextcloud.deck.model.Attachment; import it.niedermann.nextcloud.deck.model.Card; import it.niedermann.nextcloud.deck.model.enums.DBStatus; import it.niedermann.nextcloud.deck.persistence.sync.SyncManager; -import it.niedermann.nextcloud.deck.ui.branding.BrandedSnackbar; import it.niedermann.nextcloud.deck.ui.card.EditCardViewModel; import it.niedermann.nextcloud.deck.ui.card.attachments.picker.AbstractPickerAdapter; import it.niedermann.nextcloud.deck.ui.card.attachments.picker.ContactAdapter; @@ -84,7 +82,8 @@ import it.niedermann.nextcloud.deck.ui.card.attachments.previewdialog.PreviewDia import it.niedermann.nextcloud.deck.ui.card.attachments.previewdialog.PreviewDialogViewModel; import it.niedermann.nextcloud.deck.ui.exception.ExceptionDialogFragment; import it.niedermann.nextcloud.deck.ui.takephoto.TakePhotoActivity; -import it.niedermann.nextcloud.deck.util.DeckColorUtil; +import it.niedermann.nextcloud.deck.ui.theme.ThemeUtils; +import it.niedermann.nextcloud.deck.ui.theme.ThemedSnackbar; import it.niedermann.nextcloud.deck.util.JavaCompressor; import it.niedermann.nextcloud.deck.util.MimeTypeUtil; import it.niedermann.nextcloud.deck.util.VCardUtil; @@ -223,7 +222,7 @@ public class CardAttachmentsFragment extends Fragment implements AttachmentDelet } final var sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext()); compressImagesOnUpload = sharedPreferences.getBoolean(getString(R.string.pref_key_compress_image_attachments), true); - editViewModel.getBrandingColor().observe(getViewLifecycleOwner(), this::applyBrand); + editViewModel.getBoardColor().observe(getViewLifecycleOwner(), this::applyTheme); return binding.getRoot(); } @@ -416,7 +415,7 @@ public class CardAttachmentsFragment extends Fragment implements AttachmentDelet for (final var existingAttachment : editViewModel.getFullCard().getAttachments()) { final String existingPath = existingAttachment.getLocalPath(); if (existingPath != null && existingPath.equals(fileToUpload.getAbsolutePath())) { - BrandedSnackbar.make(binding.coordinatorLayout, R.string.attachment_already_exists, Snackbar.LENGTH_LONG).show(); + ThemedSnackbar.make(binding.coordinatorLayout, R.string.attachment_already_exists, Snackbar.LENGTH_LONG).show(); return; } } @@ -451,7 +450,7 @@ public class CardAttachmentsFragment extends Fragment implements AttachmentDelet // https://github.com/stefan-niedermann/nextcloud-deck/issues/534 editViewModel.getFullCard().getAttachments().remove(a); adapter.removeAttachment(a); - BrandedSnackbar.make(binding.coordinatorLayout, R.string.attachment_already_exists, Snackbar.LENGTH_LONG).show(); + ThemedSnackbar.make(binding.coordinatorLayout, R.string.attachment_already_exists, Snackbar.LENGTH_LONG).show(); } else { ExceptionDialogFragment.newInstance(new UploadAttachmentFailedException("Unknown URI scheme", throwable), editViewModel.getAccount()).show(getChildFragmentManager(), ExceptionDialogFragment.class.getSimpleName()); } @@ -521,13 +520,15 @@ public class CardAttachmentsFragment extends Fragment implements AttachmentDelet this.clickedItemPosition = position; } - private void applyBrand(@ColorInt int boardColor) { - applyBrandToFAB(boardColor, binding.fab); - @ColorInt final int finalMainColor = DeckColorUtil.contrastRatioIsSufficient(boardColor, primaryColor) ? boardColor : accentColor; - final ColorStateList list = new ColorStateList(new int[][]{new int[]{android.R.attr.state_checked}, new int[]{}}, new int[]{finalMainColor, accentColor}); - binding.bottomNavigation.setItemIconTintList(list); - binding.bottomNavigation.setItemTextColor(list); - adapter.applyBrand(boardColor); + private void applyTheme(@ColorInt int color) { + final var utils = ThemeUtils.of(color, requireContext()); + + utils.material.themeFAB(binding.fab); + utils.deck.colorBottomNavigationView(binding.bottomNavigation); + utils.platform.colorViewBackground(binding.pickerHeader, ColorRole.SURFACE); + utils.platform.colorViewBackground(binding.pickerRecyclerView, ColorRole.SURFACE); + + adapter.applyTheme(color); } public static Fragment newInstance() { diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/DefaultAttachmentViewHolder.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/DefaultAttachmentViewHolder.java index b41a4e9d1..21f858218 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/DefaultAttachmentViewHolder.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/DefaultAttachmentViewHolder.java @@ -1,5 +1,8 @@ package it.niedermann.nextcloud.deck.ui.card.attachments; +import static it.niedermann.nextcloud.deck.util.AttachmentUtil.getIconForMimeType; +import static it.niedermann.nextcloud.deck.util.AttachmentUtil.openAttachmentInBrowser; + import android.text.format.Formatter; import android.view.MenuInflater; import android.view.View; @@ -15,9 +18,6 @@ import it.niedermann.nextcloud.deck.model.Account; import it.niedermann.nextcloud.deck.model.Attachment; import it.niedermann.nextcloud.deck.util.DateUtil; -import static it.niedermann.nextcloud.deck.util.AttachmentUtil.getIconForMimeType; -import static it.niedermann.nextcloud.deck.util.AttachmentUtil.openAttachmentInBrowser; - public class DefaultAttachmentViewHolder extends AttachmentViewHolder { private final ItemAttachmentDefaultBinding binding; @@ -37,8 +37,8 @@ public class DefaultAttachmentViewHolder extends AttachmentViewHolder { return binding.notSyncedYet; } - public void bind(@NonNull Account account, @NonNull MenuInflater menuInflater, @NonNull FragmentManager fragmentManager, Long cardRemoteId, Attachment attachment, @Nullable View.OnClickListener onClickListener, @ColorInt int mainColor) { - super.bind(account, menuInflater, fragmentManager, cardRemoteId, attachment, onClickListener, mainColor); + public void bind(@NonNull Account account, @NonNull MenuInflater menuInflater, @NonNull FragmentManager fragmentManager, Long cardRemoteId, Attachment attachment, @Nullable View.OnClickListener onClickListener, @ColorInt int color) { + super.bind(account, menuInflater, fragmentManager, cardRemoteId, attachment, onClickListener, color); getPreview().setImageResource(getIconForMimeType(attachment.getMimetype())); itemView.setOnClickListener((event) -> openAttachmentInBrowser(account, itemView.getContext(), cardRemoteId, attachment)); binding.filename.setText(attachment.getBasename()); diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/DeleteAttachmentDialogFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/DeleteAttachmentDialogFragment.java index 63441bbd4..532e59dce 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/DeleteAttachmentDialogFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/DeleteAttachmentDialogFragment.java @@ -9,7 +9,7 @@ import androidx.fragment.app.DialogFragment; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.model.Attachment; -import it.niedermann.nextcloud.deck.ui.branding.DeleteAlertDialogBuilder; +import it.niedermann.nextcloud.deck.ui.theme.DeleteAlertDialogBuilder; public class DeleteAttachmentDialogFragment extends DialogFragment { diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/ImageAttachmentViewHolder.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/ImageAttachmentViewHolder.java index fae2255f2..72d942618 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/ImageAttachmentViewHolder.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/ImageAttachmentViewHolder.java @@ -36,8 +36,8 @@ public class ImageAttachmentViewHolder extends AttachmentViewHolder { return binding.notSyncedYet; } - public void bind(@NonNull Account account, @NonNull MenuInflater menuInflater, @NonNull FragmentManager fragmentManager, Long cardRemoteId, Attachment attachment, @Nullable View.OnClickListener onClickListener, @ColorInt int mainColor) { - super.bind(account, menuInflater, fragmentManager, cardRemoteId, attachment, onClickListener, mainColor); + public void bind(@NonNull Account account, @NonNull MenuInflater menuInflater, @NonNull FragmentManager fragmentManager, Long cardRemoteId, Attachment attachment, @Nullable View.OnClickListener onClickListener, @ColorInt int color) { + super.bind(account, menuInflater, fragmentManager, cardRemoteId, attachment, onClickListener, color); getPreview().post(() -> { @Nullable final String uri = AttachmentUtil.getThumbnailUrl(account, cardRemoteId, attachment, getPreview().getWidth()); diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/CardCommentsAdapter.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/CardCommentsAdapter.java index 18bf21ceb..b392d5725 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/CardCommentsAdapter.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/CardCommentsAdapter.java @@ -1,7 +1,6 @@ package it.niedermann.nextcloud.deck.ui.card.comments; -import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.getSecondaryForegroundColorDependingOnTheme; -import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.readBrandMainColor; +import static it.niedermann.nextcloud.deck.ui.theme.ThemeUtils.readBrandMainColor; import android.content.Context; import android.view.LayoutInflater; @@ -20,10 +19,12 @@ import it.niedermann.nextcloud.deck.DeckLog; import it.niedermann.nextcloud.deck.databinding.ItemCommentBinding; import it.niedermann.nextcloud.deck.model.Account; import it.niedermann.nextcloud.deck.model.ocs.comment.full.FullDeckComment; +import it.niedermann.nextcloud.deck.ui.theme.ThemeUtils; +import scheme.Scheme; public class CardCommentsAdapter extends RecyclerView.Adapter<ItemCommentViewHolder> { - private final int mainColor; + private final Scheme scheme; @NonNull private final List<FullDeckComment> comments = new ArrayList<>(); @NonNull @@ -46,7 +47,7 @@ public class CardCommentsAdapter extends RecyclerView.Adapter<ItemCommentViewHol this.selectAsReplyListener = selectAsReplyListener; this.fragmentManager = fragmentManager; this.editListener = editListener; - this.mainColor = getSecondaryForegroundColorDependingOnTheme(context, readBrandMainColor(context)); + this.scheme = ThemeUtils.createScheme(readBrandMainColor(context), context); setHasStableIds(true); } @@ -64,7 +65,7 @@ public class CardCommentsAdapter extends RecyclerView.Adapter<ItemCommentViewHol @Override public void onBindViewHolder(@NonNull ItemCommentViewHolder holder, int position) { final var comment = comments.get(position); - holder.bind(comment, account, mainColor, menuInflater, deletedListener, selectAsReplyListener, fragmentManager, (changedText) -> { + holder.bind(comment, account, scheme, menuInflater, deletedListener, selectAsReplyListener, fragmentManager, (changedText) -> { if (!Objects.equals(changedText, comment.getComment().getMessage())) { DeckLog.info("Toggled checkbox in comment with localId", comment.getLocalId()); this.editListener.onCommentEdited(comment.getLocalId(), changedText.toString()); 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 b26ee84c5..abb3b1033 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 @@ -1,7 +1,5 @@ package it.niedermann.nextcloud.deck.ui.card.comments; -import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.applyBrandToEditTextInputLayout; - import android.app.Dialog; import android.content.Context; import android.os.Bundle; @@ -20,9 +18,10 @@ import java.util.Objects; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.DialogAddCommentBinding; -import it.niedermann.nextcloud.deck.ui.branding.BrandedDialogFragment; +import it.niedermann.nextcloud.deck.ui.theme.ThemeUtils; +import it.niedermann.nextcloud.deck.ui.theme.ThemedDialogFragment; -public class CardCommentsEditDialogFragment extends BrandedDialogFragment { +public class CardCommentsEditDialogFragment extends ThemedDialogFragment { private static final String BUNDLE_KEY_COMMENT_ID = "commentId"; private static final String BUNDLE_KEY_COMMENT_MESSAGE = "commentMessage"; private CommentEditedListener addCommentListener; @@ -86,8 +85,10 @@ public class CardCommentsEditDialogFragment extends BrandedDialogFragment { } @Override - public void applyBrand(int mainColor) { - applyBrandToEditTextInputLayout(mainColor, binding.inputWrapper); + public void applyTheme(int color) { + final var utils = ThemeUtils.of(color, requireContext()); + + utils.material.colorTextInputLayout(binding.inputWrapper); } } 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 7e90e513a..277eb82c2 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 @@ -2,8 +2,6 @@ package it.niedermann.nextcloud.deck.ui.card.comments; import static android.view.View.GONE; import static android.view.View.VISIBLE; -import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.applyBrandToEditTextInputLayout; -import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.applyBrandToFAB; import android.os.Bundle; import android.text.TextUtils; @@ -33,6 +31,7 @@ import it.niedermann.nextcloud.deck.persistence.sync.SyncManager; import it.niedermann.nextcloud.deck.ui.card.EditActivity; import it.niedermann.nextcloud.deck.ui.card.EditCardViewModel; import it.niedermann.nextcloud.deck.ui.exception.ExceptionDialogFragment; +import it.niedermann.nextcloud.deck.ui.theme.ThemeUtils; import it.niedermann.nextcloud.deck.util.ViewUtil; public class CardCommentsFragment extends Fragment implements CommentEditedListener, CommentDeletedListener, CommentSelectAsReplyListener { @@ -131,7 +130,7 @@ public class CardCommentsFragment extends Fragment implements CommentEditedListe binding.message.requestFocus(); requireActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); } - mainViewModel.getBrandingColor().observe(getViewLifecycleOwner(), this::applyBrand); + mainViewModel.getBoardColor().observe(getViewLifecycleOwner(), this::applyTheme); } @Override @@ -163,9 +162,11 @@ public class CardCommentsFragment extends Fragment implements CommentEditedListe }); } - private void applyBrand(int mainColor) { - applyBrandToFAB(mainColor, binding.fab); - applyBrandToEditTextInputLayout(mainColor, binding.messageWrapper); + private void applyTheme(int color) { + final var utils = ThemeUtils.of(color, requireContext()); + + utils.material.themeFAB(binding.fab); + utils.material.colorTextInputLayout(binding.messageWrapper); } @Override diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/ItemCommentViewHolder.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/ItemCommentViewHolder.java index 88cc1a904..f38e41f93 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/ItemCommentViewHolder.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/ItemCommentViewHolder.java @@ -4,7 +4,6 @@ import android.text.method.LinkMovementMethod; import android.view.MenuInflater; import android.view.View; -import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.appcompat.widget.TooltipCompat; import androidx.core.graphics.drawable.DrawableCompat; @@ -26,6 +25,7 @@ import it.niedermann.nextcloud.deck.model.enums.DBStatus; import it.niedermann.nextcloud.deck.model.ocs.comment.full.FullDeckComment; import it.niedermann.nextcloud.deck.util.DateUtil; import it.niedermann.nextcloud.deck.util.ViewUtil; +import scheme.Scheme; public class ItemCommentViewHolder extends RecyclerView.ViewHolder { private final ItemCommentBinding binding; @@ -38,7 +38,7 @@ public class ItemCommentViewHolder extends RecyclerView.ViewHolder { this.binding.message.setMovementMethod(LinkMovementMethod.getInstance()); } - public void bind(@NonNull FullDeckComment comment, @NonNull Account account, @ColorInt int mainColor, @NonNull MenuInflater inflater, @NonNull CommentDeletedListener deletedListener, @NonNull CommentSelectAsReplyListener selectAsReplyListener, @NonNull FragmentManager fragmentManager, @NonNull Consumer<CharSequence> editListener) { + public void bind(@NonNull FullDeckComment comment, @NonNull Account account, @NonNull Scheme scheme, @NonNull MenuInflater inflater, @NonNull CommentDeletedListener deletedListener, @NonNull CommentSelectAsReplyListener selectAsReplyListener, @NonNull FragmentManager fragmentManager, @NonNull Consumer<CharSequence> editListener) { ViewUtil.addAvatar(binding.avatar, account.getUrl(), comment.getComment().getActorId(), DimensionUtil.INSTANCE.dpToPx(binding.avatar.getContext(), R.dimen.icon_size_details), R.drawable.ic_person_grey600_24dp); final var mentions = new HashMap<String, String>(comment.getComment().getMentions().size()); for (final var mention : comment.getComment().getMentions()) { @@ -80,7 +80,7 @@ public class ItemCommentViewHolder extends RecyclerView.ViewHolder { }); TooltipCompat.setTooltipText(binding.creationDateTime, comment.getComment().getCreationDateTime().atZone(ZoneId.systemDefault()).format(dateFormatter)); - DrawableCompat.setTint(binding.notSyncedYet.getDrawable(), mainColor); + DrawableCompat.setTint(binding.notSyncedYet.getDrawable(), scheme.getOnPrimaryContainer()); binding.notSyncedYet.setVisibility(DBStatus.LOCAL_EDITED.equals(comment.getStatusEnum()) ? View.VISIBLE : View.GONE); if (comment.getParent() == null) { @@ -88,7 +88,7 @@ public class ItemCommentViewHolder extends RecyclerView.ViewHolder { } else { final int commentParentMaxLines = itemView.getContext().getResources().getInteger(R.integer.comment_parent_max_lines); binding.parentContainer.setVisibility(View.VISIBLE); - binding.parentBorder.setBackgroundColor(mainColor); + binding.parentBorder.setBackgroundColor(scheme.getOnPrimaryContainer()); binding.parent.setText(comment.getParent().getMessage()); binding.parent.setOnClickListener((v) -> { final boolean previouslyCollapsed = binding.parent.getMaxLines() == commentParentMaxLines; 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 1a56a195f..4eec53ece 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 @@ -3,7 +3,6 @@ package it.niedermann.nextcloud.deck.ui.card.details; import static android.view.View.GONE; import static android.view.View.INVISIBLE; import static android.view.View.VISIBLE; -import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.applyBrandToEditTextInputLayout; import android.content.res.ColorStateList; import android.graphics.drawable.Drawable; @@ -39,6 +38,7 @@ import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.format.FormatStyle; +import java.util.stream.Stream; import it.niedermann.android.markdown.MarkdownEditor; import it.niedermann.android.util.ColorUtil; @@ -50,15 +50,16 @@ import it.niedermann.nextcloud.deck.databinding.FragmentCardEditTabDetailsBindin 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.ui.branding.BrandedDatePickerDialog; -import it.niedermann.nextcloud.deck.ui.branding.BrandedSnackbar; -import it.niedermann.nextcloud.deck.ui.branding.BrandedTimePickerDialog; import it.niedermann.nextcloud.deck.ui.card.EditCardViewModel; import it.niedermann.nextcloud.deck.ui.card.LabelAutoCompleteAdapter; import it.niedermann.nextcloud.deck.ui.card.UserAutoCompleteAdapter; import it.niedermann.nextcloud.deck.ui.card.assignee.CardAssigneeDialog; import it.niedermann.nextcloud.deck.ui.card.assignee.CardAssigneeListener; import it.niedermann.nextcloud.deck.ui.exception.ExceptionDialogFragment; +import it.niedermann.nextcloud.deck.ui.theme.ThemeUtils; +import it.niedermann.nextcloud.deck.ui.theme.ThemedDatePickerDialog; +import it.niedermann.nextcloud.deck.ui.theme.ThemedSnackbar; +import it.niedermann.nextcloud.deck.ui.theme.ThemedTimePickerDialog; public class CardDetailsFragment extends Fragment implements OnDateSetListener, OnTimeSetListener, CardAssigneeListener { @@ -102,7 +103,7 @@ public class CardDetailsFragment extends Fragment implements OnDateSetListener, @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); - viewModel.getBrandingColor().observe(getViewLifecycleOwner(), this::applyBrand); + viewModel.getBoardColor().observe(getViewLifecycleOwner(), this::applyTheme); } @Override @@ -110,8 +111,8 @@ public class CardDetailsFragment extends Fragment implements OnDateSetListener, super.onResume(); // https://github.com/wdullaer/MaterialDateTimePicker#why-are-my-callbacks-lost-when-the-device-changes-orientation - final var dpd = (DatePickerDialog) getChildFragmentManager().findFragmentByTag(BrandedDatePickerDialog.class.getCanonicalName()); - final var tpd = (TimePickerDialog) getChildFragmentManager().findFragmentByTag(BrandedTimePickerDialog.class.getCanonicalName()); + final var dpd = (DatePickerDialog) getChildFragmentManager().findFragmentByTag(ThemedDatePickerDialog.class.getCanonicalName()); + final var tpd = (TimePickerDialog) getChildFragmentManager().findFragmentByTag(ThemedTimePickerDialog.class.getCanonicalName()); if (tpd != null) tpd.setOnTimeSetListener(this); if (dpd != null) dpd.setOnDateSetListener(this); } @@ -122,15 +123,21 @@ public class CardDetailsFragment extends Fragment implements OnDateSetListener, this.binding = null; } - private void applyBrand(@ColorInt int boardColor) { + private void applyTheme(@ColorInt int color) { + final var utils = ThemeUtils.of(color, requireContext()); + + Stream.of( + binding.labelsWrapper, + binding.dueDateDateWrapper, + binding.dueDateTimeWrapper, + binding.peopleWrapper, + binding.descriptionEditorWrapper + ).forEach(utils.material::colorTextInputLayout); + + binding.descriptionEditor.setSearchColor(color); + binding.descriptionViewer.setSearchColor(color); + // TODO apply correct branding on the BrandedDatePicker - applyBrandToEditTextInputLayout(boardColor, binding.labelsWrapper); - applyBrandToEditTextInputLayout(boardColor, binding.dueDateDateWrapper); - applyBrandToEditTextInputLayout(boardColor, binding.dueDateTimeWrapper); - applyBrandToEditTextInputLayout(boardColor, binding.peopleWrapper); - applyBrandToEditTextInputLayout(boardColor, binding.descriptionEditorWrapper); - binding.descriptionEditor.setSearchColor(boardColor); - binding.descriptionViewer.setSearchColor(boardColor); } private void setupDescription() { @@ -191,8 +198,8 @@ public class CardDetailsFragment extends Fragment implements OnDateSetListener, } else { date = LocalDate.now(); } - BrandedDatePickerDialog.newInstance(this, date.getYear(), date.getMonthValue(), date.getDayOfMonth()) - .show(getChildFragmentManager(), BrandedDatePickerDialog.class.getCanonicalName()); + ThemedDatePickerDialog.newInstance(this, date.getYear(), date.getMonthValue(), date.getDayOfMonth()) + .show(getChildFragmentManager(), ThemedDatePickerDialog.class.getCanonicalName()); }); binding.dueDateTime.setOnClickListener(v -> { @@ -202,8 +209,8 @@ public class CardDetailsFragment extends Fragment implements OnDateSetListener, } else { time = LocalTime.now(); } - BrandedTimePickerDialog.newInstance(this, time.getHour(), time.getMinute(), true) - .show(getChildFragmentManager(), BrandedTimePickerDialog.class.getCanonicalName()); + ThemedTimePickerDialog.newInstance(this, time.getHour(), time.getMinute(), true) + .show(getChildFragmentManager(), ThemedTimePickerDialog.class.getCanonicalName()); }); binding.clearDueDate.setOnClickListener(v -> { @@ -249,7 +256,7 @@ public class CardDetailsFragment extends Fragment implements OnDateSetListener, @Override public void onError(Throwable throwable) { IResponseCallback.super.onError(throwable); - requireActivity().runOnUiThread(() -> BrandedSnackbar.make(requireView(), getString(R.string.error_create_label, newLabel.getTitle()), Snackbar.LENGTH_LONG) + requireActivity().runOnUiThread(() -> ThemedSnackbar.make(requireView(), getString(R.string.error_create_label, newLabel.getTitle()), Snackbar.LENGTH_LONG) .setAction(R.string.simple_more, v -> ExceptionDialogFragment.newInstance(throwable, viewModel.getAccount()).show(getChildFragmentManager(), ExceptionDialogFragment.class.getSimpleName())).show()); } }); @@ -401,9 +408,9 @@ public class CardDetailsFragment extends Fragment implements OnDateSetListener, viewModel.getFullCard().getAssignedUsers().remove(user); adapter.removeUser(user); ((UserAutoCompleteAdapter) binding.people.getAdapter()).include(user); - BrandedSnackbar.make( - requireView(), getString(R.string.unassigned_user, user.getDisplayname()), - Snackbar.LENGTH_LONG) + ThemedSnackbar.make( + requireView(), getString(R.string.unassigned_user, user.getDisplayname()), + Snackbar.LENGTH_LONG) .setAction(R.string.simple_undo, v1 -> { viewModel.getFullCard().getAssignedUsers().add(user); ((UserAutoCompleteAdapter) binding.people.getAdapter()).exclude(user); diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterDialogFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterDialogFragment.java index 50e3a05c3..75b30dc1c 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterDialogFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterDialogFragment.java @@ -1,13 +1,11 @@ package it.niedermann.nextcloud.deck.ui.filter; -import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.getSecondaryForegroundColorDependingOnTheme; - import android.app.Dialog; +import android.graphics.Color; import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; import android.os.Bundle; -import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.core.content.ContextCompat; import androidx.fragment.app.DialogFragment; @@ -19,13 +17,13 @@ import androidx.viewpager2.widget.ViewPager2; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.tabs.TabLayoutMediator; -import it.niedermann.android.util.ColorUtil; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.DialogFilterBinding; import it.niedermann.nextcloud.deck.model.enums.EDueType; -import it.niedermann.nextcloud.deck.ui.branding.BrandedDialogFragment; +import it.niedermann.nextcloud.deck.ui.theme.ThemeUtils; +import it.niedermann.nextcloud.deck.ui.theme.ThemedDialogFragment; -public class FilterDialogFragment extends BrandedDialogFragment { +public class FilterDialogFragment extends ThemedDialogFragment { private DialogFilterBinding binding; private FilterViewModel filterViewModel; @@ -108,11 +106,10 @@ public class FilterDialogFragment extends BrandedDialogFragment { } @Override - public void applyBrand(int mainColor) { - @ColorInt final int finalMainColor = getSecondaryForegroundColorDependingOnTheme(binding.tabLayout.getContext(), mainColor); - final boolean contrastRatioIsSufficient = ColorUtil.INSTANCE.getContrastRatio(mainColor, ContextCompat.getColor(binding.tabLayout.getContext(), R.color.primary)) > 1.7d; - binding.tabLayout.setSelectedTabIndicatorColor(contrastRatioIsSufficient ? mainColor : finalMainColor); - indicator.setColorFilter(contrastRatioIsSufficient ? mainColor : finalMainColor, PorterDuff.Mode.SRC_ATOP); + public void applyTheme(int color) { + final var utils = ThemeUtils.of(color, requireContext()); + + utils.deck.themeTabLayout(binding.tabLayout, Color.TRANSPARENT); } private static class TabsPagerAdapter extends FragmentStateAdapter { diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/movecard/MoveCardDialogFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/movecard/MoveCardDialogFragment.java index 95b234439..5dd430d1d 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/movecard/MoveCardDialogFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/movecard/MoveCardDialogFragment.java @@ -4,7 +4,6 @@ import static android.view.View.GONE; import static android.view.View.VISIBLE; import android.content.Context; -import android.content.res.ColorStateList; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; @@ -20,13 +19,13 @@ import it.niedermann.nextcloud.deck.databinding.DialogMoveCardBinding; import it.niedermann.nextcloud.deck.model.Account; import it.niedermann.nextcloud.deck.model.Board; import it.niedermann.nextcloud.deck.model.Stack; -import it.niedermann.nextcloud.deck.ui.branding.BrandedDialogFragment; -import it.niedermann.nextcloud.deck.ui.branding.BrandingUtil; import it.niedermann.nextcloud.deck.ui.pickstack.PickStackFragment; import it.niedermann.nextcloud.deck.ui.pickstack.PickStackListener; import it.niedermann.nextcloud.deck.ui.pickstack.PickStackViewModel; +import it.niedermann.nextcloud.deck.ui.theme.ThemeUtils; +import it.niedermann.nextcloud.deck.ui.theme.ThemedDialogFragment; -public class MoveCardDialogFragment extends BrandedDialogFragment implements PickStackListener { +public class MoveCardDialogFragment extends ThemedDialogFragment implements PickStackListener { private static final String KEY_ORIGIN_ACCOUNT_ID = "account_id"; private static final String KEY_ORIGIN_BOARD_LOCAL_ID = "board_local_id"; @@ -107,6 +106,11 @@ public class MoveCardDialogFragment extends BrandedDialogFragment implements Pic public void onStackPicked(@NonNull Account account, @Nullable Board board, @Nullable Stack stack) { this.selectedAccount = account; this.selectedBoard = board; + + if (board != null) { + applyTheme(board.getColor()); + } + this.selectedStack = stack; if (board == null || stack == null) { binding.submit.setEnabled(false); @@ -118,10 +122,11 @@ public class MoveCardDialogFragment extends BrandedDialogFragment implements Pic } @Override - public void applyBrand(int mainColor) { - final ColorStateList mainColorStateList = ColorStateList.valueOf(BrandingUtil.getSecondaryForegroundColorDependingOnTheme(requireContext(), mainColor)); - binding.cancel.setTextColor(mainColorStateList); - binding.submit.setTextColor(mainColorStateList); + public void applyTheme(int color) { + final var utils = ThemeUtils.of(color, requireContext()); + + utils.material.colorMaterialButtonText(binding.cancel); + utils.material.colorMaterialButtonText(binding.submit); } public static DialogFragment newInstance(long originAccountId, long originBoardLocalId, String originCardTitle, Long originCardLocalId, boolean hasAttachmentsOrComments) { 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 b4b91a2cb..cc01781e6 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 @@ -16,16 +16,16 @@ import it.niedermann.nextcloud.deck.DeckApplication; 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.BrandedSwitchPreference; +import it.niedermann.nextcloud.deck.ui.theme.ThemedSwitchPreference; public class SettingsFragment extends PreferenceFragmentCompat { - private BrandedSwitchPreference wifiOnlyPref; - private BrandedSwitchPreference compactPref; - private BrandedSwitchPreference coverImagesPref; - private BrandedSwitchPreference compressImageAttachmentsPref; - private BrandedSwitchPreference debuggingPref; - private BrandedSwitchPreference eTagPref; + private ThemedSwitchPreference wifiOnlyPref; + private ThemedSwitchPreference compactPref; + private ThemedSwitchPreference coverImagesPref; + private ThemedSwitchPreference compressImageAttachmentsPref; + private ThemedSwitchPreference debuggingPref; + private ThemedSwitchPreference eTagPref; @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { @@ -76,12 +76,12 @@ public class SettingsFragment extends PreferenceFragmentCompat { super.onViewCreated(view, savedInstanceState); DeckApplication.readCurrentAccountColor().observe(getViewLifecycleOwner(), (mainColor) -> { - wifiOnlyPref.applyBrand(mainColor); - compactPref.applyBrand(mainColor); - coverImagesPref.applyBrand(mainColor); - compressImageAttachmentsPref.applyBrand(mainColor); - debuggingPref.applyBrand(mainColor); - eTagPref.applyBrand(mainColor); + wifiOnlyPref.applyTheme(mainColor); + compactPref.applyTheme(mainColor); + coverImagesPref.applyTheme(mainColor); + compressImageAttachmentsPref.applyTheme(mainColor); + debuggingPref.applyTheme(mainColor); + eTagPref.applyTheme(mainColor); }); } } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/sharetarget/ShareProgressDialogFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/sharetarget/ShareProgressDialogFragment.java index 207db9006..852735352 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/sharetarget/ShareProgressDialogFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/sharetarget/ShareProgressDialogFragment.java @@ -1,8 +1,5 @@ package it.niedermann.nextcloud.deck.ui.sharetarget; -import static android.graphics.PorterDuff.Mode; -import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.getSecondaryForegroundColorDependingOnTheme; - import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; @@ -22,11 +19,12 @@ import it.niedermann.nextcloud.deck.BuildConfig; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.DialogShareProgressBinding; import it.niedermann.nextcloud.deck.exceptions.UploadAttachmentFailedException; -import it.niedermann.nextcloud.deck.ui.branding.BrandedDialogFragment; import it.niedermann.nextcloud.deck.ui.exception.ExceptionDialogFragment; +import it.niedermann.nextcloud.deck.ui.theme.ThemeUtils; +import it.niedermann.nextcloud.deck.ui.theme.ThemedDialogFragment; import it.niedermann.nextcloud.exception.ExceptionUtil; -public class ShareProgressDialogFragment extends BrandedDialogFragment { +public class ShareProgressDialogFragment extends ThemedDialogFragment { private DialogShareProgressBinding binding; private ShareProgressViewModel viewModel; @@ -122,9 +120,10 @@ public class ShareProgressDialogFragment extends BrandedDialogFragment { } @Override - public void applyBrand(int mainColor) { - binding.progress.getProgressDrawable().setColorFilter( - getSecondaryForegroundColorDependingOnTheme(requireContext(), mainColor), Mode.SRC_IN); - binding.errorReportButton.setTextColor(getSecondaryForegroundColorDependingOnTheme(requireContext(), mainColor)); + public void applyTheme(int color) { + final var utils = ThemeUtils.of(color, requireContext()); + + utils.platform.themeHorizontalProgressBar(binding.progress); + utils.material.colorMaterialButtonText(binding.errorReportButton); } } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/stack/DeleteStackDialogFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/stack/DeleteStackDialogFragment.java index 6594ccc0f..4120e5e00 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/stack/DeleteStackDialogFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/stack/DeleteStackDialogFragment.java @@ -8,7 +8,7 @@ import androidx.annotation.NonNull; import androidx.fragment.app.DialogFragment; import it.niedermann.nextcloud.deck.R; -import it.niedermann.nextcloud.deck.ui.branding.DeleteAlertDialogBuilder; +import it.niedermann.nextcloud.deck.ui.theme.DeleteAlertDialogBuilder; public class DeleteStackDialogFragment extends DialogFragment { 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 eb0ad1e26..4b37a3924 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 @@ -1,7 +1,5 @@ package it.niedermann.nextcloud.deck.ui.stack; -import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.applyBrandToEditTextInputLayout; - import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; @@ -24,9 +22,10 @@ import java.util.Objects; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.DialogStackCreateBinding; -import it.niedermann.nextcloud.deck.ui.branding.BrandedDialogFragment; +import it.niedermann.nextcloud.deck.ui.theme.ThemeUtils; +import it.niedermann.nextcloud.deck.ui.theme.ThemedDialogFragment; -public class EditStackDialogFragment extends BrandedDialogFragment implements DialogInterface.OnClickListener { +public class EditStackDialogFragment extends ThemedDialogFragment implements DialogInterface.OnClickListener { private static final String KEY_STACK_ID = "stack_id"; private static final String KEY_OLD_TITLE = "old_title"; private EditStackListener editStackListener; @@ -135,8 +134,10 @@ public class EditStackDialogFragment extends BrandedDialogFragment implements Di } @Override - public void applyBrand(int mainColor) { - applyBrandToEditTextInputLayout(mainColor, binding.inputWrapper); + public void applyTheme(int color) { + final var utils = ThemeUtils.of(color, requireContext()); + + utils.material.colorTextInputLayout(binding.inputWrapper); } @Override diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/stack/StackFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/stack/StackFragment.java index c28573289..f090ac59e 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/stack/StackFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/stack/StackFragment.java @@ -139,7 +139,7 @@ public class StackFragment extends Fragment implements DragAndDropTab<CardAdapte @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); - DeckApplication.readCurrentBoardColor().observe(getViewLifecycleOwner(), this::applyBrand); + DeckApplication.readCurrentBoardColor().observe(getViewLifecycleOwner(), this::applyTheme); } @Override @@ -159,9 +159,9 @@ public class StackFragment extends Fragment implements DragAndDropTab<CardAdapte return binding.recyclerView; } - private void applyBrand(int mainColor) { + private void applyTheme(int color) { if (this.adapter != null) { - this.adapter.applyBrand(mainColor); + this.adapter.applyTheme(color); } } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/takephoto/TakePhotoActivity.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/takephoto/TakePhotoActivity.java index 67ca627ca..a8fd87ae6 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/takephoto/TakePhotoActivity.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/takephoto/TakePhotoActivity.java @@ -34,9 +34,9 @@ import java.util.concurrent.ExecutionException; import it.niedermann.nextcloud.deck.DeckApplication; import it.niedermann.nextcloud.deck.DeckLog; import it.niedermann.nextcloud.deck.databinding.ActivityTakePhotoBinding; -import it.niedermann.nextcloud.deck.ui.branding.BrandingUtil; import it.niedermann.nextcloud.deck.ui.exception.ExceptionDialogFragment; import it.niedermann.nextcloud.deck.ui.exception.ExceptionHandler; +import it.niedermann.nextcloud.deck.ui.theme.ThemeUtils; import it.niedermann.nextcloud.deck.util.FilesUtil; public class TakePhotoActivity extends AppCompatActivity { @@ -180,7 +180,9 @@ public class TakePhotoActivity extends AppCompatActivity { return new Intent(context, TakePhotoActivity.class).setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); } - private void applyBoardColorBrand(int mainColor) { - Arrays.stream(brandedFABs).forEach(fab -> BrandingUtil.applyBrandToFAB(mainColor, fab)); + private void applyBoardColorBrand(int color) { + final var utils = ThemeUtils.of(color, this); + + Arrays.stream(brandedFABs).forEach(utils.material::themeFAB); } } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/theme/DeckViewThemeUtils.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/theme/DeckViewThemeUtils.java new file mode 100644 index 000000000..ee11cbbea --- /dev/null +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/theme/DeckViewThemeUtils.java @@ -0,0 +1,120 @@ +package it.niedermann.nextcloud.deck.ui.theme; + +import static com.nextcloud.android.common.ui.util.ColorStateListUtilsKt.buildColorStateList; + +import android.content.res.ColorStateList; +import android.graphics.Color; + +import androidx.annotation.ColorInt; +import androidx.annotation.NonNull; +import androidx.core.content.ContextCompat; + +import com.google.android.material.bottomnavigation.BottomNavigationView; +import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton; +import com.google.android.material.navigation.NavigationView; +import com.google.android.material.tabs.TabLayout; +import com.nextcloud.android.common.ui.theme.MaterialSchemes; +import com.nextcloud.android.common.ui.theme.ViewThemeUtilsBase; +import com.nextcloud.android.common.ui.theme.utils.MaterialViewThemeUtils; + +import it.niedermann.nextcloud.deck.R; +import kotlin.Pair; + +/** + * UI Elements which are not yet supported by the <a href="https://github.com/nextcloud/android-common"><code>android-commons</code></a> library. + * Ideally there should at least be one Pull Request for Upstream for each method here. + */ +public class DeckViewThemeUtils extends ViewThemeUtilsBase { + + private final MaterialViewThemeUtils material; + + public DeckViewThemeUtils( + @NonNull MaterialSchemes schemes, + @NonNull MaterialViewThemeUtils material + ) { + super(schemes); + this.material = material; + } + + /** + * @param fab {@link ExtendedFloatingActionButton} + * @see <a href="https://github.com/nextcloud/android-common/pull/68">Upstream Pull Request</a> + */ + public void themeExtendedFAB(ExtendedFloatingActionButton fab) { + withScheme(fab, scheme -> { + fab.setBackgroundTintList(buildColorStateList( + new Pair<>(android.R.attr.state_enabled, scheme.getPrimaryContainer()), + new Pair<>(-android.R.attr.state_enabled, Color.GRAY) + )); + fab.setIconTint(buildColorStateList( + new Pair<>(android.R.attr.state_enabled, scheme.getOnPrimaryContainer()), + new Pair<>(-android.R.attr.state_enabled, Color.WHITE) + )); + return fab; + }); + } + + /** + * @param navigationView {@link NavigationView} + * @see <a href="https://github.com/nextcloud/android-common/pull/69">Upstream Pull Request</a> + */ + public void colorNavigationView(NavigationView navigationView) { + withScheme(navigationView, scheme -> { + if (navigationView.getItemBackground() != null) { + navigationView.getItemBackground().setTintList(buildColorStateList( + new Pair<>(android.R.attr.state_checked, scheme.getSecondaryContainer()), + new Pair<>(-android.R.attr.state_checked, Color.TRANSPARENT) + )); + } + // Fixes https://github.com/nextcloud/android-common/issues/66 + navigationView.getBackground().setTintList(ColorStateList.valueOf(scheme.getSurface())); + + final var colorStateList = buildColorStateList( + new Pair<>(android.R.attr.state_checked, scheme.getOnSecondaryContainer()), + new Pair<>(-android.R.attr.state_checked, scheme.getOnSurfaceVariant()) + ); + + navigationView.setItemTextColor(colorStateList); + // Fixes https://github.com/nextcloud/android-common/issues/64 + // navigationView.setItemIconTintList(colorStateList); + return navigationView; + }); + } + + /** + * @param bottomNavigationView {@link BottomNavigationView} + * @see <a href="https://github.com/nextcloud/android-common/pull/71">Upstream Pull Request</a> + */ + public void colorBottomNavigationView(BottomNavigationView bottomNavigationView) { + withScheme(bottomNavigationView, scheme -> { + bottomNavigationView.setBackgroundColor(scheme.getSurface()); + bottomNavigationView.setItemIconTintList(buildColorStateList( + new Pair<>(android.R.attr.state_checked, scheme.getOnSecondaryContainer()), + new Pair<>(-android.R.attr.state_checked, scheme.getOnSurfaceVariant()) + )); + bottomNavigationView.setItemTextColor(buildColorStateList( + new Pair<>(android.R.attr.state_checked, scheme.getOnSurface()), + new Pair<>(-android.R.attr.state_checked, scheme.getOnSurfaceVariant()) + )); + bottomNavigationView.setItemActiveIndicatorColor(ColorStateList.valueOf(scheme.getSecondaryContainer())); + + return bottomNavigationView; + }); + } + + /** + * Convenience method for calling {@link #themeTabLayout(TabLayout, int)} with the primary color + */ + public void themeTabLayout(@NonNull TabLayout tabLayout) { + themeTabLayout(tabLayout, ContextCompat.getColor(tabLayout.getContext(), R.color.primary)); + } + + /** + * Themes the <code>tabLayout</code> using {@link MaterialViewThemeUtils#themeTabLayout(TabLayout)} + * and then applies <code>backgroundColor</code>. + */ + public void themeTabLayout(@NonNull TabLayout tabLayout, @ColorInt int backgroundColor) { + this.material.themeTabLayout(tabLayout); + tabLayout.setBackgroundColor(backgroundColor); + } +}
\ No newline at end of file diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/DeleteAlertDialogBuilder.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/theme/DeleteAlertDialogBuilder.java index e769797b4..2b5543e59 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/DeleteAlertDialogBuilder.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/theme/DeleteAlertDialogBuilder.java @@ -1,4 +1,4 @@ -package it.niedermann.nextcloud.deck.ui.branding; +package it.niedermann.nextcloud.deck.ui.theme; import android.content.Context; import android.content.DialogInterface; @@ -23,12 +23,12 @@ public class DeleteAlertDialogBuilder extends MaterialAlertDialogBuilder { @Override public AlertDialog create() { this.dialog = super.create(); - applyBrand(); - dialog.setOnShowListener(dialog -> applyBrand()); + applyTheme(); + dialog.setOnShowListener(dialog -> applyTheme()); return dialog; } - public void applyBrand() { + public void applyTheme() { final var positiveButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE); if (positiveButton != null) { positiveButton.setTextColor(ContextCompat.getColor(getContext(), R.color.danger)); diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/theme/ThemeUtils.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/theme/ThemeUtils.java new file mode 100644 index 000000000..3bd6c6bbf --- /dev/null +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/theme/ThemeUtils.java @@ -0,0 +1,81 @@ +package it.niedermann.nextcloud.deck.ui.theme; + +import static it.niedermann.nextcloud.deck.DeckApplication.isDarkTheme; + +import android.content.Context; + +import androidx.annotation.ColorInt; +import androidx.annotation.NonNull; +import androidx.core.content.ContextCompat; +import androidx.preference.PreferenceManager; + +import com.nextcloud.android.common.ui.color.ColorUtil; +import com.nextcloud.android.common.ui.theme.MaterialSchemes; +import com.nextcloud.android.common.ui.theme.ViewThemeUtilsBase; +import com.nextcloud.android.common.ui.theme.utils.AndroidViewThemeUtils; +import com.nextcloud.android.common.ui.theme.utils.AndroidXViewThemeUtils; +import com.nextcloud.android.common.ui.theme.utils.DialogViewThemeUtils; +import com.nextcloud.android.common.ui.theme.utils.MaterialViewThemeUtils; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import it.niedermann.nextcloud.deck.DeckLog; +import it.niedermann.nextcloud.deck.R; +import scheme.Scheme; + +public class ThemeUtils extends ViewThemeUtilsBase { + + private static final ConcurrentMap<Integer, ThemeUtils> CACHE = new ConcurrentHashMap<>(); + + public final AndroidViewThemeUtils platform; + public final MaterialViewThemeUtils material; + public final AndroidXViewThemeUtils androidx; + public final DialogViewThemeUtils dialog; + public final DeckViewThemeUtils deck; + + private ThemeUtils( + final MaterialSchemes schemes, + final ColorUtil colorUtil + ) { + super(schemes); + + this.platform = new AndroidViewThemeUtils(schemes, colorUtil); + this.material = new MaterialViewThemeUtils(schemes, colorUtil); + this.androidx = new AndroidXViewThemeUtils(schemes, this.platform); + this.dialog = new DialogViewThemeUtils(schemes); + this.deck = new DeckViewThemeUtils(schemes, this.material); + } + + public static ThemeUtils of(@ColorInt int color, @NonNull Context context) { + return CACHE.computeIfAbsent(color, c -> new ThemeUtils( + MaterialSchemes.Companion.fromColor(c), + new ColorUtil(context) + )); + } + + public static Scheme createScheme(@ColorInt int color, @NonNull Context context) { + return isDarkTheme(context) ? Scheme.dark(color) : Scheme.light(color); + } + + @ColorInt + public static int readBrandMainColor(@NonNull Context context) { + final var sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext()); + DeckLog.log("--- Read:", context.getString(R.string.shared_preference_theme_main)); + return sharedPreferences.getInt(context.getString(R.string.shared_preference_theme_main), ContextCompat.getColor(context, R.color.defaultBrand)); + } + + public static void saveBrandColors(@NonNull Context context, @ColorInt int color) { + final var editor = PreferenceManager.getDefaultSharedPreferences(context).edit(); + DeckLog.log("--- Write:", context.getString(R.string.shared_preference_theme_main), "|", color); + editor.putInt(context.getString(R.string.shared_preference_theme_main), color); + editor.apply(); + } + + public static void clearBrandColors(@NonNull Context context) { + final var editor = PreferenceManager.getDefaultSharedPreferences(context).edit(); + DeckLog.log("--- Remove:", context.getString(R.string.shared_preference_theme_main)); + editor.remove(context.getString(R.string.shared_preference_theme_main)); + editor.apply(); + } +} diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/theme/Themed.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/theme/Themed.java new file mode 100644 index 000000000..c0cdcc0bf --- /dev/null +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/theme/Themed.java @@ -0,0 +1,9 @@ +package it.niedermann.nextcloud.deck.ui.theme; + +import androidx.annotation.ColorInt; +import androidx.annotation.UiThread; + +public interface Themed { + @UiThread + void applyTheme(@ColorInt int color); +}
\ No newline at end of file diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedDatePickerDialog.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/theme/ThemedDatePickerDialog.java index 950fee2e0..6b6a5ddfa 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedDatePickerDialog.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/theme/ThemedDatePickerDialog.java @@ -1,10 +1,8 @@ -package it.niedermann.nextcloud.deck.ui.branding; +package it.niedermann.nextcloud.deck.ui.theme; import static it.niedermann.nextcloud.deck.DeckApplication.isDarkTheme; -import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.getSecondaryForegroundColorDependingOnTheme; -import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.readBrandMainColor; +import static it.niedermann.nextcloud.deck.ui.theme.ThemeUtils.readBrandMainColor; -import android.graphics.Color; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; @@ -12,38 +10,32 @@ import android.view.ViewGroup; import androidx.annotation.ColorInt; import androidx.annotation.NonNull; -import androidx.core.content.ContextCompat; import com.wdullaer.materialdatetimepicker.date.DatePickerDialog; import java.util.Calendar; -import it.niedermann.nextcloud.deck.R; -import it.niedermann.nextcloud.deck.util.DeckColorUtil; +import scheme.Scheme; -public class BrandedDatePickerDialog extends DatePickerDialog implements Branded { +public class ThemedDatePickerDialog extends DatePickerDialog implements Themed { @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { final var context = requireContext(); setThemeDark(isDarkTheme(context)); - applyBrand(readBrandMainColor(context)); + applyTheme(readBrandMainColor(context)); return super.onCreateView(inflater, container, savedInstanceState); } @Override - public void applyBrand(int mainColor) { - @ColorInt final int buttonTextColor = getSecondaryForegroundColorDependingOnTheme(requireContext(), mainColor); + public void applyTheme(int color) { + final var scheme = ThemeUtils.createScheme(color, requireContext()); + + @ColorInt final int buttonTextColor = scheme.getOnPrimaryContainer(); setOkColor(buttonTextColor); setCancelColor(buttonTextColor); - setAccentColor( - DeckColorUtil.contrastRatioIsSufficientBigAreas(Color.WHITE, mainColor) - ? mainColor - // Text in picker title is always white (also in dark mode) - : isThemeDark() - ? Color.BLACK - : ContextCompat.getColor(requireContext(), R.color.accent) - ); + + setAccentColor(Scheme.dark(color).getPrimaryContainer()); } /** @@ -56,7 +48,7 @@ public class BrandedDatePickerDialog extends DatePickerDialog implements Branded * @return a new DatePickerDialog instance. */ public static DatePickerDialog newInstance(OnDateSetListener callBack, int year, int monthOfYear, int dayOfMonth) { - final var dialog = new BrandedDatePickerDialog(); + final var dialog = new ThemedDatePickerDialog(); dialog.initialize(callBack, year, monthOfYear - 1, dayOfMonth); return dialog; } @@ -71,7 +63,7 @@ public class BrandedDatePickerDialog extends DatePickerDialog implements Branded * @return a new DatePickerDialog instance */ public static DatePickerDialog newInstance(OnDateSetListener callback, Calendar initialSelection) { - final var dialog = new BrandedDatePickerDialog(); + final var dialog = new ThemedDatePickerDialog(); dialog.initialize(callback, initialSelection); return dialog; } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/theme/ThemedDialogFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/theme/ThemedDialogFragment.java new file mode 100644 index 000000000..8638780f7 --- /dev/null +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/theme/ThemedDialogFragment.java @@ -0,0 +1,19 @@ +package it.niedermann.nextcloud.deck.ui.theme; + +import static it.niedermann.nextcloud.deck.ui.theme.ThemeUtils.readBrandMainColor; + +import androidx.annotation.Nullable; +import androidx.fragment.app.DialogFragment; + +public abstract class ThemedDialogFragment extends DialogFragment implements Themed { + + @Override + public void onStart() { + super.onStart(); + + @Nullable final var context = getContext(); + if (context != null) { + applyTheme(readBrandMainColor(context)); + } + } +} diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/theme/ThemedPreferenceCategory.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/theme/ThemedPreferenceCategory.java new file mode 100644 index 000000000..cb9dd2be1 --- /dev/null +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/theme/ThemedPreferenceCategory.java @@ -0,0 +1,43 @@ +package it.niedermann.nextcloud.deck.ui.theme; + +import static it.niedermann.nextcloud.deck.DeckApplication.readCurrentAccountColor; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.preference.PreferenceCategory; +import androidx.preference.PreferenceViewHolder; + +public class ThemedPreferenceCategory extends PreferenceCategory { + + @Override + public void onBindViewHolder(@NonNull PreferenceViewHolder holder) { + super.onBindViewHolder(holder); + + final var view = holder.itemView.findViewById(android.R.id.title); + @Nullable final Context context = getContext(); + if (view instanceof TextView) { + final var scheme = ThemeUtils.createScheme(readCurrentAccountColor(context), context); + ((TextView) view).setTextColor(scheme.getOnPrimaryContainer()); + } + } + + public ThemedPreferenceCategory(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + public ThemedPreferenceCategory(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public ThemedPreferenceCategory(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public ThemedPreferenceCategory(Context context) { + super(context); + } +} diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/theme/ThemedSnackbar.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/theme/ThemedSnackbar.java new file mode 100644 index 000000000..ac6829b12 --- /dev/null +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/theme/ThemedSnackbar.java @@ -0,0 +1,31 @@ +package it.niedermann.nextcloud.deck.ui.theme; + +import static it.niedermann.nextcloud.deck.ui.theme.ThemeUtils.readBrandMainColor; + +import android.view.View; + +import androidx.annotation.ColorInt; +import androidx.annotation.NonNull; +import androidx.annotation.StringRes; + +import com.google.android.material.snackbar.BaseTransientBottomBar; +import com.google.android.material.snackbar.Snackbar; + +public class ThemedSnackbar { + + @NonNull + public static Snackbar make(@NonNull View view, @NonNull CharSequence text, @BaseTransientBottomBar.Duration int duration) { + @ColorInt final int color = readBrandMainColor(view.getContext()); + final var snackbar = Snackbar.make(view, text, duration); + final var utils = ThemeUtils.of(color, view.getContext()); + + utils.material.themeSnackbar(snackbar); + + return snackbar; + } + + @NonNull + public static Snackbar make(@NonNull View view, @StringRes int resId, @BaseTransientBottomBar.Duration int duration) { + return make(view, view.getResources().getText(resId), duration); + } +}
\ No newline at end of file diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/theme/ThemedSwitchPreference.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/theme/ThemedSwitchPreference.java new file mode 100644 index 000000000..c9cba5b7c --- /dev/null +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/theme/ThemedSwitchPreference.java @@ -0,0 +1,89 @@ +package it.niedermann.nextcloud.deck.ui.theme; + +import android.annotation.SuppressLint; +import android.content.Context; +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.preference.PreferenceViewHolder; +import androidx.preference.SwitchPreference; + +public class ThemedSwitchPreference extends SwitchPreference implements Themed { + + @Nullable + private ThemeUtils utils = null; + + @SuppressLint("UseSwitchCompatOrMaterialCode") + @Nullable + private Switch switchView; + + public ThemedSwitchPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + public ThemedSwitchPreference(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public ThemedSwitchPreference(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public ThemedSwitchPreference(Context context) { + super(context); + } + + @Override + public void onBindViewHolder(PreferenceViewHolder holder) { + super.onBindViewHolder(holder); + + if (holder.itemView instanceof ViewGroup) { + switchView = findSwitchWidget(holder.itemView); + applyTheme(); + } + } + + @Override + public void applyTheme(@ColorInt int color) { + this.utils = ThemeUtils.of(color, getContext()); + // onBindViewHolder is called after applyTheme, therefore we have to store the given values and apply them later. + applyTheme(); + } + + private void applyTheme() { + if (utils != null && switchView != null) { + utils.platform.colorSwitch(switchView); + } + } + + /** + * 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 <a href="https://gist.github.com/marchold/45e22839eb94aa14dfb5">Source</a> + */ + 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++) { + final var child = viewGroup.getChildAt(i); + if (child instanceof ViewGroup) { + @SuppressLint("UseSwitchCompatOrMaterialCode") final var 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/branding/BrandedTimePickerDialog.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/theme/ThemedTimePickerDialog.java index 4a8879f30..3a0ce8561 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedTimePickerDialog.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/theme/ThemedTimePickerDialog.java @@ -1,11 +1,9 @@ -package it.niedermann.nextcloud.deck.ui.branding; +package it.niedermann.nextcloud.deck.ui.theme; import static it.niedermann.nextcloud.deck.DeckApplication.isDarkTheme; -import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.getSecondaryForegroundColorDependingOnTheme; -import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.readBrandMainColor; +import static it.niedermann.nextcloud.deck.ui.theme.ThemeUtils.readBrandMainColor; import android.content.Context; -import android.graphics.Color; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; @@ -14,34 +12,34 @@ import android.view.ViewGroup; import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.core.content.ContextCompat; import com.wdullaer.materialdatetimepicker.time.TimePickerDialog; import java.time.LocalTime; -import it.niedermann.nextcloud.deck.R; -import it.niedermann.nextcloud.deck.util.DeckColorUtil; +import scheme.Scheme; -public class BrandedTimePickerDialog extends TimePickerDialog implements Branded { +public class ThemedTimePickerDialog extends TimePickerDialog implements Themed { @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @Nullable Context context = getContext(); if (context != null) { setThemeDark(isDarkTheme(context)); - applyBrand(readBrandMainColor(context)); + applyTheme(readBrandMainColor(context)); } return super.onCreateView(inflater, container, savedInstanceState); } @Override - public void applyBrand(int mainColor) { - @ColorInt final int buttonTextColor = getSecondaryForegroundColorDependingOnTheme(requireContext(), mainColor); + public void applyTheme(int color) { + final var scheme = ThemeUtils.createScheme(color, requireContext()); + + @ColorInt final int buttonTextColor = scheme.getOnPrimaryContainer(); setOkColor(buttonTextColor); setCancelColor(buttonTextColor); - // Text in picker title is always white - setAccentColor(DeckColorUtil.contrastRatioIsSufficientBigAreas(Color.WHITE, mainColor) ? mainColor : ContextCompat.getColor(requireContext(), R.color.accent)); + + setAccentColor(Scheme.dark(color).getPrimaryContainer()); } /** @@ -57,7 +55,7 @@ public class BrandedTimePickerDialog extends TimePickerDialog implements Branded @SuppressWarnings({"SameParameterValue"}) public static TimePickerDialog newInstance(OnTimeSetListener callback, int hourOfDay, int minute, int second, boolean is24HourMode) { - final var dialog = new BrandedTimePickerDialog(); + final var dialog = new ThemedTimePickerDialog(); dialog.initialize(callback, hourOfDay, minute, second, is24HourMode); return dialog; } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/upcomingcards/UpcomingCardsAdapter.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/upcomingcards/UpcomingCardsAdapter.java index 7f1c64ad6..1df85af63 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/upcomingcards/UpcomingCardsAdapter.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/upcomingcards/UpcomingCardsAdapter.java @@ -6,7 +6,6 @@ import android.app.Activity; import android.view.LayoutInflater; import android.view.ViewGroup; -import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.core.content.ContextCompat; import androidx.fragment.app.FragmentManager; @@ -32,6 +31,8 @@ import it.niedermann.nextcloud.deck.ui.card.CompactCardViewHolder; import it.niedermann.nextcloud.deck.ui.card.DefaultCardOnlyTitleViewHolder; import it.niedermann.nextcloud.deck.ui.card.DefaultCardViewHolder; import it.niedermann.nextcloud.deck.ui.card.EditActivity; +import it.niedermann.nextcloud.deck.ui.theme.ThemeUtils; +import scheme.Scheme; public class UpcomingCardsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { @@ -44,8 +45,8 @@ public class UpcomingCardsAdapter extends RecyclerView.Adapter<RecyclerView.View protected List<Object> items = new ArrayList<>(); @NonNull protected String counterMaxValue; - @ColorInt - protected int mainColor; + @NonNull + protected Scheme scheme; @NonNull private final BiConsumer<Account, Card> assignCard; @NonNull @@ -64,7 +65,7 @@ public class UpcomingCardsAdapter extends RecyclerView.Adapter<RecyclerView.View this.activity = activity; this.counterMaxValue = this.activity.getString(R.string.counter_max_value); this.fragmentManager = fragmentManager; - this.mainColor = ContextCompat.getColor(this.activity, R.color.defaultBrand); + this.scheme = ThemeUtils.createScheme(ContextCompat.getColor(this.activity, R.color.defaultBrand), this.activity); this.compactMode = getDefaultSharedPreferences(this.activity).getBoolean(this.activity.getString(R.string.pref_key_compact), false); this.assignCard = assignCard; this.unassignCard = unassignCard; @@ -154,7 +155,7 @@ public class UpcomingCardsAdapter extends RecyclerView.Adapter<RecyclerView.View unassignCard, archiveCard, deleteCard - ), counterMaxValue, mainColor); + ), counterMaxValue, scheme); cardViewHolder.bindCardClickListener((v) -> activity.startActivity(EditActivity.createEditCardIntent(activity, cardItem.getAccount(), cardItem.getCurrentBoardLocalId(), cardItem.getFullCard().getLocalId()))); } else { throw new IllegalStateException("Item at position " + position + " is a " + item.getClass().getSimpleName() + " but viewHolder is no " + AbstractCardViewHolder.class.getSimpleName()); diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/widget/singlecard/SelectCardForWidgetActivity.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/widget/singlecard/SelectCardForWidgetActivity.java index c664f1441..ef1451c23 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/widget/singlecard/SelectCardForWidgetActivity.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/widget/singlecard/SelectCardForWidgetActivity.java @@ -1,6 +1,6 @@ package it.niedermann.nextcloud.deck.ui.widget.singlecard; -import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.saveBrandColors; +import static it.niedermann.nextcloud.deck.ui.theme.ThemeUtils.saveBrandColors; import android.appwidget.AppWidgetManager; import android.content.Intent; @@ -15,8 +15,8 @@ import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.model.Board; import it.niedermann.nextcloud.deck.model.full.FullCard; import it.niedermann.nextcloud.deck.ui.MainActivity; -import it.niedermann.nextcloud.deck.ui.branding.BrandingUtil; import it.niedermann.nextcloud.deck.ui.card.SelectCardListener; +import it.niedermann.nextcloud.deck.ui.theme.ThemeUtils; public class SelectCardForWidgetActivity extends MainActivity implements SelectCardListener { @@ -40,7 +40,7 @@ public class SelectCardForWidgetActivity extends MainActivity implements SelectC if (appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) { finish(); } - originalBrandColor = BrandingUtil.readBrandMainColor(this); + originalBrandColor = ThemeUtils.readBrandMainColor(this); } @Override diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/util/DeckColorUtil.java b/app/src/main/java/it/niedermann/nextcloud/deck/util/DeckColorUtil.java deleted file mode 100644 index 82a3efd1f..000000000 --- a/app/src/main/java/it/niedermann/nextcloud/deck/util/DeckColorUtil.java +++ /dev/null @@ -1,63 +0,0 @@ -package it.niedermann.nextcloud.deck.util; - -import androidx.annotation.ColorInt; -import androidx.annotation.Nullable; -import androidx.core.util.Pair; - -import java.util.HashMap; -import java.util.Map; - -import it.niedermann.android.util.ColorUtil; - -public final class DeckColorUtil { - - private DeckColorUtil() { - throw new UnsupportedOperationException("This class must not get instantiated"); - } - - private static final Map<ColorPair, Boolean> CONTRAST_RATIO_SUFFICIENT_CACHE = new HashMap<>(); - - public static boolean contrastRatioIsSufficient(@ColorInt int colorOne, @ColorInt int colorTwo) { - final var key = new ColorPair(colorOne, colorTwo); - var ret = CONTRAST_RATIO_SUFFICIENT_CACHE.get(key); - if (ret == null) { - ret = ColorUtil.INSTANCE.getContrastRatio(colorOne, colorTwo) > 3d; - CONTRAST_RATIO_SUFFICIENT_CACHE.put(key, ret); - return ret; - } - return ret; - } - - public static boolean contrastRatioIsSufficientBigAreas(@ColorInt int colorOne, @ColorInt int colorTwo) { - final var key = new ColorPair(colorOne, colorTwo); - var ret = CONTRAST_RATIO_SUFFICIENT_CACHE.get(key); - if (ret == null) { - ret = ColorUtil.INSTANCE.getContrastRatio(colorOne, colorTwo) > 1.47d; - CONTRAST_RATIO_SUFFICIENT_CACHE.put(key, ret); - return ret; - } - return ret; - } - - private static class ColorPair extends Pair<Integer, Integer> { - - private ColorPair(@Nullable Integer first, @Nullable Integer second) { - super(first, second); - } - - @SuppressWarnings({"EqualsWhichDoesntCheckParameterClass", "NumberEquality"}) - @Override - public boolean equals(Object o) { - final var colorPair = (ColorPair) o; - if (first != colorPair.first) return false; - return second == colorPair.second; - } - - @Override - public int hashCode() { - int result = first; - result = 31 * result + second; - return result; - } - } -} diff --git a/app/src/main/res/layout/activity_pick_stack.xml b/app/src/main/res/layout/activity_pick_stack.xml index 9251cb474..8c456fc13 100644 --- a/app/src/main/res/layout/activity_pick_stack.xml +++ b/app/src/main/res/layout/activity_pick_stack.xml @@ -95,6 +95,7 @@ <com.google.android.material.button.MaterialButton android:id="@+id/submit" style="@style/Widget.Material3.Button" + android:enabled="false" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="@dimen/spacer_1x" diff --git a/app/src/main/res/layout/dialog_move_card.xml b/app/src/main/res/layout/dialog_move_card.xml index 7f37f8130..0466ed915 100644 --- a/app/src/main/res/layout/dialog_move_card.xml +++ b/app/src/main/res/layout/dialog_move_card.xml @@ -57,9 +57,9 @@ android:orientation="horizontal" app:justifyContent="space_between"> - <Button + <com.google.android.material.button.MaterialButton android:id="@+id/cancel" - style="@style/Widget.AppCompat.Button.ButtonBar.AlertDialog" + style="@style/Widget.Material3.Button.TextButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="start" @@ -67,9 +67,9 @@ android:text="@android:string/cancel" android:textColor="@color/defaultBrand" /> - <Button + <com.google.android.material.button.MaterialButton android:id="@+id/submit" - style="@style/Widget.AppCompat.Button.ButtonBar.AlertDialog" + style="@style/Widget.Material3.Button.TextButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="end" diff --git a/app/src/main/res/layout/fragment_card_edit_tab_attachments.xml b/app/src/main/res/layout/fragment_card_edit_tab_attachments.xml index c1cccc310..c300edcb1 100644 --- a/app/src/main/res/layout/fragment_card_edit_tab_attachments.xml +++ b/app/src/main/res/layout/fragment_card_edit_tab_attachments.xml @@ -54,6 +54,7 @@ app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"> <RelativeLayout + android:id="@+id/pickerHeader" android:layout_width="match_parent" android:layout_height="@dimen/spacer_2x" android:background="@drawable/bottom_sheet_rounded"> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9a2e999b0..30cb73eec 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -21,7 +21,7 @@ <string name="simple_filter">Filter</string> <string name="simple_overdue">Overdue</string> <string name="simple_clear">Clear</string> - <string name="simple_discard">discard</string> + <string name="simple_discard">Discard</string> <string name="simple_update">Update</string> <string name="simple_delete">Delete</string> <string name="simple_rename">Rename</string> diff --git a/app/src/main/res/xml/settings.xml b/app/src/main/res/xml/settings.xml index 3340a4cb6..3896c8ebd 100644 --- a/app/src/main/res/xml/settings.xml +++ b/app/src/main/res/xml/settings.xml @@ -1,8 +1,8 @@ <?xml version="1.0" encoding="utf-8"?> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> - <it.niedermann.nextcloud.deck.ui.branding.BrandedPreferenceCategory app:title="@string/simple_synchronization"> - <it.niedermann.nextcloud.deck.ui.branding.BrandedSwitchPreference + <it.niedermann.nextcloud.deck.ui.theme.ThemedPreferenceCategory app:title="@string/simple_synchronization"> + <it.niedermann.nextcloud.deck.ui.theme.ThemedSwitchPreference android:defaultValue="@string/pref_value_wifi_and_mobile" android:icon="@drawable/ic_network_wifi_grey600_24dp" android:key="@string/pref_key_wifi_only" @@ -17,15 +17,15 @@ android:summary="%s" android:title="@string/settings_background_sync" /> - <it.niedermann.nextcloud.deck.ui.branding.BrandedSwitchPreference + <it.niedermann.nextcloud.deck.ui.theme.ThemedSwitchPreference android:icon="@drawable/ic_baseline_photo_size_select_small_24" android:key="@string/pref_key_compress_image_attachments" android:summary="@string/settings_compress_image_attachments_summary" android:title="@string/settings_compress_image_attachments" app:defaultValue="true" /> - </it.niedermann.nextcloud.deck.ui.branding.BrandedPreferenceCategory> + </it.niedermann.nextcloud.deck.ui.theme.ThemedPreferenceCategory> - <it.niedermann.nextcloud.deck.ui.branding.BrandedPreferenceCategory android:title="@string/simple_appearance"> + <it.niedermann.nextcloud.deck.ui.theme.ThemedPreferenceCategory android:title="@string/simple_appearance"> <ListPreference android:defaultValue="@string/pref_value_theme_system_default" android:entries="@array/darkmode_entries" @@ -35,32 +35,32 @@ android:summary="%s" android:title="@string/settings_theme_title" /> - <it.niedermann.nextcloud.deck.ui.branding.BrandedSwitchPreference + <it.niedermann.nextcloud.deck.ui.theme.ThemedSwitchPreference android:icon="@drawable/ic_baseline_compact_24" android:key="@string/pref_key_compact" android:title="@string/settings_compact_title" app:defaultValue="false" /> - <it.niedermann.nextcloud.deck.ui.branding.BrandedSwitchPreference + <it.niedermann.nextcloud.deck.ui.theme.ThemedSwitchPreference android:icon="@drawable/ic_image_grey600_24dp" android:key="@string/pref_key_cover_images" android:title="@string/settings_cover_images_title" app:defaultValue="true" /> - </it.niedermann.nextcloud.deck.ui.branding.BrandedPreferenceCategory> + </it.niedermann.nextcloud.deck.ui.theme.ThemedPreferenceCategory> - <it.niedermann.nextcloud.deck.ui.branding.BrandedPreferenceCategory android:title="@string/simple_expert_settings"> + <it.niedermann.nextcloud.deck.ui.theme.ThemedPreferenceCategory android:title="@string/simple_expert_settings"> - <it.niedermann.nextcloud.deck.ui.branding.BrandedSwitchPreference + <it.niedermann.nextcloud.deck.ui.theme.ThemedSwitchPreference android:icon="@drawable/ic_bug_report_black_24dp" android:key="@string/pref_key_debugging" android:title="@string/settings_debugging" app:defaultValue="false" /> - <it.niedermann.nextcloud.deck.ui.branding.BrandedSwitchPreference + <it.niedermann.nextcloud.deck.ui.theme.ThemedSwitchPreference android:icon="@drawable/ic_baseline_speed_24" android:key="@string/pref_key_etags" android:summary="@string/settings_etags_summary" android:title="@string/settings_etags" app:defaultValue="true" /> - </it.niedermann.nextcloud.deck.ui.branding.BrandedPreferenceCategory> + </it.niedermann.nextcloud.deck.ui.theme.ThemedPreferenceCategory> </PreferenceScreen> diff --git a/app/src/test/java/it/niedermann/nextcloud/deck/util/DeckColorUtilTest.java b/app/src/test/java/it/niedermann/nextcloud/deck/util/DeckColorUtilTest.java deleted file mode 100644 index d76b81b1b..000000000 --- a/app/src/test/java/it/niedermann/nextcloud/deck/util/DeckColorUtilTest.java +++ /dev/null @@ -1,48 +0,0 @@ -package it.niedermann.nextcloud.deck.util; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import android.graphics.Color; - -import androidx.core.util.Pair; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; - -import java.util.ArrayList; - -@RunWith(RobolectricTestRunner.class) -public class DeckColorUtilTest { - - @Test - public void testContrastRatioIsSufficient() { - final var sufficientContrastColorPairs = new ArrayList<Pair<Integer, Integer>>(); - sufficientContrastColorPairs.add(new Pair<>(Color.BLACK, Color.WHITE)); - sufficientContrastColorPairs.add(new Pair<>(Color.WHITE, Color.parseColor("#0082C9"))); - - for (final var colorPair : sufficientContrastColorPairs) { - assert colorPair.first != null; - assert colorPair.second != null; - assertTrue( - "Expect contrast between " + String.format("#%06X", (0xFFFFFF & colorPair.first)) + " and " + String.format("#%06X", (0xFFFFFF & colorPair.second)) + " to be sufficient", - DeckColorUtil.contrastRatioIsSufficient(colorPair.first, colorPair.second) - ); - } - - final var insufficientContrastColorPairs = new ArrayList<Pair<Integer, Integer>>(); - insufficientContrastColorPairs.add(new Pair<>(Color.WHITE, Color.WHITE)); - insufficientContrastColorPairs.add(new Pair<>(Color.BLACK, Color.BLACK)); - - for (final var colorPair : insufficientContrastColorPairs) { - assert colorPair.first != null; - assert colorPair.second != null; - assertFalse( - "Expect contrast between " + String.format("#%06X", (0xFFFFFF & colorPair.first)) + " and " + String.format("#%06X", (0xFFFFFF & colorPair.second)) + " to be insufficient", - DeckColorUtil.contrastRatioIsSufficient(colorPair.first, colorPair.second) - ); - } - } - -} |