diff --git a/Android/app/build.gradle b/Android/app/build.gradle index 77fcacdddaadf0ca4ece00a89c72a15c6de6cf7a..579d8d3e2ed8fa142da303a8a2a3c117a67ba669 100644 --- a/Android/app/build.gradle +++ b/Android/app/build.gradle @@ -1,8 +1,8 @@ plugins { id 'com.android.application' - id 'kotlin-android' id 'com.google.gms.google-services' id 'kotlin-kapt' + id 'kotlin-android' } android { @@ -13,19 +13,18 @@ android { applicationId "com.matthaigh27.chatgptwrapper" minSdk 28 targetSdk 33 - versionCode 7 - versionName "1.5" + versionCode 1 + versionName "1.6" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { debug { - buildConfigField "String", "BASE_URL", "\"https://smartphone.herokuapp.com/\"" + buildConfigField "String", "BASE_URL", "\"https://ttt246-brain.hf.space/\"" } release { - // Use your desired server address for the release version - buildConfigField "String", "BASE_URL", "\"https://chatgptphone.herokuapp.com/\"" + buildConfigField "String", "BASE_URL", "\"https://ttt246-brain.hf.space/\"" minifyEnabled true shrinkResources true @@ -41,6 +40,7 @@ android { sourceCompatibility JavaVersion.VERSION_11 targetCompatibility JavaVersion.VERSION_11 } + kotlinOptions { jvmTarget = JavaVersion.VERSION_11 } @@ -51,63 +51,73 @@ android { } dependencies { - // App dependencies + //Core implementation 'androidx.core:core-ktx:1.9.0' + implementation 'com.google.code.gson:gson:2.8.5' + + //UI & UX implementation 'androidx.appcompat:appcompat:1.6.0' implementation 'com.google.android.material:material:1.8.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' - - implementation platform('com.google.firebase:firebase-bom:31.4.0') - implementation 'com.google.android.gms:play-services-gcm:17.0.0' - - implementation 'com.google.firebase:firebase-messaging' - implementation 'com.google.firebase:firebase-analytics' - implementation 'com.google.firebase:firebase-firestore-ktx:24.4.5' - implementation 'com.google.firebase:firebase-firestore:15.0.0' - - implementation 'com.google.firebase:firebase-messaging-ktx' - implementation 'com.google.firebase:firebase-analytics-ktx' - implementation 'com.firebaseui:firebase-ui-storage:7.2.0' - - implementation 'com.squareup.okhttp3:okhttp:3.0.1' + implementation 'de.hdodenhof:circleimageview:3.1.0' implementation 'com.github.soulqw:CoCo:1.1.2' implementation 'com.github.dhaval2404:imagepicker:2.1' - implementation 'com.google.firebase:firebase-storage-ktx:20.1.0' + implementation 'com.github.bumptech.glide:glide:4.12.0' + annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0' + testImplementation 'org.testng:testng:6.9.6' // Testing-only dependencies - androidTestImplementation 'androidx.test:core:' + rootProject.coreVersion; - androidTestImplementation 'androidx.test.ext:junit:' + rootProject.extJUnitVersion; - androidTestImplementation 'androidx.test:runner:' + rootProject.runnerVersion; + androidTestImplementation "androidx.test:core:$rootProject.coreVersion" + androidTestImplementation "androidx.test.ext:junit:$rootProject.extJUnitVersion" + androidTestImplementation "androidx.test:runner:$rootProject.runnerVersion" testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' // UiAutomator Testing - androidTestImplementation 'androidx.test.uiautomator:uiautomator:' + rootProject.uiAutomatorVersion; + androidTestImplementation "androidx.test.uiautomator:uiautomator:$rootProject.uiAutomatorVersion" androidTestImplementation 'org.hamcrest:hamcrest-integration:1.3' implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.2" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.2" implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0-alpha05" - implementation 'com.github.bumptech.glide:glide:4.12.0' - annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0' + //Firebase + implementation platform('com.google.firebase:firebase-bom:31.4.0') + implementation 'com.google.android.gms:play-services-gcm:17.0.0' - implementation 'com.google.code.gson:gson:2.8.5' + implementation 'com.google.firebase:firebase-messaging' + implementation 'com.google.firebase:firebase-analytics' + implementation 'com.google.firebase:firebase-firestore-ktx:24.4.5' + implementation 'com.google.firebase:firebase-firestore:15.0.0' + implementation 'com.google.firebase:firebase-storage-ktx:20.1.0' + implementation 'com.google.firebase:firebase-messaging-ktx' + implementation 'com.google.firebase:firebase-analytics-ktx' + implementation 'com.firebaseui:firebase-ui-storage:7.2.0' - implementation "androidx.room:room-runtime:$room_version" - annotationProcessor "androidx.room:room-compiler:$room_version" + implementation "androidx.room:room-runtime:$rootProject.roomVersion" + annotationProcessor "androidx.room:room-compiler:$rootProject.roomVersion" // To use room database - implementation "androidx.room:room-ktx:$room_version" - kapt "androidx.room:room-compiler:$room_version" - implementation "androidx.room:room-rxjava2:$room_version" - implementation "androidx.room:room-rxjava3:$room_version" - implementation "androidx.room:room-guava:$room_version" - testImplementation "androidx.room:room-testing:$room_version" - implementation "androidx.room:room-paging:$room_version" + implementation "androidx.room:room-ktx:$rootProject.roomVersion" + kapt "androidx.room:room-compiler:$rootProject.roomVersion" + implementation "androidx.room:room-rxjava2:$rootProject.roomVersion" + implementation "androidx.room:room-rxjava3:$rootProject.roomVersion" + implementation "androidx.room:room-guava:$rootProject.roomVersion" + testImplementation "androidx.room:room-testing:$rootProject.roomVersion" + implementation "androidx.room:room-paging:$rootProject.roomVersion" - implementation 'de.hdodenhof:circleimageview:3.1.0' + //To use Retrofit + + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$rootProject.lifecycleVersion" + implementation "androidx.lifecycle:lifecycle-livedata-ktx:$rootProject.lifecycleVersion" + + implementation "com.squareup.retrofit2:retrofit:$rootProject.retrofitVersion" + implementation "com.squareup.retrofit2:converter-gson:$rootProject.retrofitVersion" + + implementation "com.squareup.okhttp3:okhttp:$rootProject.okHttpVersion" + implementation "com.squareup.okhttp3:logging-interceptor:$rootProject.okHttpVersion" + implementation "com.squareup.okhttp3:okhttp-urlconnection:$rootProject.okHttpVersion" } diff --git a/Android/app/src/main/AndroidManifest.xml b/Android/app/src/main/AndroidManifest.xml index 1084fe2e7f87fcb9dbb470065ec30cc2f390558b..ba0f495f6065fcca8608f5ce7ff5d4a94241389a 100644 --- a/Android/app/src/main/AndroidManifest.xml +++ b/Android/app/src/main/AndroidManifest.xml @@ -9,40 +9,45 @@ - - + + - - + + + + android:theme="@style/AppTheme"> + android:name=".ui.setting.view.SettingActivity" + android:exported="false" /> + + - - - - - + \ No newline at end of file diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/MyApplication.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/MyApplication.kt deleted file mode 100644 index f34f8430b9e8ffb9d4eeb875d75191e08826d65f..0000000000000000000000000000000000000000 --- a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/MyApplication.kt +++ /dev/null @@ -1,105 +0,0 @@ -package com.matthaigh27.chatgptwrapper - -import android.Manifest -import android.annotation.SuppressLint -import android.app.Application -import android.app.NotificationChannel -import android.app.NotificationManager -import android.content.pm.PackageManager -import android.util.Log -import com.google.android.gms.tasks.OnCompleteListener -import com.google.firebase.messaging.FirebaseMessaging -import com.matthaigh27.chatgptwrapper.utils.Constants -import android.provider.Settings -import androidx.core.app.ActivityCompat -import androidx.core.app.NotificationCompat -import androidx.core.app.NotificationManagerCompat -import java.util.Random - -class MyApplication : Application() { - - private var mFCMToken: String = String() - private var mUUID: String = String() - @SuppressLint("HardwareIds") - override fun onCreate() { - super.onCreate() - - initToken() - // on below line we are getting device id. - mUUID = Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID) - appContext = applicationContext as MyApplication - - Log.v("risingandroid mUUID: ", mUUID) - Log.v("risingandroid FCMToken: ", mFCMToken) - } - - private fun initToken() { - FirebaseMessaging.getInstance().token.addOnCompleteListener(OnCompleteListener { task -> - if (!task.isSuccessful) { - Log.w(Constants.TAG, "Fetching FCM registration token failed", task.exception) - return@OnCompleteListener - } - - /** - * Get new FCM registration token - */ - - /** - * Get new FCM registration token - */ - mFCMToken = task.result - Log.d(Constants.TAG, mFCMToken) - }) - } - - fun getFCMToken(): String { - return this.mFCMToken - } - - fun getUUID(): String { - return this.mUUID - } - - /** - * this shows system notification with message - * @param message to be shown with system notification - */ - fun showNotification(message: String) { - val notificationId: Int = Random().nextInt() - val channelId = "chat_message" - - val builder = NotificationCompat.Builder(this, channelId) - builder.setSmallIcon(R.drawable.ic_notification) - builder.setContentTitle(Constants.TAG) - builder.setContentText(message) - builder.setStyle( - NotificationCompat.BigTextStyle().bigText( - message - ) - ) - builder.priority = NotificationCompat.PRIORITY_DEFAULT - builder.setAutoCancel(true) - - val channelName: CharSequence = "Chat Message" - val channelDescription = "This notification channel is used for chat message notifications" - val importance = NotificationManager.IMPORTANCE_DEFAULT - val channel = NotificationChannel(channelId, channelName, importance) - channel.description = channelDescription - val notificationManager = getSystemService( - NotificationManager::class.java - ) - notificationManager.createNotificationChannel(channel) - val notificationManagerCompat = NotificationManagerCompat.from(this) - if (ActivityCompat.checkSelfPermission( - this, Manifest.permission.POST_NOTIFICATIONS - ) != PackageManager.PERMISSION_GRANTED - ) { - return - } - notificationManagerCompat.notify(notificationId, builder.build()) - } - - companion object { - lateinit var appContext: MyApplication - } -} \ No newline at end of file diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/RisingApplication.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/RisingApplication.kt new file mode 100644 index 0000000000000000000000000000000000000000..b5910f63aa2d54c3141a59bb90f9626ed8c3d5d0 --- /dev/null +++ b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/RisingApplication.kt @@ -0,0 +1,45 @@ +package com.matthaigh27.chatgptwrapper + +import android.annotation.SuppressLint +import android.app.Application +import android.provider.Settings +import com.google.android.gms.tasks.OnCompleteListener +import com.google.firebase.messaging.FirebaseMessaging + +class RisingApplication : Application() { + + private var fcmToken: String = String() + private var uuid: String = String() + + @SuppressLint("HardwareIds") + override fun onCreate() { + super.onCreate() + + initToken() + // on below line we are getting device id. + uuid = Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID) + appContext = applicationContext as RisingApplication + } + + private fun initToken() { + FirebaseMessaging.getInstance().token.addOnCompleteListener(OnCompleteListener { task -> + if (!task.isSuccessful) { + return@OnCompleteListener + } + + fcmToken = task.result + }) + } + + fun getFCMToken(): String { + return this.fcmToken + } + + fun getUUID(): String { + return this.uuid + } + + companion object { + lateinit var appContext: RisingApplication + } +} \ No newline at end of file diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/adapters/ChatAdapter.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/adapters/ChatAdapter.kt deleted file mode 100644 index ee08db3bda773aa3f1b540ea6ff27b177df8c522..0000000000000000000000000000000000000000 --- a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/adapters/ChatAdapter.kt +++ /dev/null @@ -1,337 +0,0 @@ -package com.matthaigh27.chatgptwrapper.adapters - -import android.annotation.SuppressLint -import android.app.Dialog -import android.content.Context -import android.graphics.Bitmap -import android.graphics.BitmapFactory -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.FrameLayout -import android.widget.ImageView -import android.widget.LinearLayout -import android.widget.TextView -import androidx.constraintlayout.widget.ConstraintLayout -import androidx.recyclerview.widget.RecyclerView -import com.matthaigh27.chatgptwrapper.R -import com.matthaigh27.chatgptwrapper.models.common.ContactModel -import com.matthaigh27.chatgptwrapper.models.common.HelpPromptModel -import com.matthaigh27.chatgptwrapper.models.viewmodels.ChatMessageModel -import com.matthaigh27.chatgptwrapper.utils.Constants.MSG_WIDGET_TYPE_HELP_PRMOPT -import com.matthaigh27.chatgptwrapper.utils.Constants.MSG_WIDGET_TYPE_SEARCH_CONTACT -import com.matthaigh27.chatgptwrapper.utils.Constants.MSG_WIDGET_TYPE_SMS -import com.matthaigh27.chatgptwrapper.utils.ImageHelper -import com.matthaigh27.chatgptwrapper.utils.Utils -import com.matthaigh27.chatgptwrapper.widgets.ContactDetailItem -import com.matthaigh27.chatgptwrapper.widgets.ContactDetailWidget -import com.matthaigh27.chatgptwrapper.widgets.HelpPromptWidget -import com.matthaigh27.chatgptwrapper.widgets.SearchContactWidget -import com.matthaigh27.chatgptwrapper.widgets.SmsEditorWidget -import org.json.JSONArray - -class ChatAdapter(list: ArrayList, context: Context) : - RecyclerView.Adapter() { - - private var mChatModelList: ArrayList = ArrayList() - private var mContext: Context - - private var mListener: MessageWidgetListener? = null - var mOnSMSClickListener: ContactDetailItem.OnSMSClickListener? = null - - private val feedbackData = arrayOf( - arrayOf(R.drawable.ic_thumb_up_disable, R.drawable.ic_thumb_down), - arrayOf(R.drawable.ic_thumb_up_disable, R.drawable.ic_thumb_down_disable), - arrayOf(R.drawable.ic_thumb_up, R.drawable.ic_thumb_down_disable), - ) - - init { - mChatModelList = list - mContext = context - } - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { - val context = parent.context - val inflater = LayoutInflater.from(context) - - /** - * Inflate the custom layout and Return a new holder instance - */ - return if (viewType == 0) { - SendMessageViewHolder( - inflater.inflate( - R.layout.item_container_sent_message, parent, false - ) - ) - } else { - ReceiveMessageViewHolder( - inflater.inflate( - R.layout.item_container_received_message, parent, false - ) - ) - } - } - - /** - * Involves populating data into the item through holder - */ - override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { - /** - * Get the data model based on position - */ - val index = holder.adapterPosition - val messageModel: ChatMessageModel = mChatModelList[index] - if (messageModel.isMe) { - setSentMessageData(holder as SendMessageViewHolder, messageModel) - } else { - setReceiveMessageData(holder as ReceiveMessageViewHolder, messageModel) - } - } - - private fun setSentMessageData(holder: SendMessageViewHolder, messageModel: ChatMessageModel) { - /** - * Set item views based on your views and data model - */ - if (messageModel.message.isEmpty()) { - holder.textMessage.visibility = View.GONE - } else { - holder.textMessage.text = messageModel.message - holder.textMessage.visibility = View.VISIBLE - } - - - if (messageModel.image != null) { - val radius = mContext.resources.getDimensionPixelSize(R.dimen.chat_message_item_radius) - - val originBmp = BitmapFactory.decodeByteArray(messageModel.image, 0, messageModel.image!!.size) - val bmp = ImageHelper.getRoundedCornerBitmap(originBmp, radius) - holder.imgMessage.visibility = View.VISIBLE - holder.imgMessage.setImageBitmap(bmp) - holder.imgMessage.setOnClickListener { - onImageClick(originBmp) - } - } else { - holder.imgMessage.visibility = View.GONE - } - - if (messageModel.isWidget) { - when (messageModel.widgetType) { - MSG_WIDGET_TYPE_HELP_PRMOPT -> { - val model: HelpPromptModel = - HelpPromptModel.initModelWithString(messageModel.widgetDescription) - val helpPromptWidget = HelpPromptWidget(mContext, model) - val helpPromptListener = object : HelpPromptWidget.OnHelpPromptListener { - override fun onSuccess(prompt: String) { - mChatModelList[holder.adapterPosition].isWidget = false - holder.llMessageWidget.visibility = View.GONE - holder.llMessageWidget.removeAllViews() - mListener!!.sentHelpPrompt(prompt) - } - - override fun onCancel() { - mChatModelList[holder.adapterPosition].isWidget = false - holder.llMessageWidget.visibility = View.GONE - holder.llMessageWidget.removeAllViews() - mListener!!.canceledHelpPrompt() - } - } - helpPromptWidget.setOnClickListener(helpPromptListener) - holder.llMessageWidget.addView(helpPromptWidget) - holder.llMessageWidget.visibility = View.VISIBLE - } - } - } else { - holder.llMessageWidget.visibility = View.GONE - } - } - - @SuppressLint("UseCompatLoadingForDrawables") - fun setReceiveMessageData(holder: ReceiveMessageViewHolder, messageModel: ChatMessageModel) { - /** - * Set item views based on your views and data model - */ - if (messageModel.message.isEmpty()) { - holder.textMessage.visibility = View.GONE - } else { - holder.textMessage.text = messageModel.message - holder.textMessage.visibility = View.VISIBLE - } - - - if (messageModel.image != null) { - val radius = mContext.resources.getDimensionPixelSize(R.dimen.chat_message_item_radius) - - val originBmp = BitmapFactory.decodeByteArray(messageModel.image, 0, messageModel.image!!.size) - val bmp = ImageHelper.getRoundedCornerBitmap(originBmp, radius) - holder.imgMessage.visibility = View.VISIBLE - holder.imgMessage.setImageBitmap(bmp) - holder.imgMessage.setOnClickListener { - onImageClick(originBmp) - } - } else { - holder.imgMessage.visibility = View.GONE - } - - holder.llFeedback.visibility = if (messageModel.visibleFeedback) { - View.VISIBLE - } else { - View.GONE - } - - setThumb(holder) - - holder.itemLayout.setOnLongClickListener { - if (holder.llFeedback.visibility == View.VISIBLE) { - holder.llFeedback.visibility = View.GONE - mChatModelList[holder.adapterPosition].visibleFeedback = false - } else { - holder.llFeedback.visibility = View.VISIBLE - mChatModelList[holder.adapterPosition].visibleFeedback = true - } - return@setOnLongClickListener true - } - - holder.btnThumbUp.setOnClickListener { - mChatModelList[holder.adapterPosition].feedback = 1 - setThumb(holder) - - } - - holder.btnThumbDown.setOnClickListener { - mChatModelList[holder.adapterPosition].feedback = -1 - setThumb(holder) - } - - if (messageModel.isWidget) { - holder.llContactWidget.removeAllViews() - when (messageModel.widgetType) { - MSG_WIDGET_TYPE_SMS -> { - val smsWidget = SmsEditorWidget(mContext, null) - if(messageModel.widgetDescription.isNotEmpty()) { - smsWidget.setToUserName(messageModel.widgetDescription) - } - holder.llMessageWidget.addView(smsWidget) - holder.llMessageWidget.visibility = View.VISIBLE - - val smsListener = object : SmsEditorWidget.OnClickListener { - override fun confirmSMS(phonenumber: String, message: String) { - mChatModelList[holder.adapterPosition].isWidget = false - holder.llMessageWidget.visibility = View.GONE - holder.llMessageWidget.removeAllViews() - mListener!!.sentSMS(phonenumber, message) - } - - override fun cancelSMS() { - mChatModelList[holder.adapterPosition].isWidget = false - holder.llMessageWidget.visibility = View.GONE - holder.llMessageWidget.removeAllViews() - mListener!!.canceledSMS() - } - } - - smsWidget.setOnClickListener(smsListener) - } - - MSG_WIDGET_TYPE_SEARCH_CONTACT -> { - val contacts = Utils.instance.getContacts(mContext) - - val contactIds = JSONArray(messageModel.widgetDescription) - for (i in 0 until contactIds.length()) { - val contactId = contactIds[i].toString() - val contact = Utils.instance.getContactModelById(contactId, contacts) - - val searchContactWidget = SearchContactWidget(mContext, contact, null) - searchContactWidget.mSMSOnClickListener = mOnSMSClickListener - holder.llContactWidget.addView(searchContactWidget) - } - holder.llContactWidget.visibility = View.VISIBLE - } - } - } else { - holder.llMessageWidget.visibility = View.GONE - holder.llContactWidget.visibility = View.GONE - } - } - - @SuppressLint("UseCompatLoadingForDrawables") - fun setThumb(holder: ReceiveMessageViewHolder) { - holder.btnThumbUp.setImageDrawable( - mContext.getDrawable( - feedbackData[mChatModelList[holder.adapterPosition].feedback + 1][0] - ) - ) - holder.btnThumbDown.setImageDrawable( - mContext.getDrawable( - feedbackData[mChatModelList[holder.adapterPosition].feedback + 1][1] - ) - ) - } - - /** - * Returns the total count of items in the list - */ - override fun getItemCount(): Int { - return mChatModelList.size - } - - override fun getItemViewType(position: Int): Int { - return if (mChatModelList[position].isMe) 0 else 1 - } - - private fun onImageClick(bitmap: Bitmap) { - val dialog = Dialog(mContext) - dialog.setContentView(R.layout.view_full_image) - val fullImage = dialog.findViewById(R.id.fullImage) as ImageView - fullImage.setImageBitmap(bitmap) - dialog.show() - } - - inner class ReceiveMessageViewHolder internal constructor(itemView: View) : - RecyclerView.ViewHolder(itemView) { - var textMessage: TextView - var imgMessage: ImageView - var llFeedback: LinearLayout - var btnThumbUp: ImageView - var btnThumbDown: ImageView - var itemLayout: ConstraintLayout - var llMessageWidget: LinearLayout - var llContactWidget: LinearLayout - - init { - textMessage = itemView.findViewById(R.id.textMessage) as TextView - imgMessage = itemView.findViewById(R.id.imgMessage) as ImageView - btnThumbUp = itemView.findViewById(R.id.btn_thumb_up) as ImageView - btnThumbDown = itemView.findViewById(R.id.btn_thumb_down) as ImageView - llFeedback = itemView.findViewById(R.id.ll_feedback) as LinearLayout - itemLayout = itemView.findViewById(R.id.cl_receive_message) as ConstraintLayout - llMessageWidget = itemView.findViewById(R.id.ll_message_widget) as LinearLayout - llContactWidget = itemView.findViewById(R.id.ll_contacts_widget) as LinearLayout - } - } - - inner class SendMessageViewHolder internal constructor(itemView: View) : - RecyclerView.ViewHolder(itemView) { - var textMessage: TextView - var imgMessage: ImageView - var itemLayout: ConstraintLayout - var llMessageWidget: LinearLayout - - init { - textMessage = itemView.findViewById(R.id.textMessage) as TextView - imgMessage = itemView.findViewById(R.id.imgMessage) as ImageView - itemLayout = itemView.findViewById(R.id.cl_sent_message) as ConstraintLayout - llMessageWidget = itemView.findViewById(R.id.ll_message_widget) as LinearLayout - } - } - - interface MessageWidgetListener { - fun sentSMS(phonenumber: String, message: String) - fun canceledSMS() - fun sentHelpPrompt(prompt: String) - fun canceledHelpPrompt() - } - - fun setMessageWidgetListener(listener: MessageWidgetListener) { - mListener = listener - } -} \ No newline at end of file diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/database/MyDatabase.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/local/AppDatabase.kt similarity index 50% rename from Android/app/src/main/java/com/matthaigh27/chatgptwrapper/database/MyDatabase.kt rename to Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/local/AppDatabase.kt index 24767388367000054442350471abf4c4cfb497e1..4375a5fcfc2f857e295c21443947be3b0b5dc309 100644 --- a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/database/MyDatabase.kt +++ b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/local/AppDatabase.kt @@ -1,34 +1,36 @@ -package com.matthaigh27.chatgptwrapper.database +package com.matthaigh27.chatgptwrapper.data.local import android.content.Context import androidx.room.Database import androidx.room.Room import androidx.room.RoomDatabase -import com.matthaigh27.chatgptwrapper.database.dao.ContactDao -import com.matthaigh27.chatgptwrapper.database.dao.ImageDao -import com.matthaigh27.chatgptwrapper.database.entity.ContactEntity -import com.matthaigh27.chatgptwrapper.database.entity.ImageEntity +import com.matthaigh27.chatgptwrapper.data.local.dao.ContactDao +import com.matthaigh27.chatgptwrapper.data.local.dao.ImageDao +import com.matthaigh27.chatgptwrapper.data.local.entity.ContactEntity +import com.matthaigh27.chatgptwrapper.data.local.entity.ImageEntity @Database(entities = [ImageEntity::class, ContactEntity::class], version = 1, exportSchema = false) -abstract class MyDatabase : RoomDatabase() { +abstract class AppDatabase : RoomDatabase() { abstract fun imageDao(): ImageDao abstract fun contactDao(): ContactDao companion object { + private val DATABASE_NAME = "RisingPhone" + @Volatile - private var INSTANCE: MyDatabase? = null + private var INSTANCE: AppDatabase? = null - fun getDatabase(context: Context): MyDatabase { + fun getDatabase(context: Context): AppDatabase { val tempInstance = INSTANCE if (tempInstance != null) { return tempInstance } synchronized(this) { val instance = Room.databaseBuilder( - context.applicationContext, - MyDatabase::class.java, - "risingphone_database" + context = context.applicationContext, + klass = AppDatabase::class.java, + name = DATABASE_NAME ).build() INSTANCE = instance return instance diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/local/dao/ContactDao.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/local/dao/ContactDao.kt new file mode 100644 index 0000000000000000000000000000000000000000..9eb2010056fe52c67f60cb174ea3f2431a0d26a6 --- /dev/null +++ b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/local/dao/ContactDao.kt @@ -0,0 +1,20 @@ +package com.matthaigh27.chatgptwrapper.data.local.dao + +import androidx.lifecycle.MutableLiveData +import androidx.room.* +import com.matthaigh27.chatgptwrapper.data.local.entity.ContactEntity + +@Dao +interface ContactDao { + @Insert + fun insert(contact: ContactEntity) + + @Update + fun update(contact: ContactEntity) + + @Delete + fun delete(contact: ContactEntity) + + @Query("SELECT * FROM contacts") + fun getAllData(): List +} \ No newline at end of file diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/local/dao/ImageDao.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/local/dao/ImageDao.kt new file mode 100644 index 0000000000000000000000000000000000000000..a68090332134d04543ac8dcbd3e8eae5481c3264 --- /dev/null +++ b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/local/dao/ImageDao.kt @@ -0,0 +1,20 @@ +package com.matthaigh27.chatgptwrapper.data.local.dao + +import androidx.lifecycle.MutableLiveData +import androidx.room.* +import com.matthaigh27.chatgptwrapper.data.local.entity.ImageEntity + +@Dao +interface ImageDao { + @Insert + fun insert(image: ImageEntity) + + @Update + fun update(image: ImageEntity) + + @Delete + fun delete(image: ImageEntity) + + @Query("SELECT * FROM images") + fun getAllData(): List +} \ No newline at end of file diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/database/entity/ContactEntity.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/local/entity/ContactEntity.kt similarity index 79% rename from Android/app/src/main/java/com/matthaigh27/chatgptwrapper/database/entity/ContactEntity.kt rename to Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/local/entity/ContactEntity.kt index be6009712233de4e7746f62d19a3ae5c0ed20a98..481659826963d0ea9f95bb0c3b1d80c4c145e264 100644 --- a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/database/entity/ContactEntity.kt +++ b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/local/entity/ContactEntity.kt @@ -1,4 +1,4 @@ -package com.matthaigh27.chatgptwrapper.database.entity +package com.matthaigh27.chatgptwrapper.data.local.entity import androidx.room.Entity import androidx.room.PrimaryKey diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/database/entity/ImageEntity.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/local/entity/ImageEntity.kt similarity index 71% rename from Android/app/src/main/java/com/matthaigh27/chatgptwrapper/database/entity/ImageEntity.kt rename to Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/local/entity/ImageEntity.kt index 8734569a0faf073e90669393fb2cd5c83a6c0026..337599d26b55b2d6de8d338a75a570f8a5a0d3c0 100644 --- a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/database/entity/ImageEntity.kt +++ b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/local/entity/ImageEntity.kt @@ -1,4 +1,4 @@ -package com.matthaigh27.chatgptwrapper.database.entity +package com.matthaigh27.chatgptwrapper.data.local.entity import androidx.room.Entity import androidx.room.PrimaryKey @@ -8,4 +8,5 @@ data class ImageEntity ( @PrimaryKey(autoGenerate = true) val id: Int, val path: String, val name: String, + val dataModified: Long ) \ No newline at end of file diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/models/AlarmModel.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/models/AlarmModel.kt new file mode 100644 index 0000000000000000000000000000000000000000..8ffd6e9b0d9492f5431e0df363e42e550dfe8833 --- /dev/null +++ b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/models/AlarmModel.kt @@ -0,0 +1,3 @@ +package com.matthaigh27.chatgptwrapper.data.models + +data class AlarmModel(val id: Int, val time: Long, val enabled: Boolean, val label: String) \ No newline at end of file diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/models/ChatMessageModel.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/models/ChatMessageModel.kt new file mode 100644 index 0000000000000000000000000000000000000000..ce6fb97eb53df247050cf85163eddd3211b95e94 --- /dev/null +++ b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/models/ChatMessageModel.kt @@ -0,0 +1,36 @@ +package com.matthaigh27.chatgptwrapper.data.models + +import com.google.gson.JsonElement + +data class ChatMessageModel( + val type: Int, + val content: String? = null, + val data: JsonElement? = null, + val hasImage: Boolean = false, + val image: ByteArray? = null, +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as ChatMessageModel + + if (type != other.type) return false + if (content != other.content) return false + if (data != other.data) return false + if (image != null) { + if (other.image == null) return false + if (!image.contentEquals(other.image)) return false + } else if (other.image != null) return false + + return true + } + + override fun hashCode(): Int { + var result = type + result = 31 * result + content.hashCode() + result = 31 * result + (data?.hashCode() ?: 0) + result = 31 * result + (image?.contentHashCode() ?: 0) + return result + } +} diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/models/ContactModel.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/models/ContactModel.kt new file mode 100644 index 0000000000000000000000000000000000000000..37acc802af87eec9934b70770c16e8a8da2fff27 --- /dev/null +++ b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/models/ContactModel.kt @@ -0,0 +1,8 @@ +package com.matthaigh27.chatgptwrapper.data.models + +data class ContactModel( + var id: String = "", + var name: String = "", + var phoneList: ArrayList = ArrayList(), + var status: String = "" +) diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/models/HelpCommandModel.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/models/HelpCommandModel.kt new file mode 100644 index 0000000000000000000000000000000000000000..b6bd5d3dd5be36cd7044c7c65ba08dafe6a8be74 --- /dev/null +++ b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/models/HelpCommandModel.kt @@ -0,0 +1,6 @@ +package com.matthaigh27.chatgptwrapper.data.models + +data class HelpCommandModel ( + var main: String? = null, + var assist: String? = null +) \ No newline at end of file diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/models/HelpPromptModel.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/models/HelpPromptModel.kt new file mode 100644 index 0000000000000000000000000000000000000000..dfbf1fefd0526d3bda705a37e36fda5ab3b0dfa6 --- /dev/null +++ b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/models/HelpPromptModel.kt @@ -0,0 +1,8 @@ +package com.matthaigh27.chatgptwrapper.data.models + +data class HelpPromptModel( + var name: String = "", + var description: String = "", + var prompt: String = "", + var tags: ArrayList = ArrayList() +) diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/models/ImageModel.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/models/ImageModel.kt new file mode 100644 index 0000000000000000000000000000000000000000..c346042454201baea4d740ef0e6d638c743b724f --- /dev/null +++ b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/models/ImageModel.kt @@ -0,0 +1,8 @@ +package com.matthaigh27.chatgptwrapper.data.models + +import android.net.Uri + +data class ImageModel( + val uri: Uri, + val modifiedDate: Long +) diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/ApiClient.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/ApiClient.kt new file mode 100644 index 0000000000000000000000000000000000000000..f996199814238b7209885d74fde7021d9798ce4c --- /dev/null +++ b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/ApiClient.kt @@ -0,0 +1,25 @@ +package com.matthaigh27.chatgptwrapper.data.remote + +import com.matthaigh27.chatgptwrapper.utils.Constants.API_BASE_URL +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import java.util.concurrent.TimeUnit + +object ApiClient { + private val client = OkHttpClient.Builder() + .callTimeout(240, TimeUnit.SECONDS) + .connectTimeout(240, TimeUnit.SECONDS) + .readTimeout(240, TimeUnit.SECONDS) + .writeTimeout(240, TimeUnit.SECONDS) + .build() + + private val retrofit = Retrofit.Builder() + .baseUrl(API_BASE_URL) + .addConverterFactory(GsonConverterFactory.create()) + .client(client) + .build() + + val apiService: ApiService = retrofit.create(ApiService::class.java) +} \ No newline at end of file diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/ApiResource.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/ApiResource.kt new file mode 100644 index 0000000000000000000000000000000000000000..5d4f3da16ab283d00c2fc533d30ae8514647a83b --- /dev/null +++ b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/ApiResource.kt @@ -0,0 +1,10 @@ +package com.matthaigh27.chatgptwrapper.data.remote + +sealed class ApiResource( + val data: T? = null, + val message: String? = null +) { + class Success(data: T) : ApiResource(data) + class Error(message: String, data: T? = null) : ApiResource(data, message) + class Loading : ApiResource() +} \ No newline at end of file diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/ApiService.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/ApiService.kt new file mode 100644 index 0000000000000000000000000000000000000000..aba49215779f85e86c0614ae017d70fd11952e12 --- /dev/null +++ b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/ApiService.kt @@ -0,0 +1,15 @@ +package com.matthaigh27.chatgptwrapper.data.remote + +import com.matthaigh27.chatgptwrapper.data.remote.requests.BaseApiRequest +import com.matthaigh27.chatgptwrapper.data.remote.requests.NotificationApiRequest +import com.matthaigh27.chatgptwrapper.data.remote.responses.ApiResponse +import retrofit2.Call +import retrofit2.http.Body +import retrofit2.http.POST + +interface ApiService { + @POST("commands") + fun getAllHelpCommands(@Body request: BaseApiRequest) : Call + @POST("sendNotification") + fun sendNotification(@Body request: NotificationApiRequest) : Call +} \ No newline at end of file diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/requests/BaseApiRequest.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/requests/BaseApiRequest.kt new file mode 100644 index 0000000000000000000000000000000000000000..0142b3f2c11f4acee1bed1fe54ed463af168bad9 --- /dev/null +++ b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/requests/BaseApiRequest.kt @@ -0,0 +1,8 @@ +package com.matthaigh27.chatgptwrapper.data.remote.requests + +import com.matthaigh27.chatgptwrapper.data.remote.requests.common.Keys + +data class BaseApiRequest( + val confs: Keys +) + diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/requests/NotificationApiRequest.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/requests/NotificationApiRequest.kt new file mode 100644 index 0000000000000000000000000000000000000000..97817707951a860307654edd2042a4614cb237e8 --- /dev/null +++ b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/requests/NotificationApiRequest.kt @@ -0,0 +1,8 @@ +package com.matthaigh27.chatgptwrapper.data.remote.requests + +import com.matthaigh27.chatgptwrapper.data.remote.requests.common.Keys + +data class NotificationApiRequest( + val message: String, + val confs: Keys +) \ No newline at end of file diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/requests/common/Keys.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/requests/common/Keys.kt new file mode 100644 index 0000000000000000000000000000000000000000..c5c48cf1f49c80637bb2feb5a75b4457babd5aec --- /dev/null +++ b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/requests/common/Keys.kt @@ -0,0 +1,11 @@ +package com.matthaigh27.chatgptwrapper.data.remote.requests.common + +data class Keys( + val uuid: String, + val token: String, + val openai_key: String, + val pinecone_key: String, + val pinecone_env: String, + val firebase_key: String, + val settings: Settings, +) diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/requests/common/Settings.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/requests/common/Settings.kt new file mode 100644 index 0000000000000000000000000000000000000000..dca4c5634940e61bc1fd4201c4609f6ecc6c3c58 --- /dev/null +++ b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/requests/common/Settings.kt @@ -0,0 +1,5 @@ +package com.matthaigh27.chatgptwrapper.data.remote.requests.common + +data class Settings( + val temperature: Float +) \ No newline at end of file diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/responses/ApiResponse.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/responses/ApiResponse.kt new file mode 100644 index 0000000000000000000000000000000000000000..035c9a2a8bffde48d83ab4c67e39b4b5ab315df5 --- /dev/null +++ b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/responses/ApiResponse.kt @@ -0,0 +1,15 @@ +package com.matthaigh27.chatgptwrapper.data.remote.responses + +import com.google.gson.JsonElement + +data class ApiResponse ( + val status_code: Int, + val message: List, + val result: Result +) + +data class Result ( + val program: String, + val content: JsonElement, + val url: String +) \ No newline at end of file diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/repository/FirebaseRepository.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/repository/FirebaseRepository.kt new file mode 100644 index 0000000000000000000000000000000000000000..2b74b7ce97a00d32146d9a716e7b3f959df55a1d --- /dev/null +++ b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/repository/FirebaseRepository.kt @@ -0,0 +1,23 @@ +package com.matthaigh27.chatgptwrapper.data.repository + +import com.google.firebase.storage.FirebaseStorage +import com.matthaigh27.chatgptwrapper.utils.helpers.network.OnFailure +import com.matthaigh27.chatgptwrapper.utils.helpers.network.OnSuccess + +object FirebaseRepository { + fun downloadImageWithName( + name: String, + onSuccess: OnSuccess, + onFailure: OnFailure + ) { + val reference = "images/$name" + + val storageReference = FirebaseStorage.getInstance().getReference(reference) + storageReference.getBytes(Long.MAX_VALUE).addOnSuccessListener { bytes -> + onSuccess(bytes) + }.addOnFailureListener { e -> + onFailure(e.toString()) + } + return + } +} \ No newline at end of file diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/repository/RemoteRepository.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/repository/RemoteRepository.kt new file mode 100644 index 0000000000000000000000000000000000000000..7797ca5f08088eeb2d63b29d5853cf36b57cc1c9 --- /dev/null +++ b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/repository/RemoteRepository.kt @@ -0,0 +1,59 @@ +package com.matthaigh27.chatgptwrapper.data.repository + +import com.matthaigh27.chatgptwrapper.data.remote.ApiClient +import com.matthaigh27.chatgptwrapper.data.remote.requests.NotificationApiRequest +import com.matthaigh27.chatgptwrapper.data.remote.responses.ApiResponse +import com.matthaigh27.chatgptwrapper.utils.helpers.network.OnFailure +import com.matthaigh27.chatgptwrapper.utils.helpers.network.OnSuccess +import com.matthaigh27.chatgptwrapper.utils.helpers.network.RequestFactory.buildBaseApiRequest +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response + + +object RemoteRepository { + private val apiService = ApiClient.apiService + + fun getAllHelpCommands( + onSuccess: OnSuccess, + onFailure: OnFailure + ) { + val call = apiService.getAllHelpCommands(buildBaseApiRequest()) + + call.enqueue(object : Callback { + override fun onResponse(call: Call, response: Response) { + response.body()?.let { + onSuccess(ApiResponse(it.status_code, it.message, it.result)) + }?: run { + onFailure(response.message()) + } + } + + override fun onFailure(call: Call, t: Throwable) { + onFailure(t.message.toString()) + } + }) + } + + fun sendNotification( + request: NotificationApiRequest, + onSuccess: OnSuccess, + onFailure: OnFailure + ) { + val call = apiService.sendNotification(request) + + call.enqueue(object : Callback { + override fun onResponse(call: Call, response: Response) { + response.body()?.let { data -> + onSuccess(ApiResponse(data.status_code, data.message, data.result)) + }?: run { + onFailure(response.message()) + } + } + + override fun onFailure(call: Call, t: Throwable) { + onFailure(t.message.toString()) + } + }) + } +} \ No newline at end of file diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/repository/RoomRepository.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/repository/RoomRepository.kt new file mode 100644 index 0000000000000000000000000000000000000000..81cb0a36f9e86df7b8176fa3c5e4dc7fd6e6df01 --- /dev/null +++ b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/repository/RoomRepository.kt @@ -0,0 +1,47 @@ +package com.matthaigh27.chatgptwrapper.data.repository + +import androidx.lifecycle.MutableLiveData +import com.matthaigh27.chatgptwrapper.RisingApplication +import com.matthaigh27.chatgptwrapper.data.local.AppDatabase +import com.matthaigh27.chatgptwrapper.data.local.entity.ContactEntity +import com.matthaigh27.chatgptwrapper.data.local.entity.ImageEntity + + +object RoomRepository { + private var databaseHandler: AppDatabase = AppDatabase.getDatabase(RisingApplication.appContext) + + private var imageDao = databaseHandler.imageDao() + private var contactDao = databaseHandler.contactDao() + + fun getAllImages(): MutableLiveData> { + return MutableLiveData(imageDao.getAllData()) + } + + fun insertImage(entity: ImageEntity) { + imageDao.insert(entity) + } + + fun updateImage(entity: ImageEntity) { + imageDao.update(entity) + } + + fun deleteImage(entity: ImageEntity) { + imageDao.delete(entity) + } + + fun getAllContacts(): MutableLiveData> { + return MutableLiveData(contactDao.getAllData()) + } + + fun insertImage(entity: ContactEntity) { + contactDao.insert(entity) + } + + fun updateImage(entity: ContactEntity) { + contactDao.update(entity) + } + + fun deleteImage(entity: ContactEntity) { + contactDao.delete(entity) + } +} \ No newline at end of file diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/database/dao/ContactDao.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/database/dao/ContactDao.kt deleted file mode 100644 index 53d3f5ab8edaf727e24dfff3e382753e152b62d1..0000000000000000000000000000000000000000 --- a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/database/dao/ContactDao.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.matthaigh27.chatgptwrapper.database.dao - -import androidx.lifecycle.LiveData -import androidx.room.* -import com.matthaigh27.chatgptwrapper.database.entity.ContactEntity - -@Dao -interface ContactDao { - @Insert - suspend fun insertContact(contact: ContactEntity) - - @Update - suspend fun updateContact(contact: ContactEntity) - - @Delete - suspend fun deleteContact(contact: ContactEntity) - - @Query("SELECT * FROM contacts") - fun getAllContacts(): List -} \ No newline at end of file diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/database/dao/ImageDao.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/database/dao/ImageDao.kt deleted file mode 100644 index eb20e1682256bf245ba19178ae4090d554ccb7b5..0000000000000000000000000000000000000000 --- a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/database/dao/ImageDao.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.matthaigh27.chatgptwrapper.database.dao - -import androidx.lifecycle.LiveData -import androidx.room.* -import com.matthaigh27.chatgptwrapper.database.entity.ImageEntity - -@Dao -interface ImageDao { - @Insert - suspend fun insertImage(image: ImageEntity) - - @Update - suspend fun updateImage(image: ImageEntity) - - @Delete - suspend fun deleteImage(image: ImageEntity) - - @Query("SELECT * FROM images") - fun getAllImages(): List -} \ No newline at end of file diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/fragments/ChatFragment.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/fragments/ChatFragment.kt deleted file mode 100644 index 783270d0decee30a6a8ba0b1a863492dcea5393e..0000000000000000000000000000000000000000 --- a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/fragments/ChatFragment.kt +++ /dev/null @@ -1,888 +0,0 @@ -package com.matthaigh27.chatgptwrapper.fragments - -import android.os.Bundle -import androidx.fragment.app.Fragment -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.annotation.SuppressLint -import android.app.ActionBar.LayoutParams -import android.content.* -import android.graphics.Bitmap -import android.graphics.BitmapFactory -import android.net.Uri -import android.os.Build -import android.os.StrictMode -import android.os.StrictMode.ThreadPolicy -import android.provider.MediaStore -import android.telephony.SmsManager -import android.util.Log -import android.view.KeyEvent -import android.view.View.OnClickListener -import android.view.animation.AccelerateDecelerateInterpolator -import android.view.animation.AlphaAnimation -import android.view.animation.Animation -import android.view.animation.LinearInterpolator -import android.view.animation.RotateAnimation -import android.view.animation.TranslateAnimation -import android.widget.* -import androidx.annotation.RequiresApi -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView -import com.google.firebase.storage.FirebaseStorage -import com.matthaigh27.chatgptwrapper.MyApplication -import com.matthaigh27.chatgptwrapper.R -import com.matthaigh27.chatgptwrapper.adapters.ChatAdapter -import com.matthaigh27.chatgptwrapper.database.MyDatabase -import com.matthaigh27.chatgptwrapper.database.entity.ImageEntity -import com.matthaigh27.chatgptwrapper.widgets.ImagePickerWidget -import com.matthaigh27.chatgptwrapper.widgets.ImagePickerWidget.OnPositiveButtonClickListener -import com.matthaigh27.chatgptwrapper.models.* -import com.matthaigh27.chatgptwrapper.models.common.HelpCommandModel -import com.matthaigh27.chatgptwrapper.models.common.HelpPromptModel -import com.matthaigh27.chatgptwrapper.models.viewmodels.ChatMessageModel -import com.matthaigh27.chatgptwrapper.services.api.HttpClient -import com.matthaigh27.chatgptwrapper.services.api.HttpRisingInterface -import com.matthaigh27.chatgptwrapper.utils.Constants.* -import com.matthaigh27.chatgptwrapper.utils.Utils -import com.matthaigh27.chatgptwrapper.widgets.ContactDetailItem -import com.qw.photo.CoCo -import com.qw.photo.callback.CoCoAdapter -import com.qw.photo.callback.CoCoCallBack -import com.qw.photo.constant.Range -import com.qw.photo.pojo.PickResult -import com.qw.photo.pojo.TakeResult -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import okhttp3.* -import org.json.JSONException -import org.json.JSONObject -import java.io.* -import java.util.* -import kotlin.collections.ArrayList - -class ChatFragment : Fragment(), OnClickListener, HttpRisingInterface { - private lateinit var rootView: View - private var mContext: Context? = null - - /** ui components for chatlist recyclerview */ - private lateinit var mRvChatList: RecyclerView - private lateinit var mEtMessage: EditText - - /** ui components for loading spinner */ - private lateinit var mSpLoading: ImageView - private lateinit var mTvLoading: TextView - private lateinit var mLlLoading: LinearLayout - - /** ui components for loading photo */ - private lateinit var mLlLoadPhoto: LinearLayout - private lateinit var mIvLoadedPhoto: ImageView - private lateinit var mBtnCancelLoadPhoto: TextView - - /** adapter for chat recyclerview and arraylist for store chatting history */ - private var mMessageList: ArrayList = ArrayList() - private lateinit var mAdapter: ChatAdapter - - /** when a user selects image by camera or gallery, - * these two variables are used to save image source and name */ - private var mSelectedImage: ByteArray? = null - private var mSelectedImageName: String = "" - - /** - * mImagePickerType is - * 'image_uplaod' when user is going to upload image - * 'image_picker' when user is going to pick image for prompting - */ - private lateinit var mImagePickerWidget: ImagePickerWidget - private var mImagePickerType: String = "" - - /** HttpClient for restful apis */ - private lateinit var httpClient: HttpClient - - /** list of help prompt commands */ - private var mHelpPromptList: ArrayList? = null - - /** animation variable for loading spinner */ - private val rotate = RotateAnimation( - 0f, 360f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f - ) - - /** room database handler for local database */ - private lateinit var mRoomDataHandler: MyDatabase - - /** status variable that checks if widget in chatting interface does exist */ - private var mIsExistWidget: Boolean = false - - /** - * this is invoked when users click the message icon to send sms on contact detail dialog - * that is shown when a user search contacts - */ - private var mSMSOnClickListener: ContactDetailItem.OnSMSClickListener? = null - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View { - rootView = inflater.inflate(R.layout.fragment_chat, container, false) - init() - return rootView - } - - private fun init() { - initEnvironment() - initValues() - initView() - initDatabase() - - trainImages() - getAllPromptCommands() - } - - - private fun getAllPromptCommands() { - showLoading(true, "Loading Help Prompt Data") - httpClient.getALlHelpPromptCommands() - } - - - private fun initEnvironment() { - val policy = ThreadPolicy.Builder().permitAll().build() - StrictMode.setThreadPolicy(policy) - } - - private fun initValues() { - mContext = context - - httpClient = HttpClient(this) - - rotate.duration = 3000 - rotate.repeatCount = Animation.INFINITE - rotate.interpolator = LinearInterpolator() - - initSMSOnClickListener() - } - - private fun initView() { - this.mAdapter = ChatAdapter(mMessageList, mContext!!) - this.mAdapter.mOnSMSClickListener = mSMSOnClickListener - mRvChatList = rootView.findViewById(R.id.chatRecycleView) as RecyclerView - mRvChatList.adapter = mAdapter - - val sendSMSListener = object : ChatAdapter.MessageWidgetListener { - override fun sentSMS(phonenumber: String, message: String) { - mIsExistWidget = false - sendSms(phonenumber, message) - addMessage( - "You sent SMS with belowing content.\n\n " + "To: $phonenumber\n " + "Message: $message", - false - ) - } - - override fun canceledSMS() { - mIsExistWidget = false - addMessage("You canceled SMS.", false) - } - - override fun sentHelpPrompt(prompt: String) { - mIsExistWidget = false - addMessage(prompt, true) - } - - override fun canceledHelpPrompt() { - mIsExistWidget = false - addMessage("You canceled help command.", false) - } - } - - mAdapter.setMessageWidgetListener(sendSMSListener) - - val linearLayoutManager = LinearLayoutManager(mContext) - mRvChatList.layoutManager = linearLayoutManager - - this.mEtMessage = rootView.findViewById(R.id.et_message) - mEtMessage.setOnKeyListener { _, keyCode, keyEvent -> - if (keyEvent.action == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_ENTER) { - addMessage(mEtMessage.text.toString(), true) - } - return@setOnKeyListener false - } - - rootView.findViewById(R.id.btn_send_message).setOnClickListener(this) - rootView.findViewById(R.id.btn_image_upload).setOnClickListener(this) - rootView.findViewById(R.id.btn_image_picker).setOnClickListener(this) - - mSpLoading = rootView.findViewById(R.id.sp_loading) - mTvLoading = rootView.findViewById(R.id.tv_loading) - mLlLoading = rootView.findViewById(R.id.ll_loading) - - mLlLoadPhoto = rootView.findViewById(R.id.ll_load_photo) - mIvLoadedPhoto = rootView.findViewById(R.id.iv_load_photo) - mBtnCancelLoadPhoto = rootView.findViewById(R.id.btn_cancel_load_photo) - mBtnCancelLoadPhoto.setOnClickListener(this) - - initImagePickerWidget() - } - - private fun initDatabase() { - mRoomDataHandler = MyDatabase.getDatabase(mContext!!) - } - - private fun initSMSOnClickListener() { - mSMSOnClickListener = object : ContactDetailItem.OnSMSClickListener { - override fun onSMSClickListener(phoneNumber: String) { - addMessage( - "SMS", - isMe = false, - isSend = false, - isWidget = true, - widgetType = MSG_WIDGET_TYPE_SMS, - widgetDescription = phoneNumber - ) - } - - override fun onVoiceCallListener(phoneNumber: String, toName: String) { - addMessage( - message = "You made a voice call to $toName($phoneNumber)", - isMe = false, - isSend = false - ) - } - } - } - - /** - * set loading spinner visible - */ - private fun setDisableActivity(enable: Boolean) { - runOnUIThread { - mEtMessage.isEnabled = enable - rootView.findViewById(R.id.btn_send_message).isEnabled = enable - rootView.findViewById(R.id.btn_image_upload).isEnabled = enable - rootView.findViewById(R.id.btn_image_picker).isEnabled = enable - } - } - - /** - * set loading spinner visible - */ - private fun showLoading(visible: Boolean, text: String = "") { - runOnUIThread { - setDisableActivity(!visible) - - if (visible) { - mSpLoading.startAnimation(rotate) - mLlLoading.visibility = View.VISIBLE - mTvLoading.text = text - } else { - mSpLoading.clearAnimation() - mLlLoading.visibility = View.GONE - } - } - } - - /** - * show overlay when you picked image and users can cancel to load photo if users want - */ - private fun showLoadPhotoOverlay(imageByteArray: ByteArray) { - mLlLoadPhoto.visibility = View.VISIBLE - - mIvLoadedPhoto.setImageBitmap( - BitmapFactory.decodeByteArray( - /* data = */ imageByteArray, /* offset = */ 0, /* length = */ imageByteArray.size - ) - ) - } - - /** - * Bind broadcast sent by MessageService - */ - private val receiver = object : BroadcastReceiver() { - @RequiresApi(Build.VERSION_CODES.TIRAMISU) - override fun onReceive(context: Context, intent: Intent) { - Log.d(TAG, "Receive") - } - } - - - /** - * get download url from firebase imagename in response and generate download url from the imagename - * With getBitmapFromURL method, get bitmap of the image and set it to mSelectedImage to display it to chatlist - * Finally, run addmessage and insert it to chat recyclerview - * - * @param imageName the firebase store imagename - */ - private fun getImageResponse(imageName: String, imageDesc: String) { - if (imageName.isEmpty() && imageDesc.isNotEmpty()) { - addMessage(message = imageDesc, isMe = false) - return - } - - showLoading(visible = true, text = LOADING_DOWNLOADING_IMAGE) - - val imageNameToUpload = "images/$imageName" - - val storageReference = FirebaseStorage.getInstance().getReference(imageNameToUpload) - storageReference.downloadUrl.addOnSuccessListener { uri -> - try { - val image = Utils.instance.getBitmapFromURL(uri.toString()) - if (image == null) showToast("can not get bitmap from url") - val byteArrayOutputStream = ByteArrayOutputStream() - - val isSuccess = - image!!.compress(Bitmap.CompressFormat.JPEG, 100, byteArrayOutputStream) - if (!isSuccess) { - showToast("Fail to compress image") - } - mSelectedImage = byteArrayOutputStream.toByteArray() - } catch (e: Exception) { - showToast("cannot get download url from firebase store") - e.printStackTrace() - } - - addMessage(message = imageDesc, isMe = false) - showLoading(visible = false) - } - return - } - - override fun onResume() { - super.onResume() - updateChangedUserData() - } - - private fun updateChangedUserData() { - trainContacts() - } - - /** - * Open Help Command Widget with analysing string command - */ - private fun openHelpCommandWidget(strCommand: String) { - try { - val command: HelpCommandModel = Utils.instance.getHelpCommandFromStr(strCommand) - if (command.isMainCommand()) { - when (command.mainCommandName) { - "sms" -> { - addMessage( - message = "SMS", - isMe = false, - isSend = false, - isWidget = true, - widgetType = MSG_WIDGET_TYPE_SMS - ) - } - - else -> { - mHelpPromptList!!.forEach { model -> - if (model.name == command.mainCommandName) { - addMessage( - "Help Prompt Command", - isMe = true, - isSend = false, - isWidget = true, - widgetType = MSG_WIDGET_TYPE_HELP_PRMOPT, - widgetDescription = model.toString() - ) - return - } - } - addMessage( - message = "No such command name exists.", isMe = false, isSend = false - ) - } - } - } else { - if (command.assistCommandName == "all") { - val usage = - "usage:\n" + "- help command: /help [command name]\n" + "- prompt command: /\n\n" - var strHelpList = "help prompt commands:" - mHelpPromptList!!.forEach { model -> - strHelpList += "\n- " + model.name - } - - addMessage(message = usage + strHelpList, isMe = false, isSend = false) - } else { - var strHelpDesc = "" - mHelpPromptList!!.forEach { model -> - if (model.name == command.assistCommandName) { - var strTags = "" - model.tags!!.forEach { tag -> - strTags += " $tag" - } - strHelpDesc = "description: " + model.description + "\ntags:" + strTags - } - } - if (strHelpDesc.isEmpty()) addMessage( - message = "No such command name exists.", isMe = false, isSend = false - ) - else addMessage(message = strHelpDesc, isMe = false, isSend = false) - } - } - } catch (e: Exception) { - e.printStackTrace() - showToast(e.message.toString()) - } - } - - /** - * add message to chat list - * if users picked image from camera or gallery, send post request to `sendNotification` to ask generally - * else do 'imageRelateness' to search image - * - * @param message message content for chat list - * @param isMe this identify if this is sent by langchain server or users send message to server - * @param isSend is boolean that checks if you send request to server - * @param isWidget is boolean that checks if message item has widget - * @param widgetType is type of Widget ex: SMS, HELP_COMMAND, etc - * @param widgetDescription is string that saves information for widget - */ - @SuppressLint("NotifyDataSetChanged") - private fun addMessage( - message: String, - isMe: Boolean, - isSend: Boolean = true, - isWidget: Boolean = false, - widgetType: String = "", - widgetDescription: String = "" - ) { - if ((message.isEmpty() && mSelectedImage == null) || mIsExistWidget) return - if (isWidget) { - if (widgetType != MSG_WIDGET_TYPE_SEARCH_CONTACT) { - mIsExistWidget = true - } - } - - val msg = ChatMessageModel() - msg.message = message - msg.isMe = isMe - msg.isWidget = isWidget - msg.widgetType = widgetType - msg.widgetDescription = widgetDescription - - /** - * if users picked some image from camera or gallery, add the image to chatting message - */ - if (mSelectedImage != null) { - msg.image = mSelectedImage - mSelectedImage = null - } - - /** - * if users picked some image from camera or gallery, the image upload to firebase store - * mSelectedImageName is uuid created uploading to firebase store - */ - if (mSelectedImageName.isNotEmpty()) { - msg.imageName = mSelectedImageName - mSelectedImageName = "" - } - - runOnUIThread { - mLlLoadPhoto.visibility = View.GONE - - mMessageList.add(msg) - mAdapter.notifyDataSetChanged() - mEtMessage.setText("") - mRvChatList.scrollTo(/* x = */ 1000, /* y = */ -1000) - mRvChatList.scrollToPosition(mMessageList.size - 1) - } - - if ((message.isNotEmpty() && message.first() == '/') && isMe) { - openHelpCommandWidget(message) - return - } - - if (isMe) { - if (!isSend) { - return - } - - showLoading(visible = true, text = LOADING_ASKING_TO_GPT) - if (msg.image != null) { - httpClient.callImageRelatedness(msg.imageName) - } else { - httpClient.callSendNotification(message) - } - } - } - - override fun onClick(view: View) { - when (view.id) { - R.id.btn_send_message -> { - addMessage(mEtMessage.text.toString(), true) - } - - R.id.btn_image_upload -> { - mImagePickerType = PICKERTYPE_IMAGE_UPLOAD - if(rootView.findViewById(R.id.ll_toolbar).visibility == View.VISIBLE) - hideSlidingWidget() - else - showSlidingWidget() - } - - R.id.btn_image_picker -> { - mImagePickerType = PICKERTYPE_IMAGE_PICK - if(rootView.findViewById(R.id.ll_toolbar).visibility == View.VISIBLE) - hideSlidingWidget() - else - showSlidingWidget() - } - - R.id.btn_cancel_load_photo -> { - mLlLoadPhoto.visibility = View.GONE - mSelectedImageName = "" - mSelectedImage = null - } - } - } - - /** - * open browser with url - * @param url to open with browser - */ - private fun openBrowser(url: String) { - val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) - startActivity(Intent.createChooser(intent, "Browse with")) - } - - /** - * calls when finish picking image - * - * @param imageByteData is bytearray of picked image - * @param type if users are going to upload image or pick image for query - */ - private fun pickedImage(imageByteData: ByteArray, type: String) { - if (type == "image_upload") { - uploadImageToFirebaseStorage(imageByteData) - } else { - uploadSearchImage(imageByteData) - } - } - - /** - * toast message is invoked when error happens - */ - private fun showToast(message: String) { - runOnUIThread { - Toast.makeText( - mContext, message, Toast.LENGTH_SHORT - ).show() - } - } - - - /** - * this can show dialog with camera and gallery icon. - * when you click camera icon, camera application runs and users can get an image by capturing using camera. - * when you click gallery icon, users can select image in your storage. - * A picked image converts into bytearray data and upload to firebase storage. - */ - private fun initImagePickerWidget() { - mImagePickerWidget = ImagePickerWidget(mContext!!) - - val myImplementation = object : OnPositiveButtonClickListener { - override fun onPositiveBtnClick(isCamera: Boolean?) { - if (isCamera == true) { - CoCo.with(activity!!).take(Utils.instance.createSDCardFile()) - .start(object : CoCoAdapter() { - override fun onSuccess(data: TakeResult) { - val byteArray: ByteArray = - Utils.instance.getBytesFromPath(data.savedFile!!.absolutePath) - pickedImage( - byteArray, mImagePickerType - ) - } - - override fun onFailed(exception: Exception) { - super.onFailed(exception) - showToast("Fail to pick image. Please try again.") - } - }) - } else { - CoCo.with(activity!!).pick().range(Range.PICK_CONTENT) - .start(object : CoCoCallBack { - - override fun onSuccess(data: PickResult) { - val byteArray: ByteArray? = - Utils.instance.convertImageToByte(data.originUri) - if (byteArray == null) showToast("can not find such a file") - pickedImage(byteArray!!, mImagePickerType) - } - - override fun onFailed(exception: Exception) { - showToast("Fail to pick image. Please try again.") - } - }) - } - } - } - - mImagePickerWidget.setOnClickListener(myImplementation) - - val slidingWidget = rootView.findViewById(R.id.ll_toolbar) - slidingWidget.addView(mImagePickerWidget) - } - - private fun uploadSearchImage(imageByteArray: ByteArray) { - showLoading(true, LOADING_ANALYZING_IMAGE) - val storageRef = FirebaseStorage.getInstance().reference - val uuid = UUID.randomUUID() - val imageName = "images/${uuid}" - val imageRef = storageRef.child(imageName) - - val uploadTask = imageRef.putBytes(imageByteArray) - uploadTask.addOnFailureListener { - showLoading(false) - }.addOnSuccessListener { - Log.d(TAG, "Success upload to firebase storage") - showLoading(false) - - mSelectedImageName = "$uuid" - mSelectedImage = imageByteArray - - showLoadPhotoOverlay(imageByteArray) - } - } - - /** - * @param imageByteArray ByteArray data for image to upload to firebase storage - */ - private fun uploadImageToFirebaseStorage(imageByteArray: ByteArray) { - showLoading(true, LOADING_UPLOADING_IAMGE) - val storageRef = FirebaseStorage.getInstance().reference - val uuid = UUID.randomUUID() - val imageName = "images/${uuid}" - val imageRef = storageRef.child(imageName) - - val uploadTask = imageRef.putBytes(imageByteArray) - uploadTask.addOnFailureListener { - showLoading(false) - }.addOnSuccessListener { - Log.d(TAG, "Success upload to firebase storage") - - showLoading(false) - httpClient.callImageUpload("$uuid") - } - } - - override fun onSuccessResult(msg: String) { - showLoading(false) - try { - val json = JSONObject(msg) - if (json.has(RESPONSE_TYPE_PROGRAM)) { - when (json.getString(RESPONSE_TYPE_PROGRAM)) { - RESPONSE_TYPE_BROWSER -> { - addMessage(json.getString(RESPONSE_TYPE_URL), false) - openBrowser(json.getString(RESPONSE_TYPE_URL)) - return - } - - RESPONSE_TYPE_ALERT -> { - MyApplication.appContext.showNotification( - json.getString( - RESPONSE_TYPE_CONTENT - ) - ) - return - } - - RESPONSE_TYPE_MESSAGE -> { - addMessage(json.getString(RESPONSE_TYPE_CONTENT), false) - return - } - - RESPONSE_TYPE_IMAGE -> { - try { - val imageRes = JSONObject(json.getString(RESPONSE_TYPE_CONTENT)) - - val imageName = if (imageRes.has("image_name")) { - imageRes["image_name"] as String - } else { - "" - } - - val imageDesc = if (imageRes.has("image_desc")) { - imageRes["image_desc"] as String - } else { - "" - } - - getImageResponse(imageName, imageDesc) - } catch (e: Exception) { - e.printStackTrace() - } - return - } - - RESPONSE_TYPE_SMS -> { - addMessage( - json.getString(RESPONSE_TYPE_CONTENT), - false, - isSend = false, - isWidget = true, - widgetType = MSG_WIDGET_TYPE_SMS - ) - } - - RESPONSE_TYPE_HELP_COMMAND -> { - try { - mHelpPromptList = Utils.instance.getHelpCommandListFromJsonString( - json.getString(RESPONSE_TYPE_CONTENT) - ) - } catch (e: java.lang.Exception) { - e.printStackTrace() - showToast("JSON Error occured") - } - } - - RESPONSE_TYPE_CONTACT -> { - try { - addMessage( - message = "Contacts that you are looking for.", - isMe = false, - isSend = false, - isWidget = true, - widgetType = MSG_WIDGET_TYPE_SEARCH_CONTACT, - widgetDescription = json.getString(RESPONSE_TYPE_CONTENT) - ) - } catch (e: Exception) { - e.printStackTrace() - } - } - } - } - } catch (e: JSONException) { - e.printStackTrace() - addMessage(msg, false) - } - } - - override fun onFailureResult(msg: String) { - showLoading(false) - - showToast(msg) - } - - private fun queryImagesFromExternalStorage(contentResolver: ContentResolver): ArrayList { - val listOfImageUris = ArrayList() - - val projection = arrayOf(MediaStore.Images.Media._ID) - - val sortOrder = "${MediaStore.Images.Media.DATE_TAKEN} DESC" - - contentResolver.query( - MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection, null, null, sortOrder - )?.use { cursor -> - val idColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID) - while (cursor.moveToNext()) { - val id = cursor.getLong(idColumn) - val contentUri = Uri.withAppendedPath( - MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id.toString() - ) - listOfImageUris.add(contentUri) - } - } - return listOfImageUris - } - - private fun trainImages() { - CoroutineScope(Dispatchers.IO).launch { - val images = queryImagesFromExternalStorage(requireContext().contentResolver) - val originalImages = mRoomDataHandler.imageDao().getAllImages() - - images.forEach { uri -> - var isExist = false - val path = Utils.instance.getRealPathFromUri(requireContext(), uri) - for (i in originalImages.indices) { - val entity: ImageEntity = originalImages[i] - if (entity.path == path) { - isExist = true - break - } - } - if (!isExist) { - val byteArray = Utils.instance.getBytesFromPath(path) - val uuid = uploadImageToFirebaseStorage(byteArray) - - if (path != null) - mRoomDataHandler.imageDao().insertImage(ImageEntity(0, path, "$uuid")) - } - } - } - } - - private fun trainContacts() { - showLoading(true, "Train Contacts") - val contacts = Utils.instance.getContacts(mContext!!) - CoroutineScope(Dispatchers.Main).launch { - val changedContacts = Utils.instance.getChangedContacts(contacts, mRoomDataHandler) - httpClient.trainContacts(changedContacts) - } - } - - fun sendSms(phoneNumber: String, message: String) { - try { - val smsManager = SmsManager.getDefault() - val parts = smsManager.divideMessage(message) - smsManager.sendMultipartTextMessage(/* destinationAddress = */ phoneNumber, /* scAddress = */ - null, /* parts = */ - parts, /* sentIntents = */ - null, /* deliveryIntents = */ - null) - } catch (e: SecurityException) { - e.printStackTrace() - } catch (e: Exception) { - e.printStackTrace() - } - } - - @SuppressLint("UseRequireInsteadOfGet") - fun runOnUIThread(action: () -> Unit) { - requireActivity().runOnUiThread { - action() - } - } - - private fun showSlidingWidget() { - val slidingWidget = rootView.findViewById(R.id.ll_toolbar) - - val dy = slidingWidget.measuredHeight.toFloat() - slidingWidget.visibility = View.VISIBLE - - val anim = TranslateAnimation(0f, 0f, dy, 0f).apply { - duration = 150 // Set the animation duration, e.g., 300ms - interpolator = AccelerateDecelerateInterpolator() - setAnimationListener(object : Animation.AnimationListener { - override fun onAnimationStart(animation: Animation?) { - slidingWidget.visibility = View.VISIBLE - } - - override fun onAnimationEnd(animation: Animation?) {} - - override fun onAnimationRepeat(animation: Animation?) {} - }) - } - - slidingWidget.startAnimation(anim) - } - - private fun hideSlidingWidget() { - val slidingWidget = rootView.findViewById(R.id.ll_toolbar) - - val anim = AlphaAnimation(1f, 0f).apply { - duration = 100 // Set the animation duration, e.g., 300ms - interpolator = AccelerateDecelerateInterpolator() - setAnimationListener(object : Animation.AnimationListener { - override fun onAnimationStart(animation: Animation?) {} - - override fun onAnimationEnd(animation: Animation?) { - slidingWidget.visibility = View.GONE - } - - override fun onAnimationRepeat(animation: Animation?) {} - }) - } - - slidingWidget.startAnimation(anim) - } -} - diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/models/common/ContactModel.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/models/common/ContactModel.kt deleted file mode 100644 index 25153bde8bd1003bcf2f327b76f0a0b876e8b34a..0000000000000000000000000000000000000000 --- a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/models/common/ContactModel.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.matthaigh27.chatgptwrapper.models.common - -class ContactModel { - var id: String = "" - var name: String = "" - var phoneList: ArrayList? = null - var status: String = "" - - init { - phoneList = ArrayList() - } - - fun setData(id: String, name: String, phoneList: ArrayList, status: String) { - this.id = id - this.name = name - this.phoneList = phoneList - this.status = status - } -} \ No newline at end of file diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/models/common/HelpCommandModel.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/models/common/HelpCommandModel.kt deleted file mode 100644 index 3f1871ab5e601ef58168d3aa51f91870e8774247..0000000000000000000000000000000000000000 --- a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/models/common/HelpCommandModel.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.matthaigh27.chatgptwrapper.models.common - -class HelpCommandModel { - var mainCommandName: String = "" - var assistCommandName: String = "" - - fun isMainCommand(): Boolean { - return mainCommandName != "help" - } -} \ No newline at end of file diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/models/common/HelpPromptModel.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/models/common/HelpPromptModel.kt deleted file mode 100644 index 891cce0539ce69703f865fdbd3a0b8973a6e6f94..0000000000000000000000000000000000000000 --- a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/models/common/HelpPromptModel.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.matthaigh27.chatgptwrapper.models.common - -import com.google.gson.Gson -import com.google.gson.GsonBuilder - -class HelpPromptModel { - var name: String = "" - var description: String = "" - var prompt: String = "" - var tags: ArrayList? = null - - override fun toString(): String { - val gson = Gson() - val str = gson.toJson(this) - return str - } - - companion object { - fun initModelWithString(strJson: String): HelpPromptModel { - val gson = Gson() - val model: HelpPromptModel = gson.fromJson(strJson, HelpPromptModel::class.java) - return model - } - } -} \ No newline at end of file diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/models/common/ImagePromptModel.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/models/common/ImagePromptModel.kt deleted file mode 100644 index 53c9e9350a40d6dca39afb083e9e5d12b49561f7..0000000000000000000000000000000000000000 --- a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/models/common/ImagePromptModel.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.matthaigh27.chatgptwrapper.models.common - -class ImagePromptModel { - var id: String = "" - var path: String = "" - - constructor(id: String, path: String) { - this.id = id - this.path = path - } -} \ No newline at end of file diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/models/requestmodels/RequestBodyModel.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/models/requestmodels/RequestBodyModel.kt deleted file mode 100644 index 7c5451d6118db9d1517513770a42000bf405814c..0000000000000000000000000000000000000000 --- a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/models/requestmodels/RequestBodyModel.kt +++ /dev/null @@ -1,86 +0,0 @@ -package com.matthaigh27.chatgptwrapper.models.requestmodels - -import com.matthaigh27.chatgptwrapper.MyApplication -import org.json.JSONException -import org.json.JSONObject - -class RequestBodyModel(builder: Builder) { - - /** this identify request type - * example: it will be 'message' when users send message, 'image' when users upload image - */ - var type: String = "" - var token: String = "" - var message: String = "" - var imageName: String = "" - var uuid: String = "" - - init { - this.token = MyApplication.appContext.getFCMToken() - this.uuid = MyApplication.appContext.getUUID() - this.message = builder.message - this.imageName = builder.imageName - } - - @Throws(JSONException::class) - fun buildJsonObject(): JSONObject { - - val jsonObject = JSONObject() - jsonObject.accumulate("type", type) - jsonObject.accumulate("token", token) - jsonObject.accumulate("message", message) - jsonObject.accumulate("image_name", imageName) - jsonObject.accumulate("uuid", uuid) - - return jsonObject - } - - class Builder { - var type: String = "" - var token: String = "" - var message: String = "" - var imageName: String = "" - var uuid: String = "" - - constructor() { - - } - - constructor(request: RequestBodyModel) { - this.type = request.type - this.token = request.token - this.message = request.message - this.imageName = request.imageName - this.uuid = request.uuid - } - - fun type(type: String): Builder { - this.type = type - return this - } - - fun token(token: String): Builder { - this.token = token - return this - } - - fun message(message: String): Builder { - this.message = message - return this - } - - fun imageName(imageName: String): Builder { - this.imageName = imageName - return this - } - - fun uuid(uuid: String): Builder { - this.uuid = uuid - return this - } - - fun build(): RequestBodyModel { - return RequestBodyModel(this) - } - } -} \ No newline at end of file diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/models/requestmodels/RequestTrainContactModel.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/models/requestmodels/RequestTrainContactModel.kt deleted file mode 100644 index 9056f6ba0b7a76fa90736018e90a580bda36d2ff..0000000000000000000000000000000000000000 --- a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/models/requestmodels/RequestTrainContactModel.kt +++ /dev/null @@ -1,79 +0,0 @@ -package com.matthaigh27.chatgptwrapper.models.requestmodels - -import com.matthaigh27.chatgptwrapper.MyApplication -import com.matthaigh27.chatgptwrapper.models.common.ContactModel -import com.matthaigh27.chatgptwrapper.utils.Utils -import org.json.JSONArray -import org.json.JSONException -import org.json.JSONObject - -class RequestTrainContactModel(builder: Builder) { - - /** this identify request type - * example: it will be 'message' when users send message, 'image' when users upload image - */ - var type: String = "" - var token: String = "" - var contacts = JSONArray() - var uuid: String = "" - - init { - this.token = MyApplication.appContext.getFCMToken() - this.uuid = MyApplication.appContext.getUUID() - this.contacts = builder.contacts - } - - @Throws(JSONException::class) - fun buildJsonObject(): JSONObject { - - val jsonObject = JSONObject() - jsonObject.accumulate("type", type) - jsonObject.accumulate("token", token) - jsonObject.accumulate("contacts", contacts) - jsonObject.accumulate("uuid", uuid) - - return jsonObject - } - - class Builder { - var type: String = "" - var token: String = "" - var contacts = JSONArray() - var uuid: String = "" - - constructor() { - - } - - constructor(request: RequestTrainContactModel) { - this.type = request.type - this.token = request.token - this.uuid = request.uuid - this.contacts = request.contacts - } - - fun type(type: String): Builder { - this.type = type - return this - } - - fun token(token: String): Builder { - this.token = token - return this - } - - fun contacts(contacts: ArrayList): Builder { - this.contacts = Utils.instance.convertContactModelToJsonArray(contacts) - return this - } - - fun uuid(uuid: String): Builder { - this.uuid = uuid - return this - } - - fun build(): RequestTrainContactModel { - return RequestTrainContactModel(this) - } - } -} \ No newline at end of file diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/models/viewmodels/ChatMessageModel.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/models/viewmodels/ChatMessageModel.kt deleted file mode 100644 index c6dd4655cd1d37264f9b06fde72d2cda751d66cc..0000000000000000000000000000000000000000 --- a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/models/viewmodels/ChatMessageModel.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.matthaigh27.chatgptwrapper.models.viewmodels - -import androidx.lifecycle.ViewModel - -/** - * ChatModel for Chat RecyclerView - */ -class ChatMessageModel: ViewModel() { - var message: String = "" - var isMe: Boolean = true - var image: ByteArray? = null - var imageName: String = "" - var visibleFeedback = false - var feedback = 0 - var isWidget: Boolean = false - var widgetType = "" - var widgetDescription = "" -} diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/services/MessageService.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/services/MessageService.kt deleted file mode 100644 index 36f21bd2c15e97ef4f81ef42e58fe7b83c6d1759..0000000000000000000000000000000000000000 --- a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/services/MessageService.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.matthaigh27.chatgptwrapper.services - -import android.content.Intent -import android.util.Log -import com.google.firebase.messaging.FirebaseMessagingService -import com.google.firebase.messaging.RemoteMessage -import com.matthaigh27.chatgptwrapper.utils.Constants.TAG - -class MessageService : FirebaseMessagingService() { - - /** - * this function is called when langchain server pushs notification into FCM - * @param remoteMessage is sent by FCM when langchain server pushs notification - */ - override fun onMessageReceived(remoteMessage: RemoteMessage) { - super.onMessageReceived(remoteMessage) - if (remoteMessage.notification != null) { - Log.d( - TAG, "Message Notification Body: " + remoteMessage.notification!!.body - ) - - /** intent for sending broadcast to ChatActivity */ - val intent = Intent() - intent.action = "android.intent.action.MAIN" - intent.putExtra("message", remoteMessage.notification!!.body) - - /** send broadcast to ChatActivity - * So ChatActivity can receive remoteMessage from this service */ - sendBroadcast(intent) - } - } -} \ No newline at end of file diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/services/api/HttpClient.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/services/api/HttpClient.kt deleted file mode 100644 index 35583f7b84dbfce6c53ce04fbe83144f1751e2fb..0000000000000000000000000000000000000000 --- a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/services/api/HttpClient.kt +++ /dev/null @@ -1,117 +0,0 @@ -package com.matthaigh27.chatgptwrapper.services.api - -import android.util.Log -import com.matthaigh27.chatgptwrapper.BuildConfig -import com.matthaigh27.chatgptwrapper.models.common.ContactModel -import com.matthaigh27.chatgptwrapper.models.requestmodels.RequestBodyModel -import com.matthaigh27.chatgptwrapper.models.requestmodels.RequestTrainContactModel -import com.matthaigh27.chatgptwrapper.utils.Constants -import com.matthaigh27.chatgptwrapper.utils.Constants.GET -import com.matthaigh27.chatgptwrapper.utils.Constants.POST -import com.matthaigh27.chatgptwrapper.utils.ReqType -import okhttp3.Call -import okhttp3.Callback -import okhttp3.OkHttpClient -import okhttp3.Request -import okhttp3.RequestBody -import okhttp3.Response -import org.json.JSONException -import org.json.JSONObject -import java.io.IOException -import java.util.concurrent.TimeUnit - -class HttpClient { - /* Server URL and Api Endpoints */ - val SERVER_URL = BuildConfig.BASE_URL - val SEND_NOTIFICATION_URL = SERVER_URL + "sendNotification" - val IMAGE_RELATEDNESS = SERVER_URL + "image_relatedness" - val UPLOAD_IMAGE = SERVER_URL + "uploadImage" - val GET_ALL_HELP_COMMANDS = SERVER_URL + "commands" - val TRAIN_CONTACTS = SERVER_URL + "train/contacts" - - var mCallback: HttpRisingInterface - - constructor(callback: HttpRisingInterface) { - mCallback = callback - } - - private fun sendOkHttpRequest(postBody: String, postUrl: String, method: String) { - val body: RequestBody = RequestBody.create(Constants.JSON, postBody) - - /** - * set okhttpclient timeout to 120s - */ - var request: Request? = null - if (method == POST) request = Request.Builder().url(postUrl).post(body).build() - else request = Request.Builder().url(postUrl).get().build() - - val client = - OkHttpClient.Builder().connectTimeout(Constants.CUSTOM_TIMEOUT, TimeUnit.SECONDS) - .readTimeout(Constants.CUSTOM_TIMEOUT, TimeUnit.SECONDS) - .writeTimeout(Constants.CUSTOM_TIMEOUT, TimeUnit.SECONDS).build() - - client.newCall(request).enqueue(object : Callback { - override fun onFailure(call: Call, e: IOException) { - /** - * Handle failure - */ - e.printStackTrace() - - mCallback.onFailureResult("Fail to send request to server. Please ask again.") - } - - override fun onResponse(call: Call, response: Response) { - val myResponse = response.body()!!.string() - Log.d(Constants.TAG, myResponse) - - try { - val json = JSONObject(myResponse)["result"].toString() - mCallback.onSuccessResult(json) - } catch (e: JSONException) { - mCallback.onFailureResult(myResponse) - e.printStackTrace() - } - } - }) - } - - /* call sendNotification */ - fun callSendNotification(message: String) { - sendOkHttpRequest( - RequestBodyModel.Builder().message(message).type(ReqType.instance.MESSAGE).build() - .buildJsonObject().toString(), SEND_NOTIFICATION_URL, POST - ) - } - - /* call image_relatedness */ - fun callImageRelatedness(imageName: String) { - sendOkHttpRequest( - RequestBodyModel.Builder().imageName(imageName).type(ReqType.instance.MESSAGE).build() - .buildJsonObject().toString(), IMAGE_RELATEDNESS, POST - ) - } - - /* call image_upload */ - fun callImageUpload(imageName: String) { - sendOkHttpRequest( - RequestBodyModel.Builder().imageName(imageName).type(ReqType.instance.IMAGE_UPLOAD) - .build().buildJsonObject().toString(), UPLOAD_IMAGE, POST - ) - } - - fun getALlHelpPromptCommands() { - sendOkHttpRequest( - RequestBodyModel.Builder().build().buildJsonObject().toString(), - GET_ALL_HELP_COMMANDS, - GET - ) - } - - fun trainContacts(contacts: ArrayList) { - sendOkHttpRequest( - RequestTrainContactModel.Builder().contacts(contacts).build().buildJsonObject().toString(), - TRAIN_CONTACTS, - POST - ) - } -} \ No newline at end of file diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/services/api/HttpRisingInterface.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/services/api/HttpRisingInterface.kt deleted file mode 100644 index d01c29ce04878f508af5c2e5a20f7fdc6e2e30ff..0000000000000000000000000000000000000000 --- a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/services/api/HttpRisingInterface.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.matthaigh27.chatgptwrapper.services.api; - -interface HttpRisingInterface { - fun onSuccessResult(msg: String) - - fun onFailureResult(msg: String) -} diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/base/BaseActivity.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/base/BaseActivity.kt new file mode 100644 index 0000000000000000000000000000000000000000..7af743f2f001182f5889ad4e7bc8e07192901b20 --- /dev/null +++ b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/base/BaseActivity.kt @@ -0,0 +1,18 @@ +package com.matthaigh27.chatgptwrapper.ui.base + +import android.os.Bundle +import android.view.View +import androidx.appcompat.app.AppCompatActivity + +open class BaseActivity : AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + // Hide the status bar (system toolbar) + window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN + + // For hiding the action bar, if you have one + supportActionBar?.hide() + } +} \ No newline at end of file diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/activites/HomeActivity.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/chat/view/ChatActivity.kt similarity index 67% rename from Android/app/src/main/java/com/matthaigh27/chatgptwrapper/activites/HomeActivity.kt rename to Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/chat/view/ChatActivity.kt index 0a11074e924944adcac75f71f65eaf8a4e31050a..50542830f45a3f6281aa86ace7824c01721dbe5e 100644 --- a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/activites/HomeActivity.kt +++ b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/chat/view/ChatActivity.kt @@ -1,39 +1,38 @@ -package com.matthaigh27.chatgptwrapper.activites +package com.matthaigh27.chatgptwrapper.ui.chat.view import android.Manifest import android.annotation.SuppressLint import android.content.pm.PackageManager -import android.os.Build import android.os.Bundle -import android.util.Log -import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity -import com.google.android.material.tabs.TabLayout.TabGravity import com.matthaigh27.chatgptwrapper.R -import com.matthaigh27.chatgptwrapper.dialogs.CommonConfirmDialog -import com.matthaigh27.chatgptwrapper.fragments.ChatFragment -import java.io.File +import com.matthaigh27.chatgptwrapper.ui.base.BaseActivity +import com.matthaigh27.chatgptwrapper.ui.chat.view.dialogs.ConfirmDialog +import com.matthaigh27.chatgptwrapper.ui.chat.view.fragments.ChatMainFragment -class HomeActivity : AppCompatActivity() { +class ChatActivity : BaseActivity() { private val PERMISSIONS_REQUEST_CODE = 1 - private val PERMISSIONS = arrayOf( Manifest.permission.SEND_SMS, Manifest.permission.READ_CONTACTS, Manifest.permission.CALL_PHONE, Manifest.permission.READ_EXTERNAL_STORAGE ) + private val CONFIRM_MESSAGE = + "This app requires SMS, Contacts and Phone " + + "permissions to function properly. " + + "Please grant the necessary permissions." override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_home) + setContentView(R.layout.activity_chat) - requestPermission() + requestPermissions() } - private fun requestPermission() { + private fun requestPermissions() { val notGrantedPermissions = PERMISSIONS.filter { checkSelfPermission(it) != PackageManager.PERMISSION_GRANTED } @@ -41,10 +40,9 @@ class HomeActivity : AppCompatActivity() { if (notGrantedPermissions.isNotEmpty()) { if (shouldShowRequestPermissionRationale(notGrantedPermissions[0])) { // show custom permission rationale - val confirmDialog = CommonConfirmDialog(this) - confirmDialog.setMessage("This app requires SMS, Contacts and Phone permissions to function properly. Please grant the necessary permissions.") + val confirmDialog = ConfirmDialog(this@ChatActivity) confirmDialog.setOnClickListener(object : - CommonConfirmDialog.OnConfirmButtonClickListener { + ConfirmDialog.OnDialogButtonClickListener { override fun onPositiveButtonClick() { requestPermissions( notGrantedPermissions.toTypedArray(), PERMISSIONS_REQUEST_CODE @@ -55,21 +53,19 @@ class HomeActivity : AppCompatActivity() { finish() } }) + confirmDialog.show() + confirmDialog.setMessage(CONFIRM_MESSAGE) + } else { requestPermissions(notGrantedPermissions.toTypedArray(), PERMISSIONS_REQUEST_CODE) } } else { // Permissions already granted, navigate to your desired fragment - navigateToChatFragment() + navigateToChatMainFragment() } } - private fun navigateToChatFragment() { - val fragmentTransaction = supportFragmentManager.beginTransaction() - fragmentTransaction.replace(R.id.frame_container, ChatFragment()).commit() - } - @SuppressLint("MissingSuperCall") override fun onRequestPermissionsResult( requestCode: Int, permissions: Array, grantResults: IntArray @@ -78,14 +74,19 @@ class HomeActivity : AppCompatActivity() { PERMISSIONS_REQUEST_CODE -> { if (grantResults.all { it == PackageManager.PERMISSION_GRANTED }) { // Permissions granted, navigate to your desired fragment - navigateToChatFragment() + navigateToChatMainFragment() } else { - requestPermission() + requestPermissions() } return } } } + + private fun navigateToChatMainFragment() { + val fragmentTransaction = supportFragmentManager.beginTransaction() + fragmentTransaction.replace(R.id.fl_container, ChatMainFragment()).commit() + } } diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/chat/view/adapters/ChatMainAdapter.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/chat/view/adapters/ChatMainAdapter.kt new file mode 100644 index 0000000000000000000000000000000000000000..03deb5e800281637c07a1a8641ca31bb176f3f03 --- /dev/null +++ b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/chat/view/adapters/ChatMainAdapter.kt @@ -0,0 +1,179 @@ +package com.matthaigh27.chatgptwrapper.ui.chat.view.adapters + +import android.content.Context +import android.graphics.BitmapFactory +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.FrameLayout +import android.widget.ImageView +import android.widget.TextView +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.recyclerview.widget.RecyclerView +import com.matthaigh27.chatgptwrapper.R +import com.matthaigh27.chatgptwrapper.data.models.ChatMessageModel +import com.matthaigh27.chatgptwrapper.ui.chat.view.interfaces.ChatMessageInterface +import com.matthaigh27.chatgptwrapper.ui.chat.view.widgets.chatwidget.SendSmsWidget +import com.matthaigh27.chatgptwrapper.ui.chat.view.widgets.chatwidget.helpprompt.HelpPromptWidget +import com.matthaigh27.chatgptwrapper.utils.Constants +import com.matthaigh27.chatgptwrapper.utils.helpers.chat.ImageHelper + +class ChatMainAdapter( + context: Context, list: ArrayList, callbacks: ChatMessageInterface +) : RecyclerView.Adapter() { + + private val VIEW_TYPE_MSG_SENT = 0 + private val VIEW_TYPE_MSG_RECEIVED = 1 + private val VIEW_TYPE_CHAT_WIDGET = 2 + + private var context: Context + private var callbacks: ChatMessageInterface + private var chatMessageList: ArrayList = ArrayList() + + init { + this.context = context + this.chatMessageList = list + this.callbacks = callbacks + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + val inflater = LayoutInflater.from(context) + + return when (viewType) { + VIEW_TYPE_MSG_SENT -> { + SentMessageViewHolder( + inflater.inflate( + R.layout.item_container_sent_message, parent, false + ) + ) + } + + VIEW_TYPE_MSG_RECEIVED -> { + ReceivedMessageViewHolder( + inflater.inflate( + R.layout.item_container_received_message, parent, false + ) + ) + } + + else -> { + ChatWidgetViewHolder( + inflater.inflate( + R.layout.item_container_chat_widget, parent, false + ) + ) + } + } + } + + override fun getItemCount(): Int { + return chatMessageList.size + } + + override fun getItemViewType(position: Int): Int { + return chatMessageList[position].type + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + val index = holder.adapterPosition + val chatMessageModel: ChatMessageModel = chatMessageList[index] + when (chatMessageModel.type) { + VIEW_TYPE_MSG_SENT -> { + setMessageData(holder as SentMessageViewHolder, chatMessageModel) + } + + VIEW_TYPE_MSG_RECEIVED -> { + setMessageData(holder as ReceivedMessageViewHolder, chatMessageModel) + } + + else -> { + setMessageData(holder as ChatWidgetViewHolder, chatMessageModel) + } + } + } + + private fun setMessageData(holder: SentMessageViewHolder, data: ChatMessageModel) { + holder.txtMessage.text = data.content + } + + private fun setMessageData(holder: ReceivedMessageViewHolder, data: ChatMessageModel) { + if (data.hasImage) { + data.image?.let { image -> + val originBitmap = BitmapFactory.decodeByteArray(image, 0, image.size) + val radius = context.resources.getDimensionPixelSize(R.dimen.radius_small) + val bmp = ImageHelper.getRoundedCornerBitmap(originBitmap, radius) + holder.imgMessage.visibility = View.VISIBLE + holder.imgMessage.setImageBitmap(bmp) + + data.content?.let { message -> + holder.txtMessage.text = message + } ?: run { + holder.txtMessage.visibility = View.GONE + } + } + } else { + holder.txtMessage.text = data.content + holder.imgMessage.visibility = View.GONE + holder.txtMessage.visibility = View.VISIBLE + } + } + + private fun setMessageData(holder: ChatWidgetViewHolder, data: ChatMessageModel) { + when (data.content) { + Constants.TYPE_WIDGET_SMS -> { + val sendSmsWidget = SendSmsWidget(context).apply { + this.callback = callbacks + } + holder.itemLayout.addView(sendSmsWidget) + holder.itemLayout.visibility = View.VISIBLE + } + + Constants.TYPE_WIDGET_HELP_PROMPT -> { +// val helpPromptWidget = HelpPromptWidget(context) + } + + Constants.TYPE_WIDGET_SEARCH_CONTACT -> { + + } + + Constants.TYPE_WIDGET_FEEDBACK -> { + + } + } + } + + inner class ReceivedMessageViewHolder internal constructor(itemView: View) : + RecyclerView.ViewHolder(itemView) { + var txtMessage: TextView + var imgMessage: ImageView + var itemLayout: ConstraintLayout + + init { + txtMessage = itemView.findViewById(R.id.txt_message) as TextView + imgMessage = itemView.findViewById(R.id.img_message) as ImageView + itemLayout = itemView.findViewById(R.id.cl_received_message) as ConstraintLayout + } + } + + inner class SentMessageViewHolder internal constructor(itemView: View) : + RecyclerView.ViewHolder(itemView) { + var txtMessage: TextView + var imgMessage: ImageView + var itemLayout: ConstraintLayout + + init { + txtMessage = itemView.findViewById(R.id.txt_message) as TextView + imgMessage = itemView.findViewById(R.id.img_message) as ImageView + itemLayout = itemView.findViewById(R.id.cl_sent_message) as ConstraintLayout + } + } + + inner class ChatWidgetViewHolder internal constructor(itemView: View) : + RecyclerView.ViewHolder(itemView) { + var itemLayout: FrameLayout + + init { + itemLayout = itemView.findViewById(R.id.fl_widget_message) as FrameLayout + } + } +} \ No newline at end of file diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/dialogs/CommonConfirmDialog.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/chat/view/dialogs/ConfirmDialog.kt similarity index 55% rename from Android/app/src/main/java/com/matthaigh27/chatgptwrapper/dialogs/CommonConfirmDialog.kt rename to Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/chat/view/dialogs/ConfirmDialog.kt index 5cea7908d29de7db5d74c6aeeaf97c84dddb558e..08608e8e12bf8fa267ad2d9b7c7f6f15b8776e47 100644 --- a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/dialogs/CommonConfirmDialog.kt +++ b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/chat/view/dialogs/ConfirmDialog.kt @@ -1,4 +1,4 @@ -package com.matthaigh27.chatgptwrapper.dialogs +package com.matthaigh27.chatgptwrapper.ui.chat.view.dialogs import android.app.Dialog import android.content.Context @@ -12,62 +12,59 @@ import android.widget.Button import android.widget.TextView import com.matthaigh27.chatgptwrapper.R -class CommonConfirmDialog(context: Context) : Dialog(context), View.OnClickListener { +class ConfirmDialog(context: Context) : Dialog(context), View.OnClickListener { - private var mTvMessage: TextView? = null - private var mMessage: String = "" - private lateinit var mClickListener: OnConfirmButtonClickListener + private var txtMessage: TextView? = null + private lateinit var btnClickListener: OnDialogButtonClickListener init { setCancelable(false) } - fun setOnClickListener(listener: OnConfirmButtonClickListener) { - mClickListener = listener - } - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) initView() } + override fun onClick(view: View?) { + when (view?.id) { + R.id.btn_ok -> { + btnClickListener.onPositiveButtonClick() + } + + R.id.btn_cancel -> { + btnClickListener.onNegativeButtonClick() + } + } + this.dismiss() + } + private fun initView() { requestWindowFeature(Window.FEATURE_NO_TITLE) - setContentView(R.layout.dialog_common_confirm) + setContentView(R.layout.dialog_confirm) - window!!.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) - window!!.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) + window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) + window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) findViewById