Commit 0e76e49a authored by Samer Alabi's avatar Samer Alabi

Merge branch 'develop' into room-info

parents 771dadee 4c9ae11a
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name="rocket_chat_images"
path="Android/data/chat.rocket.android.dev/files/Pictures" />
</paths>
\ No newline at end of file
...@@ -110,6 +110,15 @@ ...@@ -110,6 +110,15 @@
<meta-data <meta-data
android:name="io.fabric.ApiKey" android:name="io.fabric.ApiKey"
android:value="12ac6e94f850aaffcdff52001af77ca415d06a43" /> android:value="12ac6e94f850aaffcdff52001af77ca415d06a43" />
</application>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="chat.rocket.android.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</application>
</manifest> </manifest>
package chat.rocket.android.authentication.server.ui package chat.rocket.android.authentication.server.ui
import android.os.Bundle import android.os.Bundle
import android.text.SpannableStringBuilder
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
...@@ -11,6 +12,7 @@ import android.widget.ScrollView ...@@ -11,6 +12,7 @@ import android.widget.ScrollView
import android.widget.Toast import android.widget.Toast
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.net.toUri import androidx.core.net.toUri
import androidx.core.text.color
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
...@@ -51,6 +53,8 @@ class ServerFragment : Fragment(), ServerView { ...@@ -51,6 +53,8 @@ class ServerFragment : Fragment(), ServerView {
lateinit var analyticsManager: AnalyticsManager lateinit var analyticsManager: AnalyticsManager
private var deepLinkInfo: LoginDeepLinkInfo? = null private var deepLinkInfo: LoginDeepLinkInfo? = null
private var protocol = "https://" private var protocol = "https://"
private var isDomainAppended = false
private var appendedText = ""
private lateinit var serverUrlDisposable: Disposable private lateinit var serverUrlDisposable: Disposable
private val layoutListener = ViewTreeObserver.OnGlobalLayoutListener { private val layoutListener = ViewTreeObserver.OnGlobalLayoutListener {
if (KeyboardHelper.isSoftKeyboardShown(scroll_view.rootView)) { if (KeyboardHelper.isSoftKeyboardShown(scroll_view.rootView)) {
...@@ -131,7 +135,7 @@ class ServerFragment : Fragment(), ServerView { ...@@ -131,7 +135,7 @@ class ServerFragment : Fragment(), ServerView {
} }
private fun setupOnClickListener() = private fun setupOnClickListener() =
ui { _ -> ui {
button_connect.setOnClickListener { button_connect.setOnClickListener {
presenter.checkServer("$protocol${text_server_url.textContent.sanitize()}") presenter.checkServer("$protocol${text_server_url.textContent.sanitize()}")
} }
...@@ -244,17 +248,42 @@ class ServerFragment : Fragment(), ServerView { ...@@ -244,17 +248,42 @@ class ServerFragment : Fragment(), ServerView {
private fun subscribeEditText() { private fun subscribeEditText() {
serverUrlDisposable = text_server_url.asObservable() serverUrlDisposable = text_server_url.asObservable()
.filter { it.isNotBlank() } .filter { it.isNotBlank() }
.subscribe { .subscribe { processUserInput(it.toString()) }
if ("$protocol${it.toString()}".isValidUrl()) {
enableButtonConnect()
} else {
disableButtonConnect()
}
}
} }
private fun unsubscribeEditText() = serverUrlDisposable.dispose() private fun unsubscribeEditText() = serverUrlDisposable.dispose()
private fun processUserInput(text: String) {
if (text.last().toString() == "." && !isDomainAppended) {
addDomain()
} else if (isDomainAppended && text != appendedText) {
removeDomain()
}
if ("$protocol$text".isValidUrl()) {
enableButtonConnect()
} else {
disableButtonConnect()
}
}
private fun addDomain() {
val cursorPosition = text_server_url.length()
text_server_url.append(SpannableStringBuilder()
.color(R.color.colorAuthenticationSecondaryText) { append("rocket.chat") })
text_server_url.setSelection(cursorPosition)
appendedText = text_server_url.text.toString()
isDomainAppended = true
}
private fun removeDomain() {
text_server_url.setText(
text_server_url.text.toString().substring(0, text_server_url.selectionEnd)
)
text_server_url.setSelection(text_server_url.length())
isDomainAppended = false
}
private fun enableUserInput() { private fun enableUserInput() {
enableButtonConnect() enableButtonConnect()
text_server_url.isEnabled = true text_server_url.isEnabled = true
......
package chat.rocket.android.chatroom.di package chat.rocket.android.chatroom.di
import android.app.Application
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import chat.rocket.android.chatroom.presentation.ChatRoomView import chat.rocket.android.chatroom.presentation.ChatRoomView
import chat.rocket.android.chatroom.ui.ChatRoomFragment import chat.rocket.android.chatroom.ui.ChatRoomFragment
import chat.rocket.android.chatrooms.adapter.RoomUiModelMapper
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.dagger.scope.PerFragment import chat.rocket.android.dagger.scope.PerFragment
import chat.rocket.android.db.ChatRoomDao import chat.rocket.android.db.ChatRoomDao
import chat.rocket.android.db.DatabaseManager import chat.rocket.android.db.DatabaseManager
import chat.rocket.android.db.DatabaseManagerFactory import chat.rocket.android.db.DatabaseManagerFactory
import chat.rocket.android.db.UserDao
import chat.rocket.android.server.domain.GetCurrentServerInteractor import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.domain.GetCurrentUserInteractor
import chat.rocket.android.server.domain.PermissionsInteractor
import chat.rocket.android.server.domain.SettingsRepository
import chat.rocket.android.server.domain.TokenRepository
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import kotlinx.coroutines.experimental.Job import kotlinx.coroutines.experimental.Job
...@@ -42,4 +49,30 @@ class ChatRoomFragmentModule { ...@@ -42,4 +49,30 @@ class ChatRoomFragmentModule {
@Provides @Provides
@PerFragment @PerFragment
fun provideChatRoomDao(manager: DatabaseManager): ChatRoomDao = manager.chatRoomDao() fun provideChatRoomDao(manager: DatabaseManager): ChatRoomDao = manager.chatRoomDao()
@Provides
@PerFragment
fun provideUserDao(manager: DatabaseManager): UserDao = manager.userDao()
@Provides
@PerFragment
fun provideGetCurrentUserInteractor(
tokenRepository: TokenRepository,
@Named("currentServer") serverUrl: String,
userDao: UserDao
): GetCurrentUserInteractor {
return GetCurrentUserInteractor(tokenRepository, serverUrl, userDao)
}
@Provides
@PerFragment
fun provideRoomMapper(
context: Application,
repository: SettingsRepository,
userInteractor: GetCurrentUserInteractor,
@Named("currentServer") serverUrl: String,
permissionsInteractor: PermissionsInteractor
): RoomUiModelMapper {
return RoomUiModelMapper(context, repository.get(serverUrl), userInteractor, serverUrl, permissionsInteractor)
}
} }
...@@ -5,6 +5,7 @@ import chat.rocket.android.chatroom.uimodel.suggestion.ChatRoomSuggestionUiModel ...@@ -5,6 +5,7 @@ import chat.rocket.android.chatroom.uimodel.suggestion.ChatRoomSuggestionUiModel
import chat.rocket.android.chatroom.uimodel.suggestion.CommandSuggestionUiModel import chat.rocket.android.chatroom.uimodel.suggestion.CommandSuggestionUiModel
import chat.rocket.android.chatroom.uimodel.suggestion.EmojiSuggestionUiModel import chat.rocket.android.chatroom.uimodel.suggestion.EmojiSuggestionUiModel
import chat.rocket.android.chatroom.uimodel.suggestion.PeopleSuggestionUiModel import chat.rocket.android.chatroom.uimodel.suggestion.PeopleSuggestionUiModel
import chat.rocket.android.chatrooms.adapter.model.RoomUiModel
import chat.rocket.android.core.behaviours.LoadingView import chat.rocket.android.core.behaviours.LoadingView
import chat.rocket.android.core.behaviours.MessageView import chat.rocket.android.core.behaviours.MessageView
import chat.rocket.core.internal.realtime.socket.model.State import chat.rocket.core.internal.realtime.socket.model.State
...@@ -131,12 +132,7 @@ interface ChatRoomView : LoadingView, MessageView { ...@@ -131,12 +132,7 @@ interface ChatRoomView : LoadingView, MessageView {
fun populateEmojiSuggestions(emojis: List<EmojiSuggestionUiModel>) fun populateEmojiSuggestions(emojis: List<EmojiSuggestionUiModel>)
/** fun onJoined(roomUiModel: RoomUiModel)
* This user has joined the chat callback.
*
* @param userCanPost Whether the user can post a message or not.
*/
fun onJoined(userCanPost: Boolean)
fun showReactionsPopup(messageId: String) fun showReactionsPopup(messageId: String)
...@@ -147,9 +143,6 @@ interface ChatRoomView : LoadingView, MessageView { ...@@ -147,9 +143,6 @@ interface ChatRoomView : LoadingView, MessageView {
*/ */
fun populateCommandSuggestions(commands: List<CommandSuggestionUiModel>) fun populateCommandSuggestions(commands: List<CommandSuggestionUiModel>)
/** fun onRoomUpdated(roomUiModel: RoomUiModel)
* Communicate whether it's a broadcast channel and if current user can post to it.
*/
fun onRoomUpdated(userCanPost: Boolean, channelIsBroadcast: Boolean, userCanMod: Boolean)
} }
...@@ -8,8 +8,10 @@ import android.content.Context ...@@ -8,8 +8,10 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.res.Configuration import android.content.res.Configuration
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.provider.MediaStore
import android.text.SpannableStringBuilder import android.text.SpannableStringBuilder
import android.view.KeyEvent import android.view.KeyEvent
import android.view.LayoutInflater import android.view.LayoutInflater
...@@ -23,6 +25,7 @@ import android.widget.FrameLayout ...@@ -23,6 +25,7 @@ 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.core.content.FileProvider
import androidx.core.text.bold import androidx.core.text.bold
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
...@@ -48,6 +51,7 @@ import chat.rocket.android.chatroom.uimodel.suggestion.ChatRoomSuggestionUiModel ...@@ -48,6 +51,7 @@ import chat.rocket.android.chatroom.uimodel.suggestion.ChatRoomSuggestionUiModel
import chat.rocket.android.chatroom.uimodel.suggestion.CommandSuggestionUiModel import chat.rocket.android.chatroom.uimodel.suggestion.CommandSuggestionUiModel
import chat.rocket.android.chatroom.uimodel.suggestion.EmojiSuggestionUiModel import chat.rocket.android.chatroom.uimodel.suggestion.EmojiSuggestionUiModel
import chat.rocket.android.chatroom.uimodel.suggestion.PeopleSuggestionUiModel import chat.rocket.android.chatroom.uimodel.suggestion.PeopleSuggestionUiModel
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.ComposerEditText
...@@ -73,6 +77,8 @@ import chat.rocket.android.util.extensions.rotateBy ...@@ -73,6 +77,8 @@ import chat.rocket.android.util.extensions.rotateBy
import chat.rocket.android.util.extensions.showToast import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.textContent import chat.rocket.android.util.extensions.textContent
import chat.rocket.android.util.extensions.ui import chat.rocket.android.util.extensions.ui
import chat.rocket.android.util.extension.createImageFile
import chat.rocket.android.util.extensions.getBitmpap
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
...@@ -86,6 +92,9 @@ import kotlinx.android.synthetic.main.fragment_chat_room.* ...@@ -86,6 +92,9 @@ import kotlinx.android.synthetic.main.fragment_chat_room.*
import kotlinx.android.synthetic.main.message_attachment_options.* import kotlinx.android.synthetic.main.message_attachment_options.*
import kotlinx.android.synthetic.main.message_composer.* import kotlinx.android.synthetic.main.message_composer.*
import kotlinx.android.synthetic.main.message_list.* import kotlinx.android.synthetic.main.message_list.*
import timber.log.Timber
import java.io.File
import java.io.IOException
import kotlinx.android.synthetic.main.reaction_praises_list_item.* import kotlinx.android.synthetic.main.reaction_praises_list_item.*
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
...@@ -125,6 +134,7 @@ private const val BUNDLE_CHAT_ROOM_TYPE = "chat_room_type" ...@@ -125,6 +134,7 @@ private const val BUNDLE_CHAT_ROOM_TYPE = "chat_room_type"
private const val BUNDLE_IS_CHAT_ROOM_READ_ONLY = "is_chat_room_read_only" private const val BUNDLE_IS_CHAT_ROOM_READ_ONLY = "is_chat_room_read_only"
private const val REQUEST_CODE_FOR_PERFORM_SAF = 42 private const val REQUEST_CODE_FOR_PERFORM_SAF = 42
private const val REQUEST_CODE_FOR_DRAW = 101 private const val REQUEST_CODE_FOR_DRAW = 101
private const val REQUEST_CODE_FOR_PERFORM_CAMERA = 102
private const val BUNDLE_CHAT_ROOM_LAST_SEEN = "chat_room_last_seen" private const val BUNDLE_CHAT_ROOM_LAST_SEEN = "chat_room_last_seen"
private const val BUNDLE_CHAT_ROOM_IS_SUBSCRIBED = "chat_room_is_subscribed" private const val BUNDLE_CHAT_ROOM_IS_SUBSCRIBED = "chat_room_is_subscribed"
private const val BUNDLE_CHAT_ROOM_IS_CREATOR = "chat_room_is_creator" private const val BUNDLE_CHAT_ROOM_IS_CREATOR = "chat_room_is_creator"
...@@ -192,6 +202,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -192,6 +202,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
internal val description by lazy { dialogView.findViewById<EditText>(R.id.text_file_description) } internal val description by lazy { dialogView.findViewById<EditText>(R.id.text_file_description) }
internal val audioVideoAttachment by lazy { dialogView.findViewById<FrameLayout>(R.id.audio_video_attachment) } internal val audioVideoAttachment by lazy { dialogView.findViewById<FrameLayout>(R.id.audio_video_attachment) }
internal val textFile by lazy { dialogView.findViewById<TextView>(R.id.text_file_name) } internal val textFile by lazy { dialogView.findViewById<TextView>(R.id.text_file_name) }
private var takenPhotoUri: Uri? = null
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
...@@ -280,12 +291,19 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -280,12 +291,19 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
} }
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) { if (resultCode == Activity.RESULT_OK) {
when (requestCode) { when (requestCode) {
REQUEST_CODE_FOR_PERFORM_SAF -> showFileAttachmentDialog(resultData.data) REQUEST_CODE_FOR_PERFORM_CAMERA -> takenPhotoUri?.let { uri ->
REQUEST_CODE_FOR_DRAW -> showDrawAttachmentDialog( uri.getBitmpap(requireContext())?.let { bitmap ->
resultData.getByteArrayExtra(DRAWING_BYTE_ARRAY_EXTRA_DATA) presenter.uploadImage(chatRoomId, "image/png", uri, bitmap, "")
) }
}
REQUEST_CODE_FOR_PERFORM_SAF -> resultData?.data?.let {
showFileAttachmentDialog(it)
}
REQUEST_CODE_FOR_DRAW -> resultData?.getByteArrayExtra(DRAWING_BYTE_ARRAY_EXTRA_DATA)?.let {
showDrawAttachmentDialog(it)
}
} }
} }
} }
...@@ -380,17 +398,14 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -380,17 +398,14 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
empty_chat_view.isVisible = adapter.itemCount == 0 empty_chat_view.isVisible = adapter.itemCount == 0
} }
override fun onRoomUpdated( override fun onRoomUpdated(roomUiModel: RoomUiModel) {
userCanPost: Boolean,
channelIsBroadcast: Boolean,
userCanMod: Boolean
) {
// TODO: We should rely solely on the user being able to post, but we cannot guarantee // TODO: We should rely solely on the user being able to post, but we cannot guarantee
// that the "(channels|groups).roles" endpoint is supported by the server in use. // that the "(channels|groups).roles" endpoint is supported by the server in use.
ui { ui {
setupMessageComposer(userCanPost) setupToolbar(roomUiModel.name.toString())
isBroadcastChannel = channelIsBroadcast setupMessageComposer(roomUiModel)
if (isBroadcastChannel && !userCanMod) { isBroadcastChannel = roomUiModel.broadcast
if (isBroadcastChannel && !roomUiModel.canModerate) {
disableMenu = true disableMenu = true
activity?.invalidateOptionsMenu() activity?.invalidateOptionsMenu()
} }
...@@ -527,7 +542,6 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -527,7 +542,6 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
ui { ui {
button_send.isEnabled = true button_send.isEnabled = true
text_message.isEnabled = true text_message.isEnabled = true
clearMessageComposition(true)
} }
} }
...@@ -769,12 +783,11 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -769,12 +783,11 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
} }
} }
override fun onJoined(userCanPost: Boolean) { override fun onJoined(roomUiModel: RoomUiModel) {
ui { ui {
input_container.isVisible = true input_container.isVisible = true
button_join_chat.isVisible = false button_join_chat.isVisible = false
isSubscribed = true isSubscribed = true
setupMessageComposer(userCanPost)
} }
} }
...@@ -807,8 +820,8 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -807,8 +820,8 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
} }
} }
private fun setupMessageComposer(canPost: Boolean) { private fun setupMessageComposer(roomUiModel: RoomUiModel) {
if (!canPost) { if (isReadOnly || !roomUiModel.writable) {
text_room_is_read_only.isVisible = true text_room_is_read_only.isVisible = true
input_container.isVisible = false input_container.isVisible = false
text_room_is_read_only.setText( text_room_is_read_only.setText(
...@@ -824,6 +837,8 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -824,6 +837,8 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
button_join_chat.isVisible = true button_join_chat.isVisible = true
button_join_chat.setOnClickListener { presenter.joinChat(chatRoomId) } button_join_chat.setOnClickListener { presenter.joinChat(chatRoomId) }
} else { } else {
input_container.isVisible = true
text_room_is_read_only.isVisible = false
button_send.isVisible = false button_send.isVisible = false
button_show_attachment_options.alpha = 1f button_show_attachment_options.alpha = 1f
button_show_attachment_options.isVisible = true button_show_attachment_options.isVisible = true
...@@ -880,7 +895,19 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -880,7 +895,19 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
hideAttachmentOptions() hideAttachmentOptions()
} }
button_files.setOnClickListener { button_add_reaction.setOnClickListener { _ ->
openEmojiKeyboardPopup()
}
button_take_a_photo.setOnClickListener {
dispatchTakePictureIntent()
handler.postDelayed({
hideAttachmentOptions()
}, 400)
}
button_attach_a_file.setOnClickListener {
handler.postDelayed({ handler.postDelayed({
presenter.selectFile() presenter.selectFile()
}, 200) }, 200)
...@@ -890,10 +917,6 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -890,10 +917,6 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
}, 400) }, 400)
} }
button_add_reaction.setOnClickListener { _ ->
openEmojiKeyboardPopup()
}
button_drawing.setOnClickListener { button_drawing.setOnClickListener {
activity?.let { fragmentActivity -> activity?.let { fragmentActivity ->
if (!ImageHelper.canWriteToExternalStorage(fragmentActivity)) { if (!ImageHelper.canWriteToExternalStorage(fragmentActivity)) {
...@@ -911,6 +934,26 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -911,6 +934,26 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
} }
} }
private fun dispatchTakePictureIntent() {
Intent(MediaStore.ACTION_IMAGE_CAPTURE).also { takePictureIntent ->
// Create the File where the photo should go
val photoFile: File? = try {
activity?.createImageFile()
} catch (ex: IOException) {
Timber.e(ex)
null
}
// Continue only if the File was successfully created
photoFile?.also {
takenPhotoUri = FileProvider.getUriForFile(
requireContext(), "chat.rocket.android.fileprovider", it
)
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, takenPhotoUri)
startActivityForResult(takePictureIntent, REQUEST_CODE_FOR_PERFORM_CAMERA)
}
}
}
private fun getUnfinishedMessage() { private fun getUnfinishedMessage() {
val unfinishedMessage = presenter.getUnfinishedMessage(chatRoomId) val unfinishedMessage = presenter.getUnfinishedMessage(chatRoomId)
if (unfinishedMessage.isNotBlank()) { if (unfinishedMessage.isNotBlank()) {
......
...@@ -8,9 +8,8 @@ import androidx.core.text.color ...@@ -8,9 +8,8 @@ import androidx.core.text.color
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.db.model.ChatRoom import chat.rocket.android.db.model.ChatRoom
import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.infrastructure.checkIfMyself
import chat.rocket.android.server.domain.GetCurrentUserInteractor import chat.rocket.android.server.domain.GetCurrentUserInteractor
import chat.rocket.android.server.domain.PermissionsInteractor
import chat.rocket.android.server.domain.PublicSettings import chat.rocket.android.server.domain.PublicSettings
import chat.rocket.android.server.domain.showLastMessage import chat.rocket.android.server.domain.showLastMessage
import chat.rocket.android.server.domain.useRealName import chat.rocket.android.server.domain.useRealName
...@@ -30,7 +29,8 @@ class RoomUiModelMapper( ...@@ -30,7 +29,8 @@ class RoomUiModelMapper(
private val context: Application, private val context: Application,
private val settings: PublicSettings, private val settings: PublicSettings,
private val userInteractor: GetCurrentUserInteractor, private val userInteractor: GetCurrentUserInteractor,
private val serverUrl: String private val serverUrl: String,
private val permissions: PermissionsInteractor
) { ) {
private val nameUnreadColor = ContextCompat.getColor(context, R.color.colorPrimaryText) private val nameUnreadColor = ContextCompat.getColor(context, R.color.colorPrimaryText)
private val nameColor = ContextCompat.getColor(context, R.color.colorSecondaryText) private val nameColor = ContextCompat.getColor(context, R.color.colorSecondaryText)
...@@ -97,7 +97,9 @@ class RoomUiModelMapper( ...@@ -97,7 +97,9 @@ class RoomUiModelMapper(
avatar = serverUrl.avatarUrl(name!!, isGroupOrChannel = true), avatar = serverUrl.avatarUrl(name!!, isGroupOrChannel = true),
lastMessage = if(showLastMessage) { mapLastMessage(lastMessage?.sender?.id, lastMessage?.sender?.username, lastMessage = if(showLastMessage) { mapLastMessage(lastMessage?.sender?.id, lastMessage?.sender?.username,
lastMessage?.sender?.name, lastMessage?.message, lastMessage?.sender?.name, lastMessage?.message,
isDirectMessage = type is RoomType.DirectMessage)} else { null } isDirectMessage = type is RoomType.DirectMessage)} else { null },
muted = muted.orEmpty(),
writable = isChannelWritable(muted)
) )
} }
} }
...@@ -133,11 +135,18 @@ class RoomUiModelMapper( ...@@ -133,11 +135,18 @@ class RoomUiModelMapper(
alert = isUnread, alert = isUnread,
lastMessage = lastMessageMarkdown, lastMessage = lastMessageMarkdown,
status = status, status = status,
username = if (type is RoomType.DirectMessage) name else null username = if (type is RoomType.DirectMessage) name else null,
muted = muted.orEmpty(),
writable = isChannelWritable(muted)
) )
} }
} }
private fun isChannelWritable(muted: List<String>?): Boolean {
val canWriteToReadOnlyChannels = permissions.canPostToReadOnlyChannels()
return canWriteToReadOnlyChannels || !muted.orEmpty().contains(currentUser?.username)
}
private fun roomType(type: String): String { private fun roomType(type: String): String {
val resources = context.resources val resources = context.resources
return when (type) { return when (type) {
...@@ -205,4 +214,4 @@ class RoomUiModelMapper( ...@@ -205,4 +214,4 @@ class RoomUiModelMapper(
} }
} }
} }
} }
\ No newline at end of file
...@@ -15,5 +15,8 @@ data class RoomUiModel( ...@@ -15,5 +15,8 @@ data class RoomUiModel(
val lastMessage: CharSequence? = null, val lastMessage: CharSequence? = null,
val status: UserStatus? = null, val status: UserStatus? = null,
val username: String? = null, val username: String? = null,
val broadcast: Boolean = false,
val canModerate: Boolean = false,
val writable: Boolean = true,
val muted: List<String> = emptyList() val muted: List<String> = emptyList()
) )
...@@ -12,6 +12,7 @@ import chat.rocket.android.db.DatabaseManager ...@@ -12,6 +12,7 @@ import chat.rocket.android.db.DatabaseManager
import chat.rocket.android.db.UserDao import chat.rocket.android.db.UserDao
import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.GetCurrentUserInteractor import chat.rocket.android.server.domain.GetCurrentUserInteractor
import chat.rocket.android.server.domain.PermissionsInteractor
import chat.rocket.android.server.domain.PublicSettings import chat.rocket.android.server.domain.PublicSettings
import chat.rocket.android.server.domain.SettingsRepository import chat.rocket.android.server.domain.SettingsRepository
import chat.rocket.android.server.domain.TokenRepository import chat.rocket.android.server.domain.TokenRepository
...@@ -88,9 +89,10 @@ class ChatRoomsFragmentModule { ...@@ -88,9 +89,10 @@ class ChatRoomsFragmentModule {
context: Application, context: Application,
repository: SettingsRepository, repository: SettingsRepository,
userInteractor: GetCurrentUserInteractor, userInteractor: GetCurrentUserInteractor,
@Named("currentServer") serverUrl: String @Named("currentServer") serverUrl: String,
permissionsInteractor: PermissionsInteractor
): RoomUiModelMapper { ): RoomUiModelMapper {
return RoomUiModelMapper(context, repository.get(serverUrl), userInteractor, serverUrl) return RoomUiModelMapper(context, repository.get(serverUrl), userInteractor, serverUrl, permissionsInteractor)
} }
@Provides @Provides
......
...@@ -3,9 +3,12 @@ package chat.rocket.android.chatrooms.infrastructure ...@@ -3,9 +3,12 @@ package chat.rocket.android.chatrooms.infrastructure
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import chat.rocket.android.db.ChatRoomDao import chat.rocket.android.db.ChatRoomDao
import chat.rocket.android.db.model.ChatRoom import chat.rocket.android.db.model.ChatRoom
import chat.rocket.android.util.retryDB
import javax.inject.Inject import javax.inject.Inject
class ChatRoomsRepository @Inject constructor(private val dao: ChatRoomDao){ class ChatRoomsRepository @Inject constructor(private val dao: ChatRoomDao) {
// TODO - check how to use retryDB here - suspend
fun getChatRooms(order: Order): LiveData<List<ChatRoom>> { fun getChatRooms(order: Order): LiveData<List<ChatRoom>> {
return when(order) { return when(order) {
Order.ACTIVITY -> dao.getAll() Order.ACTIVITY -> dao.getAll()
...@@ -15,9 +18,10 @@ class ChatRoomsRepository @Inject constructor(private val dao: ChatRoomDao){ ...@@ -15,9 +18,10 @@ class ChatRoomsRepository @Inject constructor(private val dao: ChatRoomDao){
} }
} }
fun search(query: String) = dao.searchSync(query) suspend fun search(query: String) =
retryDB("roomSearch($query)") { dao.searchSync(query) }
fun count() = dao.count() suspend fun count() = retryDB("roomsCount") { dao.count() }
enum class Order { enum class Order {
ACTIVITY, ACTIVITY,
......
...@@ -13,6 +13,7 @@ import chat.rocket.android.server.domain.useRealName ...@@ -13,6 +13,7 @@ import chat.rocket.android.server.domain.useRealName
import chat.rocket.android.server.domain.useSpecialCharsOnRoom import chat.rocket.android.server.domain.useSpecialCharsOnRoom
import chat.rocket.android.server.infraestructure.ConnectionManager import chat.rocket.android.server.infraestructure.ConnectionManager
import chat.rocket.android.util.extension.launchUI import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.retryDB
import chat.rocket.android.util.retryIO import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatException import chat.rocket.common.RocketChatException
import chat.rocket.common.model.RoomType import chat.rocket.common.model.RoomType
...@@ -41,11 +42,31 @@ class ChatRoomsPresenter @Inject constructor( ...@@ -41,11 +42,31 @@ class ChatRoomsPresenter @Inject constructor(
private val client = manager.client private val client = manager.client
private val settings = settingsRepository.get(currentServer) private val settings = settingsRepository.get(currentServer)
fun loadChatRoom(roomId: String) {
launchUI(strategy) {
view.showLoadingRoom("")
try {
val room = dbManager.getRoom(roomId)
if (room != null) {
loadChatRoom(room.chatRoom, true)
} else {
Timber.d("Error loading channel")
view.showGenericErrorMessage()
}
} catch (ex: Exception) {
Timber.d(ex, "Error loading channel")
view.showGenericErrorMessage()
} finally {
view.hideLoadingRoom()
}
}
}
fun loadChatRoom(chatRoom: RoomUiModel) { fun loadChatRoom(chatRoom: RoomUiModel) {
launchUI(strategy) { launchUI(strategy) {
view.showLoadingRoom(chatRoom.name) view.showLoadingRoom(chatRoom.name)
try { try {
val room = dbManager.getRoom(chatRoom.id) val room = retryDB("getRoom(${chatRoom.id}") { dbManager.getRoom(chatRoom.id) }
if (room != null) { if (room != null) {
loadChatRoom(room.chatRoom, true) loadChatRoom(room.chatRoom, true)
} else { } else {
......
...@@ -84,8 +84,7 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView { ...@@ -84,8 +84,7 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
if (bundle != null) { if (bundle != null) {
chatRoomId = bundle.getString(BUNDLE_CHAT_ROOM_ID) chatRoomId = bundle.getString(BUNDLE_CHAT_ROOM_ID)
chatRoomId?.let { chatRoomId?.let {
// TODO - bring back support to load a room from id. presenter.loadChatRoom(it)
//presenter.goToChatRoomWithId(it)
chatRoomId = null chatRoomId = null
} }
} }
......
...@@ -19,6 +19,7 @@ import chat.rocket.android.util.extensions.exhaustive ...@@ -19,6 +19,7 @@ import chat.rocket.android.util.extensions.exhaustive
import chat.rocket.android.util.extensions.removeTrailingSlash import chat.rocket.android.util.extensions.removeTrailingSlash
import chat.rocket.android.util.extensions.toEntity import chat.rocket.android.util.extensions.toEntity
import chat.rocket.android.util.extensions.userId import chat.rocket.android.util.extensions.userId
import chat.rocket.android.util.retryDB
import chat.rocket.common.model.BaseRoom import chat.rocket.common.model.BaseRoom
import chat.rocket.common.model.RoomType import chat.rocket.common.model.RoomType
import chat.rocket.common.model.SimpleUser import chat.rocket.common.model.SimpleUser
...@@ -91,12 +92,14 @@ class DatabaseManager(val context: Application, val serverUrl: String) { ...@@ -91,12 +92,14 @@ class DatabaseManager(val context: Application, val serverUrl: String) {
} }
} }
fun logout() { suspend fun logout() {
database.clearAllTables() retryDB("clearAllTables") { database.clearAllTables() }
} }
suspend fun getRoom(id: String) = withContext(dbManagerContext) { suspend fun getRoom(id: String) = withContext(dbManagerContext) {
chatRoomDao().getSync(id) retryDB("getRoom($id)") {
chatRoomDao().getSync(id)
}
} }
fun processUsersBatch(users: List<User>) { fun processUsersBatch(users: List<User>) {
...@@ -151,7 +154,7 @@ class DatabaseManager(val context: Application, val serverUrl: String) { ...@@ -151,7 +154,7 @@ class DatabaseManager(val context: Application, val serverUrl: String) {
fun updateSelfUser(myself: Myself) { fun updateSelfUser(myself: Myself) {
launch(dbManagerContext) { launch(dbManagerContext) {
val user = userDao().getUser(myself.id) val user = retryDB("getUser(${myself.id})") { userDao().getUser(myself.id) }
val entity = user?.copy( val entity = user?.copy(
name = myself.name ?: user.name, name = myself.name ?: user.name,
username = myself.username ?: user.username, username = myself.username ?: user.username,
...@@ -335,7 +338,7 @@ class DatabaseManager(val context: Application, val serverUrl: String) { ...@@ -335,7 +338,7 @@ class DatabaseManager(val context: Application, val serverUrl: String) {
} }
private suspend fun updateRoom(data: Room): ChatRoomEntity? { private suspend fun updateRoom(data: Room): ChatRoomEntity? {
return chatRoomDao().getSync(data.id)?.let { current -> return retryDB("getChatRoom(${data.id})") { chatRoomDao().getSync(data.id) }?.let { current ->
with(data) { with(data) {
val chatRoom = current.chatRoom val chatRoom = current.chatRoom
...@@ -376,7 +379,7 @@ class DatabaseManager(val context: Application, val serverUrl: String) { ...@@ -376,7 +379,7 @@ class DatabaseManager(val context: Application, val serverUrl: String) {
context.getString(R.string.msg_sent_attachment) context.getString(R.string.msg_sent_attachment)
private suspend fun updateSubscription(data: Subscription): ChatRoomEntity? { private suspend fun updateSubscription(data: Subscription): ChatRoomEntity? {
return chatRoomDao().getSync(data.roomId)?.let { current -> return retryDB("getRoom(${data.roomId}") { chatRoomDao().getSync(data.roomId) }?.let { current ->
with(data) { with(data) {
val userId = if (type is RoomType.DirectMessage) { val userId = if (type is RoomType.DirectMessage) {
...@@ -551,39 +554,42 @@ class DatabaseManager(val context: Application, val serverUrl: String) { ...@@ -551,39 +554,42 @@ class DatabaseManager(val context: Application, val serverUrl: String) {
} }
} }
private fun findUser(userId: String): String? = userDao().findUser(userId) private suspend fun findUser(userId: String): String? =
retryDB("findUser($userId)") { userDao().findUser(userId) }
private fun doOperation(operation: Operation) { private suspend fun doOperation(operation: Operation) {
when (operation) { retryDB(description = "doOperation($operation)") {
is Operation.ClearStatus -> userDao().clearStatus() when (operation) {
is Operation.UpdateRooms -> { is Operation.ClearStatus -> userDao().clearStatus()
Timber.d("Running ChatRooms transaction: remove: ${operation.toRemove} - insert: ${operation.toInsert} - update: ${operation.toUpdate}") is Operation.UpdateRooms -> {
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 -> { is Operation.CleanInsertRooms -> {
chatRoomDao().cleanInsert(operation.chatRooms) 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 -> { is Operation.UpsertUser -> {
userDao().upsert(operation.user) userDao().upsert(operation.user)
} }
is Operation.InsertMessages -> { is Operation.InsertMessages -> {
messageDao().insert(operation.list) messageDao().insert(operation.list)
} }
is Operation.SaveLastSync -> { is Operation.SaveLastSync -> {
messageDao().saveLastSync(operation.sync) messageDao().saveLastSync(operation.sync)
} }
}.exhaustive }.exhaustive
}
} }
} }
......
...@@ -5,6 +5,7 @@ import chat.rocket.android.core.lifecycle.CancelStrategy ...@@ -5,6 +5,7 @@ import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.db.DatabaseManager import chat.rocket.android.db.DatabaseManager
import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extension.launchUI import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.retryDB
import chat.rocket.common.RocketChatException import chat.rocket.common.RocketChatException
import chat.rocket.common.model.roomTypeOf import chat.rocket.common.model.roomTypeOf
import chat.rocket.common.util.ifNull import chat.rocket.common.util.ifNull
...@@ -36,7 +37,7 @@ class FavoriteMessagesPresenter @Inject constructor( ...@@ -36,7 +37,7 @@ class FavoriteMessagesPresenter @Inject constructor(
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
......
...@@ -7,6 +7,7 @@ import chat.rocket.android.files.uimodel.FileUiModel ...@@ -7,6 +7,7 @@ import chat.rocket.android.files.uimodel.FileUiModel
import chat.rocket.android.files.uimodel.FileUiModelMapper import chat.rocket.android.files.uimodel.FileUiModelMapper
import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extension.launchUI import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.retryDB
import chat.rocket.common.RocketChatException import chat.rocket.common.RocketChatException
import chat.rocket.common.model.roomTypeOf import chat.rocket.common.model.roomTypeOf
import chat.rocket.common.util.ifNull import chat.rocket.common.util.ifNull
......
...@@ -62,10 +62,7 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, ...@@ -62,10 +62,7 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector,
setContentView(R.layout.activity_main) setContentView(R.layout.activity_main)
refreshPushToken() refreshPushToken()
chatRoomId = intent.getStringExtra(INTENT_CHAT_ROOM_ID) chatRoomId = intent.getStringExtra(INTENT_CHAT_ROOM_ID)
println("ChatRoomId: $chatRoomId")
presenter.clearNotificationsForChatroom(chatRoomId) presenter.clearNotificationsForChatroom(chatRoomId)
presenter.connect() presenter.connect()
......
...@@ -6,6 +6,7 @@ import chat.rocket.android.members.uimodel.MemberUiModel ...@@ -6,6 +6,7 @@ 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
import chat.rocket.android.util.extension.launchUI import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.retryDB
import chat.rocket.common.RocketChatException import chat.rocket.common.RocketChatException
import chat.rocket.common.model.roomTypeOf import chat.rocket.common.model.roomTypeOf
import chat.rocket.common.util.ifNull import chat.rocket.common.util.ifNull
...@@ -38,7 +39,7 @@ class MembersPresenter @Inject constructor( ...@@ -38,7 +39,7 @@ class MembersPresenter @Inject constructor(
view.showLoading() view.showLoading()
dbManager.getRoom(roomId)?.let { dbManager.getRoom(roomId)?.let {
val members = val members =
client.getMembers(roomId, roomTypeOf(it.chatRoom.type), offset, 60) client.getMembers(roomId, roomTypeOf(it.chatRoom.type), offset, 60)
val memberUiModels = mapper.mapToUiModelList(members.result) val memberUiModels = mapper.mapToUiModelList(members.result)
view.showMembers(memberUiModels, members.total) view.showMembers(memberUiModels, members.total)
offset += 1 * 60L offset += 1 * 60L
......
...@@ -5,6 +5,7 @@ import chat.rocket.android.core.lifecycle.CancelStrategy ...@@ -5,6 +5,7 @@ import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.db.DatabaseManager import chat.rocket.android.db.DatabaseManager
import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extension.launchUI import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.retryDB
import chat.rocket.common.RocketChatException import chat.rocket.common.RocketChatException
import chat.rocket.common.model.roomTypeOf import chat.rocket.common.model.roomTypeOf
import chat.rocket.common.util.ifNull import chat.rocket.common.util.ifNull
...@@ -36,7 +37,7 @@ class PinnedMessagesPresenter @Inject constructor( ...@@ -36,7 +37,7 @@ class PinnedMessagesPresenter @Inject constructor(
view.showLoading() view.showLoading()
dbManager.getRoom(roomId)?.let { dbManager.getRoom(roomId)?.let {
val pinnedMessages = val pinnedMessages =
client.getPinnedMessages(roomId, roomTypeOf(it.chatRoom.type), offset) client.getPinnedMessages(roomId, roomTypeOf(it.chatRoom.type), offset)
val messageList = mapper.map(pinnedMessages.result, asNotReversed = true) val messageList = mapper.map(pinnedMessages.result, asNotReversed = true)
view.showPinnedMessages(messageList) view.showPinnedMessages(messageList)
offset += 1 * 30 offset += 1 * 30
......
...@@ -29,7 +29,6 @@ import chat.rocket.core.internal.rest.setAvatar ...@@ -29,7 +29,6 @@ import chat.rocket.core.internal.rest.setAvatar
import chat.rocket.core.internal.rest.updateProfile import chat.rocket.core.internal.rest.updateProfile
import kotlinx.coroutines.experimental.DefaultDispatcher import kotlinx.coroutines.experimental.DefaultDispatcher
import kotlinx.coroutines.experimental.withContext import kotlinx.coroutines.experimental.withContext
import java.lang.Exception
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
...@@ -58,20 +57,17 @@ class ProfilePresenter @Inject constructor( ...@@ -58,20 +57,17 @@ class ProfilePresenter @Inject constructor(
) { ) {
private val serverUrl = serverInteractor.get()!! private val serverUrl = serverInteractor.get()!!
private val client: RocketChatClient = factory.create(serverUrl) private val client: RocketChatClient = factory.create(serverUrl)
private val myselfId = userHelper.user()?.id ?: "" private val user = userHelper.user()
private var myselfName = userHelper.user()?.name ?: ""
private var myselfUsername = userHelper.username() ?: ""
private var myselfEmailAddress = userHelper.user()?.emails?.getOrNull(0)?.address ?: ""
fun loadUserProfile() { fun loadUserProfile() {
launchUI(strategy) { launchUI(strategy) {
view.showLoading() view.showLoading()
try { try {
view.showProfile( view.showProfile(
serverUrl.avatarUrl(myselfUsername), serverUrl.avatarUrl(user?.username ?: ""),
myselfName, user?.name ?: "",
myselfUsername, user?.username ?: "",
myselfEmailAddress user?.emails?.getOrNull(0)?.address ?: ""
) )
} catch (exception: RocketChatException) { } catch (exception: RocketChatException) {
view.showMessage(exception) view.showMessage(exception)
...@@ -85,14 +81,16 @@ class ProfilePresenter @Inject constructor( ...@@ -85,14 +81,16 @@ class ProfilePresenter @Inject constructor(
launchUI(strategy) { launchUI(strategy) {
view.showLoading() view.showLoading()
try { try {
retryIO { client.updateProfile(myselfId, email, name, username) } user?.id?.let { id ->
retryIO { client.updateProfile(id, email, name, username) }
myselfEmailAddress = email view.showProfileUpdateSuccessfullyMessage()
myselfName = name view.showProfile(
myselfUsername = username serverUrl.avatarUrl(user.username ?: ""),
name,
view.showProfileUpdateSuccessfullyMessage() username,
loadUserProfile() email
)
}
} catch (exception: RocketChatException) { } catch (exception: RocketChatException) {
exception.message?.let { exception.message?.let {
view.showMessage(it) view.showMessage(it)
...@@ -117,7 +115,7 @@ class ProfilePresenter @Inject constructor( ...@@ -117,7 +115,7 @@ class ProfilePresenter @Inject constructor(
uriInteractor.getInputStream(uri) uriInteractor.getInputStream(uri)
} }
} }
view.reloadUserAvatar(serverUrl.avatarUrl(myselfUsername)) user?.username?.let { view.reloadUserAvatar(it) }
} catch (exception: RocketChatException) { } catch (exception: RocketChatException) {
exception.message?.let { exception.message?.let {
view.showMessage(it) view.showMessage(it)
...@@ -144,7 +142,8 @@ class ProfilePresenter @Inject constructor( ...@@ -144,7 +142,8 @@ class ProfilePresenter @Inject constructor(
byteArray?.inputStream() byteArray?.inputStream()
} }
} }
view.reloadUserAvatar(serverUrl.avatarUrl(myselfUsername))
user?.username?.let { view.reloadUserAvatar(it) }
} catch (exception: RocketChatException) { } catch (exception: RocketChatException) {
exception.message?.let { exception.message?.let {
view.showMessage(it) view.showMessage(it)
...@@ -161,8 +160,10 @@ class ProfilePresenter @Inject constructor( ...@@ -161,8 +160,10 @@ class ProfilePresenter @Inject constructor(
launchUI(strategy) { launchUI(strategy) {
view.showLoading() view.showLoading()
try { try {
retryIO { client.resetAvatar(myselfId) } user?.id?.let { id ->
view.reloadUserAvatar(serverUrl.avatarUrl(myselfUsername)) retryIO { client.resetAvatar(id) }
}
user?.username?.let { view.reloadUserAvatar(it) }
} catch (exception: RocketChatException) { } catch (exception: RocketChatException) {
exception.message?.let { exception.message?.let {
view.showMessage(it) view.showMessage(it)
......
...@@ -217,7 +217,7 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback { ...@@ -217,7 +217,7 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback {
hideUpdateAvatarOptions() hideUpdateAvatarOptions()
} }
button_take_photo.setOnClickListener { button_take_a_photo.setOnClickListener {
dispatchTakePicture(REQUEST_CODE_FOR_PERFORM_CAMERA) dispatchTakePicture(REQUEST_CODE_FOR_PERFORM_CAMERA)
hideUpdateAvatarOptions() hideUpdateAvatarOptions()
} }
......
...@@ -296,11 +296,8 @@ class PushManager @Inject constructor( ...@@ -296,11 +296,8 @@ class PushManager @Inject constructor(
} }
private fun getContentIntent(context: Context, notificationId: Int, pushMessage: PushMessage, grouped: Boolean = false): PendingIntent { private fun getContentIntent(context: Context, notificationId: Int, pushMessage: PushMessage, grouped: Boolean = false): PendingIntent {
val notificationIntent = context.changeServerIntent(pushMessage.info.host, chatRoomId = pushMessage.info.roomId) val roomId = if (!grouped) pushMessage.info.roomId else null
// TODO - add support to go directly to the chatroom val notificationIntent = context.changeServerIntent(pushMessage.info.host, chatRoomId = roomId)
/*if (!isGrouped) {
notificationIntent.putExtra(EXTRA_ROOM_ID, pushMessage.info.roomId)
}*/
return PendingIntent.getActivity(context, random.nextInt(), notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT) return PendingIntent.getActivity(context, random.nextInt(), notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT)
} }
......
...@@ -22,6 +22,7 @@ import chat.rocket.core.internal.realtime.unsubscribe ...@@ -22,6 +22,7 @@ import chat.rocket.core.internal.realtime.unsubscribe
import chat.rocket.core.internal.rest.chatRooms import chat.rocket.core.internal.rest.chatRooms
import chat.rocket.core.model.Message import chat.rocket.core.model.Message
import chat.rocket.core.model.Myself import chat.rocket.core.model.Myself
import chat.rocket.core.model.Room
import kotlinx.coroutines.experimental.CommonPool import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.Job import kotlinx.coroutines.experimental.Job
import kotlinx.coroutines.experimental.channels.Channel import kotlinx.coroutines.experimental.channels.Channel
...@@ -45,6 +46,7 @@ class ConnectionManager( ...@@ -45,6 +46,7 @@ class ConnectionManager(
private val roomMessagesChannels = LinkedHashMap<String, Channel<Message>>() private val roomMessagesChannels = LinkedHashMap<String, Channel<Message>>()
private val userDataChannels = ArrayList<Channel<Myself>>() private val userDataChannels = ArrayList<Channel<Myself>>()
private val roomsChannels = LinkedHashMap<String, Channel<Room>>()
private val subscriptionIdMap = HashMap<String, String>() private val subscriptionIdMap = HashMap<String, String>()
private var subscriptionId: String? = null private var subscriptionId: String? = null
...@@ -127,6 +129,18 @@ class ConnectionManager( ...@@ -127,6 +129,18 @@ class ConnectionManager(
maxSize = 10) { batch -> maxSize = 10) { batch ->
Timber.d("processing Stream batch: ${batch.size} - $batch") Timber.d("processing Stream batch: ${batch.size} - $batch")
dbManager.processChatRoomsBatch(batch) dbManager.processChatRoomsBatch(batch)
batch.forEach {
//TODO - Do we need to handle Type.Removed and Type.Inserted here?
if (it.type == Type.Updated) {
if (it.data is Room) {
val room = it.data as Room
roomsChannels[it.data.id]?.let { channel ->
channel.offer(room)
}
}
}
}
} }
val messagesActor = createBatchActor<Message>(messagesContext, parent = connectJob, val messagesActor = createBatchActor<Message>(messagesContext, parent = connectJob,
...@@ -241,6 +255,14 @@ class ConnectionManager( ...@@ -241,6 +255,14 @@ class ConnectionManager(
fun removeUserDataChannel(channel: Channel<Myself>) = userDataChannels.remove(channel) fun removeUserDataChannel(channel: Channel<Myself>) = userDataChannels.remove(channel)
fun addRoomChannel(roomId: String, channel: Channel<Room>) {
roomsChannels[roomId] = channel
}
fun removeRoomChannel(roomId: String) {
roomsChannels.remove(roomId)
}
fun subscribeRoomMessages(roomId: String, channel: Channel<Message>) { fun subscribeRoomMessages(roomId: String, channel: Channel<Message>) {
val oldSub = roomMessagesChannels.put(roomId, channel) val oldSub = roomMessagesChannels.put(roomId, channel)
if (oldSub != null) { if (oldSub != null) {
......
...@@ -7,6 +7,7 @@ import chat.rocket.android.db.model.FullMessage ...@@ -7,6 +7,7 @@ import chat.rocket.android.db.model.FullMessage
import chat.rocket.android.db.model.ReactionEntity import chat.rocket.android.db.model.ReactionEntity
import chat.rocket.android.db.model.UrlEntity import chat.rocket.android.db.model.UrlEntity
import chat.rocket.android.db.model.UserEntity import chat.rocket.android.db.model.UserEntity
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
import chat.rocket.core.model.Message import chat.rocket.core.model.Message
...@@ -135,14 +136,18 @@ class DatabaseMessageMapper(private val dbManager: DatabaseManager) { ...@@ -135,14 +136,18 @@ class DatabaseMessageMapper(private val dbManager: DatabaseManager) {
with(attachment) { with(attachment) {
val fields = if (hasFields) { val fields = if (hasFields) {
withContext(CommonPool) { withContext(CommonPool) {
dbManager.messageDao().getAttachmentFields(attachment._id) retryDB("getAttachmentFields(${attachment._id})") {
dbManager.messageDao().getAttachmentFields(attachment._id)
}
}.map { Field(it.title, it.value) } }.map { Field(it.title, it.value) }
} else { } else {
null null
} }
val actions = if (hasActions) { val actions = if (hasActions) {
withContext(CommonPool) { withContext(CommonPool) {
dbManager.messageDao().getAttachmentActions(attachment._id) retryDB("getAttachmentActions(${attachment._id})") {
dbManager.messageDao().getAttachmentActions(attachment._id)
}
}.mapNotNull { mapAction(it) } }.mapNotNull { mapAction(it) }
} else { } else {
null null
...@@ -183,29 +188,6 @@ class DatabaseMessageMapper(private val dbManager: DatabaseManager) { ...@@ -183,29 +188,6 @@ class DatabaseMessageMapper(private val dbManager: DatabaseManager) {
return list return list
} }
/*private suspend fun mapColorAttachmentWithFields(entity: AttachmentEntity): ColorAttachment {
val fields = withContext(CommonPool) {
dbManager.messageDao().getAttachmentFields(entity._id)
}.map { Field(it.title, it.value) }
return with(entity) {
ColorAttachment(
color = Color.Custom(color ?: DEFAULT_COLOR_STR),
text = text ?: "",
fallback = fallback,
fields = fields)
}
}
private suspend fun mapActionAttachment(attachment: AttachmentEntity): ActionsAttachment {
val actions = withContext(CommonPool) {
dbManager.messageDao().getAttachmentActions(attachment._id)
}.mapNotNull { mapAction(it) }
return with(attachment) {
// TODO - remove the default "vertical" value from here...
ActionsAttachment(title, actions, buttonAlignment ?: "vertical")
}
}*/
private fun mapAction(action: AttachmentActionEntity): Action? { private fun mapAction(action: AttachmentActionEntity): Action? {
return when (action.type) { return when (action.type) {
"button" -> ButtonAction(action.type, action.text, action.url, action.isWebView, "button" -> ButtonAction(action.type, action.text, action.url, action.isWebView,
...@@ -214,13 +196,4 @@ class DatabaseMessageMapper(private val dbManager: DatabaseManager) { ...@@ -214,13 +196,4 @@ class DatabaseMessageMapper(private val dbManager: DatabaseManager) {
else -> null else -> null
} }
} }
/*private suspend fun mapAuthorAttachment(attachment: AttachmentEntity): AuthorAttachment {
val fields = withContext(CommonPool) {
dbManager.messageDao().getAttachmentFields(attachment._id)
}.map { Field(it.title, it.value) }
return with(attachment) {
AuthorAttachment(authorLink!!, authorIcon, authorName, fields)
}
}*/
} }
\ No newline at end of file
...@@ -4,6 +4,7 @@ import chat.rocket.android.db.DatabaseManager ...@@ -4,6 +4,7 @@ import chat.rocket.android.db.DatabaseManager
import chat.rocket.android.db.Operation import chat.rocket.android.db.Operation
import chat.rocket.android.db.model.MessagesSync import chat.rocket.android.db.model.MessagesSync
import chat.rocket.android.server.domain.MessagesRepository import chat.rocket.android.server.domain.MessagesRepository
import chat.rocket.android.util.retryDB
import chat.rocket.core.model.Message import chat.rocket.core.model.Message
import kotlinx.coroutines.experimental.CommonPool import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.withContext import kotlinx.coroutines.experimental.withContext
...@@ -14,25 +15,31 @@ class DatabaseMessagesRepository( ...@@ -14,25 +15,31 @@ class DatabaseMessagesRepository(
) : MessagesRepository { ) : MessagesRepository {
override suspend fun getById(id: String): Message? = withContext(CommonPool) { override suspend fun getById(id: String): Message? = withContext(CommonPool) {
dbManager.messageDao().getMessageById(id)?.let { message -> mapper.map(message) } retryDB("getMessageById($id)") {
dbManager.messageDao().getMessageById(id)?.let { message -> mapper.map(message) }
}
} }
override suspend fun getByRoomId(roomId: String): List<Message> = withContext(CommonPool) { override suspend fun getByRoomId(roomId: String): List<Message> = withContext(CommonPool) {
// FIXME - investigate how to avoid this distinctBy here, since DAO is returning a lot of // FIXME - investigate how to avoid this distinctBy here, since DAO is returning a lot of
// duplicate rows (something related to our JOINS and relations on Room) // duplicate rows (something related to our JOINS and relations on Room)
dbManager.messageDao().getMessagesByRoomId(roomId) retryDB("getMessagesByRoomId($roomId)") {
.distinctBy { it.message.message.id } dbManager.messageDao().getMessagesByRoomId(roomId)
.let { messages -> .distinctBy { it.message.message.id }
mapper.map(messages) .let { messages ->
} mapper.map(messages)
}
}
} }
override suspend fun getRecentMessages(roomId: String, count: Long): List<Message> = withContext(CommonPool) { override suspend fun getRecentMessages(roomId: String, count: Long): List<Message> = withContext(CommonPool) {
dbManager.messageDao().getRecentMessagesByRoomId(roomId, count) retryDB("getRecentMessagesByRoomId($roomId, $count)") {
.distinctBy { it.message.message.id } dbManager.messageDao().getRecentMessagesByRoomId(roomId, count)
.let { messages -> .distinctBy { it.message.message.id }
mapper.map(messages) .let { messages ->
} mapper.map(messages)
}
}
} }
override suspend fun save(message: Message) { override suspend fun save(message: Message) {
...@@ -45,20 +52,24 @@ class DatabaseMessagesRepository( ...@@ -45,20 +52,24 @@ class DatabaseMessagesRepository(
override suspend fun removeById(id: String) { override suspend fun removeById(id: String) {
withContext(CommonPool) { withContext(CommonPool) {
dbManager.messageDao().delete(id) retryDB("delete($id)") { dbManager.messageDao().delete(id) }
} }
} }
override suspend fun removeByRoomId(roomId: String) { override suspend fun removeByRoomId(roomId: String) {
withContext(CommonPool) { withContext(CommonPool) {
dbManager.messageDao().deleteByRoomId(roomId) retryDB("deleteByRoomId($roomId)") {
dbManager.messageDao().deleteByRoomId(roomId)
}
} }
} }
override suspend fun getAllUnsent(): List<Message> = withContext(CommonPool) { override suspend fun getAllUnsent(): List<Message> = withContext(CommonPool) {
dbManager.messageDao().getUnsentMessages() retryDB("getUnsentMessages") {
.distinctBy { it.message.message.id } dbManager.messageDao().getUnsentMessages()
.let { mapper.map(it) } .distinctBy { it.message.message.id }
.let { mapper.map(it) }
}
} }
override suspend fun saveLastSyncDate(roomId: String, timeMillis: Long) { override suspend fun saveLastSyncDate(roomId: String, timeMillis: Long) {
...@@ -66,6 +77,8 @@ class DatabaseMessagesRepository( ...@@ -66,6 +77,8 @@ class DatabaseMessagesRepository(
} }
override suspend fun getLastSyncDate(roomId: String): Long? = withContext(CommonPool) { override suspend fun getLastSyncDate(roomId: String): Long? = withContext(CommonPool) {
dbManager.messageDao().getLastSync(roomId)?.let { it.timestamp } retryDB("getLastSync($roomId)") {
dbManager.messageDao().getLastSync(roomId)?.let { it.timestamp }
}
} }
} }
\ No newline at end of file
package chat.rocket.android.util package chat.rocket.android.util
import android.database.sqlite.SQLiteDatabaseLockedException
import chat.rocket.common.RocketChatNetworkErrorException import chat.rocket.common.RocketChatNetworkErrorException
import kotlinx.coroutines.experimental.TimeoutCancellationException import kotlinx.coroutines.experimental.TimeoutCancellationException
import kotlinx.coroutines.experimental.delay import kotlinx.coroutines.experimental.delay
...@@ -8,6 +9,7 @@ import timber.log.Timber ...@@ -8,6 +9,7 @@ import timber.log.Timber
import kotlin.coroutines.experimental.coroutineContext import kotlin.coroutines.experimental.coroutineContext
const val DEFAULT_RETRY = 3 const val DEFAULT_RETRY = 3
private const val DEFAULT_DB_RETRY = 5
suspend fun <T> retryIO( suspend fun <T> retryIO(
description: String = "<missing description>", description: String = "<missing description>",
...@@ -32,6 +34,33 @@ suspend fun <T> retryIO( ...@@ -32,6 +34,33 @@ suspend fun <T> retryIO(
currentDelay = (currentDelay * factor).toLong().coerceAtMost(maxDelay) currentDelay = (currentDelay * factor).toLong().coerceAtMost(maxDelay)
} }
if (!coroutineContext.isActive) throw TimeoutCancellationException("job canceled")
return block() // last attempt
}
suspend fun <T> retryDB(
description: String = "<missing description>",
times: Int = DEFAULT_DB_RETRY,
initialDelay: Long = 100, // 0.1 second
maxDelay: Long = 500, // 0.5 second
factor: Double = 1.2,
block: suspend () -> T): T
{
var currentDelay = initialDelay
repeat(times - 1) { currentTry ->
if (!coroutineContext.isActive) throw TimeoutCancellationException("job canceled")
try {
return block()
} catch (e: SQLiteDatabaseLockedException) {
Timber.d(e, "failed call($currentTry): $description")
e.printStackTrace()
}
if (!coroutineContext.isActive) throw TimeoutCancellationException("job canceled")
delay(currentDelay)
currentDelay = (currentDelay * factor).toLong().coerceAtMost(maxDelay)
}
if (!coroutineContext.isActive) throw TimeoutCancellationException("job canceled") if (!coroutineContext.isActive) throw TimeoutCancellationException("job canceled")
return block() // last attempt return block() // last attempt
} }
\ No newline at end of file
...@@ -6,14 +6,24 @@ ...@@ -6,14 +6,24 @@
android:orientation="vertical"> android:orientation="vertical">
<Button <Button
android:id="@+id/button_files" android:id="@+id/button_take_a_photo"
style="?android:attr/borderlessButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawablePadding="20dp"
android:drawableStart="@drawable/ic_photo_camera_black_24dp"
android:gravity="start|center"
android:text="@string/action_take_a_photo" />
<Button
android:id="@+id/button_attach_a_file"
style="?android:attr/borderlessButtonStyle" style="?android:attr/borderlessButtonStyle"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:drawablePadding="20dp" android:drawablePadding="20dp"
android:drawableStart="@drawable/ic_files_24dp" android:drawableStart="@drawable/ic_files_24dp"
android:gravity="start|center" android:gravity="start|center"
android:text="@string/action_files" /> android:text="@string/action_attach_a_files" />
<Button <Button
android:id="@+id/button_drawing" android:id="@+id/button_drawing"
......
...@@ -16,14 +16,14 @@ ...@@ -16,14 +16,14 @@
android:text="@string/action_select_photo_from_gallery" /> android:text="@string/action_select_photo_from_gallery" />
<Button <Button
android:id="@+id/button_take_photo" android:id="@+id/button_take_a_photo"
style="?android:attr/borderlessButtonStyle" style="?android:attr/borderlessButtonStyle"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:drawableStart="@drawable/ic_photo_camera_black_24dp" android:drawableStart="@drawable/ic_photo_camera_black_24dp"
android:drawablePadding="20dp" android:drawablePadding="20dp"
android:gravity="start|center" android:gravity="start|center"
android:text="@string/action_take_photo" /> android:text="@string/action_take_a_photo" />
<Button <Button
android:id="@+id/button_reset_avatar" android:id="@+id/button_reset_avatar"
......
...@@ -39,7 +39,7 @@ ...@@ -39,7 +39,7 @@
<string name="action_create_channel">Erstelle Raum</string> <string name="action_create_channel">Erstelle Raum</string>
<string name="action_create">Erstelle</string> <string name="action_create">Erstelle</string>
<string name="action_logout">Abmelden</string> <string name="action_logout">Abmelden</string>
<string name="action_files">Dateien</string> <string name="action_attach_a_files">Attach a file</string> <!-- TODO Add translation -->
<string name="action_confirm_password">Bestätige Passwort Änderung</string> <string name="action_confirm_password">Bestätige Passwort Änderung</string>
<string name="action_join_chat">Trete Chat bei</string> <string name="action_join_chat">Trete Chat bei</string>
<string name="action_add_account">Erstelle Account</string> <string name="action_add_account">Erstelle Account</string>
...@@ -50,7 +50,7 @@ ...@@ -50,7 +50,7 @@
<string name="action_drawing">Zeichnung</string> <string name="action_drawing">Zeichnung</string>
<string name="action_save_to_gallery">Sichern in Gallerie</string> <string name="action_save_to_gallery">Sichern in Gallerie</string>
<string name="action_select_photo_from_gallery">Select photo from gallery</string> <!-- TODO Add translation --> <string name="action_select_photo_from_gallery">Select photo from gallery</string> <!-- TODO Add translation -->
<string name="action_take_photo">Select photo from gallery</string> <!-- TODO Add translation --> <string name="action_take_a_photo">Take a photo from galery</string> <!-- TODO Add translation -->
<string name="action_reset_avatar">Reset avatar</string> <!-- TODO Add translation --> <string name="action_reset_avatar">Reset avatar</string> <!-- TODO Add translation -->
<string name="action_connect_server">Connect with a server</string> <!-- TODO Add translation --> <string name="action_connect_server">Connect with a server</string> <!-- TODO Add translation -->
<string name="action_join_community">Join in the community</string> <!-- TODO Add translation --> <string name="action_join_community">Join in the community</string> <!-- TODO Add translation -->
......
...@@ -38,7 +38,7 @@ ...@@ -38,7 +38,7 @@
<string name="action_create_channel">Crear canal</string> <string name="action_create_channel">Crear canal</string>
<string name="action_create">Create</string> <string name="action_create">Create</string>
<string name="action_logout">Cerrar sesión</string> <string name="action_logout">Cerrar sesión</string>
<string name="action_files">Archivos</string> <string name="action_attach_a_files">Attach a file</string> <!-- TODO Add translation -->
<string name="action_confirm_password">Confirmar cambio de contraseña</string> <string name="action_confirm_password">Confirmar cambio de contraseña</string>
<string name="action_join_chat">Unirse al chat</string> <string name="action_join_chat">Unirse al chat</string>
<string name="action_add_account">Añadir cuenta</string> <string name="action_add_account">Añadir cuenta</string>
...@@ -49,7 +49,7 @@ ...@@ -49,7 +49,7 @@
<string name="action_drawing">Dibujo</string> <string name="action_drawing">Dibujo</string>
<string name="action_save_to_gallery">Guardar en la galería</string> <string name="action_save_to_gallery">Guardar en la galería</string>
<string name="action_select_photo_from_gallery">Select photo from gallery</string> <!-- TODO Add translation --> <string name="action_select_photo_from_gallery">Select photo from gallery</string> <!-- TODO Add translation -->
<string name="action_take_photo">Select photo from gallery</string> <!-- TODO Add translation --> <string name="action_take_a_photo">Take a photo</string> <!-- TODO Add translation -->
<string name="action_reset_avatar">Reset avatar</string> <!-- TODO Add translation --> <string name="action_reset_avatar">Reset avatar</string> <!-- TODO Add translation -->
<string name="action_connect_server">Connect with a server</string> <!-- TODO Add translation --> <string name="action_connect_server">Connect with a server</string> <!-- TODO Add translation -->
<string name="action_join_community">Join in the community</string> <!-- TODO Add translation --> <string name="action_join_community">Join in the community</string> <!-- TODO Add translation -->
......
...@@ -39,7 +39,7 @@ ...@@ -39,7 +39,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_files">Fichiers</string> <string name="action_attach_a_files">Attach a file</string> <!-- TODO Add translation -->
<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>
...@@ -50,7 +50,7 @@ ...@@ -50,7 +50,7 @@
<string name="action_drawing">Dessin</string> <string name="action_drawing">Dessin</string>
<string name="action_save_to_gallery">Sauvegarder vers la gallerie</string> <string name="action_save_to_gallery">Sauvegarder vers la gallerie</string>
<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_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">Connect with a server</string> <!-- TODO Add translation -->
<string name="action_join_community">Join in the community</string> <!-- TODO Add translation --> <string name="action_join_community">Join in the community</string> <!-- TODO Add translation -->
......
...@@ -39,7 +39,7 @@ ...@@ -39,7 +39,7 @@
<string name="action_create_channel">चैनल बनाएं</string> <string name="action_create_channel">चैनल बनाएं</string>
<string name="action_create">बनाएं</string> <string name="action_create">बनाएं</string>
<string name="action_logout">लोग आउट करें</string> <string name="action_logout">लोग आउट करें</string>
<string name="action_files">फ़ाइलें</string> <string name="action_attach_a_files">Attach a file</string> <!-- TODO Add translation -->
<string name="action_confirm_password">पासवर्ड परिवर्तन की पुष्टि करें</string> <string name="action_confirm_password">पासवर्ड परिवर्तन की पुष्टि करें</string>
<string name="action_join_chat">चैट में शामिल हों</string> <string name="action_join_chat">चैट में शामिल हों</string>
<string name="action_add_account">खाता जोड़ो</string> <string name="action_add_account">खाता जोड़ो</string>
...@@ -50,7 +50,7 @@ ...@@ -50,7 +50,7 @@
<string name="action_save_to_gallery">गैलरी में सहेजें</string> <string name="action_save_to_gallery">गैलरी में सहेजें</string>
<string name="action_drawing">चित्रकारी</string> <string name="action_drawing">चित्रकारी</string>
<string name="action_select_photo_from_gallery">गैलरी से फोटो का चयन करें</string> <string name="action_select_photo_from_gallery">गैलरी से फोटो का चयन करें</string>
<string name="action_take_photo">फोटो खेचिये</string> <string name="action_take_a_photo">फोटो खेचिये</string>
<string name="action_reset_avatar">अवतार रीसेट करें</string> <string name="action_reset_avatar">अवतार रीसेट करें</string>
<string name="action_connect_server">सर्वर से कनेक्ट करें</string> <string name="action_connect_server">सर्वर से कनेक्ट करें</string>
<string name="action_join_community">समुदाय में शामिल हों</string> <string name="action_join_community">समुदाय में शामिल हों</string>
......
This diff is collapsed.
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
<string name="title_update_profile">プロフィールの更新</string> <string name="title_update_profile">プロフィールの更新</string>
<string name="title_about">About</string> <string name="title_about">About</string>
<string name="title_create_channel">新しいチャネルを作成</string> <string name="title_create_channel">新しいチャネルを作成</string>
<string name="title_are_you_sure">本気ですか?</string> <string name="title_are_you_sure">本気ですか?</string>
<string name="title_channel_details">Channel Details</string> <!-- TODO add translation --> <string name="title_channel_details">Channel Details</string> <!-- TODO add translation -->
<string name="title_topic">トピック</string> <string name="title_topic">トピック</string>
<string name="title_announcement">発表</string> <string name="title_announcement">発表</string>
...@@ -41,7 +41,7 @@ ...@@ -41,7 +41,7 @@
<string name="action_create_channel">チャンネル作成</string> <string name="action_create_channel">チャンネル作成</string>
<string name="action_create">作ります</string> <string name="action_create">作ります</string>
<string name="action_logout">ログアウト</string> <string name="action_logout">ログアウト</string>
<string name="action_files">ファイル</string> <string name="action_attach_a_files">Attach a file</string> <!-- TODO Add translation -->
<string name="action_confirm_password">変更したパスワードの確認</string> <string name="action_confirm_password">変更したパスワードの確認</string>
<string name="action_join_chat">チャットに参加</string> <string name="action_join_chat">チャットに参加</string>
<string name="action_add_account">サーバーの追加</string> <string name="action_add_account">サーバーの追加</string>
...@@ -52,7 +52,7 @@ ...@@ -52,7 +52,7 @@
<string name="action_drawing">絵を描く</string> <string name="action_drawing">絵を描く</string>
<string name="action_save_to_gallery">ギャラリーに保存</string> <string name="action_save_to_gallery">ギャラリーに保存</string>
<string name="action_select_photo_from_gallery">ギャラリーの写真を選択</string> <string name="action_select_photo_from_gallery">ギャラリーの写真を選択</string>
<string name="action_take_photo">写真を撮る</string> <string name="action_take_a_photo">写真を撮る</string>
<string name="action_reset_avatar">アバターをリセット</string> <string name="action_reset_avatar">アバターをリセット</string>
<string name="action_connect_server">サーバーに接続</string> <string name="action_connect_server">サーバーに接続</string>
<string name="action_join_community">コミュニティに参加</string> <string name="action_join_community">コミュニティに参加</string>
......
...@@ -39,7 +39,7 @@ ...@@ -39,7 +39,7 @@
<string name="action_create_channel">Criar chat</string> <string name="action_create_channel">Criar chat</string>
<string name="action_create">Criar</string> <string name="action_create">Criar</string>
<string name="action_logout">Sair</string> <string name="action_logout">Sair</string>
<string name="action_files">Arquivos</string> <string name="action_attach_a_files">Anexar um arquivo</string>
<string name="action_confirm_password">Confirme a nova senha</string> <string name="action_confirm_password">Confirme a nova senha</string>
<string name="action_join_chat">Entrar no Chat</string> <string name="action_join_chat">Entrar no Chat</string>
<string name="action_add_account">Adicionar conta</string> <string name="action_add_account">Adicionar conta</string>
...@@ -50,7 +50,7 @@ ...@@ -50,7 +50,7 @@
<string name="action_drawing">Desenhando</string> <string name="action_drawing">Desenhando</string>
<string name="action_save_to_gallery">Salvar na galeria</string> <string name="action_save_to_gallery">Salvar na galeria</string>
<string name="action_select_photo_from_gallery">Escolher foto da galeria</string> <string name="action_select_photo_from_gallery">Escolher foto da galeria</string>
<string name="action_take_photo">Tirar foto</string> <string name="action_take_a_photo">Tirar uma foto</string>
<string name="action_reset_avatar">Resetar avatar</string> <string name="action_reset_avatar">Resetar avatar</string>
<string name="action_connect_server">Conectar com um servidor</string> <string name="action_connect_server">Conectar com um servidor</string>
<string name="action_join_community">Junte-se à comunidade</string> <string name="action_join_community">Junte-se à comunidade</string>
......
...@@ -39,7 +39,7 @@ ...@@ -39,7 +39,7 @@
<string name="action_create_channel">Создать канал</string> <string name="action_create_channel">Создать канал</string>
<string name="action_create">Создать</string> <string name="action_create">Создать</string>
<string name="action_logout">Выйти</string> <string name="action_logout">Выйти</string>
<string name="action_files">Файлы</string> <string name="action_attach_a_files">Attach a file</string> <!-- TODO Add translation -->
<string name="action_confirm_password">Подтверждение изменения пароля</string> <string name="action_confirm_password">Подтверждение изменения пароля</string>
<string name="action_join_chat">Присоединиться к чату</string> <string name="action_join_chat">Присоединиться к чату</string>
<string name="action_add_account">Добавить аккаунт</string> <string name="action_add_account">Добавить аккаунт</string>
...@@ -50,7 +50,7 @@ ...@@ -50,7 +50,7 @@
<string name="action_drawing">Рисунок</string> <string name="action_drawing">Рисунок</string>
<string name="action_save_to_gallery">Сохранить в галерею</string> <string name="action_save_to_gallery">Сохранить в галерею</string>
<string name="action_select_photo_from_gallery">Выбрать из галереи</string> <string name="action_select_photo_from_gallery">Выбрать из галереи</string>
<string name="action_take_photo">Сделать снимок</string> <string name="action_take_a_photo">Сделать снимок</string>
<string name="action_reset_avatar">Восстановить аватар</string> <string name="action_reset_avatar">Восстановить аватар</string>
<string name="action_connect_server">Соединиться с сервером</string> <string name="action_connect_server">Соединиться с сервером</string>
<string name="action_join_community">Сообщество</string> <string name="action_join_community">Сообщество</string>
......
...@@ -39,7 +39,7 @@ ...@@ -39,7 +39,7 @@
<string name="action_create_channel">Yeni Kanal Oluştur</string> <string name="action_create_channel">Yeni Kanal Oluştur</string>
<string name="action_create">Oluştur</string> <string name="action_create">Oluştur</string>
<string name="action_logout">Çıkış Yap</string> <string name="action_logout">Çıkış Yap</string>
<string name="action_files">Dosyalar</string> <string name="action_attach_a_files">Attach a file</string> <!-- TODO Add translation -->
<string name="action_confirm_password">Şifre Değişikliğini Onaylayın</string> <string name="action_confirm_password">Şifre Değişikliğini Onaylayın</string>
<string name="action_join_chat">Sohbete Bağlan</string> <string name="action_join_chat">Sohbete Bağlan</string>
<string name="action_add_account">Hesap Ekle</string> <string name="action_add_account">Hesap Ekle</string>
...@@ -50,7 +50,7 @@ ...@@ -50,7 +50,7 @@
<string name="action_drawing">Çizim</string> <string name="action_drawing">Çizim</string>
<string name="action_save_to_gallery">Galeriye kaydet</string> <string name="action_save_to_gallery">Galeriye kaydet</string>
<string name="action_select_photo_from_gallery">Galeriden resim seç</string> <string name="action_select_photo_from_gallery">Galeriden resim seç</string>
<string name="action_take_photo">Fotoğraf çek</string> <string name="action_take_a_photo">Fotoğraf çek</string>
<string name="action_reset_avatar">Avatarı kaldır</string> <string name="action_reset_avatar">Avatarı kaldır</string>
<string name="action_connect_server">Connect with a server</string> <!-- TODO Add translation --> <string name="action_connect_server">Connect with a server</string> <!-- TODO Add translation -->
<string name="action_join_community">Join in the community</string> <!-- TODO Add translation --> <string name="action_join_community">Join in the community</string> <!-- TODO Add translation -->
......
...@@ -39,7 +39,7 @@ ...@@ -39,7 +39,7 @@
<string name="action_create_channel">Створити канал</string> <string name="action_create_channel">Створити канал</string>
<string name="action_create">Створити</string> <string name="action_create">Створити</string>
<string name="action_logout">Вийти</string> <string name="action_logout">Вийти</string>
<string name="action_files">Файли</string> <string name="action_attach_a_files">Attach a file</string> <!-- TODO Add translation -->
<string name="action_confirm_password">Підтвердження зміни пароля</string> <string name="action_confirm_password">Підтвердження зміни пароля</string>
<string name="action_join_chat">Приєднатися до чату</string> <string name="action_join_chat">Приєднатися до чату</string>
<string name="action_add_account">Додати аккаунт</string> <string name="action_add_account">Додати аккаунт</string>
...@@ -50,7 +50,7 @@ ...@@ -50,7 +50,7 @@
<string name="action_drawing">Малюнок</string> <string name="action_drawing">Малюнок</string>
<string name="action_save_to_gallery">Зберегти до галереї</string> <string name="action_save_to_gallery">Зберегти до галереї</string>
<string name="action_select_photo_from_gallery">Вибрати з галереї</string> <string name="action_select_photo_from_gallery">Вибрати з галереї</string>
<string name="action_take_photo">Зробити знімок</string> <string name="action_take_a_photo">Зробити знімок</string>
<string name="action_reset_avatar">Відновити аватар</string> <string name="action_reset_avatar">Відновити аватар</string>
<string name="action_connect_server">Connect with a server</string> <!-- TODO Add translation --> <string name="action_connect_server">Connect with a server</string> <!-- TODO Add translation -->
<string name="action_join_community">Join in the community</string> <!-- TODO Add translation --> <string name="action_join_community">Join in the community</string> <!-- TODO Add translation -->
......
...@@ -51,7 +51,7 @@ https://github.com/RocketChat/java-code-styles/blob/master/CODING_STYLE.md#strin ...@@ -51,7 +51,7 @@ https://github.com/RocketChat/java-code-styles/blob/master/CODING_STYLE.md#strin
<string name="action_create_channel">Create channel</string> <string name="action_create_channel">Create channel</string>
<string name="action_create">Create</string> <string name="action_create">Create</string>
<string name="action_logout">Logout</string> <string name="action_logout">Logout</string>
<string name="action_files">Files</string> <string name="action_attach_a_files">Attach a file</string>
<string name="action_confirm_password">Confirm Password Change</string> <string name="action_confirm_password">Confirm Password Change</string>
<string name="action_join_chat">Join Chat</string> <string name="action_join_chat">Join Chat</string>
<string name="action_add_account">Add account</string> <string name="action_add_account">Add account</string>
...@@ -62,7 +62,7 @@ https://github.com/RocketChat/java-code-styles/blob/master/CODING_STYLE.md#strin ...@@ -62,7 +62,7 @@ https://github.com/RocketChat/java-code-styles/blob/master/CODING_STYLE.md#strin
<string name="action_drawing">Drawing</string> <string name="action_drawing">Drawing</string>
<string name="action_save_to_gallery">Save to gallery</string> <string name="action_save_to_gallery">Save to gallery</string>
<string name="action_select_photo_from_gallery">Select photo from gallery</string> <string name="action_select_photo_from_gallery">Select photo from gallery</string>
<string name="action_take_photo">Take photo</string> <string name="action_take_a_photo">Take a photo</string>
<string name="action_reset_avatar">Reset avatar</string> <string name="action_reset_avatar">Reset avatar</string>
<string name="action_connect_server">Connect with a server</string> <string name="action_connect_server">Connect with a server</string>
<string name="action_join_community">Join in the community</string> <string name="action_join_community">Join in the community</string>
......
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name="rocket_chat_images"
path="Android/data/chat.rocket.android/files/Pictures" />
</paths>
\ No newline at end of file
package chat.rocket.android.push package chat.rocket.android.push
import android.os.Bundle
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import androidx.work.Constraints import androidx.work.Constraints
import androidx.work.NetworkType import androidx.work.NetworkType
...@@ -23,9 +24,12 @@ class FirebaseMessagingService : FirebaseMessagingService() { ...@@ -23,9 +24,12 @@ class FirebaseMessagingService : FirebaseMessagingService() {
} }
override fun onMessageReceived(message: RemoteMessage) { override fun onMessageReceived(message: RemoteMessage) {
// XXX - for now this is ok, if we start to do network calls, use a Worker instead message.data?.let { data ->
message.data?.let { val bundle = Bundle()
pushManager.handle(bundleOf(*(it.map { Pair(it.key, it.value) }).toTypedArray())) data.entries.forEach { entry ->
bundle.putString(entry.key, entry.value)
}
pushManager.handle(bundle)
} }
} }
......
...@@ -10,7 +10,6 @@ import android.view.View ...@@ -10,7 +10,6 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.EditText import android.widget.EditText
import android.widget.ImageView import android.widget.ImageView
import android.widget.TextView
import androidx.annotation.ColorInt import androidx.annotation.ColorInt
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
...@@ -22,6 +21,7 @@ import chat.rocket.android.emoji.internal.EmojiCategory ...@@ -22,6 +21,7 @@ import chat.rocket.android.emoji.internal.EmojiCategory
import chat.rocket.android.emoji.internal.EmojiPagerAdapter import chat.rocket.android.emoji.internal.EmojiPagerAdapter
import chat.rocket.android.emoji.internal.PREF_EMOJI_SKIN_TONE import chat.rocket.android.emoji.internal.PREF_EMOJI_SKIN_TONE
import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayout
import kotlinx.android.synthetic.main.dialog_skin_tone_chooser.view.*
import kotlinx.coroutines.experimental.android.UI import kotlinx.coroutines.experimental.android.UI
import kotlinx.coroutines.experimental.launch import kotlinx.coroutines.experimental.launch
...@@ -77,59 +77,50 @@ class EmojiKeyboardPopup(context: Context, view: View) : OverKeyboardPopupWindow ...@@ -77,59 +77,50 @@ class EmojiKeyboardPopup(context: Context, view: View) : OverKeyboardPopupWindow
} }
private fun showSkinToneChooser() { private fun showSkinToneChooser() {
val view = LayoutInflater.from(context).inflate(R.layout.color_select_popup, null) val view = LayoutInflater.from(context).inflate(R.layout.dialog_skin_tone_chooser, null)
val dialog = AlertDialog.Builder(context, R.style.Dialog) val dialog = AlertDialog.Builder(context, R.style.Dialog)
.setView(view) .setView(view)
.setTitle(context.getString(R.string.alert_title_default_skin_tone)) .setTitle(context.getString(R.string.alert_title_default_skin_tone))
.setCancelable(true) .setCancelable(true)
.create() .create()
view.findViewById<TextView>(R.id.default_tone_text).also { with (view) {
it.text = EmojiParser.parse(context, it.text) image_view_default_tone.setOnClickListener {
}.setOnClickListener { dialog.dismiss()
dialog.dismiss() changeSkinTone(Fitzpatrick.Default)
changeSkinTone(Fitzpatrick.Default) }
}
view.findViewById<TextView>(R.id.light_tone_text).also { image_view_light_tone.setOnClickListener {
it.text = EmojiParser.parse(context, it.text) dialog.dismiss()
}.setOnClickListener { changeSkinTone(Fitzpatrick.LightTone)
dialog.dismiss() }
changeSkinTone(Fitzpatrick.LightTone)
}
view.findViewById<TextView>(R.id.medium_light_text).also { image_view_medium_light.setOnClickListener {
it.text = EmojiParser.parse(context, it.text) dialog.dismiss()
}.setOnClickListener { changeSkinTone(Fitzpatrick.MediumLightTone)
dialog.dismiss() }
changeSkinTone(Fitzpatrick.MediumLightTone)
}
view.findViewById<TextView>(R.id.medium_tone_text).also { image_view_medium_tone.setOnClickListener {
it.text = EmojiParser.parse(context, it.text) dialog.dismiss()
}.setOnClickListener { changeSkinTone(Fitzpatrick.MediumTone)
dialog.dismiss() }
changeSkinTone(Fitzpatrick.MediumTone)
}
view.findViewById<TextView>(R.id.medium_dark_tone_text).also { image_view_medium_dark_tone.setOnClickListener {
it.text = EmojiParser.parse(context, it.text) dialog.dismiss()
}.setOnClickListener { changeSkinTone(Fitzpatrick.MediumDarkTone)
dialog.dismiss() }
changeSkinTone(Fitzpatrick.MediumDarkTone)
}
view.findViewById<TextView>(R.id.dark_tone_text).also { image_view_dark_tone.setOnClickListener {
it.text = EmojiParser.parse(context, it.text) dialog.dismiss()
}.setOnClickListener { changeSkinTone(Fitzpatrick.DarkTone)
dialog.dismiss() }
changeSkinTone(Fitzpatrick.DarkTone)
} }
dialog.show() dialog.show()
} }
private fun changeSkinTone(tone: Fitzpatrick) { private fun changeSkinTone(tone: Fitzpatrick) {
val drawable = ContextCompat.getDrawable(context, R.drawable.color_change_circle)!! val drawable = ContextCompat.getDrawable(context, R.drawable.bg_skin_tone)!!
val wrappedDrawable = DrawableCompat.wrap(drawable) val wrappedDrawable = DrawableCompat.wrap(drawable)
DrawableCompat.setTint(wrappedDrawable, getFitzpatrickColor(tone)) DrawableCompat.setTint(wrappedDrawable, getFitzpatrickColor(tone))
(changeColorView as ImageView).setImageDrawable(wrappedDrawable) (changeColorView as ImageView).setImageDrawable(wrappedDrawable)
......
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" <shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval"> android:shape="oval">
<solid android:color="@color/tone_default" /> <solid android:color="@color/tone_default" />
<size <size
android:width="24dp" android:width="24dp"
android:height="24dp" /> android:height="24dp" />
</shape> </shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<TextView <ImageView
android:id="@+id/default_tone_text" android:id="@+id/image_view_default_tone"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
android:text="👌" android:layout_marginBottom="16dp"
android:textSize="32sp"
android:tint="@color/tone_default" android:tint="@color/tone_default"
app:layout_constraintEnd_toStartOf="@+id/light_tone_text" app:layout_constraintBottom_toTopOf="@+id/image_view_medium_tone"
app:layout_constraintEnd_toStartOf="@+id/image_view_light_tone"
app:layout_constraintHorizontal_bias="0.5" app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintHorizontal_chainStyle="packed" app:layout_constraintHorizontal_chainStyle="spread_inside"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/color_change_circle" /> app:srcCompat="@drawable/bg_skin_tone" />
<TextView <ImageView
android:id="@+id/light_tone_text" android:id="@+id/image_view_light_tone"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
android:layout_marginEnd="24dp"
android:text="👌🏻"
android:textSize="32sp"
android:tint="@color/tone_light" android:tint="@color/tone_light"
app:layout_constraintEnd_toStartOf="@+id/medium_light_text" app:layout_constraintEnd_toStartOf="@+id/image_view_medium_light"
app:layout_constraintHorizontal_bias="0.5" app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/default_tone_text" app:layout_constraintStart_toEndOf="@+id/image_view_default_tone"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/color_change_circle" app:srcCompat="@drawable/bg_skin_tone" />
tools:text="👌" />
<TextView <ImageView
android:id="@+id/medium_light_text" android:id="@+id/image_view_medium_light"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
android:text="👌🏼" android:layout_marginEnd="24dp"
android:textSize="32sp"
android:tint="@color/tone_medium_light" android:tint="@color/tone_medium_light"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5" app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/light_tone_text" app:layout_constraintStart_toEndOf="@+id/image_view_light_tone"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/color_change_circle" app:srcCompat="@drawable/bg_skin_tone" />
tools:text="👌" />
<TextView <ImageView
android:id="@+id/medium_tone_text" android:id="@+id/image_view_medium_tone"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginBottom="16dp" android:layout_marginBottom="16dp"
android:text="👌🏽"
android:textSize="32sp"
android:tint="@color/tone_medium" android:tint="@color/tone_medium"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/medium_dark_tone_text" app:layout_constraintEnd_toStartOf="@+id/image_view_medium_dark_tone"
app:layout_constraintHorizontal_chainStyle="packed" app:layout_constraintHorizontal_chainStyle="spread_inside"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="@+id/image_view_default_tone"
app:layout_constraintTop_toBottomOf="@+id/default_tone_text" app:layout_constraintTop_toBottomOf="@+id/image_view_default_tone"
app:srcCompat="@drawable/color_change_circle" app:srcCompat="@drawable/bg_skin_tone" />
tools:text="👌" />
<TextView <ImageView
android:id="@+id/medium_dark_tone_text" android:id="@+id/image_view_medium_dark_tone"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="24dp"
android:layout_marginBottom="16dp"
android:text="👌🏾"
android:textSize="32sp"
android:tint="@color/tone_medium_dark" android:tint="@color/tone_medium_dark"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="@+id/image_view_medium_tone"
app:layout_constraintEnd_toStartOf="@+id/dark_tone_text" app:layout_constraintEnd_toStartOf="@+id/image_view_dark_tone"
app:layout_constraintHorizontal_bias="0.5" app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/medium_tone_text" app:layout_constraintStart_toEndOf="@+id/image_view_medium_tone"
app:layout_constraintTop_toBottomOf="@+id/light_tone_text" app:layout_constraintTop_toTopOf="@+id/image_view_medium_tone"
app:srcCompat="@drawable/color_change_circle" app:srcCompat="@drawable/bg_skin_tone" />
tools:text="👌" />
<TextView <ImageView
android:id="@+id/dark_tone_text" android:id="@+id/image_view_dark_tone"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="16dp"
android:text="👌🏿"
android:textSize="32sp"
android:tint="@color/tone_dark" android:tint="@color/tone_dark"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="@+id/image_view_medium_dark_tone"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="@+id/image_view_medium_light"
app:layout_constraintStart_toEndOf="@+id/medium_dark_tone_text" app:layout_constraintStart_toEndOf="@+id/image_view_medium_dark_tone"
app:layout_constraintTop_toBottomOf="@+id/medium_light_text" app:layout_constraintTop_toTopOf="@+id/image_view_medium_dark_tone"
app:srcCompat="@drawable/color_change_circle" app:srcCompat="@drawable/bg_skin_tone" />
tools:text="👌" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
...@@ -43,7 +43,7 @@ ...@@ -43,7 +43,7 @@
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/color_change_circle" /> app:srcCompat="@drawable/bg_skin_tone" />
<ImageView <ImageView
android:id="@+id/emoji_search" android:id="@+id/emoji_search"
......
...@@ -2,13 +2,19 @@ package chat.rocket.android.util.extension ...@@ -2,13 +2,19 @@ package chat.rocket.android.util.extension
import android.content.Intent import android.content.Intent
import android.graphics.Bitmap import android.graphics.Bitmap
import android.os.Environment
import android.provider.MediaStore import android.provider.MediaStore
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import kotlinx.coroutines.experimental.DefaultDispatcher import kotlinx.coroutines.experimental.DefaultDispatcher
import kotlinx.coroutines.experimental.withContext import kotlinx.coroutines.experimental.withContext
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.InputStream import java.io.InputStream
import java.io.IOException
import java.io.File
import java.text.SimpleDateFormat
import java.util.*
/** /**
* Compress a [Bitmap] image. * Compress a [Bitmap] image.
...@@ -104,4 +110,16 @@ fun Fragment.dispatchTakePicture(requestCode: Int) { ...@@ -104,4 +110,16 @@ fun Fragment.dispatchTakePicture(requestCode: Int) {
if (takePictureIntent.resolveActivity(context?.packageManager) != null) { if (takePictureIntent.resolveActivity(context?.packageManager) != null) {
startActivityForResult(takePictureIntent, requestCode) startActivityForResult(takePictureIntent, requestCode)
} }
}
@Throws(IOException::class)
fun FragmentActivity.createImageFile(): File {
// Create an image file name
val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
val storageDir: File = getExternalFilesDir(Environment.DIRECTORY_PICTURES)
return File.createTempFile(
"PNG_${timeStamp}_", /* prefix */
".png", /* suffix */
storageDir /* directory */
)
} }
\ 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