Commit 31b9a410 authored by Lucio Maciel's avatar Lucio Maciel

More improvements on the Socket connection/reconnection

parent 3016ef5b
...@@ -60,6 +60,8 @@ dependencies { ...@@ -60,6 +60,8 @@ dependencies {
implementation libraries.constraintLayout implementation libraries.constraintLayout
implementation libraries.cardView implementation libraries.cardView
implementation libraries.androidKtx
implementation libraries.dagger implementation libraries.dagger
implementation libraries.daggerSupport implementation libraries.daggerSupport
kapt libraries.daggerProcessor kapt libraries.daggerProcessor
......
...@@ -18,6 +18,7 @@ import chat.rocket.core.internal.rest.* ...@@ -18,6 +18,7 @@ import chat.rocket.core.internal.rest.*
import chat.rocket.core.model.Message import chat.rocket.core.model.Message
import chat.rocket.core.model.Value import chat.rocket.core.model.Value
import kotlinx.coroutines.experimental.CommonPool import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.android.UI
import kotlinx.coroutines.experimental.async import kotlinx.coroutines.experimental.async
import kotlinx.coroutines.experimental.channels.Channel import kotlinx.coroutines.experimental.channels.Channel
import kotlinx.coroutines.experimental.launch import kotlinx.coroutines.experimental.launch
...@@ -73,6 +74,10 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView, ...@@ -73,6 +74,10 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
} finally { } finally {
view.hideLoading() view.hideLoading()
} }
if (offset == 0L) {
subscribeState()
}
} }
} }
...@@ -146,6 +151,26 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView, ...@@ -146,6 +151,26 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
} }
} }
private fun subscribeState() {
Timber.d("Subscribing to Status changes")
lastState = manager.state
manager.addStatusChannel(stateChannel)
launch(CommonPool + strategy.jobs) {
for (state in stateChannel) {
Timber.d("Got new state: $state - last: $lastState")
if (state != lastState) {
launch(UI) {
view.showConnectionState(state)
}
if (state is State.Connected) {
loadMissingMessages()
}
}
lastState = state
}
}
}
private fun subscribeMessages(roomId: String) { private fun subscribeMessages(roomId: String) {
manager.subscribeRoomMessages(roomId, messagesChannel) manager.subscribeRoomMessages(roomId, messagesChannel)
...@@ -156,18 +181,6 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView, ...@@ -156,18 +181,6 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
updateMessage(message) updateMessage(message)
} }
} }
lastState = manager.state
manager.addStatusChannel(stateChannel)
launch(CommonPool + strategy.jobs) {
for (state in stateChannel) {
Timber.d("Got new state: $state - last: $lastState")
if (state is State.Connected && lastState != state) {
loadMissingMessages()
}
lastState = state
}
}
} }
private fun loadMissingMessages() { private fun loadMissingMessages() {
......
...@@ -4,6 +4,7 @@ import android.net.Uri ...@@ -4,6 +4,7 @@ import android.net.Uri
import chat.rocket.android.chatroom.viewmodel.BaseViewModel import chat.rocket.android.chatroom.viewmodel.BaseViewModel
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.State
interface ChatRoomView : LoadingView, MessageView { interface ChatRoomView : LoadingView, MessageView {
...@@ -97,4 +98,6 @@ interface ChatRoomView : LoadingView, MessageView { ...@@ -97,4 +98,6 @@ interface ChatRoomView : LoadingView, MessageView {
fun clearMessageComposition() fun clearMessageComposition()
fun showInvalidFileSize(fileSize: Int, maxFileSize: Int) fun showInvalidFileSize(fileSize: Int, maxFileSize: Int)
fun showConnectionState(state: State)
} }
\ No newline at end of file
...@@ -6,6 +6,8 @@ import android.os.Bundle ...@@ -6,6 +6,8 @@ import android.os.Bundle
import android.support.v4.app.Fragment import android.support.v4.app.Fragment
import android.support.v7.app.AppCompatActivity import android.support.v7.app.AppCompatActivity
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import chat.rocket.android.util.extensions.addFragment import chat.rocket.android.util.extensions.addFragment
import chat.rocket.android.util.extensions.textContent import chat.rocket.android.util.extensions.textContent
import dagger.android.AndroidInjection import dagger.android.AndroidInjection
...@@ -32,6 +34,11 @@ private const val INTENT_IS_CHAT_ROOM_READ_ONLY = "is_chat_room_read_only" ...@@ -32,6 +34,11 @@ private const val INTENT_IS_CHAT_ROOM_READ_ONLY = "is_chat_room_read_only"
class ChatRoomActivity : AppCompatActivity(), HasSupportFragmentInjector { class ChatRoomActivity : AppCompatActivity(), HasSupportFragmentInjector {
@Inject lateinit var fragmentDispatchingAndroidInjector: DispatchingAndroidInjector<Fragment> @Inject lateinit var fragmentDispatchingAndroidInjector: DispatchingAndroidInjector<Fragment>
// TODO - workaround for now... We will move to a single activity
@Inject lateinit var serverInteractor: GetCurrentServerInteractor
@Inject lateinit var managerFactory: ConnectionManagerFactory
private lateinit var chatRoomId: String private lateinit var chatRoomId: String
private lateinit var chatRoomName: String private lateinit var chatRoomName: String
private lateinit var chatRoomType: String private lateinit var chatRoomType: String
...@@ -42,6 +49,9 @@ class ChatRoomActivity : AppCompatActivity(), HasSupportFragmentInjector { ...@@ -42,6 +49,9 @@ class ChatRoomActivity : AppCompatActivity(), HasSupportFragmentInjector {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_chat_room) setContentView(R.layout.activity_chat_room)
// Workaround for when we are coming to the app via the recents app and the app was killed.
managerFactory.create(serverInteractor.get()!!).connect()
chatRoomId = intent.getStringExtra(INTENT_CHAT_ROOM_ID) chatRoomId = intent.getStringExtra(INTENT_CHAT_ROOM_ID)
requireNotNull(chatRoomId) { "no chat_room_id provided in Intent extras" } requireNotNull(chatRoomId) { "no chat_room_id provided in Intent extras" }
......
...@@ -27,6 +27,7 @@ import chat.rocket.android.widget.emoji.ComposerEditText ...@@ -27,6 +27,7 @@ import chat.rocket.android.widget.emoji.ComposerEditText
import chat.rocket.android.widget.emoji.Emoji import chat.rocket.android.widget.emoji.Emoji
import chat.rocket.android.widget.emoji.EmojiKeyboardPopup import chat.rocket.android.widget.emoji.EmojiKeyboardPopup
import chat.rocket.android.widget.emoji.EmojiParser import chat.rocket.android.widget.emoji.EmojiParser
import chat.rocket.core.internal.realtime.State
import dagger.android.support.AndroidSupportInjection import dagger.android.support.AndroidSupportInjection
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import kotlinx.android.synthetic.main.fragment_chat_room.* import kotlinx.android.synthetic.main.fragment_chat_room.*
...@@ -290,6 +291,28 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardPopup.Listener { ...@@ -290,6 +291,28 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardPopup.Listener {
showMessage(getString(R.string.max_file_size_exceeded, fileSize, maxFileSize)) showMessage(getString(R.string.max_file_size_exceeded, fileSize, maxFileSize))
} }
override fun showConnectionState(state: State) {
activity?.apply {
connection_status_text.fadeIn()
handler.removeCallbacks(dismissStatus)
when (state) {
is State.Connected -> {
connection_status_text.text = getString(R.string.status_connected)
handler.postDelayed(dismissStatus, 2000)
}
is State.Disconnected -> connection_status_text.text = getString(R.string.status_disconnected)
is State.Connecting -> connection_status_text.text = getString(R.string.status_connecting)
is State.Authenticating -> connection_status_text.text = getString(R.string.status_authenticating)
is State.Disconnecting -> connection_status_text.text = getString(R.string.status_disconnecting)
is State.Waiting -> connection_status_text.text = getString(R.string.status_waiting, state.seconds)
}
}
}
private val dismissStatus = {
connection_status_text.fadeOut()
}
private fun setupRecyclerView() { private fun setupRecyclerView() {
recycler_view.addOnScrollListener(object : RecyclerView.OnScrollListener() { recycler_view.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
......
...@@ -17,6 +17,7 @@ import chat.rocket.core.internal.realtime.Type ...@@ -17,6 +17,7 @@ import chat.rocket.core.internal.realtime.Type
import chat.rocket.core.model.ChatRoom import chat.rocket.core.model.ChatRoom
import chat.rocket.core.model.Room import chat.rocket.core.model.Room
import kotlinx.coroutines.experimental.* import kotlinx.coroutines.experimental.*
import kotlinx.coroutines.experimental.android.UI
import kotlinx.coroutines.experimental.channels.Channel import kotlinx.coroutines.experimental.channels.Channel
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
...@@ -44,15 +45,16 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView, ...@@ -44,15 +45,16 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
refreshSettingsInteractor.refreshAsync(currentServer) refreshSettingsInteractor.refreshAsync(currentServer)
launchUI(strategy) { launchUI(strategy) {
view.showLoading() view.showLoading()
subscribeStatusChange()
try { try {
view.updateChatRooms(loadRooms()) view.updateChatRooms(loadRooms())
subscribeRoomUpdates()
} catch (e: RocketChatException) { } catch (e: RocketChatException) {
Timber.e(e) Timber.e(e)
view.showMessage(e.message!!) view.showMessage(e.message!!)
} finally { } finally {
view.hideLoading() view.hideLoading()
} }
subscribeRoomUpdates()
} }
} }
...@@ -111,48 +113,28 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView, ...@@ -111,48 +113,28 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
} }
} }
// TODO - Temporary stuff, remove when adding DB support private suspend fun subscribeStatusChange() {
private suspend fun subscribeRoomUpdates() { lastState = manager.state
/*client.addStateChannel(stateChannel)
launch(CommonPool + strategy.jobs) { launch(CommonPool + strategy.jobs) {
for (status in stateChannel) { for (state in stateChannel) {
Timber.d("Changing status to: $status") Timber.d("Got new state: $state - last: $lastState")
when (status) { if (state != lastState) {
State.Authenticating -> Timber.d("Authenticating") launch(UI) {
State.Connected -> { view.showConnectionState(state)
Timber.d("Connected")
client.subscribeSubscriptions {
Timber.d("subscriptions: $it")
}
client.subscribeRooms {
Timber.d("rooms: $it")
}
} }
}
}
Timber.d("Done on statusChannel")
}
when (manager.state) {
State.Connected -> {
Timber.d("Already connected")
}
else -> client.connect()
}
launch(CommonPool + strategy.jobs) { if (state is State.Connected) {
for (message in client.roomsChannel) { reloadRooms()
Timber.d("Got message: $message") updateRooms()
updateRoom(message) }
}
lastState = state
} }
} }
}
launch(CommonPool + strategy.jobs) { // TODO - Temporary stuff, remove when adding DB support
for (message in client.subscriptionsChannel) { private suspend fun subscribeRoomUpdates() {
Timber.d("Got message: $message")
updateSubscription(message)
}
}*/
manager.addStatusChannel(stateChannel) manager.addStatusChannel(stateChannel)
manager.addRoomsAndSubscriptionsChannel(subscriptionsChannel) manager.addRoomsAndSubscriptionsChannel(subscriptionsChannel)
launch(CommonPool + strategy.jobs) { launch(CommonPool + strategy.jobs) {
...@@ -164,72 +146,60 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView, ...@@ -164,72 +146,60 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
} }
} }
} }
lastState = manager.state
launch(CommonPool + strategy.jobs) {
for (state in stateChannel) {
Timber.d("Got new state: $state - last: $lastState")
if (state is State.Connected && lastState != state) {
reloadRooms()
updateRooms()
}
lastState = state
}
}
} }
private suspend fun updateRoom(message: StreamMessage<Room>) { private suspend fun updateRoom(message: StreamMessage<Room>) {
Timber.d("Update Room: ${message.type} - ${message.data.id} - ${message.data.name}") Timber.d("Update Room: ${message.type} - ${message.data.id} - ${message.data.name}")
//launchUI(strategy) { when (message.type) {
when (message.type) { Type.Removed -> {
Type.Removed -> { removeRoom(message.data.id)
removeRoom(message.data.id) }
} Type.Updated -> {
Type.Updated -> { updateRoom(message.data)
updateRoom(message.data) }
} Type.Inserted -> {
Type.Inserted -> { // On insertion, just get all chatrooms again, since we can't create one just
// On insertion, just get all chatrooms again, since we can't create one just // from a Room
// from a Room reloadRooms()
reloadRooms()
}
} }
}
updateRooms() updateRooms()
//}
} }
private suspend fun updateSubscription(message: StreamMessage<Subscription>) { private suspend fun updateSubscription(message: StreamMessage<Subscription>) {
Timber.d("Update Subscription: ${message.type} - ${message.data.id} - ${message.data.name}") Timber.d("Update Subscription: ${message.type} - ${message.data.id} - ${message.data.name}")
//launchUI(strategy) { when (message.type) {
when (message.type) { Type.Removed -> {
Type.Removed -> { removeRoom(message.data.roomId)
removeRoom(message.data.roomId) }
} Type.Updated -> {
Type.Updated -> { updateSubscription(message.data)
updateSubscription(message.data) }
} Type.Inserted -> {
Type.Inserted -> { // On insertion, just get all chatrooms again, since we can't create one just
// On insertion, just get all chatrooms again, since we can't create one just // from a Subscription
// from a Subscription reloadRooms()
reloadRooms()
}
} }
}
updateRooms() updateRooms()
//}
} }
private suspend fun reloadRooms() { private suspend fun reloadRooms() {
Timber.d("realoadRooms()") Timber.d("realoadRooms()")
reloadJob?.cancel() reloadJob?.cancel()
reloadJob = async(CommonPool + strategy.jobs) { try {
delay(1000) reloadJob = async(CommonPool + strategy.jobs) {
Timber.d("reloading rooms after wait") delay(1000)
loadRooms() Timber.d("reloading rooms after wait")
loadRooms()
}
reloadJob?.await()
} catch (ex: Exception) {
ex.printStackTrace()
} }
reloadJob?.await()
} }
// Update a ChatRoom with a Room information // Update a ChatRoom with a Room information
......
...@@ -2,6 +2,7 @@ package chat.rocket.android.chatrooms.presentation ...@@ -2,6 +2,7 @@ package chat.rocket.android.chatrooms.presentation
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.State
import chat.rocket.core.model.ChatRoom import chat.rocket.core.model.ChatRoom
interface ChatRoomsView : LoadingView, MessageView { interface ChatRoomsView : LoadingView, MessageView {
...@@ -17,4 +18,6 @@ interface ChatRoomsView : LoadingView, MessageView { ...@@ -17,4 +18,6 @@ interface ChatRoomsView : LoadingView, MessageView {
* Shows no chat rooms to display. * Shows no chat rooms to display.
*/ */
fun showNoChatRoomsToDisplay() fun showNoChatRoomsToDisplay()
fun showConnectionState(state: State)
} }
\ No newline at end of file
package chat.rocket.android.chatrooms.ui package chat.rocket.android.chatrooms.ui
import android.os.Bundle import android.os.Bundle
import android.os.Handler
import android.support.v4.app.Fragment import android.support.v4.app.Fragment
import android.support.v7.app.AppCompatActivity import android.support.v7.app.AppCompatActivity
import android.support.v7.util.DiffUtil import android.support.v7.util.DiffUtil
...@@ -13,10 +14,9 @@ import chat.rocket.android.chatrooms.presentation.ChatRoomsPresenter ...@@ -13,10 +14,9 @@ import chat.rocket.android.chatrooms.presentation.ChatRoomsPresenter
import chat.rocket.android.chatrooms.presentation.ChatRoomsView import chat.rocket.android.chatrooms.presentation.ChatRoomsView
import chat.rocket.android.server.domain.GetCurrentServerInteractor import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.domain.SettingsRepository import chat.rocket.android.server.domain.SettingsRepository
import chat.rocket.android.util.extensions.inflate import chat.rocket.android.util.extensions.*
import chat.rocket.android.util.extensions.setVisible
import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.widget.DividerItemDecoration import chat.rocket.android.widget.DividerItemDecoration
import chat.rocket.core.internal.realtime.State
import chat.rocket.core.model.ChatRoom import chat.rocket.core.model.ChatRoom
import dagger.android.support.AndroidSupportInjection import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_chat_rooms.* import kotlinx.android.synthetic.main.fragment_chat_rooms.*
...@@ -32,6 +32,7 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView { ...@@ -32,6 +32,7 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
@Inject lateinit var serverInteractor: GetCurrentServerInteractor @Inject lateinit var serverInteractor: GetCurrentServerInteractor
@Inject lateinit var settingsRepository: SettingsRepository @Inject lateinit var settingsRepository: SettingsRepository
private var searchView: SearchView? = null private var searchView: SearchView? = null
private val handler = Handler()
private var listJob: Job? = null private var listJob: Job? = null
...@@ -46,6 +47,7 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView { ...@@ -46,6 +47,7 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
} }
override fun onDestroy() { override fun onDestroy() {
handler.removeCallbacks(dismissStatus)
presenter.disconnect() presenter.disconnect()
super.onDestroy() super.onDestroy()
} }
...@@ -108,6 +110,28 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView { ...@@ -108,6 +110,28 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
override fun showGenericErrorMessage() = showMessage(getString(R.string.msg_generic_error)) override fun showGenericErrorMessage() = showMessage(getString(R.string.msg_generic_error))
override fun showConnectionState(state: State) {
activity?.apply {
connection_status_text.fadeIn()
handler.removeCallbacks(dismissStatus)
when (state) {
is State.Connected -> {
connection_status_text.text = getString(R.string.status_connected)
handler.postDelayed(dismissStatus, 2000)
}
is State.Disconnected -> connection_status_text.text = getString(R.string.status_disconnected)
is State.Connecting -> connection_status_text.text = getString(R.string.status_connecting)
is State.Authenticating -> connection_status_text.text = getString(R.string.status_authenticating)
is State.Disconnecting -> connection_status_text.text = getString(R.string.status_disconnecting)
is State.Waiting -> connection_status_text.text = getString(R.string.status_waiting, state.seconds)
}
}
}
private val dismissStatus = {
connection_status_text.fadeOut()
}
private fun setupToolbar() { private fun setupToolbar() {
(activity as AppCompatActivity).supportActionBar?.title = getString(R.string.title_chats) (activity as AppCompatActivity).supportActionBar?.title = getString(R.string.title_chats)
} }
......
...@@ -24,7 +24,8 @@ class ConnectionManager(internal val client: RocketChatClient) { ...@@ -24,7 +24,8 @@ class ConnectionManager(internal val client: RocketChatClient) {
private var roomsId: String? = null private var roomsId: String? = null
fun connect() { fun connect() {
if (connectJob?.isActive == true && client.state is State.Connected) { if (connectJob?.isActive == true
&& (state !is State.Disconnected)) {
Timber.d("Already connected, just returning...") Timber.d("Already connected, just returning...")
return return
} }
...@@ -35,8 +36,6 @@ class ConnectionManager(internal val client: RocketChatClient) { ...@@ -35,8 +36,6 @@ class ConnectionManager(internal val client: RocketChatClient) {
connectJob?.cancel() connectJob?.cancel()
// Connect and setup // Connect and setup
client.connect()
client.addStateChannel(statusChannel) client.addStateChannel(statusChannel)
connectJob = launch { connectJob = launch {
for (status in statusChannel) { for (status in statusChannel) {
...@@ -92,6 +91,8 @@ class ConnectionManager(internal val client: RocketChatClient) { ...@@ -92,6 +91,8 @@ class ConnectionManager(internal val client: RocketChatClient) {
} }
} }
client.connect()
// Broadcast initial state... // Broadcast initial state...
val state = client.state val state = client.state
for (channel in statusChannelList) { for (channel in statusChannelList) {
...@@ -109,6 +110,7 @@ class ConnectionManager(internal val client: RocketChatClient) { ...@@ -109,6 +110,7 @@ class ConnectionManager(internal val client: RocketChatClient) {
} }
fun disconnect() { fun disconnect() {
Timber.d("ConnectionManager DISCONNECT")
client.removeStateChannel(statusChannel) client.removeStateChannel(statusChannel)
client.disconnect() client.disconnect()
connectJob?.cancel() connectJob?.cancel()
......
...@@ -2,7 +2,6 @@ package chat.rocket.android.util.extensions ...@@ -2,7 +2,6 @@ package chat.rocket.android.util.extensions
import android.view.View import android.view.View
import android.view.ViewAnimationUtils import android.view.ViewAnimationUtils
import android.view.animation.AccelerateInterpolator
import android.view.animation.DecelerateInterpolator import android.view.animation.DecelerateInterpolator
fun View.rotateBy(value: Float, duration: Long = 100) { fun View.rotateBy(value: Float, duration: Long = 100) {
...@@ -12,36 +11,59 @@ fun View.rotateBy(value: Float, duration: Long = 100) { ...@@ -12,36 +11,59 @@ fun View.rotateBy(value: Float, duration: Long = 100) {
.start() .start()
} }
fun View.fadeIn(startValue: Float, finishValue: Float, duration: Long = 200) { fun View.fadeIn(start: Float = 0f, end: Float = 1f, duration: Long = 200) {
animate() // already at end alpha, just set visible and return
.alpha(startValue) if (alpha == end) {
.setDuration(duration) setVisible(true)
.setInterpolator(DecelerateInterpolator()) return
.withEndAction({ }
animate()
.alpha(finishValue)
.setDuration(duration)
.setInterpolator(AccelerateInterpolator()).start()
}).start()
val animation = animate()
.alpha(end)
.setDuration(duration)
.setInterpolator(DecelerateInterpolator())
if (start != alpha) {
animate()
.alpha(start)
.setDuration(duration / 2) // half the time, so the entire animation runs on duration
.setInterpolator(DecelerateInterpolator())
.withEndAction {
animation.setDuration(duration / 2).start()
}.start()
} else {
animation.start()
}
setVisible(true) setVisible(true)
} }
fun View.fadeOut(startValue: Float, finishValue: Float, duration: Long = 200) { fun View.fadeOut(start: Float = 1f, end: Float = 0f, duration: Long = 200) {
animate() if (alpha == end) {
.alpha(startValue) setVisible(false)
.setDuration(duration) return
.setInterpolator(DecelerateInterpolator()) }
.withEndAction({
animate() val animation = animate()
.alpha(finishValue) .alpha(end)
.setDuration(duration) .setDuration(duration)
.setInterpolator(AccelerateInterpolator()).start() .setInterpolator(DecelerateInterpolator())
}).start() .withEndAction {
setVisible(false)
setVisible(false) }
if (start != alpha) {
animate()
.alpha(start)
.setDuration(duration / 2) // half the time, so the entire animation runs on duration
.setInterpolator(DecelerateInterpolator())
.withEndAction {
animation.setDuration(duration / 2).start()
}.start()
} else {
animation.start()
}
} }
fun View.circularRevealOrUnreveal(centerX: Int, centerY: Int, startRadius: Float, endRadius: Float, duration: Long = 200) { fun View.circularRevealOrUnreveal(centerX: Int, centerY: Int, startRadius: Float, endRadius: Float, duration: Long = 200) {
val anim = ViewAnimationUtils.createCircularReveal(this, centerX, centerY, startRadius, endRadius) val anim = ViewAnimationUtils.createCircularReveal(this, centerX, centerY, startRadius, endRadius)
anim.duration = duration anim.duration = duration
......
...@@ -53,4 +53,19 @@ ...@@ -53,4 +53,19 @@
android:layout_margin="5dp" android:layout_margin="5dp"
android:visibility="gone" /> android:visibility="gone" />
<TextView
android:id="@+id/connection_status_text"
android:layout_width="match_parent"
android:layout_height="32dp"
android:background="@color/colorPrimary"
android:elevation="4dp"
android:textColor="@color/white"
android:gravity="center"
android:textAppearance="@style/TextAppearance.AppCompat.Body2"
android:visibility="gone"
android:alpha="0"
tools:alpha="1"
tools:visibility="visible"
tools:text="connected"/>
</RelativeLayout> </RelativeLayout>
...@@ -29,4 +29,19 @@ ...@@ -29,4 +29,19 @@
android:visibility="gone" android:visibility="gone"
tools:visibility="visible" /> tools:visibility="visible" />
<TextView
android:id="@+id/connection_status_text"
android:layout_width="match_parent"
android:layout_height="32dp"
android:background="@color/colorPrimary"
android:elevation="4dp"
android:textColor="@color/white"
android:gravity="center"
android:textAppearance="@style/TextAppearance.AppCompat.Body2"
android:visibility="gone"
android:alpha="0"
tools:alpha="1"
tools:visibility="visible"
tools:text="connected"/>
</RelativeLayout> </RelativeLayout>
\ No newline at end of file
...@@ -84,4 +84,11 @@ ...@@ -84,4 +84,11 @@
<!-- Upload Messages --> <!-- Upload Messages -->
<string name="max_file_size_exceeded">Tamanho de arquivo (%1$d bytes) excedeu tamanho máximo de upload (%2$d bytes)</string> <string name="max_file_size_exceeded">Tamanho de arquivo (%1$d bytes) excedeu tamanho máximo de upload (%2$d bytes)</string>
<!-- Socket status -->
<string name="status_connected">conectado</string>
<string name="status_disconnected">desconetado</string>
<string name="status_connecting">conectando</string>
<string name="status_authenticating">autenticando</string>
<string name="status_disconnecting">desconectando</string>
<string name="status_waiting">conectando em %d segundos</string>
</resources> </resources>
\ No newline at end of file
...@@ -86,4 +86,12 @@ ...@@ -86,4 +86,12 @@
<!-- Upload Messages --> <!-- Upload Messages -->
<string name="max_file_size_exceeded">File size %1$d bytes exceeded max upload size of %2$d bytes</string> <string name="max_file_size_exceeded">File size %1$d bytes exceeded max upload size of %2$d bytes</string>
<!-- Socket status -->
<string name="status_connected">connected</string>
<string name="status_disconnected">disconnected</string>
<string name="status_connecting">connecting</string>
<string name="status_authenticating">authenticating</string>
<string name="status_disconnecting">disconnecting</string>
<string name="status_waiting">connecting in %d seconds</string>
</resources> </resources>
\ No newline at end of file
...@@ -11,6 +11,7 @@ ext { ...@@ -11,6 +11,7 @@ ext {
// Main dependencies // Main dependencies
support : '27.0.2', support : '27.0.2',
constraintLayout : '1.0.2', constraintLayout : '1.0.2',
androidKtx : '0.1',
dagger : '2.14.1', dagger : '2.14.1',
exoPlayer : '2.6.0', exoPlayer : '2.6.0',
playServices : '11.8.0', playServices : '11.8.0',
...@@ -49,6 +50,8 @@ ext { ...@@ -49,6 +50,8 @@ ext {
constraintLayout : "com.android.support.constraint:constraint-layout:${versions.constraintLayout}", constraintLayout : "com.android.support.constraint:constraint-layout:${versions.constraintLayout}",
cardView : "com.android.support:cardview-v7:${versions.support}", cardView : "com.android.support:cardview-v7:${versions.support}",
androidKtx : "androidx.core:core-ktx:${versions.androidKtx}",
dagger : "com.google.dagger:dagger:${versions.dagger}", dagger : "com.google.dagger:dagger:${versions.dagger}",
daggerSupport : "com.google.dagger:dagger-android-support:${versions.dagger}", daggerSupport : "com.google.dagger:dagger-android-support:${versions.dagger}",
daggerProcessor : "com.google.dagger:dagger-compiler:${versions.dagger}", daggerProcessor : "com.google.dagger:dagger-compiler:${versions.dagger}",
......
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