Merge pull request #2217 from nextcloud/feature/250/server-theme

🎨 Server theming
This commit is contained in:
Andy Scherzinger 2022-08-11 09:47:40 +02:00 committed by GitHub
commit ce71746461
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
74 changed files with 1457 additions and 369 deletions

View File

@ -79,6 +79,7 @@ android {
disable 'InvalidPackage' disable 'InvalidPackage'
disable 'MissingTranslation' disable 'MissingTranslation'
disable 'VectorPath' disable 'VectorPath'
disable 'UnusedQuantity'
} }
javaCompileOptions { javaCompileOptions {

View File

@ -41,8 +41,10 @@ import android.widget.Toast;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import com.nextcloud.talk.R; import com.nextcloud.talk.R;
import com.nextcloud.talk.application.NextcloudTalkApplication;
import com.nextcloud.talk.databinding.ActivityTakePictureBinding; import com.nextcloud.talk.databinding.ActivityTakePictureBinding;
import com.nextcloud.talk.models.TakePictureViewModel; import com.nextcloud.talk.models.TakePictureViewModel;
import com.nextcloud.talk.ui.theme.ViewThemeUtils;
import com.nextcloud.talk.utils.BitmapShrinker; import com.nextcloud.talk.utils.BitmapShrinker;
import com.nextcloud.talk.utils.FileUtils; import com.nextcloud.talk.utils.FileUtils;
@ -52,6 +54,8 @@ import java.util.Date;
import java.util.Locale; import java.util.Locale;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import javax.inject.Inject;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.OptIn; import androidx.annotation.OptIn;
@ -66,9 +70,11 @@ import androidx.camera.lifecycle.ProcessCameraProvider;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import androidx.exifinterface.media.ExifInterface; import androidx.exifinterface.media.ExifInterface;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import autodagger.AutoInjector;
import static com.nextcloud.talk.utils.Mimetype.IMAGE_JPEG; import static com.nextcloud.talk.utils.Mimetype.IMAGE_JPEG;
@AutoInjector(NextcloudTalkApplication.class)
public class TakePhotoActivity extends AppCompatActivity { public class TakePhotoActivity extends AppCompatActivity {
private static final String TAG = TakePhotoActivity.class.getSimpleName(); private static final String TAG = TakePhotoActivity.class.getSimpleName();
@ -86,15 +92,22 @@ public class TakePhotoActivity extends AppCompatActivity {
private Camera camera; private Camera camera;
@Inject
ViewThemeUtils viewThemeUtils;
@Override @Override
protected void onCreate(@Nullable Bundle savedInstanceState) { protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this);
binding = ActivityTakePictureBinding.inflate(getLayoutInflater()); binding = ActivityTakePictureBinding.inflate(getLayoutInflater());
viewModel = new ViewModelProvider(this).get(TakePictureViewModel.class); viewModel = new ViewModelProvider(this).get(TakePictureViewModel.class);
setContentView(binding.getRoot()); setContentView(binding.getRoot());
viewThemeUtils.themeFAB(binding.takePhoto);
viewThemeUtils.colorMaterialButtonBackground(binding.send);
cameraProviderFuture = ProcessCameraProvider.getInstance(this); cameraProviderFuture = ProcessCameraProvider.getInstance(this);
cameraProviderFuture.addListener(() -> { cameraProviderFuture.addListener(() -> {
try { try {

View File

@ -35,6 +35,7 @@ import com.nextcloud.talk.application.NextcloudTalkApplication;
import com.nextcloud.talk.data.user.model.User; import com.nextcloud.talk.data.user.model.User;
import com.nextcloud.talk.databinding.RvItemContactBinding; import com.nextcloud.talk.databinding.RvItemContactBinding;
import com.nextcloud.talk.models.json.participants.Participant; import com.nextcloud.talk.models.json.participants.Participant;
import com.nextcloud.talk.ui.theme.ViewThemeUtils;
import com.nextcloud.talk.utils.ApiUtils; import com.nextcloud.talk.utils.ApiUtils;
import com.nextcloud.talk.utils.DisplayUtils; import com.nextcloud.talk.utils.DisplayUtils;
@ -59,14 +60,17 @@ public class ContactItem extends AbstractFlexibleItem<ContactItem.ContactItemVie
private final Participant participant; private final Participant participant;
private final User user; private final User user;
private GenericTextHeaderItem header; private GenericTextHeaderItem header;
private final ViewThemeUtils viewThemeUtils;
public boolean isOnline = true; public boolean isOnline = true;
public ContactItem(Participant participant, public ContactItem(Participant participant,
User user, User user,
GenericTextHeaderItem genericTextHeaderItem) { GenericTextHeaderItem genericTextHeaderItem,
ViewThemeUtils viewThemeUtils) {
this.participant = participant; this.participant = participant;
this.user = user; this.user = user;
this.header = genericTextHeaderItem; this.header = genericTextHeaderItem;
this.viewThemeUtils = viewThemeUtils;
} }
@Override @Override
@ -108,6 +112,7 @@ public class ContactItem extends AbstractFlexibleItem<ContactItem.ContactItemVie
holder.binding.avatarDraweeView.setController(null); holder.binding.avatarDraweeView.setController(null);
if (participant.getSelected()) { if (participant.getSelected()) {
viewThemeUtils.colorImageView(holder.binding.checkedImageView);
holder.binding.checkedImageView.setVisibility(View.VISIBLE); holder.binding.checkedImageView.setVisibility(View.VISIBLE);
} else { } else {
holder.binding.checkedImageView.setVisibility(View.GONE); holder.binding.checkedImageView.setVisibility(View.GONE);

View File

@ -27,7 +27,6 @@ package com.nextcloud.talk.adapters.items;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.content.res.ColorStateList; import android.content.res.ColorStateList;
import android.graphics.Color;
import android.graphics.Typeface; import android.graphics.Typeface;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable; import android.graphics.drawable.LayerDrawable;
@ -46,6 +45,7 @@ import com.nextcloud.talk.models.json.chat.ChatMessage;
import com.nextcloud.talk.models.json.conversations.Conversation; import com.nextcloud.talk.models.json.conversations.Conversation;
import com.nextcloud.talk.models.json.status.Status; import com.nextcloud.talk.models.json.status.Status;
import com.nextcloud.talk.ui.StatusDrawable; import com.nextcloud.talk.ui.StatusDrawable;
import com.nextcloud.talk.ui.theme.ViewThemeUtils;
import com.nextcloud.talk.utils.ApiUtils; import com.nextcloud.talk.utils.ApiUtils;
import com.nextcloud.talk.utils.DisplayUtils; import com.nextcloud.talk.utils.DisplayUtils;
import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew; import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew;
@ -76,22 +76,22 @@ public class ConversationItem extends AbstractFlexibleItem<ConversationItem.Conv
private final Context context; private final Context context;
private GenericTextHeaderItem header; private GenericTextHeaderItem header;
private final Status status; private final Status status;
private final ViewThemeUtils viewThemeUtils;
public ConversationItem(Conversation conversation, User user, Context activityContext, Status status) { public ConversationItem(Conversation conversation, User user, Context activityContext, Status status, final ViewThemeUtils viewThemeUtils) {
this.conversation = conversation; this.conversation = conversation;
this.user = user; this.user = user;
this.context = activityContext; this.context = activityContext;
this.status = status; this.status = status;
this.viewThemeUtils = viewThemeUtils;
} }
public ConversationItem(Conversation conversation, User user, public ConversationItem(Conversation conversation, User user,
Context activityContext, GenericTextHeaderItem genericTextHeaderItem, Status status) { Context activityContext, GenericTextHeaderItem genericTextHeaderItem, Status status,
this.conversation = conversation; final ViewThemeUtils viewThemeUtils) {
this.user = user; this(conversation, user, activityContext, status, viewThemeUtils);
this.context = activityContext;
this.header = genericTextHeaderItem; this.header = genericTextHeaderItem;
this.status = status;
} }
@Override @Override
@ -146,11 +146,7 @@ public class ConversationItem extends AbstractFlexibleItem<ConversationItem.Conv
if (adapter.hasFilter()) { if (adapter.hasFilter()) {
FlexibleUtils.highlightText(holder.binding.dialogName, conversation.getDisplayName(), FlexibleUtils.highlightText(holder.binding.dialogName, conversation.getDisplayName(),
String.valueOf(adapter.getFilter(String.class)), String.valueOf(adapter.getFilter(String.class)),
NextcloudTalkApplication viewThemeUtils.getElementColor(holder.binding.dialogName.getContext()));
.Companion
.getSharedApplication()
.getResources()
.getColor(R.color.colorPrimary));
} else { } else {
holder.binding.dialogName.setText(conversation.getDisplayName()); holder.binding.dialogName.setText(conversation.getDisplayName());
} }
@ -171,29 +167,18 @@ public class ConversationItem extends AbstractFlexibleItem<ConversationItem.Conv
int lightBubbleTextColor = ContextCompat.getColor( int lightBubbleTextColor = ContextCompat.getColor(
context, context,
R.color.conversation_unread_bubble_text); R.color.conversation_unread_bubble_text);
ColorStateList lightBubbleStrokeColor = ColorStateList.valueOf(
ContextCompat.getColor(context,
R.color.colorPrimary));
if (conversation.getType() == Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL) { if (conversation.getType() == Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL) {
holder.binding.dialogUnreadBubble.setChipBackgroundColorResource(R.color.colorPrimary); viewThemeUtils.colorChipBackground(holder.binding.dialogUnreadBubble);
holder.binding.dialogUnreadBubble.setTextColor(Color.WHITE);
} else if (conversation.getUnreadMention()) { } else if (conversation.getUnreadMention()) {
if (CapabilitiesUtilNew.hasSpreedFeatureCapability(user, "direct-mention-flag")) { if (CapabilitiesUtilNew.hasSpreedFeatureCapability(user, "direct-mention-flag")) {
if (conversation.getUnreadMentionDirect()) { if (conversation.getUnreadMentionDirect()) {
holder.binding.dialogUnreadBubble.setChipBackgroundColorResource(R.color.colorPrimary); viewThemeUtils.colorChipBackground(holder.binding.dialogUnreadBubble);
holder.binding.dialogUnreadBubble.setTextColor(Color.WHITE);
} else { } else {
holder.binding.dialogUnreadBubble.setChipBackgroundColorResource(R.color.bg_default); viewThemeUtils.colorChipOutlined(holder.binding.dialogUnreadBubble, 6.0f);
holder.binding.dialogUnreadBubble.setTextColor(ContextCompat.getColor(
context,
R.color.colorPrimary));
holder.binding.dialogUnreadBubble.setChipStrokeWidth(6.0f);
holder.binding.dialogUnreadBubble.setChipStrokeColor(lightBubbleStrokeColor);
} }
} else { } else {
holder.binding.dialogUnreadBubble.setChipBackgroundColorResource(R.color.colorPrimary); viewThemeUtils.colorChipBackground(holder.binding.dialogUnreadBubble);
holder.binding.dialogUnreadBubble.setTextColor(Color.WHITE);
} }
} else { } else {
holder.binding.dialogUnreadBubble.setChipBackgroundColor(lightBubbleFillColor); holder.binding.dialogUnreadBubble.setChipBackgroundColor(lightBubbleFillColor);

View File

@ -27,6 +27,7 @@ import android.view.View;
import com.nextcloud.talk.R; import com.nextcloud.talk.R;
import com.nextcloud.talk.databinding.RvItemTitleHeaderBinding; import com.nextcloud.talk.databinding.RvItemTitleHeaderBinding;
import com.nextcloud.talk.ui.theme.ViewThemeUtils;
import java.util.List; import java.util.List;
@ -39,12 +40,14 @@ public class GenericTextHeaderItem extends AbstractHeaderItem<GenericTextHeaderI
private static final String TAG = "GenericTextHeaderItem"; private static final String TAG = "GenericTextHeaderItem";
private final String title; private final String title;
private final ViewThemeUtils viewThemeUtils;
public GenericTextHeaderItem(String title) { public GenericTextHeaderItem(String title, ViewThemeUtils viewThemeUtils) {
super(); super();
setHidden(false); setHidden(false);
setSelectable(false); setSelectable(false);
this.title = title; this.title = title;
this.viewThemeUtils = viewThemeUtils;
} }
public String getModel() { public String getModel() {
@ -71,6 +74,7 @@ public class GenericTextHeaderItem extends AbstractHeaderItem<GenericTextHeaderI
Log.d(TAG, "We have payloads, so ignoring!"); Log.d(TAG, "We have payloads, so ignoring!");
} else { } else {
holder.binding.titleTextView.setText(title); holder.binding.titleTextView.setText(title);
viewThemeUtils.colorTextViewElement(holder.binding.titleTextView);
} }
} }

View File

@ -24,12 +24,12 @@ package com.nextcloud.talk.adapters.items
import android.content.Context import android.content.Context
import android.text.SpannableString import android.text.SpannableString
import android.view.View import android.view.View
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.nextcloud.talk.R import com.nextcloud.talk.R
import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.data.user.model.User
import com.nextcloud.talk.databinding.RvItemSearchMessageBinding import com.nextcloud.talk.databinding.RvItemSearchMessageBinding
import com.nextcloud.talk.models.domain.SearchMessageEntry import com.nextcloud.talk.models.domain.SearchMessageEntry
import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.utils.DisplayUtils import com.nextcloud.talk.utils.DisplayUtils
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
@ -42,7 +42,8 @@ data class MessageResultItem constructor(
private val context: Context, private val context: Context,
private val currentUser: User, private val currentUser: User,
val messageEntry: SearchMessageEntry, val messageEntry: SearchMessageEntry,
private val showHeader: Boolean = false private val showHeader: Boolean = false,
private val viewThemeUtils: ViewThemeUtils
) : ) :
AbstractFlexibleItem<MessageResultItem.ViewHolder>(), AbstractFlexibleItem<MessageResultItem.ViewHolder>(),
IFilterable<String>, IFilterable<String>,
@ -77,7 +78,7 @@ data class MessageResultItem constructor(
private fun bindMessageExcerpt(holder: ViewHolder) { private fun bindMessageExcerpt(holder: ViewHolder) {
val messageSpannable = SpannableString(messageEntry.messageExcerpt) val messageSpannable = SpannableString(messageEntry.messageExcerpt)
val highlightColor = ContextCompat.getColor(context, R.color.colorPrimary) val highlightColor = viewThemeUtils.getElementColor(holder.binding.messageExcerpt.context)
val highlightedSpan = DisplayUtils.searchAndColor(messageSpannable, messageEntry.searchTerm, highlightColor) val highlightedSpan = DisplayUtils.searchAndColor(messageSpannable, messageEntry.searchTerm, highlightColor)
holder.binding.messageExcerpt.text = highlightedSpan holder.binding.messageExcerpt.text = highlightedSpan
} }
@ -104,7 +105,7 @@ data class MessageResultItem constructor(
const val VIEW_TYPE: Int = R.layout.rv_item_search_message const val VIEW_TYPE: Int = R.layout.rv_item_search_message
} }
override fun getHeader(): GenericTextHeaderItem = MessagesTextHeaderItem(context) override fun getHeader(): GenericTextHeaderItem = MessagesTextHeaderItem(context, viewThemeUtils)
.apply { .apply {
isHidden = showHeader // FlexibleAdapter needs this hack for some reason isHidden = showHeader // FlexibleAdapter needs this hack for some reason
} }

View File

@ -23,8 +23,10 @@ package com.nextcloud.talk.adapters.items
import android.content.Context import android.content.Context
import com.nextcloud.talk.R import com.nextcloud.talk.R
import com.nextcloud.talk.ui.theme.ViewThemeUtils
class MessagesTextHeaderItem(context: Context) : GenericTextHeaderItem(context.getString(R.string.messages)) { class MessagesTextHeaderItem(context: Context, viewThemeUtils: ViewThemeUtils) :
GenericTextHeaderItem(context.getString(R.string.messages), viewThemeUtils) {
companion object { companion object {
/** /**
* "Random" value, just has to be different than other view types * "Random" value, just has to be different than other view types

View File

@ -48,6 +48,7 @@ import com.nextcloud.talk.application.NextcloudTalkApplication
import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
import com.nextcloud.talk.databinding.ItemCustomIncomingVoiceMessageBinding import com.nextcloud.talk.databinding.ItemCustomIncomingVoiceMessageBinding
import com.nextcloud.talk.models.json.chat.ChatMessage import com.nextcloud.talk.models.json.chat.ChatMessage
import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.DisplayUtils import com.nextcloud.talk.utils.DisplayUtils
import com.nextcloud.talk.utils.preferences.AppPreferences import com.nextcloud.talk.utils.preferences.AppPreferences
@ -66,6 +67,9 @@ class IncomingVoiceMessageViewHolder(incomingView: View, payload: Any) : Message
@Inject @Inject
var context: Context? = null var context: Context? = null
@Inject
lateinit var viewThemeUtils: ViewThemeUtils
@JvmField @JvmField
@Inject @Inject
var appPreferences: AppPreferences? = null var appPreferences: AppPreferences? = null
@ -93,6 +97,7 @@ class IncomingVoiceMessageViewHolder(incomingView: View, payload: Any) : Message
updateDownloadState(message) updateDownloadState(message)
binding.seekbar.max = message.voiceMessageDuration binding.seekbar.max = message.voiceMessageDuration
viewThemeUtils.themeHorizontalSeekBar(binding.seekbar)
if (message.isPlayingVoiceMessage) { if (message.isPlayingVoiceMessage) {
showPlayButton() showPlayButton()

View File

@ -25,14 +25,13 @@ package com.nextcloud.talk.adapters.messages
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.graphics.PorterDuff
import android.net.Uri import android.net.Uri
import android.text.Spannable import android.text.Spannable
import android.text.SpannableString import android.text.SpannableString
import android.util.TypedValue import android.util.TypedValue
import android.view.View import android.view.View
import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.ColorUtils
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import autodagger.AutoInjector import autodagger.AutoInjector
import coil.load import coil.load
@ -44,22 +43,29 @@ import com.nextcloud.talk.databinding.ItemCustomOutcomingTextMessageBinding
import com.nextcloud.talk.models.json.chat.ChatMessage import com.nextcloud.talk.models.json.chat.ChatMessage
import com.nextcloud.talk.models.json.chat.ReadStatus import com.nextcloud.talk.models.json.chat.ReadStatus
import com.nextcloud.talk.ui.recyclerview.MessageSwipeCallback import com.nextcloud.talk.ui.recyclerview.MessageSwipeCallback
import com.nextcloud.talk.ui.theme.ServerTheme
import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.DisplayUtils.getMessageSelector import com.nextcloud.talk.utils.DisplayUtils.getMessageSelector
import com.nextcloud.talk.utils.DisplayUtils.searchAndReplaceWithMentionSpan import com.nextcloud.talk.utils.DisplayUtils.searchAndReplaceWithMentionSpan
import com.nextcloud.talk.utils.TextMatchers import com.nextcloud.talk.utils.TextMatchers
import com.stfalcon.chatkit.messages.MessageHolders.OutcomingTextMessageViewHolder import com.stfalcon.chatkit.messages.MessageHolders.OutcomingTextMessageViewHolder
import java.util.HashMap
import javax.inject.Inject import javax.inject.Inject
import kotlin.math.roundToInt
@AutoInjector(NextcloudTalkApplication::class) @AutoInjector(NextcloudTalkApplication::class)
class MagicOutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessageViewHolder<ChatMessage>(itemView) { class MagicOutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessageViewHolder<ChatMessage>(itemView) {
private val binding: ItemCustomOutcomingTextMessageBinding = ItemCustomOutcomingTextMessageBinding.bind(itemView) private val binding: ItemCustomOutcomingTextMessageBinding = ItemCustomOutcomingTextMessageBinding.bind(itemView)
private val realView: View = itemView private val realView: View = itemView
@JvmField
@Inject @Inject
var context: Context? = null lateinit var context: Context
@Inject
lateinit var viewThemeUtils: ViewThemeUtils
@Inject
lateinit var serverTheme: ServerTheme
lateinit var reactionsInterface: ReactionsInterface lateinit var reactionsInterface: ReactionsInterface
@ -69,7 +75,7 @@ class MagicOutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessage
val messageParameters: HashMap<String?, HashMap<String?, String?>>? = message.messageParameters val messageParameters: HashMap<String?, HashMap<String?, String?>>? = message.messageParameters
var messageString: Spannable = SpannableString(message.text) var messageString: Spannable = SpannableString(message.text)
realView.isSelected = false realView.isSelected = false
binding.messageTime.setTextColor(context!!.resources.getColor(R.color.white60)) binding.messageTime.setTextColor(ColorUtils.setAlphaComponent(serverTheme.colorText, ALPHA_60_INT))
val layoutParams = binding.messageTime.layoutParams as FlexboxLayout.LayoutParams val layoutParams = binding.messageTime.layoutParams as FlexboxLayout.LayoutParams
layoutParams.isWrapBefore = false layoutParams.isWrapBefore = false
var textSize = context!!.resources.getDimension(R.dimen.chat_text_size) var textSize = context!!.resources.getDimension(R.dimen.chat_text_size)
@ -89,6 +95,8 @@ class MagicOutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessage
binding.messageText.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize) binding.messageText.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize)
binding.messageTime.layoutParams = layoutParams binding.messageTime.layoutParams = layoutParams
binding.messageText.text = messageString binding.messageText.text = messageString
binding.messageText.setTextColor(serverTheme.colorText)
binding.messageText.setLinkTextColor(serverTheme.colorText)
// parent message handling // parent message handling
if (!message.isDeleted && message.parentMessage != null) { if (!message.isDeleted && message.parentMessage != null) {
@ -112,8 +120,8 @@ class MagicOutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessage
readStatusDrawableInt?.let { drawableInt -> readStatusDrawableInt?.let { drawableInt ->
ResourcesCompat.getDrawable(context!!.resources, drawableInt, null)?.let { ResourcesCompat.getDrawable(context!!.resources, drawableInt, null)?.let {
it.setColorFilter(ContextCompat.getColor(context!!, R.color.white60), PorterDuff.Mode.SRC_ATOP)
binding.checkMark.setImageDrawable(it) binding.checkMark.setImageDrawable(it)
viewThemeUtils.colorImageViewText(binding.checkMark)
} }
} }
@ -148,20 +156,25 @@ class MagicOutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessage
binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName
?: context!!.getText(R.string.nc_nick_guest) ?: context!!.getText(R.string.nc_nick_guest)
binding.messageQuote.quotedMessage.text = parentChatMessage.text binding.messageQuote.quotedMessage.text = parentChatMessage.text
binding.messageQuote.quotedMessage.setTextColor( binding.messageQuote.quotedMessage.setTextColor(serverTheme.colorText)
ContextCompat.getColor(context!!, R.color.nc_outcoming_text_default)
)
binding.messageQuote.quotedMessageAuthor.setTextColor(ContextCompat.getColor(context!!, R.color.nc_grey))
binding.messageQuote.quoteColoredView.setBackgroundResource(R.color.white) binding.messageQuote.quotedMessageAuthor.setTextColor(
ColorUtils.setAlphaComponent(
serverTheme.colorText,
ALPHA_80_INT
)
)
binding.messageQuote.quoteColoredView.setBackgroundColor(serverTheme.colorText)
} }
private fun setBubbleOnChatMessage(message: ChatMessage) { private fun setBubbleOnChatMessage(message: ChatMessage) {
val resources = sharedApplication!!.resources val resources = sharedApplication!!.resources
val elementColor = viewThemeUtils.getElementColor(binding.root.context)
val bgBubbleColor = if (message.isDeleted) { val bgBubbleColor = if (message.isDeleted) {
ResourcesCompat.getColor(resources, R.color.bg_message_list_outcoming_bubble_deleted, null) ColorUtils.setAlphaComponent(elementColor, HALF_ALPHA_INT)
} else { } else {
ResourcesCompat.getColor(resources, R.color.bg_message_list_outcoming_bubble, null) elementColor
} }
if (message.isGrouped) { if (message.isGrouped) {
val bubbleDrawable = getMessageSelector( val bubbleDrawable = getMessageSelector(
@ -221,5 +234,8 @@ class MagicOutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessage
companion object { companion object {
const val TEXT_SIZE_MULTIPLIER = 2.5 const val TEXT_SIZE_MULTIPLIER = 2.5
private const val HALF_ALPHA_INT: Int = 255 / 2
private val ALPHA_60_INT: Int = (255 * 0.6).roundToInt()
private val ALPHA_80_INT: Int = (255 * 0.8).roundToInt()
} }
} }

View File

@ -29,6 +29,7 @@ package com.nextcloud.talk.adapters.messages;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable; import android.graphics.drawable.LayerDrawable;
import android.net.Uri; import android.net.Uri;
@ -49,6 +50,7 @@ import com.nextcloud.talk.components.filebrowser.webdav.ReadFilesystemOperation;
import com.nextcloud.talk.data.user.model.User; import com.nextcloud.talk.data.user.model.User;
import com.nextcloud.talk.databinding.ReactionsInsideMessageBinding; import com.nextcloud.talk.databinding.ReactionsInsideMessageBinding;
import com.nextcloud.talk.models.json.chat.ChatMessage; import com.nextcloud.talk.models.json.chat.ChatMessage;
import com.nextcloud.talk.ui.theme.ServerTheme;
import com.nextcloud.talk.utils.DisplayUtils; import com.nextcloud.talk.utils.DisplayUtils;
import com.nextcloud.talk.utils.DrawableUtils; import com.nextcloud.talk.utils.DrawableUtils;
import com.nextcloud.talk.utils.FileViewerUtils; import com.nextcloud.talk.utils.FileViewerUtils;
@ -91,6 +93,9 @@ public abstract class MagicPreviewMessageViewHolder extends MessageHolders.Incom
@Inject @Inject
Context context; Context context;
@Inject
ServerTheme serverTheme;
@Inject @Inject
OkHttpClient okHttpClient; OkHttpClient okHttpClient;
@ -175,6 +180,13 @@ public abstract class MagicPreviewMessageViewHolder extends MessageHolders.Incom
String mimetype = message.getSelectedIndividualHashMap().get(KEY_MIMETYPE); String mimetype = message.getSelectedIndividualHashMap().get(KEY_MIMETYPE);
int drawableResourceId = DrawableUtils.INSTANCE.getDrawableResourceIdForMimeType(mimetype); int drawableResourceId = DrawableUtils.INSTANCE.getDrawableResourceIdForMimeType(mimetype);
Drawable drawable = ContextCompat.getDrawable(context, drawableResourceId); Drawable drawable = ContextCompat.getDrawable(context, drawableResourceId);
if (drawable != null &&
(drawableResourceId == R.drawable.ic_mimetype_folder ||
drawableResourceId == R.drawable.ic_mimetype_package_x_generic)) {
drawable.setColorFilter(serverTheme.getPrimaryColor(), PorterDuff.Mode.SRC_ATOP);
}
image.getHierarchy().setPlaceholderImage(drawable); image.getHierarchy().setPlaceholderImage(drawable);
} else { } else {
fetchFileInformation("/" + message.getSelectedIndividualHashMap().get(KEY_PATH), fetchFileInformation("/" + message.getSelectedIndividualHashMap().get(KEY_PATH),

View File

@ -25,7 +25,6 @@ package com.nextcloud.talk.adapters.messages
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.graphics.PorterDuff
import android.net.Uri import android.net.Uri
import android.util.Log import android.util.Log
import android.util.TypedValue import android.util.TypedValue
@ -35,6 +34,8 @@ import android.webkit.WebView
import android.webkit.WebViewClient import android.webkit.WebViewClient
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.content.res.AppCompatResources import androidx.appcompat.content.res.AppCompatResources
import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.ColorUtils
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import autodagger.AutoInjector import autodagger.AutoInjector
import coil.load import coil.load
@ -45,12 +46,15 @@ import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedA
import com.nextcloud.talk.databinding.ItemCustomOutcomingLocationMessageBinding import com.nextcloud.talk.databinding.ItemCustomOutcomingLocationMessageBinding
import com.nextcloud.talk.models.json.chat.ChatMessage import com.nextcloud.talk.models.json.chat.ChatMessage
import com.nextcloud.talk.models.json.chat.ReadStatus import com.nextcloud.talk.models.json.chat.ReadStatus
import com.nextcloud.talk.ui.theme.ServerTheme
import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.DisplayUtils import com.nextcloud.talk.utils.DisplayUtils
import com.nextcloud.talk.utils.UriUtils import com.nextcloud.talk.utils.UriUtils
import com.stfalcon.chatkit.messages.MessageHolders import com.stfalcon.chatkit.messages.MessageHolders
import java.net.URLEncoder import java.net.URLEncoder
import javax.inject.Inject import javax.inject.Inject
import kotlin.math.roundToInt
@AutoInjector(NextcloudTalkApplication::class) @AutoInjector(NextcloudTalkApplication::class)
class OutcomingLocationMessageViewHolder(incomingView: View) : MessageHolders class OutcomingLocationMessageViewHolder(incomingView: View) : MessageHolders
@ -68,6 +72,12 @@ class OutcomingLocationMessageViewHolder(incomingView: View) : MessageHolders
@Inject @Inject
var context: Context? = null var context: Context? = null
@Inject
lateinit var viewThemeUtils: ViewThemeUtils
@Inject
lateinit var serverTheme: ServerTheme
lateinit var reactionsInterface: ReactionsInterface lateinit var reactionsInterface: ReactionsInterface
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
@ -76,7 +86,6 @@ class OutcomingLocationMessageViewHolder(incomingView: View) : MessageHolders
sharedApplication!!.componentApplication.inject(this) sharedApplication!!.componentApplication.inject(this)
realView.isSelected = false realView.isSelected = false
binding.messageTime.setTextColor(context!!.resources.getColor(R.color.white60))
val layoutParams = binding.messageTime.layoutParams as FlexboxLayout.LayoutParams val layoutParams = binding.messageTime.layoutParams as FlexboxLayout.LayoutParams
layoutParams.isWrapBefore = false layoutParams.isWrapBefore = false
@ -85,7 +94,11 @@ class OutcomingLocationMessageViewHolder(incomingView: View) : MessageHolders
colorizeMessageBubble(message) colorizeMessageBubble(message)
binding.messageText.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize) binding.messageText.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize)
binding.messageTime.layoutParams = layoutParams binding.messageTime.layoutParams = layoutParams
binding.messageTime.setTextColor(ColorUtils.setAlphaComponent(serverTheme.colorText, ALPHA_60_INT))
binding.messageText.text = message.text binding.messageText.text = message.text
binding.messageText.setTextColor(serverTheme.colorText)
binding.messageText.setLinkTextColor(serverTheme.colorText)
// parent message handling // parent message handling
setParentMessageDataOnMessageItem(message) setParentMessageDataOnMessageItem(message)
@ -104,8 +117,8 @@ class OutcomingLocationMessageViewHolder(incomingView: View) : MessageHolders
readStatusDrawableInt?.let { drawableInt -> readStatusDrawableInt?.let { drawableInt ->
AppCompatResources.getDrawable(context!!, drawableInt)?.let { AppCompatResources.getDrawable(context!!, drawableInt)?.let {
it.setColorFilter(context?.resources!!.getColor(R.color.white60), PorterDuff.Mode.SRC_ATOP)
binding.checkMark.setImageDrawable(it) binding.checkMark.setImageDrawable(it)
viewThemeUtils.colorImageViewText(binding.checkMark)
} }
} }
@ -200,12 +213,12 @@ class OutcomingLocationMessageViewHolder(incomingView: View) : MessageHolders
binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName
?: context!!.getText(R.string.nc_nick_guest) ?: context!!.getText(R.string.nc_nick_guest)
binding.messageQuote.quotedMessage.text = parentChatMessage.text binding.messageQuote.quotedMessage.text = parentChatMessage.text
binding.messageQuote.quotedMessage.setTextColor( binding.messageQuote.quotedMessage.setTextColor(serverTheme.colorText)
context!!.resources.getColor(R.color.nc_outcoming_text_default) binding.messageQuote.quotedMessageAuthor.setTextColor(
ColorUtils.setAlphaComponent(serverTheme.colorText, ALPHA_80_INT)
) )
binding.messageQuote.quotedMessageAuthor.setTextColor(context!!.resources.getColor(R.color.nc_grey))
binding.messageQuote.quoteColoredView.setBackgroundResource(R.color.white) binding.messageQuote.quoteColoredView.setBackgroundColor(serverTheme.colorText)
binding.messageQuote.quotedChatMessageView.visibility = View.VISIBLE binding.messageQuote.quotedChatMessageView.visibility = View.VISIBLE
} else { } else {
@ -215,15 +228,16 @@ class OutcomingLocationMessageViewHolder(incomingView: View) : MessageHolders
private fun colorizeMessageBubble(message: ChatMessage) { private fun colorizeMessageBubble(message: ChatMessage) {
val resources = sharedApplication!!.resources val resources = sharedApplication!!.resources
val elementColor = viewThemeUtils.getElementColor(binding.root.context)
val bgBubbleColor = if (message.isDeleted) { val bgBubbleColor = if (message.isDeleted) {
resources.getColor(R.color.bg_message_list_outcoming_bubble_deleted) ColorUtils.setAlphaComponent(elementColor, HALF_ALPHA_INT)
} else { } else {
resources.getColor(R.color.bg_message_list_outcoming_bubble) elementColor
} }
if (message.isGrouped) { if (message.isGrouped) {
val bubbleDrawable = DisplayUtils.getMessageSelector( val bubbleDrawable = DisplayUtils.getMessageSelector(
bgBubbleColor, bgBubbleColor,
resources.getColor(R.color.transparent), ResourcesCompat.getColor(resources, R.color.transparent, null),
bgBubbleColor, bgBubbleColor,
R.drawable.shape_grouped_outcoming_message R.drawable.shape_grouped_outcoming_message
) )
@ -231,7 +245,7 @@ class OutcomingLocationMessageViewHolder(incomingView: View) : MessageHolders
} else { } else {
val bubbleDrawable = DisplayUtils.getMessageSelector( val bubbleDrawable = DisplayUtils.getMessageSelector(
bgBubbleColor, bgBubbleColor,
resources.getColor(R.color.transparent), ResourcesCompat.getColor(resources, R.color.transparent, null),
bgBubbleColor, bgBubbleColor,
R.drawable.shape_outcoming_message R.drawable.shape_outcoming_message
) )
@ -261,5 +275,8 @@ class OutcomingLocationMessageViewHolder(incomingView: View) : MessageHolders
companion object { companion object {
private const val TAG = "LocOutMessageView" private const val TAG = "LocOutMessageView"
private const val HALF_ALPHA_INT: Int = 255 / 2
private val ALPHA_60_INT: Int = (255 * 0.6).roundToInt()
private val ALPHA_80_INT: Int = (255 * 0.8).roundToInt()
} }
} }

View File

@ -23,9 +23,11 @@ package com.nextcloud.talk.adapters.messages
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.graphics.PorterDuff import android.content.res.ColorStateList
import android.view.View import android.view.View
import androidx.appcompat.content.res.AppCompatResources import androidx.appcompat.content.res.AppCompatResources
import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.ColorUtils
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import autodagger.AutoInjector import autodagger.AutoInjector
import coil.load import coil.load
@ -38,11 +40,14 @@ import com.nextcloud.talk.databinding.ItemCustomOutcomingPollMessageBinding
import com.nextcloud.talk.models.json.chat.ChatMessage import com.nextcloud.talk.models.json.chat.ChatMessage
import com.nextcloud.talk.models.json.chat.ReadStatus import com.nextcloud.talk.models.json.chat.ReadStatus
import com.nextcloud.talk.polls.ui.PollMainDialogFragment import com.nextcloud.talk.polls.ui.PollMainDialogFragment
import com.nextcloud.talk.ui.theme.ServerTheme
import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.DisplayUtils import com.nextcloud.talk.utils.DisplayUtils
import com.nextcloud.talk.utils.preferences.AppPreferences import com.nextcloud.talk.utils.preferences.AppPreferences
import com.stfalcon.chatkit.messages.MessageHolders import com.stfalcon.chatkit.messages.MessageHolders
import javax.inject.Inject import javax.inject.Inject
import kotlin.math.roundToInt
@AutoInjector(NextcloudTalkApplication::class) @AutoInjector(NextcloudTalkApplication::class)
class OutcomingPollMessageViewHolder(outcomingView: View, payload: Any) : MessageHolders class OutcomingPollMessageViewHolder(outcomingView: View, payload: Any) : MessageHolders
@ -54,6 +59,12 @@ class OutcomingPollMessageViewHolder(outcomingView: View, payload: Any) : Messag
@Inject @Inject
lateinit var context: Context lateinit var context: Context
@Inject
lateinit var viewThemeUtils: ViewThemeUtils
@Inject
lateinit var serverTheme: ServerTheme
@Inject @Inject
lateinit var appPreferences: AppPreferences lateinit var appPreferences: AppPreferences
@ -73,7 +84,12 @@ class OutcomingPollMessageViewHolder(outcomingView: View, payload: Any) : Messag
colorizeMessageBubble(message) colorizeMessageBubble(message)
itemView.isSelected = false itemView.isSelected = false
binding.messageTime.setTextColor(context.resources.getColor(R.color.white60)) binding.messageTime.setTextColor(
ColorUtils.setAlphaComponent(
serverTheme.colorText,
ALPHA_60_INT
)
)
// parent message handling // parent message handling
setParentMessageDataOnMessageItem(message) setParentMessageDataOnMessageItem(message)
@ -92,8 +108,8 @@ class OutcomingPollMessageViewHolder(outcomingView: View, payload: Any) : Messag
readStatusDrawableInt?.let { drawableInt -> readStatusDrawableInt?.let { drawableInt ->
AppCompatResources.getDrawable(context, drawableInt)?.let { AppCompatResources.getDrawable(context, drawableInt)?.let {
it.setColorFilter(context.resources!!.getColor(R.color.white60), PorterDuff.Mode.SRC_ATOP)
binding.checkMark.setImageDrawable(it) binding.checkMark.setImageDrawable(it)
viewThemeUtils.colorImageViewText(binding.checkMark)
} }
} }
@ -126,6 +142,9 @@ class OutcomingPollMessageViewHolder(outcomingView: View, payload: Any) : Messag
} }
if (pollId != null && pollName != null) { if (pollId != null && pollName != null) {
binding.messagePollTitle.setTextColor(serverTheme.colorText)
binding.messagePollSubtitle.setTextColor(serverTheme.colorText)
binding.messagePollIcon.imageTintList = ColorStateList.valueOf(serverTheme.colorText)
binding.messagePollTitle.text = pollName binding.messagePollTitle.text = pollName
val roomToken = (payload as? MessagePayload)!!.roomToken val roomToken = (payload as? MessagePayload)!!.roomToken
@ -165,12 +184,12 @@ class OutcomingPollMessageViewHolder(outcomingView: View, payload: Any) : Messag
binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName
?: context.getText(R.string.nc_nick_guest) ?: context.getText(R.string.nc_nick_guest)
binding.messageQuote.quotedMessage.text = parentChatMessage.text binding.messageQuote.quotedMessage.text = parentChatMessage.text
binding.messageQuote.quotedMessage.setTextColor( binding.messageQuote.quotedMessage.setTextColor(serverTheme.colorText)
context.resources.getColor(R.color.nc_outcoming_text_default) binding.messageQuote.quotedMessageAuthor.setTextColor(
ColorUtils.setAlphaComponent(serverTheme.colorText, ALPHA_80_INT)
) )
binding.messageQuote.quotedMessageAuthor.setTextColor(context.resources.getColor(R.color.nc_grey))
binding.messageQuote.quoteColoredView.setBackgroundResource(R.color.white) binding.messageQuote.quoteColoredView.setBackgroundColor(serverTheme.colorText)
binding.messageQuote.quotedChatMessageView.visibility = View.VISIBLE binding.messageQuote.quotedChatMessageView.visibility = View.VISIBLE
} else { } else {
@ -180,15 +199,16 @@ class OutcomingPollMessageViewHolder(outcomingView: View, payload: Any) : Messag
private fun colorizeMessageBubble(message: ChatMessage) { private fun colorizeMessageBubble(message: ChatMessage) {
val resources = sharedApplication!!.resources val resources = sharedApplication!!.resources
val elementColor = viewThemeUtils.getElementColor(binding.root.context)
val bgBubbleColor = if (message.isDeleted) { val bgBubbleColor = if (message.isDeleted) {
resources.getColor(R.color.bg_message_list_outcoming_bubble_deleted) ColorUtils.setAlphaComponent(elementColor, HALF_ALPHA_INT)
} else { } else {
resources.getColor(R.color.bg_message_list_outcoming_bubble) elementColor
} }
if (message.isGrouped) { if (message.isGrouped) {
val bubbleDrawable = DisplayUtils.getMessageSelector( val bubbleDrawable = DisplayUtils.getMessageSelector(
bgBubbleColor, bgBubbleColor,
resources.getColor(R.color.transparent), ResourcesCompat.getColor(resources, R.color.transparent, null),
bgBubbleColor, bgBubbleColor,
R.drawable.shape_grouped_outcoming_message R.drawable.shape_grouped_outcoming_message
) )
@ -196,7 +216,7 @@ class OutcomingPollMessageViewHolder(outcomingView: View, payload: Any) : Messag
} else { } else {
val bubbleDrawable = DisplayUtils.getMessageSelector( val bubbleDrawable = DisplayUtils.getMessageSelector(
bgBubbleColor, bgBubbleColor,
resources.getColor(R.color.transparent), ResourcesCompat.getColor(resources, R.color.transparent, null),
bgBubbleColor, bgBubbleColor,
R.drawable.shape_outcoming_message R.drawable.shape_outcoming_message
) )
@ -210,5 +230,8 @@ class OutcomingPollMessageViewHolder(outcomingView: View, payload: Any) : Messag
companion object { companion object {
private val TAG = NextcloudTalkApplication::class.java.simpleName private val TAG = NextcloudTalkApplication::class.java.simpleName
private val ALPHA_60_INT: Int = (255 * 0.6).roundToInt()
private val ALPHA_80_INT: Int = (255 * 0.8).roundToInt()
private const val HALF_ALPHA_INT: Int = 255 / 2
} }
} }

View File

@ -31,6 +31,8 @@ import android.view.View
import android.widget.SeekBar import android.widget.SeekBar
import androidx.appcompat.content.res.AppCompatResources import androidx.appcompat.content.res.AppCompatResources
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.ColorUtils
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.work.WorkInfo import androidx.work.WorkInfo
import androidx.work.WorkManager import androidx.work.WorkManager
@ -42,12 +44,15 @@ import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedA
import com.nextcloud.talk.databinding.ItemCustomOutcomingVoiceMessageBinding import com.nextcloud.talk.databinding.ItemCustomOutcomingVoiceMessageBinding
import com.nextcloud.talk.models.json.chat.ChatMessage import com.nextcloud.talk.models.json.chat.ChatMessage
import com.nextcloud.talk.models.json.chat.ReadStatus import com.nextcloud.talk.models.json.chat.ReadStatus
import com.nextcloud.talk.ui.theme.ServerTheme
import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.DisplayUtils import com.nextcloud.talk.utils.DisplayUtils
import com.nextcloud.talk.utils.preferences.AppPreferences import com.nextcloud.talk.utils.preferences.AppPreferences
import com.stfalcon.chatkit.messages.MessageHolders import com.stfalcon.chatkit.messages.MessageHolders
import java.util.concurrent.ExecutionException import java.util.concurrent.ExecutionException
import javax.inject.Inject import javax.inject.Inject
import kotlin.math.roundToInt
@AutoInjector(NextcloudTalkApplication::class) @AutoInjector(NextcloudTalkApplication::class)
class OutcomingVoiceMessageViewHolder(outcomingView: View) : MessageHolders class OutcomingVoiceMessageViewHolder(outcomingView: View) : MessageHolders
@ -60,6 +65,12 @@ class OutcomingVoiceMessageViewHolder(outcomingView: View) : MessageHolders
@Inject @Inject
var context: Context? = null var context: Context? = null
@Inject
lateinit var viewThemeUtils: ViewThemeUtils
@Inject
lateinit var serverTheme: ServerTheme
@JvmField @JvmField
@Inject @Inject
var appPreferences: AppPreferences? = null var appPreferences: AppPreferences? = null
@ -80,13 +91,19 @@ class OutcomingVoiceMessageViewHolder(outcomingView: View) : MessageHolders
colorizeMessageBubble(message) colorizeMessageBubble(message)
itemView.isSelected = false itemView.isSelected = false
binding.messageTime.setTextColor(context!!.resources.getColor(R.color.white60)) binding.messageTime.setTextColor(
ColorUtils.setAlphaComponent(
serverTheme.colorText,
ALPHA_60_INT
)
)
// parent message handling // parent message handling
setParentMessageDataOnMessageItem(message) setParentMessageDataOnMessageItem(message)
updateDownloadState(message) updateDownloadState(message)
binding.seekbar.max = message.voiceMessageDuration binding.seekbar.max = message.voiceMessageDuration
viewThemeUtils.themeHorizontalSeekBar(binding.seekbar, serverTheme.colorText)
handleIsPlayingVoiceMessageState(message) handleIsPlayingVoiceMessageState(message)
@ -124,8 +141,8 @@ class OutcomingVoiceMessageViewHolder(outcomingView: View) : MessageHolders
readStatusDrawableInt?.let { drawableInt -> readStatusDrawableInt?.let { drawableInt ->
AppCompatResources.getDrawable(context!!, drawableInt)?.let { AppCompatResources.getDrawable(context!!, drawableInt)?.let {
it.setColorFilter(context?.resources!!.getColor(R.color.white60), PorterDuff.Mode.SRC_ATOP)
binding.checkMark.setImageDrawable(it) binding.checkMark.setImageDrawable(it)
viewThemeUtils.colorImageViewText(binding.checkMark)
} }
} }
@ -148,6 +165,7 @@ class OutcomingVoiceMessageViewHolder(outcomingView: View) : MessageHolders
context!!, context!!,
R.drawable.ic_baseline_play_arrow_voice_message_24 R.drawable.ic_baseline_play_arrow_voice_message_24
) )
binding.playPauseBtn.icon.setColorFilter(serverTheme.colorText, PorterDuff.Mode.SRC_ATOP)
binding.seekbar.progress = SEEKBAR_START binding.seekbar.progress = SEEKBAR_START
message.resetVoiceMessage = false message.resetVoiceMessage = false
} }
@ -168,6 +186,7 @@ class OutcomingVoiceMessageViewHolder(outcomingView: View) : MessageHolders
context!!, context!!,
R.drawable.ic_baseline_pause_voice_message_24 R.drawable.ic_baseline_pause_voice_message_24
) )
binding.playPauseBtn.icon.setColorFilter(serverTheme.colorText, PorterDuff.Mode.SRC_ATOP)
binding.seekbar.progress = message.voiceMessagePlayedSeconds binding.seekbar.progress = message.voiceMessagePlayedSeconds
} else { } else {
binding.playPauseBtn.visibility = View.VISIBLE binding.playPauseBtn.visibility = View.VISIBLE
@ -175,6 +194,7 @@ class OutcomingVoiceMessageViewHolder(outcomingView: View) : MessageHolders
context!!, context!!,
R.drawable.ic_baseline_play_arrow_voice_message_24 R.drawable.ic_baseline_play_arrow_voice_message_24
) )
binding.playPauseBtn.icon.setColorFilter(serverTheme.colorText, PorterDuff.Mode.SRC_ATOP)
} }
} }
@ -250,12 +270,15 @@ class OutcomingVoiceMessageViewHolder(outcomingView: View) : MessageHolders
binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName
?: context!!.getText(R.string.nc_nick_guest) ?: context!!.getText(R.string.nc_nick_guest)
binding.messageQuote.quotedMessage.text = parentChatMessage.text binding.messageQuote.quotedMessage.text = parentChatMessage.text
binding.messageQuote.quotedMessage.setTextColor( binding.messageQuote.quotedMessage.setTextColor(serverTheme.colorText)
context!!.resources.getColor(R.color.nc_outcoming_text_default) binding.messageQuote.quotedMessageAuthor.setTextColor(
ColorUtils.setAlphaComponent(
serverTheme.colorText,
ALPHA_80_INT
)
) )
binding.messageQuote.quotedMessageAuthor.setTextColor(context!!.resources.getColor(R.color.nc_grey))
binding.messageQuote.quoteColoredView.setBackgroundResource(R.color.white) binding.messageQuote.quoteColoredView.setBackgroundColor(serverTheme.colorText)
binding.messageQuote.quotedChatMessageView.visibility = View.VISIBLE binding.messageQuote.quotedChatMessageView.visibility = View.VISIBLE
} else { } else {
@ -265,15 +288,16 @@ class OutcomingVoiceMessageViewHolder(outcomingView: View) : MessageHolders
private fun colorizeMessageBubble(message: ChatMessage) { private fun colorizeMessageBubble(message: ChatMessage) {
val resources = sharedApplication!!.resources val resources = sharedApplication!!.resources
val elementColor = viewThemeUtils.getElementColor(binding.root.context)
val bgBubbleColor = if (message.isDeleted) { val bgBubbleColor = if (message.isDeleted) {
resources.getColor(R.color.bg_message_list_outcoming_bubble_deleted) ColorUtils.setAlphaComponent(elementColor, HALF_ALPHA_INT)
} else { } else {
resources.getColor(R.color.bg_message_list_outcoming_bubble) elementColor
} }
if (message.isGrouped) { if (message.isGrouped) {
val bubbleDrawable = DisplayUtils.getMessageSelector( val bubbleDrawable = DisplayUtils.getMessageSelector(
bgBubbleColor, bgBubbleColor,
resources.getColor(R.color.transparent), ResourcesCompat.getColor(resources, R.color.transparent, null),
bgBubbleColor, bgBubbleColor,
R.drawable.shape_grouped_outcoming_message R.drawable.shape_grouped_outcoming_message
) )
@ -281,7 +305,7 @@ class OutcomingVoiceMessageViewHolder(outcomingView: View) : MessageHolders
} else { } else {
val bubbleDrawable = DisplayUtils.getMessageSelector( val bubbleDrawable = DisplayUtils.getMessageSelector(
bgBubbleColor, bgBubbleColor,
resources.getColor(R.color.transparent), ResourcesCompat.getColor(resources, R.color.transparent, null),
bgBubbleColor, bgBubbleColor,
R.drawable.shape_outcoming_message R.drawable.shape_outcoming_message
) )
@ -300,5 +324,8 @@ class OutcomingVoiceMessageViewHolder(outcomingView: View) : MessageHolders
companion object { companion object {
private const val TAG = "VoiceOutMessageView" private const val TAG = "VoiceOutMessageView"
private const val SEEKBAR_START: Int = 0 private const val SEEKBAR_START: Int = 0
private const val HALF_ALPHA_INT: Int = 255 / 2
private val ALPHA_80_INT: Int = (255 * 0.8).roundToInt()
private val ALPHA_60_INT: Int = (255 * 0.6).roundToInt()
} }
} }

View File

@ -61,6 +61,7 @@ import com.nextcloud.talk.dagger.modules.ViewModelModule
import com.nextcloud.talk.jobs.AccountRemovalWorker import com.nextcloud.talk.jobs.AccountRemovalWorker
import com.nextcloud.talk.jobs.CapabilitiesWorker import com.nextcloud.talk.jobs.CapabilitiesWorker
import com.nextcloud.talk.jobs.SignalingSettingsWorker import com.nextcloud.talk.jobs.SignalingSettingsWorker
import com.nextcloud.talk.ui.theme.ThemeModule
import com.nextcloud.talk.utils.ClosedInterfaceImpl import com.nextcloud.talk.utils.ClosedInterfaceImpl
import com.nextcloud.talk.utils.DeviceUtils import com.nextcloud.talk.utils.DeviceUtils
import com.nextcloud.talk.utils.DisplayUtils import com.nextcloud.talk.utils.DisplayUtils
@ -96,7 +97,8 @@ import javax.inject.Singleton
ArbitraryStorageModule::class, ArbitraryStorageModule::class,
ViewModelModule::class, ViewModelModule::class,
RepositoryModule::class, RepositoryModule::class,
UtilsModule::class UtilsModule::class,
ThemeModule::class
] ]
) )
@Singleton @Singleton
@ -120,6 +122,7 @@ class NextcloudTalkApplication : MultiDexApplication(), LifecycleObserver {
override fun preKey(database: SQLiteDatabase) { override fun preKey(database: SQLiteDatabase) {
// unused atm // unused atm
} }
override fun postKey(database: SQLiteDatabase) { override fun postKey(database: SQLiteDatabase) {
Log.i("TalkApplication", "DB cipher_migrate START") Log.i("TalkApplication", "DB cipher_migrate START")
database.rawExecSQL("PRAGMA cipher_migrate;") database.rawExecSQL("PRAGMA cipher_migrate;")

View File

@ -154,6 +154,8 @@ import com.nextcloud.talk.ui.dialog.MessageActionsDialog
import com.nextcloud.talk.ui.dialog.ShowReactionsDialog import com.nextcloud.talk.ui.dialog.ShowReactionsDialog
import com.nextcloud.talk.ui.recyclerview.MessageSwipeActions import com.nextcloud.talk.ui.recyclerview.MessageSwipeActions
import com.nextcloud.talk.ui.recyclerview.MessageSwipeCallback import com.nextcloud.talk.ui.recyclerview.MessageSwipeCallback
import com.nextcloud.talk.ui.theme.ServerTheme
import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.AttendeePermissionsUtil import com.nextcloud.talk.utils.AttendeePermissionsUtil
import com.nextcloud.talk.utils.ConductorRemapping import com.nextcloud.talk.utils.ConductorRemapping
@ -235,6 +237,12 @@ class ChatController(args: Bundle) :
@Inject @Inject
lateinit var permissionUtil: PlatformPermissionUtil lateinit var permissionUtil: PlatformPermissionUtil
@Inject
lateinit var viewThemeUtils: ViewThemeUtils
@Inject
lateinit var serverTheme: ServerTheme
val disposables = DisposableSet() val disposables = DisposableSet()
var roomToken: String? = null var roomToken: String? = null
@ -872,6 +880,8 @@ class ChatController(args: Bundle) :
.nc_description_send_message_button .nc_description_send_message_button
) )
viewThemeUtils.colorImageView(binding.messageInputView.button)
if (currentConversation != null && currentConversation?.roomId != null) { if (currentConversation != null && currentConversation?.roomId != null) {
loadAvatarForStatusBar() loadAvatarForStatusBar()
setTitle() setTitle()
@ -2675,7 +2685,8 @@ class ChatController(args: Bundle) :
chatMessage, chatMessage,
conversationUser, conversationUser,
hasChatPermission, hasChatPermission,
ncApi!! ncApi!!,
serverTheme
).show() ).show()
} }
} }

View File

@ -65,6 +65,7 @@ import com.nextcloud.talk.models.json.converters.EnumActorTypeConverter
import com.nextcloud.talk.models.json.participants.Participant import com.nextcloud.talk.models.json.participants.Participant
import com.nextcloud.talk.ui.dialog.ContactsBottomDialog import com.nextcloud.talk.ui.dialog.ContactsBottomDialog
import com.nextcloud.talk.users.UserManager import com.nextcloud.talk.users.UserManager
import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.ConductorRemapping import com.nextcloud.talk.utils.ConductorRemapping
import com.nextcloud.talk.utils.bundle.BundleKeys import com.nextcloud.talk.utils.bundle.BundleKeys
@ -103,6 +104,9 @@ class ContactsController(args: Bundle) :
@Inject @Inject
lateinit var ncApi: NcApi lateinit var ncApi: NcApi
@Inject
lateinit var viewThemeUtils: ViewThemeUtils
private var credentials: String? = null private var credentials: String? = null
private var currentUser: User? = null private var currentUser: User? = null
private var contactsQueryDisposable: Disposable? = null private var contactsQueryDisposable: Disposable? = null
@ -492,13 +496,14 @@ class ContactsController(args: Bundle) :
val headerTitle = getHeaderTitle(participant) val headerTitle = getHeaderTitle(participant)
var genericTextHeaderItem: GenericTextHeaderItem var genericTextHeaderItem: GenericTextHeaderItem
if (!userHeaderItems.containsKey(headerTitle)) { if (!userHeaderItems.containsKey(headerTitle)) {
genericTextHeaderItem = GenericTextHeaderItem(headerTitle) genericTextHeaderItem = GenericTextHeaderItem(headerTitle, viewThemeUtils)
userHeaderItems.put(headerTitle, genericTextHeaderItem) userHeaderItems.put(headerTitle, genericTextHeaderItem)
} }
val newContactItem = ContactItem( val newContactItem = ContactItem(
participant, participant,
currentUser, currentUser,
userHeaderItems[headerTitle] userHeaderItems[headerTitle],
viewThemeUtils
) )
if (!contactItems!!.contains(newContactItem)) { if (!contactItems!!.contains(newContactItem)) {
newUserItemList.add(newContactItem) newUserItemList.add(newContactItem)
@ -618,21 +623,16 @@ class ContactsController(args: Bundle) :
binding.controllerGenericRv.recyclerView.setHasFixedSize(true) binding.controllerGenericRv.recyclerView.setHasFixedSize(true)
binding.controllerGenericRv.recyclerView.adapter = adapter binding.controllerGenericRv.recyclerView.adapter = adapter
binding.controllerGenericRv.swipeRefreshLayout.setOnRefreshListener { fetchData() } binding.controllerGenericRv.swipeRefreshLayout.setOnRefreshListener { fetchData() }
binding.controllerGenericRv.swipeRefreshLayout.setColorSchemeResources(R.color.colorPrimary)
binding.controllerGenericRv.swipeRefreshLayout viewThemeUtils.themeSwipeRefreshLayout(binding.controllerGenericRv.swipeRefreshLayout)
.setProgressBackgroundColorSchemeResource(R.color.refresh_spinner_background)
binding.joinConversationViaLink.joinConversationViaLinkImageView binding.joinConversationViaLink.joinConversationViaLinkImageView
.background .background
.setColorFilter( .setColorFilter(
ResourcesCompat.getColor(resources!!, R.color.colorBackgroundDarker, null), ResourcesCompat.getColor(resources!!, R.color.colorBackgroundDarker, null),
PorterDuff.Mode.SRC_IN PorterDuff.Mode.SRC_IN
) )
binding.conversationPrivacyToggle.publicCallLink viewThemeUtils.colorImageViewButton(binding.conversationPrivacyToggle.publicCallLink)
.background
.setColorFilter(
ResourcesCompat.getColor(resources!!, R.color.colorPrimary, null),
PorterDuff.Mode.SRC_IN
)
disengageProgressBar() disengageProgressBar()
} }

View File

@ -73,6 +73,7 @@ import com.nextcloud.talk.models.json.participants.Participant.ActorType.GROUPS
import com.nextcloud.talk.models.json.participants.Participant.ActorType.USERS import com.nextcloud.talk.models.json.participants.Participant.ActorType.USERS
import com.nextcloud.talk.models.json.participants.ParticipantsOverall import com.nextcloud.talk.models.json.participants.ParticipantsOverall
import com.nextcloud.talk.shareditems.activities.SharedItemsActivity import com.nextcloud.talk.shareditems.activities.SharedItemsActivity
import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.DateConstants import com.nextcloud.talk.utils.DateConstants
import com.nextcloud.talk.utils.DateUtils import com.nextcloud.talk.utils.DateUtils
@ -112,6 +113,9 @@ class ConversationInfoController(args: Bundle) :
@Inject @Inject
lateinit var eventBus: EventBus lateinit var eventBus: EventBus
@Inject
lateinit var viewThemeUtils: ViewThemeUtils
private val conversationToken: String? private val conversationToken: String?
private val conversationUser: User? private val conversationUser: User?
private val hasAvatarSpacing: Boolean private val hasAvatarSpacing: Boolean
@ -181,6 +185,34 @@ class ConversationInfoController(args: Bundle) :
} }
fetchRoomInfo() fetchRoomInfo()
themeCategories()
themeSwitchPreferences()
}
private fun themeSwitchPreferences() {
binding.run {
listOf(
binding.webinarInfoView.conversationInfoLobby,
binding.notificationSettingsView.callNotifications,
binding.notificationSettingsView.conversationInfoPriorityConversation
).forEach(viewThemeUtils::colorSwitchPreference)
}
}
private fun themeCategories() {
binding.run {
listOf(
conversationInfoName,
conversationDescription,
otherRoomOptions,
participantsListCategory,
ownOptions,
categorySharedItems,
binding.webinarInfoView.conversationInfoWebinar,
binding.notificationSettingsView.notificationSettingsCategory
).forEach(viewThemeUtils::colorPreferenceCategory)
}
} }
private fun showSharedItems() { private fun showSharedItems() {
@ -299,7 +331,7 @@ class ConversationInfoController(args: Bundle) :
val apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1)) val apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1))
ncApi?.setLobbyForConversation( ncApi.setLobbyForConversation(
ApiUtils.getCredentials(conversationUser!!.username, conversationUser.token), ApiUtils.getCredentials(conversationUser!!.username, conversationUser.token),
ApiUtils.getUrlForRoomWebinaryLobby(apiVersion, conversationUser.baseUrl, conversation!!.token), ApiUtils.getUrlForRoomWebinaryLobby(apiVersion, conversationUser.baseUrl, conversation!!.token),
state, state,
@ -343,7 +375,7 @@ class ConversationInfoController(args: Bundle) :
override fun onDetach(view: View) { override fun onDetach(view: View) {
super.onDetach(view) super.onDetach(view)
eventBus?.unregister(this) eventBus.unregister(this)
} }
private fun showDeleteConversationDialog(savedInstanceState: Bundle?) { private fun showDeleteConversationDialog(savedInstanceState: Bundle?) {
@ -352,11 +384,11 @@ class ConversationInfoController(args: Bundle) :
.setTopColorRes(R.color.nc_darkRed) .setTopColorRes(R.color.nc_darkRed)
.setIcon( .setIcon(
DisplayUtils.getTintedDrawable( DisplayUtils.getTintedDrawable(
context!!.resources, context.resources,
R.drawable.ic_delete_black_24dp, R.color.bg_default R.drawable.ic_delete_black_24dp, R.color.bg_default
) )
) )
.setPositiveButtonColor(context!!.resources.getColor(R.color.nc_darkRed)) .setPositiveButtonColor(context.resources.getColor(R.color.nc_darkRed))
.setTitle(R.string.nc_delete_call) .setTitle(R.string.nc_delete_call)
.setMessage(R.string.nc_delete_conversation_more) .setMessage(R.string.nc_delete_conversation_more)
.setPositiveButton(R.string.nc_delete) { deleteConversation() } .setPositiveButton(R.string.nc_delete) { deleteConversation() }
@ -409,7 +441,7 @@ class ConversationInfoController(args: Bundle) :
if (participant.sessionId != null) { if (participant.sessionId != null) {
userItem.isOnline = !participant.sessionId.equals("0") userItem.isOnline = !participant.sessionId.equals("0")
} else { } else {
userItem.isOnline = !participant.sessionIds!!.isEmpty() userItem.isOnline = !participant.sessionIds.isEmpty()
} }
if (participant.calculatedActorType == USERS && if (participant.calculatedActorType == USERS &&
@ -453,7 +485,7 @@ class ConversationInfoController(args: Bundle) :
val fieldMap = HashMap<String, Boolean>() val fieldMap = HashMap<String, Boolean>()
fieldMap["includeStatus"] = true fieldMap["includeStatus"] = true
ncApi?.getPeersForCall( ncApi.getPeersForCall(
credentials, credentials,
ApiUtils.getUrlForParticipants( ApiUtils.getUrlForParticipants(
apiVersion, apiVersion,
@ -504,7 +536,7 @@ class ConversationInfoController(args: Bundle) :
bundle.putStringArrayList(BundleKeys.KEY_EXISTING_PARTICIPANTS, existingParticipantsId) bundle.putStringArrayList(BundleKeys.KEY_EXISTING_PARTICIPANTS, existingParticipantsId)
bundle.putString(BundleKeys.KEY_TOKEN, conversation!!.token) bundle.putString(BundleKeys.KEY_TOKEN, conversation!!.token)
getRouter().pushController( router.pushController(
( (
RouterTransaction.with( RouterTransaction.with(
ContactsController(bundle) ContactsController(bundle)
@ -537,11 +569,11 @@ class ConversationInfoController(args: Bundle) :
.setTopColorRes(R.color.nc_darkRed) .setTopColorRes(R.color.nc_darkRed)
.setIcon( .setIcon(
DisplayUtils.getTintedDrawable( DisplayUtils.getTintedDrawable(
context!!.resources, context.resources,
R.drawable.ic_delete_black_24dp, R.color.bg_default R.drawable.ic_delete_black_24dp, R.color.bg_default
) )
) )
.setPositiveButtonColor(context!!.resources.getColor(R.color.nc_darkRed)) .setPositiveButtonColor(context.resources.getColor(R.color.nc_darkRed))
.setTitle(R.string.nc_clear_history) .setTitle(R.string.nc_clear_history)
.setMessage(R.string.nc_clear_history_warning) .setMessage(R.string.nc_clear_history_warning)
.setPositiveButton(R.string.nc_delete_all) { clearHistory() } .setPositiveButton(R.string.nc_delete_all) { clearHistory() }
@ -555,7 +587,7 @@ class ConversationInfoController(args: Bundle) :
private fun clearHistory() { private fun clearHistory() {
val apiVersion = ApiUtils.getChatApiVersion(conversationUser, intArrayOf(1)) val apiVersion = ApiUtils.getChatApiVersion(conversationUser, intArrayOf(1))
ncApi?.clearChatHistory( ncApi.clearChatHistory(
credentials, credentials,
ApiUtils.getUrlForChat(apiVersion, conversationUser!!.baseUrl, conversationToken) ApiUtils.getUrlForChat(apiVersion, conversationUser!!.baseUrl, conversationToken)
) )
@ -567,7 +599,7 @@ class ConversationInfoController(args: Bundle) :
} }
override fun onNext(genericOverall: GenericOverall) { override fun onNext(genericOverall: GenericOverall) {
Toast.makeText(context, context?.getString(R.string.nc_clear_history_success), Toast.LENGTH_LONG) Toast.makeText(context, context.getString(R.string.nc_clear_history_success), Toast.LENGTH_LONG)
.show() .show()
} }
@ -606,7 +638,7 @@ class ConversationInfoController(args: Bundle) :
apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1)) apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1))
} }
ncApi?.getRoom(credentials, ApiUtils.getUrlForRoom(apiVersion, conversationUser!!.baseUrl, conversationToken)) ncApi.getRoom(credentials, ApiUtils.getUrlForRoom(apiVersion, conversationUser!!.baseUrl, conversationToken))
?.subscribeOn(Schedulers.io()) ?.subscribeOn(Schedulers.io())
?.observeOn(AndroidSchedulers.mainThread()) ?.observeOn(AndroidSchedulers.mainThread())
?.subscribe(object : Observer<RoomOverall> { ?.subscribe(object : Observer<RoomOverall> {
@ -765,8 +797,8 @@ class ConversationInfoController(args: Bundle) :
) )
Conversation.ConversationType.ROOM_SYSTEM -> { Conversation.ConversationType.ROOM_SYSTEM -> {
val layers = arrayOfNulls<Drawable>(2) val layers = arrayOfNulls<Drawable>(2)
layers[0] = ContextCompat.getDrawable(context!!, R.drawable.ic_launcher_background) layers[0] = ContextCompat.getDrawable(context, R.drawable.ic_launcher_background)
layers[1] = ContextCompat.getDrawable(context!!, R.drawable.ic_launcher_foreground) layers[1] = ContextCompat.getDrawable(context, R.drawable.ic_launcher_foreground)
val layerDrawable = LayerDrawable(layers) val layerDrawable = LayerDrawable(layers)
binding.avatarImage.hierarchy.setPlaceholderImage(DisplayUtils.getRoundedDrawable(layerDrawable)) binding.avatarImage.hierarchy.setPlaceholderImage(DisplayUtils.getRoundedDrawable(layerDrawable))
} }
@ -800,7 +832,7 @@ class ConversationInfoController(args: Bundle) :
if (participant.type == Participant.ParticipantType.MODERATOR || if (participant.type == Participant.ParticipantType.MODERATOR ||
participant.type == Participant.ParticipantType.GUEST_MODERATOR participant.type == Participant.ParticipantType.GUEST_MODERATOR
) { ) {
ncApi?.demoteAttendeeFromModerator( ncApi.demoteAttendeeFromModerator(
credentials, credentials,
ApiUtils.getUrlForRoomModerators( ApiUtils.getUrlForRoomModerators(
apiVersion, apiVersion,
@ -815,7 +847,7 @@ class ConversationInfoController(args: Bundle) :
} else if (participant.type == Participant.ParticipantType.USER || } else if (participant.type == Participant.ParticipantType.USER ||
participant.type == Participant.ParticipantType.GUEST participant.type == Participant.ParticipantType.GUEST
) { ) {
ncApi?.promoteAttendeeToModerator( ncApi.promoteAttendeeToModerator(
credentials, credentials,
ApiUtils.getUrlForRoomModerators( ApiUtils.getUrlForRoomModerators(
apiVersion, apiVersion,
@ -851,7 +883,7 @@ class ConversationInfoController(args: Bundle) :
} }
if (participant.type == Participant.ParticipantType.MODERATOR) { if (participant.type == Participant.ParticipantType.MODERATOR) {
ncApi?.demoteModeratorToUser( ncApi.demoteModeratorToUser(
credentials, credentials,
ApiUtils.getUrlForRoomModerators( ApiUtils.getUrlForRoomModerators(
apiVersion, apiVersion,
@ -864,7 +896,7 @@ class ConversationInfoController(args: Bundle) :
?.observeOn(AndroidSchedulers.mainThread()) ?.observeOn(AndroidSchedulers.mainThread())
?.subscribe(subscriber) ?.subscribe(subscriber)
} else if (participant.type == Participant.ParticipantType.USER) { } else if (participant.type == Participant.ParticipantType.USER) {
ncApi?.promoteUserToModerator( ncApi.promoteUserToModerator(
credentials, credentials,
ApiUtils.getUrlForRoomModerators( ApiUtils.getUrlForRoomModerators(
apiVersion, apiVersion,
@ -881,7 +913,7 @@ class ConversationInfoController(args: Bundle) :
fun removeAttendeeFromConversation(apiVersion: Int, participant: Participant) { fun removeAttendeeFromConversation(apiVersion: Int, participant: Participant) {
if (apiVersion >= ApiUtils.APIv4) { if (apiVersion >= ApiUtils.APIv4) {
ncApi?.removeAttendeeFromConversation( ncApi.removeAttendeeFromConversation(
credentials, credentials,
ApiUtils.getUrlForAttendees( ApiUtils.getUrlForAttendees(
apiVersion, apiVersion,
@ -914,7 +946,7 @@ class ConversationInfoController(args: Bundle) :
if (participant.type == Participant.ParticipantType.GUEST || if (participant.type == Participant.ParticipantType.GUEST ||
participant.type == Participant.ParticipantType.USER_FOLLOWING_LINK participant.type == Participant.ParticipantType.USER_FOLLOWING_LINK
) { ) {
ncApi?.removeParticipantFromConversation( ncApi.removeParticipantFromConversation(
credentials, credentials,
ApiUtils.getUrlForRemovingParticipantFromConversation( ApiUtils.getUrlForRemovingParticipantFromConversation(
conversationUser!!.baseUrl, conversationUser!!.baseUrl,
@ -944,7 +976,7 @@ class ConversationInfoController(args: Bundle) :
} }
}) })
} else { } else {
ncApi?.removeParticipantFromConversation( ncApi.removeParticipantFromConversation(
credentials, credentials,
ApiUtils.getUrlForRemovingParticipantFromConversation( ApiUtils.getUrlForRemovingParticipantFromConversation(
conversationUser!!.baseUrl, conversationUser!!.baseUrl,
@ -987,12 +1019,12 @@ class ConversationInfoController(args: Bundle) :
val apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1)) val apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1))
if (participant.calculatedActorType == USERS && participant.calculatedActorId == conversationUser!!.userId) { if (participant.calculatedActorType == USERS && participant.calculatedActorId == conversationUser.userId) {
if (participant.attendeePin?.isNotEmpty() == true) { if (participant.attendeePin?.isNotEmpty() == true) {
val items = mutableListOf( val items = mutableListOf(
BasicListItemWithImage( BasicListItemWithImage(
R.drawable.ic_lock_grey600_24px, R.drawable.ic_lock_grey600_24px,
context!!.getString(R.string.nc_attendee_pin, participant.attendeePin) context.getString(R.string.nc_attendee_pin, participant.attendeePin)
) )
) )
MaterialDialog(activity!!, BottomSheet(WRAP_CONTENT)).show { MaterialDialog(activity!!, BottomSheet(WRAP_CONTENT)).show {
@ -1018,7 +1050,7 @@ class ConversationInfoController(args: Bundle) :
val items = mutableListOf( val items = mutableListOf(
BasicListItemWithImage( BasicListItemWithImage(
R.drawable.ic_delete_grey600_24dp, R.drawable.ic_delete_grey600_24dp,
context!!.getString(R.string.nc_remove_group_and_members) context.getString(R.string.nc_remove_group_and_members)
) )
) )
MaterialDialog(activity!!, BottomSheet(WRAP_CONTENT)).show { MaterialDialog(activity!!, BottomSheet(WRAP_CONTENT)).show {
@ -1038,7 +1070,7 @@ class ConversationInfoController(args: Bundle) :
val items = mutableListOf( val items = mutableListOf(
BasicListItemWithImage( BasicListItemWithImage(
R.drawable.ic_delete_grey600_24dp, R.drawable.ic_delete_grey600_24dp,
context!!.getString(R.string.nc_remove_circle_and_members) context.getString(R.string.nc_remove_circle_and_members)
) )
) )
MaterialDialog(activity!!, BottomSheet(WRAP_CONTENT)).show { MaterialDialog(activity!!, BottomSheet(WRAP_CONTENT)).show {
@ -1057,19 +1089,19 @@ class ConversationInfoController(args: Bundle) :
val items = mutableListOf( val items = mutableListOf(
BasicListItemWithImage( BasicListItemWithImage(
R.drawable.ic_lock_grey600_24px, R.drawable.ic_lock_grey600_24px,
context!!.getString(R.string.nc_attendee_pin, participant.attendeePin) context.getString(R.string.nc_attendee_pin, participant.attendeePin)
), ),
BasicListItemWithImage( BasicListItemWithImage(
R.drawable.ic_pencil_grey600_24dp, R.drawable.ic_pencil_grey600_24dp,
context!!.getString(R.string.nc_promote) context.getString(R.string.nc_promote)
), ),
BasicListItemWithImage( BasicListItemWithImage(
R.drawable.ic_pencil_grey600_24dp, R.drawable.ic_pencil_grey600_24dp,
context!!.getString(R.string.nc_demote) context.getString(R.string.nc_demote)
), ),
BasicListItemWithImage( BasicListItemWithImage(
R.drawable.ic_delete_grey600_24dp, R.drawable.ic_delete_grey600_24dp,
context!!.getString(R.string.nc_remove_participant) context.getString(R.string.nc_remove_participant)
) )
) )
@ -1167,8 +1199,8 @@ class ConversationInfoController(args: Bundle) :
return 1 return 1
} }
return left.model.displayName!!.toLowerCase(Locale.ROOT).compareTo( return left.model.displayName!!.lowercase(Locale.ROOT).compareTo(
right.model.displayName!!.toLowerCase(Locale.ROOT) right.model.displayName!!.lowercase(Locale.ROOT)
) )
} }
} }

View File

@ -92,6 +92,7 @@ import com.nextcloud.talk.models.json.statuses.StatusesOverall;
import com.nextcloud.talk.repositories.unifiedsearch.UnifiedSearchRepository; import com.nextcloud.talk.repositories.unifiedsearch.UnifiedSearchRepository;
import com.nextcloud.talk.ui.dialog.ChooseAccountDialogFragment; import com.nextcloud.talk.ui.dialog.ChooseAccountDialogFragment;
import com.nextcloud.talk.ui.dialog.ConversationsListBottomDialog; import com.nextcloud.talk.ui.dialog.ConversationsListBottomDialog;
import com.nextcloud.talk.ui.theme.ViewThemeUtils;
import com.nextcloud.talk.users.UserManager; import com.nextcloud.talk.users.UserManager;
import com.nextcloud.talk.utils.ApiUtils; import com.nextcloud.talk.utils.ApiUtils;
import com.nextcloud.talk.utils.AttendeePermissionsUtil; import com.nextcloud.talk.utils.AttendeePermissionsUtil;
@ -181,6 +182,9 @@ public class ConversationsListController extends BaseController implements Flexi
@Inject @Inject
UnifiedSearchRepository unifiedSearchRepository; UnifiedSearchRepository unifiedSearchRepository;
@Inject
ViewThemeUtils viewThemeUtils;
@BindView(R.id.recycler_view) @BindView(R.id.recycler_view)
RecyclerView recyclerView; RecyclerView recyclerView;
@ -618,7 +622,7 @@ public class ConversationsListController extends BaseController implements Flexi
GenericTextHeaderItem genericTextHeaderItem; GenericTextHeaderItem genericTextHeaderItem;
if (!callHeaderItems.containsKey(headerTitle)) { if (!callHeaderItems.containsKey(headerTitle)) {
genericTextHeaderItem = new GenericTextHeaderItem(headerTitle); genericTextHeaderItem = new GenericTextHeaderItem(headerTitle, viewThemeUtils);
callHeaderItems.put(headerTitle, genericTextHeaderItem); callHeaderItems.put(headerTitle, genericTextHeaderItem);
} }
@ -627,7 +631,8 @@ public class ConversationsListController extends BaseController implements Flexi
conversation, conversation,
currentUser, currentUser,
getActivity(), getActivity(),
userStatuses.get(conversation.getName())); userStatuses.get(conversation.getName()),
viewThemeUtils);
conversationItems.add(conversationItem); conversationItems.add(conversationItem);
ConversationItem conversationItemWithHeader = new ConversationItem( ConversationItem conversationItemWithHeader = new ConversationItem(
@ -635,7 +640,8 @@ public class ConversationsListController extends BaseController implements Flexi
currentUser, currentUser,
getActivity(), getActivity(),
callHeaderItems.get(headerTitle), callHeaderItems.get(headerTitle),
userStatuses.get(conversation.getName())); userStatuses.get(conversation.getName()),
viewThemeUtils);
conversationItemsWithHeader.add(conversationItemWithHeader); conversationItemsWithHeader.add(conversationItemWithHeader);
} }
} }
@ -699,7 +705,7 @@ public class ConversationsListController extends BaseController implements Flexi
GenericTextHeaderItem genericTextHeaderItem; GenericTextHeaderItem genericTextHeaderItem;
if (!callHeaderItems.containsKey(headerTitle)) { if (!callHeaderItems.containsKey(headerTitle)) {
genericTextHeaderItem = new GenericTextHeaderItem(headerTitle); genericTextHeaderItem = new GenericTextHeaderItem(headerTitle, viewThemeUtils);
callHeaderItems.put(headerTitle, genericTextHeaderItem); callHeaderItems.put(headerTitle, genericTextHeaderItem);
} }
@ -708,7 +714,8 @@ public class ConversationsListController extends BaseController implements Flexi
currentUser, currentUser,
getActivity(), getActivity(),
callHeaderItems.get(headerTitle), callHeaderItems.get(headerTitle),
userStatuses.get(conversation.getName())); userStatuses.get(conversation.getName()),
viewThemeUtils);
openConversationItems.add(conversationItem); openConversationItems.add(conversationItem);
} }
@ -776,8 +783,7 @@ public class ConversationsListController extends BaseController implements Flexi
}); });
swipeRefreshLayout.setOnRefreshListener(() -> fetchData()); swipeRefreshLayout.setOnRefreshListener(() -> fetchData());
swipeRefreshLayout.setColorSchemeResources(R.color.colorPrimary); viewThemeUtils.themeSwipeRefreshLayout(swipeRefreshLayout);
swipeRefreshLayout.setProgressBackgroundColorSchemeResource(R.color.refresh_spinner_background);
emptyLayoutView.setOnClickListener(v -> showNewConversationsScreen()); emptyLayoutView.setOnClickListener(v -> showNewConversationsScreen());
floatingActionButton.setOnClickListener(v -> { floatingActionButton.setOnClickListener(v -> {
@ -785,6 +791,8 @@ public class ConversationsListController extends BaseController implements Flexi
showNewConversationsScreen(); showNewConversationsScreen();
}); });
viewThemeUtils.themeFAB(floatingActionButton);
if (getActivity() != null && getActivity() instanceof MainActivity) { if (getActivity() != null && getActivity() instanceof MainActivity) {
MainActivity activity = (MainActivity) getActivity(); MainActivity activity = (MainActivity) getActivity();
@ -1409,7 +1417,7 @@ public class ConversationsListController extends BaseController implements Flexi
List<AbstractFlexibleItem> adapterItems = new ArrayList<>(entries.size() + 1); List<AbstractFlexibleItem> adapterItems = new ArrayList<>(entries.size() + 1);
for (int i = 0; i < entries.size(); i++) { for (int i = 0; i < entries.size(); i++) {
final boolean showHeader = i == 0; final boolean showHeader = i == 0;
adapterItems.add(new MessageResultItem(context, currentUser, entries.get(i), showHeader)); adapterItems.add(new MessageResultItem(context, currentUser, entries.get(i), showHeader, viewThemeUtils));
} }
if (results.getHasMore()) { if (results.getHasMore()) {
adapterItems.add(LoadMoreResultsItem.INSTANCE); adapterItems.add(LoadMoreResultsItem.INSTANCE);

View File

@ -24,10 +24,8 @@ package com.nextcloud.talk.controllers
import android.app.Activity import android.app.Activity
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.content.res.ColorStateList
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.graphics.Color
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.text.Editable import android.text.Editable
@ -43,7 +41,6 @@ import android.view.ViewGroup
import android.widget.Toast import android.widget.Toast
import androidx.annotation.ColorInt import androidx.annotation.ColorInt
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.DrawableCompat import androidx.core.graphics.drawable.DrawableCompat
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
@ -69,6 +66,7 @@ import com.nextcloud.talk.models.json.userprofile.UserProfileFieldsOverall
import com.nextcloud.talk.models.json.userprofile.UserProfileOverall import com.nextcloud.talk.models.json.userprofile.UserProfileOverall
import com.nextcloud.talk.remotefilebrowser.activities.RemoteFileBrowserActivity import com.nextcloud.talk.remotefilebrowser.activities.RemoteFileBrowserActivity
import com.nextcloud.talk.ui.dialog.ScopeDialog import com.nextcloud.talk.ui.dialog.ScopeDialog
import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.users.UserManager import com.nextcloud.talk.users.UserManager
import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.DisplayUtils import com.nextcloud.talk.utils.DisplayUtils
@ -110,6 +108,9 @@ class ProfileController : NewBaseController(R.layout.controller_profile) {
@Inject @Inject
lateinit var permissionUtil: PlatformPermissionUtil lateinit var permissionUtil: PlatformPermissionUtil
@Inject
lateinit var viewThemeUtils: ViewThemeUtils
private var currentUser: User? = null private var currentUser: User? = null
private var edit = false private var edit = false
private var adapter: UserInfoAdapter? = null private var adapter: UserInfoAdapter? = null
@ -196,7 +197,7 @@ class ProfileController : NewBaseController(R.layout.controller_profile) {
override fun onAttach(view: View) { override fun onAttach(view: View) {
super.onAttach(view) super.onAttach(view)
adapter = UserInfoAdapter(null, activity!!.resources.getColor(R.color.colorPrimary), this) adapter = UserInfoAdapter(null, viewThemeUtils.getElementColor(activity!!), this)
binding.userinfoList.adapter = adapter binding.userinfoList.adapter = adapter
binding.userinfoList.setItemViewCacheSize(DEFAULT_CACHE_SIZE) binding.userinfoList.setItemViewCacheSize(DEFAULT_CACHE_SIZE)
currentUser = userManager.currentUser.blockingGet() currentUser = userManager.currentUser.blockingGet()
@ -260,6 +261,13 @@ class ProfileController : NewBaseController(R.layout.controller_profile) {
// unused atm // unused atm
} }
}) })
colorIcons()
}
private fun colorIcons() {
viewThemeUtils.colorImageView(binding.avatarChoose)
viewThemeUtils.colorImageView(binding.avatarCamera)
} }
private fun isAllEmpty(items: Array<String?>): Boolean { private fun isAllEmpty(items: Array<String?>): Boolean {
@ -301,7 +309,8 @@ class ProfileController : NewBaseController(R.layout.controller_profile) {
binding.emptyList.root.visibility = View.VISIBLE binding.emptyList.root.visibility = View.VISIBLE
setErrorMessageForMultiList( setErrorMessageForMultiList(
activity!!.getString(R.string.userinfo_no_info_headline), activity!!.getString(R.string.userinfo_no_info_headline),
activity!!.getString(R.string.userinfo_no_info_text), R.drawable.ic_user activity!!.getString(R.string.userinfo_no_info_text),
R.drawable.ic_user
) )
} else { } else {
binding.emptyList.root.visibility = View.GONE binding.emptyList.root.visibility = View.GONE
@ -616,11 +625,13 @@ class ProfileController : NewBaseController(R.layout.controller_profile) {
val builder = MultipartBody.Builder() val builder = MultipartBody.Builder()
builder.setType(MultipartBody.FORM) builder.setType(MultipartBody.FORM)
builder.addFormDataPart( builder.addFormDataPart(
"files[]", file!!.name, "files[]",
file!!.name,
file.asRequestBody(IMAGE_PREFIX_GENERIC.toMediaTypeOrNull()) file.asRequestBody(IMAGE_PREFIX_GENERIC.toMediaTypeOrNull())
) )
val filePart: MultipartBody.Part = MultipartBody.Part.createFormData( val filePart: MultipartBody.Part = MultipartBody.Part.createFormData(
"files[]", file.name, "files[]",
file.name,
file.asRequestBody(IMAGE_JPG.toMediaTypeOrNull()) file.asRequestBody(IMAGE_JPG.toMediaTypeOrNull())
) )
@ -643,7 +654,8 @@ class ProfileController : NewBaseController(R.layout.controller_profile) {
override fun onError(e: Throwable) { override fun onError(e: Throwable) {
Toast.makeText( Toast.makeText(
applicationContext, context.getString(R.string.default_error_msg), applicationContext,
context.getString(R.string.default_error_msg),
Toast Toast
.LENGTH_LONG .LENGTH_LONG
).show() ).show()
@ -688,7 +700,8 @@ class ProfileController : NewBaseController(R.layout.controller_profile) {
} }
class UserInfoDetailsItem( class UserInfoDetailsItem(
@field:DrawableRes @param:DrawableRes var icon: Int, @field:DrawableRes @param:DrawableRes
var icon: Int,
var text: String?, var text: String?,
var hint: String, var hint: String,
val field: Field, val field: Field,
@ -748,22 +761,14 @@ class ProfileController : NewBaseController(R.layout.controller_profile) {
DrawableCompat.setTint(holder.binding.icon.drawable, mTintColor) DrawableCompat.setTint(holder.binding.icon.drawable, mTintColor)
if (!TextUtils.isEmpty(item.text) || controller.edit) { if (!TextUtils.isEmpty(item.text) || controller.edit) {
holder.binding.userInfoDetailContainer.visibility = View.VISIBLE holder.binding.userInfoDetailContainer.visibility = View.VISIBLE
if (controller.activity != null) { controller.viewThemeUtils.colorTextInputLayout(holder.binding.userInfoInputLayout)
holder.binding.userInfoEditText.setTextColor(
ContextCompat.getColor(
controller.activity!!,
R.color.conversation_item_header
)
)
}
if (controller.edit && if (controller.edit &&
controller.editableFields.contains(item.field.toString().lowercase()) controller.editableFields.contains(item.field.toString().lowercase())
) { ) {
holder.binding.userInfoEditText.isEnabled = true holder.binding.userInfoEditTextEdit.isEnabled = true
holder.binding.userInfoEditText.isFocusableInTouchMode = true holder.binding.userInfoEditTextEdit.isFocusableInTouchMode = true
holder.binding.userInfoEditText.isEnabled = true holder.binding.userInfoEditTextEdit.isEnabled = true
holder.binding.userInfoEditText.isCursorVisible = true holder.binding.userInfoEditTextEdit.isCursorVisible = true
holder.binding.userInfoEditText.backgroundTintList = ColorStateList.valueOf(mTintColor)
holder.binding.scope.setOnClickListener { holder.binding.scope.setOnClickListener {
ScopeDialog( ScopeDialog(
controller.activity!!, controller.activity!!,
@ -774,11 +779,10 @@ class ProfileController : NewBaseController(R.layout.controller_profile) {
} }
holder.binding.scope.alpha = HIGH_EMPHASIS_ALPHA holder.binding.scope.alpha = HIGH_EMPHASIS_ALPHA
} else { } else {
holder.binding.userInfoEditText.isEnabled = false holder.binding.userInfoEditTextEdit.isEnabled = false
holder.binding.userInfoEditText.isFocusableInTouchMode = false holder.binding.userInfoEditTextEdit.isFocusableInTouchMode = false
holder.binding.userInfoEditText.isEnabled = false holder.binding.userInfoEditTextEdit.isEnabled = false
holder.binding.userInfoEditText.isCursorVisible = false holder.binding.userInfoEditTextEdit.isCursorVisible = false
holder.binding.userInfoEditText.backgroundTintList = ColorStateList.valueOf(Color.TRANSPARENT)
holder.binding.scope.setOnClickListener(null) holder.binding.scope.setOnClickListener(null)
holder.binding.scope.alpha = MEDIUM_EMPHASIS_ALPHA holder.binding.scope.alpha = MEDIUM_EMPHASIS_ALPHA
} }
@ -791,19 +795,19 @@ class ProfileController : NewBaseController(R.layout.controller_profile) {
holder: ViewHolder, holder: ViewHolder,
item: UserInfoDetailsItem item: UserInfoDetailsItem
) { ) {
holder.binding.userInfoEditText.setText(item.text) holder.binding.userInfoEditTextEdit.setText(item.text)
holder.binding.userInfoEditText.hint = item.hint holder.binding.userInfoInputLayout.hint = item.hint
holder.binding.userInfoEditText.addTextChangedListener(object : TextWatcher { holder.binding.userInfoEditTextEdit.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
// unused atm // unused atm
} }
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
if (controller.edit) { if (controller.edit) {
displayList!![holder.adapterPosition].text = holder.binding.userInfoEditText.text.toString() displayList!![holder.adapterPosition].text = holder.binding.userInfoEditTextEdit.text.toString()
} else { } else {
filteredDisplayList[holder.adapterPosition].text = filteredDisplayList[holder.adapterPosition].text =
holder.binding.userInfoEditText.text.toString() holder.binding.userInfoEditTextEdit.text.toString()
} }
} }

View File

@ -78,6 +78,7 @@ import com.nextcloud.talk.jobs.ContactAddressBookWorker.Companion.deleteAll
import com.nextcloud.talk.models.json.generic.GenericOverall import com.nextcloud.talk.models.json.generic.GenericOverall
import com.nextcloud.talk.models.json.userprofile.UserProfileOverall import com.nextcloud.talk.models.json.userprofile.UserProfileOverall
import com.nextcloud.talk.users.UserManager import com.nextcloud.talk.users.UserManager
import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.DisplayUtils import com.nextcloud.talk.utils.DisplayUtils
import com.nextcloud.talk.utils.LoggingUtils.sendMailWithAttachment import com.nextcloud.talk.utils.LoggingUtils.sendMailWithAttachment
@ -118,6 +119,9 @@ class SettingsController : NewBaseController(R.layout.controller_settings) {
@Inject @Inject
lateinit var currentUserProvider: CurrentUserProviderNew lateinit var currentUserProvider: CurrentUserProviderNew
@Inject
lateinit var viewThemeUtils: ViewThemeUtils
private var saveStateHandler: LovelySaveStateHandler? = null private var saveStateHandler: LovelySaveStateHandler? = null
private var currentUser: User? = null private var currentUser: User? = null
private var credentials: String? = null private var credentials: String? = null
@ -402,7 +406,8 @@ class SettingsController : NewBaseController(R.layout.controller_settings) {
.setIcon( .setIcon(
DisplayUtils.getTintedDrawable( DisplayUtils.getTintedDrawable(
resources, resources,
R.drawable.ic_delete_black_24dp, R.color.bg_default R.drawable.ic_delete_black_24dp,
R.color.bg_default
) )
) )
.setPositiveButtonColor(context!!.resources.getColor(R.color.nc_darkRed)) .setPositiveButtonColor(context!!.resources.getColor(R.color.nc_darkRed))
@ -511,6 +516,34 @@ class SettingsController : NewBaseController(R.layout.controller_settings) {
) )
} }
themeCategories()
themeSwitchPreferences()
}
private fun themeSwitchPreferences() {
binding.run {
listOf(
settingsScreenLock,
settingsScreenSecurity,
settingsIncognitoKeyboard,
settingsPhoneBookIntegration,
settingsReadPrivacy,
settingsProxyUseCredentials
).forEach(viewThemeUtils::colorSwitchPreference)
}
}
private fun themeCategories() {
binding.run {
listOf(
settingsNotificationsCategory,
settingsAboutCategory,
settingsAdvancedCategory,
settingsAppearanceCategory,
settingsPrivacyCategory
).forEach(viewThemeUtils::colorPreferenceCategory)
}
} }
private fun setupProxyTypeSettings() { private fun setupProxyTypeSettings() {
@ -952,7 +985,9 @@ class SettingsController : NewBaseController(R.layout.controller_settings) {
val phoneNumber = textInputLayout.editText!!.text.toString() val phoneNumber = textInputLayout.editText!!.text.toString()
ncApi.setUserData( ncApi.setUserData(
ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token), ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token),
ApiUtils.getUrlForUserData(currentUser!!.baseUrl, currentUser!!.userId), "phone", phoneNumber ApiUtils.getUrlForUserData(currentUser!!.baseUrl, currentUser!!.userId),
"phone",
phoneNumber
).subscribeOn(Schedulers.io()) ).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Observer<GenericOverall> { .subscribe(object : Observer<GenericOverall> {

View File

@ -25,7 +25,7 @@ package com.nextcloud.talk.controllers.bottomsheet
import android.content.ComponentName import android.content.ComponentName
import android.content.Intent import android.content.Intent
import android.graphics.PorterDuff import android.content.res.ColorStateList
import android.os.Bundle import android.os.Bundle
import android.os.Parcelable import android.os.Parcelable
import android.text.Editable import android.text.Editable
@ -34,6 +34,7 @@ import android.text.TextUtils
import android.text.TextWatcher import android.text.TextWatcher
import android.view.View import android.view.View
import android.view.inputmethod.EditorInfo import android.view.inputmethod.EditorInfo
import androidx.core.content.res.ResourcesCompat
import autodagger.AutoInjector import autodagger.AutoInjector
import com.bluelinelabs.conductor.RouterTransaction import com.bluelinelabs.conductor.RouterTransaction
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler
@ -45,6 +46,8 @@ import com.nextcloud.talk.controllers.base.NewBaseController
import com.nextcloud.talk.controllers.util.viewBinding import com.nextcloud.talk.controllers.util.viewBinding
import com.nextcloud.talk.databinding.ControllerEntryMenuBinding import com.nextcloud.talk.databinding.ControllerEntryMenuBinding
import com.nextcloud.talk.models.json.conversations.Conversation import com.nextcloud.talk.models.json.conversations.Conversation
import com.nextcloud.talk.ui.theme.ServerTheme
import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.users.UserManager import com.nextcloud.talk.users.UserManager
import com.nextcloud.talk.utils.ShareUtils import com.nextcloud.talk.utils.ShareUtils
import com.nextcloud.talk.utils.UriUtils import com.nextcloud.talk.utils.UriUtils
@ -71,6 +74,12 @@ class EntryMenuController(args: Bundle) :
@Inject @Inject
lateinit var userManager: UserManager lateinit var userManager: UserManager
@Inject
lateinit var viewThemeUtils: ViewThemeUtils
@Inject
lateinit var serverTheme: ServerTheme
private val operation: ConversationOperationEnum private val operation: ConversationOperationEnum
private var conversation: Conversation? = null private var conversation: Conversation? = null
private var shareIntent: Intent? = null private var shareIntent: Intent? = null
@ -125,17 +134,11 @@ class EntryMenuController(args: Bundle) :
rootView = view, rootView = view,
editText = binding.textEdit, editText = binding.textEdit,
onEmojiPopupShownListener = { onEmojiPopupShownListener = {
if (resources != null) { viewThemeUtils.colorImageView(binding.smileyButton)
binding.smileyButton.setColorFilter(
resources!!.getColor(R.color.colorPrimary),
PorterDuff.Mode.SRC_IN
)
}
}, },
onEmojiPopupDismissListener = { onEmojiPopupDismissListener = {
binding.smileyButton.setColorFilter( binding.smileyButton.imageTintList = ColorStateList.valueOf(
resources!!.getColor(R.color.emoji_icons), ResourcesCompat.getColor(resources!!, R.color.medium_emphasis_text, context.theme)
PorterDuff.Mode.SRC_IN
) )
}, },
onEmojiClickListener = { onEmojiClickListener = {
@ -171,6 +174,9 @@ class EntryMenuController(args: Bundle) :
binding.textInputLayout.endIconMode = TextInputLayout.END_ICON_NONE binding.textInputLayout.endIconMode = TextInputLayout.END_ICON_NONE
} }
viewThemeUtils.colorTextInputLayout(binding.textInputLayout)
viewThemeUtils.colorMaterialButtonText(binding.okButton)
binding.textInputLayout.hint = labelText binding.textInputLayout.hint = labelText
binding.textInputLayout.requestFocus() binding.textInputLayout.requestFocus()

View File

@ -32,6 +32,7 @@ import androidx.room.Transaction
import androidx.room.Update import androidx.room.Update
import com.nextcloud.talk.data.user.model.UserEntity import com.nextcloud.talk.data.user.model.UserEntity
import io.reactivex.Maybe import io.reactivex.Maybe
import io.reactivex.Observable
import io.reactivex.Single import io.reactivex.Single
@Dao @Dao
@ -41,6 +42,10 @@ abstract class UsersDao {
@Query("SELECT * FROM User where current = 1") @Query("SELECT * FROM User where current = 1")
abstract fun getActiveUser(): Maybe<UserEntity> abstract fun getActiveUser(): Maybe<UserEntity>
// get active user
@Query("SELECT * FROM User where current = 1")
abstract fun getActiveUserObservable(): Observable<UserEntity>
@Query("SELECT * FROM User where current = 1") @Query("SELECT * FROM User where current = 1")
abstract fun getActiveUserSynchronously(): UserEntity? abstract fun getActiveUserSynchronously(): UserEntity?

View File

@ -24,11 +24,13 @@ package com.nextcloud.talk.data.user
import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.data.user.model.User
import io.reactivex.Maybe import io.reactivex.Maybe
import io.reactivex.Observable
import io.reactivex.Single import io.reactivex.Single
@Suppress("TooManyFunctions") @Suppress("TooManyFunctions")
interface UsersRepository { interface UsersRepository {
fun getActiveUser(): Maybe<User> fun getActiveUser(): Maybe<User>
fun getActiveUserObservable(): Observable<User>
fun getUsers(): Single<List<User>> fun getUsers(): Single<List<User>>
fun getUserWithId(id: Long): Maybe<User> fun getUserWithId(id: Long): Maybe<User>
fun getUserWithIdNotScheduledForDeletion(id: Long): Maybe<User> fun getUserWithIdNotScheduledForDeletion(id: Long): Maybe<User>

View File

@ -24,6 +24,7 @@ package com.nextcloud.talk.data.user
import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.data.user.model.User
import io.reactivex.Maybe import io.reactivex.Maybe
import io.reactivex.Observable
import io.reactivex.Single import io.reactivex.Single
@Suppress("TooManyFunctions") @Suppress("TooManyFunctions")
@ -33,6 +34,10 @@ class UsersRepositoryImpl(private val usersDao: UsersDao) : UsersRepository {
return usersDao.getActiveUser().map { UserMapper.toModel(it) } return usersDao.getActiveUser().map { UserMapper.toModel(it) }
} }
override fun getActiveUserObservable(): Observable<User> {
return usersDao.getActiveUserObservable().map { UserMapper.toModel(it) }
}
override fun getUsers(): Single<List<User>> { override fun getUsers(): Single<List<User>> {
return usersDao.getUsers().map { UserMapper.toModel(it) } return usersDao.getUsers().map { UserMapper.toModel(it) }
} }

View File

@ -41,6 +41,7 @@ import com.nextcloud.talk.application.NextcloudTalkApplication
import com.nextcloud.talk.controllers.ConversationsListController import com.nextcloud.talk.controllers.ConversationsListController
import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.data.user.model.User
import com.nextcloud.talk.databinding.ActivityMessageSearchBinding import com.nextcloud.talk.databinding.ActivityMessageSearchBinding
import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.utils.DisplayUtils import com.nextcloud.talk.utils.DisplayUtils
import com.nextcloud.talk.utils.bundle.BundleKeys import com.nextcloud.talk.utils.bundle.BundleKeys
import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew
@ -64,6 +65,9 @@ class MessageSearchActivity : BaseActivity() {
@Inject @Inject
lateinit var userProvider: CurrentUserProviderNew lateinit var userProvider: CurrentUserProviderNew
@Inject
lateinit var viewThemeUtils: ViewThemeUtils
private lateinit var binding: ActivityMessageSearchBinding private lateinit var binding: ActivityMessageSearchBinding
private lateinit var searchView: SearchView private lateinit var searchView: SearchView
@ -105,7 +109,9 @@ class MessageSearchActivity : BaseActivity() {
DisplayUtils.applyColorToStatusBar( DisplayUtils.applyColorToStatusBar(
this, this,
ResourcesCompat.getColor( ResourcesCompat.getColor(
resources, R.color.appbar, null resources,
R.color.appbar,
null
) )
) )
DisplayUtils.applyColorToNavigationBar( DisplayUtils.applyColorToNavigationBar(
@ -154,7 +160,7 @@ class MessageSearchActivity : BaseActivity() {
emptyList() emptyList()
} }
val newItems = val newItems =
state.results.map { MessageResultItem(this, user, it) } + loadMoreItems state.results.map { MessageResultItem(this, user, it, false, viewThemeUtils) } + loadMoreItems
if (adapter != null) { if (adapter != null) {
adapter!!.updateDataSet(newItems) adapter!!.updateDataSet(newItems)

View File

@ -43,6 +43,10 @@ data class ThemingCapability(
var colorText: String?, var colorText: String?,
@JsonField(name = ["color-element"]) @JsonField(name = ["color-element"])
var colorElement: String?, var colorElement: String?,
@JsonField(name = ["color-element-bright"])
var colorElementBright: String?,
@JsonField(name = ["color-element-dark"])
var colorElementDark: String?,
@JsonField(name = ["logo"]) @JsonField(name = ["logo"])
var logo: String?, var logo: String?,
@JsonField(name = ["background"]) @JsonField(name = ["background"])
@ -53,5 +57,5 @@ data class ThemingCapability(
var backgroundDefault: Boolean? var backgroundDefault: Boolean?
) : Parcelable { ) : Parcelable {
// This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject'
constructor() : this(null, null, null, null, null, null, null, null, null, null) constructor() : this(null, null, null, null, null, null, null, null, null, null, null, null)
} }

View File

@ -26,10 +26,12 @@ import android.text.TextWatcher
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.nextcloud.talk.R import com.nextcloud.talk.R
import com.nextcloud.talk.databinding.PollCreateOptionsItemBinding import com.nextcloud.talk.databinding.PollCreateOptionsItemBinding
import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.utils.EmojiTextInputEditText import com.nextcloud.talk.utils.EmojiTextInputEditText
class PollCreateOptionViewHolder( class PollCreateOptionViewHolder(
private val binding: PollCreateOptionsItemBinding private val binding: PollCreateOptionsItemBinding,
private val viewThemeUtils: ViewThemeUtils
) : RecyclerView.ViewHolder(binding.root) { ) : RecyclerView.ViewHolder(binding.root) {
lateinit var optionText: EmojiTextInputEditText lateinit var optionText: EmojiTextInputEditText
@ -48,6 +50,7 @@ class PollCreateOptionViewHolder(
} }
binding.pollOptionTextEdit.setText(pollCreateOptionItem.pollOption) binding.pollOptionTextEdit.setText(pollCreateOptionItem.pollOption)
viewThemeUtils.colorEditText(binding.pollOptionTextEdit)
if (focus) { if (focus) {
itemsListener.requestFocus(binding.pollOptionTextEdit) itemsListener.requestFocus(binding.pollOptionTextEdit)

View File

@ -24,9 +24,11 @@ import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.nextcloud.talk.databinding.PollCreateOptionsItemBinding import com.nextcloud.talk.databinding.PollCreateOptionsItemBinding
import com.nextcloud.talk.ui.theme.ViewThemeUtils
class PollCreateOptionsAdapter( class PollCreateOptionsAdapter(
private val clickListener: PollCreateOptionsItemListener private val clickListener: PollCreateOptionsItemListener,
private val viewThemeUtils: ViewThemeUtils
) : RecyclerView.Adapter<PollCreateOptionViewHolder>() { ) : RecyclerView.Adapter<PollCreateOptionViewHolder>() {
internal var list: ArrayList<PollCreateOptionItem> = ArrayList() internal var list: ArrayList<PollCreateOptionItem> = ArrayList()
@ -34,7 +36,7 @@ class PollCreateOptionsAdapter(
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PollCreateOptionViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PollCreateOptionViewHolder {
val itemBinding = PollCreateOptionsItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) val itemBinding = PollCreateOptionsItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return PollCreateOptionViewHolder(itemBinding) return PollCreateOptionViewHolder(itemBinding, viewThemeUtils)
} }
override fun onBindViewHolder(holder: PollCreateOptionViewHolder, position: Int) { override fun onBindViewHolder(holder: PollCreateOptionViewHolder, position: Int) {

View File

@ -23,15 +23,19 @@ package com.nextcloud.talk.polls.adapters
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.graphics.Typeface import android.graphics.Typeface
import com.nextcloud.talk.databinding.PollResultHeaderItemBinding import com.nextcloud.talk.databinding.PollResultHeaderItemBinding
import com.nextcloud.talk.ui.theme.ViewThemeUtils
class PollResultHeaderViewHolder( class PollResultHeaderViewHolder(
override val binding: PollResultHeaderItemBinding override val binding: PollResultHeaderItemBinding,
private val viewThemeUtils: ViewThemeUtils
) : PollResultViewHolder(binding) { ) : PollResultViewHolder(binding) {
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
override fun bind(pollResultItem: PollResultItem, clickListener: PollResultItemClickListener) { override fun bind(pollResultItem: PollResultItem, clickListener: PollResultItemClickListener) {
val item = pollResultItem as PollResultHeaderItem val item = pollResultItem as PollResultHeaderItem
viewThemeUtils.colorProgressBar(binding.pollOptionBar)
binding.root.setOnClickListener { clickListener.onClick() } binding.root.setOnClickListener { clickListener.onClick() }
binding.pollOptionText.text = item.name binding.pollOptionText.text = item.name

View File

@ -27,10 +27,12 @@ import com.nextcloud.talk.data.user.model.User
import com.nextcloud.talk.databinding.PollResultHeaderItemBinding import com.nextcloud.talk.databinding.PollResultHeaderItemBinding
import com.nextcloud.talk.databinding.PollResultVoterItemBinding import com.nextcloud.talk.databinding.PollResultVoterItemBinding
import com.nextcloud.talk.databinding.PollResultVotersOverviewItemBinding import com.nextcloud.talk.databinding.PollResultVotersOverviewItemBinding
import com.nextcloud.talk.ui.theme.ViewThemeUtils
class PollResultsAdapter( class PollResultsAdapter(
private val user: User, private val user: User,
private val clickListener: PollResultItemClickListener, private val clickListener: PollResultItemClickListener,
private val viewThemeUtils: ViewThemeUtils
) : RecyclerView.Adapter<PollResultViewHolder>() { ) : RecyclerView.Adapter<PollResultViewHolder>() {
internal var list: MutableList<PollResultItem> = ArrayList() internal var list: MutableList<PollResultItem> = ArrayList()
@ -43,7 +45,7 @@ class PollResultsAdapter(
LayoutInflater.from(parent.context), parent, LayoutInflater.from(parent.context), parent,
false false
) )
viewHolder = PollResultHeaderViewHolder(itemBinding) viewHolder = PollResultHeaderViewHolder(itemBinding, viewThemeUtils)
} }
PollResultVoterItem.VIEW_TYPE -> { PollResultVoterItem.VIEW_TYPE -> {
val itemBinding = PollResultVoterItemBinding.inflate( val itemBinding = PollResultVoterItemBinding.inflate(

View File

@ -43,6 +43,7 @@ import com.nextcloud.talk.polls.adapters.PollCreateOptionItem
import com.nextcloud.talk.polls.adapters.PollCreateOptionsAdapter import com.nextcloud.talk.polls.adapters.PollCreateOptionsAdapter
import com.nextcloud.talk.polls.adapters.PollCreateOptionsItemListener import com.nextcloud.talk.polls.adapters.PollCreateOptionsItemListener
import com.nextcloud.talk.polls.viewmodels.PollCreateViewModel import com.nextcloud.talk.polls.viewmodels.PollCreateViewModel
import com.nextcloud.talk.ui.theme.ViewThemeUtils
import javax.inject.Inject import javax.inject.Inject
@AutoInjector(NextcloudTalkApplication::class) @AutoInjector(NextcloudTalkApplication::class)
@ -51,6 +52,9 @@ class PollCreateDialogFragment : DialogFragment(), PollCreateOptionsItemListener
@Inject @Inject
lateinit var viewModelFactory: ViewModelProvider.Factory lateinit var viewModelFactory: ViewModelProvider.Factory
@Inject
lateinit var viewThemeUtils: ViewThemeUtils
private lateinit var binding: DialogPollCreateBinding private lateinit var binding: DialogPollCreateBinding
private lateinit var viewModel: PollCreateViewModel private lateinit var viewModel: PollCreateViewModel
@ -85,13 +89,31 @@ class PollCreateDialogFragment : DialogFragment(), PollCreateOptionsItemListener
binding.pollCreateOptionsList.layoutManager = LinearLayoutManager(context) binding.pollCreateOptionsList.layoutManager = LinearLayoutManager(context)
adapter = PollCreateOptionsAdapter(this) adapter = PollCreateOptionsAdapter(this, viewThemeUtils)
binding.pollCreateOptionsList.adapter = adapter binding.pollCreateOptionsList.adapter = adapter
themeDialog()
setupListeners() setupListeners()
setupStateObserver() setupStateObserver()
} }
private fun themeDialog() {
viewThemeUtils.colorTextViewText(binding.pollQuestion)
viewThemeUtils.colorTextViewText(binding.pollOptions)
viewThemeUtils.colorTextViewText(binding.pollSettings)
viewThemeUtils.colorEditText(binding.pollCreateQuestionTextEdit)
viewThemeUtils.colorMaterialButtonText(binding.pollAddOptionsItem)
// TODO button also needs a disabled state handling for colors
viewThemeUtils.colorMaterialButtonText(binding.pollDismiss)
viewThemeUtils.colorMaterialButtonBackground(binding.pollCreateButton)
viewThemeUtils.themeCheckbox(binding.pollPrivatePollCheckbox)
viewThemeUtils.themeCheckbox(binding.pollMultipleAnswersCheckbox)
}
private fun setupListeners() { private fun setupListeners() {
binding.pollAddOptionsItem.setOnClickListener { binding.pollAddOptionsItem.setOnClickListener {
viewModel.addOption() viewModel.addOption()

View File

@ -38,6 +38,7 @@ import com.nextcloud.talk.polls.adapters.PollResultItemClickListener
import com.nextcloud.talk.polls.adapters.PollResultsAdapter import com.nextcloud.talk.polls.adapters.PollResultsAdapter
import com.nextcloud.talk.polls.viewmodels.PollMainViewModel import com.nextcloud.talk.polls.viewmodels.PollMainViewModel
import com.nextcloud.talk.polls.viewmodels.PollResultsViewModel import com.nextcloud.talk.polls.viewmodels.PollResultsViewModel
import com.nextcloud.talk.ui.theme.ViewThemeUtils
import javax.inject.Inject import javax.inject.Inject
@AutoInjector(NextcloudTalkApplication::class) @AutoInjector(NextcloudTalkApplication::class)
@ -46,6 +47,9 @@ class PollResultsFragment : Fragment(), PollResultItemClickListener {
@Inject @Inject
lateinit var viewModelFactory: ViewModelProvider.Factory lateinit var viewModelFactory: ViewModelProvider.Factory
@Inject
lateinit var viewThemeUtils: ViewThemeUtils
private lateinit var parentViewModel: PollMainViewModel private lateinit var parentViewModel: PollMainViewModel
lateinit var viewModel: PollResultsViewModel lateinit var viewModel: PollResultsViewModel
@ -82,17 +86,24 @@ class PollResultsFragment : Fragment(), PollResultItemClickListener {
} }
viewModel.items.observe(viewLifecycleOwner) { viewModel.items.observe(viewLifecycleOwner) {
val adapter = PollResultsAdapter(parentViewModel.user, this).apply { val adapter = PollResultsAdapter(parentViewModel.user, this, viewThemeUtils).apply {
if (it != null) { if (it != null) {
list = it list = it
} }
} }
binding.pollResultsList.adapter = adapter binding.pollResultsList.adapter = adapter
} }
themeDialog()
}
private fun themeDialog() {
viewThemeUtils.colorMaterialButtonBackground(binding.editVoteButton)
viewThemeUtils.colorMaterialButtonText(binding.pollResultsEndPollButton)
} }
private fun initAdapter() { private fun initAdapter() {
adapter = PollResultsAdapter(parentViewModel.user, this) adapter = PollResultsAdapter(parentViewModel.user, this, viewThemeUtils)
binding.pollResultsList.adapter = adapter binding.pollResultsList.adapter = adapter
binding.pollResultsList.layoutManager = LinearLayoutManager(context) binding.pollResultsList.layoutManager = LinearLayoutManager(context)
} }

View File

@ -43,6 +43,7 @@ import com.nextcloud.talk.databinding.DialogPollVoteBinding
import com.nextcloud.talk.polls.model.Poll import com.nextcloud.talk.polls.model.Poll
import com.nextcloud.talk.polls.viewmodels.PollMainViewModel import com.nextcloud.talk.polls.viewmodels.PollMainViewModel
import com.nextcloud.talk.polls.viewmodels.PollVoteViewModel import com.nextcloud.talk.polls.viewmodels.PollVoteViewModel
import com.nextcloud.talk.ui.theme.ViewThemeUtils
import javax.inject.Inject import javax.inject.Inject
@AutoInjector(NextcloudTalkApplication::class) @AutoInjector(NextcloudTalkApplication::class)
@ -51,6 +52,9 @@ class PollVoteFragment : Fragment() {
@Inject @Inject
lateinit var viewModelFactory: ViewModelProvider.Factory lateinit var viewModelFactory: ViewModelProvider.Factory
@Inject
lateinit var viewThemeUtils: ViewThemeUtils
private lateinit var parentViewModel: PollMainViewModel private lateinit var parentViewModel: PollMainViewModel
lateinit var viewModel: PollVoteViewModel lateinit var viewModel: PollVoteViewModel
@ -117,6 +121,14 @@ class PollVoteFragment : Fragment() {
binding.pollVoteEditDismiss.setOnClickListener { binding.pollVoteEditDismiss.setOnClickListener {
parentViewModel.dismissEditVotes() parentViewModel.dismissEditVotes()
} }
themeDialog()
}
private fun themeDialog() {
viewThemeUtils.colorMaterialButtonBackground(binding.pollVoteSubmitButton)
viewThemeUtils.colorMaterialButtonText(binding.pollVoteEndPollButton)
viewThemeUtils.colorMaterialButtonText(binding.pollVoteEditDismiss)
} }
private fun updateDismissEditButton(showDismissEditButton: Boolean) { private fun updateDismissEditButton(showDismissEditButton: Boolean) {
@ -136,6 +148,7 @@ class PollVoteFragment : Fragment() {
RadioButton(context).apply { text = option } RadioButton(context).apply { text = option }
}?.forEachIndexed { index, radioButton -> }?.forEachIndexed { index, radioButton ->
radioButton.id = index radioButton.id = index
viewThemeUtils.themeRadioButton(radioButton)
makeOptionBoldIfSelfVoted(radioButton, poll, index) makeOptionBoldIfSelfVoted(radioButton, poll, index)
binding.pollVoteRadioGroup.addView(radioButton) binding.pollVoteRadioGroup.addView(radioButton)
@ -156,6 +169,7 @@ class PollVoteFragment : Fragment() {
setLayoutParams(layoutParams) setLayoutParams(layoutParams)
} }
}?.forEachIndexed { index, checkBox -> }?.forEachIndexed { index, checkBox ->
viewThemeUtils.themeCheckbox(checkBox)
checkBox.id = index checkBox.id = index
makeOptionBoldIfSelfVoted(checkBox, poll, index) makeOptionBoldIfSelfVoted(checkBox, poll, index)
binding.voteOptionsCheckboxesWrapper.addView(checkBox) binding.voteOptionsCheckboxesWrapper.addView(checkBox)

View File

@ -44,6 +44,7 @@ import com.nextcloud.talk.remotefilebrowser.SelectionInterface
import com.nextcloud.talk.remotefilebrowser.adapters.RemoteFileBrowserItemsAdapter import com.nextcloud.talk.remotefilebrowser.adapters.RemoteFileBrowserItemsAdapter
import com.nextcloud.talk.remotefilebrowser.viewmodels.RemoteFileBrowserItemsViewModel import com.nextcloud.talk.remotefilebrowser.viewmodels.RemoteFileBrowserItemsViewModel
import com.nextcloud.talk.ui.dialog.SortingOrderDialogFragment import com.nextcloud.talk.ui.dialog.SortingOrderDialogFragment
import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.utils.DisplayUtils import com.nextcloud.talk.utils.DisplayUtils
import com.nextcloud.talk.utils.FileSortOrder import com.nextcloud.talk.utils.FileSortOrder
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_MIME_TYPE_FILTER import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_MIME_TYPE_FILTER
@ -59,6 +60,9 @@ class RemoteFileBrowserActivity : AppCompatActivity(), SelectionInterface, Swipe
@Inject @Inject
lateinit var currentUserProvider: CurrentUserProviderNew lateinit var currentUserProvider: CurrentUserProviderNew
@Inject
lateinit var viewThemeUtils: ViewThemeUtils
private lateinit var binding: ActivityRemoteFileBrowserBinding private lateinit var binding: ActivityRemoteFileBrowserBinding
private lateinit var viewModel: RemoteFileBrowserItemsViewModel private lateinit var viewModel: RemoteFileBrowserItemsViewModel
@ -91,8 +95,7 @@ class RemoteFileBrowserActivity : AppCompatActivity(), SelectionInterface, Swipe
initViewModel(mimeTypeSelectionFilter) initViewModel(mimeTypeSelectionFilter)
binding.swipeRefreshList.setOnRefreshListener(this) binding.swipeRefreshList.setOnRefreshListener(this)
binding.swipeRefreshList.setColorSchemeResources(R.color.colorPrimary) viewThemeUtils.themeSwipeRefreshLayout(binding.swipeRefreshList)
binding.swipeRefreshList.setProgressBackgroundColorSchemeResource(R.color.refresh_spinner_background)
binding.pathNavigationBackButton.setOnClickListener { viewModel.navigateUp() } binding.pathNavigationBackButton.setOnClickListener { viewModel.navigateUp() }
binding.sortButton.setOnClickListener { changeSorting() } binding.sortButton.setOnClickListener { changeSorting() }
@ -160,6 +163,7 @@ class RemoteFileBrowserActivity : AppCompatActivity(), SelectionInterface, Swipe
mimeTypeSelectionFilter = mimeTypeSelectionFilter, mimeTypeSelectionFilter = mimeTypeSelectionFilter,
user = currentUserProvider.currentUser.blockingGet(), user = currentUserProvider.currentUser.blockingGet(),
selectionInterface = this, selectionInterface = this,
viewThemeUtils = viewThemeUtils,
onItemClicked = viewModel::onItemClicked onItemClicked = viewModel::onItemClicked
) )
adapter.items = remoteFileBrowserItems adapter.items = remoteFileBrowserItems

View File

@ -28,19 +28,20 @@ import com.nextcloud.talk.data.user.model.User
import com.nextcloud.talk.databinding.RvItemBrowserFileBinding import com.nextcloud.talk.databinding.RvItemBrowserFileBinding
import com.nextcloud.talk.remotefilebrowser.SelectionInterface import com.nextcloud.talk.remotefilebrowser.SelectionInterface
import com.nextcloud.talk.remotefilebrowser.model.RemoteFileBrowserItem import com.nextcloud.talk.remotefilebrowser.model.RemoteFileBrowserItem
import com.nextcloud.talk.ui.theme.ViewThemeUtils
class RemoteFileBrowserItemsAdapter( class RemoteFileBrowserItemsAdapter(
private val showGrid: Boolean = false, private val showGrid: Boolean = false,
private val mimeTypeSelectionFilter: String? = null, private val mimeTypeSelectionFilter: String? = null,
private val user: User, private val user: User,
private val selectionInterface: SelectionInterface, private val selectionInterface: SelectionInterface,
private val viewThemeUtils: ViewThemeUtils,
private val onItemClicked: (RemoteFileBrowserItem) -> Unit private val onItemClicked: (RemoteFileBrowserItem) -> Unit
) : RecyclerView.Adapter<RemoteFileBrowserItemsViewHolder>() { ) : RecyclerView.Adapter<RemoteFileBrowserItemsViewHolder>() {
var items: List<RemoteFileBrowserItem> = emptyList() var items: List<RemoteFileBrowserItem> = emptyList()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RemoteFileBrowserItemsViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RemoteFileBrowserItemsViewHolder {
return if (showGrid) { return if (showGrid) {
RemoteFileBrowserItemsListViewHolder( RemoteFileBrowserItemsListViewHolder(
RvItemBrowserFileBinding.inflate( RvItemBrowserFileBinding.inflate(
@ -50,7 +51,8 @@ class RemoteFileBrowserItemsAdapter(
), ),
mimeTypeSelectionFilter, mimeTypeSelectionFilter,
user, user,
selectionInterface selectionInterface,
viewThemeUtils
) { ) {
onItemClicked(items[it]) onItemClicked(items[it])
} }
@ -63,7 +65,8 @@ class RemoteFileBrowserItemsAdapter(
), ),
mimeTypeSelectionFilter, mimeTypeSelectionFilter,
user, user,
selectionInterface selectionInterface,
viewThemeUtils
) { ) {
onItemClicked(items[it]) onItemClicked(items[it])
} }

View File

@ -22,7 +22,6 @@ package com.nextcloud.talk.remotefilebrowser.adapters
import android.text.format.Formatter import android.text.format.Formatter
import android.view.View import android.view.View
import androidx.appcompat.content.res.AppCompatResources
import autodagger.AutoInjector import autodagger.AutoInjector
import com.facebook.drawee.backends.pipeline.Fresco import com.facebook.drawee.backends.pipeline.Fresco
import com.facebook.drawee.interfaces.DraweeController import com.facebook.drawee.interfaces.DraweeController
@ -33,10 +32,10 @@ import com.nextcloud.talk.data.user.model.User
import com.nextcloud.talk.databinding.RvItemBrowserFileBinding import com.nextcloud.talk.databinding.RvItemBrowserFileBinding
import com.nextcloud.talk.remotefilebrowser.SelectionInterface import com.nextcloud.talk.remotefilebrowser.SelectionInterface
import com.nextcloud.talk.remotefilebrowser.model.RemoteFileBrowserItem import com.nextcloud.talk.remotefilebrowser.model.RemoteFileBrowserItem
import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.DateUtils.getLocalDateTimeStringFromTimestamp import com.nextcloud.talk.utils.DateUtils.getLocalDateTimeStringFromTimestamp
import com.nextcloud.talk.utils.DisplayUtils import com.nextcloud.talk.utils.DisplayUtils
import com.nextcloud.talk.utils.DrawableUtils.getDrawableResourceIdForMimeType
import com.nextcloud.talk.utils.Mimetype.FOLDER import com.nextcloud.talk.utils.Mimetype.FOLDER
@AutoInjector(NextcloudTalkApplication::class) @AutoInjector(NextcloudTalkApplication::class)
@ -45,6 +44,7 @@ class RemoteFileBrowserItemsListViewHolder(
mimeTypeSelectionFilter: String?, mimeTypeSelectionFilter: String?,
currentUser: User, currentUser: User,
selectionInterface: SelectionInterface, selectionInterface: SelectionInterface,
private val viewThemeUtils: ViewThemeUtils,
onItemClicked: (Int) -> Unit onItemClicked: (Int) -> Unit
) : RemoteFileBrowserItemsViewHolder(binding, mimeTypeSelectionFilter, currentUser, selectionInterface) { ) : RemoteFileBrowserItemsViewHolder(binding, mimeTypeSelectionFilter, currentUser, selectionInterface) {
@ -66,7 +66,6 @@ class RemoteFileBrowserItemsListViewHolder(
} }
override fun onBind(item: RemoteFileBrowserItem) { override fun onBind(item: RemoteFileBrowserItem) {
super.onBind(item) super.onBind(item)
binding.fileIcon.controller = null binding.fileIcon.controller = null
@ -99,9 +98,7 @@ class RemoteFileBrowserItemsListViewHolder(
binding.fileIcon binding.fileIcon
.hierarchy .hierarchy
.setPlaceholderImage( .setPlaceholderImage(
AppCompatResources.getDrawable( viewThemeUtils.getPlaceholderImage(binding.root.context, item.mimeType)
binding.fileIcon.context, getDrawableResourceIdForMimeType(item.mimeType)
)
) )
if (item.hasPreview) { if (item.hasPreview) {
@ -132,6 +129,7 @@ class RemoteFileBrowserItemsListViewHolder(
private fun setSelectability() { private fun setSelectability() {
if (selectable) { if (selectable) {
binding.selectFileCheckbox.visibility = View.VISIBLE binding.selectFileCheckbox.visibility = View.VISIBLE
viewThemeUtils.themeCheckbox(binding.selectFileCheckbox)
} else { } else {
binding.selectFileCheckbox.visibility = View.GONE binding.selectFileCheckbox.visibility = View.GONE
} }

View File

@ -41,6 +41,7 @@ import com.nextcloud.talk.databinding.ActivitySharedItemsBinding
import com.nextcloud.talk.shareditems.adapters.SharedItemsAdapter import com.nextcloud.talk.shareditems.adapters.SharedItemsAdapter
import com.nextcloud.talk.shareditems.model.SharedItemType import com.nextcloud.talk.shareditems.model.SharedItemType
import com.nextcloud.talk.shareditems.viewmodels.SharedItemsViewModel import com.nextcloud.talk.shareditems.viewmodels.SharedItemsViewModel
import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.utils.DisplayUtils import com.nextcloud.talk.utils.DisplayUtils
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CONVERSATION_NAME import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CONVERSATION_NAME
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN
@ -53,6 +54,9 @@ class SharedItemsActivity : AppCompatActivity() {
@Inject @Inject
lateinit var viewModelFactory: ViewModelProvider.Factory lateinit var viewModelFactory: ViewModelProvider.Factory
@Inject
lateinit var viewThemeUtils: ViewThemeUtils
private lateinit var binding: ActivitySharedItemsBinding private lateinit var binding: ActivitySharedItemsBinding
private lateinit var viewModel: SharedItemsViewModel private lateinit var viewModel: SharedItemsViewModel
@ -72,7 +76,9 @@ class SharedItemsActivity : AppCompatActivity() {
DisplayUtils.applyColorToStatusBar( DisplayUtils.applyColorToStatusBar(
this, this,
ResourcesCompat.getColor( ResourcesCompat.getColor(
resources, R.color.appbar, null resources,
R.color.appbar,
null
) )
) )
DisplayUtils.applyColorToNavigationBar( DisplayUtils.applyColorToNavigationBar(
@ -130,7 +136,8 @@ class SharedItemsActivity : AppCompatActivity() {
showGrid, showGrid,
user, user,
roomToken, roomToken,
isUserConversationOwnerOrModerator isUserConversationOwnerOrModerator,
viewThemeUtils
).apply { ).apply {
items = sharedMediaItems.items items = sharedMediaItems.items
} }
@ -142,6 +149,8 @@ class SharedItemsActivity : AppCompatActivity() {
} }
else -> {} else -> {}
} }
viewThemeUtils.colorTabLayout(binding.sharedItemsTabs)
} }
private fun clearEmptyLoading() { private fun clearEmptyLoading() {
@ -161,7 +170,6 @@ class SharedItemsActivity : AppCompatActivity() {
} }
private fun initTabs(sharedItemTypes: Set<SharedItemType>) { private fun initTabs(sharedItemTypes: Set<SharedItemType>) {
binding.sharedItemsTabs.removeAllTabs() binding.sharedItemsTabs.removeAllTabs()
if (sharedItemTypes.contains(SharedItemType.MEDIA)) { if (sharedItemTypes.contains(SharedItemType.MEDIA)) {

View File

@ -37,18 +37,19 @@ import com.nextcloud.talk.shareditems.model.SharedItem
import com.nextcloud.talk.shareditems.model.SharedLocationItem import com.nextcloud.talk.shareditems.model.SharedLocationItem
import com.nextcloud.talk.shareditems.model.SharedOtherItem import com.nextcloud.talk.shareditems.model.SharedOtherItem
import com.nextcloud.talk.shareditems.model.SharedPollItem import com.nextcloud.talk.shareditems.model.SharedPollItem
import com.nextcloud.talk.ui.theme.ViewThemeUtils
class SharedItemsAdapter( class SharedItemsAdapter(
private val showGrid: Boolean, private val showGrid: Boolean,
private val user: User, private val user: User,
private val roomToken: String, private val roomToken: String,
private val isUserConversationOwnerOrModerator: Boolean private val isUserConversationOwnerOrModerator: Boolean,
private val viewThemeUtils: ViewThemeUtils
) : RecyclerView.Adapter<SharedItemsViewHolder>() { ) : RecyclerView.Adapter<SharedItemsViewHolder>() {
var items: List<SharedItem> = emptyList() var items: List<SharedItem> = emptyList()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SharedItemsViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SharedItemsViewHolder {
return if (showGrid) { return if (showGrid) {
SharedItemsGridViewHolder( SharedItemsGridViewHolder(
SharedItemGridBinding.inflate( SharedItemGridBinding.inflate(
@ -56,7 +57,8 @@ class SharedItemsAdapter(
parent, parent,
false false
), ),
user user,
viewThemeUtils
) )
} else { } else {
SharedItemsListViewHolder( SharedItemsListViewHolder(
@ -65,7 +67,8 @@ class SharedItemsAdapter(
parent, parent,
false false
), ),
user user,
viewThemeUtils
) )
} }
} }

View File

@ -27,11 +27,13 @@ import android.widget.ProgressBar
import com.facebook.drawee.view.SimpleDraweeView import com.facebook.drawee.view.SimpleDraweeView
import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.data.user.model.User
import com.nextcloud.talk.databinding.SharedItemGridBinding import com.nextcloud.talk.databinding.SharedItemGridBinding
import com.nextcloud.talk.ui.theme.ViewThemeUtils
class SharedItemsGridViewHolder( class SharedItemsGridViewHolder(
override val binding: SharedItemGridBinding, override val binding: SharedItemGridBinding,
user: User user: User,
) : SharedItemsViewHolder(binding, user) { viewThemeUtils: ViewThemeUtils
) : SharedItemsViewHolder(binding, user, viewThemeUtils) {
override val image: SimpleDraweeView override val image: SimpleDraweeView
get() = binding.image get() = binding.image

View File

@ -38,11 +38,13 @@ import com.nextcloud.talk.shareditems.model.SharedItem
import com.nextcloud.talk.shareditems.model.SharedLocationItem import com.nextcloud.talk.shareditems.model.SharedLocationItem
import com.nextcloud.talk.shareditems.model.SharedOtherItem import com.nextcloud.talk.shareditems.model.SharedOtherItem
import com.nextcloud.talk.shareditems.model.SharedPollItem import com.nextcloud.talk.shareditems.model.SharedPollItem
import com.nextcloud.talk.ui.theme.ViewThemeUtils
class SharedItemsListViewHolder( class SharedItemsListViewHolder(
override val binding: SharedItemListBinding, override val binding: SharedItemListBinding,
user: User user: User,
) : SharedItemsViewHolder(binding, user) { viewThemeUtils: ViewThemeUtils
) : SharedItemsViewHolder(binding, user, viewThemeUtils) {
override val image: SimpleDraweeView override val image: SimpleDraweeView
get() = binding.fileImage get() = binding.fileImage
@ -52,7 +54,6 @@ class SharedItemsListViewHolder(
get() = binding.progressBar get() = binding.progressBar
override fun onBind(item: SharedFileItem) { override fun onBind(item: SharedFileItem) {
super.onBind(item) super.onBind(item)
binding.fileName.text = item.name binding.fileName.text = item.name

View File

@ -23,12 +23,10 @@
package com.nextcloud.talk.shareditems.adapters package com.nextcloud.talk.shareditems.adapters
import android.content.Context import android.content.Context
import android.graphics.drawable.Drawable
import android.net.Uri import android.net.Uri
import android.util.Log import android.util.Log
import android.view.View import android.view.View
import android.widget.ProgressBar import android.widget.ProgressBar
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding import androidx.viewbinding.ViewBinding
import com.facebook.drawee.backends.pipeline.Fresco import com.facebook.drawee.backends.pipeline.Fresco
@ -43,16 +41,17 @@ import com.nextcloud.talk.data.user.model.User
import com.nextcloud.talk.shareditems.model.SharedDeckCardItem import com.nextcloud.talk.shareditems.model.SharedDeckCardItem
import com.nextcloud.talk.shareditems.model.SharedFileItem import com.nextcloud.talk.shareditems.model.SharedFileItem
import com.nextcloud.talk.shareditems.model.SharedItem import com.nextcloud.talk.shareditems.model.SharedItem
import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.shareditems.model.SharedLocationItem import com.nextcloud.talk.shareditems.model.SharedLocationItem
import com.nextcloud.talk.shareditems.model.SharedOtherItem import com.nextcloud.talk.shareditems.model.SharedOtherItem
import com.nextcloud.talk.shareditems.model.SharedPollItem import com.nextcloud.talk.shareditems.model.SharedPollItem
import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.DrawableUtils
import com.nextcloud.talk.utils.FileViewerUtils import com.nextcloud.talk.utils.FileViewerUtils
abstract class SharedItemsViewHolder( abstract class SharedItemsViewHolder(
open val binding: ViewBinding, open val binding: ViewBinding,
internal val user: User internal val user: User,
private val viewThemeUtils: ViewThemeUtils
) : RecyclerView.ViewHolder(binding.root) { ) : RecyclerView.ViewHolder(binding.root) {
companion object { companion object {
@ -71,7 +70,7 @@ abstract class SharedItemsViewHolder(
) )
open fun onBind(item: SharedFileItem) { open fun onBind(item: SharedFileItem) {
image.hierarchy.setPlaceholderImage(staticImage(item.mimeType, image)) image.hierarchy.setPlaceholderImage(viewThemeUtils.getPlaceholderImage(image.context, item.mimeType))
if (item.previewAvailable) { if (item.previewAvailable) {
image.controller = configurePreview(item) image.controller = configurePreview(item)
} }
@ -107,7 +106,6 @@ abstract class SharedItemsViewHolder(
} }
private fun configurePreview(item: SharedFileItem): DraweeController { private fun configurePreview(item: SharedFileItem): DraweeController {
val imageRequest = ImageRequestBuilder.newBuilderWithSource(Uri.parse(item.previewLink)) val imageRequest = ImageRequestBuilder.newBuilderWithSource(Uri.parse(item.previewLink))
.setProgressiveRenderingEnabled(true) .setProgressiveRenderingEnabled(true)
.setRotationOptions(RotationOptions.autoRotate()) .setRotationOptions(RotationOptions.autoRotate())
@ -136,12 +134,4 @@ abstract class SharedItemsViewHolder(
open fun onBind(item: SharedOtherItem) {} open fun onBind(item: SharedOtherItem) {}
open fun onBind(item: SharedDeckCardItem) {} open fun onBind(item: SharedDeckCardItem) {}
private fun staticImage(
mimeType: String?,
image: SimpleDraweeView
): Drawable {
val drawableResourceId = DrawableUtils.getDrawableResourceIdForMimeType(mimeType)
return ContextCompat.getDrawable(image.context, drawableResourceId)!!
}
} }

View File

@ -24,18 +24,33 @@ import android.os.Bundle
import android.util.Log import android.util.Log
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.content.ContextCompat import autodagger.AutoInjector
import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialog
import com.nextcloud.talk.R import com.nextcloud.talk.R
import com.nextcloud.talk.activities.CallActivity import com.nextcloud.talk.activities.CallActivity
import com.nextcloud.talk.application.NextcloudTalkApplication
import com.nextcloud.talk.databinding.DialogAudioOutputBinding import com.nextcloud.talk.databinding.DialogAudioOutputBinding
import com.nextcloud.talk.ui.theme.ServerTheme
import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.webrtc.WebRtcAudioManager import com.nextcloud.talk.webrtc.WebRtcAudioManager
import javax.inject.Inject
@AutoInjector(NextcloudTalkApplication::class)
class AudioOutputDialog(val callActivity: CallActivity) : BottomSheetDialog(callActivity) { class AudioOutputDialog(val callActivity: CallActivity) : BottomSheetDialog(callActivity) {
@Inject
lateinit var viewThemeUtils: ViewThemeUtils
@Inject
lateinit var serverTheme: ServerTheme
private lateinit var dialogAudioOutputBinding: DialogAudioOutputBinding private lateinit var dialogAudioOutputBinding: DialogAudioOutputBinding
init {
NextcloudTalkApplication.sharedApplication?.componentApplication?.inject(this)
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
dialogAudioOutputBinding = DialogAudioOutputBinding.inflate(layoutInflater) dialogAudioOutputBinding = DialogAudioOutputBinding.inflate(layoutInflater)
@ -82,55 +97,23 @@ class AudioOutputDialog(val callActivity: CallActivity) : BottomSheetDialog(call
private fun highlightActiveOutputChannel() { private fun highlightActiveOutputChannel() {
when (callActivity.audioManager?.currentAudioDevice) { when (callActivity.audioManager?.currentAudioDevice) {
WebRtcAudioManager.AudioDevice.BLUETOOTH -> { WebRtcAudioManager.AudioDevice.BLUETOOTH -> {
dialogAudioOutputBinding.audioOutputBluetoothIcon.setColorFilter( viewThemeUtils.colorImageView(dialogAudioOutputBinding.audioOutputBluetoothIcon)
ContextCompat.getColor( dialogAudioOutputBinding.audioOutputBluetoothText.setTextColor(serverTheme.primaryColor)
context,
R.color.colorPrimary
),
android.graphics.PorterDuff.Mode.SRC_IN
)
dialogAudioOutputBinding.audioOutputBluetoothText.setTextColor(
callActivity.resources.getColor(R.color.colorPrimary)
)
} }
WebRtcAudioManager.AudioDevice.SPEAKER_PHONE -> { WebRtcAudioManager.AudioDevice.SPEAKER_PHONE -> {
dialogAudioOutputBinding.audioOutputSpeakerIcon.setColorFilter( viewThemeUtils.colorImageView(dialogAudioOutputBinding.audioOutputSpeakerIcon)
ContextCompat.getColor( dialogAudioOutputBinding.audioOutputSpeakerText.setTextColor(serverTheme.primaryColor)
context,
R.color.colorPrimary
),
android.graphics.PorterDuff.Mode.SRC_IN
)
dialogAudioOutputBinding.audioOutputSpeakerText.setTextColor(
callActivity.resources.getColor(R.color.colorPrimary)
)
} }
WebRtcAudioManager.AudioDevice.EARPIECE -> { WebRtcAudioManager.AudioDevice.EARPIECE -> {
dialogAudioOutputBinding.audioOutputEarspeakerIcon.setColorFilter( viewThemeUtils.colorImageView(dialogAudioOutputBinding.audioOutputEarspeakerIcon)
ContextCompat.getColor( dialogAudioOutputBinding.audioOutputEarspeakerText.setTextColor(serverTheme.primaryColor)
context,
R.color.colorPrimary
),
android.graphics.PorterDuff.Mode.SRC_IN
)
dialogAudioOutputBinding.audioOutputEarspeakerText.setTextColor(
callActivity.resources.getColor(R.color.colorPrimary)
)
} }
WebRtcAudioManager.AudioDevice.WIRED_HEADSET -> { WebRtcAudioManager.AudioDevice.WIRED_HEADSET -> {
dialogAudioOutputBinding.audioOutputWiredHeadsetIcon.setColorFilter( viewThemeUtils.colorImageView(dialogAudioOutputBinding.audioOutputWiredHeadsetIcon)
ContextCompat.getColor( dialogAudioOutputBinding.audioOutputWiredHeadsetText.setTextColor(serverTheme.primaryColor)
context,
R.color.colorPrimary
),
android.graphics.PorterDuff.Mode.SRC_IN
)
dialogAudioOutputBinding.audioOutputWiredHeadsetText.setTextColor(
callActivity.resources.getColor(R.color.colorPrimary)
)
} }
else -> Log.d(TAG, "AudioOutputDialog doesn't know this AudioDevice") else -> Log.d(TAG, "AudioOutputDialog doesn't know this AudioDevice")

View File

@ -51,6 +51,7 @@ import com.nextcloud.talk.models.json.status.Status;
import com.nextcloud.talk.models.json.status.StatusOverall; import com.nextcloud.talk.models.json.status.StatusOverall;
import com.nextcloud.talk.ui.StatusDrawable; import com.nextcloud.talk.ui.StatusDrawable;
import com.nextcloud.talk.users.UserManager; import com.nextcloud.talk.users.UserManager;
import com.nextcloud.talk.ui.theme.ViewThemeUtils;
import com.nextcloud.talk.utils.ApiUtils; import com.nextcloud.talk.utils.ApiUtils;
import com.nextcloud.talk.utils.DisplayUtils; import com.nextcloud.talk.utils.DisplayUtils;
import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew; import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew;
@ -87,6 +88,9 @@ public class ChooseAccountDialogFragment extends DialogFragment {
@Inject @Inject
NcApi ncApi; NcApi ncApi;
@Inject
ViewThemeUtils viewThemeUtils;
private DialogChooseAccountBinding binding; private DialogChooseAccountBinding binding;
private View dialogView; private View dialogView;
@ -120,6 +124,9 @@ public class ChooseAccountDialogFragment extends DialogFragment {
binding.currentAccount.ticker.setVisibility(View.GONE); binding.currentAccount.ticker.setVisibility(View.GONE);
binding.currentAccount.account.setText((Uri.parse(user.getBaseUrl()).getHost())); binding.currentAccount.account.setText((Uri.parse(user.getBaseUrl()).getHost()));
viewThemeUtils.colorImageView(binding.currentAccount.accountMenu);
if (user.getBaseUrl() != null && if (user.getBaseUrl() != null &&
(user.getBaseUrl().startsWith("http://") || user.getBaseUrl().startsWith("https://"))) { (user.getBaseUrl().startsWith("http://") || user.getBaseUrl().startsWith("https://"))) {
binding.currentAccount.userIcon.setVisibility(View.VISIBLE); binding.currentAccount.userIcon.setVisibility(View.VISIBLE);

View File

@ -34,12 +34,15 @@ import android.view.inputmethod.InputMethodManager
import android.widget.AdapterView import android.widget.AdapterView
import android.widget.AdapterView.OnItemSelectedListener import android.widget.AdapterView.OnItemSelectedListener
import android.widget.ArrayAdapter import android.widget.ArrayAdapter
import android.widget.ImageView
import android.widget.TextView
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.core.widget.doAfterTextChanged import androidx.core.widget.doAfterTextChanged
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import autodagger.AutoInjector import autodagger.AutoInjector
import com.bluelinelabs.logansquare.LoganSquare import com.bluelinelabs.logansquare.LoganSquare
import com.google.android.material.card.MaterialCardView
import com.nextcloud.talk.R import com.nextcloud.talk.R
import com.nextcloud.talk.adapters.PredefinedStatusClickListener import com.nextcloud.talk.adapters.PredefinedStatusClickListener
import com.nextcloud.talk.adapters.PredefinedStatusListAdapter import com.nextcloud.talk.adapters.PredefinedStatusListAdapter
@ -53,6 +56,7 @@ import com.nextcloud.talk.models.json.status.Status
import com.nextcloud.talk.models.json.status.StatusType import com.nextcloud.talk.models.json.status.StatusType
import com.nextcloud.talk.models.json.status.predefined.PredefinedStatus import com.nextcloud.talk.models.json.status.predefined.PredefinedStatus
import com.nextcloud.talk.models.json.status.predefined.PredefinedStatusOverall import com.nextcloud.talk.models.json.status.predefined.PredefinedStatusOverall
import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.DisplayUtils import com.nextcloud.talk.utils.DisplayUtils
import com.vanniktech.emoji.EmojiPopup import com.vanniktech.emoji.EmojiPopup
@ -105,6 +109,9 @@ class SetStatusDialogFragment :
@Inject @Inject
lateinit var ncApi: NcApi lateinit var ncApi: NcApi
@Inject
lateinit var viewThemeUtils: ViewThemeUtils
lateinit var credentials: String lateinit var credentials: String
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -234,8 +241,8 @@ class SetStatusDialogFragment :
} }
} }
binding.clearStatus.setTextColor(resources.getColor(R.color.colorPrimary)) viewThemeUtils.colorMaterialButtonText(binding.clearStatus)
binding.setStatus.setBackgroundColor(resources.getColor(R.color.colorPrimary)) viewThemeUtils.colorMaterialButtonBackground(binding.setStatus)
binding.customStatusInput.highlightColor = resources.getColor(R.color.colorPrimary) binding.customStatusInput.highlightColor = resources.getColor(R.color.colorPrimary)
@ -258,7 +265,6 @@ class SetStatusDialogFragment :
@Suppress("ComplexMethod") @Suppress("ComplexMethod")
private fun setClearStatusAfterValue(item: Int) { private fun setClearStatusAfterValue(item: Int) {
val currentTime = System.currentTimeMillis() / ONE_SECOND_IN_MILLIS val currentTime = System.currentTimeMillis() / ONE_SECOND_IN_MILLIS
when (item) { when (item) {
@ -310,7 +316,6 @@ class SetStatusDialogFragment :
} }
private fun clearAtToUnixTime(clearAt: ClearAt?): Long { private fun clearAtToUnixTime(clearAt: ClearAt?): Long {
var returnValue = -1L var returnValue = -1L
if (clearAt != null) { if (clearAt != null) {
@ -400,25 +405,18 @@ class SetStatusDialogFragment :
private fun visualizeStatus(statusType: StatusType) { private fun visualizeStatus(statusType: StatusType) {
clearTopStatus() clearTopStatus()
when (statusType) { val views: Triple<MaterialCardView, TextView, ImageView> = when (statusType) {
StatusType.ONLINE -> { StatusType.ONLINE -> Triple(binding.onlineStatus, binding.onlineHeadline, binding.onlineIcon)
binding.onlineStatus.setCardBackgroundColor(resources.getColor(R.color.colorPrimary)) StatusType.AWAY -> Triple(binding.awayStatus, binding.awayHeadline, binding.awayIcon)
binding.onlineHeadline.setTextColor(resources.getColor(R.color.high_emphasis_text_dark_background)) StatusType.DND -> Triple(binding.dndStatus, binding.dndHeadline, binding.dndIcon)
StatusType.INVISIBLE -> Triple(binding.invisibleStatus, binding.invisibleHeadline, binding.invisibleIcon)
else -> {
Log.d(TAG, "unknown status")
return
} }
StatusType.AWAY -> {
binding.awayStatus.setCardBackgroundColor(resources.getColor(R.color.colorPrimary))
binding.awayHeadline.setTextColor(resources.getColor(R.color.high_emphasis_text_dark_background))
}
StatusType.DND -> {
binding.dndStatus.setCardBackgroundColor(resources.getColor(R.color.colorPrimary))
binding.dndHeadline.setTextColor(resources.getColor(R.color.high_emphasis_text_dark_background))
}
StatusType.INVISIBLE -> {
binding.invisibleStatus.setCardBackgroundColor(resources.getColor(R.color.colorPrimary))
binding.invisibleHeadline.setTextColor(resources.getColor(R.color.high_emphasis_text_dark_background))
}
else -> Log.d(TAG, "unknown status")
} }
viewThemeUtils.colorCardViewBackground(views.first)
viewThemeUtils.colorTextViewText(views.second)
} }
private fun clearTopStatus() { private fun clearTopStatus() {
@ -433,11 +431,15 @@ class SetStatusDialogFragment :
binding.awayHeadline.setTextColor(resources.getColor(R.color.high_emphasis_text)) binding.awayHeadline.setTextColor(resources.getColor(R.color.high_emphasis_text))
binding.dndHeadline.setTextColor(resources.getColor(R.color.high_emphasis_text)) binding.dndHeadline.setTextColor(resources.getColor(R.color.high_emphasis_text))
binding.invisibleHeadline.setTextColor(resources.getColor(R.color.high_emphasis_text)) binding.invisibleHeadline.setTextColor(resources.getColor(R.color.high_emphasis_text))
binding.onlineIcon.imageTintList = null
binding.awayIcon.imageTintList = null
binding.dndIcon.imageTintList = null
binding.invisibleIcon.imageTintList = null
} }
} }
private fun setStatusMessage() { private fun setStatusMessage() {
val inputText = binding.customStatusInput.text.toString().ifEmpty { "" } val inputText = binding.customStatusInput.text.toString().ifEmpty { "" }
// The endpoint '/message/custom' expects a valid emoji as string or null // The endpoint '/message/custom' expects a valid emoji as string or null
val statusIcon = binding.emoji.text.toString().ifEmpty { null } val statusIcon = binding.emoji.text.toString().ifEmpty { null }
@ -446,7 +448,6 @@ class SetStatusDialogFragment :
selectedPredefinedStatus!!.message != inputText || selectedPredefinedStatus!!.message != inputText ||
selectedPredefinedStatus!!.icon != binding.emoji.text.toString() selectedPredefinedStatus!!.icon != binding.emoji.text.toString()
) { ) {
ncApi.setCustomStatusMessage( ncApi.setCustomStatusMessage(
credentials, credentials,
ApiUtils.getUrlForSetCustomStatus(currentUser?.baseUrl), ApiUtils.getUrlForSetCustomStatus(currentUser?.baseUrl),
@ -476,12 +477,13 @@ class SetStatusDialogFragment :
} }
}) })
} else { } else {
val clearAt = clearAtToUnixTime(selectedPredefinedStatus!!.clearAt) val clearAt = clearAtToUnixTime(selectedPredefinedStatus!!.clearAt)
ncApi.setPredefinedStatusMessage( ncApi.setPredefinedStatusMessage(
credentials, ApiUtils.getUrlForSetPredefinedStatus(currentUser?.baseUrl), credentials,
selectedPredefinedStatus!!.id, if (clearAt == -1L) null else clearAt ApiUtils.getUrlForSetPredefinedStatus(currentUser?.baseUrl),
selectedPredefinedStatus!!.id,
if (clearAt == -1L) null else clearAt
) )
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())?.subscribe(object : Observer<GenericOverall> { .observeOn(AndroidSchedulers.mainThread())?.subscribe(object : Observer<GenericOverall> {
@ -506,7 +508,6 @@ class SetStatusDialogFragment :
} }
override fun onClick(predefinedStatus: PredefinedStatus) { override fun onClick(predefinedStatus: PredefinedStatus) {
selectedPredefinedStatus = predefinedStatus selectedPredefinedStatus = predefinedStatus
clearAt = clearAtToUnixTime(predefinedStatus.clearAt) clearAt = clearAtToUnixTime(predefinedStatus.clearAt)

View File

@ -51,6 +51,7 @@ import com.nextcloud.talk.models.json.chat.ChatMessage
import com.nextcloud.talk.models.json.conversations.Conversation import com.nextcloud.talk.models.json.conversations.Conversation
import com.nextcloud.talk.models.json.generic.GenericOverall import com.nextcloud.talk.models.json.generic.GenericOverall
import com.nextcloud.talk.models.json.reactions.ReactionsOverall import com.nextcloud.talk.models.json.reactions.ReactionsOverall
import com.nextcloud.talk.ui.theme.ServerTheme
import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.ApiUtils
import io.reactivex.Observer import io.reactivex.Observer
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
@ -65,7 +66,8 @@ class ShowReactionsDialog(
private val chatMessage: ChatMessage, private val chatMessage: ChatMessage,
private val user: User?, private val user: User?,
private val hasChatPermission: Boolean, private val hasChatPermission: Boolean,
private val ncApi: NcApi private val ncApi: NcApi,
private val serverTheme: ServerTheme
) : BottomSheetDialog(activity), ReactionItemClickListener { ) : BottomSheetDialog(activity), ReactionItemClickListener {
private lateinit var binding: DialogMessageReactionsBinding private lateinit var binding: DialogMessageReactionsBinding
@ -96,6 +98,7 @@ class ShowReactionsDialog(
adapter?.list?.clear() adapter?.list?.clear()
if (chatMessage.reactions != null && chatMessage.reactions!!.isNotEmpty()) { if (chatMessage.reactions != null && chatMessage.reactions!!.isNotEmpty()) {
var reactionsTotal = 0 var reactionsTotal = 0
binding.emojiReactionsTabs.setSelectedTabIndicatorColor(serverTheme.primaryColor)
for ((emoji, amount) in chatMessage.reactions!!) { for ((emoji, amount) in chatMessage.reactions!!) {
reactionsTotal = reactionsTotal.plus(amount as Int) reactionsTotal = reactionsTotal.plus(amount as Int)
val tab: TabLayout.Tab = binding.emojiReactionsTabs.newTab() // Create a new Tab names "First Tab" val tab: TabLayout.Tab = binding.emojiReactionsTabs.newTab() // Create a new Tab names "First Tab"

View File

@ -33,9 +33,9 @@ import android.widget.TextView;
import com.google.android.material.button.MaterialButton; import com.google.android.material.button.MaterialButton;
import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.nextcloud.talk.R;
import com.nextcloud.talk.application.NextcloudTalkApplication; import com.nextcloud.talk.application.NextcloudTalkApplication;
import com.nextcloud.talk.databinding.SortingOrderFragmentBinding; import com.nextcloud.talk.databinding.SortingOrderFragmentBinding;
import com.nextcloud.talk.ui.theme.ViewThemeUtils;
import com.nextcloud.talk.utils.FileSortOrder; import com.nextcloud.talk.utils.FileSortOrder;
import com.nextcloud.talk.utils.preferences.AppPreferences; import com.nextcloud.talk.utils.preferences.AppPreferences;
@ -46,7 +46,6 @@ import javax.inject.Inject;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.fragment.app.DialogFragment; import androidx.fragment.app.DialogFragment;
import autodagger.AutoInjector; import autodagger.AutoInjector;
import kotlin.jvm.JvmField;
/** /**
* Dialog to show and choose the sorting order for the file listing. * Dialog to show and choose the sorting order for the file listing.
@ -60,9 +59,11 @@ public class SortingOrderDialogFragment extends DialogFragment implements View.O
private static final String KEY_SORT_ORDER = "SORT_ORDER"; private static final String KEY_SORT_ORDER = "SORT_ORDER";
@Inject @Inject
@JvmField
AppPreferences appPreferences; AppPreferences appPreferences;
@Inject
ViewThemeUtils viewThemeUtils;
private SortingOrderFragmentBinding binding; private SortingOrderFragmentBinding binding;
private View dialogView; private View dialogView;
@ -119,7 +120,7 @@ public class SortingOrderDialogFragment extends DialogFragment implements View.O
* find all relevant UI elements and set their values. * find all relevant UI elements and set their values.
*/ */
private void setupDialogElements() { private void setupDialogElements() {
binding.cancel.setTextColor(getResources().getColor(R.color.colorPrimary)); viewThemeUtils.colorMaterialButtonText(binding.cancel);
taggedViews = new View[12]; taggedViews = new View[12];
taggedViews[0] = binding.sortByNameAscending; taggedViews[0] = binding.sortByNameAscending;
@ -154,18 +155,17 @@ public class SortingOrderDialogFragment extends DialogFragment implements View.O
* tints the icon reflecting the actual sorting choice in the apps primary color. * tints the icon reflecting the actual sorting choice in the apps primary color.
*/ */
private void setupActiveOrderSelection() { private void setupActiveOrderSelection() {
final int color = getResources().getColor(R.color.colorPrimary); Log.i("SortOrder", "currentSortOrderName=" + currentSortOrderName);
Log.i("SortOrder", "currentSortOrderName="+currentSortOrderName);
for (View view : taggedViews) { for (View view : taggedViews) {
Log.i("SortOrder", ((FileSortOrder) view.getTag()).getName()); Log.i("SortOrder", ((FileSortOrder) view.getTag()).getName());
if (!((FileSortOrder) view.getTag()).getName().equals(currentSortOrderName)) { if (!((FileSortOrder) view.getTag()).getName().equals(currentSortOrderName)) {
continue; continue;
} }
if (view instanceof MaterialButton) { if (view instanceof MaterialButton) {
((MaterialButton) view).setIconTintResource(R.color.colorPrimary); viewThemeUtils.colorMaterialButtonText((MaterialButton) view);
} }
if (view instanceof TextView) { if (view instanceof TextView) {
((TextView) view).setTextColor(color); viewThemeUtils.colorTextViewElement((TextView) view);
((TextView) view).setTypeface(Typeface.DEFAULT_BOLD); ((TextView) view).setTypeface(Typeface.DEFAULT_BOLD);
} }
} }

View File

@ -0,0 +1,53 @@
/*
* Nextcloud Talk application
*
* @author Álvaro Brey
* Copyright (C) 2022 Álvaro Brey
* Copyright (C) 2022 Nextcloud GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.ui.theme
import androidx.annotation.ColorInt
interface ServerTheme {
@get:ColorInt
val primaryColor: Int
/**
* Default element color
*/
@get:ColorInt
val colorElement: Int
/**
* Element color for bright backgrounds
*/
@get:ColorInt
val colorElementBright: Int
/**
* Element color for dark backgrounds
*/
@get:ColorInt
val colorElementDark: Int
/**
* Text color for elements
*/
@get:ColorInt
val colorText: Int
}

View File

@ -0,0 +1,48 @@
/*
* Nextcloud Talk application
*
* @author Álvaro Brey
* @author Andy Scherzinger
* Copyright (C) 2022 Álvaro Brey
* Copyright (C) 2022 Andy Scherzinger <info@andy-scherzinger.de>
* Copyright (C) 2022 Nextcloud GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.ui.theme
import com.nextcloud.talk.R
import com.nextcloud.talk.models.json.capabilities.ThemingCapability
import com.nextcloud.talk.utils.ui.ColorUtil
internal class ServerThemeImpl(themingCapability: ThemingCapability?, colorUtil: ColorUtil) :
ServerTheme {
override val primaryColor: Int
override val colorElement: Int
override val colorElementBright: Int
override val colorElementDark: Int
override val colorText: Int
init {
primaryColor = colorUtil.getNullSafeColorWithFallbackRes(themingCapability?.color, R.color.colorPrimary)
colorElement = colorUtil.getNullSafeColor(themingCapability?.colorElement, primaryColor)
colorElementBright = colorUtil.getNullSafeColor(themingCapability?.colorElementBright, primaryColor)
colorElementDark = colorUtil.getNullSafeColor(themingCapability?.colorElementDark, primaryColor)
colorText = colorUtil.getTextColor(themingCapability?.colorText, primaryColor)
}
}

View File

@ -0,0 +1,31 @@
/*
* Nextcloud Talk application
*
* @author Álvaro Brey
* Copyright (C) 2022 Álvaro Brey
* Copyright (C) 2022 Nextcloud GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.ui.theme
import com.nextcloud.talk.data.user.model.User
import com.nextcloud.talk.models.json.capabilities.Capabilities
interface ServerThemeProvider {
fun getServerThemeForUser(user: User?): ServerTheme
fun getServerThemeForCapabilities(capabilities: Capabilities?): ServerTheme
fun getServerThemeForCurrentUser(): ServerTheme
}

View File

@ -0,0 +1,65 @@
/*
* Nextcloud Talk application
*
* @author Álvaro Brey
* @author Andy Scherzinger
* Copyright (C) 2022 Álvaro Brey
* Copyright (C) 2022 Andy Scherzinger <info@andy-scherzinger.de>
* Copyright (C) 2022 Nextcloud GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.ui.theme
import com.nextcloud.talk.data.user.model.User
import com.nextcloud.talk.models.json.capabilities.Capabilities
import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew
import com.nextcloud.talk.utils.ui.ColorUtil
import java.util.concurrent.ConcurrentHashMap
import javax.inject.Inject
internal class ServerThemeProviderImpl @Inject constructor(
private val userProvider: CurrentUserProviderNew,
private val colorUtil: ColorUtil
) : ServerThemeProvider {
private val themeCache: ConcurrentHashMap<String, ServerTheme> = ConcurrentHashMap()
override fun getServerThemeForUser(user: User?): ServerTheme {
val url: String = if (user?.baseUrl != null) {
user.baseUrl!!
} else {
FALLBACK_URL
}
if (!themeCache.containsKey(url)) {
themeCache[url] = getServerThemeForCapabilities(user?.capabilities)
}
return themeCache[url]!!
}
override fun getServerThemeForCurrentUser(): ServerTheme {
return getServerThemeForUser(userProvider.currentUser.blockingGet())
}
override fun getServerThemeForCapabilities(capabilities: Capabilities?): ServerTheme {
return ServerThemeImpl(capabilities?.themingCapability, colorUtil)
}
companion object {
const val FALLBACK_URL = "NULL"
}
}

View File

@ -0,0 +1,44 @@
/*
* Nextcloud Talk application
*
* @author Álvaro Brey
* Copyright (C) 2022 Álvaro Brey
* Copyright (C) 2022 Nextcloud GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.ui.theme
import com.nextcloud.talk.dagger.modules.ContextModule
import com.nextcloud.talk.utils.database.user.UserModule
import dagger.Binds
import dagger.Module
import dagger.Provides
import dagger.Reusable
@Module(includes = [ContextModule::class, UserModule::class])
internal abstract class ThemeModule {
@Binds
@Reusable
abstract fun bindServerThemeProvider(provider: ServerThemeProviderImpl): ServerThemeProvider
companion object {
@Provides
fun provideCurrentServerTheme(themeProvider: ServerThemeProvider): ServerTheme {
return themeProvider.getServerThemeForCurrentUser()
}
}
}

View File

@ -0,0 +1,364 @@
/*
* Nextcloud Talk application
*
* @author Álvaro Brey
* Copyright (C) 2022 Álvaro Brey
* Copyright (C) 2022 Nextcloud GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.ui.theme
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.Color
import android.graphics.PorterDuff
import android.graphics.drawable.Drawable
import android.view.View
import android.widget.CheckBox
import android.widget.EditText
import android.widget.ImageView
import android.widget.ProgressBar
import android.widget.RadioButton
import android.widget.SeekBar
import android.widget.TextView
import androidx.annotation.ColorInt
import androidx.appcompat.content.res.AppCompatResources
import androidx.appcompat.widget.SwitchCompat
import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
import androidx.core.view.children
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.google.android.material.button.MaterialButton
import com.google.android.material.card.MaterialCardView
import com.google.android.material.chip.Chip
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.progressindicator.LinearProgressIndicator
import com.google.android.material.tabs.TabLayout
import com.google.android.material.textfield.TextInputLayout
import com.nextcloud.talk.R
import com.nextcloud.talk.utils.DrawableUtils
import com.nextcloud.talk.utils.ui.ColorUtil
import com.nextcloud.talk.utils.ui.PlatformThemeUtil.isDarkMode
import com.yarolegovich.mp.MaterialPreferenceCategory
import com.yarolegovich.mp.MaterialSwitchPreference
import javax.inject.Inject
@Suppress("TooManyFunctions")
class ViewThemeUtils @Inject constructor(private val theme: ServerTheme, private val colorUtil: ColorUtil) {
/**
* Color for painting elements
*/
fun getElementColor(context: Context): Int = when {
isDarkMode(context) -> theme.colorElementDark
else -> theme.colorElementBright
}
private fun withElementColor(view: View, block: (Int) -> Unit) {
block(getElementColor(view.context))
}
fun themeFAB(fab: FloatingActionButton) {
withElementColor(fab) { color ->
fab.backgroundTintList = ColorStateList.valueOf(color)
fab.imageTintList = ColorStateList.valueOf(theme.colorText)
}
}
fun themeHorizontalSeekBar(seekBar: SeekBar) {
withElementColor(seekBar) { color ->
themeHorizontalSeekBar(seekBar, color)
}
}
fun themeHorizontalSeekBar(seekBar: SeekBar, @ColorInt color: Int) {
themeHorizontalProgressBar(seekBar, color)
seekBar.thumb.setColorFilter(color, PorterDuff.Mode.SRC_IN)
}
fun themeHorizontalProgressBar(progressBar: ProgressBar?, @ColorInt color: Int) {
if (progressBar != null) {
progressBar.indeterminateDrawable.setColorFilter(color, PorterDuff.Mode.SRC_IN)
progressBar.progressDrawable.setColorFilter(color, PorterDuff.Mode.SRC_IN)
}
}
fun colorTextViewElement(textView: TextView) {
withElementColor(textView) { color ->
textView.setTextColor(color)
}
}
fun colorTextViewText(textView: TextView) {
textView.setTextColor(theme.colorText)
}
/**
* Colors the background as element color and the foreground as text color.
*/
fun colorImageViewButton(imageView: ImageView) {
withElementColor(imageView) { color ->
imageView.imageTintList = ColorStateList.valueOf(theme.colorText)
imageView.backgroundTintList = ColorStateList.valueOf(color)
}
}
/**
* Tints the image with element color
*/
fun colorImageView(imageView: ImageView) {
withElementColor(imageView) { color ->
imageView.imageTintList = ColorStateList.valueOf(color)
}
}
/**
* Tints the image with text color
*/
fun colorImageViewText(imageView: ImageView) {
imageView.imageTintList = ColorStateList.valueOf(theme.colorText)
}
fun colorMaterialButtonText(button: MaterialButton) {
withElementColor(button) { color ->
val disabledColor = ContextCompat.getColor(button.context, R.color.disabled_text)
val colorStateList = ColorStateList(
arrayOf(
intArrayOf(android.R.attr.state_enabled),
intArrayOf(-android.R.attr.state_enabled)
),
intArrayOf(color, disabledColor)
)
button.setTextColor(colorStateList)
button.iconTint = colorStateList
}
}
fun colorMaterialButtonBackground(button: MaterialButton) {
withElementColor(button) { color ->
button.setBackgroundColor(color)
val disabledColor = ContextCompat.getColor(button.context, R.color.disabled_text)
val colorStateList = ColorStateList(
arrayOf(
intArrayOf(android.R.attr.state_enabled),
intArrayOf(-android.R.attr.state_enabled)
),
intArrayOf(theme.colorText, disabledColor)
)
button.setTextColor(colorStateList)
button.iconTint = colorStateList
}
}
fun colorCardViewBackground(card: MaterialCardView) {
withElementColor(card) { color ->
card.setCardBackgroundColor(color)
}
}
// TODO split this util into classes depending on framework views vs library views
fun colorPreferenceCategory(category: MaterialPreferenceCategory) {
withElementColor(category) { color ->
category.setTitleColor(color)
}
}
fun colorSwitchPreference(preference: MaterialSwitchPreference) {
val children = preference.children
val switch = children.find { it is SwitchCompat }
if (switch != null) {
val switchCompat = (switch as SwitchCompat)
colorSwitchCompat(switchCompat)
}
}
fun colorSwitchCompat(switchCompat: SwitchCompat) {
withElementColor(switchCompat) { color ->
val context = switchCompat.context
val thumbUncheckedColor = ResourcesCompat.getColor(
context.resources,
R.color.switch_thumb_color_unchecked,
context.theme
)
val trackUncheckedColor = ResourcesCompat.getColor(
context.resources,
R.color.switch_track_color_unchecked,
context.theme
)
val trackColor =
Color.argb(SWITCHCOMPAT_TRACK_ALPHA, Color.red(color), Color.green(color), Color.blue(color))
switchCompat.thumbTintList = ColorStateList(
arrayOf(intArrayOf(android.R.attr.state_checked), intArrayOf()),
intArrayOf(color, thumbUncheckedColor)
)
switchCompat.trackTintList = ColorStateList(
arrayOf(intArrayOf(android.R.attr.state_checked), intArrayOf()),
intArrayOf(trackColor, trackUncheckedColor)
)
}
}
fun colorDrawable(context: Context, drawable: Drawable) {
val color = getElementColor(context)
drawable.setTint(color)
}
fun themeCheckbox(checkbox: CheckBox) {
withElementColor(checkbox) { color ->
checkbox.buttonTintList = ColorStateList(
arrayOf(
intArrayOf(-android.R.attr.state_checked),
intArrayOf(android.R.attr.state_checked)
),
intArrayOf(Color.GRAY, color)
)
}
}
fun themeRadioButton(radioButton: RadioButton) {
withElementColor(radioButton) { color ->
radioButton.buttonTintList = ColorStateList(
arrayOf(
intArrayOf(-android.R.attr.state_checked),
intArrayOf(android.R.attr.state_checked)
),
intArrayOf(Color.GRAY, color)
)
}
}
fun themeSwipeRefreshLayout(swipeRefreshLayout: SwipeRefreshLayout) {
withElementColor(swipeRefreshLayout) { color ->
swipeRefreshLayout.setColorSchemeColors(color)
swipeRefreshLayout.setProgressBackgroundColorSchemeResource(R.color.refresh_spinner_background)
}
}
fun colorProgressBar(progressIndicator: LinearProgressIndicator) {
withElementColor(progressIndicator) { color ->
progressIndicator.setIndicatorColor(progressColor(progressIndicator.context, color))
}
}
private fun progressColor(context: Context, color: Int): Int {
val lightness = when (isDarkMode(context)) {
true -> PROGRESS_LIGHTNESS_DARK_THEME
false -> PROGRESS_LIGHTNESS_LIGHT_THEME
}
return colorUtil.setLightness(color, lightness)
}
fun colorEditText(editText: EditText) {
withElementColor(editText) { color ->
editText.setTextColor(color)
// TODO check API-level compatibility
// editText.background.setColorFilter(color, PorterDuff.Mode.SRC_ATOP)
editText.backgroundTintList = ColorStateList(
arrayOf(
intArrayOf(-android.R.attr.state_focused),
intArrayOf(android.R.attr.state_focused)
),
intArrayOf(
Color.GRAY,
color
)
)
}
}
fun colorTextInputLayout(textInputLayout: TextInputLayout) {
withElementColor(textInputLayout) { color ->
val errorColor = Color.GRAY
val errorColorStateList = ColorStateList(
arrayOf(
intArrayOf(-android.R.attr.state_focused),
intArrayOf(android.R.attr.state_focused)
),
intArrayOf(
errorColor,
errorColor
)
)
val coloredColorStateList = ColorStateList(
arrayOf(
intArrayOf(-android.R.attr.state_focused),
intArrayOf(android.R.attr.state_focused)
),
intArrayOf(
Color.GRAY,
color
)
)
textInputLayout.setBoxStrokeColorStateList(coloredColorStateList)
textInputLayout.setErrorIconTintList(errorColorStateList)
textInputLayout.setErrorTextColor(errorColorStateList)
textInputLayout.boxStrokeErrorColor = errorColorStateList
textInputLayout.defaultHintTextColor = coloredColorStateList
}
}
fun colorTabLayout(tabLayout: TabLayout) {
withElementColor(tabLayout) { color ->
tabLayout.setSelectedTabIndicatorColor(color)
}
}
fun getPlaceholderImage(context: Context, mimetype: String?): Drawable? {
val drawableResourceId = DrawableUtils.getDrawableResourceIdForMimeType(mimetype)
val drawable = AppCompatResources.getDrawable(
context,
drawableResourceId
)
if (drawable != null && THEMEABLE_PLACEHOLDER_IDS.contains(drawableResourceId)) {
colorDrawable(context, drawable)
}
return drawable
}
fun colorChipBackground(chip: Chip) {
withElementColor(chip) { color ->
chip.chipBackgroundColor = ColorStateList.valueOf(color)
chip.setTextColor(theme.colorText)
}
}
fun colorChipOutlined(chip: Chip, strokeWidth: Float) {
withElementColor(chip) { color ->
chip.chipBackgroundColor = ColorStateList.valueOf(Color.TRANSPARENT)
chip.chipStrokeWidth = strokeWidth
chip.chipStrokeColor = ColorStateList.valueOf(color)
chip.setTextColor(color)
}
}
companion object {
private val THEMEABLE_PLACEHOLDER_IDS = listOf(
R.drawable.ic_mimetype_package_x_generic,
R.drawable.ic_mimetype_folder
)
private const val SWITCHCOMPAT_TRACK_ALPHA: Int = 77
private const val PROGRESS_LIGHTNESS_LIGHT_THEME: Float = 0.76f
private const val PROGRESS_LIGHTNESS_DARK_THEME: Float = 0.28f
}
}

View File

@ -28,23 +28,28 @@ import com.nextcloud.talk.data.user.model.User
import com.nextcloud.talk.models.ExternalSignalingServer import com.nextcloud.talk.models.ExternalSignalingServer
import com.nextcloud.talk.models.json.capabilities.Capabilities import com.nextcloud.talk.models.json.capabilities.Capabilities
import com.nextcloud.talk.models.json.push.PushConfigurationState import com.nextcloud.talk.models.json.push.PushConfigurationState
import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew
import io.reactivex.Maybe import io.reactivex.Maybe
import io.reactivex.Observable
import io.reactivex.Single import io.reactivex.Single
@Suppress("TooManyFunctions") @Suppress("TooManyFunctions")
class UserManager internal constructor(private val userRepository: UsersRepository) : CurrentUserProviderNew { class UserManager internal constructor(private val userRepository: UsersRepository) {
val users: Single<List<User>> val users: Single<List<User>>
get() = userRepository.getUsers() get() = userRepository.getUsers()
val usersScheduledForDeletion: Single<List<User>> val usersScheduledForDeletion: Single<List<User>>
get() = userRepository.getUsersScheduledForDeletion() get() = userRepository.getUsersScheduledForDeletion()
override val currentUser: Maybe<User> val currentUser: Maybe<User>
get() { get() {
return userRepository.getActiveUser() return userRepository.getActiveUser()
} }
val currentUserObservable: Observable<User>
get() {
return userRepository.getActiveUserObservable()
}
fun deleteUser(internalId: Long): Int { fun deleteUser(internalId: Long): Int {
return userRepository.deleteUser(userRepository.getUserWithId(internalId).blockingGet()) return userRepository.deleteUser(userRepository.getUserWithId(internalId).blockingGet())
} }

View File

@ -148,33 +148,23 @@ object DrawableUtils {
drawableMap["unknown"] = R.drawable.ic_mimetype_file drawableMap["unknown"] = R.drawable.ic_mimetype_file
drawableMap["application/pdf"] = R.drawable.ic_mimetype_application_pdf drawableMap["application/pdf"] = R.drawable.ic_mimetype_application_pdf
if (localMimetype.isNullOrEmpty()) { return if (localMimetype.isNullOrEmpty()) {
return drawableMap["unknown"]!! drawableMap["unknown"]!!
} } else if ("DIR" == localMimetype) {
if ("DIR" == localMimetype) {
localMimetype = FOLDER localMimetype = FOLDER
return drawableMap[localMimetype]!! drawableMap[localMimetype]!!
} } else if (drawableMap.containsKey(localMimetype)) {
drawableMap[localMimetype]!!
if (drawableMap.containsKey(localMimetype)) { } else if (localMimetype.startsWith(IMAGE_PREFIX)) {
return drawableMap[localMimetype]!! R.drawable.ic_mimetype_image
} } else if (localMimetype.startsWith(VIDEO_PREFIX)) {
R.drawable.ic_mimetype_video
if (localMimetype.startsWith(IMAGE_PREFIX)) { } else if (localMimetype.startsWith(TEXT_PREFIX)) {
return R.drawable.ic_mimetype_image R.drawable.ic_mimetype_text
} } else if (localMimetype.startsWith(AUDIO_PREFIX)) {
if (localMimetype.startsWith(VIDEO_PREFIX)) {
return R.drawable.ic_mimetype_video
}
if (localMimetype.startsWith(TEXT_PREFIX)) {
return R.drawable.ic_mimetype_text
}
return if (localMimetype.startsWith(AUDIO_PREFIX)) {
R.drawable.ic_mimetype_audio R.drawable.ic_mimetype_audio
} else drawableMap["unknown"]!! } else {
drawableMap["unknown"]!!
}
} }
} }

View File

@ -0,0 +1,49 @@
/*
* Nextcloud Talk application
*
* @author Álvaro Brey
* Copyright (C) 2022 Álvaro Brey
* Copyright (C) 2022 Nextcloud GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.utils.database.user
import com.nextcloud.talk.data.user.model.User
import com.nextcloud.talk.users.UserManager
import io.reactivex.Maybe
import io.reactivex.disposables.Disposable
import javax.inject.Inject
/**
* Listens to changes in the database and provides the current user without needing to query the database everytime.
*/
class CurrentUserProviderImpl @Inject constructor(private val userManager: UserManager) : CurrentUserProviderNew {
private var _currentUser: User? = null
private var currentUserObserver: Disposable? = null
override val currentUser: Maybe<User>
get() {
if (_currentUser == null) {
// immediately get a result synchronously
_currentUser = userManager.currentUser.blockingGet()
if (currentUserObserver == null) {
// start observable for auto-updates
currentUserObserver = userManager.currentUserObservable.subscribe { _currentUser = it }
}
}
return _currentUser?.let { Maybe.just(it) } ?: Maybe.empty()
}
}

View File

@ -30,7 +30,7 @@ import dagger.Provides
abstract class UserModule { abstract class UserModule {
@Binds @Binds
abstract fun bindCurrentUserProviderNew(userManager: UserManager): CurrentUserProviderNew abstract fun bindCurrentUserProviderNew(currentUserProviderImpl: CurrentUserProviderImpl): CurrentUserProviderNew
companion object { companion object {
@Provides @Provides

View File

@ -0,0 +1,81 @@
/*
* Nextcloud Talk application
*
* @author Álvaro Brey
* Copyright (C) 2022 Álvaro Brey
* Copyright (C) 2022 Nextcloud GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.utils.ui
import android.content.Context
import android.graphics.Color
import androidx.annotation.ColorInt
import androidx.annotation.ColorRes
import androidx.core.content.ContextCompat
import androidx.core.graphics.ColorUtils
import com.nextcloud.talk.R
import javax.inject.Inject
class ColorUtil @Inject constructor(private val context: Context) {
@ColorInt
fun getNullSafeColor(color: String?, @ColorInt fallbackColor: Int): Int {
return color.parseColorOrFallback { fallbackColor }
}
@ColorInt
fun getNullSafeColorWithFallbackRes(color: String?, @ColorRes fallbackColorRes: Int): Int {
return color.parseColorOrFallback { ContextCompat.getColor(context, fallbackColorRes) }
}
@ColorInt
fun getTextColor(colorText: String?, @ColorInt backgroundColor: Int): Int {
return colorText.parseColorOrFallback { getForegroundColorForBackgroundColor(backgroundColor) }
}
@ColorInt
fun getForegroundColorForBackgroundColor(@ColorInt color: Int): Int {
val hsl = FloatArray(HSL_SIZE)
ColorUtils.RGBToHSL(Color.red(color), Color.green(color), Color.blue(color), hsl)
return if (hsl[INDEX_LIGHTNESS] < LIGHTNESS_DARK_THRESHOLD) {
Color.WHITE
} else {
ContextCompat.getColor(context, R.color.grey_900)
}
}
fun setLightness(@ColorInt color: Int, lightness: Float): Int {
require(lightness in 0.0..1.0) { "Lightness must be between 0 and 1" }
val hsl = FloatArray(HSL_SIZE)
ColorUtils.RGBToHSL(Color.red(color), Color.green(color), Color.blue(color), hsl)
hsl[INDEX_LIGHTNESS] = lightness
return ColorUtils.HSLToColor(hsl)
}
@ColorInt
private fun String?.parseColorOrFallback(fallback: () -> Int): Int {
return this?.let { Color.parseColor(this) } ?: fallback()
}
companion object {
private const val HSL_SIZE: Int = 3
private const val INDEX_LIGHTNESS: Int = 2
private const val LIGHTNESS_DARK_THRESHOLD: Float = 0.6f
}
}

View File

@ -0,0 +1,33 @@
/*
* Nextcloud Talk application
*
* @author Álvaro Brey
* Copyright (C) 2022 Álvaro Brey
* Copyright (C) 2022 Nextcloud GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.utils.ui
import android.content.Context
import android.content.res.Configuration
object PlatformThemeUtil {
fun isDarkMode(context: Context): Boolean =
when (context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) {
Configuration.UI_MODE_NIGHT_YES -> true
else -> false
}
}

View File

@ -222,6 +222,7 @@
android:theme="@style/Button.Primary" android:theme="@style/Button.Primary"
android:tint="@android:color/white" android:tint="@android:color/white"
android:visibility="gone" android:visibility="gone"
tools:visibility="visible"
app:backgroundTint="@color/colorPrimary" app:backgroundTint="@color/colorPrimary"
app:cornerRadius="48dp" app:cornerRadius="48dp"
app:elevation="0dp" app:elevation="0dp"

View File

@ -23,6 +23,7 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:apc="http://schemas.android.com/apk/res-auto" xmlns:apc="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
tools:background="@color/white"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
@ -76,6 +77,7 @@
android:layout_width="@dimen/avatar_size_big" android:layout_width="@dimen/avatar_size_big"
android:layout_height="@dimen/avatar_size_big" android:layout_height="@dimen/avatar_size_big"
android:layout_centerHorizontal="true" android:layout_centerHorizontal="true"
tools:background="@color/hwSecurityRed"
apc:roundAsCircle="true" /> apc:roundAsCircle="true" />
</RelativeLayout> </RelativeLayout>

View File

@ -115,11 +115,11 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="bottom|end" android:layout_gravity="bottom|end"
android:layout_margin="16dp" android:layout_margin="16dp"
android:backgroundTint="@color/colorPrimary"
android:contentDescription="@string/nc_new_conversation" android:contentDescription="@string/nc_new_conversation"
app:borderWidth="0dp" app:borderWidth="0dp"
app:srcCompat="@drawable/ic_add_white_24px" app:srcCompat="@drawable/ic_add_white_24px"
app:tint="@color/white" /> app:tint="@color/white"
app:backgroundTint="@color/colorPrimary"/>
<com.nextcloud.ui.popupbubble.PopupBubble <com.nextcloud.ui.popupbubble.PopupBubble
android:id="@+id/newMentionPopupBubble" android:id="@+id/newMentionPopupBubble"

View File

@ -63,7 +63,7 @@
android:inputType="textUri" android:inputType="textUri"
android:singleLine="true" android:singleLine="true"
android:textAlignment="viewStart" android:textAlignment="viewStart"
android:textColor="@color/colorPrimary" /> android:textColor="@color/high_emphasis_text" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
@ -78,7 +78,7 @@
android:contentDescription="@string/nc_add_emojis" android:contentDescription="@string/nc_add_emojis"
android:src="@drawable/ic_insert_emoticon_black_24dp" android:src="@drawable/ic_insert_emoticon_black_24dp"
android:visibility="gone" android:visibility="gone"
app:tint="@color/emoji_icons" app:tint="@color/medium_emphasis_text"
tools:visibility="visible" /> tools:visibility="visible" />
</RelativeLayout> </RelativeLayout>

View File

@ -29,7 +29,8 @@
<RelativeLayout <RelativeLayout
android:id="@+id/avatarContainer" android:id="@+id/avatarContainer"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content"
android:paddingBottom="@dimen/standard_padding">
<com.facebook.drawee.view.SimpleDraweeView <com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/avatar_image" android:id="@+id/avatar_image"

View File

@ -27,6 +27,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:fresco="http://schemas.android.com/apk/res-auto" xmlns:fresco="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
tools:background="@color/white"
android:id="@+id/settings_screen" android:id="@+id/settings_screen"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
@ -43,6 +44,7 @@
android:id="@+id/message_text" android:id="@+id/message_text"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:text="This is a test message"
android:gravity="center" /> android:gravity="center" />
</com.yarolegovich.mp.MaterialPreferenceCategory> </com.yarolegovich.mp.MaterialPreferenceCategory>
@ -105,10 +107,10 @@
android:id="@+id/server_age_warning_text" android:id="@+id/server_age_warning_text"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_toEndOf="@id/server_age_warning_icon" android:layout_toEndOf="@id/server_age_warning_icon"
android:paddingStart="@dimen/standard_padding" android:paddingStart="@dimen/standard_padding"
android:paddingEnd="0dp" android:paddingEnd="0dp"
android:layout_centerHorizontal="true"
android:textAlignment="viewStart" android:textAlignment="viewStart"
android:textColor="@color/nc_darkRed" android:textColor="@color/nc_darkRed"
tools:text="@string/nc_settings_server_almost_eol" /> tools:text="@string/nc_settings_server_almost_eol" />
@ -142,6 +144,7 @@
<com.yarolegovich.mp.MaterialPreferenceCategory <com.yarolegovich.mp.MaterialPreferenceCategory
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:id="@+id/settings_appearance_category"
android:animateLayoutChanges="true" android:animateLayoutChanges="true"
apc:cardBackgroundColor="@color/bg_default" apc:cardBackgroundColor="@color/bg_default"
apc:cardElevation="0dp" apc:cardElevation="0dp"
@ -186,6 +189,7 @@
</com.yarolegovich.mp.MaterialPreferenceCategory> </com.yarolegovich.mp.MaterialPreferenceCategory>
<com.yarolegovich.mp.MaterialPreferenceCategory <com.yarolegovich.mp.MaterialPreferenceCategory
android:id="@+id/settings_privacy_category"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:animateLayoutChanges="true" android:animateLayoutChanges="true"
@ -249,6 +253,7 @@
</com.yarolegovich.mp.MaterialPreferenceCategory> </com.yarolegovich.mp.MaterialPreferenceCategory>
<com.yarolegovich.mp.MaterialPreferenceCategory <com.yarolegovich.mp.MaterialPreferenceCategory
android:id="@+id/settings_advanced_category"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:animateLayoutChanges="true" android:animateLayoutChanges="true"
@ -323,6 +328,7 @@
</com.yarolegovich.mp.MaterialPreferenceCategory> </com.yarolegovich.mp.MaterialPreferenceCategory>
<com.yarolegovich.mp.MaterialPreferenceCategory <com.yarolegovich.mp.MaterialPreferenceCategory
android:id="@+id/settings_about_category"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:animateLayoutChanges="true" android:animateLayoutChanges="true"

View File

@ -31,6 +31,7 @@
app:cardElevation="0dp"> app:cardElevation="0dp">
<RelativeLayout <RelativeLayout
tools:background="@color/white"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="horizontal" android:orientation="horizontal"

View File

@ -18,6 +18,7 @@
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
tools:background="@color/white"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">

View File

@ -35,6 +35,7 @@
android:paddingTop="@dimen/dialog_padding_top_bottom"> android:paddingTop="@dimen/dialog_padding_top_bottom">
<TextView <TextView
android:id="@+id/poll_question"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingStart="@dimen/dialog_padding" android:paddingStart="@dimen/dialog_padding"
@ -73,6 +74,7 @@
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
<TextView <TextView
android:id="@+id/poll_options"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingTop="@dimen/standard_padding" android:paddingTop="@dimen/standard_padding"
@ -102,6 +104,7 @@
app:icon="@drawable/ic_add_grey600_24px" /> app:icon="@drawable/ic_add_grey600_24px" />
<TextView <TextView
android:id="@+id/poll_settings"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="@dimen/standard_margin" android:layout_marginTop="@dimen/standard_margin"

View File

@ -77,8 +77,8 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@id/messageTime" android:layout_below="@id/messageTime"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
app:layout_alignSelf="center" android:contentDescription="@null"
android:contentDescription="@null" /> app:layout_alignSelf="center" />
<include <include
android:id="@+id/reactions" android:id="@+id/reactions"

View File

@ -63,8 +63,8 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textAlignment="viewStart" android:textAlignment="viewStart"
android:textStyle="bold"
android:textColor="@color/nc_outcoming_text_default" android:textColor="@color/nc_outcoming_text_default"
android:textStyle="bold"
tools:text="This is the poll title?" /> tools:text="This is the poll title?" />
</LinearLayout> </LinearLayout>
@ -83,8 +83,8 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@id/messageText" android:layout_below="@id/messageText"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
app:layout_alignSelf="center"
android:textColor="@color/nc_outcoming_text_default" android:textColor="@color/nc_outcoming_text_default"
app:layout_alignSelf="center"
tools:text="10:35" /> tools:text="10:35" />
<ImageView <ImageView
@ -93,9 +93,9 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@id/messageTime" android:layout_below="@id/messageTime"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
android:contentDescription="@null"
android:textColor="@color/nc_outcoming_text_default" android:textColor="@color/nc_outcoming_text_default"
app:layout_alignSelf="center" app:layout_alignSelf="center" />
android:contentDescription="@null" />
<include <include
android:id="@+id/reactions" android:id="@+id/reactions"

View File

@ -27,6 +27,7 @@
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<com.yarolegovich.mp.MaterialPreferenceCategory <com.yarolegovich.mp.MaterialPreferenceCategory
android:id="@+id/notification_settings_category"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:animateLayoutChanges="true" android:animateLayoutChanges="true"

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?><!-- <?xml version="1.0" encoding="utf-8"?><!--
Nextcloud Android client application Nextcloud Android client application
Copyright (C) 2018-2021 Andy Scherzinger Copyright (C) 2018-2022 Andy Scherzinger
Copyright (C) 2018 Nextcloud Copyright (C) 2018 Nextcloud
This program is free software; you can redistribute it and/or This program is free software; you can redistribute it and/or
@ -22,8 +22,10 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/user_info_detail_container" android:id="@+id/user_info_detail_container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="@dimen/iconized_single_line_item_layout_height" android:layout_height="wrap_content"
android:orientation="horizontal"> android:minHeight="@dimen/min_size_clickable_area"
android:orientation="horizontal"
android:paddingBottom="@dimen/standard_padding">
<ImageView <ImageView
android:id="@+id/icon" android:id="@+id/icon"
@ -36,34 +38,44 @@
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
tools:src="@drawable/ic_phone" /> tools:src="@drawable/ic_phone" />
<EditText <com.google.android.material.textfield.TextInputLayout
android:id="@+id/user_info_edit_text" android:id="@+id/user_info_input_layout"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="48dp" android:layout_height="wrap_content"
android:layout_marginStart="@dimen/standard_double_margin" android:layout_marginStart="@dimen/standard_margin"
android:layout_marginEnd="@dimen/standard_margin" android:minHeight="@dimen/min_size_clickable_area"
android:autofillHints="none" app:boxStrokeColor="@color/colorPrimary"
android:ellipsize="end" app:errorTextAppearance="@style/ErrorAppearance"
android:inputType="text" app:hintTextColor="@color/colorPrimary"
android:maxLines="1"
android:textSize="@dimen/two_line_primary_text_size"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/scope" app:layout_constraintEnd_toStartOf="@id/scope"
app:layout_constraintStart_toEndOf="@id/icon" app:layout_constraintStart_toEndOf="@id/icon"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent">
tools:ignore="LabelFor"
<com.nextcloud.talk.utils.EmojiTextInputEditText
android:id="@+id/user_info_edit_text_edit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:imeOptions="actionNext"
android:inputType="text"
android:singleLine="true"
android:textAlignment="viewStart"
tools:text="+49 123 456 789 12" /> tools:text="+49 123 456 789 12" />
</com.google.android.material.textfield.TextInputLayout>
<ImageView <ImageView
android:id="@+id/scope" android:id="@+id/scope"
android:layout_width="48dp" android:layout_width="@dimen/min_size_clickable_area"
android:layout_height="48dp" android:layout_height="@dimen/min_size_clickable_area"
android:padding="12dp" android:layout_marginStart="@dimen/standard_quarter_margin"
android:layout_marginEnd="4dp" android:layout_marginEnd="@dimen/standard_quarter_margin"
android:contentDescription="@string/scope_toggle" android:contentDescription="@string/scope_toggle"
android:padding="@dimen/scope_padding"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/user_info_edit_text" app:layout_constraintStart_toEndOf="@id/user_info_input_layout"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
tools:src="@drawable/ic_link" /> tools:src="@drawable/ic_link" />

View File

@ -76,4 +76,8 @@
<color name="dialog_background">#353535</color> <color name="dialog_background">#353535</color>
<color name="vote_dialog_background">#424242</color> <color name="vote_dialog_background">#424242</color>
<color name="switch_thumb_color_unchecked">#cbcbcb</color>
<color name="switch_track_color_unchecked">#5a5a5a</color>
</resources> </resources>

View File

@ -63,15 +63,13 @@
<color name="nc_darkGreen">#006400</color> <color name="nc_darkGreen">#006400</color>
<color name="controller_chat_separator">#E8E8E8</color> <color name="controller_chat_separator">#E8E8E8</color>
<color name="grey_600">#757575</color> <color name="grey_600">#757575</color>
<color name="grey_900">#212121</color>
<color name="nc_grey">#D5D5D5</color> <color name="nc_grey">#D5D5D5</color>
<color name="controller_call_incomingCallTextView">#E9FFFFFF</color> <color name="controller_call_incomingCallTextView">#E9FFFFFF</color>
<color name="grey950">#111111</color> <color name="grey950">#111111</color>
<color name="textColorMaxContrast">#767676</color> <color name="textColorMaxContrast">#767676</color>
<color name="colorBackgroundDarker">#DBDBDB</color> <color name="colorBackgroundDarker">#DBDBDB</color>
<!-- Emoji list in chat window -->
<color name="emoji_icons">#61000000</color>
<color name="fg_default">#666666</color> <color name="fg_default">#666666</color>
<color name="fg_inverse">#FFFFFF</color> <color name="fg_inverse">#FFFFFF</color>
@ -82,8 +80,6 @@
<color name="bg_message_list_incoming_bubble">#EFEFEF</color> <color name="bg_message_list_incoming_bubble">#EFEFEF</color>
<color name="bg_message_list_incoming_bubble_deleted">#66EFEFEF</color> <color name="bg_message_list_incoming_bubble_deleted">#66EFEFEF</color>
<color name="bg_message_list_outcoming_bubble">@color/colorPrimary</color>
<color name="bg_message_list_outcoming_bubble_deleted">#800082C9</color>
<color name="bg_bottom_sheet">#FFFFFF</color> <color name="bg_bottom_sheet">#FFFFFF</color>
<color name="bg_call_screen_dialog">#121212</color> <color name="bg_call_screen_dialog">#121212</color>
@ -111,4 +107,7 @@
<color name="dialog_background">#FFFFFF</color> <color name="dialog_background">#FFFFFF</color>
<color name="vote_dialog_background">#FFFFFF</color> <color name="vote_dialog_background">#FFFFFF</color>
<color name="switch_thumb_color_unchecked">#ececec</color>
<color name="switch_track_color_unchecked">#b2b2b2</color>
</resources> </resources>

View File

@ -72,6 +72,7 @@
<dimen name="activity_row_layout_height">48dp</dimen> <dimen name="activity_row_layout_height">48dp</dimen>
<dimen name="reaction_bottom_sheet_layout_size">40dp</dimen> <dimen name="reaction_bottom_sheet_layout_size">40dp</dimen>
<dimen name="standard_eighth_margin">2dp</dimen> <dimen name="standard_eighth_margin">2dp</dimen>
<dimen name="scope_padding">12dp</dimen>
<dimen name="default_checkbox_dialog_start_margin">18dp</dimen> <dimen name="default_checkbox_dialog_start_margin">18dp</dimen>