From b61030e64f5563fd8f72e0593a998330ee774016 Mon Sep 17 00:00:00 2001 From: kiranbollepalli9 Date: Fri, 22 Aug 2025 17:35:06 +0530 Subject: [PATCH 01/12] Refactor: - Introduced Constants and moved the constants - Created Message Factory to generate random emoji messages --- .../main/java/ai/elimu/chat/ChatActivity.kt | 220 ++++-------------- .../ai/elimu/chat/MessageListArrayAdapter.kt | 3 +- .../java/ai/elimu/chat/dao/MessageDao.java | 1 + .../ai/elimu/chat/model/MessageFactory.kt | 15 ++ .../chat/receiver/StudentUpdateReceiver.kt | 17 +- .../main/java/ai/elimu/chat/util/Constants.kt | 96 ++++++++ app/src/main/java/ai/elimu/chat/util/Utils.kt | 16 ++ 7 files changed, 188 insertions(+), 180 deletions(-) create mode 100644 app/src/main/java/ai/elimu/chat/model/MessageFactory.kt create mode 100644 app/src/main/java/ai/elimu/chat/util/Constants.kt create mode 100644 app/src/main/java/ai/elimu/chat/util/Utils.kt diff --git a/app/src/main/java/ai/elimu/chat/ChatActivity.kt b/app/src/main/java/ai/elimu/chat/ChatActivity.kt index 11e5b41..34e7d13 100644 --- a/app/src/main/java/ai/elimu/chat/ChatActivity.kt +++ b/app/src/main/java/ai/elimu/chat/ChatActivity.kt @@ -2,7 +2,8 @@ package ai.elimu.chat import ai.elimu.chat.dao.MessageDao import ai.elimu.chat.model.Message -import ai.elimu.chat.receiver.StudentUpdateReceiver +import ai.elimu.chat.model.MessageFactory +import ai.elimu.chat.util.Constants import ai.elimu.chat.util.DeviceInfoHelper import android.app.Activity import android.os.Bundle @@ -58,7 +59,7 @@ class ChatActivity : Activity() { messages = messageDao!!.queryBuilder() .where(MessageDao.Properties.TimeSent.gt(calendar24HoursAgo.getTimeInMillis())) .list() - Log.i(javaClass.getName(), "messages.size(): " + messages!!.size) + Log.i(javaClass.getName(), "messages.size(): " + messages.size) arrayAdapter = MessageListArrayAdapter(applicationContext, messages) mListPreviousMessages!!.setAdapter(arrayAdapter) @@ -109,71 +110,54 @@ class ChatActivity : Activity() { mButtonSend!!.setEnabled(false) mButtonSend!!.setImageDrawable(getDrawable(R.drawable.ic_send_grey_24dp)) - mButtonSend!!.setOnClickListener(object : View.OnClickListener { - override fun onClick(view: View?) { - Log.i(javaClass.getName(), "mButtonSend onClick") + mButtonSend!!.setOnClickListener { + Log.i(javaClass.getName(), "mButtonSend onClick") - val text = messageText!!.getText().toString() - Log.i(javaClass.getName(), "text: $text") + val text = messageText!!.getText().toString() + Log.i(javaClass.getName(), "text: $text") - val sharedPreferences = - PreferenceManager.getDefaultSharedPreferences(applicationContext) + val sharedPreferences = + PreferenceManager.getDefaultSharedPreferences(applicationContext) - // Store in database - val message = Message() - message.deviceId = DeviceInfoHelper.getDeviceId(applicationContext) - val studentId = - sharedPreferences.getString(StudentUpdateReceiver.PREF_STUDENT_ID, null) - if (!TextUtils.isEmpty(studentId)) { - message.studentId = studentId - } - val studentAvatar = - sharedPreferences.getString(StudentUpdateReceiver.PREF_STUDENT_AVATAR, null) - if (!TextUtils.isEmpty(studentAvatar)) { - message.studentAvatar = studentAvatar - } - message.timeSent = Calendar.getInstance() - message.text = text - messageDao!!.insert(message) - - // Add to UI - addToMessageListAndRefresh(message) - - // Reset input field - messageText!!.setText("") - - val showMessageFromAkili = Math.random() > 0.5 - if (showMessageFromAkili) { - mButtonSend!!.postDelayed(object : Runnable { - override fun run() { - // Simulate message from AI tutor - - // Akili - - val message = Message() - message.studentId = "00000000aaaaaaaa_1" - message.timeSent = Calendar.getInstance() - message.text = randomEmoji - addToMessageListAndRefresh(message) - } - }, (2000 + (Math.random() * 8000).toInt()).toLong()) - } + // Store in database + val message = Message() + message.deviceId = DeviceInfoHelper.getDeviceId(applicationContext) + val studentId = + sharedPreferences.getString(Constants.PREF_STUDENT_ID, null) + if (!TextUtils.isEmpty(studentId)) { + message.studentId = studentId + } + val studentAvatar = + sharedPreferences.getString(Constants.PREF_STUDENT_AVATAR, null) + if (!TextUtils.isEmpty(studentAvatar)) { + message.studentAvatar = studentAvatar + } + message.timeSent = Calendar.getInstance() + message.text = text + messageDao!!.insert(message) + + // Add to UI + addToMessageListAndRefresh(message) + + // Reset input field + messageText!!.setText("") + + val showMessageFromAkili = Math.random() > 0.5 + if (showMessageFromAkili) { + mButtonSend!!.postDelayed({ // Simulate message from AI tutor + val akiliMessage = MessageFactory.generateEmojiMessage("00000000aaaaaaaa_1") + addToMessageListAndRefresh(akiliMessage) + }, (2000 + (Math.random() * 8000).toInt()).toLong()) + } - val showMessageFromPenguin = Math.random() > 0.5 - if (showMessageFromPenguin) { - mButtonSend!!.postDelayed(object : Runnable { - override fun run() { - // Penguin - val messagePenguin = Message() - messagePenguin.studentId = "00000000aaaaaaaa_2" - messagePenguin.timeSent = Calendar.getInstance() - messagePenguin.text = randomEmoji - addToMessageListAndRefresh(messagePenguin) - } - }, (2000 + (Math.random() * 8000).toInt()).toLong()) - } + val showMessageFromPenguin = Math.random() > 0.5 + if (showMessageFromPenguin) { + mButtonSend!!.postDelayed({ // Penguin + val penguinMessage = MessageFactory.generateEmojiMessage("00000000aaaaaaaa_2") + addToMessageListAndRefresh(penguinMessage) + }, (2000 + (Math.random() * 8000).toInt()).toLong()) } - }) + } } private fun addToMessageListAndRefresh(message: Message) { @@ -188,115 +172,9 @@ class ChatActivity : Activity() { arrayAdapter!!.notifyDataSetChanged() mListPreviousMessages!!.smoothScrollToPosition(mListPreviousMessages!!.count) // Fix problem with scrolling when keyboard is present - mListPreviousMessages!!.postDelayed(object : Runnable { - override fun run() { - Log.i(javaClass.getName(), "mListPreviousMessages.postDelayed") - mListPreviousMessages!!.setSelection(mListPreviousMessages!!.count) - } + mListPreviousMessages!!.postDelayed({ + Log.i(javaClass.getName(), "mListPreviousMessages.postDelayed") + mListPreviousMessages!!.setSelection(mListPreviousMessages!!.count) }, 100) } - - private val randomEmoji: String - get() { - val unicodes = intArrayOf( - // Emoticons - 0x1F601, - 0x1F602, - 0x1F603, - 0x1F604, - 0x1F605, - 0x1F606, - 0x1F609, - 0x1F60A, - 0x1F60B, - 0x1F60C, - 0x1F60D, - 0x1F60F, - 0x1F612, - 0x1F613, - 0x1F614, - 0x1F616, - 0x1F618, - 0x1F61A, - 0x1F61C, - 0x1F61D, - 0x1F61E, - 0x1F620, - 0x1F621, - 0x1F622, - 0x1F623, - 0x1F624, - 0x1F625, - 0x1F628, - 0x1F629, - 0x1F62A, - 0x1F62B, - 0x1F62D, - 0x1F630, - 0x1F631, - 0x1F632, - 0x1F633, - 0x1F635, - 0x1F637, - 0x1F638, - 0x1F639, - 0x1F63A, - 0x1F63B, - 0x1F63C, - 0x1F63D, - 0x1F63E, - 0x1F63F, - 0x1F640, - 0x1F645, - 0x1F646, - 0x1F647, - 0x1F648, - 0x1F649, - 0x1F64A, - 0x1F64B, - 0x1F64C, - 0x1F64D, - 0x1F64E, - 0x1F64F, // Uncategorized - - 0x1F40C, - 0x1F40D, - 0x1F40E, - 0x1F411, - 0x1F412, - 0x1F414, - 0x1F418, - 0x1F419, - 0x1F41A, - 0x1F41B, - 0x1F41C, - 0x1F41D, - 0x1F41E, - 0x1F41F, - 0x1F420, - 0x1F421, - 0x1F422, - 0x1F423, - 0x1F424, - 0x1F425, - 0x1F426, - 0x1F427, - 0x1F428, - ) - val randomIndex = (Math.random() * unicodes.size).toInt() - val unicode = unicodes[randomIndex] - Log.d(javaClass.getName(), "unicode: $unicode") - val emoji = getEmijoByUnicode(unicode) - Log.i(javaClass.getName(), "emoji: $emoji") - return emoji - } - - /** - * See http://apps.timwhitlock.info/emoji/tables/unicode - * @param unicode Example: "U+1F601" --> "0x1F601" - * @return - */ - private fun getEmijoByUnicode(unicode: Int): String { - return String(Character.toChars(unicode)) - } } diff --git a/app/src/main/java/ai/elimu/chat/MessageListArrayAdapter.kt b/app/src/main/java/ai/elimu/chat/MessageListArrayAdapter.kt index 61b3676..556160d 100644 --- a/app/src/main/java/ai/elimu/chat/MessageListArrayAdapter.kt +++ b/app/src/main/java/ai/elimu/chat/MessageListArrayAdapter.kt @@ -2,6 +2,7 @@ package ai.elimu.chat import ai.elimu.chat.model.Message import ai.elimu.chat.receiver.StudentUpdateReceiver +import ai.elimu.chat.util.Constants import android.content.Context import android.graphics.BitmapFactory import android.preference.PreferenceManager @@ -35,7 +36,7 @@ class MessageListArrayAdapter(context: Context, messages: List) : this.messages = messages val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) - studentId = sharedPreferences.getString(StudentUpdateReceiver.PREF_STUDENT_ID, null) + studentId = sharedPreferences.getString(Constants.PREF_STUDENT_ID, null) } override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { diff --git a/app/src/main/java/ai/elimu/chat/dao/MessageDao.java b/app/src/main/java/ai/elimu/chat/dao/MessageDao.java index b98de70..490b123 100644 --- a/app/src/main/java/ai/elimu/chat/dao/MessageDao.java +++ b/app/src/main/java/ai/elimu/chat/dao/MessageDao.java @@ -10,6 +10,7 @@ import org.greenrobot.greendao.database.DatabaseStatement; import ai.elimu.chat.dao.converter.CalendarConverter; +import java.util.Calendar; import ai.elimu.chat.model.Message; diff --git a/app/src/main/java/ai/elimu/chat/model/MessageFactory.kt b/app/src/main/java/ai/elimu/chat/model/MessageFactory.kt new file mode 100644 index 0000000..cf93e04 --- /dev/null +++ b/app/src/main/java/ai/elimu/chat/model/MessageFactory.kt @@ -0,0 +1,15 @@ +package ai.elimu.chat.model + +import ai.elimu.chat.util.getRandomEmoji +import java.util.Calendar + +object MessageFactory { + + fun generateEmojiMessage(studentId: String): Message { + val message = Message() + message.studentId = studentId + message.timeSent = Calendar.getInstance() + message.text = getRandomEmoji() + return message + } +} \ No newline at end of file diff --git a/app/src/main/java/ai/elimu/chat/receiver/StudentUpdateReceiver.kt b/app/src/main/java/ai/elimu/chat/receiver/StudentUpdateReceiver.kt index 98303eb..c37f3fa 100644 --- a/app/src/main/java/ai/elimu/chat/receiver/StudentUpdateReceiver.kt +++ b/app/src/main/java/ai/elimu/chat/receiver/StudentUpdateReceiver.kt @@ -2,6 +2,7 @@ package ai.elimu.chat.receiver import ai.elimu.chat.ChatApplication import ai.elimu.chat.dao.MessageDao +import ai.elimu.chat.util.Constants import ai.elimu.chat.util.DeviceInfoHelper.getDeviceId import android.content.BroadcastReceiver import android.content.Context @@ -14,17 +15,16 @@ import androidx.core.content.edit class StudentUpdateReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { Log.i(javaClass.getName(), "onReceive") - - val studentId = intent.getStringExtra("studentId") + val studentId = intent.getStringExtra(EXTRA_STUDENT_ID) Log.i(javaClass.getName(), "studentId: $studentId") - val studentAvatar = intent.getStringExtra("studentAvatar") + val studentAvatar = intent.getStringExtra(EXTRA_STUDENT_AVATAR) Log.i(javaClass.getName(), "studentAvatar: $studentAvatar") val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) if (!TextUtils.isEmpty(studentId)) { - val existingStudentId = sharedPreferences.getString(PREF_STUDENT_ID, null) + val existingStudentId = sharedPreferences.getString(Constants.PREF_STUDENT_ID, null) Log.i(javaClass.getName(), "existingStudentId: $existingStudentId") if (TextUtils.isEmpty(existingStudentId)) { // Update previously sent messages on the current device @@ -44,16 +44,17 @@ class StudentUpdateReceiver : BroadcastReceiver() { } } - sharedPreferences.edit(commit = true) { putString(PREF_STUDENT_ID, studentId) } + sharedPreferences.edit(commit = true) { putString(Constants.PREF_STUDENT_ID, studentId) } } if (!TextUtils.isEmpty(studentAvatar)) { - sharedPreferences.edit(commit = true) { putString(PREF_STUDENT_AVATAR, studentAvatar) } + sharedPreferences.edit(commit = true) { putString(Constants.PREF_STUDENT_AVATAR, studentAvatar) } } } companion object { - const val PREF_STUDENT_ID: String = "pref_student_id" - const val PREF_STUDENT_AVATAR: String = "pref_student_avatar" + const val EXTRA_STUDENT_ID = "studentId" + + const val EXTRA_STUDENT_AVATAR = "studentAvatar" } } diff --git a/app/src/main/java/ai/elimu/chat/util/Constants.kt b/app/src/main/java/ai/elimu/chat/util/Constants.kt new file mode 100644 index 0000000..72b5460 --- /dev/null +++ b/app/src/main/java/ai/elimu/chat/util/Constants.kt @@ -0,0 +1,96 @@ +package ai.elimu.chat.util + +import android.util.Log + +object Constants { + + val PREF_STUDENT_ID: String = "pref_student_id" + + val PREF_STUDENT_AVATAR: String = "pref_student_avatar" + + val EMOJI_UNICODES = intArrayOf( + // Emoticons + 0x1F601, + 0x1F602, + 0x1F603, + 0x1F604, + 0x1F605, + 0x1F606, + 0x1F609, + 0x1F60A, + 0x1F60B, + 0x1F60C, + 0x1F60D, + 0x1F60F, + 0x1F612, + 0x1F613, + 0x1F614, + 0x1F616, + 0x1F618, + 0x1F61A, + 0x1F61C, + 0x1F61D, + 0x1F61E, + 0x1F620, + 0x1F621, + 0x1F622, + 0x1F623, + 0x1F624, + 0x1F625, + 0x1F628, + 0x1F629, + 0x1F62A, + 0x1F62B, + 0x1F62D, + 0x1F630, + 0x1F631, + 0x1F632, + 0x1F633, + 0x1F635, + 0x1F637, + 0x1F638, + 0x1F639, + 0x1F63A, + 0x1F63B, + 0x1F63C, + 0x1F63D, + 0x1F63E, + 0x1F63F, + 0x1F640, + 0x1F645, + 0x1F646, + 0x1F647, + 0x1F648, + 0x1F649, + 0x1F64A, + 0x1F64B, + 0x1F64C, + 0x1F64D, + 0x1F64E, + 0x1F64F, // Uncategorized + + 0x1F40C, + 0x1F40D, + 0x1F40E, + 0x1F411, + 0x1F412, + 0x1F414, + 0x1F418, + 0x1F419, + 0x1F41A, + 0x1F41B, + 0x1F41C, + 0x1F41D, + 0x1F41E, + 0x1F41F, + 0x1F420, + 0x1F421, + 0x1F422, + 0x1F423, + 0x1F424, + 0x1F425, + 0x1F426, + 0x1F427, + 0x1F428, + ) +} \ No newline at end of file diff --git a/app/src/main/java/ai/elimu/chat/util/Utils.kt b/app/src/main/java/ai/elimu/chat/util/Utils.kt new file mode 100644 index 0000000..190dd6b --- /dev/null +++ b/app/src/main/java/ai/elimu/chat/util/Utils.kt @@ -0,0 +1,16 @@ +package ai.elimu.chat.util + +import ai.elimu.chat.util.Constants.EMOJI_UNICODES + +fun getRandomEmoji(): String { + val randomIndex = (Math.random() * EMOJI_UNICODES.size).toInt() + val unicode = EMOJI_UNICODES[randomIndex] + + /** + * See http://apps.timwhitlock.info/emoji/tables/unicode + * @param unicode Example: "U+1F601" --> "0x1F601" + * @return + */ + val emoji = String(Character.toChars(unicode)) + return emoji +} \ No newline at end of file From 0882304e187681c47bff352b6abd2ed2c98b8a11 Mon Sep 17 00:00:00 2001 From: kiranbollepalli9 Date: Mon, 25 Aug 2025 21:25:04 +0530 Subject: [PATCH 02/12] Refactor: - Migrating from GreemDAO to Room with DAO. Its in progress. --- app/build.gradle | 12 ++++++ .../main/java/ai/elimu/chat/ChatActivity.kt | 40 +++++++++++++++---- .../java/ai/elimu/chat/ChatApplication.kt | 13 +++--- .../ai/elimu/chat/data/local/AppDatabase.kt | 9 +++++ .../elimu/chat/data/local/ChatMessageDao.kt | 30 ++++++++++++++ .../ai/elimu/chat/data/local/MessageEntity.kt | 16 ++++++++ .../ai/elimu/chat/data/local/MessageMapper.kt | 25 ++++++++++++ .../java/ai/elimu/chat/di/DatabaseModule.kt | 26 ++++++++++++ .../main/java/ai/elimu/chat/util/Constants.kt | 2 + gradle/libs.versions.toml | 14 ++++++- 10 files changed, 172 insertions(+), 15 deletions(-) create mode 100644 app/src/main/java/ai/elimu/chat/data/local/AppDatabase.kt create mode 100644 app/src/main/java/ai/elimu/chat/data/local/ChatMessageDao.kt create mode 100644 app/src/main/java/ai/elimu/chat/data/local/MessageEntity.kt create mode 100644 app/src/main/java/ai/elimu/chat/data/local/MessageMapper.kt create mode 100644 app/src/main/java/ai/elimu/chat/di/DatabaseModule.kt diff --git a/app/build.gradle b/app/build.gradle index 9ab8822..d78da7f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -9,6 +9,8 @@ buildscript { plugins { alias(libs.plugins.kotlin.android) + alias(libs.plugins.kotlin.kapt) + alias(libs.plugins.dagger.hilt.android) } import java.util.regex.Matcher import java.util.regex.Pattern @@ -69,10 +71,20 @@ dependencies { implementation libs.circle.imageview implementation libs.androidx.core.ktx + implementation libs.room.runtime + implementation libs.room.ktx + kapt libs.room.compiler + + implementation libs.hilt.android + kapt libs.hilt.android.compiler + + implementation 'com.squareup:javapoet:1.13.0' + testImplementation libs.junit androidTestImplementation libs.androidx.junit androidTestImplementation libs.androidx.espresso + } publishing { diff --git a/app/src/main/java/ai/elimu/chat/ChatActivity.kt b/app/src/main/java/ai/elimu/chat/ChatActivity.kt index 34e7d13..0494fbc 100644 --- a/app/src/main/java/ai/elimu/chat/ChatActivity.kt +++ b/app/src/main/java/ai/elimu/chat/ChatActivity.kt @@ -1,11 +1,13 @@ package ai.elimu.chat import ai.elimu.chat.dao.MessageDao +import ai.elimu.chat.data.local.ChatMessageDao +import ai.elimu.chat.data.local.toEntity +import ai.elimu.chat.data.local.toMessage import ai.elimu.chat.model.Message import ai.elimu.chat.model.MessageFactory import ai.elimu.chat.util.Constants import ai.elimu.chat.util.DeviceInfoHelper -import android.app.Activity import android.os.Bundle import android.preference.PreferenceManager import android.text.Editable @@ -17,10 +19,20 @@ import android.widget.ArrayAdapter import android.widget.EditText import android.widget.ImageButton import android.widget.ListView +import androidx.activity.ComponentActivity +import androidx.lifecycle.lifecycleScope +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.launch import java.util.Calendar +import javax.inject.Inject -class ChatActivity : Activity() { - private var messageDao: MessageDao? = null +@AndroidEntryPoint +class ChatActivity : ComponentActivity() { + + @Inject + lateinit var chatMessageDao: ChatMessageDao + + // private var messageDao: MessageDao? = null private var messages: MutableList = mutableListOf() @@ -38,7 +50,7 @@ class ChatActivity : Activity() { setContentView(R.layout.activity_chat) - messageDao = (application as ChatApplication).daoSession!!.messageDao + // messageDao = (application as ChatApplication).daoSession!!.messageDao mListPreviousMessages = findViewById(R.id.listPreviousMessages) as ListView messageText = findViewById(R.id.message) as EditText @@ -54,16 +66,26 @@ class ChatActivity : Activity() { // Log.i(getClass().getName(), "letters: " + letters); // Load messages sent within the last 24 hours - val calendar24HoursAgo = Calendar.getInstance() +/* val calendar24HoursAgo = Calendar.getInstance() calendar24HoursAgo.add(Calendar.HOUR_OF_DAY, -24) messages = messageDao!!.queryBuilder() .where(MessageDao.Properties.TimeSent.gt(calendar24HoursAgo.getTimeInMillis())) .list() - Log.i(javaClass.getName(), "messages.size(): " + messages.size) + Log.i(javaClass.getName(), "messages.size(): " + messages.size)*/ arrayAdapter = MessageListArrayAdapter(applicationContext, messages) mListPreviousMessages!!.setAdapter(arrayAdapter) + lifecycleScope.launch { + val calendar24HoursAgo = Calendar.getInstance() + calendar24HoursAgo.add(Calendar.HOUR_OF_DAY, -24) + val newMessages = + chatMessageDao.getMessages(timeStamp = calendar24HoursAgo.timeInMillis) + val uiMessages = newMessages.map { it.toMessage() } + messages.addAll(uiMessages) + refreshMessageList() + } + messageText!!.addTextChangedListener(object : TextWatcher { override fun beforeTextChanged(charSequence: CharSequence?, i: Int, i1: Int, i2: Int) { } @@ -134,7 +156,11 @@ class ChatActivity : Activity() { } message.timeSent = Calendar.getInstance() message.text = text - messageDao!!.insert(message) + + lifecycleScope.launch { + chatMessageDao.insert(message.toEntity()) + } + // messageDao!!.insert(message) // Add to UI addToMessageListAndRefresh(message) diff --git a/app/src/main/java/ai/elimu/chat/ChatApplication.kt b/app/src/main/java/ai/elimu/chat/ChatApplication.kt index cd6a786..221a603 100644 --- a/app/src/main/java/ai/elimu/chat/ChatApplication.kt +++ b/app/src/main/java/ai/elimu/chat/ChatApplication.kt @@ -3,12 +3,15 @@ package ai.elimu.chat import ai.elimu.chat.dao.DaoMaster import ai.elimu.chat.dao.DaoMaster.DevOpenHelper import ai.elimu.chat.dao.DaoSession +import ai.elimu.chat.util.Constants import ai.elimu.chat.util.VersionHelper import android.app.Application import android.preference.PreferenceManager import android.util.Log import androidx.core.content.edit +import dagger.hilt.android.HiltAndroidApp +@HiltAndroidApp class ChatApplication : Application() { var daoSession: DaoSession? = null private set @@ -24,10 +27,10 @@ class ChatApplication : Application() { // Check if the application's versionCode was upgraded val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(applicationContext) - var oldVersionCode = sharedPreferences.getInt(PREF_APP_VERSION_CODE, 0) + var oldVersionCode = sharedPreferences.getInt(Constants.PREF_APP_VERSION_CODE, 0) val newVersionCode = VersionHelper.getAppVersionCode(applicationContext) if (oldVersionCode == 0) { - sharedPreferences.edit(commit = true) { putInt(PREF_APP_VERSION_CODE, newVersionCode) } + sharedPreferences.edit(commit = true) { putInt(Constants.PREF_APP_VERSION_CODE, newVersionCode) } oldVersionCode = newVersionCode } if (oldVersionCode < newVersionCode) { @@ -38,11 +41,7 @@ class ChatApplication : Application() { // if (newVersionCode == ???) { // // Put relevant tasks required for upgrading here // } - sharedPreferences.edit(commit = true) { putInt(PREF_APP_VERSION_CODE, newVersionCode) } + sharedPreferences.edit(commit = true) { putInt(Constants.PREF_APP_VERSION_CODE, newVersionCode) } } } - - companion object { - const val PREF_APP_VERSION_CODE: String = "pref_app_version_code" - } } diff --git a/app/src/main/java/ai/elimu/chat/data/local/AppDatabase.kt b/app/src/main/java/ai/elimu/chat/data/local/AppDatabase.kt new file mode 100644 index 0000000..23a52a9 --- /dev/null +++ b/app/src/main/java/ai/elimu/chat/data/local/AppDatabase.kt @@ -0,0 +1,9 @@ +package ai.elimu.chat.data.local + +import androidx.room.Database +import androidx.room.RoomDatabase + +@Database(entities = [MessageEntity::class], version = 1) +abstract class AppDatabase : RoomDatabase() { + abstract fun messageDao(): ChatMessageDao +} \ No newline at end of file diff --git a/app/src/main/java/ai/elimu/chat/data/local/ChatMessageDao.kt b/app/src/main/java/ai/elimu/chat/data/local/ChatMessageDao.kt new file mode 100644 index 0000000..83d1d27 --- /dev/null +++ b/app/src/main/java/ai/elimu/chat/data/local/ChatMessageDao.kt @@ -0,0 +1,30 @@ +package ai.elimu.chat.data.local + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.Query + +@Dao +interface ChatMessageDao { + @Insert + suspend fun insert(message: MessageEntity): Long + + @Query( + "SELECT * FROM messages WHERE device_id LIKE :deviceId AND" + + " student_id LIKE :studentId" + ) + suspend fun getAllMessages(deviceId: String, studentId: String): List + + @Query("SELECT * FROM messages WHERE timestamp > :timeStamp") + suspend fun getMessages(timeStamp: Long): List + + @Query( + "UPDATE messages SET student_id = :studentId, " + + "student_avatar = :studentAvatar WHERE device_id = :deviceId AND student_id IS NULL" + ) + suspend fun updateStudentInfoForDevice( + deviceId: String, + studentId: String?, + studentAvatar: String? + ) +} \ No newline at end of file diff --git a/app/src/main/java/ai/elimu/chat/data/local/MessageEntity.kt b/app/src/main/java/ai/elimu/chat/data/local/MessageEntity.kt new file mode 100644 index 0000000..f1f6cb5 --- /dev/null +++ b/app/src/main/java/ai/elimu/chat/data/local/MessageEntity.kt @@ -0,0 +1,16 @@ +package ai.elimu.chat.data.local + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "messages") + +data class MessageEntity( + @PrimaryKey(autoGenerate = true) val id: Long = 0, + @ColumnInfo(name = "device_id") val deviceId: String, + @ColumnInfo(name = "student_id") val studentId: String, + @ColumnInfo(name = "student_avatar") val studentAvatar: String, + @ColumnInfo(name = "message") val text: String, + @ColumnInfo(name = "timestamp") val timeSent: Long +) diff --git a/app/src/main/java/ai/elimu/chat/data/local/MessageMapper.kt b/app/src/main/java/ai/elimu/chat/data/local/MessageMapper.kt new file mode 100644 index 0000000..a643b4b --- /dev/null +++ b/app/src/main/java/ai/elimu/chat/data/local/MessageMapper.kt @@ -0,0 +1,25 @@ +package ai.elimu.chat.data.local + +import ai.elimu.chat.model.Message +import java.util.Calendar + +fun MessageEntity.toMessage(): Message { + return Message( + this.id, + this.deviceId, + this.studentId, + this.studentAvatar, + Calendar.getInstance().apply { timeInMillis = this@toMessage.timeSent }, + this.text + ) +} + +fun Message.toEntity(): MessageEntity { + return MessageEntity( + deviceId = this.deviceId, + studentId = this.studentId, + studentAvatar = this.studentAvatar, + timeSent = this.timeSent.timeInMillis, + text = this.text + ) +} \ No newline at end of file diff --git a/app/src/main/java/ai/elimu/chat/di/DatabaseModule.kt b/app/src/main/java/ai/elimu/chat/di/DatabaseModule.kt new file mode 100644 index 0000000..96ebcb9 --- /dev/null +++ b/app/src/main/java/ai/elimu/chat/di/DatabaseModule.kt @@ -0,0 +1,26 @@ +package ai.elimu.chat.di + +import ai.elimu.chat.data.local.AppDatabase +import ai.elimu.chat.data.local.ChatMessageDao +import android.content.Context +import androidx.room.Room +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +object DatabaseModule { + + @Provides + @Singleton + fun provideDatabase(@ApplicationContext appContext: Context): AppDatabase = + Room.databaseBuilder(appContext, AppDatabase::class.java, "chat-db").build() + + @Provides + fun provideMessageDao(db: AppDatabase): ChatMessageDao = db.messageDao() + +} \ No newline at end of file diff --git a/app/src/main/java/ai/elimu/chat/util/Constants.kt b/app/src/main/java/ai/elimu/chat/util/Constants.kt index 72b5460..0459c64 100644 --- a/app/src/main/java/ai/elimu/chat/util/Constants.kt +++ b/app/src/main/java/ai/elimu/chat/util/Constants.kt @@ -4,6 +4,8 @@ import android.util.Log object Constants { + val PREF_APP_VERSION_CODE: String = "pref_app_version_code" + val PREF_STUDENT_ID: String = "pref_student_id" val PREF_STUDENT_AVATAR: String = "pref_student_avatar" diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a9f08ce..66eba1c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -10,6 +10,8 @@ androidXJunit = "1.2.1" androidXEspresso = "3.7.0" coreKtx = "1.16.0" kotlin = "2.2.0" +room = "2.7.2" +hilt = "2.57.1" [libraries] elimu-content-provider = { group = "ai.elimu", name = "content-provider", version.ref = "elimuContentProvider" } @@ -20,8 +22,18 @@ agp = { module = "com.android.tools.build:gradle", version.ref = "agp" } androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidXJunit" } androidx-espresso = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "androidXEspresso" } +room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" } +room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" } +room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" } + +hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" } +hilt-android-compiler = { group = "com.google.dagger", name = "hilt-android-compiler", version.ref = "hilt" } + + junit = { group = "junit", name = "junit", version.ref = "junit" } androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } [plugins] -kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } \ No newline at end of file +kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } +kotlin-kapt = { id = "org.jetbrains.kotlin.kapt", version.ref = "kotlin" } +dagger-hilt-android = { id = "com.google.dagger.hilt.android", version.ref = "hilt" } \ No newline at end of file From b150dfd232c5974bb70bea76a666cb2abd9663bc Mon Sep 17 00:00:00 2001 From: kiranbollepalli9 Date: Thu, 4 Sep 2025 18:06:11 +0530 Subject: [PATCH 03/12] Refactor: - Reverting HILT dependency to avoid build issue. --- .github/.config | 2 + app/build.gradle | 9 ++-- .../main/java/ai/elimu/chat/ChatActivity.kt | 8 ++-- .../java/ai/elimu/chat/ChatApplication.kt | 3 +- .../java/ai/elimu/chat/di/DatabaseModule.kt | 48 +++++++++---------- gradle/libs.versions.toml | 12 +++-- 6 files changed, 44 insertions(+), 38 deletions(-) create mode 100644 .github/.config diff --git a/.github/.config b/.github/.config new file mode 100644 index 0000000..81d58d3 --- /dev/null +++ b/.github/.config @@ -0,0 +1,2 @@ +git config user.name "Kiran" +git config user.email "kiranbollepalli@gmail.com" \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index d78da7f..22b2f78 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -10,7 +10,7 @@ buildscript { plugins { alias(libs.plugins.kotlin.android) alias(libs.plugins.kotlin.kapt) - alias(libs.plugins.dagger.hilt.android) + //alias(libs.plugins.dagger.hilt.android) } import java.util.regex.Matcher import java.util.regex.Pattern @@ -70,15 +70,16 @@ dependencies { implementation libs.green.dao implementation libs.circle.imageview implementation libs.androidx.core.ktx + implementation libs.androidx.activity.ktx implementation libs.room.runtime implementation libs.room.ktx kapt libs.room.compiler - implementation libs.hilt.android - kapt libs.hilt.android.compiler + //implementation libs.hilt.android + // kapt libs.hilt.android.compiler - implementation 'com.squareup:javapoet:1.13.0' + implementation libs.javapoet testImplementation libs.junit diff --git a/app/src/main/java/ai/elimu/chat/ChatActivity.kt b/app/src/main/java/ai/elimu/chat/ChatActivity.kt index 0494fbc..03e6d5b 100644 --- a/app/src/main/java/ai/elimu/chat/ChatActivity.kt +++ b/app/src/main/java/ai/elimu/chat/ChatActivity.kt @@ -19,17 +19,15 @@ import android.widget.ArrayAdapter import android.widget.EditText import android.widget.ImageButton import android.widget.ListView -import androidx.activity.ComponentActivity +import androidx.core.app.ComponentActivity import androidx.lifecycle.lifecycleScope -import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch import java.util.Calendar -import javax.inject.Inject -@AndroidEntryPoint +//@AndroidEntryPoint class ChatActivity : ComponentActivity() { - @Inject + // @Inject lateinit var chatMessageDao: ChatMessageDao // private var messageDao: MessageDao? = null diff --git a/app/src/main/java/ai/elimu/chat/ChatApplication.kt b/app/src/main/java/ai/elimu/chat/ChatApplication.kt index 221a603..28825af 100644 --- a/app/src/main/java/ai/elimu/chat/ChatApplication.kt +++ b/app/src/main/java/ai/elimu/chat/ChatApplication.kt @@ -9,9 +9,8 @@ import android.app.Application import android.preference.PreferenceManager import android.util.Log import androidx.core.content.edit -import dagger.hilt.android.HiltAndroidApp -@HiltAndroidApp +//@HiltAndroidApp class ChatApplication : Application() { var daoSession: DaoSession? = null private set diff --git a/app/src/main/java/ai/elimu/chat/di/DatabaseModule.kt b/app/src/main/java/ai/elimu/chat/di/DatabaseModule.kt index 96ebcb9..d0ec57d 100644 --- a/app/src/main/java/ai/elimu/chat/di/DatabaseModule.kt +++ b/app/src/main/java/ai/elimu/chat/di/DatabaseModule.kt @@ -1,26 +1,26 @@ package ai.elimu.chat.di -import ai.elimu.chat.data.local.AppDatabase -import ai.elimu.chat.data.local.ChatMessageDao -import android.content.Context -import androidx.room.Room -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.android.qualifiers.ApplicationContext -import dagger.hilt.components.SingletonComponent -import javax.inject.Singleton - -@Module -@InstallIn(SingletonComponent::class) -object DatabaseModule { - - @Provides - @Singleton - fun provideDatabase(@ApplicationContext appContext: Context): AppDatabase = - Room.databaseBuilder(appContext, AppDatabase::class.java, "chat-db").build() - - @Provides - fun provideMessageDao(db: AppDatabase): ChatMessageDao = db.messageDao() - -} \ No newline at end of file +//import ai.elimu.chat.data.local.AppDatabase +//import ai.elimu.chat.data.local.ChatMessageDao +//import android.content.Context +//import androidx.room.Room +//import dagger.Module +//import dagger.Provides +//import dagger.hilt.InstallIn +//import dagger.hilt.android.qualifiers.ApplicationContext +//import dagger.hilt.components.SingletonComponent +//import javax.inject.Singleton +// +//@Module +//@InstallIn(SingletonComponent::class) +//object DatabaseModule { +// +// @Provides +// @Singleton +// fun provideDatabase(@ApplicationContext appContext: Context): AppDatabase = +// Room.databaseBuilder(appContext, AppDatabase::class.java, "chat-db").build() +// +// @Provides +// fun provideMessageDao(db: AppDatabase): ChatMessageDao = db.messageDao() +// +//} \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 66eba1c..6ce9586 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,7 @@ [versions] elimuContentProvider = "1.4.50" agp = "8.6.0" +javapoet = "1.13.0" junit = "4.13.2" greenDao = "3.3.0" greenDaoGradlePlugin = "3.3.1" @@ -9,6 +10,7 @@ circleImageView = "3.1.0" androidXJunit = "1.2.1" androidXEspresso = "3.7.0" coreKtx = "1.16.0" +activityKtx="1.10.1" kotlin = "2.2.0" room = "2.7.2" hilt = "2.57.1" @@ -22,18 +24,22 @@ agp = { module = "com.android.tools.build:gradle", version.ref = "agp" } androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidXJunit" } androidx-espresso = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "androidXEspresso" } +javapoet = { module = "com.squareup:javapoet", version.ref = "javapoet" } room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" } room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" } room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" } -hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" } -hilt-android-compiler = { group = "com.google.dagger", name = "hilt-android-compiler", version.ref = "hilt" } +#hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" } +#hilt-android-compiler = { group = "com.google.dagger", name = "hilt-android-compiler", version.ref = "hilt" } junit = { group = "junit", name = "junit", version.ref = "junit" } androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } +androidx-activity-ktx = { group = "androidx.activity", name = "activity-ktx", version.ref = "activityKtx" } + + [plugins] kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } kotlin-kapt = { id = "org.jetbrains.kotlin.kapt", version.ref = "kotlin" } -dagger-hilt-android = { id = "com.google.dagger.hilt.android", version.ref = "hilt" } \ No newline at end of file +#dagger-hilt-android = { id = "com.google.dagger.hilt.android", version.ref = "hilt" } \ No newline at end of file From 6db552957ab45cac42ec0622c17852592ea64bb2 Mon Sep 17 00:00:00 2001 From: kiranbollepalli9 Date: Thu, 4 Sep 2025 18:44:08 +0530 Subject: [PATCH 04/12] Refactor: - Fixing crashes. --- .../main/java/ai/elimu/chat/ChatActivity.kt | 5 +++- .../java/ai/elimu/chat/ChatApplication.kt | 2 ++ .../ai/elimu/chat/data/local/AppDatabase.kt | 5 +++- .../ai/elimu/chat/data/local/MessageEntity.kt | 4 ++-- .../java/ai/elimu/chat/di/ServiceLocator.kt | 24 +++++++++++++++++++ 5 files changed, 36 insertions(+), 4 deletions(-) create mode 100644 app/src/main/java/ai/elimu/chat/di/ServiceLocator.kt diff --git a/app/src/main/java/ai/elimu/chat/ChatActivity.kt b/app/src/main/java/ai/elimu/chat/ChatActivity.kt index 03e6d5b..2bae3c1 100644 --- a/app/src/main/java/ai/elimu/chat/ChatActivity.kt +++ b/app/src/main/java/ai/elimu/chat/ChatActivity.kt @@ -4,6 +4,7 @@ import ai.elimu.chat.dao.MessageDao import ai.elimu.chat.data.local.ChatMessageDao import ai.elimu.chat.data.local.toEntity import ai.elimu.chat.data.local.toMessage +import ai.elimu.chat.di.ServiceLocator import ai.elimu.chat.model.Message import ai.elimu.chat.model.MessageFactory import ai.elimu.chat.util.Constants @@ -47,8 +48,10 @@ class ChatActivity : ComponentActivity() { super.onCreate(savedInstanceState) setContentView(R.layout.activity_chat) + chatMessageDao = ServiceLocator.provideChatMessageDao() - // messageDao = (application as ChatApplication).daoSession!!.messageDao + + // messageDao = (application as ChatApplication).daoSession!!.messageDao mListPreviousMessages = findViewById(R.id.listPreviousMessages) as ListView messageText = findViewById(R.id.message) as EditText diff --git a/app/src/main/java/ai/elimu/chat/ChatApplication.kt b/app/src/main/java/ai/elimu/chat/ChatApplication.kt index 28825af..dd122b3 100644 --- a/app/src/main/java/ai/elimu/chat/ChatApplication.kt +++ b/app/src/main/java/ai/elimu/chat/ChatApplication.kt @@ -3,6 +3,7 @@ package ai.elimu.chat import ai.elimu.chat.dao.DaoMaster import ai.elimu.chat.dao.DaoMaster.DevOpenHelper import ai.elimu.chat.dao.DaoSession +import ai.elimu.chat.di.ServiceLocator import ai.elimu.chat.util.Constants import ai.elimu.chat.util.VersionHelper import android.app.Application @@ -18,6 +19,7 @@ class ChatApplication : Application() { override fun onCreate() { Log.i(javaClass.getName(), "onCreate") super.onCreate() + ServiceLocator.initialize(this) val helper = DevOpenHelper(this, "chat-db") val db = helper.writableDb diff --git a/app/src/main/java/ai/elimu/chat/data/local/AppDatabase.kt b/app/src/main/java/ai/elimu/chat/data/local/AppDatabase.kt index 23a52a9..072f04e 100644 --- a/app/src/main/java/ai/elimu/chat/data/local/AppDatabase.kt +++ b/app/src/main/java/ai/elimu/chat/data/local/AppDatabase.kt @@ -3,7 +3,10 @@ package ai.elimu.chat.data.local import androidx.room.Database import androidx.room.RoomDatabase -@Database(entities = [MessageEntity::class], version = 1) +@Database( + entities = [MessageEntity::class], + version = 1 +) abstract class AppDatabase : RoomDatabase() { abstract fun messageDao(): ChatMessageDao } \ No newline at end of file diff --git a/app/src/main/java/ai/elimu/chat/data/local/MessageEntity.kt b/app/src/main/java/ai/elimu/chat/data/local/MessageEntity.kt index f1f6cb5..e6666aa 100644 --- a/app/src/main/java/ai/elimu/chat/data/local/MessageEntity.kt +++ b/app/src/main/java/ai/elimu/chat/data/local/MessageEntity.kt @@ -9,8 +9,8 @@ import androidx.room.PrimaryKey data class MessageEntity( @PrimaryKey(autoGenerate = true) val id: Long = 0, @ColumnInfo(name = "device_id") val deviceId: String, - @ColumnInfo(name = "student_id") val studentId: String, - @ColumnInfo(name = "student_avatar") val studentAvatar: String, + @ColumnInfo(name = "student_id") val studentId: String?, + @ColumnInfo(name = "student_avatar") val studentAvatar: String?, @ColumnInfo(name = "message") val text: String, @ColumnInfo(name = "timestamp") val timeSent: Long ) diff --git a/app/src/main/java/ai/elimu/chat/di/ServiceLocator.kt b/app/src/main/java/ai/elimu/chat/di/ServiceLocator.kt new file mode 100644 index 0000000..23eec49 --- /dev/null +++ b/app/src/main/java/ai/elimu/chat/di/ServiceLocator.kt @@ -0,0 +1,24 @@ +package ai.elimu.chat.di + +import ai.elimu.chat.data.local.AppDatabase +import ai.elimu.chat.data.local.ChatMessageDao +import android.content.Context +import androidx.room.Room + +object ServiceLocator { + + private var chatMessageDao: ChatMessageDao? = null + + fun initialize(context: Context) { + val db = Room.databaseBuilder( + context.applicationContext, + AppDatabase::class.java, + "chat-db-new" + ).build() + chatMessageDao = db.messageDao() + } + + fun provideChatMessageDao(): ChatMessageDao { + return chatMessageDao ?: throw IllegalStateException("ServiceLocator not initialized") + } +} \ No newline at end of file From 5dec4d3e89a4d226231f14e30ce0d6755044a10d Mon Sep 17 00:00:00 2001 From: kiranbollepalli9 Date: Thu, 4 Sep 2025 19:12:26 +0530 Subject: [PATCH 05/12] Rename .java to .kt --- .../main/java/ai/elimu/chat/model/{Message.java => Message.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/src/main/java/ai/elimu/chat/model/{Message.java => Message.kt} (100%) diff --git a/app/src/main/java/ai/elimu/chat/model/Message.java b/app/src/main/java/ai/elimu/chat/model/Message.kt similarity index 100% rename from app/src/main/java/ai/elimu/chat/model/Message.java rename to app/src/main/java/ai/elimu/chat/model/Message.kt From d701ccd9fd4d64e063e427c7339d9e553da3719b Mon Sep 17 00:00:00 2001 From: kiranbollepalli9 Date: Thu, 4 Sep 2025 19:12:26 +0530 Subject: [PATCH 06/12] Refactor: - Removing GreenDAO dependency and related code. Now code migration to Room is completed --- app/build.gradle | 11 -- .../main/java/ai/elimu/chat/ChatActivity.kt | 28 +-- .../java/ai/elimu/chat/ChatApplication.kt | 11 +- .../ai/elimu/chat/MessageListArrayAdapter.kt | 1 - .../java/ai/elimu/chat/dao/DaoSession.java | 48 ----- .../java/ai/elimu/chat/dao/MessageDao.java | 165 ------------------ .../chat/dao/converter/CalendarConverter.kt | 20 --- .../java/ai/elimu/chat/di/ServiceLocator.kt | 12 ++ .../main/java/ai/elimu/chat/model/Message.kt | 127 ++++---------- .../ai/elimu/chat/model/MessageFactory.kt | 15 -- .../chat/receiver/StudentUpdateReceiver.kt | 6 +- .../ai/elimu/chat/util/DeviceInfoHelper.kt | 11 -- gradle/libs.versions.toml | 4 - 13 files changed, 64 insertions(+), 395 deletions(-) delete mode 100644 app/src/main/java/ai/elimu/chat/dao/DaoSession.java delete mode 100644 app/src/main/java/ai/elimu/chat/dao/MessageDao.java delete mode 100644 app/src/main/java/ai/elimu/chat/dao/converter/CalendarConverter.kt delete mode 100644 app/src/main/java/ai/elimu/chat/model/MessageFactory.kt delete mode 100644 app/src/main/java/ai/elimu/chat/util/DeviceInfoHelper.kt diff --git a/app/build.gradle b/app/build.gradle index 22b2f78..0e1af71 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -2,9 +2,6 @@ buildscript { repositories { mavenCentral() } - dependencies { - classpath libs.green.dao.gradle.plugin - } } plugins { @@ -16,7 +13,6 @@ import java.util.regex.Matcher import java.util.regex.Pattern apply plugin: 'com.android.application' -apply plugin: 'org.greenrobot.greendao' apply plugin: 'maven-publish' android { @@ -56,18 +52,11 @@ android { } } -greendao { - schemaVersion android.defaultConfig.versionCode - targetGenDir = new File('app/src/main/java') - daoPackage = android.defaultConfig.applicationId + '.dao' -} - dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation libs.elimu.content.provider // https://jitpack.io/#ai.elimu/content-provider - implementation libs.green.dao implementation libs.circle.imageview implementation libs.androidx.core.ktx implementation libs.androidx.activity.ktx diff --git a/app/src/main/java/ai/elimu/chat/ChatActivity.kt b/app/src/main/java/ai/elimu/chat/ChatActivity.kt index 2bae3c1..d1adbcd 100644 --- a/app/src/main/java/ai/elimu/chat/ChatActivity.kt +++ b/app/src/main/java/ai/elimu/chat/ChatActivity.kt @@ -1,14 +1,13 @@ package ai.elimu.chat -import ai.elimu.chat.dao.MessageDao import ai.elimu.chat.data.local.ChatMessageDao import ai.elimu.chat.data.local.toEntity import ai.elimu.chat.data.local.toMessage import ai.elimu.chat.di.ServiceLocator import ai.elimu.chat.model.Message -import ai.elimu.chat.model.MessageFactory +import ai.elimu.chat.model.MessageBuilder +import ai.elimu.chat.model.generateEmojiMessage import ai.elimu.chat.util.Constants -import ai.elimu.chat.util.DeviceInfoHelper import android.os.Bundle import android.preference.PreferenceManager import android.text.Editable @@ -143,20 +142,23 @@ class ChatActivity : ComponentActivity() { PreferenceManager.getDefaultSharedPreferences(applicationContext) // Store in database - val message = Message() - message.deviceId = DeviceInfoHelper.getDeviceId(applicationContext) + val messageBuilder = MessageBuilder() + messageBuilder.deviceId(ServiceLocator.provideDeviceId()) val studentId = sharedPreferences.getString(Constants.PREF_STUDENT_ID, null) - if (!TextUtils.isEmpty(studentId)) { - message.studentId = studentId + studentId?.let { + messageBuilder.studentId(it) } + val studentAvatar = sharedPreferences.getString(Constants.PREF_STUDENT_AVATAR, null) - if (!TextUtils.isEmpty(studentAvatar)) { - message.studentAvatar = studentAvatar + studentAvatar?.let { + messageBuilder.studentAvatar(it) } - message.timeSent = Calendar.getInstance() - message.text = text + + messageBuilder.message(text) + + val message = messageBuilder.build() lifecycleScope.launch { chatMessageDao.insert(message.toEntity()) @@ -172,7 +174,7 @@ class ChatActivity : ComponentActivity() { val showMessageFromAkili = Math.random() > 0.5 if (showMessageFromAkili) { mButtonSend!!.postDelayed({ // Simulate message from AI tutor - val akiliMessage = MessageFactory.generateEmojiMessage("00000000aaaaaaaa_1") + val akiliMessage = generateEmojiMessage("00000000aaaaaaaa_1") addToMessageListAndRefresh(akiliMessage) }, (2000 + (Math.random() * 8000).toInt()).toLong()) } @@ -180,7 +182,7 @@ class ChatActivity : ComponentActivity() { val showMessageFromPenguin = Math.random() > 0.5 if (showMessageFromPenguin) { mButtonSend!!.postDelayed({ // Penguin - val penguinMessage = MessageFactory.generateEmojiMessage("00000000aaaaaaaa_2") + val penguinMessage = generateEmojiMessage("00000000aaaaaaaa_2") addToMessageListAndRefresh(penguinMessage) }, (2000 + (Math.random() * 8000).toInt()).toLong()) } diff --git a/app/src/main/java/ai/elimu/chat/ChatApplication.kt b/app/src/main/java/ai/elimu/chat/ChatApplication.kt index dd122b3..02ebccf 100644 --- a/app/src/main/java/ai/elimu/chat/ChatApplication.kt +++ b/app/src/main/java/ai/elimu/chat/ChatApplication.kt @@ -1,8 +1,6 @@ package ai.elimu.chat -import ai.elimu.chat.dao.DaoMaster -import ai.elimu.chat.dao.DaoMaster.DevOpenHelper -import ai.elimu.chat.dao.DaoSession + import ai.elimu.chat.di.ServiceLocator import ai.elimu.chat.util.Constants import ai.elimu.chat.util.VersionHelper @@ -13,18 +11,13 @@ import androidx.core.content.edit //@HiltAndroidApp class ChatApplication : Application() { - var daoSession: DaoSession? = null - private set + override fun onCreate() { Log.i(javaClass.getName(), "onCreate") super.onCreate() ServiceLocator.initialize(this) - val helper = DevOpenHelper(this, "chat-db") - val db = helper.writableDb - daoSession = DaoMaster(db).newSession() - // Check if the application's versionCode was upgraded val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(applicationContext) diff --git a/app/src/main/java/ai/elimu/chat/MessageListArrayAdapter.kt b/app/src/main/java/ai/elimu/chat/MessageListArrayAdapter.kt index 556160d..d0bf402 100644 --- a/app/src/main/java/ai/elimu/chat/MessageListArrayAdapter.kt +++ b/app/src/main/java/ai/elimu/chat/MessageListArrayAdapter.kt @@ -1,7 +1,6 @@ package ai.elimu.chat import ai.elimu.chat.model.Message -import ai.elimu.chat.receiver.StudentUpdateReceiver import ai.elimu.chat.util.Constants import android.content.Context import android.graphics.BitmapFactory diff --git a/app/src/main/java/ai/elimu/chat/dao/DaoSession.java b/app/src/main/java/ai/elimu/chat/dao/DaoSession.java deleted file mode 100644 index b5e3ff9..0000000 --- a/app/src/main/java/ai/elimu/chat/dao/DaoSession.java +++ /dev/null @@ -1,48 +0,0 @@ -package ai.elimu.chat.dao; - -import java.util.Map; - -import org.greenrobot.greendao.AbstractDao; -import org.greenrobot.greendao.AbstractDaoSession; -import org.greenrobot.greendao.database.Database; -import org.greenrobot.greendao.identityscope.IdentityScopeType; -import org.greenrobot.greendao.internal.DaoConfig; - -import ai.elimu.chat.model.Message; - -import ai.elimu.chat.dao.MessageDao; - -// THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT. - -/** - * {@inheritDoc} - * - * @see org.greenrobot.greendao.AbstractDaoSession - */ -public class DaoSession extends AbstractDaoSession { - - private final DaoConfig messageDaoConfig; - - private final MessageDao messageDao; - - public DaoSession(Database db, IdentityScopeType type, Map>, DaoConfig> - daoConfigMap) { - super(db); - - messageDaoConfig = daoConfigMap.get(MessageDao.class).clone(); - messageDaoConfig.initIdentityScope(type); - - messageDao = new MessageDao(messageDaoConfig, this); - - registerDao(Message.class, messageDao); - } - - public void clear() { - messageDaoConfig.clearIdentityScope(); - } - - public MessageDao getMessageDao() { - return messageDao; - } - -} diff --git a/app/src/main/java/ai/elimu/chat/dao/MessageDao.java b/app/src/main/java/ai/elimu/chat/dao/MessageDao.java deleted file mode 100644 index 490b123..0000000 --- a/app/src/main/java/ai/elimu/chat/dao/MessageDao.java +++ /dev/null @@ -1,165 +0,0 @@ -package ai.elimu.chat.dao; - -import android.database.Cursor; -import android.database.sqlite.SQLiteStatement; - -import org.greenrobot.greendao.AbstractDao; -import org.greenrobot.greendao.Property; -import org.greenrobot.greendao.internal.DaoConfig; -import org.greenrobot.greendao.database.Database; -import org.greenrobot.greendao.database.DatabaseStatement; - -import ai.elimu.chat.dao.converter.CalendarConverter; -import java.util.Calendar; - -import ai.elimu.chat.model.Message; - -// THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT. -/** - * DAO for table "MESSAGE". -*/ -public class MessageDao extends AbstractDao { - - public static final String TABLENAME = "MESSAGE"; - - /** - * Properties of entity Message.
- * Can be used for QueryBuilder and for referencing column names. - */ - public static class Properties { - public final static Property Id = new Property(0, Long.class, "id", true, "_id"); - public final static Property DeviceId = new Property(1, String.class, "deviceId", false, "DEVICE_ID"); - public final static Property StudentId = new Property(2, String.class, "studentId", false, "STUDENT_ID"); - public final static Property StudentAvatar = new Property(3, String.class, "studentAvatar", false, "STUDENT_AVATAR"); - public final static Property TimeSent = new Property(4, long.class, "timeSent", false, "TIME_SENT"); - public final static Property Text = new Property(5, String.class, "text", false, "TEXT"); - } - - private final CalendarConverter timeSentConverter = new CalendarConverter(); - - public MessageDao(DaoConfig config) { - super(config); - } - - public MessageDao(DaoConfig config, DaoSession daoSession) { - super(config, daoSession); - } - - /** Creates the underlying database table. */ - public static void createTable(Database db, boolean ifNotExists) { - String constraint = ifNotExists? "IF NOT EXISTS ": ""; - db.execSQL("CREATE TABLE " + constraint + "\"MESSAGE\" (" + // - "\"_id\" INTEGER PRIMARY KEY AUTOINCREMENT ," + // 0: id - "\"DEVICE_ID\" TEXT NOT NULL ," + // 1: deviceId - "\"STUDENT_ID\" TEXT," + // 2: studentId - "\"STUDENT_AVATAR\" TEXT," + // 3: studentAvatar - "\"TIME_SENT\" INTEGER NOT NULL ," + // 4: timeSent - "\"TEXT\" TEXT NOT NULL );"); // 5: text - } - - /** Drops the underlying database table. */ - public static void dropTable(Database db, boolean ifExists) { - String sql = "DROP TABLE " + (ifExists ? "IF EXISTS " : "") + "\"MESSAGE\""; - db.execSQL(sql); - } - - @Override - protected final void bindValues(DatabaseStatement stmt, Message entity) { - stmt.clearBindings(); - - Long id = entity.getId(); - if (id != null) { - stmt.bindLong(1, id); - } - stmt.bindString(2, entity.getDeviceId()); - - String studentId = entity.getStudentId(); - if (studentId != null) { - stmt.bindString(3, studentId); - } - - String studentAvatar = entity.getStudentAvatar(); - if (studentAvatar != null) { - stmt.bindString(4, studentAvatar); - } - stmt.bindLong(5, timeSentConverter.convertToDatabaseValue(entity.getTimeSent())); - stmt.bindString(6, entity.getText()); - } - - @Override - protected final void bindValues(SQLiteStatement stmt, Message entity) { - stmt.clearBindings(); - - Long id = entity.getId(); - if (id != null) { - stmt.bindLong(1, id); - } - stmt.bindString(2, entity.getDeviceId()); - - String studentId = entity.getStudentId(); - if (studentId != null) { - stmt.bindString(3, studentId); - } - - String studentAvatar = entity.getStudentAvatar(); - if (studentAvatar != null) { - stmt.bindString(4, studentAvatar); - } - stmt.bindLong(5, timeSentConverter.convertToDatabaseValue(entity.getTimeSent())); - stmt.bindString(6, entity.getText()); - } - - @Override - public Long readKey(Cursor cursor, int offset) { - return cursor.isNull(offset + 0) ? null : cursor.getLong(offset + 0); - } - - @Override - public Message readEntity(Cursor cursor, int offset) { - Message entity = new Message( // - cursor.isNull(offset + 0) ? null : cursor.getLong(offset + 0), // id - cursor.getString(offset + 1), // deviceId - cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2), // studentId - cursor.isNull(offset + 3) ? null : cursor.getString(offset + 3), // studentAvatar - timeSentConverter.convertToEntityProperty(cursor.getLong(offset + 4)), // timeSent - cursor.getString(offset + 5) // text - ); - return entity; - } - - @Override - public void readEntity(Cursor cursor, Message entity, int offset) { - entity.setId(cursor.isNull(offset + 0) ? null : cursor.getLong(offset + 0)); - entity.setDeviceId(cursor.getString(offset + 1)); - entity.setStudentId(cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2)); - entity.setStudentAvatar(cursor.isNull(offset + 3) ? null : cursor.getString(offset + 3)); - entity.setTimeSent(timeSentConverter.convertToEntityProperty(cursor.getLong(offset + 4))); - entity.setText(cursor.getString(offset + 5)); - } - - @Override - protected final Long updateKeyAfterInsert(Message entity, long rowId) { - entity.setId(rowId); - return rowId; - } - - @Override - public Long getKey(Message entity) { - if(entity != null) { - return entity.getId(); - } else { - return null; - } - } - - @Override - public boolean hasKey(Message entity) { - return entity.getId() != null; - } - - @Override - protected final boolean isEntityUpdateable() { - return true; - } - -} diff --git a/app/src/main/java/ai/elimu/chat/dao/converter/CalendarConverter.kt b/app/src/main/java/ai/elimu/chat/dao/converter/CalendarConverter.kt deleted file mode 100644 index fc69fbf..0000000 --- a/app/src/main/java/ai/elimu/chat/dao/converter/CalendarConverter.kt +++ /dev/null @@ -1,20 +0,0 @@ -package ai.elimu.chat.dao.converter - -import org.greenrobot.greendao.converter.PropertyConverter -import java.util.Calendar - -class CalendarConverter : PropertyConverter { - - override fun convertToEntityProperty(databaseValue: Long?): Calendar? { - val calendar = Calendar.getInstance() - databaseValue?.let { - calendar.setTimeInMillis(databaseValue) - } - return calendar - } - - override fun convertToDatabaseValue(entityProperty: Calendar?): Long? { - val databaseValue = entityProperty?.getTimeInMillis() - return databaseValue - } -} diff --git a/app/src/main/java/ai/elimu/chat/di/ServiceLocator.kt b/app/src/main/java/ai/elimu/chat/di/ServiceLocator.kt index 23eec49..86bafb0 100644 --- a/app/src/main/java/ai/elimu/chat/di/ServiceLocator.kt +++ b/app/src/main/java/ai/elimu/chat/di/ServiceLocator.kt @@ -2,13 +2,18 @@ package ai.elimu.chat.di import ai.elimu.chat.data.local.AppDatabase import ai.elimu.chat.data.local.ChatMessageDao +import android.annotation.SuppressLint import android.content.Context +import android.provider.Settings import androidx.room.Room object ServiceLocator { private var chatMessageDao: ChatMessageDao? = null + private var deviceId: String? = null + + @SuppressLint("HardwareIds") fun initialize(context: Context) { val db = Room.databaseBuilder( context.applicationContext, @@ -16,9 +21,16 @@ object ServiceLocator { "chat-db-new" ).build() chatMessageDao = db.messageDao() + + deviceId = Settings.Secure.getString(context.contentResolver, Settings.Secure.ANDROID_ID) } fun provideChatMessageDao(): ChatMessageDao { return chatMessageDao ?: throw IllegalStateException("ServiceLocator not initialized") } + + fun provideDeviceId(): String { + return deviceId ?: throw IllegalStateException("ServiceLocator not initialized") + + } } \ No newline at end of file diff --git a/app/src/main/java/ai/elimu/chat/model/Message.kt b/app/src/main/java/ai/elimu/chat/model/Message.kt index 6b9fc1f..3f0782e 100644 --- a/app/src/main/java/ai/elimu/chat/model/Message.kt +++ b/app/src/main/java/ai/elimu/chat/model/Message.kt @@ -1,95 +1,34 @@ -package ai.elimu.chat.model; - -import org.greenrobot.greendao.annotation.Convert; -import org.greenrobot.greendao.annotation.Entity; -import org.greenrobot.greendao.annotation.Generated; -import org.greenrobot.greendao.annotation.Id; -import org.greenrobot.greendao.annotation.NotNull; -import ai.elimu.chat.dao.converter.CalendarConverter; - -import java.util.Calendar; - -@Entity -public class Message { - - @Id(autoincrement = true) - private Long id; - - @NotNull - private String deviceId; - - private String studentId; - - private String studentAvatar; - - @NotNull - @Convert(converter = CalendarConverter.class, columnType = Long.class) - private Calendar timeSent; - - @NotNull - private String text; - - @Generated(hash = 449884345) - public Message(Long id, @NotNull String deviceId, String studentId, - String studentAvatar, @NotNull Calendar timeSent, - @NotNull String text) { - this.id = id; - this.deviceId = deviceId; - this.studentId = studentId; - this.studentAvatar = studentAvatar; - this.timeSent = timeSent; - this.text = text; - } - - @Generated(hash = 637306882) - public Message() { - } - - public Long getId() { - return this.id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getDeviceId() { - return this.deviceId; - } - - public void setDeviceId(String deviceId) { - this.deviceId = deviceId; - } - - public String getStudentId() { - return this.studentId; - } - - public void setStudentId(String studentId) { - this.studentId = studentId; - } - - public String getStudentAvatar() { - return this.studentAvatar; - } - - public void setStudentAvatar(String studentAvatar) { - this.studentAvatar = studentAvatar; - } - - public Calendar getTimeSent() { - return this.timeSent; - } - - public void setTimeSent(Calendar timeSent) { - this.timeSent = timeSent; - } - - public String getText() { - return this.text; - } - - public void setText(String text) { - this.text = text; - } +package ai.elimu.chat.model + +import ai.elimu.chat.util.getRandomEmoji +import java.util.Calendar + +data class Message( + val id: Long, val deviceId: String, val studentId: String?, val studentAvatar: String?, + val timeSent: Calendar, val text: String +) + +class MessageBuilder { + private var id: Long = 0 + private var deviceId: String = "" + private var studentId: String? = null + private var studentAvatar: String? = null + private var timeSent: Calendar = Calendar.getInstance() + private var text: String = "" + + fun id(id: Long) = apply { this.id = id } + fun deviceId(deviceId: String) = apply { this.deviceId = deviceId } + fun studentId(studentId: String?) = apply { this.studentId = studentId } + fun studentAvatar(studentAvatar: String?) = apply { this.studentAvatar = studentAvatar } + fun timeSent(timesent: Calendar) = apply { this.timeSent = timesent } + fun message(text: String) = apply { this.text = text } + + fun build() = Message(id, deviceId, studentId, studentAvatar, timeSent, text) } + +fun generateEmojiMessage(studentId: String): Message { + val message = MessageBuilder() + message.studentId(studentId) + message.message(getRandomEmoji()) + return message.build() +} \ No newline at end of file diff --git a/app/src/main/java/ai/elimu/chat/model/MessageFactory.kt b/app/src/main/java/ai/elimu/chat/model/MessageFactory.kt deleted file mode 100644 index cf93e04..0000000 --- a/app/src/main/java/ai/elimu/chat/model/MessageFactory.kt +++ /dev/null @@ -1,15 +0,0 @@ -package ai.elimu.chat.model - -import ai.elimu.chat.util.getRandomEmoji -import java.util.Calendar - -object MessageFactory { - - fun generateEmojiMessage(studentId: String): Message { - val message = Message() - message.studentId = studentId - message.timeSent = Calendar.getInstance() - message.text = getRandomEmoji() - return message - } -} \ No newline at end of file diff --git a/app/src/main/java/ai/elimu/chat/receiver/StudentUpdateReceiver.kt b/app/src/main/java/ai/elimu/chat/receiver/StudentUpdateReceiver.kt index c37f3fa..c457cce 100644 --- a/app/src/main/java/ai/elimu/chat/receiver/StudentUpdateReceiver.kt +++ b/app/src/main/java/ai/elimu/chat/receiver/StudentUpdateReceiver.kt @@ -1,9 +1,7 @@ package ai.elimu.chat.receiver import ai.elimu.chat.ChatApplication -import ai.elimu.chat.dao.MessageDao import ai.elimu.chat.util.Constants -import ai.elimu.chat.util.DeviceInfoHelper.getDeviceId import android.content.BroadcastReceiver import android.content.Context import android.content.Intent @@ -27,7 +25,7 @@ class StudentUpdateReceiver : BroadcastReceiver() { val existingStudentId = sharedPreferences.getString(Constants.PREF_STUDENT_ID, null) Log.i(javaClass.getName(), "existingStudentId: $existingStudentId") if (TextUtils.isEmpty(existingStudentId)) { - // Update previously sent messages on the current device +/* // Update previously sent messages on the current device // TODO: Migrate to room val chatApplication = context.applicationContext as ChatApplication val messageDao = chatApplication.daoSession!!.messageDao val existingMessages = messageDao.queryBuilder() @@ -41,7 +39,7 @@ class StudentUpdateReceiver : BroadcastReceiver() { message.studentId = studentId message.studentAvatar = studentAvatar messageDao.update(message) - } + }*/ } sharedPreferences.edit(commit = true) { putString(Constants.PREF_STUDENT_ID, studentId) } diff --git a/app/src/main/java/ai/elimu/chat/util/DeviceInfoHelper.kt b/app/src/main/java/ai/elimu/chat/util/DeviceInfoHelper.kt deleted file mode 100644 index e2c829f..0000000 --- a/app/src/main/java/ai/elimu/chat/util/DeviceInfoHelper.kt +++ /dev/null @@ -1,11 +0,0 @@ -package ai.elimu.chat.util - -import android.content.Context -import android.provider.Settings - -object DeviceInfoHelper { - @JvmStatic - fun getDeviceId(context: Context): String? { - return Settings.Secure.getString(context.contentResolver, Settings.Secure.ANDROID_ID) - } -} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6ce9586..df671a6 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,8 +3,6 @@ elimuContentProvider = "1.4.50" agp = "8.6.0" javapoet = "1.13.0" junit = "4.13.2" -greenDao = "3.3.0" -greenDaoGradlePlugin = "3.3.1" circleImageView = "3.1.0" androidXJunit = "1.2.1" @@ -17,8 +15,6 @@ hilt = "2.57.1" [libraries] elimu-content-provider = { group = "ai.elimu", name = "content-provider", version.ref = "elimuContentProvider" } -green-dao = { group = "org.greenrobot", name = "greendao", version.ref = "greenDao" } -green-dao-gradle-plugin = { group = "org.greenrobot", name = "greendao-gradle-plugin", version.ref = "greenDaoGradlePlugin" } circle-imageview = { group = "de.hdodenhof", name = "circleimageview", version.ref = "circleImageView" } agp = { module = "com.android.tools.build:gradle", version.ref = "agp" } androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidXJunit" } From f912307f7b5c3d91485209d2e4a73901af349dea Mon Sep 17 00:00:00 2001 From: kiranbollepalli9 Date: Thu, 4 Sep 2025 19:21:30 +0530 Subject: [PATCH 07/12] Refactor: - Move initialization of SharedPreference to Service Locator --- .../main/java/ai/elimu/chat/ChatActivity.kt | 28 +++++------ .../ai/elimu/chat/MessageListArrayAdapter.kt | 5 +- .../java/ai/elimu/chat/di/ServiceLocator.kt | 8 +++ .../chat/receiver/StudentUpdateReceiver.kt | 49 +++++++++++-------- 4 files changed, 51 insertions(+), 39 deletions(-) diff --git a/app/src/main/java/ai/elimu/chat/ChatActivity.kt b/app/src/main/java/ai/elimu/chat/ChatActivity.kt index d1adbcd..da88e50 100644 --- a/app/src/main/java/ai/elimu/chat/ChatActivity.kt +++ b/app/src/main/java/ai/elimu/chat/ChatActivity.kt @@ -8,8 +8,8 @@ import ai.elimu.chat.model.Message import ai.elimu.chat.model.MessageBuilder import ai.elimu.chat.model.generateEmojiMessage import ai.elimu.chat.util.Constants +import android.content.SharedPreferences import android.os.Bundle -import android.preference.PreferenceManager import android.text.Editable import android.text.TextUtils import android.text.TextWatcher @@ -27,10 +27,10 @@ import java.util.Calendar //@AndroidEntryPoint class ChatActivity : ComponentActivity() { - // @Inject + // @Inject lateinit var chatMessageDao: ChatMessageDao - // private var messageDao: MessageDao? = null + lateinit var sharedPreferences: SharedPreferences private var messages: MutableList = mutableListOf() @@ -49,8 +49,7 @@ class ChatActivity : ComponentActivity() { setContentView(R.layout.activity_chat) chatMessageDao = ServiceLocator.provideChatMessageDao() - - // messageDao = (application as ChatApplication).daoSession!!.messageDao + sharedPreferences = ServiceLocator.provideSharedPreference() mListPreviousMessages = findViewById(R.id.listPreviousMessages) as ListView messageText = findViewById(R.id.message) as EditText @@ -66,12 +65,12 @@ class ChatActivity : ComponentActivity() { // Log.i(getClass().getName(), "letters: " + letters); // Load messages sent within the last 24 hours -/* val calendar24HoursAgo = Calendar.getInstance() - calendar24HoursAgo.add(Calendar.HOUR_OF_DAY, -24) - messages = messageDao!!.queryBuilder() - .where(MessageDao.Properties.TimeSent.gt(calendar24HoursAgo.getTimeInMillis())) - .list() - Log.i(javaClass.getName(), "messages.size(): " + messages.size)*/ + /* val calendar24HoursAgo = Calendar.getInstance() + calendar24HoursAgo.add(Calendar.HOUR_OF_DAY, -24) + messages = messageDao!!.queryBuilder() + .where(MessageDao.Properties.TimeSent.gt(calendar24HoursAgo.getTimeInMillis())) + .list() + Log.i(javaClass.getName(), "messages.size(): " + messages.size)*/ arrayAdapter = MessageListArrayAdapter(applicationContext, messages) mListPreviousMessages!!.setAdapter(arrayAdapter) @@ -81,7 +80,7 @@ class ChatActivity : ComponentActivity() { calendar24HoursAgo.add(Calendar.HOUR_OF_DAY, -24) val newMessages = chatMessageDao.getMessages(timeStamp = calendar24HoursAgo.timeInMillis) - val uiMessages = newMessages.map { it.toMessage() } + val uiMessages = newMessages.map { it.toMessage() } messages.addAll(uiMessages) refreshMessageList() } @@ -138,9 +137,6 @@ class ChatActivity : ComponentActivity() { val text = messageText!!.getText().toString() Log.i(javaClass.getName(), "text: $text") - val sharedPreferences = - PreferenceManager.getDefaultSharedPreferences(applicationContext) - // Store in database val messageBuilder = MessageBuilder() messageBuilder.deviceId(ServiceLocator.provideDeviceId()) @@ -163,7 +159,7 @@ class ChatActivity : ComponentActivity() { lifecycleScope.launch { chatMessageDao.insert(message.toEntity()) } - // messageDao!!.insert(message) + // messageDao!!.insert(message) // Add to UI addToMessageListAndRefresh(message) diff --git a/app/src/main/java/ai/elimu/chat/MessageListArrayAdapter.kt b/app/src/main/java/ai/elimu/chat/MessageListArrayAdapter.kt index d0bf402..a99de94 100644 --- a/app/src/main/java/ai/elimu/chat/MessageListArrayAdapter.kt +++ b/app/src/main/java/ai/elimu/chat/MessageListArrayAdapter.kt @@ -1,10 +1,10 @@ package ai.elimu.chat +import ai.elimu.chat.di.ServiceLocator import ai.elimu.chat.model.Message import ai.elimu.chat.util.Constants import android.content.Context import android.graphics.BitmapFactory -import android.preference.PreferenceManager import android.text.TextUtils import android.util.Log import android.view.LayoutInflater @@ -34,8 +34,7 @@ class MessageListArrayAdapter(context: Context, messages: List) : this.context = context this.messages = messages - val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) - studentId = sharedPreferences.getString(Constants.PREF_STUDENT_ID, null) + studentId = ServiceLocator.provideSharedPreference().getString(Constants.PREF_STUDENT_ID, null) } override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { diff --git a/app/src/main/java/ai/elimu/chat/di/ServiceLocator.kt b/app/src/main/java/ai/elimu/chat/di/ServiceLocator.kt index 86bafb0..20794f5 100644 --- a/app/src/main/java/ai/elimu/chat/di/ServiceLocator.kt +++ b/app/src/main/java/ai/elimu/chat/di/ServiceLocator.kt @@ -4,6 +4,7 @@ import ai.elimu.chat.data.local.AppDatabase import ai.elimu.chat.data.local.ChatMessageDao import android.annotation.SuppressLint import android.content.Context +import android.content.SharedPreferences import android.provider.Settings import androidx.room.Room @@ -13,6 +14,8 @@ object ServiceLocator { private var deviceId: String? = null + private var sharedPreferences: SharedPreferences? = null + @SuppressLint("HardwareIds") fun initialize(context: Context) { val db = Room.databaseBuilder( @@ -23,6 +26,8 @@ object ServiceLocator { chatMessageDao = db.messageDao() deviceId = Settings.Secure.getString(context.contentResolver, Settings.Secure.ANDROID_ID) + + sharedPreferences = context.getSharedPreferences("your_pref_name", Context.MODE_PRIVATE) } fun provideChatMessageDao(): ChatMessageDao { @@ -31,6 +36,9 @@ object ServiceLocator { fun provideDeviceId(): String { return deviceId ?: throw IllegalStateException("ServiceLocator not initialized") + } + fun provideSharedPreference(): SharedPreferences { + return sharedPreferences ?: throw IllegalStateException("ServiceLocator not initialized") } } \ No newline at end of file diff --git a/app/src/main/java/ai/elimu/chat/receiver/StudentUpdateReceiver.kt b/app/src/main/java/ai/elimu/chat/receiver/StudentUpdateReceiver.kt index c457cce..571c5e7 100644 --- a/app/src/main/java/ai/elimu/chat/receiver/StudentUpdateReceiver.kt +++ b/app/src/main/java/ai/elimu/chat/receiver/StudentUpdateReceiver.kt @@ -1,11 +1,10 @@ package ai.elimu.chat.receiver -import ai.elimu.chat.ChatApplication +import ai.elimu.chat.di.ServiceLocator import ai.elimu.chat.util.Constants import android.content.BroadcastReceiver import android.content.Context import android.content.Intent -import android.preference.PreferenceManager import android.text.TextUtils import android.util.Log import androidx.core.content.edit @@ -19,34 +18,44 @@ class StudentUpdateReceiver : BroadcastReceiver() { val studentAvatar = intent.getStringExtra(EXTRA_STUDENT_AVATAR) Log.i(javaClass.getName(), "studentAvatar: $studentAvatar") - val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) + val sharedPreferences = ServiceLocator.provideSharedPreference() if (!TextUtils.isEmpty(studentId)) { val existingStudentId = sharedPreferences.getString(Constants.PREF_STUDENT_ID, null) Log.i(javaClass.getName(), "existingStudentId: $existingStudentId") if (TextUtils.isEmpty(existingStudentId)) { -/* // Update previously sent messages on the current device // TODO: Migrate to room - val chatApplication = context.applicationContext as ChatApplication - val messageDao = chatApplication.daoSession!!.messageDao - val existingMessages = messageDao.queryBuilder() - .where( - MessageDao.Properties.DeviceId.eq(getDeviceId(context)), - MessageDao.Properties.StudentId.isNull() - ) - .list() - Log.i(javaClass.getName(), "existingMessages.size(): " + existingMessages.size) - for (message in existingMessages) { - message.studentId = studentId - message.studentAvatar = studentAvatar - messageDao.update(message) - }*/ + /* // Update previously sent messages on the current device // TODO: Migrate to room + val chatApplication = context.applicationContext as ChatApplication + val messageDao = chatApplication.daoSession!!.messageDao + val existingMessages = messageDao.queryBuilder() + .where( + MessageDao.Properties.DeviceId.eq(getDeviceId(context)), + MessageDao.Properties.StudentId.isNull() + ) + .list() + Log.i(javaClass.getName(), "existingMessages.size(): " + existingMessages.size) + for (message in existingMessages) { + message.studentId = studentId + message.studentAvatar = studentAvatar + messageDao.update(message) + }*/ } - sharedPreferences.edit(commit = true) { putString(Constants.PREF_STUDENT_ID, studentId) } + sharedPreferences.edit(commit = true) { + putString( + Constants.PREF_STUDENT_ID, + studentId + ) + } } if (!TextUtils.isEmpty(studentAvatar)) { - sharedPreferences.edit(commit = true) { putString(Constants.PREF_STUDENT_AVATAR, studentAvatar) } + sharedPreferences.edit(commit = true) { + putString( + Constants.PREF_STUDENT_AVATAR, + studentAvatar + ) + } } } From d1375ec96d9418a96c2c3acb49c490affee09d8b Mon Sep 17 00:00:00 2001 From: kiranbollepalli9 Date: Thu, 4 Sep 2025 20:12:53 +0530 Subject: [PATCH 08/12] Refactor: - Created ViewModel and moved the relevent logic from Activity to ViewModel --- app/src/main/AndroidManifest.xml | 2 +- .../main/java/ai/elimu/chat/ChatActivity.kt | 205 ------------------ .../java/ai/elimu/chat/di/ServiceLocator.kt | 4 +- .../java/ai/elimu/chat/ui/ChatActivity.kt | 116 ++++++++++ .../java/ai/elimu/chat/ui/ChatViewModel.kt | 74 +++++++ .../chat/{ => ui}/MessageListArrayAdapter.kt | 5 +- .../main/java/ai/elimu/chat/util/Constants.kt | 6 + 7 files changed, 203 insertions(+), 209 deletions(-) delete mode 100644 app/src/main/java/ai/elimu/chat/ChatActivity.kt create mode 100644 app/src/main/java/ai/elimu/chat/ui/ChatActivity.kt create mode 100644 app/src/main/java/ai/elimu/chat/ui/ChatViewModel.kt rename app/src/main/java/ai/elimu/chat/{ => ui}/MessageListArrayAdapter.kt (98%) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5cff74e..9946225 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -11,7 +11,7 @@ android:theme="@style/AppTheme"> diff --git a/app/src/main/java/ai/elimu/chat/ChatActivity.kt b/app/src/main/java/ai/elimu/chat/ChatActivity.kt deleted file mode 100644 index da88e50..0000000 --- a/app/src/main/java/ai/elimu/chat/ChatActivity.kt +++ /dev/null @@ -1,205 +0,0 @@ -package ai.elimu.chat - -import ai.elimu.chat.data.local.ChatMessageDao -import ai.elimu.chat.data.local.toEntity -import ai.elimu.chat.data.local.toMessage -import ai.elimu.chat.di.ServiceLocator -import ai.elimu.chat.model.Message -import ai.elimu.chat.model.MessageBuilder -import ai.elimu.chat.model.generateEmojiMessage -import ai.elimu.chat.util.Constants -import android.content.SharedPreferences -import android.os.Bundle -import android.text.Editable -import android.text.TextUtils -import android.text.TextWatcher -import android.util.Log -import android.view.View -import android.widget.ArrayAdapter -import android.widget.EditText -import android.widget.ImageButton -import android.widget.ListView -import androidx.core.app.ComponentActivity -import androidx.lifecycle.lifecycleScope -import kotlinx.coroutines.launch -import java.util.Calendar - -//@AndroidEntryPoint -class ChatActivity : ComponentActivity() { - - // @Inject - lateinit var chatMessageDao: ChatMessageDao - - lateinit var sharedPreferences: SharedPreferences - - private var messages: MutableList = mutableListOf() - - private var arrayAdapter: ArrayAdapter<*>? = null - - private var mListPreviousMessages: ListView? = null - - private var messageText: EditText? = null - - private var mButtonSend: ImageButton? = null - - override fun onCreate(savedInstanceState: Bundle?) { - Log.i(javaClass.getName(), "onCreate") - super.onCreate(savedInstanceState) - - setContentView(R.layout.activity_chat) - chatMessageDao = ServiceLocator.provideChatMessageDao() - - sharedPreferences = ServiceLocator.provideSharedPreference() - - mListPreviousMessages = findViewById(R.id.listPreviousMessages) as ListView - messageText = findViewById(R.id.message) as EditText - mButtonSend = findViewById(R.id.buttonSend) as ImageButton - } - - override fun onStart() { - Log.i(javaClass.getName(), "onStart") - super.onStart() - - // ContentProvider.initializeDb(this); -// List letters = ContentProvider.getAvailableLetters(); -// Log.i(getClass().getName(), "letters: " + letters); - - // Load messages sent within the last 24 hours - /* val calendar24HoursAgo = Calendar.getInstance() - calendar24HoursAgo.add(Calendar.HOUR_OF_DAY, -24) - messages = messageDao!!.queryBuilder() - .where(MessageDao.Properties.TimeSent.gt(calendar24HoursAgo.getTimeInMillis())) - .list() - Log.i(javaClass.getName(), "messages.size(): " + messages.size)*/ - - arrayAdapter = MessageListArrayAdapter(applicationContext, messages) - mListPreviousMessages!!.setAdapter(arrayAdapter) - - lifecycleScope.launch { - val calendar24HoursAgo = Calendar.getInstance() - calendar24HoursAgo.add(Calendar.HOUR_OF_DAY, -24) - val newMessages = - chatMessageDao.getMessages(timeStamp = calendar24HoursAgo.timeInMillis) - val uiMessages = newMessages.map { it.toMessage() } - messages.addAll(uiMessages) - refreshMessageList() - } - - messageText!!.addTextChangedListener(object : TextWatcher { - override fun beforeTextChanged(charSequence: CharSequence?, i: Int, i1: Int, i2: Int) { - } - - override fun onTextChanged(charSequence: CharSequence?, i: Int, i1: Int, i2: Int) { - } - - override fun afterTextChanged(editable: Editable?) { - Log.i(javaClass.getName(), "afterTextChanged") - - Log.i(javaClass.getName(), "editable: $editable") - - if (!TextUtils.isEmpty(editable)) { - if (!mButtonSend!!.isEnabled) { - mButtonSend!!.setEnabled(true) - mButtonSend!!.setImageDrawable(getDrawable(R.drawable.ic_send_white_24dp)) - - // // Animate button to indicate that it can be pressed -// final long duration = 300; -// -// final ObjectAnimator scaleXAnimator = ObjectAnimator.ofFloat(mButtonSend, View.SCALE_X, 1f, 1.2f, 1f); -// scaleXAnimator.setDuration(duration); -// scaleXAnimator.setRepeatCount(1); -// -// final ObjectAnimator scaleYAnimator = ObjectAnimator.ofFloat(mButtonSend, View.SCALE_Y, 1f, 1.2f, 1f); -// scaleYAnimator.setDuration(duration); -// scaleYAnimator.setRepeatCount(1); -// -// scaleXAnimator.start(); -// scaleYAnimator.start(); -// -// final AnimatorSet animatorSet = new AnimatorSet(); -// animatorSet.play(scaleXAnimator).with(scaleYAnimator); -// animatorSet.start(); - } - } else { - mButtonSend!!.setEnabled(false) - mButtonSend!!.setImageDrawable(getDrawable(R.drawable.ic_send_grey_24dp)) - } - } - }) - - // Default to grey button - mButtonSend!!.setEnabled(false) - mButtonSend!!.setImageDrawable(getDrawable(R.drawable.ic_send_grey_24dp)) - - mButtonSend!!.setOnClickListener { - Log.i(javaClass.getName(), "mButtonSend onClick") - - val text = messageText!!.getText().toString() - Log.i(javaClass.getName(), "text: $text") - - // Store in database - val messageBuilder = MessageBuilder() - messageBuilder.deviceId(ServiceLocator.provideDeviceId()) - val studentId = - sharedPreferences.getString(Constants.PREF_STUDENT_ID, null) - studentId?.let { - messageBuilder.studentId(it) - } - - val studentAvatar = - sharedPreferences.getString(Constants.PREF_STUDENT_AVATAR, null) - studentAvatar?.let { - messageBuilder.studentAvatar(it) - } - - messageBuilder.message(text) - - val message = messageBuilder.build() - - lifecycleScope.launch { - chatMessageDao.insert(message.toEntity()) - } - // messageDao!!.insert(message) - - // Add to UI - addToMessageListAndRefresh(message) - - // Reset input field - messageText!!.setText("") - - val showMessageFromAkili = Math.random() > 0.5 - if (showMessageFromAkili) { - mButtonSend!!.postDelayed({ // Simulate message from AI tutor - val akiliMessage = generateEmojiMessage("00000000aaaaaaaa_1") - addToMessageListAndRefresh(akiliMessage) - }, (2000 + (Math.random() * 8000).toInt()).toLong()) - } - - val showMessageFromPenguin = Math.random() > 0.5 - if (showMessageFromPenguin) { - mButtonSend!!.postDelayed({ // Penguin - val penguinMessage = generateEmojiMessage("00000000aaaaaaaa_2") - addToMessageListAndRefresh(penguinMessage) - }, (2000 + (Math.random() * 8000).toInt()).toLong()) - } - } - } - - private fun addToMessageListAndRefresh(message: Message) { - Log.i(javaClass.getName(), "addToMessageListAndRefresh") - messages.add(message) - refreshMessageList() - } - - private fun refreshMessageList() { - Log.i(javaClass.getName(), "refreshMessageList") - - arrayAdapter!!.notifyDataSetChanged() - mListPreviousMessages!!.smoothScrollToPosition(mListPreviousMessages!!.count) - // Fix problem with scrolling when keyboard is present - mListPreviousMessages!!.postDelayed({ - Log.i(javaClass.getName(), "mListPreviousMessages.postDelayed") - mListPreviousMessages!!.setSelection(mListPreviousMessages!!.count) - }, 100) - } -} diff --git a/app/src/main/java/ai/elimu/chat/di/ServiceLocator.kt b/app/src/main/java/ai/elimu/chat/di/ServiceLocator.kt index 20794f5..9296b7e 100644 --- a/app/src/main/java/ai/elimu/chat/di/ServiceLocator.kt +++ b/app/src/main/java/ai/elimu/chat/di/ServiceLocator.kt @@ -2,6 +2,7 @@ package ai.elimu.chat.di import ai.elimu.chat.data.local.AppDatabase import ai.elimu.chat.data.local.ChatMessageDao +import ai.elimu.chat.util.Constants import android.annotation.SuppressLint import android.content.Context import android.content.SharedPreferences @@ -25,9 +26,10 @@ object ServiceLocator { ).build() chatMessageDao = db.messageDao() + //TODO: Need to change this deviceId = Settings.Secure.getString(context.contentResolver, Settings.Secure.ANDROID_ID) - sharedPreferences = context.getSharedPreferences("your_pref_name", Context.MODE_PRIVATE) + sharedPreferences = context.getSharedPreferences(Constants.PREF_FILE_NAME, Context.MODE_PRIVATE) } fun provideChatMessageDao(): ChatMessageDao { diff --git a/app/src/main/java/ai/elimu/chat/ui/ChatActivity.kt b/app/src/main/java/ai/elimu/chat/ui/ChatActivity.kt new file mode 100644 index 0000000..0c04fe0 --- /dev/null +++ b/app/src/main/java/ai/elimu/chat/ui/ChatActivity.kt @@ -0,0 +1,116 @@ +package ai.elimu.chat.ui + +import ai.elimu.chat.R +import ai.elimu.chat.model.Message +import ai.elimu.chat.util.Constants +import android.os.Bundle +import android.text.Editable +import android.text.TextUtils +import android.text.TextWatcher +import android.util.Log +import android.view.View +import android.widget.ArrayAdapter +import android.widget.EditText +import android.widget.ImageButton +import android.widget.ListView +import androidx.core.app.ComponentActivity +import androidx.lifecycle.lifecycleScope +import kotlinx.coroutines.launch + +class ChatActivity : ComponentActivity() { + + private var messages: MutableList = mutableListOf() + + private lateinit var arrayAdapter: ArrayAdapter<*> + + private lateinit var mListPreviousMessages: ListView + + private lateinit var messageText: EditText + + private lateinit var mButtonSend: ImageButton + + lateinit var viewModel: ChatViewModel + + override fun onCreate(savedInstanceState: Bundle?) { + Log.i(javaClass.getName(), "onCreate") + super.onCreate(savedInstanceState) + + setContentView(R.layout.activity_chat) + + viewModel = ChatViewModel() + + mListPreviousMessages = findViewById(R.id.listPreviousMessages) as ListView + messageText = findViewById(R.id.message) as EditText + mButtonSend = findViewById(R.id.buttonSend) as ImageButton + + arrayAdapter = MessageListArrayAdapter(applicationContext, messages) + mListPreviousMessages.setAdapter(arrayAdapter) + + viewModel.loadRecentMessages() + + lifecycleScope.launch { + viewModel.messages.collect { newMessages -> + messages.clear() + messages.addAll(newMessages) + refreshMessageList() + } + } + + messageText.addTextChangedListener(object : TextWatcher { + override fun beforeTextChanged(charSequence: CharSequence?, i: Int, i1: Int, i2: Int) { + } + + override fun onTextChanged(charSequence: CharSequence?, i: Int, i1: Int, i2: Int) { + } + + override fun afterTextChanged(editable: Editable?) { + Log.i(javaClass.getName(), "afterTextChanged") + setSendButtonState(TextUtils.isEmpty(editable)) + } + }) + + setSendButtonState(false) + + mButtonSend.setOnClickListener { + Log.i(javaClass.getName(), "mButtonSend onClick") + val message = messageText.getText().toString() + sendMessage(message) + } + } + + private fun sendMessage(message: String) { + viewModel.sendMessage(message) + + // Reset input field + messageText.setText("") + + //showMessageFromAkili + viewModel.maybeSimulateMessage(Constants.STUDENTID_AKILI) + + //showMessageFromPenguin + viewModel.maybeSimulateMessage(Constants.STUDENTID_PENGUIN) + } + + private fun setSendButtonState(isEmpty: Boolean) { + mButtonSend.apply { + isEnabled = !isEmpty + setImageDrawable( + getDrawable( + if (isEmpty) R.drawable.ic_send_grey_24dp else R.drawable.ic_send_white_24dp + ) + ) + } + } + + private fun refreshMessageList() { + Log.i(javaClass.getName(), "refreshMessageList") + + arrayAdapter.notifyDataSetChanged() + mListPreviousMessages.smoothScrollToPosition(mListPreviousMessages.count) + // Fix problem with scrolling when keyboard is present + mListPreviousMessages.postDelayed({ + Log.i(javaClass.getName(), "mListPreviousMessages.postDelayed") + mListPreviousMessages.setSelection(mListPreviousMessages.count) + }, 100) + } +} \ No newline at end of file diff --git a/app/src/main/java/ai/elimu/chat/ui/ChatViewModel.kt b/app/src/main/java/ai/elimu/chat/ui/ChatViewModel.kt new file mode 100644 index 0000000..fc84b59 --- /dev/null +++ b/app/src/main/java/ai/elimu/chat/ui/ChatViewModel.kt @@ -0,0 +1,74 @@ +package ai.elimu.chat.ui + +import ai.elimu.chat.data.local.toEntity +import ai.elimu.chat.data.local.toMessage +import ai.elimu.chat.di.ServiceLocator +import ai.elimu.chat.model.Message +import ai.elimu.chat.model.MessageBuilder +import ai.elimu.chat.model.generateEmojiMessage +import ai.elimu.chat.util.Constants +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.launch +import java.util.Calendar +import kotlin.random.Random + +class ChatViewModel() : ViewModel() { + private val _messages = MutableStateFlow>(emptyList()) + + val messages: StateFlow> = _messages + + val sharedPreferences = ServiceLocator.provideSharedPreference() + + val chatMessageDao = ServiceLocator.provideChatMessageDao() + + fun loadRecentMessages() { + viewModelScope.launch { + val calendar24HoursAgo = Calendar.getInstance() + calendar24HoursAgo.add(Calendar.HOUR_OF_DAY, -24) + val newMessages = + chatMessageDao.getMessages(timeStamp = calendar24HoursAgo.timeInMillis) + val uiMessages = newMessages.map { it.toMessage() } + _messages.value = emptyList() + _messages.value = uiMessages + } + } + + fun sendMessage(message: String) { + val messageBuilder = MessageBuilder() + messageBuilder.deviceId(ServiceLocator.provideDeviceId()) + val studentId = + sharedPreferences.getString(Constants.PREF_STUDENT_ID, null) + studentId?.let { + messageBuilder.studentId(it) + } + + val studentAvatar = + sharedPreferences.getString(Constants.PREF_STUDENT_AVATAR, null) + studentAvatar?.let { + messageBuilder.studentAvatar(it) + } + + messageBuilder.message(message) + + viewModelScope.launch { + val message = messageBuilder.build() + chatMessageDao.insert(message.toEntity()) + _messages.value = _messages.value + message + } + } + + fun maybeSimulateMessage(studentId: String) { + if (Random.nextBoolean()) { + val delayMillis = (2000..10000).random().toLong() + viewModelScope.launch { + delay(delayMillis) + val message = generateEmojiMessage(studentId) + _messages.value = _messages.value + message + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ai/elimu/chat/MessageListArrayAdapter.kt b/app/src/main/java/ai/elimu/chat/ui/MessageListArrayAdapter.kt similarity index 98% rename from app/src/main/java/ai/elimu/chat/MessageListArrayAdapter.kt rename to app/src/main/java/ai/elimu/chat/ui/MessageListArrayAdapter.kt index a99de94..03163cf 100644 --- a/app/src/main/java/ai/elimu/chat/MessageListArrayAdapter.kt +++ b/app/src/main/java/ai/elimu/chat/ui/MessageListArrayAdapter.kt @@ -1,5 +1,6 @@ -package ai.elimu.chat +package ai.elimu.chat.ui +import ai.elimu.chat.R import ai.elimu.chat.di.ServiceLocator import ai.elimu.chat.model.Message import ai.elimu.chat.util.Constants @@ -75,4 +76,4 @@ class MessageListArrayAdapter(context: Context, messages: List) : return listItem } -} +} \ No newline at end of file diff --git a/app/src/main/java/ai/elimu/chat/util/Constants.kt b/app/src/main/java/ai/elimu/chat/util/Constants.kt index 0459c64..d199001 100644 --- a/app/src/main/java/ai/elimu/chat/util/Constants.kt +++ b/app/src/main/java/ai/elimu/chat/util/Constants.kt @@ -4,12 +4,18 @@ import android.util.Log object Constants { + val PREF_FILE_NAME: String = "pref_app_001" + val PREF_APP_VERSION_CODE: String = "pref_app_version_code" val PREF_STUDENT_ID: String = "pref_student_id" val PREF_STUDENT_AVATAR: String = "pref_student_avatar" + val STUDENTID_AKILI = "00000000aaaaaaaa_1" + + val STUDENTID_PENGUIN = "00000000aaaaaaaa_2" + val EMOJI_UNICODES = intArrayOf( // Emoticons 0x1F601, From 9d3a4cda3d36a1847e9057eb9c096fb9af08a96f Mon Sep 17 00:00:00 2001 From: kiranbollepalli9 Date: Thu, 4 Sep 2025 20:18:23 +0530 Subject: [PATCH 09/12] Refactor: - Minor refactoring of ChatApplication file. --- .../java/ai/elimu/chat/ChatApplication.kt | 30 +++++++++++-------- .../main/java/ai/elimu/chat/model/Message.kt | 15 +++++++++- app/src/main/java/ai/elimu/chat/util/Utils.kt | 16 ---------- 3 files changed, 32 insertions(+), 29 deletions(-) delete mode 100644 app/src/main/java/ai/elimu/chat/util/Utils.kt diff --git a/app/src/main/java/ai/elimu/chat/ChatApplication.kt b/app/src/main/java/ai/elimu/chat/ChatApplication.kt index 02ebccf..a14837e 100644 --- a/app/src/main/java/ai/elimu/chat/ChatApplication.kt +++ b/app/src/main/java/ai/elimu/chat/ChatApplication.kt @@ -5,7 +5,6 @@ import ai.elimu.chat.di.ServiceLocator import ai.elimu.chat.util.Constants import ai.elimu.chat.util.VersionHelper import android.app.Application -import android.preference.PreferenceManager import android.util.Log import androidx.core.content.edit @@ -17,25 +16,32 @@ class ChatApplication : Application() { Log.i(javaClass.getName(), "onCreate") super.onCreate() ServiceLocator.initialize(this) + checkAndUpdateVersionCode() + } + private fun checkAndUpdateVersionCode() { // Check if the application's versionCode was upgraded - val sharedPreferences = - PreferenceManager.getDefaultSharedPreferences(applicationContext) - var oldVersionCode = sharedPreferences.getInt(Constants.PREF_APP_VERSION_CODE, 0) + val sharedPreferences = ServiceLocator.provideSharedPreference() + val oldVersionCode = sharedPreferences.getInt(Constants.PREF_APP_VERSION_CODE, 0) val newVersionCode = VersionHelper.getAppVersionCode(applicationContext) - if (oldVersionCode == 0) { - sharedPreferences.edit(commit = true) { putInt(Constants.PREF_APP_VERSION_CODE, newVersionCode) } - oldVersionCode = newVersionCode - } if (oldVersionCode < newVersionCode) { Log.i( javaClass.getName(), "Upgrading application from version $oldVersionCode to $newVersionCode" ) - // if (newVersionCode == ???) { -// // Put relevant tasks required for upgrading here -// } - sharedPreferences.edit(commit = true) { putInt(Constants.PREF_APP_VERSION_CODE, newVersionCode) } + sharedPreferences.edit(commit = true) { + putInt( + Constants.PREF_APP_VERSION_CODE, + newVersionCode + ) + } + } else if (oldVersionCode == 0) { + sharedPreferences.edit(commit = true) { + putInt( + Constants.PREF_APP_VERSION_CODE, + newVersionCode + ) + } } } } diff --git a/app/src/main/java/ai/elimu/chat/model/Message.kt b/app/src/main/java/ai/elimu/chat/model/Message.kt index 3f0782e..2fb1680 100644 --- a/app/src/main/java/ai/elimu/chat/model/Message.kt +++ b/app/src/main/java/ai/elimu/chat/model/Message.kt @@ -1,6 +1,6 @@ package ai.elimu.chat.model -import ai.elimu.chat.util.getRandomEmoji +import ai.elimu.chat.util.Constants.EMOJI_UNICODES import java.util.Calendar data class Message( @@ -31,4 +31,17 @@ fun generateEmojiMessage(studentId: String): Message { message.studentId(studentId) message.message(getRandomEmoji()) return message.build() +} + +private fun getRandomEmoji(): String { + val randomIndex = (Math.random() * EMOJI_UNICODES.size).toInt() + val unicode = EMOJI_UNICODES[randomIndex] + + /** + * See http://apps.timwhitlock.info/emoji/tables/unicode + * @param unicode Example: "U+1F601" --> "0x1F601" + * @return + */ + val emoji = String(Character.toChars(unicode)) + return emoji } \ No newline at end of file diff --git a/app/src/main/java/ai/elimu/chat/util/Utils.kt b/app/src/main/java/ai/elimu/chat/util/Utils.kt deleted file mode 100644 index 190dd6b..0000000 --- a/app/src/main/java/ai/elimu/chat/util/Utils.kt +++ /dev/null @@ -1,16 +0,0 @@ -package ai.elimu.chat.util - -import ai.elimu.chat.util.Constants.EMOJI_UNICODES - -fun getRandomEmoji(): String { - val randomIndex = (Math.random() * EMOJI_UNICODES.size).toInt() - val unicode = EMOJI_UNICODES[randomIndex] - - /** - * See http://apps.timwhitlock.info/emoji/tables/unicode - * @param unicode Example: "U+1F601" --> "0x1F601" - * @return - */ - val emoji = String(Character.toChars(unicode)) - return emoji -} \ No newline at end of file From 2047cd8869f6aa9e842b153f1d2fd131b4987bf9 Mon Sep 17 00:00:00 2001 From: kiranbollepalli9 Date: Thu, 4 Sep 2025 20:24:21 +0530 Subject: [PATCH 10/12] Refactor: - Reverted HILT Di, as there is an issue with the library(2.57.1). The fix for it is still not merged but waiting for new release. --- app/build.gradle | 4 --- .../java/ai/elimu/chat/ChatApplication.kt | 2 -- .../java/ai/elimu/chat/di/DatabaseModule.kt | 26 ------------------- .../main/java/ai/elimu/chat/model/Message.kt | 8 ++++-- .../java/ai/elimu/chat/ui/ChatActivity.kt | 2 ++ gradle/libs.versions.toml | 8 +----- 6 files changed, 9 insertions(+), 41 deletions(-) delete mode 100644 app/src/main/java/ai/elimu/chat/di/DatabaseModule.kt diff --git a/app/build.gradle b/app/build.gradle index 0e1af71..b4c9908 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -7,7 +7,6 @@ buildscript { plugins { alias(libs.plugins.kotlin.android) alias(libs.plugins.kotlin.kapt) - //alias(libs.plugins.dagger.hilt.android) } import java.util.regex.Matcher import java.util.regex.Pattern @@ -65,9 +64,6 @@ dependencies { implementation libs.room.ktx kapt libs.room.compiler - //implementation libs.hilt.android - // kapt libs.hilt.android.compiler - implementation libs.javapoet testImplementation libs.junit diff --git a/app/src/main/java/ai/elimu/chat/ChatApplication.kt b/app/src/main/java/ai/elimu/chat/ChatApplication.kt index a14837e..24201c5 100644 --- a/app/src/main/java/ai/elimu/chat/ChatApplication.kt +++ b/app/src/main/java/ai/elimu/chat/ChatApplication.kt @@ -8,10 +8,8 @@ import android.app.Application import android.util.Log import androidx.core.content.edit -//@HiltAndroidApp class ChatApplication : Application() { - override fun onCreate() { Log.i(javaClass.getName(), "onCreate") super.onCreate() diff --git a/app/src/main/java/ai/elimu/chat/di/DatabaseModule.kt b/app/src/main/java/ai/elimu/chat/di/DatabaseModule.kt deleted file mode 100644 index d0ec57d..0000000 --- a/app/src/main/java/ai/elimu/chat/di/DatabaseModule.kt +++ /dev/null @@ -1,26 +0,0 @@ -package ai.elimu.chat.di - -//import ai.elimu.chat.data.local.AppDatabase -//import ai.elimu.chat.data.local.ChatMessageDao -//import android.content.Context -//import androidx.room.Room -//import dagger.Module -//import dagger.Provides -//import dagger.hilt.InstallIn -//import dagger.hilt.android.qualifiers.ApplicationContext -//import dagger.hilt.components.SingletonComponent -//import javax.inject.Singleton -// -//@Module -//@InstallIn(SingletonComponent::class) -//object DatabaseModule { -// -// @Provides -// @Singleton -// fun provideDatabase(@ApplicationContext appContext: Context): AppDatabase = -// Room.databaseBuilder(appContext, AppDatabase::class.java, "chat-db").build() -// -// @Provides -// fun provideMessageDao(db: AppDatabase): ChatMessageDao = db.messageDao() -// -//} \ No newline at end of file diff --git a/app/src/main/java/ai/elimu/chat/model/Message.kt b/app/src/main/java/ai/elimu/chat/model/Message.kt index 2fb1680..ae9c54f 100644 --- a/app/src/main/java/ai/elimu/chat/model/Message.kt +++ b/app/src/main/java/ai/elimu/chat/model/Message.kt @@ -4,8 +4,12 @@ import ai.elimu.chat.util.Constants.EMOJI_UNICODES import java.util.Calendar data class Message( - val id: Long, val deviceId: String, val studentId: String?, val studentAvatar: String?, - val timeSent: Calendar, val text: String + val id: Long, + val deviceId: String, + val studentId: String?, + val studentAvatar: String?, + val timeSent: Calendar, + val text: String ) class MessageBuilder { diff --git a/app/src/main/java/ai/elimu/chat/ui/ChatActivity.kt b/app/src/main/java/ai/elimu/chat/ui/ChatActivity.kt index 0c04fe0..2ca69e4 100644 --- a/app/src/main/java/ai/elimu/chat/ui/ChatActivity.kt +++ b/app/src/main/java/ai/elimu/chat/ui/ChatActivity.kt @@ -3,6 +3,7 @@ package ai.elimu.chat.ui import ai.elimu.chat.R import ai.elimu.chat.model.Message import ai.elimu.chat.util.Constants +import android.annotation.SuppressLint import android.os.Bundle import android.text.Editable import android.text.TextUtils @@ -17,6 +18,7 @@ import androidx.core.app.ComponentActivity import androidx.lifecycle.lifecycleScope import kotlinx.coroutines.launch +@SuppressLint("RestrictedApi") class ChatActivity : ComponentActivity() { private var messages: MutableList = mutableListOf() diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index df671a6..a94e43a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -11,7 +11,6 @@ coreKtx = "1.16.0" activityKtx="1.10.1" kotlin = "2.2.0" room = "2.7.2" -hilt = "2.57.1" [libraries] elimu-content-provider = { group = "ai.elimu", name = "content-provider", version.ref = "elimuContentProvider" } @@ -25,10 +24,6 @@ room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = " room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" } room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" } -#hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" } -#hilt-android-compiler = { group = "com.google.dagger", name = "hilt-android-compiler", version.ref = "hilt" } - - junit = { group = "junit", name = "junit", version.ref = "junit" } androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } @@ -37,5 +32,4 @@ androidx-activity-ktx = { group = "androidx.activity", name = "activity-ktx", ve [plugins] kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } -kotlin-kapt = { id = "org.jetbrains.kotlin.kapt", version.ref = "kotlin" } -#dagger-hilt-android = { id = "com.google.dagger.hilt.android", version.ref = "hilt" } \ No newline at end of file +kotlin-kapt = { id = "org.jetbrains.kotlin.kapt", version.ref = "kotlin" } \ No newline at end of file From 343424895abd661b2fedfce512f72416bac0b2d2 Mon Sep 17 00:00:00 2001 From: kiranbollepalli9 Date: Thu, 4 Sep 2025 20:48:47 +0530 Subject: [PATCH 11/12] Refactor: - Minor enhancements --- .../elimu/chat/receiver/StudentUpdateReceiver.kt | 8 ++++---- .../ai/elimu/chat/ui/MessageListArrayAdapter.kt | 2 +- app/src/main/java/ai/elimu/chat/util/Constants.kt | 14 ++++++-------- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/ai/elimu/chat/receiver/StudentUpdateReceiver.kt b/app/src/main/java/ai/elimu/chat/receiver/StudentUpdateReceiver.kt index 571c5e7..bb2ce82 100644 --- a/app/src/main/java/ai/elimu/chat/receiver/StudentUpdateReceiver.kt +++ b/app/src/main/java/ai/elimu/chat/receiver/StudentUpdateReceiver.kt @@ -23,8 +23,8 @@ class StudentUpdateReceiver : BroadcastReceiver() { if (!TextUtils.isEmpty(studentId)) { val existingStudentId = sharedPreferences.getString(Constants.PREF_STUDENT_ID, null) Log.i(javaClass.getName(), "existingStudentId: $existingStudentId") - if (TextUtils.isEmpty(existingStudentId)) { - /* // Update previously sent messages on the current device // TODO: Migrate to room + /* if (TextUtils.isEmpty(existingStudentId)) { + // Update previously sent messages on the current device // TODO: Migrate to room val chatApplication = context.applicationContext as ChatApplication val messageDao = chatApplication.daoSession!!.messageDao val existingMessages = messageDao.queryBuilder() @@ -38,8 +38,8 @@ class StudentUpdateReceiver : BroadcastReceiver() { message.studentId = studentId message.studentAvatar = studentAvatar messageDao.update(message) - }*/ - } + } + }*/ sharedPreferences.edit(commit = true) { putString( diff --git a/app/src/main/java/ai/elimu/chat/ui/MessageListArrayAdapter.kt b/app/src/main/java/ai/elimu/chat/ui/MessageListArrayAdapter.kt index 03163cf..83970e7 100644 --- a/app/src/main/java/ai/elimu/chat/ui/MessageListArrayAdapter.kt +++ b/app/src/main/java/ai/elimu/chat/ui/MessageListArrayAdapter.kt @@ -43,7 +43,7 @@ class MessageListArrayAdapter(context: Context, messages: List) : val message = messages[position] - var listItem: View? = null + var listItem: View? val layoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater diff --git a/app/src/main/java/ai/elimu/chat/util/Constants.kt b/app/src/main/java/ai/elimu/chat/util/Constants.kt index d199001..21bd5cd 100644 --- a/app/src/main/java/ai/elimu/chat/util/Constants.kt +++ b/app/src/main/java/ai/elimu/chat/util/Constants.kt @@ -1,20 +1,18 @@ package ai.elimu.chat.util -import android.util.Log - object Constants { - val PREF_FILE_NAME: String = "pref_app_001" + const val PREF_FILE_NAME: String = "pref_app_001" - val PREF_APP_VERSION_CODE: String = "pref_app_version_code" + const val PREF_APP_VERSION_CODE: String = "pref_app_version_code" - val PREF_STUDENT_ID: String = "pref_student_id" + const val PREF_STUDENT_ID: String = "pref_student_id" - val PREF_STUDENT_AVATAR: String = "pref_student_avatar" + const val PREF_STUDENT_AVATAR: String = "pref_student_avatar" - val STUDENTID_AKILI = "00000000aaaaaaaa_1" + const val STUDENTID_AKILI = "00000000aaaaaaaa_1" - val STUDENTID_PENGUIN = "00000000aaaaaaaa_2" + const val STUDENTID_PENGUIN = "00000000aaaaaaaa_2" val EMOJI_UNICODES = intArrayOf( // Emoticons From 0b490a4acd840fabff525d4a2b8a666545189216 Mon Sep 17 00:00:00 2001 From: Kiran Bollepalli <34787464+kiranbollepalli9@users.noreply.github.com> Date: Thu, 4 Sep 2025 21:28:20 +0530 Subject: [PATCH 12/12] Delete .github/.config --- .github/.config | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 .github/.config diff --git a/.github/.config b/.github/.config deleted file mode 100644 index 81d58d3..0000000 --- a/.github/.config +++ /dev/null @@ -1,2 +0,0 @@ -git config user.name "Kiran" -git config user.email "kiranbollepalli@gmail.com" \ No newline at end of file