본문 바로가기

안드로이드 프로그래밍

[안드로이드 1일차] 챗봇 만들기

결과물!





WeatherDTO.java

package com.cjwplatform.lbot
// json 데이터를 파싱하기 위한 DTO
/*
JSON 데이터를 파싱하기 위해서는 안드로이드 라이브러리 GSON이 필요함.
setting modules에서 gson, okhttp 디펜던시 추가.
* */
data class WeatherDTO(
var cod : String? = null,
var message : Float? = null,
var cnt : Int? = null,
var list : MutableList<List>? = null
){
data class List(
var dt : Int? = null,
var main : Main? = null,
var weather : MutableList<Weather>? = null,
var clouds : Clouds?= null,
var wind : Wind? = null,
var dt_txt : String? = null
){
data class Main(
var temp : Float? = null,
var temp_min : Float? = null,
var temp_max : Float? = null,
var pressure : Float? = null,
var sea_level : Float? = null,
var grnd_level : Float? = null,
var humidity : Float? = null,
var temp_kf : Float? = null
)
data class Weather(
var id : Int?= null,
var main : String? = null,
var description : String? =null,
var icon : String?= null
)
data class Clouds(
var all : Int?=null
)
data class Wind(
var speed : Float? = null,
var deg : Float? = null
)
}
}


MyRecyclerViewAdapter.kt

package com.cjwplatform.lbot
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.ViewGroup
import android.view.View
import kotlinx.android.synthetic.main.item_comment.view.*


data class MessageDTO(var myMessage:Boolean? = null, var message:String? = null)

class MyRecyclerViewAdapter(messageDTOs:ArrayList<MessageDTO>) : RecyclerView.Adapter<RecyclerView.ViewHolder>(){
var messageDTOs = messageDTOs
override fun onCreateViewHolder(p0: ViewGroup, p1: Int): RecyclerView.ViewHolder {
var view = LayoutInflater.from(p0.context).inflate(R.layout.item_comment,p0,false)
return CustomViewHolder(view)
}

class CustomViewHolder(view: View?) : RecyclerView.ViewHolder(view!!){
}
override fun getItemCount(): Int {
return messageDTOs.size
}

override fun onBindViewHolder(p0: RecyclerView.ViewHolder, p1: Int) {
if(messageDTOs[p1].myMessage!!){
p0.itemView.right_textView.visibility = View.VISIBLE
p0.itemView.right_textView.text = messageDTOs[p1].message
p0.itemView.left_textView.visibility = View.INVISIBLE

}else{
p0.itemView.right_textView.visibility = View.INVISIBLE
p0.itemView.left_textView.visibility = View.VISIBLE
p0.itemView.left_textView.text = messageDTOs[p1].message
}
}

}

MainActivity

package com.cjwplatform.lbot
import ai.api.AIConfiguration
import ai.api.AIDataService
import ai.api.model.AIRequest
import ai.api.model.Result
import android.os.AsyncTask
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.os.Message
import android.support.v7.widget.LinearLayoutManager
import android.text.TextUtils
import com.google.firebase.firestore.FirebaseFirestore
import com.google.gson.Gson
import kotlinx.android.synthetic.main.activity_main.*
import okhttp3.*
import java.io.IOException
import java.text.SimpleDateFormat
import java.time.DayOfWeek
import java.util.*

class MainActivity : AppCompatActivity() {
var messageDTOs = arrayListOf<MessageDTO>()
var aiDataService : AIDataService? = null // 인공지능 순서 1
var dateFormatFromString = SimpleDateFormat("yyyy-MM-dd")
var weatherDateFormatFromString = SimpleDateFormat("yyyy-MM-dd hh:mm:ss")
var weatherDateFormatToString = SimpleDateFormat("MM월 dd일 hh시")

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

recyclerview.adapter = MyRecyclerViewAdapter(messageDTOs)
recyclerview.layoutManager = LinearLayoutManager(this)

var config = AIConfiguration("b85db96e0b4449c19cdfd3e1c03e4b60",AIConfiguration.SupportedLanguages.Korean) // 인공지능 순서 2
aiDataService = AIDataService(config) // 인공지능 순서 3

button.setOnClickListener {
// 보내기 옆에 있는 텍스트뷰를 말함. 인공지능 순서 4
if(!TextUtils.isEmpty(editText.text)){
messageDTOs.add(MessageDTO(true,editText.text.toString()))
recyclerview.adapter!!.notifyDataSetChanged()
recyclerview.smoothScrollToPosition(messageDTOs.size - 1)
TalkAsyncTask().execute(editText.text.toString())
editText.setText("")
}
}
}
// 다이얼로그 플로우와 통신하는 스레드 작성 인공지능 순서 5
inner class TalkAsyncTask: AsyncTask<String, Void, Result>(){
override fun doInBackground(vararg p0: String?): Result {
var aiRequest = AIRequest()
aiRequest.setQuery(p0[0])
return aiDataService!!.request(aiRequest).result
}

override fun onPostExecute(result: Result?) {
if(result != null){
makeMessage(result) // AI의 답변을 외부의 함수로 만들어 onPostExcute 에 등록한다. 인공지능 순서 7
}
}
}
// 인간의 질문에 AI의 답변을 연결하는 함수 인공지능 순서 6
fun makeMessage(result:Result){
when(result.metadata.intentName)
{
// 여기서 Weather와 Schedule의 값은 Dialogflow 내에서 만든 Intent를 의미한다. 정확히는 Diagnostic 정보에서 intentName 을 의미함.
"Weather" ->{
var city = result.parameters["geo-city"]
if(city==null){
weatherMessage("서울특별시")
}else{
weatherMessage(city.asString)
}
}


"Schedule" -> {
var date = result.parameters["date"]?.asString
if(date == null){
var dayOfWeek = Calendar.getInstance().get(Calendar.DAY_OF_WEEK)
scheduleMessage(dayOfWeek)
}else{
var dateFromString = dateFormatFromString.parse(date)
var cal = Calendar.getInstance()
cal.time = dateFromString
var dayOfWeek = cal.get(Calendar.DAY_OF_WEEK)
scheduleMessage(dayOfWeek)
}
}
else -> {
var speech = result.fulfillment.speech
messageDTOs.add(MessageDTO(false,speech))
recyclerview.adapter!!.notifyDataSetChanged()
recyclerview.smoothScrollToPosition(messageDTOs.size - 1)
}
}
}
fun weatherMessage(city:String){
var weatherUrl = "https://api.openweathermap.org/data/2.5/forecast?id=524901&APPID=79416c191a22348f6b276d35e3d44074&q="+city+"&units=metric"
var request = Request.Builder().url(weatherUrl).build()
OkHttpClient().newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call?, e: IOException?) {

}

override fun onResponse(call: Call?, response: Response?) {
var result = response?.body()?.string() // 여기서 response?.body()?.string()은 JSON 전체 데이터임.
var weatherDTO = Gson().fromJson(result,WeatherDTO::class.java) // 모든 JSON 데이터들이 object로 만들어지게 된다.
for(item in weatherDTO.list!!){
var weatherItemUnixTime = weatherDateFormatFromString.parse(item.dt_txt).time // 초만 뽑아냄.
if(weatherItemUnixTime > System.currentTimeMillis()){ // 미래 인 경우 그러니까 그냥 넣는 조건문이라고 생각하면 됑!! 뒤의 break 문이 필요함.
// break가 없으면 현재 이후의 모든 시간을 출력하는데 뒤에 break를 해주면 현재 시간에서 가장 가까운 미래의 시간만 출려해주게 돼 !!!
// 온도
var temp = item.main?.temp
// 날씨 상태
var description = item.weather!![0].description
// 시간
var time = weatherDateFormatToString.format(weatherItemUnixTime)
// 습도
var humidity = item.main?.humidity
// 메시지
var message = time + "," + city + "의 기온은 " + temp + "도 하늘은 " + description + " 그리고 습도는 " + humidity + "% 입니다."
// 자식스레드는 부모스레드를 건들지 못한다. 그래서 핸들러가 필요한데 핸들러 대신에 이러한 것을 쓸 수 있음.
runOnUiThread {
messageDTOs.add(MessageDTO(false,message))
recyclerview.adapter!!.notifyDataSetChanged()
recyclerview.smoothScrollToPosition(messageDTOs.size -1)
}
break
}
}



}

})

}


fun scheduleMessage(dayOfWeek : Int?){
var dayOfWeekString : String? = null
when(dayOfWeek){
1 ->{
dayOfWeekString = "일요일"
}
2 -> {
dayOfWeekString = "월요일"
}
3 -> {
dayOfWeekString = "화요일"
}
4 -> {
dayOfWeekString = "수요일"
}
5 -> {
dayOfWeekString = "목요일"
}
6 -> {
dayOfWeekString = "금요일"
}
7 -> {
dayOfWeekString = "토요일"
}
}
FirebaseFirestore.getInstance().collection("schedules").whereEqualTo("dayofweek",dayOfWeekString).get().addOnCompleteListener {
task ->
if(task.isSuccessful){
for(document in task.result){
var message = dayOfWeekString + "의 수업은 " + document.data["lecture"] + "입니다. "
messageDTOs.add(MessageDTO(false,message))
recyclerview.adapter!!.notifyDataSetChanged()
recyclerview.smoothScrollToPosition(messageDTOs.size -1)
break
}
}
}

}
}