Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/dataAndAi' into feature/home
Browse files Browse the repository at this point in the history
# Conflicts:
#	app/src/main/java/com/example/pa/ui/home/HomeFragment.kt
#	app/src/main/res/layout/fragment_home.xml
#	app/src/main/res/layout/fragment_slideshow.xml
#	app/src/main/res/menu/activity_main_drawer.xml
#	app/src/main/res/navigation/mobile_navigation.xml
#	app/src/main/res/values/strings.xml
#	gradle.properties
  • Loading branch information
maisawr committed Feb 20, 2024
2 parents 68a413a + cc68754 commit 83620f4
Show file tree
Hide file tree
Showing 19 changed files with 329 additions and 87 deletions.
22 changes: 16 additions & 6 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
Expand All @@ -17,15 +18,17 @@ android {
versionName = "1.0"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"

}

buildTypes {
release {
getByName("debug") {
buildConfigField("String", "OPEN_AI_KEY", "${properties["OpenAIKey"]}")
}
getByName("release") {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
buildConfigField("String", "OPEN_AI_KEY", "${properties["OpenAIKey"]}")
}
}
compileOptions {
Expand All @@ -37,11 +40,19 @@ android {
}
buildFeatures {
viewBinding = true
buildConfig = true
}

}

dependencies {

implementation("androidx.room:room-runtime:2.6.1")
kapt("androidx.room:room-compiler:2.6.1")
implementation("androidx.room:room-ktx:2.6.1")

implementation("com.aallam.openai:openai-client:3.0.0")
implementation("io.ktor:ktor-client-android:2.2.4")
implementation("androidx.core:core-ktx:1.12.0")
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("com.google.android.material:material:1.11.0")
Expand All @@ -50,7 +61,6 @@ dependencies {
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0")
implementation("androidx.navigation:navigation-fragment-ktx:2.7.7")
implementation("androidx.navigation:navigation-ui-ktx:2.7.7")
implementation("androidx.room:room-compiler:2.6.1")
implementation("androidx.room:room-common:2.6.1")
implementation("androidx.room:room-ktx:2.6.1")
testImplementation("junit:junit:4.13.2")
Expand Down
8 changes: 8 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.INTERNET" />

<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
Expand All @@ -12,6 +14,12 @@
android:supportsRtl="true"
android:theme="@style/Theme.PA"
tools:targetApi="31">
<activity
android:name=".data.local.ChangeLocalDataString"
android:exported="false" />
<activity
android:name=".ui.aigenerate.AiGenerateViewData"
android:exported="false" />
<activity
android:name=".MainActivity"
android:exported="true"
Expand Down
19 changes: 13 additions & 6 deletions app/src/main/java/com/example/pa/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,48 +2,55 @@ package com.example.pa

import android.os.Bundle
import android.view.Menu
import com.google.android.material.snackbar.Snackbar
import com.google.android.material.navigation.NavigationView
import androidx.appcompat.app.AppCompatActivity
import androidx.drawerlayout.widget.DrawerLayout
import androidx.navigation.findNavController
import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.navigateUp
import androidx.navigation.ui.setupActionBarWithNavController
import androidx.navigation.ui.setupWithNavController
import androidx.drawerlayout.widget.DrawerLayout
import androidx.appcompat.app.AppCompatActivity
import com.example.pa.databinding.ActivityMainBinding
import com.google.android.material.navigation.NavigationView
import com.google.android.material.snackbar.Snackbar


class MainActivity : AppCompatActivity() {

private lateinit var appBarConfiguration: AppBarConfiguration
private lateinit var binding: ActivityMainBinding


override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)



setSupportActionBar(binding.appBarMain.toolbar)

binding.appBarMain.fab.setOnClickListener { view ->
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show()
}

val drawerLayout: DrawerLayout = binding.drawerLayout
val navView: NavigationView = binding.navView
val navController = findNavController(R.id.nav_host_fragment_content_main)
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
appBarConfiguration = AppBarConfiguration(
setOf(
R.id.nav_home, R.id.nav_create_manually, R.id.nav_slideshow
), drawerLayout
)
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)


}



override fun onCreateOptionsMenu(menu: Menu): Boolean {
// Inflate the menu; this adds items to the action bar if it is present.
menuInflater.inflate(R.menu.main, menu)
Expand Down
3 changes: 3 additions & 0 deletions app/src/main/java/com/example/pa/data/DatabaseRepository.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
*/
package com.example.pa.data

import android.util.Log
import androidx.annotation.WorkerThread
import com.example.pa.data.local.TaskDao
import com.example.pa.data.local.Tasks
Expand All @@ -23,5 +24,7 @@ class DatabaseRepository(private val taskDao: TaskDao) {
@WorkerThread
suspend fun insert(task: Tasks) {
taskDao.insert(task)
Log.d("DatabaseLog", "Insert database: $task")

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.example.pa.data.local
import androidx.room.TypeConverter
import java.time.LocalDate
import java.time.format.DateTimeFormatter

class LocalDateTimeConverters {

@TypeConverter
fun changeString(value: String?): LocalDate? {
return value?.let {
LocalDate.parse(it, DateTimeFormatter.ISO_LOCAL_DATE)
}
}

@TypeConverter
fun changeLocalDate(date: LocalDate?): String? {
return date?.format(DateTimeFormatter.ISO_LOCAL_DATE)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.room.TypeConverters

// Define the database with the classes of each table
// Assign a version for the database
@Database(entities = arrayOf(Tasks::class), version = 1, exportSchema = false)
public abstract class PlannerDatabase: RoomDatabase() {
@Database(entities = [Tasks::class], version = 1, exportSchema = false)
@TypeConverters(LocalDateTimeConverters::class)
abstract class PlannerDatabase: RoomDatabase() {

abstract fun taskDao(): TaskDao

Expand Down
10 changes: 5 additions & 5 deletions app/src/main/java/com/example/pa/data/local/Tasks.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ package com.example.pa.data.local
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import java.time.LocalDateTime
import java.time.LocalDate

@Entity(tableName = "tasks_table")
class Tasks (
@PrimaryKey(autoGenerate = true) val taskId: Int,
data class Tasks(
@PrimaryKey(autoGenerate = true) @ColumnInfo(name = "task_id") val taskId: Int,
@ColumnInfo(name = "task_description") val taskInput: String?,
@ColumnInfo(name = "start_date_time") val startDatetime: LocalDateTime, //TODO: check SQLite data for datetime
@ColumnInfo(name = "task_duration") val taskDuration: Integer,
@ColumnInfo(name = "start_date_time") val startDate: LocalDate, //TODO: check SQLite data for datetime
@ColumnInfo(name = "task_duration") val taskDuration: Int,
@ColumnInfo(name = "task_complete") val isTaskComplete: Boolean
)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.example.pa.ui.aigenerate

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import com.example.pa.R
import com.example.pa.data.DatabaseRepository
import com.example.pa.data.local.PlannerDatabase
import com.example.pa.databinding.FragmentAiGenerateBinding

class AiGenerateFragment : Fragment() {

private var _binding: FragmentAiGenerateBinding? = null
private val binding get() = _binding!!
private lateinit var viewModel: AiGenerateViewModel

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
restorePreviousState: Bundle?
): View {
val database = PlannerDatabase.getDatabase(requireContext())
val repository = DatabaseRepository(database.taskDao())
val factory = AiGenerateViewData(repository)

viewModel = ViewModelProvider(this, factory).get(AiGenerateViewModel::class.java)


_binding = FragmentAiGenerateBinding.inflate(inflater, container, false)
val root: View = binding.root

viewModel.text.observe(viewLifecycleOwner) { responseText ->
binding.aiGenerateResponse.text = responseText
}

binding.aiGenerateButton.setOnClickListener {
val topicInput = binding.aiGenerateTopicInput.text.toString()
val dateInput = binding.aiGenerateDateInput.text.toString()
val durationInput = binding.aiGenerateDurationInput.text.toString()

if (topicInput.isNotEmpty() && dateInput.isNotEmpty() && durationInput.isNotEmpty()) {
val durationInt = durationInput.filter { it.isDigit() }.toIntOrNull() ?: 0
viewModel.fetchChatCompletion(topicInput, dateInput, durationInt)
viewModel.insertTask(topicInput, dateInput, durationInt)

} else {
binding.aiGenerateResponse.text = getString(R.string.ai_generate_topic_input_hint)

}

}

return root
}

override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.example.pa.ui.aigenerate

import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.example.pa.data.DatabaseRepository

// Implement the viewModelProvider.Factory
class AiGenerateViewData(private val repository: DatabaseRepository) : ViewModelProvider.Factory {

//Deliver to the ViewModel creator to create an instance and provide this instance when requested by the ViewModelProvider
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(AiGenerateViewModel::class.java)) {
@Suppress("UNCHECKED_CAST")
return AiGenerateViewModel(repository) as T
}
throw IllegalArgumentException("No correspond ViewModel")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.example.pa.ui.aigenerate

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.aallam.openai.api.BetaOpenAI
import com.aallam.openai.api.chat.ChatCompletion
import com.aallam.openai.api.chat.ChatCompletionRequest
import com.aallam.openai.api.chat.ChatMessage
import com.aallam.openai.api.chat.ChatRole
import com.aallam.openai.api.model.ModelId
import com.aallam.openai.client.OpenAI
import com.example.pa.BuildConfig
import com.example.pa.data.DatabaseRepository
import com.example.pa.data.local.Tasks
import kotlinx.coroutines.launch
import java.time.LocalDate
import java.time.format.DateTimeFormatter

class AiGenerateViewModel(private val repository: DatabaseRepository) : ViewModel() {

private val openAI = OpenAI(BuildConfig.OPEN_AI_KEY)

private val _text = MutableLiveData<String>()
val text: LiveData<String> = _text
fun insertTask(topic: String, startDate: String, duration: Int) {
// Convert the start date from String to LocalDateTime
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")
val startDate = LocalDate.parse(startDate, formatter)

// Convert duration from String to Integer
val taskDuration = duration

// Create the Tasks entity
val task = Tasks(
taskId = 0,
taskInput = topic,
startDate = startDate,
taskDuration = taskDuration,
isTaskComplete = false
)
viewModelScope.launch {
repository.insert(task)
}
}
@OptIn(BetaOpenAI::class)
fun fetchChatCompletion(topic: String, date: String, duration: Int) {
viewModelScope.launch {
val schedule = "Create a schedule for the following details: " +
"Topic - $topic, Date - $date, Duration - $duration weeks."
val chatCompletionRequest = ChatCompletionRequest(
model = ModelId("gpt-3.5-turbo"),
messages = listOf(
ChatMessage(
role = ChatRole.System,
content = "You are a helpful assistant!"
),
ChatMessage(
role = ChatRole.User,
content = schedule
)
)
)

val completion: ChatCompletion = openAI.chatCompletion(chatCompletionRequest)
_text.postValue(completion.choices.first().message?.content ?: "No response")
}
}
}
Loading

0 comments on commit 83620f4

Please sign in to comment.