diff --git a/README.md b/README.md
index de8dec04..3105d8b6 100644
--- a/README.md
+++ b/README.md
@@ -87,7 +87,7 @@ While we are working on Bintray support, Scarlet is available via [JitPack][jitp
com.github.tinder.scarlet
scarlet
- 0.2.2-alpha4
+ 0.2.3-alpha1
```
@@ -98,7 +98,7 @@ repositories {
maven { url "https://jitpack.io" }
}
-implementation 'com.github.tinder.scarlet:scarlet:$0.2.2-alpha4'
+implementation 'com.github.tinder.scarlet:scarlet:$0.2.3-alpha1'
```
### Plug-in Roadmap
diff --git a/demo/build.gradle b/demo/build.gradle
index 335017f0..5efaaa30 100755
--- a/demo/build.gradle
+++ b/demo/build.gradle
@@ -51,6 +51,7 @@ dependencies {
implementation rootProject.ext.multiDex
implementation rootProject.ext.constraintLayout
implementation rootProject.ext.liveDateReactiveSterams
+ implementation rootProject.ext.lifecycleExtensions
implementation rootProject.ext.rxBinding
implementation rootProject.ext.rxJava
implementation rootProject.ext.rxAndroid
diff --git a/demo/src/main/AndroidManifest.xml b/demo/src/main/AndroidManifest.xml
index b6cadabd..e44001f0 100755
--- a/demo/src/main/AndroidManifest.xml
+++ b/demo/src/main/AndroidManifest.xml
@@ -11,7 +11,7 @@
@@ -28,6 +28,12 @@
+
+
+
-
+
\ No newline at end of file
diff --git a/demo/src/main/java/com/tinder/app/root/view/DemoActivity.kt b/demo/src/main/java/com/tinder/app/root/view/DemoActivity.kt
index b6eb3dd9..6d231fa5 100755
--- a/demo/src/main/java/com/tinder/app/root/view/DemoActivity.kt
+++ b/demo/src/main/java/com/tinder/app/root/view/DemoActivity.kt
@@ -5,18 +5,19 @@
package com.tinder.app.root.view
import android.os.Bundle
-import androidx.fragment.app.Fragment
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
+import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentStatePagerAdapter
import androidx.viewpager.widget.ViewPager
import com.google.android.material.tabs.TabLayout
import com.tinder.R
import com.tinder.app.socketio.chatroom.view.ChatRoomFragment
+import com.tinder.app.socketio.chatroomservice.view.ChatRoomServiceFragment
+import com.tinder.app.sse.stockprice.view.StockPriceFragment
import com.tinder.app.websocket.echo.view.EchoBotFragment
import com.tinder.app.websocket.gdax.view.GdaxFragment
-import com.tinder.app.sse.stockprice.view.StockPriceFragment
class DemoActivity : AppCompatActivity() {
@@ -57,7 +58,8 @@ class DemoActivity : AppCompatActivity() {
"WS - Echo Bot" to { EchoBotFragment() },
"WS - GDAX" to { GdaxFragment() },
"SSE - Stock Price" to { StockPriceFragment() },
- "SocketIo - Chat Room" to { ChatRoomFragment() }
+ "SocketIo - Chat Room" to { ChatRoomFragment() },
+ "SocketIo - Chat Room Service" to { ChatRoomServiceFragment() }
)
}
}
diff --git a/demo/src/main/java/com/tinder/app/socketio/chatroomservice/view/ChatRoomServiceFragment.kt b/demo/src/main/java/com/tinder/app/socketio/chatroomservice/view/ChatRoomServiceFragment.kt
new file mode 100644
index 00000000..ffdc5942
--- /dev/null
+++ b/demo/src/main/java/com/tinder/app/socketio/chatroomservice/view/ChatRoomServiceFragment.kt
@@ -0,0 +1,72 @@
+/*
+ * © 2019 Match Group, LLC.
+ */
+
+package com.tinder.app.socketio.chatroomservice.view
+
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.ServiceConnection
+import android.os.Bundle
+import android.os.IBinder
+import android.os.Messenger
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import com.tinder.R
+import com.tinder.service.ChatRoomSocketIoService
+import timber.log.Timber
+
+class ChatRoomServiceFragment : Fragment() {
+
+ private var messenger: Messenger? = null
+ private var isBound = false
+
+ private val serviceConnection = object : ServiceConnection {
+
+ override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
+ messenger = Messenger(service)
+ isBound = true
+
+ Timber.d("chat onServiceConnected")
+ }
+
+ override fun onServiceDisconnected(name: ComponentName?) {
+ messenger = null
+ isBound = false
+
+ Timber.d("chat onServiceDisconnected")
+ }
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ val view = inflater.inflate(R.layout.fragment_chatroom_service, container, false) as View
+ return view
+ }
+
+ override fun onStart() {
+ super.onStart()
+ bindService()
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ unbindService()
+ }
+
+ private fun bindService() {
+ val intent = Intent(activity, ChatRoomSocketIoService::class.java)
+ activity?.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)
+ }
+
+ private fun unbindService() {
+ activity?.unbindService(serviceConnection)
+ isBound = false
+ }
+}
\ No newline at end of file
diff --git a/demo/src/main/java/com/tinder/service/ChatRoomSocketIoService.kt b/demo/src/main/java/com/tinder/service/ChatRoomSocketIoService.kt
new file mode 100644
index 00000000..ab7513ba
--- /dev/null
+++ b/demo/src/main/java/com/tinder/service/ChatRoomSocketIoService.kt
@@ -0,0 +1,160 @@
+/*
+ * © 2018 Match Group, LLC.
+ */
+
+package com.tinder.service
+
+import android.app.Notification
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.content.Intent
+import android.os.Handler
+import android.os.IBinder
+import android.os.Messenger
+import androidx.lifecycle.LifecycleService
+import com.tinder.R
+import com.tinder.app.socketio.chatroom.api.AddUserTopic
+import com.tinder.app.socketio.chatroom.api.ChatRoomService
+import com.tinder.app.socketio.chatroom.api.NewMessageTopic
+import com.tinder.app.socketio.chatroom.api.TypingStartedTopic
+import com.tinder.app.socketio.chatroom.api.TypingStoppedTopic
+import com.tinder.app.socketio.chatroom.api.UserJoinedTopic
+import com.tinder.app.socketio.chatroom.api.UserLeftTopic
+import com.tinder.scarlet.Scarlet
+import com.tinder.scarlet.lifecycle.android.AndroidLifecycle
+import com.tinder.scarlet.messageadapter.moshi.MoshiMessageAdapter
+import com.tinder.scarlet.socketio.SocketIoEvent
+import com.tinder.scarlet.socketio.client.SocketIoClient
+import com.tinder.scarlet.socketio.client.SocketIoEventName
+import com.tinder.scarlet.streamadapter.rxjava2.RxJava2StreamAdapterFactory
+import io.reactivex.Flowable
+import io.reactivex.schedulers.Schedulers
+import timber.log.Timber
+
+class ChatRoomSocketIoService : LifecycleService() {
+
+ var incomingMessageCount = 0
+ private val messenger = Messenger(IncomingHandler())
+
+ override fun onCreate() {
+ super.onCreate()
+
+ val config = Scarlet.Configuration(
+ lifecycle = AndroidLifecycle.ofLifecycleServiceForeground(application, this),
+ messageAdapterFactories = listOf(MoshiMessageAdapter.Factory()),
+ streamAdapterFactories = listOf(RxJava2StreamAdapterFactory())
+ )
+ val serverUrl = "https://socket-io-chat.now.sh/"
+
+ val scarlet = Scarlet(
+ SocketIoClient({ serverUrl }),
+ config
+ )
+
+ val chatRoomService = scarlet.create()
+ val addUserTopic = scarlet
+ .child(SocketIoEventName("add user"), config)
+ .create()
+ val newMessageTopic = scarlet
+ .child(SocketIoEventName("new message"), config)
+ .create()
+ val typingStartedTopic = scarlet
+ .child(SocketIoEventName("typing"), config)
+ .create()
+ val typingStoppedTopic = scarlet
+ .child(SocketIoEventName("stop typing"), config)
+ .create()
+ val userJoinedTopic = scarlet
+ .child(SocketIoEventName("user joined"), config)
+ .create()
+ val userLeftTopic = scarlet
+ .child(SocketIoEventName("user left"), config)
+ .create()
+
+ val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
+
+ val notificationChannel =
+ NotificationChannel(channelId, "Default", NotificationManager.IMPORTANCE_HIGH)
+ notificationManager.createNotificationChannel(notificationChannel)
+
+ Timber.d("chatroom scarlet created")
+
+ chatRoomService.observeStateTransition()
+ .subscribe {
+ Timber.d("chatroom service: $it")
+
+ val notification = createNotification {
+ setContentText("State Transition ${it.toState}")
+ }
+ notificationManager.notify(notificationIdCurrentState, notification)
+ }
+
+ val username = "scarlet service"
+
+ addUserTopic.observeSocketIoEvent()
+ .filter { it is SocketIoEvent.OnConnectionOpened }
+ .observeOn(Schedulers.io())
+ .subscribe({
+ addUserTopic.sendAddUser(username)
+
+ Timber.d("chatroom added user: $it")
+ val notification = createNotification {
+ setContentText("Joined chatroom")
+ }
+ notificationManager.notify(notificationIdCurrentState, notification)
+ }, { e ->
+ Timber.e(e)
+ })
+
+ Flowable.fromPublisher(AndroidLifecycle.ofLifecycleServiceForeground(application, this))
+ .subscribe({
+ Timber.d("chatroom lifecycle: $it")
+
+ val notification = createNotification {
+ setContentText("Lifecycle State: $it")
+ }
+ notificationManager.notify(notificationIdCurrentState, notification)
+ })
+
+ Flowable.merge(
+ newMessageTopic.observeNewMessage().map { "${it.username}: ${it.message}" },
+ typingStartedTopic.observeTypingStarted().map { "${it.username} started typing" },
+ typingStoppedTopic.observeTypingStopped().map { "${it.username} stopped typing" }
+ ).subscribe {
+ Timber.d("chatroom new message: $it")
+
+ val notification = createNotification {
+ setContentText("$it")
+ }
+
+ val notificationId = notificationIdIncomingMessage + incomingMessageCount
+ incomingMessageCount += 1
+ notificationManager.notify(notificationId, notification)
+ }
+ }
+
+ override fun onBind(intent: Intent?): IBinder? {
+ super.onBind(intent)
+ Timber.d("chatroom onbind")
+ return messenger.binder
+ }
+
+ private fun createNotification(builder: Notification.Builder.() -> Unit): Notification {
+ return Notification.Builder(applicationContext, channelId)
+ .setSmallIcon(R.drawable.ic_action_info)
+ .setWhen(System.currentTimeMillis())
+ .setContentTitle("Chat Room")
+ .setAutoCancel(true)
+ .setOngoing(true)
+ .apply(builder)
+ .build()
+ }
+
+ private inner class IncomingHandler : Handler()
+
+ companion object {
+ const val notificationIdCurrentState = 10000
+ const val notificationIdIncomingMessage = 10001
+ const val channelId = "default"
+ }
+}
diff --git a/demo/src/main/res/layout/fragment_chatroom_service.xml b/demo/src/main/res/layout/fragment_chatroom_service.xml
new file mode 100644
index 00000000..19cc8a1b
--- /dev/null
+++ b/demo/src/main/res/layout/fragment_chatroom_service.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dependencies.gradle b/dependencies.gradle
index 09fe8d1d..ff72df7b 100644
--- a/dependencies.gradle
+++ b/dependencies.gradle
@@ -3,14 +3,14 @@
*/
ext {
- minSdkVersion = 21
+ minSdkVersion = 28
minSdkVersionAndroidLifecycle = 19
targetSdkVersion = 28
compileSdkVersion = 28
buildToolsVersion = '28.0.3'
appCompat = 'androidx.appcompat:appcompat:1.0.0'
- lifecycleExtensions = 'androidx.lifecycle:lifecycle-extensions:2.0.0-rc01'
+ lifecycleExtensions = 'androidx.lifecycle:lifecycle-extensions:2.0.0'
material = 'com.google.android.material:material:1.0.0'
multiDex = 'androidx.multidex:multidex:2.0.0'
constraintLayout = 'androidx.constraintlayout:constraintlayout:2.0.0-alpha3'
diff --git a/gradle.properties b/gradle.properties
index 6ab48e20..04789687 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -4,4 +4,4 @@
android.enableJetifier=true
android.useAndroidX=true
-version=0.2.2-alpha4
+version=0.2.3-alpha1
diff --git a/scarlet-lifecycle-android/src/main/java/com/tinder/scarlet/lifecycle/android/AndroidLifecycle.kt b/scarlet-lifecycle-android/src/main/java/com/tinder/scarlet/lifecycle/android/AndroidLifecycle.kt
index fbdf3383..5b0f62cb 100755
--- a/scarlet-lifecycle-android/src/main/java/com/tinder/scarlet/lifecycle/android/AndroidLifecycle.kt
+++ b/scarlet-lifecycle-android/src/main/java/com/tinder/scarlet/lifecycle/android/AndroidLifecycle.kt
@@ -40,4 +40,18 @@ object AndroidLifecycle {
)
.combineWith(ConnectivityOnLifecycle(application))
}
+
+ @JvmStatic
+ @JvmOverloads
+ fun ofLifecycleServiceForeground(
+ application: Application,
+ lifecycleOwner: LifecycleOwner,
+ throttleTimeoutMillis: Long = ACTIVITY_THROTTLE_TIMEOUT_MILLIS
+ ): Lifecycle {
+ return LifecycleOwnerStartedLifecycle(
+ lifecycleOwner,
+ LifecycleRegistry(throttleTimeoutMillis)
+ )
+ .combineWith(ConnectivityOnLifecycle(application))
+ }
}
diff --git a/scarlet-lifecycle-android/src/main/java/com/tinder/scarlet/lifecycle/android/LifecycleOwnerStartedLifecycle.kt b/scarlet-lifecycle-android/src/main/java/com/tinder/scarlet/lifecycle/android/LifecycleOwnerStartedLifecycle.kt
new file mode 100644
index 00000000..08c0dfa0
--- /dev/null
+++ b/scarlet-lifecycle-android/src/main/java/com/tinder/scarlet/lifecycle/android/LifecycleOwnerStartedLifecycle.kt
@@ -0,0 +1,39 @@
+/*
+ * © 2019 Match Group, LLC.
+ */
+
+package com.tinder.scarlet.lifecycle.android
+
+import androidx.lifecycle.LifecycleObserver
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.OnLifecycleEvent
+import com.tinder.scarlet.Lifecycle
+import com.tinder.scarlet.LifecycleState
+import com.tinder.scarlet.lifecycle.LifecycleRegistry
+
+internal class LifecycleOwnerStartedLifecycle(
+ private val lifecycleOwner: LifecycleOwner,
+ private val lifecycleRegistry: LifecycleRegistry
+) : Lifecycle by lifecycleRegistry {
+
+ init {
+ lifecycleOwner.lifecycle.addObserver(ALifecycleObserver())
+ }
+
+ private inner class ALifecycleObserver : LifecycleObserver {
+ @OnLifecycleEvent(androidx.lifecycle.Lifecycle.Event.ON_STOP)
+ fun onPause() {
+ lifecycleRegistry.onNext(LifecycleState.Stopped)
+ }
+
+ @OnLifecycleEvent(androidx.lifecycle.Lifecycle.Event.ON_START)
+ fun onResume() {
+ lifecycleRegistry.onNext(LifecycleState.Started)
+ }
+
+ @OnLifecycleEvent(androidx.lifecycle.Lifecycle.Event.ON_DESTROY)
+ fun onDestroy() {
+ lifecycleRegistry.onComplete()
+ }
+ }
+}
\ No newline at end of file