Commit e0ef6159 authored by Filipe de Lima Brito's avatar Filipe de Lima Brito

Merge branch 'develop' of github.com:RocketChat/Rocket.Chat.Android into new/video-call-support

Also enables the video conferencing from the UserDetails fragment.
parents 85e5b838 3525893c
...@@ -43,8 +43,8 @@ cd Rocket.Chat.Android/app ...@@ -43,8 +43,8 @@ cd Rocket.Chat.Android/app
## Bug report & Feature request ## 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 ## 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.
...@@ -99,87 +99,68 @@ android { ...@@ -99,87 +99,68 @@ android {
dependencies { dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs') implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation project(':player') implementation project(':player')
implementation project(':emoji') implementation project(':emoji')
implementation project(':draw') implementation project(':draw')
implementation project(':util') implementation project(':util')
implementation project(':core') implementation project(':core')
implementation project(':suggestions') implementation project(':suggestions')
implementation libraries.kotlin implementation libraries.kotlin
implementation libraries.coroutinesCore implementation libraries.coroutinesCore
implementation libraries.coroutinesAndroid implementation libraries.coroutinesAndroid
implementation libraries.appCompat implementation libraries.appCompat
implementation libraries.recyclerview implementation libraries.recyclerview
implementation libraries.constraintlayout implementation libraries.constraintlayout
implementation libraries.cardview implementation libraries.cardview
implementation libraries.browser implementation libraries.browser
implementation libraries.androidKtx implementation libraries.androidKtx
implementation libraries.fragmentsKtx implementation libraries.fragmentsKtx
implementation libraries.dagger implementation libraries.dagger
implementation libraries.daggerSupport implementation libraries.daggerSupport
kapt libraries.daggerProcessor kapt libraries.daggerProcessor
kapt libraries.daggerAndroidApt kapt libraries.daggerAndroidApt
implementation libraries.flexbox implementation libraries.flexbox
implementation libraries.material implementation libraries.material
implementation libraries.room implementation libraries.room
kapt libraries.roomProcessor kapt libraries.roomProcessor
implementation libraries.lifecycleExtensions implementation libraries.lifecycleExtensions
kapt libraries.lifecycleCompiler kapt libraries.lifecycleCompiler
implementation libraries.viewmodelKtx implementation libraries.viewmodelKtx
implementation libraries.workmanager implementation libraries.workmanager
implementation libraries.livedataKtx implementation libraries.livedataKtx
implementation libraries.rxKotlin implementation libraries.rxKotlin
implementation libraries.rxAndroid implementation libraries.rxAndroid
implementation libraries.moshi implementation libraries.moshi
implementation libraries.okhttp implementation libraries.okhttp
implementation libraries.okhttpLogger implementation libraries.okhttpLogger
implementation libraries.timber implementation libraries.timber
implementation libraries.threeTenABP implementation libraries.threeTenABP
kapt libraries.kotshiCompiler kapt libraries.kotshiCompiler
implementation libraries.kotshiApi implementation libraries.kotshiApi
implementation libraries.fresco implementation libraries.fresco
api libraries.frescoOkHttp api libraries.frescoOkHttp
implementation libraries.frescoAnimatedGif implementation libraries.frescoAnimatedGif
implementation libraries.frescoWebP implementation libraries.frescoWebP
implementation libraries.frescoAnimatedWebP implementation libraries.frescoAnimatedWebP
implementation libraries.frescoImageViewer implementation libraries.frescoImageViewer
implementation libraries.markwon implementation libraries.markwon
implementation libraries.aVLoadingIndicatorView implementation libraries.aVLoadingIndicatorView
implementation libraries.glide implementation libraries.glide
implementation libraries.glideTransformations implementation libraries.glideTransformations
implementation(libraries.jitsi) { transitive = true } implementation(libraries.jitsi) { transitive = true }
implementation 'com.google.code.findbugs:jsr305:3.0.2' implementation 'com.google.code.findbugs:jsr305:3.0.2'
// Proprietary libraries // Proprietary libraries
playImplementation libraries.fcm playImplementation libraries.fcm
playImplementation libraries.firebaseAnalytics playImplementation libraries.firebaseAnalytics
playImplementation libraries.playServicesAuth playImplementation libraries.playServicesAuth
playImplementation('com.crashlytics.sdk.android:crashlytics:2.9.8@aar') { transitive = true } playImplementation('com.crashlytics.sdk.android:crashlytics:2.9.8@aar') { transitive = true }
playImplementation('com.crashlytics.sdk.android:answers:1.4.6@aar') { transitive = true } playImplementation('com.crashlytics.sdk.android:answers:1.4.6@aar') { transitive = true }
testImplementation libraries.junit testImplementation libraries.junit
testImplementation libraries.truth testImplementation libraries.truth
androidTestImplementation libraries.espressoCore androidTestImplementation libraries.espressoCore
androidTestImplementation libraries.espressoIntents androidTestImplementation libraries.espressoIntents
implementation files('libs/core-2af5921.jar')
implementation files('libs/common-2af5921.jar')
} }
androidExtensions { androidExtensions {
......
...@@ -34,12 +34,16 @@ class AboutFragment : Fragment() { ...@@ -34,12 +34,16 @@ class AboutFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
setupToolbar()
setupViews() setupViews()
analyticsManager.logScreenView(ScreenViewEvent.About) analyticsManager.logScreenView(ScreenViewEvent.About)
} }
override fun onResume() {
super.onResume()
setupToolbar()
}
private fun setupViews() { private fun setupViews() {
text_version_name.text = BuildConfig.VERSION_NAME text_version_name.text = BuildConfig.VERSION_NAME
text_build_number.text = getString( text_build_number.text = getString(
......
import android.content.Context import android.content.Context
import chat.rocket.android.R 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.DateTimeFormatter
import org.threeten.bp.format.FormatStyle import org.threeten.bp.format.FormatStyle
import org.threeten.bp.format.TextStyle import org.threeten.bp.format.TextStyle
...@@ -53,39 +58,39 @@ object DateTimeHelper { ...@@ -53,39 +58,39 @@ object DateTimeHelper {
} }
} }
/** /**
* Returns a time from a [LocalDateTime]. * Returns a time from a [LocalDateTime].
* *
* @param localDateTime The [LocalDateTime]. * @param localDateTime The [LocalDateTime].
* @return The time from a [LocalDateTime]. * @return The time from a [LocalDateTime].
*/ */
fun getTime(localDateTime: LocalDateTime): String { fun getTime(localDateTime: LocalDateTime): String {
val formatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT) val formatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)
return localDateTime.toLocalTime().format(formatter).toString() return localDateTime.toLocalTime().format(formatter).toString()
} }
/** /**
* Returns a date time from a [LocalDateTime]. * Returns a date time from a [LocalDateTime].
* *
* @param localDateTime The [LocalDateTime]. * @param localDateTime The [LocalDateTime].
* @return The time from a [LocalDateTime]. * @return The time from a [LocalDateTime].
*/ */
fun getDateTime(localDateTime: LocalDateTime): String { fun getDateTime(localDateTime: LocalDateTime): String {
return formatLocalDateTime(localDateTime) return formatLocalDateTime(localDateTime)
} }
private fun formatLocalDateTime(localDateTime: LocalDateTime): String { private fun formatLocalDateTime(localDateTime: LocalDateTime): String {
val formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT) val formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT)
return localDateTime.format(formatter).toString() return localDateTime.format(formatter).toString()
} }
private fun formatLocalDate(localDate: LocalDate): String { private fun formatLocalDate(localDate: LocalDate): String {
val formatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM) val formatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)
return localDate.format(formatter).toString() return localDate.format(formatter).toString()
} }
private fun formatLocalTime(localTime: LocalTime): String { private fun formatLocalTime(localTime: LocalTime): String {
val formatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT) val formatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)
return localTime.format(formatter).toString() return localTime.format(formatter).toString()
} }
} }
\ No newline at end of file
...@@ -8,7 +8,8 @@ import chat.rocket.android.server.domain.MultiServerTokenRepository ...@@ -8,7 +8,8 @@ import chat.rocket.android.server.domain.MultiServerTokenRepository
import com.squareup.moshi.Moshi import com.squareup.moshi.Moshi
@PerActivity @PerActivity
class SharedPreferencesMultiServerTokenRepository(private val repository: LocalRepository, class SharedPreferencesMultiServerTokenRepository(
private val repository: LocalRepository,
private val moshi: Moshi private val moshi: Moshi
) : MultiServerTokenRepository { ) : MultiServerTokenRepository {
...@@ -16,11 +17,7 @@ class SharedPreferencesMultiServerTokenRepository(private val repository: LocalR ...@@ -16,11 +17,7 @@ class SharedPreferencesMultiServerTokenRepository(private val repository: LocalR
val token = repository.get("$TOKEN_KEY$server") val token = repository.get("$TOKEN_KEY$server")
val adapter = moshi.adapter<TokenModel>(TokenModel::class.java) val adapter = moshi.adapter<TokenModel>(TokenModel::class.java)
token?.let { return token?.let { adapter.fromJson(it) }
return adapter.fromJson(token)
}
return null
} }
override fun save(server: String, token: TokenModel) { override fun save(server: String, token: TokenModel) {
......
...@@ -39,12 +39,10 @@ internal const val REQUEST_CODE_FOR_SIGN_IN_REQUIRED = 1 ...@@ -39,12 +39,10 @@ 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_MULTIPLE_ACCOUNTS_RESOLUTION = 2
internal const val REQUEST_CODE_FOR_SAVE_RESOLUTION = 3 internal const val REQUEST_CODE_FOR_SAVE_RESOLUTION = 3
fun newInstance(serverName: String): Fragment { fun newInstance(serverName: String): Fragment = LoginFragment().apply {
return LoginFragment().apply {
arguments = Bundle(1).apply { arguments = Bundle(1).apply {
putString(SERVER_NAME, serverName) putString(SERVER_NAME, serverName)
} }
}
} }
class LoginFragment : Fragment(), LoginView { class LoginFragment : Fragment(), LoginView {
...@@ -59,9 +57,8 @@ class LoginFragment : Fragment(), LoginView { ...@@ -59,9 +57,8 @@ class LoginFragment : Fragment(), LoginView {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
AndroidSupportInjection.inject(this) AndroidSupportInjection.inject(this)
val bundle = arguments arguments?.run {
if (bundle != null) { serverName = getString(SERVER_NAME)
serverName = bundle.getString(SERVER_NAME)
} }
} }
...@@ -150,7 +147,7 @@ class LoginFragment : Fragment(), LoginView { ...@@ -150,7 +147,7 @@ class LoginFragment : Fragment(), LoginView {
override fun showGenericErrorMessage() = showMessage(R.string.msg_generic_error) override fun showGenericErrorMessage() = showMessage(R.string.msg_generic_error)
private fun setupOnClickListener() = private fun setupOnClickListener() =
ui { _ -> ui {
button_log_in.setOnClickListener { button_log_in.setOnClickListener {
presenter.authenticateWithUserAndPassword( presenter.authenticateWithUserAndPassword(
text_username_or_email.textContent, text_username_or_email.textContent,
...@@ -160,7 +157,7 @@ class LoginFragment : Fragment(), LoginView { ...@@ -160,7 +157,7 @@ class LoginFragment : Fragment(), LoginView {
} }
override fun showForgotPasswordView() { override fun showForgotPasswordView() {
ui { _ -> ui {
button_forgot_your_password.isVisible = true button_forgot_your_password.isVisible = true
button_forgot_your_password.setOnClickListener { presenter.forgotPassword() } button_forgot_your_password.setOnClickListener { presenter.forgotPassword() }
......
...@@ -88,8 +88,7 @@ fun newInstance( ...@@ -88,8 +88,7 @@ fun newInstance(
isLoginFormEnabled: Boolean, isLoginFormEnabled: Boolean,
isNewAccountCreationEnabled: Boolean, isNewAccountCreationEnabled: Boolean,
deepLinkInfo: LoginDeepLinkInfo? = null deepLinkInfo: LoginDeepLinkInfo? = null
): Fragment { ): Fragment = LoginOptionsFragment().apply {
return LoginOptionsFragment().apply {
arguments = Bundle(23).apply { arguments = Bundle(23).apply {
putString(SERVER_NAME, serverName) putString(SERVER_NAME, serverName)
putString(STATE, state) putString(STATE, state)
...@@ -118,7 +117,6 @@ fun newInstance( ...@@ -118,7 +117,6 @@ fun newInstance(
putBoolean(IS_NEW_ACCOUNT_CREATION_ENABLED, isNewAccountCreationEnabled) putBoolean(IS_NEW_ACCOUNT_CREATION_ENABLED, isNewAccountCreationEnabled)
putParcelable(DEEP_LINK_INFO, deepLinkInfo) putParcelable(DEEP_LINK_INFO, deepLinkInfo)
} }
}
} }
class LoginOptionsFragment : Fragment(), LoginOptionsView { class LoginOptionsFragment : Fragment(), LoginOptionsView {
...@@ -157,34 +155,33 @@ class LoginOptionsFragment : Fragment(), LoginOptionsView { ...@@ -157,34 +155,33 @@ class LoginOptionsFragment : Fragment(), LoginOptionsView {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
AndroidSupportInjection.inject(this) AndroidSupportInjection.inject(this)
val bundle = arguments arguments?.run {
if (bundle != null) { serverName = getString(SERVER_NAME)
serverName = bundle.getString(SERVER_NAME) state = getString(STATE)
state = bundle.getString(STATE) facebookOauthUrl = getString(FACEBOOK_OAUTH_URL)
facebookOauthUrl = bundle.getString(FACEBOOK_OAUTH_URL) githubOauthUrl = getString(GITHUB_OAUTH_URL)
githubOauthUrl = bundle.getString(GITHUB_OAUTH_URL) googleOauthUrl = getString(GOOGLE_OAUTH_URL)
googleOauthUrl = bundle.getString(GOOGLE_OAUTH_URL) linkedinOauthUrl = getString(LINKEDIN_OAUTH_URL)
linkedinOauthUrl = bundle.getString(LINKEDIN_OAUTH_URL) gitlabOauthUrl = getString(GITLAB_OAUTH_URL)
gitlabOauthUrl = bundle.getString(GITLAB_OAUTH_URL) wordpressOauthUrl = getString(WORDPRESS_OAUTH_URL)
wordpressOauthUrl = bundle.getString(WORDPRESS_OAUTH_URL) casLoginUrl = getString(CAS_LOGIN_URL)
casLoginUrl = bundle.getString(CAS_LOGIN_URL) casToken = getString(CAS_TOKEN)
casToken = bundle.getString(CAS_TOKEN) casServiceName = getString(CAS_SERVICE_NAME)
casServiceName = bundle.getString(CAS_SERVICE_NAME) casServiceNameTextColor = getInt(CAS_SERVICE_NAME_TEXT_COLOR)
casServiceNameTextColor = bundle.getInt(CAS_SERVICE_NAME_TEXT_COLOR) casServiceButtonColor = getInt(CAS_SERVICE_BUTTON_COLOR)
casServiceButtonColor = bundle.getInt(CAS_SERVICE_BUTTON_COLOR) customOauthUrl = getString(CUSTOM_OAUTH_URL)
customOauthUrl = bundle.getString(CUSTOM_OAUTH_URL) customOauthServiceName = getString(CUSTOM_OAUTH_SERVICE_NAME)
customOauthServiceName = bundle.getString(CUSTOM_OAUTH_SERVICE_NAME) customOauthServiceTextColor = getInt(CUSTOM_OAUTH_SERVICE_NAME_TEXT_COLOR)
customOauthServiceTextColor = bundle.getInt(CUSTOM_OAUTH_SERVICE_NAME_TEXT_COLOR) customOauthServiceButtonColor = getInt(CUSTOM_OAUTH_SERVICE_BUTTON_COLOR)
customOauthServiceButtonColor = bundle.getInt(CUSTOM_OAUTH_SERVICE_BUTTON_COLOR) samlUrl = getString(SAML_URL)
samlUrl = bundle.getString(SAML_URL) samlToken = getString(SAML_TOKEN)
samlToken = bundle.getString(SAML_TOKEN) samlServiceName = getString(SAML_SERVICE_NAME)
samlServiceName = bundle.getString(SAML_SERVICE_NAME) samlServiceTextColor = getInt(SAML_SERVICE_NAME_TEXT_COLOR)
samlServiceTextColor = bundle.getInt(SAML_SERVICE_NAME_TEXT_COLOR) samlServiceButtonColor = getInt(SAML_SERVICE_BUTTON_COLOR)
samlServiceButtonColor = bundle.getInt(SAML_SERVICE_BUTTON_COLOR) totalSocialAccountsEnabled = getInt(TOTAL_SOCIAL_ACCOUNTS)
totalSocialAccountsEnabled = bundle.getInt(TOTAL_SOCIAL_ACCOUNTS) isLoginFormEnabled = getBoolean(IS_LOGIN_FORM_ENABLED)
isLoginFormEnabled = bundle.getBoolean(IS_LOGIN_FORM_ENABLED) isNewAccountCreationEnabled = getBoolean(IS_NEW_ACCOUNT_CREATION_ENABLED)
isNewAccountCreationEnabled = bundle.getBoolean(IS_NEW_ACCOUNT_CREATION_ENABLED) deepLinkInfo = getParcelable(DEEP_LINK_INFO)
deepLinkInfo = bundle.getParcelable(DEEP_LINK_INFO)
} }
} }
...@@ -388,7 +385,7 @@ class LoginOptionsFragment : Fragment(), LoginOptionsView { ...@@ -388,7 +385,7 @@ class LoginOptionsFragment : Fragment(), LoginOptionsView {
} }
override fun setupExpandAccountsView() { override fun setupExpandAccountsView() {
ui { _ -> ui {
expand_more_accounts_container.isVisible = true expand_more_accounts_container.isVisible = true
var isAccountsCollapsed = true var isAccountsCollapsed = true
button_expand_collapse_accounts.setOnClickListener { button_expand_collapse_accounts.setOnClickListener {
...@@ -406,14 +403,14 @@ class LoginOptionsFragment : Fragment(), LoginOptionsView { ...@@ -406,14 +403,14 @@ class LoginOptionsFragment : Fragment(), LoginOptionsView {
} }
override fun showLoginWithEmailButton() { override fun showLoginWithEmailButton() {
ui { _ -> ui {
button_login_with_email.setOnClickListener { presenter.toLoginWithEmail() } button_login_with_email.setOnClickListener { presenter.toLoginWithEmail() }
button_login_with_email.isVisible = true button_login_with_email.isVisible = true
} }
} }
override fun showCreateNewAccountButton() { override fun showCreateNewAccountButton() {
ui { _ -> ui {
button_create_an_account.setOnClickListener { presenter.toCreateAccount() } button_create_an_account.setOnClickListener { presenter.toCreateAccount() }
button_create_an_account.isVisible = true button_create_an_account.isVisible = true
} }
......
...@@ -31,13 +31,11 @@ import javax.inject.Inject ...@@ -31,13 +31,11 @@ import javax.inject.Inject
private const val BUNDLE_USER_ID = "user_id" private const val BUNDLE_USER_ID = "user_id"
private const val BUNDLE_AUTH_TOKEN = "auth_token" private const val BUNDLE_AUTH_TOKEN = "auth_token"
fun newInstance(userId: String, authToken: String): Fragment { fun newInstance(userId: String, authToken: String): Fragment = RegisterUsernameFragment().apply {
return RegisterUsernameFragment().apply {
arguments = Bundle(2).apply { arguments = Bundle(2).apply {
putString(BUNDLE_USER_ID, userId) putString(BUNDLE_USER_ID, userId)
putString(BUNDLE_AUTH_TOKEN, authToken) putString(BUNDLE_AUTH_TOKEN, authToken)
} }
}
} }
class RegisterUsernameFragment : Fragment(), RegisterUsernameView { class RegisterUsernameFragment : Fragment(), RegisterUsernameView {
...@@ -53,13 +51,10 @@ class RegisterUsernameFragment : Fragment(), RegisterUsernameView { ...@@ -53,13 +51,10 @@ class RegisterUsernameFragment : Fragment(), RegisterUsernameView {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
AndroidSupportInjection.inject(this) AndroidSupportInjection.inject(this)
val bundle = arguments arguments?.run {
if (bundle != null) { userId = getString(BUNDLE_USER_ID, "")
userId = bundle.getString(BUNDLE_USER_ID) authToken = getString(BUNDLE_AUTH_TOKEN, "")
authToken = bundle.getString(BUNDLE_AUTH_TOKEN) } ?: requireNotNull(arguments) { "no arguments supplied when the fragment was instantiated" }
} else {
requireNotNull(bundle) { "no arguments supplied when the fragment was instantiated" }
}
} }
override fun onCreateView( override fun onCreateView(
......
...@@ -75,7 +75,7 @@ class SignupFragment : Fragment(), SignupView { ...@@ -75,7 +75,7 @@ class SignupFragment : Fragment(), SignupView {
} }
private fun setupOnClickListener() = private fun setupOnClickListener() =
ui { _ -> ui {
button_register.setOnClickListener { button_register.setOnClickListener {
presenter.signup( presenter.signup(
text_username.textContent, text_username.textContent,
......
...@@ -25,13 +25,11 @@ import io.reactivex.disposables.Disposable ...@@ -25,13 +25,11 @@ import io.reactivex.disposables.Disposable
import kotlinx.android.synthetic.main.fragment_authentication_two_fa.* import kotlinx.android.synthetic.main.fragment_authentication_two_fa.*
import javax.inject.Inject import javax.inject.Inject
fun newInstance(username: String, password: String): Fragment { fun newInstance(username: String, password: String): Fragment = TwoFAFragment().apply {
return TwoFAFragment().apply {
arguments = Bundle(2).apply { arguments = Bundle(2).apply {
putString(BUNDLE_USERNAME, username) putString(BUNDLE_USERNAME, username)
putString(BUNDLE_PASSWORD, password) putString(BUNDLE_PASSWORD, password)
} }
}
} }
private const val BUNDLE_USERNAME = "username" private const val BUNDLE_USERNAME = "username"
...@@ -50,13 +48,10 @@ class TwoFAFragment : Fragment(), TwoFAView { ...@@ -50,13 +48,10 @@ class TwoFAFragment : Fragment(), TwoFAView {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
AndroidSupportInjection.inject(this) AndroidSupportInjection.inject(this)
val bundle = arguments arguments?.run {
if (bundle != null) { username = getString(BUNDLE_USERNAME, "")
username = bundle.getString(BUNDLE_USERNAME) password = getString(BUNDLE_PASSWORD, "")
password = bundle.getString(BUNDLE_PASSWORD) } ?: requireNotNull(arguments) { "no arguments supplied when the fragment was instantiated" }
} else {
requireNotNull(bundle) { "no arguments supplied when the fragment was instantiated" }
}
} }
override fun onCreateView( override fun onCreateView(
......
package chat.rocket.android.chatdetails.adapter package chat.rocket.android.chatdetails.adapter
import android.content.Context import DrawableHelper
import android.view.View import android.view.View
import android.widget.ImageView import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
......
...@@ -11,9 +11,15 @@ import chat.rocket.android.util.extensions.inflate ...@@ -11,9 +11,15 @@ import chat.rocket.android.util.extensions.inflate
class ChatDetailsAdapter: RecyclerView.Adapter<OptionViewHolder>() { class ChatDetailsAdapter: RecyclerView.Adapter<OptionViewHolder>() {
private val options: MutableList<Option> = ArrayList() 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 override fun getItemCount(): Int = options.size
......
...@@ -81,16 +81,13 @@ class ChatDetailsFragment : Fragment(), ChatDetailsView { ...@@ -81,16 +81,13 @@ class ChatDetailsFragment : Fragment(), ChatDetailsView {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
AndroidSupportInjection.inject(this) AndroidSupportInjection.inject(this)
val bundle = arguments arguments?.run {
if (bundle != null) { chatRoomId = getString(BUNDLE_CHAT_ROOM_ID)
chatRoomId = bundle.getString(BUNDLE_CHAT_ROOM_ID) chatRoomType = getString(BUNDLE_CHAT_ROOM_TYPE)
chatRoomType = bundle.getString(BUNDLE_CHAT_ROOM_TYPE) isSubscribed = getBoolean(BUNDLE_IS_SUBSCRIBED)
isSubscribed = bundle.getBoolean(BUNDLE_IS_SUBSCRIBED) isFavorite = getBoolean(BUNDLE_IS_FAVORITE)
isFavorite = bundle.getBoolean(BUNDLE_IS_FAVORITE) disableMenu = getBoolean(BUNDLE_DISABLE_MENU)
disableMenu = bundle.getBoolean(BUNDLE_DISABLE_MENU) } ?: requireNotNull(arguments) { "no arguments supplied when the fragment was instantiated" }
} else {
requireNotNull(bundle) { "no arguments supplied when the fragment was instantiated" }
}
setHasOptionsMenu(true) setHasOptionsMenu(true)
} }
......
...@@ -5,9 +5,10 @@ import androidx.lifecycle.ViewModelProvider ...@@ -5,9 +5,10 @@ import androidx.lifecycle.ViewModelProvider
import chat.rocket.android.db.ChatRoomDao import chat.rocket.android.db.ChatRoomDao
import javax.inject.Inject 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") @Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>) = override fun <T : ViewModel?> create(modelClass: Class<T>) = ChatDetailsViewModel(chatRoomDao) as T
ChatDetailsViewModel(chatRoomDao) as T
} }
\ No newline at end of file
...@@ -12,7 +12,10 @@ import com.facebook.drawee.backends.pipeline.Fresco ...@@ -12,7 +12,10 @@ import com.facebook.drawee.backends.pipeline.Fresco
import kotlinx.android.synthetic.main.item_action_button.view.* import kotlinx.android.synthetic.main.item_action_button.view.*
import timber.log.Timber 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 var actions: List<Action> = actions
...@@ -62,9 +65,7 @@ class ActionsListAdapter(actions: List<Action>, var actionAttachmentOnClickListe ...@@ -62,9 +65,7 @@ class ActionsListAdapter(actions: List<Action>, var actionAttachmentOnClickListe
return ViewHolder(view) return ViewHolder(view)
} }
override fun getItemCount(): Int { override fun getItemCount(): Int = actions.size
return actions.size
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) { override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val action = actions[position] val action = actions[position]
......
...@@ -73,13 +73,9 @@ class ChatRoomAdapter( ...@@ -73,13 +73,9 @@ class ChatRoomAdapter(
} }
} }
override fun getItemViewType(position: Int): Int { override fun getItemViewType(position: Int): Int = dataSet[position].viewType
return dataSet[position].viewType
}
override fun getItemCount(): Int { override fun getItemCount(): Int = dataSet.size
return dataSet.size
}
override fun onBindViewHolder(holder: BaseViewHolder<*>, position: Int) { override fun onBindViewHolder(holder: BaseViewHolder<*>, position: Int) {
if (holder !is MessageViewHolder) { if (holder !is MessageViewHolder) {
...@@ -171,12 +167,12 @@ class ChatRoomAdapter( ...@@ -171,12 +167,12 @@ class ChatRoomAdapter(
Timber.d("index: $index") Timber.d("index: $index")
if (index > -1) { if (index > -1) {
dataSet[index] = message dataSet[index] = message
dataSet.forEachIndexed { index, viewModel -> dataSet.forEachIndexed { ind, viewModel ->
if (viewModel.messageId == message.messageId) { if (viewModel.messageId == message.messageId) {
if (viewModel.nextDownStreamMessage == null) { if (viewModel.nextDownStreamMessage == null) {
viewModel.reactions = message.reactions viewModel.reactions = message.reactions
} }
notifyItemChanged(index) notifyItemChanged(ind)
} }
} }
// Delete message only if current is a system message update, i.e.: Message Removed // Delete message only if current is a system message update, i.e.: Message Removed
...@@ -255,10 +251,10 @@ class ChatRoomAdapter( ...@@ -255,10 +251,10 @@ class ChatRoomAdapter(
actionSelectListener?.editMessage(roomId, id, message.message) actionSelectListener?.editMessage(roomId, id, message.message)
} }
R.id.action_message_star -> { R.id.action_message_star -> {
actionSelectListener?.toogleStar(id, !item.isChecked) actionSelectListener?.toggleStar(id, !item.isChecked)
} }
R.id.action_message_unpin -> { R.id.action_message_unpin -> {
actionSelectListener?.tooglePin(id, !item.isChecked) actionSelectListener?.togglePin(id, !item.isChecked)
} }
R.id.action_message_delete -> { R.id.action_message_delete -> {
actionSelectListener?.deleteMessage(roomId, id) actionSelectListener?.deleteMessage(roomId, id)
...@@ -295,9 +291,9 @@ class ChatRoomAdapter( ...@@ -295,9 +291,9 @@ class ChatRoomAdapter(
fun editMessage(roomId: String, messageId: String, text: String) 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) fun deleteMessage(roomId: String, id: String)
......
...@@ -24,8 +24,8 @@ class RoomSuggestionsAdapter : SuggestionsAdapter<RoomSuggestionsViewHolder>("#" ...@@ -24,8 +24,8 @@ class RoomSuggestionsAdapter : SuggestionsAdapter<RoomSuggestionsViewHolder>("#"
override fun bind(item: SuggestionModel, itemClickListener: SuggestionsAdapter.ItemClickListener?) { override fun bind(item: SuggestionModel, itemClickListener: SuggestionsAdapter.ItemClickListener?) {
item as ChatRoomSuggestionUiModel item as ChatRoomSuggestionUiModel
with(itemView) { with(itemView) {
val fullname = itemView.findViewById<TextView>(R.id.text_fullname) val fullname = findViewById<TextView>(R.id.text_fullname)
val name = itemView.findViewById<TextView>(R.id.text_name) val name = findViewById<TextView>(R.id.text_name)
name.text = item.name name.text = item.name
fullname.text = item.fullName fullname.text = item.fullName
setOnClickListener { setOnClickListener {
......
...@@ -15,9 +15,7 @@ class UrlPreviewViewHolder( ...@@ -15,9 +15,7 @@ class UrlPreviewViewHolder(
) : BaseViewHolder<UrlPreviewUiModel>(itemView, listener, reactionListener) { ) : BaseViewHolder<UrlPreviewUiModel>(itemView, listener, reactionListener) {
init { init {
with(itemView) { setupActionMenu(itemView.url_preview_layout)
setupActionMenu(url_preview_layout)
}
} }
override fun bindViews(data: UrlPreviewUiModel) { override fun bindViews(data: UrlPreviewUiModel) {
......
...@@ -180,8 +180,8 @@ class ChatRoomPresenter @Inject constructor( ...@@ -180,8 +180,8 @@ class ChatRoomPresenter @Inject constructor(
chatRoomId?.let { chatRoomId?.let {
manager.addRoomChannel(it, roomChangesChannel) manager.addRoomChannel(it, roomChangesChannel)
for (room in roomChangesChannel) { for (room in roomChangesChannel) {
dbManager.getRoom(room.id)?.let { dbManager.getRoom(room.id)?.let { chatRoom ->
view.onRoomUpdated(roomMapper.map(chatRoom = it, showLastMessage = true)) view.onRoomUpdated(roomMapper.map(chatRoom = chatRoom, showLastMessage = true))
} }
} }
} }
...@@ -314,16 +314,18 @@ class ChatRoomPresenter @Inject constructor( ...@@ -314,16 +314,18 @@ class ChatRoomPresenter @Inject constructor(
fun sendMessage(chatRoomId: String, text: String, messageId: String?) { fun sendMessage(chatRoomId: String, text: String, messageId: String?) {
launchUI(strategy) { launchUI(strategy) {
try { try {
view.disableSendMessageButton()
// ignore message for now, will receive it on the stream // ignore message for now, will receive it on the stream
if (messageId == null) { if (messageId == null) {
val id = UUID.randomUUID().toString() val id = UUID.randomUUID().toString()
val username = userHelper.username() val username = userHelper.username()
val user = userHelper.user()
val newMessage = Message( val newMessage = Message(
id = id, id = id,
roomId = chatRoomId, roomId = chatRoomId,
message = text, message = text,
timestamp = Instant.now().toEpochMilli(), timestamp = Instant.now().toEpochMilli(),
sender = SimpleUser(null, username, username), sender = SimpleUser(user?.id, user?.username ?: username, user?.name),
attachments = null, attachments = null,
avatar = currentServer.avatarUrl(username ?: ""), avatar = currentServer.avatarUrl(username ?: ""),
channels = null, channels = null,
...@@ -1052,7 +1054,7 @@ class ChatRoomPresenter @Inject constructor( ...@@ -1052,7 +1054,7 @@ class ChatRoomPresenter @Inject constructor(
launchUI(strategy) { launchUI(strategy) {
try { try {
messagesRepository.getById(messageId)?.let { message -> messagesRepository.getById(messageId)?.let { message ->
getChatRoomAsync(message.roomId)?.let { chatRoom -> getChatRoomAsync(message.roomId)?.let {
val models = mapper.map(message) val models = mapper.map(message)
models.firstOrNull()?.permalink?.let { models.firstOrNull()?.permalink?.let {
view.copyToClipboard(it) view.copyToClipboard(it)
...@@ -1273,10 +1275,8 @@ class ChatRoomPresenter @Inject constructor( ...@@ -1273,10 +1275,8 @@ class ChatRoomPresenter @Inject constructor(
* @param unfinishedMessage The unfinished message to save. * @param unfinishedMessage The unfinished message to save.
*/ */
fun saveDraftMessage(unfinishedMessage: String) { fun saveDraftMessage(unfinishedMessage: String) {
if (unfinishedMessage.isNotBlank()) {
localRepository.save(draftKey, unfinishedMessage) localRepository.save(draftKey, unfinishedMessage)
} }
}
fun clearDraftMessage() { fun clearDraftMessage() {
localRepository.clear(draftKey) localRepository.clear(draftKey)
......
...@@ -30,8 +30,7 @@ fun Context.chatRoomIntent( ...@@ -30,8 +30,7 @@ fun Context.chatRoomIntent(
isCreator: Boolean = false, isCreator: Boolean = false,
isFavorite: Boolean = false, isFavorite: Boolean = false,
chatRoomMessage: String? = null chatRoomMessage: String? = null
): Intent { ): Intent = Intent(this, ChatRoomActivity::class.java).apply {
return Intent(this, ChatRoomActivity::class.java).apply {
putExtra(INTENT_CHAT_ROOM_ID, chatRoomId) putExtra(INTENT_CHAT_ROOM_ID, chatRoomId)
putExtra(INTENT_CHAT_ROOM_NAME, chatRoomName) putExtra(INTENT_CHAT_ROOM_NAME, chatRoomName)
putExtra(INTENT_CHAT_ROOM_TYPE, chatRoomType) putExtra(INTENT_CHAT_ROOM_TYPE, chatRoomType)
...@@ -41,7 +40,6 @@ fun Context.chatRoomIntent( ...@@ -41,7 +40,6 @@ fun Context.chatRoomIntent(
putExtra(INTENT_CHAT_ROOM_IS_CREATOR, isCreator) putExtra(INTENT_CHAT_ROOM_IS_CREATOR, isCreator)
putExtra(INTENT_CHAT_ROOM_IS_FAVORITE, isFavorite) putExtra(INTENT_CHAT_ROOM_IS_FAVORITE, isFavorite)
putExtra(INTENT_CHAT_ROOM_MESSAGE, chatRoomMessage) putExtra(INTENT_CHAT_ROOM_MESSAGE, chatRoomMessage)
}
} }
private const val INTENT_CHAT_ROOM_ID = "chat_room_id" private const val INTENT_CHAT_ROOM_ID = "chat_room_id"
...@@ -79,26 +77,27 @@ class ChatRoomActivity : AppCompatActivity(), HasSupportFragmentInjector { ...@@ -79,26 +77,27 @@ class ChatRoomActivity : AppCompatActivity(), HasSupportFragmentInjector {
return return
} }
val chatRoomId = intent.getStringExtra(INTENT_CHAT_ROOM_ID) with(intent) {
val chatRoomId = getStringExtra(INTENT_CHAT_ROOM_ID)
requireNotNull(chatRoomId) { "no chat_room_id provided in Intent extras" } requireNotNull(chatRoomId) { "no chat_room_id provided in Intent extras" }
val chatRoomName = intent.getStringExtra(INTENT_CHAT_ROOM_NAME) val chatRoomName = getStringExtra(INTENT_CHAT_ROOM_NAME)
requireNotNull(chatRoomName) { "no chat_room_name provided in Intent extras" } requireNotNull(chatRoomName) { "no chat_room_name provided in Intent extras" }
val chatRoomType = intent.getStringExtra(INTENT_CHAT_ROOM_TYPE) val chatRoomType = getStringExtra(INTENT_CHAT_ROOM_TYPE)
requireNotNull(chatRoomType) { "no chat_room_type provided in Intent extras" } 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()
...@@ -118,6 +117,7 @@ class ChatRoomActivity : AppCompatActivity(), HasSupportFragmentInjector { ...@@ -118,6 +117,7 @@ class ChatRoomActivity : AppCompatActivity(), HasSupportFragmentInjector {
} }
} }
} }
}
override fun onBackPressed() { override fun onBackPressed() {
finishActivity() finishActivity()
......
package chat.rocket.android.chatroom.ui package chat.rocket.android.chatroom.ui
import android.app.Activity import android.app.Activity
import androidx.appcompat.app.AlertDialog
import android.content.ClipData import android.content.ClipData
import android.content.ClipboardManager import android.content.ClipboardManager
import android.content.Context import android.content.Context
...@@ -12,16 +11,13 @@ import android.os.Bundle ...@@ -12,16 +11,13 @@ import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.provider.MediaStore import android.provider.MediaStore
import android.text.SpannableStringBuilder import android.text.SpannableStringBuilder
import android.view.KeyEvent import android.view.*
import android.view.LayoutInflater
import android.view.Menu
import android.view.View
import android.view.ViewGroup
import android.widget.EditText import android.widget.EditText
import android.widget.FrameLayout import android.widget.FrameLayout
import android.widget.ImageView import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.appcompat.app.AlertDialog
import androidx.core.content.FileProvider import androidx.core.content.FileProvider
import androidx.core.text.bold import androidx.core.text.bold
import androidx.core.view.isVisible import androidx.core.view.isVisible
...@@ -33,12 +29,7 @@ import androidx.recyclerview.widget.RecyclerView ...@@ -33,12 +29,7 @@ import androidx.recyclerview.widget.RecyclerView
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.analytics.AnalyticsManager import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.ScreenViewEvent import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.chatroom.adapter.ChatRoomAdapter import chat.rocket.android.chatroom.adapter.*
import chat.rocket.android.chatroom.adapter.CommandSuggestionsAdapter
import chat.rocket.android.chatroom.adapter.EmojiSuggestionsAdapter
import chat.rocket.android.chatroom.adapter.PEOPLE
import chat.rocket.android.chatroom.adapter.PeopleSuggestionsAdapter
import chat.rocket.android.chatroom.adapter.RoomSuggestionsAdapter
import chat.rocket.android.chatroom.presentation.ChatRoomNavigator import chat.rocket.android.chatroom.presentation.ChatRoomNavigator
import chat.rocket.android.chatroom.presentation.ChatRoomPresenter import chat.rocket.android.chatroom.presentation.ChatRoomPresenter
import chat.rocket.android.chatroom.presentation.ChatRoomView import chat.rocket.android.chatroom.presentation.ChatRoomView
...@@ -52,13 +43,7 @@ import chat.rocket.android.chatroom.uimodel.suggestion.PeopleSuggestionUiModel ...@@ -52,13 +43,7 @@ import chat.rocket.android.chatroom.uimodel.suggestion.PeopleSuggestionUiModel
import chat.rocket.android.chatrooms.adapter.model.RoomUiModel import chat.rocket.android.chatrooms.adapter.model.RoomUiModel
import chat.rocket.android.draw.main.ui.DRAWING_BYTE_ARRAY_EXTRA_DATA import chat.rocket.android.draw.main.ui.DRAWING_BYTE_ARRAY_EXTRA_DATA
import chat.rocket.android.draw.main.ui.DrawingActivity import chat.rocket.android.draw.main.ui.DrawingActivity
import chat.rocket.android.emoji.ComposerEditText import chat.rocket.android.emoji.*
import chat.rocket.android.emoji.Emoji
import chat.rocket.android.emoji.EmojiKeyboardListener
import chat.rocket.android.emoji.EmojiKeyboardPopup
import chat.rocket.android.emoji.EmojiParser
import chat.rocket.android.emoji.EmojiPickerPopup
import chat.rocket.android.emoji.EmojiReactionListener
import chat.rocket.android.emoji.internal.isCustom import chat.rocket.android.emoji.internal.isCustom
import chat.rocket.android.helper.EndlessRecyclerViewScrollListener import chat.rocket.android.helper.EndlessRecyclerViewScrollListener
import chat.rocket.android.helper.ImageHelper import chat.rocket.android.helper.ImageHelper
...@@ -66,17 +51,7 @@ import chat.rocket.android.helper.KeyboardHelper ...@@ -66,17 +51,7 @@ import chat.rocket.android.helper.KeyboardHelper
import chat.rocket.android.helper.MessageParser import chat.rocket.android.helper.MessageParser
import chat.rocket.android.util.extension.asObservable import chat.rocket.android.util.extension.asObservable
import chat.rocket.android.util.extension.createImageFile import chat.rocket.android.util.extension.createImageFile
import chat.rocket.android.util.extensions.circularRevealOrUnreveal import chat.rocket.android.util.extensions.*
import chat.rocket.android.util.extensions.clearLightStatusBar
import chat.rocket.android.util.extensions.fadeIn
import chat.rocket.android.util.extensions.fadeOut
import chat.rocket.android.util.extensions.hideKeyboard
import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.isNotNullNorEmpty
import chat.rocket.android.util.extensions.rotateBy
import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.textContent
import chat.rocket.android.util.extensions.ui
import chat.rocket.common.model.RoomType import chat.rocket.common.model.RoomType
import chat.rocket.common.model.roomTypeOf import chat.rocket.common.model.roomTypeOf
import chat.rocket.core.internal.realtime.socket.model.State import chat.rocket.core.internal.realtime.socket.model.State
...@@ -110,8 +85,7 @@ fun newInstance( ...@@ -110,8 +85,7 @@ fun newInstance(
isCreator: Boolean = false, isCreator: Boolean = false,
isFavorite: Boolean = false, isFavorite: Boolean = false,
chatRoomMessage: String? = null chatRoomMessage: String? = null
): Fragment { ): Fragment = ChatRoomFragment().apply {
return ChatRoomFragment().apply {
arguments = Bundle(1).apply { arguments = Bundle(1).apply {
putString(BUNDLE_CHAT_ROOM_ID, chatRoomId) putString(BUNDLE_CHAT_ROOM_ID, chatRoomId)
putString(BUNDLE_CHAT_ROOM_NAME, chatRoomName) putString(BUNDLE_CHAT_ROOM_NAME, chatRoomName)
...@@ -123,7 +97,6 @@ fun newInstance( ...@@ -123,7 +97,6 @@ fun newInstance(
putBoolean(BUNDLE_CHAT_ROOM_IS_FAVORITE, isFavorite) putBoolean(BUNDLE_CHAT_ROOM_IS_FAVORITE, isFavorite)
putString(BUNDLE_CHAT_ROOM_MESSAGE, chatRoomMessage) putString(BUNDLE_CHAT_ROOM_MESSAGE, chatRoomMessage)
} }
}
} }
internal const val TAG_CHAT_ROOM_FRAGMENT = "ChatRoomFragment" internal const val TAG_CHAT_ROOM_FRAGMENT = "ChatRoomFragment"
...@@ -195,7 +168,11 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -195,7 +168,11 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
private var verticalScrollOffset = AtomicInteger(0) private var verticalScrollOffset = AtomicInteger(0)
private val dialogView by lazy { View.inflate(context, R.layout.file_attachments_dialog, null) } private val dialogView by lazy { View.inflate(context, R.layout.file_attachments_dialog, null) }
internal val alertDialog by lazy { activity?.let { AlertDialog.Builder(it).setView(dialogView).create() } } internal val alertDialog by lazy {
activity?.let {
AlertDialog.Builder(it).setView(dialogView).create()
}
}
internal val imagePreview by lazy { dialogView.findViewById<ImageView>(R.id.image_preview) } internal val imagePreview by lazy { dialogView.findViewById<ImageView>(R.id.image_preview) }
internal val sendButton by lazy { dialogView.findViewById<android.widget.Button>(R.id.button_send) } internal val sendButton by lazy { dialogView.findViewById<android.widget.Button>(R.id.button_send) }
internal val cancelButton by lazy { dialogView.findViewById<android.widget.Button>(R.id.button_cancel) } internal val cancelButton by lazy { dialogView.findViewById<android.widget.Button>(R.id.button_cancel) }
...@@ -255,7 +232,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -255,7 +232,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
button_fab.hide() button_fab.hide()
newMessageCount = 0 newMessageCount = 0
} else { } else {
if (dy < 0 && !button_fab.isVisible) { if (dy < 0 && isAdded && !button_fab.isVisible) {
button_fab.show() button_fab.show()
if (newMessageCount != 0) text_count.isVisible = true if (newMessageCount != 0) text_count.isVisible = true
} }
...@@ -268,22 +245,27 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -268,22 +245,27 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
AndroidSupportInjection.inject(this) AndroidSupportInjection.inject(this)
setHasOptionsMenu(true) setHasOptionsMenu(true)
val bundle = arguments arguments?.run {
if (bundle != null) { chatRoomId = getString(BUNDLE_CHAT_ROOM_ID, "")
chatRoomId = bundle.getString(BUNDLE_CHAT_ROOM_ID) chatRoomName = getString(BUNDLE_CHAT_ROOM_NAME, "")
chatRoomName = bundle.getString(BUNDLE_CHAT_ROOM_NAME) chatRoomType = getString(BUNDLE_CHAT_ROOM_TYPE, "")
chatRoomType = bundle.getString(BUNDLE_CHAT_ROOM_TYPE) isReadOnly = getBoolean(BUNDLE_IS_CHAT_ROOM_READ_ONLY)
isReadOnly = bundle.getBoolean(BUNDLE_IS_CHAT_ROOM_READ_ONLY) isSubscribed = getBoolean(BUNDLE_CHAT_ROOM_IS_SUBSCRIBED)
isSubscribed = bundle.getBoolean(BUNDLE_CHAT_ROOM_IS_SUBSCRIBED) chatRoomLastSeen = getLong(BUNDLE_CHAT_ROOM_LAST_SEEN)
chatRoomLastSeen = bundle.getLong(BUNDLE_CHAT_ROOM_LAST_SEEN) isCreator = getBoolean(BUNDLE_CHAT_ROOM_IS_CREATOR)
isCreator = bundle.getBoolean(BUNDLE_CHAT_ROOM_IS_CREATOR) isFavorite = getBoolean(BUNDLE_CHAT_ROOM_IS_FAVORITE)
isFavorite = bundle.getBoolean(BUNDLE_CHAT_ROOM_IS_FAVORITE) chatRoomMessage = getString(BUNDLE_CHAT_ROOM_MESSAGE)
chatRoomMessage = bundle.getString(BUNDLE_CHAT_ROOM_MESSAGE) }
} else { ?: requireNotNull(arguments) { "no arguments supplied when the fragment was instantiated" }
requireNotNull(bundle) { "no arguments supplied when the fragment was instantiated" }
} adapter = ChatRoomAdapter(
chatRoomId,
adapter = ChatRoomAdapter(chatRoomId, chatRoomType, chatRoomName, this, reactionListener = this, navigator = navigator) chatRoomType,
chatRoomName,
this,
reactionListener = this,
navigator = navigator
)
} }
override fun onCreateView( override fun onCreateView(
...@@ -305,7 +287,13 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -305,7 +287,13 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
with(activity as ChatRoomActivity) { with(activity as ChatRoomActivity) {
setupToolbarTitle(chatRoomName) setupToolbarTitle(chatRoomName)
setupExpandMoreForToolbar { setupExpandMoreForToolbar {
presenter.toChatDetails(chatRoomId, chatRoomType, isSubscribed, isFavorite, disableMenu) presenter.toChatDetails(
chatRoomId,
chatRoomType,
isSubscribed,
isFavorite,
disableMenu
)
} }
} }
getDraftMessage() getDraftMessage()
...@@ -528,9 +516,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -528,9 +516,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
text_count.text = "99+" text_count.text = "99+"
} }
text_count.isVisible = true text_count.isVisible = true
} } else if (!button_fab.isVisible) {
else if (!button_fab.isVisible) {
recycler_view.scrollToPosition(0) recycler_view.scrollToPosition(0)
} }
verticalScrollOffset.set(0) verticalScrollOffset.set(0)
...@@ -561,7 +547,11 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -561,7 +547,11 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
} }
} }
override fun showReplyingAction(username: String, replyMarkdown: String, quotedMessage: String) { override fun showReplyingAction(
username: String,
replyMarkdown: String,
quotedMessage: String
) {
ui { ui {
citation = replyMarkdown citation = replyMarkdown
actionSnackbar.title = username actionSnackbar.title = username
...@@ -644,7 +634,10 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -644,7 +634,10 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
if (cursorPosition > -1) { if (cursorPosition > -1) {
context?.let { context?.let {
val offset = if (!emoji.isCustom()) emoji.unicode.length else emoji.shortname.length val offset = if (!emoji.isCustom()) emoji.unicode.length else emoji.shortname.length
val parsed = if (emoji.isCustom()) emoji.shortname else EmojiParser.parse(it, emoji.shortname) val parsed = if (emoji.isCustom()) emoji.shortname else EmojiParser.parse(
it,
emoji.shortname
)
text_message.text?.insert(cursorPosition, parsed) text_message.text?.insert(cursorPosition, parsed)
text_message.setSelection(cursorPosition + offset) text_message.setSelection(cursorPosition + offset)
} }
...@@ -668,8 +661,14 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -668,8 +661,14 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
presenter.react(messageId, emoji.shortname) presenter.react(messageId, emoji.shortname)
} }
override fun onReactionLongClicked(shortname: String, isCustom: Boolean, url: String?, usernames: List<String>) { override fun onReactionLongClicked(
val layout = LayoutInflater.from(requireContext()).inflate(R.layout.reaction_praises_list_item, null) shortname: String,
isCustom: Boolean,
url: String?,
usernames: List<String>
) {
val layout =
LayoutInflater.from(requireContext()).inflate(R.layout.reaction_praises_list_item, null)
val dialog = AlertDialog.Builder(requireContext()) val dialog = AlertDialog.Builder(requireContext())
.setView(layout) .setView(layout)
.setCancelable(true) .setCancelable(true)
...@@ -696,11 +695,13 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -696,11 +695,13 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
listing += if (index == usernames.size - 1) "|$username" else "$username, " listing += if (index == usernames.size - 1) "|$username" else "$username, "
} }
listing = listing.replace(", |", " ${requireContext().getString(R.string.msg_and)} ") listing =
listing.replace(", |", " ${requireContext().getString(R.string.msg_and)} ")
} }
text_view_usernames.text = requireContext().resources.getQuantityString( text_view_usernames.text = requireContext().resources.getQuantityString(
R.plurals.msg_reacted_with_, usernames.size, listing, shortname) R.plurals.msg_reacted_with_, usernames.size, listing, shortname
)
dialog.show() dialog.show()
} }
...@@ -747,21 +748,20 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -747,21 +748,20 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
ui { ui {
text_connection_status.fadeIn() text_connection_status.fadeIn()
handler.removeCallbacks(dismissStatus) handler.removeCallbacks(dismissStatus)
when (state) { text_connection_status.text = when (state) {
is State.Connected -> { is State.Connected -> {
text_connection_status.text = getString(R.string.status_connected)
handler.postDelayed(dismissStatus, 2000) 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)
} }
} }
} }
...@@ -835,7 +835,8 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -835,7 +835,8 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
true true
) )
emojiKeyboardPopup = EmojiKeyboardPopup(activity!!, activity!!.findViewById(R.id.fragment_container)) emojiKeyboardPopup =
EmojiKeyboardPopup(activity!!, activity!!.findViewById(R.id.fragment_container))
emojiKeyboardPopup.listener = this emojiKeyboardPopup.listener = this
...@@ -1087,7 +1088,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -1087,7 +1088,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
presenter.editMessage(roomId, messageId, text) presenter.editMessage(roomId, messageId, text)
} }
override fun toogleStar(id: String, star: Boolean) { override fun toggleStar(id: String, star: Boolean) {
if (star) { if (star) {
presenter.starMessage(id) presenter.starMessage(id)
} else { } else {
...@@ -1095,7 +1096,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -1095,7 +1096,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
} }
} }
override fun tooglePin(id: String, pin: Boolean) { override fun togglePin(id: String, pin: Boolean) {
if (pin) { if (pin) {
presenter.pinMessage(id) presenter.pinMessage(id)
} else { } else {
...@@ -1136,8 +1137,10 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -1136,8 +1137,10 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
} }
override fun reportMessage(id: String) { override fun reportMessage(id: String) {
presenter.reportMessage(messageId = id, presenter.reportMessage(
description = "This message was reported by a user from the Android app") messageId = id,
description = "This message was reported by a user from the Android app"
)
} }
fun openEmojiKeyboard() { fun openEmojiKeyboard() {
......
...@@ -416,21 +416,19 @@ class UiModelMapper @Inject constructor( ...@@ -416,21 +416,19 @@ class UiModelMapper @Inject constructor(
return fullUrl return fullUrl
} }
private fun attachmentText(text: String?, attachment: Attachment?, context: Context): String? { private fun attachmentText(text: String?, attachment: Attachment?, context: Context): String? = attachment?.run {
return if (attachment != null) { with(context) {
when { when {
attachment.imageUrl.isNotNullNorEmpty() -> context.getString(R.string.msg_preview_photo) imageUrl.isNotNullNorEmpty() -> getString(R.string.msg_preview_photo)
attachment.videoUrl.isNotNullNorEmpty() -> context.getString(R.string.msg_preview_video) videoUrl.isNotNullNorEmpty() -> getString(R.string.msg_preview_video)
attachment.audioUrl.isNotNullNorEmpty() -> context.getString(R.string.msg_preview_audio) audioUrl.isNotNullNorEmpty() -> getString(R.string.msg_preview_audio)
attachment.titleLink.isNotNullNorEmpty() && titleLink.isNotNullNorEmpty() &&
attachment.type?.contentEquals("file") == true -> type?.contentEquals("file") == true ->
context.getString(R.string.msg_preview_file) getString(R.string.msg_preview_file)
else -> text else -> text
} }
} else {
text
}
} }
} ?: text
private fun attachmentDescription(attachment: Attachment): String? { private fun attachmentDescription(attachment: Attachment): String? {
return attachment.description return attachment.description
...@@ -464,12 +462,10 @@ class UiModelMapper @Inject constructor( ...@@ -464,12 +462,10 @@ class UiModelMapper @Inject constructor(
subscriptionId = chatRoom.subscriptionId) subscriptionId = chatRoom.subscriptionId)
} }
private fun mapMessagePreview(message: Message): Message { private fun mapMessagePreview(message: Message): Message = when (message.isSystemMessage()) {
return when (message.isSystemMessage()) {
false -> stripMessageQuotes(message) false -> stripMessageQuotes(message)
true -> message.copy(message = getSystemMessage(message).toString()) true -> message.copy(message = getSystemMessage(message).toString())
} }
}
private fun getReactions(message: Message): List<ReactionUiModel> { private fun getReactions(message: Message): List<ReactionUiModel> {
val reactions = message.reactions?.let { val reactions = message.reactions?.let {
...@@ -535,75 +531,36 @@ class UiModelMapper @Inject constructor( ...@@ -535,75 +531,36 @@ class UiModelMapper @Inject constructor(
private fun getTime(timestamp: Long) = DateTimeHelper.getTime(DateTimeHelper.getLocalDateTime(timestamp)) private fun getTime(timestamp: Long) = DateTimeHelper.getTime(DateTimeHelper.getLocalDateTime(timestamp))
private fun getContent(message: Message): CharSequence { private fun getContent(message: Message): CharSequence = when (message.isSystemMessage()) {
return when (message.isSystemMessage()) {
true -> getSystemMessage(message) true -> getSystemMessage(message)
false -> parser.render(message, currentUsername) false -> parser.render(message, currentUsername)
} }
}
private fun getSystemMessage(message: Message): CharSequence { private fun getSystemMessage(message: Message): CharSequence {
val content = when (message.type) { val content = with(context) {
when (message.type) {
//TODO: Add implementation for Welcome type. //TODO: Add implementation for Welcome type.
is MessageType.MessageRemoved -> context.getString(R.string.message_removed) is MessageType.MessageRemoved -> getString(R.string.message_removed)
is MessageType.UserJoined -> context.getString(R.string.message_user_joined_channel) is MessageType.UserJoined -> getString(R.string.message_user_joined_channel)
is MessageType.UserLeft -> context.getString(R.string.message_user_left) is MessageType.UserLeft -> getString(R.string.message_user_left)
is MessageType.UserAdded -> context.getString( is MessageType.UserAdded -> getString(R.string.message_user_added_by, message.message, message.sender?.username)
R.string.message_user_added_by, is MessageType.RoomNameChanged -> getString(R.string.message_room_name_changed, message.message, message.sender?.username)
message.message, is MessageType.UserRemoved -> getString(R.string.message_user_removed_by, message.message, message.sender?.username)
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.RoomNameChanged -> context.getString( is MessageType.UserUnMuted -> getString(R.string.message_unmuted, message.message, message.sender?.username)
R.string.message_room_name_changed, 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.UserRemoved -> context.getString( is MessageType.RoomChangedPrivacy -> getString(R.string.message_room_changed_privacy, message.message, message.sender?.username)
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
)
is MessageType.JitsiCallStarted -> context.getString( is MessageType.JitsiCallStarted -> context.getString(
R.string.message_video_call_started, message.sender?.username R.string.message_video_call_started, message.sender?.username
) )
else -> { else -> throw InvalidParameterException("Invalid message type: ${message.type}")
throw InvalidParameterException("Invalid message type: ${message.type}")
} }
} }
val spannableMsg = SpannableStringBuilder(content) val spannableMsg = SpannableStringBuilder(content)
spannableMsg.setSpan( spannableMsg.setSpan(StyleSpan(Typeface.ITALIC), 0, spannableMsg.length, 0)
StyleSpan(Typeface.ITALIC), 0, spannableMsg.length, 0 spannableMsg.setSpan(ForegroundColorSpan(Color.GRAY), 0, spannableMsg.length, 0)
)
spannableMsg.setSpan(
ForegroundColorSpan(Color.GRAY), 0, spannableMsg.length, 0
)
return spannableMsg return spannableMsg
} }
} }
\ No newline at end of file
...@@ -36,15 +36,30 @@ class RoomUiModelMapper( ...@@ -36,15 +36,30 @@ class RoomUiModelMapper(
grouped: Boolean = false, grouped: Boolean = false,
showLastMessage: Boolean = true showLastMessage: Boolean = true
): List<ItemHolder<*>> { ): List<ItemHolder<*>> {
val list = ArrayList<ItemHolder<*>>(rooms.size + 4) val list = ArrayList<ItemHolder<*>>(rooms.size + 5)
var lastType: String? = null var lastType: String? = null
rooms.forEach { room -> if (grouped) {
if (grouped && lastType != room.chatRoom.type) { 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(HeaderItemHolder(roomType(room.chatRoom.type)))
} }
list.add(RoomItemHolder(map(room, showLastMessage))) list.add(RoomItemHolder(map(room, showLastMessage)))
lastType = room.chatRoom.type lastType = room.chatRoom.type
} }
} else {
rooms.forEach { room ->
list.add(RoomItemHolder(map(room, showLastMessage)))
}
}
return list return list
} }
...@@ -62,8 +77,7 @@ class RoomUiModelMapper( ...@@ -62,8 +77,7 @@ class RoomUiModelMapper(
return list return list
} }
private fun mapUser(user: User): RoomUiModel { private fun mapUser(user: User): RoomUiModel = with(user) {
return with(user) {
val name = mapName(user.username!!, user.name) val name = mapName(user.username!!, user.name)
val status = user.status val status = user.status
val avatar = serverUrl.avatarUrl(user.username!!) val avatar = serverUrl.avatarUrl(user.username!!)
...@@ -78,10 +92,8 @@ class RoomUiModelMapper( ...@@ -78,10 +92,8 @@ class RoomUiModelMapper(
username = username username = username
) )
} }
}
private fun mapRoom(room: Room, showLastMessage: Boolean = true): RoomUiModel { private fun mapRoom(room: Room, showLastMessage: Boolean = true): RoomUiModel = with(room) {
return with(room) {
RoomUiModel( RoomUiModel(
id = id, id = id,
name = name!!, name = name!!,
...@@ -100,14 +112,13 @@ class RoomUiModelMapper( ...@@ -100,14 +112,13 @@ class RoomUiModelMapper(
writable = isChannelWritable(muted) writable = isChannelWritable(muted)
) )
} }
}
fun map(chatRoom: ChatRoom, showLastMessage: Boolean = true): RoomUiModel { fun map(chatRoom: ChatRoom, showLastMessage: Boolean = true): RoomUiModel = with(chatRoom.chatRoom) {
return with(chatRoom.chatRoom) {
val isUnread = alert || unread > 0 val isUnread = alert || unread > 0
val type = roomTypeOf(type) val type = roomTypeOf(type)
val status = chatRoom.status?.let { userStatusOf(it) } val status = chatRoom.status?.let { userStatusOf(it) }
val roomName = mapName(name, fullname) val roomName = mapName(name, fullname)
val favorite = favorite
val timestamp = mapDate(lastMessageTimestamp ?: updatedAt) val timestamp = mapDate(lastMessageTimestamp ?: updatedAt)
val avatar = if (type is RoomType.DirectMessage) { val avatar = if (type is RoomType.DirectMessage) {
serverUrl.avatarUrl(name) serverUrl.avatarUrl(name)
...@@ -128,8 +139,7 @@ class RoomUiModelMapper( ...@@ -128,8 +139,7 @@ class RoomUiModelMapper(
} }
val hasMentions = mapMentions(userMentions, groupMentions) val hasMentions = mapMentions(userMentions, groupMentions)
val open = open val open = open
val lastMessageMarkdown = val lastMessageMarkdown = lastMessage?.let { Markwon.markdown(context, it.toString()).toString() }
lastMessage?.let { Markwon.markdown(context, it.toString()).toString() }
RoomUiModel( RoomUiModel(
id = id, id = id,
...@@ -140,6 +150,7 @@ class RoomUiModelMapper( ...@@ -140,6 +150,7 @@ class RoomUiModelMapper(
date = timestamp, date = timestamp,
unread = unread, unread = unread,
mentions = hasMentions, mentions = hasMentions,
favorite = favorite,
alert = isUnread, alert = isUnread,
lastMessage = lastMessageMarkdown, lastMessage = lastMessageMarkdown,
status = status, status = status,
...@@ -148,21 +159,19 @@ class RoomUiModelMapper( ...@@ -148,21 +159,19 @@ class RoomUiModelMapper(
writable = isChannelWritable(muted) writable = isChannelWritable(muted)
) )
} }
}
private fun isChannelWritable(muted: List<String>?): Boolean { private fun isChannelWritable(muted: List<String>?): Boolean {
val canWriteToReadOnlyChannels = permissions.canPostToReadOnlyChannels() val canWriteToReadOnlyChannels = permissions.canPostToReadOnlyChannels()
return canWriteToReadOnlyChannels || !muted.orEmpty().contains(currentUser?.username) return canWriteToReadOnlyChannels || !muted.orEmpty().contains(currentUser?.username)
} }
private fun roomType(type: String): String { private fun roomType(type: String): String = with(context.resources) {
val resources = context.resources when (type) {
return when (type) { RoomType.CHANNEL -> getString(R.string.header_channel)
RoomType.CHANNEL -> resources.getString(R.string.header_channel) RoomType.PRIVATE_GROUP -> getString(R.string.header_private_groups)
RoomType.PRIVATE_GROUP -> resources.getString(R.string.header_private_groups) RoomType.DIRECT_MESSAGE -> getString(R.string.header_direct_messages)
RoomType.DIRECT_MESSAGE -> resources.getString(R.string.header_direct_messages) RoomType.LIVECHAT -> getString(R.string.header_live_chats)
RoomType.LIVECHAT -> resources.getString(R.string.header_live_chats) else -> getString(R.string.header_unknown)
else -> resources.getString(R.string.header_unknown)
} }
} }
...@@ -195,14 +204,12 @@ class RoomUiModelMapper( ...@@ -195,14 +204,12 @@ class RoomUiModelMapper(
} }
} }
private fun mapUnread(unread: Long): String? { private fun mapUnread(unread: Long): String? = when (unread) {
return when (unread) {
0L -> null 0L -> null
in 1..99 -> unread.toString() in 1..99 -> unread.toString()
else -> context.getString(R.string.msg_more_than_ninety_nine_unread_messages) else -> context.getString(R.string.msg_more_than_ninety_nine_unread_messages)
} }
}
private fun mapMentions(userMentions: Long?, groupMentions: Long?): Boolean { private fun mapMentions(userMentions: Long?, groupMentions: Long?): Boolean {
if (userMentions != null && groupMentions != null) { if (userMentions != null && groupMentions != null) {
......
...@@ -8,6 +8,7 @@ import androidx.core.view.isInvisible ...@@ -8,6 +8,7 @@ import androidx.core.view.isInvisible
import androidx.core.view.isVisible import androidx.core.view.isVisible
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.chatrooms.adapter.model.RoomUiModel 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.RoomType
import chat.rocket.common.model.UserStatus import chat.rocket.common.model.UserStatus
import kotlinx.android.synthetic.main.item_chat.view.* import kotlinx.android.synthetic.main.item_chat.view.*
...@@ -16,12 +17,12 @@ import kotlinx.android.synthetic.main.unread_messages_badge.view.* ...@@ -16,12 +17,12 @@ import kotlinx.android.synthetic.main.unread_messages_badge.view.*
class RoomViewHolder(itemView: View, private val listener: (RoomUiModel) -> Unit) : class RoomViewHolder(itemView: View, private val listener: (RoomUiModel) -> Unit) :
ViewHolder<RoomItemHolder>(itemView) { ViewHolder<RoomItemHolder>(itemView) {
private val resources: Resources = itemView.resources private val resources: Resources = itemView.resources
private val channelIcon: Drawable = resources.getDrawable(R.drawable.ic_hashtag_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) 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) 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) 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) 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) private val offlineIcon: Drawable = resources.getDrawable(R.drawable.ic_status_invisible_12dp, null)
override fun bindViews(data: RoomItemHolder) { override fun bindViews(data: RoomItemHolder) {
val room = data.data val room = data.data
...@@ -53,14 +54,14 @@ class RoomViewHolder(itemView: View, private val listener: (RoomUiModel) -> Unit ...@@ -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 = "!"
if (room.unread != null) text_total_unread_messages.text = room.unread if (room.unread != null) text_total_unread_messages.text = room.unread
if (room.mentions) 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_chat_name.setTextViewAppearance(context, R.style.ChatList_ChatName_Unread_TextView)
text_timestamp.setTextAppearance(context, R.style.ChatList_Timestamp_Unread_TextView) text_timestamp.setTextViewAppearance(context, R.style.ChatList_Timestamp_Unread_TextView)
text_last_message.setTextAppearance(context, R.style.ChatList_LastMessage_Unread_TextView) text_last_message.setTextViewAppearance(context, R.style.ChatList_LastMessage_Unread_TextView)
text_total_unread_messages.isVisible = true text_total_unread_messages.isVisible = true
} else { } else {
text_chat_name.setTextAppearance(context, R.style.ChatList_ChatName_TextView) text_chat_name.setTextViewAppearance(context, R.style.ChatList_ChatName_TextView)
text_timestamp.setTextAppearance(context, R.style.ChatList_Timestamp_TextView) text_timestamp.setTextViewAppearance(context, R.style.ChatList_Timestamp_TextView)
text_last_message.setTextAppearance(context, R.style.ChatList_LastMessage_TextView) text_last_message.setTextViewAppearance(context, R.style.ChatList_LastMessage_TextView)
text_total_unread_messages.isInvisible = true text_total_unread_messages.isInvisible = true
} }
...@@ -68,20 +69,16 @@ class RoomViewHolder(itemView: View, private val listener: (RoomUiModel) -> Unit ...@@ -68,20 +69,16 @@ class RoomViewHolder(itemView: View, private val listener: (RoomUiModel) -> Unit
} }
} }
private fun getRoomDrawable(type: RoomType): Drawable? { private fun getRoomDrawable(type: RoomType): Drawable? = when (type) {
return when (type) {
is RoomType.Channel -> channelIcon is RoomType.Channel -> channelIcon
is RoomType.PrivateGroup -> groupIcon is RoomType.PrivateGroup -> groupIcon
else -> null else -> null
} }
}
private fun getStatusDrawable(status: UserStatus): Drawable { private fun getStatusDrawable(status: UserStatus): Drawable = when (status) {
return when (status) {
is UserStatus.Online -> onlineIcon is UserStatus.Online -> onlineIcon
is UserStatus.Away -> awayIcon is UserStatus.Away -> awayIcon
is UserStatus.Busy -> busyIcon is UserStatus.Busy -> busyIcon
else -> offlineIcon else -> offlineIcon
} }
}
} }
\ No newline at end of file
...@@ -19,8 +19,7 @@ class RoomsAdapter(private val listener: (RoomUiModel) -> Unit) : ...@@ -19,8 +19,7 @@ class RoomsAdapter(private val listener: (RoomUiModel) -> Unit) :
notifyDataSetChanged() notifyDataSetChanged()
} }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder<*> { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder<*> = when (viewType) {
return when (viewType) {
VIEW_TYPE_ROOM -> { VIEW_TYPE_ROOM -> {
val view = parent.inflate(R.layout.item_chat) val view = parent.inflate(R.layout.item_chat)
RoomViewHolder(view, listener) RoomViewHolder(view, listener)
...@@ -35,7 +34,6 @@ class RoomsAdapter(private val listener: (RoomUiModel) -> Unit) : ...@@ -35,7 +34,6 @@ class RoomsAdapter(private val listener: (RoomUiModel) -> Unit) :
} }
else -> throw IllegalStateException("View type must be either Room, Header or Loading") else -> throw IllegalStateException("View type must be either Room, Header or Loading")
} }
}
override fun getItemCount() = values.size override fun getItemCount() = values.size
...@@ -49,14 +47,12 @@ class RoomsAdapter(private val listener: (RoomUiModel) -> Unit) : ...@@ -49,14 +47,12 @@ class RoomsAdapter(private val listener: (RoomUiModel) -> Unit) :
} }
} }
override fun getItemViewType(position: Int): Int { override fun getItemViewType(position: Int): Int = when (values[position]) {
return when (values[position]) {
is RoomItemHolder -> VIEW_TYPE_ROOM is RoomItemHolder -> VIEW_TYPE_ROOM
is HeaderItemHolder -> VIEW_TYPE_HEADER is HeaderItemHolder -> VIEW_TYPE_HEADER
is LoadingItemHolder -> VIEW_TYPE_LOADING is LoadingItemHolder -> VIEW_TYPE_LOADING
else -> throw IllegalStateException("View type must be either Room, Header or Loading") else -> throw IllegalStateException("View type must be either Room, Header or Loading")
} }
}
override fun onBindViewHolder(holder: ViewHolder<*>, position: Int) { override fun onBindViewHolder(holder: ViewHolder<*>, position: Int) {
if (holder is RoomViewHolder) { if (holder is RoomViewHolder) {
......
...@@ -12,6 +12,7 @@ data class RoomUiModel( ...@@ -12,6 +12,7 @@ data class RoomUiModel(
val date: CharSequence? = null, val date: CharSequence? = null,
val unread: String? = null, val unread: String? = null,
val alert: Boolean = false, val alert: Boolean = false,
val favorite: Boolean? = false,
val mentions: Boolean = false, val mentions: Boolean = false,
val lastMessage: CharSequence? = null, val lastMessage: CharSequence? = null,
val status: UserStatus? = null, val status: UserStatus? = null,
......
...@@ -2,6 +2,7 @@ package chat.rocket.android.chatrooms.presentation ...@@ -2,6 +2,7 @@ package chat.rocket.android.chatrooms.presentation
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.chatrooms.adapter.model.RoomUiModel import chat.rocket.android.chatrooms.adapter.model.RoomUiModel
import chat.rocket.android.chatrooms.domain.FetchChatRoomsInteractor
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.db.DatabaseManager import chat.rocket.android.db.DatabaseManager
import chat.rocket.android.db.model.ChatRoomEntity import chat.rocket.android.db.model.ChatRoomEntity
...@@ -117,6 +118,7 @@ class ChatRoomsPresenter @Inject constructor( ...@@ -117,6 +118,7 @@ class ChatRoomsPresenter @Inject constructor(
retryIO("createDirectMessage($name)") { retryIO("createDirectMessage($name)") {
withTimeout(10000) { withTimeout(10000) {
createDirectMessage(name) createDirectMessage(name)
FetchChatRoomsInteractor(client, dbManager).refreshChatRooms()
} }
} }
val fromTo = mutableListOf(myself.id, id).apply { val fromTo = mutableListOf(myself.id, id).apply {
......
...@@ -36,7 +36,6 @@ import chat.rocket.android.helper.SharedPreferenceHelper ...@@ -36,7 +36,6 @@ import chat.rocket.android.helper.SharedPreferenceHelper
import chat.rocket.android.util.extension.onQueryTextListener import chat.rocket.android.util.extension.onQueryTextListener
import chat.rocket.android.util.extensions.fadeIn import chat.rocket.android.util.extensions.fadeIn
import chat.rocket.android.util.extensions.fadeOut 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.ifNotNullNotEmpty
import chat.rocket.android.util.extensions.inflate import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.showToast import chat.rocket.android.util.extensions.showToast
...@@ -69,22 +68,19 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView { ...@@ -69,22 +68,19 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
private var progressDialog: ProgressDialog? = null private var progressDialog: ProgressDialog? = null
companion object { companion object {
fun newInstance(chatRoomId: String? = null): ChatRoomsFragment { fun newInstance(chatRoomId: String? = null): ChatRoomsFragment = ChatRoomsFragment().apply {
return ChatRoomsFragment().apply {
arguments = Bundle(1).apply { arguments = Bundle(1).apply {
putString(BUNDLE_CHAT_ROOM_ID, chatRoomId) putString(BUNDLE_CHAT_ROOM_ID, chatRoomId)
} }
} }
} }
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
AndroidSupportInjection.inject(this) AndroidSupportInjection.inject(this)
setHasOptionsMenu(true) setHasOptionsMenu(true)
val bundle = arguments arguments?.run {
if (bundle != null) { chatRoomId = getString(BUNDLE_CHAT_ROOM_ID)
chatRoomId = bundle.getString(BUNDLE_CHAT_ROOM_ID)
chatRoomId.ifNotNullNotEmpty { roomId -> chatRoomId.ifNotNullNotEmpty { roomId ->
presenter.loadChatRoom(roomId) presenter.loadChatRoom(roomId)
chatRoomId = null chatRoomId = null
...@@ -129,12 +125,14 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView { ...@@ -129,12 +125,14 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
) )
) )
recycler_view.itemAnimator = DefaultItemAnimator() recycler_view.itemAnimator = DefaultItemAnimator()
recycler_view.adapter = adapter
viewModel.getChatRooms().observe(viewLifecycleOwner, Observer { rooms -> viewModel.getChatRooms().observe(viewLifecycleOwner, Observer { rooms ->
rooms?.let { rooms?.let {
Timber.d("Got items: $it") Timber.d("Got items: $it")
adapter.values = it adapter.values = it
if (recycler_view.adapter != adapter) {
recycler_view.adapter = adapter
}
if (rooms.isNotEmpty()) { if (rooms.isNotEmpty()) {
text_no_data_to_display.isVisible = false text_no_data_to_display.isVisible = false
} }
...@@ -320,21 +318,20 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView { ...@@ -320,21 +318,20 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
ui { ui {
text_connection_status.fadeIn() text_connection_status.fadeIn()
handler.removeCallbacks(dismissStatus) handler.removeCallbacks(dismissStatus)
when (state) { text_connection_status.text = when (state) {
is State.Connected -> { is State.Connected -> {
text_connection_status.text = getString(R.string.status_connected)
handler.postDelayed(dismissStatus, 2000) 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 ...@@ -45,9 +45,7 @@ class CreateChannelFragment : Fragment(), CreateChannelView, ActionMode.Callback
lateinit var analyticsManager: AnalyticsManager lateinit var analyticsManager: AnalyticsManager
private var actionMode: ActionMode? = null private var actionMode: ActionMode? = null
private val adapter: MembersAdapter = MembersAdapter { private val adapter: MembersAdapter = MembersAdapter {
if (it.username != null) { it.username?.run { processSelectedMember(this) }
processSelectedMember(it.username)
}
} }
private val compositeDisposable = CompositeDisposable() private val compositeDisposable = CompositeDisposable()
private var channelType: String = RoomType.CHANNEL private var channelType: String = RoomType.CHANNEL
...@@ -294,8 +292,8 @@ class CreateChannelFragment : Fragment(), CreateChannelView, ActionMode.Callback ...@@ -294,8 +292,8 @@ class CreateChannelFragment : Fragment(), CreateChannelView, ActionMode.Callback
private fun addChip(chipText: String) { private fun addChip(chipText: String) {
val chip = Chip(context) val chip = Chip(context)
chip.chipText = chipText chip.text = chipText
chip.isCloseIconEnabled = true chip.isCloseIconVisible = true
chip.setChipBackgroundColorResource(R.color.icon_grey) chip.setChipBackgroundColorResource(R.color.icon_grey)
setupChipOnCloseIconClickListener(chip) setupChipOnCloseIconClickListener(chip)
chip_group_member.addView(chip) chip_group_member.addView(chip)
...@@ -304,7 +302,7 @@ class CreateChannelFragment : Fragment(), CreateChannelView, ActionMode.Callback ...@@ -304,7 +302,7 @@ class CreateChannelFragment : Fragment(), CreateChannelView, ActionMode.Callback
private fun setupChipOnCloseIconClickListener(chip: Chip) { private fun setupChipOnCloseIconClickListener(chip: Chip) {
chip.setOnCloseIconClickListener { chip.setOnCloseIconClickListener {
removeChip(it) 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. // whenever we remove a chip we should process the chip group visibility.
processChipGroupVisibility() processChipGroupVisibility()
} }
......
...@@ -41,12 +41,20 @@ import kotlinx.coroutines.newSingleThreadContext ...@@ -41,12 +41,20 @@ import kotlinx.coroutines.newSingleThreadContext
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import timber.log.Timber import timber.log.Timber
import java.util.HashSet 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 import kotlin.system.measureTimeMillis
class DatabaseManager(val context: Application, val serverUrl: String) { class DatabaseManager(val context: Application, val serverUrl: String) {
private val database: RCDatabase = androidx.room.Room.databaseBuilder(context, private val database: RCDatabase = androidx.room.Room.databaseBuilder(
RCDatabase::class.java, serverUrl.databaseName()) context,
RCDatabase::class.java, serverUrl.databaseName()
)
.fallbackToDestructiveMigration() .fallbackToDestructiveMigration()
.build() .build()
private val dbContext = newSingleThreadContext("$serverUrl-db-context") private val dbContext = newSingleThreadContext("$serverUrl-db-context")
...@@ -141,10 +149,11 @@ class DatabaseManager(val context: Application, val serverUrl: String) { ...@@ -141,10 +149,11 @@ class DatabaseManager(val context: Application, val serverUrl: String) {
batch.forEach { batch.forEach {
when (it.type) { when (it.type) {
is Type.Removed -> toRemove.add(removeChatRoom(it.data)) 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 -> { is Type.Updated -> {
when (it.data) { when (it.data) {
is Subscription -> updateSubs[(it.data as Subscription).roomId] = it.data as Subscription is Subscription -> updateSubs[(it.data as Subscription).roomId] =
it.data as Subscription
is Room -> updateRooms[(it.data as Room).id] = it.data as Room is Room -> updateRooms[(it.data as Room).id] = it.data as Room
} }
} }
...@@ -158,7 +167,13 @@ class DatabaseManager(val context: Application, val serverUrl: String) { ...@@ -158,7 +167,13 @@ class DatabaseManager(val context: Application, val serverUrl: String) {
val filteredUpdate = toUpdate.filterNot { toRemove.contains(it.id) } val filteredUpdate = toUpdate.filterNot { toRemove.contains(it.id) }
val filteredInsert = toInsert.filterNot { toRemove.contains(it.id) } val filteredInsert = toInsert.filterNot { toRemove.contains(it.id) }
sendOperation(Operation.UpdateRooms(filteredInsert, filteredUpdate, toRemove.toList())) sendOperation(
Operation.UpdateRooms(
filteredInsert,
filteredUpdate,
toRemove.toList()
)
)
} catch (ex: Exception) { } catch (ex: Exception) {
Timber.d(ex, "Error updating chatrooms") Timber.d(ex, "Error updating chatrooms")
} }
...@@ -187,17 +202,14 @@ class DatabaseManager(val context: Application, val serverUrl: String) { ...@@ -187,17 +202,14 @@ class DatabaseManager(val context: Application, val serverUrl: String) {
} }
} }
fun processMessagesBatch(messages: List<Message>): Job { fun processMessagesBatch(messages: List<Message>): Job = GlobalScope.launch(dbManagerContext) {
return GlobalScope.launch(dbManagerContext) {
val list = mutableListOf<Pair<MessageEntity, List<BaseMessageEntity>>>() val list = mutableListOf<Pair<MessageEntity, List<BaseMessageEntity>>>()
messages.forEach { message -> messages.forEach { message ->
val pair = createMessageEntities(message) val pair = createMessageEntities(message)
list.add(pair) list.add(pair)
} }
sendOperation(Operation.InsertMessages(list)) sendOperation(Operation.InsertMessages(list))
} }
}
private suspend fun createMessageEntities(message: Message): Pair<MessageEntity, List<BaseMessageEntity>> { private suspend fun createMessageEntities(message: Message): Pair<MessageEntity, List<BaseMessageEntity>> {
val messageEntity = message.toEntity() val messageEntity = message.toEntity()
...@@ -214,91 +226,86 @@ class DatabaseManager(val context: Application, val serverUrl: String) { ...@@ -214,91 +226,86 @@ class DatabaseManager(val context: Application, val serverUrl: String) {
return Pair(messageEntity, list) return Pair(messageEntity, list)
} }
private fun createReactions(message: Message): List<BaseMessageEntity>? { private fun createReactions(message: Message): List<BaseMessageEntity>? =
if (message.reactions == null || message.reactions!!.isEmpty()) { message.reactions?.run {
return null if (isNotEmpty()) {
}
val reactions = message.reactions!!
val list = mutableListOf<BaseMessageEntity>() val list = mutableListOf<BaseMessageEntity>()
reactions.keys.forEach { reaction -> keys.forEach { reaction ->
val users = reactions[reaction] get(reaction)?.let { reactionValue ->
users?.let { users -> list.add(
list.add(ReactionEntity(reaction, message.id, users.size, users.joinToString())) ReactionEntity(
reaction,
message.id,
size,
reactionValue.joinToString()
)
)
} }
} }
list
return list } else null
}
private fun createUrlEntities(message: Message): List<BaseMessageEntity>? {
if (message.urls == null || message.urls!!.isEmpty()) {
return null
} }
private fun createUrlEntities(message: Message): List<BaseMessageEntity>? = message.urls?.run {
if (isNotEmpty()) {
val list = mutableListOf<UrlEntity>() val list = mutableListOf<UrlEntity>()
message.urls!!.forEach { url -> forEach { url ->
list.add(UrlEntity(message.id, url.url, url.parsedUrl?.host, url.meta?.title, list.add(
url.meta?.description, url.meta?.imageUrl)) UrlEntity(
} message.id, url.url, url.parsedUrl?.host, url.meta?.title,
url.meta?.description, url.meta?.imageUrl
return list )
)
} }
list
private fun createChannelRelations(message: Message): List<BaseMessageEntity>? { } else null
if (message.channels == null || message.channels!!.isEmpty()) {
return null
} }
private fun createChannelRelations(message: Message): List<BaseMessageEntity>? =
message.channels?.run {
if (isNotEmpty()) {
val list = mutableListOf<MessageChannels>() val list = mutableListOf<MessageChannels>()
message.channels!!.forEach { channel -> forEach { channel ->
list.add(MessageChannels(message.id, channel.id, channel.name)) list.add(MessageChannels(message.id, channel.id, channel.name))
} }
list
return list } else null
}
private suspend fun createMentionRelations(message: Message): List<BaseMessageEntity>? {
if (message.mentions == null || message.mentions!!.isEmpty()) {
return null
} }
private suspend fun createMentionRelations(message: Message): List<BaseMessageEntity>? =
message.mentions?.run {
if (isNotEmpty()) {
val list = mutableListOf<MessageMentionsRelation>() val list = mutableListOf<MessageMentionsRelation>()
message.mentions!!.filterNot { user -> user.id.isNullOrEmpty() }.forEach { mention -> filterNot { user -> user.id.isNullOrEmpty() }.forEach { mention ->
insertUserIfMissing(mention) insertUserIfMissing(mention)
list.add(MessageMentionsRelation(message.id, mention.id!!)) list.add(MessageMentionsRelation(message.id, mention.id!!))
} }
list
return list } else null
}
private suspend fun createFavoriteRelations(message: Message): List<BaseMessageEntity>? {
if (message.starred == null || message.starred!!.isEmpty()) {
return null
} }
private suspend fun createFavoriteRelations(message: Message): List<BaseMessageEntity>? =
message.starred?.run {
if (isNotEmpty()) {
val list = mutableListOf<MessageFavoritesRelation>() val list = mutableListOf<MessageFavoritesRelation>()
message.starred!!.filterNot { user -> user.id.isNullOrEmpty() }.forEach { userId -> filterNot { user -> user.id.isNullOrEmpty() }.forEach { userId ->
insertUserIfMissing(userId) insertUserIfMissing(userId)
list.add(MessageFavoritesRelation(message.id, userId.id!!)) list.add(MessageFavoritesRelation(message.id, userId.id!!))
} }
list
return 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 -> 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.addAll(attachment.asEntity(message.id, context))
} }
list
return list } else null
} }
private suspend fun createUpdates(): List<ChatRoomEntity> { private suspend fun createUpdates(): List<ChatRoomEntity> {
...@@ -376,15 +383,11 @@ class DatabaseManager(val context: Application, val serverUrl: String) { ...@@ -376,15 +383,11 @@ class DatabaseManager(val context: Application, val serverUrl: String) {
} }
} }
private fun mapLastMessageText(message: Message?): String? { private fun mapLastMessageText(message: Message?): String? = message?.run {
return if (message == null) { if (this.message.isEmpty() && attachments?.isNotEmpty() == true) {
null message.attachments?.let { mapAttachmentText(it[0]) }
} else { } else {
return if (message.message.isEmpty() && message.attachments?.isNotEmpty() == true) { this.message
mapAttachmentText(message.attachments!![0])
} else {
message.message
}
} }
} }
...@@ -432,13 +435,11 @@ class DatabaseManager(val context: Application, val serverUrl: String) { ...@@ -432,13 +435,11 @@ class DatabaseManager(val context: Application, val serverUrl: String) {
} }
} }
private suspend fun insertChatRoom(data: BaseRoom): ChatRoomEntity? { private suspend fun insertChatRoom(data: BaseRoom): ChatRoomEntity? = when (data) {
return when (data) {
is Room -> insertRoom(data) is Room -> insertRoom(data)
is Subscription -> insertSubscription(data) is Subscription -> insertSubscription(data)
else -> null else -> null
} }
}
private suspend fun insertRoom(data: Room): ChatRoomEntity? { private suspend fun insertRoom(data: Room): ChatRoomEntity? {
val subscription = insertSubs.remove(data.id) val subscription = insertSubs.remove(data.id)
...@@ -576,31 +577,18 @@ class DatabaseManager(val context: Application, val serverUrl: String) { ...@@ -576,31 +577,18 @@ class DatabaseManager(val context: Application, val serverUrl: String) {
is Operation.ClearStatus -> userDao().clearStatus() is Operation.ClearStatus -> userDao().clearStatus()
is Operation.UpdateRooms -> { is Operation.UpdateRooms -> {
Timber.d("Running ChatRooms transaction: remove: ${operation.toRemove} - insert: ${operation.toInsert} - update: ${operation.toUpdate}") Timber.d("Running ChatRooms transaction: remove: ${operation.toRemove} - insert: ${operation.toInsert} - update: ${operation.toUpdate}")
chatRoomDao().update(operation.toInsert, operation.toUpdate, operation.toRemove) chatRoomDao().update(operation.toInsert, operation.toUpdate, operation.toRemove)
} }
is Operation.InsertRooms -> { is Operation.InsertRooms -> chatRoomDao().insertOrReplace(operation.chatRooms)
chatRoomDao().insertOrReplace(operation.chatRooms) is Operation.CleanInsertRooms -> chatRoomDao().cleanInsert(operation.chatRooms)
}
is Operation.CleanInsertRooms -> {
chatRoomDao().cleanInsert(operation.chatRooms)
}
is Operation.InsertUsers -> { is Operation.InsertUsers -> {
val time = measureTimeMillis { userDao().upsert(operation.users) } val time = measureTimeMillis { userDao().upsert(operation.users) }
Timber.d("Upserted users batch(${operation.users.size}) in $time MS") Timber.d("Upserted users batch(${operation.users.size}) in $time MS")
} }
is Operation.InsertUser -> { is Operation.InsertUser -> userDao().insert(operation.user)
userDao().insert(operation.user) is Operation.UpsertUser -> userDao().upsert(operation.user)
} is Operation.InsertMessages -> messageDao().insert(operation.list)
is Operation.UpsertUser -> { is Operation.SaveLastSync -> messageDao().saveLastSync(operation.sync)
userDao().upsert(operation.user)
}
is Operation.InsertMessages -> {
messageDao().insert(operation.list)
}
is Operation.SaveLastSync -> {
messageDao().saveLastSync(operation.sync)
}
}.exhaustive }.exhaustive
} }
} }
...@@ -622,7 +610,8 @@ sealed class Operation { ...@@ -622,7 +610,8 @@ sealed class Operation {
data class UpsertUser(val user: BaseUserEntity) : Operation() data class UpsertUser(val user: BaseUserEntity) : Operation()
data class InsertUser(val user: UserEntity) : Operation() data class InsertUser(val user: UserEntity) : Operation()
data class InsertMessages(val list: List<Pair<MessageEntity, List<BaseMessageEntity>>>) : Operation() data class InsertMessages(val list: List<Pair<MessageEntity, List<BaseMessageEntity>>>) :
Operation()
data class SaveLastSync(val sync: MessagesSync) : Operation() data class SaveLastSync(val sync: MessagesSync) : Operation()
} }
......
...@@ -115,7 +115,7 @@ fun Attachment.asEntity(msgId: String, context: Context): List<BaseMessageEntity ...@@ -115,7 +115,7 @@ fun Attachment.asEntity(msgId: String, context: Context): List<BaseMessageEntity
val text = mapAttachmentText(text, attachments?.firstOrNull(), context) val text = mapAttachmentText(text, attachments?.firstOrNull(), context)
val entity = AttachmentEntity( list.add(AttachmentEntity(
_id = attachmentId, _id = attachmentId,
messageId = msgId, messageId = msgId,
title = title, title = title,
...@@ -144,16 +144,14 @@ fun Attachment.asEntity(msgId: String, context: Context): List<BaseMessageEntity ...@@ -144,16 +144,14 @@ fun Attachment.asEntity(msgId: String, context: Context): List<BaseMessageEntity
buttonAlignment = buttonAlignment, buttonAlignment = buttonAlignment,
hasActions = actions?.isNotEmpty() == true, hasActions = actions?.isNotEmpty() == true,
hasFields = fields?.isNotEmpty() == true hasFields = fields?.isNotEmpty() == true
) ))
list.add(entity)
fields?.forEach { field -> fields?.forEach { field ->
val entity = AttachmentFieldEntity( list.add(AttachmentFieldEntity(
attachmentId = attachmentId, attachmentId = attachmentId,
title = field.title, title = field.title,
value = field.value value = field.value
) ))
list.add(entity)
} }
actions?.forEach { action -> actions?.forEach { action ->
...@@ -175,18 +173,14 @@ fun Attachment.asEntity(msgId: String, context: Context): List<BaseMessageEntity ...@@ -175,18 +173,14 @@ fun Attachment.asEntity(msgId: String, context: Context): List<BaseMessageEntity
return list return list
} }
fun mapAttachmentText(text: String?, attachment: Attachment?, context: Context): String? { fun mapAttachmentText(text: String?, attachment: Attachment?, context: Context): String? = attachment?.run {
return if (attachment != null) {
when { when {
attachment.imageUrl.isNotNullNorEmpty() -> context.getString(R.string.msg_preview_photo) imageUrl.isNotNullNorEmpty() -> context.getString(R.string.msg_preview_photo)
attachment.videoUrl.isNotNullNorEmpty() -> context.getString(R.string.msg_preview_video) videoUrl.isNotNullNorEmpty() -> context.getString(R.string.msg_preview_video)
attachment.audioUrl.isNotNullNorEmpty() -> context.getString(R.string.msg_preview_audio) audioUrl.isNotNullNorEmpty() -> context.getString(R.string.msg_preview_audio)
attachment.titleLink.isNotNullNorEmpty() && titleLink.isNotNullNorEmpty() &&
attachment.type?.contentEquals("file") == true -> type?.contentEquals("file") == true ->
context.getString(R.string.msg_preview_file) context.getString(R.string.msg_preview_file)
else -> text else -> text
} }
} else { } ?: text
text \ No newline at end of file
}
}
...@@ -36,8 +36,7 @@ class FavoriteMessagesPresenter @Inject constructor( ...@@ -36,8 +36,7 @@ class FavoriteMessagesPresenter @Inject constructor(
try { try {
view.showLoading() view.showLoading()
dbManager.getRoom(roomId)?.let { dbManager.getRoom(roomId)?.let {
val favoriteMessages = val favoriteMessages = client.getFavoriteMessages(roomId, roomTypeOf(it.chatRoom.type), offset)
client.getFavoriteMessages(roomId, roomTypeOf(it.chatRoom.type), offset)
val messageList = mapper.map(favoriteMessages.result, asNotReversed = true) val messageList = mapper.map(favoriteMessages.result, asNotReversed = true)
view.showFavoriteMessages(messageList) view.showFavoriteMessages(messageList)
offset += 1 * 30 offset += 1 * 30
......
...@@ -25,12 +25,10 @@ import dagger.android.support.AndroidSupportInjection ...@@ -25,12 +25,10 @@ import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_favorite_messages.* import kotlinx.android.synthetic.main.fragment_favorite_messages.*
import javax.inject.Inject import javax.inject.Inject
fun newInstance(chatRoomId: String): Fragment { fun newInstance(chatRoomId: String): Fragment = FavoriteMessagesFragment().apply {
return FavoriteMessagesFragment().apply {
arguments = Bundle(1).apply { arguments = Bundle(1).apply {
putString(INTENT_CHAT_ROOM_ID, chatRoomId) putString(INTENT_CHAT_ROOM_ID, chatRoomId)
} }
}
} }
internal const val TAG_FAVORITE_MESSAGES_FRAGMENT = "FavoriteMessagesFragment" internal const val TAG_FAVORITE_MESSAGES_FRAGMENT = "FavoriteMessagesFragment"
...@@ -48,12 +46,9 @@ class FavoriteMessagesFragment : Fragment(), FavoriteMessagesView { ...@@ -48,12 +46,9 @@ class FavoriteMessagesFragment : Fragment(), FavoriteMessagesView {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
AndroidSupportInjection.inject(this) AndroidSupportInjection.inject(this)
val bundle = arguments arguments?.run {
if (bundle != null) { chatRoomId = getString(INTENT_CHAT_ROOM_ID, "")
chatRoomId = bundle.getString(INTENT_CHAT_ROOM_ID) } ?: requireNotNull(arguments) { "no arguments supplied when the fragment was instantiated" }
} else {
requireNotNull(bundle) { "no arguments supplied when the fragment was instantiated" }
}
} }
override fun onCreateView( override fun onCreateView(
......
...@@ -30,12 +30,10 @@ import dagger.android.support.AndroidSupportInjection ...@@ -30,12 +30,10 @@ import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_files.* import kotlinx.android.synthetic.main.fragment_files.*
import javax.inject.Inject import javax.inject.Inject
fun newInstance(chatRoomId: String): Fragment { fun newInstance(chatRoomId: String): Fragment = FilesFragment().apply {
return FilesFragment().apply {
arguments = Bundle(1).apply { arguments = Bundle(1).apply {
putString(BUNDLE_CHAT_ROOM_ID, chatRoomId) putString(BUNDLE_CHAT_ROOM_ID, chatRoomId)
} }
}
} }
internal const val TAG_FILES_FRAGMENT = "FilesFragment" internal const val TAG_FILES_FRAGMENT = "FilesFragment"
...@@ -55,12 +53,9 @@ class FilesFragment : Fragment(), FilesView { ...@@ -55,12 +53,9 @@ class FilesFragment : Fragment(), FilesView {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
AndroidSupportInjection.inject(this) AndroidSupportInjection.inject(this)
val bundle = arguments arguments?.run {
if (bundle != null) { chatRoomId = getString(BUNDLE_CHAT_ROOM_ID, "")
chatRoomId = bundle.getString(BUNDLE_CHAT_ROOM_ID) } ?: requireNotNull(arguments) { "no arguments supplied when the fragment was instantiated" }
} else {
requireNotNull(bundle) { "no arguments supplied when the fragment was instantiated" }
}
} }
override fun onCreateView( override fun onCreateView(
......
...@@ -53,8 +53,8 @@ object ImageHelper { ...@@ -53,8 +53,8 @@ object ImageHelper {
) )
val toolbar = Toolbar(context).also { val toolbar = Toolbar(context).also {
it.inflateMenu(R.menu.image_actions) it.inflateMenu(R.menu.image_actions)
it.setOnMenuItemClickListener { it.setOnMenuItemClickListener { view ->
return@setOnMenuItemClickListener when (it.itemId) { return@setOnMenuItemClickListener when (view.itemId) {
R.id.action_save_image -> saveImage(context) R.id.action_save_image -> saveImage(context)
else -> true else -> true
} }
...@@ -62,20 +62,24 @@ object ImageHelper { ...@@ -62,20 +62,24 @@ object ImageHelper {
val titleSize = context.resources val titleSize = context.resources
.getDimensionPixelSize(R.dimen.viewer_toolbar_title) .getDimensionPixelSize(R.dimen.viewer_toolbar_title)
val titleTextView = TextView(context).also { val titleTextView = TextView(context).also { tv ->
it.text = imageName with(tv) {
it.setTextColor(Color.WHITE) text = imageName
it.setTextSize(TypedValue.COMPLEX_UNIT_PX, titleSize.toFloat()) setTextColor(Color.WHITE)
it.ellipsize = TextUtils.TruncateAt.END setTextSize(TypedValue.COMPLEX_UNIT_PX, titleSize.toFloat())
it.setSingleLine() ellipsize = TextUtils.TruncateAt.END
it.typeface = Typeface.DEFAULT_BOLD setSingleLine()
it.setPadding(pad) typeface = Typeface.DEFAULT_BOLD
setPadding(pad)
}
} }
val backArrowView = ImageView(context).also { val backArrowView = ImageView(context).also { imgView ->
it.setImageResource(R.drawable.ic_arrow_back_white_24dp) with(imgView) {
it.setOnClickListener { imageViewer?.onDismiss() } setImageResource(R.drawable.ic_arrow_back_white_24dp)
it.setPadding(0, pad, pad, pad) setOnClickListener { imageViewer?.onDismiss() }
setPadding(0, pad, pad, pad)
}
} }
val layoutParams = AppBarLayout.LayoutParams( val layoutParams = AppBarLayout.LayoutParams(
...@@ -88,15 +92,17 @@ object ImageHelper { ...@@ -88,15 +92,17 @@ object ImageHelper {
} }
val appBarLayout = AppBarLayout(context).also { val appBarLayout = AppBarLayout(context).also {
it.layoutParams = lparams with(it) {
it.setBackgroundColor(Color.BLACK) layoutParams = lparams
it.addView( setBackgroundColor(Color.BLACK)
addView(
toolbar, AppBarLayout.LayoutParams( toolbar, AppBarLayout.LayoutParams(
AppBarLayout.LayoutParams.MATCH_PARENT, AppBarLayout.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT ViewGroup.LayoutParams.WRAP_CONTENT
) )
) )
} }
}
val builder = ImageViewer.createPipelineDraweeControllerBuilder() val builder = ImageViewer.createPipelineDraweeControllerBuilder()
.setImageRequest(request) .setImageRequest(request)
......
...@@ -38,6 +38,9 @@ import kotlinx.android.synthetic.main.activity_main.* ...@@ -38,6 +38,9 @@ import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.app_bar.* import kotlinx.android.synthetic.main.app_bar.*
import kotlinx.android.synthetic.main.nav_header.view.* import kotlinx.android.synthetic.main.nav_header.view.*
import javax.inject.Inject import javax.inject.Inject
import android.app.NotificationManager
import android.content.Context
private const val CURRENT_STATE = "current_state" private const val CURRENT_STATE = "current_state"
...@@ -90,6 +93,9 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, ...@@ -90,6 +93,9 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector,
presenter.toChatList(chatRoomId) presenter.toChatList(chatRoomId)
isFragmentAdded = true isFragmentAdded = true
} }
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE)
as NotificationManager
notificationManager.cancelAll()
} }
override fun onDestroy() { override fun onDestroy() {
......
...@@ -10,8 +10,9 @@ import chat.rocket.android.util.extensions.inflate ...@@ -10,8 +10,9 @@ import chat.rocket.android.util.extensions.inflate
import kotlinx.android.synthetic.main.avatar.view.* import kotlinx.android.synthetic.main.avatar.view.*
import kotlinx.android.synthetic.main.item_member.view.* import kotlinx.android.synthetic.main.item_member.view.*
class MembersAdapter(private val listener: (MemberUiModel) -> Unit) : class MembersAdapter(
RecyclerView.Adapter<MembersAdapter.ViewHolder>() { private val listener: (MemberUiModel) -> Unit
) : RecyclerView.Adapter<MembersAdapter.ViewHolder>() {
private var dataSet: List<MemberUiModel> = ArrayList() private var dataSet: List<MemberUiModel> = ArrayList()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MembersAdapter.ViewHolder = override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MembersAdapter.ViewHolder =
...@@ -43,7 +44,8 @@ class MembersAdapter(private val listener: (MemberUiModel) -> Unit) : ...@@ -43,7 +44,8 @@ class MembersAdapter(private val listener: (MemberUiModel) -> Unit) :
fun bind(memberUiModel: MemberUiModel, listener: (MemberUiModel) -> Unit) = with(itemView) { fun bind(memberUiModel: MemberUiModel, listener: (MemberUiModel) -> Unit) = with(itemView) {
image_avatar.setImageURI(memberUiModel.avatarUri) image_avatar.setImageURI(memberUiModel.avatarUri)
text_member.content = memberUiModel.displayName 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) } setOnClickListener { listener(memberUiModel) }
} }
} }
......
...@@ -3,6 +3,7 @@ package chat.rocket.android.members.presentation ...@@ -3,6 +3,7 @@ package chat.rocket.android.members.presentation
import chat.rocket.android.chatroom.presentation.ChatRoomNavigator import chat.rocket.android.chatroom.presentation.ChatRoomNavigator
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.db.DatabaseManager import chat.rocket.android.db.DatabaseManager
import chat.rocket.android.helper.UserHelper
import chat.rocket.android.members.uimodel.MemberUiModel import chat.rocket.android.members.uimodel.MemberUiModel
import chat.rocket.android.members.uimodel.MemberUiModelMapper import chat.rocket.android.members.uimodel.MemberUiModelMapper
import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
...@@ -23,7 +24,8 @@ class MembersPresenter @Inject constructor( ...@@ -23,7 +24,8 @@ class MembersPresenter @Inject constructor(
@Named("currentServer") private val currentServer: String, @Named("currentServer") private val currentServer: String,
private val strategy: CancelStrategy, private val strategy: CancelStrategy,
private val mapper: MemberUiModelMapper, private val mapper: MemberUiModelMapper,
val factory: RocketChatClientFactory val factory: RocketChatClientFactory,
private val userHelper: UserHelper
) { ) {
private val client: RocketChatClient = factory.create(currentServer) private val client: RocketChatClient = factory.create(currentServer)
private var offset: Long = 0 private var offset: Long = 0
...@@ -59,6 +61,10 @@ class MembersPresenter @Inject constructor( ...@@ -59,6 +61,10 @@ class MembersPresenter @Inject constructor(
} }
fun toMemberDetails(memberUiModel: MemberUiModel) { fun toMemberDetails(memberUiModel: MemberUiModel) {
navigator.toMemberDetails(memberUiModel.userId) with(memberUiModel) {
if (userId != userHelper.user()?.id) {
navigator.toMemberDetails(userId)
}
}
} }
} }
...@@ -27,12 +27,10 @@ import kotlinx.android.synthetic.main.app_bar_chat_room.* ...@@ -27,12 +27,10 @@ import kotlinx.android.synthetic.main.app_bar_chat_room.*
import kotlinx.android.synthetic.main.fragment_members.* import kotlinx.android.synthetic.main.fragment_members.*
import javax.inject.Inject import javax.inject.Inject
fun newInstance(chatRoomId: String): Fragment { fun newInstance(chatRoomId: String): Fragment = MembersFragment().apply {
return MembersFragment().apply {
arguments = Bundle(1).apply { arguments = Bundle(1).apply {
putString(BUNDLE_CHAT_ROOM_ID, chatRoomId) putString(BUNDLE_CHAT_ROOM_ID, chatRoomId)
} }
}
} }
internal const val TAG_MEMBERS_FRAGMENT = "MembersFragment" internal const val TAG_MEMBERS_FRAGMENT = "MembersFragment"
...@@ -52,12 +50,9 @@ class MembersFragment : Fragment(), MembersView { ...@@ -52,12 +50,9 @@ class MembersFragment : Fragment(), MembersView {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
AndroidSupportInjection.inject(this) AndroidSupportInjection.inject(this)
val bundle = arguments arguments?.run {
if (bundle != null) { chatRoomId = getString(BUNDLE_CHAT_ROOM_ID, "")
chatRoomId = bundle.getString(BUNDLE_CHAT_ROOM_ID) } ?: requireNotNull(arguments) { "no arguments supplied when the fragment was instantiated" }
} else {
requireNotNull(bundle) { "no arguments supplied when the fragment was instantiated" }
}
} }
override fun onCreateView( override fun onCreateView(
...@@ -80,7 +75,7 @@ class MembersFragment : Fragment(), MembersView { ...@@ -80,7 +75,7 @@ class MembersFragment : Fragment(), MembersView {
setupToolbar(total) setupToolbar(total)
if (adapter.itemCount == 0) { if (adapter.itemCount == 0) {
adapter.prependData(dataSet) 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 : recycler_view.addOnScrollListener(object :
EndlessRecyclerViewScrollListener(linearLayoutManager) { EndlessRecyclerViewScrollListener(linearLayoutManager) {
override fun onLoadMore( override fun onLoadMore(
......
...@@ -25,12 +25,10 @@ import dagger.android.support.AndroidSupportInjection ...@@ -25,12 +25,10 @@ import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_mentions.* import kotlinx.android.synthetic.main.fragment_mentions.*
import javax.inject.Inject import javax.inject.Inject
fun newInstance(chatRoomId: String): Fragment { fun newInstance(chatRoomId: String): Fragment = MentionsFragment().apply {
return MentionsFragment().apply {
arguments = Bundle(1).apply { arguments = Bundle(1).apply {
putString(BUNDLE_CHAT_ROOM_ID, chatRoomId) putString(BUNDLE_CHAT_ROOM_ID, chatRoomId)
} }
}
} }
internal const val TAG_MENTIONS_FRAGMENT = "MentionsFragment" internal const val TAG_MENTIONS_FRAGMENT = "MentionsFragment"
...@@ -48,12 +46,9 @@ class MentionsFragment : Fragment(), MentionsView { ...@@ -48,12 +46,9 @@ class MentionsFragment : Fragment(), MentionsView {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
AndroidSupportInjection.inject(this) AndroidSupportInjection.inject(this)
val bundle = arguments arguments?.run {
if (bundle != null) { chatRoomId = getString(BUNDLE_CHAT_ROOM_ID, "")
chatRoomId = bundle.getString(BUNDLE_CHAT_ROOM_ID) } ?: requireNotNull(arguments) { "no arguments supplied when the fragment was instantiated" }
} else {
requireNotNull(bundle) { "no arguments supplied when the fragment was instantiated" }
}
} }
override fun onCreateView( override fun onCreateView(
......
...@@ -25,12 +25,10 @@ import dagger.android.support.AndroidSupportInjection ...@@ -25,12 +25,10 @@ import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_pinned_messages.* import kotlinx.android.synthetic.main.fragment_pinned_messages.*
import javax.inject.Inject import javax.inject.Inject
fun newInstance(chatRoomId: String): Fragment { fun newInstance(chatRoomId: String): Fragment = PinnedMessagesFragment().apply {
return PinnedMessagesFragment().apply {
arguments = Bundle(1).apply { arguments = Bundle(1).apply {
putString(BUNDLE_CHAT_ROOM_ID, chatRoomId) putString(BUNDLE_CHAT_ROOM_ID, chatRoomId)
} }
}
} }
internal const val TAG_PINNED_MESSAGES_FRAGMENT = "PinnedMessagesFragment" internal const val TAG_PINNED_MESSAGES_FRAGMENT = "PinnedMessagesFragment"
...@@ -48,12 +46,9 @@ class PinnedMessagesFragment : Fragment(), PinnedMessagesView { ...@@ -48,12 +46,9 @@ class PinnedMessagesFragment : Fragment(), PinnedMessagesView {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
AndroidSupportInjection.inject(this) AndroidSupportInjection.inject(this)
val bundle = arguments arguments?.run {
if (bundle != null) { chatRoomId = getString(BUNDLE_CHAT_ROOM_ID, "")
chatRoomId = bundle.getString(BUNDLE_CHAT_ROOM_ID) } ?: requireNotNull(arguments) { "no arguments supplied when the fragment was instantiated" }
} else {
requireNotNull(bundle) { "no arguments supplied when the fragment was instantiated" }
}
} }
override fun onCreateView( override fun onCreateView(
......
...@@ -38,13 +38,17 @@ class PreferencesFragment : Fragment(), PreferencesView { ...@@ -38,13 +38,17 @@ class PreferencesFragment : Fragment(), PreferencesView {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
setupToolbar()
setupListeners() setupListeners()
presenter.loadAnalyticsTrackingInformation() presenter.loadAnalyticsTrackingInformation()
analyticsManager.logScreenView(ScreenViewEvent.Preferences) analyticsManager.logScreenView(ScreenViewEvent.Preferences)
} }
override fun onResume() {
setupToolbar()
super.onResume()
}
override fun setupAnalyticsTrackingView(isAnalyticsTrackingEnabled: Boolean) { override fun setupAnalyticsTrackingView(isAnalyticsTrackingEnabled: Boolean) {
if (BuildConfig.FLAVOR == "foss") { if (BuildConfig.FLAVOR == "foss") {
switch_analytics_tracking.isChecked = false switch_analytics_tracking.isChecked = false
......
...@@ -82,7 +82,7 @@ class ProfilePresenter @Inject constructor( ...@@ -82,7 +82,7 @@ class ProfilePresenter @Inject constructor(
view.showLoading() view.showLoading()
try { try {
user?.id?.let { id -> user?.id?.let { id ->
retryIO { client.updateProfile(id, email, name, username) } retryIO { client.updateProfile(userId = id, email = email, name = name, username = username) }
view.showProfileUpdateSuccessfullyMessage() view.showProfileUpdateSuccessfullyMessage()
view.showProfile( view.showProfile(
serverUrl.avatarUrl(user.username ?: ""), serverUrl.avatarUrl(user.username ?: ""),
...@@ -115,7 +115,7 @@ class ProfilePresenter @Inject constructor( ...@@ -115,7 +115,7 @@ class ProfilePresenter @Inject constructor(
uriInteractor.getInputStream(uri) uriInteractor.getInputStream(uri)
} }
} }
user?.username?.let { view.reloadUserAvatar(it) } user?.username?.let { view.reloadUserAvatar(serverUrl.avatarUrl(it)) }
} catch (exception: RocketChatException) { } catch (exception: RocketChatException) {
exception.message?.let { exception.message?.let {
view.showMessage(it) view.showMessage(it)
...@@ -143,7 +143,7 @@ class ProfilePresenter @Inject constructor( ...@@ -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) { } catch (exception: RocketChatException) {
exception.message?.let { exception.message?.let {
view.showMessage(it) view.showMessage(it)
...@@ -163,7 +163,7 @@ class ProfilePresenter @Inject constructor( ...@@ -163,7 +163,7 @@ class ProfilePresenter @Inject constructor(
user?.id?.let { id -> user?.id?.let { id ->
retryIO { client.resetAvatar(id) } retryIO { client.resetAvatar(id) }
} }
user?.username?.let { view.reloadUserAvatar(it) } user?.username?.let { view.reloadUserAvatar(serverUrl.avatarUrl(it)) }
} catch (exception: RocketChatException) { } catch (exception: RocketChatException) {
exception.message?.let { exception.message?.let {
view.showMessage(it) view.showMessage(it)
......
...@@ -94,11 +94,13 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback { ...@@ -94,11 +94,13 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback {
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
if (resultData != null && resultCode == Activity.RESULT_OK) { resultData?.run {
if (resultCode == Activity.RESULT_OK) {
if (requestCode == REQUEST_CODE_FOR_PERFORM_SAF) { if (requestCode == REQUEST_CODE_FOR_PERFORM_SAF) {
presenter.updateAvatar(resultData.data) data?.let { presenter.updateAvatar(it) }
} else if (requestCode == REQUEST_CODE_FOR_PERFORM_CAMERA) { } else if (requestCode == REQUEST_CODE_FOR_PERFORM_CAMERA) {
presenter.preparePhotoAndUpdateAvatar(resultData.extras["data"] as Bitmap) extras?.get("data")?.let { presenter.preparePhotoAndUpdateAvatar(it as Bitmap) }
}
} }
} }
} }
...@@ -203,8 +205,7 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback { ...@@ -203,8 +205,7 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback {
} }
private fun setupToolbar() { private fun setupToolbar() {
(activity as AppCompatActivity?)?.supportActionBar?.title = (activity as AppCompatActivity?)?.supportActionBar?.title = getString(R.string.title_profile)
getString(R.string.title_profile)
} }
private fun setupListeners() { private fun setupListeners() {
...@@ -293,19 +294,14 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback { ...@@ -293,19 +294,14 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback {
} }
fun showDeleteAccountDialog() { fun showDeleteAccountDialog() {
val passwordEditText = EditText(context)
passwordEditText.hint = getString(R.string.msg_password)
context?.let { context?.let {
val builder = AlertDialog.Builder(it) val passwordEText = EditText(context);
builder.setTitle(R.string.title_are_you_sure) val mDialogView = LayoutInflater.from(it).inflate(R.layout.item_account_delete, null)
.setView(passwordEditText) val mBuilder = AlertDialog.Builder(it)
.setPositiveButton(R.string.action_delete_account) { _, _ ->
presenter.deleteAccount(passwordEditText.text.toString()) mBuilder.setView(mDialogView).setPositiveButton(R.string.action_delete_account) { _, _ ->
} presenter.deleteAccount(passwordEText.text.toString())
.setNegativeButton(android.R.string.no) { dialog, _ -> dialog.cancel() } }.setNegativeButton(android.R.string.no) { dialog, _ -> dialog.cancel() }.create().show()
.create()
.show()
} }
} }
} }
...@@ -12,13 +12,14 @@ import android.os.Build ...@@ -12,13 +12,14 @@ import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.Parcel import android.os.Parcel
import android.os.Parcelable import android.os.Parcelable
import android.text.Html
import android.text.Spanned
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat import androidx.core.app.NotificationManagerCompat
import androidx.core.app.RemoteInput import androidx.core.app.RemoteInput
import android.text.Html
import android.text.Spanned
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.text.HtmlCompat.FROM_HTML_MODE_LEGACY
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.main.ui.MainActivity import chat.rocket.android.main.ui.MainActivity
import chat.rocket.android.server.domain.GetAccountInteractor import chat.rocket.android.server.domain.GetAccountInteractor
...@@ -303,7 +304,11 @@ class PushManager @Inject constructor( ...@@ -303,7 +304,11 @@ class PushManager @Inject constructor(
// CharSequence extensions // CharSequence extensions
private fun CharSequence.fromHtml(): Spanned { 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 // NotificationCompat.Builder extensions
...@@ -383,12 +388,12 @@ data class PushMessage( ...@@ -383,12 +388,12 @@ data class PushMessage(
) : Parcelable { ) : Parcelable {
constructor(parcel: Parcel) : this( constructor(parcel: Parcel) : this(
parcel.readString().orEmpty(),
parcel.readString().orEmpty(),
parcel.readParcelable(PushMessage::class.java.classLoader) ?: PushInfo.EMPTY,
parcel.readString(), parcel.readString(),
parcel.readString(), parcel.readString(),
parcel.readParcelable(PushMessage::class.java.classLoader), parcel.readString().orEmpty(),
parcel.readString(),
parcel.readString(),
parcel.readString(),
parcel.readString(), parcel.readString(),
parcel.readString()) parcel.readString())
...@@ -433,9 +438,9 @@ data class PushInfo @KotshiConstructor constructor( ...@@ -433,9 +438,9 @@ data class PushInfo @KotshiConstructor constructor(
} }
constructor(parcel: Parcel) : this( constructor(parcel: Parcel) : this(
parcel.readString(), parcel.readString().orEmpty(),
parcel.readString(), parcel.readString().orEmpty(),
roomTypeOf(parcel.readString()), roomTypeOf(parcel.readString().orEmpty()),
parcel.readString(), parcel.readString(),
parcel.readParcelable(PushInfo::class.java.classLoader)) parcel.readParcelable(PushInfo::class.java.classLoader))
...@@ -481,7 +486,7 @@ data class PushSender @KotshiConstructor constructor( ...@@ -481,7 +486,7 @@ data class PushSender @KotshiConstructor constructor(
val name: String? val name: String?
) : Parcelable { ) : Parcelable {
constructor(parcel: Parcel) : this( constructor(parcel: Parcel) : this(
parcel.readString(), parcel.readString().orEmpty(),
parcel.readString(), parcel.readString(),
parcel.readString()) parcel.readString())
......
...@@ -106,6 +106,7 @@ class ConnectionManager( ...@@ -106,6 +106,7 @@ class ConnectionManager(
resubscribeRooms() resubscribeRooms()
temporaryStatus?.let { client.setTemporaryStatus(it) } temporaryStatus?.let { client.setTemporaryStatus(it) }
} }
is State.Waiting -> Timber.d("Connection in: ${status.seconds}") is State.Waiting -> Timber.d("Connection in: ${status.seconds}")
} }
...@@ -236,7 +237,7 @@ class ConnectionManager( ...@@ -236,7 +237,7 @@ class ConnectionManager(
} }
private fun resubscribeRooms() { private fun resubscribeRooms() {
roomMessagesChannels.toList().map { (roomId, channel) -> roomMessagesChannels.toList().map { (roomId, _) ->
client.subscribeRoomMessages(roomId) { _, id -> client.subscribeRoomMessages(roomId) { _, id ->
Timber.d("Subscribed to $roomId: $id") Timber.d("Subscribed to $roomId: $id")
subscriptionIdMap[roomId] = id subscriptionIdMap[roomId] = id
......
package chat.rocket.android.server.infraestructure package chat.rocket.android.server.infraestructure
import chat.rocket.android.db.DatabaseManager import chat.rocket.android.db.DatabaseManager
import chat.rocket.android.db.model.AttachmentActionEntity import chat.rocket.android.db.model.*
import chat.rocket.android.db.model.AttachmentEntity
import chat.rocket.android.db.model.FullMessage
import chat.rocket.android.db.model.ReactionEntity
import chat.rocket.android.db.model.UrlEntity
import chat.rocket.android.db.model.UserEntity
import chat.rocket.android.util.retryDB import chat.rocket.android.util.retryDB
import chat.rocket.common.model.SimpleRoom import chat.rocket.common.model.SimpleRoom
import chat.rocket.common.model.SimpleUser import chat.rocket.common.model.SimpleUser
...@@ -159,8 +154,8 @@ class DatabaseMessageMapper(private val dbManager: DatabaseManager) { ...@@ -159,8 +154,8 @@ class DatabaseMessageMapper(private val dbManager: DatabaseManager) {
} else { } else {
null null
} }
list.add(
val attachment = Attachment( Attachment(
title = title, title = title,
type = type, type = type,
description = description, description = description,
...@@ -190,7 +185,7 @@ class DatabaseMessageMapper(private val dbManager: DatabaseManager) { ...@@ -190,7 +185,7 @@ class DatabaseMessageMapper(private val dbManager: DatabaseManager) {
?: "vertical" else null, ?: "vertical" else null,
actions = actions actions = actions
) )
list.add(attachment) )
} }
} }
return list return list
......
...@@ -47,8 +47,8 @@ class DatabaseMessagesRepository( ...@@ -47,8 +47,8 @@ class DatabaseMessagesRepository(
dbManager.processMessagesBatch(listOf(message)).join() dbManager.processMessagesBatch(listOf(message)).join()
} }
override suspend fun saveAll(messages: List<Message>) { override suspend fun saveAll(newMessages: List<Message>) {
dbManager.processMessagesBatch(messages).join() dbManager.processMessagesBatch(newMessages).join()
} }
override suspend fun removeById(id: String) { override suspend fun removeById(id: String) {
...@@ -79,7 +79,7 @@ class DatabaseMessagesRepository( ...@@ -79,7 +79,7 @@ class DatabaseMessagesRepository(
override suspend fun getLastSyncDate(roomId: String): Long? = withContext(Dispatchers.IO) { override suspend fun getLastSyncDate(roomId: String): Long? = withContext(Dispatchers.IO) {
retryDB("getLastSync($roomId)") { 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( ...@@ -14,13 +14,8 @@ class SharedPreferencesAccountsRepository(
private val moshi: Moshi private val moshi: Moshi
) : AccountsRepository { ) : AccountsRepository {
override fun save(newAccount: Account) { override fun save(account: Account) {
val accounts = load() save(load().filter { item -> item.serverUrl != item.serverUrl }.toMutableList().apply { add(0, account) })
val newList = accounts.filter { account -> newAccount.serverUrl != account.serverUrl }
.toMutableList()
newList.add(0, newAccount)
save(newList)
} }
override fun load(): List<Account> { override fun load(): List<Account> {
...@@ -28,15 +23,11 @@ class SharedPreferencesAccountsRepository( ...@@ -28,15 +23,11 @@ class SharedPreferencesAccountsRepository(
val type = Types.newParameterizedType(List::class.java, Account::class.java) val type = Types.newParameterizedType(List::class.java, Account::class.java)
val adapter = moshi.adapter<List<Account>>(type) 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) { override fun remove(serverUrl: String) {
val accounts = load() save(load().filter { account -> serverUrl != account.serverUrl }.toMutableList())
val newList = accounts.filter { account -> serverUrl != account.serverUrl }
.toMutableList()
save(newList)
} }
private fun save(accounts: List<Account>) { private fun save(accounts: List<Account>) {
......
...@@ -15,8 +15,7 @@ class SharedPrefsBasicAuthRepository( ...@@ -15,8 +15,7 @@ class SharedPrefsBasicAuthRepository(
) : BasicAuthRepository { ) : BasicAuthRepository {
override fun save(basicAuth: BasicAuth) { override fun save(basicAuth: BasicAuth) {
val newList = load().filter { basicAuth -> basicAuth.host != basicAuth.host } val newList = load().filter { auth -> auth.host != auth.host }.toMutableList()
.toMutableList()
newList.add(0, basicAuth) newList.add(0, basicAuth)
save(newList) save(newList)
} }
...@@ -26,7 +25,7 @@ class SharedPrefsBasicAuthRepository( ...@@ -26,7 +25,7 @@ class SharedPrefsBasicAuthRepository(
val type = Types.newParameterizedType(List::class.java, BasicAuth::class.java) val type = Types.newParameterizedType(List::class.java, BasicAuth::class.java)
val adapter = moshi.adapter<List<BasicAuth>>(type) 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>) { private fun save(basicAuths: List<BasicAuth>) {
......
...@@ -106,8 +106,7 @@ class PasswordFragment : Fragment(), PasswordView, ActionMode.Callback { ...@@ -106,8 +106,7 @@ class PasswordFragment : Fragment(), PasswordView, ActionMode.Callback {
private fun finishActionMode() = actionMode?.finish() private fun finishActionMode() = actionMode?.finish()
private fun listenToChanges(): Disposable { private fun listenToChanges(): Disposable = Observables.combineLatest(
return Observables.combineLatest(
text_new_password.asObservable(), text_new_password.asObservable(),
text_confirm_password.asObservable() text_confirm_password.asObservable()
).subscribe { ).subscribe {
...@@ -119,7 +118,6 @@ class PasswordFragment : Fragment(), PasswordView, ActionMode.Callback { ...@@ -119,7 +118,6 @@ class PasswordFragment : Fragment(), PasswordView, ActionMode.Callback {
finishActionMode() finishActionMode()
} }
} }
}
private fun startActionMode() { private fun startActionMode() {
if (actionMode == null) { if (actionMode == null) {
......
...@@ -2,6 +2,7 @@ package chat.rocket.android.settings.ui ...@@ -2,6 +2,7 @@ package chat.rocket.android.settings.ui
import android.content.ActivityNotFoundException import android.content.ActivityNotFoundException
import android.content.Intent import android.content.Intent
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
...@@ -114,8 +115,7 @@ class SettingsFragment : Fragment(), SettingsView, AdapterView.OnItemClickListen ...@@ -114,8 +115,7 @@ class SettingsFragment : Fragment(), SettingsView, AdapterView.OnItemClickListen
} }
private fun setupToolbar() { private fun setupToolbar() {
(activity as AppCompatActivity?)?.supportActionBar?.title = (activity as AppCompatActivity?)?.supportActionBar?.title = getString(R.string.title_settings)
getString(R.string.title_settings)
} }
private fun shareApp() { private fun shareApp() {
...@@ -128,11 +128,12 @@ class SettingsFragment : Fragment(), SettingsView, AdapterView.OnItemClickListen ...@@ -128,11 +128,12 @@ class SettingsFragment : Fragment(), SettingsView, AdapterView.OnItemClickListen
} }
private fun contactSupport() { private fun contactSupport() {
with(Intent(Intent.ACTION_SEND)) { val uriText = "mailto:${"support@rocket.chat"}" +
type = "message/rfc822" "?subject=" + Uri.encode(getString(R.string.msg_android_app_support)) +
putExtra(Intent.EXTRA_EMAIL, arrayOf("support@rocket.chat")) "&body=" + Uri.encode(getDeviceAndAppInformation())
putExtra(Intent.EXTRA_SUBJECT, getString(R.string.msg_android_app_support))
putExtra(Intent.EXTRA_TEXT, getDeviceAndAppInformation()) with(Intent(Intent.ACTION_SENDTO)) {
data = uriText.toUri()
try { try {
startActivity(Intent.createChooser(this, getString(R.string.msg_send_email))) startActivity(Intent.createChooser(this, getString(R.string.msg_send_email)))
} catch (ex: ActivityNotFoundException) { } catch (ex: ActivityNotFoundException) {
......
...@@ -44,7 +44,7 @@ class UserDetailsPresenter @Inject constructor( ...@@ -44,7 +44,7 @@ class UserDetailsPresenter @Inject constructor(
dbManager.getUser(userId)?.let { dbManager.getUser(userId)?.let {
userEntity = it userEntity = it
val avatarUrl = val avatarUrl =
userEntity.username?.let { currentServer.avatarUrl(avatar = it) } userEntity.username?.let { username -> currentServer.avatarUrl(avatar = username) }
val username = userEntity.username val username = userEntity.username
val name = userEntity.name val name = userEntity.name
val utcOffset = val utcOffset =
...@@ -124,6 +124,24 @@ class UserDetailsPresenter @Inject constructor( ...@@ -124,6 +124,24 @@ class UserDetailsPresenter @Inject constructor(
} }
} }
// TODO fun toVideoConferencing(username: String) {
fun startVideoCall() {} launchUI(strategy) {
try {
withContext(Dispatchers.Default) {
val directMessage = retryIO("createDirectMessage($username") {
client.createDirectMessage(username)
}
navigator.toVideoConferencing(directMessage.id)
}
} catch (ex: Exception) {
Timber.e(ex)
ex.message?.let {
view.showMessage(it)
}.ifNull {
view.showGenericErrorMessage()
}
}
}
}
} }
...@@ -29,12 +29,10 @@ import kotlinx.android.synthetic.main.app_bar_chat_room.* ...@@ -29,12 +29,10 @@ import kotlinx.android.synthetic.main.app_bar_chat_room.*
import kotlinx.android.synthetic.main.fragment_user_details.* import kotlinx.android.synthetic.main.fragment_user_details.*
import javax.inject.Inject import javax.inject.Inject
fun newInstance(userId: String): Fragment { fun newInstance(userId: String): Fragment = UserDetailsFragment().apply {
return UserDetailsFragment().apply {
arguments = Bundle(1).apply { arguments = Bundle(1).apply {
putString(BUNDLE_USER_ID, userId) putString(BUNDLE_USER_ID, userId)
} }
}
} }
internal const val TAG_USER_DETAILS_FRAGMENT = "UserDetailsFragment" internal const val TAG_USER_DETAILS_FRAGMENT = "UserDetailsFragment"
...@@ -52,12 +50,10 @@ class UserDetailsFragment : Fragment(), UserDetailsView { ...@@ -52,12 +50,10 @@ class UserDetailsFragment : Fragment(), UserDetailsView {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
AndroidSupportInjection.inject(this) AndroidSupportInjection.inject(this)
val bundle = arguments arguments?.run {
if (bundle != null) { userId = getString(BUNDLE_USER_ID, "")
userId = bundle.getString(BUNDLE_USER_ID)
} else {
requireNotNull(bundle) { "no arguments supplied when the fragment was instantiated" }
} }
?: requireNotNull(arguments) { "no arguments supplied when the fragment was instantiated" }
} }
override fun onCreateView( override fun onCreateView(
...@@ -108,7 +104,7 @@ class UserDetailsFragment : Fragment(), UserDetailsView { ...@@ -108,7 +104,7 @@ class UserDetailsFragment : Fragment(), UserDetailsView {
if (isVideoCallAllowed) { if (isVideoCallAllowed) {
text_video_call.isVisible = true text_video_call.isVisible = true
text_video_call.setOnClickListener { presenter.startVideoCall() } text_video_call.setOnClickListener { presenter.toVideoConferencing(username) }
} else { } else {
text_video_call.isVisible = false text_video_call.isVisible = false
} }
......
...@@ -191,7 +191,7 @@ class HttpLoggingInterceptor constructor(private val logger: Logger) : Intercept ...@@ -191,7 +191,7 @@ class HttpLoggingInterceptor constructor(private val logger: Logger) : Intercept
val responseBody = response.body() val responseBody = response.body()
val contentLength = responseBody!!.contentLength() 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()}" val responseStr = if (response.message().isEmpty()) "" else " ${response.message()}"
logger.log("<-- ${response.code()}$responseStr ${response.request().url()}" logger.log("<-- ${response.code()}$responseStr ${response.request().url()}"
+ " (" + tookMs + "ms" + (if (!logHeaders) ", $bodySize body" else "") + ')'.toString()) + " (" + tookMs + "ms" + (if (!logHeaders) ", $bodySize body" else "") + ')'.toString())
......
...@@ -69,11 +69,8 @@ fun String.userId(userId: String?): String? { ...@@ -69,11 +69,8 @@ fun String.userId(userId: String?): String? {
return userId?.let { this.replace(it, "") } return userId?.let { this.replace(it, "") }
} }
fun String.lowercaseUrl(): String? { fun String.lowercaseUrl(): String? = HttpUrl.parse(this)?.run {
val httpUrl = HttpUrl.parse(this) newBuilder().scheme(scheme().toLowerCase()).build().toString()
val newScheme = httpUrl?.scheme()?.toLowerCase()
return httpUrl?.newBuilder()?.scheme(newScheme)?.build()?.toString()
} }
fun String?.isNotNullNorEmpty(): Boolean = this != null && this.isNotEmpty() fun String?.isNotNullNorEmpty(): Boolean = this != null && this.isNotEmpty()
......
...@@ -80,12 +80,10 @@ fun AppCompatActivity.toPreviousView() { ...@@ -80,12 +80,10 @@ fun AppCompatActivity.toPreviousView() {
} }
fun Activity.hideKeyboard() { fun Activity.hideKeyboard() {
if (currentFocus != null) { currentFocus?.run {
val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager (getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager).also {
imm.hideSoftInputFromWindow( it.hideSoftInputFromWindow(windowToken, InputMethodManager.RESULT_UNCHANGED_SHOWN)
currentFocus.windowToken, }
InputMethodManager.RESULT_UNCHANGED_SHOWN
)
} }
} }
......
...@@ -51,7 +51,7 @@ fun Uri.getFileSize(context: Context): Int { ...@@ -51,7 +51,7 @@ fun Uri.getFileSize(context: Context): Int {
fun Uri.getMimeType(context: Context): String { fun Uri.getMimeType(context: Context): String {
return if (scheme == ContentResolver.SCHEME_CONTENT) { return if (scheme == ContentResolver.SCHEME_CONTENT) {
context.contentResolver.getType(this) context.contentResolver?.getType(this) ?: ""
} else { } else {
val fileExtension = MimeTypeMap.getFileExtensionFromUrl(toString()) val fileExtension = MimeTypeMap.getFileExtensionFromUrl(toString())
if (fileExtension != null) { if (fileExtension != null) {
......
...@@ -3,7 +3,6 @@ package chat.rocket.android.videoconferencing.presenter ...@@ -3,7 +3,6 @@ package chat.rocket.android.videoconferencing.presenter
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.helper.JitsiHelper import chat.rocket.android.helper.JitsiHelper
import chat.rocket.android.server.domain.* import chat.rocket.android.server.domain.*
import chat.rocket.android.server.infraestructure.ConnectionManager
import chat.rocket.android.server.infraestructure.ConnectionManagerFactory import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import chat.rocket.android.util.extension.launchUI import chat.rocket.android.util.extension.launchUI
import chat.rocket.core.RocketChatClient import chat.rocket.core.RocketChatClient
...@@ -22,8 +21,6 @@ class VideoConferencingPresenter @Inject constructor( ...@@ -22,8 +21,6 @@ class VideoConferencingPresenter @Inject constructor(
private val connectionManagerFactory: ConnectionManagerFactory, private val connectionManagerFactory: ConnectionManagerFactory,
private val settings: GetSettingsInteractor private val settings: GetSettingsInteractor
) { ) {
private lateinit var currentServerUrl: String
private lateinit var connectionManager: ConnectionManager
private lateinit var client: RocketChatClient private lateinit var client: RocketChatClient
private lateinit var publicSettings: PublicSettings private lateinit var publicSettings: PublicSettings
private lateinit var chatRoomId: String private lateinit var chatRoomId: String
...@@ -31,9 +28,7 @@ class VideoConferencingPresenter @Inject constructor( ...@@ -31,9 +28,7 @@ class VideoConferencingPresenter @Inject constructor(
fun setup(chatRoomId: String) { fun setup(chatRoomId: String) {
currentServerRepository.get()?.let { currentServerRepository.get()?.let {
currentServerUrl = it client = connectionManagerFactory.create(it).client
connectionManager = connectionManagerFactory.create(it)
client = connectionManager.client
publicSettings = settings.get(it) publicSettings = settings.get(it)
} }
this.chatRoomId = chatRoomId this.chatRoomId = chatRoomId
......
...@@ -27,13 +27,10 @@ class AdminPanelWebViewFragment : DaggerFragment() { ...@@ -27,13 +27,10 @@ class AdminPanelWebViewFragment : DaggerFragment() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
val bundle = arguments arguments?.run {
if (bundle != null) { webPageUrl = getString(BUNDLE_WEB_PAGE_URL, "")
webPageUrl = bundle.getString(BUNDLE_WEB_PAGE_URL) userToken = getString(BUNDLE_USER_TOKEN, "")
userToken = bundle.getString(BUNDLE_USER_TOKEN) } ?: requireNotNull(arguments) { "no arguments supplied when the fragment was instantiated" }
} else {
requireNotNull(bundle) { "no arguments supplied when the fragment was instantiated" }
}
} }
override fun onCreateView( override fun onCreateView(
...@@ -65,7 +62,7 @@ class AdminPanelWebViewFragment : DaggerFragment() { ...@@ -65,7 +62,7 @@ class AdminPanelWebViewFragment : DaggerFragment() {
web_view.webViewClient = object : WebViewClient() { web_view.webViewClient = object : WebViewClient() {
override fun onPageFinished(view: WebView, url: String) { override fun onPageFinished(view: WebView, url: String) {
super.onPageFinished(view, url) super.onPageFinished(view, url)
ui { _ -> ui {
view_loading.hide() view_loading.hide()
web_view.evaluateJavascript("Meteor.loginWithToken('$userToken', function() { })") {} web_view.evaluateJavascript("Meteor.loginWithToken('$userToken', function() { })") {}
} }
......
...@@ -8,13 +8,14 @@ import android.view.View ...@@ -8,13 +8,14 @@ import android.view.View
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import chat.rocket.android.chatrooms.adapter.RoomsAdapter
/** /**
* Adds a default or custom divider to specific item views from the adapter's data set. * Adds a default or custom divider to specific item views from the adapter's data set.
* @see RecyclerView.ItemDecoration * @see RecyclerView.ItemDecoration
*/ */
class DividerItemDecoration() : RecyclerView.ItemDecoration() { class DividerItemDecoration() : RecyclerView.ItemDecoration() {
private lateinit var divider: Drawable private var divider: Drawable? = null
private var boundStart = 0 private var boundStart = 0
private var boundEnd = 0 private var boundEnd = 0
...@@ -39,10 +40,7 @@ class DividerItemDecoration() : RecyclerView.ItemDecoration() { ...@@ -39,10 +40,7 @@ class DividerItemDecoration() : RecyclerView.ItemDecoration() {
// Custom divider will be used. // Custom divider will be used.
constructor(context: Context, @DrawableRes drawableResId: Int) : this() { constructor(context: Context, @DrawableRes drawableResId: Int) : this() {
val customDrawable = ContextCompat.getDrawable(context, drawableResId) divider = ContextCompat.getDrawable(context, drawableResId)
if (customDrawable != null) {
divider = customDrawable
}
} }
override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) { override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
...@@ -53,16 +51,16 @@ class DividerItemDecoration() : RecyclerView.ItemDecoration() { ...@@ -53,16 +51,16 @@ class DividerItemDecoration() : RecyclerView.ItemDecoration() {
for (i in 0 until childCount) { for (i in 0 until childCount) {
val child = parent.getChildAt(i) val child = parent.getChildAt(i)
if (isLastView(child, parent)) if (isLastView(child, parent) || isViewTypeHeader(child, parent))
continue continue
val params = child.layoutParams as RecyclerView.LayoutParams val params = child.layoutParams as RecyclerView.LayoutParams
val top = child.bottom + params.bottomMargin val bottom = child.bottom + params.bottomMargin
val bottom = top + divider.intrinsicHeight val top = bottom - (divider?.intrinsicHeight ?: 0)
divider.setBounds(left, top, right, bottom) divider?.setBounds(left, top, right, bottom)
divider.draw(c) divider?.draw(c)
} }
} }
...@@ -70,4 +68,9 @@ class DividerItemDecoration() : RecyclerView.ItemDecoration() { ...@@ -70,4 +68,9 @@ class DividerItemDecoration() : RecyclerView.ItemDecoration() {
val position = parent.getChildAdapterPosition(view) val position = parent.getChildAdapterPosition(view)
return position == parent.adapter?.itemCount?.minus(1) ?: false return position == parent.adapter?.itemCount?.minus(1) ?: false
} }
private fun isViewTypeHeader(view: View, parent: RecyclerView): Boolean {
val position = parent.getChildViewHolder(view).itemViewType
return position == RoomsAdapter.VIEW_TYPE_HEADER
}
} }
<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 @@ ...@@ -10,6 +10,7 @@
android:layout_width="120dp" android:layout_width="120dp"
android:layout_height="120dp" android:layout_height="120dp"
android:layout_centerHorizontal="true" android:layout_centerHorizontal="true"
android:background="@drawable/bg_empty_user_avatar"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="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" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context="chat.rocket.android.about.ui.AboutFragment"> tools:context="chat.rocket.android.about.ui.AboutFragment">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="50dp"
android:layout_gravity="center">
<ImageView <ImageView
android:id="@+id/image_app_name" android:id="@+id/image_app_name"
android:layout_width="wrap_content" android:layout_width="wrap_content"
...@@ -18,13 +25,14 @@ ...@@ -18,13 +25,14 @@
<ImageView <ImageView
android:layout_width="160dp" android:layout_width="160dp"
android:layout_height="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:adjustViewBounds="true"
android:scaleX="1.5" android:scaleX="1.5"
android:scaleY="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 <TextView
android:id="@+id/text_version_name" android:id="@+id/text_version_name"
...@@ -49,4 +57,6 @@ ...@@ -49,4 +57,6 @@
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:textColor="@color/colorSecondaryText" /> 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
...@@ -4,24 +4,14 @@ ...@@ -4,24 +4,14 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical">
<!--<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/darkGray" />-->
<TextView <TextView
android:id="@+id/text_chatroom_header" android:id="@+id/text_chatroom_header"
style="@style/ChatRooms.Header" style="@style/ChatRooms.Header"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:padding="8dp" android:padding="16dp"
android:paddingEnd="@dimen/screen_edge_left_and_right_padding" android:paddingEnd="@dimen/screen_edge_left_and_right_padding"
android:paddingStart="@dimen/screen_edge_left_and_right_padding" android:paddingStart="@dimen/screen_edge_left_and_right_padding"
android:text="@string/chatroom_header" /> android:text="@string/chatroom_header" />
<View </LinearLayout>
android:layout_width="match_parent" \ No newline at end of file
android:layout_height="1dp"
android:background="@color/quoteBar" />
</LinearLayout>
\ No newline at end of file
...@@ -34,8 +34,8 @@ ...@@ -34,8 +34,8 @@
android:id="@+id/day" android:id="@+id/day"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="16dp" android:layout_marginStart="16dp"
android:layout_marginRight="16dp" android:layout_marginEnd="16dp"
android:textAppearance="@style/Message.DayMarker" android:textAppearance="@style/Message.DayMarker"
tools:text="Wednesday" /> tools:text="Wednesday" />
...@@ -51,7 +51,7 @@ ...@@ -51,7 +51,7 @@
layout="@layout/avatar" layout="@layout/avatar"
android:layout_width="40dp" android:layout_width="40dp"
android:layout_height="40dp" android:layout_height="40dp"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/message_header" /> app:layout_constraintTop_toTopOf="@+id/message_header" />
<LinearLayout <LinearLayout
...@@ -95,17 +95,16 @@ ...@@ -95,17 +95,16 @@
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:orientation="horizontal" android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintLeft_toRightOf="@+id/layout_avatar"
app:layout_constraintStart_toEndOf="@+id/layout_avatar" app:layout_constraintStart_toEndOf="@+id/layout_avatar"
app:layout_constraintTop_toBottomOf="@+id/day_marker_layout"> app:layout_constraintTop_toBottomOf="@+id/day_marker_layout">
<TextView <TextView
android:id="@+id/text_sender" android:id="@+id/text_sender"
style="@style/Sender.Name.TextView" style="@style/Sender.Name.TextView"
android:layout_width="0dp" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="8dp" android:layout_marginEnd="8dp"
android:layout_weight="1" android:maxLength="22"
tools:text="User Name with long name" /> tools:text="User Name with long name" />
<TextView <TextView
...@@ -123,7 +122,7 @@ ...@@ -123,7 +122,7 @@
android:layout_marginStart="4dp" android:layout_marginStart="4dp"
android:text="@string/msg_edited" android:text="@string/msg_edited"
android:textStyle="italic" android:textStyle="italic"
android:visibility="gone" android:visibility="invisible"
tools:visibility="visible" /> tools:visibility="visible" />
<ImageView <ImageView
...@@ -133,7 +132,7 @@ ...@@ -133,7 +132,7 @@
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:layout_marginStart="4dp" android:layout_marginStart="4dp"
android:layout_marginTop="2dp" android:layout_marginTop="2dp"
android:visibility="gone" android:visibility="invisible"
app:srcCompat="@drawable/ic_action_message_star_24dp" app:srcCompat="@drawable/ic_action_message_star_24dp"
tools:visibility="visible" /> tools:visibility="visible" />
...@@ -141,7 +140,7 @@ ...@@ -141,7 +140,7 @@
android:id="@+id/read_receipt_view" android:id="@+id/read_receipt_view"
android:layout_width="24dp" android:layout_width="24dp"
android:layout_height="24dp" android:layout_height="24dp"
android:visibility="gone" android:visibility="invisible"
app:srcCompat="@drawable/ic_check_unread_24dp" app:srcCompat="@drawable/ic_check_unread_24dp"
tools:visibility="visible" /> tools:visibility="visible" />
</LinearLayout> </LinearLayout>
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
android:id="@+id/image_avatar" android:id="@+id/image_avatar"
android:layout_width="60dp" android:layout_width="60dp"
android:layout_height="60dp" android:layout_height="60dp"
android:background="@drawable/bg_empty_user_avatar"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
......
...@@ -325,6 +325,7 @@ ...@@ -325,6 +325,7 @@
<string name="chatroom_header">Kopf</string> <string name="chatroom_header">Kopf</string>
<!--ChatRooms Headers--> <!--ChatRooms Headers-->
<string name="header_favorite">Favorites</string><!-- TODO - Add proper translation -->
<string name="header_channel">Räume</string> <string name="header_channel">Räume</string>
<string name="header_private_groups">Private Räume</string> <string name="header_private_groups">Private Räume</string>
<string name="header_direct_messages">Direkt Nachrichten</string> <string name="header_direct_messages">Direkt Nachrichten</string>
......
...@@ -316,6 +316,7 @@ ...@@ -316,6 +316,7 @@
<string name="chatroom_header">Cabezazo</string> <string name="chatroom_header">Cabezazo</string>
<!--ChatRooms Headers--> <!--ChatRooms Headers-->
<string name="header_favorite">Favorites</string><!-- TODO - Add proper translation -->
<string name="header_channel">Canales</string> <string name="header_channel">Canales</string>
<string name="header_private_groups">Grupos privados</string> <string name="header_private_groups">Grupos privados</string>
<string name="header_direct_messages">Mensajes directos</string> <string name="header_direct_messages">Mensajes directos</string>
......
...@@ -319,6 +319,7 @@ ...@@ -319,6 +319,7 @@
<string name="chatroom_header">سرپیام</string> <string name="chatroom_header">سرپیام</string>
<!--ChatRooms Headers--> <!--ChatRooms Headers-->
<string name="header_favorite">Favorites</string><!-- TODO - Add proper translation -->
<string name="header_channel">کانال‌ها</string> <string name="header_channel">کانال‌ها</string>
<string name="header_private_groups">گروه‌های خصوصی</string> <string name="header_private_groups">گروه‌های خصوصی</string>
<string name="header_direct_messages">پیام‌های خصوصی</string> <string name="header_direct_messages">پیام‌های خصوصی</string>
......
...@@ -17,14 +17,14 @@ ...@@ -17,14 +17,14 @@
<string name="title_preferences">Préférences</string> <string name="title_preferences">Préférences</string>
<string name="title_change_password">Changer le mot de passe</string> <string name="title_change_password">Changer le mot de passe</string>
<string name="title_rate_us">évaluez nous</string> <string name="title_rate_us">évaluez nous</string>
<string name="title_admin_panel">Administration</string> <string name="title_admin_panel">l\' administration</string>
<string name="title_password">Changer le mot de passe</string> <string name="title_password">Changer le mot de passe</string>
<string name="title_update_profile">Mettre à jour le profil</string> <string name="title_update_profile">Mettre à jour le profil</string>
<string name="title_about">À propos</string> <string name="title_about">À propos</string>
<string name="title_create_channel">Créer salon</string> <string name="title_create_channel">Créer salon</string>
<string name="title_licence">Licence</string> <!-- TODO Add translation --> <string name="title_licence">les permis</string>
<string name="title_are_you_sure">Are you sure?</string> <!-- TODO Add translation --> <string name="title_are_you_sure">Êtes-vous sûr?</string>
<string name="title_channel_details">Channel Details</string> <!-- TODO add translation --> <string name="title_channel_details">Détails de la chaîne</string>
<string name="title_topic">Sujet</string> <string name="title_topic">Sujet</string>
<string name="title_announcement">Annonce</string> <string name="title_announcement">Annonce</string>
<string name="title_description">La description</string> <string name="title_description">La description</string>
...@@ -40,7 +40,7 @@ ...@@ -40,7 +40,7 @@
<string name="action_create_channel">Créer salon</string> <string name="action_create_channel">Créer salon</string>
<string name="action_create">Créer</string> <string name="action_create">Créer</string>
<string name="action_logout">Se déconnecter</string> <string name="action_logout">Se déconnecter</string>
<string name="action_attach_a_files">Attach a file</string> <!-- TODO Add translation --> <string name="action_attach_a_files">Joindre un fichier</string>
<string name="action_confirm_password">Confirmer le mot de passe</string> <string name="action_confirm_password">Confirmer le mot de passe</string>
<string name="action_join_chat">Rejoignez le chat</string> <string name="action_join_chat">Rejoignez le chat</string>
<string name="action_add_account">Ajouter un compte</string> <string name="action_add_account">Ajouter un compte</string>
...@@ -53,25 +53,25 @@ ...@@ -53,25 +53,25 @@
<string name="action_select_photo_from_gallery">Sélectionner depuis la gallerie</string> <string name="action_select_photo_from_gallery">Sélectionner depuis la gallerie</string>
<string name="action_take_a_photo">Prendre une photo</string> <string name="action_take_a_photo">Prendre une photo</string>
<string name="action_reset_avatar">Réinitialiser l\'avatar</string> <string name="action_reset_avatar">Réinitialiser l\'avatar</string>
<string name="action_connect_server">Connect with a server</string> <!-- TODO Add translation --> <string name="action_connect_server">Se connecter avec un serveur</string>
<string name="action_join_community">Join in the community</string> <!-- TODO Add translation --> <string name="action_join_community">Rejoignez la communauté</string>
<string name="action_create_server">Create a new server</string> <!-- TODO Add translation --> <string name="action_create_server">Créer un nouveau serveur</string>
<string name="action_register">Register</string> <!-- TODO Add translation --> <string name="action_register">registre</string>
<string name="action_confirm">Confirm</string> <!-- TODO Add translation --> <string name="action_confirm">Confirmer</string>
<string name="action_delete_account">Delete account</string> <!-- TODO Add translation --> <string name="action_delete_account">Effacer le compte</string>
<string name="action_favorite">Favorite</string> <!-- TODO Add translation --> <string name="action_favorite">Favorite</string> <!-- TODO Add translation -->
<string name="action_remove_favorite">Remove favorite</string> <!-- TODO Add translation --> <string name="action_remove_favorite">Remove favorite</string> <!-- TODO Add translation -->
<!-- Settings List --> <!-- Settings List -->
<string-array name="settings_actions"> <string-array name="settings_actions">
<item name="item_preferences">Preferences</item> <!-- TODO Add translation --> <item name="item_preferences">Préférences</item>
<item name="item_password">Change password</item> <!-- TODO Add translation --> <item name="item_password">Changer le mot de passe</item>
<item name="change_language">Change language</item> <!-- TODO Add translation --> <item name="change_language">Changer de langue</item>
<item name="item_share_app">Share app</item> <!-- TODO Add translation --> <item name="item_share_app">Partager l\'application</item>
<item name="item_rate_us">Rate us</item> <!-- TODO Add translation --> <item name="item_rate_us">Évaluez nous</item>
<item name="item_contact_us">Contact us</item> <!-- TODO Add translation --> <item name="item_contact_us">Contactez nous</item>
<item name="item_licence">Licence</item> <!-- TODO Add translation --> <item name="item_licence">les permis</item>
<item name="item_about">About</item> <!-- TODO Add translation --> <item name="item_about">Sur</item>
</string-array> </string-array>
<!-- Regular information messages --> <!-- Regular information messages -->
...@@ -111,7 +111,7 @@ ...@@ -111,7 +111,7 @@
<string name="msg_content_description_log_in_using_gitlab">Connectez-vous en utilisant Gitlab</string> <string name="msg_content_description_log_in_using_gitlab">Connectez-vous en utilisant Gitlab</string>
<string name="msg_content_description_log_in_using_wordpress">Connectez-vous en utilisant WordPress</string> <string name="msg_content_description_log_in_using_wordpress">Connectez-vous en utilisant WordPress</string>
<string name="msg_content_description_send_message">Envoyer message</string> <string name="msg_content_description_send_message">Envoyer message</string>
<string name="msg_content_description_show_more_login_options">Show more login options</string> <!-- TODO Translate--> <string name="msg_content_description_show_more_login_options">montrer plus d\'options le login</string>
<string name="msg_content_description_show_attachment_options">Afficher les options de fichiers</string> <string name="msg_content_description_show_attachment_options">Afficher les options de fichiers</string>
<string name="msg_you">Vous</string> <string name="msg_you">Vous</string>
<string name="msg_unknown">Inconnu</string> <string name="msg_unknown">Inconnu</string>
...@@ -125,7 +125,7 @@ ...@@ -125,7 +125,7 @@
<string name="msg_preview_photo">Photo</string> <string name="msg_preview_photo">Photo</string>
<string name="msg_preview_file">Fichier</string> <string name="msg_preview_file">Fichier</string>
<string name="msg_no_messages_yet">Aucun message pour le moment</string> <string name="msg_no_messages_yet">Aucun message pour le moment</string>
<string name="msg_build">Build %1$d - %2$s - %3$s</string> <!-- TODO Add translation --> <string name="msg_build">bâtir %1$d - %2$s - %3$s</string>
<string name="msg_update_app_version_in_order_to_continue">La version du serveur est trop ancienne. Veuillez contacter l\'administateur pour mettre à jour le serveur afin de pouvoir vous y connecter.</string> <string name="msg_update_app_version_in_order_to_continue">La version du serveur est trop ancienne. Veuillez contacter l\'administateur pour mettre à jour le serveur afin de pouvoir vous y connecter.</string>
<string name="msg_ver_not_recommended"> <string name="msg_ver_not_recommended">
On dirait que la version de votre serveur est en dessous de la version recommandée %1$s.\nVous pouvez toujours vous connecter mais vous pouvez rencontrer des comportements inattendus.</string> On dirait que la version de votre serveur est en dessous de la version recommandée %1$s.\nVous pouvez toujours vous connecter mais vous pouvez rencontrer des comportements inattendus.</string>
...@@ -153,32 +153,32 @@ ...@@ -153,32 +153,32 @@
<string name="msg_send">envoyer</string> <string name="msg_send">envoyer</string>
<string name="msg_delete_message">Supprimer Message</string> <string name="msg_delete_message">Supprimer Message</string>
<string name="msg_delete_description">Êtes-vous sûr de vouloir supprimer ce message</string> <string name="msg_delete_description">Êtes-vous sûr de vouloir supprimer ce message</string>
<string name="msg_welcome_to_rocket_chat">Welcome to Rocket.Chat</string> <!-- TODO Add translation --> <string name="msg_welcome_to_rocket_chat">Bienvenue à Rocket.Chat</string>
<string name="msg_team_communication">Team Communication</string> <!-- TODO Translate --> <string name="msg_team_communication">Communication d\'équipe</string>
<string name="msg_login_with_email">Login with <b>e-mail</b></string> <!-- TODO Add translation --> <string name="msg_login_with_email">le login avec <b>e-mail</b></string>
<string name="msg_create_account">Create an account</string> <!-- TODO Add translation --> <string name="msg_create_account">Créer un compte</string>
<string name="msg_continue_with_facebook">Continue with <b>Facebook</b></string> <!-- TODO Add translation --> <string name="msg_continue_with_facebook">Continuer avec <b>Facebook</b></string>
<string name="msg_continue_with_github">Continue with <b>Github</b></string> <!-- TODO Add translation --> <string name="msg_continue_with_github">Continuer avec <b>Github</b></string>
<string name="msg_continue_with_google">Continue with <b>Google</b></string> <!-- TODO Add translation --> <string name="msg_continue_with_google">Continuer avec <b>Google</b></string>
<string name="msg_continue_with_linkedin">Continue with <b>Linkedin</b></string> <!-- TODO Add translation --> <string name="msg_continue_with_linkedin">Continuer avec <b>Linkedin</b></string>
<string name="msg_continue_with_gitlab">Continue with <b>GitLab</b></string> <!-- TODO Add translation --> <string name="msg_continue_with_gitlab">Continuer avec <b>GitLab</b></string>
<string name="msg_continue_with_wordpress">Continue with <b>WordPress</b></string> <!-- TODO Add translation --> <string name="msg_continue_with_wordpress">Continuer avec <b>WordPress</b></string>
<string name="msg_two_factor_authentication">Two-factor Authentication</string> <!-- TODO Add translation --> <string name="msg_two_factor_authentication">authentification à deux facteurs</string>
<string name="msg__your_2fa_code">What’s your 2FA code?</string> <!-- TODO Add translation --> <string name="msg__your_2fa_code">Quel est votre code 2FA?</string>
<string name="msg_view_more">view more</string> <!-- TODO - Add proper translation --> <string name="msg_view_more">voir de plus</string>
<string name="msg_view_less">view less</string> <!-- TODO - Add proper translation --> <string name="msg_view_less">voir de plus</string>
<string name="msg_permalink_copied">Permalink copied</string> <!-- TODO - Add proper translation --> <string name="msg_permalink_copied">Permalink copié</string>
<string name="msg_send_email">Send email</string> <!-- TODO - Add proper translation --> <string name="msg_send_email">Envoyer email</string>
<string name="msg_android_app_support">Android app support</string> <!-- TODO - Add proper translation --> <string name="msg_android_app_support">Android app le support</string>
<string name="msg_muted_on_this_channel">You are muted on this channel</string> <!-- TODO - Add proper translation --> <string name="msg_muted_on_this_channel">Vous êtes en sourdine sur ce canal</string>
<string name="msg_no_topic">No topic</string> <!-- TODO Add translation --> <string name="msg_no_topic">sujet non ajouté</string>
<string name="msg_no_announcement">No announcement</string> <!-- TODO Add translation --> <string name="msg_no_announcement">annonce non ajoutée</string>
<string name="msg_no_description">No description</string> <!-- TODO Add translation --> <string name="msg_no_description">description non ajoutée</string>
<string name="msg_unable_to_update_password">Unable to update password. Error message: %1$s</string> <!-- TODO - Add proper translation --> <string name="msg_unable_to_update_password">Impossible de mettre à jour le mot de passe. Message d\'erreur: %1$s</string>
<string name="msg_password_updated_successfully">Password updated successfully</string> <!-- TODO - Add proper translation --> <string name="msg_password_updated_successfully">Mot de passe mis à jour avec succès</string>
<plurals name="msg_reacted_with_"> <plurals name="msg_reacted_with_">
<item quantity="one">%1$s reacted with %2$s</item> <!-- TODO - Add proper translation --> <item quantity="one">%1$s a réagi avec %2$s</item>
<item quantity="other">%1$s reacted with %2$s</item> <!-- TODO - Add proper translation --> <item quantity="other">%1$s a réagi avec %2$s</item>
</plurals> </plurals>
<string name="msg_credentials_saved_successfully">Certificats sauvegardés</string> <string name="msg_credentials_saved_successfully">Certificats sauvegardés</string>
...@@ -229,10 +229,8 @@ ...@@ -229,10 +229,8 @@
<string name="action_msg_share">Partager</string> <string name="action_msg_share">Partager</string>
<string name="action_title_editing">Modification du message</string> <string name="action_title_editing">Modification du message</string>
<string name="action_msg_add_reaction">Ajouter une réaction</string> <string name="action_msg_add_reaction">Ajouter une réaction</string>
<!-- TODO - Add proper translation --> <string name="action_msg_copy_permalink">copie permalien</string>
<string name="action_msg_copy_permalink">Copy permalink</string> <string name="action_msg_report">rapport</string>
<!-- TODO - Add proper translation -->
<string name="action_msg_report">Report</string>
<!-- Permission messages --> <!-- Permission messages -->
<string name="permission_editing_not_allowed">L\'édition n\'est pas autorisée</string> <string name="permission_editing_not_allowed">L\'édition n\'est pas autorisée</string>
...@@ -319,6 +317,7 @@ ...@@ -319,6 +317,7 @@
<string name="chatroom_header">Entête</string> <string name="chatroom_header">Entête</string>
<!--ChatRooms Headers--> <!--ChatRooms Headers-->
<string name="header_favorite">Favoris</string>
<string name="header_channel">Salons</string> <string name="header_channel">Salons</string>
<string name="header_private_groups">Groupes privés</string> <string name="header_private_groups">Groupes privés</string>
<string name="header_direct_messages">Messages directs</string> <string name="header_direct_messages">Messages directs</string>
...@@ -334,14 +333,14 @@ ...@@ -334,14 +333,14 @@
<string name="message_information_title">Informations sur le message</string> <string name="message_information_title">Informations sur le message</string>
<string name="msg_log_out">Déconnecter…</string> <string name="msg_log_out">Déconnecter…</string>
<string name="msg_sent_attachment">Envoyé un fichier</string> <string name="msg_sent_attachment">Envoyé un fichier</string>
<string name="message_room_changed_privacy">Room type changed to: %1$s by %2$s</string> <!--TODO - Add proper translation--> <string name="message_room_changed_privacy">Changé de type de chambre : %1$s de %2$s</string>
<!-- User Details --> <!-- User Details -->
<string name="timezone">Timezone</string> <!-- TODO - Add proper translation --> <string name="timezone">Fuseau horaire</string>
<!-- Report --> <!-- Report -->
<string name="submit">Submit</string> <!-- TODO - Add proper translation --> <string name="submit">Soumettre</string>
<string name="required">*required</string> <!-- TODO - Add proper translation --> <string name="required">*Obligatoire</string>
<string name="report_sent">Your report has been sent!</string> <!-- TODO - Add proper translation --> <string name="report_sent">Votre rapport a été envoyé!</string>
</resources> </resources>
...@@ -125,7 +125,7 @@ ...@@ -125,7 +125,7 @@
<string name="msg_preview_file">फ़ाइल</string> <string name="msg_preview_file">फ़ाइल</string>
<string name="msg_unread_messages">अपठित संदेश</string> <string name="msg_unread_messages">अपठित संदेश</string>
<string name="msg_no_messages_yet">अभी तक कोई पोस्ट नहीं</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_update_app_version_in_order_to_continue">पुराना सर्वर वर्शन। जारी रखने के लिए सर्वर वर्शन को अद्यतन करने के लिए कृपया सर्वर व्यवस्थापक से संपर्क करें।</string>
<string name="msg_ver_not_recommended"> <string name="msg_ver_not_recommended">
ऐसा लगता है कि आपका सर्वर संस्करण अनुशंसित संस्करण %1$s के नीचे है।\nआप अभी भी लॉगिन कर सकते हैं लेकिन आप अप्रत्याशित व्यवहार का अनुभव कर सकते हैं ऐसा लगता है कि आपका सर्वर संस्करण अनुशंसित संस्करण %1$s के नीचे है।\nआप अभी भी लॉगिन कर सकते हैं लेकिन आप अप्रत्याशित व्यवहार का अनुभव कर सकते हैं
...@@ -320,6 +320,7 @@ ...@@ -320,6 +320,7 @@
<string name="chatroom_header">हैडर</string> <string name="chatroom_header">हैडर</string>
<!--ChatRooms Headers--> <!--ChatRooms Headers-->
<string name="header_favorite">पसंदीदा</string>
<string name="header_channel">चैनलों</string> <string name="header_channel">चैनलों</string>
<string name="header_private_groups">निजी समूहों</string> <string name="header_private_groups">निजी समूहों</string>
<string name="header_direct_messages">प्रत्यक्ष संदेश</string> <string name="header_direct_messages">प्रत्यक्ष संदेश</string>
......
...@@ -66,7 +66,7 @@ ...@@ -66,7 +66,7 @@
<string-array name="settings_actions"> <string-array name="settings_actions">
<item name="item_preferences">Preferenze</item> <item name="item_preferences">Preferenze</item>
<item name="item_password">Cambia password</item> <item name="item_password">Cambia password</item>
<item name="change_language">Change language</item><!-- TODO Add translation --> <item name="change_language">Cambia lingua</item>
<item name="item_share_app">Condividi app</item> <item name="item_share_app">Condividi app</item>
<item name="item_rate_us">Votaci</item> <item name="item_rate_us">Votaci</item>
<item name="item_contact_us">Contattaci</item> <item name="item_contact_us">Contattaci</item>
...@@ -127,7 +127,7 @@ ...@@ -127,7 +127,7 @@
<string name="msg_preview_photo">Foto</string> <string name="msg_preview_photo">Foto</string>
<string name="msg_preview_file">Documento</string> <string name="msg_preview_file">Documento</string>
<string name="msg_no_messages_yet">Nessun nuovo messaggio</string> <string name="msg_no_messages_yet">Nessun nuovo messaggio</string>
<string name="msg_build">Build %1$d - %2$s - %3$s</string> <!-- TODO Add translation --> <string name="msg_build">Versione %1$d - %2$s - %3$s</string>
<string name="msg_update_app_version_in_order_to_continue">Versione server non aggiornata. Si prega di contattare l\'amministratore del server per aggiornare la versione del server per continuare.</string> <string name="msg_update_app_version_in_order_to_continue">Versione server non aggiornata. Si prega di contattare l\'amministratore del server per aggiornare la versione del server per continuare.</string>
<string name="msg_ver_not_recommended">Sembra che la versione del tuo server sia inferiore alla versione consigliata %1$s.\nÈ ancora possibile accedere ma è possibile che si verifichino comportamenti imprevisti.</string> <string name="msg_ver_not_recommended">Sembra che la versione del tuo server sia inferiore alla versione consigliata %1$s.\nÈ ancora possibile accedere ma è possibile che si verifichino comportamenti imprevisti.</string>
<string name="msg_ver_not_minimum">Sembra che la versione del tuo server sia inferiore alla versione minima richiesta %1$s.\nSi prega di aggiornare il server per accedere!</string> <string name="msg_ver_not_minimum">Sembra che la versione del tuo server sia inferiore alla versione minima richiesta %1$s.\nSi prega di aggiornare il server per accedere!</string>
...@@ -316,6 +316,7 @@ ...@@ -316,6 +316,7 @@
<string name="chatroom_header">Intestazione</string> <string name="chatroom_header">Intestazione</string>
<!--ChatRooms Headers--> <!--ChatRooms Headers-->
<string name="header_favorite">Favorites</string><!-- TODO - Add proper translation -->
<string name="header_channel">Canali</string> <string name="header_channel">Canali</string>
<string name="header_private_groups">Gruppi Privati</string> <string name="header_private_groups">Gruppi Privati</string>
<string name="header_direct_messages">Messaggi Diretti</string> <string name="header_direct_messages">Messaggi Diretti</string>
......
...@@ -320,6 +320,7 @@ ...@@ -320,6 +320,7 @@
<!--ChatRooms Headers--> <!--ChatRooms Headers-->
<string name="header_favorite">Favorites</string><!-- TODO - Add proper translation -->
<string name="header_channel">チャンネル</string> <string name="header_channel">チャンネル</string>
<string name="header_private_groups">プライベートグループ</string> <string name="header_private_groups">プライベートグループ</string>
<string name="header_direct_messages">ダイレクトメッセージ</string> <string name="header_direct_messages">ダイレクトメッセージ</string>
......
...@@ -318,6 +318,7 @@ ...@@ -318,6 +318,7 @@
<string name="chatroom_header">Cabeçalho</string> <string name="chatroom_header">Cabeçalho</string>
<!--ChatRooms Headers--> <!--ChatRooms Headers-->
<string name="header_favorite">Favoritos</string>
<string name="header_channel">Canais</string> <string name="header_channel">Canais</string>
<string name="header_private_groups">Grupos Privados</string> <string name="header_private_groups">Grupos Privados</string>
<string name="header_direct_messages">Mensagens diretas</string> <string name="header_direct_messages">Mensagens diretas</string>
......
<resources>
<!-- Titles -->
<string name="title_sign_in_your_server">Entre no seu servidor</string>
<string name="title_log_in">Entrar</string>
<string name="title_share_the_app">Partilhe a Aplicação</string>
<string name="title_register_username">Registe um utilizador</string>
<string name="title_reset_password">Reponha a palavra-passe</string>
<string name="title_sign_up">Inscreva-se</string>
<string name="title_authentication">Autenticação</string>
<string name="title_legal_terms">Termos Legais</string>
<string name="title_chats">Conversas</string>
<string name="title_profile">Pefil</string>
<string name="title_members">Membros</string>
<string name="title_counted_members">Membros (%d)</string>
<string name="title_settings">Definições</string>
<string name="title_preferences">Preferências</string>
<string name="title_change_password">Alterar palavra-passe</string>
<string name="title_rate_us">Avalie-nos</string>
<string name="title_admin_panel">Painel de Administração</string>
<string name="title_password">Alterar palavra-passe</string>
<string name="title_update_profile">Actualizar perfil</string>
<string name="title_about">Acerca</string>
<string name="title_create_channel">Criar Canal</string>
<string name="title_licence">Licença</string>
<string name="title_are_you_sure">Tem a certeza?</string>
<string name="title_channel_details">Detalhes do Canal</string>
<string name="title_topic">Tópico</string>
<string name="title_announcement">Anúncio</string>
<string name="title_description">Descrição</string>
<!-- Actions -->
<string name="action_connect">Ligar</string>
<string name="action_use_this_username">Use este nome de utilizador</string>
<string name="action_terms_of_service">Termos do Serviço</string>
<string name="action_privacy_policy">Política de Privacidade</string>
<string name="action_search">Pesquisar</string>
<string name="action_update">Actualizar</string>
<string name="action_settings">Definições</string>
<string name="action_create_channel">Criar canal</string>
<string name="action_create">Criar</string>
<string name="action_logout">Terminar sessão</string>
<string name="action_attach_a_files">Enviar um ficheiro</string>
<string name="action_confirm_password">Confirme a alteração da palavra-passe</string>
<string name="action_join_chat">Entre no chat</string>
<string name="action_add_account">Adicionar conta</string>
<string name="action_online">Online</string>
<string name="action_away">Ausente</string>
<string name="action_busy">Ocupado</string>
<string name="action_invisible">Invisível</string>
<string name="action_drawing">A desenhar</string>
<string name="action_save_to_gallery">Guardar na galeria</string>
<string name="action_select_photo_from_gallery">Selecione a foto da galeria</string>
<string name="action_take_a_photo">Tire uma foto</string>
<string name="action_reset_avatar">Repor avatar</string>
<string name="action_connect_server">Ligue-se a um servidor</string>
<string name="action_join_community">Junte-se à comunidade</string>
<string name="action_create_server">Crie um novo servidor</string>
<string name="action_register">Registar</string>
<string name="action_confirm">Confirmar</string>
<string name="action_delete_account">Apagar conta</string>
<!-- Settings List -->
<string-array name="settings_actions">
<item name="item_preferences">Preferências</item>
<item name="item_password">Alterar palavra-passe</item>
<item name="change_language">Alterar idioma</item>
<item name="item_share_app">Partilhe a aplicação</item>
<item name="item_rate_us">Avalie-nos</item>
<item name="item_contact_us">Contacte-nos</item>
<item name="item_licence">Licença</item>
<item name="item_about">Acerca</item>
</string-array>
<!-- Regular information messages -->
<string name="msg_generic_error">Lamentamos, ocorreu um erro, tente novamente</string>
<string name="msg_no_data_to_display">Sem dados para mostrar</string>
<string name="msg_check_this_out">Experimente isto</string>
<string name="msg_share_using">Partilhe usando</string>
<string name="msg_profile_update_successfully">Perfil actualizado com sucesso</string>
<string name="msg_username">utilizador</string>
<string name="msg_username_or_email">Utilizador ou e-mail</string>
<string name="msg_password">Palavra-passe</string>
<string name="msg_name">Nome</string>
<string name="msg_email">E-mail</string>
<string name="msg_avatar_url">URL do avatar</string>
<string name="msg_or_continue_using_social_accounts">Ou continue utilizando uma conta social</string>
<string name="msg_new_user">Novo utilizador? %1$s</string>
<string name="msg_forgot__your_password">Esqueceu a sua palavra-passe?</string>
<string name="msg_reset">Repor</string>
<string name="msg_check_your_email_to_reset_your_password">E-mail enviado! Verifique a sua caixa de entrada para repor a sua palavra-passe.</string>
<string name="msg_invalid_email">Por favor introduza um endereço de e-mail válido</string>
<string name="msg_new_user_agreement">Ao prosseguir, você concorda com os nossos \n%1$s e %2$s</string>
<string name="msg_more_than_ninety_nine_unread_messages" translatable="false">99+</string>
<string name="msg_yesterday">Ontem</string>
<string name="msg_today">Hoje</string>
<string name="msg_message">Mensagem</string>
<string name="msg_this_room_is_read_only">Esta sala é apenas de leitura</string>
<string name="msg_invalid_2fa_code">Código 2FA inválido</string>
<string name="msg_invalid_file">Ficheiro inválido</string>
<string name="msg_invalid_server_url">URL de servidor inválido</string>
<string name="msg_content_description_log_in_using_facebook">Entre utilizando Facebook</string>
<string name="msg_content_description_log_in_using_github">Entre utilizando Github</string>
<string name="msg_content_description_log_in_using_google">Entre utilizando Google</string>
<string name="msg_content_description_log_in_using_linkedin">Entre utilizando LinkedIn</string>
<string name="msg_content_description_log_in_using_meteor">Entre utilizando Meteor</string>
<string name="msg_content_description_log_in_using_twitter">Entre utilizando Twitter</string>
<string name="msg_content_description_log_in_using_gitlab">Entre utilizando Gitlab</string>
<string name="msg_content_description_log_in_using_wordpress">Entre utilizando WordPress</string>
<string name="msg_content_description_send_message">Enviar mensagem</string>
<string name="msg_content_description_show_more_login_options">Mostrar mais opções de entrada</string>
<string name="msg_content_description_show_attachment_options">Mostrar opções de ficheiro</string>
<string name="msg_you">Você</string>
<string name="msg_unknown">Desconhecido</string>
<string name="msg_email_address">Endereço de E-mail</string>
<string name="msg_utc_offset">Deslocamento UTC</string>
<string name="msg_new_password">Introduza a nova palavra-passe</string>
<string name="msg_confirm_password">Confirme a nova palavra-passe</string>
<string name="msg_channel_name">Nome do canal</string>
<string name="msg_search">Pesquisar</string>
<string name="msg_unread_messages">Mensagens não lidas</string>
<string name="msg_preview_video">Vídeo</string>
<string name="msg_preview_audio">Audio</string>
<string name="msg_preview_photo">Foto</string>
<string name="msg_preview_file">Ficheiro</string>
<string name="msg_no_messages_yet">Nenhuma mensagem ainda</string>
<string name="msg_build">Compilação %1$d - %2$s - %3$s</string>
<string name="msg_update_app_version_in_order_to_continue">Versão do servidor desactualizada. Entre em contato com o administrador do servidor para atualizar a versão do servidor para continuar.</string>
<string name="msg_ver_not_recommended">
Parece que a versão do seu servidor está abaixo da versão recomendada, %1$s.\nAinda é possível entrar, mas você pode experimentar comportamentos inesperados.</string>
<string name="msg_ver_not_minimum">
Parece que a versão do seu servidor está abaixo da versão mínima exigida, %1$s.\nPor favor, actualize o seu servidor para entrar!
</string>
<string name="msg_no_chat_title">Nenhuma mensagem de chat</string>
<string name="msg_no_chat_description">Inicie uma conversa para ver as\nsuas mensagens aqui.</string>
<string name="msg_http_insecure">Quando utiliza HTTP você liga-se a um servidor inseguro. Não lhe recomendamos que faça isso.</string>
<string name="msg_error_checking_server_version">Ocorreu um erro ao verificar a versão do seu servidor, tente novamente</string>
<string name="msg_invalid_server_protocol">O protocolo seleccionado não é aceite por este servidor, tente utilizar HTTPS</string>
<string name="msg_image_saved_successfully">Imagem guardada na galeria</string>
<string name="msg_image_saved_failed">Falha ao guardar imagem</string>
<string name="msg_edited">(editada)</string>
<string name="msg_and">\u0020e\u0020</string>
<string name="msg_is_typing">\u0020está a escrever…</string>
<string name="msg_are_typing">\u0020estão a escrever…</string>
<string name="msg_several_users_are_typing">Varios utilizadores estão a escrever…</string>
<string name="msg_no_search_found">Não foram encontrados resultados</string>
<string name="msg_log_out">A terminar sessão…</string>
<string name="msg_upload_file">Enviar ficheiro</string>
<string name="msg_file_description">Descrição do ficheiro</string>
<string name="msg_send">Enviar</string>
<string name="msg_sent_attachment">Enviou um ficheiro</string>
<string name="msg_welcome_to_rocket_chat">Bem vindo(a) ao Rocket.Chat</string>
<string name="msg_team_communication">Comunicação de Equipa</string>
<string name="msg_login_with_email">Entre com <b>e-mail</b></string>
<string name="msg_create_account">Crie uma conta</string>
<string name="msg_continue_with_facebook">Continue com <b>Facebook</b></string>
<string name="msg_continue_with_github">Continue com <b>Github</b></string>
<string name="msg_continue_with_google">Continue com <b>Google</b></string>
<string name="msg_continue_with_linkedin">Continue com <b>Linkedin</b></string>
<string name="msg_continue_with_gitlab">Continue com <b>GitLab</b></string>
<string name="msg_continue_with_wordpress">Continue com <b>WordPress</b></string>
<string name="msg_two_factor_authentication">Autenticação 2FA</string>
<string name="msg__your_2fa_code">Qual é o seu código 2FA?</string>
<string name="msg_permalink_copied">Link permanente copiado</string>
<string name="msg_no_topic">Nenhum tópico adicionado</string>
<string name="msg_no_announcement">Nenhum anúncio adicionado</string>
<string name="msg_no_description">Nenhuma descrição adicionada</string>
<string name="msg_send_email">Enviar e-mail</string>
<string name="msg_android_app_support">Suporte à aplicação Android</string>
<string name="msg_unable_to_update_password">Não é possível atualizar a palavra-passe. Erro: %1$s</string>
<string name="msg_password_updated_successfully">Palavra-passe actualizada com sucesso</string>
<plurals name="msg_reacted_with_">
<item quantity="one">%1$s reagiu com %2$s</item>
<item quantity="other">%1$s reagiram com %2$s</item>
</plurals>
<!-- Create channel messages -->
<string name="msg_private_channel">Privado</string>
<string name="msg_public_channel">Público</string>
<string name="msg_private_channel_description">Apenas você e membros convidados podem entrar neste canal</string>
<string name="msg_public_channel_description">Todos podem entrar neste canal</string>
<string name="msg_ready_only_channel">Canal apenas de leitura</string>
<string name="msg_ready_only_channel_description">Apenas administradores podem escrever novas mensagens</string>
<string name="msg_invite_members">Convidar membros para o canal</string>
<string name="msg_member_already_added">Você já seleccionou este utilizador</string>
<string name="msg_member_not_found">Membro não encontrado</string>
<string name="msg_channel_created_successfully">Canal criado com sucesso</string>
<string name="msg_message_copied">Mensagem copiada</string>
<string name="msg_delete_message">Apagar mensagem</string>
<string name="msg_delete_description">Tem a certeza que deseja apagar esta mensagem?</string>
<string name="msg_view_more">mostrar mais</string>
<string name="msg_view_less">mostrar menos</string>
<string name="msg_muted_on_this_channel">Você está silenciado neste canal</string>
<!-- Preferences messages -->
<string name="msg_analytics_tracking">Acompanhamento de análise</string>
<string name="msg_send_analytics_tracking">Enviar estatísticas anónimas para ajudar a melhorar esta aplicação</string>
<string name="msg_do_not_send_analytics_tracking">Não enviar estatísticas anónimas para ajudar a melhorar esta aplicação</string>
<string name="msg_not_applicable_since_it_is_a_foss_version">Não aplicável, visto ser uma versão FOSS</string>
<!-- System messages -->
<string name="message_room_name_changed">Nome da sala alterado para: %1$s por %2$s</string>
<string name="message_user_added_by">Utilizador %1$s adicionado por %2$s</string>
<string name="message_user_removed_by">Utilizador %1$s removido por %2$s</string>
<string name="message_user_left">Saiu do canal.</string>
<string name="message_user_joined_channel">Entrou no canal.</string>
<string name="message_welcome">Bem vindo(a) %s</string>
<string name="message_removed">Mensagem removida</string>
<string name="message_pinned">Afixou uma mensagem:</string>
<string name="message_muted">Utilizador %1$s silenciado por %2$s</string>
<string name="message_unmuted">Utilizador %2$s removeu o silêncio de %1$s</string>
<string name="message_role_add">%3$s deu estatuto de %2$s a %1$s</string>
<string name="message_role_removed">%3$s retirou o estatuto de %2$s a %1$s</string>
<string name="message_credentials_saved_successfully">Credenciais guardadas com sucesso</string>
<!-- Message actions -->
<string name="action_msg_reply">Responder</string>
<string name="action_msg_info">Informação da mensagem</string>
<string name="action_msg_edit">Editar</string>
<string name="action_msg_copy">Copiar</string>
<string name="action_msg_quote">Citar</string>
<string name="action_msg_delete">Apagar</string>
<string name="action_msg_pin">Afixar mensagem</string>
<string name="action_msg_unpin">Desafixar mensagem</string>
<string name="action_msg_star">Dar um estrela</string>
<string name="action_msg_unstar">Remover estrela</string>
<string name="action_msg_share">Partilhar</string>
<string name="action_title_editing">A editar mensagem</string>
<string name="action_msg_add_reaction">Adicionar reacção</string>
<string name="action_msg_copy_permalink">Copiar link permanente</string>
<string name="action_msg_report">Relatar</string>
<!-- Permission messages -->
<string name="permission_editing_not_allowed">Edição não é permitida</string>
<string name="permission_deleting_not_allowed">A remoção não é permitida</string>
<string name="permission_pinning_not_allowed">Afixação não é permitida</string>
<string name="permission_starring_not_allowed">Dar estrelas não é permitido</string>
<!-- Search message -->
<string name="title_search_message">Pesquisar mensagem</string>
<!-- Favorite/Unfavorite chat room -->
<string name="title_favorite_chat">Marcar chat como favorito</string>
<string name="title_unfavorite_chat">Desmarcar chat como favorito</string>
<!-- Members List -->
<string name="title_members_list">Membros</string>
<!-- Mentions -->
<string name="msg_mentions">Menções</string>
<string name="msg_no_mention">Nenhuma menção</string>
<string name="msg_all_the_mentions_appear_here">Todas as menções\naparecem aqui</string>
<!-- Pinned Messages -->
<string name="title_pinned_messages">Mensagens Afixadas</string>
<string name="no_pinned_messages">Sem mensagens afixadas</string>
<string name="no_pinned_description">Todas as mensagens afixadas\naparecem aqui</string>
<!-- Favorite Messages -->
<string name="title_favorite_messages">Mensagens Favoritas</string>
<string name="no_favorite_messages">Nenhuma mensagem favorita</string>
<string name="no_favorite_description">Todas as mensagens favoritas\naparecem aqui</string>
<!-- Files -->
<string name="title_files">Ficheiros</string>
<string name="title_files_total">Ficheiros (%d)</string>
<string name="msg_no_files">Nenhum ficheiro</string>
<string name="msg_all_files_appear_here">Todos os ficheiros aparecem aqui</string>
<!-- Upload Messages -->
<string name="max_file_size_exceeded">O tamanho do ficheiro (%1$d bytes) excedeu o máximo permitido de %2$d bytes</string>
<!-- Socket status -->
<string name="status_connected">Ligado</string>
<string name="status_disconnected">Desligado</string>
<string name="status_connecting">A ligar</string>
<string name="status_authenticating">A autenticar</string>
<string name="status_disconnecting">A desligar</string>
<string name="status_waiting">A ligar dentro de %d segundos</string>
<!--Suggestions-->
<string name="suggest_all_description">Notifica todos os utilizadores nesta sala</string>
<string name="suggest_here_description">Notifica os utilizadores activos nesta sala</string>
<!-- Slash Commands -->
<string name="Slash_Gimme_Description">Adiciona ༼ つ ◕_◕ ༽つ antes da sua mensagem</string>
<string name="Slash_LennyFace_Description">Adiciona ( ͡° ͜ʖ ͡°) depois da sua mensagem</string>
<string name="Slash_Shrug_Description">Adiciona ¯\_(ツ)_/¯ depois da sua mensagem</string>
<string name="Slash_Tableflip_Description">Adiciona (╯°□°)╯︵ ┻━┻</string>
<string name="Slash_TableUnflip_Description">Adiciona ┬─┬ ノ( ゜-゜ノ)</string>
<string name="Create_A_New_Channel">Criar um novo canal</string>
<string name="Show_the_keyboard_shortcut_list">Mostra a lista de atalhos do teclado</string>
<string name="Invite_user_to_join_channel_all_from">Convida todos os utilizadores do [#canal] para entrar neste canal</string>
<string name="Invite_user_to_join_channel_all_to">Convida todos os utilizadores deste canal a entrar no [#canal]</string>
<string name="Archive">Arquivar</string>
<string name="Remove_someone_from_room">Remove alguém da sala</string>
<string name="Leave_the_current_channel">Sai do canal actual</string>
<string name="Displays_action_text">Escreve um texto de acção</string>
<string name="Direct_message_someone">Envia uma mensagem directa para alguém</string>
<string name="Mute_someone_in_room">Silencia alguém na sala</string>
<string name="Unmute_someone_in_room">Remove o silêncio de alguém na sala</string>
<string name="Invite_user_to_join_channel">Convida um utilizador a entrar neste canal</string>
<string name="Unarchive">Desarquivar</string>
<string name="Join_the_given_channel">Entra no canal especificado</string>
<string name="Guggy_Command_Description">Gera um GIF baseado no texto fornecido</string>
<string name="Slash_Topic_Description">Define o tópico</string>
<!-- Emoji message-->
<string name="msg_no_recent_emoji">Nenhum emojis recente</string>
<string name="alert_title_default_skin_tone">Tom de pele padrão</string>
<!-- Sorting and grouping-->
<string name="msg_sort">Ordenar</string>
<string name="dialog_sort_title">Ordenar por</string>
<string name="dialog_sort_by_alphabet">Alfabeticamente</string>
<string name="dialog_sort_by_activity">Actividade</string>
<string name="dialog_group_by_type">Agrupar por tipo</string>
<string name="dialog_group_favourites">Agrupar favoritos</string>
<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 Directas</string>
<string name="header_live_chats">Chats ao Vivo</string>
<string name="header_unknown">Desconhecido</string>
<!--Notifications-->
<string name="share_label">Editar mensagem partilhada</string>
<string name="notif_action_reply_hint">RESPONDER</string>
<string name="notif_error_sending">A resposta falhou. Tente novamente.</string>
<string name="notif_success_sending">Mensagem enviada para %1$s!</string>
<string name="read_by">Lida por</string>
<string name="message_information_title">Informação da Mensagem</string>
<string name="message_room_changed_privacy">Tipo de sala alterado para: %1$s por %2$s</string>
<string name="foss" translatable="false">(FOSS)</string>
<!-- User Details -->
<string name="timezone">Fuso Horário</string>
<string name="status">Estado</string>
<!-- Report -->
<string name="submit">Enviar</string>
<string name="required">*requerido</string>
<string name="report_sent">O seu relatório foi enviado!</string>
</resources>
...@@ -316,6 +316,7 @@ ...@@ -316,6 +316,7 @@
<string name="chatroom_header">Заголовок</string> <string name="chatroom_header">Заголовок</string>
<!--ChatRooms Headers--> <!--ChatRooms Headers-->
<string name="header_favorite">Favorites</string><!-- TODO - Add proper translation -->
<string name="header_channel">Каналы</string> <string name="header_channel">Каналы</string>
<string name="header_private_groups">Приватные каналы</string> <string name="header_private_groups">Приватные каналы</string>
<string name="header_direct_messages">Личная переписка</string> <string name="header_direct_messages">Личная переписка</string>
...@@ -333,7 +334,7 @@ ...@@ -333,7 +334,7 @@
<string name="message_room_changed_privacy">Тип канала изменен на: %1$s пользователем %2$s</string> <string name="message_room_changed_privacy">Тип канала изменен на: %1$s пользователем %2$s</string>
<!-- User Details --> <!-- User Details -->
<string name="timezone">Часовой поясe</string> <string name="timezone">Часовой пояс</string>
<!-- Report --> <!-- Report -->
<string name="submit">Отправить</string> <string name="submit">Отправить</string>
......
...@@ -321,6 +321,7 @@ ...@@ -321,6 +321,7 @@
<string name="chatroom_header">Başlık</string> <string name="chatroom_header">Başlık</string>
<!--ChatRooms Headers--> <!--ChatRooms Headers-->
<string name="header_favorite">Favorites</string><!-- TODO - Add proper translation -->
<string name="header_channel">Kanallar</string> <string name="header_channel">Kanallar</string>
<string name="header_private_groups">Gizli Gruplar</string> <string name="header_private_groups">Gizli Gruplar</string>
<string name="header_direct_messages">Direkt Mesajlar</string> <string name="header_direct_messages">Direkt Mesajlar</string>
......
...@@ -317,6 +317,7 @@ ...@@ -317,6 +317,7 @@
<string name="chatroom_header">Заголовок</string> <string name="chatroom_header">Заголовок</string>
<!--ChatRooms Headers--> <!--ChatRooms Headers-->
<string name="header_favorite">Favorites</string><!-- TODO - Add proper translation -->
<string name="header_channel">Канали</string> <string name="header_channel">Канали</string>
<string name="header_private_groups">Приватні канали</string> <string name="header_private_groups">Приватні канали</string>
<string name="header_direct_messages">Особисті повідомлення</string> <string name="header_direct_messages">Особисті повідомлення</string>
......
...@@ -316,6 +316,7 @@ ...@@ -316,6 +316,7 @@
<string name="chatroom_header">头部</string> <string name="chatroom_header">头部</string>
<!--ChatRooms Headers--> <!--ChatRooms Headers-->
<string name="header_favorite">Favorites</string><!-- TODO - Add proper translation -->
<string name="header_channel">频道</string> <string name="header_channel">频道</string>
<string name="header_private_groups">私人组</string> <string name="header_private_groups">私人组</string>
<string name="header_direct_messages">直接对话</string> <string name="header_direct_messages">直接对话</string>
......
...@@ -332,6 +332,7 @@ https://github.com/RocketChat/java-code-styles/blob/master/CODING_STYLE.md#strin ...@@ -332,6 +332,7 @@ https://github.com/RocketChat/java-code-styles/blob/master/CODING_STYLE.md#strin
<string name="chatroom_header">Header</string> <string name="chatroom_header">Header</string>
<!--ChatRooms Headers--> <!--ChatRooms Headers-->
<string name="header_favorite">Favorites</string>
<string name="header_channel">Channels</string> <string name="header_channel">Channels</string>
<string name="header_private_groups">Private Groups</string> <string name="header_private_groups">Private Groups</string>
<string name="header_direct_messages">Direct Messages</string> <string name="header_direct_messages">Direct Messages</string>
......
...@@ -53,8 +53,15 @@ class DrawingActivity : DaggerAppCompatActivity(), DrawView { ...@@ -53,8 +53,15 @@ class DrawingActivity : DaggerAppCompatActivity(), DrawView {
} }
private fun setupDrawTools() { private fun setupDrawTools() {
image_draw_eraser.setOnClickListener { image_draw_eraser.setOnLongClickListener{
custom_draw_view.clearCanvas() 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) toggleDrawTools(draw_tools, false)
} }
...@@ -111,6 +118,13 @@ class DrawingActivity : DaggerAppCompatActivity(), DrawView { ...@@ -111,6 +118,13 @@ class DrawingActivity : DaggerAppCompatActivity(), DrawView {
} }
private fun colorSelector() { 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 { image_color_red.setOnClickListener {
custom_draw_view.setColor( custom_draw_view.setColor(
ResourcesCompat.getColor(resources, R.color.color_red, null) ResourcesCompat.getColor(resources, R.color.color_red, null)
......
...@@ -42,7 +42,6 @@ class CustomDrawView(context: Context, attrs: AttributeSet) : View(context, attr ...@@ -42,7 +42,6 @@ class CustomDrawView(context: Context, attrs: AttributeSet) : View(context, attr
fun undo() { fun undo() {
if (mPaths.isEmpty() && mLastPaths.isNotEmpty()) { if (mPaths.isEmpty() && mLastPaths.isNotEmpty()) {
mPaths = mLastPaths.clone() as LinkedHashMap<MyPath, PaintOptions> mPaths = mLastPaths.clone() as LinkedHashMap<MyPath, PaintOptions>
mLastPaths.clear()
invalidate() invalidate()
return return
} }
......
package chat.rocket.android.util.extension package chat.rocket.android.util.extension
import android.content.Context
import android.os.Build
import android.widget.TextView
import androidx.appcompat.widget.SearchView import androidx.appcompat.widget.SearchView
fun SearchView.onQueryTextListener(queryListener: (String) -> Unit) { fun SearchView.onQueryTextListener(queryListener: (String) -> Unit) {
...@@ -15,3 +18,11 @@ fun SearchView.onQueryTextListener(queryListener: (String) -> Unit) { ...@@ -15,3 +18,11 @@ fun SearchView.onQueryTextListener(queryListener: (String) -> Unit) {
} }
}) })
} }
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