EventsFlow
This commit is contained in:
parent
464ef4e697
commit
20d5350a60
7 changed files with 92 additions and 8 deletions
|
@ -22,7 +22,7 @@ android {
|
||||||
compileSdkVersion 30
|
compileSdkVersion 30
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.example.recyclersample"
|
applicationId "io.heckel.ntfy"
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 30
|
targetSdkVersion 30
|
||||||
versionCode 1
|
versionCode 1
|
||||||
|
@ -45,7 +45,6 @@ android {
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
jvmTarget = JavaVersion.VERSION_1_8.toString()
|
jvmTarget = JavaVersion.VERSION_1_8.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
@ -54,6 +53,7 @@ dependencies {
|
||||||
implementation "androidx.core:core-ktx:$rootProject.coreKtxVersion"
|
implementation "androidx.core:core-ktx:$rootProject.coreKtxVersion"
|
||||||
implementation "androidx.constraintlayout:constraintlayout:$rootProject.constraintLayoutVersion"
|
implementation "androidx.constraintlayout:constraintlayout:$rootProject.constraintLayoutVersion"
|
||||||
implementation "androidx.activity:activity-ktx:$rootProject.activityVersion"
|
implementation "androidx.activity:activity-ktx:$rootProject.activityVersion"
|
||||||
|
implementation 'com.google.code.gson:gson:2.8.8'
|
||||||
|
|
||||||
// RecyclerView
|
// RecyclerView
|
||||||
implementation "androidx.recyclerview:recyclerview:$rootProject.recyclerViewVersion"
|
implementation "androidx.recyclerview:recyclerview:$rootProject.recyclerViewVersion"
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="com.heckel.ntfy">
|
package="io.heckel.ntfy">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.widget.Button
|
import android.widget.Button
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import com.heckel.ntfy.R
|
import io.heckel.ntfy.R
|
||||||
import com.google.android.material.textfield.TextInputEditText
|
import com.google.android.material.textfield.TextInputEditText
|
||||||
|
|
||||||
const val TOPIC_URL = "url"
|
const val TOPIC_URL = "url"
|
||||||
|
|
64
app/src/main/java/io/heckel/ntfy/data/NtfyApi.kt
Normal file
64
app/src/main/java/io/heckel/ntfy/data/NtfyApi.kt
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
package io.heckel.ntfy.data
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.google.gson.GsonBuilder
|
||||||
|
import com.google.gson.JsonObject
|
||||||
|
import com.google.gson.JsonSyntaxException
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.flow
|
||||||
|
import java.io.IOException
|
||||||
|
import java.net.HttpURLConnection
|
||||||
|
import java.net.URL
|
||||||
|
|
||||||
|
class NtfyApi(context: Context) {
|
||||||
|
private val gson = GsonBuilder().create()
|
||||||
|
|
||||||
|
private suspend fun getStreamConnection(url: String): HttpURLConnection =
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
return@withContext (URL(url).openConnection() as HttpURLConnection).also {
|
||||||
|
it.setRequestProperty("Accept", "text/event-stream")
|
||||||
|
it.doInput = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class Event(val name: String = "", val data: JsonObject = JsonObject())
|
||||||
|
|
||||||
|
fun getEventsFlow(): Flow<Event> = flow {
|
||||||
|
coroutineScope {
|
||||||
|
val conn = getStreamConnection("https://ntfy.sh/_phil")
|
||||||
|
val input = conn.inputStream.bufferedReader()
|
||||||
|
try {
|
||||||
|
conn.connect()
|
||||||
|
var event = Event()
|
||||||
|
while (isActive) {
|
||||||
|
val line = input.readLine()
|
||||||
|
println("PHIL: " + line)
|
||||||
|
when {
|
||||||
|
line.startsWith("event:") -> {
|
||||||
|
event = event.copy(name = line.substring(6).trim())
|
||||||
|
}
|
||||||
|
line.startsWith("data:") -> {
|
||||||
|
val data = line.substring(5).trim()
|
||||||
|
try {
|
||||||
|
event = event.copy(data = gson.fromJson(data, JsonObject::class.java))
|
||||||
|
} catch (e: JsonSyntaxException) {
|
||||||
|
// Nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
line.isEmpty() -> {
|
||||||
|
emit(event)
|
||||||
|
event = Event()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: IOException) {
|
||||||
|
println("PHIL: " + e.message)
|
||||||
|
this.cancel(CancellationException("Network Problem", e))
|
||||||
|
} finally {
|
||||||
|
conn.disconnect()
|
||||||
|
input.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,7 +21,7 @@ import android.widget.Button
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import com.heckel.ntfy.R
|
import io.heckel.ntfy.R
|
||||||
import io.heckel.ntfy.list.TOPIC_ID
|
import io.heckel.ntfy.list.TOPIC_ID
|
||||||
|
|
||||||
class TopicDetailActivity : AppCompatActivity() {
|
class TopicDetailActivity : AppCompatActivity() {
|
||||||
|
|
|
@ -19,12 +19,11 @@ package io.heckel.ntfy.list
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ImageView
|
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.recyclerview.widget.DiffUtil
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
import androidx.recyclerview.widget.ListAdapter
|
import androidx.recyclerview.widget.ListAdapter
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.heckel.ntfy.R
|
import io.heckel.ntfy.R
|
||||||
import io.heckel.ntfy.data.Topic
|
import io.heckel.ntfy.data.Topic
|
||||||
|
|
||||||
class TopicsAdapter(private val onClick: (Topic) -> Unit) :
|
class TopicsAdapter(private val onClick: (Topic) -> Unit) :
|
||||||
|
|
|
@ -22,16 +22,24 @@ import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.lifecycle.Observer
|
||||||
|
import androidx.lifecycle.asLiveData
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.heckel.ntfy.R
|
import io.heckel.ntfy.R
|
||||||
import io.heckel.ntfy.add.AddTopicActivity
|
import io.heckel.ntfy.add.AddTopicActivity
|
||||||
import io.heckel.ntfy.add.TOPIC_URL
|
import io.heckel.ntfy.add.TOPIC_URL
|
||||||
|
import io.heckel.ntfy.data.NtfyApi
|
||||||
import io.heckel.ntfy.data.Topic
|
import io.heckel.ntfy.data.Topic
|
||||||
import io.heckel.ntfy.detail.TopicDetailActivity
|
import io.heckel.ntfy.detail.TopicDetailActivity
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
const val TOPIC_ID = "topic id"
|
const val TOPIC_ID = "topic id"
|
||||||
|
|
||||||
class TopicsListActivity : AppCompatActivity() {
|
class TopicsListActivity : AppCompatActivity() {
|
||||||
|
private val api = NtfyApi(this)
|
||||||
private val newTopicActivityRequestCode = 1
|
private val newTopicActivityRequestCode = 1
|
||||||
private val topicsListViewModel by viewModels<TopicsListViewModel> {
|
private val topicsListViewModel by viewModels<TopicsListViewModel> {
|
||||||
TopicsListViewModelFactory(this)
|
TopicsListViewModelFactory(this)
|
||||||
|
@ -55,6 +63,19 @@ class TopicsListActivity : AppCompatActivity() {
|
||||||
fab.setOnClickListener {
|
fab.setOnClickListener {
|
||||||
fabOnClick()
|
fabOnClick()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val self = this
|
||||||
|
api.getEventsFlow().asLiveData(Dispatchers.IO).observe(this, Observer { event ->
|
||||||
|
// Get the Activity's lifecycleScope and launch
|
||||||
|
this.lifecycleScope.launch(Dispatchers.Main) {
|
||||||
|
// run the code again in IO context
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
println(event.data)
|
||||||
|
//Toast.makeText(self, event.data, Toast.LENGTH_SHORT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Opens TopicDetailActivity when RecyclerView item is clicked. */
|
/* Opens TopicDetailActivity when RecyclerView item is clicked. */
|
||||||
|
|
Loading…
Reference in a new issue