mirror of
https://github.com/souravanand001/ai-assistant-android.git
synced 2026-10-03 04:36:08 +00:00
Refactor step 4 migrated to Room DB
This commit is contained in:
parent
3ba51c50ff
commit
8724ad25b7
15 changed files with 482 additions and 442 deletions
|
|
@ -1,115 +1,134 @@
|
|||
import java.util.Properties
|
||||
import java.io.FileInputStream
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.android.application)
|
||||
alias(libs.plugins.jetbrains.kotlin.android)
|
||||
alias(libs.plugins.jetbrains.kotlin.serialization)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.app.assistant"
|
||||
compileSdk = 35
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "com.app.assistant"
|
||||
minSdk = 24
|
||||
targetSdk = 35
|
||||
versionCode = 1
|
||||
versionName = "1.0"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables {
|
||||
useSupportLibrary = true
|
||||
}
|
||||
|
||||
// Read local.properties
|
||||
val localProperties = Properties()
|
||||
val localPropertiesFile = rootProject.file("local.properties")
|
||||
if (localPropertiesFile.exists()) {
|
||||
localProperties.load(FileInputStream(localPropertiesFile))
|
||||
}
|
||||
|
||||
// Define BuildConfig fields for API keys
|
||||
// Make sure YOUTUBE_API_KEY and GROQ_API_KEY exist in your local.properties
|
||||
buildConfigField("String", "YOUTUBE_API_KEY",
|
||||
"\"${localProperties.getProperty("YOUTUBE_API_KEY") ?: ""}\"" // Add quotes
|
||||
)
|
||||
|
||||
buildConfigField("String", "GROQ_API_KEY",
|
||||
"\"${localProperties.getProperty("GROQ_API_KEY") ?: ""}\"" // Add quotes
|
||||
)
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = false
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
// You might want to also define buildConfigFields for release
|
||||
// if you have different keys for release builds, or if local.properties is not available in CI
|
||||
}
|
||||
debug {
|
||||
// BuildConfigFields from defaultConfig are inherited.
|
||||
// Add specific debug configurations here if needed, for example:
|
||||
// isMinifyEnabled = false
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
buildFeatures {
|
||||
compose = true
|
||||
buildConfig = true // Ensure buildConfig is enabled
|
||||
}
|
||||
composeOptions {
|
||||
kotlinCompilerExtensionVersion = libs.versions.androidxComposeCompiler.get()
|
||||
}
|
||||
packaging {
|
||||
resources {
|
||||
excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation(libs.androidx.core.ktx)
|
||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||
implementation(libs.androidx.activity.compose)
|
||||
implementation(platform(libs.androidx.compose.bom))
|
||||
implementation(libs.androidx.ui)
|
||||
implementation(libs.androidx.ui.graphics)
|
||||
implementation(libs.androidx.ui.tooling.preview)
|
||||
implementation(libs.androidx.material3)
|
||||
implementation(libs.androidx.material3.window.size)
|
||||
implementation(libs.androidx.material3.adaptive.navigation.suite)
|
||||
implementation(libs.okhttp)
|
||||
implementation(libs.kotlinx.serialization.json)
|
||||
implementation(libs.mlkit.translate)
|
||||
implementation(libs.commonmark)
|
||||
//implementation ("com.github.jeziellago:compose-markdown:0.5.4")
|
||||
implementation(libs.coil.compose)
|
||||
implementation(libs.coil.network.okhttp)
|
||||
implementation(libs.kotlin.reflect)
|
||||
implementation(libs.play.services.location)
|
||||
implementation(libs.androidx.ui.test.android)
|
||||
implementation(libs.androidx.security.crypto)
|
||||
implementation(libs.androidx.runtime.saveable)
|
||||
testImplementation(libs.junit)
|
||||
// androidTestImplementation(libs.androidx.junit)
|
||||
// androidTestImplementation(libs.androidx.espresso.core)
|
||||
// androidTestImplementation(platform(libs.androidx.compose.bom))
|
||||
// androidTestImplementation(libs.androidx.ui.test.junit4)
|
||||
debugImplementation(libs.androidx.ui.tooling)
|
||||
debugImplementation(libs.androidx.ui.test.manifest)
|
||||
|
||||
//MediaPipe library
|
||||
implementation(libs.mediapipe.tasks.text)
|
||||
}
|
||||
import java.util.Properties
|
||||
import java.io.FileInputStream
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.android.application)
|
||||
alias(libs.plugins.jetbrains.kotlin.android)
|
||||
alias(libs.plugins.jetbrains.kotlin.serialization)
|
||||
kotlin("kapt")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.app.assistant"
|
||||
compileSdk = 35
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "com.app.assistant"
|
||||
minSdk = 24
|
||||
targetSdk = 35
|
||||
versionCode = 1
|
||||
versionName = "1.0"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables {
|
||||
useSupportLibrary = true
|
||||
}
|
||||
|
||||
// Read local.properties
|
||||
val localProperties = Properties()
|
||||
val localPropertiesFile = rootProject.file("local.properties")
|
||||
if (localPropertiesFile.exists()) {
|
||||
localProperties.load(FileInputStream(localPropertiesFile))
|
||||
}
|
||||
|
||||
// Define BuildConfig fields for API keys
|
||||
// Make sure YOUTUBE_API_KEY and GROQ_API_KEY exist in your local.properties
|
||||
buildConfigField("String", "YOUTUBE_API_KEY",
|
||||
"\"${localProperties.getProperty("YOUTUBE_API_KEY") ?: ""}\"" // Add quotes
|
||||
)
|
||||
|
||||
buildConfigField("String", "GROQ_API_KEY",
|
||||
"\"${localProperties.getProperty("GROQ_API_KEY") ?: ""}\"" // Add quotes
|
||||
)
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = false
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
// You might want to also define buildConfigFields for release
|
||||
// if you have different keys for release builds, or if local.properties is not available in CI
|
||||
}
|
||||
debug {
|
||||
// BuildConfigFields from defaultConfig are inherited.
|
||||
// Add specific debug configurations here if needed, for example:
|
||||
// isMinifyEnabled = false
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
buildFeatures {
|
||||
compose = true
|
||||
buildConfig = true // Ensure buildConfig is enabled
|
||||
}
|
||||
composeOptions {
|
||||
kotlinCompilerExtensionVersion = libs.versions.androidxComposeCompiler.get()
|
||||
}
|
||||
packaging {
|
||||
resources {
|
||||
excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation(libs.androidx.core.ktx)
|
||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||
implementation(libs.androidx.activity.compose)
|
||||
implementation(platform(libs.androidx.compose.bom))
|
||||
implementation(libs.androidx.ui)
|
||||
implementation(libs.androidx.ui.graphics)
|
||||
implementation(libs.androidx.ui.tooling.preview)
|
||||
implementation(libs.androidx.material3)
|
||||
implementation(libs.androidx.material3.window.size)
|
||||
implementation(libs.androidx.material3.adaptive.navigation.suite)
|
||||
implementation(libs.okhttp)
|
||||
constraints {
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3") {
|
||||
because("Kotlin 1.9.0 compatibility")
|
||||
}
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:1.6.3") {
|
||||
because("Kotlin 1.9.0 compatibility")
|
||||
}
|
||||
}
|
||||
implementation(libs.kotlinx.serialization.json)
|
||||
implementation(libs.mlkit.translate)
|
||||
implementation(libs.commonmark)
|
||||
//implementation ("com.github.jeziellago:compose-markdown:0.5.4")
|
||||
implementation(libs.coil.compose)
|
||||
implementation(libs.coil.network.okhttp)
|
||||
implementation(libs.androidx.room.runtime)
|
||||
implementation(libs.androidx.room.ktx)
|
||||
kapt(libs.androidx.room.compiler)
|
||||
implementation(libs.play.services.location)
|
||||
implementation(libs.androidx.ui.test.android)
|
||||
implementation(libs.androidx.security.crypto)
|
||||
implementation(libs.androidx.runtime.saveable)
|
||||
testImplementation(libs.junit)
|
||||
// androidTestImplementation(libs.androidx.junit)
|
||||
// androidTestImplementation(libs.androidx.espresso.core)
|
||||
// androidTestImplementation(platform(libs.androidx.compose.bom))
|
||||
// androidTestImplementation(libs.androidx.ui.test.junit4)
|
||||
debugImplementation(libs.androidx.ui.tooling)
|
||||
debugImplementation(libs.androidx.ui.test.manifest)
|
||||
|
||||
//MediaPipe library
|
||||
implementation(libs.mediapipe.tasks.text)
|
||||
}
|
||||
|
||||
configurations.all {
|
||||
resolutionStrategy {
|
||||
force("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3")
|
||||
force("org.jetbrains.kotlinx:kotlinx-serialization-core:1.6.3")
|
||||
force("org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.6.3")
|
||||
}
|
||||
}
|
||||
32
app/src/main/java/com/app/assistant/db/AppDatabase.kt
Normal file
32
app/src/main/java/com/app/assistant/db/AppDatabase.kt
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
package com.app.assistant.db
|
||||
|
||||
import android.content.Context
|
||||
import androidx.room.Database
|
||||
import androidx.room.Room
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.TypeConverters
|
||||
|
||||
@Database(entities = [GroupEntity::class, MessageEntity::class], version = 1, exportSchema = false)
|
||||
@TypeConverters(Converters::class)
|
||||
abstract class AppDatabase : RoomDatabase() {
|
||||
abstract fun conversationDao(): ConversationDao
|
||||
|
||||
companion object {
|
||||
@Volatile
|
||||
private var INSTANCE: AppDatabase? = null
|
||||
|
||||
fun getDatabase(context: Context): AppDatabase {
|
||||
return INSTANCE ?: synchronized(this) {
|
||||
val instance = Room.databaseBuilder(
|
||||
context.applicationContext,
|
||||
AppDatabase::class.java,
|
||||
"conversation.db"
|
||||
)
|
||||
.fallbackToDestructiveMigration()
|
||||
.build()
|
||||
INSTANCE = instance
|
||||
instance
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
34
app/src/main/java/com/app/assistant/db/ConversationDao.kt
Normal file
34
app/src/main/java/com/app/assistant/db/ConversationDao.kt
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
package com.app.assistant.db
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.Update
|
||||
|
||||
@Dao
|
||||
interface ConversationDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insertGroup(group: GroupEntity): Long
|
||||
|
||||
@Query("SELECT * FROM `groups` ORDER BY group_id DESC")
|
||||
suspend fun getAllGroups(): List<GroupEntity>
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insertMessage(message: MessageEntity)
|
||||
|
||||
@Query("DELETE FROM messages WHERE id = :messageId")
|
||||
suspend fun deleteMessageById(messageId: Long)
|
||||
|
||||
@Update
|
||||
suspend fun updateMessage(message: MessageEntity)
|
||||
|
||||
@Query("DELETE FROM messages WHERE id IN (:ids)")
|
||||
suspend fun deleteMessagesByIds(ids: List<Long>)
|
||||
|
||||
@Query("DELETE FROM `groups` WHERE group_id = :groupId")
|
||||
suspend fun deleteGroupById(groupId: Long)
|
||||
|
||||
@Query("SELECT * FROM messages WHERE group_id = :groupId ORDER BY id ASC")
|
||||
suspend fun getMessagesForGroup(groupId: Long): List<MessageEntity>
|
||||
}
|
||||
16
app/src/main/java/com/app/assistant/db/Converters.kt
Normal file
16
app/src/main/java/com/app/assistant/db/Converters.kt
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package com.app.assistant.db
|
||||
|
||||
import androidx.room.TypeConverter
|
||||
import java.net.URI
|
||||
|
||||
class Converters {
|
||||
@TypeConverter
|
||||
fun fromString(value: String?): URI? {
|
||||
return value?.let { URI(it) }
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun uriToString(uri: URI?): String? {
|
||||
return uri?.toString()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
package com.app.assistant.db
|
||||
|
||||
import android.content.Context
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import android.database.sqlite.SQLiteOpenHelper
|
||||
import com.app.assistant.model.Conversation
|
||||
import kotlin.reflect.full.memberProperties
|
||||
|
||||
class DynamicConversationDbHelper(
|
||||
context: Context,
|
||||
) : SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) {
|
||||
companion object {
|
||||
private const val DATABASE_NAME = "conversation.db"
|
||||
private const val DATABASE_VERSION = 1
|
||||
|
||||
private const val COLUMN_IV = "iv"
|
||||
|
||||
// Groups Table
|
||||
private const val TABLE_GROUPS = "`groups`"
|
||||
private const val COLUMN_GROUP_ID = "group_id"
|
||||
private const val COLUMN_TITLE = "title"
|
||||
private const val COLUMN_CREATED_AT = "created_at"
|
||||
|
||||
// Messages Table
|
||||
private const val TABLE_MESSAGES = "messages"
|
||||
}
|
||||
|
||||
override fun onCreate(db: SQLiteDatabase?) {
|
||||
val createGroupsTableQuery =
|
||||
"""
|
||||
CREATE TABLE $TABLE_GROUPS (
|
||||
$COLUMN_GROUP_ID INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
$COLUMN_TITLE TEXT,
|
||||
$COLUMN_CREATED_AT INTEGER
|
||||
)
|
||||
""".trimIndent()
|
||||
db?.execSQL(createGroupsTableQuery)
|
||||
|
||||
val createMessageTableQuery = buildCreateTableQuery()
|
||||
db?.execSQL(createMessageTableQuery)
|
||||
}
|
||||
|
||||
override fun onUpgrade(
|
||||
db: SQLiteDatabase?,
|
||||
oldVersion: Int,
|
||||
newVersion: Int,
|
||||
) {
|
||||
db?.execSQL("DROP TABLE IF EXISTS $TABLE_MESSAGES")
|
||||
db?.execSQL("DROP TABLE IF EXISTS $TABLE_GROUPS")
|
||||
onCreate(db)
|
||||
}
|
||||
|
||||
private fun buildCreateTableQuery(): String {
|
||||
val fields = Conversation::class.memberProperties
|
||||
val columns =
|
||||
fields.filterNot { it.name == "isLoading" }.joinToString(", ") { field ->
|
||||
val fieldType = mapFieldTypeToSQLiteType(field.returnType.toString())
|
||||
if (field.name == "id") {
|
||||
"${field.name} INTEGER PRIMARY KEY"
|
||||
} else {
|
||||
"${field.name} $fieldType"
|
||||
}
|
||||
}
|
||||
|
||||
return """
|
||||
CREATE TABLE $TABLE_MESSAGES (
|
||||
$columns,
|
||||
iv TEXT,
|
||||
$COLUMN_GROUP_ID INTEGER,
|
||||
FOREIGN KEY ($COLUMN_GROUP_ID) REFERENCES $TABLE_GROUPS($COLUMN_GROUP_ID)
|
||||
)
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
private fun mapFieldTypeToSQLiteType(kotlinType: String): String =
|
||||
when (kotlinType) {
|
||||
"kotlin.String" -> "TEXT"
|
||||
"kotlin.Int", "kotlin.Long", "kotlin.Boolean" -> "INTEGER"
|
||||
"java.net.URI" -> "TEXT"
|
||||
else -> "TEXT"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,48 +1,28 @@
|
|||
package com.app.assistant.db
|
||||
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.database.Cursor
|
||||
import android.text.format.DateFormat
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.snapshots.SnapshotStateList
|
||||
import com.app.assistant.model.Conversation
|
||||
import com.app.assistant.model.Group
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.net.URI
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
import kotlin.reflect.full.memberProperties
|
||||
import kotlin.reflect.full.primaryConstructor
|
||||
|
||||
class DynamicConversationRepository(
|
||||
private val context: Context,
|
||||
context: Context,
|
||||
) {
|
||||
companion object {
|
||||
private const val TABLE_GROUPS = "groups"
|
||||
private const val COLUMN_GROUP_ID = "group_id"
|
||||
private const val COLUMN_TITLE = "title"
|
||||
private const val COLUMN_CREATED_AT = "created_at"
|
||||
private const val TABLE_MESSAGES = "messages"
|
||||
private const val COLUMN_MESSAGE_ID = "id"
|
||||
}
|
||||
|
||||
private val dbHelper = DynamicConversationDbHelper(context)
|
||||
private val db = AppDatabase.getDatabase(context)
|
||||
private val dao = db.conversationDao()
|
||||
var currentGroupId: Long = -1L
|
||||
|
||||
private suspend fun startNewChat(msg: String): Long {
|
||||
withContext(Dispatchers.IO) {
|
||||
val db = dbHelper.writableDatabase
|
||||
val contentValues =
|
||||
ContentValues().apply {
|
||||
put(COLUMN_TITLE, generateChatTitle(msg))
|
||||
put(COLUMN_CREATED_AT, System.nanoTime())
|
||||
}
|
||||
currentGroupId = db.insert(TABLE_GROUPS, null, contentValues)
|
||||
return withContext(Dispatchers.IO) {
|
||||
val title = generateChatTitle(msg)
|
||||
val group = GroupEntity(title = title, createdAt = System.nanoTime())
|
||||
currentGroupId = dao.insertGroup(group)
|
||||
currentGroupId
|
||||
}
|
||||
return currentGroupId
|
||||
}
|
||||
|
||||
private fun generateChatTitle(msg: String): String {
|
||||
|
|
@ -58,30 +38,25 @@ class DynamicConversationRepository(
|
|||
currentGroupId = startNewChat(conversation.englishText.takeIf { it.isNotEmpty() } ?: conversation.translatedText)
|
||||
}
|
||||
|
||||
val db = dbHelper.writableDatabase
|
||||
val (encryptedMap, iv) = encryptConversation(conversation)
|
||||
val values =
|
||||
ContentValues().apply {
|
||||
encryptedMap.forEach { (key, value) -> put(key, value) }
|
||||
put("iv", iv)
|
||||
put(COLUMN_GROUP_ID, currentGroupId)
|
||||
}
|
||||
try {
|
||||
db.insert(TABLE_MESSAGES, null, values)
|
||||
} catch (e: Exception) {
|
||||
e.message?.let { Log.d("Test", it) }
|
||||
}
|
||||
val iv = EncryptionUtil.generateIV()
|
||||
val messageEntity = MessageEntity(
|
||||
id = conversation.id,
|
||||
englishText = conversation.englishText,
|
||||
translatedText = conversation.translatedText,
|
||||
isMe = conversation.isMe,
|
||||
category = conversation.category,
|
||||
contentURL = conversation.contentURL,
|
||||
navigationURI = conversation.navigationURI,
|
||||
iv = iv,
|
||||
groupId = currentGroupId
|
||||
)
|
||||
dao.insertMessage(messageEntity)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun deleteMessage(id: Long) {
|
||||
withContext(Dispatchers.IO) {
|
||||
val db = dbHelper.writableDatabase
|
||||
db.delete(
|
||||
TABLE_MESSAGES,
|
||||
"id = ?",
|
||||
arrayOf(id.toString()),
|
||||
)
|
||||
dao.deleteMessageById(id)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -90,130 +65,53 @@ class DynamicConversationRepository(
|
|||
newConversation: Conversation,
|
||||
) {
|
||||
withContext(Dispatchers.IO) {
|
||||
val db = dbHelper.writableDatabase
|
||||
val (encryptedMap, iv) = encryptConversation(newConversation)
|
||||
val values =
|
||||
ContentValues().apply {
|
||||
encryptedMap.forEach { (key, value) -> put(key, value) }
|
||||
put("iv", iv)
|
||||
}
|
||||
db.update(
|
||||
TABLE_MESSAGES,
|
||||
values,
|
||||
"id = ?",
|
||||
arrayOf(oldConversation.id.toString()),
|
||||
val iv = EncryptionUtil.generateIV()
|
||||
val messageEntity = MessageEntity(
|
||||
id = newConversation.id,
|
||||
englishText = newConversation.englishText,
|
||||
translatedText = newConversation.translatedText,
|
||||
isMe = newConversation.isMe,
|
||||
category = newConversation.category,
|
||||
contentURL = newConversation.contentURL,
|
||||
navigationURI = newConversation.navigationURI,
|
||||
iv = iv,
|
||||
groupId = currentGroupId
|
||||
)
|
||||
dao.updateMessage(messageEntity)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun clearMessages(conversations: List<Conversation>) {
|
||||
withContext(Dispatchers.IO) {
|
||||
val db = dbHelper.writableDatabase
|
||||
val ids = conversations.joinToString(",") { it.id.toString() }
|
||||
db.execSQL("DELETE FROM $TABLE_MESSAGES WHERE id IN ($ids)")
|
||||
db.execSQL("DELETE FROM $TABLE_GROUPS WHERE $COLUMN_GROUP_ID == $currentGroupId")
|
||||
val ids = conversations.map { it.id }
|
||||
dao.deleteMessagesByIds(ids)
|
||||
dao.deleteGroupById(currentGroupId)
|
||||
currentGroupId = -1L
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun loadAllGroups(): MutableList<Group> {
|
||||
val groupList = mutableListOf<Group>()
|
||||
withContext(Dispatchers.IO) {
|
||||
val db = dbHelper.readableDatabase
|
||||
val cursor: Cursor =
|
||||
db.query(
|
||||
TABLE_GROUPS,
|
||||
arrayOf(COLUMN_GROUP_ID, COLUMN_TITLE),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
"$COLUMN_GROUP_ID DESC", // Sort by latest created
|
||||
)
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
val groupId = cursor.getLong(cursor.getColumnIndexOrThrow(COLUMN_GROUP_ID))
|
||||
val title = cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_TITLE))
|
||||
groupList.add(Group(groupId, title))
|
||||
}
|
||||
cursor.close()
|
||||
return withContext(Dispatchers.IO) {
|
||||
dao.getAllGroups().map { entity ->
|
||||
Group(groupId = entity.groupId, title = entity.title)
|
||||
}.toMutableList()
|
||||
}
|
||||
return groupList
|
||||
}
|
||||
|
||||
suspend fun loadMessagesForGroup(groupId: Long): MutableList<Conversation> {
|
||||
val tempList = mutableListOf<Conversation>()
|
||||
withContext(Dispatchers.IO) {
|
||||
val db = dbHelper.readableDatabase
|
||||
val cursor: Cursor =
|
||||
db.query(
|
||||
TABLE_MESSAGES,
|
||||
null,
|
||||
"$COLUMN_GROUP_ID = ?",
|
||||
arrayOf(groupId.toString()),
|
||||
null,
|
||||
null,
|
||||
COLUMN_MESSAGE_ID, // Sort by latest created
|
||||
return withContext(Dispatchers.IO) {
|
||||
dao.getMessagesForGroup(groupId).map { entity ->
|
||||
Conversation(
|
||||
id = entity.id,
|
||||
englishText = entity.englishText,
|
||||
translatedText = entity.translatedText,
|
||||
isMe = entity.isMe,
|
||||
isLoading = false,
|
||||
category = entity.category,
|
||||
contentURL = entity.contentURL,
|
||||
navigationURI = entity.navigationURI
|
||||
)
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
val conversation = cursorToConversation(cursor)
|
||||
tempList.add(conversation)
|
||||
}
|
||||
cursor.close()
|
||||
}.toMutableList()
|
||||
}
|
||||
return tempList
|
||||
}
|
||||
|
||||
private fun cursorToConversation(cursor: Cursor): Conversation {
|
||||
val properties = Conversation::class.memberProperties
|
||||
val fieldMap = mutableMapOf<String, Any?>()
|
||||
val iv = cursor.getString(cursor.getColumnIndexOrThrow("iv"))
|
||||
|
||||
properties.filter { it.name != "isLoading" }.forEach { property ->
|
||||
val columnName = property.name
|
||||
val columnIndex = cursor.getColumnIndexOrThrow(columnName)
|
||||
val value =
|
||||
when (property.returnType.toString()) {
|
||||
"kotlin.String" -> cursor.getString(columnIndex)
|
||||
|
||||
// ?.let { EncryptionUtil.decrypt(it, iv) }
|
||||
|
||||
"kotlin.Int" -> cursor.getInt(columnIndex)
|
||||
|
||||
"kotlin.Long" -> cursor.getLong(columnIndex)
|
||||
|
||||
"kotlin.Boolean" -> cursor.getString(columnIndex).toBooleanStrictOrNull() ?: false
|
||||
|
||||
"java.net.URI" -> URI(cursor.getString(columnIndex))
|
||||
|
||||
else -> null
|
||||
}
|
||||
fieldMap[columnName] = value
|
||||
}
|
||||
|
||||
return Conversation::class.constructors.first().callBy(
|
||||
Conversation::class
|
||||
.primaryConstructor!!
|
||||
.parameters
|
||||
.filter { it.name != "isLoading" } // Ignore "isLoading"
|
||||
.associateWith { parameter -> fieldMap[parameter.name] },
|
||||
)
|
||||
}
|
||||
|
||||
private fun encryptConversation(conversation: Conversation): Pair<Map<String, String>, String> {
|
||||
val properties = Conversation::class.memberProperties
|
||||
val encryptedMap = mutableMapOf<String, String>()
|
||||
val iv = EncryptionUtil.generateIV()
|
||||
|
||||
properties.forEach { property ->
|
||||
val key = property.name
|
||||
val value = property.getter.call(conversation)?.toString()
|
||||
if (value != null && key != "isLoading") {
|
||||
// encryptedMap[key] = EncryptionUtil.encrypt(value, iv).first
|
||||
encryptedMap[key] = value
|
||||
}
|
||||
}
|
||||
return Pair(encryptedMap, iv)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
18
app/src/main/java/com/app/assistant/db/GroupEntity.kt
Normal file
18
app/src/main/java/com/app/assistant/db/GroupEntity.kt
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
package com.app.assistant.db
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(tableName = "groups")
|
||||
data class GroupEntity(
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
@ColumnInfo(name = "group_id")
|
||||
val groupId: Long = 0,
|
||||
|
||||
@ColumnInfo(name = "title")
|
||||
val title: String,
|
||||
|
||||
@ColumnInfo(name = "created_at")
|
||||
val createdAt: Long
|
||||
)
|
||||
49
app/src/main/java/com/app/assistant/db/MessageEntity.kt
Normal file
49
app/src/main/java/com/app/assistant/db/MessageEntity.kt
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
package com.app.assistant.db
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import androidx.room.Index
|
||||
import androidx.room.PrimaryKey
|
||||
import java.net.URI
|
||||
|
||||
@Entity(
|
||||
tableName = "messages",
|
||||
foreignKeys = [
|
||||
ForeignKey(
|
||||
entity = GroupEntity::class,
|
||||
parentColumns = ["group_id"],
|
||||
childColumns = ["group_id"],
|
||||
onDelete = ForeignKey.CASCADE
|
||||
)
|
||||
],
|
||||
indices = [Index(value = ["group_id"])]
|
||||
)
|
||||
data class MessageEntity(
|
||||
@PrimaryKey
|
||||
val id: Long,
|
||||
|
||||
@ColumnInfo(name = "englishText")
|
||||
val englishText: String,
|
||||
|
||||
@ColumnInfo(name = "translatedText")
|
||||
val translatedText: String,
|
||||
|
||||
@ColumnInfo(name = "isMe")
|
||||
val isMe: Boolean,
|
||||
|
||||
@ColumnInfo(name = "category")
|
||||
val category: String,
|
||||
|
||||
@ColumnInfo(name = "contentURL")
|
||||
val contentURL: String,
|
||||
|
||||
@ColumnInfo(name = "navigationURI")
|
||||
val navigationURI: URI,
|
||||
|
||||
@ColumnInfo(name = "iv")
|
||||
val iv: String,
|
||||
|
||||
@ColumnInfo(name = "group_id")
|
||||
val groupId: Long
|
||||
)
|
||||
|
|
@ -6,21 +6,34 @@ import android.util.Log
|
|||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.Call
|
||||
import okhttp3.Callback
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
import java.io.IOException
|
||||
import java.util.Locale
|
||||
|
||||
@Serializable
|
||||
data class GeocodingResult(
|
||||
val name: String,
|
||||
val latitude: Double,
|
||||
val longitude: Double
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class GeocodingResponse(
|
||||
val results: List<GeocodingResult>? = null
|
||||
)
|
||||
|
||||
class WeatherRepository(
|
||||
private val context: Context,
|
||||
) {
|
||||
private val client = OkHttpClient()
|
||||
private val json = Json { ignoreUnknownKeys = true }
|
||||
|
||||
fun extractPlaceName(inputText: String): String {
|
||||
// Pattern to match place names after "in" or "for", excluding trailing words like "today", "tomorrow", etc.
|
||||
|
|
@ -98,32 +111,17 @@ class WeatherRepository(
|
|||
cityName: String,
|
||||
): Pair<Double, Double>? {
|
||||
if (body.isEmpty()) return null
|
||||
val jsonObject = JSONObject(body)
|
||||
var latitude: Double? = null
|
||||
var longitude: Double? = null
|
||||
|
||||
try {
|
||||
val results = jsonObject.getJSONArray("results")
|
||||
val response = json.decodeFromString<GeocodingResponse>(body)
|
||||
val results = response.results ?: return null
|
||||
if (results.isEmpty()) return null
|
||||
|
||||
for (i in 0 until results.length()) {
|
||||
val result = results.getJSONObject(i)
|
||||
if (result.getString("name").equals(cityName, ignoreCase = true)) {
|
||||
latitude = result.getDouble("latitude")
|
||||
longitude = result.getDouble("longitude")
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if ((latitude == null || longitude == null) && results.length() > 0) {
|
||||
val firstResult = results.getJSONObject(0)
|
||||
latitude = firstResult.getDouble("latitude")
|
||||
longitude = firstResult.getDouble("longitude")
|
||||
}
|
||||
val matchedResult = results.find { it.name.equals(cityName, ignoreCase = true) } ?: results[0]
|
||||
return Pair(matchedResult.latitude, matchedResult.longitude)
|
||||
} catch (e: Exception) {
|
||||
e.message?.let { Log.d("extractCoordinates Excepetion", it) }
|
||||
Log.d("extractCoordinates Exception", e.message ?: "")
|
||||
}
|
||||
|
||||
return if (latitude != null && longitude != null) Pair(latitude, longitude) else null
|
||||
return null
|
||||
}
|
||||
|
||||
fun getCityNameFromLocation(
|
||||
|
|
@ -147,8 +145,8 @@ class WeatherRepository(
|
|||
suspend fun getWeatherData(
|
||||
latitude: Double,
|
||||
longitude: Double,
|
||||
): JSONObject? {
|
||||
val deferredResult = CompletableDeferred<JSONObject?>()
|
||||
): String? {
|
||||
val deferredResult = CompletableDeferred<String?>()
|
||||
|
||||
val urlBuilder =
|
||||
"https://api.open-meteo.com/v1/forecast"
|
||||
|
|
@ -184,12 +182,7 @@ class WeatherRepository(
|
|||
) {
|
||||
if (response.isSuccessful) {
|
||||
val body = response.body?.string() ?: ""
|
||||
try {
|
||||
deferredResult.complete(JSONObject(body))
|
||||
} catch (e: JSONException) {
|
||||
Log.e("WeatherRepository", "Error parsing JSON: $e")
|
||||
deferredResult.complete(null)
|
||||
}
|
||||
deferredResult.complete(body)
|
||||
} else {
|
||||
Log.e("WeatherRepository", "Weather data request unsuccessful: ${response.code}")
|
||||
deferredResult.complete(null)
|
||||
|
|
|
|||
|
|
@ -5,16 +5,42 @@ import com.app.assistant.BuildConfig
|
|||
import com.app.assistant.repository.SettingsRepository
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import java.io.IOException
|
||||
import java.net.UnknownHostException
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
@Serializable
|
||||
data class GroqMessage(
|
||||
val role: String,
|
||||
val content: String
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class GroqRequest(
|
||||
val model: String,
|
||||
val messages: List<GroqMessage>,
|
||||
val temperature: Double = 1.0,
|
||||
val top_p: Double = 1.0,
|
||||
val stop: String? = null
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class GroqResponse(
|
||||
val choices: List<GroqChoice>
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class GroqChoice(
|
||||
val message: GroqMessage
|
||||
)
|
||||
|
||||
class GetAiResponseUseCase(
|
||||
private val settingsRepository: SettingsRepository
|
||||
) {
|
||||
|
|
@ -24,16 +50,18 @@ class GetAiResponseUseCase(
|
|||
.writeTimeout(60, TimeUnit.SECONDS)
|
||||
.build()
|
||||
|
||||
suspend fun execute(messagesArray: JSONArray): String? {
|
||||
val requestBodyJson = JSONObject().apply {
|
||||
put("model", "llama-3.3-70b-versatile")
|
||||
put("messages", messagesArray)
|
||||
put("temperature", 1)
|
||||
put("top_p", 1)
|
||||
put("stop", null as Any?)
|
||||
}
|
||||
private val json = Json { ignoreUnknownKeys = true }
|
||||
|
||||
val requestBody = requestBodyJson.toString()
|
||||
suspend fun execute(messages: List<GroqMessage>): String? {
|
||||
val requestObj = GroqRequest(
|
||||
model = "llama-3.3-70b-versatile",
|
||||
messages = messages,
|
||||
temperature = 1.0,
|
||||
top_p = 1.0,
|
||||
stop = null
|
||||
)
|
||||
|
||||
val requestBody = json.encodeToString(requestObj)
|
||||
.toRequestBody("application/json".toMediaTypeOrNull())
|
||||
|
||||
val chatKey = loadChatKey()
|
||||
|
|
@ -69,12 +97,9 @@ class GetAiResponseUseCase(
|
|||
fun extractFromAI(response: String): String? {
|
||||
try {
|
||||
if (response.isNotEmpty()) {
|
||||
val jsonObject = JSONObject(response)
|
||||
val choicesArray = jsonObject.getJSONArray("choices")
|
||||
if (choicesArray.length() > 0) {
|
||||
val choiceObject = choicesArray.getJSONObject(0)
|
||||
val messageObject = choiceObject.getJSONObject("message")
|
||||
var content = messageObject.getString("content")
|
||||
val responseObj = json.decodeFromString<GroqResponse>(response)
|
||||
if (responseObj.choices.isNotEmpty()) {
|
||||
var content = responseObj.choices[0].message.content
|
||||
|
||||
// Regex to remove <think> tags and the content inside
|
||||
val thinkTagRegex = Regex("<think>.*?</think>", RegexOption.DOT_MATCHES_ALL)
|
||||
|
|
|
|||
|
|
@ -6,8 +6,9 @@ import android.content.pm.PackageManager
|
|||
import android.util.Log
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.app.assistant.repository.WeatherRepository
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Calendar
|
||||
import java.util.Locale
|
||||
|
|
@ -32,10 +33,11 @@ class GetWeatherUseCase(
|
|||
val (lat, long) = coordinates
|
||||
val weatherData = weatherRepository.getWeatherData(lat, long)
|
||||
if (weatherData != null) {
|
||||
weatherData.remove("latitude")
|
||||
weatherData.remove("longitude")
|
||||
val weatherJson = Json.parseToJsonElement(weatherData).jsonObject
|
||||
val filteredMap = weatherJson.filterKeys { it != "latitude" && it != "longitude" }
|
||||
val filteredJson = JsonObject(filteredMap)
|
||||
|
||||
val aiResponse = queryWeatherAI(prompt, weatherData.toString())
|
||||
val aiResponse = queryWeatherAI(prompt, filteredJson.toString())
|
||||
if (!aiResponse.isNullOrEmpty()) {
|
||||
onSuccess(aiResponse, location)
|
||||
} else {
|
||||
|
|
@ -75,10 +77,11 @@ class GetWeatherUseCase(
|
|||
val city = weatherRepository.getCityNameFromLocation(lat, long) ?: "your location"
|
||||
val weatherData = weatherRepository.getWeatherData(lat, long)
|
||||
if (weatherData != null) {
|
||||
weatherData.remove("latitude")
|
||||
weatherData.remove("longitude")
|
||||
val weatherJson = Json.parseToJsonElement(weatherData).jsonObject
|
||||
val filteredMap = weatherJson.filterKeys { it != "latitude" && it != "longitude" }
|
||||
val filteredJson = JsonObject(filteredMap)
|
||||
|
||||
val aiResponse = queryWeatherAI(prompt, weatherData.toString())
|
||||
val aiResponse = queryWeatherAI(prompt, filteredJson.toString())
|
||||
if (!aiResponse.isNullOrEmpty()) {
|
||||
onSuccess(aiResponse, city)
|
||||
} else {
|
||||
|
|
@ -90,8 +93,6 @@ class GetWeatherUseCase(
|
|||
}
|
||||
|
||||
private suspend fun queryWeatherAI(question: String, weatherData: String): String? {
|
||||
val messagesArray = JSONArray()
|
||||
|
||||
val date = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault(Locale.Category.FORMAT)).format(Calendar.getInstance().time)
|
||||
val time = SimpleDateFormat("HH:mm:ss", Locale.getDefault(Locale.Category.FORMAT)).format(Calendar.getInstance().time)
|
||||
val systemContext =
|
||||
|
|
@ -99,9 +100,11 @@ class GetWeatherUseCase(
|
|||
"which is $date at $time. Please respond with an answer to the user's question based on " +
|
||||
"the latest weather data provided. No need to mention data or time; just answer naturally in 2 to 3 lines."
|
||||
|
||||
messagesArray.put(JSONObject().put("role", "system").put("content", systemContext))
|
||||
messagesArray.put(JSONObject().put("role", "user").put("content", question + System.lineSeparator() + weatherData))
|
||||
val messages = listOf(
|
||||
GroqMessage(role = "system", content = systemContext),
|
||||
GroqMessage(role = "user", content = question + System.lineSeparator() + weatherData)
|
||||
)
|
||||
|
||||
return getAiResponseUseCase.execute(messagesArray)
|
||||
return getAiResponseUseCase.execute(messages)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,21 +6,54 @@ import android.util.Log
|
|||
import com.app.assistant.BuildConfig
|
||||
import com.app.assistant.repository.SettingsRepository
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.Call
|
||||
import okhttp3.Callback
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import org.json.JSONObject
|
||||
import java.io.IOException
|
||||
import java.net.URI
|
||||
|
||||
@Serializable
|
||||
data class YouTubeItemId(
|
||||
val kind: String? = null,
|
||||
val videoId: String? = null
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class YouTubeThumbnailDetail(
|
||||
val url: String? = null
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class YouTubeThumbnails(
|
||||
val high: YouTubeThumbnailDetail? = null
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class YouTubeSnippet(
|
||||
val thumbnails: YouTubeThumbnails? = null
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class YouTubeSearchItem(
|
||||
val id: YouTubeItemId? = null,
|
||||
val snippet: YouTubeSnippet? = null
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class YouTubeSearchResponse(
|
||||
val items: List<YouTubeSearchItem>? = null
|
||||
)
|
||||
|
||||
class PlaySongUseCase(
|
||||
private val settingsRepository: SettingsRepository
|
||||
) {
|
||||
private val json = Json { ignoreUnknownKeys = true }
|
||||
|
||||
suspend fun execute(
|
||||
prompt: String,
|
||||
onIntentTriggered: suspend (Intent) -> Unit,
|
||||
|
|
@ -112,23 +145,20 @@ class PlaySongUseCase(
|
|||
}
|
||||
|
||||
private fun extractVideoIdAndThumbnail(jsonString: String): Pair<String, String> {
|
||||
val jsonObject = JSONObject(jsonString)
|
||||
val itemsArray = jsonObject.optJSONArray("items") ?: return Pair("", "")
|
||||
|
||||
if (itemsArray.length() > 0) {
|
||||
val firstItem = itemsArray.optJSONObject(0) ?: return Pair("", "")
|
||||
val idObject = firstItem.optJSONObject("id") ?: return Pair("", "")
|
||||
|
||||
if (idObject.optString("kind") == "youtube#video") {
|
||||
val videoId = idObject.optString("videoId", "")
|
||||
val thumbnailUrl = firstItem
|
||||
.optJSONObject("snippet")
|
||||
?.optJSONObject("thumbnails")
|
||||
?.optJSONObject("high")
|
||||
?.optString("url", "") ?: ""
|
||||
|
||||
return Pair(videoId, thumbnailUrl)
|
||||
try {
|
||||
val response = json.decodeFromString<YouTubeSearchResponse>(jsonString)
|
||||
val items = response.items ?: return Pair("", "")
|
||||
if (items.isNotEmpty()) {
|
||||
val firstItem = items[0]
|
||||
val idObject = firstItem.id ?: return Pair("", "")
|
||||
if (idObject.kind == "youtube#video") {
|
||||
val videoId = idObject.videoId ?: ""
|
||||
val thumbnailUrl = firstItem.snippet?.thumbnails?.high?.url ?: ""
|
||||
return Pair(videoId, thumbnailUrl)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("PlaySongUseCase", "Error parsing YouTube JSON", e)
|
||||
}
|
||||
return Pair("", "")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@ package com.app.assistant.usecase
|
|||
import com.app.assistant.model.Conversation
|
||||
import com.app.assistant.util.Category
|
||||
import com.google.mediapipe.tasks.text.textclassifier.TextClassifierResult
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import java.util.Locale
|
||||
|
||||
class ProcessChatCommandUseCase(
|
||||
|
|
@ -96,15 +94,14 @@ class ProcessChatCommandUseCase(
|
|||
systemContext: String,
|
||||
chatHistory: List<Conversation>
|
||||
): String? {
|
||||
val messagesArray = JSONArray()
|
||||
messagesArray.put(JSONObject().put("role", "system").put("content", systemContext))
|
||||
val messages = mutableListOf<GroqMessage>()
|
||||
messages.add(GroqMessage(role = "system", content = systemContext))
|
||||
|
||||
for (item in chatHistory) {
|
||||
val role = if (item.isMe) "user" else "assistant"
|
||||
val messageObject = JSONObject().put("role", role).put("content", item.englishText)
|
||||
messagesArray.put(messageObject)
|
||||
messages.add(GroqMessage(role = role, content = item.englishText))
|
||||
}
|
||||
|
||||
return getAiResponseUseCase.execute(messagesArray)
|
||||
return getAiResponseUseCase.execute(messages)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
||||
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 --add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED --add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED --add-opens=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED --add-opens=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED --add-opens=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED --add-opens=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED --add-opens=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED --add-opens=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED --add-opens=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. For more details, visit
|
||||
# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
|
||||
|
|
@ -20,4 +20,6 @@ kotlin.code.style=official
|
|||
# Enables namespacing of each library's R class so that its R class includes only the
|
||||
# resources declared in the library itself and none from the library's dependencies,
|
||||
# thereby reducing the size of the R class for that library
|
||||
android.nonTransitiveRClass=true
|
||||
android.nonTransitiveRClass=true
|
||||
kotlin.daemon.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 --add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED --add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED --add-opens=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED --add-opens=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED --add-opens=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED --add-opens=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED --add-opens=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED --add-opens=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED --add-opens=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
|
||||
kotlin.compiler.execution.strategy=in-process
|
||||
|
|
@ -25,6 +25,8 @@ mediapipeTasksText = "0.10.29"
|
|||
kotlinSerialization = "1.9.0"
|
||||
androidxComposeCompiler = "1.5.1"
|
||||
spotless = "8.4.0"
|
||||
room = "2.6.1"
|
||||
|
||||
|
||||
[libraries]
|
||||
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
||||
|
|
@ -57,6 +59,10 @@ androidx-ui-test-android = { group = "androidx.compose.ui", name = "ui-test-andr
|
|||
androidx-security-crypto = { group = "androidx.security", name = "security-crypto", version.ref = "securityCrypto" }
|
||||
androidx-runtime-saveable = { group = "androidx.compose.runtime", name = "runtime-saveable", version.ref = "runtimeSaveable" }
|
||||
mediapipe-tasks-text = { group = "com.google.mediapipe", name = "tasks-text", version.ref = "mediapipeTasksText" }
|
||||
androidx-room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" }
|
||||
androidx-room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" }
|
||||
androidx-room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" }
|
||||
|
||||
|
||||
[plugins]
|
||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue