From acff45024942e1886a4a6daa058d6d9bfbb3cd25 Mon Sep 17 00:00:00 2001 From: Dmitriy Chechyotkin Date: Mon, 30 Dec 2019 22:24:30 +0300 Subject: [PATCH] history list view (#3) --- app/build.gradle.kts | 10 +++- app/src/main/java/com/porfir/MainActivity.kt | 56 +++++++++++++++++-- .../main/java/com/porfir/PorfirApplication.kt | 42 +++++++++++++- .../main/java/com/porfir/dao/HistoryDao.kt | 16 ++++++ .../java/com/porfir/dao/PorfirDatabase.kt | 10 ++++ .../main/java/com/porfir/model/HistoryItem.kt | 20 +++++++ .../java/com/porfir/module/HistoryModule.kt | 20 +++++++ app/src/main/res/layout/history_item.xml | 22 ++++++++ .../main/res/layout/layout_activity_main.xml | 49 +++++++++++----- app/src/main/res/values/styles.xml | 8 +-- gradle.properties | 1 + 11 files changed, 229 insertions(+), 25 deletions(-) create mode 100644 app/src/main/java/com/porfir/dao/HistoryDao.kt create mode 100644 app/src/main/java/com/porfir/dao/PorfirDatabase.kt create mode 100644 app/src/main/java/com/porfir/model/HistoryItem.kt create mode 100644 app/src/main/java/com/porfir/module/HistoryModule.kt create mode 100644 app/src/main/res/layout/history_item.xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 5fa2bce..95f918d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -2,6 +2,7 @@ plugins { id("com.android.application") kotlin("android") id("kotlin-android-extensions") + id("kotlin-kapt") } android { @@ -51,7 +52,9 @@ repositories { dependencies { implementation("androidx.appcompat:appcompat:1.1.0") - implementation("androidx.recyclerview:recyclerview:1.0.0") + implementation("androidx.cardview:cardview:1.0.0") + + implementation("androidx.recyclerview:recyclerview:1.1.0") implementation("androidx.constraintlayout:constraintlayout:1.1.3") implementation("androidx.core:core-ktx:1.1.0") implementation("androidx.lifecycle:lifecycle-extensions:2.1.0") @@ -64,4 +67,9 @@ dependencies { implementation("com.justai.aimybox:google-platform-speechkit:0.7.0") implementation("com.squareup.retrofit2:retrofit:2.5.0") + + implementation("androidx.room:room-runtime:2.2.3") + kapt("androidx.room:room-compiler:2.2.3") + + implementation("com.ironsource.aura.oneadapter:oneadapter:1.4.0") } \ No newline at end of file diff --git a/app/src/main/java/com/porfir/MainActivity.kt b/app/src/main/java/com/porfir/MainActivity.kt index d9eef86..13840d9 100644 --- a/app/src/main/java/com/porfir/MainActivity.kt +++ b/app/src/main/java/com/porfir/MainActivity.kt @@ -2,9 +2,17 @@ package com.porfir import android.content.Intent import android.os.Bundle +import android.view.View import android.view.WindowManager import androidx.appcompat.app.AppCompatActivity +import androidx.recyclerview.widget.RecyclerView +import com.idanatz.oneadapter.OneAdapter +import com.idanatz.oneadapter.external.event_hooks.ClickEventHook +import com.idanatz.oneadapter.internal.holders.ViewBinder import com.justai.aimybox.components.AimyboxAssistantFragment +import com.porfir.dao.PorfirDatabase +import com.porfir.model.HistoryItem +import com.porfir.module.HistoryModule import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.channels.consume @@ -14,22 +22,37 @@ class MainActivity : AppCompatActivity(), CoroutineScope { override val coroutineContext = Dispatchers.Main + private lateinit var promptLayout: View + private lateinit var recyclerView: RecyclerView + private lateinit var recyclerAdapter: OneAdapter + private lateinit var database: PorfirDatabase + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.layout_activity_main) - window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + promptLayout = findViewById(R.id.prompt_layout) + recyclerView = findViewById(R.id.recycler_view) + recyclerAdapter = OneAdapter(recyclerView) + .attachItemModule(HistoryModule().addEventHook(historyClickHook)) - val assistantFragment = AimyboxAssistantFragment() + database = (application as PorfirApplication).database + + window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) supportFragmentManager.beginTransaction().apply { - replace(R.id.assistant_container, assistantFragment) + replace(R.id.assistant_container, AimyboxAssistantFragment()) commit() } onNewIntent(intent) } + override fun onResume() { + super.onResume() + refreshRecyclerViewData() + } + override fun onNewIntent(intent: Intent?) { super.onNewIntent(intent) if (intent?.action == Intent.ACTION_ASSIST) { @@ -46,7 +69,32 @@ class MainActivity : AppCompatActivity(), CoroutineScope { override fun onBackPressed() { val assistantFragment = (supportFragmentManager.findFragmentById(R.id.assistant_container) as? AimyboxAssistantFragment) - if (assistantFragment?.onBackPressed() != true) super.onBackPressed() + if (assistantFragment?.onBackPressed() == true) { + refreshRecyclerViewData() + } else { + super.onBackPressed() + } + } + + private fun refreshRecyclerViewData() = launch(Dispatchers.IO) { + val items = database.historyDao().getAll() + recyclerAdapter.setItems(items) + + promptLayout.visibility = if (items.isEmpty()) View.VISIBLE else View.GONE + recyclerView.visibility = if (items.isNotEmpty()) View.VISIBLE else View.GONE + } + + private val historyClickHook = object : ClickEventHook() { + override fun onClick(model: HistoryItem, viewBinder: ViewBinder) { + val sendIntent: Intent = Intent().apply { + action = Intent.ACTION_SEND + putExtra(Intent.EXTRA_TEXT, model.text) + type = "text/plain" + } + + val shareIntent = Intent.createChooser(sendIntent, null) + startActivity(shareIntent) + } } } \ No newline at end of file diff --git a/app/src/main/java/com/porfir/PorfirApplication.kt b/app/src/main/java/com/porfir/PorfirApplication.kt index 606a827..f67f8b6 100644 --- a/app/src/main/java/com/porfir/PorfirApplication.kt +++ b/app/src/main/java/com/porfir/PorfirApplication.kt @@ -2,17 +2,31 @@ package com.porfir import android.app.Application import android.content.Context +import androidx.room.Room import com.justai.aimybox.Aimybox +import com.justai.aimybox.api.DialogApi import com.justai.aimybox.components.AimyboxProvider import com.justai.aimybox.core.Config +import com.justai.aimybox.model.reply.TextReply import com.justai.aimybox.speechkit.google.platform.GooglePlatformSpeechToText import com.justai.aimybox.speechkit.google.platform.GooglePlatformTextToSpeech import com.porfir.api.PorfirDialogApi +import com.porfir.dao.PorfirDatabase +import com.porfir.model.HistoryItem +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.channels.consumeEach +import kotlinx.coroutines.launch import java.util.* -class PorfirApplication: Application(), AimyboxProvider { +class PorfirApplication: Application(), AimyboxProvider, CoroutineScope { + + val database by lazy { createDatabase(this) } + override val aimybox by lazy { createAimybox(this) } + override val coroutineContext = Dispatchers.IO + private fun createAimybox(context: Context): Aimybox { val textToSpeech = GooglePlatformTextToSpeech(context, Locale("ru")) val speechToText = GooglePlatformSpeechToText(context, Locale("ru")) @@ -20,5 +34,31 @@ class PorfirApplication: Application(), AimyboxProvider { val dialogApi = PorfirDialogApi() return Aimybox(Config.create(speechToText, textToSpeech, dialogApi)) + .also { subscribeToDialogApi(it) } + } + + private fun createDatabase(context: Context): PorfirDatabase { + return Room.databaseBuilder( + context, + PorfirDatabase::class.java, + "porfir.db").build() + } + + private fun subscribeToDialogApi(aimybox: Aimybox) { + val channel = aimybox.dialogApiEvents.openSubscription() + launch { + channel.consumeEach { event -> + event + .takeIf { it is DialogApi.Event.ResponseReceived } + ?.also { it -> + val now = Calendar.getInstance().timeInMillis + val response = (it as DialogApi.Event.ResponseReceived).response + response.replies + .filterIsInstance(TextReply::class.java) + .map { reply -> HistoryItem(0, reply.text, now) } + .forEach { item -> database.historyDao().insert(item) } + } + } + } } } \ No newline at end of file diff --git a/app/src/main/java/com/porfir/dao/HistoryDao.kt b/app/src/main/java/com/porfir/dao/HistoryDao.kt new file mode 100644 index 0000000..fb9d9b6 --- /dev/null +++ b/app/src/main/java/com/porfir/dao/HistoryDao.kt @@ -0,0 +1,16 @@ +package com.porfir.dao + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import com.porfir.model.HistoryItem + +@Dao +interface HistoryDao { + @Query("SELECT * FROM history_items ORDER BY timestamp DESC") + fun getAll(): List + + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun insert(item: HistoryItem) +} \ No newline at end of file diff --git a/app/src/main/java/com/porfir/dao/PorfirDatabase.kt b/app/src/main/java/com/porfir/dao/PorfirDatabase.kt new file mode 100644 index 0000000..7722f79 --- /dev/null +++ b/app/src/main/java/com/porfir/dao/PorfirDatabase.kt @@ -0,0 +1,10 @@ +package com.porfir.dao + +import androidx.room.Database +import androidx.room.RoomDatabase +import com.porfir.model.HistoryItem + +@Database(entities = [HistoryItem::class], version = 1, exportSchema = false) +abstract class PorfirDatabase: RoomDatabase() { + abstract fun historyDao(): HistoryDao +} \ No newline at end of file diff --git a/app/src/main/java/com/porfir/model/HistoryItem.kt b/app/src/main/java/com/porfir/model/HistoryItem.kt new file mode 100644 index 0000000..7843e59 --- /dev/null +++ b/app/src/main/java/com/porfir/model/HistoryItem.kt @@ -0,0 +1,20 @@ +package com.porfir.model + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import com.idanatz.oneadapter.external.interfaces.Diffable + +@Entity(tableName = "history_items") +data class HistoryItem( + @PrimaryKey(autoGenerate = true) + val id: Long, + @ColumnInfo(name = "generated_text") + val text: String, + @ColumnInfo(name = "timestamp") + val timestamp: Long +): Diffable { + override fun areContentTheSame(other: Any) = other is HistoryItem && other.id == id + + override fun getUniqueIdentifier(): Long = id +} \ No newline at end of file diff --git a/app/src/main/java/com/porfir/module/HistoryModule.kt b/app/src/main/java/com/porfir/module/HistoryModule.kt new file mode 100644 index 0000000..f691522 --- /dev/null +++ b/app/src/main/java/com/porfir/module/HistoryModule.kt @@ -0,0 +1,20 @@ +package com.porfir.module + +import android.widget.TextView +import com.idanatz.oneadapter.external.modules.ItemModule +import com.idanatz.oneadapter.external.modules.ItemModuleConfig +import com.idanatz.oneadapter.internal.holders.ViewBinder +import com.porfir.R +import com.porfir.model.HistoryItem + +class HistoryModule: ItemModule() { + + override fun provideModuleConfig() = object : ItemModuleConfig() { + override fun withLayoutResource() = R.layout.history_item + } + + override fun onBind(model: HistoryItem, viewBinder: ViewBinder) { + val textView = viewBinder.findViewById(R.id.history_item_text) + textView.text = model.text + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/history_item.xml b/app/src/main/res/layout/history_item.xml new file mode 100644 index 0000000..cfd2be4 --- /dev/null +++ b/app/src/main/res/layout/history_item.xml @@ -0,0 +1,22 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_activity_main.xml b/app/src/main/res/layout/layout_activity_main.xml index 6b1e3fd..6aa00a4 100644 --- a/app/src/main/res/layout/layout_activity_main.xml +++ b/app/src/main/res/layout/layout_activity_main.xml @@ -1,26 +1,45 @@ - + + + + + + + - + + + -