diff --git a/.gitignore b/.gitignore index e7b9f8d296dfc60c809a9e32f9f473f1bc9698af..07958fdf4391420420b3b52bc97c0feec66c5adc 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,4 @@ /.idea Brain/firebase_cred.json Brain/logs/* - +**/.DS_Store diff --git a/Android/app/src/main/AndroidManifest.xml b/Android/app/src/main/AndroidManifest.xml index 51be55a1a2968d64f0b101d360576b37cc77cdbf..390f27ef59f40a12a2fd9677246e4fd7e80405b2 100644 --- a/Android/app/src/main/AndroidManifest.xml +++ b/Android/app/src/main/AndroidManifest.xml @@ -39,18 +39,20 @@ android:supportsRtl="true" android:theme="@style/AppTheme"> - + android:name=".ui.splash.SplashActivity" + android:exported="true"> + + +) { + override fun toString(): String { + val gson = Gson() + return gson.toJson(this) + } + + companion object { + fun init(string: String): MailsProps { + val gson = Gson() + return gson.fromJson(string, MailsProps::class.java) + } + } +} \ No newline at end of file diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/models/chatwidgetprops/ScheduleAlarmProps.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/models/chatwidgetprops/ScheduleAlarmProps.kt index 5e91e71c3058d603a2b93215296285ab5aba6f3e..fc1e1cacef4c221225a63de309e9b8498cb6adcd 100644 --- a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/models/chatwidgetprops/ScheduleAlarmProps.kt +++ b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/models/chatwidgetprops/ScheduleAlarmProps.kt @@ -9,29 +9,6 @@ data class ScheduleAlarmProps( val label: String? = null, val repeat: BooleanArray? = null ) { - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as ScheduleAlarmProps - - if (time != other.time) return false - if (repeat != null) { - if (other.repeat == null) return false - if (!repeat.contentEquals(other.repeat)) return false - } else if (other.repeat != null) return false - if (label != other.label) return false - - return true - } - - override fun hashCode(): Int { - var result = time?.hashCode() ?: 0 - result = 31 * result + (repeat?.contentHashCode() ?: 0) - result = 31 * result + (label?.hashCode() ?: 0) - return result - } - override fun toString(): String { val gson = Gson() return gson.toJson(this) 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 index 27b0809674a361711884f29b7c26c759129fdbe8..c7a9ea2da8573c066146df5b8544112313a978fa 100644 --- 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 @@ -1,25 +1,27 @@ package com.matthaigh27.chatgptwrapper.data.remote import com.matthaigh27.chatgptwrapper.utils.constants.CommonConstants.API_BASE_URL -import com.matthaigh27.chatgptwrapper.utils.constants.CommonConstants.TIME_OUT_CALL -import com.matthaigh27.chatgptwrapper.utils.constants.CommonConstants.TIME_OUT_CONNECT -import com.matthaigh27.chatgptwrapper.utils.constants.CommonConstants.TIME_OUT_READ -import com.matthaigh27.chatgptwrapper.utils.constants.CommonConstants.TIME_OUT_WRITE 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() + val TIME_OUT_CALL = 60L + val TIME_OUT_CONNECT = 60L + val TIME_OUT_READ = 60L + val TIME_OUT_WRITE = 60L + + private val client = OkHttpClient + .Builder() .callTimeout(TIME_OUT_CALL, TimeUnit.SECONDS) .connectTimeout(TIME_OUT_CONNECT, TimeUnit.SECONDS) .readTimeout(TIME_OUT_READ, TimeUnit.SECONDS) .writeTimeout(TIME_OUT_WRITE, TimeUnit.SECONDS) .build() - private val retrofit = Retrofit.Builder() + private val retrofit = Retrofit + .Builder() .baseUrl(API_BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .client(client) 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 index a10d70c74c0540a89045b9e6ca5d14b227a42000..6583064efcde18a0bf5bc11834cffdc6804d302e 100644 --- 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 @@ -1,8 +1,7 @@ package com.matthaigh27.chatgptwrapper.data.remote sealed class ApiResource( - val data: T? = null, - val message: String? = null + val data: T? = null, val message: String? = null ) { class Success(data: T) : ApiResource(data) class Error(message: String, data: T? = null) : ApiResource(data, message) 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 index 350558e4533aa663e16f26f26514c643076e3ecd..b92e422e874cf1ad5c2b239701383f08bb871e42 100644 --- 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 @@ -1,8 +1,11 @@ package com.matthaigh27.chatgptwrapper.data.remote +import com.matthaigh27.chatgptwrapper.data.models.chat.MailModel import com.matthaigh27.chatgptwrapper.data.remote.requests.BaseApiRequest +import com.matthaigh27.chatgptwrapper.data.remote.requests.ComposeMailApiRequest import com.matthaigh27.chatgptwrapper.data.remote.requests.ImageRelatednessApiRequest import com.matthaigh27.chatgptwrapper.data.remote.requests.NotificationApiRequest +import com.matthaigh27.chatgptwrapper.data.remote.requests.ReadMailApiRequest import com.matthaigh27.chatgptwrapper.data.remote.requests.TrainContactsApiRequest import com.matthaigh27.chatgptwrapper.data.remote.requests.TrainImageApiRequest import com.matthaigh27.chatgptwrapper.data.remote.responses.ApiResponse @@ -16,13 +19,23 @@ import retrofit2.http.POST interface ApiService { @POST("commands") - fun getAllHelpCommands(@Body request: BaseApiRequest) : Call> + fun getAllHelpCommands(@Body request: BaseApiRequest): Call> + @POST("sendNotification") - fun sendNotification(@Body request: NotificationApiRequest) : Call> + fun sendNotification(@Body request: NotificationApiRequest): Call> + @POST("train/contacts") - fun trainContacts(@Body request: TrainContactsApiRequest) : Call> + fun trainContacts(@Body request: TrainContactsApiRequest): Call> + @POST("image_relatedness") - fun getImageRelatedness(@Body request: ImageRelatednessApiRequest) : Call> + fun getImageRelatedness(@Body request: ImageRelatednessApiRequest): Call> + @POST("uploadImage") - fun trainImage(@Body request: TrainImageApiRequest) : Call> + fun trainImage(@Body request: TrainImageApiRequest): Call> + + @POST("email/read_emails") + fun readEmails(@Body request: ReadMailApiRequest): Call>> + + @POST("email/send_email") + fun sendEmail(@Body request: ComposeMailApiRequest): Call> } \ No newline at end of file diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/requests/ComposeMailApiRequest.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/requests/ComposeMailApiRequest.kt new file mode 100644 index 0000000000000000000000000000000000000000..4420fbdc772d2f26b9a0e8de7e7139e81d6b6cf6 --- /dev/null +++ b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/requests/ComposeMailApiRequest.kt @@ -0,0 +1,19 @@ +package com.matthaigh27.chatgptwrapper.data.remote.requests + +import com.matthaigh27.chatgptwrapper.data.remote.requests.common.Keys + +data class ComposeMailApiRequest( + val data: ComposeMailData, + val confs: Keys +) + +data class ComposeMailData( + private val sender: String, + private val pwd: String, + private val to: String, + private val subject: String, + private val body: String, + private val to_send: Boolean, + private val filename: String, + private val file_content: String +) \ No newline at end of file diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/requests/ReadMailApiRequest.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/requests/ReadMailApiRequest.kt new file mode 100644 index 0000000000000000000000000000000000000000..57fc69d137020b91f851ddc09384abe3c5d00aec --- /dev/null +++ b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/requests/ReadMailApiRequest.kt @@ -0,0 +1,14 @@ +package com.matthaigh27.chatgptwrapper.data.remote.requests + +import com.matthaigh27.chatgptwrapper.data.remote.requests.common.Keys + +data class ReadMailApiRequest( + val data: ReadMailData, + val confs: Keys +) + +data class ReadMailData( + val sender: String, + val pwd: String, + val imap_folder: String, +) \ 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 index eea004d6a8e78c4187d1731ddac677639163cf18..ef76ad39d88dfbb97cf01c8c5b884587f9dea5fd 100644 --- 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 @@ -1,6 +1,6 @@ package com.matthaigh27.chatgptwrapper.data.remote.responses -data class ApiResponse ( +data class ApiResponse( val status_code: Int, val message: List, val result: T diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/responses/results/CommonResult.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/responses/results/CommonResult.kt index 8c0f53d90f43fbd896010bf4ee170d1654ae0a78..16cc8a284ba1e0869736d388338117983df6ab63 100644 --- a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/responses/results/CommonResult.kt +++ b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/responses/results/CommonResult.kt @@ -2,7 +2,7 @@ package com.matthaigh27.chatgptwrapper.data.remote.responses.results import com.google.gson.JsonElement -data class CommonResult ( +data class CommonResult( val program: String, val content: JsonElement ) \ No newline at end of file diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/responses/results/ImageRelatenessResult.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/responses/results/ImageRelatenessResult.kt index d24ea8c7a58ce206077713f329b665d8364cb42c..f8cec8336edfa1bf52fefda5fcba94b41908e3a6 100644 --- a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/responses/results/ImageRelatenessResult.kt +++ b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/responses/results/ImageRelatenessResult.kt @@ -5,7 +5,7 @@ data class ImageRelatenessResult( val content: ImageRelatenessContent ) -data class ImageRelatenessContent ( +data class ImageRelatenessContent( val image_name: String, val image_desc: 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 index adbb2166f9d2d809dcc6bc049d28d82ebbe9bc70..4cfb2204a91177f6dc46dcdecf3f47d93cf72652 100644 --- 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 @@ -1,21 +1,15 @@ package com.matthaigh27.chatgptwrapper.data.repository import com.google.firebase.storage.FirebaseStorage -import com.google.protobuf.Empty -import com.matthaigh27.chatgptwrapper.data.models.chat.AutoTaskModel -import com.matthaigh27.chatgptwrapper.data.remote.ApiResource import com.matthaigh27.chatgptwrapper.utils.helpers.OnFailure import com.matthaigh27.chatgptwrapper.utils.helpers.OnSuccess -import kotlinx.coroutines.suspendCancellableCoroutine import java.util.UUID import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine object FirebaseRepository { fun downloadImageWithName( - name: String, - onSuccess: OnSuccess, - onFailure: OnFailure + name: String, onSuccess: OnSuccess, onFailure: OnFailure ) { val reference = "images/$name" @@ -29,9 +23,7 @@ object FirebaseRepository { } fun uploadImageAsync( - imageByteArray: ByteArray, - onSuccess: OnSuccess, - onFailure: OnFailure + imageByteArray: ByteArray, onSuccess: OnSuccess, onFailure: OnFailure ) { val storageRef = FirebaseStorage.getInstance().reference val uuid = UUID.randomUUID() 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 index 4fd15d0c46dcce36333cdd9cf0b67b8efbb9511c..7774ec3896f697345b8d21beda4308d5841e0ee8 100644 --- 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 @@ -1,12 +1,15 @@ package com.matthaigh27.chatgptwrapper.data.repository import com.matthaigh27.chatgptwrapper.RisingApplication.Companion.appContext +import com.matthaigh27.chatgptwrapper.data.models.chat.MailModel import com.matthaigh27.chatgptwrapper.data.models.setting.SettingModel import com.matthaigh27.chatgptwrapper.data.remote.ApiClient import com.matthaigh27.chatgptwrapper.data.remote.ApiResource import com.matthaigh27.chatgptwrapper.data.remote.requests.BaseApiRequest +import com.matthaigh27.chatgptwrapper.data.remote.requests.ComposeMailApiRequest import com.matthaigh27.chatgptwrapper.data.remote.requests.ImageRelatednessApiRequest import com.matthaigh27.chatgptwrapper.data.remote.requests.NotificationApiRequest +import com.matthaigh27.chatgptwrapper.data.remote.requests.ReadMailApiRequest import com.matthaigh27.chatgptwrapper.data.remote.requests.TrainContactsApiRequest import com.matthaigh27.chatgptwrapper.data.remote.requests.TrainImageApiRequest import com.matthaigh27.chatgptwrapper.data.remote.requests.common.Keys @@ -45,7 +48,8 @@ object RemoteRepository { } fun getAllHelpCommands( - onSuccess: OnSuccess>, onFailure: OnFailure + onSuccess: OnSuccess>, + onFailure: OnFailure ) { val call = apiService.getAllHelpCommands(BaseApiRequest(getKeys())) @@ -154,4 +158,52 @@ object RemoteRepository { } }) } + + fun readEmails( + request: ReadMailApiRequest, + onSuccess: OnSuccess>>, + onFailure: OnFailure + ) { + val call = apiService.readEmails(request) + + call.enqueue(object : Callback>> { + override fun onResponse( + call: Call>>, response: Response>> + ) { + response.body()?.let { data -> + onSuccess(data) + } ?: run { + onFailure(response.message()) + } + } + + override fun onFailure(call: Call>>, t: Throwable) { + onFailure(t.message.toString()) + } + }) + } + + fun sendEmail( + request: ComposeMailApiRequest, + onSuccess: OnSuccess>, + onFailure: OnFailure + ) { + val call = apiService.sendEmail(request) + + call.enqueue(object : Callback> { + override fun onResponse( + call: Call>, response: Response> + ) { + response.body()?.let { data -> + onSuccess(data) + } ?: 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/SharedPreferencesRepository.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/repository/SharedPreferencesRepository.kt index a5084b3903372d3eaef19b5f017a5e7eedec235e..fa725cc3450b7c58f73850ce5587a56f7ed3d4fb 100644 --- a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/repository/SharedPreferencesRepository.kt +++ b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/repository/SharedPreferencesRepository.kt @@ -1,7 +1,6 @@ package com.matthaigh27.chatgptwrapper.data.repository import android.content.Context -import android.content.SharedPreferences import com.matthaigh27.chatgptwrapper.RisingApplication.Companion.appContext import com.matthaigh27.chatgptwrapper.data.models.setting.SettingModel import com.matthaigh27.chatgptwrapper.utils.helpers.chat.SettingHelper.emptySettingModel @@ -18,7 +17,7 @@ object SharedPreferencesRepository { fun getConfig(): SettingModel { val sharedPreferences = appContext.getSharedPreferences("prefs", Context.MODE_PRIVATE) val jsonString = sharedPreferences.getString("config", "") - if(jsonString == "" || jsonString == null) { + if (jsonString == "" || jsonString == null) { return emptySettingModel() } else { return SettingModel.init(jsonString) diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/chat/view/ChatActivity.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/chat/view/ChatActivity.kt index dfb736b69b0dfca23ae0c7af8a4b8f80444161c4..e44895d3582d1b630905222f6d5abef955d1eeae 100644 --- a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/chat/view/ChatActivity.kt +++ b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/chat/view/ChatActivity.kt @@ -1,100 +1,16 @@ 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 androidx.appcompat.app.AppCompatActivity import com.matthaigh27.chatgptwrapper.R 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 ChatActivity : BaseActivity() { - - private val PERMISSIONS_REQUEST_CODE = 1 - private lateinit var permissions: Array - 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_chat) - - requestPermissions() - } - - private fun requestPermissions() { - /** - * In mobile phones that use Google API 33 or higher, the permission for reading external storage - * is disabled because the phones don't support the feature. - */ - permissions = if(Build.VERSION.SDK_INT > Build.VERSION_CODES.S_V2) { - arrayOf( - Manifest.permission.SEND_SMS, - Manifest.permission.READ_CONTACTS, - Manifest.permission.CALL_PHONE - ) - } else { - arrayOf( - Manifest.permission.SEND_SMS, - Manifest.permission.READ_CONTACTS, - Manifest.permission.CALL_PHONE, - Manifest.permission.READ_EXTERNAL_STORAGE - ) - } - val notGrantedPermissions = permissions.filter { - checkSelfPermission(it) != PackageManager.PERMISSION_GRANTED - } - - if (notGrantedPermissions.isNotEmpty()) { - if (shouldShowRequestPermissionRationale(notGrantedPermissions[0])) { - // show custom permission rationale - val confirmDialog = ConfirmDialog(this@ChatActivity) - confirmDialog.setOnClickListener(object : - ConfirmDialog.OnDialogButtonClickListener { - override fun onPositiveButtonClick() { - requestPermissions( - notGrantedPermissions.toTypedArray(), PERMISSIONS_REQUEST_CODE - ) - } - - override fun onNegativeButtonClick() { - finish() - } - }) - - confirmDialog.show() - confirmDialog.setMessage(CONFIRM_MESSAGE) - - } else { - requestPermissions(notGrantedPermissions.toTypedArray(), PERMISSIONS_REQUEST_CODE) - } - } else { - // Permissions already granted, navigate to your desired fragment - navigateToChatMainFragment() - } - } - - @SuppressLint("MissingSuperCall") - override fun onRequestPermissionsResult( - requestCode: Int, permissions: Array, grantResults: IntArray - ) { - when (requestCode) { - PERMISSIONS_REQUEST_CODE -> { - if (grantResults.all { it == PackageManager.PERMISSION_GRANTED }) { - // Permissions granted, navigate to your desired fragment - navigateToChatMainFragment() - } else { - requestPermissions() - } - return - } - } + navigateToChatMainFragment() } private fun navigateToChatMainFragment() { 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 index e7db4f55a39bf961bb299d0c208e166329a237c4..a91a7d69cf577d01328f236b352c298d2136092d 100644 --- 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 @@ -14,6 +14,7 @@ import androidx.recyclerview.widget.RecyclerView import com.matthaigh27.chatgptwrapper.R import com.matthaigh27.chatgptwrapper.data.models.chat.ChatMessageModel import com.matthaigh27.chatgptwrapper.data.models.chat.HelpPromptModel +import com.matthaigh27.chatgptwrapper.data.models.chatwidgetprops.MailsProps import com.matthaigh27.chatgptwrapper.data.models.chatwidgetprops.ScheduleAlarmProps import com.matthaigh27.chatgptwrapper.ui.chat.view.interfaces.ChatMessageInterface import com.matthaigh27.chatgptwrapper.ui.chat.view.interfaces.OnHideListener @@ -21,10 +22,12 @@ import com.matthaigh27.chatgptwrapper.ui.chat.view.widgets.chatwidget.SendSmsWid import com.matthaigh27.chatgptwrapper.ui.chat.view.widgets.chatwidget.alarm.ScheduleAlarmWidget import com.matthaigh27.chatgptwrapper.ui.chat.view.widgets.chatwidget.contact.ContactWidget import com.matthaigh27.chatgptwrapper.ui.chat.view.widgets.chatwidget.helpprompt.HelpPromptWidget +import com.matthaigh27.chatgptwrapper.ui.chat.view.widgets.chatwidget.mail.ComposeMailWidget import com.matthaigh27.chatgptwrapper.ui.chat.view.widgets.chatwidget.mail.MailWidget -import com.matthaigh27.chatgptwrapper.ui.chat.view.widgets.chatwidget.mail.compose.ComposeMailWidget +import com.matthaigh27.chatgptwrapper.ui.chat.view.widgets.chatwidget.mail.ReadMailWidget import com.matthaigh27.chatgptwrapper.utils.constants.CommonConstants.PROPS_WIDGET_DESC import com.matthaigh27.chatgptwrapper.utils.constants.TypeChatWidgetConstants.TYPE_WIDGET_HELP_PROMPT +import com.matthaigh27.chatgptwrapper.utils.constants.TypeChatWidgetConstants.TYPE_WIDGET_MAILS import com.matthaigh27.chatgptwrapper.utils.constants.TypeChatWidgetConstants.TYPE_WIDGET_MAIL_READ import com.matthaigh27.chatgptwrapper.utils.constants.TypeChatWidgetConstants.TYPE_WIDGET_MAIL_WRITE import com.matthaigh27.chatgptwrapper.utils.constants.TypeChatWidgetConstants.TYPE_WIDGET_SCHEDULE_ALARM @@ -35,19 +38,29 @@ import com.matthaigh27.chatgptwrapper.utils.helpers.chat.ContactHelper.getContac import com.matthaigh27.chatgptwrapper.utils.helpers.chat.ImageHelper import org.json.JSONArray +/** + * This adapter class is used to display a list of chat messages on a recycler view. + */ class ChatMainAdapter( context: Context, list: ArrayList, callbacks: ChatMessageInterface ) : RecyclerView.Adapter() { + /** + * These variables are used to presents the type of messages + */ private val VIEW_TYPE_MSG_SENT = 0 private val VIEW_TYPE_MSG_RECEIVED = 1 private val VIEW_TYPE_CHAT_WIDGET = 2 private val VIEW_TYPE_CHAT_ERROR = 3 private var context: Context - private var callbacks: ChatMessageInterface private var chatMessageList: ArrayList = ArrayList() + /** + * This is a callback that retrieves result from chat widgets. + */ + private var callbacks: ChatMessageInterface + init { this.context = context this.chatMessageList = list @@ -114,7 +127,13 @@ class ChatMainAdapter( } } + /** + * This function is used to set data for common messages. + */ private fun setMessageData(holder: MessageViewHolder, data: ChatMessageModel) { + /** + * If an image is included into a message, the image is displayed by below code. + */ if (data.hasImage) { data.image?.let { image -> val originBitmap = BitmapFactory.decodeByteArray(image, 0, image.size) @@ -140,12 +159,18 @@ class ChatMainAdapter( } } + /** + * This function is used to display chat widgets on a recycler view. + */ private fun setMessageData(holder: ChatWidgetViewHolder, data: ChatMessageModel) { holder.itemLayout.visibility = View.VISIBLE holder.llHorizontalScroll.removeAllViews() holder.itemLayout.removeAllViews() val index = holder.adapterPosition + /** + * Depending on type of widget, the proper widget is displayed. + */ when (data.content) { TYPE_WIDGET_SMS -> { val sendSmsWidget = SendSmsWidget(context).apply { @@ -222,19 +247,46 @@ class ChatMainAdapter( holder.itemLayout.addView(scheduleAlarmWidget) } - TYPE_WIDGET_MAIL_READ -> { + TYPE_WIDGET_MAILS -> { holder.llHorizontalScroll.visibility = View.VISIBLE + val props = data.data?.run { + val widgetDesc = data.data.asJsonObject[PROPS_WIDGET_DESC].asString + MailsProps.init(widgetDesc) + } - for (i in 0 until 10) { - val mailWidget = MailWidget(context).apply { + props?.mails?.forEach { mail -> + val mailWidget = MailWidget(context, mail).apply { this.callback = callbacks } holder.llHorizontalScroll.addView(mailWidget) } } + TYPE_WIDGET_MAIL_READ -> { + val readMailWidget = ReadMailWidget(context).apply { + this.callback = callbacks + this.hideListener = object : OnHideListener { + override fun hide() { + holder.itemLayout.visibility = View.GONE + chatMessageList.removeAt(index) + notifyItemRemoved(index) + } + } + } + holder.itemLayout.addView(readMailWidget) + } + TYPE_WIDGET_MAIL_WRITE -> { - val composeMailWIdget = ComposeMailWidget(context) + val composeMailWIdget = ComposeMailWidget(context).apply { + this.callback = callbacks + this.hideListener = object : OnHideListener { + override fun hide() { + holder.itemLayout.visibility = View.GONE + chatMessageList.removeAt(index) + notifyItemRemoved(index) + } + } + } holder.itemLayout.addView(composeMailWIdget) } @@ -244,6 +296,9 @@ class ChatMainAdapter( } } + /** + * ViewHolder for common messages with message and image + */ inner class MessageViewHolder internal constructor(itemView: View) : RecyclerView.ViewHolder(itemView) { var txtMessage: TextView @@ -257,6 +312,9 @@ class ChatMainAdapter( } } + /** + * ViewHolder for chat widgets + */ inner class ChatWidgetViewHolder internal constructor(itemView: View) : RecyclerView.ViewHolder(itemView) { var itemLayout: FrameLayout diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/chat/view/dialogs/ConfirmDialog.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/chat/view/dialogs/ConfirmDialog.kt index 08608e8e12bf8fa267ad2d9b7c7f6f15b8776e47..c69bd29e5d3fdc4b342f903de85ab2939768f12c 100644 --- a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/chat/view/dialogs/ConfirmDialog.kt +++ b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/chat/view/dialogs/ConfirmDialog.kt @@ -12,6 +12,9 @@ import android.widget.Button import android.widget.TextView import com.matthaigh27.chatgptwrapper.R +/** + * This dialog is used to show confirm message to users. + */ class ConfirmDialog(context: Context) : Dialog(context), View.OnClickListener { private var txtMessage: TextView? = null diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/chat/view/fragments/ChatMainFragment.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/chat/view/fragments/ChatMainFragment.kt index 68f63eaf018883bbc5dbb27dd26518a1a1c79712..544735e7f0e5e19558f3111ce7fe99b5203822fe 100644 --- a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/chat/view/fragments/ChatMainFragment.kt +++ b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/chat/view/fragments/ChatMainFragment.kt @@ -23,6 +23,8 @@ import com.matthaigh27.chatgptwrapper.R import com.matthaigh27.chatgptwrapper.data.models.chat.ChatMessageModel import com.matthaigh27.chatgptwrapper.data.models.chat.HelpCommandModel import com.matthaigh27.chatgptwrapper.data.models.chat.HelpPromptModel +import com.matthaigh27.chatgptwrapper.data.models.chat.MailModel +import com.matthaigh27.chatgptwrapper.data.models.chatwidgetprops.MailsProps import com.matthaigh27.chatgptwrapper.data.models.chatwidgetprops.ScheduleAlarmProps import com.matthaigh27.chatgptwrapper.data.models.common.Time import com.matthaigh27.chatgptwrapper.data.remote.ApiResource @@ -33,9 +35,11 @@ import com.matthaigh27.chatgptwrapper.ui.chat.view.interfaces.ChatMessageInterfa import com.matthaigh27.chatgptwrapper.ui.chat.view.widgets.toolbar.ChatToolsWidget import com.matthaigh27.chatgptwrapper.ui.chat.viewmodel.ChatViewModel import com.matthaigh27.chatgptwrapper.utils.constants.CommonConstants.ERROR_MSG_NOEXIST_COMMAND +import com.matthaigh27.chatgptwrapper.utils.constants.CommonConstants.ERROR_MSG_UNKNOWN_ERROR import com.matthaigh27.chatgptwrapper.utils.constants.CommonConstants.HELP_COMMAND_ALL import com.matthaigh27.chatgptwrapper.utils.constants.CommonConstants.PROPS_WIDGET_DESC import com.matthaigh27.chatgptwrapper.utils.constants.TypeChatWidgetConstants.TYPE_WIDGET_HELP_PROMPT +import com.matthaigh27.chatgptwrapper.utils.constants.TypeChatWidgetConstants.TYPE_WIDGET_MAILS import com.matthaigh27.chatgptwrapper.utils.constants.TypeChatWidgetConstants.TYPE_WIDGET_MAIL_READ import com.matthaigh27.chatgptwrapper.utils.constants.TypeChatWidgetConstants.TYPE_WIDGET_MAIL_WRITE import com.matthaigh27.chatgptwrapper.utils.constants.TypeChatWidgetConstants.TYPE_WIDGET_SCHEDULE_ALARM @@ -50,8 +54,10 @@ import com.matthaigh27.chatgptwrapper.utils.constants.TypeResponseConstants.TYPE import com.matthaigh27.chatgptwrapper.utils.constants.TypeResponseConstants.TYPE_RESPONSE_MAIL_SEND import com.matthaigh27.chatgptwrapper.utils.constants.TypeResponseConstants.TYPE_RESPONSE_MAIL_WRITE import com.matthaigh27.chatgptwrapper.utils.constants.TypeResponseConstants.TYPE_RESPONSE_MESSAGE +import com.matthaigh27.chatgptwrapper.utils.constants.TypeResponseConstants.TYPE_RESPONSE_SMS import com.matthaigh27.chatgptwrapper.utils.helpers.Converter import com.matthaigh27.chatgptwrapper.utils.helpers.Converter.responseToHelpPromptList +import com.matthaigh27.chatgptwrapper.utils.helpers.OnSuccess import com.matthaigh27.chatgptwrapper.utils.helpers.chat.CommandHelper.getHelpCommandFromStr import com.matthaigh27.chatgptwrapper.utils.helpers.chat.CommandHelper.isMainHelpCommand import com.matthaigh27.chatgptwrapper.utils.helpers.chat.CommandHelper.makePromptItemUsage @@ -59,8 +65,17 @@ import com.matthaigh27.chatgptwrapper.utils.helpers.chat.CommandHelper.makePromp import com.matthaigh27.chatgptwrapper.utils.helpers.ui.NoNewLineInputFilter import org.json.JSONArray +/** + * A ChatMainFragment class is a class for chatting with users and show the result that + * our AI plugin - Brain returned. + * + * In this class, almost important features are implemented. + */ class ChatMainFragment : Fragment(), OnClickListener { + /** + * These variables(TYPE_CHAT_XXX) are variables that present type of widget to display on chatting list. + */ private val TYPE_CHAT_SENT = 0 private val TYPE_CHAT_RECEIVE = 1 private val TYPE_CHAT_WIDGET = 2 @@ -69,10 +84,12 @@ class ChatMainFragment : Fragment(), OnClickListener { private lateinit var rootView: View lateinit var viewModel: ChatViewModel - /** View Components */ private var loadingRotate: RotateAnimation? = null private var edtMessageInput: EditText? = null + /** + * These variables are for recyclerview that shows chatting list items. + */ private var rvChatList: RecyclerView? = null private var chatMainAdapter: ChatMainAdapter? = null private var chatMessageList: ArrayList = ArrayList() @@ -81,9 +98,16 @@ class ChatMainFragment : Fragment(), OnClickListener { private var chatToolsWidget: ChatToolsWidget? = null private var helpPromptList: ArrayList? = null - private var currentSelectedImage: ByteArray? = null - private var currentUploadedImageName: String? = null - private var isImagePicked: Boolean = false + /** + * These variables are used to identify whether users picked an image to send to Brain. + */ + private var currentSelectedImage: ByteArray? = + null // bytearray data of a picked image by camera or gallery + private var currentUploadedImageName: String? = + null // resource name that is uploaded to firebase storage + private var isImagePicked: Boolean = + false // boolean variable that presents whether users picked an image + private var showLoadingCount = 0 override fun onCreateView( @@ -95,6 +119,9 @@ class ChatMainFragment : Fragment(), OnClickListener { } private fun init() { + /** + * These are functions to init views + */ initViewModel() initLoadingRotate() initButtonsClickListener() @@ -105,11 +132,16 @@ class ChatMainFragment : Fragment(), OnClickListener { fetchAllCommands() } - + /** + * This function is used to init viewmodel. + */ private fun initViewModel() { viewModel = ViewModelProvider(this)[ChatViewModel::class.java] } + /** + * This function is used to init rotate animation for loading spinner. + */ private fun initLoadingRotate() { loadingRotate = RotateAnimation(/* fromDegrees = */ 0f, /* toDegrees = */ 720f, /* pivotXType = */ @@ -124,11 +156,17 @@ class ChatMainFragment : Fragment(), OnClickListener { } } + /** + * In this function, click listener is set to buttons on main chat interface. + */ private fun initButtonsClickListener() { rootView.findViewById(R.id.btn_open_chat_tools).setOnClickListener(this) rootView.findViewById(R.id.btn_audio_recognition).setOnClickListener(this) } + /** + * Send text message to Brain when users finish typing text to EditText component. + */ private fun initChatInputListener() { edtMessageInput = rootView.findViewById(R.id.edt_message) edtMessageInput?.filters = edtMessageInput?.filters?.plus(NoNewLineInputFilter()) @@ -141,7 +179,13 @@ class ChatMainFragment : Fragment(), OnClickListener { } } + /** + * This function is used to init recyclerview that stores chatting messages. + */ private fun initChatRecyclerView() { + /** + * Init callback function that binds the event emitted in all chat widgets + */ initChatInterface() rvChatList = rootView.findViewById(R.id.rv_chat_list) @@ -153,6 +197,10 @@ class ChatMainFragment : Fragment(), OnClickListener { rvChatList?.layoutManager = LinearLayoutManager(context) } + /** + * This function is used to init chat tool widget that pops up when users click plus button + * at the right bottom corner. + */ private fun initChatToolsWidget() { chatToolsWidget = ChatToolsWidget(requireContext(), requireActivity()).apply { this.callback = chatMessageInterface @@ -161,6 +209,14 @@ class ChatMainFragment : Fragment(), OnClickListener { llToolBar.addView(chatToolsWidget) } + /** + * This function is used to show loading spinner. + * + * If isLoading is true, loading spinner is displayed. But loading spinner might not be visible, + * even though isLoading is true. Current visible loading spinner count should be 0 to make loading spinner invisible. + * + * @param isLoading boolean parameter that presents whether loading spinner is visible + */ private fun showLoading(isLoading: Boolean) { val imgLoading = rootView.findViewById(R.id.sp_loading) @@ -179,6 +235,19 @@ class ChatMainFragment : Fragment(), OnClickListener { } } + /** + * This function is used mostly to add messages to chatting list. + * + * According to type of message, messages can be common messages or widgets for sending sms, setting an alarm, sending help prompt, etc. + * Depending on whether an image is contained into message, different network functions are executed. + * If message start with '/', help prompt widget is displayed. + * + * @param type A variable that present type of chat messages + * @param content Contains content of message to present + * @param data Contains properties of widget as json string when type is widget. + * @param hasImage Presents whether an image is contained into message + * @param image If a message has an image, this variable is used to store the bytearray data of the contained image. + */ private fun addMessage( type: Int, content: String? = null, @@ -188,11 +257,17 @@ class ChatMainFragment : Fragment(), OnClickListener { ) { when (type) { TYPE_CHAT_SENT -> { + /** + * If message starts with '/', help prompt widget is displayed. + */ if (content!!.isNotEmpty() && content.first() == '/') { openHelpPromptWidget(content) return } if (isImagePicked) { + /** + * If an image is contained into a message, image relatedness function is executed. + */ addChatItemToList( ChatMessageModel( type = type, @@ -205,6 +280,9 @@ class ChatMainFragment : Fragment(), OnClickListener { fetchImageRelatedness(currentUploadedImageName!!, content) isImagePicked = false } else { + /** + * If not, send notification function is executed. + */ addChatItemToList(ChatMessageModel(type, content, data, hasImage, image)) sendNotification(content) } @@ -216,6 +294,9 @@ class ChatMainFragment : Fragment(), OnClickListener { } } + /** + * This function is used to display messages to show errors in more detail to users + */ private fun addErrorMessage( message: String ) { @@ -233,42 +314,49 @@ class ChatMainFragment : Fragment(), OnClickListener { rvChatList?.scrollToPosition(chatMessageList.size - 1) } - private fun trainContacts() { - viewModel.trainContacts().observe(viewLifecycleOwner) { resource -> - when (resource) { - is ApiResource.Loading -> { - showLoading(true) - } - - is ApiResource.Success -> { - showLoading(false) - } - - is ApiResource.Error -> { - showLoading(false) - } + /** + * This function is used to process ApiResource data from viewmodel. + * + * When status is loading or error, default functions are executed. When success, onSuccess + * lambda function is executed. + * + * @param resource A ApiResource that is retrieved from ViewModel + * @param onSuccess A lambda function to process the resource data + */ + private fun commonProcessResource( + resource: ApiResource, + onSuccess: OnSuccess> + ) { + when (resource) { + is ApiResource.Loading -> { + showLoading(true) } - } - } - private fun trainImages() { - viewModel.trainImages().observe(viewLifecycleOwner) { resource -> - when (resource) { - is ApiResource.Loading -> { - showLoading(true) - } + is ApiResource.Success -> { + showLoading(false) + onSuccess(resource) + } - is ApiResource.Success -> { - showLoading(false) + is ApiResource.Error -> { + showLoading(false) + resource.message?.let { + addErrorMessage(resource.message) + } ?: run { + addErrorMessage(ERROR_MSG_UNKNOWN_ERROR) } - is ApiResource.Error -> { - showLoading(false) - } } } } + /** + * Open help prompt widgets when message is valid help command. + * + * @param message A String variable that contains message users sent + * If message is /help, all the description of help commands is displayed, + * If message is /help prompt name, the description of a given prompt help command is displayed. + * If message is /prompt name, A help prompt widget about the given name is displayed. + */ private fun openHelpPromptWidget(message: String) { try { val command: HelpCommandModel = getHelpCommandFromStr(message) @@ -318,112 +406,90 @@ class ChatMainFragment : Fragment(), OnClickListener { } } } catch (e: Exception) { - e.printStackTrace() addErrorMessage(e.message.toString()) } } + /** + * This Network function is used to fetch all help commands + */ private fun fetchAllCommands() { viewModel.getAllHelpCommands().observe(viewLifecycleOwner) { resource -> - when (resource) { - is ApiResource.Loading -> { - showLoading(true) - } - - is ApiResource.Success -> { - showLoading(false) - resource.data?.let { data -> - helpPromptList = responseToHelpPromptList(data.result.content) - } - } - - is ApiResource.Error -> { - showLoading(false) - addErrorMessage(resource.message!!) + commonProcessResource(resource) { + resource.data?.let { data -> + helpPromptList = responseToHelpPromptList(data.result.content) } } } } + /** + * This Network function is used to fetch a similar image to image that is stored with imageName parameter + * in Firebase storage. + * + * @param imageName The image name of a image that a user uploaded to the Firebase + * @param message The description about the image + */ private fun fetchImageRelatedness(imageName: String, message: String) { viewModel.getImageRelatedness(imageName, message).observe(viewLifecycleOwner) { resource -> - when (resource) { - is ApiResource.Loading -> { - showLoading(true) - } - - is ApiResource.Success -> { - showLoading(false) - addMessage( - type = TYPE_CHAT_RECEIVE, - content = resource.data?.description, - data = null, - hasImage = true, - image = resource.data?.image - ) - } - - is ApiResource.Error -> { - showLoading(false) - addErrorMessage(resource.message!!) - } + commonProcessResource(resource) { + addMessage( + type = TYPE_CHAT_RECEIVE, + content = resource.data?.description, + data = null, + hasImage = true, + image = resource.data?.image + ) } } } + /** + * This network function is used to retrieve a command analyzed with user's message by Brain + * + * @param message A message that users sent + */ private fun sendNotification(message: String) { viewModel.sendNotification(message).observe(viewLifecycleOwner) { resource -> - when (resource) { - is ApiResource.Loading -> { - showLoading(true) - } - - is ApiResource.Success -> { - showLoading(false) - val apiResponse = resource.data - when (apiResponse?.result?.program) { - TYPE_RESPONSE_MESSAGE -> addMessage( - TYPE_CHAT_RECEIVE, apiResponse.result.content.asString - ) - - TYPE_RESPONSE_BROWSER -> fetchResponseBrowser(apiResponse) - TYPE_RESPONSE_CONTACT -> fetchResponseContact(apiResponse) - TYPE_RESPONSE_IMAGE -> fetchResponseImage(apiResponse) - TYPE_RESPONSE_ALARM -> fetchResponseAlarm(apiResponse) - TYPE_RESPONSE_MAIL_READ -> fetchResponseMail(TYPE_RESPONSE_MAIL_READ) - TYPE_RESPONSE_MAIL_WRITE -> fetchResponseMail(TYPE_RESPONSE_MAIL_WRITE) - TYPE_RESPONSE_MAIL_SEND -> fetchResponseMail(TYPE_RESPONSE_MAIL_SEND) - TYPE_RESPONSE_AUTO_TASK -> fetchResponseAutoTask(apiResponse) - else -> addMessage(TYPE_CHAT_RECEIVE, apiResponse!!.result.toString()) - } - } + commonProcessResource(resource) { + val apiResponse = resource.data + when (apiResponse?.result?.program) { + TYPE_RESPONSE_MESSAGE -> addMessage( + TYPE_CHAT_RECEIVE, + apiResponse.result.content.asString + ) - is ApiResource.Error -> { - addErrorMessage(resource.message!!) - showLoading(false) + TYPE_RESPONSE_SMS -> addMessage(TYPE_CHAT_WIDGET, TYPE_WIDGET_SMS) + TYPE_RESPONSE_BROWSER -> fetchResponseBrowser(apiResponse) + TYPE_RESPONSE_CONTACT -> fetchResponseContact(apiResponse) + TYPE_RESPONSE_IMAGE -> fetchResponseImage(apiResponse) + TYPE_RESPONSE_ALARM -> fetchResponseAlarm(apiResponse) + TYPE_RESPONSE_MAIL_READ -> fetchResponseMail(TYPE_RESPONSE_MAIL_READ) + TYPE_RESPONSE_MAIL_WRITE -> fetchResponseMail(TYPE_RESPONSE_MAIL_WRITE) + TYPE_RESPONSE_MAIL_SEND -> fetchResponseMail(TYPE_RESPONSE_MAIL_SEND) + TYPE_RESPONSE_AUTO_TASK -> fetchResponseAutoTask(apiResponse) + else -> addMessage(TYPE_CHAT_RECEIVE, apiResponse!!.result.toString()) } } - } } + /** + * This function is used to process data from Brain that is for browser. + */ private fun fetchResponseBrowser(apiResponse: ApiResponse) { addMessage(TYPE_CHAT_RECEIVE, apiResponse.result.content.asString) } + /** + * This function is used to process data from Brain that is for image. + */ private fun fetchResponseImage(apiResponse: ApiResponse) { val content = apiResponse.result.content.asJsonObject - viewModel.downloadImageFromFirebase( - content["image_name"].asString - ).observe(viewLifecycleOwner) { resource -> - when (resource) { - is ApiResource.Loading -> { - showLoading(true) - } - - is ApiResource.Success -> { - showLoading(false) + viewModel.downloadImageFromFirebase(content["image_name"].asString) + .observe(viewLifecycleOwner) { resource -> + commonProcessResource(resource) { addMessage( type = TYPE_CHAT_RECEIVE, content = null, @@ -432,15 +498,12 @@ class ChatMainFragment : Fragment(), OnClickListener { image = resource.data ) } - - is ApiResource.Error -> { - addErrorMessage(resource.message!!) - showLoading(false) - } } - } } + /** + * This function is used to process data from Brain that is for contact. + */ private fun fetchResponseContact(apiResponse: ApiResponse) { val contactIds = JSONArray(apiResponse.result.content.asString.replace("'", "\"")) if (contactIds.length() > 0) { @@ -457,6 +520,9 @@ class ChatMainFragment : Fragment(), OnClickListener { } } + /** + * This function is used to process data from Brain that is for alarm. + */ private fun fetchResponseAlarm(apiResponse: ApiResponse) { val time: Time = Converter.stringToTime(apiResponse.result.content.asJsonObject.get("time").asString) @@ -468,18 +534,13 @@ class ChatMainFragment : Fragment(), OnClickListener { addMessage(TYPE_CHAT_WIDGET, TYPE_WIDGET_SCHEDULE_ALARM, widgetDesc) } + /** + * This function is used to process data from Brain that is for mail. + */ private fun fetchResponseMail(type: String) { when (type) { - TYPE_RESPONSE_MAIL_READ -> { - addMessage(TYPE_CHAT_WIDGET, TYPE_WIDGET_MAIL_READ) - } - - TYPE_RESPONSE_MAIL_WRITE -> { - addMessage(TYPE_CHAT_WIDGET, TYPE_WIDGET_MAIL_WRITE) - } - - TYPE_RESPONSE_MAIL_SEND -> { - } + TYPE_RESPONSE_MAIL_READ -> addMessage(TYPE_CHAT_WIDGET, TYPE_WIDGET_MAIL_READ) + TYPE_RESPONSE_MAIL_SEND -> addMessage(TYPE_CHAT_WIDGET, TYPE_WIDGET_MAIL_WRITE) } } @@ -495,35 +556,47 @@ class ChatMainFragment : Fragment(), OnClickListener { ).observe(viewLifecycleOwner) { resource -> when (resource) { is ApiResource.Loading -> { + showLoading(true) resource.data?.let { data -> if (data.result == null) { data.thoughts?.let { thoughts -> addMessage( - type = TYPE_CHAT_RECEIVE, - content = thoughts.speak + type = TYPE_CHAT_RECEIVE, content = thoughts.speak ) } } else { addMessage( - type = TYPE_CHAT_RECEIVE, - content = data.result + type = TYPE_CHAT_RECEIVE, content = data.result ) } } } is ApiResource.Success -> { + showLoading(false) addMessage(TYPE_CHAT_RECEIVE, "Task is finished.") } is ApiResource.Error -> { + showLoading(false) + resource.message?.let { + addErrorMessage(resource.message) + } ?: run { + addErrorMessage(ERROR_MSG_UNKNOWN_ERROR) + } } } } } + /** + * This interface is a callback function that retrieves the results of chat widgets. + */ private fun initChatInterface() { chatMessageInterface = object : ChatMessageInterface { + /** + * When a user sends sms using SMS Widget, this function is called. + */ override fun sentSms(phoneNumber: String, message: String) { addMessage( type = TYPE_CHAT_RECEIVE, @@ -531,6 +604,9 @@ class ChatMainFragment : Fragment(), OnClickListener { ) } + /** + * When a user cancels sms using SMS Widget, this function is called. + */ override fun canceledSms() { addMessage( type = TYPE_CHAT_RECEIVE, @@ -538,30 +614,46 @@ class ChatMainFragment : Fragment(), OnClickListener { ) } + /** + * When a user sends help prompt using help Prompt Widget, this function is called. + */ override fun sentHelpPrompt(prompt: String) { addMessage( type = TYPE_CHAT_SENT, content = prompt ) } + /** + * When a user cancels help prompt using help prompt Widget, this function is called. + */ override fun canceledHelpPrompt() { addMessage( type = TYPE_CHAT_RECEIVE, content = "You canceled Help prompt." ) } + /** + * When a user makes a voice call after searching for contacts, this function is called. + */ override fun doVoiceCall(phoneNumber: String) { addMessage( type = TYPE_CHAT_RECEIVE, content = "You made a voice call to $phoneNumber" ) } + /** + * When a user makes a video call after searching for contacts, this function is called. + */ override fun doVideoCall(phoneNumber: String) { addMessage( type = TYPE_CHAT_RECEIVE, content = "You made a video call to $phoneNumber" ) } + /** + * When a user sends sms with phone number after searching for contacts, + * this function is called. + */ override fun sendSmsWithPhoneNumber(phoneNumber: String) { val widgetDesc = JsonObject().apply { this.addProperty(PROPS_WIDGET_DESC, phoneNumber) @@ -571,29 +663,25 @@ class ChatMainFragment : Fragment(), OnClickListener { ) } + /** + * When a user picks an image to send to Brain to search or compare similarity, + * this function is called. + */ override fun pickImage(isSuccess: Boolean, data: ByteArray?) { if (!isSuccess) addErrorMessage("Fail to pick image") - viewModel.uploadImageToFirebase(data!!).observe(viewLifecycleOwner) { resource -> - when (resource) { - is ApiResource.Loading -> { - showLoading(true) - } - is ApiResource.Success -> { - showLoading(false) - currentSelectedImage = data - currentUploadedImageName = resource.data - isImagePicked = true - } - - is ApiResource.Error -> { - addErrorMessage(resource.message!!) - showLoading(false) - } + viewModel.uploadImageToFirebase(data!!).observe(viewLifecycleOwner) { resource -> + commonProcessResource(resource) { + currentSelectedImage = data + currentUploadedImageName = resource.data + isImagePicked = true } } } + /** + * When a user sets an alarm by using alarm widget, this function is called. + */ override fun setAlarm(hours: Int, minutes: Int, label: String) { addMessage( type = TYPE_CHAT_RECEIVE, @@ -601,11 +689,64 @@ class ChatMainFragment : Fragment(), OnClickListener { ) } + /** + * When a user cancels an alarm by using alarm widget, this function is called. + */ override fun cancelAlarm() { addMessage( type = TYPE_CHAT_RECEIVE, content = "You canceled setting an alarm." ) } + + /** + * When a user reads mail by using read mail widget, this function is called. + */ + override fun readMail(from: String, password: String, imap_folder: String) { + viewModel.readMails(from, password, imap_folder) + .observe(viewLifecycleOwner) { resource -> + commonProcessResource(resource) { + val props = MailsProps(resource.data!!) + val widgetDesc = JsonObject().apply { + this.addProperty(PROPS_WIDGET_DESC, props.toString()) + } + addMessage( + type = TYPE_CHAT_WIDGET, + content = TYPE_WIDGET_MAILS, + data = widgetDesc + ) + } + } + } + + /** + * When a user sends mail by using compose mail widget, this function is called. + */ + override fun sendMail( + from: String, + password: String, + to: String, + subject: String, + body: String, + isInbox: Boolean, + filename: String, + fileContent: String + ) { + + viewModel.sendMail( + sender = from, + pwd = password, + to = to, + subject = subject, + body = body, + to_send = isInbox, + filename = filename, + file_content = fileContent + ).observe(viewLifecycleOwner) { resource -> commonProcessResource(resource) {} } + } + + override fun readMailInDetail(mail: MailModel) { + + } } } @@ -616,14 +757,21 @@ class ChatMainFragment : Fragment(), OnClickListener { } R.id.btn_audio_recognition -> { - + //Here are some codes. } } } override fun onResume() { super.onResume() - trainImages() - trainContacts() + + /** + * when main fragment resumes, search for changed images and contacts in mobile and send the changed data + * to Brain to train. + */ + viewModel.trainContacts() + .observe(viewLifecycleOwner) { resource -> commonProcessResource(resource) {} } + viewModel.trainImages() + .observe(viewLifecycleOwner) { resource -> commonProcessResource(resource) {} } } } \ No newline at end of file diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/chat/view/interfaces/ChatMessageInterface.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/chat/view/interfaces/ChatMessageInterface.kt index fe99148c23f44c8d0a4aea8f411c4be3becc8556..89d88bc92af56774a06829615831904d9115a6f4 100644 --- a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/chat/view/interfaces/ChatMessageInterface.kt +++ b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/chat/view/interfaces/ChatMessageInterface.kt @@ -1,5 +1,10 @@ package com.matthaigh27.chatgptwrapper.ui.chat.view.interfaces +import com.matthaigh27.chatgptwrapper.data.models.chat.MailModel + +/** + * This interface is a callback function that retrieves the results of chat widgets. + */ interface ChatMessageInterface { fun sentSms(phoneNumber: String, message: String) fun canceledSms() @@ -9,6 +14,19 @@ interface ChatMessageInterface { fun doVideoCall(phoneNumber: String) fun sendSmsWithPhoneNumber(phoneNumber: String) fun pickImage(isSuccess: Boolean, data: ByteArray? = null) - fun setAlarm(hours: Int, minutes:Int, label: String) + fun setAlarm(hours: Int, minutes: Int, label: String) fun cancelAlarm() + fun readMail(from: String, password: String, imap_folder: String) + fun sendMail( + from: String, + password: String, + to: String, + subject: String, + body: String, + isInbox: Boolean, + filename: String, + fileContent: String + ) + + fun readMailInDetail(mail: MailModel) } \ No newline at end of file diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/chat/view/interfaces/OnHideListener.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/chat/view/interfaces/OnHideListener.kt index a1fe0595ccf8714498dc5a868973adf3965972ed..67ec2ec434aab28f3521aabe663b3de39383e7f2 100644 --- a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/chat/view/interfaces/OnHideListener.kt +++ b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/chat/view/interfaces/OnHideListener.kt @@ -1,5 +1,8 @@ package com.matthaigh27.chatgptwrapper.ui.chat.view.interfaces +/** + * This interface is used to retrieve hide event when a user closes chat widgets. + */ interface OnHideListener { fun hide() } \ No newline at end of file diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/chat/view/widgets/chatwidget/helpprompt/HelpPromptKeyEditText.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/chat/view/widgets/chatwidget/helpprompt/HelpPromptKeyEditText.kt deleted file mode 100644 index 8ba3b65e544e42ef91b848a4b9fe8404aea77017..0000000000000000000000000000000000000000 --- a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/chat/view/widgets/chatwidget/helpprompt/HelpPromptKeyEditText.kt +++ /dev/null @@ -1,44 +0,0 @@ -package com.matthaigh27.chatgptwrapper.ui.chat.view.widgets.chatwidget.helpprompt - -import android.annotation.SuppressLint -import android.content.Context -import android.view.ViewGroup -import android.widget.EditText -import android.widget.LinearLayout -import androidx.annotation.Dimension -import com.matthaigh27.chatgptwrapper.R - -@SuppressLint("AppCompatCustomView") -class HelpPromptKeyEditText(context: Context) : EditText(context) { - init { - val layoutParams: LinearLayout.LayoutParams = LinearLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT - ) - val marginBottom = - context.resources.getDimensionPixelSize(R.dimen.spacing_tiny) - layoutParams.setMargins(0, 0, 0, marginBottom) - this.layoutParams = layoutParams - - minHeight = - context.resources.getDimensionPixelSize(R.dimen.height_edittext_normal) - - val paddingStart = - context.resources.getDimensionPixelSize(R.dimen.spacing_tiny) - setPadding(paddingStart, 0, 0, 0) - - - setTextSize( - Dimension.DP, - context.resources.getDimensionPixelSize(R.dimen.font_normal) - .toFloat() - ) - setTextColor(context.getColor(R.color.color_accent)) - setHintTextColor(context.getColor(R.color.color_primary_dark)) - background = context.getDrawable(R.drawable.bg_edittext_radius_small) - } - - fun initView(keyName: String) { - hint = keyName - tag = keyName - } -} \ No newline at end of file diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/chat/view/widgets/chatwidget/helpprompt/HelpPromptKeyItem.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/chat/view/widgets/chatwidget/helpprompt/HelpPromptKeyItem.kt new file mode 100644 index 0000000000000000000000000000000000000000..dc9781c21487e043f158a431439696f5224c09ad --- /dev/null +++ b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/chat/view/widgets/chatwidget/helpprompt/HelpPromptKeyItem.kt @@ -0,0 +1,30 @@ +package com.matthaigh27.chatgptwrapper.ui.chat.view.widgets.chatwidget.helpprompt + +import android.annotation.SuppressLint +import android.content.Context +import android.view.LayoutInflater +import android.widget.FrameLayout +import com.google.android.material.textfield.TextInputLayout +import com.matthaigh27.chatgptwrapper.R + +@SuppressLint("AppCompatCustomView") +class HelpPromptKeyItem(context: Context) : FrameLayout(context) { + + private val edtKey: TextInputLayout + + init { + LayoutInflater.from(context).inflate(R.layout.item_help_prompt_key, this, true) + + edtKey = findViewById(R.id.edt_key) + } + + fun initView(keyName: String) { + if(keyName.isNotEmpty()) + edtKey.hint = keyName.subSequence(1, keyName.length) + tag = keyName + } + + fun getText(): String { + return edtKey.editText?.text.toString() + } +} \ No newline at end of file diff --git a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/chat/view/widgets/chatwidget/helpprompt/HelpPromptWidget.kt b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/chat/view/widgets/chatwidget/helpprompt/HelpPromptWidget.kt index 48c59992c939a5df05b6c95e802d8b81ca09d942..565ae8a373c07d2d25e7317ebee6be6c808f5033 100644 --- a/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/chat/view/widgets/chatwidget/helpprompt/HelpPromptWidget.kt +++ b/Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/chat/view/widgets/chatwidget/helpprompt/HelpPromptWidget.kt @@ -6,6 +6,7 @@ import android.view.View import android.view.ViewGroup import android.widget.Button import android.widget.EditText +import android.widget.ImageView import android.widget.LinearLayout import android.widget.TextView import androidx.constraintlayout.widget.ConstraintLayout @@ -19,7 +20,7 @@ class HelpPromptWidget(context: Context, model: HelpPromptModel) : ConstraintLay private lateinit var llPromptKeys: LinearLayout private lateinit var txtKeysTitle: TextView - private var promptEditTextList: ArrayList? = null + private var promptEditTextList: ArrayList? = null private val promptModel: HelpPromptModel var callback: ChatMessageInterface? = null var hideListener: OnHideListener? = null @@ -41,8 +42,8 @@ class HelpPromptWidget(context: Context, model: HelpPromptModel) : ConstraintLay txtKeysTitle = findViewById(R.id.txt_keys_title) txtKeysTitle.text = promptModel.name - findViewById