peterluo3131 commited on
Commit
d616f51
·
1 Parent(s): 8f051f8

feature(#55): migrate app to mvvm archtecture

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. Android/app/build.gradle +49 -39
  2. Android/app/src/main/AndroidManifest.xml +21 -16
  3. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/MyApplication.kt +0 -105
  4. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/RisingApplication.kt +45 -0
  5. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/adapters/ChatAdapter.kt +0 -337
  6. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/{database/MyDatabase.kt → data/local/AppDatabase.kt} +13 -11
  7. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/local/dao/ContactDao.kt +20 -0
  8. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/local/dao/ImageDao.kt +20 -0
  9. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/{database → data/local}/entity/ContactEntity.kt +1 -1
  10. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/{database → data/local}/entity/ImageEntity.kt +2 -1
  11. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/models/AlarmModel.kt +3 -0
  12. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/models/ChatMessageModel.kt +36 -0
  13. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/models/ContactModel.kt +8 -0
  14. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/models/HelpCommandModel.kt +6 -0
  15. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/models/HelpPromptModel.kt +8 -0
  16. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/models/ImageModel.kt +8 -0
  17. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/ApiClient.kt +25 -0
  18. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/ApiResource.kt +10 -0
  19. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/ApiService.kt +15 -0
  20. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/requests/BaseApiRequest.kt +8 -0
  21. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/requests/NotificationApiRequest.kt +8 -0
  22. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/requests/common/Keys.kt +11 -0
  23. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/requests/common/Settings.kt +5 -0
  24. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/responses/ApiResponse.kt +15 -0
  25. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/repository/FirebaseRepository.kt +23 -0
  26. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/repository/RemoteRepository.kt +59 -0
  27. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/repository/RoomRepository.kt +47 -0
  28. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/database/dao/ContactDao.kt +0 -20
  29. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/database/dao/ImageDao.kt +0 -20
  30. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/fragments/ChatFragment.kt +0 -888
  31. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/models/common/ContactModel.kt +0 -19
  32. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/models/common/HelpCommandModel.kt +0 -10
  33. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/models/common/HelpPromptModel.kt +0 -25
  34. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/models/common/ImagePromptModel.kt +0 -11
  35. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/models/requestmodels/RequestBodyModel.kt +0 -86
  36. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/models/requestmodels/RequestTrainContactModel.kt +0 -79
  37. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/models/viewmodels/ChatMessageModel.kt +0 -18
  38. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/services/MessageService.kt +0 -32
  39. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/services/api/HttpClient.kt +0 -117
  40. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/services/api/HttpRisingInterface.kt +0 -7
  41. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/base/BaseActivity.kt +18 -0
  42. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/{activites/HomeActivity.kt → ui/chat/view/ChatActivity.kt} +25 -24
  43. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/chat/view/adapters/ChatMainAdapter.kt +179 -0
  44. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/{dialogs/CommonConfirmDialog.kt → ui/chat/view/dialogs/ConfirmDialog.kt} +27 -30
  45. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/chat/view/fragments/ChatMainFragment.kt +383 -0
  46. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/chat/view/interfaces/ChatMessageInterface.kt +12 -0
  47. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/chat/view/widgets/chatwidget/FeedbackWidget.kt +4 -0
  48. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/{widgets/SmsEditorWidget.kt → ui/chat/view/widgets/chatwidget/SendSmsWidget.kt} +26 -34
  49. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/{widgets → ui/chat/view/widgets/chatwidget/contact}/ContactDetailItem.kt +22 -34
  50. Android/app/src/main/java/com/matthaigh27/chatgptwrapper/{widgets → ui/chat/view/widgets/chatwidget/contact}/ContactDetailWidget.kt +39 -30
Android/app/build.gradle CHANGED
@@ -1,8 +1,8 @@
1
  plugins {
2
  id 'com.android.application'
3
- id 'kotlin-android'
4
  id 'com.google.gms.google-services'
5
  id 'kotlin-kapt'
 
6
  }
7
 
8
  android {
@@ -13,19 +13,18 @@ android {
13
  applicationId "com.matthaigh27.chatgptwrapper"
14
  minSdk 28
15
  targetSdk 33
16
- versionCode 7
17
- versionName "1.5"
18
 
19
  testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
20
  }
21
 
22
  buildTypes {
23
  debug {
24
- buildConfigField "String", "BASE_URL", "\"https://smartphone.herokuapp.com/\""
25
  }
26
  release {
27
- // Use your desired server address for the release version
28
- buildConfigField "String", "BASE_URL", "\"https://chatgptphone.herokuapp.com/\""
29
 
30
  minifyEnabled true
31
  shrinkResources true
@@ -41,6 +40,7 @@ android {
41
  sourceCompatibility JavaVersion.VERSION_11
42
  targetCompatibility JavaVersion.VERSION_11
43
  }
 
44
  kotlinOptions {
45
  jvmTarget = JavaVersion.VERSION_11
46
  }
@@ -51,63 +51,73 @@ android {
51
  }
52
 
53
  dependencies {
54
- // App dependencies
55
  implementation 'androidx.core:core-ktx:1.9.0'
 
 
 
56
  implementation 'androidx.appcompat:appcompat:1.6.0'
57
  implementation 'com.google.android.material:material:1.8.0'
58
  implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
59
-
60
- implementation platform('com.google.firebase:firebase-bom:31.4.0')
61
- implementation 'com.google.android.gms:play-services-gcm:17.0.0'
62
-
63
- implementation 'com.google.firebase:firebase-messaging'
64
- implementation 'com.google.firebase:firebase-analytics'
65
- implementation 'com.google.firebase:firebase-firestore-ktx:24.4.5'
66
- implementation 'com.google.firebase:firebase-firestore:15.0.0'
67
-
68
- implementation 'com.google.firebase:firebase-messaging-ktx'
69
- implementation 'com.google.firebase:firebase-analytics-ktx'
70
- implementation 'com.firebaseui:firebase-ui-storage:7.2.0'
71
-
72
- implementation 'com.squareup.okhttp3:okhttp:3.0.1'
73
  implementation 'com.github.soulqw:CoCo:1.1.2'
74
  implementation 'com.github.dhaval2404:imagepicker:2.1'
75
- implementation 'com.google.firebase:firebase-storage-ktx:20.1.0'
 
 
76
  testImplementation 'org.testng:testng:6.9.6'
77
 
78
  // Testing-only dependencies
79
- androidTestImplementation 'androidx.test:core:' + rootProject.coreVersion;
80
- androidTestImplementation 'androidx.test.ext:junit:' + rootProject.extJUnitVersion;
81
- androidTestImplementation 'androidx.test:runner:' + rootProject.runnerVersion;
82
 
83
  testImplementation 'junit:junit:4.13.2'
84
  androidTestImplementation 'androidx.test.ext:junit:1.1.5'
85
  androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
86
 
87
  // UiAutomator Testing
88
- androidTestImplementation 'androidx.test.uiautomator:uiautomator:' + rootProject.uiAutomatorVersion;
89
  androidTestImplementation 'org.hamcrest:hamcrest-integration:1.3'
90
 
91
  implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.2"
92
  implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.2"
93
  implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0-alpha05"
94
 
95
- implementation 'com.github.bumptech.glide:glide:4.12.0'
96
- annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
 
97
 
98
- implementation 'com.google.code.gson:gson:2.8.5'
 
 
 
 
 
 
 
99
 
100
- implementation "androidx.room:room-runtime:$room_version"
101
- annotationProcessor "androidx.room:room-compiler:$room_version"
102
 
103
  // To use room database
104
- implementation "androidx.room:room-ktx:$room_version"
105
- kapt "androidx.room:room-compiler:$room_version"
106
- implementation "androidx.room:room-rxjava2:$room_version"
107
- implementation "androidx.room:room-rxjava3:$room_version"
108
- implementation "androidx.room:room-guava:$room_version"
109
- testImplementation "androidx.room:room-testing:$room_version"
110
- implementation "androidx.room:room-paging:$room_version"
111
 
112
- implementation 'de.hdodenhof:circleimageview:3.1.0'
 
 
 
 
 
 
 
 
 
 
113
  }
 
1
  plugins {
2
  id 'com.android.application'
 
3
  id 'com.google.gms.google-services'
4
  id 'kotlin-kapt'
5
+ id 'kotlin-android'
6
  }
7
 
8
  android {
 
13
  applicationId "com.matthaigh27.chatgptwrapper"
14
  minSdk 28
15
  targetSdk 33
16
+ versionCode 1
17
+ versionName "1.6"
18
 
19
  testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
20
  }
21
 
22
  buildTypes {
23
  debug {
24
+ buildConfigField "String", "BASE_URL", "\"https://ttt246-brain.hf.space/\""
25
  }
26
  release {
27
+ buildConfigField "String", "BASE_URL", "\"https://ttt246-brain.hf.space/\""
 
28
 
29
  minifyEnabled true
30
  shrinkResources true
 
40
  sourceCompatibility JavaVersion.VERSION_11
41
  targetCompatibility JavaVersion.VERSION_11
42
  }
43
+
44
  kotlinOptions {
45
  jvmTarget = JavaVersion.VERSION_11
46
  }
 
51
  }
52
 
53
  dependencies {
54
+ //Core
55
  implementation 'androidx.core:core-ktx:1.9.0'
56
+ implementation 'com.google.code.gson:gson:2.8.5'
57
+
58
+ //UI & UX
59
  implementation 'androidx.appcompat:appcompat:1.6.0'
60
  implementation 'com.google.android.material:material:1.8.0'
61
  implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
62
+ implementation 'de.hdodenhof:circleimageview:3.1.0'
 
 
 
 
 
 
 
 
 
 
 
 
 
63
  implementation 'com.github.soulqw:CoCo:1.1.2'
64
  implementation 'com.github.dhaval2404:imagepicker:2.1'
65
+ implementation 'com.github.bumptech.glide:glide:4.12.0'
66
+ annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
67
+
68
  testImplementation 'org.testng:testng:6.9.6'
69
 
70
  // Testing-only dependencies
71
+ androidTestImplementation "androidx.test:core:$rootProject.coreVersion"
72
+ androidTestImplementation "androidx.test.ext:junit:$rootProject.extJUnitVersion"
73
+ androidTestImplementation "androidx.test:runner:$rootProject.runnerVersion"
74
 
75
  testImplementation 'junit:junit:4.13.2'
76
  androidTestImplementation 'androidx.test.ext:junit:1.1.5'
77
  androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
78
 
79
  // UiAutomator Testing
80
+ androidTestImplementation "androidx.test.uiautomator:uiautomator:$rootProject.uiAutomatorVersion"
81
  androidTestImplementation 'org.hamcrest:hamcrest-integration:1.3'
82
 
83
  implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.2"
84
  implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.2"
85
  implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0-alpha05"
86
 
87
+ //Firebase
88
+ implementation platform('com.google.firebase:firebase-bom:31.4.0')
89
+ implementation 'com.google.android.gms:play-services-gcm:17.0.0'
90
 
91
+ implementation 'com.google.firebase:firebase-messaging'
92
+ implementation 'com.google.firebase:firebase-analytics'
93
+ implementation 'com.google.firebase:firebase-firestore-ktx:24.4.5'
94
+ implementation 'com.google.firebase:firebase-firestore:15.0.0'
95
+ implementation 'com.google.firebase:firebase-storage-ktx:20.1.0'
96
+ implementation 'com.google.firebase:firebase-messaging-ktx'
97
+ implementation 'com.google.firebase:firebase-analytics-ktx'
98
+ implementation 'com.firebaseui:firebase-ui-storage:7.2.0'
99
 
100
+ implementation "androidx.room:room-runtime:$rootProject.roomVersion"
101
+ annotationProcessor "androidx.room:room-compiler:$rootProject.roomVersion"
102
 
103
  // To use room database
104
+ implementation "androidx.room:room-ktx:$rootProject.roomVersion"
105
+ kapt "androidx.room:room-compiler:$rootProject.roomVersion"
106
+ implementation "androidx.room:room-rxjava2:$rootProject.roomVersion"
107
+ implementation "androidx.room:room-rxjava3:$rootProject.roomVersion"
108
+ implementation "androidx.room:room-guava:$rootProject.roomVersion"
109
+ testImplementation "androidx.room:room-testing:$rootProject.roomVersion"
110
+ implementation "androidx.room:room-paging:$rootProject.roomVersion"
111
 
112
+ //To use Retrofit
113
+
114
+ implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$rootProject.lifecycleVersion"
115
+ implementation "androidx.lifecycle:lifecycle-livedata-ktx:$rootProject.lifecycleVersion"
116
+
117
+ implementation "com.squareup.retrofit2:retrofit:$rootProject.retrofitVersion"
118
+ implementation "com.squareup.retrofit2:converter-gson:$rootProject.retrofitVersion"
119
+
120
+ implementation "com.squareup.okhttp3:okhttp:$rootProject.okHttpVersion"
121
+ implementation "com.squareup.okhttp3:logging-interceptor:$rootProject.okHttpVersion"
122
+ implementation "com.squareup.okhttp3:okhttp-urlconnection:$rootProject.okHttpVersion"
123
  }
Android/app/src/main/AndroidManifest.xml CHANGED
@@ -9,40 +9,45 @@
9
  <uses-permission android:name="android.permission.GET_PACKAGE_SIZE" />
10
  <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
11
  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
12
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
13
- <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
 
 
 
 
14
  <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
15
- <uses-permission android:name="android.permission.READ_CONTACTS"/>
16
- <uses-permission android:name="android.permission.SEND_SMS"/>
17
  <uses-permission android:name="android.permission.CALL_PHONE" />
18
 
19
  <uses-feature android:name="android.hardware.audio.low_latency" />
20
  <uses-feature android:name="android.hardware.audio.pro" />
21
  <uses-feature android:name="android.hardware.microphone" />
 
 
 
22
 
23
  <application
24
- android:name=".MyApplication"
25
  android:hardwareAccelerated="true"
26
  android:icon="@mipmap/gpt_icon"
27
  android:label="@string/app_name"
28
  android:roundIcon="@mipmap/gpt_icon_round"
29
  android:supportsRtl="true"
30
- android:theme="@style/Theme.Design.NoActionBar">
31
  <activity
32
- android:name=".activites.HomeActivity"
33
- android:windowSoftInputMode="adjustResize"
34
- android:exported="true">
 
 
 
35
  <intent-filter>
36
  <action android:name="android.intent.action.MAIN" />
 
37
  <category android:name="android.intent.category.LAUNCHER" />
38
  </intent-filter>
39
  </activity>
40
- <service
41
- android:name=".services.MessageService"
42
- android:exported="false">
43
- <intent-filter>
44
- <action android:name="com.google.firebase.MESSAGING_EVENT" />
45
- </intent-filter>
46
- </service>
47
  </application>
 
48
  </manifest>
 
9
  <uses-permission android:name="android.permission.GET_PACKAGE_SIZE" />
10
  <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
11
  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
12
+ <uses-permission
13
+ android:name="android.permission.WRITE_EXTERNAL_STORAGE"
14
+ android:maxSdkVersion="32" />
15
+ <uses-permission
16
+ android:name="android.permission.READ_EXTERNAL_STORAGE"
17
+ android:maxSdkVersion="32" />
18
  <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
19
+ <uses-permission android:name="android.permission.READ_CONTACTS" />
20
+ <uses-permission android:name="android.permission.SEND_SMS" />
21
  <uses-permission android:name="android.permission.CALL_PHONE" />
22
 
23
  <uses-feature android:name="android.hardware.audio.low_latency" />
24
  <uses-feature android:name="android.hardware.audio.pro" />
25
  <uses-feature android:name="android.hardware.microphone" />
26
+ <uses-feature
27
+ android:name="android.hardware.telephony"
28
+ android:required="false" />
29
 
30
  <application
31
+ android:name=".RisingApplication"
32
  android:hardwareAccelerated="true"
33
  android:icon="@mipmap/gpt_icon"
34
  android:label="@string/app_name"
35
  android:roundIcon="@mipmap/gpt_icon_round"
36
  android:supportsRtl="true"
37
+ android:theme="@style/AppTheme">
38
  <activity
39
+ android:name=".ui.setting.view.SettingActivity"
40
+ android:exported="false" />
41
+ <activity
42
+ android:name=".ui.chat.view.ChatActivity"
43
+ android:exported="true"
44
+ android:windowSoftInputMode="adjustResize">
45
  <intent-filter>
46
  <action android:name="android.intent.action.MAIN" />
47
+
48
  <category android:name="android.intent.category.LAUNCHER" />
49
  </intent-filter>
50
  </activity>
 
 
 
 
 
 
 
51
  </application>
52
+
53
  </manifest>
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/MyApplication.kt DELETED
@@ -1,105 +0,0 @@
1
- package com.matthaigh27.chatgptwrapper
2
-
3
- import android.Manifest
4
- import android.annotation.SuppressLint
5
- import android.app.Application
6
- import android.app.NotificationChannel
7
- import android.app.NotificationManager
8
- import android.content.pm.PackageManager
9
- import android.util.Log
10
- import com.google.android.gms.tasks.OnCompleteListener
11
- import com.google.firebase.messaging.FirebaseMessaging
12
- import com.matthaigh27.chatgptwrapper.utils.Constants
13
- import android.provider.Settings
14
- import androidx.core.app.ActivityCompat
15
- import androidx.core.app.NotificationCompat
16
- import androidx.core.app.NotificationManagerCompat
17
- import java.util.Random
18
-
19
- class MyApplication : Application() {
20
-
21
- private var mFCMToken: String = String()
22
- private var mUUID: String = String()
23
- @SuppressLint("HardwareIds")
24
- override fun onCreate() {
25
- super.onCreate()
26
-
27
- initToken()
28
- // on below line we are getting device id.
29
- mUUID = Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID)
30
- appContext = applicationContext as MyApplication
31
-
32
- Log.v("risingandroid mUUID: ", mUUID)
33
- Log.v("risingandroid FCMToken: ", mFCMToken)
34
- }
35
-
36
- private fun initToken() {
37
- FirebaseMessaging.getInstance().token.addOnCompleteListener(OnCompleteListener { task ->
38
- if (!task.isSuccessful) {
39
- Log.w(Constants.TAG, "Fetching FCM registration token failed", task.exception)
40
- return@OnCompleteListener
41
- }
42
-
43
- /**
44
- * Get new FCM registration token
45
- */
46
-
47
- /**
48
- * Get new FCM registration token
49
- */
50
- mFCMToken = task.result
51
- Log.d(Constants.TAG, mFCMToken)
52
- })
53
- }
54
-
55
- fun getFCMToken(): String {
56
- return this.mFCMToken
57
- }
58
-
59
- fun getUUID(): String {
60
- return this.mUUID
61
- }
62
-
63
- /**
64
- * this shows system notification with message
65
- * @param message to be shown with system notification
66
- */
67
- fun showNotification(message: String) {
68
- val notificationId: Int = Random().nextInt()
69
- val channelId = "chat_message"
70
-
71
- val builder = NotificationCompat.Builder(this, channelId)
72
- builder.setSmallIcon(R.drawable.ic_notification)
73
- builder.setContentTitle(Constants.TAG)
74
- builder.setContentText(message)
75
- builder.setStyle(
76
- NotificationCompat.BigTextStyle().bigText(
77
- message
78
- )
79
- )
80
- builder.priority = NotificationCompat.PRIORITY_DEFAULT
81
- builder.setAutoCancel(true)
82
-
83
- val channelName: CharSequence = "Chat Message"
84
- val channelDescription = "This notification channel is used for chat message notifications"
85
- val importance = NotificationManager.IMPORTANCE_DEFAULT
86
- val channel = NotificationChannel(channelId, channelName, importance)
87
- channel.description = channelDescription
88
- val notificationManager = getSystemService(
89
- NotificationManager::class.java
90
- )
91
- notificationManager.createNotificationChannel(channel)
92
- val notificationManagerCompat = NotificationManagerCompat.from(this)
93
- if (ActivityCompat.checkSelfPermission(
94
- this, Manifest.permission.POST_NOTIFICATIONS
95
- ) != PackageManager.PERMISSION_GRANTED
96
- ) {
97
- return
98
- }
99
- notificationManagerCompat.notify(notificationId, builder.build())
100
- }
101
-
102
- companion object {
103
- lateinit var appContext: MyApplication
104
- }
105
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/RisingApplication.kt ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.matthaigh27.chatgptwrapper
2
+
3
+ import android.annotation.SuppressLint
4
+ import android.app.Application
5
+ import android.provider.Settings
6
+ import com.google.android.gms.tasks.OnCompleteListener
7
+ import com.google.firebase.messaging.FirebaseMessaging
8
+
9
+ class RisingApplication : Application() {
10
+
11
+ private var fcmToken: String = String()
12
+ private var uuid: String = String()
13
+
14
+ @SuppressLint("HardwareIds")
15
+ override fun onCreate() {
16
+ super.onCreate()
17
+
18
+ initToken()
19
+ // on below line we are getting device id.
20
+ uuid = Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID)
21
+ appContext = applicationContext as RisingApplication
22
+ }
23
+
24
+ private fun initToken() {
25
+ FirebaseMessaging.getInstance().token.addOnCompleteListener(OnCompleteListener { task ->
26
+ if (!task.isSuccessful) {
27
+ return@OnCompleteListener
28
+ }
29
+
30
+ fcmToken = task.result
31
+ })
32
+ }
33
+
34
+ fun getFCMToken(): String {
35
+ return this.fcmToken
36
+ }
37
+
38
+ fun getUUID(): String {
39
+ return this.uuid
40
+ }
41
+
42
+ companion object {
43
+ lateinit var appContext: RisingApplication
44
+ }
45
+ }
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/adapters/ChatAdapter.kt DELETED
@@ -1,337 +0,0 @@
1
- package com.matthaigh27.chatgptwrapper.adapters
2
-
3
- import android.annotation.SuppressLint
4
- import android.app.Dialog
5
- import android.content.Context
6
- import android.graphics.Bitmap
7
- import android.graphics.BitmapFactory
8
- import android.view.LayoutInflater
9
- import android.view.View
10
- import android.view.ViewGroup
11
- import android.widget.FrameLayout
12
- import android.widget.ImageView
13
- import android.widget.LinearLayout
14
- import android.widget.TextView
15
- import androidx.constraintlayout.widget.ConstraintLayout
16
- import androidx.recyclerview.widget.RecyclerView
17
- import com.matthaigh27.chatgptwrapper.R
18
- import com.matthaigh27.chatgptwrapper.models.common.ContactModel
19
- import com.matthaigh27.chatgptwrapper.models.common.HelpPromptModel
20
- import com.matthaigh27.chatgptwrapper.models.viewmodels.ChatMessageModel
21
- import com.matthaigh27.chatgptwrapper.utils.Constants.MSG_WIDGET_TYPE_HELP_PRMOPT
22
- import com.matthaigh27.chatgptwrapper.utils.Constants.MSG_WIDGET_TYPE_SEARCH_CONTACT
23
- import com.matthaigh27.chatgptwrapper.utils.Constants.MSG_WIDGET_TYPE_SMS
24
- import com.matthaigh27.chatgptwrapper.utils.ImageHelper
25
- import com.matthaigh27.chatgptwrapper.utils.Utils
26
- import com.matthaigh27.chatgptwrapper.widgets.ContactDetailItem
27
- import com.matthaigh27.chatgptwrapper.widgets.ContactDetailWidget
28
- import com.matthaigh27.chatgptwrapper.widgets.HelpPromptWidget
29
- import com.matthaigh27.chatgptwrapper.widgets.SearchContactWidget
30
- import com.matthaigh27.chatgptwrapper.widgets.SmsEditorWidget
31
- import org.json.JSONArray
32
-
33
- class ChatAdapter(list: ArrayList<ChatMessageModel>, context: Context) :
34
- RecyclerView.Adapter<RecyclerView.ViewHolder>() {
35
-
36
- private var mChatModelList: ArrayList<ChatMessageModel> = ArrayList()
37
- private var mContext: Context
38
-
39
- private var mListener: MessageWidgetListener? = null
40
- var mOnSMSClickListener: ContactDetailItem.OnSMSClickListener? = null
41
-
42
- private val feedbackData = arrayOf(
43
- arrayOf(R.drawable.ic_thumb_up_disable, R.drawable.ic_thumb_down),
44
- arrayOf(R.drawable.ic_thumb_up_disable, R.drawable.ic_thumb_down_disable),
45
- arrayOf(R.drawable.ic_thumb_up, R.drawable.ic_thumb_down_disable),
46
- )
47
-
48
- init {
49
- mChatModelList = list
50
- mContext = context
51
- }
52
-
53
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
54
- val context = parent.context
55
- val inflater = LayoutInflater.from(context)
56
-
57
- /**
58
- * Inflate the custom layout and Return a new holder instance
59
- */
60
- return if (viewType == 0) {
61
- SendMessageViewHolder(
62
- inflater.inflate(
63
- R.layout.item_container_sent_message, parent, false
64
- )
65
- )
66
- } else {
67
- ReceiveMessageViewHolder(
68
- inflater.inflate(
69
- R.layout.item_container_received_message, parent, false
70
- )
71
- )
72
- }
73
- }
74
-
75
- /**
76
- * Involves populating data into the item through holder
77
- */
78
- override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
79
- /**
80
- * Get the data model based on position
81
- */
82
- val index = holder.adapterPosition
83
- val messageModel: ChatMessageModel = mChatModelList[index]
84
- if (messageModel.isMe) {
85
- setSentMessageData(holder as SendMessageViewHolder, messageModel)
86
- } else {
87
- setReceiveMessageData(holder as ReceiveMessageViewHolder, messageModel)
88
- }
89
- }
90
-
91
- private fun setSentMessageData(holder: SendMessageViewHolder, messageModel: ChatMessageModel) {
92
- /**
93
- * Set item views based on your views and data model
94
- */
95
- if (messageModel.message.isEmpty()) {
96
- holder.textMessage.visibility = View.GONE
97
- } else {
98
- holder.textMessage.text = messageModel.message
99
- holder.textMessage.visibility = View.VISIBLE
100
- }
101
-
102
-
103
- if (messageModel.image != null) {
104
- val radius = mContext.resources.getDimensionPixelSize(R.dimen.chat_message_item_radius)
105
-
106
- val originBmp = BitmapFactory.decodeByteArray(messageModel.image, 0, messageModel.image!!.size)
107
- val bmp = ImageHelper.getRoundedCornerBitmap(originBmp, radius)
108
- holder.imgMessage.visibility = View.VISIBLE
109
- holder.imgMessage.setImageBitmap(bmp)
110
- holder.imgMessage.setOnClickListener {
111
- onImageClick(originBmp)
112
- }
113
- } else {
114
- holder.imgMessage.visibility = View.GONE
115
- }
116
-
117
- if (messageModel.isWidget) {
118
- when (messageModel.widgetType) {
119
- MSG_WIDGET_TYPE_HELP_PRMOPT -> {
120
- val model: HelpPromptModel =
121
- HelpPromptModel.initModelWithString(messageModel.widgetDescription)
122
- val helpPromptWidget = HelpPromptWidget(mContext, model)
123
- val helpPromptListener = object : HelpPromptWidget.OnHelpPromptListener {
124
- override fun onSuccess(prompt: String) {
125
- mChatModelList[holder.adapterPosition].isWidget = false
126
- holder.llMessageWidget.visibility = View.GONE
127
- holder.llMessageWidget.removeAllViews()
128
- mListener!!.sentHelpPrompt(prompt)
129
- }
130
-
131
- override fun onCancel() {
132
- mChatModelList[holder.adapterPosition].isWidget = false
133
- holder.llMessageWidget.visibility = View.GONE
134
- holder.llMessageWidget.removeAllViews()
135
- mListener!!.canceledHelpPrompt()
136
- }
137
- }
138
- helpPromptWidget.setOnClickListener(helpPromptListener)
139
- holder.llMessageWidget.addView(helpPromptWidget)
140
- holder.llMessageWidget.visibility = View.VISIBLE
141
- }
142
- }
143
- } else {
144
- holder.llMessageWidget.visibility = View.GONE
145
- }
146
- }
147
-
148
- @SuppressLint("UseCompatLoadingForDrawables")
149
- fun setReceiveMessageData(holder: ReceiveMessageViewHolder, messageModel: ChatMessageModel) {
150
- /**
151
- * Set item views based on your views and data model
152
- */
153
- if (messageModel.message.isEmpty()) {
154
- holder.textMessage.visibility = View.GONE
155
- } else {
156
- holder.textMessage.text = messageModel.message
157
- holder.textMessage.visibility = View.VISIBLE
158
- }
159
-
160
-
161
- if (messageModel.image != null) {
162
- val radius = mContext.resources.getDimensionPixelSize(R.dimen.chat_message_item_radius)
163
-
164
- val originBmp = BitmapFactory.decodeByteArray(messageModel.image, 0, messageModel.image!!.size)
165
- val bmp = ImageHelper.getRoundedCornerBitmap(originBmp, radius)
166
- holder.imgMessage.visibility = View.VISIBLE
167
- holder.imgMessage.setImageBitmap(bmp)
168
- holder.imgMessage.setOnClickListener {
169
- onImageClick(originBmp)
170
- }
171
- } else {
172
- holder.imgMessage.visibility = View.GONE
173
- }
174
-
175
- holder.llFeedback.visibility = if (messageModel.visibleFeedback) {
176
- View.VISIBLE
177
- } else {
178
- View.GONE
179
- }
180
-
181
- setThumb(holder)
182
-
183
- holder.itemLayout.setOnLongClickListener {
184
- if (holder.llFeedback.visibility == View.VISIBLE) {
185
- holder.llFeedback.visibility = View.GONE
186
- mChatModelList[holder.adapterPosition].visibleFeedback = false
187
- } else {
188
- holder.llFeedback.visibility = View.VISIBLE
189
- mChatModelList[holder.adapterPosition].visibleFeedback = true
190
- }
191
- return@setOnLongClickListener true
192
- }
193
-
194
- holder.btnThumbUp.setOnClickListener {
195
- mChatModelList[holder.adapterPosition].feedback = 1
196
- setThumb(holder)
197
-
198
- }
199
-
200
- holder.btnThumbDown.setOnClickListener {
201
- mChatModelList[holder.adapterPosition].feedback = -1
202
- setThumb(holder)
203
- }
204
-
205
- if (messageModel.isWidget) {
206
- holder.llContactWidget.removeAllViews()
207
- when (messageModel.widgetType) {
208
- MSG_WIDGET_TYPE_SMS -> {
209
- val smsWidget = SmsEditorWidget(mContext, null)
210
- if(messageModel.widgetDescription.isNotEmpty()) {
211
- smsWidget.setToUserName(messageModel.widgetDescription)
212
- }
213
- holder.llMessageWidget.addView(smsWidget)
214
- holder.llMessageWidget.visibility = View.VISIBLE
215
-
216
- val smsListener = object : SmsEditorWidget.OnClickListener {
217
- override fun confirmSMS(phonenumber: String, message: String) {
218
- mChatModelList[holder.adapterPosition].isWidget = false
219
- holder.llMessageWidget.visibility = View.GONE
220
- holder.llMessageWidget.removeAllViews()
221
- mListener!!.sentSMS(phonenumber, message)
222
- }
223
-
224
- override fun cancelSMS() {
225
- mChatModelList[holder.adapterPosition].isWidget = false
226
- holder.llMessageWidget.visibility = View.GONE
227
- holder.llMessageWidget.removeAllViews()
228
- mListener!!.canceledSMS()
229
- }
230
- }
231
-
232
- smsWidget.setOnClickListener(smsListener)
233
- }
234
-
235
- MSG_WIDGET_TYPE_SEARCH_CONTACT -> {
236
- val contacts = Utils.instance.getContacts(mContext)
237
-
238
- val contactIds = JSONArray(messageModel.widgetDescription)
239
- for (i in 0 until contactIds.length()) {
240
- val contactId = contactIds[i].toString()
241
- val contact = Utils.instance.getContactModelById(contactId, contacts)
242
-
243
- val searchContactWidget = SearchContactWidget(mContext, contact, null)
244
- searchContactWidget.mSMSOnClickListener = mOnSMSClickListener
245
- holder.llContactWidget.addView(searchContactWidget)
246
- }
247
- holder.llContactWidget.visibility = View.VISIBLE
248
- }
249
- }
250
- } else {
251
- holder.llMessageWidget.visibility = View.GONE
252
- holder.llContactWidget.visibility = View.GONE
253
- }
254
- }
255
-
256
- @SuppressLint("UseCompatLoadingForDrawables")
257
- fun setThumb(holder: ReceiveMessageViewHolder) {
258
- holder.btnThumbUp.setImageDrawable(
259
- mContext.getDrawable(
260
- feedbackData[mChatModelList[holder.adapterPosition].feedback + 1][0]
261
- )
262
- )
263
- holder.btnThumbDown.setImageDrawable(
264
- mContext.getDrawable(
265
- feedbackData[mChatModelList[holder.adapterPosition].feedback + 1][1]
266
- )
267
- )
268
- }
269
-
270
- /**
271
- * Returns the total count of items in the list
272
- */
273
- override fun getItemCount(): Int {
274
- return mChatModelList.size
275
- }
276
-
277
- override fun getItemViewType(position: Int): Int {
278
- return if (mChatModelList[position].isMe) 0 else 1
279
- }
280
-
281
- private fun onImageClick(bitmap: Bitmap) {
282
- val dialog = Dialog(mContext)
283
- dialog.setContentView(R.layout.view_full_image)
284
- val fullImage = dialog.findViewById(R.id.fullImage) as ImageView
285
- fullImage.setImageBitmap(bitmap)
286
- dialog.show()
287
- }
288
-
289
- inner class ReceiveMessageViewHolder internal constructor(itemView: View) :
290
- RecyclerView.ViewHolder(itemView) {
291
- var textMessage: TextView
292
- var imgMessage: ImageView
293
- var llFeedback: LinearLayout
294
- var btnThumbUp: ImageView
295
- var btnThumbDown: ImageView
296
- var itemLayout: ConstraintLayout
297
- var llMessageWidget: LinearLayout
298
- var llContactWidget: LinearLayout
299
-
300
- init {
301
- textMessage = itemView.findViewById<View>(R.id.textMessage) as TextView
302
- imgMessage = itemView.findViewById<View>(R.id.imgMessage) as ImageView
303
- btnThumbUp = itemView.findViewById<View>(R.id.btn_thumb_up) as ImageView
304
- btnThumbDown = itemView.findViewById<View>(R.id.btn_thumb_down) as ImageView
305
- llFeedback = itemView.findViewById<View>(R.id.ll_feedback) as LinearLayout
306
- itemLayout = itemView.findViewById<View>(R.id.cl_receive_message) as ConstraintLayout
307
- llMessageWidget = itemView.findViewById<View>(R.id.ll_message_widget) as LinearLayout
308
- llContactWidget = itemView.findViewById<View>(R.id.ll_contacts_widget) as LinearLayout
309
- }
310
- }
311
-
312
- inner class SendMessageViewHolder internal constructor(itemView: View) :
313
- RecyclerView.ViewHolder(itemView) {
314
- var textMessage: TextView
315
- var imgMessage: ImageView
316
- var itemLayout: ConstraintLayout
317
- var llMessageWidget: LinearLayout
318
-
319
- init {
320
- textMessage = itemView.findViewById<View>(R.id.textMessage) as TextView
321
- imgMessage = itemView.findViewById<View>(R.id.imgMessage) as ImageView
322
- itemLayout = itemView.findViewById<View>(R.id.cl_sent_message) as ConstraintLayout
323
- llMessageWidget = itemView.findViewById<View>(R.id.ll_message_widget) as LinearLayout
324
- }
325
- }
326
-
327
- interface MessageWidgetListener {
328
- fun sentSMS(phonenumber: String, message: String)
329
- fun canceledSMS()
330
- fun sentHelpPrompt(prompt: String)
331
- fun canceledHelpPrompt()
332
- }
333
-
334
- fun setMessageWidgetListener(listener: MessageWidgetListener) {
335
- mListener = listener
336
- }
337
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/{database/MyDatabase.kt → data/local/AppDatabase.kt} RENAMED
@@ -1,34 +1,36 @@
1
- package com.matthaigh27.chatgptwrapper.database
2
 
3
  import android.content.Context
4
  import androidx.room.Database
5
  import androidx.room.Room
6
  import androidx.room.RoomDatabase
7
- import com.matthaigh27.chatgptwrapper.database.dao.ContactDao
8
- import com.matthaigh27.chatgptwrapper.database.dao.ImageDao
9
- import com.matthaigh27.chatgptwrapper.database.entity.ContactEntity
10
- import com.matthaigh27.chatgptwrapper.database.entity.ImageEntity
11
 
12
  @Database(entities = [ImageEntity::class, ContactEntity::class], version = 1, exportSchema = false)
13
- abstract class MyDatabase : RoomDatabase() {
14
 
15
  abstract fun imageDao(): ImageDao
16
  abstract fun contactDao(): ContactDao
17
 
18
  companion object {
 
 
19
  @Volatile
20
- private var INSTANCE: MyDatabase? = null
21
 
22
- fun getDatabase(context: Context): MyDatabase {
23
  val tempInstance = INSTANCE
24
  if (tempInstance != null) {
25
  return tempInstance
26
  }
27
  synchronized(this) {
28
  val instance = Room.databaseBuilder(
29
- context.applicationContext,
30
- MyDatabase::class.java,
31
- "risingphone_database"
32
  ).build()
33
  INSTANCE = instance
34
  return instance
 
1
+ package com.matthaigh27.chatgptwrapper.data.local
2
 
3
  import android.content.Context
4
  import androidx.room.Database
5
  import androidx.room.Room
6
  import androidx.room.RoomDatabase
7
+ import com.matthaigh27.chatgptwrapper.data.local.dao.ContactDao
8
+ import com.matthaigh27.chatgptwrapper.data.local.dao.ImageDao
9
+ import com.matthaigh27.chatgptwrapper.data.local.entity.ContactEntity
10
+ import com.matthaigh27.chatgptwrapper.data.local.entity.ImageEntity
11
 
12
  @Database(entities = [ImageEntity::class, ContactEntity::class], version = 1, exportSchema = false)
13
+ abstract class AppDatabase : RoomDatabase() {
14
 
15
  abstract fun imageDao(): ImageDao
16
  abstract fun contactDao(): ContactDao
17
 
18
  companion object {
19
+ private val DATABASE_NAME = "RisingPhone"
20
+
21
  @Volatile
22
+ private var INSTANCE: AppDatabase? = null
23
 
24
+ fun getDatabase(context: Context): AppDatabase {
25
  val tempInstance = INSTANCE
26
  if (tempInstance != null) {
27
  return tempInstance
28
  }
29
  synchronized(this) {
30
  val instance = Room.databaseBuilder(
31
+ context = context.applicationContext,
32
+ klass = AppDatabase::class.java,
33
+ name = DATABASE_NAME
34
  ).build()
35
  INSTANCE = instance
36
  return instance
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/local/dao/ContactDao.kt ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.matthaigh27.chatgptwrapper.data.local.dao
2
+
3
+ import androidx.lifecycle.MutableLiveData
4
+ import androidx.room.*
5
+ import com.matthaigh27.chatgptwrapper.data.local.entity.ContactEntity
6
+
7
+ @Dao
8
+ interface ContactDao {
9
+ @Insert
10
+ fun insert(contact: ContactEntity)
11
+
12
+ @Update
13
+ fun update(contact: ContactEntity)
14
+
15
+ @Delete
16
+ fun delete(contact: ContactEntity)
17
+
18
+ @Query("SELECT * FROM contacts")
19
+ fun getAllData(): List<ContactEntity>
20
+ }
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/local/dao/ImageDao.kt ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.matthaigh27.chatgptwrapper.data.local.dao
2
+
3
+ import androidx.lifecycle.MutableLiveData
4
+ import androidx.room.*
5
+ import com.matthaigh27.chatgptwrapper.data.local.entity.ImageEntity
6
+
7
+ @Dao
8
+ interface ImageDao {
9
+ @Insert
10
+ fun insert(image: ImageEntity)
11
+
12
+ @Update
13
+ fun update(image: ImageEntity)
14
+
15
+ @Delete
16
+ fun delete(image: ImageEntity)
17
+
18
+ @Query("SELECT * FROM images")
19
+ fun getAllData(): List<ImageEntity>
20
+ }
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/{database → data/local}/entity/ContactEntity.kt RENAMED
@@ -1,4 +1,4 @@
1
- package com.matthaigh27.chatgptwrapper.database.entity
2
 
3
  import androidx.room.Entity
4
  import androidx.room.PrimaryKey
 
1
+ package com.matthaigh27.chatgptwrapper.data.local.entity
2
 
3
  import androidx.room.Entity
4
  import androidx.room.PrimaryKey
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/{database → data/local}/entity/ImageEntity.kt RENAMED
@@ -1,4 +1,4 @@
1
- package com.matthaigh27.chatgptwrapper.database.entity
2
 
3
  import androidx.room.Entity
4
  import androidx.room.PrimaryKey
@@ -8,4 +8,5 @@ data class ImageEntity (
8
  @PrimaryKey(autoGenerate = true) val id: Int,
9
  val path: String,
10
  val name: String,
 
11
  )
 
1
+ package com.matthaigh27.chatgptwrapper.data.local.entity
2
 
3
  import androidx.room.Entity
4
  import androidx.room.PrimaryKey
 
8
  @PrimaryKey(autoGenerate = true) val id: Int,
9
  val path: String,
10
  val name: String,
11
+ val dataModified: Long
12
  )
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/models/AlarmModel.kt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ package com.matthaigh27.chatgptwrapper.data.models
2
+
3
+ data class AlarmModel(val id: Int, val time: Long, val enabled: Boolean, val label: String)
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/models/ChatMessageModel.kt ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.matthaigh27.chatgptwrapper.data.models
2
+
3
+ import com.google.gson.JsonElement
4
+
5
+ data class ChatMessageModel(
6
+ val type: Int,
7
+ val content: String? = null,
8
+ val data: JsonElement? = null,
9
+ val hasImage: Boolean = false,
10
+ val image: ByteArray? = null,
11
+ ) {
12
+ override fun equals(other: Any?): Boolean {
13
+ if (this === other) return true
14
+ if (javaClass != other?.javaClass) return false
15
+
16
+ other as ChatMessageModel
17
+
18
+ if (type != other.type) return false
19
+ if (content != other.content) return false
20
+ if (data != other.data) return false
21
+ if (image != null) {
22
+ if (other.image == null) return false
23
+ if (!image.contentEquals(other.image)) return false
24
+ } else if (other.image != null) return false
25
+
26
+ return true
27
+ }
28
+
29
+ override fun hashCode(): Int {
30
+ var result = type
31
+ result = 31 * result + content.hashCode()
32
+ result = 31 * result + (data?.hashCode() ?: 0)
33
+ result = 31 * result + (image?.contentHashCode() ?: 0)
34
+ return result
35
+ }
36
+ }
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/models/ContactModel.kt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ package com.matthaigh27.chatgptwrapper.data.models
2
+
3
+ data class ContactModel(
4
+ var id: String = "",
5
+ var name: String = "",
6
+ var phoneList: ArrayList<String> = ArrayList(),
7
+ var status: String = ""
8
+ )
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/models/HelpCommandModel.kt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ package com.matthaigh27.chatgptwrapper.data.models
2
+
3
+ data class HelpCommandModel (
4
+ var main: String? = null,
5
+ var assist: String? = null
6
+ )
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/models/HelpPromptModel.kt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ package com.matthaigh27.chatgptwrapper.data.models
2
+
3
+ data class HelpPromptModel(
4
+ var name: String = "",
5
+ var description: String = "",
6
+ var prompt: String = "",
7
+ var tags: ArrayList<String> = ArrayList()
8
+ )
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/models/ImageModel.kt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ package com.matthaigh27.chatgptwrapper.data.models
2
+
3
+ import android.net.Uri
4
+
5
+ data class ImageModel(
6
+ val uri: Uri,
7
+ val modifiedDate: Long
8
+ )
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/ApiClient.kt ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.matthaigh27.chatgptwrapper.data.remote
2
+
3
+ import com.matthaigh27.chatgptwrapper.utils.Constants.API_BASE_URL
4
+ import okhttp3.OkHttpClient
5
+ import okhttp3.logging.HttpLoggingInterceptor
6
+ import retrofit2.Retrofit
7
+ import retrofit2.converter.gson.GsonConverterFactory
8
+ import java.util.concurrent.TimeUnit
9
+
10
+ object ApiClient {
11
+ private val client = OkHttpClient.Builder()
12
+ .callTimeout(240, TimeUnit.SECONDS)
13
+ .connectTimeout(240, TimeUnit.SECONDS)
14
+ .readTimeout(240, TimeUnit.SECONDS)
15
+ .writeTimeout(240, TimeUnit.SECONDS)
16
+ .build()
17
+
18
+ private val retrofit = Retrofit.Builder()
19
+ .baseUrl(API_BASE_URL)
20
+ .addConverterFactory(GsonConverterFactory.create())
21
+ .client(client)
22
+ .build()
23
+
24
+ val apiService: ApiService = retrofit.create(ApiService::class.java)
25
+ }
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/ApiResource.kt ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.matthaigh27.chatgptwrapper.data.remote
2
+
3
+ sealed class ApiResource<T>(
4
+ val data: T? = null,
5
+ val message: String? = null
6
+ ) {
7
+ class Success<T>(data: T) : ApiResource<T>(data)
8
+ class Error<T>(message: String, data: T? = null) : ApiResource<T>(data, message)
9
+ class Loading<T> : ApiResource<T>()
10
+ }
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/ApiService.kt ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.matthaigh27.chatgptwrapper.data.remote
2
+
3
+ import com.matthaigh27.chatgptwrapper.data.remote.requests.BaseApiRequest
4
+ import com.matthaigh27.chatgptwrapper.data.remote.requests.NotificationApiRequest
5
+ import com.matthaigh27.chatgptwrapper.data.remote.responses.ApiResponse
6
+ import retrofit2.Call
7
+ import retrofit2.http.Body
8
+ import retrofit2.http.POST
9
+
10
+ interface ApiService {
11
+ @POST("commands")
12
+ fun getAllHelpCommands(@Body request: BaseApiRequest) : Call<ApiResponse>
13
+ @POST("sendNotification")
14
+ fun sendNotification(@Body request: NotificationApiRequest) : Call<ApiResponse>
15
+ }
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/requests/BaseApiRequest.kt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ package com.matthaigh27.chatgptwrapper.data.remote.requests
2
+
3
+ import com.matthaigh27.chatgptwrapper.data.remote.requests.common.Keys
4
+
5
+ data class BaseApiRequest(
6
+ val confs: Keys
7
+ )
8
+
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/requests/NotificationApiRequest.kt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ package com.matthaigh27.chatgptwrapper.data.remote.requests
2
+
3
+ import com.matthaigh27.chatgptwrapper.data.remote.requests.common.Keys
4
+
5
+ data class NotificationApiRequest(
6
+ val message: String,
7
+ val confs: Keys
8
+ )
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/requests/common/Keys.kt ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.matthaigh27.chatgptwrapper.data.remote.requests.common
2
+
3
+ data class Keys(
4
+ val uuid: String,
5
+ val token: String,
6
+ val openai_key: String,
7
+ val pinecone_key: String,
8
+ val pinecone_env: String,
9
+ val firebase_key: String,
10
+ val settings: Settings,
11
+ )
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/requests/common/Settings.kt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ package com.matthaigh27.chatgptwrapper.data.remote.requests.common
2
+
3
+ data class Settings(
4
+ val temperature: Float
5
+ )
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/remote/responses/ApiResponse.kt ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.matthaigh27.chatgptwrapper.data.remote.responses
2
+
3
+ import com.google.gson.JsonElement
4
+
5
+ data class ApiResponse (
6
+ val status_code: Int,
7
+ val message: List<String>,
8
+ val result: Result
9
+ )
10
+
11
+ data class Result (
12
+ val program: String,
13
+ val content: JsonElement,
14
+ val url: String
15
+ )
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/repository/FirebaseRepository.kt ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.matthaigh27.chatgptwrapper.data.repository
2
+
3
+ import com.google.firebase.storage.FirebaseStorage
4
+ import com.matthaigh27.chatgptwrapper.utils.helpers.network.OnFailure
5
+ import com.matthaigh27.chatgptwrapper.utils.helpers.network.OnSuccess
6
+
7
+ object FirebaseRepository {
8
+ fun downloadImageWithName(
9
+ name: String,
10
+ onSuccess: OnSuccess<ByteArray>,
11
+ onFailure: OnFailure<String>
12
+ ) {
13
+ val reference = "images/$name"
14
+
15
+ val storageReference = FirebaseStorage.getInstance().getReference(reference)
16
+ storageReference.getBytes(Long.MAX_VALUE).addOnSuccessListener { bytes ->
17
+ onSuccess(bytes)
18
+ }.addOnFailureListener { e ->
19
+ onFailure(e.toString())
20
+ }
21
+ return
22
+ }
23
+ }
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/repository/RemoteRepository.kt ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.matthaigh27.chatgptwrapper.data.repository
2
+
3
+ import com.matthaigh27.chatgptwrapper.data.remote.ApiClient
4
+ import com.matthaigh27.chatgptwrapper.data.remote.requests.NotificationApiRequest
5
+ import com.matthaigh27.chatgptwrapper.data.remote.responses.ApiResponse
6
+ import com.matthaigh27.chatgptwrapper.utils.helpers.network.OnFailure
7
+ import com.matthaigh27.chatgptwrapper.utils.helpers.network.OnSuccess
8
+ import com.matthaigh27.chatgptwrapper.utils.helpers.network.RequestFactory.buildBaseApiRequest
9
+ import retrofit2.Call
10
+ import retrofit2.Callback
11
+ import retrofit2.Response
12
+
13
+
14
+ object RemoteRepository {
15
+ private val apiService = ApiClient.apiService
16
+
17
+ fun getAllHelpCommands(
18
+ onSuccess: OnSuccess<ApiResponse>,
19
+ onFailure: OnFailure<String>
20
+ ) {
21
+ val call = apiService.getAllHelpCommands(buildBaseApiRequest())
22
+
23
+ call.enqueue(object : Callback<ApiResponse> {
24
+ override fun onResponse(call: Call<ApiResponse>, response: Response<ApiResponse>) {
25
+ response.body()?.let {
26
+ onSuccess(ApiResponse(it.status_code, it.message, it.result))
27
+ }?: run {
28
+ onFailure(response.message())
29
+ }
30
+ }
31
+
32
+ override fun onFailure(call: Call<ApiResponse>, t: Throwable) {
33
+ onFailure(t.message.toString())
34
+ }
35
+ })
36
+ }
37
+
38
+ fun sendNotification(
39
+ request: NotificationApiRequest,
40
+ onSuccess: OnSuccess<ApiResponse>,
41
+ onFailure: OnFailure<String>
42
+ ) {
43
+ val call = apiService.sendNotification(request)
44
+
45
+ call.enqueue(object : Callback<ApiResponse> {
46
+ override fun onResponse(call: Call<ApiResponse>, response: Response<ApiResponse>) {
47
+ response.body()?.let { data ->
48
+ onSuccess(ApiResponse(data.status_code, data.message, data.result))
49
+ }?: run {
50
+ onFailure(response.message())
51
+ }
52
+ }
53
+
54
+ override fun onFailure(call: Call<ApiResponse>, t: Throwable) {
55
+ onFailure(t.message.toString())
56
+ }
57
+ })
58
+ }
59
+ }
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/data/repository/RoomRepository.kt ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.matthaigh27.chatgptwrapper.data.repository
2
+
3
+ import androidx.lifecycle.MutableLiveData
4
+ import com.matthaigh27.chatgptwrapper.RisingApplication
5
+ import com.matthaigh27.chatgptwrapper.data.local.AppDatabase
6
+ import com.matthaigh27.chatgptwrapper.data.local.entity.ContactEntity
7
+ import com.matthaigh27.chatgptwrapper.data.local.entity.ImageEntity
8
+
9
+
10
+ object RoomRepository {
11
+ private var databaseHandler: AppDatabase = AppDatabase.getDatabase(RisingApplication.appContext)
12
+
13
+ private var imageDao = databaseHandler.imageDao()
14
+ private var contactDao = databaseHandler.contactDao()
15
+
16
+ fun getAllImages(): MutableLiveData<List<ImageEntity>> {
17
+ return MutableLiveData(imageDao.getAllData())
18
+ }
19
+
20
+ fun insertImage(entity: ImageEntity) {
21
+ imageDao.insert(entity)
22
+ }
23
+
24
+ fun updateImage(entity: ImageEntity) {
25
+ imageDao.update(entity)
26
+ }
27
+
28
+ fun deleteImage(entity: ImageEntity) {
29
+ imageDao.delete(entity)
30
+ }
31
+
32
+ fun getAllContacts(): MutableLiveData<List<ContactEntity>> {
33
+ return MutableLiveData(contactDao.getAllData())
34
+ }
35
+
36
+ fun insertImage(entity: ContactEntity) {
37
+ contactDao.insert(entity)
38
+ }
39
+
40
+ fun updateImage(entity: ContactEntity) {
41
+ contactDao.update(entity)
42
+ }
43
+
44
+ fun deleteImage(entity: ContactEntity) {
45
+ contactDao.delete(entity)
46
+ }
47
+ }
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/database/dao/ContactDao.kt DELETED
@@ -1,20 +0,0 @@
1
- package com.matthaigh27.chatgptwrapper.database.dao
2
-
3
- import androidx.lifecycle.LiveData
4
- import androidx.room.*
5
- import com.matthaigh27.chatgptwrapper.database.entity.ContactEntity
6
-
7
- @Dao
8
- interface ContactDao {
9
- @Insert
10
- suspend fun insertContact(contact: ContactEntity)
11
-
12
- @Update
13
- suspend fun updateContact(contact: ContactEntity)
14
-
15
- @Delete
16
- suspend fun deleteContact(contact: ContactEntity)
17
-
18
- @Query("SELECT * FROM contacts")
19
- fun getAllContacts(): List<ContactEntity>
20
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/database/dao/ImageDao.kt DELETED
@@ -1,20 +0,0 @@
1
- package com.matthaigh27.chatgptwrapper.database.dao
2
-
3
- import androidx.lifecycle.LiveData
4
- import androidx.room.*
5
- import com.matthaigh27.chatgptwrapper.database.entity.ImageEntity
6
-
7
- @Dao
8
- interface ImageDao {
9
- @Insert
10
- suspend fun insertImage(image: ImageEntity)
11
-
12
- @Update
13
- suspend fun updateImage(image: ImageEntity)
14
-
15
- @Delete
16
- suspend fun deleteImage(image: ImageEntity)
17
-
18
- @Query("SELECT * FROM images")
19
- fun getAllImages(): List<ImageEntity>
20
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/fragments/ChatFragment.kt DELETED
@@ -1,888 +0,0 @@
1
- package com.matthaigh27.chatgptwrapper.fragments
2
-
3
- import android.os.Bundle
4
- import androidx.fragment.app.Fragment
5
- import android.view.LayoutInflater
6
- import android.view.View
7
- import android.view.ViewGroup
8
- import android.annotation.SuppressLint
9
- import android.app.ActionBar.LayoutParams
10
- import android.content.*
11
- import android.graphics.Bitmap
12
- import android.graphics.BitmapFactory
13
- import android.net.Uri
14
- import android.os.Build
15
- import android.os.StrictMode
16
- import android.os.StrictMode.ThreadPolicy
17
- import android.provider.MediaStore
18
- import android.telephony.SmsManager
19
- import android.util.Log
20
- import android.view.KeyEvent
21
- import android.view.View.OnClickListener
22
- import android.view.animation.AccelerateDecelerateInterpolator
23
- import android.view.animation.AlphaAnimation
24
- import android.view.animation.Animation
25
- import android.view.animation.LinearInterpolator
26
- import android.view.animation.RotateAnimation
27
- import android.view.animation.TranslateAnimation
28
- import android.widget.*
29
- import androidx.annotation.RequiresApi
30
- import androidx.recyclerview.widget.LinearLayoutManager
31
- import androidx.recyclerview.widget.RecyclerView
32
- import com.google.firebase.storage.FirebaseStorage
33
- import com.matthaigh27.chatgptwrapper.MyApplication
34
- import com.matthaigh27.chatgptwrapper.R
35
- import com.matthaigh27.chatgptwrapper.adapters.ChatAdapter
36
- import com.matthaigh27.chatgptwrapper.database.MyDatabase
37
- import com.matthaigh27.chatgptwrapper.database.entity.ImageEntity
38
- import com.matthaigh27.chatgptwrapper.widgets.ImagePickerWidget
39
- import com.matthaigh27.chatgptwrapper.widgets.ImagePickerWidget.OnPositiveButtonClickListener
40
- import com.matthaigh27.chatgptwrapper.models.*
41
- import com.matthaigh27.chatgptwrapper.models.common.HelpCommandModel
42
- import com.matthaigh27.chatgptwrapper.models.common.HelpPromptModel
43
- import com.matthaigh27.chatgptwrapper.models.viewmodels.ChatMessageModel
44
- import com.matthaigh27.chatgptwrapper.services.api.HttpClient
45
- import com.matthaigh27.chatgptwrapper.services.api.HttpRisingInterface
46
- import com.matthaigh27.chatgptwrapper.utils.Constants.*
47
- import com.matthaigh27.chatgptwrapper.utils.Utils
48
- import com.matthaigh27.chatgptwrapper.widgets.ContactDetailItem
49
- import com.qw.photo.CoCo
50
- import com.qw.photo.callback.CoCoAdapter
51
- import com.qw.photo.callback.CoCoCallBack
52
- import com.qw.photo.constant.Range
53
- import com.qw.photo.pojo.PickResult
54
- import com.qw.photo.pojo.TakeResult
55
- import kotlinx.coroutines.CoroutineScope
56
- import kotlinx.coroutines.Dispatchers
57
- import kotlinx.coroutines.launch
58
- import okhttp3.*
59
- import org.json.JSONException
60
- import org.json.JSONObject
61
- import java.io.*
62
- import java.util.*
63
- import kotlin.collections.ArrayList
64
-
65
- class ChatFragment : Fragment(), OnClickListener, HttpRisingInterface {
66
- private lateinit var rootView: View
67
- private var mContext: Context? = null
68
-
69
- /** ui components for chatlist recyclerview */
70
- private lateinit var mRvChatList: RecyclerView
71
- private lateinit var mEtMessage: EditText
72
-
73
- /** ui components for loading spinner */
74
- private lateinit var mSpLoading: ImageView
75
- private lateinit var mTvLoading: TextView
76
- private lateinit var mLlLoading: LinearLayout
77
-
78
- /** ui components for loading photo */
79
- private lateinit var mLlLoadPhoto: LinearLayout
80
- private lateinit var mIvLoadedPhoto: ImageView
81
- private lateinit var mBtnCancelLoadPhoto: TextView
82
-
83
- /** adapter for chat recyclerview and arraylist for store chatting history */
84
- private var mMessageList: ArrayList<ChatMessageModel> = ArrayList()
85
- private lateinit var mAdapter: ChatAdapter
86
-
87
- /** when a user selects image by camera or gallery,
88
- * these two variables are used to save image source and name */
89
- private var mSelectedImage: ByteArray? = null
90
- private var mSelectedImageName: String = ""
91
-
92
- /**
93
- * mImagePickerType is
94
- * 'image_uplaod' when user is going to upload image
95
- * 'image_picker' when user is going to pick image for prompting
96
- */
97
- private lateinit var mImagePickerWidget: ImagePickerWidget
98
- private var mImagePickerType: String = ""
99
-
100
- /** HttpClient for restful apis */
101
- private lateinit var httpClient: HttpClient
102
-
103
- /** list of help prompt commands */
104
- private var mHelpPromptList: ArrayList<HelpPromptModel>? = null
105
-
106
- /** animation variable for loading spinner */
107
- private val rotate = RotateAnimation(
108
- 0f, 360f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f
109
- )
110
-
111
- /** room database handler for local database */
112
- private lateinit var mRoomDataHandler: MyDatabase
113
-
114
- /** status variable that checks if widget in chatting interface does exist */
115
- private var mIsExistWidget: Boolean = false
116
-
117
- /**
118
- * this is invoked when users click the message icon to send sms on contact detail dialog
119
- * that is shown when a user search contacts
120
- */
121
- private var mSMSOnClickListener: ContactDetailItem.OnSMSClickListener? = null
122
-
123
- override fun onCreateView(
124
- inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
125
- ): View {
126
- rootView = inflater.inflate(R.layout.fragment_chat, container, false)
127
- init()
128
- return rootView
129
- }
130
-
131
- private fun init() {
132
- initEnvironment()
133
- initValues()
134
- initView()
135
- initDatabase()
136
-
137
- trainImages()
138
- getAllPromptCommands()
139
- }
140
-
141
-
142
- private fun getAllPromptCommands() {
143
- showLoading(true, "Loading Help Prompt Data")
144
- httpClient.getALlHelpPromptCommands()
145
- }
146
-
147
-
148
- private fun initEnvironment() {
149
- val policy = ThreadPolicy.Builder().permitAll().build()
150
- StrictMode.setThreadPolicy(policy)
151
- }
152
-
153
- private fun initValues() {
154
- mContext = context
155
-
156
- httpClient = HttpClient(this)
157
-
158
- rotate.duration = 3000
159
- rotate.repeatCount = Animation.INFINITE
160
- rotate.interpolator = LinearInterpolator()
161
-
162
- initSMSOnClickListener()
163
- }
164
-
165
- private fun initView() {
166
- this.mAdapter = ChatAdapter(mMessageList, mContext!!)
167
- this.mAdapter.mOnSMSClickListener = mSMSOnClickListener
168
- mRvChatList = rootView.findViewById<View>(R.id.chatRecycleView) as RecyclerView
169
- mRvChatList.adapter = mAdapter
170
-
171
- val sendSMSListener = object : ChatAdapter.MessageWidgetListener {
172
- override fun sentSMS(phonenumber: String, message: String) {
173
- mIsExistWidget = false
174
- sendSms(phonenumber, message)
175
- addMessage(
176
- "You sent SMS with belowing content.\n\n " + "To: $phonenumber\n " + "Message: $message",
177
- false
178
- )
179
- }
180
-
181
- override fun canceledSMS() {
182
- mIsExistWidget = false
183
- addMessage("You canceled SMS.", false)
184
- }
185
-
186
- override fun sentHelpPrompt(prompt: String) {
187
- mIsExistWidget = false
188
- addMessage(prompt, true)
189
- }
190
-
191
- override fun canceledHelpPrompt() {
192
- mIsExistWidget = false
193
- addMessage("You canceled help command.", false)
194
- }
195
- }
196
-
197
- mAdapter.setMessageWidgetListener(sendSMSListener)
198
-
199
- val linearLayoutManager = LinearLayoutManager(mContext)
200
- mRvChatList.layoutManager = linearLayoutManager
201
-
202
- this.mEtMessage = rootView.findViewById(R.id.et_message)
203
- mEtMessage.setOnKeyListener { _, keyCode, keyEvent ->
204
- if (keyEvent.action == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_ENTER) {
205
- addMessage(mEtMessage.text.toString(), true)
206
- }
207
- return@setOnKeyListener false
208
- }
209
-
210
- rootView.findViewById<View>(R.id.btn_send_message).setOnClickListener(this)
211
- rootView.findViewById<View>(R.id.btn_image_upload).setOnClickListener(this)
212
- rootView.findViewById<View>(R.id.btn_image_picker).setOnClickListener(this)
213
-
214
- mSpLoading = rootView.findViewById(R.id.sp_loading)
215
- mTvLoading = rootView.findViewById(R.id.tv_loading)
216
- mLlLoading = rootView.findViewById(R.id.ll_loading)
217
-
218
- mLlLoadPhoto = rootView.findViewById(R.id.ll_load_photo)
219
- mIvLoadedPhoto = rootView.findViewById(R.id.iv_load_photo)
220
- mBtnCancelLoadPhoto = rootView.findViewById(R.id.btn_cancel_load_photo)
221
- mBtnCancelLoadPhoto.setOnClickListener(this)
222
-
223
- initImagePickerWidget()
224
- }
225
-
226
- private fun initDatabase() {
227
- mRoomDataHandler = MyDatabase.getDatabase(mContext!!)
228
- }
229
-
230
- private fun initSMSOnClickListener() {
231
- mSMSOnClickListener = object : ContactDetailItem.OnSMSClickListener {
232
- override fun onSMSClickListener(phoneNumber: String) {
233
- addMessage(
234
- "SMS",
235
- isMe = false,
236
- isSend = false,
237
- isWidget = true,
238
- widgetType = MSG_WIDGET_TYPE_SMS,
239
- widgetDescription = phoneNumber
240
- )
241
- }
242
-
243
- override fun onVoiceCallListener(phoneNumber: String, toName: String) {
244
- addMessage(
245
- message = "You made a voice call to $toName($phoneNumber)",
246
- isMe = false,
247
- isSend = false
248
- )
249
- }
250
- }
251
- }
252
-
253
- /**
254
- * set loading spinner visible
255
- */
256
- private fun setDisableActivity(enable: Boolean) {
257
- runOnUIThread {
258
- mEtMessage.isEnabled = enable
259
- rootView.findViewById<View>(R.id.btn_send_message).isEnabled = enable
260
- rootView.findViewById<View>(R.id.btn_image_upload).isEnabled = enable
261
- rootView.findViewById<View>(R.id.btn_image_picker).isEnabled = enable
262
- }
263
- }
264
-
265
- /**
266
- * set loading spinner visible
267
- */
268
- private fun showLoading(visible: Boolean, text: String = "") {
269
- runOnUIThread {
270
- setDisableActivity(!visible)
271
-
272
- if (visible) {
273
- mSpLoading.startAnimation(rotate)
274
- mLlLoading.visibility = View.VISIBLE
275
- mTvLoading.text = text
276
- } else {
277
- mSpLoading.clearAnimation()
278
- mLlLoading.visibility = View.GONE
279
- }
280
- }
281
- }
282
-
283
- /**
284
- * show overlay when you picked image and users can cancel to load photo if users want
285
- */
286
- private fun showLoadPhotoOverlay(imageByteArray: ByteArray) {
287
- mLlLoadPhoto.visibility = View.VISIBLE
288
-
289
- mIvLoadedPhoto.setImageBitmap(
290
- BitmapFactory.decodeByteArray(
291
- /* data = */ imageByteArray, /* offset = */ 0, /* length = */ imageByteArray.size
292
- )
293
- )
294
- }
295
-
296
- /**
297
- * Bind broadcast sent by MessageService
298
- */
299
- private val receiver = object : BroadcastReceiver() {
300
- @RequiresApi(Build.VERSION_CODES.TIRAMISU)
301
- override fun onReceive(context: Context, intent: Intent) {
302
- Log.d(TAG, "Receive")
303
- }
304
- }
305
-
306
-
307
- /**
308
- * get download url from firebase imagename in response and generate download url from the imagename
309
- * With getBitmapFromURL method, get bitmap of the image and set it to mSelectedImage to display it to chatlist
310
- * Finally, run addmessage and insert it to chat recyclerview
311
- *
312
- * @param imageName the firebase store imagename
313
- */
314
- private fun getImageResponse(imageName: String, imageDesc: String) {
315
- if (imageName.isEmpty() && imageDesc.isNotEmpty()) {
316
- addMessage(message = imageDesc, isMe = false)
317
- return
318
- }
319
-
320
- showLoading(visible = true, text = LOADING_DOWNLOADING_IMAGE)
321
-
322
- val imageNameToUpload = "images/$imageName"
323
-
324
- val storageReference = FirebaseStorage.getInstance().getReference(imageNameToUpload)
325
- storageReference.downloadUrl.addOnSuccessListener { uri ->
326
- try {
327
- val image = Utils.instance.getBitmapFromURL(uri.toString())
328
- if (image == null) showToast("can not get bitmap from url")
329
- val byteArrayOutputStream = ByteArrayOutputStream()
330
-
331
- val isSuccess =
332
- image!!.compress(Bitmap.CompressFormat.JPEG, 100, byteArrayOutputStream)
333
- if (!isSuccess) {
334
- showToast("Fail to compress image")
335
- }
336
- mSelectedImage = byteArrayOutputStream.toByteArray()
337
- } catch (e: Exception) {
338
- showToast("cannot get download url from firebase store")
339
- e.printStackTrace()
340
- }
341
-
342
- addMessage(message = imageDesc, isMe = false)
343
- showLoading(visible = false)
344
- }
345
- return
346
- }
347
-
348
- override fun onResume() {
349
- super.onResume()
350
- updateChangedUserData()
351
- }
352
-
353
- private fun updateChangedUserData() {
354
- trainContacts()
355
- }
356
-
357
- /**
358
- * Open Help Command Widget with analysing string command
359
- */
360
- private fun openHelpCommandWidget(strCommand: String) {
361
- try {
362
- val command: HelpCommandModel = Utils.instance.getHelpCommandFromStr(strCommand)
363
- if (command.isMainCommand()) {
364
- when (command.mainCommandName) {
365
- "sms" -> {
366
- addMessage(
367
- message = "SMS",
368
- isMe = false,
369
- isSend = false,
370
- isWidget = true,
371
- widgetType = MSG_WIDGET_TYPE_SMS
372
- )
373
- }
374
-
375
- else -> {
376
- mHelpPromptList!!.forEach { model ->
377
- if (model.name == command.mainCommandName) {
378
- addMessage(
379
- "Help Prompt Command",
380
- isMe = true,
381
- isSend = false,
382
- isWidget = true,
383
- widgetType = MSG_WIDGET_TYPE_HELP_PRMOPT,
384
- widgetDescription = model.toString()
385
- )
386
- return
387
- }
388
- }
389
- addMessage(
390
- message = "No such command name exists.", isMe = false, isSend = false
391
- )
392
- }
393
- }
394
- } else {
395
- if (command.assistCommandName == "all") {
396
- val usage =
397
- "usage:\n" + "- help command: /help [command name]\n" + "- prompt command: /<command name>\n\n"
398
- var strHelpList = "help prompt commands:"
399
- mHelpPromptList!!.forEach { model ->
400
- strHelpList += "\n- " + model.name
401
- }
402
-
403
- addMessage(message = usage + strHelpList, isMe = false, isSend = false)
404
- } else {
405
- var strHelpDesc = ""
406
- mHelpPromptList!!.forEach { model ->
407
- if (model.name == command.assistCommandName) {
408
- var strTags = ""
409
- model.tags!!.forEach { tag ->
410
- strTags += " $tag"
411
- }
412
- strHelpDesc = "description: " + model.description + "\ntags:" + strTags
413
- }
414
- }
415
- if (strHelpDesc.isEmpty()) addMessage(
416
- message = "No such command name exists.", isMe = false, isSend = false
417
- )
418
- else addMessage(message = strHelpDesc, isMe = false, isSend = false)
419
- }
420
- }
421
- } catch (e: Exception) {
422
- e.printStackTrace()
423
- showToast(e.message.toString())
424
- }
425
- }
426
-
427
- /**
428
- * add message to chat list
429
- * if users picked image from camera or gallery, send post request to `sendNotification` to ask generally
430
- * else do 'imageRelateness' to search image
431
- *
432
- * @param message message content for chat list
433
- * @param isMe this identify if this is sent by langchain server or users send message to server
434
- * @param isSend is boolean that checks if you send request to server
435
- * @param isWidget is boolean that checks if message item has widget
436
- * @param widgetType is type of Widget ex: SMS, HELP_COMMAND, etc
437
- * @param widgetDescription is string that saves information for widget
438
- */
439
- @SuppressLint("NotifyDataSetChanged")
440
- private fun addMessage(
441
- message: String,
442
- isMe: Boolean,
443
- isSend: Boolean = true,
444
- isWidget: Boolean = false,
445
- widgetType: String = "",
446
- widgetDescription: String = ""
447
- ) {
448
- if ((message.isEmpty() && mSelectedImage == null) || mIsExistWidget) return
449
- if (isWidget) {
450
- if (widgetType != MSG_WIDGET_TYPE_SEARCH_CONTACT) {
451
- mIsExistWidget = true
452
- }
453
- }
454
-
455
- val msg = ChatMessageModel()
456
- msg.message = message
457
- msg.isMe = isMe
458
- msg.isWidget = isWidget
459
- msg.widgetType = widgetType
460
- msg.widgetDescription = widgetDescription
461
-
462
- /**
463
- * if users picked some image from camera or gallery, add the image to chatting message
464
- */
465
- if (mSelectedImage != null) {
466
- msg.image = mSelectedImage
467
- mSelectedImage = null
468
- }
469
-
470
- /**
471
- * if users picked some image from camera or gallery, the image upload to firebase store
472
- * mSelectedImageName is uuid created uploading to firebase store
473
- */
474
- if (mSelectedImageName.isNotEmpty()) {
475
- msg.imageName = mSelectedImageName
476
- mSelectedImageName = ""
477
- }
478
-
479
- runOnUIThread {
480
- mLlLoadPhoto.visibility = View.GONE
481
-
482
- mMessageList.add(msg)
483
- mAdapter.notifyDataSetChanged()
484
- mEtMessage.setText("")
485
- mRvChatList.scrollTo(/* x = */ 1000, /* y = */ -1000)
486
- mRvChatList.scrollToPosition(mMessageList.size - 1)
487
- }
488
-
489
- if ((message.isNotEmpty() && message.first() == '/') && isMe) {
490
- openHelpCommandWidget(message)
491
- return
492
- }
493
-
494
- if (isMe) {
495
- if (!isSend) {
496
- return
497
- }
498
-
499
- showLoading(visible = true, text = LOADING_ASKING_TO_GPT)
500
- if (msg.image != null) {
501
- httpClient.callImageRelatedness(msg.imageName)
502
- } else {
503
- httpClient.callSendNotification(message)
504
- }
505
- }
506
- }
507
-
508
- override fun onClick(view: View) {
509
- when (view.id) {
510
- R.id.btn_send_message -> {
511
- addMessage(mEtMessage.text.toString(), true)
512
- }
513
-
514
- R.id.btn_image_upload -> {
515
- mImagePickerType = PICKERTYPE_IMAGE_UPLOAD
516
- if(rootView.findViewById<View>(R.id.ll_toolbar).visibility == View.VISIBLE)
517
- hideSlidingWidget()
518
- else
519
- showSlidingWidget()
520
- }
521
-
522
- R.id.btn_image_picker -> {
523
- mImagePickerType = PICKERTYPE_IMAGE_PICK
524
- if(rootView.findViewById<View>(R.id.ll_toolbar).visibility == View.VISIBLE)
525
- hideSlidingWidget()
526
- else
527
- showSlidingWidget()
528
- }
529
-
530
- R.id.btn_cancel_load_photo -> {
531
- mLlLoadPhoto.visibility = View.GONE
532
- mSelectedImageName = ""
533
- mSelectedImage = null
534
- }
535
- }
536
- }
537
-
538
- /**
539
- * open browser with url
540
- * @param url to open with browser
541
- */
542
- private fun openBrowser(url: String) {
543
- val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
544
- startActivity(Intent.createChooser(intent, "Browse with"))
545
- }
546
-
547
- /**
548
- * calls when finish picking image
549
- *
550
- * @param imageByteData is bytearray of picked image
551
- * @param type if users are going to upload image or pick image for query
552
- */
553
- private fun pickedImage(imageByteData: ByteArray, type: String) {
554
- if (type == "image_upload") {
555
- uploadImageToFirebaseStorage(imageByteData)
556
- } else {
557
- uploadSearchImage(imageByteData)
558
- }
559
- }
560
-
561
- /**
562
- * toast message is invoked when error happens
563
- */
564
- private fun showToast(message: String) {
565
- runOnUIThread {
566
- Toast.makeText(
567
- mContext, message, Toast.LENGTH_SHORT
568
- ).show()
569
- }
570
- }
571
-
572
-
573
- /**
574
- * this can show dialog with camera and gallery icon.
575
- * when you click camera icon, camera application runs and users can get an image by capturing using camera.
576
- * when you click gallery icon, users can select image in your storage.
577
- * A picked image converts into bytearray data and upload to firebase storage.
578
- */
579
- private fun initImagePickerWidget() {
580
- mImagePickerWidget = ImagePickerWidget(mContext!!)
581
-
582
- val myImplementation = object : OnPositiveButtonClickListener {
583
- override fun onPositiveBtnClick(isCamera: Boolean?) {
584
- if (isCamera == true) {
585
- CoCo.with(activity!!).take(Utils.instance.createSDCardFile())
586
- .start(object : CoCoAdapter<TakeResult>() {
587
- override fun onSuccess(data: TakeResult) {
588
- val byteArray: ByteArray =
589
- Utils.instance.getBytesFromPath(data.savedFile!!.absolutePath)
590
- pickedImage(
591
- byteArray, mImagePickerType
592
- )
593
- }
594
-
595
- override fun onFailed(exception: Exception) {
596
- super.onFailed(exception)
597
- showToast("Fail to pick image. Please try again.")
598
- }
599
- })
600
- } else {
601
- CoCo.with(activity!!).pick().range(Range.PICK_CONTENT)
602
- .start(object : CoCoCallBack<PickResult> {
603
-
604
- override fun onSuccess(data: PickResult) {
605
- val byteArray: ByteArray? =
606
- Utils.instance.convertImageToByte(data.originUri)
607
- if (byteArray == null) showToast("can not find such a file")
608
- pickedImage(byteArray!!, mImagePickerType)
609
- }
610
-
611
- override fun onFailed(exception: Exception) {
612
- showToast("Fail to pick image. Please try again.")
613
- }
614
- })
615
- }
616
- }
617
- }
618
-
619
- mImagePickerWidget.setOnClickListener(myImplementation)
620
-
621
- val slidingWidget = rootView.findViewById<LinearLayout>(R.id.ll_toolbar)
622
- slidingWidget.addView(mImagePickerWidget)
623
- }
624
-
625
- private fun uploadSearchImage(imageByteArray: ByteArray) {
626
- showLoading(true, LOADING_ANALYZING_IMAGE)
627
- val storageRef = FirebaseStorage.getInstance().reference
628
- val uuid = UUID.randomUUID()
629
- val imageName = "images/${uuid}"
630
- val imageRef = storageRef.child(imageName)
631
-
632
- val uploadTask = imageRef.putBytes(imageByteArray)
633
- uploadTask.addOnFailureListener {
634
- showLoading(false)
635
- }.addOnSuccessListener {
636
- Log.d(TAG, "Success upload to firebase storage")
637
- showLoading(false)
638
-
639
- mSelectedImageName = "$uuid"
640
- mSelectedImage = imageByteArray
641
-
642
- showLoadPhotoOverlay(imageByteArray)
643
- }
644
- }
645
-
646
- /**
647
- * @param imageByteArray ByteArray data for image to upload to firebase storage
648
- */
649
- private fun uploadImageToFirebaseStorage(imageByteArray: ByteArray) {
650
- showLoading(true, LOADING_UPLOADING_IAMGE)
651
- val storageRef = FirebaseStorage.getInstance().reference
652
- val uuid = UUID.randomUUID()
653
- val imageName = "images/${uuid}"
654
- val imageRef = storageRef.child(imageName)
655
-
656
- val uploadTask = imageRef.putBytes(imageByteArray)
657
- uploadTask.addOnFailureListener {
658
- showLoading(false)
659
- }.addOnSuccessListener {
660
- Log.d(TAG, "Success upload to firebase storage")
661
-
662
- showLoading(false)
663
- httpClient.callImageUpload("$uuid")
664
- }
665
- }
666
-
667
- override fun onSuccessResult(msg: String) {
668
- showLoading(false)
669
- try {
670
- val json = JSONObject(msg)
671
- if (json.has(RESPONSE_TYPE_PROGRAM)) {
672
- when (json.getString(RESPONSE_TYPE_PROGRAM)) {
673
- RESPONSE_TYPE_BROWSER -> {
674
- addMessage(json.getString(RESPONSE_TYPE_URL), false)
675
- openBrowser(json.getString(RESPONSE_TYPE_URL))
676
- return
677
- }
678
-
679
- RESPONSE_TYPE_ALERT -> {
680
- MyApplication.appContext.showNotification(
681
- json.getString(
682
- RESPONSE_TYPE_CONTENT
683
- )
684
- )
685
- return
686
- }
687
-
688
- RESPONSE_TYPE_MESSAGE -> {
689
- addMessage(json.getString(RESPONSE_TYPE_CONTENT), false)
690
- return
691
- }
692
-
693
- RESPONSE_TYPE_IMAGE -> {
694
- try {
695
- val imageRes = JSONObject(json.getString(RESPONSE_TYPE_CONTENT))
696
-
697
- val imageName = if (imageRes.has("image_name")) {
698
- imageRes["image_name"] as String
699
- } else {
700
- ""
701
- }
702
-
703
- val imageDesc = if (imageRes.has("image_desc")) {
704
- imageRes["image_desc"] as String
705
- } else {
706
- ""
707
- }
708
-
709
- getImageResponse(imageName, imageDesc)
710
- } catch (e: Exception) {
711
- e.printStackTrace()
712
- }
713
- return
714
- }
715
-
716
- RESPONSE_TYPE_SMS -> {
717
- addMessage(
718
- json.getString(RESPONSE_TYPE_CONTENT),
719
- false,
720
- isSend = false,
721
- isWidget = true,
722
- widgetType = MSG_WIDGET_TYPE_SMS
723
- )
724
- }
725
-
726
- RESPONSE_TYPE_HELP_COMMAND -> {
727
- try {
728
- mHelpPromptList = Utils.instance.getHelpCommandListFromJsonString(
729
- json.getString(RESPONSE_TYPE_CONTENT)
730
- )
731
- } catch (e: java.lang.Exception) {
732
- e.printStackTrace()
733
- showToast("JSON Error occured")
734
- }
735
- }
736
-
737
- RESPONSE_TYPE_CONTACT -> {
738
- try {
739
- addMessage(
740
- message = "Contacts that you are looking for.",
741
- isMe = false,
742
- isSend = false,
743
- isWidget = true,
744
- widgetType = MSG_WIDGET_TYPE_SEARCH_CONTACT,
745
- widgetDescription = json.getString(RESPONSE_TYPE_CONTENT)
746
- )
747
- } catch (e: Exception) {
748
- e.printStackTrace()
749
- }
750
- }
751
- }
752
- }
753
- } catch (e: JSONException) {
754
- e.printStackTrace()
755
- addMessage(msg, false)
756
- }
757
- }
758
-
759
- override fun onFailureResult(msg: String) {
760
- showLoading(false)
761
-
762
- showToast(msg)
763
- }
764
-
765
- private fun queryImagesFromExternalStorage(contentResolver: ContentResolver): ArrayList<Uri> {
766
- val listOfImageUris = ArrayList<Uri>()
767
-
768
- val projection = arrayOf(MediaStore.Images.Media._ID)
769
-
770
- val sortOrder = "${MediaStore.Images.Media.DATE_TAKEN} DESC"
771
-
772
- contentResolver.query(
773
- MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection, null, null, sortOrder
774
- )?.use { cursor ->
775
- val idColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID)
776
- while (cursor.moveToNext()) {
777
- val id = cursor.getLong(idColumn)
778
- val contentUri = Uri.withAppendedPath(
779
- MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id.toString()
780
- )
781
- listOfImageUris.add(contentUri)
782
- }
783
- }
784
- return listOfImageUris
785
- }
786
-
787
- private fun trainImages() {
788
- CoroutineScope(Dispatchers.IO).launch {
789
- val images = queryImagesFromExternalStorage(requireContext().contentResolver)
790
- val originalImages = mRoomDataHandler.imageDao().getAllImages()
791
-
792
- images.forEach { uri ->
793
- var isExist = false
794
- val path = Utils.instance.getRealPathFromUri(requireContext(), uri)
795
- for (i in originalImages.indices) {
796
- val entity: ImageEntity = originalImages[i]
797
- if (entity.path == path) {
798
- isExist = true
799
- break
800
- }
801
- }
802
- if (!isExist) {
803
- val byteArray = Utils.instance.getBytesFromPath(path)
804
- val uuid = uploadImageToFirebaseStorage(byteArray)
805
-
806
- if (path != null)
807
- mRoomDataHandler.imageDao().insertImage(ImageEntity(0, path, "$uuid"))
808
- }
809
- }
810
- }
811
- }
812
-
813
- private fun trainContacts() {
814
- showLoading(true, "Train Contacts")
815
- val contacts = Utils.instance.getContacts(mContext!!)
816
- CoroutineScope(Dispatchers.Main).launch {
817
- val changedContacts = Utils.instance.getChangedContacts(contacts, mRoomDataHandler)
818
- httpClient.trainContacts(changedContacts)
819
- }
820
- }
821
-
822
- fun sendSms(phoneNumber: String, message: String) {
823
- try {
824
- val smsManager = SmsManager.getDefault()
825
- val parts = smsManager.divideMessage(message)
826
- smsManager.sendMultipartTextMessage(/* destinationAddress = */ phoneNumber, /* scAddress = */
827
- null, /* parts = */
828
- parts, /* sentIntents = */
829
- null, /* deliveryIntents = */
830
- null)
831
- } catch (e: SecurityException) {
832
- e.printStackTrace()
833
- } catch (e: Exception) {
834
- e.printStackTrace()
835
- }
836
- }
837
-
838
- @SuppressLint("UseRequireInsteadOfGet")
839
- fun runOnUIThread(action: () -> Unit) {
840
- requireActivity().runOnUiThread {
841
- action()
842
- }
843
- }
844
-
845
- private fun showSlidingWidget() {
846
- val slidingWidget = rootView.findViewById<View>(R.id.ll_toolbar)
847
-
848
- val dy = slidingWidget.measuredHeight.toFloat()
849
- slidingWidget.visibility = View.VISIBLE
850
-
851
- val anim = TranslateAnimation(0f, 0f, dy, 0f).apply {
852
- duration = 150 // Set the animation duration, e.g., 300ms
853
- interpolator = AccelerateDecelerateInterpolator()
854
- setAnimationListener(object : Animation.AnimationListener {
855
- override fun onAnimationStart(animation: Animation?) {
856
- slidingWidget.visibility = View.VISIBLE
857
- }
858
-
859
- override fun onAnimationEnd(animation: Animation?) {}
860
-
861
- override fun onAnimationRepeat(animation: Animation?) {}
862
- })
863
- }
864
-
865
- slidingWidget.startAnimation(anim)
866
- }
867
-
868
- private fun hideSlidingWidget() {
869
- val slidingWidget = rootView.findViewById<View>(R.id.ll_toolbar)
870
-
871
- val anim = AlphaAnimation(1f, 0f).apply {
872
- duration = 100 // Set the animation duration, e.g., 300ms
873
- interpolator = AccelerateDecelerateInterpolator()
874
- setAnimationListener(object : Animation.AnimationListener {
875
- override fun onAnimationStart(animation: Animation?) {}
876
-
877
- override fun onAnimationEnd(animation: Animation?) {
878
- slidingWidget.visibility = View.GONE
879
- }
880
-
881
- override fun onAnimationRepeat(animation: Animation?) {}
882
- })
883
- }
884
-
885
- slidingWidget.startAnimation(anim)
886
- }
887
- }
888
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/models/common/ContactModel.kt DELETED
@@ -1,19 +0,0 @@
1
- package com.matthaigh27.chatgptwrapper.models.common
2
-
3
- class ContactModel {
4
- var id: String = ""
5
- var name: String = ""
6
- var phoneList: ArrayList<String>? = null
7
- var status: String = ""
8
-
9
- init {
10
- phoneList = ArrayList()
11
- }
12
-
13
- fun setData(id: String, name: String, phoneList: ArrayList<String>, status: String) {
14
- this.id = id
15
- this.name = name
16
- this.phoneList = phoneList
17
- this.status = status
18
- }
19
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/models/common/HelpCommandModel.kt DELETED
@@ -1,10 +0,0 @@
1
- package com.matthaigh27.chatgptwrapper.models.common
2
-
3
- class HelpCommandModel {
4
- var mainCommandName: String = ""
5
- var assistCommandName: String = ""
6
-
7
- fun isMainCommand(): Boolean {
8
- return mainCommandName != "help"
9
- }
10
- }
 
 
 
 
 
 
 
 
 
 
 
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/models/common/HelpPromptModel.kt DELETED
@@ -1,25 +0,0 @@
1
- package com.matthaigh27.chatgptwrapper.models.common
2
-
3
- import com.google.gson.Gson
4
- import com.google.gson.GsonBuilder
5
-
6
- class HelpPromptModel {
7
- var name: String = ""
8
- var description: String = ""
9
- var prompt: String = ""
10
- var tags: ArrayList<String>? = null
11
-
12
- override fun toString(): String {
13
- val gson = Gson()
14
- val str = gson.toJson(this)
15
- return str
16
- }
17
-
18
- companion object {
19
- fun initModelWithString(strJson: String): HelpPromptModel {
20
- val gson = Gson()
21
- val model: HelpPromptModel = gson.fromJson(strJson, HelpPromptModel::class.java)
22
- return model
23
- }
24
- }
25
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/models/common/ImagePromptModel.kt DELETED
@@ -1,11 +0,0 @@
1
- package com.matthaigh27.chatgptwrapper.models.common
2
-
3
- class ImagePromptModel {
4
- var id: String = ""
5
- var path: String = ""
6
-
7
- constructor(id: String, path: String) {
8
- this.id = id
9
- this.path = path
10
- }
11
- }
 
 
 
 
 
 
 
 
 
 
 
 
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/models/requestmodels/RequestBodyModel.kt DELETED
@@ -1,86 +0,0 @@
1
- package com.matthaigh27.chatgptwrapper.models.requestmodels
2
-
3
- import com.matthaigh27.chatgptwrapper.MyApplication
4
- import org.json.JSONException
5
- import org.json.JSONObject
6
-
7
- class RequestBodyModel(builder: Builder) {
8
-
9
- /** this identify request type
10
- * example: it will be 'message' when users send message, 'image' when users upload image
11
- */
12
- var type: String = ""
13
- var token: String = ""
14
- var message: String = ""
15
- var imageName: String = ""
16
- var uuid: String = ""
17
-
18
- init {
19
- this.token = MyApplication.appContext.getFCMToken()
20
- this.uuid = MyApplication.appContext.getUUID()
21
- this.message = builder.message
22
- this.imageName = builder.imageName
23
- }
24
-
25
- @Throws(JSONException::class)
26
- fun buildJsonObject(): JSONObject {
27
-
28
- val jsonObject = JSONObject()
29
- jsonObject.accumulate("type", type)
30
- jsonObject.accumulate("token", token)
31
- jsonObject.accumulate("message", message)
32
- jsonObject.accumulate("image_name", imageName)
33
- jsonObject.accumulate("uuid", uuid)
34
-
35
- return jsonObject
36
- }
37
-
38
- class Builder {
39
- var type: String = ""
40
- var token: String = ""
41
- var message: String = ""
42
- var imageName: String = ""
43
- var uuid: String = ""
44
-
45
- constructor() {
46
-
47
- }
48
-
49
- constructor(request: RequestBodyModel) {
50
- this.type = request.type
51
- this.token = request.token
52
- this.message = request.message
53
- this.imageName = request.imageName
54
- this.uuid = request.uuid
55
- }
56
-
57
- fun type(type: String): Builder {
58
- this.type = type
59
- return this
60
- }
61
-
62
- fun token(token: String): Builder {
63
- this.token = token
64
- return this
65
- }
66
-
67
- fun message(message: String): Builder {
68
- this.message = message
69
- return this
70
- }
71
-
72
- fun imageName(imageName: String): Builder {
73
- this.imageName = imageName
74
- return this
75
- }
76
-
77
- fun uuid(uuid: String): Builder {
78
- this.uuid = uuid
79
- return this
80
- }
81
-
82
- fun build(): RequestBodyModel {
83
- return RequestBodyModel(this)
84
- }
85
- }
86
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/models/requestmodels/RequestTrainContactModel.kt DELETED
@@ -1,79 +0,0 @@
1
- package com.matthaigh27.chatgptwrapper.models.requestmodels
2
-
3
- import com.matthaigh27.chatgptwrapper.MyApplication
4
- import com.matthaigh27.chatgptwrapper.models.common.ContactModel
5
- import com.matthaigh27.chatgptwrapper.utils.Utils
6
- import org.json.JSONArray
7
- import org.json.JSONException
8
- import org.json.JSONObject
9
-
10
- class RequestTrainContactModel(builder: Builder) {
11
-
12
- /** this identify request type
13
- * example: it will be 'message' when users send message, 'image' when users upload image
14
- */
15
- var type: String = ""
16
- var token: String = ""
17
- var contacts = JSONArray()
18
- var uuid: String = ""
19
-
20
- init {
21
- this.token = MyApplication.appContext.getFCMToken()
22
- this.uuid = MyApplication.appContext.getUUID()
23
- this.contacts = builder.contacts
24
- }
25
-
26
- @Throws(JSONException::class)
27
- fun buildJsonObject(): JSONObject {
28
-
29
- val jsonObject = JSONObject()
30
- jsonObject.accumulate("type", type)
31
- jsonObject.accumulate("token", token)
32
- jsonObject.accumulate("contacts", contacts)
33
- jsonObject.accumulate("uuid", uuid)
34
-
35
- return jsonObject
36
- }
37
-
38
- class Builder {
39
- var type: String = ""
40
- var token: String = ""
41
- var contacts = JSONArray()
42
- var uuid: String = ""
43
-
44
- constructor() {
45
-
46
- }
47
-
48
- constructor(request: RequestTrainContactModel) {
49
- this.type = request.type
50
- this.token = request.token
51
- this.uuid = request.uuid
52
- this.contacts = request.contacts
53
- }
54
-
55
- fun type(type: String): Builder {
56
- this.type = type
57
- return this
58
- }
59
-
60
- fun token(token: String): Builder {
61
- this.token = token
62
- return this
63
- }
64
-
65
- fun contacts(contacts: ArrayList<ContactModel>): Builder {
66
- this.contacts = Utils.instance.convertContactModelToJsonArray(contacts)
67
- return this
68
- }
69
-
70
- fun uuid(uuid: String): Builder {
71
- this.uuid = uuid
72
- return this
73
- }
74
-
75
- fun build(): RequestTrainContactModel {
76
- return RequestTrainContactModel(this)
77
- }
78
- }
79
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/models/viewmodels/ChatMessageModel.kt DELETED
@@ -1,18 +0,0 @@
1
- package com.matthaigh27.chatgptwrapper.models.viewmodels
2
-
3
- import androidx.lifecycle.ViewModel
4
-
5
- /**
6
- * ChatModel for Chat RecyclerView
7
- */
8
- class ChatMessageModel: ViewModel() {
9
- var message: String = ""
10
- var isMe: Boolean = true
11
- var image: ByteArray? = null
12
- var imageName: String = ""
13
- var visibleFeedback = false
14
- var feedback = 0
15
- var isWidget: Boolean = false
16
- var widgetType = ""
17
- var widgetDescription = ""
18
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/services/MessageService.kt DELETED
@@ -1,32 +0,0 @@
1
- package com.matthaigh27.chatgptwrapper.services
2
-
3
- import android.content.Intent
4
- import android.util.Log
5
- import com.google.firebase.messaging.FirebaseMessagingService
6
- import com.google.firebase.messaging.RemoteMessage
7
- import com.matthaigh27.chatgptwrapper.utils.Constants.TAG
8
-
9
- class MessageService : FirebaseMessagingService() {
10
-
11
- /**
12
- * this function is called when langchain server pushs notification into FCM
13
- * @param remoteMessage is sent by FCM when langchain server pushs notification
14
- */
15
- override fun onMessageReceived(remoteMessage: RemoteMessage) {
16
- super.onMessageReceived(remoteMessage)
17
- if (remoteMessage.notification != null) {
18
- Log.d(
19
- TAG, "Message Notification Body: " + remoteMessage.notification!!.body
20
- )
21
-
22
- /** intent for sending broadcast to ChatActivity */
23
- val intent = Intent()
24
- intent.action = "android.intent.action.MAIN"
25
- intent.putExtra("message", remoteMessage.notification!!.body)
26
-
27
- /** send broadcast to ChatActivity
28
- * So ChatActivity can receive remoteMessage from this service */
29
- sendBroadcast(intent)
30
- }
31
- }
32
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/services/api/HttpClient.kt DELETED
@@ -1,117 +0,0 @@
1
- package com.matthaigh27.chatgptwrapper.services.api
2
-
3
- import android.util.Log
4
- import com.matthaigh27.chatgptwrapper.BuildConfig
5
- import com.matthaigh27.chatgptwrapper.models.common.ContactModel
6
- import com.matthaigh27.chatgptwrapper.models.requestmodels.RequestBodyModel
7
- import com.matthaigh27.chatgptwrapper.models.requestmodels.RequestTrainContactModel
8
- import com.matthaigh27.chatgptwrapper.utils.Constants
9
- import com.matthaigh27.chatgptwrapper.utils.Constants.GET
10
- import com.matthaigh27.chatgptwrapper.utils.Constants.POST
11
- import com.matthaigh27.chatgptwrapper.utils.ReqType
12
- import okhttp3.Call
13
- import okhttp3.Callback
14
- import okhttp3.OkHttpClient
15
- import okhttp3.Request
16
- import okhttp3.RequestBody
17
- import okhttp3.Response
18
- import org.json.JSONException
19
- import org.json.JSONObject
20
- import java.io.IOException
21
- import java.util.concurrent.TimeUnit
22
-
23
- class HttpClient {
24
- /* Server URL and Api Endpoints */
25
- val SERVER_URL = BuildConfig.BASE_URL
26
- val SEND_NOTIFICATION_URL = SERVER_URL + "sendNotification"
27
- val IMAGE_RELATEDNESS = SERVER_URL + "image_relatedness"
28
- val UPLOAD_IMAGE = SERVER_URL + "uploadImage"
29
- val GET_ALL_HELP_COMMANDS = SERVER_URL + "commands"
30
- val TRAIN_CONTACTS = SERVER_URL + "train/contacts"
31
-
32
- var mCallback: HttpRisingInterface
33
-
34
- constructor(callback: HttpRisingInterface) {
35
- mCallback = callback
36
- }
37
-
38
- private fun sendOkHttpRequest(postBody: String, postUrl: String, method: String) {
39
- val body: RequestBody = RequestBody.create(Constants.JSON, postBody)
40
-
41
- /**
42
- * set okhttpclient timeout to 120s
43
- */
44
- var request: Request? = null
45
- if (method == POST) request = Request.Builder().url(postUrl).post(body).build()
46
- else request = Request.Builder().url(postUrl).get().build()
47
-
48
- val client =
49
- OkHttpClient.Builder().connectTimeout(Constants.CUSTOM_TIMEOUT, TimeUnit.SECONDS)
50
- .readTimeout(Constants.CUSTOM_TIMEOUT, TimeUnit.SECONDS)
51
- .writeTimeout(Constants.CUSTOM_TIMEOUT, TimeUnit.SECONDS).build()
52
-
53
- client.newCall(request).enqueue(object : Callback {
54
- override fun onFailure(call: Call, e: IOException) {
55
- /**
56
- * Handle failure
57
- */
58
- e.printStackTrace()
59
-
60
- mCallback.onFailureResult("Fail to send request to server. Please ask again.")
61
- }
62
-
63
- override fun onResponse(call: Call, response: Response) {
64
- val myResponse = response.body()!!.string()
65
- Log.d(Constants.TAG, myResponse)
66
-
67
- try {
68
- val json = JSONObject(myResponse)["result"].toString()
69
- mCallback.onSuccessResult(json)
70
- } catch (e: JSONException) {
71
- mCallback.onFailureResult(myResponse)
72
- e.printStackTrace()
73
- }
74
- }
75
- })
76
- }
77
-
78
- /* call sendNotification */
79
- fun callSendNotification(message: String) {
80
- sendOkHttpRequest(
81
- RequestBodyModel.Builder().message(message).type(ReqType.instance.MESSAGE).build()
82
- .buildJsonObject().toString(), SEND_NOTIFICATION_URL, POST
83
- )
84
- }
85
-
86
- /* call image_relatedness */
87
- fun callImageRelatedness(imageName: String) {
88
- sendOkHttpRequest(
89
- RequestBodyModel.Builder().imageName(imageName).type(ReqType.instance.MESSAGE).build()
90
- .buildJsonObject().toString(), IMAGE_RELATEDNESS, POST
91
- )
92
- }
93
-
94
- /* call image_upload */
95
- fun callImageUpload(imageName: String) {
96
- sendOkHttpRequest(
97
- RequestBodyModel.Builder().imageName(imageName).type(ReqType.instance.IMAGE_UPLOAD)
98
- .build().buildJsonObject().toString(), UPLOAD_IMAGE, POST
99
- )
100
- }
101
-
102
- fun getALlHelpPromptCommands() {
103
- sendOkHttpRequest(
104
- RequestBodyModel.Builder().build().buildJsonObject().toString(),
105
- GET_ALL_HELP_COMMANDS,
106
- GET
107
- )
108
- }
109
-
110
- fun trainContacts(contacts: ArrayList<ContactModel>) {
111
- sendOkHttpRequest(
112
- RequestTrainContactModel.Builder().contacts(contacts).build().buildJsonObject().toString(),
113
- TRAIN_CONTACTS,
114
- POST
115
- )
116
- }
117
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/services/api/HttpRisingInterface.kt DELETED
@@ -1,7 +0,0 @@
1
- package com.matthaigh27.chatgptwrapper.services.api;
2
-
3
- interface HttpRisingInterface {
4
- fun onSuccessResult(msg: String)
5
-
6
- fun onFailureResult(msg: String)
7
- }
 
 
 
 
 
 
 
 
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/base/BaseActivity.kt ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.matthaigh27.chatgptwrapper.ui.base
2
+
3
+ import android.os.Bundle
4
+ import android.view.View
5
+ import androidx.appcompat.app.AppCompatActivity
6
+
7
+ open class BaseActivity : AppCompatActivity() {
8
+
9
+ override fun onCreate(savedInstanceState: Bundle?) {
10
+ super.onCreate(savedInstanceState)
11
+
12
+ // Hide the status bar (system toolbar)
13
+ window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN
14
+
15
+ // For hiding the action bar, if you have one
16
+ supportActionBar?.hide()
17
+ }
18
+ }
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/{activites/HomeActivity.kt → ui/chat/view/ChatActivity.kt} RENAMED
@@ -1,39 +1,38 @@
1
- package com.matthaigh27.chatgptwrapper.activites
2
 
3
  import android.Manifest
4
  import android.annotation.SuppressLint
5
  import android.content.pm.PackageManager
6
- import android.os.Build
7
  import android.os.Bundle
8
- import android.util.Log
9
- import androidx.appcompat.app.AlertDialog
10
  import androidx.appcompat.app.AppCompatActivity
11
- import com.google.android.material.tabs.TabLayout.TabGravity
12
  import com.matthaigh27.chatgptwrapper.R
13
- import com.matthaigh27.chatgptwrapper.dialogs.CommonConfirmDialog
14
- import com.matthaigh27.chatgptwrapper.fragments.ChatFragment
15
- import java.io.File
16
 
17
 
18
- class HomeActivity : AppCompatActivity() {
19
 
20
  private val PERMISSIONS_REQUEST_CODE = 1
21
-
22
  private val PERMISSIONS = arrayOf(
23
  Manifest.permission.SEND_SMS,
24
  Manifest.permission.READ_CONTACTS,
25
  Manifest.permission.CALL_PHONE,
26
  Manifest.permission.READ_EXTERNAL_STORAGE
27
  )
 
 
 
 
28
 
29
  override fun onCreate(savedInstanceState: Bundle?) {
30
  super.onCreate(savedInstanceState)
31
- setContentView(R.layout.activity_home)
32
 
33
- requestPermission()
34
  }
35
 
36
- private fun requestPermission() {
37
  val notGrantedPermissions = PERMISSIONS.filter {
38
  checkSelfPermission(it) != PackageManager.PERMISSION_GRANTED
39
  }
@@ -41,10 +40,9 @@ class HomeActivity : AppCompatActivity() {
41
  if (notGrantedPermissions.isNotEmpty()) {
42
  if (shouldShowRequestPermissionRationale(notGrantedPermissions[0])) {
43
  // show custom permission rationale
44
- val confirmDialog = CommonConfirmDialog(this)
45
- confirmDialog.setMessage("This app requires SMS, Contacts and Phone permissions to function properly. Please grant the necessary permissions.")
46
  confirmDialog.setOnClickListener(object :
47
- CommonConfirmDialog.OnConfirmButtonClickListener {
48
  override fun onPositiveButtonClick() {
49
  requestPermissions(
50
  notGrantedPermissions.toTypedArray(), PERMISSIONS_REQUEST_CODE
@@ -55,21 +53,19 @@ class HomeActivity : AppCompatActivity() {
55
  finish()
56
  }
57
  })
 
58
  confirmDialog.show()
 
 
59
  } else {
60
  requestPermissions(notGrantedPermissions.toTypedArray(), PERMISSIONS_REQUEST_CODE)
61
  }
62
  } else {
63
  // Permissions already granted, navigate to your desired fragment
64
- navigateToChatFragment()
65
  }
66
  }
67
 
68
- private fun navigateToChatFragment() {
69
- val fragmentTransaction = supportFragmentManager.beginTransaction()
70
- fragmentTransaction.replace(R.id.frame_container, ChatFragment()).commit()
71
- }
72
-
73
  @SuppressLint("MissingSuperCall")
74
  override fun onRequestPermissionsResult(
75
  requestCode: Int, permissions: Array<out String>, grantResults: IntArray
@@ -78,14 +74,19 @@ class HomeActivity : AppCompatActivity() {
78
  PERMISSIONS_REQUEST_CODE -> {
79
  if (grantResults.all { it == PackageManager.PERMISSION_GRANTED }) {
80
  // Permissions granted, navigate to your desired fragment
81
- navigateToChatFragment()
82
  } else {
83
- requestPermission()
84
  }
85
  return
86
  }
87
  }
88
  }
 
 
 
 
 
89
  }
90
 
91
 
 
1
+ package com.matthaigh27.chatgptwrapper.ui.chat.view
2
 
3
  import android.Manifest
4
  import android.annotation.SuppressLint
5
  import android.content.pm.PackageManager
 
6
  import android.os.Bundle
 
 
7
  import androidx.appcompat.app.AppCompatActivity
 
8
  import com.matthaigh27.chatgptwrapper.R
9
+ import com.matthaigh27.chatgptwrapper.ui.base.BaseActivity
10
+ import com.matthaigh27.chatgptwrapper.ui.chat.view.dialogs.ConfirmDialog
11
+ import com.matthaigh27.chatgptwrapper.ui.chat.view.fragments.ChatMainFragment
12
 
13
 
14
+ class ChatActivity : BaseActivity() {
15
 
16
  private val PERMISSIONS_REQUEST_CODE = 1
 
17
  private val PERMISSIONS = arrayOf(
18
  Manifest.permission.SEND_SMS,
19
  Manifest.permission.READ_CONTACTS,
20
  Manifest.permission.CALL_PHONE,
21
  Manifest.permission.READ_EXTERNAL_STORAGE
22
  )
23
+ private val CONFIRM_MESSAGE =
24
+ "This app requires SMS, Contacts and Phone " +
25
+ "permissions to function properly. " +
26
+ "Please grant the necessary permissions."
27
 
28
  override fun onCreate(savedInstanceState: Bundle?) {
29
  super.onCreate(savedInstanceState)
30
+ setContentView(R.layout.activity_chat)
31
 
32
+ requestPermissions()
33
  }
34
 
35
+ private fun requestPermissions() {
36
  val notGrantedPermissions = PERMISSIONS.filter {
37
  checkSelfPermission(it) != PackageManager.PERMISSION_GRANTED
38
  }
 
40
  if (notGrantedPermissions.isNotEmpty()) {
41
  if (shouldShowRequestPermissionRationale(notGrantedPermissions[0])) {
42
  // show custom permission rationale
43
+ val confirmDialog = ConfirmDialog(this@ChatActivity)
 
44
  confirmDialog.setOnClickListener(object :
45
+ ConfirmDialog.OnDialogButtonClickListener {
46
  override fun onPositiveButtonClick() {
47
  requestPermissions(
48
  notGrantedPermissions.toTypedArray(), PERMISSIONS_REQUEST_CODE
 
53
  finish()
54
  }
55
  })
56
+
57
  confirmDialog.show()
58
+ confirmDialog.setMessage(CONFIRM_MESSAGE)
59
+
60
  } else {
61
  requestPermissions(notGrantedPermissions.toTypedArray(), PERMISSIONS_REQUEST_CODE)
62
  }
63
  } else {
64
  // Permissions already granted, navigate to your desired fragment
65
+ navigateToChatMainFragment()
66
  }
67
  }
68
 
 
 
 
 
 
69
  @SuppressLint("MissingSuperCall")
70
  override fun onRequestPermissionsResult(
71
  requestCode: Int, permissions: Array<out String>, grantResults: IntArray
 
74
  PERMISSIONS_REQUEST_CODE -> {
75
  if (grantResults.all { it == PackageManager.PERMISSION_GRANTED }) {
76
  // Permissions granted, navigate to your desired fragment
77
+ navigateToChatMainFragment()
78
  } else {
79
+ requestPermissions()
80
  }
81
  return
82
  }
83
  }
84
  }
85
+
86
+ private fun navigateToChatMainFragment() {
87
+ val fragmentTransaction = supportFragmentManager.beginTransaction()
88
+ fragmentTransaction.replace(R.id.fl_container, ChatMainFragment()).commit()
89
+ }
90
  }
91
 
92
 
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/chat/view/adapters/ChatMainAdapter.kt ADDED
@@ -0,0 +1,179 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.matthaigh27.chatgptwrapper.ui.chat.view.adapters
2
+
3
+ import android.content.Context
4
+ import android.graphics.BitmapFactory
5
+ import android.view.LayoutInflater
6
+ import android.view.View
7
+ import android.view.ViewGroup
8
+ import android.widget.FrameLayout
9
+ import android.widget.ImageView
10
+ import android.widget.TextView
11
+ import androidx.constraintlayout.widget.ConstraintLayout
12
+ import androidx.recyclerview.widget.RecyclerView
13
+ import com.matthaigh27.chatgptwrapper.R
14
+ import com.matthaigh27.chatgptwrapper.data.models.ChatMessageModel
15
+ import com.matthaigh27.chatgptwrapper.ui.chat.view.interfaces.ChatMessageInterface
16
+ import com.matthaigh27.chatgptwrapper.ui.chat.view.widgets.chatwidget.SendSmsWidget
17
+ import com.matthaigh27.chatgptwrapper.ui.chat.view.widgets.chatwidget.helpprompt.HelpPromptWidget
18
+ import com.matthaigh27.chatgptwrapper.utils.Constants
19
+ import com.matthaigh27.chatgptwrapper.utils.helpers.chat.ImageHelper
20
+
21
+ class ChatMainAdapter(
22
+ context: Context, list: ArrayList<ChatMessageModel>, callbacks: ChatMessageInterface
23
+ ) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
24
+
25
+ private val VIEW_TYPE_MSG_SENT = 0
26
+ private val VIEW_TYPE_MSG_RECEIVED = 1
27
+ private val VIEW_TYPE_CHAT_WIDGET = 2
28
+
29
+ private var context: Context
30
+ private var callbacks: ChatMessageInterface
31
+ private var chatMessageList: ArrayList<ChatMessageModel> = ArrayList()
32
+
33
+ init {
34
+ this.context = context
35
+ this.chatMessageList = list
36
+ this.callbacks = callbacks
37
+ }
38
+
39
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
40
+ val inflater = LayoutInflater.from(context)
41
+
42
+ return when (viewType) {
43
+ VIEW_TYPE_MSG_SENT -> {
44
+ SentMessageViewHolder(
45
+ inflater.inflate(
46
+ R.layout.item_container_sent_message, parent, false
47
+ )
48
+ )
49
+ }
50
+
51
+ VIEW_TYPE_MSG_RECEIVED -> {
52
+ ReceivedMessageViewHolder(
53
+ inflater.inflate(
54
+ R.layout.item_container_received_message, parent, false
55
+ )
56
+ )
57
+ }
58
+
59
+ else -> {
60
+ ChatWidgetViewHolder(
61
+ inflater.inflate(
62
+ R.layout.item_container_chat_widget, parent, false
63
+ )
64
+ )
65
+ }
66
+ }
67
+ }
68
+
69
+ override fun getItemCount(): Int {
70
+ return chatMessageList.size
71
+ }
72
+
73
+ override fun getItemViewType(position: Int): Int {
74
+ return chatMessageList[position].type
75
+ }
76
+
77
+ override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
78
+ val index = holder.adapterPosition
79
+ val chatMessageModel: ChatMessageModel = chatMessageList[index]
80
+ when (chatMessageModel.type) {
81
+ VIEW_TYPE_MSG_SENT -> {
82
+ setMessageData(holder as SentMessageViewHolder, chatMessageModel)
83
+ }
84
+
85
+ VIEW_TYPE_MSG_RECEIVED -> {
86
+ setMessageData(holder as ReceivedMessageViewHolder, chatMessageModel)
87
+ }
88
+
89
+ else -> {
90
+ setMessageData(holder as ChatWidgetViewHolder, chatMessageModel)
91
+ }
92
+ }
93
+ }
94
+
95
+ private fun setMessageData(holder: SentMessageViewHolder, data: ChatMessageModel) {
96
+ holder.txtMessage.text = data.content
97
+ }
98
+
99
+ private fun setMessageData(holder: ReceivedMessageViewHolder, data: ChatMessageModel) {
100
+ if (data.hasImage) {
101
+ data.image?.let { image ->
102
+ val originBitmap = BitmapFactory.decodeByteArray(image, 0, image.size)
103
+ val radius = context.resources.getDimensionPixelSize(R.dimen.radius_small)
104
+ val bmp = ImageHelper.getRoundedCornerBitmap(originBitmap, radius)
105
+ holder.imgMessage.visibility = View.VISIBLE
106
+ holder.imgMessage.setImageBitmap(bmp)
107
+
108
+ data.content?.let { message ->
109
+ holder.txtMessage.text = message
110
+ } ?: run {
111
+ holder.txtMessage.visibility = View.GONE
112
+ }
113
+ }
114
+ } else {
115
+ holder.txtMessage.text = data.content
116
+ holder.imgMessage.visibility = View.GONE
117
+ holder.txtMessage.visibility = View.VISIBLE
118
+ }
119
+ }
120
+
121
+ private fun setMessageData(holder: ChatWidgetViewHolder, data: ChatMessageModel) {
122
+ when (data.content) {
123
+ Constants.TYPE_WIDGET_SMS -> {
124
+ val sendSmsWidget = SendSmsWidget(context).apply {
125
+ this.callback = callbacks
126
+ }
127
+ holder.itemLayout.addView(sendSmsWidget)
128
+ holder.itemLayout.visibility = View.VISIBLE
129
+ }
130
+
131
+ Constants.TYPE_WIDGET_HELP_PROMPT -> {
132
+ // val helpPromptWidget = HelpPromptWidget(context)
133
+ }
134
+
135
+ Constants.TYPE_WIDGET_SEARCH_CONTACT -> {
136
+
137
+ }
138
+
139
+ Constants.TYPE_WIDGET_FEEDBACK -> {
140
+
141
+ }
142
+ }
143
+ }
144
+
145
+ inner class ReceivedMessageViewHolder internal constructor(itemView: View) :
146
+ RecyclerView.ViewHolder(itemView) {
147
+ var txtMessage: TextView
148
+ var imgMessage: ImageView
149
+ var itemLayout: ConstraintLayout
150
+
151
+ init {
152
+ txtMessage = itemView.findViewById<View>(R.id.txt_message) as TextView
153
+ imgMessage = itemView.findViewById<View>(R.id.img_message) as ImageView
154
+ itemLayout = itemView.findViewById<View>(R.id.cl_received_message) as ConstraintLayout
155
+ }
156
+ }
157
+
158
+ inner class SentMessageViewHolder internal constructor(itemView: View) :
159
+ RecyclerView.ViewHolder(itemView) {
160
+ var txtMessage: TextView
161
+ var imgMessage: ImageView
162
+ var itemLayout: ConstraintLayout
163
+
164
+ init {
165
+ txtMessage = itemView.findViewById<View>(R.id.txt_message) as TextView
166
+ imgMessage = itemView.findViewById<View>(R.id.img_message) as ImageView
167
+ itemLayout = itemView.findViewById<View>(R.id.cl_sent_message) as ConstraintLayout
168
+ }
169
+ }
170
+
171
+ inner class ChatWidgetViewHolder internal constructor(itemView: View) :
172
+ RecyclerView.ViewHolder(itemView) {
173
+ var itemLayout: FrameLayout
174
+
175
+ init {
176
+ itemLayout = itemView.findViewById<View>(R.id.fl_widget_message) as FrameLayout
177
+ }
178
+ }
179
+ }
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/{dialogs/CommonConfirmDialog.kt → ui/chat/view/dialogs/ConfirmDialog.kt} RENAMED
@@ -1,4 +1,4 @@
1
- package com.matthaigh27.chatgptwrapper.dialogs
2
 
3
  import android.app.Dialog
4
  import android.content.Context
@@ -12,62 +12,59 @@ import android.widget.Button
12
  import android.widget.TextView
13
  import com.matthaigh27.chatgptwrapper.R
14
 
15
- class CommonConfirmDialog(context: Context) : Dialog(context), View.OnClickListener {
16
 
17
- private var mTvMessage: TextView? = null
18
- private var mMessage: String = ""
19
- private lateinit var mClickListener: OnConfirmButtonClickListener
20
 
21
  init {
22
  setCancelable(false)
23
  }
24
 
25
- fun setOnClickListener(listener: OnConfirmButtonClickListener) {
26
- mClickListener = listener
27
- }
28
-
29
  override fun onCreate(savedInstanceState: Bundle?) {
30
  super.onCreate(savedInstanceState)
31
 
32
  initView()
33
  }
34
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  private fun initView() {
36
  requestWindowFeature(Window.FEATURE_NO_TITLE)
37
- setContentView(R.layout.dialog_common_confirm)
38
 
39
- window!!.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
40
- window!!.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
41
 
42
  findViewById<Button>(R.id.btn_ok).setOnClickListener(this)
43
  findViewById<Button>(R.id.btn_cancel).setOnClickListener(this)
44
 
45
- mTvMessage = findViewById(R.id.tv_message)
46
 
47
  setCanceledOnTouchOutside(true)
48
  }
49
 
50
- fun setMessage(message: String) {
51
- mMessage = message
52
- mTvMessage?.text = mMessage
53
  }
54
 
55
-
56
- override fun onClick(view: View?) {
57
- when (view?.id) {
58
- R.id.btn_ok -> {
59
- mClickListener.onPositiveButtonClick()
60
- }
61
-
62
- R.id.btn_cancel -> {
63
- mClickListener.onNegativeButtonClick()
64
- }
65
- }
66
-
67
- this.dismiss()
68
  }
69
 
70
- interface OnConfirmButtonClickListener {
 
71
  fun onPositiveButtonClick()
72
  fun onNegativeButtonClick()
73
  }
 
1
+ package com.matthaigh27.chatgptwrapper.ui.chat.view.dialogs
2
 
3
  import android.app.Dialog
4
  import android.content.Context
 
12
  import android.widget.TextView
13
  import com.matthaigh27.chatgptwrapper.R
14
 
15
+ class ConfirmDialog(context: Context) : Dialog(context), View.OnClickListener {
16
 
17
+ private var txtMessage: TextView? = null
18
+ private lateinit var btnClickListener: OnDialogButtonClickListener
 
19
 
20
  init {
21
  setCancelable(false)
22
  }
23
 
 
 
 
 
24
  override fun onCreate(savedInstanceState: Bundle?) {
25
  super.onCreate(savedInstanceState)
26
 
27
  initView()
28
  }
29
 
30
+ override fun onClick(view: View?) {
31
+ when (view?.id) {
32
+ R.id.btn_ok -> {
33
+ btnClickListener.onPositiveButtonClick()
34
+ }
35
+
36
+ R.id.btn_cancel -> {
37
+ btnClickListener.onNegativeButtonClick()
38
+ }
39
+ }
40
+ this.dismiss()
41
+ }
42
+
43
  private fun initView() {
44
  requestWindowFeature(Window.FEATURE_NO_TITLE)
45
+ setContentView(R.layout.dialog_confirm)
46
 
47
+ window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
48
+ window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
49
 
50
  findViewById<Button>(R.id.btn_ok).setOnClickListener(this)
51
  findViewById<Button>(R.id.btn_cancel).setOnClickListener(this)
52
 
53
+ txtMessage = findViewById(R.id.txt_confirm_message)
54
 
55
  setCanceledOnTouchOutside(true)
56
  }
57
 
58
+ fun setOnClickListener(listener: OnDialogButtonClickListener) {
59
+ btnClickListener = listener
 
60
  }
61
 
62
+ fun setMessage(message: String) {
63
+ txtMessage?.text = message
 
 
 
 
 
 
 
 
 
 
 
64
  }
65
 
66
+
67
+ interface OnDialogButtonClickListener {
68
  fun onPositiveButtonClick()
69
  fun onNegativeButtonClick()
70
  }
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/chat/view/fragments/ChatMainFragment.kt ADDED
@@ -0,0 +1,383 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.matthaigh27.chatgptwrapper.ui.chat.view.fragments
2
+
3
+ import android.annotation.SuppressLint
4
+ import android.os.Bundle
5
+ import android.view.KeyEvent
6
+ import android.view.LayoutInflater
7
+ import android.view.View
8
+ import android.view.View.OnClickListener
9
+ import android.view.ViewGroup
10
+ import android.view.animation.Animation
11
+ import android.view.animation.LinearInterpolator
12
+ import android.view.animation.RotateAnimation
13
+ import android.widget.EditText
14
+ import android.widget.ImageView
15
+ import android.widget.LinearLayout
16
+ import android.widget.Toast
17
+ import androidx.fragment.app.Fragment
18
+ import androidx.lifecycle.Observer
19
+ import androidx.lifecycle.ViewModelProvider
20
+ import androidx.recyclerview.widget.LinearLayoutManager
21
+ import androidx.recyclerview.widget.RecyclerView
22
+ import com.google.gson.JsonElement
23
+ import com.matthaigh27.chatgptwrapper.R
24
+ import com.matthaigh27.chatgptwrapper.data.models.ChatMessageModel
25
+ import com.matthaigh27.chatgptwrapper.data.models.HelpCommandModel
26
+ import com.matthaigh27.chatgptwrapper.data.models.HelpPromptModel
27
+ import com.matthaigh27.chatgptwrapper.data.remote.ApiResource
28
+ import com.matthaigh27.chatgptwrapper.data.remote.responses.ApiResponse
29
+ import com.matthaigh27.chatgptwrapper.ui.chat.view.adapters.ChatMainAdapter
30
+ import com.matthaigh27.chatgptwrapper.ui.chat.view.interfaces.ChatMessageInterface
31
+ import com.matthaigh27.chatgptwrapper.ui.chat.view.widgets.toolbar.ChatToolsWidget
32
+ import com.matthaigh27.chatgptwrapper.ui.chat.viewmodel.ChatViewModel
33
+ import com.matthaigh27.chatgptwrapper.utils.Constants.ERROR_MSG_NOEXIST_COMMAND
34
+ import com.matthaigh27.chatgptwrapper.utils.Constants.HELP_COMMAND_ALL
35
+ import com.matthaigh27.chatgptwrapper.utils.Constants.TYPE_RESPONSE_ALERT
36
+ import com.matthaigh27.chatgptwrapper.utils.Constants.TYPE_RESPONSE_BROWSER
37
+ import com.matthaigh27.chatgptwrapper.utils.Constants.TYPE_RESPONSE_CONTACT
38
+ import com.matthaigh27.chatgptwrapper.utils.Constants.TYPE_RESPONSE_IMAGE
39
+ import com.matthaigh27.chatgptwrapper.utils.Constants.TYPE_RESPONSE_MESSAGE
40
+ import com.matthaigh27.chatgptwrapper.utils.Constants.TYPE_RESPONSE_SMS
41
+ import com.matthaigh27.chatgptwrapper.utils.Constants.TYPE_WIDGET_HELP_PROMPT
42
+ import com.matthaigh27.chatgptwrapper.utils.Constants.TYPE_WIDGET_SMS
43
+ import com.matthaigh27.chatgptwrapper.utils.helpers.Converter.stringToHelpPromptList
44
+ import com.matthaigh27.chatgptwrapper.utils.helpers.chat.CommandHelper.getHelpCommandFromStr
45
+ import com.matthaigh27.chatgptwrapper.utils.helpers.chat.CommandHelper.isMainHelpCommand
46
+ import com.matthaigh27.chatgptwrapper.utils.helpers.chat.CommandHelper.makePromptItemUsage
47
+ import com.matthaigh27.chatgptwrapper.utils.helpers.chat.CommandHelper.makePromptUsage
48
+ import com.matthaigh27.chatgptwrapper.utils.helpers.ui.NoNewLineInputFilter
49
+
50
+ class ChatMainFragment : Fragment(), OnClickListener {
51
+
52
+ private val TYPE_CHAT_SENT = 0
53
+ private val TYPE_CHAT_RECEIVE = 1
54
+ private val TYPE_CHAT_WIDGET = 2
55
+
56
+ private lateinit var rootView: View
57
+ lateinit var viewModel: ChatViewModel
58
+
59
+ /** View Components */
60
+ private var loadingRotate: RotateAnimation? = null
61
+ private var edtMessageInput: EditText? = null
62
+
63
+ private var rvChatList: RecyclerView? = null
64
+ private var chatMainAdapter: ChatMainAdapter? = null
65
+ private var chatMessageList: ArrayList<ChatMessageModel> = ArrayList()
66
+ private lateinit var chatMessageInterface: ChatMessageInterface
67
+
68
+ private var chatToolsWidget: ChatToolsWidget? = null
69
+ private var helpPromptList: ArrayList<HelpPromptModel>? = null
70
+
71
+ override fun onCreateView(
72
+ inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
73
+ ): View {
74
+ rootView = inflater.inflate(R.layout.fragment_chat_main, container, false)
75
+ init()
76
+ return rootView
77
+ }
78
+
79
+ private fun init() {
80
+ initViewModel()
81
+ initLoadingRotate()
82
+ initButtonsClickListener()
83
+ initChatInputListener()
84
+ initChatRecyclerView()
85
+ initChatToolsWidget()
86
+
87
+ fetchAllCommands()
88
+ }
89
+
90
+ private fun initViewModel() {
91
+ viewModel = ViewModelProvider(this)[ChatViewModel::class.java]
92
+ }
93
+
94
+ private fun initLoadingRotate() {
95
+ loadingRotate = RotateAnimation(/* fromDegrees = */ 0f, /* toDegrees = */
96
+ 360f, /* pivotXType = */
97
+ Animation.RELATIVE_TO_SELF, /* pivotXValue = */
98
+ 0.5f, /* pivotYType = */
99
+ Animation.RELATIVE_TO_SELF, /* pivotYValue = */
100
+ 0.5f
101
+ ).apply {
102
+ duration = 3000
103
+ repeatCount = Animation.INFINITE
104
+ interpolator = LinearInterpolator()
105
+ }
106
+ }
107
+
108
+ private fun initButtonsClickListener() {
109
+ rootView.findViewById<View>(R.id.btn_open_chat_tools).setOnClickListener(this)
110
+ rootView.findViewById<View>(R.id.btn_audio_recognition).setOnClickListener(this)
111
+ }
112
+
113
+ private fun initChatInputListener() {
114
+ edtMessageInput = rootView.findViewById(R.id.edt_message)
115
+ edtMessageInput?.filters = edtMessageInput?.filters?.plus(NoNewLineInputFilter())
116
+
117
+ edtMessageInput?.setOnKeyListener { _, keyCode, keyEvent ->
118
+ if (keyEvent.action == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_ENTER) {
119
+ addMessage(TYPE_CHAT_SENT, edtMessageInput?.text.toString())
120
+ }
121
+ return@setOnKeyListener false
122
+ }
123
+ }
124
+
125
+ private fun initChatRecyclerView() {
126
+ initChatInterface()
127
+
128
+ rvChatList = rootView.findViewById(R.id.rv_chat_list)
129
+ chatMainAdapter = ChatMainAdapter(
130
+ requireContext(), chatMessageList, chatMessageInterface
131
+ )
132
+
133
+ rvChatList?.adapter = chatMainAdapter
134
+ rvChatList?.layoutManager = LinearLayoutManager(context)
135
+ }
136
+
137
+ private fun initChatToolsWidget() {
138
+ chatToolsWidget = ChatToolsWidget(requireContext(), requireActivity())
139
+ val llToolBar = rootView.findViewById<LinearLayout>(R.id.ll_toolbar)
140
+ llToolBar.addView(chatToolsWidget)
141
+ }
142
+
143
+ private fun showToast(message: String) {
144
+ Toast.makeText(requireContext(), message, Toast.LENGTH_LONG).show()
145
+ }
146
+
147
+ private fun showLoading(isLoading: Boolean) {
148
+ val imgLoading = rootView.findViewById<ImageView>(R.id.sp_loading)
149
+
150
+ if (isLoading) {
151
+ imgLoading.startAnimation(loadingRotate)
152
+ imgLoading.visibility = View.VISIBLE
153
+ } else {
154
+ imgLoading.clearAnimation()
155
+ imgLoading.visibility = View.GONE
156
+ }
157
+ }
158
+
159
+ private fun addMessage(
160
+ type: Int,
161
+ content: String? = null,
162
+ data: JsonElement? = null,
163
+ hasImage: Boolean = false,
164
+ image: ByteArray? = null
165
+ ) {
166
+ addChatItemToList(ChatMessageModel(type, content, data, hasImage, image))
167
+ when (type) {
168
+ TYPE_CHAT_SENT -> {
169
+ if (content!!.isNotEmpty() && content.first() == '/') {
170
+ openHelpPromptWidget(content)
171
+ return
172
+ }
173
+ sendNotification(content)
174
+ }
175
+ }
176
+ }
177
+
178
+ @SuppressLint("NotifyDataSetChanged")
179
+ private fun addChatItemToList(chatModel: ChatMessageModel) {
180
+ edtMessageInput?.setText("")
181
+
182
+ chatMessageList.add(chatModel)
183
+ chatMainAdapter?.notifyDataSetChanged()
184
+ rvChatList?.scrollToPosition(chatMessageList.size - 1)
185
+ }
186
+
187
+ private fun openHelpPromptWidget(message: String) {
188
+ try {
189
+ val command: HelpCommandModel = getHelpCommandFromStr(message)
190
+ if (isMainHelpCommand(command)) {
191
+ when (command.main) {
192
+ TYPE_WIDGET_SMS -> {
193
+ addMessage(
194
+ type = TYPE_CHAT_WIDGET, content = TYPE_WIDGET_SMS
195
+ )
196
+ }
197
+
198
+ else -> {
199
+ helpPromptList?.let { list ->
200
+ val data = list.filter { model ->
201
+ model.name == command.main
202
+ }
203
+
204
+ if(data.isEmpty()) {
205
+ addMessage(
206
+ type = TYPE_CHAT_RECEIVE, content = ERROR_MSG_NOEXIST_COMMAND
207
+ )
208
+ } else {
209
+ addMessage(
210
+ type = TYPE_CHAT_WIDGET,
211
+ content = TYPE_WIDGET_HELP_PROMPT,
212
+ data = data[0].toString() as JsonElement
213
+ )
214
+ }
215
+ }
216
+ }
217
+ }
218
+ } else {
219
+ if (command.assist == HELP_COMMAND_ALL) {
220
+ addMessage(
221
+ type = TYPE_CHAT_RECEIVE, content = makePromptUsage(helpPromptList!!)
222
+ )
223
+ } else {
224
+ addMessage(
225
+ type = TYPE_CHAT_RECEIVE,
226
+ content = makePromptItemUsage(helpPromptList!!, command.assist!!)
227
+ )
228
+ }
229
+ }
230
+ } catch (e: Exception) {
231
+ e.printStackTrace()
232
+ showToast(e.message.toString())
233
+ }
234
+ }
235
+
236
+
237
+ private fun fetchAllCommands() {
238
+ viewModel.getAllHelpCommands().observe(viewLifecycleOwner, Observer { resource ->
239
+ when (resource) {
240
+ is ApiResource.Loading -> {
241
+ showLoading(true)
242
+ }
243
+
244
+ is ApiResource.Success -> {
245
+ showLoading(false)
246
+ resource.data?.let { data ->
247
+ helpPromptList =
248
+ stringToHelpPromptList(data.result.content.asJsonArray.toString())
249
+ }
250
+ }
251
+
252
+ is ApiResource.Error -> {
253
+ showLoading(false)
254
+ showToast(resource.message!!)
255
+ }
256
+ }
257
+ })
258
+ }
259
+
260
+ private fun sendNotification(message: String) {
261
+ viewModel.sendNotification(message).observe(viewLifecycleOwner, Observer { resource ->
262
+ when (resource) {
263
+ is ApiResource.Loading -> {
264
+ showLoading(true)
265
+ }
266
+ is ApiResource.Success -> {
267
+ showLoading(false)
268
+ val apiResponse = resource.data
269
+ when (apiResponse?.result?.program) {
270
+ TYPE_RESPONSE_MESSAGE -> {
271
+ addMessage(TYPE_CHAT_RECEIVE, apiResponse.result.content.toString())
272
+ }
273
+ TYPE_RESPONSE_BROWSER -> {
274
+ fetchResponseBrowser(apiResponse)
275
+ }
276
+ TYPE_RESPONSE_ALERT -> {
277
+
278
+ }
279
+ TYPE_RESPONSE_CONTACT -> {
280
+
281
+ }
282
+ TYPE_RESPONSE_IMAGE -> {
283
+ fetchResponseImage(apiResponse)
284
+ }
285
+ TYPE_RESPONSE_SMS -> {
286
+
287
+ }
288
+ else -> {
289
+
290
+ }
291
+ }
292
+ }
293
+ is ApiResource.Error -> {
294
+ showToast(resource.message!!)
295
+ showLoading(false)
296
+ }
297
+ }
298
+
299
+ })
300
+ }
301
+
302
+ private fun fetchResponseBrowser(apiResponse: ApiResponse) {
303
+ addMessage(TYPE_CHAT_RECEIVE, apiResponse.result.url)
304
+ }
305
+
306
+ private fun fetchResponseImage(apiResponse: ApiResponse) {
307
+ val content = apiResponse.result.content.asJsonObject
308
+ viewModel.downloadImageFromFirebase(
309
+ content["image_name"].asString
310
+ ).observe(viewLifecycleOwner, Observer { resource ->
311
+ when (resource) {
312
+ is ApiResource.Loading -> {
313
+ showLoading(true)
314
+ }
315
+
316
+ is ApiResource.Success -> {
317
+ showLoading(false)
318
+ addMessage(
319
+ type = TYPE_CHAT_RECEIVE,
320
+ content = null,
321
+ data = null,
322
+ hasImage = true,
323
+ image = resource.data
324
+ )
325
+ }
326
+
327
+ is ApiResource.Error -> {
328
+ showToast(resource.message!!)
329
+ showLoading(false)
330
+ }
331
+ }
332
+ })
333
+ }
334
+
335
+
336
+ private fun initChatInterface() {
337
+ chatMessageInterface = object : ChatMessageInterface {
338
+ override fun sentSms(phoneNumber: String, message: String) {
339
+ addMessage(
340
+ type = TYPE_CHAT_RECEIVE,
341
+ content = "You sent SMS with belowing content.\n\n " + "To: $phoneNumber\n " + "Message: $message",
342
+ )
343
+ }
344
+
345
+ override fun canceledSms() {
346
+ addMessage(
347
+ type = TYPE_CHAT_RECEIVE,
348
+ content = "You canceled SMS.",
349
+ )
350
+ }
351
+
352
+ override fun sentHelpPrompt(prompt: String) {
353
+ }
354
+
355
+ override fun canceledHelpPrompt() {
356
+ }
357
+
358
+ override fun doVoiceCall(phoneNumber: String) {
359
+ }
360
+
361
+ override fun doVideoCall(phoneNumber: String) {
362
+ }
363
+
364
+ override fun sendSmsWithPhoneNumber(phoneNumber: String) {
365
+ }
366
+
367
+ override fun pickImage(isSuccess: Boolean, data: ByteArray?) {
368
+ }
369
+ }
370
+ }
371
+
372
+ override fun onClick(view: View?) {
373
+ when (view?.id) {
374
+ R.id.btn_open_chat_tools -> {
375
+ chatToolsWidget?.toggle()
376
+ }
377
+
378
+ R.id.btn_audio_recognition -> {
379
+
380
+ }
381
+ }
382
+ }
383
+ }
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/chat/view/interfaces/ChatMessageInterface.kt ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.matthaigh27.chatgptwrapper.ui.chat.view.interfaces
2
+
3
+ interface ChatMessageInterface {
4
+ fun sentSms(phoneNumber: String, message: String)
5
+ fun canceledSms()
6
+ fun sentHelpPrompt(prompt: String)
7
+ fun canceledHelpPrompt()
8
+ fun doVoiceCall(phoneNumber: String)
9
+ fun doVideoCall(phoneNumber: String)
10
+ fun sendSmsWithPhoneNumber(phoneNumber: String)
11
+ fun pickImage(isSuccess: Boolean, data: ByteArray? = null)
12
+ }
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/ui/chat/view/widgets/chatwidget/FeedbackWidget.kt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ package com.matthaigh27.chatgptwrapper.ui.chat.view.widgets.chatwidget
2
+
3
+ class FeedbackWidget {
4
+ }
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/{widgets/SmsEditorWidget.kt → ui/chat/view/widgets/chatwidget/SendSmsWidget.kt} RENAMED
@@ -1,77 +1,69 @@
1
- package com.matthaigh27.chatgptwrapper.widgets
2
 
3
  import android.content.Context
4
  import android.util.AttributeSet
5
  import android.view.View
 
6
  import android.widget.Button
7
  import android.widget.EditText
8
  import android.widget.Toast
9
  import androidx.constraintlayout.widget.ConstraintLayout
10
  import com.matthaigh27.chatgptwrapper.R
 
11
 
12
-
13
- class SmsEditorWidget(
14
- context: Context, attrs: AttributeSet?
15
  ) : ConstraintLayout(context, attrs) {
16
 
17
- private val mContext: Context
18
 
19
- private val etToName: EditText
20
- private val etMessage: EditText
21
 
22
- private val btnConfirm: Button
23
  private val btnCancel: Button
24
 
25
- private var mListener: OnClickListener? = null
26
 
27
  init {
28
- inflate(context, R.layout.view_sms_editor, this)
29
- mContext = context
30
 
31
  layoutParams = LayoutParams(
32
- android.view.ViewGroup.LayoutParams.MATCH_PARENT,
33
- android.view.ViewGroup.LayoutParams.WRAP_CONTENT
34
  )
35
 
36
- etToName = findViewById(R.id.et_to_name)
37
- etMessage = findViewById(R.id.et_message)
38
 
39
- btnConfirm = findViewById(R.id.btn_confirm)
40
  btnCancel = findViewById(R.id.btn_cancel)
41
 
42
- btnConfirm.setOnClickListener {
43
- if (etToName.text.toString().isEmpty() || etMessage.text.toString().isEmpty()) {
44
  Toast.makeText(
45
- mContext, "Please input phone number and message.", Toast.LENGTH_SHORT
46
  ).show()
47
  return@setOnClickListener
48
  }
49
- mListener!!.confirmSMS(etToName.text.toString(), etMessage.text.toString())
50
  hide()
 
51
  }
52
 
53
  btnCancel.setOnClickListener {
54
  hide()
55
- mListener!!.cancelSMS()
56
  }
57
  }
58
 
59
- fun setToUserName(name: String) {
60
- etToName.setText(name)
61
  }
62
 
63
  fun hide() {
64
  this.visibility = View.GONE
65
- etToName.setText("")
66
- etMessage.setText("")
67
- }
68
-
69
- fun setOnClickListener(listener: OnClickListener) {
70
- mListener = listener
71
- }
72
-
73
- interface OnClickListener {
74
- fun confirmSMS(phonenumber: String, message: String);
75
- fun cancelSMS();
76
  }
77
  }
 
1
+ package com.matthaigh27.chatgptwrapper.ui.chat.view.widgets.chatwidget
2
 
3
  import android.content.Context
4
  import android.util.AttributeSet
5
  import android.view.View
6
+ import android.view.ViewGroup
7
  import android.widget.Button
8
  import android.widget.EditText
9
  import android.widget.Toast
10
  import androidx.constraintlayout.widget.ConstraintLayout
11
  import com.matthaigh27.chatgptwrapper.R
12
+ import com.matthaigh27.chatgptwrapper.ui.chat.view.interfaces.ChatMessageInterface
13
 
14
+ class SendSmsWidget(
15
+ context: Context, attrs: AttributeSet? = null
 
16
  ) : ConstraintLayout(context, attrs) {
17
 
18
+ private val context: Context
19
 
20
+ private val edtPhoneNumber: EditText
21
+ private val edtMessage: EditText
22
 
23
+ private val btnOk: Button
24
  private val btnCancel: Button
25
 
26
+ var callback: ChatMessageInterface? = null
27
 
28
  init {
29
+ inflate(context, R.layout.widget_send_sms, this)
30
+ this.context = context
31
 
32
  layoutParams = LayoutParams(
33
+ ViewGroup.LayoutParams.MATCH_PARENT,
34
+ ViewGroup.LayoutParams.WRAP_CONTENT
35
  )
36
 
37
+ edtPhoneNumber = findViewById(R.id.edt_phone_to_send)
38
+ edtMessage = findViewById(R.id.edt_message)
39
 
40
+ btnOk = findViewById(R.id.btn_ok)
41
  btnCancel = findViewById(R.id.btn_cancel)
42
 
43
+ btnOk.setOnClickListener {
44
+ if (edtPhoneNumber.text.toString().isEmpty() || edtMessage.text.toString().isEmpty()) {
45
  Toast.makeText(
46
+ context, "Please input phone number and message.", Toast.LENGTH_SHORT
47
  ).show()
48
  return@setOnClickListener
49
  }
 
50
  hide()
51
+ callback?.sentSms(edtPhoneNumber.text.toString(), edtMessage.text.toString())
52
  }
53
 
54
  btnCancel.setOnClickListener {
55
  hide()
56
+ callback?.canceledSms()
57
  }
58
  }
59
 
60
+ fun setPhoneNumber(phonenumber: String) {
61
+ edtPhoneNumber.setText(phonenumber)
62
  }
63
 
64
  fun hide() {
65
  this.visibility = View.GONE
66
+ edtMessage.setText("")
67
+ edtPhoneNumber.setText("")
 
 
 
 
 
 
 
 
 
68
  }
69
  }
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/{widgets → ui/chat/view/widgets/chatwidget/contact}/ContactDetailItem.kt RENAMED
@@ -1,11 +1,9 @@
1
- package com.matthaigh27.chatgptwrapper.widgets
2
 
3
- import android.content.ContentUris
4
  import android.content.Context
5
  import android.content.Intent
6
  import android.content.pm.PackageManager
7
  import android.net.Uri
8
- import android.provider.ContactsContract
9
  import android.telecom.VideoProfile
10
  import android.util.AttributeSet
11
  import android.view.LayoutInflater
@@ -14,66 +12,61 @@ import android.widget.ImageView
14
  import android.widget.TextView
15
  import androidx.constraintlayout.widget.ConstraintLayout
16
  import com.matthaigh27.chatgptwrapper.R
17
- import com.matthaigh27.chatgptwrapper.models.common.ContactModel
18
 
19
  class ContactDetailItem(
20
  context: Context, attrs: AttributeSet? = null
21
  ) : ConstraintLayout(context, attrs), View.OnClickListener {
22
- private lateinit var mTvPhoneNumber: TextView
23
- private lateinit var mTvPhoneType: TextView
24
- private var mPhoneNumber: String = ""
25
- private var mUserName: String = ""
26
 
27
- private var mContext = context
28
- private var mListener: OnSMSClickListener? = null
29
- private var mContactDetailVisibilityListener: OnContactDetailVisibilityListener? = null
30
 
31
  init {
32
  initView()
33
  }
34
 
35
- fun setOnSMSClickListener(listener: OnSMSClickListener) {
36
- mListener = listener
37
- }
38
-
39
- fun setOnContactDetailVisibilityListener(listener: OnContactDetailVisibilityListener) {
40
- mContactDetailVisibilityListener = listener
41
  }
42
 
43
  private fun initView() {
44
  LayoutInflater.from(context).inflate(R.layout.item_contact_detail, this, true)
45
 
46
- mTvPhoneNumber = findViewById(R.id.tv_phone_number)
47
- mTvPhoneType = findViewById(R.id.tv_phone_type)
48
 
49
  findViewById<ImageView>(R.id.btn_voice_call).setOnClickListener(this)
50
  findViewById<ImageView>(R.id.btn_send_message).setOnClickListener(this)
51
  }
52
 
53
  fun setContactDetailItemInfo(phoneNumber: String, username: String) {
54
- mPhoneNumber = phoneNumber
55
- mUserName = username
56
- mTvPhoneNumber.text = phoneNumber
57
  }
58
 
59
  override fun onClick(view: View?) {
60
  when (view!!.id) {
61
  R.id.btn_voice_call -> {
62
- mContactDetailVisibilityListener!!.invisible()
63
- mListener!!.onVoiceCallListener(mPhoneNumber, mUserName)
64
- doVoiceCall(mPhoneNumber)
65
  }
66
 
67
  R.id.btn_send_message -> {
68
- mListener!!.onSMSClickListener(mPhoneNumber)
69
- mContactDetailVisibilityListener!!.invisible()
70
  }
71
  }
 
72
  }
73
 
74
  private fun doVoiceCall(phoneNumber: String) {
75
  val callIntent = Intent(Intent.ACTION_CALL, Uri.parse("tel:$phoneNumber"))
76
- mContext.startActivity(callIntent)
77
  return
78
  }
79
 
@@ -82,7 +75,7 @@ class ContactDetailItem(
82
  val intent = Intent(Intent.ACTION_VIEW, uri).apply {
83
  putExtra("sms_body", message)
84
  }
85
- mContext.startActivity(intent)
86
  }
87
 
88
  private fun doVideoCall(phoneNumber: String) {
@@ -102,11 +95,6 @@ class ContactDetailItem(
102
  }
103
  }
104
 
105
- interface OnSMSClickListener {
106
- fun onSMSClickListener(phoneNumber: String)
107
- fun onVoiceCallListener(phoneNumber: String, toName: String)
108
- }
109
-
110
  interface OnContactDetailVisibilityListener {
111
  fun invisible()
112
  }
 
1
+ package com.matthaigh27.chatgptwrapper.ui.chat.view.widgets.chatwidget.contact
2
 
 
3
  import android.content.Context
4
  import android.content.Intent
5
  import android.content.pm.PackageManager
6
  import android.net.Uri
 
7
  import android.telecom.VideoProfile
8
  import android.util.AttributeSet
9
  import android.view.LayoutInflater
 
12
  import android.widget.TextView
13
  import androidx.constraintlayout.widget.ConstraintLayout
14
  import com.matthaigh27.chatgptwrapper.R
15
+ import com.matthaigh27.chatgptwrapper.ui.chat.view.interfaces.ChatMessageInterface
16
 
17
  class ContactDetailItem(
18
  context: Context, attrs: AttributeSet? = null
19
  ) : ConstraintLayout(context, attrs), View.OnClickListener {
20
+ private lateinit var txtPhoneNumber: TextView
21
+ private lateinit var txtPhoneType: TextView
22
+ private var phoneNumber: String = ""
23
+ private var userName: String = ""
24
 
25
+ private var context = context
26
+ private var visibilityListener: OnContactDetailVisibilityListener? = null
27
+ var callback: ChatMessageInterface? = null
28
 
29
  init {
30
  initView()
31
  }
32
 
33
+ fun setVisibilityListener(listener: OnContactDetailVisibilityListener) {
34
+ visibilityListener = listener
 
 
 
 
35
  }
36
 
37
  private fun initView() {
38
  LayoutInflater.from(context).inflate(R.layout.item_contact_detail, this, true)
39
 
40
+ txtPhoneNumber = findViewById(R.id.txt_phone_number)
41
+ txtPhoneType = findViewById(R.id.txt_phone_type)
42
 
43
  findViewById<ImageView>(R.id.btn_voice_call).setOnClickListener(this)
44
  findViewById<ImageView>(R.id.btn_send_message).setOnClickListener(this)
45
  }
46
 
47
  fun setContactDetailItemInfo(phoneNumber: String, username: String) {
48
+ this.phoneNumber = phoneNumber
49
+ this.userName = username
50
+ txtPhoneNumber.text = phoneNumber
51
  }
52
 
53
  override fun onClick(view: View?) {
54
  when (view!!.id) {
55
  R.id.btn_voice_call -> {
56
+ callback?.doVoiceCall(phoneNumber)
57
+ doVoiceCall(phoneNumber)
 
58
  }
59
 
60
  R.id.btn_send_message -> {
61
+ callback?.sendSmsWithPhoneNumber(phoneNumber)
 
62
  }
63
  }
64
+ visibilityListener?.invisible()
65
  }
66
 
67
  private fun doVoiceCall(phoneNumber: String) {
68
  val callIntent = Intent(Intent.ACTION_CALL, Uri.parse("tel:$phoneNumber"))
69
+ context.startActivity(callIntent)
70
  return
71
  }
72
 
 
75
  val intent = Intent(Intent.ACTION_VIEW, uri).apply {
76
  putExtra("sms_body", message)
77
  }
78
+ context.startActivity(intent)
79
  }
80
 
81
  private fun doVideoCall(phoneNumber: String) {
 
95
  }
96
  }
97
 
 
 
 
 
 
98
  interface OnContactDetailVisibilityListener {
99
  fun invisible()
100
  }
Android/app/src/main/java/com/matthaigh27/chatgptwrapper/{widgets → ui/chat/view/widgets/chatwidget/contact}/ContactDetailWidget.kt RENAMED
@@ -1,72 +1,81 @@
1
- package com.matthaigh27.chatgptwrapper.widgets
2
 
3
  import android.content.ContentUris
4
  import android.content.Context
5
  import android.content.Intent
6
  import android.provider.ContactsContract
7
- import android.util.AttributeSet
8
- import android.view.LayoutInflater
9
  import android.view.View
10
  import android.widget.ImageView
11
  import android.widget.LinearLayout
12
  import android.widget.TextView
13
- import androidx.constraintlayout.widget.ConstraintLayout
14
- import androidx.core.view.forEach
15
  import com.google.android.material.bottomsheet.BottomSheetDialog
16
  import com.matthaigh27.chatgptwrapper.R
17
- import com.matthaigh27.chatgptwrapper.models.common.ContactModel
18
- import com.matthaigh27.chatgptwrapper.utils.Utils
19
 
20
  class ContactDetailWidget(
21
- context: Context, contactModel: ContactModel, smsClickListener: ContactDetailItem.OnSMSClickListener
22
  ) : BottomSheetDialog(context), View.OnClickListener {
23
 
24
- private lateinit var mAvatar: ImageView
25
- private lateinit var mDisplayName: TextView
26
- private lateinit var mBtnEditContact: ImageView
27
- private lateinit var mLlPhones: LinearLayout
28
 
29
- private var mContactModel = contactModel
30
 
31
- private var mOnSMSClickListener: ContactDetailItem.OnSMSClickListener = smsClickListener
32
 
33
  init {
34
  initView()
35
  }
36
 
37
-
38
  private fun initView() {
39
- setContentView(R.layout.view_contact_detail)
40
 
41
- mDisplayName = findViewById(R.id.tv_displayname)!!
42
- mBtnEditContact = findViewById(R.id.btn_edit_contact)!!
43
- mLlPhones = findViewById(R.id.ll_contacts)!!
44
 
45
- mLlPhones.removeAllViews()
46
- mDisplayName.text = mContactModel.name
47
- mContactModel.phoneList!!.forEach { phoneNumber ->
48
  val contactDetailItem = ContactDetailItem(context)
49
- contactDetailItem.setContactDetailItemInfo(phoneNumber, mContactModel.name)
50
- contactDetailItem.setOnSMSClickListener(mOnSMSClickListener)
51
- contactDetailItem.setOnContactDetailVisibilityListener(object:
52
  ContactDetailItem.OnContactDetailVisibilityListener {
53
  override fun invisible() {
54
  this@ContactDetailWidget.dismiss()
55
  }
56
  })
57
- mLlPhones.addView(contactDetailItem)
58
  }
59
 
60
- mBtnEditContact.setOnClickListener(this)
61
- mAvatar = findViewById(R.id.iv_avatar)!!
62
- Utils.instance.setContactAvatar(mContactModel.id.toLong(), context, mAvatar)
 
 
 
 
 
 
 
 
 
 
 
 
 
63
  }
64
 
65
  override fun onClick(view: View?) {
66
  when (view!!.id) {
67
  R.id.btn_edit_contact -> {
68
- goToContactEditor(mContactModel.id)
69
  }
 
70
  R.id.btn_send_message -> {
71
  }
72
  }
 
1
+ package com.matthaigh27.chatgptwrapper.ui.chat.view.widgets.chatwidget.contact
2
 
3
  import android.content.ContentUris
4
  import android.content.Context
5
  import android.content.Intent
6
  import android.provider.ContactsContract
 
 
7
  import android.view.View
8
  import android.widget.ImageView
9
  import android.widget.LinearLayout
10
  import android.widget.TextView
11
+ import com.bumptech.glide.Glide
 
12
  import com.google.android.material.bottomsheet.BottomSheetDialog
13
  import com.matthaigh27.chatgptwrapper.R
14
+ import com.matthaigh27.chatgptwrapper.data.models.ContactModel
15
+ import com.matthaigh27.chatgptwrapper.ui.chat.view.interfaces.ChatMessageInterface
16
 
17
  class ContactDetailWidget(
18
+ context: Context, contactModel: ContactModel
19
  ) : BottomSheetDialog(context), View.OnClickListener {
20
 
21
+ private var imgAvatar: ImageView? = null
22
+ private var txtDisplayName: TextView? = null
23
+ private var btnEditContact: ImageView? = null
24
+ private var llPhones: LinearLayout? = null
25
 
26
+ private var contactModel = contactModel
27
 
28
+ var callback: ChatMessageInterface? = null
29
 
30
  init {
31
  initView()
32
  }
33
 
 
34
  private fun initView() {
35
+ setContentView(R.layout.widget_contact_detail)
36
 
37
+ txtDisplayName = findViewById(R.id.txt_display_name)
38
+ btnEditContact = findViewById(R.id.btn_edit_contact)
39
+ llPhones = findViewById(R.id.ll_contacts)
40
 
41
+ llPhones?.removeAllViews()
42
+ txtDisplayName?.text = contactModel.name
43
+ contactModel.phoneList.forEach { phoneNumber ->
44
  val contactDetailItem = ContactDetailItem(context)
45
+ contactDetailItem.setContactDetailItemInfo(phoneNumber, contactModel.name)
46
+ contactDetailItem.setVisibilityListener(object :
 
47
  ContactDetailItem.OnContactDetailVisibilityListener {
48
  override fun invisible() {
49
  this@ContactDetailWidget.dismiss()
50
  }
51
  })
52
+ llPhones?.addView(contactDetailItem)
53
  }
54
 
55
+ btnEditContact?.setOnClickListener(this)
56
+ imgAvatar = findViewById(R.id.img_avatar)
57
+ imgAvatar?.setContactAvatar(contactModel.id.toLong())
58
+ }
59
+
60
+ private fun ImageView.setContactAvatar(contactId: Long) {
61
+ val uri = ContentUris.withAppendedId(
62
+ ContactsContract.Contacts.CONTENT_URI, contactId
63
+ )
64
+
65
+ Glide.with(context)
66
+ .load(uri)
67
+ .placeholder(R.drawable.image_default_avatar) // Set placeholder image
68
+ .error(R.drawable.image_default_avatar) // Set error image
69
+ .fallback(R.drawable.image_default_avatar) // Set fallback image
70
+ .into(this)
71
  }
72
 
73
  override fun onClick(view: View?) {
74
  when (view!!.id) {
75
  R.id.btn_edit_contact -> {
76
+ goToContactEditor(contactModel.id)
77
  }
78
+
79
  R.id.btn_send_message -> {
80
  }
81
  }