EventsFlow

This commit is contained in:
Philipp Heckel 2021-10-25 16:16:23 -04:00
parent 464ef4e697
commit 20d5350a60
7 changed files with 92 additions and 8 deletions

View file

@ -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"

View file

@ -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"/>

View file

@ -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"

View 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()
}
}
}
}

View file

@ -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() {

View file

@ -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) :

View file

@ -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. */