Unverified Commit f1d4537e authored by Filipe de Lima Brito's avatar Filipe de Lima Brito Committed by GitHub

Merge branch 'develop' into fix-2109

parents 923adeec 7ad32331
......@@ -43,8 +43,8 @@ cd Rocket.Chat.Android/app
## Bug report & Feature request
Are you having a technical issue trying to compile the app, or setting up Push Notifications? Please use our Community Support channel for that: https://forums.rocket.chat/c/community-support. The issues are only suppose to be used for bugs, improvements and features in the native Android application.
Are you having a technical issue trying to compile the app, or setting up Push Notifications? Please use our Community Support channel for that: https://forums.rocket.chat/c/community-support. The issues are only supposed to be used for bugs, improvements, and features in the native Android application.
## Coding Style
Please follow the official [Kotlin coding convections](https://kotlinlang.org/docs/reference/coding-conventions.html) when contributing.
Please follow the official [Kotlin coding conventions](https://kotlinlang.org/docs/reference/coding-conventions.html) when contributing.
......@@ -34,12 +34,16 @@ class AboutFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupToolbar()
setupViews()
analyticsManager.logScreenView(ScreenViewEvent.About)
}
override fun onResume() {
super.onResume()
setupToolbar()
}
private fun setupViews() {
text_version_name.text = BuildConfig.VERSION_NAME
text_build_number.text = getString(
......
......@@ -3,15 +3,10 @@ package chat.rocket.android.app
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent
import chat.rocket.android.server.domain.GetAccountInteractor
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.common.RocketChatException
import chat.rocket.common.model.UserStatus
import chat.rocket.core.internal.realtime.setTemporaryStatus
import kotlinx.coroutines.experimental.launch
import timber.log.Timber
import javax.inject.Inject
class AppLifecycleObserver @Inject constructor(
......
import android.content.Context
import chat.rocket.android.R
import org.threeten.bp.*
import org.threeten.bp.Instant
import org.threeten.bp.LocalDate
import org.threeten.bp.LocalDateTime
import org.threeten.bp.LocalTime
import org.threeten.bp.Period
import org.threeten.bp.ZoneId
import org.threeten.bp.format.DateTimeFormatter
import org.threeten.bp.format.FormatStyle
import org.threeten.bp.format.TextStyle
......@@ -53,39 +58,39 @@ object DateTimeHelper {
}
}
/**
* Returns a time from a [LocalDateTime].
*
* @param localDateTime The [LocalDateTime].
* @return The time from a [LocalDateTime].
*/
fun getTime(localDateTime: LocalDateTime): String {
val formatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)
return localDateTime.toLocalTime().format(formatter).toString()
}
/**
* Returns a time from a [LocalDateTime].
*
* @param localDateTime The [LocalDateTime].
* @return The time from a [LocalDateTime].
*/
fun getTime(localDateTime: LocalDateTime): String {
val formatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)
return localDateTime.toLocalTime().format(formatter).toString()
}
/**
* Returns a date time from a [LocalDateTime].
*
* @param localDateTime The [LocalDateTime].
* @return The time from a [LocalDateTime].
*/
fun getDateTime(localDateTime: LocalDateTime): String {
return formatLocalDateTime(localDateTime)
}
/**
* Returns a date time from a [LocalDateTime].
*
* @param localDateTime The [LocalDateTime].
* @return The time from a [LocalDateTime].
*/
fun getDateTime(localDateTime: LocalDateTime): String {
return formatLocalDateTime(localDateTime)
}
private fun formatLocalDateTime(localDateTime: LocalDateTime): String {
val formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT)
return localDateTime.format(formatter).toString()
}
private fun formatLocalDateTime(localDateTime: LocalDateTime): String {
val formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT)
return localDateTime.format(formatter).toString()
}
private fun formatLocalDate(localDate: LocalDate): String {
val formatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)
return localDate.format(formatter).toString()
}
private fun formatLocalDate(localDate: LocalDate): String {
val formatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)
return localDate.format(formatter).toString()
}
private fun formatLocalTime(localTime: LocalTime): String {
val formatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)
return localTime.format(formatter).toString()
}
private fun formatLocalTime(localTime: LocalTime): String {
val formatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)
return localTime.format(formatter).toString()
}
}
\ No newline at end of file
......@@ -8,19 +8,16 @@ import chat.rocket.android.server.domain.MultiServerTokenRepository
import com.squareup.moshi.Moshi
@PerActivity
class SharedPreferencesMultiServerTokenRepository(private val repository: LocalRepository,
private val moshi: Moshi
class SharedPreferencesMultiServerTokenRepository(
private val repository: LocalRepository,
private val moshi: Moshi
) : MultiServerTokenRepository {
override fun get(server: String): TokenModel? {
val token = repository.get("$TOKEN_KEY$server")
val adapter = moshi.adapter<TokenModel>(TokenModel::class.java)
token?.let {
return adapter.fromJson(token)
}
return null
return token?.let { adapter.fromJson(it) }
}
override fun save(server: String, token: TokenModel) {
......
......@@ -39,11 +39,9 @@ internal const val REQUEST_CODE_FOR_SIGN_IN_REQUIRED = 1
internal const val REQUEST_CODE_FOR_MULTIPLE_ACCOUNTS_RESOLUTION = 2
internal const val REQUEST_CODE_FOR_SAVE_RESOLUTION = 3
fun newInstance(serverName: String): Fragment {
return LoginFragment().apply {
arguments = Bundle(1).apply {
putString(SERVER_NAME, serverName)
}
fun newInstance(serverName: String): Fragment = LoginFragment().apply {
arguments = Bundle(1).apply {
putString(SERVER_NAME, serverName)
}
}
......@@ -59,9 +57,8 @@ class LoginFragment : Fragment(), LoginView {
super.onCreate(savedInstanceState)
AndroidSupportInjection.inject(this)
val bundle = arguments
if (bundle != null) {
serverName = bundle.getString(SERVER_NAME)
arguments?.run {
serverName = getString(SERVER_NAME)
}
}
......@@ -150,7 +147,7 @@ class LoginFragment : Fragment(), LoginView {
override fun showGenericErrorMessage() = showMessage(R.string.msg_generic_error)
private fun setupOnClickListener() =
ui { _ ->
ui {
button_log_in.setOnClickListener {
presenter.authenticateWithUserAndPassword(
text_username_or_email.textContent,
......@@ -160,7 +157,7 @@ class LoginFragment : Fragment(), LoginView {
}
override fun showForgotPasswordView() {
ui { _ ->
ui {
button_forgot_your_password.isVisible = true
button_forgot_your_password.setOnClickListener { presenter.forgotPassword() }
......
......@@ -88,36 +88,34 @@ fun newInstance(
isLoginFormEnabled: Boolean,
isNewAccountCreationEnabled: Boolean,
deepLinkInfo: LoginDeepLinkInfo? = null
): Fragment {
return LoginOptionsFragment().apply {
arguments = Bundle(23).apply {
putString(SERVER_NAME, serverName)
putString(STATE, state)
putString(FACEBOOK_OAUTH_URL, facebookOauthUrl)
putString(GITHUB_OAUTH_URL, githubOauthUrl)
putString(GOOGLE_OAUTH_URL, googleOauthUrl)
putString(LINKEDIN_OAUTH_URL, linkedinOauthUrl)
putString(GITLAB_OAUTH_URL, gitlabOauthUrl)
putString(WORDPRESS_OAUTH_URL, wordpressOauthUrl)
putString(CAS_LOGIN_URL, casLoginUrl)
putString(CAS_TOKEN, casToken)
putString(CAS_SERVICE_NAME, casServiceName)
putInt(CAS_SERVICE_NAME_TEXT_COLOR, casServiceNameTextColor)
putInt(CAS_SERVICE_BUTTON_COLOR, casServiceButtonColor)
putString(CUSTOM_OAUTH_URL, customOauthUrl)
putString(CUSTOM_OAUTH_SERVICE_NAME, customOauthServiceName)
putInt(CUSTOM_OAUTH_SERVICE_NAME_TEXT_COLOR, customOauthServiceNameTextColor)
putInt(CUSTOM_OAUTH_SERVICE_BUTTON_COLOR, customOauthServiceButtonColor)
putString(SAML_URL, samlUrl)
putString(SAML_TOKEN, samlToken)
putString(SAML_SERVICE_NAME, samlServiceName)
putInt(SAML_SERVICE_NAME_TEXT_COLOR, samlServiceNameTextColor)
putInt(SAML_SERVICE_BUTTON_COLOR, samlServiceButtonColor)
putInt(TOTAL_SOCIAL_ACCOUNTS, totalSocialAccountsEnabled)
putBoolean(IS_LOGIN_FORM_ENABLED, isLoginFormEnabled)
putBoolean(IS_NEW_ACCOUNT_CREATION_ENABLED, isNewAccountCreationEnabled)
putParcelable(DEEP_LINK_INFO, deepLinkInfo)
}
): Fragment = LoginOptionsFragment().apply {
arguments = Bundle(23).apply {
putString(SERVER_NAME, serverName)
putString(STATE, state)
putString(FACEBOOK_OAUTH_URL, facebookOauthUrl)
putString(GITHUB_OAUTH_URL, githubOauthUrl)
putString(GOOGLE_OAUTH_URL, googleOauthUrl)
putString(LINKEDIN_OAUTH_URL, linkedinOauthUrl)
putString(GITLAB_OAUTH_URL, gitlabOauthUrl)
putString(WORDPRESS_OAUTH_URL, wordpressOauthUrl)
putString(CAS_LOGIN_URL, casLoginUrl)
putString(CAS_TOKEN, casToken)
putString(CAS_SERVICE_NAME, casServiceName)
putInt(CAS_SERVICE_NAME_TEXT_COLOR, casServiceNameTextColor)
putInt(CAS_SERVICE_BUTTON_COLOR, casServiceButtonColor)
putString(CUSTOM_OAUTH_URL, customOauthUrl)
putString(CUSTOM_OAUTH_SERVICE_NAME, customOauthServiceName)
putInt(CUSTOM_OAUTH_SERVICE_NAME_TEXT_COLOR, customOauthServiceNameTextColor)
putInt(CUSTOM_OAUTH_SERVICE_BUTTON_COLOR, customOauthServiceButtonColor)
putString(SAML_URL, samlUrl)
putString(SAML_TOKEN, samlToken)
putString(SAML_SERVICE_NAME, samlServiceName)
putInt(SAML_SERVICE_NAME_TEXT_COLOR, samlServiceNameTextColor)
putInt(SAML_SERVICE_BUTTON_COLOR, samlServiceButtonColor)
putInt(TOTAL_SOCIAL_ACCOUNTS, totalSocialAccountsEnabled)
putBoolean(IS_LOGIN_FORM_ENABLED, isLoginFormEnabled)
putBoolean(IS_NEW_ACCOUNT_CREATION_ENABLED, isNewAccountCreationEnabled)
putParcelable(DEEP_LINK_INFO, deepLinkInfo)
}
}
......@@ -157,34 +155,33 @@ class LoginOptionsFragment : Fragment(), LoginOptionsView {
super.onCreate(savedInstanceState)
AndroidSupportInjection.inject(this)
val bundle = arguments
if (bundle != null) {
serverName = bundle.getString(SERVER_NAME)
state = bundle.getString(STATE)
facebookOauthUrl = bundle.getString(FACEBOOK_OAUTH_URL)
githubOauthUrl = bundle.getString(GITHUB_OAUTH_URL)
googleOauthUrl = bundle.getString(GOOGLE_OAUTH_URL)
linkedinOauthUrl = bundle.getString(LINKEDIN_OAUTH_URL)
gitlabOauthUrl = bundle.getString(GITLAB_OAUTH_URL)
wordpressOauthUrl = bundle.getString(WORDPRESS_OAUTH_URL)
casLoginUrl = bundle.getString(CAS_LOGIN_URL)
casToken = bundle.getString(CAS_TOKEN)
casServiceName = bundle.getString(CAS_SERVICE_NAME)
casServiceNameTextColor = bundle.getInt(CAS_SERVICE_NAME_TEXT_COLOR)
casServiceButtonColor = bundle.getInt(CAS_SERVICE_BUTTON_COLOR)
customOauthUrl = bundle.getString(CUSTOM_OAUTH_URL)
customOauthServiceName = bundle.getString(CUSTOM_OAUTH_SERVICE_NAME)
customOauthServiceTextColor = bundle.getInt(CUSTOM_OAUTH_SERVICE_NAME_TEXT_COLOR)
customOauthServiceButtonColor = bundle.getInt(CUSTOM_OAUTH_SERVICE_BUTTON_COLOR)
samlUrl = bundle.getString(SAML_URL)
samlToken = bundle.getString(SAML_TOKEN)
samlServiceName = bundle.getString(SAML_SERVICE_NAME)
samlServiceTextColor = bundle.getInt(SAML_SERVICE_NAME_TEXT_COLOR)
samlServiceButtonColor = bundle.getInt(SAML_SERVICE_BUTTON_COLOR)
totalSocialAccountsEnabled = bundle.getInt(TOTAL_SOCIAL_ACCOUNTS)
isLoginFormEnabled = bundle.getBoolean(IS_LOGIN_FORM_ENABLED)
isNewAccountCreationEnabled = bundle.getBoolean(IS_NEW_ACCOUNT_CREATION_ENABLED)
deepLinkInfo = bundle.getParcelable(DEEP_LINK_INFO)
arguments?.run {
serverName = getString(SERVER_NAME)
state = getString(STATE)
facebookOauthUrl = getString(FACEBOOK_OAUTH_URL)
githubOauthUrl = getString(GITHUB_OAUTH_URL)
googleOauthUrl = getString(GOOGLE_OAUTH_URL)
linkedinOauthUrl = getString(LINKEDIN_OAUTH_URL)
gitlabOauthUrl = getString(GITLAB_OAUTH_URL)
wordpressOauthUrl = getString(WORDPRESS_OAUTH_URL)
casLoginUrl = getString(CAS_LOGIN_URL)
casToken = getString(CAS_TOKEN)
casServiceName = getString(CAS_SERVICE_NAME)
casServiceNameTextColor = getInt(CAS_SERVICE_NAME_TEXT_COLOR)
casServiceButtonColor = getInt(CAS_SERVICE_BUTTON_COLOR)
customOauthUrl = getString(CUSTOM_OAUTH_URL)
customOauthServiceName = getString(CUSTOM_OAUTH_SERVICE_NAME)
customOauthServiceTextColor = getInt(CUSTOM_OAUTH_SERVICE_NAME_TEXT_COLOR)
customOauthServiceButtonColor = getInt(CUSTOM_OAUTH_SERVICE_BUTTON_COLOR)
samlUrl = getString(SAML_URL)
samlToken = getString(SAML_TOKEN)
samlServiceName = getString(SAML_SERVICE_NAME)
samlServiceTextColor = getInt(SAML_SERVICE_NAME_TEXT_COLOR)
samlServiceButtonColor = getInt(SAML_SERVICE_BUTTON_COLOR)
totalSocialAccountsEnabled = getInt(TOTAL_SOCIAL_ACCOUNTS)
isLoginFormEnabled = getBoolean(IS_LOGIN_FORM_ENABLED)
isNewAccountCreationEnabled = getBoolean(IS_NEW_ACCOUNT_CREATION_ENABLED)
deepLinkInfo = getParcelable(DEEP_LINK_INFO)
}
}
......@@ -388,7 +385,7 @@ class LoginOptionsFragment : Fragment(), LoginOptionsView {
}
override fun setupExpandAccountsView() {
ui { _ ->
ui {
expand_more_accounts_container.isVisible = true
var isAccountsCollapsed = true
button_expand_collapse_accounts.setOnClickListener {
......@@ -406,14 +403,14 @@ class LoginOptionsFragment : Fragment(), LoginOptionsView {
}
override fun showLoginWithEmailButton() {
ui { _ ->
ui {
button_login_with_email.setOnClickListener { presenter.toLoginWithEmail() }
button_login_with_email.isVisible = true
}
}
override fun showCreateNewAccountButton() {
ui { _ ->
ui {
button_create_an_account.setOnClickListener { presenter.toCreateAccount() }
button_create_an_account.isVisible = true
}
......
......@@ -31,12 +31,10 @@ import javax.inject.Inject
private const val BUNDLE_USER_ID = "user_id"
private const val BUNDLE_AUTH_TOKEN = "auth_token"
fun newInstance(userId: String, authToken: String): Fragment {
return RegisterUsernameFragment().apply {
arguments = Bundle(2).apply {
putString(BUNDLE_USER_ID, userId)
putString(BUNDLE_AUTH_TOKEN, authToken)
}
fun newInstance(userId: String, authToken: String): Fragment = RegisterUsernameFragment().apply {
arguments = Bundle(2).apply {
putString(BUNDLE_USER_ID, userId)
putString(BUNDLE_AUTH_TOKEN, authToken)
}
}
......@@ -53,13 +51,10 @@ class RegisterUsernameFragment : Fragment(), RegisterUsernameView {
super.onCreate(savedInstanceState)
AndroidSupportInjection.inject(this)
val bundle = arguments
if (bundle != null) {
userId = bundle.getString(BUNDLE_USER_ID)
authToken = bundle.getString(BUNDLE_AUTH_TOKEN)
} else {
requireNotNull(bundle) { "no arguments supplied when the fragment was instantiated" }
}
arguments?.run {
userId = getString(BUNDLE_USER_ID, "")
authToken = getString(BUNDLE_AUTH_TOKEN, "")
} ?: requireNotNull(arguments) { "no arguments supplied when the fragment was instantiated" }
}
override fun onCreateView(
......
......@@ -75,7 +75,7 @@ class SignupFragment : Fragment(), SignupView {
}
private fun setupOnClickListener() =
ui { _ ->
ui {
button_register.setOnClickListener {
presenter.signup(
text_username.textContent,
......
......@@ -25,12 +25,10 @@ import io.reactivex.disposables.Disposable
import kotlinx.android.synthetic.main.fragment_authentication_two_fa.*
import javax.inject.Inject
fun newInstance(username: String, password: String): Fragment {
return TwoFAFragment().apply {
arguments = Bundle(2).apply {
putString(BUNDLE_USERNAME, username)
putString(BUNDLE_PASSWORD, password)
}
fun newInstance(username: String, password: String): Fragment = TwoFAFragment().apply {
arguments = Bundle(2).apply {
putString(BUNDLE_USERNAME, username)
putString(BUNDLE_PASSWORD, password)
}
}
......@@ -50,13 +48,10 @@ class TwoFAFragment : Fragment(), TwoFAView {
super.onCreate(savedInstanceState)
AndroidSupportInjection.inject(this)
val bundle = arguments
if (bundle != null) {
username = bundle.getString(BUNDLE_USERNAME)
password = bundle.getString(BUNDLE_PASSWORD)
} else {
requireNotNull(bundle) { "no arguments supplied when the fragment was instantiated" }
}
arguments?.run {
username = getString(BUNDLE_USERNAME, "")
password = getString(BUNDLE_PASSWORD, "")
} ?: requireNotNull(arguments) { "no arguments supplied when the fragment was instantiated" }
}
override fun onCreateView(
......
package chat.rocket.android.chatdetails.adapter
import android.content.Context
import DrawableHelper
import android.view.View
import android.widget.ImageView
import android.widget.TextView
......
......@@ -11,9 +11,15 @@ import chat.rocket.android.util.extensions.inflate
class ChatDetailsAdapter: RecyclerView.Adapter<OptionViewHolder>() {
private val options: MutableList<Option> = ArrayList()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): OptionViewHolder = OptionViewHolder(parent.inflate(R.layout.item_detail_option))
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): OptionViewHolder = OptionViewHolder(parent.inflate(R.layout.item_detail_option))
override fun onBindViewHolder(holder: OptionViewHolder, position: Int) = holder.bindViews(OptionItemHolder(options[position]))
override fun onBindViewHolder(
holder: OptionViewHolder,
position: Int
) = holder.bindViews(OptionItemHolder(options[position]))
override fun getItemCount(): Int = options.size
......
......@@ -32,14 +32,12 @@ fun newInstance(
chatRoomType: String,
isSubscribed: Boolean,
disableMenu: Boolean
): ChatDetailsFragment {
return ChatDetailsFragment().apply {
arguments = Bundle(4).apply {
putString(BUNDLE_CHAT_ROOM_ID, chatRoomId)
putString(BUNDLE_CHAT_ROOM_TYPE, chatRoomType)
putBoolean(BUNDLE_IS_SUBSCRIBED, isSubscribed)
putBoolean(BUNDLE_DISABLE_MENU, disableMenu)
}
): ChatDetailsFragment = ChatDetailsFragment().apply {
arguments = Bundle(4).apply {
putString(BUNDLE_CHAT_ROOM_ID, chatRoomId)
putString(BUNDLE_CHAT_ROOM_TYPE, chatRoomType)
putBoolean(BUNDLE_IS_SUBSCRIBED, isSubscribed)
putBoolean(BUNDLE_DISABLE_MENU, disableMenu)
}
}
......@@ -66,15 +64,13 @@ class ChatDetailsFragment : Fragment(), ChatDetailsView {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
AndroidSupportInjection.inject(this)
val bundle = arguments
if (bundle != null) {
chatRoomId = bundle.getString(BUNDLE_CHAT_ROOM_ID)
chatRoomType = bundle.getString(BUNDLE_CHAT_ROOM_TYPE)
isSubscribed = bundle.getBoolean(BUNDLE_IS_SUBSCRIBED)
disableMenu = bundle.getBoolean(BUNDLE_DISABLE_MENU)
} else {
requireNotNull(bundle) { "no arguments supplied when the fragment was instantiated" }
}
arguments?.run {
chatRoomId = getString(BUNDLE_CHAT_ROOM_ID)
chatRoomType = getString(BUNDLE_CHAT_ROOM_TYPE)
isSubscribed = getBoolean(BUNDLE_IS_SUBSCRIBED)
disableMenu = getBoolean(BUNDLE_DISABLE_MENU)
} ?: requireNotNull(arguments) { "no arguments supplied when the fragment was instantiated" }
}
override fun onCreateView(
......
......@@ -5,9 +5,10 @@ import androidx.lifecycle.ViewModelProvider
import chat.rocket.android.db.ChatRoomDao
import javax.inject.Inject
class ChatDetailsViewModelFactory @Inject constructor(private val chatRoomDao: ChatRoomDao) : ViewModelProvider.NewInstanceFactory() {
class ChatDetailsViewModelFactory @Inject constructor(
private val chatRoomDao: ChatRoomDao
) : ViewModelProvider.NewInstanceFactory() {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>) =
ChatDetailsViewModel(chatRoomDao) as T
override fun <T : ViewModel?> create(modelClass: Class<T>) = ChatDetailsViewModel(chatRoomDao) as T
}
\ No newline at end of file
......@@ -12,7 +12,10 @@ import com.facebook.drawee.backends.pipeline.Fresco
import kotlinx.android.synthetic.main.item_action_button.view.*
import timber.log.Timber
class ActionsListAdapter(actions: List<Action>, var actionAttachmentOnClickListener: ActionAttachmentOnClickListener) : RecyclerView.Adapter<ActionsListAdapter.ViewHolder>() {
class ActionsListAdapter(
actions: List<Action>,
var actionAttachmentOnClickListener: ActionAttachmentOnClickListener
) : RecyclerView.Adapter<ActionsListAdapter.ViewHolder>() {
var actions: List<Action> = actions
......@@ -62,9 +65,7 @@ class ActionsListAdapter(actions: List<Action>, var actionAttachmentOnClickListe
return ViewHolder(view)
}
override fun getItemCount(): Int {
return actions.size
}
override fun getItemCount(): Int = actions.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val action = actions[position]
......
......@@ -76,13 +76,9 @@ class ChatRoomAdapter(
}
}
override fun getItemViewType(position: Int): Int {
return dataSet[position].viewType
}
override fun getItemViewType(position: Int): Int = dataSet[position].viewType
override fun getItemCount(): Int {
return dataSet.size
}
override fun getItemCount(): Int = dataSet.size
override fun onBindViewHolder(holder: BaseViewHolder<*>, position: Int) {
if (holder !is MessageViewHolder) {
......@@ -174,12 +170,12 @@ class ChatRoomAdapter(
Timber.d("index: $index")
if (index > -1) {
dataSet[index] = message
dataSet.forEachIndexed { index, viewModel ->
dataSet.forEachIndexed { ind, viewModel ->
if (viewModel.messageId == message.messageId) {
if (viewModel.nextDownStreamMessage == null) {
viewModel.reactions = message.reactions
}
notifyItemChanged(index)
notifyItemChanged(ind)
}
}
// Delete message only if current is a system message update, i.e.: Message Removed
......@@ -258,10 +254,10 @@ class ChatRoomAdapter(
actionSelectListener?.editMessage(roomId, id, message.message)
}
R.id.action_message_star -> {
actionSelectListener?.toogleStar(id, !item.isChecked)
actionSelectListener?.toggleStar(id, !item.isChecked)
}
R.id.action_message_unpin -> {
actionSelectListener?.tooglePin(id, !item.isChecked)
actionSelectListener?.togglePin(id, !item.isChecked)
}
R.id.action_message_delete -> {
actionSelectListener?.deleteMessage(roomId, id)
......@@ -298,9 +294,9 @@ class ChatRoomAdapter(
fun editMessage(roomId: String, messageId: String, text: String)
fun toogleStar(id: String, star: Boolean)
fun toggleStar(id: String, star: Boolean)
fun tooglePin(id: String, pin: Boolean)
fun togglePin(id: String, pin: Boolean)
fun deleteMessage(roomId: String, id: String)
......
......@@ -24,8 +24,8 @@ class RoomSuggestionsAdapter : SuggestionsAdapter<RoomSuggestionsViewHolder>("#"
override fun bind(item: SuggestionModel, itemClickListener: SuggestionsAdapter.ItemClickListener?) {
item as ChatRoomSuggestionUiModel
with(itemView) {
val fullname = itemView.findViewById<TextView>(R.id.text_fullname)
val name = itemView.findViewById<TextView>(R.id.text_name)
val fullname = findViewById<TextView>(R.id.text_fullname)
val name = findViewById<TextView>(R.id.text_name)
name.text = item.name
fullname.text = item.fullName
setOnClickListener {
......
......@@ -8,15 +8,14 @@ import chat.rocket.android.util.extensions.content
import chat.rocket.android.util.extensions.openTabbedUrl
import kotlinx.android.synthetic.main.message_url_preview.view.*
class UrlPreviewViewHolder(itemView: View,
listener: ActionsListener,
reactionListener: EmojiReactionListener? = null)
: BaseViewHolder<UrlPreviewUiModel>(itemView, listener, reactionListener) {
class UrlPreviewViewHolder(
itemView: View,
listener: ActionsListener,
reactionListener: EmojiReactionListener? = null
) : BaseViewHolder<UrlPreviewUiModel>(itemView, listener, reactionListener) {
init {
with(itemView) {
setupActionMenu(url_preview_layout)
}
setupActionMenu(itemView.url_preview_layout)
}
override fun bindViews(data: UrlPreviewUiModel) {
......
......@@ -182,8 +182,8 @@ class ChatRoomPresenter @Inject constructor(
chatRoomId?.let {
manager.addRoomChannel(it, roomChangesChannel)
for (room in roomChangesChannel) {
dbManager.getRoom(room.id)?.let {
view.onRoomUpdated(roomMapper.map(chatRoom = it, showLastMessage = true))
dbManager.getRoom(room.id)?.let { chatRoom ->
view.onRoomUpdated(roomMapper.map(chatRoom = chatRoom, showLastMessage = true))
}
}
}
......@@ -321,12 +321,13 @@ class ChatRoomPresenter @Inject constructor(
if (messageId == null) {
val id = UUID.randomUUID().toString()
val username = userHelper.username()
val user = userHelper.user()
val newMessage = Message(
id = id,
roomId = chatRoomId,
message = text,
timestamp = Instant.now().toEpochMilli(),
sender = SimpleUser(null, username, username),
sender = SimpleUser(user?.id, user?.username ?: username, user?.name),
attachments = null,
avatar = currentServer.avatarUrl(username ?: ""),
channels = null,
......@@ -1073,7 +1074,7 @@ class ChatRoomPresenter @Inject constructor(
launchUI(strategy) {
try {
messagesRepository.getById(messageId)?.let { message ->
getChatRoomAsync(message.roomId)?.let { chatRoom ->
getChatRoomAsync(message.roomId)?.let {
val models = mapper.map(message)
models.firstOrNull()?.permalink?.let {
view.copyToClipboard(it)
......@@ -1294,9 +1295,7 @@ class ChatRoomPresenter @Inject constructor(
* @param unfinishedMessage The unfinished message to save.
*/
fun saveDraftMessage(unfinishedMessage: String) {
if (unfinishedMessage.isNotBlank()) {
localRepository.save(draftKey, unfinishedMessage)
}
localRepository.save(draftKey, unfinishedMessage)
}
fun clearDraftMessage() {
......
......@@ -31,18 +31,16 @@ fun Context.chatRoomIntent(
isCreator: Boolean = false,
isFavorite: Boolean = false,
chatRoomMessage: String? = null
): Intent {
return Intent(this, ChatRoomActivity::class.java).apply {
putExtra(INTENT_CHAT_ROOM_ID, chatRoomId)
putExtra(INTENT_CHAT_ROOM_NAME, chatRoomName)
putExtra(INTENT_CHAT_ROOM_TYPE, chatRoomType)
putExtra(INTENT_CHAT_ROOM_IS_READ_ONLY, isReadOnly)
putExtra(INTENT_CHAT_ROOM_LAST_SEEN, chatRoomLastSeen)
putExtra(INTENT_CHAT_IS_SUBSCRIBED, isSubscribed)
putExtra(INTENT_CHAT_ROOM_IS_CREATOR, isCreator)
putExtra(INTENT_CHAT_ROOM_IS_FAVORITE, isFavorite)
putExtra(INTENT_CHAT_ROOM_MESSAGE, chatRoomMessage)
}
): Intent = Intent(this, ChatRoomActivity::class.java).apply {
putExtra(INTENT_CHAT_ROOM_ID, chatRoomId)
putExtra(INTENT_CHAT_ROOM_NAME, chatRoomName)
putExtra(INTENT_CHAT_ROOM_TYPE, chatRoomType)
putExtra(INTENT_CHAT_ROOM_IS_READ_ONLY, isReadOnly)
putExtra(INTENT_CHAT_ROOM_LAST_SEEN, chatRoomLastSeen)
putExtra(INTENT_CHAT_IS_SUBSCRIBED, isSubscribed)
putExtra(INTENT_CHAT_ROOM_IS_CREATOR, isCreator)
putExtra(INTENT_CHAT_ROOM_IS_FAVORITE, isFavorite)
putExtra(INTENT_CHAT_ROOM_MESSAGE, chatRoomMessage)
}
private const val INTENT_CHAT_ROOM_ID = "chat_room_id"
......@@ -81,42 +79,44 @@ class ChatRoomActivity : AppCompatActivity(), HasSupportFragmentInjector {
return
}
val chatRoomId = intent.getStringExtra(INTENT_CHAT_ROOM_ID)
requireNotNull(chatRoomId) { "no chat_room_id provided in Intent extras" }
with(intent) {
val chatRoomId = getStringExtra(INTENT_CHAT_ROOM_ID)
requireNotNull(chatRoomId) { "no chat_room_id provided in Intent extras" }
val chatRoomName = intent.getStringExtra(INTENT_CHAT_ROOM_NAME)
requireNotNull(chatRoomName) { "no chat_room_name provided in Intent extras" }
val chatRoomName = getStringExtra(INTENT_CHAT_ROOM_NAME)
requireNotNull(chatRoomName) { "no chat_room_name provided in Intent extras" }
val chatRoomType = intent.getStringExtra(INTENT_CHAT_ROOM_TYPE)
requireNotNull(chatRoomType) { "no chat_room_type provided in Intent extras" }
val chatRoomType = getStringExtra(INTENT_CHAT_ROOM_TYPE)
requireNotNull(chatRoomType) { "no chat_room_type provided in Intent extras" }
val isReadOnly = intent.getBooleanExtra(INTENT_CHAT_ROOM_IS_READ_ONLY, true)
val isReadOnly = getBooleanExtra(INTENT_CHAT_ROOM_IS_READ_ONLY, true)
val isCreator = intent.getBooleanExtra(INTENT_CHAT_ROOM_IS_CREATOR, false)
val isCreator = getBooleanExtra(INTENT_CHAT_ROOM_IS_CREATOR, false)
val isFavorite = intent.getBooleanExtra(INTENT_CHAT_ROOM_IS_FAVORITE, false)
val isFavorite = getBooleanExtra(INTENT_CHAT_ROOM_IS_FAVORITE, false)
val chatRoomLastSeen = intent.getLongExtra(INTENT_CHAT_ROOM_LAST_SEEN, -1)
val chatRoomLastSeen = getLongExtra(INTENT_CHAT_ROOM_LAST_SEEN, -1)
val isSubscribed = intent.getBooleanExtra(INTENT_CHAT_IS_SUBSCRIBED, true)
val isSubscribed = getBooleanExtra(INTENT_CHAT_IS_SUBSCRIBED, true)
val chatRoomMessage = intent.getStringExtra(INTENT_CHAT_ROOM_MESSAGE)
val chatRoomMessage = getStringExtra(INTENT_CHAT_ROOM_MESSAGE)
setupToolbar()
setupToolbar()
if (supportFragmentManager.findFragmentByTag(TAG_CHAT_ROOM_FRAGMENT) == null) {
addFragment(TAG_CHAT_ROOM_FRAGMENT, R.id.fragment_container) {
newInstance(
chatRoomId,
chatRoomName,
chatRoomType,
isReadOnly,
chatRoomLastSeen,
isSubscribed,
isCreator,
isFavorite,
chatRoomMessage
)
if (supportFragmentManager.findFragmentByTag(TAG_CHAT_ROOM_FRAGMENT) == null) {
addFragment(TAG_CHAT_ROOM_FRAGMENT, R.id.fragment_container) {
newInstance(
chatRoomId,
chatRoomName,
chatRoomType,
isReadOnly,
chatRoomLastSeen,
isSubscribed,
isCreator,
isFavorite,
chatRoomMessage
)
}
}
}
}
......
......@@ -111,19 +111,17 @@ fun newInstance(
isCreator: Boolean = false,
isFavorite: Boolean = false,
chatRoomMessage: String? = null
): Fragment {
return ChatRoomFragment().apply {
arguments = Bundle(1).apply {
putString(BUNDLE_CHAT_ROOM_ID, chatRoomId)
putString(BUNDLE_CHAT_ROOM_NAME, chatRoomName)
putString(BUNDLE_CHAT_ROOM_TYPE, chatRoomType)
putBoolean(BUNDLE_IS_CHAT_ROOM_READ_ONLY, isReadOnly)
putLong(BUNDLE_CHAT_ROOM_LAST_SEEN, chatRoomLastSeen)
putBoolean(BUNDLE_CHAT_ROOM_IS_SUBSCRIBED, isSubscribed)
putBoolean(BUNDLE_CHAT_ROOM_IS_CREATOR, isCreator)
putBoolean(BUNDLE_CHAT_ROOM_IS_FAVORITE, isFavorite)
putString(BUNDLE_CHAT_ROOM_MESSAGE, chatRoomMessage)
}
): Fragment = ChatRoomFragment().apply {
arguments = Bundle(1).apply {
putString(BUNDLE_CHAT_ROOM_ID, chatRoomId)
putString(BUNDLE_CHAT_ROOM_NAME, chatRoomName)
putString(BUNDLE_CHAT_ROOM_TYPE, chatRoomType)
putBoolean(BUNDLE_IS_CHAT_ROOM_READ_ONLY, isReadOnly)
putLong(BUNDLE_CHAT_ROOM_LAST_SEEN, chatRoomLastSeen)
putBoolean(BUNDLE_CHAT_ROOM_IS_SUBSCRIBED, isSubscribed)
putBoolean(BUNDLE_CHAT_ROOM_IS_CREATOR, isCreator)
putBoolean(BUNDLE_CHAT_ROOM_IS_FAVORITE, isFavorite)
putString(BUNDLE_CHAT_ROOM_MESSAGE, chatRoomMessage)
}
}
......@@ -259,7 +257,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
button_fab.hide()
newMessageCount = 0
} else {
if (dy < 0 && !button_fab.isVisible) {
if (dy < 0 && isAdded && !button_fab.isVisible) {
button_fab.show()
if (newMessageCount != 0) text_count.isVisible = true
}
......@@ -272,20 +270,17 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
AndroidSupportInjection.inject(this)
setHasOptionsMenu(true)
val bundle = arguments
if (bundle != null) {
chatRoomId = bundle.getString(BUNDLE_CHAT_ROOM_ID)
chatRoomName = bundle.getString(BUNDLE_CHAT_ROOM_NAME)
chatRoomType = bundle.getString(BUNDLE_CHAT_ROOM_TYPE)
isReadOnly = bundle.getBoolean(BUNDLE_IS_CHAT_ROOM_READ_ONLY)
isSubscribed = bundle.getBoolean(BUNDLE_CHAT_ROOM_IS_SUBSCRIBED)
chatRoomLastSeen = bundle.getLong(BUNDLE_CHAT_ROOM_LAST_SEEN)
isCreator = bundle.getBoolean(BUNDLE_CHAT_ROOM_IS_CREATOR)
isFavorite = bundle.getBoolean(BUNDLE_CHAT_ROOM_IS_FAVORITE)
chatRoomMessage = bundle.getString(BUNDLE_CHAT_ROOM_MESSAGE)
} else {
requireNotNull(bundle) { "no arguments supplied when the fragment was instantiated" }
}
arguments?.run {
chatRoomId = getString(BUNDLE_CHAT_ROOM_ID, "")
chatRoomName = getString(BUNDLE_CHAT_ROOM_NAME, "")
chatRoomType = getString(BUNDLE_CHAT_ROOM_TYPE, "")
isReadOnly = getBoolean(BUNDLE_IS_CHAT_ROOM_READ_ONLY)
isSubscribed = getBoolean(BUNDLE_CHAT_ROOM_IS_SUBSCRIBED)
chatRoomLastSeen = getLong(BUNDLE_CHAT_ROOM_LAST_SEEN)
isCreator = getBoolean(BUNDLE_CHAT_ROOM_IS_CREATOR)
isFavorite = getBoolean(BUNDLE_CHAT_ROOM_IS_FAVORITE)
chatRoomMessage = getString(BUNDLE_CHAT_ROOM_MESSAGE)
} ?: requireNotNull(arguments) { "no arguments supplied when the fragment was instantiated" }
adapter = ChatRoomAdapter(chatRoomId, chatRoomType, chatRoomName, this, reactionListener = this, navigator = navigator)
}
......@@ -758,21 +753,20 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
ui {
text_connection_status.fadeIn()
handler.removeCallbacks(dismissStatus)
when (state) {
text_connection_status.text = when (state) {
is State.Connected -> {
text_connection_status.text = getString(R.string.status_connected)
handler.postDelayed(dismissStatus, 2000)
getString(R.string.status_connected)
}
is State.Disconnected -> getString(R.string.status_disconnected)
is State.Connecting -> getString(R.string.status_connecting)
is State.Authenticating -> getString(R.string.status_authenticating)
is State.Disconnecting -> getString(R.string.status_disconnecting)
is State.Waiting -> getString(R.string.status_waiting, state.seconds)
else -> {
handler.postDelayed(dismissStatus, 500)
""
}
is State.Disconnected ->
text_connection_status.text = getString(R.string.status_disconnected)
is State.Connecting ->
text_connection_status.text = getString(R.string.status_connecting)
is State.Authenticating ->
text_connection_status.text = getString(R.string.status_authenticating)
is State.Disconnecting ->
text_connection_status.text = getString(R.string.status_disconnecting)
is State.Waiting ->
text_connection_status.text = getString(R.string.status_waiting, state.seconds)
}
}
}
......@@ -1098,7 +1092,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
presenter.editMessage(roomId, messageId, text)
}
override fun toogleStar(id: String, star: Boolean) {
override fun toggleStar(id: String, star: Boolean) {
if (star) {
presenter.starMessage(id)
} else {
......@@ -1106,7 +1100,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
}
}
override fun tooglePin(id: String, pin: Boolean) {
override fun togglePin(id: String, pin: Boolean) {
if (pin) {
presenter.pinMessage(id)
} else {
......
......@@ -44,11 +44,15 @@ private fun ChatRoomFragment.setupSearchMessageMenuItem(menu: Menu, context: Con
.setOnActionExpandListener(object : MenuItem.OnActionExpandListener {
override fun onMenuItemActionExpand(item: MenuItem?): Boolean {
dismissEmojiKeyboard()
removeFavoriteMenuItem(menu)
removeDetailMenuItem(menu)
return true
}
override fun onMenuItemActionCollapse(item: MenuItem?): Boolean {
dismissEmojiKeyboard()
setupFavoriteMenuItem(menu)
setupDetailsMenuItem(menu)
return true
}
})
......@@ -73,7 +77,6 @@ private fun stylizeSearchView(searchView: SearchView, context: Context) {
private fun ChatRoomFragment.setupSearchViewTextListener(searchView: SearchView) {
searchView.onQueryTextListener {
// TODO: We use isSearchTermQueried to avoid querying when the search view is expanded but the user doesn't start typing. Check for a native solution.
if (it.isEmpty() && isSearchTermQueried) {
presenter.loadMessages(chatRoomId, chatRoomType, clearDataSet = true)
} else if (it.isNotEmpty()) {
......@@ -105,10 +108,18 @@ private fun ChatRoomFragment.setupFavoriteMenuItem(menu: Menu) {
private fun ChatRoomFragment.setupDetailsMenuItem(menu: Menu) {
menu.add(
Menu.NONE,
MENU_ACTION_SHOW_DETAILS,
Menu.NONE,
R.string.title_channel_details
Menu.NONE,
MENU_ACTION_SHOW_DETAILS,
Menu.NONE,
R.string.title_channel_details
).setIcon(R.drawable.ic_info_outline_white_24dp)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM)
}
private fun removeFavoriteMenuItem(menu: Menu) {
menu.removeItem(MENU_ACTION_FAVORITE_UNFAVOURITE_CHAT)
}
private fun removeDetailMenuItem(menu: Menu) {
menu.removeItem(MENU_ACTION_SHOW_DETAILS)
}
\ No newline at end of file
......@@ -416,21 +416,19 @@ class UiModelMapper @Inject constructor(
return fullUrl
}
private fun attachmentText(text: String?, attachment: Attachment?, context: Context): String? {
return if (attachment != null) {
private fun attachmentText(text: String?, attachment: Attachment?, context: Context): String? = attachment?.run {
with(context) {
when {
attachment.imageUrl.isNotNullNorEmpty() -> context.getString(R.string.msg_preview_photo)
attachment.videoUrl.isNotNullNorEmpty() -> context.getString(R.string.msg_preview_video)
attachment.audioUrl.isNotNullNorEmpty() -> context.getString(R.string.msg_preview_audio)
attachment.titleLink.isNotNullNorEmpty() &&
attachment.type?.contentEquals("file") == true ->
context.getString(R.string.msg_preview_file)
imageUrl.isNotNullNorEmpty() -> getString(R.string.msg_preview_photo)
videoUrl.isNotNullNorEmpty() -> getString(R.string.msg_preview_video)
audioUrl.isNotNullNorEmpty() -> getString(R.string.msg_preview_audio)
titleLink.isNotNullNorEmpty() &&
type?.contentEquals("file") == true ->
getString(R.string.msg_preview_file)
else -> text
}
} else {
text
}
}
} ?: text
private fun attachmentDescription(attachment: Attachment): String? {
return attachment.description
......@@ -464,11 +462,9 @@ class UiModelMapper @Inject constructor(
subscriptionId = chatRoom.subscriptionId)
}
private fun mapMessagePreview(message: Message): Message {
return when (message.isSystemMessage()) {
false -> stripMessageQuotes(message)
true -> message.copy(message = getSystemMessage(message).toString())
}
private fun mapMessagePreview(message: Message): Message = when (message.isSystemMessage()) {
false -> stripMessageQuotes(message)
true -> message.copy(message = getSystemMessage(message).toString())
}
private fun getReactions(message: Message): List<ReactionUiModel> {
......@@ -535,37 +531,33 @@ class UiModelMapper @Inject constructor(
private fun getTime(timestamp: Long) = DateTimeHelper.getTime(DateTimeHelper.getLocalDateTime(timestamp))
private fun getContent(message: Message): CharSequence {
return when (message.isSystemMessage()) {
true -> getSystemMessage(message)
false -> parser.render(message, currentUsername)
}
private fun getContent(message: Message): CharSequence = when (message.isSystemMessage()) {
true -> getSystemMessage(message)
false -> parser.render(message, currentUsername)
}
private fun getSystemMessage(message: Message): CharSequence {
val content = when (message.type) {
//TODO: Add implementation for Welcome type.
is MessageType.MessageRemoved -> context.getString(R.string.message_removed)
is MessageType.UserJoined -> context.getString(R.string.message_user_joined_channel)
is MessageType.UserLeft -> context.getString(R.string.message_user_left)
is MessageType.UserAdded -> context.getString(R.string.message_user_added_by, message.message, message.sender?.username)
is MessageType.RoomNameChanged -> context.getString(R.string.message_room_name_changed, message.message, message.sender?.username)
is MessageType.UserRemoved -> context.getString(R.string.message_user_removed_by, message.message, message.sender?.username)
is MessageType.MessagePinned -> context.getString(R.string.message_pinned)
is MessageType.UserMuted -> context.getString(R.string.message_muted, message.message, message.sender?.username)
is MessageType.UserUnMuted -> context.getString(R.string.message_unmuted, message.message, message.sender?.username)
is MessageType.SubscriptionRoleAdded -> context.getString(R.string.message_role_add, message.message, message.role, message.sender?.username)
is MessageType.SubscriptionRoleRemoved -> context.getString(R.string.message_role_removed, message.message, message.role, message.sender?.username)
is MessageType.RoomChangedPrivacy -> context.getString(R.string.message_room_changed_privacy, message.message, message.sender?.username)
else -> {
throw InvalidParameterException("Invalid message type: ${message.type}")
val content = with(context) {
when (message.type) {
//TODO: Add implementation for Welcome type.
is MessageType.MessageRemoved -> getString(R.string.message_removed)
is MessageType.UserJoined -> getString(R.string.message_user_joined_channel)
is MessageType.UserLeft -> getString(R.string.message_user_left)
is MessageType.UserAdded -> getString(R.string.message_user_added_by, message.message, message.sender?.username)
is MessageType.RoomNameChanged -> getString(R.string.message_room_name_changed, message.message, message.sender?.username)
is MessageType.UserRemoved -> getString(R.string.message_user_removed_by, message.message, message.sender?.username)
is MessageType.MessagePinned -> getString(R.string.message_pinned)
is MessageType.UserMuted -> getString(R.string.message_muted, message.message, message.sender?.username)
is MessageType.UserUnMuted -> getString(R.string.message_unmuted, message.message, message.sender?.username)
is MessageType.SubscriptionRoleAdded -> getString(R.string.message_role_add, message.message, message.role, message.sender?.username)
is MessageType.SubscriptionRoleRemoved -> getString(R.string.message_role_removed, message.message, message.role, message.sender?.username)
is MessageType.RoomChangedPrivacy -> getString(R.string.message_room_changed_privacy, message.message, message.sender?.username)
else -> throw InvalidParameterException("Invalid message type: ${message.type}")
}
}
val spannableMsg = SpannableStringBuilder(content)
spannableMsg.setSpan(StyleSpan(Typeface.ITALIC), 0, spannableMsg.length,
0)
spannableMsg.setSpan(ForegroundColorSpan(Color.GRAY), 0, spannableMsg.length,
0)
spannableMsg.setSpan(StyleSpan(Typeface.ITALIC), 0, spannableMsg.length, 0)
spannableMsg.setSpan(ForegroundColorSpan(Color.GRAY), 0, spannableMsg.length, 0)
return spannableMsg
}
......
......@@ -36,14 +36,29 @@ class RoomUiModelMapper(
grouped: Boolean = false,
showLastMessage: Boolean = true
): List<ItemHolder<*>> {
val list = ArrayList<ItemHolder<*>>(rooms.size + 4)
val list = ArrayList<ItemHolder<*>>(rooms.size + 5)
var lastType: String? = null
rooms.forEach { room ->
if (grouped && lastType != room.chatRoom.type) {
list.add(HeaderItemHolder(roomType(room.chatRoom.type)))
if (grouped) {
val favRooms = rooms.filter { it.chatRoom.favorite == true }
val unfavRooms = rooms.filterNot { it.chatRoom.favorite == true }
if (favRooms.isNotEmpty()) {
list.add(HeaderItemHolder(context.resources.getString(R.string.header_favorite)))
}
favRooms.forEach { room ->
list.add(RoomItemHolder(map(room, showLastMessage)))
}
unfavRooms.forEach { room ->
if (lastType != room.chatRoom.type) {
list.add(HeaderItemHolder(roomType(room.chatRoom.type)))
}
list.add(RoomItemHolder(map(room, showLastMessage)))
lastType = room.chatRoom.type
}
} else {
rooms.forEach { room ->
list.add(RoomItemHolder(map(room, showLastMessage)))
}
list.add(RoomItemHolder(map(room, showLastMessage)))
lastType = room.chatRoom.type
}
return list
......@@ -62,76 +77,71 @@ class RoomUiModelMapper(
return list
}
private fun mapUser(user: User): RoomUiModel {
return with(user) {
val name = mapName(user.username!!, user.name)
val status = user.status
val avatar = serverUrl.avatarUrl(user.username!!)
val username = user.username!!
private fun mapUser(user: User): RoomUiModel = with(user) {
val name = mapName(user.username!!, user.name)
val status = user.status
val avatar = serverUrl.avatarUrl(user.username!!)
val username = user.username!!
RoomUiModel(
RoomUiModel(
id = user.id,
name = name,
type = roomTypeOf(RoomType.DIRECT_MESSAGE),
avatar = avatar,
status = status,
username = username
)
}
)
}
private fun mapRoom(room: Room, showLastMessage: Boolean = true): RoomUiModel {
return with(room) {
RoomUiModel(
private fun mapRoom(room: Room, showLastMessage: Boolean = true): RoomUiModel = with(room) {
RoomUiModel(
id = id,
name = name!!,
type = type,
avatar = serverUrl.avatarUrl(name!!, isGroupOrChannel = true),
lastMessage = if (showLastMessage) {
mapLastMessage(
lastMessage?.sender?.id, lastMessage?.sender?.username,
lastMessage?.sender?.name, lastMessage?.message,
isDirectMessage = type is RoomType.DirectMessage
lastMessage?.sender?.id, lastMessage?.sender?.username,
lastMessage?.sender?.name, lastMessage?.message,
isDirectMessage = type is RoomType.DirectMessage
)
} else {
null
},
muted = muted.orEmpty(),
writable = isChannelWritable(muted)
)
}
)
}
fun map(chatRoom: ChatRoom, showLastMessage: Boolean = true): RoomUiModel {
return with(chatRoom.chatRoom) {
val isUnread = alert || unread > 0
val type = roomTypeOf(type)
val status = chatRoom.status?.let { userStatusOf(it) }
val roomName = mapName(name, fullname)
val timestamp = mapDate(lastMessageTimestamp ?: updatedAt)
val avatar = if (type is RoomType.DirectMessage) {
serverUrl.avatarUrl(name)
} else {
serverUrl.avatarUrl(name, isGroupOrChannel = true)
}
val unread = mapUnread(unread)
val lastMessage = if (showLastMessage) {
mapLastMessage(
fun map(chatRoom: ChatRoom, showLastMessage: Boolean = true): RoomUiModel = with(chatRoom.chatRoom) {
val isUnread = alert || unread > 0
val type = roomTypeOf(type)
val status = chatRoom.status?.let { userStatusOf(it) }
val roomName = mapName(name, fullname)
val favorite = favorite
val timestamp = mapDate(lastMessageTimestamp ?: updatedAt)
val avatar = if (type is RoomType.DirectMessage) {
serverUrl.avatarUrl(name)
} else {
serverUrl.avatarUrl(name, isGroupOrChannel = true)
}
val unread = mapUnread(unread)
val lastMessage = if (showLastMessage) {
mapLastMessage(
lastMessageUserId,
chatRoom.lastMessageUserName,
chatRoom.lastMessageUserFullName,
lastMessageText,
type is RoomType.DirectMessage
)
} else {
null
}
val hasMentions = mapMentions(userMentions, groupMentions)
val open = open
val lastMessageMarkdown =
lastMessage?.let { Markwon.markdown(context, it.toString()).toString() }
)
} else {
null
}
val hasMentions = mapMentions(userMentions, groupMentions)
val open = open
val lastMessageMarkdown = lastMessage?.let { Markwon.markdown(context, it.toString()).toString() }
RoomUiModel(
RoomUiModel(
id = id,
name = roomName,
type = type,
......@@ -140,14 +150,14 @@ class RoomUiModelMapper(
date = timestamp,
unread = unread,
mentions = hasMentions,
favorite = favorite,
alert = isUnread,
lastMessage = lastMessageMarkdown,
status = status,
username = if (type is RoomType.DirectMessage) name else null,
muted = muted.orEmpty(),
writable = isChannelWritable(muted)
)
}
)
}
private fun isChannelWritable(muted: List<String>?): Boolean {
......@@ -155,14 +165,13 @@ class RoomUiModelMapper(
return canWriteToReadOnlyChannels || !muted.orEmpty().contains(currentUser?.username)
}
private fun roomType(type: String): String {
val resources = context.resources
return when (type) {
RoomType.CHANNEL -> resources.getString(R.string.header_channel)
RoomType.PRIVATE_GROUP -> resources.getString(R.string.header_private_groups)
RoomType.DIRECT_MESSAGE -> resources.getString(R.string.header_direct_messages)
RoomType.LIVECHAT -> resources.getString(R.string.header_live_chats)
else -> resources.getString(R.string.header_unknown)
private fun roomType(type: String): String = with(context.resources) {
when (type) {
RoomType.CHANNEL -> getString(R.string.header_channel)
RoomType.PRIVATE_GROUP -> getString(R.string.header_private_groups)
RoomType.DIRECT_MESSAGE -> getString(R.string.header_direct_messages)
RoomType.LIVECHAT -> getString(R.string.header_live_chats)
else -> getString(R.string.header_unknown)
}
}
......@@ -195,13 +204,11 @@ class RoomUiModelMapper(
}
}
private fun mapUnread(unread: Long): String? {
return when (unread) {
0L -> null
in 1..99 -> unread.toString()
else -> context.getString(R.string.msg_more_than_ninety_nine_unread_messages)
private fun mapUnread(unread: Long): String? = when (unread) {
0L -> null
in 1..99 -> unread.toString()
else -> context.getString(R.string.msg_more_than_ninety_nine_unread_messages)
}
}
private fun mapMentions(userMentions: Long?, groupMentions: Long?): Boolean {
......
......@@ -8,6 +8,7 @@ import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import chat.rocket.android.R
import chat.rocket.android.chatrooms.adapter.model.RoomUiModel
import chat.rocket.android.util.extension.setTextViewAppearance
import chat.rocket.common.model.RoomType
import chat.rocket.common.model.UserStatus
import kotlinx.android.synthetic.main.item_chat.view.*
......@@ -16,12 +17,12 @@ import kotlinx.android.synthetic.main.unread_messages_badge.view.*
class RoomViewHolder(itemView: View, private val listener: (RoomUiModel) -> Unit) :
ViewHolder<RoomItemHolder>(itemView) {
private val resources: Resources = itemView.resources
private val channelIcon: Drawable = resources.getDrawable(R.drawable.ic_hashtag_12dp)
private val groupIcon: Drawable = resources.getDrawable(R.drawable.ic_lock_12_dp)
private val onlineIcon: Drawable = resources.getDrawable(R.drawable.ic_status_online_12dp)
private val awayIcon: Drawable = resources.getDrawable(R.drawable.ic_status_away_12dp)
private val busyIcon: Drawable = resources.getDrawable(R.drawable.ic_status_busy_12dp)
private val offlineIcon: Drawable = resources.getDrawable(R.drawable.ic_status_invisible_12dp)
private val channelIcon: Drawable = resources.getDrawable(R.drawable.ic_hashtag_12dp, null)
private val groupIcon: Drawable = resources.getDrawable(R.drawable.ic_lock_12_dp, null)
private val onlineIcon: Drawable = resources.getDrawable(R.drawable.ic_status_online_12dp, null)
private val awayIcon: Drawable = resources.getDrawable(R.drawable.ic_status_away_12dp, null)
private val busyIcon: Drawable = resources.getDrawable(R.drawable.ic_status_busy_12dp, null)
private val offlineIcon: Drawable = resources.getDrawable(R.drawable.ic_status_invisible_12dp, null)
override fun bindViews(data: RoomItemHolder) {
val room = data.data
......@@ -53,14 +54,14 @@ class RoomViewHolder(itemView: View, private val listener: (RoomUiModel) -> Unit
if (room.unread == null) text_total_unread_messages.text = "!"
if (room.unread != null) text_total_unread_messages.text = room.unread
if (room.mentions) text_total_unread_messages.text = "@${room.unread}"
text_chat_name.setTextAppearance(context, R.style.ChatList_ChatName_Unread_TextView)
text_timestamp.setTextAppearance(context, R.style.ChatList_Timestamp_Unread_TextView)
text_last_message.setTextAppearance(context, R.style.ChatList_LastMessage_Unread_TextView)
text_chat_name.setTextViewAppearance(context, R.style.ChatList_ChatName_Unread_TextView)
text_timestamp.setTextViewAppearance(context, R.style.ChatList_Timestamp_Unread_TextView)
text_last_message.setTextViewAppearance(context, R.style.ChatList_LastMessage_Unread_TextView)
text_total_unread_messages.isVisible = true
} else {
text_chat_name.setTextAppearance(context, R.style.ChatList_ChatName_TextView)
text_timestamp.setTextAppearance(context, R.style.ChatList_Timestamp_TextView)
text_last_message.setTextAppearance(context, R.style.ChatList_LastMessage_TextView)
text_chat_name.setTextViewAppearance(context, R.style.ChatList_ChatName_TextView)
text_timestamp.setTextViewAppearance(context, R.style.ChatList_Timestamp_TextView)
text_last_message.setTextViewAppearance(context, R.style.ChatList_LastMessage_TextView)
text_total_unread_messages.isInvisible = true
}
......@@ -68,20 +69,16 @@ class RoomViewHolder(itemView: View, private val listener: (RoomUiModel) -> Unit
}
}
private fun getRoomDrawable(type: RoomType): Drawable? {
return when (type) {
is RoomType.Channel -> channelIcon
is RoomType.PrivateGroup -> groupIcon
else -> null
}
private fun getRoomDrawable(type: RoomType): Drawable? = when (type) {
is RoomType.Channel -> channelIcon
is RoomType.PrivateGroup -> groupIcon
else -> null
}
private fun getStatusDrawable(status: UserStatus): Drawable {
return when (status) {
is UserStatus.Online -> onlineIcon
is UserStatus.Away -> awayIcon
is UserStatus.Busy -> busyIcon
else -> offlineIcon
}
private fun getStatusDrawable(status: UserStatus): Drawable = when (status) {
is UserStatus.Online -> onlineIcon
is UserStatus.Away -> awayIcon
is UserStatus.Busy -> busyIcon
else -> offlineIcon
}
}
\ No newline at end of file
......@@ -19,22 +19,20 @@ class RoomsAdapter(private val listener: (RoomUiModel) -> Unit) :
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder<*> {
return when (viewType) {
VIEW_TYPE_ROOM -> {
val view = parent.inflate(R.layout.item_chat)
RoomViewHolder(view, listener)
}
VIEW_TYPE_HEADER -> {
val view = parent.inflate(R.layout.item_chatroom_header)
HeaderViewHolder(view)
}
VIEW_TYPE_LOADING -> {
val view = parent.inflate(R.layout.item_loading)
LoadingViewHolder(view)
}
else -> throw IllegalStateException("View type must be either Room, Header or Loading")
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder<*> = when (viewType) {
VIEW_TYPE_ROOM -> {
val view = parent.inflate(R.layout.item_chat)
RoomViewHolder(view, listener)
}
VIEW_TYPE_HEADER -> {
val view = parent.inflate(R.layout.item_chatroom_header)
HeaderViewHolder(view)
}
VIEW_TYPE_LOADING -> {
val view = parent.inflate(R.layout.item_loading)
LoadingViewHolder(view)
}
else -> throw IllegalStateException("View type must be either Room, Header or Loading")
}
override fun getItemCount() = values.size
......@@ -49,13 +47,11 @@ class RoomsAdapter(private val listener: (RoomUiModel) -> Unit) :
}
}
override fun getItemViewType(position: Int): Int {
return when (values[position]) {
is RoomItemHolder -> VIEW_TYPE_ROOM
is HeaderItemHolder -> VIEW_TYPE_HEADER
is LoadingItemHolder -> VIEW_TYPE_LOADING
else -> throw IllegalStateException("View type must be either Room, Header or Loading")
}
override fun getItemViewType(position: Int): Int = when (values[position]) {
is RoomItemHolder -> VIEW_TYPE_ROOM
is HeaderItemHolder -> VIEW_TYPE_HEADER
is LoadingItemHolder -> VIEW_TYPE_LOADING
else -> throw IllegalStateException("View type must be either Room, Header or Loading")
}
override fun onBindViewHolder(holder: ViewHolder<*>, position: Int) {
......
......@@ -12,6 +12,7 @@ data class RoomUiModel(
val date: CharSequence? = null,
val unread: String? = null,
val alert: Boolean = false,
val favorite: Boolean? = false,
val mentions: Boolean = false,
val lastMessage: CharSequence? = null,
val status: UserStatus? = null,
......
......@@ -36,7 +36,6 @@ import chat.rocket.android.helper.SharedPreferenceHelper
import chat.rocket.android.util.extension.onQueryTextListener
import chat.rocket.android.util.extensions.fadeIn
import chat.rocket.android.util.extensions.fadeOut
import chat.rocket.android.util.extensions.ifNotNullNorEmpty
import chat.rocket.android.util.extensions.ifNotNullNotEmpty
import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.showToast
......@@ -69,11 +68,9 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
private var progressDialog: ProgressDialog? = null
companion object {
fun newInstance(chatRoomId: String? = null): ChatRoomsFragment {
return ChatRoomsFragment().apply {
arguments = Bundle(1).apply {
putString(BUNDLE_CHAT_ROOM_ID, chatRoomId)
}
fun newInstance(chatRoomId: String? = null): ChatRoomsFragment = ChatRoomsFragment().apply {
arguments = Bundle(1).apply {
putString(BUNDLE_CHAT_ROOM_ID, chatRoomId)
}
}
}
......@@ -82,9 +79,8 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
super.onCreate(savedInstanceState)
AndroidSupportInjection.inject(this)
setHasOptionsMenu(true)
val bundle = arguments
if (bundle != null) {
chatRoomId = bundle.getString(BUNDLE_CHAT_ROOM_ID)
arguments?.run {
chatRoomId = getString(BUNDLE_CHAT_ROOM_ID)
chatRoomId.ifNotNullNotEmpty { roomId ->
presenter.loadChatRoom(roomId)
chatRoomId = null
......@@ -129,12 +125,14 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
)
)
recycler_view.itemAnimator = DefaultItemAnimator()
recycler_view.adapter = adapter
viewModel.getChatRooms().observe(viewLifecycleOwner, Observer { rooms ->
rooms?.let {
Timber.d("Got items: $it")
adapter.values = it
if (recycler_view.adapter != adapter) {
recycler_view.adapter = adapter
}
if (rooms.isNotEmpty()) {
text_no_data_to_display.isVisible = false
}
......@@ -320,21 +318,20 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
ui {
text_connection_status.fadeIn()
handler.removeCallbacks(dismissStatus)
when (state) {
text_connection_status.text = when (state) {
is State.Connected -> {
text_connection_status.text = getString(R.string.status_connected)
handler.postDelayed(dismissStatus, 2000)
getString(R.string.status_connected)
}
is State.Disconnected -> getString(R.string.status_disconnected)
is State.Connecting -> getString(R.string.status_connecting)
is State.Authenticating -> getString(R.string.status_authenticating)
is State.Disconnecting -> getString(R.string.status_disconnecting)
is State.Waiting -> getString(R.string.status_waiting, state.seconds)
else -> {
handler.postDelayed(dismissStatus, 500)
""
}
is State.Disconnected -> text_connection_status.text =
getString(R.string.status_disconnected)
is State.Connecting -> text_connection_status.text =
getString(R.string.status_connecting)
is State.Authenticating -> text_connection_status.text =
getString(R.string.status_authenticating)
is State.Disconnecting -> text_connection_status.text =
getString(R.string.status_disconnecting)
is State.Waiting -> text_connection_status.text =
getString(R.string.status_waiting, state.seconds)
}
}
}
......
......@@ -45,9 +45,7 @@ class CreateChannelFragment : Fragment(), CreateChannelView, ActionMode.Callback
lateinit var analyticsManager: AnalyticsManager
private var actionMode: ActionMode? = null
private val adapter: MembersAdapter = MembersAdapter {
if (it.username != null) {
processSelectedMember(it.username)
}
it.username?.run { processSelectedMember(this) }
}
private val compositeDisposable = CompositeDisposable()
private var channelType: String = RoomType.CHANNEL
......@@ -294,8 +292,8 @@ class CreateChannelFragment : Fragment(), CreateChannelView, ActionMode.Callback
private fun addChip(chipText: String) {
val chip = Chip(context)
chip.chipText = chipText
chip.isCloseIconEnabled = true
chip.text = chipText
chip.isCloseIconVisible = true
chip.setChipBackgroundColorResource(R.color.icon_grey)
setupChipOnCloseIconClickListener(chip)
chip_group_member.addView(chip)
......@@ -304,7 +302,7 @@ class CreateChannelFragment : Fragment(), CreateChannelView, ActionMode.Callback
private fun setupChipOnCloseIconClickListener(chip: Chip) {
chip.setOnCloseIconClickListener {
removeChip(it)
removeMember((it as Chip).chipText.toString())
removeMember((it as Chip).text.toString())
// whenever we remove a chip we should process the chip group visibility.
processChipGroupVisibility()
}
......
......@@ -40,6 +40,12 @@ import kotlinx.coroutines.experimental.newSingleThreadContext
import kotlinx.coroutines.experimental.withContext
import timber.log.Timber
import java.util.HashSet
import kotlin.collections.ArrayList
import kotlin.collections.HashMap
import kotlin.collections.LinkedHashMap
import kotlin.collections.component1
import kotlin.collections.component2
import kotlin.collections.set
import kotlin.system.measureTimeMillis
class DatabaseManager(val context: Application, val serverUrl: String) {
......@@ -140,7 +146,7 @@ class DatabaseManager(val context: Application, val serverUrl: String) {
batch.forEach {
when (it.type) {
is Type.Removed -> toRemove.add(removeChatRoom(it.data))
is Type.Inserted -> insertChatRoom(it.data)?.let { toInsert.add(it) }
is Type.Inserted -> insertChatRoom(it.data)?.let { room -> toInsert.add(room) }
is Type.Updated -> {
when (it.data) {
is Subscription -> updateSubs[(it.data as Subscription).roomId] = it.data as Subscription
......@@ -186,16 +192,14 @@ class DatabaseManager(val context: Application, val serverUrl: String) {
}
}
fun processMessagesBatch(messages: List<Message>): Job {
return launch(dbManagerContext) {
val list = mutableListOf<Pair<MessageEntity, List<BaseMessageEntity>>>()
messages.forEach { message ->
val pair = createMessageEntities(message)
list.add(pair)
}
sendOperation(Operation.InsertMessages(list))
fun processMessagesBatch(messages: List<Message>): Job = launch(dbManagerContext) {
val list = mutableListOf<Pair<MessageEntity, List<BaseMessageEntity>>>()
messages.forEach { message ->
val pair = createMessageEntities(message)
list.add(pair)
}
sendOperation(Operation.InsertMessages(list))
}
private suspend fun createMessageEntities(message: Message): Pair<MessageEntity, List<BaseMessageEntity>> {
......@@ -213,91 +217,70 @@ class DatabaseManager(val context: Application, val serverUrl: String) {
return Pair(messageEntity, list)
}
private fun createReactions(message: Message): List<BaseMessageEntity>? {
if (message.reactions == null || message.reactions!!.isEmpty()) {
return null
}
val reactions = message.reactions!!
val list = mutableListOf<BaseMessageEntity>()
reactions.keys.forEach { reaction ->
val users = reactions[reaction]
users?.let { users ->
list.add(ReactionEntity(reaction, message.id, users.size, users.joinToString()))
private fun createReactions(message: Message): List<BaseMessageEntity>? = message.reactions?.run {
if (isNotEmpty()) {
val list = mutableListOf<BaseMessageEntity>()
keys.forEach { reaction ->
get(reaction)?.let { reactionValue ->
list.add(ReactionEntity(reaction, message.id, size, reactionValue.joinToString()))
}
}
}
return list
list
} else null
}
private fun createUrlEntities(message: Message): List<BaseMessageEntity>? {
if (message.urls == null || message.urls!!.isEmpty()) {
return null
}
val list = mutableListOf<UrlEntity>()
message.urls!!.forEach { url ->
list.add(UrlEntity(message.id, url.url, url.parsedUrl?.host, url.meta?.title,
url.meta?.description, url.meta?.imageUrl))
}
return list
private fun createUrlEntities(message: Message): List<BaseMessageEntity>? = message.urls?.run {
if (isNotEmpty()) {
val list = mutableListOf<UrlEntity>()
forEach { url ->
list.add(UrlEntity(message.id, url.url, url.parsedUrl?.host, url.meta?.title,
url.meta?.description, url.meta?.imageUrl))
}
list
} else null
}
private fun createChannelRelations(message: Message): List<BaseMessageEntity>? {
if (message.channels == null || message.channels!!.isEmpty()) {
return null
}
val list = mutableListOf<MessageChannels>()
message.channels!!.forEach { channel ->
list.add(MessageChannels(message.id, channel.id, channel.name))
}
return list
private fun createChannelRelations(message: Message): List<BaseMessageEntity>? = message.channels?.run {
if (isNotEmpty()) {
val list = mutableListOf<MessageChannels>()
forEach { channel ->
list.add(MessageChannels(message.id, channel.id, channel.name))
}
list
} else null
}
private suspend fun createMentionRelations(message: Message): List<BaseMessageEntity>? {
if (message.mentions == null || message.mentions!!.isEmpty()) {
return null
}
val list = mutableListOf<MessageMentionsRelation>()
message.mentions!!.filterNot { user -> user.id.isNullOrEmpty() }.forEach { mention ->
insertUserIfMissing(mention)
list.add(MessageMentionsRelation(message.id, mention.id!!))
}
return list
private suspend fun createMentionRelations(message: Message): List<BaseMessageEntity>? = message.mentions?.run {
if (isNotEmpty()) {
val list = mutableListOf<MessageMentionsRelation>()
filterNot { user -> user.id.isNullOrEmpty() }.forEach { mention ->
insertUserIfMissing(mention)
list.add(MessageMentionsRelation(message.id, mention.id!!))
}
list
} else null
}
private suspend fun createFavoriteRelations(message: Message): List<BaseMessageEntity>? {
if (message.starred == null || message.starred!!.isEmpty()) {
return null
}
val list = mutableListOf<MessageFavoritesRelation>()
message.starred!!.filterNot { user -> user.id.isNullOrEmpty() }.forEach { userId ->
insertUserIfMissing(userId)
list.add(MessageFavoritesRelation(message.id, userId.id!!))
}
return list
private suspend fun createFavoriteRelations(message: Message): List<BaseMessageEntity>? = message.starred?.run {
if (isNotEmpty()) {
val list = mutableListOf<MessageFavoritesRelation>()
filterNot { user -> user.id.isNullOrEmpty() }.forEach { userId ->
insertUserIfMissing(userId)
list.add(MessageFavoritesRelation(message.id, userId.id!!))
}
list
} else null
}
private fun createAttachments(message: Message): List<BaseMessageEntity>? {
if (message.attachments == null || message.attachments!!.isEmpty()) {
return null
}
val list = ArrayList<BaseMessageEntity>(message.attachments!!.size)
message.attachments!!.forEach { attachment ->
list.addAll(attachment.asEntity(message.id, context))
}
return list
private fun createAttachments(message: Message): List<BaseMessageEntity>? = message.attachments?.run {
if (isNotEmpty()) {
val list = ArrayList<BaseMessageEntity>(size)
forEach { attachment ->
list.addAll(attachment.asEntity(message.id, context))
}
list
} else null
}
private suspend fun createUpdates(): List<ChatRoomEntity> {
......@@ -375,20 +358,15 @@ class DatabaseManager(val context: Application, val serverUrl: String) {
}
}
private fun mapLastMessageText(message: Message?): String? {
return if (message == null) {
null
private fun mapLastMessageText(message: Message?): String? = message?.run {
if (this.message.isEmpty() && attachments?.isNotEmpty() == true) {
message.attachments?.let { mapAttachmentText(it[0]) }
} else {
return if (message.message.isEmpty() && message.attachments?.isNotEmpty() == true) {
mapAttachmentText(message.attachments!![0])
} else {
message.message
}
this.message
}
}
private fun mapAttachmentText(attachment: Attachment): String =
context.getString(R.string.msg_sent_attachment)
private fun mapAttachmentText(attachment: Attachment): String = context.getString(R.string.msg_sent_attachment)
private suspend fun updateSubscription(data: Subscription): ChatRoomEntity? {
return retryDB("getRoom(${data.roomId}") { chatRoomDao().getSync(data.roomId) }?.let { current ->
......@@ -431,12 +409,10 @@ class DatabaseManager(val context: Application, val serverUrl: String) {
}
}
private suspend fun insertChatRoom(data: BaseRoom): ChatRoomEntity? {
return when (data) {
is Room -> insertRoom(data)
is Subscription -> insertSubscription(data)
else -> null
}
private suspend fun insertChatRoom(data: BaseRoom): ChatRoomEntity? = when (data) {
is Room -> insertRoom(data)
is Subscription -> insertSubscription(data)
else -> null
}
private suspend fun insertRoom(data: Room): ChatRoomEntity? {
......
......@@ -115,12 +115,12 @@ fun Attachment.asEntity(msgId: String, context: Context): List<BaseMessageEntity
val text = mapAttachmentText(text, attachments?.firstOrNull(), context)
val entity = AttachmentEntity(
list.add(AttachmentEntity(
_id = attachmentId,
messageId = msgId,
title = title,
type = type,
description = description,
description = description,
text = text,
titleLink = titleLink,
titleLinkDownload = titleLinkDownload.orFalse(),
......@@ -144,16 +144,14 @@ fun Attachment.asEntity(msgId: String, context: Context): List<BaseMessageEntity
buttonAlignment = buttonAlignment,
hasActions = actions?.isNotEmpty() == true,
hasFields = fields?.isNotEmpty() == true
)
list.add(entity)
))
fields?.forEach { field ->
val entity = AttachmentFieldEntity(
list.add(AttachmentFieldEntity(
attachmentId = attachmentId,
title = field.title,
value = field.value
)
list.add(entity)
))
}
actions?.forEach { action ->
......@@ -175,18 +173,14 @@ fun Attachment.asEntity(msgId: String, context: Context): List<BaseMessageEntity
return list
}
fun mapAttachmentText(text: String?, attachment: Attachment?, context: Context): String? {
return if (attachment != null) {
when {
attachment.imageUrl.isNotNullNorEmpty() -> context.getString(R.string.msg_preview_photo)
attachment.videoUrl.isNotNullNorEmpty() -> context.getString(R.string.msg_preview_video)
attachment.audioUrl.isNotNullNorEmpty() -> context.getString(R.string.msg_preview_audio)
attachment.titleLink.isNotNullNorEmpty() &&
attachment.type?.contentEquals("file") == true ->
context.getString(R.string.msg_preview_file)
else -> text
}
} else {
text
fun mapAttachmentText(text: String?, attachment: Attachment?, context: Context): String? = attachment?.run {
when {
imageUrl.isNotNullNorEmpty() -> context.getString(R.string.msg_preview_photo)
videoUrl.isNotNullNorEmpty() -> context.getString(R.string.msg_preview_video)
audioUrl.isNotNullNorEmpty() -> context.getString(R.string.msg_preview_audio)
titleLink.isNotNullNorEmpty() &&
type?.contentEquals("file") == true ->
context.getString(R.string.msg_preview_file)
else -> text
}
}
} ?: text
\ No newline at end of file
......@@ -36,8 +36,7 @@ class FavoriteMessagesPresenter @Inject constructor(
try {
view.showLoading()
dbManager.getRoom(roomId)?.let {
val favoriteMessages =
client.getFavoriteMessages(roomId, roomTypeOf(it.chatRoom.type), offset)
val favoriteMessages = client.getFavoriteMessages(roomId, roomTypeOf(it.chatRoom.type), offset)
val messageList = mapper.map(favoriteMessages.result, asNotReversed = true)
view.showFavoriteMessages(messageList)
offset += 1 * 30
......
......@@ -25,11 +25,9 @@ import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_favorite_messages.*
import javax.inject.Inject
fun newInstance(chatRoomId: String): Fragment {
return FavoriteMessagesFragment().apply {
arguments = Bundle(1).apply {
putString(INTENT_CHAT_ROOM_ID, chatRoomId)
}
fun newInstance(chatRoomId: String): Fragment = FavoriteMessagesFragment().apply {
arguments = Bundle(1).apply {
putString(INTENT_CHAT_ROOM_ID, chatRoomId)
}
}
......@@ -48,12 +46,9 @@ class FavoriteMessagesFragment : Fragment(), FavoriteMessagesView {
super.onCreate(savedInstanceState)
AndroidSupportInjection.inject(this)
val bundle = arguments
if (bundle != null) {
chatRoomId = bundle.getString(INTENT_CHAT_ROOM_ID)
} else {
requireNotNull(bundle) { "no arguments supplied when the fragment was instantiated" }
}
arguments?.run {
chatRoomId = getString(INTENT_CHAT_ROOM_ID, "")
} ?: requireNotNull(arguments) { "no arguments supplied when the fragment was instantiated" }
}
override fun onCreateView(
......
......@@ -30,11 +30,9 @@ import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_files.*
import javax.inject.Inject
fun newInstance(chatRoomId: String): Fragment {
return FilesFragment().apply {
arguments = Bundle(1).apply {
putString(BUNDLE_CHAT_ROOM_ID, chatRoomId)
}
fun newInstance(chatRoomId: String): Fragment = FilesFragment().apply {
arguments = Bundle(1).apply {
putString(BUNDLE_CHAT_ROOM_ID, chatRoomId)
}
}
......@@ -55,12 +53,9 @@ class FilesFragment : Fragment(), FilesView {
super.onCreate(savedInstanceState)
AndroidSupportInjection.inject(this)
val bundle = arguments
if (bundle != null) {
chatRoomId = bundle.getString(BUNDLE_CHAT_ROOM_ID)
} else {
requireNotNull(bundle) { "no arguments supplied when the fragment was instantiated" }
}
arguments?.run {
chatRoomId = getString(BUNDLE_CHAT_ROOM_ID, "")
} ?: requireNotNull(arguments) { "no arguments supplied when the fragment was instantiated" }
}
override fun onCreateView(
......
......@@ -11,8 +11,7 @@ object AndroidPermissionsHelper {
const val WRITE_EXTERNAL_STORAGE_CODE = 1
fun checkPermission(context: Context, permission: String): Boolean {
return ContextCompat.checkSelfPermission(context, permission) ==
PackageManager.PERMISSION_GRANTED
return ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED
}
fun requestPermission(context: Activity, permission: String, requestCode: Int) {
......
......@@ -53,8 +53,8 @@ object ImageHelper {
)
val toolbar = Toolbar(context).also {
it.inflateMenu(R.menu.image_actions)
it.setOnMenuItemClickListener {
return@setOnMenuItemClickListener when (it.itemId) {
it.setOnMenuItemClickListener { view ->
return@setOnMenuItemClickListener when (view.itemId) {
R.id.action_save_image -> saveImage(context)
else -> true
}
......@@ -62,20 +62,24 @@ object ImageHelper {
val titleSize = context.resources
.getDimensionPixelSize(R.dimen.viewer_toolbar_title)
val titleTextView = TextView(context).also {
it.text = imageName
it.setTextColor(Color.WHITE)
it.setTextSize(TypedValue.COMPLEX_UNIT_PX, titleSize.toFloat())
it.ellipsize = TextUtils.TruncateAt.END
it.setSingleLine()
it.typeface = Typeface.DEFAULT_BOLD
it.setPadding(pad)
val titleTextView = TextView(context).also { tv ->
with(tv) {
text = imageName
setTextColor(Color.WHITE)
setTextSize(TypedValue.COMPLEX_UNIT_PX, titleSize.toFloat())
ellipsize = TextUtils.TruncateAt.END
setSingleLine()
typeface = Typeface.DEFAULT_BOLD
setPadding(pad)
}
}
val backArrowView = ImageView(context).also {
it.setImageResource(R.drawable.ic_arrow_back_white_24dp)
it.setOnClickListener { imageViewer?.onDismiss() }
it.setPadding(0, pad, pad, pad)
val backArrowView = ImageView(context).also { imgView ->
with(imgView) {
setImageResource(R.drawable.ic_arrow_back_white_24dp)
setOnClickListener { imageViewer?.onDismiss() }
setPadding(0, pad, pad, pad)
}
}
val layoutParams = AppBarLayout.LayoutParams(
......@@ -88,14 +92,16 @@ object ImageHelper {
}
val appBarLayout = AppBarLayout(context).also {
it.layoutParams = lparams
it.setBackgroundColor(Color.BLACK)
it.addView(
toolbar, AppBarLayout.LayoutParams(
AppBarLayout.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
with(it) {
layoutParams = lparams
setBackgroundColor(Color.BLACK)
addView(
toolbar, AppBarLayout.LayoutParams(
AppBarLayout.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
)
)
}
}
val builder = ImageViewer.createPipelineDraweeControllerBuilder()
......
......@@ -10,8 +10,9 @@ import chat.rocket.android.util.extensions.inflate
import kotlinx.android.synthetic.main.avatar.view.*
import kotlinx.android.synthetic.main.item_member.view.*
class MembersAdapter(private val listener: (MemberUiModel) -> Unit) :
RecyclerView.Adapter<MembersAdapter.ViewHolder>() {
class MembersAdapter(
private val listener: (MemberUiModel) -> Unit
) : RecyclerView.Adapter<MembersAdapter.ViewHolder>() {
private var dataSet: List<MemberUiModel> = ArrayList()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MembersAdapter.ViewHolder =
......@@ -43,7 +44,8 @@ class MembersAdapter(private val listener: (MemberUiModel) -> Unit) :
fun bind(memberUiModel: MemberUiModel, listener: (MemberUiModel) -> Unit) = with(itemView) {
image_avatar.setImageURI(memberUiModel.avatarUri)
text_member.content = memberUiModel.displayName
text_member.setCompoundDrawablesRelativeWithIntrinsicBounds(DrawableHelper.getUserStatusDrawable(memberUiModel.status, context), null, null, null)
text_member.setCompoundDrawablesRelativeWithIntrinsicBounds(
DrawableHelper.getUserStatusDrawable(memberUiModel.status, context), null, null, null)
setOnClickListener { listener(memberUiModel) }
}
}
......
......@@ -27,11 +27,9 @@ import kotlinx.android.synthetic.main.app_bar_chat_room.*
import kotlinx.android.synthetic.main.fragment_members.*
import javax.inject.Inject
fun newInstance(chatRoomId: String): Fragment {
return MembersFragment().apply {
arguments = Bundle(1).apply {
putString(BUNDLE_CHAT_ROOM_ID, chatRoomId)
}
fun newInstance(chatRoomId: String): Fragment = MembersFragment().apply {
arguments = Bundle(1).apply {
putString(BUNDLE_CHAT_ROOM_ID, chatRoomId)
}
}
......@@ -52,12 +50,9 @@ class MembersFragment : Fragment(), MembersView {
super.onCreate(savedInstanceState)
AndroidSupportInjection.inject(this)
val bundle = arguments
if (bundle != null) {
chatRoomId = bundle.getString(BUNDLE_CHAT_ROOM_ID)
} else {
requireNotNull(bundle) { "no arguments supplied when the fragment was instantiated" }
}
arguments?.run {
chatRoomId = getString(BUNDLE_CHAT_ROOM_ID, "")
} ?: requireNotNull(arguments) { "no arguments supplied when the fragment was instantiated" }
}
override fun onCreateView(
......@@ -80,7 +75,7 @@ class MembersFragment : Fragment(), MembersView {
setupToolbar(total)
if (adapter.itemCount == 0) {
adapter.prependData(dataSet)
if (dataSet.size >= 59) { // TODO Check why the API retorns the specified count -1
if (dataSet.size >= 59) { // TODO Check why the API returns the specified count -1
recycler_view.addOnScrollListener(object :
EndlessRecyclerViewScrollListener(linearLayoutManager) {
override fun onLoadMore(
......
......@@ -25,11 +25,9 @@ import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_mentions.*
import javax.inject.Inject
fun newInstance(chatRoomId: String): Fragment {
return MentionsFragment().apply {
arguments = Bundle(1).apply {
putString(BUNDLE_CHAT_ROOM_ID, chatRoomId)
}
fun newInstance(chatRoomId: String): Fragment = MentionsFragment().apply {
arguments = Bundle(1).apply {
putString(BUNDLE_CHAT_ROOM_ID, chatRoomId)
}
}
......@@ -48,12 +46,9 @@ class MentionsFragment : Fragment(), MentionsView {
super.onCreate(savedInstanceState)
AndroidSupportInjection.inject(this)
val bundle = arguments
if (bundle != null) {
chatRoomId = bundle.getString(BUNDLE_CHAT_ROOM_ID)
} else {
requireNotNull(bundle) { "no arguments supplied when the fragment was instantiated" }
}
arguments?.run {
chatRoomId = getString(BUNDLE_CHAT_ROOM_ID, "")
} ?: requireNotNull(arguments) { "no arguments supplied when the fragment was instantiated" }
}
override fun onCreateView(
......
......@@ -25,11 +25,9 @@ import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_pinned_messages.*
import javax.inject.Inject
fun newInstance(chatRoomId: String): Fragment {
return PinnedMessagesFragment().apply {
arguments = Bundle(1).apply {
putString(BUNDLE_CHAT_ROOM_ID, chatRoomId)
}
fun newInstance(chatRoomId: String): Fragment = PinnedMessagesFragment().apply {
arguments = Bundle(1).apply {
putString(BUNDLE_CHAT_ROOM_ID, chatRoomId)
}
}
......@@ -48,12 +46,9 @@ class PinnedMessagesFragment : Fragment(), PinnedMessagesView {
super.onCreate(savedInstanceState)
AndroidSupportInjection.inject(this)
val bundle = arguments
if (bundle != null) {
chatRoomId = bundle.getString(BUNDLE_CHAT_ROOM_ID)
} else {
requireNotNull(bundle) { "no arguments supplied when the fragment was instantiated" }
}
arguments?.run {
chatRoomId = getString(BUNDLE_CHAT_ROOM_ID, "")
} ?: requireNotNull(arguments) { "no arguments supplied when the fragment was instantiated" }
}
override fun onCreateView(
......
......@@ -38,13 +38,17 @@ class PreferencesFragment : Fragment(), PreferencesView {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupToolbar()
setupListeners()
presenter.loadAnalyticsTrackingInformation()
analyticsManager.logScreenView(ScreenViewEvent.Preferences)
}
override fun onResume() {
setupToolbar()
super.onResume()
}
override fun setupAnalyticsTrackingView(isAnalyticsTrackingEnabled: Boolean) {
if (BuildConfig.FLAVOR == "foss") {
switch_analytics_tracking.isChecked = false
......
......@@ -115,7 +115,7 @@ class ProfilePresenter @Inject constructor(
uriInteractor.getInputStream(uri)
}
}
user?.username?.let { view.reloadUserAvatar(it) }
user?.username?.let { view.reloadUserAvatar(serverUrl.avatarUrl(it)) }
} catch (exception: RocketChatException) {
exception.message?.let {
view.showMessage(it)
......@@ -143,7 +143,7 @@ class ProfilePresenter @Inject constructor(
}
}
user?.username?.let { view.reloadUserAvatar(it) }
user?.username?.let { view.reloadUserAvatar(serverUrl.avatarUrl(it)) }
} catch (exception: RocketChatException) {
exception.message?.let {
view.showMessage(it)
......@@ -163,7 +163,7 @@ class ProfilePresenter @Inject constructor(
user?.id?.let { id ->
retryIO { client.resetAvatar(id) }
}
user?.username?.let { view.reloadUserAvatar(it) }
user?.username?.let { view.reloadUserAvatar(serverUrl.avatarUrl(it)) }
} catch (exception: RocketChatException) {
exception.message?.let {
view.showMessage(it)
......
......@@ -94,11 +94,13 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback {
}
override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
if (resultData != null && resultCode == Activity.RESULT_OK) {
if (requestCode == REQUEST_CODE_FOR_PERFORM_SAF) {
presenter.updateAvatar(resultData.data)
} else if (requestCode == REQUEST_CODE_FOR_PERFORM_CAMERA) {
presenter.preparePhotoAndUpdateAvatar(resultData.extras["data"] as Bitmap)
resultData?.run {
if (resultCode == Activity.RESULT_OK) {
if (requestCode == REQUEST_CODE_FOR_PERFORM_SAF) {
data?.let { presenter.updateAvatar(it) }
} else if (requestCode == REQUEST_CODE_FOR_PERFORM_CAMERA) {
extras?.get("data")?.let { presenter.preparePhotoAndUpdateAvatar(it as Bitmap) }
}
}
}
}
......@@ -203,8 +205,7 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback {
}
private fun setupToolbar() {
(activity as AppCompatActivity?)?.supportActionBar?.title =
getString(R.string.title_profile)
(activity as AppCompatActivity?)?.supportActionBar?.title = getString(R.string.title_profile)
}
private fun setupListeners() {
......@@ -293,19 +294,14 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback {
}
fun showDeleteAccountDialog() {
val passwordEditText = EditText(context)
passwordEditText.hint = getString(R.string.msg_password)
context?.let {
val builder = AlertDialog.Builder(it)
builder.setTitle(R.string.title_are_you_sure)
.setView(passwordEditText)
.setPositiveButton(R.string.action_delete_account) { _, _ ->
presenter.deleteAccount(passwordEditText.text.toString())
}
.setNegativeButton(android.R.string.no) { dialog, _ -> dialog.cancel() }
.create()
.show()
val passwordEText = EditText(context);
val mDialogView = LayoutInflater.from(it).inflate(R.layout.item_account_delete, null)
val mBuilder = AlertDialog.Builder(it)
mBuilder.setView(mDialogView).setPositiveButton(R.string.action_delete_account) { _, _ ->
presenter.deleteAccount(passwordEText.text.toString())
}.setNegativeButton(android.R.string.no) { dialog, _ -> dialog.cancel() }.create().show()
}
}
}
......@@ -12,13 +12,14 @@ import android.os.Build
import android.os.Bundle
import android.os.Parcel
import android.os.Parcelable
import android.text.Html
import android.text.Spanned
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.app.RemoteInput
import android.text.Html
import android.text.Spanned
import androidx.core.content.ContextCompat
import androidx.core.text.HtmlCompat.FROM_HTML_MODE_LEGACY
import chat.rocket.android.R
import chat.rocket.android.main.ui.MainActivity
import chat.rocket.android.server.domain.GetAccountInteractor
......@@ -303,7 +304,11 @@ class PushManager @Inject constructor(
// CharSequence extensions
private fun CharSequence.fromHtml(): Spanned {
return Html.fromHtml(this as String)
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Html.fromHtml(this as String, FROM_HTML_MODE_LEGACY, null, null)
} else {
Html.fromHtml(this as String)
}
}
// NotificationCompat.Builder extensions
......@@ -383,12 +388,12 @@ data class PushMessage(
) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.readString().orEmpty(),
parcel.readString().orEmpty(),
parcel.readParcelable(PushMessage::class.java.classLoader) ?: PushInfo.EMPTY,
parcel.readString(),
parcel.readString(),
parcel.readParcelable(PushMessage::class.java.classLoader),
parcel.readString(),
parcel.readString(),
parcel.readString(),
parcel.readString().orEmpty(),
parcel.readString(),
parcel.readString())
......@@ -433,9 +438,9 @@ data class PushInfo @KotshiConstructor constructor(
}
constructor(parcel: Parcel) : this(
parcel.readString(),
parcel.readString(),
roomTypeOf(parcel.readString()),
parcel.readString().orEmpty(),
parcel.readString().orEmpty(),
roomTypeOf(parcel.readString().orEmpty()),
parcel.readString(),
parcel.readParcelable(PushInfo::class.java.classLoader))
......@@ -481,7 +486,7 @@ data class PushSender @KotshiConstructor constructor(
val name: String?
) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.readString(),
parcel.readString().orEmpty(),
parcel.readString(),
parcel.readString())
......
......@@ -97,8 +97,8 @@ class ConnectionManager(
resubscribeRooms()
temporaryStatus?.let { status ->
client.setTemporaryStatus(status)
temporaryStatus?.let { tempStatus ->
client.setTemporaryStatus(tempStatus)
}
}
is State.Waiting -> {
......@@ -135,9 +135,7 @@ class ConnectionManager(
if (it.type == Type.Updated) {
if (it.data is Room) {
val room = it.data as Room
roomsChannels[it.data.id]?.let { channel ->
channel.offer(room)
}
roomsChannels[it.data.id]?.offer(room)
}
}
}
......@@ -231,7 +229,7 @@ class ConnectionManager(
}
private fun resubscribeRooms() {
roomMessagesChannels.toList().map { (roomId, channel) ->
roomMessagesChannels.toList().map { (roomId, _) ->
client.subscribeRoomMessages(roomId) { _, id ->
Timber.d("Subscribed to $roomId: $id")
subscriptionIdMap[roomId] = id
......
......@@ -14,7 +14,6 @@ import chat.rocket.core.model.Message
import chat.rocket.core.model.Reactions
import chat.rocket.core.model.attachment.Attachment
import chat.rocket.core.model.attachment.Color
import chat.rocket.core.model.attachment.DEFAULT_COLOR_STR
import chat.rocket.core.model.attachment.Field
import chat.rocket.core.model.attachment.actions.Action
import chat.rocket.core.model.attachment.actions.ButtonAction
......@@ -153,7 +152,7 @@ class DatabaseMessageMapper(private val dbManager: DatabaseManager) {
null
}
val attachment = Attachment(
list.add(Attachment(
title = title,
type = type,
description = description,
......@@ -179,10 +178,10 @@ class DatabaseMessageMapper(private val dbManager: DatabaseManager) {
authorLink = authorLink,
fields = fields,
fallback = fallback,
buttonAlignment = if (actions != null && actions.isNotEmpty()) buttonAlignment ?: "vertical" else null,
buttonAlignment = if (actions != null && actions.isNotEmpty()) buttonAlignment
?: "vertical" else null,
actions = actions
)
list.add(attachment)
))
}
}
return list
......
......@@ -46,8 +46,8 @@ class DatabaseMessagesRepository(
dbManager.processMessagesBatch(listOf(message)).join()
}
override suspend fun saveAll(messages: List<Message>) {
dbManager.processMessagesBatch(messages).join()
override suspend fun saveAll(newMessages: List<Message>) {
dbManager.processMessagesBatch(newMessages).join()
}
override suspend fun removeById(id: String) {
......@@ -78,7 +78,7 @@ class DatabaseMessagesRepository(
override suspend fun getLastSyncDate(roomId: String): Long? = withContext(CommonPool) {
retryDB("getLastSync($roomId)") {
dbManager.messageDao().getLastSync(roomId)?.let { it.timestamp }
dbManager.messageDao().getLastSync(roomId)?.timestamp
}
}
}
\ No newline at end of file
......@@ -14,13 +14,8 @@ class SharedPreferencesAccountsRepository(
private val moshi: Moshi
) : AccountsRepository {
override fun save(newAccount: Account) {
val accounts = load()
val newList = accounts.filter { account -> newAccount.serverUrl != account.serverUrl }
.toMutableList()
newList.add(0, newAccount)
save(newList)
override fun save(account: Account) {
save(load().filter { item -> item.serverUrl != item.serverUrl }.toMutableList().apply { add(0, account) })
}
override fun load(): List<Account> {
......@@ -28,15 +23,11 @@ class SharedPreferencesAccountsRepository(
val type = Types.newParameterizedType(List::class.java, Account::class.java)
val adapter = moshi.adapter<List<Account>>(type)
return adapter.fromJson(json) ?: emptyList()
return json?.let { adapter.fromJson(it) ?: emptyList() } ?: emptyList()
}
override fun remove(serverUrl: String) {
val accounts = load()
val newList = accounts.filter { account -> serverUrl != account.serverUrl }
.toMutableList()
save(newList)
save(load().filter { account -> serverUrl != account.serverUrl }.toMutableList())
}
private fun save(accounts: List<Account>) {
......
......@@ -15,8 +15,7 @@ class SharedPrefsBasicAuthRepository(
) : BasicAuthRepository {
override fun save(basicAuth: BasicAuth) {
val newList = load().filter { basicAuth -> basicAuth.host != basicAuth.host }
.toMutableList()
val newList = load().filter { auth -> auth.host != auth.host }.toMutableList()
newList.add(0, basicAuth)
save(newList)
}
......@@ -26,7 +25,7 @@ class SharedPrefsBasicAuthRepository(
val type = Types.newParameterizedType(List::class.java, BasicAuth::class.java)
val adapter = moshi.adapter<List<BasicAuth>>(type)
return adapter.fromJson(json) ?: emptyList()
return json?.let { adapter.fromJson(it) ?: emptyList() } ?: emptyList()
}
private fun save(basicAuths: List<BasicAuth>) {
......
......@@ -106,18 +106,16 @@ class PasswordFragment : Fragment(), PasswordView, ActionMode.Callback {
private fun finishActionMode() = actionMode?.finish()
private fun listenToChanges(): Disposable {
return Observables.combineLatest(
text_new_password.asObservable(),
text_confirm_password.asObservable()
).subscribe {
val newPassword = it.first.toString()
val newPasswordConfirmation = it.second.toString()
if (newPassword.length > 5 && newPassword == newPasswordConfirmation) {
startActionMode()
} else {
finishActionMode()
}
private fun listenToChanges(): Disposable = Observables.combineLatest(
text_new_password.asObservable(),
text_confirm_password.asObservable()
).subscribe {
val newPassword = it.first.toString()
val newPasswordConfirmation = it.second.toString()
if (newPassword.length > 5 && newPassword == newPasswordConfirmation) {
startActionMode()
} else {
finishActionMode()
}
}
......
......@@ -2,6 +2,7 @@ package chat.rocket.android.settings.ui
import android.content.ActivityNotFoundException
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
......@@ -114,8 +115,7 @@ class SettingsFragment : Fragment(), SettingsView, AdapterView.OnItemClickListen
}
private fun setupToolbar() {
(activity as AppCompatActivity?)?.supportActionBar?.title =
getString(R.string.title_settings)
(activity as AppCompatActivity?)?.supportActionBar?.title = getString(R.string.title_settings)
}
private fun shareApp() {
......@@ -128,11 +128,12 @@ class SettingsFragment : Fragment(), SettingsView, AdapterView.OnItemClickListen
}
private fun contactSupport() {
with(Intent(Intent.ACTION_SEND)) {
type = "message/rfc822"
putExtra(Intent.EXTRA_EMAIL, arrayOf("support@rocket.chat"))
putExtra(Intent.EXTRA_SUBJECT, getString(R.string.msg_android_app_support))
putExtra(Intent.EXTRA_TEXT, getDeviceAndAppInformation())
val uriText = "mailto:${"support@rocket.chat"}" +
"?subject=" + Uri.encode(getString(R.string.msg_android_app_support)) +
"&body=" + Uri.encode(getDeviceAndAppInformation())
with(Intent(Intent.ACTION_SENDTO)) {
data = uriText.toUri()
try {
startActivity(Intent.createChooser(this, getString(R.string.msg_send_email)))
} catch (ex: ActivityNotFoundException) {
......
......@@ -40,7 +40,7 @@ class UserDetailsPresenter @Inject constructor(
dbManager.getUser(userId)?.let {
userEntity = it
val avatarUrl =
userEntity.username?.let { currentServer.avatarUrl(avatar = it) }
userEntity.username?.let { username -> currentServer.avatarUrl(avatar = username) }
val username = userEntity.username
val name = userEntity.name
val utcOffset =
......
......@@ -29,11 +29,9 @@ import kotlinx.android.synthetic.main.app_bar_chat_room.*
import kotlinx.android.synthetic.main.fragment_user_details.*
import javax.inject.Inject
fun newInstance(userId: String): Fragment {
return UserDetailsFragment().apply {
arguments = Bundle(1).apply {
putString(BUNDLE_USER_ID, userId)
}
fun newInstance(userId: String): Fragment = UserDetailsFragment().apply {
arguments = Bundle(1).apply {
putString(BUNDLE_USER_ID, userId)
}
}
......@@ -52,12 +50,9 @@ class UserDetailsFragment : Fragment(), UserDetailsView {
super.onCreate(savedInstanceState)
AndroidSupportInjection.inject(this)
val bundle = arguments
if (bundle != null) {
userId = bundle.getString(BUNDLE_USER_ID)
} else {
requireNotNull(bundle) { "no arguments supplied when the fragment was instantiated" }
}
arguments?.run {
userId = getString(BUNDLE_USER_ID, "")
} ?: requireNotNull(arguments) { "no arguments supplied when the fragment was instantiated" }
}
override fun onCreateView(
......
......@@ -191,7 +191,7 @@ class HttpLoggingInterceptor constructor(private val logger: Logger) : Intercept
val responseBody = response.body()
val contentLength = responseBody!!.contentLength()
val bodySize = if (contentLength != -1L) contentLength.toString() + "-byte" else "unknown-length"
val bodySize = if (contentLength != -1L) "$contentLength-byte" else "unknown-length"
val responseStr = if (response.message().isEmpty()) "" else " ${response.message()}"
logger.log("<-- ${response.code()}$responseStr ${response.request().url()}"
+ " (" + tookMs + "ms" + (if (!logHeaders) ", $bodySize body" else "") + ')'.toString())
......
......@@ -69,11 +69,8 @@ fun String.userId(userId: String?): String? {
return userId?.let { this.replace(it, "") }
}
fun String.lowercaseUrl(): String? {
val httpUrl = HttpUrl.parse(this)
val newScheme = httpUrl?.scheme()?.toLowerCase()
return httpUrl?.newBuilder()?.scheme(newScheme)?.build()?.toString()
fun String.lowercaseUrl(): String? = HttpUrl.parse(this)?.run {
newBuilder().scheme(scheme().toLowerCase()).build().toString()
}
fun String?.isNotNullNorEmpty(): Boolean = this != null && this.isNotEmpty()
......
......@@ -80,12 +80,10 @@ fun AppCompatActivity.toPreviousView() {
}
fun Activity.hideKeyboard() {
if (currentFocus != null) {
val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(
currentFocus.windowToken,
InputMethodManager.RESULT_UNCHANGED_SHOWN
)
currentFocus?.run {
(getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager).also {
it.hideSoftInputFromWindow(windowToken, InputMethodManager.RESULT_UNCHANGED_SHOWN)
}
}
}
......
......@@ -51,7 +51,7 @@ fun Uri.getFileSize(context: Context): Int {
fun Uri.getMimeType(context: Context): String {
return if (scheme == ContentResolver.SCHEME_CONTENT) {
context.contentResolver.getType(this)
context.contentResolver?.getType(this) ?: ""
} else {
val fileExtension = MimeTypeMap.getFileExtensionFromUrl(toString())
if (fileExtension != null) {
......
......@@ -27,13 +27,10 @@ class AdminPanelWebViewFragment : DaggerFragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val bundle = arguments
if (bundle != null) {
webPageUrl = bundle.getString(BUNDLE_WEB_PAGE_URL)
userToken = bundle.getString(BUNDLE_USER_TOKEN)
} else {
requireNotNull(bundle) { "no arguments supplied when the fragment was instantiated" }
}
arguments?.run {
webPageUrl = getString(BUNDLE_WEB_PAGE_URL, "")
userToken = getString(BUNDLE_USER_TOKEN, "")
} ?: requireNotNull(arguments) { "no arguments supplied when the fragment was instantiated" }
}
override fun onCreateView(
......@@ -65,7 +62,7 @@ class AdminPanelWebViewFragment : DaggerFragment() {
web_view.webViewClient = object : WebViewClient() {
override fun onPageFinished(view: WebView, url: String) {
super.onPageFinished(view, url)
ui { _ ->
ui {
view_loading.hide()
web_view.evaluateJavascript("Meteor.loginWithToken('$userToken', function() { })") {}
}
......
......@@ -15,7 +15,7 @@ import chat.rocket.android.chatrooms.adapter.RoomsAdapter
* @see RecyclerView.ItemDecoration
*/
class DividerItemDecoration() : RecyclerView.ItemDecoration() {
private lateinit var divider: Drawable
private var divider: Drawable? = null
private var boundStart = 0
private var boundEnd = 0
......@@ -40,10 +40,7 @@ class DividerItemDecoration() : RecyclerView.ItemDecoration() {
// Custom divider will be used.
constructor(context: Context, @DrawableRes drawableResId: Int) : this() {
val customDrawable = ContextCompat.getDrawable(context, drawableResId)
if (customDrawable != null) {
divider = customDrawable
}
divider = ContextCompat.getDrawable(context, drawableResId)
}
override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
......@@ -62,8 +59,8 @@ class DividerItemDecoration() : RecyclerView.ItemDecoration() {
val bottom = child.bottom + params.bottomMargin
val top = bottom - divider.intrinsicHeight
divider.setBounds(left, top, right, bottom)
divider.draw(c)
divider?.setBounds(left, top, right, bottom)
divider?.draw(c)
}
}
......
<vector android:height="24dp" android:viewportHeight="100"
android:viewportWidth="100" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#ededed" android:pathData="M50,100q-24.66,0 -49.32,0c-0.57,0 -0.68,-0.11 -0.68,-0.68Q0,50 0,0.68C0,0.11 0.11,0 0.68,0Q50,0 99.32,0c0.57,0 0.68,0.11 0.68,0.68q0,49.32 0,98.64c0,0.57 -0.11,0.68 -0.68,0.68Q74.66,100 50,100Z"/>
<path android:fillColor="#afafae" android:pathData="M49.42,90C41.72,90 34,90 26.34,90a13.82,13.82 0,0 1,-8.55 -2.75A11.18,11.18 0,0 1,13.22 79a46.76,46.76 0,0 1,3 -21.44,17.06 17.06,0 0,1 6.56,-8.48 15.67,15.67 0,0 1,7.3 -2.28,4.52 4.52,0 0,1 2.84,1c1.62,1 3.18,2.14 4.86,3.05a23.24,23.24 0,0 0,20.34 1.5A25.39,25.39 0,0 0,65 48.51c2.81,-2.26 5.65,-1.76 8.61,-0.71 3.73,1.32 6.24,4 8.11,7.39A35.18,35.18 0,0 1,85.31 67,54.2 54.2,0 0,1 86,77.31a12.47,12.47 0,0 1,-4 9.28,12.65 12.65,0 0,1 -7.33,3.22c-3.9,0.4 -7.82,0.13 -11.73,0.17C58.41,90 53.92,90 49.42,90Z"/>
<path android:fillColor="#afafae" android:pathData="M69.35,30.28c0.4,10.36 -8.74,20 -19.89,19.93s-19.92,-9.58 -19.89,-20 8.81,-19.93 20,-19.88C60.64,10.35 69.77,19.87 69.35,30.28Z"/>
</vector>
......@@ -10,6 +10,7 @@
android:layout_width="120dp"
android:layout_height="120dp"
android:layout_centerHorizontal="true"
android:background="@drawable/bg_empty_user_avatar"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
......
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context="chat.rocket.android.about.ui.AboutFragment">
<ImageView
android:id="@+id/image_app_name"
android:layout_width="wrap_content"
android:layout_height="60dp"
android:src="@drawable/ic_app_name"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="50dp"
android:layout_gravity="center">
<ImageView
android:layout_width="160dp"
android:layout_height="160dp"
android:src="@drawable/ic_launcher_foreground"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/image_app_name"
android:adjustViewBounds="true"
android:scaleX="1.5"
android:scaleY="1.5" />
<ImageView
android:id="@+id/image_app_name"
android:layout_width="wrap_content"
android:layout_height="60dp"
android:src="@drawable/ic_app_name"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<TextView
android:id="@+id/text_version_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="Version alpha2.0.1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/image_app_name"
android:layout_marginTop="16dp"
android:textColor="@color/colorSecondaryText"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"/>
<ImageView
android:layout_width="160dp"
android:layout_height="160dp"
android:adjustViewBounds="true"
android:scaleX="1.5"
android:scaleY="1.5"
android:src="@drawable/ic_launcher_foreground"
app:layout_constraintBottom_toTopOf="@id/image_app_name"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/text_build_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="Build # 2000"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_version_name"
android:layout_marginTop="8dp"
android:textColor="@color/colorSecondaryText" />
<TextView
android:id="@+id/text_version_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="Version alpha2.0.1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/image_app_name"
android:layout_marginTop="16dp"
android:textColor="@color/colorSecondaryText"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"/>
<TextView
android:id="@+id/text_build_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="Build # 2000"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_version_name"
android:layout_marginTop="8dp"
android:textColor="@color/colorSecondaryText" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="@dimen/screen_edge_left_and_right_margins">
<TextView
android:id="@+id/text_reset_password"
style="@style/Authentication.TextView.Headline"
android:text="@string/title_are_you_sure"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/text_delete_account_password"
style="@style/Authentication.EditText.Border"
android:layout_marginTop="16dp"
android:drawableStart="@drawable/ic_key_black_20dp"
android:hint="@string/msg_password"
android:imeOptions="actionDone"
android:inputType="textPassword"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/text_reset_password" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
......@@ -34,8 +34,8 @@
android:id="@+id/day"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:textAppearance="@style/Message.DayMarker"
tools:text="Wednesday" />
......@@ -51,7 +51,7 @@
layout="@layout/avatar"
android:layout_width="40dp"
android:layout_height="40dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/message_header" />
<LinearLayout
......@@ -95,17 +95,16 @@
android:layout_marginStart="16dp"
android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintLeft_toRightOf="@+id/layout_avatar"
app:layout_constraintStart_toEndOf="@+id/layout_avatar"
app:layout_constraintTop_toBottomOf="@+id/day_marker_layout">
<TextView
android:id="@+id/text_sender"
style="@style/Sender.Name.TextView"
android:layout_width="0dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_weight="1"
android:maxLength="22"
tools:text="User Name with long name" />
<TextView
......@@ -123,7 +122,7 @@
android:layout_marginStart="4dp"
android:text="@string/msg_edited"
android:textStyle="italic"
android:visibility="gone"
android:visibility="invisible"
tools:visibility="visible" />
<ImageView
......@@ -133,7 +132,7 @@
android:layout_gravity="center_vertical"
android:layout_marginStart="4dp"
android:layout_marginTop="2dp"
android:visibility="gone"
android:visibility="invisible"
app:srcCompat="@drawable/ic_action_message_star_24dp"
tools:visibility="visible" />
......@@ -141,7 +140,7 @@
android:id="@+id/read_receipt_view"
android:layout_width="24dp"
android:layout_height="24dp"
android:visibility="gone"
android:visibility="invisible"
app:srcCompat="@drawable/ic_check_unread_24dp"
tools:visibility="visible" />
</LinearLayout>
......
......@@ -21,6 +21,7 @@
android:id="@+id/image_avatar"
android:layout_width="60dp"
android:layout_height="60dp"
android:background="@drawable/bg_empty_user_avatar"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
app:layout_constraintStart_toStartOf="parent"
......
......@@ -324,6 +324,7 @@
<string name="chatroom_header">Kopf</string>
<!--ChatRooms Headers-->
<string name="header_favorite">Favorites</string><!-- TODO - Add proper translation -->
<string name="header_channel">Räume</string>
<string name="header_private_groups">Private Räume</string>
<string name="header_direct_messages">Direkt Nachrichten</string>
......
......@@ -315,6 +315,7 @@
<string name="chatroom_header">Cabezazo</string>
<!--ChatRooms Headers-->
<string name="header_favorite">Favorites</string><!-- TODO - Add proper translation -->
<string name="header_channel">Canales</string>
<string name="header_private_groups">Grupos privados</string>
<string name="header_direct_messages">Mensajes directos</string>
......
......@@ -318,6 +318,7 @@
<string name="chatroom_header">سرپیام</string>
<!--ChatRooms Headers-->
<string name="header_favorite">Favorites</string><!-- TODO - Add proper translation -->
<string name="header_channel">کانال‌ها</string>
<string name="header_private_groups">گروه‌های خصوصی</string>
<string name="header_direct_messages">پیام‌های خصوصی</string>
......
This diff is collapsed.
......@@ -121,7 +121,7 @@
<string name="msg_preview_file">फ़ाइल</string>
<string name="msg_unread_messages">अपठित संदेश</string>
<string name="msg_no_messages_yet">अभी तक कोई पोस्ट नहीं</string>
<string name="msg_build">Build %1$d - %2$s - %3$s</string> <!-- TODO Add translation -->
<string name="msg_build">निर्माण %1$d - %2$s - %3$s</string>
<string name="msg_update_app_version_in_order_to_continue">पुराना सर्वर वर्शन। जारी रखने के लिए सर्वर वर्शन को अद्यतन करने के लिए कृपया सर्वर व्यवस्थापक से संपर्क करें।</string>
<string name="msg_ver_not_recommended">
ऐसा लगता है कि आपका सर्वर संस्करण अनुशंसित संस्करण %1$s के नीचे है।\nआप अभी भी लॉगिन कर सकते हैं लेकिन आप अप्रत्याशित व्यवहार का अनुभव कर सकते हैं
......@@ -319,6 +319,7 @@
<string name="chatroom_header">हैडर</string>
<!--ChatRooms Headers-->
<string name="header_favorite">पसंदीदा</string>
<string name="header_channel">चैनलों</string>
<string name="header_private_groups">निजी समूहों</string>
<string name="header_direct_messages">प्रत्यक्ष संदेश</string>
......
......@@ -315,6 +315,7 @@
<string name="chatroom_header">Intestazione</string>
<!--ChatRooms Headers-->
<string name="header_favorite">Favorites</string><!-- TODO - Add proper translation -->
<string name="header_channel">Canali</string>
<string name="header_private_groups">Gruppi Privati</string>
<string name="header_direct_messages">Messaggi Diretti</string>
......
......@@ -319,6 +319,7 @@
<!--ChatRooms Headers-->
<string name="header_favorite">Favorites</string><!-- TODO - Add proper translation -->
<string name="header_channel">チャンネル</string>
<string name="header_private_groups">プライベートグループ</string>
<string name="header_direct_messages">ダイレクトメッセージ</string>
......
......@@ -317,6 +317,7 @@
<string name="chatroom_header">Cabeçalho</string>
<!--ChatRooms Headers-->
<string name="header_favorite">Favoritos</string>
<string name="header_channel">Canais</string>
<string name="header_private_groups">Grupos Privados</string>
<string name="header_direct_messages">Mensagens diretas</string>
......
This diff is collapsed.
......@@ -315,6 +315,7 @@
<string name="chatroom_header">Заголовок</string>
<!--ChatRooms Headers-->
<string name="header_favorite">Favorites</string><!-- TODO - Add proper translation -->
<string name="header_channel">Каналы</string>
<string name="header_private_groups">Приватные каналы</string>
<string name="header_direct_messages">Личная переписка</string>
......
......@@ -320,6 +320,7 @@
<string name="chatroom_header">Başlık</string>
<!--ChatRooms Headers-->
<string name="header_favorite">Favorites</string><!-- TODO - Add proper translation -->
<string name="header_channel">Kanallar</string>
<string name="header_private_groups">Gizli Gruplar</string>
<string name="header_direct_messages">Direkt Mesajlar</string>
......
......@@ -316,6 +316,7 @@
<string name="chatroom_header">Заголовок</string>
<!--ChatRooms Headers-->
<string name="header_favorite">Favorites</string><!-- TODO - Add proper translation -->
<string name="header_channel">Канали</string>
<string name="header_private_groups">Приватні канали</string>
<string name="header_direct_messages">Особисті повідомлення</string>
......
......@@ -315,6 +315,7 @@
<string name="chatroom_header">头部</string>
<!--ChatRooms Headers-->
<string name="header_favorite">Favorites</string><!-- TODO - Add proper translation -->
<string name="header_channel">频道</string>
<string name="header_private_groups">私人组</string>
<string name="header_direct_messages">直接对话</string>
......
......@@ -331,6 +331,7 @@ https://github.com/RocketChat/java-code-styles/blob/master/CODING_STYLE.md#strin
<string name="chatroom_header">Header</string>
<!--ChatRooms Headers-->
<string name="header_favorite">Favorites</string>
<string name="header_channel">Channels</string>
<string name="header_private_groups">Private Groups</string>
<string name="header_direct_messages">Direct Messages</string>
......
......@@ -53,8 +53,15 @@ class DrawingActivity : DaggerAppCompatActivity(), DrawView {
}
private fun setupDrawTools() {
image_draw_eraser.setOnClickListener {
image_draw_eraser.setOnLongClickListener{
custom_draw_view.clearCanvas()
return@setOnLongClickListener true
}
image_draw_eraser.setOnClickListener {
custom_draw_view.setColor(
ResourcesCompat.getColor(resources, R.color.color_white, null)
)
toggleDrawTools(draw_tools, false)
}
......@@ -111,6 +118,13 @@ class DrawingActivity : DaggerAppCompatActivity(), DrawView {
}
private fun colorSelector() {
image_color_black.setOnClickListener {
custom_draw_view.setColor(
ResourcesCompat.getColor(resources, R.color.color_black, null)
)
scaleColorView(image_color_black)
}
image_color_red.setOnClickListener {
custom_draw_view.setColor(
ResourcesCompat.getColor(resources, R.color.color_red, null)
......
......@@ -42,7 +42,6 @@ class CustomDrawView(context: Context, attrs: AttributeSet) : View(context, attr
fun undo() {
if (mPaths.isEmpty() && mLastPaths.isNotEmpty()) {
mPaths = mLastPaths.clone() as LinkedHashMap<MyPath, PaintOptions>
mLastPaths.clear()
invalidate()
return
}
......
package chat.rocket.android.util.extension
import android.content.Context
import android.os.Build
import android.widget.TextView
import androidx.appcompat.widget.SearchView
fun SearchView.onQueryTextListener(queryListener: (String) -> Unit) {
......@@ -14,4 +17,12 @@ fun SearchView.onQueryTextListener(queryListener: (String) -> Unit) {
return true
}
})
}
fun TextView.setTextViewAppearance(context: Context, style: Int) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
setTextAppearance(style)
} else {
setTextAppearance(context, style)
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment