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 'MissingTranslation'
disable 'VectorPath'
disable 'UnusedQuantity'
}
javaCompileOptions {

View file

@ -41,8 +41,10 @@ import android.widget.Toast;
import com.google.common.util.concurrent.ListenableFuture;
import com.nextcloud.talk.R;
import com.nextcloud.talk.application.NextcloudTalkApplication;
import com.nextcloud.talk.databinding.ActivityTakePictureBinding;
import com.nextcloud.talk.models.TakePictureViewModel;
import com.nextcloud.talk.ui.theme.ViewThemeUtils;
import com.nextcloud.talk.utils.BitmapShrinker;
import com.nextcloud.talk.utils.FileUtils;
@ -52,6 +54,8 @@ import java.util.Date;
import java.util.Locale;
import java.util.concurrent.ExecutionException;
import javax.inject.Inject;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.OptIn;
@ -66,9 +70,11 @@ import androidx.camera.lifecycle.ProcessCameraProvider;
import androidx.core.content.ContextCompat;
import androidx.exifinterface.media.ExifInterface;
import androidx.lifecycle.ViewModelProvider;
import autodagger.AutoInjector;
import static com.nextcloud.talk.utils.Mimetype.IMAGE_JPEG;
@AutoInjector(NextcloudTalkApplication.class)
public class TakePhotoActivity extends AppCompatActivity {
private static final String TAG = TakePhotoActivity.class.getSimpleName();
@ -86,15 +92,22 @@ public class TakePhotoActivity extends AppCompatActivity {
private Camera camera;
@Inject
ViewThemeUtils viewThemeUtils;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this);
binding = ActivityTakePictureBinding.inflate(getLayoutInflater());
viewModel = new ViewModelProvider(this).get(TakePictureViewModel.class);
setContentView(binding.getRoot());
viewThemeUtils.themeFAB(binding.takePhoto);
viewThemeUtils.colorMaterialButtonBackground(binding.send);
cameraProviderFuture = ProcessCameraProvider.getInstance(this);
cameraProviderFuture.addListener(() -> {
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.databinding.RvItemContactBinding;
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.DisplayUtils;
@ -59,14 +60,17 @@ public class ContactItem extends AbstractFlexibleItem<ContactItem.ContactItemVie
private final Participant participant;
private final User user;
private GenericTextHeaderItem header;
private final ViewThemeUtils viewThemeUtils;
public boolean isOnline = true;
public ContactItem(Participant participant,
User user,
GenericTextHeaderItem genericTextHeaderItem) {
GenericTextHeaderItem genericTextHeaderItem,
ViewThemeUtils viewThemeUtils) {
this.participant = participant;
this.user = user;
this.header = genericTextHeaderItem;
this.viewThemeUtils = viewThemeUtils;
}
@Override
@ -108,6 +112,7 @@ public class ContactItem extends AbstractFlexibleItem<ContactItem.ContactItemVie
holder.binding.avatarDraweeView.setController(null);
if (participant.getSelected()) {
viewThemeUtils.colorImageView(holder.binding.checkedImageView);
holder.binding.checkedImageView.setVisibility(View.VISIBLE);
} else {
holder.binding.checkedImageView.setVisibility(View.GONE);

View file

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

View file

@ -27,6 +27,7 @@ import android.view.View;
import com.nextcloud.talk.R;
import com.nextcloud.talk.databinding.RvItemTitleHeaderBinding;
import com.nextcloud.talk.ui.theme.ViewThemeUtils;
import java.util.List;
@ -39,12 +40,14 @@ public class GenericTextHeaderItem extends AbstractHeaderItem<GenericTextHeaderI
private static final String TAG = "GenericTextHeaderItem";
private final String title;
private final ViewThemeUtils viewThemeUtils;
public GenericTextHeaderItem(String title) {
public GenericTextHeaderItem(String title, ViewThemeUtils viewThemeUtils) {
super();
setHidden(false);
setSelectable(false);
this.title = title;
this.viewThemeUtils = viewThemeUtils;
}
public String getModel() {
@ -71,6 +74,7 @@ public class GenericTextHeaderItem extends AbstractHeaderItem<GenericTextHeaderI
Log.d(TAG, "We have payloads, so ignoring!");
} else {
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.text.SpannableString
import android.view.View
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import com.nextcloud.talk.R
import com.nextcloud.talk.data.user.model.User
import com.nextcloud.talk.databinding.RvItemSearchMessageBinding
import com.nextcloud.talk.models.domain.SearchMessageEntry
import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.utils.DisplayUtils
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
@ -42,7 +42,8 @@ data class MessageResultItem constructor(
private val context: Context,
private val currentUser: User,
val messageEntry: SearchMessageEntry,
private val showHeader: Boolean = false
private val showHeader: Boolean = false,
private val viewThemeUtils: ViewThemeUtils
) :
AbstractFlexibleItem<MessageResultItem.ViewHolder>(),
IFilterable<String>,
@ -77,7 +78,7 @@ data class MessageResultItem constructor(
private fun bindMessageExcerpt(holder: ViewHolder) {
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)
holder.binding.messageExcerpt.text = highlightedSpan
}
@ -104,7 +105,7 @@ data class MessageResultItem constructor(
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 {
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 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 {
/**
* "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.databinding.ItemCustomIncomingVoiceMessageBinding
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.DisplayUtils
import com.nextcloud.talk.utils.preferences.AppPreferences
@ -66,6 +67,9 @@ class IncomingVoiceMessageViewHolder(incomingView: View, payload: Any) : Message
@Inject
var context: Context? = null
@Inject
lateinit var viewThemeUtils: ViewThemeUtils
@JvmField
@Inject
var appPreferences: AppPreferences? = null
@ -93,6 +97,7 @@ class IncomingVoiceMessageViewHolder(incomingView: View, payload: Any) : Message
updateDownloadState(message)
binding.seekbar.max = message.voiceMessageDuration
viewThemeUtils.themeHorizontalSeekBar(binding.seekbar)
if (message.isPlayingVoiceMessage) {
showPlayButton()

View file

@ -25,14 +25,13 @@ package com.nextcloud.talk.adapters.messages
import android.content.Context
import android.content.Intent
import android.graphics.PorterDuff
import android.net.Uri
import android.text.Spannable
import android.text.SpannableString
import android.util.TypedValue
import android.view.View
import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.ColorUtils
import androidx.core.view.ViewCompat
import autodagger.AutoInjector
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.ReadStatus
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.DisplayUtils.getMessageSelector
import com.nextcloud.talk.utils.DisplayUtils.searchAndReplaceWithMentionSpan
import com.nextcloud.talk.utils.TextMatchers
import com.stfalcon.chatkit.messages.MessageHolders.OutcomingTextMessageViewHolder
import java.util.HashMap
import javax.inject.Inject
import kotlin.math.roundToInt
@AutoInjector(NextcloudTalkApplication::class)
class MagicOutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessageViewHolder<ChatMessage>(itemView) {
private val binding: ItemCustomOutcomingTextMessageBinding = ItemCustomOutcomingTextMessageBinding.bind(itemView)
private val realView: View = itemView
@JvmField
@Inject
var context: Context? = null
lateinit var context: Context
@Inject
lateinit var viewThemeUtils: ViewThemeUtils
@Inject
lateinit var serverTheme: ServerTheme
lateinit var reactionsInterface: ReactionsInterface
@ -69,7 +75,7 @@ class MagicOutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessage
val messageParameters: HashMap<String?, HashMap<String?, String?>>? = message.messageParameters
var messageString: Spannable = SpannableString(message.text)
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
layoutParams.isWrapBefore = false
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.messageTime.layoutParams = layoutParams
binding.messageText.text = messageString
binding.messageText.setTextColor(serverTheme.colorText)
binding.messageText.setLinkTextColor(serverTheme.colorText)
// parent message handling
if (!message.isDeleted && message.parentMessage != null) {
@ -112,8 +120,8 @@ class MagicOutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessage
readStatusDrawableInt?.let { drawableInt ->
ResourcesCompat.getDrawable(context!!.resources, drawableInt, null)?.let {
it.setColorFilter(ContextCompat.getColor(context!!, R.color.white60), PorterDuff.Mode.SRC_ATOP)
binding.checkMark.setImageDrawable(it)
viewThemeUtils.colorImageViewText(binding.checkMark)
}
}
@ -148,20 +156,25 @@ class MagicOutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessage
binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName
?: context!!.getText(R.string.nc_nick_guest)
binding.messageQuote.quotedMessage.text = parentChatMessage.text
binding.messageQuote.quotedMessage.setTextColor(
ContextCompat.getColor(context!!, R.color.nc_outcoming_text_default)
)
binding.messageQuote.quotedMessageAuthor.setTextColor(ContextCompat.getColor(context!!, R.color.nc_grey))
binding.messageQuote.quotedMessage.setTextColor(serverTheme.colorText)
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) {
val resources = sharedApplication!!.resources
val elementColor = viewThemeUtils.getElementColor(binding.root.context)
val bgBubbleColor = if (message.isDeleted) {
ResourcesCompat.getColor(resources, R.color.bg_message_list_outcoming_bubble_deleted, null)
ColorUtils.setAlphaComponent(elementColor, HALF_ALPHA_INT)
} else {
ResourcesCompat.getColor(resources, R.color.bg_message_list_outcoming_bubble, null)
elementColor
}
if (message.isGrouped) {
val bubbleDrawable = getMessageSelector(
@ -221,5 +234,8 @@ class MagicOutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessage
companion object {
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.content.Context;
import android.content.Intent;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
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.databinding.ReactionsInsideMessageBinding;
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.DrawableUtils;
import com.nextcloud.talk.utils.FileViewerUtils;
@ -91,6 +93,9 @@ public abstract class MagicPreviewMessageViewHolder extends MessageHolders.Incom
@Inject
Context context;
@Inject
ServerTheme serverTheme;
@Inject
OkHttpClient okHttpClient;
@ -175,6 +180,13 @@ public abstract class MagicPreviewMessageViewHolder extends MessageHolders.Incom
String mimetype = message.getSelectedIndividualHashMap().get(KEY_MIMETYPE);
int drawableResourceId = DrawableUtils.INSTANCE.getDrawableResourceIdForMimeType(mimetype);
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);
} else {
fetchFileInformation("/" + message.getSelectedIndividualHashMap().get(KEY_PATH),

View file

@ -25,7 +25,6 @@ package com.nextcloud.talk.adapters.messages
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.graphics.PorterDuff
import android.net.Uri
import android.util.Log
import android.util.TypedValue
@ -35,6 +34,8 @@ import android.webkit.WebView
import android.webkit.WebViewClient
import android.widget.Toast
import androidx.appcompat.content.res.AppCompatResources
import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.ColorUtils
import androidx.core.view.ViewCompat
import autodagger.AutoInjector
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.models.json.chat.ChatMessage
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.DisplayUtils
import com.nextcloud.talk.utils.UriUtils
import com.stfalcon.chatkit.messages.MessageHolders
import java.net.URLEncoder
import javax.inject.Inject
import kotlin.math.roundToInt
@AutoInjector(NextcloudTalkApplication::class)
class OutcomingLocationMessageViewHolder(incomingView: View) : MessageHolders
@ -68,6 +72,12 @@ class OutcomingLocationMessageViewHolder(incomingView: View) : MessageHolders
@Inject
var context: Context? = null
@Inject
lateinit var viewThemeUtils: ViewThemeUtils
@Inject
lateinit var serverTheme: ServerTheme
lateinit var reactionsInterface: ReactionsInterface
@SuppressLint("SetTextI18n")
@ -76,7 +86,6 @@ class OutcomingLocationMessageViewHolder(incomingView: View) : MessageHolders
sharedApplication!!.componentApplication.inject(this)
realView.isSelected = false
binding.messageTime.setTextColor(context!!.resources.getColor(R.color.white60))
val layoutParams = binding.messageTime.layoutParams as FlexboxLayout.LayoutParams
layoutParams.isWrapBefore = false
@ -85,7 +94,11 @@ class OutcomingLocationMessageViewHolder(incomingView: View) : MessageHolders
colorizeMessageBubble(message)
binding.messageText.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize)
binding.messageTime.layoutParams = layoutParams
binding.messageTime.setTextColor(ColorUtils.setAlphaComponent(serverTheme.colorText, ALPHA_60_INT))
binding.messageText.text = message.text
binding.messageText.setTextColor(serverTheme.colorText)
binding.messageText.setLinkTextColor(serverTheme.colorText)
// parent message handling
setParentMessageDataOnMessageItem(message)
@ -104,8 +117,8 @@ class OutcomingLocationMessageViewHolder(incomingView: View) : MessageHolders
readStatusDrawableInt?.let { drawableInt ->
AppCompatResources.getDrawable(context!!, drawableInt)?.let {
it.setColorFilter(context?.resources!!.getColor(R.color.white60), PorterDuff.Mode.SRC_ATOP)
binding.checkMark.setImageDrawable(it)
viewThemeUtils.colorImageViewText(binding.checkMark)
}
}
@ -200,12 +213,12 @@ class OutcomingLocationMessageViewHolder(incomingView: View) : MessageHolders
binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName
?: context!!.getText(R.string.nc_nick_guest)
binding.messageQuote.quotedMessage.text = parentChatMessage.text
binding.messageQuote.quotedMessage.setTextColor(
context!!.resources.getColor(R.color.nc_outcoming_text_default)
binding.messageQuote.quotedMessage.setTextColor(serverTheme.colorText)
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
} else {
@ -215,15 +228,16 @@ class OutcomingLocationMessageViewHolder(incomingView: View) : MessageHolders
private fun colorizeMessageBubble(message: ChatMessage) {
val resources = sharedApplication!!.resources
val elementColor = viewThemeUtils.getElementColor(binding.root.context)
val bgBubbleColor = if (message.isDeleted) {
resources.getColor(R.color.bg_message_list_outcoming_bubble_deleted)
ColorUtils.setAlphaComponent(elementColor, HALF_ALPHA_INT)
} else {
resources.getColor(R.color.bg_message_list_outcoming_bubble)
elementColor
}
if (message.isGrouped) {
val bubbleDrawable = DisplayUtils.getMessageSelector(
bgBubbleColor,
resources.getColor(R.color.transparent),
ResourcesCompat.getColor(resources, R.color.transparent, null),
bgBubbleColor,
R.drawable.shape_grouped_outcoming_message
)
@ -231,7 +245,7 @@ class OutcomingLocationMessageViewHolder(incomingView: View) : MessageHolders
} else {
val bubbleDrawable = DisplayUtils.getMessageSelector(
bgBubbleColor,
resources.getColor(R.color.transparent),
ResourcesCompat.getColor(resources, R.color.transparent, null),
bgBubbleColor,
R.drawable.shape_outcoming_message
)
@ -261,5 +275,8 @@ class OutcomingLocationMessageViewHolder(incomingView: View) : MessageHolders
companion object {
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.content.Context
import android.graphics.PorterDuff
import android.content.res.ColorStateList
import android.view.View
import androidx.appcompat.content.res.AppCompatResources
import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.ColorUtils
import androidx.core.view.ViewCompat
import autodagger.AutoInjector
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.ReadStatus
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.DisplayUtils
import com.nextcloud.talk.utils.preferences.AppPreferences
import com.stfalcon.chatkit.messages.MessageHolders
import javax.inject.Inject
import kotlin.math.roundToInt
@AutoInjector(NextcloudTalkApplication::class)
class OutcomingPollMessageViewHolder(outcomingView: View, payload: Any) : MessageHolders
@ -54,6 +59,12 @@ class OutcomingPollMessageViewHolder(outcomingView: View, payload: Any) : Messag
@Inject
lateinit var context: Context
@Inject
lateinit var viewThemeUtils: ViewThemeUtils
@Inject
lateinit var serverTheme: ServerTheme
@Inject
lateinit var appPreferences: AppPreferences
@ -73,7 +84,12 @@ class OutcomingPollMessageViewHolder(outcomingView: View, payload: Any) : Messag
colorizeMessageBubble(message)
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
setParentMessageDataOnMessageItem(message)
@ -92,8 +108,8 @@ class OutcomingPollMessageViewHolder(outcomingView: View, payload: Any) : Messag
readStatusDrawableInt?.let { drawableInt ->
AppCompatResources.getDrawable(context, drawableInt)?.let {
it.setColorFilter(context.resources!!.getColor(R.color.white60), PorterDuff.Mode.SRC_ATOP)
binding.checkMark.setImageDrawable(it)
viewThemeUtils.colorImageViewText(binding.checkMark)
}
}
@ -126,6 +142,9 @@ class OutcomingPollMessageViewHolder(outcomingView: View, payload: Any) : Messag
}
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
val roomToken = (payload as? MessagePayload)!!.roomToken
@ -165,12 +184,12 @@ class OutcomingPollMessageViewHolder(outcomingView: View, payload: Any) : Messag
binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName
?: context.getText(R.string.nc_nick_guest)
binding.messageQuote.quotedMessage.text = parentChatMessage.text
binding.messageQuote.quotedMessage.setTextColor(
context.resources.getColor(R.color.nc_outcoming_text_default)
binding.messageQuote.quotedMessage.setTextColor(serverTheme.colorText)
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
} else {
@ -180,15 +199,16 @@ class OutcomingPollMessageViewHolder(outcomingView: View, payload: Any) : Messag
private fun colorizeMessageBubble(message: ChatMessage) {
val resources = sharedApplication!!.resources
val elementColor = viewThemeUtils.getElementColor(binding.root.context)
val bgBubbleColor = if (message.isDeleted) {
resources.getColor(R.color.bg_message_list_outcoming_bubble_deleted)
ColorUtils.setAlphaComponent(elementColor, HALF_ALPHA_INT)
} else {
resources.getColor(R.color.bg_message_list_outcoming_bubble)
elementColor
}
if (message.isGrouped) {
val bubbleDrawable = DisplayUtils.getMessageSelector(
bgBubbleColor,
resources.getColor(R.color.transparent),
ResourcesCompat.getColor(resources, R.color.transparent, null),
bgBubbleColor,
R.drawable.shape_grouped_outcoming_message
)
@ -196,7 +216,7 @@ class OutcomingPollMessageViewHolder(outcomingView: View, payload: Any) : Messag
} else {
val bubbleDrawable = DisplayUtils.getMessageSelector(
bgBubbleColor,
resources.getColor(R.color.transparent),
ResourcesCompat.getColor(resources, R.color.transparent, null),
bgBubbleColor,
R.drawable.shape_outcoming_message
)
@ -210,5 +230,8 @@ class OutcomingPollMessageViewHolder(outcomingView: View, payload: Any) : Messag
companion object {
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 androidx.appcompat.content.res.AppCompatResources
import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.ColorUtils
import androidx.core.view.ViewCompat
import androidx.work.WorkInfo
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.models.json.chat.ChatMessage
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.DisplayUtils
import com.nextcloud.talk.utils.preferences.AppPreferences
import com.stfalcon.chatkit.messages.MessageHolders
import java.util.concurrent.ExecutionException
import javax.inject.Inject
import kotlin.math.roundToInt
@AutoInjector(NextcloudTalkApplication::class)
class OutcomingVoiceMessageViewHolder(outcomingView: View) : MessageHolders
@ -60,6 +65,12 @@ class OutcomingVoiceMessageViewHolder(outcomingView: View) : MessageHolders
@Inject
var context: Context? = null
@Inject
lateinit var viewThemeUtils: ViewThemeUtils
@Inject
lateinit var serverTheme: ServerTheme
@JvmField
@Inject
var appPreferences: AppPreferences? = null
@ -80,13 +91,19 @@ class OutcomingVoiceMessageViewHolder(outcomingView: View) : MessageHolders
colorizeMessageBubble(message)
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
setParentMessageDataOnMessageItem(message)
updateDownloadState(message)
binding.seekbar.max = message.voiceMessageDuration
viewThemeUtils.themeHorizontalSeekBar(binding.seekbar, serverTheme.colorText)
handleIsPlayingVoiceMessageState(message)
@ -124,8 +141,8 @@ class OutcomingVoiceMessageViewHolder(outcomingView: View) : MessageHolders
readStatusDrawableInt?.let { drawableInt ->
AppCompatResources.getDrawable(context!!, drawableInt)?.let {
it.setColorFilter(context?.resources!!.getColor(R.color.white60), PorterDuff.Mode.SRC_ATOP)
binding.checkMark.setImageDrawable(it)
viewThemeUtils.colorImageViewText(binding.checkMark)
}
}
@ -148,6 +165,7 @@ class OutcomingVoiceMessageViewHolder(outcomingView: View) : MessageHolders
context!!,
R.drawable.ic_baseline_play_arrow_voice_message_24
)
binding.playPauseBtn.icon.setColorFilter(serverTheme.colorText, PorterDuff.Mode.SRC_ATOP)
binding.seekbar.progress = SEEKBAR_START
message.resetVoiceMessage = false
}
@ -168,6 +186,7 @@ class OutcomingVoiceMessageViewHolder(outcomingView: View) : MessageHolders
context!!,
R.drawable.ic_baseline_pause_voice_message_24
)
binding.playPauseBtn.icon.setColorFilter(serverTheme.colorText, PorterDuff.Mode.SRC_ATOP)
binding.seekbar.progress = message.voiceMessagePlayedSeconds
} else {
binding.playPauseBtn.visibility = View.VISIBLE
@ -175,6 +194,7 @@ class OutcomingVoiceMessageViewHolder(outcomingView: View) : MessageHolders
context!!,
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
?: context!!.getText(R.string.nc_nick_guest)
binding.messageQuote.quotedMessage.text = parentChatMessage.text
binding.messageQuote.quotedMessage.setTextColor(
context!!.resources.getColor(R.color.nc_outcoming_text_default)
binding.messageQuote.quotedMessage.setTextColor(serverTheme.colorText)
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
} else {
@ -265,15 +288,16 @@ class OutcomingVoiceMessageViewHolder(outcomingView: View) : MessageHolders
private fun colorizeMessageBubble(message: ChatMessage) {
val resources = sharedApplication!!.resources
val elementColor = viewThemeUtils.getElementColor(binding.root.context)
val bgBubbleColor = if (message.isDeleted) {
resources.getColor(R.color.bg_message_list_outcoming_bubble_deleted)
ColorUtils.setAlphaComponent(elementColor, HALF_ALPHA_INT)
} else {
resources.getColor(R.color.bg_message_list_outcoming_bubble)
elementColor
}
if (message.isGrouped) {
val bubbleDrawable = DisplayUtils.getMessageSelector(
bgBubbleColor,
resources.getColor(R.color.transparent),
ResourcesCompat.getColor(resources, R.color.transparent, null),
bgBubbleColor,
R.drawable.shape_grouped_outcoming_message
)
@ -281,7 +305,7 @@ class OutcomingVoiceMessageViewHolder(outcomingView: View) : MessageHolders
} else {
val bubbleDrawable = DisplayUtils.getMessageSelector(
bgBubbleColor,
resources.getColor(R.color.transparent),
ResourcesCompat.getColor(resources, R.color.transparent, null),
bgBubbleColor,
R.drawable.shape_outcoming_message
)
@ -300,5 +324,8 @@ class OutcomingVoiceMessageViewHolder(outcomingView: View) : MessageHolders
companion object {
private const val TAG = "VoiceOutMessageView"
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.CapabilitiesWorker
import com.nextcloud.talk.jobs.SignalingSettingsWorker
import com.nextcloud.talk.ui.theme.ThemeModule
import com.nextcloud.talk.utils.ClosedInterfaceImpl
import com.nextcloud.talk.utils.DeviceUtils
import com.nextcloud.talk.utils.DisplayUtils
@ -96,7 +97,8 @@ import javax.inject.Singleton
ArbitraryStorageModule::class,
ViewModelModule::class,
RepositoryModule::class,
UtilsModule::class
UtilsModule::class,
ThemeModule::class
]
)
@Singleton
@ -120,6 +122,7 @@ class NextcloudTalkApplication : MultiDexApplication(), LifecycleObserver {
override fun preKey(database: SQLiteDatabase) {
// unused atm
}
override fun postKey(database: SQLiteDatabase) {
Log.i("TalkApplication", "DB cipher_migrate START")
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.recyclerview.MessageSwipeActions
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.AttendeePermissionsUtil
import com.nextcloud.talk.utils.ConductorRemapping
@ -235,6 +237,12 @@ class ChatController(args: Bundle) :
@Inject
lateinit var permissionUtil: PlatformPermissionUtil
@Inject
lateinit var viewThemeUtils: ViewThemeUtils
@Inject
lateinit var serverTheme: ServerTheme
val disposables = DisposableSet()
var roomToken: String? = null
@ -872,6 +880,8 @@ class ChatController(args: Bundle) :
.nc_description_send_message_button
)
viewThemeUtils.colorImageView(binding.messageInputView.button)
if (currentConversation != null && currentConversation?.roomId != null) {
loadAvatarForStatusBar()
setTitle()
@ -2675,7 +2685,8 @@ class ChatController(args: Bundle) :
chatMessage,
conversationUser,
hasChatPermission,
ncApi!!
ncApi!!,
serverTheme
).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.ui.dialog.ContactsBottomDialog
import com.nextcloud.talk.users.UserManager
import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.ConductorRemapping
import com.nextcloud.talk.utils.bundle.BundleKeys
@ -103,6 +104,9 @@ class ContactsController(args: Bundle) :
@Inject
lateinit var ncApi: NcApi
@Inject
lateinit var viewThemeUtils: ViewThemeUtils
private var credentials: String? = null
private var currentUser: User? = null
private var contactsQueryDisposable: Disposable? = null
@ -492,13 +496,14 @@ class ContactsController(args: Bundle) :
val headerTitle = getHeaderTitle(participant)
var genericTextHeaderItem: GenericTextHeaderItem
if (!userHeaderItems.containsKey(headerTitle)) {
genericTextHeaderItem = GenericTextHeaderItem(headerTitle)
genericTextHeaderItem = GenericTextHeaderItem(headerTitle, viewThemeUtils)
userHeaderItems.put(headerTitle, genericTextHeaderItem)
}
val newContactItem = ContactItem(
participant,
currentUser,
userHeaderItems[headerTitle]
userHeaderItems[headerTitle],
viewThemeUtils
)
if (!contactItems!!.contains(newContactItem)) {
newUserItemList.add(newContactItem)
@ -618,21 +623,16 @@ class ContactsController(args: Bundle) :
binding.controllerGenericRv.recyclerView.setHasFixedSize(true)
binding.controllerGenericRv.recyclerView.adapter = adapter
binding.controllerGenericRv.swipeRefreshLayout.setOnRefreshListener { fetchData() }
binding.controllerGenericRv.swipeRefreshLayout.setColorSchemeResources(R.color.colorPrimary)
binding.controllerGenericRv.swipeRefreshLayout
.setProgressBackgroundColorSchemeResource(R.color.refresh_spinner_background)
viewThemeUtils.themeSwipeRefreshLayout(binding.controllerGenericRv.swipeRefreshLayout)
binding.joinConversationViaLink.joinConversationViaLinkImageView
.background
.setColorFilter(
ResourcesCompat.getColor(resources!!, R.color.colorBackgroundDarker, null),
PorterDuff.Mode.SRC_IN
)
binding.conversationPrivacyToggle.publicCallLink
.background
.setColorFilter(
ResourcesCompat.getColor(resources!!, R.color.colorPrimary, null),
PorterDuff.Mode.SRC_IN
)
viewThemeUtils.colorImageViewButton(binding.conversationPrivacyToggle.publicCallLink)
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.ParticipantsOverall
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.DateConstants
import com.nextcloud.talk.utils.DateUtils
@ -112,6 +113,9 @@ class ConversationInfoController(args: Bundle) :
@Inject
lateinit var eventBus: EventBus
@Inject
lateinit var viewThemeUtils: ViewThemeUtils
private val conversationToken: String?
private val conversationUser: User?
private val hasAvatarSpacing: Boolean
@ -181,6 +185,34 @@ class ConversationInfoController(args: Bundle) :
}
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() {
@ -299,7 +331,7 @@ class ConversationInfoController(args: Bundle) :
val apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1))
ncApi?.setLobbyForConversation(
ncApi.setLobbyForConversation(
ApiUtils.getCredentials(conversationUser!!.username, conversationUser.token),
ApiUtils.getUrlForRoomWebinaryLobby(apiVersion, conversationUser.baseUrl, conversation!!.token),
state,
@ -343,7 +375,7 @@ class ConversationInfoController(args: Bundle) :
override fun onDetach(view: View) {
super.onDetach(view)
eventBus?.unregister(this)
eventBus.unregister(this)
}
private fun showDeleteConversationDialog(savedInstanceState: Bundle?) {
@ -352,11 +384,11 @@ class ConversationInfoController(args: Bundle) :
.setTopColorRes(R.color.nc_darkRed)
.setIcon(
DisplayUtils.getTintedDrawable(
context!!.resources,
context.resources,
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)
.setMessage(R.string.nc_delete_conversation_more)
.setPositiveButton(R.string.nc_delete) { deleteConversation() }
@ -409,7 +441,7 @@ class ConversationInfoController(args: Bundle) :
if (participant.sessionId != null) {
userItem.isOnline = !participant.sessionId.equals("0")
} else {
userItem.isOnline = !participant.sessionIds!!.isEmpty()
userItem.isOnline = !participant.sessionIds.isEmpty()
}
if (participant.calculatedActorType == USERS &&
@ -453,7 +485,7 @@ class ConversationInfoController(args: Bundle) :
val fieldMap = HashMap<String, Boolean>()
fieldMap["includeStatus"] = true
ncApi?.getPeersForCall(
ncApi.getPeersForCall(
credentials,
ApiUtils.getUrlForParticipants(
apiVersion,
@ -504,7 +536,7 @@ class ConversationInfoController(args: Bundle) :
bundle.putStringArrayList(BundleKeys.KEY_EXISTING_PARTICIPANTS, existingParticipantsId)
bundle.putString(BundleKeys.KEY_TOKEN, conversation!!.token)
getRouter().pushController(
router.pushController(
(
RouterTransaction.with(
ContactsController(bundle)
@ -537,11 +569,11 @@ class ConversationInfoController(args: Bundle) :
.setTopColorRes(R.color.nc_darkRed)
.setIcon(
DisplayUtils.getTintedDrawable(
context!!.resources,
context.resources,
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)
.setMessage(R.string.nc_clear_history_warning)
.setPositiveButton(R.string.nc_delete_all) { clearHistory() }
@ -555,7 +587,7 @@ class ConversationInfoController(args: Bundle) :
private fun clearHistory() {
val apiVersion = ApiUtils.getChatApiVersion(conversationUser, intArrayOf(1))
ncApi?.clearChatHistory(
ncApi.clearChatHistory(
credentials,
ApiUtils.getUrlForChat(apiVersion, conversationUser!!.baseUrl, conversationToken)
)
@ -567,7 +599,7 @@ class ConversationInfoController(args: Bundle) :
}
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()
}
@ -606,7 +638,7 @@ class ConversationInfoController(args: Bundle) :
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())
?.observeOn(AndroidSchedulers.mainThread())
?.subscribe(object : Observer<RoomOverall> {
@ -765,8 +797,8 @@ class ConversationInfoController(args: Bundle) :
)
Conversation.ConversationType.ROOM_SYSTEM -> {
val layers = arrayOfNulls<Drawable>(2)
layers[0] = ContextCompat.getDrawable(context!!, R.drawable.ic_launcher_background)
layers[1] = ContextCompat.getDrawable(context!!, R.drawable.ic_launcher_foreground)
layers[0] = ContextCompat.getDrawable(context, R.drawable.ic_launcher_background)
layers[1] = ContextCompat.getDrawable(context, R.drawable.ic_launcher_foreground)
val layerDrawable = LayerDrawable(layers)
binding.avatarImage.hierarchy.setPlaceholderImage(DisplayUtils.getRoundedDrawable(layerDrawable))
}
@ -800,7 +832,7 @@ class ConversationInfoController(args: Bundle) :
if (participant.type == Participant.ParticipantType.MODERATOR ||
participant.type == Participant.ParticipantType.GUEST_MODERATOR
) {
ncApi?.demoteAttendeeFromModerator(
ncApi.demoteAttendeeFromModerator(
credentials,
ApiUtils.getUrlForRoomModerators(
apiVersion,
@ -815,7 +847,7 @@ class ConversationInfoController(args: Bundle) :
} else if (participant.type == Participant.ParticipantType.USER ||
participant.type == Participant.ParticipantType.GUEST
) {
ncApi?.promoteAttendeeToModerator(
ncApi.promoteAttendeeToModerator(
credentials,
ApiUtils.getUrlForRoomModerators(
apiVersion,
@ -851,7 +883,7 @@ class ConversationInfoController(args: Bundle) :
}
if (participant.type == Participant.ParticipantType.MODERATOR) {
ncApi?.demoteModeratorToUser(
ncApi.demoteModeratorToUser(
credentials,
ApiUtils.getUrlForRoomModerators(
apiVersion,
@ -864,7 +896,7 @@ class ConversationInfoController(args: Bundle) :
?.observeOn(AndroidSchedulers.mainThread())
?.subscribe(subscriber)
} else if (participant.type == Participant.ParticipantType.USER) {
ncApi?.promoteUserToModerator(
ncApi.promoteUserToModerator(
credentials,
ApiUtils.getUrlForRoomModerators(
apiVersion,
@ -881,7 +913,7 @@ class ConversationInfoController(args: Bundle) :
fun removeAttendeeFromConversation(apiVersion: Int, participant: Participant) {
if (apiVersion >= ApiUtils.APIv4) {
ncApi?.removeAttendeeFromConversation(
ncApi.removeAttendeeFromConversation(
credentials,
ApiUtils.getUrlForAttendees(
apiVersion,
@ -914,7 +946,7 @@ class ConversationInfoController(args: Bundle) :
if (participant.type == Participant.ParticipantType.GUEST ||
participant.type == Participant.ParticipantType.USER_FOLLOWING_LINK
) {
ncApi?.removeParticipantFromConversation(
ncApi.removeParticipantFromConversation(
credentials,
ApiUtils.getUrlForRemovingParticipantFromConversation(
conversationUser!!.baseUrl,
@ -944,7 +976,7 @@ class ConversationInfoController(args: Bundle) :
}
})
} else {
ncApi?.removeParticipantFromConversation(
ncApi.removeParticipantFromConversation(
credentials,
ApiUtils.getUrlForRemovingParticipantFromConversation(
conversationUser!!.baseUrl,
@ -987,12 +1019,12 @@ class ConversationInfoController(args: Bundle) :
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) {
val items = mutableListOf(
BasicListItemWithImage(
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 {
@ -1018,7 +1050,7 @@ class ConversationInfoController(args: Bundle) :
val items = mutableListOf(
BasicListItemWithImage(
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 {
@ -1038,7 +1070,7 @@ class ConversationInfoController(args: Bundle) :
val items = mutableListOf(
BasicListItemWithImage(
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 {
@ -1057,19 +1089,19 @@ class ConversationInfoController(args: Bundle) :
val items = mutableListOf(
BasicListItemWithImage(
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(
R.drawable.ic_pencil_grey600_24dp,
context!!.getString(R.string.nc_promote)
context.getString(R.string.nc_promote)
),
BasicListItemWithImage(
R.drawable.ic_pencil_grey600_24dp,
context!!.getString(R.string.nc_demote)
context.getString(R.string.nc_demote)
),
BasicListItemWithImage(
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 left.model.displayName!!.toLowerCase(Locale.ROOT).compareTo(
right.model.displayName!!.toLowerCase(Locale.ROOT)
return left.model.displayName!!.lowercase(Locale.ROOT).compareTo(
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.ui.dialog.ChooseAccountDialogFragment;
import com.nextcloud.talk.ui.dialog.ConversationsListBottomDialog;
import com.nextcloud.talk.ui.theme.ViewThemeUtils;
import com.nextcloud.talk.users.UserManager;
import com.nextcloud.talk.utils.ApiUtils;
import com.nextcloud.talk.utils.AttendeePermissionsUtil;
@ -181,6 +182,9 @@ public class ConversationsListController extends BaseController implements Flexi
@Inject
UnifiedSearchRepository unifiedSearchRepository;
@Inject
ViewThemeUtils viewThemeUtils;
@BindView(R.id.recycler_view)
RecyclerView recyclerView;
@ -618,7 +622,7 @@ public class ConversationsListController extends BaseController implements Flexi
GenericTextHeaderItem genericTextHeaderItem;
if (!callHeaderItems.containsKey(headerTitle)) {
genericTextHeaderItem = new GenericTextHeaderItem(headerTitle);
genericTextHeaderItem = new GenericTextHeaderItem(headerTitle, viewThemeUtils);
callHeaderItems.put(headerTitle, genericTextHeaderItem);
}
@ -627,7 +631,8 @@ public class ConversationsListController extends BaseController implements Flexi
conversation,
currentUser,
getActivity(),
userStatuses.get(conversation.getName()));
userStatuses.get(conversation.getName()),
viewThemeUtils);
conversationItems.add(conversationItem);
ConversationItem conversationItemWithHeader = new ConversationItem(
@ -635,7 +640,8 @@ public class ConversationsListController extends BaseController implements Flexi
currentUser,
getActivity(),
callHeaderItems.get(headerTitle),
userStatuses.get(conversation.getName()));
userStatuses.get(conversation.getName()),
viewThemeUtils);
conversationItemsWithHeader.add(conversationItemWithHeader);
}
}
@ -699,7 +705,7 @@ public class ConversationsListController extends BaseController implements Flexi
GenericTextHeaderItem genericTextHeaderItem;
if (!callHeaderItems.containsKey(headerTitle)) {
genericTextHeaderItem = new GenericTextHeaderItem(headerTitle);
genericTextHeaderItem = new GenericTextHeaderItem(headerTitle, viewThemeUtils);
callHeaderItems.put(headerTitle, genericTextHeaderItem);
}
@ -708,7 +714,8 @@ public class ConversationsListController extends BaseController implements Flexi
currentUser,
getActivity(),
callHeaderItems.get(headerTitle),
userStatuses.get(conversation.getName()));
userStatuses.get(conversation.getName()),
viewThemeUtils);
openConversationItems.add(conversationItem);
}
@ -776,8 +783,7 @@ public class ConversationsListController extends BaseController implements Flexi
});
swipeRefreshLayout.setOnRefreshListener(() -> fetchData());
swipeRefreshLayout.setColorSchemeResources(R.color.colorPrimary);
swipeRefreshLayout.setProgressBackgroundColorSchemeResource(R.color.refresh_spinner_background);
viewThemeUtils.themeSwipeRefreshLayout(swipeRefreshLayout);
emptyLayoutView.setOnClickListener(v -> showNewConversationsScreen());
floatingActionButton.setOnClickListener(v -> {
@ -785,6 +791,8 @@ public class ConversationsListController extends BaseController implements Flexi
showNewConversationsScreen();
});
viewThemeUtils.themeFAB(floatingActionButton);
if (getActivity() != null && getActivity() instanceof MainActivity) {
MainActivity activity = (MainActivity) getActivity();
@ -1409,7 +1417,7 @@ public class ConversationsListController extends BaseController implements Flexi
List<AbstractFlexibleItem> adapterItems = new ArrayList<>(entries.size() + 1);
for (int i = 0; i < entries.size(); i++) {
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()) {
adapterItems.add(LoadMoreResultsItem.INSTANCE);

View file

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

View file

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

View file

@ -32,6 +32,7 @@ import androidx.room.Transaction
import androidx.room.Update
import com.nextcloud.talk.data.user.model.UserEntity
import io.reactivex.Maybe
import io.reactivex.Observable
import io.reactivex.Single
@Dao
@ -41,6 +42,10 @@ abstract class UsersDao {
@Query("SELECT * FROM User where current = 1")
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")
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 io.reactivex.Maybe
import io.reactivex.Observable
import io.reactivex.Single
@Suppress("TooManyFunctions")
interface UsersRepository {
fun getActiveUser(): Maybe<User>
fun getActiveUserObservable(): Observable<User>
fun getUsers(): Single<List<User>>
fun getUserWithId(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 io.reactivex.Maybe
import io.reactivex.Observable
import io.reactivex.Single
@Suppress("TooManyFunctions")
@ -33,6 +34,10 @@ class UsersRepositoryImpl(private val usersDao: UsersDao) : UsersRepository {
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>> {
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.data.user.model.User
import com.nextcloud.talk.databinding.ActivityMessageSearchBinding
import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.utils.DisplayUtils
import com.nextcloud.talk.utils.bundle.BundleKeys
import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew
@ -64,6 +65,9 @@ class MessageSearchActivity : BaseActivity() {
@Inject
lateinit var userProvider: CurrentUserProviderNew
@Inject
lateinit var viewThemeUtils: ViewThemeUtils
private lateinit var binding: ActivityMessageSearchBinding
private lateinit var searchView: SearchView
@ -105,7 +109,9 @@ class MessageSearchActivity : BaseActivity() {
DisplayUtils.applyColorToStatusBar(
this,
ResourcesCompat.getColor(
resources, R.color.appbar, null
resources,
R.color.appbar,
null
)
)
DisplayUtils.applyColorToNavigationBar(
@ -154,7 +160,7 @@ class MessageSearchActivity : BaseActivity() {
emptyList()
}
val newItems =
state.results.map { MessageResultItem(this, user, it) } + loadMoreItems
state.results.map { MessageResultItem(this, user, it, false, viewThemeUtils) } + loadMoreItems
if (adapter != null) {
adapter!!.updateDataSet(newItems)

View file

@ -43,6 +43,10 @@ data class ThemingCapability(
var colorText: String?,
@JsonField(name = ["color-element"])
var colorElement: String?,
@JsonField(name = ["color-element-bright"])
var colorElementBright: String?,
@JsonField(name = ["color-element-dark"])
var colorElementDark: String?,
@JsonField(name = ["logo"])
var logo: String?,
@JsonField(name = ["background"])
@ -53,5 +57,5 @@ data class ThemingCapability(
var backgroundDefault: Boolean?
) : Parcelable {
// 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 com.nextcloud.talk.R
import com.nextcloud.talk.databinding.PollCreateOptionsItemBinding
import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.utils.EmojiTextInputEditText
class PollCreateOptionViewHolder(
private val binding: PollCreateOptionsItemBinding
private val binding: PollCreateOptionsItemBinding,
private val viewThemeUtils: ViewThemeUtils
) : RecyclerView.ViewHolder(binding.root) {
lateinit var optionText: EmojiTextInputEditText
@ -48,6 +50,7 @@ class PollCreateOptionViewHolder(
}
binding.pollOptionTextEdit.setText(pollCreateOptionItem.pollOption)
viewThemeUtils.colorEditText(binding.pollOptionTextEdit)
if (focus) {
itemsListener.requestFocus(binding.pollOptionTextEdit)

View file

@ -24,9 +24,11 @@ import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.nextcloud.talk.databinding.PollCreateOptionsItemBinding
import com.nextcloud.talk.ui.theme.ViewThemeUtils
class PollCreateOptionsAdapter(
private val clickListener: PollCreateOptionsItemListener
private val clickListener: PollCreateOptionsItemListener,
private val viewThemeUtils: ViewThemeUtils
) : RecyclerView.Adapter<PollCreateOptionViewHolder>() {
internal var list: ArrayList<PollCreateOptionItem> = ArrayList()
@ -34,7 +36,7 @@ class PollCreateOptionsAdapter(
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PollCreateOptionViewHolder {
val itemBinding = PollCreateOptionsItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return PollCreateOptionViewHolder(itemBinding)
return PollCreateOptionViewHolder(itemBinding, viewThemeUtils)
}
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.graphics.Typeface
import com.nextcloud.talk.databinding.PollResultHeaderItemBinding
import com.nextcloud.talk.ui.theme.ViewThemeUtils
class PollResultHeaderViewHolder(
override val binding: PollResultHeaderItemBinding
override val binding: PollResultHeaderItemBinding,
private val viewThemeUtils: ViewThemeUtils
) : PollResultViewHolder(binding) {
@SuppressLint("SetTextI18n")
override fun bind(pollResultItem: PollResultItem, clickListener: PollResultItemClickListener) {
val item = pollResultItem as PollResultHeaderItem
viewThemeUtils.colorProgressBar(binding.pollOptionBar)
binding.root.setOnClickListener { clickListener.onClick() }
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.PollResultVoterItemBinding
import com.nextcloud.talk.databinding.PollResultVotersOverviewItemBinding
import com.nextcloud.talk.ui.theme.ViewThemeUtils
class PollResultsAdapter(
private val user: User,
private val clickListener: PollResultItemClickListener,
private val viewThemeUtils: ViewThemeUtils
) : RecyclerView.Adapter<PollResultViewHolder>() {
internal var list: MutableList<PollResultItem> = ArrayList()
@ -43,7 +45,7 @@ class PollResultsAdapter(
LayoutInflater.from(parent.context), parent,
false
)
viewHolder = PollResultHeaderViewHolder(itemBinding)
viewHolder = PollResultHeaderViewHolder(itemBinding, viewThemeUtils)
}
PollResultVoterItem.VIEW_TYPE -> {
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.PollCreateOptionsItemListener
import com.nextcloud.talk.polls.viewmodels.PollCreateViewModel
import com.nextcloud.talk.ui.theme.ViewThemeUtils
import javax.inject.Inject
@AutoInjector(NextcloudTalkApplication::class)
@ -51,6 +52,9 @@ class PollCreateDialogFragment : DialogFragment(), PollCreateOptionsItemListener
@Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
@Inject
lateinit var viewThemeUtils: ViewThemeUtils
private lateinit var binding: DialogPollCreateBinding
private lateinit var viewModel: PollCreateViewModel
@ -85,13 +89,31 @@ class PollCreateDialogFragment : DialogFragment(), PollCreateOptionsItemListener
binding.pollCreateOptionsList.layoutManager = LinearLayoutManager(context)
adapter = PollCreateOptionsAdapter(this)
adapter = PollCreateOptionsAdapter(this, viewThemeUtils)
binding.pollCreateOptionsList.adapter = adapter
themeDialog()
setupListeners()
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() {
binding.pollAddOptionsItem.setOnClickListener {
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.viewmodels.PollMainViewModel
import com.nextcloud.talk.polls.viewmodels.PollResultsViewModel
import com.nextcloud.talk.ui.theme.ViewThemeUtils
import javax.inject.Inject
@AutoInjector(NextcloudTalkApplication::class)
@ -46,6 +47,9 @@ class PollResultsFragment : Fragment(), PollResultItemClickListener {
@Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
@Inject
lateinit var viewThemeUtils: ViewThemeUtils
private lateinit var parentViewModel: PollMainViewModel
lateinit var viewModel: PollResultsViewModel
@ -82,17 +86,24 @@ class PollResultsFragment : Fragment(), PollResultItemClickListener {
}
viewModel.items.observe(viewLifecycleOwner) {
val adapter = PollResultsAdapter(parentViewModel.user, this).apply {
val adapter = PollResultsAdapter(parentViewModel.user, this, viewThemeUtils).apply {
if (it != null) {
list = it
}
}
binding.pollResultsList.adapter = adapter
}
themeDialog()
}
private fun themeDialog() {
viewThemeUtils.colorMaterialButtonBackground(binding.editVoteButton)
viewThemeUtils.colorMaterialButtonText(binding.pollResultsEndPollButton)
}
private fun initAdapter() {
adapter = PollResultsAdapter(parentViewModel.user, this)
adapter = PollResultsAdapter(parentViewModel.user, this, viewThemeUtils)
binding.pollResultsList.adapter = adapter
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.viewmodels.PollMainViewModel
import com.nextcloud.talk.polls.viewmodels.PollVoteViewModel
import com.nextcloud.talk.ui.theme.ViewThemeUtils
import javax.inject.Inject
@AutoInjector(NextcloudTalkApplication::class)
@ -51,6 +52,9 @@ class PollVoteFragment : Fragment() {
@Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
@Inject
lateinit var viewThemeUtils: ViewThemeUtils
private lateinit var parentViewModel: PollMainViewModel
lateinit var viewModel: PollVoteViewModel
@ -117,6 +121,14 @@ class PollVoteFragment : Fragment() {
binding.pollVoteEditDismiss.setOnClickListener {
parentViewModel.dismissEditVotes()
}
themeDialog()
}
private fun themeDialog() {
viewThemeUtils.colorMaterialButtonBackground(binding.pollVoteSubmitButton)
viewThemeUtils.colorMaterialButtonText(binding.pollVoteEndPollButton)
viewThemeUtils.colorMaterialButtonText(binding.pollVoteEditDismiss)
}
private fun updateDismissEditButton(showDismissEditButton: Boolean) {
@ -136,6 +148,7 @@ class PollVoteFragment : Fragment() {
RadioButton(context).apply { text = option }
}?.forEachIndexed { index, radioButton ->
radioButton.id = index
viewThemeUtils.themeRadioButton(radioButton)
makeOptionBoldIfSelfVoted(radioButton, poll, index)
binding.pollVoteRadioGroup.addView(radioButton)
@ -156,6 +169,7 @@ class PollVoteFragment : Fragment() {
setLayoutParams(layoutParams)
}
}?.forEachIndexed { index, checkBox ->
viewThemeUtils.themeCheckbox(checkBox)
checkBox.id = index
makeOptionBoldIfSelfVoted(checkBox, poll, index)
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.viewmodels.RemoteFileBrowserItemsViewModel
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.FileSortOrder
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_MIME_TYPE_FILTER
@ -59,6 +60,9 @@ class RemoteFileBrowserActivity : AppCompatActivity(), SelectionInterface, Swipe
@Inject
lateinit var currentUserProvider: CurrentUserProviderNew
@Inject
lateinit var viewThemeUtils: ViewThemeUtils
private lateinit var binding: ActivityRemoteFileBrowserBinding
private lateinit var viewModel: RemoteFileBrowserItemsViewModel
@ -91,8 +95,7 @@ class RemoteFileBrowserActivity : AppCompatActivity(), SelectionInterface, Swipe
initViewModel(mimeTypeSelectionFilter)
binding.swipeRefreshList.setOnRefreshListener(this)
binding.swipeRefreshList.setColorSchemeResources(R.color.colorPrimary)
binding.swipeRefreshList.setProgressBackgroundColorSchemeResource(R.color.refresh_spinner_background)
viewThemeUtils.themeSwipeRefreshLayout(binding.swipeRefreshList)
binding.pathNavigationBackButton.setOnClickListener { viewModel.navigateUp() }
binding.sortButton.setOnClickListener { changeSorting() }
@ -160,6 +163,7 @@ class RemoteFileBrowserActivity : AppCompatActivity(), SelectionInterface, Swipe
mimeTypeSelectionFilter = mimeTypeSelectionFilter,
user = currentUserProvider.currentUser.blockingGet(),
selectionInterface = this,
viewThemeUtils = viewThemeUtils,
onItemClicked = viewModel::onItemClicked
)
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.remotefilebrowser.SelectionInterface
import com.nextcloud.talk.remotefilebrowser.model.RemoteFileBrowserItem
import com.nextcloud.talk.ui.theme.ViewThemeUtils
class RemoteFileBrowserItemsAdapter(
private val showGrid: Boolean = false,
private val mimeTypeSelectionFilter: String? = null,
private val user: User,
private val selectionInterface: SelectionInterface,
private val viewThemeUtils: ViewThemeUtils,
private val onItemClicked: (RemoteFileBrowserItem) -> Unit
) : RecyclerView.Adapter<RemoteFileBrowserItemsViewHolder>() {
var items: List<RemoteFileBrowserItem> = emptyList()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RemoteFileBrowserItemsViewHolder {
return if (showGrid) {
RemoteFileBrowserItemsListViewHolder(
RvItemBrowserFileBinding.inflate(
@ -50,7 +51,8 @@ class RemoteFileBrowserItemsAdapter(
),
mimeTypeSelectionFilter,
user,
selectionInterface
selectionInterface,
viewThemeUtils
) {
onItemClicked(items[it])
}
@ -63,7 +65,8 @@ class RemoteFileBrowserItemsAdapter(
),
mimeTypeSelectionFilter,
user,
selectionInterface
selectionInterface,
viewThemeUtils
) {
onItemClicked(items[it])
}

View file

@ -22,7 +22,6 @@ package com.nextcloud.talk.remotefilebrowser.adapters
import android.text.format.Formatter
import android.view.View
import androidx.appcompat.content.res.AppCompatResources
import autodagger.AutoInjector
import com.facebook.drawee.backends.pipeline.Fresco
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.remotefilebrowser.SelectionInterface
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.DateUtils.getLocalDateTimeStringFromTimestamp
import com.nextcloud.talk.utils.DisplayUtils
import com.nextcloud.talk.utils.DrawableUtils.getDrawableResourceIdForMimeType
import com.nextcloud.talk.utils.Mimetype.FOLDER
@AutoInjector(NextcloudTalkApplication::class)
@ -45,6 +44,7 @@ class RemoteFileBrowserItemsListViewHolder(
mimeTypeSelectionFilter: String?,
currentUser: User,
selectionInterface: SelectionInterface,
private val viewThemeUtils: ViewThemeUtils,
onItemClicked: (Int) -> Unit
) : RemoteFileBrowserItemsViewHolder(binding, mimeTypeSelectionFilter, currentUser, selectionInterface) {
@ -66,7 +66,6 @@ class RemoteFileBrowserItemsListViewHolder(
}
override fun onBind(item: RemoteFileBrowserItem) {
super.onBind(item)
binding.fileIcon.controller = null
@ -99,9 +98,7 @@ class RemoteFileBrowserItemsListViewHolder(
binding.fileIcon
.hierarchy
.setPlaceholderImage(
AppCompatResources.getDrawable(
binding.fileIcon.context, getDrawableResourceIdForMimeType(item.mimeType)
)
viewThemeUtils.getPlaceholderImage(binding.root.context, item.mimeType)
)
if (item.hasPreview) {
@ -132,6 +129,7 @@ class RemoteFileBrowserItemsListViewHolder(
private fun setSelectability() {
if (selectable) {
binding.selectFileCheckbox.visibility = View.VISIBLE
viewThemeUtils.themeCheckbox(binding.selectFileCheckbox)
} else {
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.model.SharedItemType
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.bundle.BundleKeys.KEY_CONVERSATION_NAME
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN
@ -53,6 +54,9 @@ class SharedItemsActivity : AppCompatActivity() {
@Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
@Inject
lateinit var viewThemeUtils: ViewThemeUtils
private lateinit var binding: ActivitySharedItemsBinding
private lateinit var viewModel: SharedItemsViewModel
@ -72,7 +76,9 @@ class SharedItemsActivity : AppCompatActivity() {
DisplayUtils.applyColorToStatusBar(
this,
ResourcesCompat.getColor(
resources, R.color.appbar, null
resources,
R.color.appbar,
null
)
)
DisplayUtils.applyColorToNavigationBar(
@ -130,7 +136,8 @@ class SharedItemsActivity : AppCompatActivity() {
showGrid,
user,
roomToken,
isUserConversationOwnerOrModerator
isUserConversationOwnerOrModerator,
viewThemeUtils
).apply {
items = sharedMediaItems.items
}
@ -142,6 +149,8 @@ class SharedItemsActivity : AppCompatActivity() {
}
else -> {}
}
viewThemeUtils.colorTabLayout(binding.sharedItemsTabs)
}
private fun clearEmptyLoading() {
@ -161,7 +170,6 @@ class SharedItemsActivity : AppCompatActivity() {
}
private fun initTabs(sharedItemTypes: Set<SharedItemType>) {
binding.sharedItemsTabs.removeAllTabs()
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.SharedOtherItem
import com.nextcloud.talk.shareditems.model.SharedPollItem
import com.nextcloud.talk.ui.theme.ViewThemeUtils
class SharedItemsAdapter(
private val showGrid: Boolean,
private val user: User,
private val roomToken: String,
private val isUserConversationOwnerOrModerator: Boolean
private val isUserConversationOwnerOrModerator: Boolean,
private val viewThemeUtils: ViewThemeUtils
) : RecyclerView.Adapter<SharedItemsViewHolder>() {
var items: List<SharedItem> = emptyList()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SharedItemsViewHolder {
return if (showGrid) {
SharedItemsGridViewHolder(
SharedItemGridBinding.inflate(
@ -56,7 +57,8 @@ class SharedItemsAdapter(
parent,
false
),
user
user,
viewThemeUtils
)
} else {
SharedItemsListViewHolder(
@ -65,7 +67,8 @@ class SharedItemsAdapter(
parent,
false
),
user
user,
viewThemeUtils
)
}
}

View file

@ -27,11 +27,13 @@ import android.widget.ProgressBar
import com.facebook.drawee.view.SimpleDraweeView
import com.nextcloud.talk.data.user.model.User
import com.nextcloud.talk.databinding.SharedItemGridBinding
import com.nextcloud.talk.ui.theme.ViewThemeUtils
class SharedItemsGridViewHolder(
override val binding: SharedItemGridBinding,
user: User
) : SharedItemsViewHolder(binding, user) {
user: User,
viewThemeUtils: ViewThemeUtils
) : SharedItemsViewHolder(binding, user, viewThemeUtils) {
override val image: SimpleDraweeView
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.SharedOtherItem
import com.nextcloud.talk.shareditems.model.SharedPollItem
import com.nextcloud.talk.ui.theme.ViewThemeUtils
class SharedItemsListViewHolder(
override val binding: SharedItemListBinding,
user: User
) : SharedItemsViewHolder(binding, user) {
user: User,
viewThemeUtils: ViewThemeUtils
) : SharedItemsViewHolder(binding, user, viewThemeUtils) {
override val image: SimpleDraweeView
get() = binding.fileImage
@ -52,7 +54,6 @@ class SharedItemsListViewHolder(
get() = binding.progressBar
override fun onBind(item: SharedFileItem) {
super.onBind(item)
binding.fileName.text = item.name

View file

@ -23,12 +23,10 @@
package com.nextcloud.talk.shareditems.adapters
import android.content.Context
import android.graphics.drawable.Drawable
import android.net.Uri
import android.util.Log
import android.view.View
import android.widget.ProgressBar
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding
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.SharedFileItem
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.SharedOtherItem
import com.nextcloud.talk.shareditems.model.SharedPollItem
import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.DrawableUtils
import com.nextcloud.talk.utils.FileViewerUtils
abstract class SharedItemsViewHolder(
open val binding: ViewBinding,
internal val user: User
internal val user: User,
private val viewThemeUtils: ViewThemeUtils
) : RecyclerView.ViewHolder(binding.root) {
companion object {
@ -71,7 +70,7 @@ abstract class SharedItemsViewHolder(
)
open fun onBind(item: SharedFileItem) {
image.hierarchy.setPlaceholderImage(staticImage(item.mimeType, image))
image.hierarchy.setPlaceholderImage(viewThemeUtils.getPlaceholderImage(image.context, item.mimeType))
if (item.previewAvailable) {
image.controller = configurePreview(item)
}
@ -107,7 +106,6 @@ abstract class SharedItemsViewHolder(
}
private fun configurePreview(item: SharedFileItem): DraweeController {
val imageRequest = ImageRequestBuilder.newBuilderWithSource(Uri.parse(item.previewLink))
.setProgressiveRenderingEnabled(true)
.setRotationOptions(RotationOptions.autoRotate())
@ -136,12 +134,4 @@ abstract class SharedItemsViewHolder(
open fun onBind(item: SharedOtherItem) {}
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.view.View
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.BottomSheetDialog
import com.nextcloud.talk.R
import com.nextcloud.talk.activities.CallActivity
import com.nextcloud.talk.application.NextcloudTalkApplication
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 javax.inject.Inject
@AutoInjector(NextcloudTalkApplication::class)
class AudioOutputDialog(val callActivity: CallActivity) : BottomSheetDialog(callActivity) {
@Inject
lateinit var viewThemeUtils: ViewThemeUtils
@Inject
lateinit var serverTheme: ServerTheme
private lateinit var dialogAudioOutputBinding: DialogAudioOutputBinding
init {
NextcloudTalkApplication.sharedApplication?.componentApplication?.inject(this)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
dialogAudioOutputBinding = DialogAudioOutputBinding.inflate(layoutInflater)
@ -82,55 +97,23 @@ class AudioOutputDialog(val callActivity: CallActivity) : BottomSheetDialog(call
private fun highlightActiveOutputChannel() {
when (callActivity.audioManager?.currentAudioDevice) {
WebRtcAudioManager.AudioDevice.BLUETOOTH -> {
dialogAudioOutputBinding.audioOutputBluetoothIcon.setColorFilter(
ContextCompat.getColor(
context,
R.color.colorPrimary
),
android.graphics.PorterDuff.Mode.SRC_IN
)
dialogAudioOutputBinding.audioOutputBluetoothText.setTextColor(
callActivity.resources.getColor(R.color.colorPrimary)
)
viewThemeUtils.colorImageView(dialogAudioOutputBinding.audioOutputBluetoothIcon)
dialogAudioOutputBinding.audioOutputBluetoothText.setTextColor(serverTheme.primaryColor)
}
WebRtcAudioManager.AudioDevice.SPEAKER_PHONE -> {
dialogAudioOutputBinding.audioOutputSpeakerIcon.setColorFilter(
ContextCompat.getColor(
context,
R.color.colorPrimary
),
android.graphics.PorterDuff.Mode.SRC_IN
)
dialogAudioOutputBinding.audioOutputSpeakerText.setTextColor(
callActivity.resources.getColor(R.color.colorPrimary)
)
viewThemeUtils.colorImageView(dialogAudioOutputBinding.audioOutputSpeakerIcon)
dialogAudioOutputBinding.audioOutputSpeakerText.setTextColor(serverTheme.primaryColor)
}
WebRtcAudioManager.AudioDevice.EARPIECE -> {
dialogAudioOutputBinding.audioOutputEarspeakerIcon.setColorFilter(
ContextCompat.getColor(
context,
R.color.colorPrimary
),
android.graphics.PorterDuff.Mode.SRC_IN
)
dialogAudioOutputBinding.audioOutputEarspeakerText.setTextColor(
callActivity.resources.getColor(R.color.colorPrimary)
)
viewThemeUtils.colorImageView(dialogAudioOutputBinding.audioOutputEarspeakerIcon)
dialogAudioOutputBinding.audioOutputEarspeakerText.setTextColor(serverTheme.primaryColor)
}
WebRtcAudioManager.AudioDevice.WIRED_HEADSET -> {
dialogAudioOutputBinding.audioOutputWiredHeadsetIcon.setColorFilter(
ContextCompat.getColor(
context,
R.color.colorPrimary
),
android.graphics.PorterDuff.Mode.SRC_IN
)
dialogAudioOutputBinding.audioOutputWiredHeadsetText.setTextColor(
callActivity.resources.getColor(R.color.colorPrimary)
)
viewThemeUtils.colorImageView(dialogAudioOutputBinding.audioOutputWiredHeadsetIcon)
dialogAudioOutputBinding.audioOutputWiredHeadsetText.setTextColor(serverTheme.primaryColor)
}
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.ui.StatusDrawable;
import com.nextcloud.talk.users.UserManager;
import com.nextcloud.talk.ui.theme.ViewThemeUtils;
import com.nextcloud.talk.utils.ApiUtils;
import com.nextcloud.talk.utils.DisplayUtils;
import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew;
@ -87,6 +88,9 @@ public class ChooseAccountDialogFragment extends DialogFragment {
@Inject
NcApi ncApi;
@Inject
ViewThemeUtils viewThemeUtils;
private DialogChooseAccountBinding binding;
private View dialogView;
@ -120,6 +124,9 @@ public class ChooseAccountDialogFragment extends DialogFragment {
binding.currentAccount.ticker.setVisibility(View.GONE);
binding.currentAccount.account.setText((Uri.parse(user.getBaseUrl()).getHost()));
viewThemeUtils.colorImageView(binding.currentAccount.accountMenu);
if (user.getBaseUrl() != null &&
(user.getBaseUrl().startsWith("http://") || user.getBaseUrl().startsWith("https://"))) {
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.OnItemSelectedListener
import android.widget.ArrayAdapter
import android.widget.ImageView
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.core.widget.doAfterTextChanged
import androidx.fragment.app.DialogFragment
import androidx.recyclerview.widget.LinearLayoutManager
import autodagger.AutoInjector
import com.bluelinelabs.logansquare.LoganSquare
import com.google.android.material.card.MaterialCardView
import com.nextcloud.talk.R
import com.nextcloud.talk.adapters.PredefinedStatusClickListener
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.predefined.PredefinedStatus
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.DisplayUtils
import com.vanniktech.emoji.EmojiPopup
@ -105,6 +109,9 @@ class SetStatusDialogFragment :
@Inject
lateinit var ncApi: NcApi
@Inject
lateinit var viewThemeUtils: ViewThemeUtils
lateinit var credentials: String
override fun onCreate(savedInstanceState: Bundle?) {
@ -234,8 +241,8 @@ class SetStatusDialogFragment :
}
}
binding.clearStatus.setTextColor(resources.getColor(R.color.colorPrimary))
binding.setStatus.setBackgroundColor(resources.getColor(R.color.colorPrimary))
viewThemeUtils.colorMaterialButtonText(binding.clearStatus)
viewThemeUtils.colorMaterialButtonBackground(binding.setStatus)
binding.customStatusInput.highlightColor = resources.getColor(R.color.colorPrimary)
@ -258,7 +265,6 @@ class SetStatusDialogFragment :
@Suppress("ComplexMethod")
private fun setClearStatusAfterValue(item: Int) {
val currentTime = System.currentTimeMillis() / ONE_SECOND_IN_MILLIS
when (item) {
@ -310,7 +316,6 @@ class SetStatusDialogFragment :
}
private fun clearAtToUnixTime(clearAt: ClearAt?): Long {
var returnValue = -1L
if (clearAt != null) {
@ -400,25 +405,18 @@ class SetStatusDialogFragment :
private fun visualizeStatus(statusType: StatusType) {
clearTopStatus()
when (statusType) {
StatusType.ONLINE -> {
binding.onlineStatus.setCardBackgroundColor(resources.getColor(R.color.colorPrimary))
binding.onlineHeadline.setTextColor(resources.getColor(R.color.high_emphasis_text_dark_background))
val views: Triple<MaterialCardView, TextView, ImageView> = when (statusType) {
StatusType.ONLINE -> Triple(binding.onlineStatus, binding.onlineHeadline, binding.onlineIcon)
StatusType.AWAY -> Triple(binding.awayStatus, binding.awayHeadline, binding.awayIcon)
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() {
@ -433,11 +431,15 @@ class SetStatusDialogFragment :
binding.awayHeadline.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.onlineIcon.imageTintList = null
binding.awayIcon.imageTintList = null
binding.dndIcon.imageTintList = null
binding.invisibleIcon.imageTintList = null
}
}
private fun setStatusMessage() {
val inputText = binding.customStatusInput.text.toString().ifEmpty { "" }
// The endpoint '/message/custom' expects a valid emoji as string or null
val statusIcon = binding.emoji.text.toString().ifEmpty { null }
@ -446,7 +448,6 @@ class SetStatusDialogFragment :
selectedPredefinedStatus!!.message != inputText ||
selectedPredefinedStatus!!.icon != binding.emoji.text.toString()
) {
ncApi.setCustomStatusMessage(
credentials,
ApiUtils.getUrlForSetCustomStatus(currentUser?.baseUrl),
@ -476,12 +477,13 @@ class SetStatusDialogFragment :
}
})
} else {
val clearAt = clearAtToUnixTime(selectedPredefinedStatus!!.clearAt)
ncApi.setPredefinedStatusMessage(
credentials, ApiUtils.getUrlForSetPredefinedStatus(currentUser?.baseUrl),
selectedPredefinedStatus!!.id, if (clearAt == -1L) null else clearAt
credentials,
ApiUtils.getUrlForSetPredefinedStatus(currentUser?.baseUrl),
selectedPredefinedStatus!!.id,
if (clearAt == -1L) null else clearAt
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())?.subscribe(object : Observer<GenericOverall> {
@ -506,7 +508,6 @@ class SetStatusDialogFragment :
}
override fun onClick(predefinedStatus: PredefinedStatus) {
selectedPredefinedStatus = predefinedStatus
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.generic.GenericOverall
import com.nextcloud.talk.models.json.reactions.ReactionsOverall
import com.nextcloud.talk.ui.theme.ServerTheme
import com.nextcloud.talk.utils.ApiUtils
import io.reactivex.Observer
import io.reactivex.android.schedulers.AndroidSchedulers
@ -65,7 +66,8 @@ class ShowReactionsDialog(
private val chatMessage: ChatMessage,
private val user: User?,
private val hasChatPermission: Boolean,
private val ncApi: NcApi
private val ncApi: NcApi,
private val serverTheme: ServerTheme
) : BottomSheetDialog(activity), ReactionItemClickListener {
private lateinit var binding: DialogMessageReactionsBinding
@ -96,6 +98,7 @@ class ShowReactionsDialog(
adapter?.list?.clear()
if (chatMessage.reactions != null && chatMessage.reactions!!.isNotEmpty()) {
var reactionsTotal = 0
binding.emojiReactionsTabs.setSelectedTabIndicatorColor(serverTheme.primaryColor)
for ((emoji, amount) in chatMessage.reactions!!) {
reactionsTotal = reactionsTotal.plus(amount as Int)
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.dialog.MaterialAlertDialogBuilder;
import com.nextcloud.talk.R;
import com.nextcloud.talk.application.NextcloudTalkApplication;
import com.nextcloud.talk.databinding.SortingOrderFragmentBinding;
import com.nextcloud.talk.ui.theme.ViewThemeUtils;
import com.nextcloud.talk.utils.FileSortOrder;
import com.nextcloud.talk.utils.preferences.AppPreferences;
@ -46,7 +46,6 @@ import javax.inject.Inject;
import androidx.annotation.NonNull;
import androidx.fragment.app.DialogFragment;
import autodagger.AutoInjector;
import kotlin.jvm.JvmField;
/**
* 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";
@Inject
@JvmField
AppPreferences appPreferences;
@Inject
ViewThemeUtils viewThemeUtils;
private SortingOrderFragmentBinding binding;
private View dialogView;
@ -119,7 +120,7 @@ public class SortingOrderDialogFragment extends DialogFragment implements View.O
* find all relevant UI elements and set their values.
*/
private void setupDialogElements() {
binding.cancel.setTextColor(getResources().getColor(R.color.colorPrimary));
viewThemeUtils.colorMaterialButtonText(binding.cancel);
taggedViews = new View[12];
taggedViews[0] = binding.sortByNameAscending;
@ -154,7 +155,6 @@ public class SortingOrderDialogFragment extends DialogFragment implements View.O
* tints the icon reflecting the actual sorting choice in the apps primary color.
*/
private void setupActiveOrderSelection() {
final int color = getResources().getColor(R.color.colorPrimary);
Log.i("SortOrder", "currentSortOrderName=" + currentSortOrderName);
for (View view : taggedViews) {
Log.i("SortOrder", ((FileSortOrder) view.getTag()).getName());
@ -162,10 +162,10 @@ public class SortingOrderDialogFragment extends DialogFragment implements View.O
continue;
}
if (view instanceof MaterialButton) {
((MaterialButton) view).setIconTintResource(R.color.colorPrimary);
viewThemeUtils.colorMaterialButtonText((MaterialButton) view);
}
if (view instanceof TextView) {
((TextView) view).setTextColor(color);
viewThemeUtils.colorTextViewElement((TextView) view);
((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.json.capabilities.Capabilities
import com.nextcloud.talk.models.json.push.PushConfigurationState
import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew
import io.reactivex.Maybe
import io.reactivex.Observable
import io.reactivex.Single
@Suppress("TooManyFunctions")
class UserManager internal constructor(private val userRepository: UsersRepository) : CurrentUserProviderNew {
class UserManager internal constructor(private val userRepository: UsersRepository) {
val users: Single<List<User>>
get() = userRepository.getUsers()
val usersScheduledForDeletion: Single<List<User>>
get() = userRepository.getUsersScheduledForDeletion()
override val currentUser: Maybe<User>
val currentUser: Maybe<User>
get() {
return userRepository.getActiveUser()
}
val currentUserObservable: Observable<User>
get() {
return userRepository.getActiveUserObservable()
}
fun deleteUser(internalId: Long): Int {
return userRepository.deleteUser(userRepository.getUserWithId(internalId).blockingGet())
}

View file

@ -148,33 +148,23 @@ object DrawableUtils {
drawableMap["unknown"] = R.drawable.ic_mimetype_file
drawableMap["application/pdf"] = R.drawable.ic_mimetype_application_pdf
if (localMimetype.isNullOrEmpty()) {
return drawableMap["unknown"]!!
}
if ("DIR" == localMimetype) {
return if (localMimetype.isNullOrEmpty()) {
drawableMap["unknown"]!!
} else if ("DIR" == localMimetype) {
localMimetype = FOLDER
return drawableMap[localMimetype]!!
}
if (drawableMap.containsKey(localMimetype)) {
return drawableMap[localMimetype]!!
}
if (localMimetype.startsWith(IMAGE_PREFIX)) {
return R.drawable.ic_mimetype_image
}
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)) {
drawableMap[localMimetype]!!
} else if (drawableMap.containsKey(localMimetype)) {
drawableMap[localMimetype]!!
} else if (localMimetype.startsWith(IMAGE_PREFIX)) {
R.drawable.ic_mimetype_image
} else if (localMimetype.startsWith(VIDEO_PREFIX)) {
R.drawable.ic_mimetype_video
} else if (localMimetype.startsWith(TEXT_PREFIX)) {
R.drawable.ic_mimetype_text
} else if (localMimetype.startsWith(AUDIO_PREFIX)) {
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 {
@Binds
abstract fun bindCurrentUserProviderNew(userManager: UserManager): CurrentUserProviderNew
abstract fun bindCurrentUserProviderNew(currentUserProviderImpl: CurrentUserProviderImpl): CurrentUserProviderNew
companion object {
@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:tint="@android:color/white"
android:visibility="gone"
tools:visibility="visible"
app:backgroundTint="@color/colorPrimary"
app:cornerRadius="48dp"
app:elevation="0dp"

View file

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

View file

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

View file

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

View file

@ -29,7 +29,8 @@
<RelativeLayout
android:id="@+id/avatarContainer"
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
android:id="@+id/avatar_image"

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -76,4 +76,8 @@
<color name="dialog_background">#353535</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>

View file

@ -63,15 +63,13 @@
<color name="nc_darkGreen">#006400</color>
<color name="controller_chat_separator">#E8E8E8</color>
<color name="grey_600">#757575</color>
<color name="grey_900">#212121</color>
<color name="nc_grey">#D5D5D5</color>
<color name="controller_call_incomingCallTextView">#E9FFFFFF</color>
<color name="grey950">#111111</color>
<color name="textColorMaxContrast">#767676</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_inverse">#FFFFFF</color>
@ -82,8 +80,6 @@
<color name="bg_message_list_incoming_bubble">#EFEFEF</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_call_screen_dialog">#121212</color>
@ -111,4 +107,7 @@
<color name="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>

View file

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