Commit d0822a47 authored by divyanshu's avatar divyanshu

Merge branch 'develop-2.x' into redesignOnboarding

# Conflicts:
#	app/src/main/java/chat/rocket/android/authentication/login/presentation/LoginPresenter.kt
#	app/src/main/java/chat/rocket/android/authentication/login/presentation/LoginView.kt
#	app/src/main/java/chat/rocket/android/authentication/login/ui/LoginFragment.kt
#	app/src/main/java/chat/rocket/android/authentication/server/ui/ServerFragment.kt
#	app/src/main/java/chat/rocket/android/authentication/ui/AuthenticationActivity.kt
#	app/src/main/res/layout/fragment_authentication_log_in.xml
#	gradle/wrapper/gradle-wrapper.properties
parents 10188c93 adf3d7e0
......@@ -12,8 +12,8 @@ android {
applicationId "chat.rocket.android"
minSdkVersion versions.minSdk
targetSdkVersion versions.targetSdk
versionCode 2034
versionName "2.5.0"
versionCode 2036
versionName "2.5.1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled true
......@@ -126,7 +126,7 @@ dependencies {
implementation "com.github.luciofm:livedata-ktx:b1e8bbc25a"
implementation('com.crashlytics.sdk.android:crashlytics:2.9.2@aar') {
implementation('com.crashlytics.sdk.android:crashlytics:2.9.4@aar') {
transitive = true
}
......
#Thu Feb 15 15:50:42 BRST 2018
#Wed Aug 01 21:56:00 EDT 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-all.zip
......@@ -80,7 +80,7 @@ private fun formatLocalDateTime(localDateTime: LocalDateTime): String {
}
private fun formatLocalDate(localDate: LocalDate): String {
val formatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)
val formatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)
return localDate.format(formatter).toString()
}
......
......@@ -11,8 +11,8 @@ import timber.log.Timber
@Parcelize
data class LoginDeepLinkInfo(
val url: String,
val userId: String,
val token: String
val userId: String?,
val token: String?
) : Parcelable
fun Intent.getLoginDeepLinkInfo(): LoginDeepLinkInfo? {
......
......@@ -23,10 +23,10 @@ import chat.rocket.android.server.domain.isGoogleAuthenticationEnabled
import chat.rocket.android.server.domain.isLdapAuthenticationEnabled
import chat.rocket.android.server.domain.isLinkedinAuthenticationEnabled
import chat.rocket.android.server.domain.isLoginFormEnabled
import chat.rocket.android.server.domain.isMeteorAuthenticationEnabled
import chat.rocket.android.server.domain.isWordpressAuthenticationEnabled
import chat.rocket.android.server.domain.isPasswordResetEnabled
import chat.rocket.android.server.domain.isRegistrationEnabledForNewUsers
import chat.rocket.android.server.domain.isTwitterAuthenticationEnabled
import chat.rocket.android.server.domain.wordpressUrl
import chat.rocket.android.server.domain.model.Account
import chat.rocket.android.server.domain.wideTile
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
......@@ -72,6 +72,7 @@ private const val SERVICE_NAME_GITHUB = "github"
private const val SERVICE_NAME_GOOGLE = "google"
private const val SERVICE_NAME_LINKEDIN = "linkedin"
private const val SERVICE_NAME_GILAB = "gitlab"
private const val SERVICE_NAME_WORDPRESS = "wordpress"
class LoginPresenter @Inject constructor(
private val view: LoginView,
......@@ -140,10 +141,15 @@ class LoginPresenter @Inject constructor(
fun authenticateWithDeepLink(deepLinkInfo: LoginDeepLinkInfo) {
val serverUrl = deepLinkInfo.url
setupConnectionInfo(serverUrl)
deepLinkUserId = deepLinkInfo.userId
deepLinkToken = deepLinkInfo.token
tokenRepository.save(serverUrl, Token(deepLinkUserId, deepLinkToken))
doAuthentication(TYPE_LOGIN_DEEP_LINK)
if (deepLinkInfo.userId != null && deepLinkInfo.token != null) {
deepLinkUserId = deepLinkInfo.userId
deepLinkToken = deepLinkInfo.token
tokenRepository.save(serverUrl, Token(deepLinkUserId, deepLinkToken))
doAuthentication(TYPE_LOGIN_DEEP_LINK)
} else {
// If we don't have the login credentials, just go through normal setup and user input.
setupView()
}
}
private fun setupConnectionInfo(serverUrl: String) {
......@@ -195,42 +201,67 @@ class LoginPresenter @Inject constructor(
var totalSocialAccountsEnabled = 0
getCustomOauthServices(services).let {
for (service in it) {
val serviceName = getCustomOauthServiceName(service)
val customOauthUrl = OauthHelper.getCustomOauthUrl(
getCustomOauthHost(service),
getCustomOauthAuthorizePath(service),
getCustomOauthClientId(service),
currentServer,
serviceName,
state,
getCustomOauthScope(service)
)
view.addCustomOauthServiceButton(
customOauthUrl,
state,
serviceName,
getServiceNameColor(service),
getServiceButtonColor(service)
)
totalSocialAccountsEnabled++
for (serviceMap in it) {
val serviceName = getCustomOauthServiceName(serviceMap)
val host = getCustomOauthHost(serviceMap)
val authorizePath = getCustomOauthAuthorizePath(serviceMap)
val clientId = getOauthClientId(serviceMap)
val scope = getCustomOauthScope(serviceMap)
val textColor = getServiceNameColorForCustomOauthOrSaml(serviceMap)
val buttonColor = getServiceButtonColor(serviceMap)
if (serviceName != null &&
host != null &&
authorizePath != null &&
clientId != null &&
scope != null &&
textColor != null &&
buttonColor != null
) {
val customOauthUrl = OauthHelper.getCustomOauthUrl(
host,
authorizePath,
clientId,
currentServer,
serviceName,
state,
scope
)
view.addCustomOauthServiceButton(
customOauthUrl,
state,
serviceName,
textColor,
buttonColor
)
totalSocialAccountsEnabled++
}
}
}
getSamlServices(services).let {
val samlToken = generateRandomString(17)
for (service in it) {
view.addSamlServiceButton(
currentServer.samlUrl(getSamlProvider(service), samlToken),
samlToken,
getSamlServiceName(service),
getServiceNameColor(service),
getServiceButtonColor(service)
)
totalSocialAccountsEnabled++
for (serviceMap in it) {
val provider = getSamlProvider(serviceMap)
val serviceName = getSamlServiceName(serviceMap)
val textColor = getServiceNameColorForCustomOauthOrSaml(serviceMap)
val buttonColor = getServiceButtonColor(serviceMap)
if (provider != null &&
serviceName != null &&
textColor != null &&
buttonColor != null
) {
view.addSamlServiceButton(
currentServer.samlUrl(provider, samlToken),
samlToken,
serviceName,
textColor,
buttonColor
)
totalSocialAccountsEnabled++
}
}
}
}
......@@ -324,55 +355,114 @@ class LoginPresenter @Inject constructor(
}
}
private fun getOauthClientId(listMap: List<Map<String, Any>>, serviceName: String): String? {
return listMap.find { map -> map.containsValue(serviceName) }?.let {
it["clientId"] ?: it["appId"]
}.toString()
}
private fun getSamlServices(listMap: List<Map<String, Any>>): List<Map<String, Any>> {
return listMap.filter { map -> map["service"] == "saml" }
}
private fun getSamlServiceName(service: Map<String, Any>): String {
return service["buttonLabelText"].toString()
}
private fun getSamlProvider(service: Map<String, Any>): String {
return (service["clientConfig"] as Map<*, *>)["provider"].toString()
}
private fun getCustomOauthServices(listMap: List<Map<String, Any>>): List<Map<String, Any>> {
return listMap.filter { map -> map["custom"] == true }
}
private fun getCustomOauthHost(service: Map<String, Any>): String {
return service["serverURL"].toString()
}
private fun getCustomOauthAuthorizePath(service: Map<String, Any>): String {
return service["authorizePath"].toString()
}
private fun getCustomOauthClientId(service: Map<String, Any>): String {
return service["clientId"].toString()
}
private fun getCustomOauthServiceName(service: Map<String, Any>): String {
return service["service"].toString()
}
private fun getCustomOauthScope(service: Map<String, Any>): String {
return service["scope"].toString()
}
private fun getServiceButtonColor(service: Map<String, Any>): Int {
return service["buttonColor"].toString().parseColor()
}
private fun getServiceNameColor(service: Map<String, Any>): Int {
return service["buttonLabelColor"].toString().parseColor()
}
/**
* Returns an OAuth service map given a [serviceName].
*
* @param listMap The list of [Map] to get the service from.
* @param serviceName The service name to get in the [listMap]
* @return The OAuth service map or null otherwise.
*/
private fun getServiceMap(
listMap: List<Map<String, Any>>,
serviceName: String
): Map<String, Any>? = listMap.find { map -> map.containsValue(serviceName) }
/**
* Returns the OAuth client ID of a [serviceMap].
* REMARK: This function works for common OAuth providers (Google, Facebook, Github and so on)
* as well as custom OAuth.
*
* @param serviceMap The service map to get the OAuth client ID.
* @return The OAuth client ID or null otherwise.
*/
private fun getOauthClientId(serviceMap: Map<String, Any>): String? =
serviceMap["clientId"] as? String ?: serviceMap["appId"] as? String
/**
* Returns a custom OAuth service list.
*
* @return A custom OAuth service list, otherwise an empty list if there is no custom OAuth service.
*/
private fun getCustomOauthServices(listMap: List<Map<String, Any>>): List<Map<String, Any>> =
listMap.filter { map -> map["custom"] == true }
/** Returns the custom OAuth service host.
*
* @param serviceMap The service map to get the custom OAuth service host.
* @return The custom OAuth service host, otherwise null.
*/
private fun getCustomOauthHost(serviceMap: Map<String, Any>): String? =
serviceMap["serverURL"] as? String
/** Returns the custom OAuth service authorize path.
*
* @param serviceMap The service map to get the custom OAuth service authorize path.
* @return The custom OAuth service authorize path, otherwise null.
*/
private fun getCustomOauthAuthorizePath(serviceMap: Map<String, Any>): String? =
serviceMap["authorizePath"] as? String
/** Returns the custom OAuth service scope.
*
* @param serviceMap The service map to get the custom OAuth service scope.
* @return The custom OAuth service scope, otherwise null.
*/
private fun getCustomOauthScope(serviceMap: Map<String, Any>): String? =
serviceMap["scope"] as? String
/** Returns the text of the custom OAuth service.
*
* @param serviceMap The service map to get the text of the custom OAuth service.
* @return The text of the custom OAuth service, otherwise null.
*/
private fun getCustomOauthServiceName(serviceMap: Map<String, Any>): String? =
serviceMap["service"] as? String
/**
* Returns a SAML OAuth service list.
*
* @return A SAML service list, otherwise an empty list if there is no SAML OAuth service.
*/
private fun getSamlServices(listMap: List<Map<String, Any>>): List<Map<String, Any>> =
listMap.filter { map -> map["service"] == "saml" }
/**
* Returns the SAML provider.
*
* @param serviceMap The service map to provider from.
* @return The SAML provider, otherwise null.
*/
private fun getSamlProvider(serviceMap: Map<String, Any>): String? =
(serviceMap["clientConfig"] as Map<*, *>)["provider"] as? String
/**
* Returns the text of the SAML service.
*
* @param serviceMap The service map to get the text of the SAML service.
* @return The text of the SAML service, otherwise null.
*/
private fun getSamlServiceName(serviceMap: Map<String, Any>): String? =
serviceMap["buttonLabelText"] as? String
/**
* Returns the text color of the service name.
* REMARK: This can be used for custom OAuth or SAML.
*
* @param serviceMap The service map to get the text color from.
* @return The text color of the service (custom OAuth or SAML), otherwise null.
*/
private fun getServiceNameColorForCustomOauthOrSaml(serviceMap: Map<String, Any>): Int? =
(serviceMap["buttonLabelColor"] as? String)?.parseColor()
/**
* Returns the button color of the service name.
* REMARK: This can be used for custom OAuth or SAML.
*
* @param serviceMap The service map to get the button color from.
* @return The button color of the service (custom OAuth or SAML), otherwise null.
*/
private fun getServiceButtonColor(serviceMap: Map<String, Any>): Int? =
(serviceMap["buttonColor"] as? String)?.parseColor()
private suspend fun saveAccount(username: String) {
val icon = settings.favicon()?.let {
......
......@@ -263,6 +263,24 @@ class LoginFragment : Fragment(), LoginView {
}
}
override fun enableLoginByWordpress() {
ui {
button_wordpress.isClickable = true
}
}
override fun setupWordpressButtonListener(wordpressUrl: String, state: String) {
ui { activity ->
button_wordpress.setOnClickListener {
startActivityForResult(
activity.oauthWebViewIntent(wordpressUrl, state),
REQUEST_CODE_FOR_OAUTH
)
activity.overridePendingTransition(R.anim.slide_up, R.anim.hold)
}
}
}
override fun addCustomOauthServiceButton(
customOauthUrl: String,
state: String,
......
......@@ -114,6 +114,9 @@ class ServerFragment : Fragment(), ServerView {
override fun onDestroyView() {
super.onDestroyView()
constraint_layout.viewTreeObserver.removeOnGlobalLayoutListener(layoutListener)
// reset deep link info, so user can come back and log to another server...
deepLinkInfo = null
relative_layout.viewTreeObserver.removeOnGlobalLayoutListener(layoutListener)
}
override fun showInvalidServerUrlMessage() = showMessage(getString(R.string.msg_invalid_server_url))
......@@ -156,7 +159,7 @@ class ServerFragment : Fragment(), ServerView {
.setPositiveButton(R.string.msg_ok) { _, _ ->
performConnect()
}
.create()
.create()
.show()
}
}
......
......@@ -85,6 +85,8 @@ class AuthenticationActivity : AppCompatActivity(), HasSupportFragmentInjector {
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
supportFragmentManager.findFragmentById(R.id.fragment_container)?.onActivityResult(requestCode, resultCode, data)
val currentFragment = supportFragmentManager.findFragmentById(R.id.fragment_container)
currentFragment?.onActivityResult(requestCode, resultCode, data)
}
override fun supportFragmentInjector(): AndroidInjector<Fragment> {
......
......@@ -6,6 +6,7 @@ import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.children
import androidx.lifecycle.Lifecycle
import androidx.recyclerview.widget.RecyclerView
import chat.rocket.android.R
import chat.rocket.android.chatroom.ui.bottomsheet.MessageActionsBottomSheet
......@@ -94,9 +95,11 @@ abstract class BaseViewHolder<T : BaseUiModel<*>>(
view.context?.let {
if (it is ContextThemeWrapper && it.baseContext is AppCompatActivity) {
with(it.baseContext as AppCompatActivity) {
val actionsBottomSheet = MessageActionsBottomSheet()
actionsBottomSheet.addItems(menuItems, this@BaseViewHolder)
actionsBottomSheet.show(supportFragmentManager, null)
if (this.lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) {
val actionsBottomSheet = MessageActionsBottomSheet()
actionsBottomSheet.addItems(menuItems, this@BaseViewHolder)
actionsBottomSheet.show(supportFragmentManager, null)
}
}
}
}
......
......@@ -18,10 +18,9 @@ import java.security.InvalidParameterException
class ChatRoomAdapter(
private val roomType: String? = null,
private val roomName: String? = null,
private val presenter: ChatRoomPresenter? = null,
private val actionSelectListener: OnActionSelected? = null,
private val enableActions: Boolean = true,
private val reactionListener: EmojiReactionListener? = null,
private val context: Context? = null
private val reactionListener: EmojiReactionListener? = null
) : RecyclerView.Adapter<BaseViewHolder<*>>() {
private val dataSet = ArrayList<BaseUiModel<*>>()
......@@ -70,7 +69,7 @@ class ChatRoomAdapter(
BaseUiModel.ViewType.MESSAGE_REPLY -> {
val view = parent.inflate(R.layout.item_message_reply)
MessageReplyViewHolder(view, actionsListener, reactionListener) { roomName, permalink ->
presenter?.openDirectMessage(roomName, permalink)
actionSelectListener?.openDirectMessage(roomName, permalink)
}
}
else -> {
......@@ -212,52 +211,53 @@ class ChatRoomAdapter(
message.apply {
when (item.itemId) {
R.id.action_message_info -> {
presenter?.messageInfo(id)
actionSelectListener?.showMessageInfo(id)
}
R.id.action_message_reply -> {
if (roomName != null && roomType != null) {
presenter?.citeMessage(roomName, roomType, id, true)
actionSelectListener?.citeMessage(roomName, roomType, id, true)
}
}
R.id.action_message_quote -> {
if (roomName != null && roomType != null) {
presenter?.citeMessage(roomName, roomType, id, false)
actionSelectListener?.citeMessage(roomName, roomType, id, false)
}
}
R.id.action_message_copy -> {
presenter?.copyMessage(id)
actionSelectListener?.copyMessage(id)
}
R.id.action_message_edit -> {
presenter?.editMessage(roomId, id, message.message)
actionSelectListener?.editMessage(roomId, id, message.message)
}
R.id.action_message_star -> {
if (!item.isChecked) {
presenter?.starMessage(id)
} else {
presenter?.unstarMessage(id)
}
actionSelectListener?.toogleStar(id, !item.isChecked)
}
R.id.action_message_unpin -> {
if (!item.isChecked) {
presenter?.pinMessage(id)
} else {
presenter?.unpinMessage(id)
}
actionSelectListener?.tooglePin(id, !item.isChecked)
}
R.id.action_message_delete -> {
context?.let {
val builder = AlertDialog.Builder(it)
builder.setTitle(it.getString(R.string.msg_delete_message))
.setMessage(it.getString(R.string.msg_delete_description))
.setPositiveButton(it.getString(R.string.msg_ok)) { _, _ -> presenter?.deleteMessage(roomId, id) }
.setNegativeButton(it.getString(R.string.msg_cancel)) { _, _ -> }
.show()
}
actionSelectListener?.deleteMessage(roomId, id)
}
R.id.action_menu_msg_react -> {
actionSelectListener?.showReactions(id)
}
else -> {
TODO("Not implemented")
}
R.id.action_menu_msg_react -> presenter?.showReactions(id)
else -> TODO("Not implemented")
}
}
}
}
interface OnActionSelected {
fun showMessageInfo(id: String)
fun citeMessage(roomName: String, roomType: String, messageId: String, mentionAuthor: Boolean)
fun copyMessage(id: String)
fun editMessage(roomId: String, messageId: String, text: String)
fun toogleStar(id: String, star: Boolean)
fun tooglePin(id: String, pin: Boolean)
fun deleteMessage(roomId: String, id: String)
fun showReactions(id: String)
fun openDirectMessage(roomName: String, message: String)
}
}
\ No newline at end of file
package chat.rocket.android.chatroom.adapter
import android.net.Uri
import android.view.View
import androidx.core.view.isVisible
import chat.rocket.android.chatroom.uimodel.UrlPreviewUiModel
import chat.rocket.android.util.extensions.openTabbedUrl
import chat.rocket.android.emoji.EmojiReactionListener
import chat.rocket.android.util.extensions.content
import chat.rocket.android.util.extensions.openTabbedUrl
import kotlinx.android.synthetic.main.message_url_preview.view.*
class UrlPreviewViewHolder(itemView: View,
......@@ -42,7 +41,7 @@ class UrlPreviewViewHolder(itemView: View,
private val onClickListener = { view: View ->
if (data != null) {
view.openTabbedUrl(Uri.parse(data!!.rawData.url))
view.openTabbedUrl(data!!.rawData.url)
}
}
}
\ No newline at end of file
......@@ -126,7 +126,8 @@ internal const val MENU_ACTION_PINNED_MESSAGES = 4
internal const val MENU_ACTION_FAVORITE_MESSAGES = 5
internal const val MENU_ACTION_FILES = 6
class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiReactionListener {
class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiReactionListener,
ChatRoomAdapter.OnActionSelected {
@Inject
lateinit var presenter: ChatRoomPresenter
@Inject
......@@ -200,6 +201,9 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
} else {
requireNotNull(bundle) { "no arguments supplied when the fragment was instantiated" }
}
adapter = ChatRoomAdapter(chatRoomType, chatRoomName, this,
reactionListener = this)
}
override fun onCreateView(
......@@ -335,13 +339,6 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
}
if (recycler_view.adapter == null) {
adapter = ChatRoomAdapter(
chatRoomType,
chatRoomName,
presenter,
reactionListener = this@ChatRoomFragment,
context = context
)
recycler_view.adapter = adapter
if (dataSet.size >= 30) {
recycler_view.addOnScrollListener(endlessRecyclerViewScrollListener)
......@@ -967,4 +964,55 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
private fun setupToolbar(toolbarTitle: String) {
(activity as ChatRoomActivity).showToolbarTitle(toolbarTitle)
}
override fun showMessageInfo(id: String) {
presenter.messageInfo(id)
}
override fun citeMessage(roomName: String, roomType: String, messageId: String, mentionAuthor: Boolean) {
presenter.citeMessage(roomName, roomType, messageId, mentionAuthor)
}
override fun copyMessage(id: String) {
presenter.copyMessage(id)
}
override fun editMessage(roomId: String, messageId: String, text: String) {
presenter.editMessage(roomId, messageId, text)
}
override fun toogleStar(id: String, star: Boolean) {
if (star) {
presenter.starMessage(id)
} else {
presenter.unstarMessage(id)
}
}
override fun tooglePin(id: String, pin: Boolean) {
if (pin) {
presenter.pinMessage(id)
} else {
presenter.unpinMessage(id)
}
}
override fun deleteMessage(roomId: String, id: String) {
ui {
val builder = AlertDialog.Builder(it)
builder.setTitle(it.getString(R.string.msg_delete_message))
.setMessage(it.getString(R.string.msg_delete_description))
.setPositiveButton(it.getString(R.string.msg_ok)) { _, _ -> presenter.deleteMessage(roomId, id) }
.setNegativeButton(it.getString(R.string.msg_cancel)) { _, _ -> }
.show()
}
}
override fun showReactions(id: String) {
presenter.showReactions(id)
}
override fun openDirectMessage(roomName: String, message: String) {
presenter.openDirectMessage(roomName, message)
}
}
......@@ -35,7 +35,7 @@ private const val INTENT_CHAT_ROOM_ID = "chat_room_id"
class FavoriteMessagesFragment : Fragment(), FavoriteMessagesView {
private lateinit var chatRoomId: String
private lateinit var adapter: ChatRoomAdapter
private val adapter = ChatRoomAdapter(enableActions = false)
@Inject
lateinit var presenter: FavoriteMessagesPresenter
......@@ -66,7 +66,6 @@ class FavoriteMessagesFragment : Fragment(), FavoriteMessagesView {
override fun showFavoriteMessages(favoriteMessages: List<BaseUiModel<*>>) {
ui {
if (recycler_view.adapter == null) {
adapter = ChatRoomAdapter(enableActions = false)
recycler_view.adapter = adapter
val linearLayoutManager = LinearLayoutManager(context)
recycler_view.layoutManager = linearLayoutManager
......
......@@ -5,21 +5,19 @@ import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.RectF
import android.net.Uri
import androidx.core.content.res.ResourcesCompat
import android.text.Spanned
import android.text.style.ClickableSpan
import android.text.style.ReplacementSpan
import android.text.style.StyleSpan
import android.util.Patterns
import android.view.View
import androidx.core.content.res.ResourcesCompat
import chat.rocket.android.R
import chat.rocket.android.server.domain.PublicSettings
import chat.rocket.android.server.domain.useRealName
import chat.rocket.android.util.extensions.openTabbedUrl
import chat.rocket.android.emoji.EmojiParser
import chat.rocket.android.emoji.EmojiRepository
import chat.rocket.android.emoji.EmojiTypefaceSpan
import chat.rocket.android.server.domain.PublicSettings
import chat.rocket.android.server.domain.useRealName
import chat.rocket.android.util.extensions.openTabbedUrl
import chat.rocket.common.model.SimpleUser
import chat.rocket.core.model.Message
import org.commonmark.node.AbstractVisitor
......@@ -159,7 +157,7 @@ class MessageParser @Inject constructor(
if (node is ListItem) {
newLine()
builder.append("$number$delimiter ")
super.visit(node.firstChild as Paragraph)
super.visitChildren(node)
newLine()
}
number++
......@@ -187,7 +185,7 @@ class MessageParser @Inject constructor(
if (!link.startsWith("@") && link !in consumed) {
builder.setSpan(object : ClickableSpan() {
override fun onClick(view: View) {
view.openTabbedUrl(getUri(link))
view.openTabbedUrl(link)
}
}, matcher.start(0), matcher.end(0))
consumed.add(link)
......@@ -195,14 +193,6 @@ class MessageParser @Inject constructor(
}
visitChildren(text)
}
private fun getUri(link: String): Uri {
val uri = Uri.parse(link)
if (uri.scheme == null) {
return Uri.parse("http://$link")
}
return uri
}
}
class MentionSpan(
......
......@@ -92,6 +92,59 @@ object OauthHelper {
"&scope=email"
}
/**
* Returns the WordPress-Com Oauth URL.
*
* @param clientId The WordPress-Com client ID.
* @param serverUrl The server URL.
* @param state An unguessable random string used to protect against forgery attacks.
* @return The WordPress-Com Oauth URL.
*/
fun getWordpressComOauthUrl(clientId: String, serverUrl: String, state: String): String {
return "https://public-api.wordpress.com/oauth2/authorize" +
"?client_id=$clientId" +
"&redirect_uri=${serverUrl.removeTrailingSlash()}/_oauth/wordpress?close" +
"&state=$state" +
"&response_type=code" +
"&scope=auth"
}
/**
* Returns the WordPress custom Oauth URL.
*
* @param host The WordPress custom OAuth host.
* @param authorizePath The WordPress custom OAuth authorization path.
* @param clientId The WordPress custom OAuth client ID.
* @param serverUrl The server URL.
* @param serviceName The service name.
* @param state An unguessable random string used to protect against forgery attacks.
* @param scope The WordPress custom OAuth scope.
* @return The WordPress custom Oauth URL.
*/
fun getWordpressCustomOauthUrl(
host: String,
authorizePath: String,
clientId: String,
serverUrl: String,
serviceName: String,
state: String,
scope: String
): String {
(authorizePath +
"?client_id=$clientId" +
"&redirect_uri=${serverUrl.removeTrailingSlash()}/_oauth/$serviceName?close" +
"&state=$state" +
"&scope=$scope" +
"&response_type=code"
).let {
return if (it.contains(host)) {
it
} else {
host + it
}
}
}
/**
* Returns the Custom Oauth URL.
*
......
......@@ -5,6 +5,7 @@ import chat.rocket.android.db.DatabaseManagerFactory
import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.main.uimodel.NavHeaderUiModel
import chat.rocket.android.main.uimodel.NavHeaderUiModelMapper
import chat.rocket.android.push.GroupedPush
import chat.rocket.android.server.domain.GetAccountsInteractor
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.domain.GetSettingsInteractor
......@@ -52,6 +53,7 @@ class MainPresenter @Inject constructor(
private val getAccountsInteractor: GetAccountsInteractor,
private val removeAccountInteractor: RemoveAccountInteractor,
private val factory: RocketChatClientFactory,
private val groupedPush: GroupedPush,
dbManagerFactory: DatabaseManagerFactory,
getSettingsInteractor: GetSettingsInteractor,
managerFactory: ConnectionManagerFactory
......@@ -232,4 +234,12 @@ class MainPresenter @Inject constructor(
private fun updateMyself(myself: Myself) =
view.setupUserAccountInfo(navHeaderMapper.mapToUiModel(myself))
}
\ No newline at end of file
fun clearNotificationsForChatroom(chatRoomId: String?) {
if (chatRoomId == null) return
groupedPush.hostToPushMessageList[currentServer]?.let { list ->
list.removeAll { it.info.roomId == chatRoomId }
}
}
}
......@@ -5,13 +5,13 @@ import android.app.Activity
import android.app.AlertDialog
import android.app.ProgressDialog
import android.os.Bundle
import androidx.fragment.app.Fragment
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import android.view.Gravity
import android.view.MenuItem
import androidx.annotation.IdRes
import androidx.appcompat.app.AppCompatActivity
import androidx.drawerlayout.widget.DrawerLayout
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import chat.rocket.android.BuildConfig
import chat.rocket.android.R
import chat.rocket.android.main.adapter.AccountsAdapter
......@@ -55,7 +55,7 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector,
private var expanded = false
private val headerLayout by lazy { view_navigation.getHeaderView(0) }
private var chatRoomId: String? = null
private var progressDialog : ProgressDialog? = null
private var progressDialog: ProgressDialog? = null
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
......@@ -74,6 +74,9 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector,
chatRoomId = intent.getStringExtra(INTENT_CHAT_ROOM_ID)
println("ChatRoomId: $chatRoomId")
presenter.clearNotificationsForChatroom(chatRoomId)
presenter.connect()
presenter.loadServerAccounts()
presenter.loadCurrentInfo()
......@@ -270,4 +273,4 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector,
progressDialog?.dismiss()
progressDialog = null
}
}
\ No newline at end of file
}
......@@ -36,7 +36,7 @@ private const val BUNDLE_CHAT_ROOM_ID = "chat_room_id"
class MentionsFragment : Fragment(), MentionsView {
private lateinit var chatRoomId: String
private lateinit var adapter: ChatRoomAdapter
private val adapter = ChatRoomAdapter(enableActions = false)
@Inject
lateinit var presenter: MentionsPresenter
......@@ -68,7 +68,6 @@ class MentionsFragment : Fragment(), MentionsView {
override fun showMentions(mentions: List<BaseUiModel<*>>) {
ui {
if (recycler_view.adapter == null) {
adapter = ChatRoomAdapter(enableActions = false)
recycler_view.adapter = adapter
val linearLayoutManager = LinearLayoutManager(context)
......
......@@ -36,7 +36,7 @@ private const val BUNDLE_CHAT_ROOM_ID = "chat_room_id"
class PinnedMessagesFragment : Fragment(), PinnedMessagesView {
private lateinit var chatRoomId: String
private lateinit var adapter: ChatRoomAdapter
private val adapter = ChatRoomAdapter(enableActions = false)
@Inject
lateinit var presenter: PinnedMessagesPresenter
......@@ -68,7 +68,6 @@ class PinnedMessagesFragment : Fragment(), PinnedMessagesView {
override fun showPinnedMessages(pinnedMessages: List<BaseUiModel<*>>) {
ui {
if (recycler_view_pinned.adapter == null) {
adapter = ChatRoomAdapter(enableActions = false)
recycler_view_pinned.adapter = adapter
val linearLayoutManager = LinearLayoutManager(context)
......
......@@ -12,4 +12,4 @@ class GroupedPush {
// Map a hostname to a list of push messages that pertain to it.
val hostToPushMessageList = HashMap<String, MutableList<PushMessage>>()
}
\ No newline at end of file
}
......@@ -18,6 +18,7 @@ import androidx.core.app.NotificationManagerCompat
import androidx.core.app.RemoteInput
import android.text.Html
import android.text.Spanned
import androidx.core.content.ContextCompat
import chat.rocket.android.R
import chat.rocket.android.main.ui.MainActivity
import chat.rocket.android.server.domain.GetAccountInteractor
......@@ -36,10 +37,6 @@ import java.util.*
import java.util.concurrent.atomic.AtomicInteger
import javax.inject.Inject
/**
* Refer to: https://github.com/RocketChat/Rocket.Chat.Android/blob/9e846b7fde8fe0c74b9e0117c37ce49293308db5/app/src/main/java/chat/rocket/android/push/PushManager.kt
* for old source code.
*/
class PushManager @Inject constructor(
private val groupedPushes: GroupedPush,
private val manager: NotificationManager,
......@@ -80,7 +77,7 @@ class PushManager @Inject constructor(
showNotification(pushMessage)
} catch (ex: Exception) {
Timber.d(ex, "Error parsing PUSH message: $data")
Timber.e(ex, "Error parsing PUSH message: $data")
ex.printStackTrace()
}
}
......@@ -101,7 +98,7 @@ class PushManager @Inject constructor(
val groupTuple = getGroupForHost(host)
groupTuple.second.incrementAndGet()
val notIdListForHostname: MutableList<PushMessage>? = groupedPushes.hostToPushMessageList.get(host)
val notIdListForHostname: MutableList<PushMessage>? = groupedPushes.hostToPushMessageList[host]
if (notIdListForHostname == null) {
groupedPushes.hostToPushMessageList[host] = arrayListOf(pushMessage)
} else {
......@@ -365,14 +362,14 @@ class PushManager @Inject constructor(
val res = context.resources
val smallIcon = res.getIdentifier(
"rocket_chat_notification", "drawable", context.packageName)
with(this, {
with(this) {
setAutoCancel(true)
setShowWhen(true)
color = context.resources.getColor(R.color.colorPrimary)
color = ContextCompat.getColor(context, R.color.colorPrimary)
setDefaults(Notification.DEFAULT_ALL)
setSmallIcon(smallIcon)
setSound(alarmSound)
})
}
return this
}
}
......
......@@ -4,7 +4,6 @@ import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.retryIO
import chat.rocket.core.internal.rest.settings
import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.async
import kotlinx.coroutines.experimental.launch
import kotlinx.coroutines.experimental.withContext
import timber.log.Timber
......@@ -16,24 +15,61 @@ class RefreshSettingsInteractor @Inject constructor(
) {
private var settingsFilter = arrayOf(
LDAP_ENABLE, CAS_ENABLE, CAS_LOGIN_URL,
LDAP_ENABLE,
CAS_ENABLE,
CAS_LOGIN_URL,
ACCOUNT_REGISTRATION, ACCOUNT_LOGIN_FORM, ACCOUNT_PASSWORD_RESET, ACCOUNT_CUSTOM_FIELDS,
ACCOUNT_GOOGLE, ACCOUNT_FACEBOOK, ACCOUNT_GITHUB, ACCOUNT_LINKEDIN, ACCOUNT_METEOR,
ACCOUNT_TWITTER, ACCOUNT_WORDPRESS, ACCOUNT_GITLAB, ACCOUNT_GITLAB_URL,
ACCOUNT_REGISTRATION,
ACCOUNT_LOGIN_FORM,
ACCOUNT_PASSWORD_RESET,
ACCOUNT_CUSTOM_FIELDS,
ACCOUNT_GOOGLE,
ACCOUNT_FACEBOOK,
ACCOUNT_GITHUB,
ACCOUNT_LINKEDIN,
ACCOUNT_METEOR,
ACCOUNT_TWITTER,
ACCOUNT_GITLAB,
ACCOUNT_GITLAB_URL,
ACCOUNT_WORDPRESS,
ACCOUNT_WORDPRESS_URL,
SITE_URL, SITE_NAME, FAVICON_512, FAVICON_196, USE_REALNAME, ALLOW_ROOM_NAME_SPECIAL_CHARS,
FAVORITE_ROOMS, UPLOAD_STORAGE_TYPE, UPLOAD_MAX_FILE_SIZE, UPLOAD_WHITELIST_MIMETYPES,
HIDE_USER_JOIN, HIDE_USER_LEAVE,
HIDE_TYPE_AU, HIDE_MUTE_UNMUTE, HIDE_TYPE_RU, ALLOW_MESSAGE_DELETING,
ALLOW_MESSAGE_EDITING, ALLOW_MESSAGE_PINNING, ALLOW_MESSAGE_STARRING, SHOW_DELETED_STATUS, SHOW_EDITED_STATUS,
WIDE_TILE_310, STORE_LAST_MESSAGE, MESSAGE_READ_RECEIPT_ENABLED, MESSAGE_READ_RECEIPT_STORE_USERS)
SITE_URL,
SITE_NAME,
FAVICON_512,
FAVICON_196,
USE_REALNAME,
ALLOW_ROOM_NAME_SPECIAL_CHARS,
FAVORITE_ROOMS,
UPLOAD_STORAGE_TYPE,
UPLOAD_MAX_FILE_SIZE,
UPLOAD_WHITELIST_MIMETYPES,
HIDE_USER_JOIN,
HIDE_USER_LEAVE,
HIDE_TYPE_AU,
HIDE_MUTE_UNMUTE,
HIDE_TYPE_RU,
ALLOW_MESSAGE_DELETING,
ALLOW_MESSAGE_EDITING,
ALLOW_MESSAGE_PINNING,
ALLOW_MESSAGE_STARRING,
SHOW_DELETED_STATUS,
SHOW_EDITED_STATUS,
WIDE_TILE_310,
STORE_LAST_MESSAGE,
MESSAGE_READ_RECEIPT_ENABLED,
MESSAGE_READ_RECEIPT_STORE_USERS
)
suspend fun refresh(server: String) {
withContext(CommonPool) {
factory.create(server).let { client ->
val settings = retryIO(description = "settings", times = 5,
maxDelay = 5000, initialDelay = 300) {
val settings = retryIO(
description = "settings",
times = 5,
maxDelay = 5000,
initialDelay = 300
) {
client.settings(*settingsFilter)
}
repository.save(server, settings)
......
......@@ -19,9 +19,10 @@ const val ACCOUNT_GITHUB = "Accounts_OAuth_Github"
const val ACCOUNT_LINKEDIN = "Accounts_OAuth_Linkedin"
const val ACCOUNT_METEOR = "Accounts_OAuth_Meteor"
const val ACCOUNT_TWITTER = "Accounts_OAuth_Twitter"
const val ACCOUNT_WORDPRESS = "Accounts_OAuth_Wordpress"
const val ACCOUNT_GITLAB = "Accounts_OAuth_Gitlab"
const val ACCOUNT_GITLAB_URL = "API_Gitlab_URL"
const val ACCOUNT_WORDPRESS = "Accounts_OAuth_Wordpress"
const val ACCOUNT_WORDPRESS_URL = "API_Wordpress_URL"
const val SITE_URL = "Site_Url"
const val SITE_NAME = "Site_Name"
......@@ -71,6 +72,7 @@ fun PublicSettings.isTwitterAuthenticationEnabled(): Boolean = this[ACCOUNT_TWIT
fun PublicSettings.isGitlabAuthenticationEnabled(): Boolean = this[ACCOUNT_GITLAB]?.value == true
fun PublicSettings.gitlabUrl(): String? = this[ACCOUNT_GITLAB_URL]?.value as String?
fun PublicSettings.isWordpressAuthenticationEnabled(): Boolean = this[ACCOUNT_WORDPRESS]?.value == true
fun PublicSettings.wordpressUrl(): String? = this[ACCOUNT_WORDPRESS_URL]?.value as String?
fun PublicSettings.useRealName(): Boolean = this[USE_REALNAME]?.value == true
fun PublicSettings.useSpecialCharsOnRoom(): Boolean = this[ALLOW_ROOM_NAME_SPECIAL_CHARS]?.value == true
......
......@@ -2,7 +2,6 @@ package chat.rocket.android.server.infraestructure
import androidx.lifecycle.MutableLiveData
import chat.rocket.android.db.DatabaseManager
import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.common.model.BaseRoom
import chat.rocket.common.model.User
import chat.rocket.common.model.UserStatus
......
......@@ -3,6 +3,7 @@ package chat.rocket.android.util.extensions
import android.graphics.Color
import android.util.Patterns
import chat.rocket.common.model.Token
import okhttp3.HttpUrl
import timber.log.Timber
fun String.removeTrailingSlash(): String {
......@@ -64,4 +65,11 @@ fun String.parseColor(): Int {
fun String.userId(userId: String?): String? {
return userId?.let { this.replace(it, "") }
}
fun String.lowercaseUrl(): String? {
val httpUrl = HttpUrl.parse(this)
val newScheme = httpUrl?.scheme()?.toLowerCase()
return httpUrl?.newBuilder()?.scheme(newScheme)?.build()?.toString()
}
\ No newline at end of file
......@@ -7,15 +7,27 @@ import android.view.View
import chat.rocket.android.R
import timber.log.Timber
fun View.openTabbedUrl(url: Uri) {
fun View.openTabbedUrl(url: String) {
with(this) {
val uri = url.ensureScheme()
val tabsbuilder = CustomTabsIntent.Builder()
tabsbuilder.setToolbarColor(ResourcesCompat.getColor(context.resources, R.color.colorPrimary, context.theme))
val customTabsIntent = tabsbuilder.build()
try {
customTabsIntent.launchUrl(context, url)
customTabsIntent.launchUrl(context, uri)
} catch (ex: Exception) {
Timber.d(ex, "Unable to launch URL")
}
}
}
private fun String.ensureScheme(): Uri? {
// check if the URL starts with a http(s) scheme
val url = if (!this.matches(Regex("^([h|H][t|T][t|T][p|P]).*"))) {
"http://$this"
} else {
this
}
return Uri.parse(url.lowercaseUrl())
}
\ No newline at end of file
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="290dp"
android:height="40dp"
android:viewportWidth="290"
android:viewportHeight="40">
<path
android:fillColor="#428BBA"
android:fillType="evenOdd"
android:pathData="M2,0L288,0A2,2 0,0 1,290 2L290,38A2,2 0,0 1,288 40L2,40A2,2 0,0 1,0 38L0,2A2,2 0,0 1,2 0z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
<path
android:fillColor="#FFFFFFFF"
android:fillType="evenOdd"
android:pathData="M134.878,23.188L136.628,14.625L138.589,14.625L135.964,26L134.073,26L131.909,17.695L129.698,26L127.8,26L125.175,14.625L127.136,14.625L128.901,23.172L131.073,14.625L132.729,14.625L134.878,23.188ZM139.824,21.695C139.824,20.867 139.988,20.121 140.316,19.457C140.645,18.793 141.105,18.283 141.699,17.926C142.293,17.569 142.975,17.391 143.746,17.391C144.887,17.391 145.813,17.758 146.523,18.492C147.234,19.227 147.618,20.201 147.676,21.414L147.684,21.859C147.684,22.693 147.523,23.437 147.203,24.094C146.883,24.75 146.424,25.258 145.828,25.617C145.232,25.977 144.543,26.156 143.762,26.156C142.569,26.156 141.615,25.759 140.898,24.965C140.182,24.171 139.824,23.112 139.824,21.789L139.824,21.695ZM141.723,21.859C141.723,22.729 141.902,23.41 142.262,23.902C142.621,24.395 143.121,24.641 143.762,24.641C144.402,24.641 144.901,24.391 145.258,23.891C145.615,23.391 145.793,22.659 145.793,21.695C145.793,20.841 145.609,20.164 145.242,19.664C144.875,19.164 144.376,18.914 143.746,18.914C143.126,18.914 142.634,19.16 142.27,19.652C141.905,20.145 141.723,20.88 141.723,21.859ZM154.286,19.281C154.036,19.24 153.778,19.219 153.513,19.219C152.643,19.219 152.057,19.552 151.755,20.219L151.755,26L149.857,26L149.857,17.547L151.669,17.547L151.716,18.492C152.174,17.758 152.81,17.391 153.622,17.391C153.893,17.391 154.117,17.427 154.294,17.5L154.286,19.281ZM155.553,21.711C155.553,20.409 155.855,19.363 156.459,18.574C157.063,17.785 157.873,17.391 158.889,17.391C159.785,17.391 160.509,17.703 161.061,18.328L161.061,14L162.959,14L162.959,26L161.241,26L161.147,25.125C160.579,25.813 159.821,26.156 158.873,26.156C157.884,26.156 157.083,25.758 156.471,24.961C155.859,24.164 155.553,23.081 155.553,21.711ZM157.451,21.875C157.451,22.734 157.617,23.405 157.948,23.887C158.278,24.368 158.748,24.609 159.358,24.609C160.134,24.609 160.701,24.263 161.061,23.57L161.061,19.961C160.712,19.284 160.149,18.945 159.373,18.945C158.759,18.945 158.285,19.189 157.951,19.676C157.618,20.163 157.451,20.896 157.451,21.875ZM167.671,21.773L167.671,26L165.695,26L165.695,14.625L170.046,14.625C171.317,14.625 172.326,14.956 173.074,15.617C173.821,16.279 174.195,17.154 174.195,18.242C174.195,19.357 173.829,20.224 173.097,20.844C172.365,21.464 171.341,21.773 170.023,21.773L167.671,21.773ZM167.671,20.188L170.046,20.188C170.749,20.188 171.286,20.022 171.656,19.691C172.025,19.361 172.21,18.883 172.21,18.258C172.21,17.643 172.023,17.152 171.648,16.785C171.273,16.418 170.757,16.229 170.101,16.219L167.671,16.219L167.671,20.188ZM180.735,19.281C180.485,19.24 180.227,19.219 179.962,19.219C179.092,19.219 178.506,19.552 178.204,20.219L178.204,26L176.305,26L176.305,17.547L178.118,17.547L178.165,18.492C178.623,17.758 179.258,17.391 180.071,17.391C180.342,17.391 180.566,17.427 180.743,17.5L180.735,19.281ZM186.08,26.156C184.877,26.156 183.901,25.777 183.154,25.02C182.407,24.262 182.033,23.253 182.033,21.992L182.033,21.758C182.033,20.914 182.196,20.16 182.521,19.496C182.847,18.832 183.304,18.315 183.892,17.945C184.481,17.576 185.137,17.391 185.861,17.391C187.012,17.391 187.901,17.758 188.529,18.492C189.157,19.227 189.47,20.266 189.47,21.609L189.47,22.375L183.947,22.375C184.004,23.073 184.237,23.625 184.646,24.031C185.055,24.438 185.569,24.641 186.189,24.641C187.059,24.641 187.767,24.289 188.314,23.586L189.338,24.563C188.999,25.068 188.547,25.46 187.982,25.738C187.417,26.017 186.783,26.156 186.08,26.156ZM185.853,18.914C185.332,18.914 184.912,19.096 184.592,19.461C184.271,19.826 184.067,20.333 183.978,20.984L187.595,20.984L187.595,20.844C187.554,20.208 187.384,19.728 187.088,19.402C186.791,19.077 186.379,18.914 185.853,18.914ZM196.253,23.703C196.253,23.365 196.113,23.107 195.835,22.93C195.556,22.753 195.094,22.596 194.448,22.461C193.802,22.326 193.263,22.154 192.831,21.945C191.883,21.487 191.409,20.823 191.409,19.953C191.409,19.224 191.716,18.615 192.331,18.125C192.945,17.635 193.727,17.391 194.675,17.391C195.685,17.391 196.501,17.641 197.124,18.141C197.746,18.641 198.057,19.289 198.057,20.086L196.159,20.086C196.159,19.721 196.024,19.418 195.753,19.176C195.482,18.934 195.123,18.813 194.675,18.813C194.258,18.813 193.918,18.909 193.655,19.102C193.392,19.294 193.261,19.552 193.261,19.875C193.261,20.167 193.383,20.393 193.628,20.555C193.873,20.716 194.367,20.879 195.112,21.043C195.857,21.207 196.442,21.402 196.866,21.629C197.291,21.855 197.606,22.128 197.811,22.445C198.017,22.763 198.12,23.148 198.12,23.602C198.12,24.362 197.805,24.978 197.175,25.449C196.544,25.921 195.719,26.156 194.698,26.156C194.005,26.156 193.388,26.031 192.847,25.781C192.305,25.531 191.883,25.188 191.581,24.75C191.279,24.312 191.128,23.841 191.128,23.336L192.972,23.336C192.998,23.784 193.167,24.129 193.479,24.371C193.792,24.613 194.206,24.734 194.722,24.734C195.222,24.734 195.602,24.639 195.862,24.449C196.123,24.259 196.253,24.01 196.253,23.703ZM205.082,23.703C205.082,23.365 204.943,23.107 204.664,22.93C204.385,22.753 203.923,22.596 203.277,22.461C202.632,22.326 202.092,22.154 201.66,21.945C200.712,21.487 200.238,20.823 200.238,19.953C200.238,19.224 200.546,18.615 201.16,18.125C201.775,17.635 202.556,17.391 203.504,17.391C204.514,17.391 205.331,17.641 205.953,18.141C206.576,18.641 206.887,19.289 206.887,20.086L204.988,20.086C204.988,19.721 204.853,19.418 204.582,19.176C204.311,18.934 203.952,18.813 203.504,18.813C203.087,18.813 202.747,18.909 202.484,19.102C202.221,19.294 202.09,19.552 202.09,19.875C202.09,20.167 202.212,20.393 202.457,20.555C202.702,20.716 203.197,20.879 203.941,21.043C204.686,21.207 205.271,21.402 205.695,21.629C206.12,21.855 206.435,22.128 206.641,22.445C206.846,22.763 206.949,23.148 206.949,23.602C206.949,24.362 206.634,24.978 206.004,25.449C205.374,25.921 204.548,26.156 203.527,26.156C202.835,26.156 202.217,26.031 201.676,25.781C201.134,25.531 200.712,25.188 200.41,24.75C200.108,24.312 199.957,23.841 199.957,23.336L201.801,23.336C201.827,23.784 201.996,24.129 202.309,24.371C202.621,24.613 203.035,24.734 203.551,24.734C204.051,24.734 204.431,24.639 204.691,24.449C204.952,24.259 205.082,24.01 205.082,23.703Z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
<path
android:fillColor="#FFFFFFFF"
android:fillType="nonZero"
android:pathData="M104.26,11C99.146,11 95,15.146 95,20.26C95,25.375 99.146,29.521 104.26,29.521C109.375,29.521 113.521,25.375 113.521,20.26C113.521,15.146 109.375,11 104.26,11M104.26,11.556C105.995,11.552 107.69,12.07 109.127,13.042C110.054,13.669 110.852,14.467 111.479,15.394C112.451,16.83 112.969,18.526 112.965,20.26C112.969,21.995 112.451,23.69 111.479,25.127C110.852,26.054 110.054,26.852 109.127,27.479C107.69,28.451 105.995,28.969 104.26,28.965C102.526,28.969 100.83,28.451 99.394,27.479C98.467,26.852 97.669,26.054 97.042,25.127C96.07,23.69 95.552,21.995 95.556,20.26C95.552,18.526 96.07,16.83 97.042,15.394C97.669,14.467 98.467,13.669 99.393,13.042C100.83,12.07 102.526,11.552 104.26,11.556"
android:strokeWidth="1"
android:strokeColor="#00000000" />
<path
android:fillColor="#FFFFFFFF"
android:fillType="nonZero"
android:pathData="M111.032,16.558C111.066,16.804 111.084,17.068 111.084,17.351C111.084,18.134 110.937,19.014 110.497,20.115L108.14,26.93C110.516,25.549 111.978,23.008 111.977,20.26C111.979,18.966 111.654,17.693 111.032,16.559L111.032,16.558ZM104.396,20.935L102.08,27.663C103.635,28.121 105.294,28.078 106.823,27.54C106.802,27.506 106.783,27.471 106.768,27.434L104.396,20.935ZM109.47,19.871C109.47,18.917 109.128,18.257 108.834,17.743C108.443,17.106 108.076,16.569 108.076,15.933C108.076,15.223 108.613,14.563 109.372,14.563C109.406,14.563 109.438,14.567 109.472,14.569C108.05,13.264 106.19,12.541 104.26,12.544C101.662,12.543 99.238,13.851 97.813,16.022C97.994,16.028 98.165,16.032 98.309,16.032C99.116,16.032 100.366,15.934 100.366,15.934C100.782,15.909 100.831,16.52 100.415,16.57C100.415,16.57 99.997,16.618 99.532,16.643L102.343,25.002L104.031,19.937L102.829,16.643C102.414,16.618 102.02,16.57 102.02,16.57C101.604,16.545 101.652,15.909 102.068,15.934C102.068,15.934 103.342,16.032 104.101,16.032C104.908,16.032 106.158,15.934 106.158,15.934C106.574,15.909 106.623,16.52 106.207,16.57C106.207,16.57 105.788,16.618 105.324,16.643L108.113,24.938L108.908,22.415C109.263,21.313 109.47,20.531 109.47,19.872L109.47,19.871ZM96.544,20.26C96.544,23.217 98.233,25.915 100.893,27.205L97.211,17.12C96.77,18.108 96.543,19.178 96.544,20.26Z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>
<resources>
<!-- Titles -->
<string name="title_sign_in_your_server">Anmelden am Server</string>
<string name="title_log_in">Anmelden</string>
<string name="title_register_username">Registriere Benutzernamen</string>
<string name="title_reset_password">Passwort zurücksetzen</string>
<string name="title_sign_up">registrieren</string>
<string name="title_authentication">Login Daten prüfen</string>
<string name="title_legal_terms">Legal Terms</string>
<string name="title_chats">Chats</string>
<string name="title_profile">Profil</string>
<string name="title_members">Benutzer (%d)</string>
<string name="title_settings">Einstellungen</string>
<string name="title_password">Ändere Passwort</string>
<string name="title_update_profile">Update Profil</string>
<string name="title_about">Über</string>
<string name="title_create_channel">Erstelle Raum</string>
<!-- Actions -->
<string name="action_connect">Verbinde</string>
<string name="action_use_this_username">Benutze den Benutzernamen</string>
<string name="action_login_or_sign_up">Klick diesen Knopf um sich anzumelden oder einen Account zu erstellen</string>
<string name="action_terms_of_service">Nutzungsbedingungen</string>
<string name="action_privacy_policy">Datenschutz</string>
<string name="action_search">Suche</string>
<string name="action_update">Updaten</string>
<string name="action_settings">Einstellungen</string>
<string name="action_create_channel">Erstelle Raum</string>
<string name="action_create">Erstelle</string>
<string name="action_logout">Abmelden</string>
<string name="action_files">Dateien</string>
<string name="action_confirm_password">Bestätige Passwort Änderung</string>
<string name="action_join_chat">Trete Chat bei</string>
<string name="action_add_account">Erstelle Account</string>
<string name="action_online">Online</string>
<string name="action_away">Abwesend</string>
<string name="action_busy">Beschäftigt</string>
<string name="action_invisible">Unsichtbar</string>
<string name="action_drawing">Zeichnung</string>
<string name="action_save_to_gallery">Sichern in Gallerie</string>
<!-- Settings List -->
<string-array name="settings_actions">
<item name="item_password">Ändere Passwort</item>
<item name="item_password">Über</item>
</string-array>
<!-- Regular information messages -->
<string name="msg_generic_error">Entschuldigung, ein Fehler ist aufgetreten, bitte versuchen Sie es noch einmal.</string>
<string name="msg_no_data_to_display">Keine Anzeigedaten vorhanden</string>
<string name="msg_profile_update_successfully">Profil update erfolgreich</string>
<string name="msg_username">Benutzername</string>
<string name="msg_username_or_email">Benutzername oder E-Mail</string>
<string name="msg_password">Passwort</string>
<string name="msg_name">Name</string>
<string name="msg_email">E-Mail</string>
<string name="msg_avatar_url">Avatar URL</string>
<string name="msg_or_continue_using_social_accounts">Oder weiter mit einem Social Account</string>
<string name="msg_new_user">Neuer Benutzer? %1$s</string>
<string name="msg_forgot_password">Passwort vergessen? %1$s</string>
<string name="msg_reset">Zurücksetzen</string>
<string name="msg_check_your_email_to_reset_your_password">E-Mail gesendet! Prüfe dein E-Mail Posteingang um dein Passwort zurückzusetzen.</string>
<string name="msg_invalid_email">Bitte eine korrekte E-Mail Adresse eingeben</string>
<string name="msg_new_user_agreement">Beim weitergehen akzeptieren Sie usere\n%1$s und %2$s</string>
<string name="msg_2fa_code">2FA Code</string>
<!-- <string name="msg_more_than_ninety_nine_unread_messages" translatable="false">99+</string> -->
<string name="msg_yesterday">Gestern</string>
<string name="msg_today">Heute</string>
<string name="msg_message">Nachricht</string>
<string name="msg_this_room_is_read_only">Dieser Raum ist nur lesen</string>
<string name="msg_invalid_2fa_code">Falscher 2FA Code</string>
<string name="msg_invalid_file">Falsche Datei</string>
<string name="msg_invalid_server_url">Falsche Server URL</string>
<string name="msg_content_description_log_in_using_facebook">Login mit Facebook</string>
<string name="msg_content_description_log_in_using_github">Login mit Github</string>
<string name="msg_content_description_log_in_using_google">Login mit Google</string>
<string name="msg_content_description_log_in_using_linkedin">Login mit Linkedin</string>
<string name="msg_content_description_log_in_using_meteor">Login mit Meteor</string>
<string name="msg_content_description_log_in_using_twitter">Login mit Twitter</string>
<string name="msg_content_description_log_in_using_gitlab">Login mit Gitlab</string>
<string name="msg_content_description_send_message">Sende Nachricht</string>
<string name="msg_content_description_show_attachment_options">Zeige Anhang Optionen</string>
<string name="msg_you">Du</string>
<string name="msg_unknown">Unbekannt</string>
<string name="msg_email_address">E-Mail Addresse</string>
<string name="msg_utc_offset">UTC Offset</string>
<string name="msg_new_password">Neues Passwort eingeben</string>
<string name="msg_confirm_password">Bestätige neues Passwort</string>
<string name="msg_channel_name">Raum Name</string>
<string name="msg_search">Suche</string>
<string name="msg_unread_messages">Ungelesene Nachrichten</string>
<string name="msg_preview_video">Video</string>
<string name="msg_preview_audio">Audio</string>
<string name="msg_preview_photo">Bild</string>
<string name="msg_preview_file">Datei</string>
<string name="msg_no_messages_yet">Noch keine Nachrichten</string>
<string name="msg_version">Version %1$s</string>
<string name="msg_build">Build %1$d</string>
<string name="msg_ok">OK</string>
<string name="msg_update_app_version_in_order_to_continue">Server Version veraltet. Bitte kontaktieren Sie ihren Server Administrator.</string>
<string name="msg_ver_not_recommended">
Die Server Version scheint älter als die empfolene Version %1$s zu sein.\nSie können sich trotzdem einloggen, aber es kann zu einem unerwartetem Verhalten kommen.</string>
<string name="msg_ver_not_minimum">
Die Server Version scheint älter als die minimale Version %1$s zu sein.\nBitte updaten Sie Ihren Server um sich einloggen zu können!
</string>
<string name="msg_no_chat_title">Keine Chat Nachrichten</string>
<string name="msg_no_chat_description">Starte die Konversation um Ihre \nNachrichten hier zu sehen.</string>
<string name="msg_proceed">WEITER</string>
<string name="msg_cancel">ABBRUCH</string>
<string name="msg_warning">WARNUNG</string>
<string name="msg_http_insecure">Bei HTTP verbinden Sie sich usicher zu einem Server. Wir empfehlen HTTPS zu benutzen.</string>
<string name="msg_error_checking_server_version">Ein Fehler ist aufgetreten beim prüfen der Server Version, bitte versuchen Sie es noch einmal</string>
<string name="msg_invalid_server_protocol">Das ausgewählte Protokoll wird vom Server nicht akzeptiert, versuchen Sie HTTPS</string>
<string name="msg_image_saved_successfully">Bild wurde in der Gallerie gespeichert</string>
<string name="msg_image_saved_failed">Bild speichern fehlgeschlagen</string>
<string name="msg_edited">(bearbeitet)</string>
<string name="msg_and">\u0020und\u0020</string>
<string name="msg_is_typing">\u0020 schreibt…</string>
<string name="msg_are_typing">\u0020 schreiben…</string>
<string name="msg_several_users_are_typing">Mehrere Benutzer schreiben…</string>
<string name="msg_no_search_found">Kein Ergebnis gefunden</string>
<string name="msg_log_out">Ausloggen…</string>
<string name="msg_upload_file">Lade Datei hoch</string>
<string name="msg_file_description">Datei Beschreibung</string>
<string name="msg_send">Sende</string>
<string name="msg_sent_attachment">Sende Anhang</string>
<!-- Create channel messages -->
<string name="msg_private_channel">Privat</string>
<string name="msg_public_channel">Öffentlich</string>
<string name="msg_private_channel_description">Nur Sie und eingeladene Benutzer haben Zugriff auf den Raum</string>
<string name="msg_public_channel_description">Jeder hat Zugriff auf diesen Raum</string>
<string name="msg_ready_only_channel">Nur lesen Raum</string>
<string name="msg_ready_only_channel_description">Nur der Admin kann neue Nachrichten schreiben</string>
<string name="msg_invite_members">Lade Benutzer in diesen Raum ein</string>
<string name="msg_member_already_added">Sie haben diesen Benutzer bereits selektiert</string>
<string name="msg_member_not_found">Benutzer nicht gefunden</string>
<string name="msg_channel_created_successfully">Raum erfolgreich erstellt</string>
<string name="msg_message_copied">Nachricht kopiert</string>
<string name="msg_delete_message">Lösche Nachricht</string>
<string name="msg_delete_description">Sind Sie sicher, dass Sie diese Nachricht löschen wollen?</string>
<!-- System messages -->
<string name="message_room_name_changed">Raum Namen geändert zu: %1$s von %2$s</string>
<string name="message_user_added_by">Benutzer %1$s hinzugefügt von %2$s</string>
<string name="message_user_removed_by">Benutzer %1$s entfernt von %2$s</string>
<string name="message_user_left">Hat den Raum verlassen.</string>
<string name="message_user_joined_channel">Hat den Raum betreten.</string>
<string name="message_welcome">Willkommen %s</string>
<string name="message_removed">Nachricht entfernt</string>
<string name="message_pinned">Nachricht angeheftet:</string>
<string name="message_muted">Benutzer %1$s stumm geschaltet von %2$s</string>
<string name="message_unmuted">Benutzer %1$s nicht mehr stumm geschaltet von %2$s</string>
<string name="message_role_add">%1$s wurde gesetzt %2$s von %3$s</string>
<string name="message_role_removed">%1$s ist nicht länger %2$s von %3$s</string>
<string name="message_credentials_saved_successfully">Login-Daten erfolgreich gespeichert</string>
<!-- Message actions -->
<string name="action_msg_reply">Antworten</string>
<string name="action_msg_info">Nachrichten Info</string>
<string name="action_msg_edit">Bearbeiten</string>
<string name="action_msg_copy">Kopieren</string>
<string name="action_msg_quote">Zitieren</string>
<string name="action_msg_delete">Löschen</string>
<string name="action_msg_pin">Nachricht anheften</string>
<string name="action_msg_unpin">Nachricht nicht mehr anheften</string>
<string name="action_msg_star">Nachricht favorisieren</string>
<string name="action_msg_unstar">Nachricht nicht mehr favorisieren</string>
<string name="action_msg_share">Teilen</string>
<string name="action_title_editing">Nachricht bearbeiten</string>
<string name="action_msg_add_reaction">Reaktion hinzufügen</string>
<string name="action_share">Teilen</string>
<!-- Permission messages -->
<string name="permission_editing_not_allowed">Bearbeiten nicht erlaubt</string>
<string name="permission_deleting_not_allowed">Löschen nicht erlaubt</string>
<string name="permission_pinning_not_allowed">Anheften nicht erlaubt</string>
<string name="permission_starring_not_allowed">Favorisieren nicht erlaubt</string>
<!-- Search message -->
<string name="title_search_message">Suche Nachricht</string>
<!-- Favorite/Unfavorite chat room -->
<string name="title_favorite_chat">Chat favorisieren</string>
<string name="title_unfavorite_chat">Chat nicht favorisieren</string>
<!-- Members List -->
<string name="title_members_list">Benutzer</string>
<!-- Mentions -->
<string name="msg_mentions">Erwähnungen</string>
<string name="msg_no_mention">Keine Erwähnung</string>
<string name="msg_all_the_mentions_appear_here">Alle Erwähnungen\nerscheinen hier</string>
<!-- Pinned Messages -->
<string name="title_pinned_messages">Angeheftete Nachrichten</string>
<string name="no_pinned_messages">Keine angehefteten Nachrichten vorhanden</string>
<string name="no_pinned_description">Alle angehefteten Nachrichten\nerscheinen hier</string>
<!-- Favorite Messages -->
<string name="title_favorite_messages">Favorisierte Nachrichten</string>
<string name="no_favorite_messages">Keine favorisierten Nachrichten vorhanden</string>
<string name="no_favorite_description">Alle favorisierten Nachrichten\nerscheinen hier</string>
<!-- Files -->
<string name="title_files">Dateien</string>
<string name="title_files_total">Dateien (%d)</string>
<string name="msg_no_files">Keine Dateien vorhanden</string>
<string name="msg_all_files_appear_here">Alle Dateien erscheinen hier</string>
<!-- Upload Messages -->
<string name="max_file_size_exceeded">Dateigröße von %1$d Bytes überschreitet maximale Hochladegröße von %2$d Bytes</string>
<!-- Socket status -->
<string name="status_connected">Verbunden</string>
<string name="status_disconnected">Getrennt</string>
<string name="status_connecting">Verbinde</string>
<string name="status_authenticating">Authentifiziere</string>
<string name="status_disconnecting">Trenne</string>
<string name="status_waiting">Verbinde in %d Sekunden</string>
<!--Suggestions-->
<string name="suggest_all_description">Benachrichtige alle in diesem Raum</string>
<string name="suggest_here_description">Benachrichtige aktive Benutzer in diesem Raum</string>
<!-- Slash Commands -->
<string name="Slash_Gimme_Description">Zeige ༼ つ ◕_◕ ༽つ vor deiner Nachricht</string>
<string name="Slash_LennyFace_Description">Zeige ( ͡° ͜ʖ ͡°) nach deiner Nachricht</string>
<string name="Slash_Shrug_Description">Zeige ¯\_(ツ)_/¯ nach deiner Nachricht</string>
<string name="Slash_Tableflip_Description">Zeige (╯°□°)╯︵ ┻━┻</string>
<string name="Slash_TableUnflip_Description">Zeige ┬─┬ ノ( ゜-゜ノ)</string>
<string name="Create_A_New_Channel">Erstelle einen neuen Raum</string>
<string name="Show_the_keyboard_shortcut_list">Zeige Tasterturkürzel</string>
<string name="Invite_user_to_join_channel_all_from">Lade alle Benutzer von [#channel] in diesen Raum ein</string>
<string name="Invite_user_to_join_channel_all_to">Lade alle Benutzer von diesem Raum in [#channel] ein</string>
<string name="Archive">Archivieren</string>
<string name="Remove_someone_from_room">Entferne jemanden vom Raum</string>
<string name="Leave_the_current_channel">Verlasse diesen Raum</string>
<string name="Displays_action_text">Zeige Aktions Text</string>
<string name="Direct_message_someone">Schicke eine Direktnachricht an jemanden</string>
<string name="Mute_someone_in_room">Schalte jemanden im Raum auf Stumm</string>
<string name="Unmute_someone_in_room">Schalte jemanden im Raum nicht mehr auf Stumm</string>
<string name="Invite_user_to_join_channel">Lade jemanden in diesen Raum ein</string>
<string name="Unarchive">Dearchivieren</string>
<string name="Join_the_given_channel">Trete diesem Raum bei</string>
<string name="Guggy_Command_Description">Erstelle ein Gif, basierend auf den eingegebenen Text</string>
<string name="Slash_Topic_Description">Setze Topic</string>
<!-- Emoji message-->
<string name="msg_no_recent_emoji">Keine letzten Emojis</string>
<string name="alert_title_default_skin_tone">Standart Hautton</string>
<!-- Sorting and grouping-->
<string name="menu_chatroom_sort">Sortiere</string>
<string name="dialog_sort_title">Sortieren nach</string>
<string name="dialog_sort_by_alphabet">Alphabetisch</string>
<string name="dialog_sort_by_activity">Aktivität</string>
<string name="dialog_group_by_type">Räume nach Typ</string>
<string name="dialog_group_favourites">Räume nach Favoriten</string>
<string name="chatroom_header">Kopf</string>
<!--ChatRooms Headers-->
<string name="header_channel">Räume</string>
<string name="header_private_groups">Private Räume</string>
<string name="header_direct_messages">Direkt Nachrichten</string>
<string name="header_live_chats">Live Chats</string>
<string name="header_unknown">Unbekannt</string>
<!--Notifications-->
<string name="share_label">Bearbeite geteilte Nachricht</string>
<string name="notif_action_reply_hint">Antworten</string>
<string name="notif_error_sending">Antworten fehlgeschlagen. Bitte versuchen Sie es noch einmal.</string>
<string name="notif_success_sending">Nachricht gesendet nach %1$s!</string>
<string name="read_by">Gelesen von</string>
<string name="message_information_title">Nachricht Information</string>
</resources>
\ No newline at end of file
......@@ -77,6 +77,7 @@
<string name="msg_content_description_log_in_using_meteor">Inicia sesión usando Meteor</string>
<string name="msg_content_description_log_in_using_twitter">Inicia sesión usando Twitter</string>
<string name="msg_content_description_log_in_using_gitlab">Inicia sesión usando Gitlab</string>
<string name="msg_content_description_log_in_using_wordprees">Login using WordPress</string> <!-- TODO Translate-->
<string name="msg_content_description_send_message">Enviar mensaje</string>
<string name="msg_content_description_show_attachment_options">Mostrar opciones de archivo adjunto</string>
<string name="msg_you"></string>
......@@ -262,6 +263,7 @@
<string name="header_unknown">Desconocido</string>
<!--Notifications-->
<string name="share_label">Edita mensaje compartido</string>
<string name="notif_action_reply_hint">RESPUESTA</string>
<string name="notif_error_sending">La respuesta ha fallado. Inténtalo de nuevo.</string>
<string name="notif_success_sending">Mensaje enviado a %1$s!</string>
......@@ -272,4 +274,5 @@
<string name="msg_file_description">Descripción del archivo</string>
<string name="msg_send">Enviar</string>
<string name="msg_sent_attachment">Envió un archivo</string>
</resources>
......@@ -85,6 +85,7 @@
<string name="msg_content_description_log_in_using_meteor">Connectez-vous en utilisant Meteor</string>
<string name="msg_content_description_log_in_using_twitter">Connectez-vous en utilisant Twitter</string>
<string name="msg_content_description_log_in_using_gitlab">Connectez-vous en utilisant Gitlab</string>
<string name="msg_content_description_log_in_using_wordprees">Login using WordPress</string> <!-- TODO Translate-->
<string name="msg_content_description_send_message">Envoyer message</string>
<string name="msg_content_description_show_attachment_options">Afficher les options de fichiers</string>
<string name="msg_you">Toi</string>
......
......@@ -79,6 +79,7 @@
<string name="msg_content_description_log_in_using_meteor">Meteor द्वारा लॉगिन करें</string>
<string name="msg_content_description_log_in_using_twitter">Twitter द्वारा लॉगिन करें</string>
<string name="msg_content_description_log_in_using_gitlab">Gitlab द्वारा लॉगिन करें</string>
<string name="msg_content_description_log_in_using_wordprees">Login using WordPress</string> <!-- TODO Translate-->
<string name="msg_content_description_send_message">मेसेज भेजें</string>
<string name="msg_content_description_show_attachment_options">अटैचमेंट विकल्प दिखाएं</string>
<string name="msg_you">आप</string>
......
......@@ -78,6 +78,7 @@
<string name="msg_content_description_log_in_using_meteor">Fazer login através do Meteor</string>
<string name="msg_content_description_log_in_using_twitter">Fazer login através do Twitter</string>
<string name="msg_content_description_log_in_using_gitlab">Fazer login através do Gitlab</string>
<string name="msg_content_description_log_in_using_wordprees">Fazer login através do WordPress</string>
<string name="msg_content_description_send_message">Enviar mensagem</string>
<string name="msg_content_description_show_attachment_options">Mostrar opções de anexo</string>
<string name="msg_you">Você</string>
......
......@@ -37,7 +37,7 @@
<string name="action_away">Отошел</string>
<string name="action_busy">Занят</string>
<string name="action_invisible">Невидимый</string>
<string name="action_drawing">малюнок</string>
<string name="action_drawing">Рисование</string>
<string name="action_save_to_gallery">Сохранить в галерею</string>
<!-- Settings List -->
......@@ -65,7 +65,7 @@
<string name="msg_new_user_agreement">Продолжая, вы соглашаетесь с нашими\n%1$s и %2$s</string>
<string name="msg_2fa_code">Код 2FA</string>
<string name="msg_yesterday">Вчера</string>
<string name="msg_today">Сьогодні</string>
<string name="msg_today">Сегодня</string>
<string name="msg_message">Сообщение</string>
<string name="msg_this_room_is_read_only">Этот канал только для чтения</string>
<string name="msg_invalid_2fa_code">Неверный код 2FA</string>
......@@ -78,6 +78,7 @@
<string name="msg_content_description_log_in_using_meteor">Войти используя Meteor</string>
<string name="msg_content_description_log_in_using_twitter">Войти используя Twitter</string>
<string name="msg_content_description_log_in_using_gitlab">Войти используя Gitlab</string>
<string name="msg_content_description_log_in_using_wordprees">Login using WordPress</string> <!-- TODO Translate-->
<string name="msg_content_description_send_message">Отправить сообщение</string>
<string name="msg_content_description_show_attachment_options">Показать параметры вложения</string>
<string name="msg_you">Вы</string>
......@@ -121,7 +122,7 @@
<string name="msg_upload_file">Загрузить файл</string>
<string name="msg_file_description">Описание файла</string>
<string name="msg_send">послать</string>
<string name="msg_sent_attachment">Надіслано вкладення</string>
<string name="msg_sent_attachment">Отправить вложение</string>
<string name="msg_delete_message">Удалить сообщение</string>
<string name="msg_delete_description">Вы уверены, что хотите удалить это сообщение?</string>
<string name="msg_channel_name">Название канала</string>
......@@ -168,7 +169,7 @@
<string name="action_msg_share">Поделиться</string>
<string name="action_title_editing">Редактирование сообщения</string>
<string name="action_msg_add_reaction">Добавить реакцию</string>
<string name="action_share">Поділитися</string>
<string name="action_share">Поделиться</string>
<!-- Permission messages -->
<string name="permission_editing_not_allowed">Редактирование запрещено</string>
......@@ -266,11 +267,11 @@
<string name="header_unknown">Неизвестные</string>
<!--Notifications-->
<string name="share_label">Редагування спільного повідомлення</string>
<string name="share_label">Изменить общее сообщение</string>
<string name="notif_action_reply_hint">ОТВЕТИТЬ</string>
<string name="notif_error_sending">Ошибка ответа. Пожалуйста, попробуйте еще раз.</string>
<string name="notif_success_sending">Сообщение отправлено %1$s!</string>
<string name="read_by">Прочитано</string>
<string name="message_information_title">Інформація про повідомлення</string>
<string name="message_information_title">Информация о сообщении</string>
<string name="msg_log_out">Выход…</string>
</resources>
......@@ -83,6 +83,7 @@
<string name="msg_content_description_log_in_using_meteor">Login using Meteor</string>
<string name="msg_content_description_log_in_using_twitter">Login using Twitter</string>
<string name="msg_content_description_log_in_using_gitlab">Login using Gitlab</string>
<string name="msg_content_description_log_in_using_wordprees">Login using WordPress</string>
<string name="msg_content_description_send_message">Send message</string>
<string name="msg_content_description_show_attachment_options">Show attachment options</string>
<string name="msg_you">You</string>
......
......@@ -13,7 +13,7 @@ buildscript {
classpath 'com.android.tools.build:gradle:3.3.0-alpha04'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}"
classpath "org.jetbrains.dokka:dokka-gradle-plugin:${versions.dokka}"
classpath 'com.google.gms:google-services:3.2.0'
classpath 'com.google.gms:google-services:4.0.2'
classpath 'io.fabric.tools:gradle:1.25.4'
// NOTE: Do not place your application dependencies here; they belong
......@@ -34,4 +34,4 @@ allprojects {
task clean(type: Delete) {
delete rootProject.buildDir
}
\ No newline at end of file
}
......@@ -18,6 +18,11 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
packagingOptions {
exclude 'META-INF/core.kotlin_module'
exclude 'META-INF/main.kotlin_module'
}
}
dependencies {
......
......@@ -5,12 +5,12 @@ ext {
compileSdk : 28,
targetSdk : 28,
minSdk : 21,
buildTools : '28.0.0-rc2',
buildTools : '28.0.1',
dokka : '0.9.16',
// For app
kotlin : '1.2.51',
coroutine : '0.23.1',
coroutine : '0.24.0',
appCompat : '1.0.0-beta01',
recyclerview : '1.0.0-beta01',
......@@ -21,9 +21,9 @@ ext {
dagger : '2.16',
firebase : '15.0.0',
playServices : '15.0.0',
exoPlayer : '2.6.0',
flexbox : '0.3.2',
playServices : '15.0.1',
exoPlayer : '2.8.2',
flexbox : '1.0.0',
material : '1.0.0-beta01',
room : '2.0.0-beta01',
......@@ -33,30 +33,30 @@ ext {
rxAndroid : '2.0.2',
moshi : '1.6.0',
okhttp : '3.10.0',
okhttp : '3.11.0',
timber : '4.7.0',
threeTenABP : '1.0.5',
rxBinding : '2.0.0',
timber : '4.7.1',
threeTenABP : '1.1.0',
rxBinding : '2.1.1',
fresco : '1.9.0',
fresco : '1.10.0',
kotshi : '1.0.2',
kotshi : '1.0.4',
frescoImageViewer : '0.5.1',
markwon : '1.0.6',
markwon : '1.1.0',
aVLoadingIndicatorView: '2.1.3',
// For wearable
wear : '2.3.0',
playServicesWearable : '15.0.1',
supportWearable : '26.1.0',
supportWearable : '27.1.1',
// For testing
junit : '4.12',
truth : '0.36',
espresso : '3.1.0-alpha2',
mockito : '2.10.0'
truth : '0.42',
espresso : '3.1.0-alpha4',
mockito : '2.21.0'
]
libraries = [
kotlin : "org.jetbrains.kotlin:kotlin-stdlib-jdk8:${versions.kotlin}",
......@@ -121,10 +121,10 @@ ext {
wearSupport : "com.android.support:wear:${versions.supportWearable}",
// For testing
junit : "junit:junit:$versions.junit",
junit : "junit:junit:${versions.junit}",
espressoCore : "androidx.test.espresso:espresso-core:${versions.espresso}",
espressoIntents : "androidx.test.espresso:espresso-intents:${versions.espresso}",
roomTest : "android.arch.persistence.room:testing:${versions.room}",
truth : "com.google.truth:truth:$versions.truth"
truth : "com.google.truth:truth:${versions.truth}"
]
}
\ No newline at end of file
}
#Sat Aug 04 13:02:54 IST 2018
#Fri May 18 17:08:16 IST 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-all.zip
distributionSha256Sum=9af7345c199f1731c187c96d3fe3d31f5405192a42046bafa71d846c3d9adacb
distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-rc-2-all.zip
distributionSha256Sum=9bc16f929e5920a9a27fef22c65a8ab8140d705e418de0159ca6eb7f08a74200
......@@ -2,7 +2,7 @@ apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 26
compileSdkVersion 27
buildToolsVersion versions.buildTools
defaultConfig {
......
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