Commit 26ead887 authored by Filipe de Lima Brito's avatar Filipe de Lima Brito

Merge branch 'beta' of github.com:RocketChat/Rocket.Chat.Android into develop

parents d46037d4 9da37707
......@@ -13,7 +13,7 @@ android {
applicationId "chat.rocket.android"
minSdkVersion 21
targetSdkVersion versions.targetSdk
versionCode 2022
versionCode 2024
versionName "2.3.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
multiDexEnabled true
......@@ -26,12 +26,19 @@ android {
keyAlias System.getenv("KEY_ALIAS")
keyPassword System.getenv("KEY_PASSWORD")
}
debug {
storeFile project.rootProject.file('debug.keystore').getCanonicalFile()
storePassword "android"
keyAlias "androiddebugkey"
keyPassword "android"
}
}
buildTypes {
release {
buildConfigField "String", "REQUIRED_SERVER_VERSION", '"0.62.0"'
buildConfigField "String", "RECOMMENDED_SERVER_VERSION", '"0.63.0"'
buildConfigField "String", "RECOMMENDED_SERVER_VERSION", '"0.64.2"'
signingConfig signingConfigs.release
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
......@@ -39,7 +46,8 @@ android {
debug {
buildConfigField "String", "REQUIRED_SERVER_VERSION", '"0.62.0"'
buildConfigField "String", "RECOMMENDED_SERVER_VERSION", '"0.63.0"'
buildConfigField "String", "RECOMMENDED_SERVER_VERSION", '"0.64.2"'
signingConfig signingConfigs.debug
applicationIdSuffix ".dev"
}
}
......@@ -73,7 +81,7 @@ dependencies {
kapt libraries.daggerProcessor
kapt libraries.daggerAndroidApt
implementation libraries.playServicesGcm
implementation libraries.fcm
implementation libraries.playServicesAuth
implementation libraries.room
......
This diff is collapsed.
......@@ -3,17 +3,9 @@
package="chat.rocket.android">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<permission
android:name="${applicationId}.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
<uses-permission android:name="${applicationId}.permission.C2D_MESSAGE" />
<application
android:name=".app.RocketChatApplication"
android:allowBackup="true"
......@@ -39,6 +31,7 @@
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.DEFAULT" />
......@@ -91,18 +84,6 @@
android:name=".settings.about.ui.AboutActivity"
android:theme="@style/AppTheme" />
<receiver
android:name="com.google.android.gms.gcm.GcmReceiver"
android:exported="true"
android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="${applicationId}" />
</intent-filter>
</receiver>
<receiver
android:name=".push.DirectReplyReceiver"
android:enabled="true"
......@@ -121,10 +102,11 @@
</service>
<service
android:name=".push.GcmListenerService"
android:exported="false">
android:name=".push.FirebaseMessagingService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
......
......@@ -14,11 +14,11 @@ object DateTimeHelper {
/**
* Returns a [LocalDateTime] from a [Long].
*
* @param long The [Long]
* @param long The [Long] to gets a [LocalDateTime].
* @return The [LocalDateTime] from a [Long].
*/
fun getLocalDateTime(long: Long?): LocalDateTime {
return LocalDateTime.ofInstant(long?.let { Instant.ofEpochMilli(it) }, ZoneId.systemDefault())
fun getLocalDateTime(long: Long): LocalDateTime {
return LocalDateTime.ofInstant(Instant.ofEpochMilli(long), ZoneId.systemDefault())
}
/**
......
......@@ -56,7 +56,6 @@ class LoginPresenter @Inject constructor(
private lateinit var credentialSecret: String
private lateinit var deepLinkUserId: String
private lateinit var deepLinkToken: String
private var loginCredentials: Credential? = null
fun setupView() {
setupConnectionInfo(currentServer)
......@@ -357,10 +356,7 @@ class LoginPresenter @Inject constructor(
saveToken(token)
registerPushToken()
if (loginType == TYPE_LOGIN_USER_EMAIL) {
loginCredentials = Credential.Builder(usernameOrEmail)
.setPassword(password)
.build()
view.saveSmartLockCredentials(loginCredentials)
view.saveSmartLockCredentials(usernameOrEmail, password)
}
navigator.toChatList()
} else if (loginType == TYPE_LOGIN_OAUTH) {
......
......@@ -243,7 +243,7 @@ interface LoginView : LoadingView, MessageView {
fun alertWrongPassword()
/**
* Save credentials via google smart lock
* Saves Google Smart Lock credentials.
*/
fun saveSmartLockCredentials(loginCredential: Credential?)
fun saveSmartLockCredentials(id: String, password: String)
}
\ No newline at end of file
......@@ -15,7 +15,6 @@ import chat.rocket.core.internal.rest.login
import chat.rocket.core.internal.rest.me
import chat.rocket.core.internal.rest.signup
import chat.rocket.core.model.Myself
import com.google.android.gms.auth.api.credentials.Credential
import javax.inject.Inject
class SignupPresenter @Inject constructor(
......@@ -64,10 +63,7 @@ class SignupPresenter @Inject constructor(
localRepository.save(LocalRepository.CURRENT_USERNAME_KEY, me.username)
saveAccount(me)
registerPushToken()
val loginCredentials = Credential.Builder(email)
.setPassword(password)
.build()
view.saveSmartLockCredentials(loginCredentials)
view.saveSmartLockCredentials(username, password)
navigator.toChatList()
} catch (exception: RocketChatException) {
exception.message?.let {
......
......@@ -27,7 +27,7 @@ interface SignupView : LoadingView, MessageView {
fun alertBlankEmail()
/**
* Save credentials via google smart lock
* Saves Google Smart Lock credentials.
*/
fun saveSmartLockCredentials(loginCredential: Credential)
fun saveSmartLockCredentials(id: String, password: String)
}
\ No newline at end of file
package chat.rocket.android.authentication.signup.ui
import DrawableHelper
import android.app.Activity.RESULT_OK
import android.app.Activity
import android.content.Intent
import android.os.Build
import android.os.Bundle
......@@ -11,30 +11,24 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver
import android.widget.Toast
import chat.rocket.android.R
import chat.rocket.android.authentication.login.ui.googleApiClient
import chat.rocket.android.R.string.message_credentials_saved_successfully
import chat.rocket.android.authentication.signup.presentation.SignupPresenter
import chat.rocket.android.authentication.signup.presentation.SignupView
import chat.rocket.android.helper.KeyboardHelper
import chat.rocket.android.helper.SmartLockHelper
import chat.rocket.android.helper.TextHelper
import chat.rocket.android.util.extensions.*
import com.google.android.gms.auth.api.Auth
import com.google.android.gms.auth.api.credentials.Credential
import com.google.android.gms.common.api.ResolvingResultCallbacks
import com.google.android.gms.common.api.Status
import com.google.android.gms.auth.api.credentials.Credentials
import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_authentication_sign_up.*
import timber.log.Timber
import javax.inject.Inject
internal const val SAVE_CREDENTIALS = 1
class SignupFragment : Fragment(), SignupView {
@Inject
lateinit var presenter: SignupPresenter
private lateinit var credentialsToBeSaved: Credential
private val layoutListener = ViewTreeObserver.OnGlobalLayoutListener {
if (KeyboardHelper.isSoftKeyboardShown(relative_layout.rootView)) {
bottom_container.setVisible(false)
......@@ -120,44 +114,16 @@ class SignupFragment : Fragment(), SignupView {
}
}
override fun saveSmartLockCredentials(loginCredential: Credential) {
credentialsToBeSaved = loginCredential
googleApiClient.let {
if (it.isConnected) {
saveCredentials()
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == SAVE_CREDENTIALS) {
if (resultCode == RESULT_OK) {
Toast.makeText(
context,
getString(R.string.message_credentials_saved_successfully),
Toast.LENGTH_SHORT
).show()
} else {
Timber.e("ERROR: Cancelled by user")
if (resultCode == Activity.RESULT_OK) {
if (data != null) {
if (requestCode == SAVE_CREDENTIALS) {
showMessage(getString(message_credentials_saved_successfully))
}
}
}
}
private fun saveCredentials() {
activity?.let {
Auth.CredentialsApi.save(googleApiClient, credentialsToBeSaved).setResultCallback(
object : ResolvingResultCallbacks<Status>(it, SAVE_CREDENTIALS) {
override fun onSuccess(status: Status) {
Timber.d("save:SUCCESS:$status")
}
override fun onUnresolvableFailure(status: Status) {
Timber.e("save:FAILURE:$status")
}
})
}
}
override fun showLoading() {
ui {
enableUserInput(false)
......@@ -188,6 +154,12 @@ class SignupFragment : Fragment(), SignupView {
showMessage(getString(R.string.msg_generic_error))
}
override fun saveSmartLockCredentials(id: String, password: String) {
activity?.let {
SmartLockHelper.save(Credentials.getClient(it), it, id, password)
}
}
private fun tintEditTextDrawableStart() {
ui {
val personDrawable =
......
......@@ -30,7 +30,7 @@ class ImageAttachmentViewHolder(
file_name.text = data.attachmentTitle
image_attachment.setOnClickListener {
ImageHelper.openImage(
it.context,
context,
data.attachmentUrl,
data.attachmentTitle.toString()
)
......
......@@ -181,9 +181,7 @@ class ChatRoomPresenter @Inject constructor(
}
subscribeTypingStatus()
if (offset == 0L) {
subscribeState()
}
subscribeState()
}
}
......@@ -229,10 +227,9 @@ class ChatRoomPresenter @Inject constructor(
)
try {
messagesRepository.save(newMessage)
val message = client.sendMessage(id, chatRoomId, text)
view.showNewMessage(mapper.map(newMessage, RoomViewModel(
roles = chatRoles, isBroadcast = chatIsBroadcast)))
message
client.sendMessage(id, chatRoomId, text)
} catch (ex: Exception) {
// Ok, not very beautiful, but the backend sends us a not valid response
// When someone sends a message on a read-only channel, so we just ignore it
......@@ -324,7 +321,7 @@ class ChatRoomPresenter @Inject constructor(
}
}
private fun subscribeState() {
private suspend fun subscribeState() {
Timber.d("Subscribing to Status changes")
lastState = manager.state
manager.addStatusChannel(stateChannel)
......@@ -790,12 +787,14 @@ class ChatRoomPresenter @Inject constructor(
}
private suspend fun subscribeTypingStatus() {
client.subscribeTypingStatus(chatRoomId.toString()) { _, id ->
typingStatusSubscriptionId = id
}
launch(CommonPool + strategy.jobs) {
client.subscribeTypingStatus(chatRoomId.toString()) { _, id ->
typingStatusSubscriptionId = id
}
for (typingStatus in client.typingStatusChannel) {
processTypingStatus(typingStatus)
for (typingStatus in client.typingStatusChannel) {
processTypingStatus(typingStatus)
}
}
}
......@@ -837,6 +836,7 @@ class ChatRoomPresenter @Inject constructor(
launchUI(strategy) {
val viewModelStreamedMessage = mapper.map(streamedMessage, RoomViewModel(
roles = chatRoles, isBroadcast = chatIsBroadcast))
val roomMessages = messagesRepository.getByRoomId(streamedMessage.roomId)
val index = roomMessages.indexOfFirst { msg -> msg.id == streamedMessage.id }
if (index > -1) {
......
......@@ -64,7 +64,7 @@ class MessageService : JobService() {
Timber.e(ex)
// TODO - remove the generic message when we implement :userId:/message subscription
if (ex is IllegalStateException) {
Timber.d(ex, "Probably a read-only problem...")
Timber.e(ex, "Probably a read-only problem...")
// TODO: For now we are only going to reschedule when api is fixed.
messageRepository.removeById(message.id)
jobFinished(params, false)
......
......@@ -31,6 +31,8 @@ import chat.rocket.android.helper.KeyboardHelper
import chat.rocket.android.helper.MessageParser
import chat.rocket.android.util.extensions.*
import chat.rocket.android.widget.emoji.*
import chat.rocket.common.model.RoomType
import chat.rocket.common.model.roomTypeOf
import chat.rocket.core.internal.realtime.socket.model.State
import chat.rocket.core.model.ChatRoom
import dagger.android.support.AndroidSupportInjection
......@@ -243,7 +245,9 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
if (recycler_view.adapter == null) {
adapter = ChatRoomAdapter(
chatRoomType, chatRoomName, presenter,
chatRoomType,
chatRoomName,
presenter,
reactionListener = this@ChatRoomFragment
)
recycler_view.adapter = adapter
......@@ -272,9 +276,11 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
) {
// 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.
setupMessageComposer(userCanPost)
isBroadcastChannel = channelIsBroadcast
if (isBroadcastChannel && !userCanMod) activity?.invalidateOptionsMenu()
ui {
setupMessageComposer(userCanPost)
isBroadcastChannel = channelIsBroadcast
if (isBroadcastChannel && !userCanMod) activity?.invalidateOptionsMenu()
}
}
override fun openDirectMessage(chatRoom: ChatRoom, permalink: String) {
......@@ -647,12 +653,11 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
if (isChatRoomReadOnly && !canPost) {
text_room_is_read_only.setVisible(true)
input_container.setVisible(false)
} else if (!isSubscribed) {
} else if (!isSubscribed && roomTypeOf(chatRoomType) != RoomType.DIRECT_MESSAGE) {
input_container.setVisible(false)
button_join_chat.setVisible(true)
button_join_chat.setOnClickListener { presenter.joinChat(chatRoomId) }
} else {
button_send.alpha = 0f
button_send.setVisible(false)
button_show_attachment_options.alpha = 1f
button_show_attachment_options.setVisible(true)
......@@ -788,14 +793,14 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
private fun setupComposeButtons(charSequence: CharSequence) {
if (charSequence.isNotEmpty() && playComposeMessageButtonsAnimation) {
button_show_attachment_options.fadeOut(1F, 0F, 120)
button_send.fadeIn(0F, 1F, 120)
button_show_attachment_options.setVisible(false)
button_send.setVisible(true)
playComposeMessageButtonsAnimation = false
}
if (charSequence.isEmpty()) {
button_send.fadeOut(1F, 0F, 120)
button_show_attachment_options.fadeIn(0F, 1F, 120)
button_send.setVisible(false)
button_show_attachment_options.setVisible(true)
playComposeMessageButtonsAnimation = true
}
}
......
......@@ -4,19 +4,19 @@ import chat.rocket.android.R
import chat.rocket.core.model.Message
data class MessageViewModel(
override val message: Message,
override val rawData: Message,
override val messageId: String,
override val avatar: String,
override val time: CharSequence,
override val senderName: CharSequence,
override val content: CharSequence,
override val isPinned: Boolean,
override var reactions: List<ReactionViewModel>,
override var nextDownStreamMessage: BaseViewModel<*>? = null,
override var preview: Message? = null,
var isFirstUnread: Boolean,
override var isTemporary: Boolean = false
override val message: Message,
override val rawData: Message,
override val messageId: String,
override val avatar: String,
override val time: CharSequence,
override val senderName: CharSequence,
override val content: CharSequence,
override val isPinned: Boolean,
override var reactions: List<ReactionViewModel>,
override var nextDownStreamMessage: BaseViewModel<*>? = null,
override var preview: Message? = null,
var isFirstUnread: Boolean,
override var isTemporary: Boolean = false
) : BaseMessageViewModel<Message> {
override val viewType: Int
get() = BaseViewModel.ViewType.MESSAGE.viewType
......
package chat.rocket.android.chatroom.viewmodel
data class ReactionViewModel(
val messageId: String,
val shortname: String,
val unicode: CharSequence,
val count: Int,
val usernames: List<String> = emptyList()
val messageId: String,
val shortname: String,
val unicode: CharSequence,
val count: Int,
val usernames: List<String> = emptyList()
)
\ No newline at end of file
......@@ -65,70 +65,131 @@ class ViewModelMapper @Inject constructor(
private val currentUsername: String? = localRepository.get(LocalRepository.CURRENT_USERNAME_KEY)
private val secondaryTextColor = ContextCompat.getColor(context, R.color.colorSecondaryText)
suspend fun map(message: Message, roomViewModel: RoomViewModel = RoomViewModel(
roles = emptyList(), isBroadcast = true)): List<BaseViewModel<*>> {
suspend fun map(
message: Message,
roomViewModel: RoomViewModel = RoomViewModel(roles = emptyList(), isBroadcast = true)
): List<BaseViewModel<*>> {
return translate(message, roomViewModel)
}
suspend fun map(messages: List<Message>, roomViewModel: RoomViewModel = RoomViewModel(
roles = emptyList(), isBroadcast = true)): List<BaseViewModel<*>> = withContext(CommonPool) {
val list = ArrayList<BaseViewModel<*>>(messages.size)
messages.forEach {
list.addAll(translate(it, roomViewModel))
suspend fun map(
messages: List<Message>,
roomViewModel: RoomViewModel = RoomViewModel(roles = emptyList(), isBroadcast = true),
asNotReversed: Boolean = false
): List<BaseViewModel<*>> =
withContext(CommonPool) {
val list = ArrayList<BaseViewModel<*>>(messages.size)
messages.forEach {
list.addAll(
if (asNotReversed) translateAsNotReversed(it, roomViewModel)
else translate(it, roomViewModel)
)
}
return@withContext list
}
return@withContext list
}
private suspend fun translate(
message: Message,
roomViewModel: RoomViewModel
): List<BaseViewModel<*>> =
withContext(CommonPool) {
val list = ArrayList<BaseViewModel<*>>()
private suspend fun translate(message: Message, roomViewModel: RoomViewModel)
: List<BaseViewModel<*>> = withContext(CommonPool) {
val list = ArrayList<BaseViewModel<*>>()
message.urls?.forEach {
val url = mapUrl(message, it)
url?.let { list.add(url) }
}
message.urls?.forEach {
val url = mapUrl(message, it)
url?.let { list.add(url) }
}
message.attachments?.forEach {
val attachment = mapAttachment(message, it)
attachment?.let { list.add(attachment) }
}
message.attachments?.forEach {
val attachment = mapAttachment(message, it)
attachment?.let { list.add(attachment) }
}
mapMessage(message).let {
if (list.isNotEmpty()) {
it.preview = list.first().preview
}
list.add(it)
}
mapMessage(message).let {
if (list.isNotEmpty()) {
it.preview = list.first().preview
for (i in list.size - 1 downTo 0) {
val next = if (i - 1 < 0) null else list[i - 1]
list[i].nextDownStreamMessage = next
}
if (isBroadcastReplyAvailable(roomViewModel, message)) {
roomsInteractor.getById(currentServer, message.roomId)?.let { chatRoom ->
val replyViewModel = mapMessageReply(message, chatRoom)
list.first().nextDownStreamMessage = replyViewModel
list.add(0, replyViewModel)
}
}
list.add(it)
}
for (i in list.size - 1 downTo 0) {
val next = if (i - 1 < 0) null else list[i - 1]
list[i].nextDownStreamMessage = next
return@withContext list
}
if (isBroadcastReplyAvailable(roomViewModel, message)) {
roomsInteractor.getById(currentServer, message.roomId)?.let { chatRoom ->
val replyViewModel = mapMessageReply(message, chatRoom)
list.first().nextDownStreamMessage = replyViewModel
list.add(0, replyViewModel)
private suspend fun translateAsNotReversed(
message: Message,
roomViewModel: RoomViewModel
): List<BaseViewModel<*>> =
withContext(CommonPool) {
val list = ArrayList<BaseViewModel<*>>()
mapMessage(message).let {
if (list.isNotEmpty()) {
it.preview = list.first().preview
}
list.add(it)
}
message.attachments?.forEach {
val attachment = mapAttachment(message, it)
attachment?.let {
list.add(attachment)
}
}
}
return@withContext list
}
message.urls?.forEach {
val url = mapUrl(message, it)
url?.let {
list.add(url)
}
}
for (i in list.size - 1 downTo 0) {
val next = if (i - 1 < 0) null else list[i - 1]
list[i].nextDownStreamMessage = next
}
if (isBroadcastReplyAvailable(roomViewModel, message)) {
roomsInteractor.getById(currentServer, message.roomId)?.let { chatRoom ->
val replyViewModel = mapMessageReply(message, chatRoom)
list.first().nextDownStreamMessage = replyViewModel
list.add(0, replyViewModel)
}
}
list.dropLast(1).forEach {
it.reactions = emptyList()
}
list.last().reactions = getReactions(message)
list.last().nextDownStreamMessage = null
return@withContext list
}
private fun isBroadcastReplyAvailable(roomViewModel: RoomViewModel, message: Message): Boolean {
val senderUsername = message.sender?.username
return roomViewModel.isRoom && roomViewModel.isBroadcast &&
!message.isSystemMessage() &&
senderUsername != currentUsername
!message.isSystemMessage() &&
senderUsername != currentUsername
}
private fun mapMessageReply(message: Message, chatRoom: ChatRoom): MessageReplyViewModel {
val name = message.sender?.name
val roomName = if (settings.useRealName() && name != null) name else message.sender?.username
?: ""
val roomName =
if (settings.useRealName() && name != null) name else message.sender?.username ?: ""
val permalink = messageHelper.createPermalink(message, chatRoom)
return MessageReplyViewModel(
messageId = message.id,
......
......@@ -34,6 +34,7 @@ import chat.rocket.common.model.SimpleUser
import chat.rocket.common.model.User
import chat.rocket.common.util.ifNull
import chat.rocket.core.internal.model.Subscription
import chat.rocket.core.internal.realtime.createDirectMessage
import chat.rocket.core.internal.realtime.socket.model.State
import chat.rocket.core.internal.realtime.socket.model.StreamMessage
import chat.rocket.core.internal.realtime.socket.model.Type
......@@ -124,8 +125,19 @@ class ChatRoomsPresenter @Inject constructor(
if (myself?.username == null) {
view.showMessage(R.string.msg_generic_error)
} else {
val id = if (isDirectMessage && !chatRoom.open) {
retryIO("createDirectMessage(${chatRoom.name})") {
client.createDirectMessage(chatRoom.name)
}
val fromTo = mutableListOf(myself.id, chatRoom.id).apply {
sort()
}
fromTo.joinToString("")
} else {
chatRoom.id
}
val isChatRoomOwner = chatRoom.user?.username == myself.username || isDirectMessage
navigator.toChatRoom(chatRoom.id, roomName,
navigator.toChatRoom(id, roomName,
chatRoom.type.toString(), chatRoom.readonly ?: false,
chatRoom.lastSeen ?: -1,
chatRoom.open, isChatRoomOwner)
......@@ -210,7 +222,7 @@ class ChatRoomsPresenter @Inject constructor(
} else {
null
},
name = it.name ?: "",
name = it.username ?: it.name ?: "",
fullName = it.name,
readonly = false,
updatedAt = null,
......@@ -640,4 +652,10 @@ class ChatRoomsPresenter @Inject constructor(
manager.removeRoomsAndSubscriptionsChannel(subscriptionsChannel)
manager.removeActiveUserChannel(activeUserChannel)
}
fun goToChatRoomWithId(chatRoomId: String) {
launchUI(strategy) {
chatRoomsInteractor.getById(currentServer, chatRoomId)?.let { loadChatRoom(it) }
}
}
}
\ No newline at end of file
package chat.rocket.android.chatrooms.ui
import android.app.AlertDialog
import android.content.Context
import android.content.SharedPreferences
import android.os.Bundle
import android.os.Handler
import android.support.v4.app.Fragment
......@@ -11,7 +9,12 @@ import android.support.v7.util.DiffUtil
import android.support.v7.widget.DefaultItemAnimator
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.SearchView
import android.view.*
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.widget.CheckBox
import android.widget.RadioGroup
import androidx.core.view.isVisible
......@@ -24,7 +27,12 @@ import chat.rocket.android.helper.SharedPreferenceHelper
import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.domain.SettingsRepository
import chat.rocket.android.util.extensions.*
import chat.rocket.android.util.extensions.fadeIn
import chat.rocket.android.util.extensions.fadeOut
import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.setVisible
import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.ui
import chat.rocket.android.widget.DividerItemDecoration
import chat.rocket.common.model.RoomType
import chat.rocket.core.internal.realtime.socket.model.State
......@@ -36,6 +44,8 @@ import kotlinx.coroutines.experimental.NonCancellable.isActive
import timber.log.Timber
import javax.inject.Inject
private const val BUNDLE_CHAT_ROOM_ID = "BUNDLE_CHAT_ROOM_ID"
class ChatRoomsFragment : Fragment(), ChatRoomsView {
@Inject
lateinit var presenter: ChatRoomsPresenter
......@@ -50,15 +60,30 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
private var listJob: Job? = null
private var sectionedAdapter: SimpleSectionedRecyclerViewAdapter? = null
private var chatRoomId: String? = null
companion object {
fun newInstance() = ChatRoomsFragment()
fun newInstance(chatRoomId: String? = null): ChatRoomsFragment {
return ChatRoomsFragment().apply {
arguments = Bundle(1).apply {
putString(BUNDLE_CHAT_ROOM_ID, chatRoomId)
}
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
AndroidSupportInjection.inject(this)
setHasOptionsMenu(true)
val bundle = arguments
if (bundle != null) {
chatRoomId = bundle.getString(BUNDLE_CHAT_ROOM_ID)
chatRoomId?.let {
presenter.goToChatRoomWithId(it)
chatRoomId = null
}
}
}
override fun onDestroy() {
......
......@@ -2,10 +2,10 @@ package chat.rocket.android.dagger.module
import chat.rocket.android.chatroom.di.MessageServiceProvider
import chat.rocket.android.chatroom.service.MessageService
import chat.rocket.android.push.FirebaseMessagingService
import chat.rocket.android.push.FirebaseTokenService
import chat.rocket.android.push.GcmListenerService
import chat.rocket.android.push.di.FirebaseMessagingServiceProvider
import chat.rocket.android.push.di.FirebaseTokenServiceProvider
import chat.rocket.android.push.di.GcmListenerServiceProvider
import dagger.Module
import dagger.android.ContributesAndroidInjector
......@@ -14,8 +14,8 @@ import dagger.android.ContributesAndroidInjector
@ContributesAndroidInjector(modules = [FirebaseTokenServiceProvider::class])
abstract fun bindFirebaseTokenService(): FirebaseTokenService
@ContributesAndroidInjector(modules = [GcmListenerServiceProvider::class])
abstract fun bindGcmListenerService(): GcmListenerService
@ContributesAndroidInjector(modules = [FirebaseMessagingServiceProvider::class])
abstract fun bindGcmListenerService(): FirebaseMessagingService
@ContributesAndroidInjector(modules = [MessageServiceProvider::class])
abstract fun bindMessageService(): MessageService
......
......@@ -25,9 +25,9 @@ class FavoriteMessagesPresenter @Inject constructor(
private var offset: Int = 0
/**
* Loads all favorite messages for room. the given room id.
* Loads all favorite messages for the given room id.
*
* @param roomId The id of the room to get its favorite messages.
* @param roomId The id of the room to get favorite messages from.
*/
fun loadFavoriteMessages(roomId: String) {
launchUI(strategy) {
......@@ -35,7 +35,7 @@ class FavoriteMessagesPresenter @Inject constructor(
view.showLoading()
roomsInteractor.getById(serverUrl, roomId)?.let {
val favoriteMessages = client.getFavoriteMessages(roomId, it.type, offset)
val messageList = mapper.map(favoriteMessages.result)
val messageList = mapper.map(favoriteMessages.result, asNotReversed = true)
view.showFavoriteMessages(messageList)
offset += 1 * 30
}.ifNull {
......
......@@ -72,7 +72,7 @@ class FavoriteMessagesFragment : Fragment(), FavoriteMessagesView {
LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
recycler_view.layoutManager = linearLayoutManager
recycler_view.itemAnimator = DefaultItemAnimator()
if (favoriteMessages.size > 10) {
if (favoriteMessages.size >= 30) {
recycler_view.addOnScrollListener(object :
EndlessRecyclerViewScrollListener(linearLayoutManager) {
override fun onLoadMore(
......
......@@ -94,17 +94,13 @@ class FilesFragment : Fragment(), FilesView {
override fun playMedia(url: String) {
ui {
activity?.let {
PlayerActivity.play(it, url)
}
PlayerActivity.play(it, url)
}
}
override fun openImage(url: String, name: String) {
ui {
activity?.let {
ImageHelper.openImage(it, url, name)
}
ImageHelper.openImage(root_layout.context, url, name)
}
}
......
......@@ -41,9 +41,10 @@ class FileViewModel(
}
private fun getFileUploadDate(): String {
return DateTimeHelper.getDateTime(
DateTimeHelper.getLocalDateTime(genericAttachment.uploadedAt)
)
genericAttachment.uploadedAt?.let {
return DateTimeHelper.getDateTime(DateTimeHelper.getLocalDateTime(it))
}
return ""
}
private fun getFileUrl(): String? {
......
......@@ -53,7 +53,6 @@ object ImageHelper {
)
val toolbar = Toolbar(context).also {
it.inflateMenu(R.menu.image_actions)
it.overflowIcon?.setTint(Color.WHITE)
it.setOnMenuItemClickListener {
return@setOnMenuItemClickListener when (it.itemId) {
R.id.action_save_image -> saveImage(context)
......@@ -109,7 +108,6 @@ object ImageHelper {
.hideStatusBar(false)
.setCustomDraweeControllerBuilder(builder)
.show()
}
private fun saveImage(context: Context): Boolean {
......@@ -166,5 +164,4 @@ object ImageHelper {
)
}
}
}
\ No newline at end of file
package chat.rocket.android.helper
import android.app.Activity
import android.content.IntentSender
import android.support.v4.app.FragmentActivity
import com.google.android.gms.auth.api.credentials.*
import com.google.android.gms.common.api.CommonStatusCodes
import com.google.android.gms.common.api.ResolvableApiException
import timber.log.Timber
const val REQUEST_CODE_FOR_SIGN_IN_REQUIRED = 1
const val REQUEST_CODE_FOR_MULTIPLE_ACCOUNTS_RESOLUTION = 2
const val REQUEST_CODE_FOR_SAVE_RESOLUTION = 3
/**
* This class handles some cases of Google Smart Lock for passwords like the request to retrieve
* credentials, to retrieve sign-in hints and to store the credentials.
*
* See https://developers.google.com/identity/smartlock-passwords/android/overview for futher
* information.
*/
object SmartLockHelper {
/**
* Requests for stored Google Smart Lock credentials.
* Note that in case of exception it will try to start a sign in
* ([REQUEST_CODE_FOR_SIGN_IN_REQUIRED]) or "multiple account"
* ([REQUEST_CODE_FOR_MULTIPLE_ACCOUNTS_RESOLUTION]) resolution.
*
* @param credentialsClient The credential client.
* @param activity The activity.
* @return null or the [Credential] result.
*/
fun requestStoredCredentials(
credentialsClient: CredentialsClient,
activity: Activity
): Credential? {
var credential: Credential? = null
val credentialRequest = CredentialRequest.Builder()
.setPasswordLoginSupported(true)
.build()
credentialsClient.request(credentialRequest)
.addOnCompleteListener {
when {
it.isSuccessful -> {
credential = it.result.credential
}
it.exception is ResolvableApiException -> {
val resolvableApiException = (it.exception as ResolvableApiException)
if (resolvableApiException.statusCode == CommonStatusCodes.SIGN_IN_REQUIRED) {
provideSignInHint(credentialsClient, activity)
} else {
// This is most likely the case where the user has multiple saved
// credentials and needs to pick one. This requires showing UI to
// resolve the read request.
resolveResult(
resolvableApiException,
REQUEST_CODE_FOR_MULTIPLE_ACCOUNTS_RESOLUTION,
activity
)
}
}
}
}
return credential
}
/**
* Saves a user credential to Google Smart Lock.
* Note that in case of exception it will try to start a save resolution,
* so the activity/fragment should expected for a request code
* ([REQUEST_CODE_FOR_SAVE_RESOLUTION]) on onActivityResult call.
*
* @param credentialsClient The credential client.
* @param activity The activity.
* @param id The user id credential.
* @param password The user password credential.
*/
fun save(
credentialsClient: CredentialsClient,
activity: FragmentActivity,
id: String,
password: String
) {
val credential = Credential.Builder(id)
.setPassword(password)
.build()
credentialsClient.save(credential)
.addOnCompleteListener {
val exception = it.exception
if (exception is ResolvableApiException) {
// Try to resolve the save request. This will prompt the user if
// the credential is new.
try {
exception.startResolutionForResult(
activity,
REQUEST_CODE_FOR_SAVE_RESOLUTION
)
} catch (e: IntentSender.SendIntentException) {
Timber.e("Failed to send resolution. Exception is: $e")
}
}
}
}
private fun provideSignInHint(credentialsClient: CredentialsClient, activity: Activity) {
val hintRequest = HintRequest.Builder()
.setHintPickerConfig(
CredentialPickerConfig.Builder()
.setShowCancelButton(true)
.build()
)
.setEmailAddressIdentifierSupported(true)
.build()
try {
val intent = credentialsClient.getHintPickerIntent(hintRequest)
activity.startIntentSenderForResult(
intent.intentSender,
REQUEST_CODE_FOR_SIGN_IN_REQUIRED,
null,
0,
0,
0,
null
)
} catch (e: IntentSender.SendIntentException) {
Timber.e("Could not start hint picker Intent. Exception is: $e")
}
}
private fun resolveResult(
exception: ResolvableApiException,
requestCode: Int,
activity: Activity
) {
try {
exception.startResolutionForResult(activity, requestCode)
} catch (e: IntentSender.SendIntentException) {
Timber.e("Failed to send resolution. Exception is: $e")
}
}
}
\ No newline at end of file
......@@ -12,9 +12,9 @@ import chat.rocket.android.util.extensions.addFragment
class MainNavigator(internal val activity: MainActivity) {
fun toChatList() {
fun toChatList(chatRoomId: String? = null) {
activity.addFragment("ChatRoomsFragment", R.id.fragment_container) {
ChatRoomsFragment.newInstance()
ChatRoomsFragment.newInstance(chatRoomId)
}
}
......@@ -43,7 +43,7 @@ class MainNavigator(internal val activity: MainActivity) {
}
fun toNewServer(serverUrl: String? = null) {
activity.startActivity(activity.changeServerIntent(serverUrl))
activity.startActivity(activity.changeServerIntent(serverUrl = serverUrl))
activity.finish()
}
......
......@@ -49,7 +49,7 @@ class MainPresenter @Inject constructor(
private val userDataChannel = Channel<Myself>()
fun toChatList() = navigator.toChatList()
fun toChatList(chatRoomId: String? = null) = navigator.toChatList(chatRoomId)
fun toUserProfile() = navigator.toUserProfile()
......@@ -105,13 +105,10 @@ class MainPresenter @Inject constructor(
disconnect()
removeAccountInteractor.remove(currentServer)
tokenRepository.remove(currentServer)
view.disableAutoSignIn()
navigator.toNewServer()
} catch (ex: Exception) {
Timber.d(ex, "Error cleaning up the session...")
}
view.disableAutoSignIn()
navigator.toNewServer()
}
}
......@@ -178,6 +175,7 @@ class MainPresenter @Inject constructor(
if (pushToken != null) {
try {
retryIO("unregisterPushToken") { client.unregisterPushToken(pushToken) }
view.invalidateToken(pushToken)
} catch (ex: Exception) {
Timber.d(ex, "Error unregistering push token")
}
......
......@@ -25,8 +25,5 @@ interface MainView : MessageView, VersionCheckView {
fun closeServerSelection()
/**
* callback to disable auto sign in for google smart lock when the user logs out
*/
fun disableAutoSignIn()
fun invalidateToken(token: String)
}
\ No newline at end of file
......@@ -18,15 +18,15 @@ import chat.rocket.android.main.presentation.MainPresenter
import chat.rocket.android.main.presentation.MainView
import chat.rocket.android.main.viewmodel.NavHeaderViewModel
import chat.rocket.android.server.domain.model.Account
import chat.rocket.android.server.ui.INTENT_CHAT_ROOM_ID
import chat.rocket.android.util.extensions.fadeIn
import chat.rocket.android.util.extensions.fadeOut
import chat.rocket.android.util.extensions.rotateBy
import chat.rocket.android.util.extensions.showToast
import chat.rocket.common.model.UserStatus
import com.google.android.gms.auth.api.Auth
import com.google.firebase.iid.FirebaseInstanceId
import com.google.firebase.messaging.FirebaseMessaging
import com.google.android.gms.common.api.GoogleApiClient
import com.google.android.gms.gcm.GoogleCloudMessaging
import com.google.android.gms.iid.InstanceID
import dagger.android.AndroidInjection
import dagger.android.AndroidInjector
import dagger.android.DispatchingAndroidInjector
......@@ -40,8 +40,10 @@ import kotlinx.coroutines.experimental.launch
import timber.log.Timber
import javax.inject.Inject
class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupportFragmentInjector,
GoogleApiClient.ConnectionCallbacks {
private const val CURRENT_STATE = "current_state"
class MainActivity : AppCompatActivity(), MainView, HasActivityInjector,
HasSupportFragmentInjector {
@Inject
lateinit var activityDispatchingAndroidInjector: DispatchingAndroidInjector<Activity>
@Inject
......@@ -50,73 +52,46 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupp
lateinit var presenter: MainPresenter
private var isFragmentAdded: Boolean = false
private var expanded = false
private lateinit var googleApiClient: GoogleApiClient
private val headerLayout by lazy { view_navigation.getHeaderView(0) }
private val CURRENT_STATE = "current_state"
private var chatRoomId: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
buildGoogleApiClient()
launch(CommonPool) {
try {
val token = InstanceID.getInstance(this@MainActivity).getToken(
getString(R.string.gcm_sender_id),
GoogleCloudMessaging.INSTANCE_ID_SCOPE,
null
)
Timber.d("GCM token: $token")
val token = FirebaseInstanceId.getInstance().token
Timber.d("FCM token: $token")
presenter.refreshToken(token)
} catch (ex: Exception) {
Timber.d(ex, "Missing play services...")
}
}
chatRoomId = intent.getStringExtra(INTENT_CHAT_ROOM_ID)
presenter.connect()
presenter.loadCurrentInfo()
setupToolbar()
setupNavigationView()
}
override fun onConnected(bundle: Bundle?) {
}
override fun onConnectionSuspended(errorCode: Int) {
}
private fun buildGoogleApiClient() {
googleApiClient = GoogleApiClient.Builder(this)
.enableAutoManage(this, {
Timber.d("ERROR: connection to client failed")
})
.addConnectionCallbacks(this)
.addApi(Auth.CREDENTIALS_API)
.build()
}
override fun onStart() {
super.onStart()
googleApiClient.let {
if (it.isConnected) {
Timber.d("Google api client connected successfully")
}
}
override fun onSaveInstanceState(outState: Bundle?) {
super.onSaveInstanceState(outState)
outState?.putBoolean(CURRENT_STATE, isFragmentAdded)
}
override fun disableAutoSignIn() {
googleApiClient.let {
if (it.isConnected) {
Auth.CredentialsApi.disableAutoSignIn(googleApiClient)
}
}
override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
super.onRestoreInstanceState(savedInstanceState)
isFragmentAdded = savedInstanceState?.getBoolean(CURRENT_STATE) ?: false
}
override fun onResume() {
super.onResume()
if (!isFragmentAdded) {
presenter.toChatList()
presenter.toChatList(chatRoomId)
isFragmentAdded = true
}
}
......@@ -128,6 +103,12 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupp
}
}
override fun activityInjector(): AndroidInjector<Activity> = activityDispatchingAndroidInjector
override fun supportFragmentInjector(): AndroidInjector<Fragment> =
fragmentDispatchingAndroidInjector
override fun showUserStatus(userStatus: UserStatus) {
headerLayout.apply {
image_user_status.setImageDrawable(
......@@ -191,38 +172,8 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupp
.show()
}
private fun setupAccountsList(header: View, accounts: List<Account>) {
accounts_list.layoutManager = LinearLayoutManager(this)
accounts_list.adapter = AccountsAdapter(accounts, object : Selector {
override fun onStatusSelected(userStatus: UserStatus) {
presenter.changeDefaultStatus(userStatus)
}
override fun onAccountSelected(serverUrl: String) {
presenter.changeServer(serverUrl)
}
override fun onAddedAccountSelected() {
presenter.addNewServer()
}
})
header.account_container.setOnClickListener {
header.image_account_expand.rotateBy(180f)
if (expanded) {
accounts_list.fadeOut()
} else {
accounts_list.fadeIn()
}
expanded = !expanded
}
header.image_avatar.setOnClickListener {
view_navigation.menu.findItem(R.id.action_profile).isChecked = true
presenter.toUserProfile()
drawer_layout.closeDrawer(Gravity.START)
}
override fun invalidateToken(token: String) {
FirebaseInstanceId.getInstance().deleteToken(token, FirebaseMessaging.INSTANCE_ID_SCOPE)
}
override fun showMessage(resId: Int) = showToast(resId)
......@@ -231,11 +182,6 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupp
override fun showGenericErrorMessage() = showMessage(getString(R.string.msg_generic_error))
override fun activityInjector(): AndroidInjector<Activity> = activityDispatchingAndroidInjector
override fun supportFragmentInjector(): AndroidInjector<Fragment> =
fragmentDispatchingAndroidInjector
private fun setupToolbar() {
setSupportActionBar(toolbar)
toolbar.setNavigationIcon(R.drawable.ic_menu_white_24dp)
......@@ -270,13 +216,37 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupp
}
}
override fun onSaveInstanceState(outState: Bundle?) {
super.onSaveInstanceState(outState)
outState?.putBoolean(CURRENT_STATE,isFragmentAdded)
}
private fun setupAccountsList(header: View, accounts: List<Account>) {
accounts_list.layoutManager = LinearLayoutManager(this)
accounts_list.adapter = AccountsAdapter(accounts, object : Selector {
override fun onStatusSelected(userStatus: UserStatus) {
presenter.changeDefaultStatus(userStatus)
}
override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
super.onRestoreInstanceState(savedInstanceState)
isFragmentAdded = savedInstanceState?.getBoolean(CURRENT_STATE) ?: false
override fun onAccountSelected(serverUrl: String) {
presenter.changeServer(serverUrl)
}
override fun onAddedAccountSelected() {
presenter.addNewServer()
}
})
header.account_container.setOnClickListener {
header.image_account_expand.rotateBy(180f)
if (expanded) {
accounts_list.fadeOut()
} else {
accounts_list.fadeIn()
}
expanded = !expanded
}
header.image_avatar.setOnClickListener {
view_navigation.menu.findItem(R.id.action_profile).isChecked = true
presenter.toUserProfile()
drawer_layout.closeDrawer(Gravity.START)
}
}
}
\ No newline at end of file
......@@ -26,7 +26,7 @@ class PinnedMessagesPresenter @Inject constructor(
private var offset: Int = 0
/**
* Load all pinned messages for the given room id.
* Loads all pinned messages for the given room id.
*
* @param roomId The id of the room to get pinned messages from.
*/
......@@ -36,8 +36,7 @@ class PinnedMessagesPresenter @Inject constructor(
view.showLoading()
roomsInteractor.getById(serverUrl, roomId)?.let {
val pinnedMessages = client.getPinnedMessages(roomId, it.type, offset)
val messageList =
mapper.map(pinnedMessages.result.filterNot { it.isSystemMessage() })
val messageList = mapper.map(pinnedMessages.result, asNotReversed = true)
view.showPinnedMessages(messageList)
offset += 1 * 30
}.ifNull {
......
......@@ -74,7 +74,7 @@ class PinnedMessagesFragment : Fragment(), PinnedMessagesView {
LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
recycler_view_pinned.layoutManager = linearLayoutManager
recycler_view_pinned.itemAnimator = DefaultItemAnimator()
if (pinnedMessages.size > 10) {
if (pinnedMessages.size >= 30) {
recycler_view_pinned.addOnScrollListener(object :
EndlessRecyclerViewScrollListener(linearLayoutManager) {
override fun onLoadMore(
......
......@@ -58,7 +58,8 @@ class ProfilePresenter @Inject constructor(private val view: ProfileView,
if(avatarUrl!="") {
retryIO { client.setAvatar(avatarUrl) }
}
val user = retryIO { client.updateProfile(myselfId, email, name, username) }
val user = retryIO { client.updateProfile(
userId = myselfId, email = email, name = name, username = username) }
view.showProfileUpdateSuccessfullyMessage()
loadUserProfile()
} catch (exception: RocketChatException) {
......
package chat.rocket.android.push
import android.os.Bundle
import com.google.android.gms.gcm.GcmListenerService
import androidx.core.os.bundleOf
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import dagger.android.AndroidInjection
import javax.inject.Inject
class GcmListenerService : GcmListenerService() {
class FirebaseMessagingService : FirebaseMessagingService() {
@Inject
lateinit var pushManager: PushManager
......@@ -15,9 +16,9 @@ class GcmListenerService : GcmListenerService() {
AndroidInjection.inject(this)
}
override fun onMessageReceived(from: String?, data: Bundle?) {
data?.let {
pushManager.handle(data)
override fun onMessageReceived(message: RemoteMessage) {
message.data?.let {
pushManager.handle(bundleOf(*(it.map { Pair(it.key, it.value) }).toTypedArray()))
}
}
}
\ No newline at end of file
package chat.rocket.android.push
import chat.rocket.android.R
import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatException
import chat.rocket.core.internal.rest.registerPushToken
import com.google.android.gms.gcm.GoogleCloudMessaging
import com.google.android.gms.iid.InstanceID
import com.google.firebase.iid.FirebaseInstanceId
import com.google.firebase.iid.FirebaseInstanceIdService
import dagger.android.AndroidInjection
import kotlinx.coroutines.experimental.launch
......@@ -31,21 +29,18 @@ class FirebaseTokenService : FirebaseInstanceIdService() {
}
override fun onTokenRefresh() {
//TODO: We need to use the Cordova Project gcm_sender_id since it's the one configured on RC
// default push gateway. We should register this project's own project sender id into it.
try {
val gcmToken = InstanceID.getInstance(this)
.getToken(getString(R.string.gcm_sender_id), GoogleCloudMessaging.INSTANCE_ID_SCOPE, null)
val fcmToken = FirebaseInstanceId.getInstance().token
val currentServer = getCurrentServerInteractor.get()
val client = currentServer?.let { factory.create(currentServer) }
gcmToken?.let {
localRepository.save(LocalRepository.KEY_PUSH_TOKEN, gcmToken)
fcmToken?.let {
localRepository.save(LocalRepository.KEY_PUSH_TOKEN, fcmToken)
client?.let {
launch {
try {
Timber.d("Registering push token: $gcmToken for ${client.url}")
retryIO("register push token") { client.registerPushToken(gcmToken) }
Timber.d("Registering push token: $fcmToken for ${client.url}")
retryIO("register push token") { client.registerPushToken(fcmToken) }
} catch (ex: RocketChatException) {
Timber.e(ex, "Error registering push token")
}
......@@ -53,7 +48,7 @@ class FirebaseTokenService : FirebaseInstanceIdService() {
}
}
} catch (ex: Exception) {
Timber.d(ex, "Error refreshing Firebase TOKEN")
Timber.e(ex, "Error refreshing Firebase TOKEN")
}
}
}
\ No newline at end of file
package chat.rocket.android.push.di
import chat.rocket.android.dagger.module.AppModule
import chat.rocket.android.push.GcmListenerService
import chat.rocket.android.push.FirebaseMessagingService
import dagger.Module
import dagger.android.ContributesAndroidInjector
@Module abstract class GcmListenerServiceProvider {
@Module abstract class FirebaseMessagingServiceProvider {
@ContributesAndroidInjector(modules = [AppModule::class])
abstract fun provideGcmListenerService(): GcmListenerService
abstract fun provideFirebaseMessagingService(): FirebaseMessagingService
}
\ No newline at end of file
......@@ -3,16 +3,16 @@ package chat.rocket.android.server.infraestructure
import chat.rocket.common.model.BaseRoom
import chat.rocket.common.model.User
import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.realtime.subscribeSubscriptions
import chat.rocket.core.internal.realtime.subscribeRooms
import chat.rocket.core.internal.realtime.subscribeUserData
import chat.rocket.core.internal.realtime.subscribeActiveUsers
import chat.rocket.core.internal.realtime.subscribeRoomMessages
import chat.rocket.core.internal.realtime.unsubscribe
import chat.rocket.core.internal.realtime.socket.connect
import chat.rocket.core.internal.realtime.socket.disconnect
import chat.rocket.core.internal.realtime.socket.model.State
import chat.rocket.core.internal.realtime.socket.model.StreamMessage
import chat.rocket.core.internal.realtime.subscribeActiveUsers
import chat.rocket.core.internal.realtime.subscribeRoomMessages
import chat.rocket.core.internal.realtime.subscribeRooms
import chat.rocket.core.internal.realtime.subscribeSubscriptions
import chat.rocket.core.internal.realtime.subscribeUserData
import chat.rocket.core.internal.realtime.unsubscribe
import chat.rocket.core.internal.rest.chatRooms
import chat.rocket.core.model.Message
import chat.rocket.core.model.Myself
......
......@@ -4,6 +4,7 @@ import android.content.Intent
import chat.rocket.android.authentication.ui.newServerIntent
import chat.rocket.android.main.ui.MainActivity
import chat.rocket.android.server.ui.ChangeServerActivity
import chat.rocket.android.server.ui.INTENT_CHAT_ROOM_ID
class ChangeServerNavigator (internal val activity: ChangeServerActivity) {
fun toServerScreen() {
......@@ -11,8 +12,10 @@ class ChangeServerNavigator (internal val activity: ChangeServerActivity) {
activity.finish()
}
fun toChatRooms() {
activity.startActivity(Intent(activity, MainActivity::class.java))
fun toChatRooms(chatRoomId: String? = null) {
activity.startActivity(Intent(activity, MainActivity::class.java).also {
it.putExtra(INTENT_CHAT_ROOM_ID, chatRoomId)
})
activity.finish()
}
......
......@@ -21,7 +21,7 @@ class ChangeServerPresenter @Inject constructor(
private val localRepository: LocalRepository,
private val connectionManager: ConnectionManagerFactory
) {
fun loadServer(newUrl: String?) {
fun loadServer(newUrl: String?, chatRoomId: String? = null) {
launchUI(strategy) {
view.showProgress()
var url = newUrl
......@@ -56,7 +56,7 @@ class ChangeServerPresenter @Inject constructor(
saveCurrentServerInteractor.save(serverUrl)
view.hideProgress()
navigator.toChatRooms()
navigator.toChatRooms(chatRoomId)
}.ifNull {
view.hideProgress()
navigator.toServerScreen()
......
......@@ -21,7 +21,8 @@ class ChangeServerActivity : AppCompatActivity(), ChangeServerView {
super.onCreate(savedInstanceState)
val serverUrl: String? = intent.getStringExtra(INTENT_SERVER_URL)
presenter.loadServer(serverUrl)
val chatRoomId: String? = intent.getStringExtra(INTENT_CHAT_ROOM_ID)
presenter.loadServer(serverUrl, chatRoomId)
}
override fun showInvalidCredentials() {
......@@ -40,11 +41,13 @@ class ChangeServerActivity : AppCompatActivity(), ChangeServerView {
private const val INTENT_SERVER_URL = "INTENT_SERVER_URL"
private const val INTENT_CHAT_ROOM_NAME = "INTENT_CHAT_ROOM_NAME"
private const val INTENT_CHAT_ROOM_TYPE = "INTENT_CHAT_ROOM_TYPE"
const val INTENT_CHAT_ROOM_ID = "INTENT_CHAT_ROOM_ID"
fun Context.changeServerIntent(serverUrl: String? = null): Intent {
fun Context.changeServerIntent(serverUrl: String? = null, chatRoomId: String? = ""): Intent {
return Intent(this, ChangeServerActivity::class.java).apply {
serverUrl?.let { url ->
putExtra(INTENT_SERVER_URL, url)
putExtra(INTENT_CHAT_ROOM_ID, chatRoomId)
}
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
......
......@@ -32,6 +32,18 @@
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_headline" />
<ImageView
android:id="@+id/image_key"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:src="@drawable/ic_vpn_key_black_24dp"
android:tint="@color/colorDrawableTintGrey"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@+id/text_username_or_email"
app:layout_constraintEnd_toEndOf="@+id/text_username_or_email"
app:layout_constraintTop_toTopOf="@+id/text_username_or_email" />
<EditText
android:id="@+id/text_password"
style="@style/Authentication.EditText"
......
......@@ -2,6 +2,7 @@
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/root_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".files.ui.FilesFragment">
......
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- This is the Cordova GCM sender id-->
<string name="gcm_sender_id" translatable="false">673693445664</string>
</resources>
\ No newline at end of file
File added
......@@ -14,7 +14,8 @@ ext {
androidKtx : '0.3',
dagger : '2.14.1',
exoPlayer : '2.6.0',
playServices : '11.8.0',
playServices : '15.0.0',
firebase : '15.0.0',
room : '1.0.0',
lifecycle : '1.1.1',
rxKotlin : '2.2.0',
......@@ -63,7 +64,7 @@ ext {
daggerSupport : "com.google.dagger:dagger-android-support:${versions.dagger}",
daggerProcessor : "com.google.dagger:dagger-compiler:${versions.dagger}",
daggerAndroidApt : "com.google.dagger:dagger-android-processor:${versions.dagger}",
playServicesGcm : "com.google.android.gms:play-services-gcm:${versions.playServices}",
fcm : "com.google.firebase:firebase-messaging:${versions.firebase}",
playServicesAuth : "com.google.android.gms:play-services-auth:${versions.playServices}",
exoPlayer : "com.google.android.exoplayer:exoplayer:${versions.exoPlayer}",
......@@ -102,19 +103,19 @@ ext {
aVLoadingIndicatorView: "com.wang.avi:library:${versions.aVLoadingIndicatorView}",
// For testing
junit : "junit:junit:$versions.junit",
espressoCore : "com.android.support.test.espresso:espresso-core:${versions.espresso}",
espressoIntents : "com.android.support.test.espresso:espresso-intents:${versions.espresso}",
roomTest : "android.arch.persistence.room:testing:${versions.room}",
truth : "com.google.truth:truth:$versions.truth",
//For the wear app
wearable : "com.google.android.support:wearable:${versions.wear}",
playServicesWearable : "com.google.android.gms:play-services-wearable:${versions.playServicesWearable}",
percentLayout : "com.android.support:percent:${versions.supportWearable}",
supportWearable : "com.android.support:support-v4:${versions.supportWearable}",
wearableRecyclerView : "com.android.support:recyclerview-v7:${versions.supportWearable}",
wearSupport : "com.android.support:wear:${versions.supportWearable}"
wearSupport : "com.android.support:wear:${versions.supportWearable}",
// For testing
junit : "junit:junit:$versions.junit",
espressoCore : "com.android.support.test.espresso:espresso-core:${versions.espresso}",
espressoIntents : "com.android.support.test.espresso:espresso-intents:${versions.espresso}",
roomTest : "android.arch.persistence.room:testing:${versions.room}",
truth : "com.google.truth:truth:$versions.truth"
]
}
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