├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── io │ │ └── edna │ │ └── threads │ │ └── demo │ │ ├── MockTestRunner.kt │ │ └── snapshot │ │ ├── AttachmentsSnapshotTest.kt │ │ ├── ChatBotSnapshotTest.kt │ │ ├── ChatErrorsSnapshotTest.kt │ │ ├── ChatFilesSnapshotTest.kt │ │ ├── ChatImagesSnapshotTest1.kt │ │ ├── ChatImagesSnapshotTest2.kt │ │ ├── ChatImagesSnapshotTest3.kt │ │ ├── ChatImagesSnapshotTest4.kt │ │ ├── ChatSystemSnapshotTest.kt │ │ ├── ChatTextSnapshotTest1.kt │ │ ├── ChatTextSnapshotTest2.kt │ │ ├── ChatTextSnapshotTest3.kt │ │ ├── ChatVoiceSnapshotTest.kt │ │ ├── ConsultActivitySnapshotTest.kt │ │ ├── GalleryActivitySnapshotTest.kt │ │ ├── ReplySnapshotTest.kt │ │ ├── SnapshotBaseTest.kt │ │ ├── SnapshotsCommonCode.kt │ │ └── ToolbarIconsSnapshotTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── assets │ │ ├── fonts │ │ │ ├── lato-bold.ttf │ │ │ ├── lato-light.ttf │ │ │ └── lato-regular.ttf │ │ ├── servers_config.json │ │ └── test_files │ │ │ ├── client_text1.ogg │ │ │ ├── client_text2.ogg │ │ │ ├── client_text3.ogg │ │ │ ├── client_text4.ogg │ │ │ ├── operator_text1.ogg │ │ │ ├── operator_text2.ogg │ │ │ ├── operator_text3.ogg │ │ │ ├── operator_text4.ogg │ │ │ ├── test_image1.jpg │ │ │ ├── test_image2.jpg │ │ │ ├── test_image3.jpg │ │ │ ├── test_image4.jpg │ │ │ ├── test_image5.jpg │ │ │ ├── test_image6.jpg │ │ │ ├── test_pdf1.pdf │ │ │ └── test_pdf2.pdf │ ├── ic_launcher-playstore.png │ ├── java │ │ └── io │ │ │ └── edna │ │ │ └── threads │ │ │ └── demo │ │ │ ├── appCode │ │ │ ├── activity │ │ │ │ ├── MainActivity.kt │ │ │ │ └── SplashScreenActivity.kt │ │ │ ├── adapters │ │ │ │ ├── EccTouchHelperCallBack.kt │ │ │ │ ├── ListItemClickListener.kt │ │ │ │ ├── demoSamplesList │ │ │ │ │ ├── DemoSamplesAdapter.kt │ │ │ │ │ ├── DemoSamplesDiffCallback.kt │ │ │ │ │ └── SampleListItemOnClick.kt │ │ │ │ ├── serverList │ │ │ │ │ ├── ServerListAdapter.kt │ │ │ │ │ └── ServerListDiffCallback.kt │ │ │ │ └── userList │ │ │ │ │ ├── UserListAdapter.kt │ │ │ │ │ └── UserListDiffCallback.kt │ │ │ ├── business │ │ │ │ ├── AfterTextChangedTextWatcher.kt │ │ │ │ ├── Extensions.kt │ │ │ │ ├── KoinModules.kt │ │ │ │ ├── PreferencesProvider.kt │ │ │ │ ├── ServersProvider.kt │ │ │ │ ├── SingleLiveEvent.kt │ │ │ │ ├── StringsProvider.kt │ │ │ │ ├── TouchHelper.kt │ │ │ │ ├── UiThemeProvider.kt │ │ │ │ ├── VolatileLiveData.kt │ │ │ │ └── mockJsonProvider │ │ │ │ │ ├── CurrentJsonProvider.kt │ │ │ │ │ └── SamplesJsonProvider.kt │ │ │ ├── extensions │ │ │ │ └── UIExtensions.kt │ │ │ ├── fragments │ │ │ │ ├── BaseAppFragment.kt │ │ │ │ ├── demoSamplesFragment │ │ │ │ │ ├── DemoSamplesFragment.kt │ │ │ │ │ └── DemoSamplesViewModel.kt │ │ │ │ ├── demoSamplesList │ │ │ │ │ ├── DemoSamplesListFragment.kt │ │ │ │ │ └── DemoSamplesListViewModel.kt │ │ │ │ ├── server │ │ │ │ │ ├── AddServerFragment.kt │ │ │ │ │ ├── AddServerViewModel.kt │ │ │ │ │ ├── ServerListFragment.kt │ │ │ │ │ └── ServerListViewModel.kt │ │ │ │ └── user │ │ │ │ │ ├── AddUserFragment.kt │ │ │ │ │ ├── AddUserViewModel.kt │ │ │ │ │ ├── UserListFragment.kt │ │ │ │ │ └── UserListViewModel.kt │ │ │ ├── models │ │ │ │ ├── DemoSamplesListItem.kt │ │ │ │ ├── ServerConfig.kt │ │ │ │ ├── TestData.kt │ │ │ │ ├── UiTheme.kt │ │ │ │ └── UserInfo.kt │ │ │ ├── push │ │ │ │ ├── CustomPushFcmIntentService.kt │ │ │ │ ├── CustomPushHcmIntentService.kt │ │ │ │ └── HCMTokenRefresher.kt │ │ │ ├── test │ │ │ │ └── TestChatActivity.kt │ │ │ ├── themes │ │ │ │ └── ChatThemes.kt │ │ │ └── views │ │ │ │ ├── InputField.kt │ │ │ │ └── ItemDecorator.kt │ │ │ └── integrationCode │ │ │ ├── EdnaThreadsApplication.kt │ │ │ ├── ThreadsLibInitializer.kt │ │ │ └── fragments │ │ │ ├── chatFragment │ │ │ └── ChatAppFragment.kt │ │ │ └── launch │ │ │ ├── LaunchFragment.kt │ │ │ └── LaunchViewModel.kt │ └── res │ │ ├── drawable-hdpi │ │ ├── alt_thread_incoming_bubble.9.png │ │ ├── alt_thread_outgoing_bubble.9.png │ │ ├── ic_launcher.webp │ │ ├── ic_launcher_round.webp │ │ └── logo.png │ │ ├── drawable-mdpi │ │ ├── alt_thread_incoming_bubble.9.png │ │ ├── alt_thread_outgoing_bubble.9.png │ │ ├── ic_launcher.webp │ │ ├── ic_launcher_round.webp │ │ └── logo.png │ │ ├── drawable-night │ │ └── splash_background.xml │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable-xhdpi │ │ ├── alt_thread_incoming_bubble.9.png │ │ ├── alt_thread_outgoing_bubble.9.png │ │ ├── ic_launcher.webp │ │ ├── ic_launcher_round.webp │ │ ├── im_empty_users.png │ │ └── logo.png │ │ ├── drawable-xxhdpi │ │ ├── alt_thread_incoming_bubble.9.png │ │ ├── alt_thread_outgoing_bubble.9.png │ │ ├── ic_launcher.webp │ │ ├── ic_launcher_round.webp │ │ └── logo.png │ │ ├── drawable │ │ ├── alt_thread_incoming_image_mask.9.png │ │ ├── alt_thread_outgoing_image_mask.9.png │ │ ├── alt_threads_scroll_down_icon_black.xml │ │ ├── alt_threads_scroll_down_icon_light.xml │ │ ├── app_logo.png │ │ ├── buttons_bg_selector.xml │ │ ├── buttons_bg_selector_dark.xml │ │ ├── buttons_text_color_selector.xml │ │ ├── dark_theme.xml │ │ ├── ic_back.xml │ │ ├── ic_chevron_right.xml │ │ ├── ic_cloud.xml │ │ ├── ic_edit.xml │ │ ├── ic_launcher_background.xml │ │ ├── ic_ok.xml │ │ ├── ic_ok_desable.xml │ │ ├── ic_ok_pressed.xml │ │ ├── ic_ok_selector.xml │ │ ├── ic_plus.xml │ │ ├── ic_remove.xml │ │ ├── ic_settings.xml │ │ ├── ic_user.xml │ │ ├── im_users_empty.xml │ │ ├── light_theme.xml │ │ ├── red_cyrcle.xml │ │ ├── secondary_buttons_bg_selector.xml │ │ ├── secondary_buttons_text_color_selector.xml │ │ ├── splash_background.xml │ │ ├── text_button_bg_selector.xml │ │ └── text_button_text_color_selector.xml │ │ ├── font │ │ └── roboto_medium.ttf │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── activity_test_chat.xml │ │ ├── fragment_add_server.xml │ │ ├── fragment_add_user.xml │ │ ├── fragment_chat.xml │ │ ├── fragment_launch.xml │ │ ├── fragment_samples_list.xml │ │ ├── fragment_server_list.xml │ │ ├── fragment_user_list.xml │ │ ├── holder_demo_samples_text.xml │ │ ├── holder_demo_samples_title.xml │ │ ├── holder_horizontal_line.xml │ │ ├── input_field.xml │ │ ├── server_list_item.xml │ │ └── user_list_item.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── navigation │ │ └── nav_graph.xml │ │ ├── raw │ │ ├── history_bot_response.json │ │ ├── history_deleted_and_edited_messages.json │ │ ├── history_errors_response.json │ │ ├── history_files_response.json │ │ ├── history_images_response.json │ │ ├── history_system_response.json │ │ ├── history_text_response.json │ │ ├── history_voice_response.json │ │ ├── snapshot_test_errors_response.json │ │ ├── snapshot_test_history_bot_response.json │ │ ├── snapshot_test_history_images_response_1.json │ │ ├── snapshot_test_history_images_response_2.json │ │ ├── snapshot_test_history_images_response_3.json │ │ ├── snapshot_test_history_images_response_4.json │ │ ├── snapshot_test_history_system_response.json │ │ ├── snapshot_test_history_text_response_1.json │ │ ├── snapshot_test_history_text_response_2.json │ │ ├── snapshot_test_history_text_response_3.json │ │ └── snapshot_test_history_voice_response.json │ │ ├── values-hdpi │ │ └── dimens.xml │ │ ├── values-land │ │ └── dimens.xml │ │ ├── values-night │ │ └── themes.xml │ │ ├── values-w1240dp │ │ └── dimens.xml │ │ ├── values-w600dp │ │ └── dimens.xml │ │ ├── values │ │ ├── attr.xml │ │ ├── bools.xml │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── ic_launcher_background.xml │ │ ├── strings.xml │ │ └── themes.xml │ │ └── xml │ │ ├── backup_rules.xml │ │ ├── data_extraction_rules.xml │ │ └── network_security_config.xml │ └── test │ └── java │ └── io │ └── edna │ └── threads │ └── demo │ └── ExampleUnitTest.kt ├── build.gradle ├── docs ├── image1.jpg ├── image2.jpg ├── image3.jpg ├── image4.png ├── image5.png ├── image6.png ├── image7.png └── image8.png ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── push-lib ├── Android_SDK_autodraw_3.3.0.docx ├── MFMSPushLite.pdf └── edna-PushService-MobileSDK-Android-Lite_3.3.0.docx └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # User-specific configurations 2 | .idea/libraries/ 3 | .idea/workspace.xml 4 | .idea/tasks.xml 5 | .idea/.name 6 | .idea/compiler.xml 7 | .idea/copyright/profiles_settings.xml 8 | .idea/encodings.xml 9 | .idea/misc.xml 10 | .idea/modules.xml 11 | .idea/scopes/scope_settings.xml 12 | .idea/vcs.xml 13 | 14 | *.iml 15 | .gradle 16 | /local.properties 17 | */.idea 18 | .DS_Store 19 | /build 20 | /captures 21 | /.idea 22 | /threads/src/main/assets/crashlytics-build.properties 23 | /threads/src/main/res/values/com_crashlytics_export_strings.xml 24 | 25 | /fastlane/report.xml -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # edna Android SDK 2 | edna Android SDK libraries and Demo project 3 | 4 | [Documentation and Guides](https://edna-io.github.io/android/intro) 5 | 6 | Contacts: 7 | https://edna.ru/
support@edna.ru 8 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in H:\AndroidSDK/tools/proguard/proguard-android.txt 4 | # You can edit the include text and order by changing the proguardFiles 5 | # directive in buildBundle.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | ##---------------Begin: proguard configuration for Gson ---------- 19 | # Gson uses generic type information stored in a class file when working with fields. Proguard 20 | # removes such information by default, so configure it to keep all of it. 21 | -keepattributes Signature 22 | 23 | # For using GSON @Expose annotation 24 | -keepattributes *Annotation* 25 | 26 | # Gson specific classes 27 | -dontwarn sun.misc.** 28 | #-keep class com.google.gson.stream.** { *; } 29 | 30 | # Application classes that will be serialized/deserialized over Gson 31 | -keep class im.threads.** { *; } 32 | 33 | # Prevent proguard from stripping interface information from TypeAdapter, TypeAdapterFactory, 34 | # JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter) 35 | -keep class * extends com.google.gson.TypeAdapter 36 | -keep class * implements com.google.gson.TypeAdapterFactory 37 | -keep class * implements com.google.gson.JsonSerializer 38 | -keep class * implements com.google.gson.JsonDeserializer 39 | 40 | # Prevent R8 from leaving Data object members always null 41 | -keepclassmembers,allowobfuscation class * { 42 | @com.google.gson.annotations.SerializedName ; 43 | } 44 | 45 | ##---------------End: proguard configuration for Gson ---------- -------------------------------------------------------------------------------- /app/src/androidTest/java/io/edna/threads/demo/MockTestRunner.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo 2 | 3 | import android.app.Application 4 | import android.content.Context 5 | import androidx.test.runner.AndroidJUnitRunner 6 | import io.edna.threads.demo.integrationCode.EdnaThreadsApplication 7 | 8 | class MockTestRunner : AndroidJUnitRunner() { 9 | 10 | override fun newApplication( 11 | cl: ClassLoader?, 12 | className: String?, 13 | context: Context? 14 | ): Application { 15 | return super.newApplication(cl, EdnaThreadsApplication::class.java.name, context) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/io/edna/threads/demo/snapshot/AttachmentsSnapshotTest.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.snapshot 2 | 3 | import android.os.Build 4 | import androidx.test.espresso.Espresso.onView 5 | import androidx.test.espresso.action.ViewActions.click 6 | import androidx.test.espresso.matcher.ViewMatchers.withTagValue 7 | import androidx.test.ext.junit.runners.AndroidJUnit4 8 | import androidx.test.rule.GrantPermissionRule 9 | import dev.testify.annotation.ScreenshotInstrumentation 10 | import io.edna.threads.demo.R 11 | import org.hamcrest.Matchers.`is` 12 | import org.junit.Rule 13 | import org.junit.Test 14 | import org.junit.runner.RunWith 15 | 16 | @RunWith(AndroidJUnit4::class) 17 | class AttachmentsSnapshotTest : SnapshotBaseTest(R.raw.snapshot_test_history_text_response_1) { 18 | @get:Rule 19 | var permissionRule = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { 20 | GrantPermissionRule.grant( 21 | android.Manifest.permission.READ_MEDIA_IMAGES, 22 | android.Manifest.permission.READ_MEDIA_VIDEO, 23 | android.Manifest.permission.READ_MEDIA_AUDIO 24 | ) 25 | } else { 26 | GrantPermissionRule.grant(android.Manifest.permission.READ_EXTERNAL_STORAGE) 27 | } 28 | 29 | @ScreenshotInstrumentation 30 | @Test 31 | override fun testChat() { 32 | rule.setEspressoActions { 33 | onView(withTagValue(`is`("add_attachment"))).perform(click()) 34 | }.assertSame() 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/src/androidTest/java/io/edna/threads/demo/snapshot/ChatBotSnapshotTest.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.snapshot 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4 4 | import io.edna.threads.demo.R 5 | import org.junit.runner.RunWith 6 | 7 | @RunWith(AndroidJUnit4::class) 8 | class ChatBotSnapshotTest : SnapshotBaseTest(R.raw.snapshot_test_history_system_response) 9 | -------------------------------------------------------------------------------- /app/src/androidTest/java/io/edna/threads/demo/snapshot/ChatErrorsSnapshotTest.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.snapshot 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4 4 | import io.edna.threads.demo.R 5 | import org.junit.runner.RunWith 6 | 7 | @RunWith(AndroidJUnit4::class) 8 | class ChatErrorsSnapshotTest : SnapshotBaseTest(R.raw.snapshot_test_errors_response) 9 | -------------------------------------------------------------------------------- /app/src/androidTest/java/io/edna/threads/demo/snapshot/ChatFilesSnapshotTest.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.snapshot 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4 4 | import io.edna.threads.demo.R 5 | import org.junit.runner.RunWith 6 | 7 | @RunWith(AndroidJUnit4::class) 8 | class ChatFilesSnapshotTest : SnapshotBaseTest(R.raw.history_files_response) 9 | -------------------------------------------------------------------------------- /app/src/androidTest/java/io/edna/threads/demo/snapshot/ChatImagesSnapshotTest1.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.snapshot 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4 4 | import io.edna.threads.demo.R 5 | import org.junit.runner.RunWith 6 | 7 | @RunWith(AndroidJUnit4::class) 8 | class ChatImagesSnapshotTest1 : SnapshotBaseTest(R.raw.snapshot_test_history_images_response_1) 9 | -------------------------------------------------------------------------------- /app/src/androidTest/java/io/edna/threads/demo/snapshot/ChatImagesSnapshotTest2.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.snapshot 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4 4 | import io.edna.threads.demo.R 5 | import org.junit.runner.RunWith 6 | 7 | @RunWith(AndroidJUnit4::class) 8 | class ChatImagesSnapshotTest2 : SnapshotBaseTest(R.raw.snapshot_test_history_images_response_2) 9 | -------------------------------------------------------------------------------- /app/src/androidTest/java/io/edna/threads/demo/snapshot/ChatImagesSnapshotTest3.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.snapshot 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4 4 | import io.edna.threads.demo.R 5 | import org.junit.runner.RunWith 6 | 7 | @RunWith(AndroidJUnit4::class) 8 | class ChatImagesSnapshotTest3 : SnapshotBaseTest(R.raw.snapshot_test_history_images_response_3) 9 | -------------------------------------------------------------------------------- /app/src/androidTest/java/io/edna/threads/demo/snapshot/ChatImagesSnapshotTest4.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.snapshot 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4 4 | import io.edna.threads.demo.R 5 | import org.junit.runner.RunWith 6 | 7 | @RunWith(AndroidJUnit4::class) 8 | class ChatImagesSnapshotTest4 : SnapshotBaseTest(R.raw.snapshot_test_history_images_response_4) 9 | -------------------------------------------------------------------------------- /app/src/androidTest/java/io/edna/threads/demo/snapshot/ChatSystemSnapshotTest.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.snapshot 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4 4 | import io.edna.threads.demo.R 5 | import org.junit.runner.RunWith 6 | 7 | @RunWith(AndroidJUnit4::class) 8 | class ChatSystemSnapshotTest : SnapshotBaseTest(R.raw.snapshot_test_history_system_response) 9 | -------------------------------------------------------------------------------- /app/src/androidTest/java/io/edna/threads/demo/snapshot/ChatTextSnapshotTest1.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.snapshot 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4 4 | import io.edna.threads.demo.R 5 | import org.junit.runner.RunWith 6 | 7 | @RunWith(AndroidJUnit4::class) 8 | class ChatTextSnapshotTest1 : SnapshotBaseTest(R.raw.snapshot_test_history_text_response_1) 9 | -------------------------------------------------------------------------------- /app/src/androidTest/java/io/edna/threads/demo/snapshot/ChatTextSnapshotTest2.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.snapshot 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4 4 | import io.edna.threads.demo.R 5 | import org.junit.runner.RunWith 6 | 7 | @RunWith(AndroidJUnit4::class) 8 | class ChatTextSnapshotTest2 : SnapshotBaseTest(R.raw.snapshot_test_history_text_response_2) 9 | -------------------------------------------------------------------------------- /app/src/androidTest/java/io/edna/threads/demo/snapshot/ChatVoiceSnapshotTest.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.snapshot 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4 4 | import io.edna.threads.demo.R 5 | import org.junit.runner.RunWith 6 | 7 | @RunWith(AndroidJUnit4::class) 8 | class ChatVoiceSnapshotTest : SnapshotBaseTest(R.raw.snapshot_test_history_voice_response) 9 | -------------------------------------------------------------------------------- /app/src/androidTest/java/io/edna/threads/demo/snapshot/ConsultActivitySnapshotTest.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.snapshot 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4 4 | import dev.testify.ScreenshotRule 5 | import dev.testify.annotation.ScreenshotInstrumentation 6 | import im.threads.business.models.ConsultInfo 7 | import im.threads.ui.activities.ConsultActivity 8 | import org.junit.Rule 9 | import org.junit.Test 10 | import org.junit.runner.RunWith 11 | 12 | @RunWith(AndroidJUnit4::class) 13 | class ConsultActivitySnapshotTest { 14 | private val avatarUrl = "https://noednaimage.ru/1.jpg" 15 | private val status = "Active" 16 | private val name = "Operator Alisa" 17 | 18 | @get:Rule 19 | val rule = ScreenshotRule(ConsultActivity::class.java).apply { 20 | addIntentExtras { 21 | it.putParcelable( 22 | ConsultActivity.consultInfoKey, 23 | ConsultInfo( 24 | photoUrl = avatarUrl, 25 | status = status, 26 | name = name 27 | ) 28 | ) 29 | } 30 | } 31 | 32 | @ScreenshotInstrumentation 33 | @Test 34 | fun testConsultActivity() { 35 | rule.assertSame() 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /app/src/androidTest/java/io/edna/threads/demo/snapshot/GalleryActivitySnapshotTest.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.snapshot 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4 4 | import dev.testify.ScreenshotRule 5 | import dev.testify.annotation.ScreenshotInstrumentation 6 | import im.threads.ui.activities.GalleryActivity 7 | import org.junit.Rule 8 | import org.junit.Test 9 | import org.junit.runner.RunWith 10 | 11 | @RunWith(AndroidJUnit4::class) 12 | class GalleryActivitySnapshotTest { 13 | @get:Rule 14 | val rule = ScreenshotRule(GalleryActivity::class.java).apply { 15 | addIntentExtras { 16 | it.putInt(PHOTOS_REQUEST_CODE_TAG, 2345) 17 | } 18 | } 19 | 20 | @ScreenshotInstrumentation 21 | @Test 22 | fun testGalleryActivity() { 23 | rule.assertSame() 24 | } 25 | } 26 | 27 | private const val PHOTOS_REQUEST_CODE_TAG = "PHOTOS_REQUEST_CODE_TAG" 28 | -------------------------------------------------------------------------------- /app/src/androidTest/java/io/edna/threads/demo/snapshot/ReplySnapshotTest.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.snapshot 2 | 3 | import androidx.test.espresso.Espresso 4 | import androidx.test.espresso.action.ViewActions 5 | import androidx.test.espresso.matcher.ViewMatchers 6 | import androidx.test.ext.junit.runners.AndroidJUnit4 7 | import dev.testify.annotation.ScreenshotInstrumentation 8 | import io.edna.threads.demo.R 9 | import org.hamcrest.Matchers 10 | import org.junit.Test 11 | import org.junit.runner.RunWith 12 | 13 | @RunWith(AndroidJUnit4::class) 14 | class ReplySnapshotTest : SnapshotBaseTest(R.raw.snapshot_test_history_text_response_1) { 15 | @ScreenshotInstrumentation 16 | @Test 17 | override fun testChat() { 18 | rule.setEspressoActions { 19 | Espresso.onView(ViewMatchers.withTagValue(Matchers.`is`("bubble"))) 20 | .perform(ViewActions.longClick()) 21 | Espresso.onView(ViewMatchers.withTagValue(Matchers.`is`("replyIcon"))) 22 | .perform(ViewActions.click()) 23 | }.assertSame() 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/androidTest/java/io/edna/threads/demo/snapshot/SnapshotBaseTest.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.snapshot 2 | 3 | import dev.testify.ScreenshotRule 4 | import dev.testify.annotation.ScreenshotInstrumentation 5 | import io.edna.threads.demo.appCode.test.TestChatActivity 6 | import org.junit.Ignore 7 | import org.junit.Rule 8 | import org.junit.Test 9 | 10 | @Ignore("Base class, run it in descendants") 11 | open class SnapshotBaseTest(private val jsonResourceId: Int) { 12 | @get:Rule 13 | val rule = ScreenshotRule(TestChatActivity::class.java).apply { 14 | saveJsonMock(jsonResourceId, this) 15 | } 16 | 17 | @ScreenshotInstrumentation 18 | @Test 19 | open fun testChat() { 20 | rule.setEspressoActions { 21 | Thread.sleep(2000) 22 | }.assertSame() 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/androidTest/java/io/edna/threads/demo/snapshot/SnapshotsCommonCode.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.snapshot 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import dev.testify.ScreenshotRule 5 | import io.edna.threads.demo.appCode.test.TestChatActivity 6 | import java.io.BufferedReader 7 | import java.io.IOException 8 | import java.io.InputStream 9 | import java.io.InputStreamReader 10 | 11 | internal fun saveJsonMock(resourceId: Int, rule: ScreenshotRule) { 12 | var string: String? = "" 13 | val stringBuilder = StringBuilder() 14 | val context = InstrumentationRegistry.getInstrumentation().targetContext 15 | val inputStream: InputStream = context.resources.openRawResource(resourceId) 16 | val reader = BufferedReader(InputStreamReader(inputStream)) 17 | 18 | while (true) { 19 | try { 20 | if (reader.readLine().also { string = it } == null) break 21 | } catch (e: IOException) { 22 | e.printStackTrace() 23 | } 24 | stringBuilder.append(string).append("\n") 25 | } 26 | 27 | inputStream.close() 28 | rule.addIntentExtras { 29 | it.putString(TestChatActivity.jsonMockExtraKey, stringBuilder.toString()) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/androidTest/java/io/edna/threads/demo/snapshot/ToolbarIconsSnapshotTest.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.snapshot 2 | 3 | import androidx.test.espresso.Espresso 4 | import androidx.test.espresso.action.ViewActions 5 | import androidx.test.espresso.matcher.ViewMatchers 6 | import androidx.test.ext.junit.runners.AndroidJUnit4 7 | import dev.testify.annotation.ScreenshotInstrumentation 8 | import io.edna.threads.demo.R 9 | import org.hamcrest.Matchers 10 | import org.junit.Test 11 | import org.junit.runner.RunWith 12 | 13 | @RunWith(AndroidJUnit4::class) 14 | class ToolbarIconsSnapshotTest : SnapshotBaseTest(R.raw.snapshot_test_history_text_response_1) { 15 | @ScreenshotInstrumentation 16 | @Test 17 | override fun testChat() { 18 | rule.setEspressoActions { 19 | Espresso.onView(ViewMatchers.withTagValue(Matchers.`is`("bubble"))) 20 | .perform(ViewActions.longClick()) 21 | }.assertSame() 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 44 | 45 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 60 | 61 | 62 | 63 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /app/src/main/assets/fonts/lato-bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/assets/fonts/lato-bold.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/lato-light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/assets/fonts/lato-light.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/lato-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/assets/fonts/lato-regular.ttf -------------------------------------------------------------------------------- /app/src/main/assets/servers_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "servers": [ 3 | { 4 | "name": "Default", 5 | "threadsGateProviderUid": "MOBILE1_93jLrvripZeDXSKJzdRfEu9QpMMvIe5LKKHQl", 6 | "serverBaseUrl": "http://mobile1.chc.dte/", 7 | "datastoreUrl": "http://datastore.mobile1.chc.dte/", 8 | "threadsGateUrl": "ws://mobile1.chc.dte/socket" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/assets/test_files/client_text1.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/assets/test_files/client_text1.ogg -------------------------------------------------------------------------------- /app/src/main/assets/test_files/client_text2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/assets/test_files/client_text2.ogg -------------------------------------------------------------------------------- /app/src/main/assets/test_files/client_text3.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/assets/test_files/client_text3.ogg -------------------------------------------------------------------------------- /app/src/main/assets/test_files/client_text4.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/assets/test_files/client_text4.ogg -------------------------------------------------------------------------------- /app/src/main/assets/test_files/operator_text1.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/assets/test_files/operator_text1.ogg -------------------------------------------------------------------------------- /app/src/main/assets/test_files/operator_text2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/assets/test_files/operator_text2.ogg -------------------------------------------------------------------------------- /app/src/main/assets/test_files/operator_text3.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/assets/test_files/operator_text3.ogg -------------------------------------------------------------------------------- /app/src/main/assets/test_files/operator_text4.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/assets/test_files/operator_text4.ogg -------------------------------------------------------------------------------- /app/src/main/assets/test_files/test_image1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/assets/test_files/test_image1.jpg -------------------------------------------------------------------------------- /app/src/main/assets/test_files/test_image2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/assets/test_files/test_image2.jpg -------------------------------------------------------------------------------- /app/src/main/assets/test_files/test_image3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/assets/test_files/test_image3.jpg -------------------------------------------------------------------------------- /app/src/main/assets/test_files/test_image4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/assets/test_files/test_image4.jpg -------------------------------------------------------------------------------- /app/src/main/assets/test_files/test_image5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/assets/test_files/test_image5.jpg -------------------------------------------------------------------------------- /app/src/main/assets/test_files/test_image6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/assets/test_files/test_image6.jpg -------------------------------------------------------------------------------- /app/src/main/assets/test_files/test_pdf1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/assets/test_files/test_pdf1.pdf -------------------------------------------------------------------------------- /app/src/main/assets/test_files/test_pdf2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/assets/test_files/test_pdf2.pdf -------------------------------------------------------------------------------- /app/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /app/src/main/java/io/edna/threads/demo/appCode/activity/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.appCode.activity 2 | 3 | import android.os.Bundle 4 | import androidx.appcompat.app.AppCompatActivity 5 | import androidx.navigation.findNavController 6 | import androidx.navigation.ui.AppBarConfiguration 7 | import androidx.navigation.ui.navigateUp 8 | import io.edna.threads.demo.R 9 | import io.edna.threads.demo.databinding.ActivityMainBinding 10 | 11 | class MainActivity : AppCompatActivity() { 12 | 13 | private lateinit var appBarConfiguration: AppBarConfiguration 14 | private lateinit var binding: ActivityMainBinding 15 | 16 | override fun onCreate(savedInstanceState: Bundle?) { 17 | super.onCreate(savedInstanceState) 18 | binding = ActivityMainBinding.inflate(layoutInflater) 19 | setContentView(binding.root) 20 | } 21 | 22 | override fun onSupportNavigateUp(): Boolean { 23 | val navController = findNavController(R.id.nav_host_fragment_content_main) 24 | return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp() 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/io/edna/threads/demo/appCode/activity/SplashScreenActivity.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.appCode.activity 2 | 3 | import android.annotation.SuppressLint 4 | import android.content.Intent 5 | import android.os.Bundle 6 | import androidx.appcompat.app.AppCompatActivity 7 | 8 | @SuppressLint("CustomSplashScreen") 9 | class SplashScreenActivity : AppCompatActivity() { 10 | override fun onCreate(savedInstanceState: Bundle?) { 11 | super.onCreate(savedInstanceState) 12 | startActivity(Intent(this, MainActivity::class.java)) 13 | finish() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/java/io/edna/threads/demo/appCode/adapters/EccTouchHelperCallBack.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.appCode.adapters 2 | 3 | import android.content.Context 4 | import android.graphics.Canvas 5 | import androidx.core.content.ContextCompat 6 | import androidx.recyclerview.widget.ItemTouchHelper 7 | import androidx.recyclerview.widget.RecyclerView 8 | import io.edna.threads.demo.R 9 | import io.edna.threads.demo.appCode.extensions.isDarkThemeOn 10 | import io.edna.threads.demo.appCode.views.ItemDecorator 11 | 12 | class EccTouchHelperCallBack( 13 | private val context: Context, 14 | private val listener: ListItemClickListener, 15 | dragDirs: Int, 16 | swipeDirs: Int 17 | ) : ItemTouchHelper.SimpleCallback(dragDirs, swipeDirs) { 18 | 19 | override fun onMove( 20 | recyclerView: RecyclerView, 21 | viewHolder: RecyclerView.ViewHolder, 22 | target: RecyclerView.ViewHolder 23 | ): Boolean = false 24 | 25 | override fun onChildDraw( 26 | canvas: Canvas, 27 | recyclerView: RecyclerView, 28 | viewHolder: RecyclerView.ViewHolder, 29 | dX: Float, 30 | dY: Float, 31 | actionState: Int, 32 | isCurrentlyActive: Boolean 33 | ) { 34 | val defaultWhiteColor = if (context.isDarkThemeOn()) { 35 | ContextCompat.getColor(context, R.color.white_color_fa) 36 | } else { 37 | ContextCompat.getColor(context, R.color.black_color) 38 | } 39 | 40 | val teal200 = ContextCompat.getColor(context, R.color.blue_color) 41 | val colorAlert = ContextCompat.getColor(context, R.color.red_color) 42 | 43 | ItemDecorator.Builder(canvas, recyclerView, viewHolder, dX, actionState).set( 44 | backgroundColorFromStartToEnd = colorAlert, 45 | backgroundColorFromEndToStart = teal200, 46 | textFromStartToEnd = context.getString(R.string.remove), 47 | textFromEndToStart = context.getString(R.string.edit), 48 | textColorFromStartToEnd = defaultWhiteColor, 49 | textColorFromEndToStart = defaultWhiteColor, 50 | iconTintColorFromStartToEnd = defaultWhiteColor, 51 | iconTintColorFromEndToStart = defaultWhiteColor, 52 | iconResIdFromStartToEnd = R.drawable.ic_remove, 53 | iconResIdFromEndToStart = R.drawable.ic_edit 54 | ) 55 | 56 | super.onChildDraw( 57 | canvas, 58 | recyclerView, 59 | viewHolder, 60 | dX, 61 | dY, 62 | actionState, 63 | isCurrentlyActive 64 | ) 65 | } 66 | 67 | override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) { 68 | val position = viewHolder.adapterPosition 69 | when (direction) { 70 | ItemTouchHelper.LEFT -> { 71 | listener.onEditItem(position) 72 | } 73 | ItemTouchHelper.RIGHT -> { 74 | listener.onRemoveItem(position) 75 | } 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /app/src/main/java/io/edna/threads/demo/appCode/adapters/ListItemClickListener.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.appCode.adapters 2 | 3 | interface ListItemClickListener { 4 | fun onClick(position: Int) 5 | fun onEditItem(position: Int) 6 | fun onRemoveItem(position: Int) 7 | } 8 | -------------------------------------------------------------------------------- /app/src/main/java/io/edna/threads/demo/appCode/adapters/demoSamplesList/DemoSamplesAdapter.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.appCode.adapters.demoSamplesList 2 | 3 | import android.content.Context 4 | import android.view.LayoutInflater 5 | import android.view.ViewGroup 6 | import androidx.core.content.ContextCompat 7 | import androidx.recyclerview.widget.DiffUtil 8 | import androidx.recyclerview.widget.RecyclerView 9 | import io.edna.threads.demo.R 10 | import io.edna.threads.demo.appCode.business.UiThemeProvider 11 | import io.edna.threads.demo.appCode.business.ordinal 12 | import io.edna.threads.demo.appCode.models.DemoSamplesListItem 13 | import io.edna.threads.demo.appCode.models.DemoSamplesListItem.DIVIDER 14 | import io.edna.threads.demo.appCode.models.DemoSamplesListItem.TEXT 15 | import io.edna.threads.demo.appCode.models.DemoSamplesListItem.TITLE 16 | import io.edna.threads.demo.databinding.HolderDemoSamplesTextBinding 17 | import io.edna.threads.demo.databinding.HolderDemoSamplesTitleBinding 18 | import io.edna.threads.demo.databinding.HolderHorizontalLineBinding 19 | import org.koin.java.KoinJavaComponent.inject 20 | 21 | class DemoSamplesAdapter(private val onItemClickListener: SampleListItemOnClick) : RecyclerView.Adapter() { 22 | private val list: MutableList = mutableListOf() 23 | private val uiThemeProvider: UiThemeProvider by inject(UiThemeProvider::class.java) 24 | private lateinit var context: Context 25 | 26 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { 27 | val inflater = LayoutInflater.from(parent.context) 28 | context = parent.context 29 | return when (viewType) { 30 | DIVIDER.ordinal() -> { 31 | LineDividerHolder(HolderHorizontalLineBinding.inflate(inflater)) 32 | } 33 | TITLE.ordinal() -> { 34 | TitleHolder(HolderDemoSamplesTitleBinding.inflate(inflater)) 35 | } 36 | TEXT.ordinal() -> { 37 | TextHolder(HolderDemoSamplesTextBinding.inflate(inflater)) 38 | } 39 | else -> throw IllegalStateException() 40 | } 41 | } 42 | 43 | override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { 44 | (holder as? DemoSamplesHolder)?.onBind(position) 45 | } 46 | 47 | override fun getItemCount() = list.count() 48 | 49 | override fun getItemViewType(position: Int) = list[position].ordinal() 50 | 51 | fun addItems(newItems: List) { 52 | notifyDatasetChangedWithDiffUtil(newItems) 53 | } 54 | 55 | private fun notifyDatasetChangedWithDiffUtil(newList: List) { 56 | val diffResult = DiffUtil.calculateDiff(DemoSamplesDiffCallback(list, newList)) 57 | list.clear() 58 | list.addAll(newList) 59 | diffResult.dispatchUpdatesTo(this) 60 | } 61 | 62 | private inner class LineDividerHolder(binding: HolderHorizontalLineBinding) : 63 | RecyclerView.ViewHolder(binding.root), DemoSamplesHolder 64 | 65 | private inner class TitleHolder(val binding: HolderDemoSamplesTitleBinding) : 66 | RecyclerView.ViewHolder(binding.root), DemoSamplesHolder { 67 | 68 | override fun onBind(position: Int) { 69 | (list[position] as? TITLE)?.let { binding.titleTextView.text = it.text } 70 | } 71 | } 72 | 73 | private inner class TextHolder(val binding: HolderDemoSamplesTextBinding) : 74 | RecyclerView.ViewHolder(binding.root), DemoSamplesHolder { 75 | 76 | override fun onBind(position: Int) { 77 | (list[position] as? TEXT)?.let { item -> 78 | binding.textTextView.apply { 79 | text = item.text 80 | if (uiThemeProvider.isDarkThemeOn()) { 81 | setTextColor(ContextCompat.getColor(context, R.color.white_color_fa)) 82 | } else { 83 | setTextColor(ContextCompat.getColor(context, R.color.black_color)) 84 | } 85 | setOnClickListener { onItemClickListener.onClick(item) } 86 | } 87 | } 88 | } 89 | } 90 | 91 | private interface DemoSamplesHolder { 92 | fun onBind(position: Int) {} 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /app/src/main/java/io/edna/threads/demo/appCode/adapters/demoSamplesList/DemoSamplesDiffCallback.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.appCode.adapters.demoSamplesList 2 | 3 | import androidx.recyclerview.widget.DiffUtil 4 | import io.edna.threads.demo.appCode.models.DemoSamplesListItem 5 | 6 | class DemoSamplesDiffCallback( 7 | private val oldList: List, 8 | private val newList: List 9 | ) : DiffUtil.Callback() { 10 | 11 | override fun getOldListSize() = oldList.size 12 | 13 | override fun getNewListSize() = newList.size 14 | 15 | override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { 16 | return oldList[oldItemPosition] == newList[newItemPosition] 17 | } 18 | 19 | override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { 20 | val oldItem = oldList[oldItemPosition] 21 | val newItem = newList[newItemPosition] 22 | 23 | if (oldItem is DemoSamplesListItem.DIVIDER && newItem is DemoSamplesListItem.DIVIDER) { 24 | return true 25 | } 26 | 27 | return oldItem.toString() == newItem.toString() 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/io/edna/threads/demo/appCode/adapters/demoSamplesList/SampleListItemOnClick.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.appCode.adapters.demoSamplesList 2 | 3 | import io.edna.threads.demo.appCode.models.DemoSamplesListItem 4 | 5 | interface SampleListItemOnClick { 6 | fun onClick(item: DemoSamplesListItem) 7 | } 8 | -------------------------------------------------------------------------------- /app/src/main/java/io/edna/threads/demo/appCode/adapters/serverList/ServerListAdapter.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.appCode.adapters.serverList 2 | 3 | import android.view.LayoutInflater 4 | import android.view.ViewGroup 5 | import androidx.core.content.ContextCompat 6 | import androidx.recyclerview.widget.DiffUtil 7 | import androidx.recyclerview.widget.RecyclerView 8 | import im.threads.ui.utils.ColorsHelper 9 | import io.edna.threads.demo.R 10 | import io.edna.threads.demo.appCode.adapters.ListItemClickListener 11 | import io.edna.threads.demo.appCode.extensions.isDarkThemeOn 12 | import io.edna.threads.demo.appCode.models.ServerConfig 13 | import io.edna.threads.demo.databinding.ServerListItemBinding 14 | 15 | class ServerListAdapter(private val onItemClickListener: ListItemClickListener) : 16 | RecyclerView.Adapter() { 17 | 18 | private val list: MutableList = mutableListOf() 19 | 20 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ServerItemHolder { 21 | val inflater = LayoutInflater.from(parent.context) 22 | return ServerItemHolder(ServerListItemBinding.inflate(inflater)) 23 | } 24 | 25 | override fun onBindViewHolder(holder: ServerItemHolder, position: Int) { 26 | holder.onBind(position) 27 | } 28 | 29 | override fun getItemCount() = list.count() 30 | 31 | fun addItems(newItems: List) { 32 | notifyDatasetChangedWithDiffUtil(newItems) 33 | } 34 | 35 | fun getItem(position: Int): ServerConfig { 36 | return list[position] 37 | } 38 | 39 | private fun notifyDatasetChangedWithDiffUtil(newList: List) { 40 | val diffResult = DiffUtil.calculateDiff(ServerListDiffCallback(list, newList)) 41 | list.clear() 42 | list.addAll(newList) 43 | diffResult.dispatchUpdatesTo(this) 44 | } 45 | 46 | inner class ServerItemHolder(val binding: ServerListItemBinding) : 47 | RecyclerView.ViewHolder(binding.root) { 48 | 49 | fun onBind(position: Int) { 50 | (list[position] as? ServerConfig)?.let { item -> 51 | binding.name.text = item.name 52 | binding.description.text = item.serverBaseUrl 53 | if (binding.root.context.isDarkThemeOn()) { 54 | binding.name.setTextColor( 55 | ContextCompat.getColor( 56 | binding.root.context, 57 | R.color.white_color_fa 58 | ) 59 | ) 60 | binding.description.setTextColor( 61 | ContextCompat.getColor( 62 | binding.root.context, 63 | R.color.white_color_fa 64 | ) 65 | ) 66 | ColorsHelper.setTint( 67 | binding.root.context, 68 | binding.image, 69 | R.color.white_color_fa 70 | ) 71 | } else { 72 | binding.name.setTextColor( 73 | ContextCompat.getColor( 74 | binding.root.context, 75 | R.color.black_color 76 | ) 77 | ) 78 | binding.description.setTextColor( 79 | ContextCompat.getColor( 80 | binding.root.context, 81 | R.color.black_color 82 | ) 83 | ) 84 | ColorsHelper.setTint(binding.root.context, binding.image, R.color.black_color) 85 | } 86 | binding.rootLayout.setOnClickListener { onItemClickListener.onClick(position) } 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /app/src/main/java/io/edna/threads/demo/appCode/adapters/serverList/ServerListDiffCallback.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.appCode.adapters.serverList 2 | 3 | import androidx.recyclerview.widget.DiffUtil 4 | import io.edna.threads.demo.appCode.models.ServerConfig 5 | 6 | class ServerListDiffCallback( 7 | private val oldList: List, 8 | private val newList: List 9 | ) : DiffUtil.Callback() { 10 | 11 | override fun getOldListSize() = oldList.size 12 | 13 | override fun getNewListSize() = newList.size 14 | 15 | override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { 16 | return oldList[oldItemPosition] == newList[newItemPosition] 17 | } 18 | 19 | override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { 20 | val oldItem = oldList[oldItemPosition] 21 | val newItem = newList[newItemPosition] 22 | return oldItem == newItem 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/io/edna/threads/demo/appCode/adapters/userList/UserListAdapter.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.appCode.adapters.userList 2 | 3 | import android.view.LayoutInflater 4 | import android.view.ViewGroup 5 | import androidx.core.content.ContextCompat 6 | import androidx.recyclerview.widget.DiffUtil 7 | import androidx.recyclerview.widget.RecyclerView 8 | import im.threads.ui.utils.ColorsHelper 9 | import io.edna.threads.demo.R 10 | import io.edna.threads.demo.appCode.adapters.ListItemClickListener 11 | import io.edna.threads.demo.appCode.extensions.isDarkThemeOn 12 | import io.edna.threads.demo.appCode.models.UserInfo 13 | import io.edna.threads.demo.databinding.UserListItemBinding 14 | import java.lang.ref.WeakReference 15 | 16 | class UserListAdapter(private val onItemClickListener: WeakReference) : 17 | RecyclerView.Adapter() { 18 | 19 | private val list: MutableList = mutableListOf() 20 | 21 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserItemHolder { 22 | val inflater = LayoutInflater.from(parent.context) 23 | return UserItemHolder(UserListItemBinding.inflate(inflater)) 24 | } 25 | 26 | override fun onBindViewHolder(holder: UserItemHolder, position: Int) { 27 | holder.onBind(position) 28 | } 29 | 30 | override fun getItemCount() = list.count() 31 | 32 | fun addItems(newItems: List) { 33 | notifyDatasetChangedWithDiffUtil(newItems) 34 | } 35 | 36 | fun getItem(position: Int): UserInfo { 37 | return list[position] 38 | } 39 | 40 | private fun notifyDatasetChangedWithDiffUtil(newList: List) { 41 | val diffResult = DiffUtil.calculateDiff(UserListDiffCallback(list, newList)) 42 | list.clear() 43 | list.addAll(newList) 44 | diffResult.dispatchUpdatesTo(this) 45 | } 46 | 47 | inner class UserItemHolder(private val binding: UserListItemBinding) : 48 | RecyclerView.ViewHolder(binding.root) { 49 | 50 | fun onBind(position: Int) { 51 | (list[position] as? UserInfo)?.let { item -> 52 | binding.userId.text = item.userId 53 | if (binding.root.context.isDarkThemeOn()) { 54 | binding.userId.setTextColor(ContextCompat.getColor(binding.root.context, R.color.white_color_fa)) 55 | ColorsHelper.setTint(binding.root.context, binding.image, R.color.white_color_fa) 56 | } else { 57 | binding.userId.setTextColor(ContextCompat.getColor(binding.root.context, R.color.black_color)) 58 | ColorsHelper.setTint(binding.root.context, binding.image, R.color.black_color) 59 | } 60 | binding.rootLayout.setOnClickListener { onItemClickListener.get()?.onClick(position) } 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /app/src/main/java/io/edna/threads/demo/appCode/adapters/userList/UserListDiffCallback.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.appCode.adapters.userList 2 | 3 | import androidx.recyclerview.widget.DiffUtil 4 | import io.edna.threads.demo.appCode.models.UserInfo 5 | 6 | class UserListDiffCallback( 7 | private val oldList: List, 8 | private val newList: List 9 | ) : DiffUtil.Callback() { 10 | 11 | override fun getOldListSize() = oldList.size 12 | 13 | override fun getNewListSize() = newList.size 14 | 15 | override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { 16 | return oldList[oldItemPosition] == newList[newItemPosition] 17 | } 18 | 19 | override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { 20 | val oldItem = oldList[oldItemPosition] 21 | val newItem = newList[newItemPosition] 22 | return oldItem == newItem 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/io/edna/threads/demo/appCode/business/AfterTextChangedTextWatcher.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.appCode.business 2 | 3 | import android.text.TextWatcher 4 | 5 | interface AfterTextChangedTextWatcher : TextWatcher { 6 | override fun beforeTextChanged(str: CharSequence?, start: Int, count: Int, after: Int) {} 7 | override fun onTextChanged(str: CharSequence?, start: Int, before: Int, count: Int) {} 8 | } 9 | -------------------------------------------------------------------------------- /app/src/main/java/io/edna/threads/demo/appCode/business/Extensions.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.appCode.business 2 | 3 | inline fun T.ordinal(): Int { 4 | if (T::class.isSealed) { 5 | return T::class.java.classes.indexOfFirst { sub -> sub == javaClass } 6 | } 7 | val klass = if (T::class.isCompanion) { 8 | javaClass.declaringClass 9 | } else { 10 | javaClass 11 | } 12 | 13 | return klass.superclass?.classes?.indexOfFirst { it == klass } ?: -1 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/java/io/edna/threads/demo/appCode/business/KoinModules.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.appCode.business 2 | 3 | import io.edna.threads.demo.appCode.business.mockJsonProvider.CurrentJsonProvider 4 | import io.edna.threads.demo.appCode.business.mockJsonProvider.SamplesJsonProvider 5 | import io.edna.threads.demo.appCode.fragments.demoSamplesFragment.DemoSamplesViewModel 6 | import io.edna.threads.demo.appCode.fragments.demoSamplesList.DemoSamplesListViewModel 7 | import io.edna.threads.demo.appCode.fragments.server.AddServerViewModel 8 | import io.edna.threads.demo.appCode.fragments.server.ServerListViewModel 9 | import io.edna.threads.demo.appCode.fragments.user.AddUserViewModel 10 | import io.edna.threads.demo.appCode.fragments.user.UserListViewModel 11 | import io.edna.threads.demo.integrationCode.fragments.launch.LaunchViewModel 12 | import org.koin.androidx.viewmodel.dsl.viewModel 13 | import org.koin.dsl.module 14 | 15 | val appModule = module { 16 | single { CurrentJsonProvider(get()) } 17 | single { SamplesJsonProvider(get()) } 18 | single { StringsProvider(get()) } 19 | single { PreferencesProvider(get()) } 20 | single { UiThemeProvider(get()) } 21 | factory { ServersProvider(get(), get()) } 22 | viewModel { LaunchViewModel(get(), get(), get()) } 23 | viewModel { UserListViewModel(get()) } 24 | viewModel { AddUserViewModel(get()) } 25 | viewModel { ServerListViewModel(get(), get()) } 26 | viewModel { AddServerViewModel(get()) } 27 | viewModel { DemoSamplesViewModel(get(), get()) } 28 | viewModel { DemoSamplesListViewModel(get(), get(), get()) } 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/io/edna/threads/demo/appCode/business/ServersProvider.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.appCode.business 2 | 3 | import android.annotation.SuppressLint 4 | import android.content.Context 5 | import io.edna.threads.demo.appCode.models.ServerConfig 6 | import org.json.JSONObject 7 | import java.io.BufferedReader 8 | import java.io.InputStream 9 | 10 | class ServersProvider( 11 | private val context: Context, 12 | private val preferences: PreferencesProvider 13 | ) { 14 | @SuppressLint("DiscouragedApi") 15 | fun readServersFromFile(): ArrayList { 16 | val inputStream: InputStream = context.assets.open("servers_config.json") 17 | val content = StringBuilder() 18 | val reader = BufferedReader(inputStream.reader()) 19 | inputStream.use { stream -> 20 | kotlin.runCatching { 21 | var line = reader.readLine() 22 | while (line != null) { 23 | content.append(line.trim()) 24 | line = reader.readLine() 25 | } 26 | stream.close() 27 | } 28 | } 29 | val jsonArray = JSONObject(content.toString()).getJSONArray("servers") 30 | val servers = ArrayList(jsonArray.length()) 31 | for (i in 0 until jsonArray.length()) { 32 | val jsonObj = jsonArray.getJSONObject(i) 33 | servers.add( 34 | ServerConfig( 35 | name = jsonObj.getString("name"), 36 | threadsGateProviderUid = jsonObj.getString("threadsGateProviderUid"), 37 | datastoreUrl = jsonObj.getString("datastoreUrl"), 38 | serverBaseUrl = jsonObj.getString("serverBaseUrl"), 39 | threadsGateUrl = jsonObj.getString("threadsGateUrl"), 40 | trustedSSLCertificates = if (jsonObj.has("trustedSSLCertificates")) { 41 | val certificates = ArrayList() 42 | for (j in 0 until jsonObj.getJSONArray("trustedSSLCertificates").length()) { 43 | val certName = jsonObj.getJSONArray("trustedSSLCertificates").get(j).toString() 44 | val certId: Int = context.resources.getIdentifier(certName, "raw", context.packageName) 45 | if (certId > 0) { 46 | certificates.add(certId) 47 | } 48 | } 49 | certificates 50 | } else { 51 | null 52 | }, 53 | allowUntrustedSSLCertificate = if (jsonObj.has("allowUntrustedSSLCertificate")) { 54 | jsonObj.getBoolean("allowUntrustedSSLCertificate") 55 | } else { 56 | false 57 | } 58 | ) 59 | ) 60 | } 61 | return servers 62 | } 63 | 64 | fun saveServersToPreferences(servers: ArrayList) { 65 | preferences.saveServers(servers) 66 | } 67 | 68 | fun saveSelectedServer(server: ServerConfig) { 69 | preferences.saveSelectedServer(server) 70 | } 71 | 72 | fun getSelectedServer(): ServerConfig? { 73 | val selected = preferences.getSelectedServer() 74 | val servers = readServersFromFile() 75 | return if (selected != null && selected.isAllFieldsFilled()) { 76 | servers.find { it.name == selected.name } ?: selected 77 | } else { 78 | if (servers.isNotEmpty()) { 79 | servers[0] 80 | } else { 81 | null 82 | } 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /app/src/main/java/io/edna/threads/demo/appCode/business/SingleLiveEvent.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.appCode.business 2 | 3 | import androidx.annotation.MainThread 4 | import androidx.lifecycle.LifecycleOwner 5 | import androidx.lifecycle.MutableLiveData 6 | import androidx.lifecycle.Observer 7 | import im.threads.business.logger.LoggerEdna 8 | import java.util.concurrent.atomic.AtomicBoolean 9 | 10 | class SingleLiveEvent : MutableLiveData() { 11 | private val pending = AtomicBoolean(false) 12 | 13 | @MainThread 14 | override fun observe(owner: LifecycleOwner, observer: Observer) { 15 | if (hasActiveObservers()) { 16 | LoggerEdna.warning("Multiple observers registered but only one will be notified of changes.") 17 | } 18 | super.observe(owner) { t -> 19 | if (pending.compareAndSet(true, false)) { 20 | observer.onChanged(t) 21 | } 22 | } 23 | } 24 | 25 | @MainThread 26 | override fun setValue(t: T?) { 27 | pending.set(true) 28 | super.setValue(t) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/src/main/java/io/edna/threads/demo/appCode/business/StringsProvider.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.appCode.business 2 | 3 | import android.content.Context 4 | import io.edna.threads.demo.R 5 | 6 | class StringsProvider(context: Context) { 7 | val textMessages = context.getString(R.string.text_messages) 8 | val connectionErrors = context.getString(R.string.connection_errors) 9 | val voiceMessages = context.getString(R.string.voice_messages) 10 | val images = context.getString(R.string.images) 11 | val files = context.getString(R.string.files) 12 | val systemMessages = context.getString(R.string.system_messages) 13 | val chatWithBot = context.getString(R.string.chat_with_bot) 14 | val chatWithEditAndDeletedMessages = context.getString(R.string.deleted_and_edited_messages) 15 | val selectTheme = context.getString(R.string.select_theme) 16 | val defaultTheme = context.getString(R.string.default_theme) 17 | val darkTheme = context.getString(R.string.dark_theme) 18 | val lightTheme = context.getString(R.string.light_theme) 19 | val ok = context.getString(R.string.ok) 20 | val requiredField = context.getString(R.string.required_field) 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/io/edna/threads/demo/appCode/business/TouchHelper.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.appCode.business 2 | 3 | import android.graphics.Canvas 4 | import android.graphics.Color 5 | import android.graphics.drawable.ColorDrawable 6 | import androidx.recyclerview.widget.ItemTouchHelper 7 | import androidx.recyclerview.widget.RecyclerView 8 | import org.koin.java.KoinJavaComponent 9 | 10 | class TouchHelper(listener: OnSwipeItemListener) { 11 | 12 | private val uiThemeProvider: UiThemeProvider by KoinJavaComponent.inject(UiThemeProvider::class.java) 13 | 14 | var touchHelperCallback: ItemTouchHelper.SimpleCallback = 15 | object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.RIGHT) { 16 | 17 | private val background = if (uiThemeProvider.isDarkThemeOn()) { 18 | ColorDrawable(Color.BLACK) 19 | } else { 20 | ColorDrawable(Color.WHITE) 21 | } 22 | 23 | override fun onMove( 24 | recyclerView: RecyclerView, 25 | viewHolder: RecyclerView.ViewHolder, 26 | target: RecyclerView.ViewHolder 27 | ): Boolean { 28 | return false 29 | } 30 | 31 | override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) { 32 | listener.onSwiped(viewHolder.adapterPosition) 33 | } 34 | 35 | override fun onChildDraw( 36 | c: Canvas, 37 | recyclerView: RecyclerView, 38 | viewHolder: RecyclerView.ViewHolder, 39 | dX: Float, 40 | dY: Float, 41 | actionState: Int, 42 | isCurrentlyActive: Boolean 43 | ) { 44 | super.onChildDraw( 45 | c, 46 | recyclerView, 47 | viewHolder, 48 | dX, 49 | dY, 50 | actionState, 51 | isCurrentlyActive 52 | ) 53 | val itemView = viewHolder.itemView 54 | if (dX > 0) { 55 | background.setBounds( 56 | itemView.left, 57 | itemView.top, 58 | itemView.left + dX.toInt(), 59 | itemView.bottom 60 | ) 61 | } else if (dX < 0) { 62 | background.setBounds( 63 | itemView.right + dX.toInt(), 64 | itemView.top, 65 | itemView.right, 66 | itemView.bottom 67 | ) 68 | } else { 69 | background.setBounds(0, 0, 0, 0) 70 | } 71 | background.draw(c) 72 | } 73 | } 74 | 75 | interface OnSwipeItemListener { 76 | fun onSwiped(position: Int) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /app/src/main/java/io/edna/threads/demo/appCode/business/UiThemeProvider.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.appCode.business 2 | 3 | import android.content.Context 4 | import im.threads.ui.extensions.isDarkThemeOn 5 | 6 | class UiThemeProvider(private val context: Context) { 7 | fun isDarkThemeOn() = context.isDarkThemeOn() 8 | } 9 | -------------------------------------------------------------------------------- /app/src/main/java/io/edna/threads/demo/appCode/business/VolatileLiveData.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.appCode.business 2 | 3 | import androidx.annotation.MainThread 4 | import androidx.lifecycle.LifecycleOwner 5 | import androidx.lifecycle.MutableLiveData 6 | import androidx.lifecycle.Observer 7 | import java.util.concurrent.atomic.AtomicInteger 8 | 9 | open class VolatileLiveData : MutableLiveData() { 10 | private val lastValueSeq = AtomicInteger(0) 11 | private val wrappers = HashMap, Observer>() 12 | 13 | @MainThread 14 | override fun setValue(value: T) { 15 | lastValueSeq.incrementAndGet() 16 | super.setValue(value) 17 | } 18 | 19 | @MainThread 20 | override fun observe(owner: LifecycleOwner, observer: Observer) { 21 | val observerWrapper = ObserverWrapper(lastValueSeq, observer) 22 | wrappers[observer] = observerWrapper 23 | super.observe(owner, observerWrapper) 24 | } 25 | 26 | @MainThread 27 | override fun observeForever(observer: Observer) { 28 | val observerWrapper = ObserverWrapper(lastValueSeq, observer) 29 | wrappers[observer] = observerWrapper 30 | super.observeForever(observerWrapper) 31 | } 32 | 33 | @MainThread 34 | override fun removeObserver(observer: Observer) { 35 | val observerWrapper = wrappers[observer] 36 | observerWrapper?.let { 37 | wrappers.remove(observerWrapper) 38 | super.removeObserver(observerWrapper) 39 | } 40 | } 41 | } 42 | 43 | private class ObserverWrapper(private var currentSeq: AtomicInteger, private val observer: Observer) : Observer { 44 | private val initialSeq = currentSeq.get() 45 | private var _observer: Observer = Observer { 46 | if (currentSeq.get() != initialSeq) { 47 | _observer = observer 48 | _observer.onChanged(it) 49 | } 50 | } 51 | 52 | override fun onChanged(value: T) { 53 | _observer.onChanged(value) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /app/src/main/java/io/edna/threads/demo/appCode/business/mockJsonProvider/CurrentJsonProvider.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.appCode.business.mockJsonProvider 2 | 3 | import android.annotation.SuppressLint 4 | import android.content.Context 5 | 6 | class CurrentJsonProvider(private val context: Context) { 7 | private val preferences = context.getSharedPreferences("JsonPreferences", Context.MODE_PRIVATE) 8 | 9 | @SuppressLint("ApplySharedPref") 10 | fun saveCurrentJson(json: String) { 11 | preferences 12 | .edit() 13 | .putString(JSON_KEY, json) 14 | .commit() 15 | } 16 | 17 | fun getCurrentJson() = preferences.getString(JSON_KEY, "") ?: "" 18 | } 19 | 20 | private const val JSON_KEY = "JSON_KEY" 21 | -------------------------------------------------------------------------------- /app/src/main/java/io/edna/threads/demo/appCode/business/mockJsonProvider/SamplesJsonProvider.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.appCode.business.mockJsonProvider 2 | 3 | import android.content.Context 4 | import io.edna.threads.demo.R 5 | import java.io.BufferedReader 6 | import java.io.IOException 7 | import java.io.InputStream 8 | import java.io.InputStreamReader 9 | 10 | class SamplesJsonProvider(private val context: Context) { 11 | fun getTextChatJson() = readTextFileFromRawResourceId(R.raw.history_text_response) 12 | 13 | fun getConnectionErrorJson() = readTextFileFromRawResourceId(R.raw.history_errors_response) 14 | 15 | fun getVoicesChatJson() = readTextFileFromRawResourceId(R.raw.history_voice_response) 16 | 17 | fun getImagesChatJson() = readTextFileFromRawResourceId(R.raw.history_images_response) 18 | 19 | fun getFilesChatJson() = readTextFileFromRawResourceId(R.raw.history_files_response) 20 | 21 | fun getSystemChatJson() = readTextFileFromRawResourceId(R.raw.history_system_response) 22 | 23 | fun getChatBotJson() = readTextFileFromRawResourceId(R.raw.history_bot_response) 24 | 25 | fun getChatWithEditAndDeletedMessages() = readTextFileFromRawResourceId(R.raw.history_deleted_and_edited_messages) 26 | 27 | private fun readTextFileFromRawResourceId(resourceId: Int): String { 28 | var string: String? = "" 29 | val stringBuilder = StringBuilder() 30 | val inputStream: InputStream = context.resources.openRawResource(resourceId) 31 | val reader = BufferedReader(InputStreamReader(inputStream)) 32 | while (true) { 33 | try { 34 | if (reader.readLine().also { string = it } == null) break 35 | } catch (e: IOException) { 36 | e.printStackTrace() 37 | } 38 | stringBuilder.append(string).append("\n") 39 | } 40 | inputStream.close() 41 | return stringBuilder.toString() 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /app/src/main/java/io/edna/threads/demo/appCode/extensions/UIExtensions.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.appCode.extensions 2 | 3 | import android.content.Context 4 | import android.content.res.Configuration 5 | 6 | fun Context.isDarkThemeOn(): Boolean { 7 | return resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES 8 | } 9 | -------------------------------------------------------------------------------- /app/src/main/java/io/edna/threads/demo/appCode/fragments/demoSamplesFragment/DemoSamplesFragment.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.appCode.fragments.demoSamplesFragment 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import io.edna.threads.demo.R 6 | import io.edna.threads.demo.appCode.fragments.BaseAppFragment 7 | import io.edna.threads.demo.databinding.FragmentChatBinding 8 | import org.koin.androidx.viewmodel.ext.android.viewModel 9 | import java.lang.ref.WeakReference 10 | 11 | class DemoSamplesFragment : BaseAppFragment(FragmentChatBinding::inflate) { 12 | private val viewModel: DemoSamplesViewModel by viewModel() 13 | 14 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 15 | super.onViewCreated(view, savedInstanceState) 16 | subscribeToData() 17 | subscribeToGlobalBackClick() 18 | viewLifecycleOwner.lifecycle.addObserver(viewModel) 19 | } 20 | 21 | private fun subscribeToData() { 22 | viewModel.chatFragmentLiveData.observe(viewLifecycleOwner) { 23 | fragment = WeakReference(it) 24 | childFragmentManager 25 | .beginTransaction() 26 | .add(R.id.chatFragmentContainer, it) 27 | .commit() 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/src/main/java/io/edna/threads/demo/appCode/fragments/demoSamplesFragment/DemoSamplesViewModel.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.appCode.fragments.demoSamplesFragment 2 | 3 | import androidx.lifecycle.DefaultLifecycleObserver 4 | import androidx.lifecycle.LifecycleOwner 5 | import androidx.lifecycle.LiveData 6 | import androidx.lifecycle.ViewModel 7 | import im.threads.business.annotation.OpenWay 8 | import im.threads.ui.core.ThreadsLib 9 | import im.threads.ui.fragments.ChatFragment 10 | import io.edna.threads.demo.appCode.business.PreferencesProvider 11 | import io.edna.threads.demo.appCode.business.SingleLiveEvent 12 | import io.edna.threads.demo.appCode.business.mockJsonProvider.CurrentJsonProvider 13 | 14 | class DemoSamplesViewModel( 15 | private val jsonProvider: CurrentJsonProvider, 16 | private val preferencesProvider: PreferencesProvider 17 | ) : ViewModel(), DefaultLifecycleObserver { 18 | private val chatFragmentMutableLiveData: SingleLiveEvent = SingleLiveEvent() 19 | val chatFragmentLiveData: LiveData get() = chatFragmentMutableLiveData 20 | 21 | override fun onCreate(owner: LifecycleOwner) { 22 | super.onCreate(owner) 23 | setJsonMock() 24 | prepareFragment() 25 | } 26 | 27 | override fun onDestroy(owner: LifecycleOwner) { 28 | super.onDestroy(owner) 29 | preferencesProvider.cleanJsonOnPreferences() 30 | ThreadsLib.getInstance().logoutClient() 31 | } 32 | 33 | private fun setJsonMock() { 34 | val json = jsonProvider.getCurrentJson() 35 | preferencesProvider.putJsonToPreferences(json) 36 | } 37 | 38 | private fun prepareFragment() { 39 | chatFragmentMutableLiveData.value = ChatFragment.newInstance(OpenWay.DEFAULT) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/src/main/java/io/edna/threads/demo/appCode/fragments/demoSamplesList/DemoSamplesListFragment.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.appCode.fragments.demoSamplesList 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import androidx.core.content.ContextCompat 6 | import androidx.navigation.fragment.findNavController 7 | import im.threads.ui.core.ThreadsLib 8 | import io.edna.threads.demo.R 9 | import io.edna.threads.demo.appCode.adapters.demoSamplesList.DemoSamplesAdapter 10 | import io.edna.threads.demo.appCode.adapters.demoSamplesList.SampleListItemOnClick 11 | import io.edna.threads.demo.appCode.fragments.BaseAppFragment 12 | import io.edna.threads.demo.appCode.models.DemoSamplesListItem 13 | import io.edna.threads.demo.databinding.FragmentSamplesListBinding 14 | import org.koin.androidx.viewmodel.ext.android.viewModel 15 | import java.lang.ref.WeakReference 16 | 17 | class DemoSamplesListFragment : BaseAppFragment(FragmentSamplesListBinding::inflate), SampleListItemOnClick { 18 | private val viewModel: DemoSamplesListViewModel by viewModel() 19 | private var adapter: WeakReference? = null 20 | 21 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 22 | super.onViewCreated(view, savedInstanceState) 23 | createAdapter() 24 | setNavigationIcon() 25 | subscribeForData() 26 | subscribeToGlobalBackClick() 27 | viewLifecycleOwner.lifecycle.addObserver(viewModel) 28 | } 29 | 30 | override fun onClick(item: DemoSamplesListItem) { 31 | viewModel.onItemClick(item) 32 | } 33 | 34 | private fun createAdapter() = getBinding()?.apply { 35 | val newAdapter = DemoSamplesAdapter(this@DemoSamplesListFragment) 36 | adapter = WeakReference(newAdapter) 37 | recyclerView.adapter = newAdapter 38 | } 39 | 40 | private fun setNavigationIcon() = getBinding()?.apply { 41 | toolbar.navigationIcon?.setTint(ContextCompat.getColor(requireContext(), R.color.white_color_ec)) 42 | toolbar.setNavigationOnClickListener { 43 | ThreadsLib.getInstance().logoutClient() 44 | findNavController().navigateUp() 45 | } 46 | } 47 | 48 | private fun subscribeForData() { 49 | viewModel.demoSamplesLiveData.observe(viewLifecycleOwner) { adapter?.get()?.addItems(it) } 50 | viewModel.navigationLiveData.observe(viewLifecycleOwner) { findNavController().navigate(it) } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /app/src/main/java/io/edna/threads/demo/appCode/fragments/demoSamplesList/DemoSamplesListViewModel.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.appCode.fragments.demoSamplesList 2 | 3 | import androidx.lifecycle.DefaultLifecycleObserver 4 | import androidx.lifecycle.LifecycleOwner 5 | import androidx.lifecycle.LiveData 6 | import androidx.lifecycle.MutableLiveData 7 | import androidx.lifecycle.ViewModel 8 | import im.threads.business.UserInfoBuilder 9 | import im.threads.business.core.ContextHolder 10 | import im.threads.ui.config.ConfigBuilder 11 | import im.threads.ui.core.ThreadsLib 12 | import io.edna.threads.demo.R 13 | import io.edna.threads.demo.appCode.business.StringsProvider 14 | import io.edna.threads.demo.appCode.business.VolatileLiveData 15 | import io.edna.threads.demo.appCode.business.mockJsonProvider.CurrentJsonProvider 16 | import io.edna.threads.demo.appCode.business.mockJsonProvider.SamplesJsonProvider 17 | import io.edna.threads.demo.appCode.models.DemoSamplesListItem 18 | import io.edna.threads.demo.appCode.models.DemoSamplesListItem.TEXT 19 | import io.edna.threads.demo.appCode.models.ServerConfig 20 | import io.edna.threads.demo.appCode.themes.ChatThemes 21 | import io.edna.threads.demo.integrationCode.ednaMockThreadsGateProviderUid 22 | import io.edna.threads.demo.integrationCode.ednaMockThreadsGateUrl 23 | import io.edna.threads.demo.integrationCode.ednaMockUrl 24 | 25 | class DemoSamplesListViewModel( 26 | private val stringsProvider: StringsProvider, 27 | private val samplesJsonProvider: SamplesJsonProvider, 28 | private val currentJsonProvider: CurrentJsonProvider 29 | ) : ViewModel(), DefaultLifecycleObserver { 30 | private val mutableDemoSamplesLiveData = MutableLiveData>() 31 | val demoSamplesLiveData: LiveData> = mutableDemoSamplesLiveData 32 | val navigationLiveData = VolatileLiveData() 33 | 34 | override fun onCreate(owner: LifecycleOwner) { 35 | super.onCreate(owner) 36 | checkSdkInit() 37 | createData() 38 | } 39 | 40 | private fun checkSdkInit() { 41 | if (!ThreadsLib.isInitialized()) { 42 | val demoServerConfig = getDefaultServerConfig() 43 | val config = ConfigBuilder(ContextHolder.context).apply { 44 | threadsGateUrl(demoServerConfig.threadsGateUrl) 45 | datastoreUrl(demoServerConfig.datastoreUrl) 46 | serverBaseUrl(demoServerConfig.serverBaseUrl) 47 | threadsGateProviderUid(demoServerConfig.threadsGateProviderUid) 48 | } 49 | ThreadsLib.init(config) 50 | ThreadsLib.getInstance().apply { 51 | // Кастомизация внешнего вида. Поддержка темной темы 52 | val themes = ChatThemes() 53 | applyLightTheme(themes.getLightChatTheme()) 54 | applyDarkTheme(themes.getDarkChatTheme()) 55 | } 56 | } 57 | } 58 | 59 | fun onItemClick(item: DemoSamplesListItem) { 60 | if (item is TEXT) { 61 | currentJsonProvider.saveCurrentJson(item.json) 62 | ThreadsLib.getInstance().initUser(UserInfoBuilder("333")) 63 | navigationLiveData.setValue(R.id.action_DemoSamplesListFragment_to_DemoSamplesFragment) 64 | } 65 | } 66 | 67 | private fun createData() { 68 | mutableDemoSamplesLiveData.postValue( 69 | listOf( 70 | TEXT(stringsProvider.textMessages, samplesJsonProvider.getTextChatJson()), 71 | TEXT(stringsProvider.connectionErrors, samplesJsonProvider.getConnectionErrorJson()), 72 | TEXT(stringsProvider.voiceMessages, samplesJsonProvider.getVoicesChatJson()), 73 | TEXT(stringsProvider.images, samplesJsonProvider.getImagesChatJson()), 74 | TEXT(stringsProvider.files, samplesJsonProvider.getFilesChatJson()), 75 | TEXT(stringsProvider.systemMessages, samplesJsonProvider.getSystemChatJson()), 76 | TEXT(stringsProvider.chatWithBot, samplesJsonProvider.getChatBotJson()), 77 | TEXT(stringsProvider.chatWithEditAndDeletedMessages, samplesJsonProvider.getChatWithEditAndDeletedMessages()) 78 | ) 79 | ) 80 | } 81 | 82 | private fun getDefaultServerConfig() = ServerConfig( 83 | name = "TestServer", 84 | threadsGateProviderUid = ednaMockThreadsGateProviderUid, 85 | datastoreUrl = ednaMockUrl, 86 | serverBaseUrl = ednaMockUrl, 87 | threadsGateUrl = ednaMockThreadsGateUrl, 88 | isShowMenu = true 89 | ) 90 | } 91 | -------------------------------------------------------------------------------- /app/src/main/java/io/edna/threads/demo/appCode/fragments/server/AddServerFragment.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.appCode.fragments.server 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import androidx.fragment.app.setFragmentResult 6 | import io.edna.threads.demo.appCode.fragments.BaseAppFragment 7 | import io.edna.threads.demo.appCode.fragments.server.ServerListFragment.Companion.SERVER_CONFIG_KEY 8 | import io.edna.threads.demo.appCode.fragments.server.ServerListFragment.Companion.SRC_SERVER_NAME_KEY 9 | import io.edna.threads.demo.databinding.FragmentAddServerBinding 10 | import org.koin.androidx.viewmodel.ext.android.viewModel 11 | import org.parceler.Parcels 12 | 13 | class AddServerFragment : BaseAppFragment(FragmentAddServerBinding::inflate) { 14 | 15 | private val viewModel: AddServerViewModel by viewModel() 16 | 17 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 18 | super.onViewCreated(view, savedInstanceState) 19 | subscribeToGlobalBackClick() 20 | subscribeForTextWatchers() 21 | subscribeForData() 22 | setOnClickListeners() 23 | viewModel.initData(arguments) 24 | } 25 | 26 | private fun subscribeForTextWatchers() = getBinding()?.apply { 27 | name.setTextChangedListener(viewModel.nameTextWatcher) 28 | providerId.setTextChangedListener(viewModel.providerIdTextWatcher) 29 | baseUrl.setTextChangedListener(viewModel.baseUrlTextWatcher) 30 | datastoreUrl.setTextChangedListener(viewModel.datastoreUrlTextWatcher) 31 | threadsGateUrl.setTextChangedListener(viewModel.threadsGateUrlTextWatcher) 32 | } 33 | 34 | private fun subscribeForData() = getBinding()?.apply { 35 | viewModel.finalServerConfigLiveData.observe(viewLifecycleOwner) { 36 | val args = Bundle() 37 | args.putParcelable(SERVER_CONFIG_KEY, Parcels.wrap(it)) 38 | viewModel.srcServerConfig?.name?.let { name -> 39 | args.putString(SRC_SERVER_NAME_KEY, name) 40 | } 41 | setFragmentResult(SERVER_CONFIG_KEY, args) 42 | } 43 | viewModel.serverConfigLiveData.observe(viewLifecycleOwner) { 44 | name.text = it.name 45 | providerId.text = it.threadsGateProviderUid 46 | baseUrl.text = it.serverBaseUrl 47 | datastoreUrl.text = it.datastoreUrl 48 | threadsGateUrl.text = it.threadsGateUrl 49 | } 50 | viewModel.errorStringForServerNameFieldLiveData.observe(viewLifecycleOwner) { 51 | name.error = it 52 | } 53 | viewModel.errorStringForProviderIdFieldLiveData.observe(viewLifecycleOwner) { 54 | providerId.error = it 55 | } 56 | viewModel.errorStringForBaseUrlFieldLiveData.observe(viewLifecycleOwner) { 57 | baseUrl.error = it 58 | } 59 | viewModel.errorStringForDatastoreUrlFieldLiveData.observe(viewLifecycleOwner) { 60 | datastoreUrl.error = it 61 | } 62 | viewModel.errorStringForThreadsGateUrlFieldLiveData.observe(viewLifecycleOwner) { 63 | datastoreUrl.error = it 64 | } 65 | } 66 | 67 | private fun setOnClickListeners() = getBinding()?.apply { 68 | backButton.setOnClickListener { viewModel.click(backButton) } 69 | okButton.setOnClickListener { viewModel.click(okButton) } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /app/src/main/java/io/edna/threads/demo/appCode/fragments/user/AddUserFragment.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.appCode.fragments.user 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import androidx.fragment.app.setFragmentResult 6 | import io.edna.threads.demo.appCode.fragments.BaseAppFragment 7 | import io.edna.threads.demo.appCode.fragments.user.UserListFragment.Companion.SRC_USER_ID_KEY 8 | import io.edna.threads.demo.appCode.fragments.user.UserListFragment.Companion.USER_KEY 9 | import io.edna.threads.demo.databinding.FragmentAddUserBinding 10 | import org.koin.androidx.viewmodel.ext.android.viewModel 11 | import org.parceler.Parcels 12 | 13 | class AddUserFragment : BaseAppFragment(FragmentAddUserBinding::inflate) { 14 | 15 | private val viewModel: AddUserViewModel by viewModel() 16 | 17 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 18 | super.onViewCreated(view, savedInstanceState) 19 | subscribeToGlobalBackClick() 20 | subscribeForClickListeners() 21 | subscribeForTextWatchers() 22 | subscribeForData() 23 | initData() 24 | } 25 | 26 | private fun subscribeForTextWatchers() = getBinding()?.apply { 27 | userId.setTextChangedListener(viewModel.userIdTextWatcher) 28 | userData.setTextChangedListener(viewModel.userDataTextWatcher) 29 | appMarker.setTextChangedListener(viewModel.appMarkerTextWatcher) 30 | signature.setTextChangedListener(viewModel.signatureTextWatcher) 31 | authorizationHeader.setTextChangedListener(viewModel.authorizationHeaderTextWatcher) 32 | xAuthSchemaHeader.setTextChangedListener(viewModel.xAuthSchemaHeaderTextWatcher) 33 | } 34 | 35 | private fun subscribeForClickListeners() = getBinding()?.apply { 36 | backButton.setOnClickListener { viewModel.click(backButton) } 37 | okButton.setOnClickListener { viewModel.click(okButton) } 38 | } 39 | 40 | private fun subscribeForData() = getBinding()?.apply { 41 | viewModel.finalUserLiveData.observe(viewLifecycleOwner) { 42 | val args = Bundle() 43 | args.putParcelable(USER_KEY, Parcels.wrap(it)) 44 | viewModel.srcUser?.let { user -> 45 | args.putString(SRC_USER_ID_KEY, user.userId) 46 | } 47 | setFragmentResult(USER_KEY, args) 48 | } 49 | viewModel.userLiveData.observe(viewLifecycleOwner) { 50 | userId.text = it.userId 51 | userData.text = it.userData 52 | appMarker.text = it.appMarker 53 | signature.text = it.signature 54 | authorizationHeader.text = it.authorizationHeader 55 | xAuthSchemaHeader.text = it.xAuthSchemaHeader 56 | } 57 | viewModel.errorStringForUserIdFieldLiveData.observe(viewLifecycleOwner) { 58 | userId.error = it 59 | } 60 | } 61 | 62 | private fun initData() { 63 | viewModel.initData(arguments) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /app/src/main/java/io/edna/threads/demo/appCode/models/DemoSamplesListItem.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.appCode.models 2 | 3 | sealed class DemoSamplesListItem { 4 | object DIVIDER : DemoSamplesListItem() 5 | 6 | data class TITLE(val text: String) : DemoSamplesListItem() { 7 | override fun toString() = text 8 | companion object 9 | } 10 | 11 | data class TEXT(val text: String, val json: String) : DemoSamplesListItem() { 12 | override fun toString() = text 13 | companion object 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/java/io/edna/threads/demo/appCode/models/ServerConfig.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.appCode.models 2 | 3 | import android.os.Parcelable 4 | import kotlinx.parcelize.Parcelize 5 | 6 | @Parcelize 7 | data class ServerConfig( 8 | var name: String? = null, 9 | var threadsGateProviderUid: String? = null, 10 | var datastoreUrl: String? = null, 11 | var serverBaseUrl: String? = null, 12 | var threadsGateUrl: String? = null, 13 | var isFromApp: Boolean = false, 14 | var isShowMenu: Boolean = false, 15 | var filesAndMediaMenuItemEnabled: Boolean = true, 16 | var trustedSSLCertificates: List? = null, 17 | var allowUntrustedSSLCertificate: Boolean = false 18 | ) : Parcelable { 19 | 20 | override fun toString() = "Server config:\n = $name, " + 21 | "\nthreadsGateProviderUid = $threadsGateProviderUid, " + 22 | "\ndatastoreUrl = $datastoreUrl, " + 23 | "\nserverBaseUrl = $serverBaseUrl, " + 24 | "\nthreadsGateUrl = $threadsGateUrl, " + 25 | "\nisFromApp = $isFromApp, " + 26 | "\nisShowMenu = $isShowMenu, " + 27 | "\nfilesAndMediaMenuItemEnabled = $filesAndMediaMenuItemEnabled, " + 28 | "\ntrustedSSLCertificates = $trustedSSLCertificates, " + 29 | "\nallowUntrustedSSLCertificate = $allowUntrustedSSLCertificate" 30 | 31 | fun isAllFieldsFilled(): Boolean { 32 | return !name.isNullOrEmpty() && 33 | !threadsGateProviderUid.isNullOrEmpty() && 34 | !datastoreUrl.isNullOrEmpty() && 35 | !serverBaseUrl.isNullOrEmpty() && 36 | !threadsGateUrl.isNullOrEmpty() 37 | } 38 | 39 | fun copy(): ServerConfig { 40 | return ServerConfig( 41 | name, 42 | threadsGateProviderUid, 43 | datastoreUrl, 44 | serverBaseUrl, 45 | threadsGateUrl, 46 | isFromApp, 47 | isShowMenu, 48 | filesAndMediaMenuItemEnabled, 49 | trustedSSLCertificates, 50 | allowUntrustedSSLCertificate 51 | ) 52 | } 53 | 54 | override fun hashCode(): Int { 55 | return super.hashCode() 56 | } 57 | 58 | override fun equals(other: Any?): Boolean { 59 | if (other is ServerConfig) { 60 | return other.name == name && 61 | other.threadsGateProviderUid == threadsGateProviderUid && 62 | other.datastoreUrl == datastoreUrl && 63 | other.serverBaseUrl == serverBaseUrl && 64 | other.threadsGateUrl == threadsGateUrl 65 | } 66 | return false 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /app/src/main/java/io/edna/threads/demo/appCode/models/TestData.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.appCode.models 2 | 3 | import com.google.gson.Gson 4 | 5 | data class TestData( 6 | val userInfo: UserInfo? = null, 7 | val serverConfig: ServerConfig? = null 8 | ) { 9 | fun toJson() = Gson().toJson(this) 10 | 11 | companion object { 12 | fun fromJson(json: String) = Gson().fromJson(json, TestData::class.java) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/java/io/edna/threads/demo/appCode/models/UiTheme.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.appCode.models 2 | 3 | enum class UiTheme { 4 | LIGHT, DARK 5 | } 6 | -------------------------------------------------------------------------------- /app/src/main/java/io/edna/threads/demo/appCode/models/UserInfo.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.appCode.models 2 | 3 | import android.os.Parcelable 4 | import kotlinx.parcelize.Parcelize 5 | 6 | @Parcelize 7 | data class UserInfo( 8 | var userId: String? = null, 9 | var userData: String? = null, 10 | var appMarker: String? = null, 11 | var signature: String? = null, 12 | var authorizationHeader: String? = null, 13 | var xAuthSchemaHeader: String? = null, 14 | var userName: String? = null, 15 | var isShowMenu: Boolean = false 16 | ) : Parcelable { 17 | 18 | override fun toString() = "$userId," + 19 | "$userData," + 20 | "$appMarker," + 21 | "$signature," + 22 | "$authorizationHeader," + 23 | "$xAuthSchemaHeader," + 24 | "$userName, " + 25 | "$isShowMenu" 26 | 27 | fun isAllFieldsFilled(): Boolean { 28 | return !userId.isNullOrEmpty() 29 | } 30 | 31 | override fun hashCode(): Int { 32 | return super.hashCode() 33 | } 34 | 35 | override fun equals(other: Any?): Boolean { 36 | if (other is UserInfo) { 37 | return other.userId == userId && 38 | other.appMarker == appMarker && 39 | other.signature == signature && 40 | other.authorizationHeader == authorizationHeader && 41 | other.xAuthSchemaHeader == xAuthSchemaHeader && 42 | other.userData == userData && 43 | other.userName == userName 44 | } 45 | return false 46 | } 47 | 48 | fun clone(): UserInfo { 49 | return UserInfo( 50 | userId, 51 | userData, 52 | appMarker, 53 | signature, 54 | authorizationHeader, 55 | xAuthSchemaHeader, 56 | userName, 57 | isShowMenu 58 | ) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /app/src/main/java/io/edna/threads/demo/appCode/push/CustomPushFcmIntentService.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.appCode.push 2 | 3 | import com.google.firebase.messaging.FirebaseMessagingService 4 | import com.google.firebase.messaging.RemoteMessage 5 | import im.threads.ui.ChatCenterPushMessageHelper 6 | 7 | class CustomPushFcmIntentService : FirebaseMessagingService() { 8 | private val chatCenterPushMessageHelper = ChatCenterPushMessageHelper() 9 | 10 | override fun onNewToken(token: String) { 11 | super.onNewToken(token) 12 | chatCenterPushMessageHelper.setFcmToken(token) 13 | } 14 | 15 | override fun onMessageReceived(message: RemoteMessage) { 16 | super.onMessageReceived(message) 17 | chatCenterPushMessageHelper.process(message.data) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/io/edna/threads/demo/appCode/push/CustomPushHcmIntentService.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.appCode.push 2 | 3 | import android.annotation.SuppressLint 4 | import android.os.Bundle 5 | import android.util.Base64 6 | import com.huawei.hms.push.HmsMessageService 7 | import com.huawei.hms.push.RemoteMessage 8 | import im.threads.ui.ChatCenterPushMessageHelper 9 | import org.json.JSONException 10 | import org.json.JSONObject 11 | import java.io.UnsupportedEncodingException 12 | 13 | class CustomPushHcmIntentService : HmsMessageService() { 14 | private val chatCenterPushMessageHelper = ChatCenterPushMessageHelper() 15 | 16 | override fun onNewToken(token: String) { 17 | super.onNewToken(token) 18 | chatCenterPushMessageHelper.setHcmToken(token) 19 | } 20 | 21 | @SuppressLint("RestrictedApi") 22 | override fun onMessageReceived(message: RemoteMessage) { 23 | super.onMessageReceived(message) 24 | chatCenterPushMessageHelper.process(base64JsonStringToBundle(message.data)) 25 | } 26 | 27 | private fun base64JsonStringToBundle(base64Str: String): Bundle { 28 | return try { 29 | val data: ByteArray = Base64.decode(base64Str, 0) 30 | val decodedStr = String(data) 31 | jsonObjectToBundle(JSONObject(decodedStr)) 32 | } catch (exception: JSONException) { 33 | Bundle() 34 | } catch (exception: UnsupportedEncodingException) { 35 | Bundle() 36 | } 37 | } 38 | 39 | @Throws(JSONException::class) 40 | private fun jsonObjectToBundle(jsonObject: JSONObject): Bundle { 41 | val bundle = Bundle() 42 | val iterator: Iterator<*> = jsonObject.keys() 43 | while (iterator.hasNext()) { 44 | val key = iterator.next() as String 45 | val value = jsonObject.getString(key) 46 | bundle.putString(key, value) 47 | } 48 | return bundle 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/java/io/edna/threads/demo/appCode/push/HCMTokenRefresher.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.appCode.push 2 | 3 | import android.content.Context 4 | import com.huawei.agconnect.AGConnectOptionsBuilder 5 | import com.huawei.hms.aaid.HmsInstanceId 6 | import com.huawei.hms.common.ApiException 7 | import im.threads.ui.ChatCenterPushMessageHelper 8 | import java.io.IOException 9 | 10 | object HCMTokenRefresher { 11 | fun requestToken(context: Context) { 12 | val hcmAppId = AGConnectOptionsBuilder().build(context).getString("client/app_id") 13 | if (hcmAppId != null) { 14 | try { 15 | val hmsInstanceId = HmsInstanceId.getInstance(context) 16 | val token = hmsInstanceId.getToken(hcmAppId, "HCM") 17 | ChatCenterPushMessageHelper().setHcmToken(token) 18 | } catch (e: IOException) { 19 | } catch (e: ApiException) { 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/io/edna/threads/demo/appCode/test/TestChatActivity.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.appCode.test 2 | 3 | import android.content.Context 4 | import android.os.Bundle 5 | import androidx.appcompat.app.AppCompatActivity 6 | import im.threads.business.annotation.OpenWay 7 | import im.threads.ui.fragments.ChatFragment 8 | import io.edna.threads.demo.R 9 | 10 | class TestChatActivity : AppCompatActivity() { 11 | override fun onCreate(savedInstanceState: Bundle?) { 12 | super.onCreate(savedInstanceState) 13 | setContentView(R.layout.activity_test_chat) 14 | 15 | saveJsonMock(intent.extras?.getString(jsonMockExtraKey)) 16 | 17 | supportFragmentManager.beginTransaction() 18 | .replace(R.id.chatFragmentContainer, ChatFragment.newInstance(OpenWay.DEFAULT)) 19 | .commitNow() 20 | } 21 | 22 | override fun onStop() { 23 | saveJsonMock(null) 24 | super.onStop() 25 | } 26 | 27 | private fun saveJsonMock(jsonMock: String?) { 28 | applicationContext 29 | .getSharedPreferences(jsonMockPreferencesKey, Context.MODE_PRIVATE) 30 | .edit() 31 | .putString(jsonMockPreferencesValueKey, jsonMock) 32 | .commit() 33 | } 34 | 35 | companion object { 36 | const val jsonMockExtraKey = "jsonMockExtraKey" 37 | private const val jsonMockPreferencesKey = "ecc_demo_json_preference" 38 | private const val jsonMockPreferencesValueKey = "ecc_demo_json_preference_key" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/io/edna/threads/demo/integrationCode/EdnaThreadsApplication.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.integrationCode 2 | 3 | import android.app.Application 4 | import android.util.Log 5 | import com.google.firebase.messaging.FirebaseMessaging 6 | import com.google.firebase.perf.FirebasePerformance 7 | import com.microsoft.appcenter.AppCenter 8 | import com.microsoft.appcenter.analytics.Analytics 9 | import com.microsoft.appcenter.crashes.Crashes 10 | import im.threads.business.core.ContextHolder 11 | import im.threads.business.models.enums.ApiVersionEnum 12 | import im.threads.ui.ChatCenterPushMessageHelper 13 | import io.edna.threads.demo.BuildConfig 14 | import io.edna.threads.demo.appCode.business.PreferencesProvider 15 | import io.edna.threads.demo.appCode.business.ServersProvider 16 | import io.edna.threads.demo.appCode.business.appModule 17 | import io.edna.threads.demo.appCode.models.ServerConfig 18 | import io.edna.threads.demo.appCode.push.HCMTokenRefresher 19 | import kotlinx.coroutines.CoroutineScope 20 | import kotlinx.coroutines.Dispatchers 21 | import org.koin.android.ext.android.inject 22 | import org.koin.android.ext.koin.androidContext 23 | import org.koin.core.context.startKoin 24 | 25 | class EdnaThreadsApplication : Application() { 26 | private val serversProvider: ServersProvider by inject() 27 | private val preferences: PreferencesProvider by inject() 28 | private val coroutineScope = CoroutineScope(Dispatchers.IO) 29 | private val asyncInit = false 30 | 31 | override fun onCreate() { 32 | super.onCreate() 33 | 34 | startAppCenter() 35 | 36 | startKoin { 37 | androidContext(this@EdnaThreadsApplication) 38 | modules(appModule) 39 | } 40 | 41 | val sdkInitializer = ThreadsLibInitializer() 42 | val apiVersion = ApiVersionEnum.createApiVersionEnum(preferences.getSelectedApiVersion()) 43 | if (!BuildConfig.IS_MOCK_WEB_SERVER.get()) { 44 | // Инициализация сдк обычным способом 45 | serversProvider.getSelectedServer()?.let { 46 | sdkInitializer.initThreadsLib(this.applicationContext, apiVersion, it) 47 | } ?: Log.i("Init sdk", "No server") 48 | } else { 49 | // Инициализация сдк моком сервера для UI тестов 50 | val serverConfig = ServerConfig( 51 | "Mock Server", 52 | ednaMockThreadsGateProviderUid, 53 | ednaMockUrl, 54 | ednaMockUrl, 55 | ednaMockThreadsGateUrl, 56 | allowUntrustedSSLCertificate = ednaMockAllowUntrustedSSLCertificate 57 | ) 58 | sdkInitializer.initThreadsLib(this.applicationContext, apiVersion, serverConfig) 59 | } 60 | checkAndUpdateTokens() 61 | } 62 | 63 | private fun checkAndUpdateTokens() { 64 | FirebaseMessaging.getInstance().token.addOnCompleteListener { task -> 65 | if (task.isSuccessful) { 66 | val token = task.result 67 | ChatCenterPushMessageHelper().setFcmToken(token) 68 | } 69 | } 70 | HCMTokenRefresher.requestToken(ContextHolder.context) 71 | } 72 | 73 | private fun startAppCenter() { 74 | if (BuildConfig.DEBUG.not()) { 75 | System.getenv("APP_CENTER_KEY")?.let { appCenterKey -> 76 | AppCenter.start( 77 | this, 78 | appCenterKey, 79 | Analytics::class.java, 80 | Crashes::class.java 81 | ) 82 | } 83 | } 84 | } 85 | } 86 | 87 | private const val LATO_BOLD_FONT_PATH = "fonts/lato-bold.ttf" 88 | private const val LATO_LIGHT_FONT_PATH = "fonts/lato-light.ttf" 89 | private const val LATO_REGULAR_FONT_PATH = "fonts/lato-regular.ttf" 90 | 91 | const val ednaMockScheme = "http" 92 | const val ednaMockHost = "localhost" 93 | const val ednaMockPort = 8080 94 | const val ednaMockUrl = "$ednaMockScheme://$ednaMockHost:$ednaMockPort/" 95 | const val ednaMockThreadsGateUrl = "ws://$ednaMockHost:$ednaMockPort/gate/socket" 96 | const val ednaMockThreadsGateProviderUid = "TEST_93jLrtnipZsfbTddRfEfbyfEe5LKKhTl" 97 | const val ednaMockAllowUntrustedSSLCertificate = true 98 | -------------------------------------------------------------------------------- /app/src/main/java/io/edna/threads/demo/integrationCode/ThreadsLibInitializer.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.integrationCode 2 | 3 | import android.content.Context 4 | import android.content.Intent 5 | import im.threads.business.core.UnreadMessagesCountListener 6 | import im.threads.business.logger.LoggerConfig 7 | import im.threads.business.logger.LoggerRetentionPolicy 8 | import im.threads.business.models.enums.ApiVersionEnum 9 | import im.threads.ui.config.ConfigBuilder 10 | import im.threads.ui.core.ThreadsLib 11 | import io.edna.threads.demo.appCode.models.ServerConfig 12 | import io.edna.threads.demo.appCode.themes.ChatThemes 13 | import io.edna.threads.demo.integrationCode.fragments.launch.LaunchFragment.Companion.APP_UNREAD_COUNT_BROADCAST 14 | import io.edna.threads.demo.integrationCode.fragments.launch.LaunchFragment.Companion.UNREAD_COUNT_KEY 15 | import java.io.File 16 | 17 | class ThreadsLibInitializer() { 18 | 19 | fun initThreadsLib(context: Context, apiVersionEnum: ApiVersionEnum, server: ServerConfig) { 20 | // Устанавливает конфиг для внутреннего логгера. Необязательный параметр 21 | val loggerConfig = LoggerConfig.Builder(context) 22 | .logToFile() 23 | .dir(File(context.filesDir, "logs")) 24 | .retentionPolicy(LoggerRetentionPolicy.TOTAL_SIZE) 25 | .maxTotalSize(5242880) 26 | .build() 27 | 28 | // Устанавливает общий конфиг для библиотеки. Обязательный параметр только context 29 | val configBuilder = ConfigBuilder(context) 30 | .unreadMessagesCountListener(object : UnreadMessagesCountListener { 31 | override fun onUnreadMessagesCountChanged(count: Int) { 32 | val intent = Intent(APP_UNREAD_COUNT_BROADCAST) 33 | intent.putExtra(UNREAD_COUNT_KEY, count) 34 | context.sendBroadcast(intent) 35 | } 36 | }) 37 | .surveyCompletionDelay(2000) 38 | .historyLoadingCount(50) 39 | .isDebugLoggingEnabled(true) 40 | .showAttachmentsButton() 41 | .enableLogging(loggerConfig) 42 | 43 | // Устанавливаем параметры подключения к серверу 44 | configBuilder.apply { 45 | serverBaseUrl(server.serverBaseUrl) 46 | datastoreUrl(server.datastoreUrl) 47 | threadsGateUrl(server.threadsGateUrl) 48 | threadsGateProviderUid(server.threadsGateProviderUid) 49 | trustedSSLCertificates(server.trustedSSLCertificates) 50 | allowUntrustedSSLCertificates(server.allowUntrustedSSLCertificate) 51 | setNewChatCenterApi() 52 | setApiVersion(apiVersionEnum) 53 | } 54 | 55 | // Инициализация библиотеки. Только после данного вызова можно начинать работу с SDK 56 | ThreadsLib.init(configBuilder) 57 | ThreadsLib.getInstance().apply { 58 | // Кастомизация внешнего вида. Поддержка темной темы 59 | val themes = ChatThemes() 60 | applyLightTheme(themes.getLightChatTheme()) 61 | applyDarkTheme(themes.getDarkChatTheme()) 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /app/src/main/java/io/edna/threads/demo/integrationCode/fragments/chatFragment/ChatAppFragment.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo.integrationCode.fragments.chatFragment 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import im.threads.business.annotation.OpenWay 6 | import im.threads.ui.fragments.ChatFragment 7 | import io.edna.threads.demo.R 8 | import io.edna.threads.demo.appCode.fragments.BaseAppFragment 9 | import io.edna.threads.demo.databinding.FragmentChatBinding 10 | import java.lang.ref.WeakReference 11 | 12 | class ChatAppFragment : BaseAppFragment(FragmentChatBinding::inflate) { 13 | 14 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 15 | super.onViewCreated(view, savedInstanceState) 16 | subscribeToGlobalBackClick() 17 | ChatFragment.newInstance(OpenWay.DEFAULT).let { 18 | fragment = WeakReference(it) 19 | childFragmentManager 20 | .beginTransaction() 21 | .add(R.id.chatFragmentContainer, it) 22 | .commit() 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/alt_thread_incoming_bubble.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/res/drawable-hdpi/alt_thread_incoming_bubble.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/alt_thread_outgoing_bubble.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/res/drawable-hdpi/alt_thread_outgoing_bubble.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/res/drawable-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/res/drawable-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/res/drawable-hdpi/logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/alt_thread_incoming_bubble.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/res/drawable-mdpi/alt_thread_incoming_bubble.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/alt_thread_outgoing_bubble.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/res/drawable-mdpi/alt_thread_outgoing_bubble.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/res/drawable-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/res/drawable-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/res/drawable-mdpi/logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-night/splash_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 11 | 12 | 13 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/alt_thread_incoming_bubble.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/res/drawable-xhdpi/alt_thread_incoming_bubble.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/alt_thread_outgoing_bubble.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/res/drawable-xhdpi/alt_thread_outgoing_bubble.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/res/drawable-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/res/drawable-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/im_empty_users.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/res/drawable-xhdpi/im_empty_users.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/res/drawable-xhdpi/logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/alt_thread_incoming_bubble.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/res/drawable-xxhdpi/alt_thread_incoming_bubble.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/alt_thread_outgoing_bubble.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/res/drawable-xxhdpi/alt_thread_outgoing_bubble.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/res/drawable-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/res/drawable-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/res/drawable-xxhdpi/logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/alt_thread_incoming_image_mask.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/res/drawable/alt_thread_incoming_image_mask.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/alt_thread_outgoing_image_mask.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/res/drawable/alt_thread_outgoing_image_mask.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/alt_threads_scroll_down_icon_black.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/alt_threads_scroll_down_icon_light.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/app_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/res/drawable/app_logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/buttons_bg_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 8 | 10 | 11 | 12 | 13 | 14 | 16 | 18 | 19 | 20 | 21 | 22 | 24 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/buttons_bg_selector_dark.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 8 | 10 | 11 | 12 | 13 | 14 | 16 | 18 | 19 | 20 | 21 | 22 | 24 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/buttons_text_color_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/dark_theme.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_back.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_chevron_right.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_cloud.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_edit.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_ok.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_ok_desable.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_ok_pressed.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_ok_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_plus.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_remove.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_settings.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_user.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/light_theme.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/red_cyrcle.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 9 | 10 | 13 | 14 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/secondary_buttons_bg_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 8 | 10 | 11 | 12 | 13 | 14 | 16 | 18 | 19 | 20 | 21 | 22 | 24 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/secondary_buttons_text_color_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/splash_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/text_button_bg_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 8 | 10 | 11 | 12 | 13 | 14 | 16 | 18 | 19 | 20 | 21 | 22 | 24 | 26 | 27 | 28 | 29 | 30 | 32 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/text_button_text_color_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/font/roboto_medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/res/font/roboto_medium.ttf -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 14 | 15 | 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_test_chat.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_chat.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_samples_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 19 | 20 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_server_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 19 | 20 | 30 | 31 | 42 | 43 | 44 | 45 | 54 | 55 | 67 | 68 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_user_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 17 | 18 | 28 | 29 | 40 | 41 | 42 | 43 | 52 | 53 | 64 | 65 | 69 | 70 | 80 | 81 | 82 | 83 | 95 | 96 | -------------------------------------------------------------------------------- /app/src/main/res/layout/holder_demo_samples_text.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/layout/holder_demo_samples_title.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/layout/holder_horizontal_line.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/layout/input_field.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | 28 | 29 | 38 | 39 | 40 | 41 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /app/src/main/res/layout/server_list_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 12 | 13 | 17 | 18 | 29 | 30 | 42 | 43 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /app/src/main/res/layout/user_list_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 12 | 13 | 17 | 18 | 31 | 32 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/raw/snapshot_test_errors_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "messages": [ 3 | { 4 | "type": "MESSAGE", 5 | "threadId": 184, 6 | "providerIds": [], 7 | "uuid": "b2eb39ef-b9f8-4f70-aaba-e89873ae1f25", 8 | "text": "Добрый день, возник вопрос", 9 | "receivedDate": "2023-04-06T08:06:57.870Z", 10 | "attachments": [], 11 | "quotes": [], 12 | "quickReplies": [], 13 | "read": true, 14 | "settings": { 15 | "blockInput": false, 16 | "masked": false 17 | }, 18 | "authorized": true, 19 | "massPushMessage": false, 20 | "origin": "threads", 21 | "errorMock": true 22 | }, 23 | { 24 | "type": "MESSAGE", 25 | "threadId": 184, 26 | "providerIds": [], 27 | "uuid": "75098c02-84f5-494a-8c9c-658f1487cc9e", 28 | "operator": { 29 | "id": 4, 30 | "name": "Оператор Елена", 31 | "alias": null, 32 | "status": null, 33 | "photoUrl": null, 34 | "gender": "FEMALE", 35 | "organizationUnit": "Skill Credit", 36 | "role": "OPERATOR" 37 | }, 38 | "text": "", 39 | "receivedDate": "2023-04-06T08:10:06.944Z", 40 | "attachments": [ 41 | { 42 | "id": 79, 43 | "result": "https://mobile4.dev.flex.mfms.ru/files/20230406-f46def28-5b72-41f9-864b-cf87e899ec28.pdf", 44 | "optional": { 45 | "size": 911839, 46 | "name": "89,93-.pdf", 47 | "type": "application/pdf" 48 | }, 49 | "state": "ERROR", 50 | "name": "89,93-.pdf", 51 | "type": "application/pdf", 52 | "size": 911839, 53 | "wellDefined": true, 54 | "url": "https://mobile4.dev.flex.mfms.ru/files/20230406-f46def28-5b72-41f9-864b-cf87e899ec28.pdf" 55 | } 56 | ], 57 | "quotes": [], 58 | "quickReplies": [], 59 | "read": true, 60 | "settings": { 61 | "blockInput": false, 62 | "masked": false 63 | }, 64 | "authorized": true, 65 | "massPushMessage": false, 66 | "origin": "threads" 67 | }, 68 | { 69 | "type": "MESSAGE", 70 | "threadId": 184, 71 | "providerIds": [], 72 | "uuid": "c1ffdbcf-4ac9-49c9-b941-4b759a414a42", 73 | "text": "", 74 | "receivedDate": "2023-04-06T08:10:24.552Z", 75 | "attachments": [ 76 | { 77 | "id": 80, 78 | "result": "https://mobile4.dev.flex.mfms.ru/files/20230406-fda68dc5-fff1-417d-a1b6-c301da1934c2.pdf", 79 | "optional": { 80 | "size": 61978, 81 | "name": "testpdf1.pdf", 82 | "type": "application/pdf" 83 | }, 84 | "state": "ERROR", 85 | "name": "testpdf1.pdf", 86 | "type": "application/pdf", 87 | "size": 61978, 88 | "wellDefined": true, 89 | "url": "https://mobile4.dev.flex.mfms.ru/files/20230406-fda68dc5-fff1-417d-a1b6-c301da1934c2.pdf" 90 | } 91 | ], 92 | "quotes": [], 93 | "quickReplies": [], 94 | "read": true, 95 | "settings": { 96 | "blockInput": false, 97 | "masked": false 98 | }, 99 | "authorized": true, 100 | "massPushMessage": false, 101 | "origin": "threads" 102 | } 103 | ], 104 | "agentInfo": { 105 | "action": "AGENT_JOINED", 106 | "actionDate": "2023-04-06T08:55:22.118Z", 107 | "thread": { 108 | "id": 184, 109 | "state": "IN_PROGRESS" 110 | }, 111 | "agent": { 112 | "id": 4, 113 | "name": "Оператор Елена", 114 | "alias": null, 115 | "status": null, 116 | "photoUrl": null, 117 | "gender": "FEMALE", 118 | "organizationUnit": "Skill Credit", 119 | "role": "OPERATOR" 120 | } 121 | }, 122 | "allowedToSendMessages": true 123 | } -------------------------------------------------------------------------------- /app/src/main/res/raw/snapshot_test_history_bot_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "messages": [ 3 | { 4 | "type": "MESSAGE", 5 | "threadId": 304, 6 | "providerIds": [], 7 | "uuid": "eda36c57-7286-4360-bb42-0cf3d7932113", 8 | "operator": { 9 | "id": 9, 10 | "name": "Бот Edna", 11 | "alias": null, 12 | "status": null, 13 | "photoUrl": null, 14 | "gender": "FEMALE", 15 | "organizationUnit": "Skill Debet", 16 | "role": "OPERATOR" 17 | }, 18 | "text": "", 19 | "formattedText": "Добрый день! Я **Бот Edna**, и я готова Вам помочь! Вижу, у Вас проблемы с зачислением средств. Читали ли вы *наиболее частые причины* по [этой ссылке](https://edna.ru/)?", 20 | "receivedDate": "2023-03-22T08:42:44.275Z", 21 | "attachments": [], 22 | "quotes": [], 23 | "quickReplies": [], 24 | "read": true, 25 | "settings": { 26 | "blockInput": false, 27 | "masked": false 28 | }, 29 | "authorized": true, 30 | "massPushMessage": false, 31 | "origin": "threads" 32 | }, 33 | { 34 | "type": "MESSAGE", 35 | "clientId": "8766", 36 | "threadId": 482, 37 | "providerIds": [], 38 | "uuid": "a6b9dc67-c9f6-4971-94a5-a4c4904b15db", 39 | "operator": { 40 | "id": 13, 41 | "name": "Бот Edna", 42 | "alias": null, 43 | "status": null, 44 | "photoUrl": null, 45 | "gender": null, 46 | "organizationUnit": null, 47 | "role": "EXTERNAL_BOT" 48 | }, 49 | "text": "Когда вы оформили карту? Давайте выберем один из вариантов", 50 | "receivedDate": "2023-03-22T08:44:19.497Z", 51 | "attachments": [], 52 | "quotes": [], 53 | "quickReplies": [ 54 | { 55 | "id": 4858, 56 | "type": "TEXT", 57 | "text": "У меня нет карты банка", 58 | "imageUrl": null, 59 | "url": null, 60 | "shown_text": null, 61 | "callback_data": null, 62 | "payload": null 63 | }, 64 | { 65 | "id": 4859, 66 | "type": "TEXT", 67 | "text": "Моя карта просрочена", 68 | "imageUrl": null, 69 | "url": null, 70 | "shown_text": null, 71 | "callback_data": null, 72 | "payload": null 73 | }, 74 | { 75 | "id": 4860, 76 | "type": "TEXT", 77 | "text": "Моя карта оформлена в этом году", 78 | "imageUrl": null, 79 | "url": null, 80 | "shown_text": null, 81 | "callback_data": null, 82 | "payload": null 83 | }, 84 | { 85 | "id": 4861, 86 | "type": "TEXT", 87 | "text": "Моя карта оформлена в прошлом году", 88 | "imageUrl": null, 89 | "url": null, 90 | "shown_text": null, 91 | "callback_data": null, 92 | "payload": null 93 | }, 94 | { 95 | "id": 4862, 96 | "type": "TEXT", 97 | "text": "Нет нужного варианта", 98 | "imageUrl": null, 99 | "url": null, 100 | "shown_text": null, 101 | "callback_data": null, 102 | "payload": null 103 | } 104 | ], 105 | "read": false, 106 | "settings": { 107 | "blockInput": false, 108 | "masked": false 109 | }, 110 | "authorized": true, 111 | "massPushMessage": false, 112 | "externalClientId": "8766", 113 | "origin": "threads" 114 | } 115 | ], 116 | "agentInfo": { 117 | "action": "AGENT_LOOKUP", 118 | "actionDate": "2023-03-22T08:51:43.124Z", 119 | "thread": { 120 | "id": 304, 121 | "state": "UNASSIGNED" 122 | }, 123 | "agent": null 124 | }, 125 | "allowedToSendMessages": true 126 | } -------------------------------------------------------------------------------- /app/src/main/res/raw/snapshot_test_history_images_response_1.json: -------------------------------------------------------------------------------- 1 | { 2 | "messages": [ 3 | { 4 | "type": "MESSAGE", 5 | "threadId": 141, 6 | "providerIds": [], 7 | "uuid": "061f6c38-2ac6-4cf9-aa26-ead6b055bc12", 8 | "text": "", 9 | "receivedDate": "2023-03-27T10:05:25.953Z", 10 | "attachments": [ 11 | { 12 | "id": 45, 13 | "result": "file:///android_asset/test_images/test_image6.jpg", 14 | "optional": { 15 | "size": 3457653, 16 | "name": "test_image6.jpg", 17 | "type": "image/jpeg" 18 | }, 19 | "state": "READY", 20 | "name": "test_image6.jpg", 21 | "type": "image/jpeg", 22 | "size": 3457653, 23 | "wellDefined": true, 24 | "url": "file:///android_asset/test_images/test_image6.jpg" 25 | } 26 | ], 27 | "quotes": [], 28 | "quickReplies": [], 29 | "read": true, 30 | "settings": { 31 | "blockInput": false, 32 | "masked": false 33 | }, 34 | "authorized": true, 35 | "massPushMessage": false, 36 | "origin": "threads" 37 | }, 38 | { 39 | "type": "MESSAGE", 40 | "threadId": 141, 41 | "providerIds": [], 42 | "uuid": "65652fe7-b99e-4250-8328-4a8c3505eb0b", 43 | "operator": { 44 | "id": 4, 45 | "name": "Оператор Елена", 46 | "alias": null, 47 | "status": null, 48 | "photoUrl": null, 49 | "gender": "FEMALE", 50 | "organizationUnit": "Skill Credit", 51 | "role": "OPERATOR" 52 | }, 53 | "text": "Добрый день!", 54 | "receivedDate": "2023-03-27T10:06:13.698Z", 55 | "attachments": [], 56 | "quotes": [ 57 | { 58 | "type": "MESSAGE", 59 | "threadId": 141, 60 | "providerIds": [], 61 | "uuid": "061f6c38-2ac6-4cf9-aa26-ead6b055bc12", 62 | "text": "", 63 | "receivedDate": "2023-03-27T10:05:25.953Z", 64 | "attachments": [ 65 | { 66 | "id": 45, 67 | "result": "file:///android_asset/test_images/test_image4.jpg", 68 | "optional": { 69 | "size": 3457653, 70 | "name": "test_image4.jpg", 71 | "type": "image/jpeg" 72 | }, 73 | "state": "READY", 74 | "name": "test_image4.jpg", 75 | "type": "image/jpeg", 76 | "size": 3457653, 77 | "wellDefined": true, 78 | "url": "file:///android_asset/test_images/test_image4.jpg" 79 | } 80 | ], 81 | "quotes": [], 82 | "quickReplies": [], 83 | "read": true, 84 | "settings": { 85 | "blockInput": false, 86 | "masked": false 87 | }, 88 | "authorized": true, 89 | "massPushMessage": false, 90 | "origin": "threads" 91 | } 92 | ], 93 | "quickReplies": [], 94 | "read": true, 95 | "settings": { 96 | "blockInput": false, 97 | "masked": false 98 | }, 99 | "authorized": true, 100 | "massPushMessage": false, 101 | "origin": "threads" 102 | } 103 | ], 104 | "agentInfo": { 105 | "action": "AGENT_JOINED", 106 | "actionDate": "2023-03-27T10:24:51.591Z", 107 | "thread": { 108 | "id": 142, 109 | "state": "WAITING" 110 | }, 111 | "agent": { 112 | "id": 4, 113 | "name": "Оператор Елена", 114 | "alias": null, 115 | "status": null, 116 | "photoUrl": null, 117 | "gender": "FEMALE", 118 | "organizationUnit": "Skill Credit", 119 | "role": "OPERATOR" 120 | } 121 | }, 122 | "allowedToSendMessages": true 123 | } -------------------------------------------------------------------------------- /app/src/main/res/raw/snapshot_test_history_images_response_2.json: -------------------------------------------------------------------------------- 1 | { 2 | "messages": [ 3 | { 4 | "type": "MESSAGE", 5 | "threadId": 142, 6 | "providerIds": [], 7 | "uuid": "23b186c2-33e1-4667-b427-1bd2b531a121", 8 | "operator": { 9 | "id": 4, 10 | "name": "Оператор Елена", 11 | "alias": null, 12 | "status": null, 13 | "photoUrl": null, 14 | "gender": "FEMALE", 15 | "organizationUnit": "Skill Credit", 16 | "role": "OPERATOR" 17 | }, 18 | "text": "", 19 | "receivedDate": "2023-03-27T10:18:34.611Z", 20 | "attachments": [ 21 | { 22 | "id": 46, 23 | "result": "file:///android_asset/test_images/test_image3.jpg", 24 | "optional": { 25 | "size": 238119, 26 | "name": "test_image3.jpg", 27 | "type": "image/jpeg" 28 | }, 29 | "state": "READY", 30 | "name": "test_image3.jpg", 31 | "type": "image/jpeg", 32 | "size": 238119, 33 | "wellDefined": true, 34 | "url": "file:///android_asset/test_images/test_image3.jpg" 35 | } 36 | ], 37 | "quotes": [], 38 | "quickReplies": [], 39 | "read": true, 40 | "settings": { 41 | "blockInput": false, 42 | "masked": false 43 | }, 44 | "authorized": true, 45 | "massPushMessage": false, 46 | "origin": "threads" 47 | }, 48 | { 49 | "type": "MESSAGE", 50 | "threadId": 142, 51 | "providerIds": [], 52 | "uuid": "ce9f4e95-e370-4bde-b6f1-ed11bc3becd5", 53 | "text": "Обожаю океан!", 54 | "receivedDate": "2023-03-27T10:19:09.272Z", 55 | "attachments": [], 56 | "quotes": [ 57 | { 58 | "type": "MESSAGE", 59 | "threadId": 142, 60 | "providerIds": [], 61 | "uuid": "23b186c2-33e1-4667-b427-1bd2b531a121", 62 | "operator": { 63 | "id": 4, 64 | "name": "Оператор Елена", 65 | "alias": null, 66 | "status": null, 67 | "photoUrl": null, 68 | "gender": "FEMALE", 69 | "organizationUnit": "Skill Credit", 70 | "role": "OPERATOR" 71 | }, 72 | "text": "", 73 | "receivedDate": "2023-03-27T10:18:34.611Z", 74 | "attachments": [ 75 | { 76 | "id": 46, 77 | "result": "file:///android_asset/test_images/test_image1.jpg", 78 | "optional": { 79 | "size": 238119, 80 | "name": "test_image1.jpg", 81 | "type": "image/jpeg" 82 | }, 83 | "state": "READY", 84 | "name": "test_image2.jpg", 85 | "type": "image/jpeg", 86 | "size": 238119, 87 | "wellDefined": true, 88 | "url": "file:///android_asset/test_images/test_image1.jpg" 89 | } 90 | ], 91 | "quotes": [], 92 | "quickReplies": [], 93 | "read": true, 94 | "settings": { 95 | "blockInput": false, 96 | "masked": false 97 | }, 98 | "authorized": true, 99 | "massPushMessage": false, 100 | "origin": "threads" 101 | } 102 | ], 103 | "quickReplies": [], 104 | "read": true, 105 | "settings": { 106 | "blockInput": false, 107 | "masked": false 108 | }, 109 | "authorized": true, 110 | "massPushMessage": false, 111 | "origin": "threads" 112 | } 113 | ], 114 | "agentInfo": { 115 | "action": "AGENT_JOINED", 116 | "actionDate": "2023-03-27T10:24:51.591Z", 117 | "thread": { 118 | "id": 142, 119 | "state": "WAITING" 120 | }, 121 | "agent": { 122 | "id": 4, 123 | "name": "Оператор Елена", 124 | "alias": null, 125 | "status": null, 126 | "photoUrl": null, 127 | "gender": "FEMALE", 128 | "organizationUnit": "Skill Credit", 129 | "role": "OPERATOR" 130 | } 131 | }, 132 | "allowedToSendMessages": true 133 | } -------------------------------------------------------------------------------- /app/src/main/res/raw/snapshot_test_history_images_response_3.json: -------------------------------------------------------------------------------- 1 | { 2 | "messages": [ 3 | { 4 | "type": "MESSAGE", 5 | "threadId": 142, 6 | "providerIds": [], 7 | "uuid": "581fc219-71c6-4c7a-ad7d-429605454115", 8 | "text": "А как вам это?", 9 | "receivedDate": "2023-03-27T10:19:45.144Z", 10 | "attachments": [ 11 | { 12 | "id": 47, 13 | "result": "file:///android_asset/test_images/test_image2.jpg", 14 | "optional": { 15 | "size": 5303953, 16 | "name": "test_image2.jpg", 17 | "type": "image/jpeg" 18 | }, 19 | "state": "READY", 20 | "name": "test_image2.jpg", 21 | "type": "image/jpeg", 22 | "size": 5303953, 23 | "wellDefined": true, 24 | "url": "file:///android_asset/test_images/test_image2.jpg" 25 | } 26 | ], 27 | "quotes": [], 28 | "quickReplies": [], 29 | "read": true, 30 | "settings": { 31 | "blockInput": false, 32 | "masked": false 33 | }, 34 | "authorized": true, 35 | "massPushMessage": false, 36 | "origin": "threads" 37 | } 38 | ], 39 | "agentInfo": { 40 | "action": "AGENT_JOINED", 41 | "actionDate": "2023-03-27T10:24:51.591Z", 42 | "thread": { 43 | "id": 142, 44 | "state": "WAITING" 45 | }, 46 | "agent": { 47 | "id": 4, 48 | "name": "Оператор Елена", 49 | "alias": null, 50 | "status": null, 51 | "photoUrl": null, 52 | "gender": "FEMALE", 53 | "organizationUnit": "Skill Credit", 54 | "role": "OPERATOR" 55 | } 56 | }, 57 | "allowedToSendMessages": true 58 | } -------------------------------------------------------------------------------- /app/src/main/res/raw/snapshot_test_history_images_response_4.json: -------------------------------------------------------------------------------- 1 | { 2 | "messages": [ 3 | { 4 | "type": "MESSAGE", 5 | "threadId": 142, 6 | "providerIds": [], 7 | "uuid": "90987158-c75a-45d9-87a8-79bbdf27956e", 8 | "operator": { 9 | "id": 4, 10 | "name": "Оператор Елена", 11 | "alias": null, 12 | "status": null, 13 | "photoUrl": null, 14 | "gender": "FEMALE", 15 | "organizationUnit": "Skill Credit", 16 | "role": "OPERATOR" 17 | }, 18 | "text": "Великолепно! Как и вот это.", 19 | "receivedDate": "2023-03-27T10:22:04.907Z", 20 | "attachments": [ 21 | { 22 | "id": 48, 23 | "result": "file:///android_asset/test_images/test_image5.jpg", 24 | "optional": { 25 | "size": 258264, 26 | "name": "test_image5.jpg", 27 | "type": "image/jpeg" 28 | }, 29 | "state": "READY", 30 | "name": "test_image5.jpg", 31 | "type": "image/jpeg", 32 | "size": 258264, 33 | "wellDefined": true, 34 | "url": "file:///android_asset/test_images/test_image5.jpg" 35 | } 36 | ], 37 | "quotes": [], 38 | "quickReplies": [], 39 | "read": false, 40 | "settings": { 41 | "blockInput": false, 42 | "masked": false 43 | }, 44 | "authorized": true, 45 | "massPushMessage": false, 46 | "origin": "threads" 47 | } 48 | ], 49 | "agentInfo": { 50 | "action": "AGENT_JOINED", 51 | "actionDate": "2023-03-27T10:24:51.591Z", 52 | "thread": { 53 | "id": 142, 54 | "state": "WAITING" 55 | }, 56 | "agent": { 57 | "id": 4, 58 | "name": "Оператор Елена", 59 | "alias": null, 60 | "status": null, 61 | "photoUrl": null, 62 | "gender": "FEMALE", 63 | "organizationUnit": "Skill Credit", 64 | "role": "OPERATOR" 65 | } 66 | }, 67 | "allowedToSendMessages": true 68 | } -------------------------------------------------------------------------------- /app/src/main/res/raw/snapshot_test_history_system_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "messages": [ 3 | { 4 | "clientId": "987525", 5 | "display": false, 6 | "questionId": 1, 7 | "rate": 1, 8 | "read": false, 9 | "receivedDate": "2023-03-24T11:40:34.324Z", 10 | "scale": 1, 11 | "sendingId": 136, 12 | "simple": true, 13 | "text": "Оцените наше обслуживание", 14 | "threadId": 137, 15 | "type": "SURVEY_QUESTION_ANSWER", 16 | "uuid": "3a84a1c8-5f2e-4e31-abcb-6fc8559b2dc9" 17 | }, 18 | { 19 | "clientId": "987525", 20 | "display": false, 21 | "questionId": 3, 22 | "rate": 3, 23 | "read": false, 24 | "receivedDate": "2023-03-24T11:40:36.218Z", 25 | "scale": 5, 26 | "sendingId": 136, 27 | "simple": false, 28 | "text": "Оцените насколько мы решили ваш вопрос", 29 | "threadId": 137, 30 | "type": "SURVEY_QUESTION_ANSWER", 31 | "uuid": "0ee9607d-e444-4a4a-8fec-a2d928c7e40f" 32 | }, 33 | { 34 | "type": "SURVEY", 35 | "providerIds": [], 36 | "uuid": "7f95eb98-bc74-490a-9426-9888abb68947", 37 | "text": "{\"id\":1,\"sendingId\":139,\"uuid\":\"7f95eb98-bc74-490a-9426-9888abb68947\",\"questions\":[{\"id\":1,\"sendingId\":139,\"text\":\"Оцените наше обслуживание\",\"scale\":1,\"simple\":true,\"displayText\":\"Качество обслуживания\",\"kpi\":3.0,\"int\":true},{\"id\":3,\"sendingId\":139,\"text\":\"Оцените насколько мы решили ваш вопрос\",\"scale\":5,\"simple\":false,\"displayText\":\"Качество обслуживания\",\"minValue\":1,\"maxValue\":5,\"kpi\":3.0,\"int\":true},{\"id\":4,\"sendingId\":139,\"text\":\"Оцените насколько внимательным был наш сотрудник\",\"scale\":5,\"simple\":false,\"displayText\":\"Качество обслуживания\",\"minValue\":1,\"maxValue\":5,\"kpi\":3.0,\"int\":true}],\"created\":\"2016-10-15T00:00:00.000Z\",\"status\":\"CREATED\",\"sendStatus\":\"CLOSED\",\"hideAfter\":999999999999999}", 38 | "receivedDate": "2023-03-24T12:52:13.667Z", 39 | "read": true, 40 | "content": { 41 | "id": 1, 42 | "sendingId": 139, 43 | "uuid": "7f95eb98-bc74-490a-9426-9888abb68947", 44 | "questions": [ 45 | { 46 | "id": 1, 47 | "sendingId": 139, 48 | "text": "Оцените наше обслуживание", 49 | "scale": 1, 50 | "simple": true, 51 | "displayText": "Качество обслуживания", 52 | "kpi": 3.0, 53 | "int": true 54 | }, 55 | { 56 | "id": 3, 57 | "sendingId": 139, 58 | "text": "Оцените насколько мы решили ваш вопрос", 59 | "scale": 5, 60 | "simple": false, 61 | "displayText": "Качество обслуживания", 62 | "minValue": 1, 63 | "maxValue": 5, 64 | "kpi": 3.0, 65 | "int": true 66 | } 67 | ], 68 | "created": "2016-10-15T00:00:00.000Z", 69 | "status": "CREATED", 70 | "sendStatus": "CLOSED", 71 | "hideAfter": 9999999999999 72 | }, 73 | "authorized": true, 74 | "massPushMessage": false, 75 | "aps/sound": "default", 76 | "origin": "threads" 77 | } 78 | ] 79 | } -------------------------------------------------------------------------------- /app/src/main/res/raw/snapshot_test_history_text_response_1.json: -------------------------------------------------------------------------------- 1 | { 2 | "messages": [ 3 | { 4 | "type": "MESSAGE", 5 | "threadId": 304, 6 | "providerIds": [], 7 | "uuid": "ad279408-a236-4fac-823b-a65a460dcb77", 8 | "text": "Добрый день! Мы создаем экосистему бизнеса.", 9 | "receivedDate": "2023-03-22T08:42:02.061Z", 10 | "attachments": [], 11 | "quotes": [], 12 | "quickReplies": [], 13 | "read": true, 14 | "settings": { 15 | "blockInput": false, 16 | "masked": false 17 | }, 18 | "authorized": true, 19 | "massPushMessage": false, 20 | "origin": "threads" 21 | }, 22 | { 23 | "type": "AVERAGE_WAIT_TIME", 24 | "threadId": 304, 25 | "providerIds": [], 26 | "uuid": "f583f7b4-fd6d-41af-8d59-8fd39ecee3bd", 27 | "text": "Среднее время ожидания ответа составляет 2 минуты", 28 | "receivedDate": "2023-03-22T08:42:02.392Z", 29 | "display": true, 30 | "authorized": true, 31 | "massPushMessage": false, 32 | "aps/sound": "default", 33 | "origin": "threads" 34 | }, 35 | { 36 | "type": "OPERATOR_JOINED", 37 | "threadId": 304, 38 | "providerIds": [], 39 | "uuid": "46c00e2e-3e65-4ea2-86d9-429c0726867e", 40 | "operator": { 41 | "id": 9, 42 | "name": "Оператор Елена", 43 | "alias": null, 44 | "status": null, 45 | "photoUrl": null, 46 | "gender": "FEMALE", 47 | "organizationUnit": "Skill Debet", 48 | "role": "OPERATOR" 49 | }, 50 | "text": "Вам ответит Оператор Елена", 51 | "receivedDate": "2023-03-22T08:42:38.526Z", 52 | "display": true, 53 | "authorized": true, 54 | "massPushMessage": false, 55 | "aps/sound": "default", 56 | "origin": "threads" 57 | }, 58 | { 59 | "type": "MESSAGE", 60 | "threadId": 304, 61 | "providerIds": [], 62 | "uuid": "eda36c57-7286-4360-bb42-0cf3d7932113", 63 | "operator": { 64 | "id": 9, 65 | "name": "Оператор Елена", 66 | "alias": null, 67 | "status": null, 68 | "photoUrl": null, 69 | "gender": "FEMALE", 70 | "organizationUnit": "Skill Debet", 71 | "role": "OPERATOR" 72 | }, 73 | "text": "Добро пожаловать в наш чат! А кто такие Edna?", 74 | "receivedDate": "2023-03-22T08:42:44.275Z", 75 | "attachments": [], 76 | "quotes": [], 77 | "quickReplies": [], 78 | "read": true, 79 | "settings": { 80 | "blockInput": false, 81 | "masked": false 82 | }, 83 | "authorized": true, 84 | "massPushMessage": false, 85 | "origin": "threads" 86 | } 87 | ], 88 | "agentInfo": { 89 | "action": "AGENT_LOOKUP", 90 | "actionDate": "2023-03-22T08:51:43.124Z", 91 | "thread": { 92 | "id": 304, 93 | "state": "UNASSIGNED" 94 | }, 95 | "agent": null 96 | }, 97 | "allowedToSendMessages": true 98 | } -------------------------------------------------------------------------------- /app/src/main/res/raw/snapshot_test_history_text_response_2.json: -------------------------------------------------------------------------------- 1 | { 2 | "messages": [ 3 | { 4 | "type": "MESSAGE", 5 | "threadId": 304, 6 | "providerIds": [], 7 | "uuid": "f78114b1-65c3-483e-a2c1-691c3d519b7d", 8 | "operator": { 9 | "id": 9, 10 | "name": "Оператор Елена", 11 | "alias": null, 12 | "status": null, 13 | "photoUrl": null, 14 | "gender": "FEMALE", 15 | "organizationUnit": "Skill Debet", 16 | "role": "OPERATOR" 17 | }, 18 | "text": "", 19 | "formattedText": "То есть это все про вас?\n- **Цифровая трансформация клиентов**. Использование мессенджеров в качестве основного канала связи стало нормой.", 20 | "receivedDate": "2023-03-22T08:44:01.005Z", 21 | "attachments": [], 22 | "quotes": [ 23 | { 24 | "type": "MESSAGE", 25 | "threadId": 304, 26 | "providerIds": [], 27 | "uuid": "110c8a46-8c38-45df-9949-5f12b7922f01", 28 | "text": "Добро пожаловать в наш чат! А кто такие Edna?", 29 | "receivedDate": "2023-03-22T08:43:19.497Z", 30 | "attachments": [], 31 | "quotes": [ 32 | { 33 | "type": "MESSAGE", 34 | "threadId": 304, 35 | "providerIds": [], 36 | "uuid": "eda36c57-7286-4360-bb42-0cf3d7932113", 37 | "operator": { 38 | "id": 9, 39 | "name": "Оператор Елена", 40 | "alias": null, 41 | "status": null, 42 | "photoUrl": null, 43 | "gender": "FEMALE", 44 | "organizationUnit": "Skill Debet", 45 | "role": "OPERATOR" 46 | }, 47 | "text": "Прежде всего, постоянное информационно-пропагандистское обеспечение нашей деятельности предопределяет высокую востребованность форм воздействия!", 48 | "receivedDate": "2023-03-22T08:42:44.275Z", 49 | "attachments": [], 50 | "quotes": [], 51 | "quickReplies": [], 52 | "read": true, 53 | "settings": { 54 | "blockInput": false, 55 | "masked": false 56 | }, 57 | "authorized": true, 58 | "massPushMessage": false, 59 | "origin": "threads" 60 | } 61 | ], 62 | "quickReplies": [], 63 | "read": true, 64 | "settings": { 65 | "blockInput": false, 66 | "masked": false 67 | }, 68 | "authorized": true, 69 | "massPushMessage": false, 70 | "origin": "threads" 71 | } 72 | ], 73 | "quickReplies": [], 74 | "read": true, 75 | "settings": { 76 | "blockInput": false, 77 | "masked": false 78 | }, 79 | "authorized": true, 80 | "massPushMessage": false, 81 | "origin": "threads" 82 | } 83 | ], 84 | "agentInfo": { 85 | "action": "AGENT_LOOKUP", 86 | "actionDate": "2023-03-22T08:51:43.124Z", 87 | "thread": { 88 | "id": 304, 89 | "state": "UNASSIGNED" 90 | }, 91 | "agent": null 92 | }, 93 | "allowedToSendMessages": true 94 | } -------------------------------------------------------------------------------- /app/src/main/res/raw/snapshot_test_history_text_response_3.json: -------------------------------------------------------------------------------- 1 | { 2 | "messages": [ 3 | { 4 | "type": "MESSAGE", 5 | "threadId": 304, 6 | "providerIds": [], 7 | "uuid": "309153f8-d58e-4e8d-bdae-592670c93333", 8 | "text": "Именно! А еще у нас есть различные каналы коммуникации с клиентами! Подробнее: http://127.0.0.1:8080/channels", 9 | "receivedDate": "2023-03-22T08:44:21.741Z", 10 | "attachments": [], 11 | "quotes": [], 12 | "quickReplies": [], 13 | "read": true, 14 | "settings": { 15 | "blockInput": false, 16 | "masked": false 17 | }, 18 | "authorized": true, 19 | "massPushMessage": false, 20 | "origin": "threads" 21 | }, 22 | { 23 | "type": "MESSAGE", 24 | "threadId": 304, 25 | "providerIds": [], 26 | "uuid": "a30877eb-3b79-4203-bc14-a622be9acc90", 27 | "operator": { 28 | "id": 9, 29 | "name": "Оператор Елена", 30 | "alias": null, 31 | "status": null, 32 | "photoUrl": null, 33 | "gender": "FEMALE", 34 | "organizationUnit": "Skill Debet", 35 | "role": "OPERATOR" 36 | }, 37 | "text": "Отлично! Давайте проверим ваши контакты. Ваш email: info@edna.ru, телефон: +7 (495) 609-60-80. Верно?", 38 | "receivedDate": "2023-03-22T08:45:08.649Z", 39 | "attachments": [], 40 | "quotes": [], 41 | "quickReplies": [], 42 | "read": true, 43 | "settings": { 44 | "blockInput": false, 45 | "masked": false 46 | }, 47 | "authorized": true, 48 | "massPushMessage": false, 49 | "origin": "threads" 50 | }, 51 | { 52 | "type": "MESSAGE", 53 | "threadId": 304, 54 | "providerIds": [], 55 | "uuid": "3aee0831-12b9-43ed-9e08-03a6dcd96843", 56 | "text": "Да, все верно!", 57 | "receivedDate": "2023-03-22T08:45:37.630Z", 58 | "attachments": [], 59 | "quotes": [], 60 | "quickReplies": [], 61 | "read": true, 62 | "settings": { 63 | "blockInput": false, 64 | "masked": false 65 | }, 66 | "authorized": true, 67 | "massPushMessage": false, 68 | "origin": "threads" 69 | }, 70 | { 71 | "type": "MESSAGE", 72 | "threadId": 304, 73 | "providerIds": [], 74 | "uuid": "20f8f1a3-6edb-4aa9-8d98-27e2dde324f0", 75 | "operator": { 76 | "id": 9, 77 | "name": "Оператор Елена", 78 | "alias": null, 79 | "status": null, 80 | "photoUrl": null, 81 | "gender": "FEMALE", 82 | "organizationUnit": "Skill Debet", 83 | "role": "OPERATOR" 84 | }, 85 | "text": "", 86 | "formattedText": "Тогда ***до связи***!", 87 | "receivedDate": "2023-03-22T08:46:18.047Z", 88 | "attachments": [], 89 | "quotes": [], 90 | "quickReplies": [], 91 | "read": true, 92 | "settings": { 93 | "blockInput": false, 94 | "masked": false 95 | }, 96 | "authorized": true, 97 | "massPushMessage": false, 98 | "origin": "threads" 99 | } 100 | ], 101 | "agentInfo": { 102 | "action": "AGENT_LOOKUP", 103 | "actionDate": "2023-03-22T08:51:43.124Z", 104 | "thread": { 105 | "id": 304, 106 | "state": "UNASSIGNED" 107 | }, 108 | "agent": null 109 | }, 110 | "allowedToSendMessages": true 111 | } -------------------------------------------------------------------------------- /app/src/main/res/raw/snapshot_test_history_voice_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "messages": [ 3 | { 4 | "type": "MESSAGE", 5 | "threadId": 182, 6 | "providerIds": [], 7 | "uuid": "8ea00dba-527d-4fd3-9321-01ce829304eb", 8 | "text": "", 9 | "receivedDate": "2023-04-05T10:29:43.666Z", 10 | "attachments": [ 11 | { 12 | "id": 70, 13 | "result": "file:///android_asset/test_files/client_text1.ogg", 14 | "optional": { 15 | "size": 52095, 16 | "name": "client_text1.ogg", 17 | "type": "audio/ogg" 18 | }, 19 | "state": "READY", 20 | "name": "client_text1.ogg", 21 | "type": "audio/ogg", 22 | "size": 52095, 23 | "wellDefined": true, 24 | "url": "file:///android_asset/test_files/client_text1.ogg" 25 | } 26 | ], 27 | "quotes": [], 28 | "quickReplies": [], 29 | "read": true, 30 | "settings": { 31 | "blockInput": false, 32 | "masked": false 33 | }, 34 | "authorized": true, 35 | "massPushMessage": false, 36 | "origin": "threads" 37 | }, 38 | { 39 | "type": "MESSAGE", 40 | "threadId": 182, 41 | "providerIds": [], 42 | "uuid": "1c1df1af-1a21-49be-9421-f2d846ae493c", 43 | "operator": { 44 | "id": 4, 45 | "name": "Оператор Елена", 46 | "alias": null, 47 | "status": null, 48 | "photoUrl": null, 49 | "gender": "FEMALE", 50 | "organizationUnit": "Skill Credit", 51 | "role": "OPERATOR" 52 | }, 53 | "text": "", 54 | "receivedDate": "2023-04-05T10:36:12.443Z", 55 | "attachments": [ 56 | { 57 | "id": 71, 58 | "result": "file:///android_asset/test_files/operator_text1.ogg", 59 | "optional": { 60 | "size": 31183, 61 | "name": "operator_text1.ogg", 62 | "type": "audio/ogg" 63 | }, 64 | "state": "READY", 65 | "name": "operator_text1.ogg", 66 | "type": "audio/ogg", 67 | "size": 31183, 68 | "wellDefined": true, 69 | "url": "file:///android_asset/test_files/operator_text1.ogg" 70 | } 71 | ], 72 | "quotes": [], 73 | "quickReplies": [], 74 | "read": false, 75 | "settings": { 76 | "blockInput": false, 77 | "masked": false 78 | }, 79 | "authorized": true, 80 | "massPushMessage": false, 81 | "origin": "threads" 82 | } 83 | ], 84 | "agentInfo": { 85 | "action": "AGENT_JOINED", 86 | "actionDate": "2023-04-05T10:39:21.522Z", 87 | "thread": { 88 | "id": 182, 89 | "state": "WAITING" 90 | }, 91 | "agent": { 92 | "id": 4, 93 | "name": "Оператор Елена", 94 | "alias": null, 95 | "status": null, 96 | "photoUrl": null, 97 | "gender": "FEMALE", 98 | "organizationUnit": "Skill Credit", 99 | "role": "OPERATOR" 100 | } 101 | }, 102 | "allowedToSendMessages": true 103 | } -------------------------------------------------------------------------------- /app/src/main/res/values-hdpi/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | @dimen/height_42 3 | @dimen/margin_5 4 | @dimen/margin_8 5 | @dimen/margin_16 6 | @dimen/height_42 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/values-land/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 48dp 3 | -------------------------------------------------------------------------------- /app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/values-w1240dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 200dp 3 | -------------------------------------------------------------------------------- /app/src/main/res/values-w600dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 48dp 3 | -------------------------------------------------------------------------------- /app/src/main/res/values/attr.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/values/bools.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | false 4 | false 5 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #121212 4 | #2d2d2d 5 | #79747E 6 | #8E8E93 7 | 8 | #2B8718 9 | 10 | #b5b5b5 11 | #b7b7b7 12 | #f2f2f7 13 | #f4f4f4 14 | #ececec 15 | #fafafa 16 | #ffffff 17 | 18 | #007AFF 19 | #0A84FF 20 | #004EA4 21 | #1C1B1F 22 | #FF3B30 23 | 24 | #883C3C43 25 | #505050 26 | #505050 27 | @color/white_color_ec 28 | @color/white_color_ec 29 | #000000 30 | #505050 31 | @color/white_color_ec 32 | #A9A9A9 33 | 34 | 35 | #BBF1D1 36 | #54DCA0 37 | #DFF3E7 38 | #2E7B59 39 | #18382A 40 | #D0D2D4 41 | #4D000000 42 | #0000FF 43 | 44 | 45 | #183D2C 46 | #2A6E50 47 | #628676 48 | #1C1B1F 49 | #000000 50 | #9fa5a9 51 | #4D000000 52 | #0000FF 53 | #505050 54 | #f2f2f7 55 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 2dp 3 | 3dp 4 | 5dp 5 | 8dp 6 | 12dp 7 | 14dp 8 | 16dp 9 | 19dp 10 | 20dp 11 | 21dp 12 | 24dp 13 | 26dp 14 | 32dp 15 | 40dp 16 | 52dp 17 | 18 | 48dp 19 | 20 | 25dp 21 | 36dp 22 | 42dp 23 | 50dp 24 | 56dp 25 | 26 | 11sp 27 | 12sp 28 | 14sp 29 | 16sp 30 | 22sp 31 | 28sp 32 | 33 | 64sp 34 | 56dp 35 | @dimen/height_56 36 | 37 | @dimen/margin_16 38 | @dimen/margin_24 39 | @dimen/margin_40 40 | @dimen/margin_52 41 | @dimen/height_56 42 | 43 | @dimen/margin_20 44 | @dimen/margin_16 45 | 46 | 47 | 23dp 48 | 8dp 49 | 8dp 50 | 8dp 51 | 22dp 52 | 12dp 53 | 12dp 54 | 12dp 55 | 12dp 56 | 12dp 57 | 18dp 58 | 12dp 59 | 60 | 61 | -------------------------------------------------------------------------------- /app/src/main/res/values/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFFFFF 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Edna ChatCenter 3 | 4 | Вход в аккаунт 5 | Сервер 6 | Пользователь 7 | Версия API 8 | Войти 9 | Logout 10 | Вход под пользователем: 11 | Демонстрационные примеры 12 | Функционал не поддерживается в текущей версии приложения. 13 | 14 | Welcome screen 15 | Select server screen 16 | Демонстрации 17 | Чат 18 | Add server screen 19 | Select user screen 20 | Add user screen 21 | 22 | Текстовые сообщения 23 | Сообщения с ошибками 24 | Голосовые сообщения 25 | Изображения 26 | Файлы 27 | Системные сообщения 28 | Чат с ботом 29 | Удаленные и измененные сообщения 30 | 31 | Серверы 32 | Пользователи 33 | Name 34 | * Name 35 | Имя пользователя 36 | * Имя пользователя 37 | ID пользователя 38 | * ID пользователя 39 | Данные пользователя 40 | appMarker 41 | signature 42 | Authorization header 43 | X-Auth-Schema header 44 | Datastore URL 45 | Threads Gate URL 46 | Server Base URL 47 | Threads Gate Provider Uid 48 | * Datastore URL 49 | * Threads Gate URL 50 | * Server Base URL 51 | * Threads Gate Provider Uid 52 | Поле обязательное для заполнения 53 | Добавьте пользователя\nдля входа в чат 54 | 55 | Кнопка назад 56 | Кнопка сохранить 57 | Кнопка добавить 58 | Иконка элемента списка серверов 59 | Кнопка редактировать 60 | Кнопка удалить 61 | Иконка элемента списка пользователей 62 | 63 | Демонстрация сообщений 64 | Оператор Елена 65 | 66 | Выберите тему 67 | По умолчанию 68 | Темная 69 | Светлая 70 | OK 71 | Удалить 72 | Изменить 73 | Предварительная регистрация 74 | Необходимо установить или заново переустановить пользователя 75 | Приложение будет перезапущено 76 | -------------------------------------------------------------------------------- /app/src/main/res/xml/backup_rules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/src/main/res/xml/data_extraction_rules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/xml/network_security_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/test/java/io/edna/threads/demo/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package io.edna.threads.demo 2 | 3 | import org.junit.Test 4 | 5 | /** 6 | * Example local unit test, which will execute on the development machine (host). 7 | * 8 | * See [testing documentation](http://d.android.com/tools/testing). 9 | */ 10 | class ExampleUnitTest { 11 | @Test 12 | fun addition_isCorrect() { 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | buildscript { 3 | ext.kotlin_version = '1.8.0' 4 | dependencies { 5 | classpath 'com.android.tools.build:gradle:7.2.1' 6 | classpath 'com.google.gms:google-services:4.3.15' 7 | classpath 'net.researchgate:gradle-release:2.8.1' 8 | classpath 'com.huawei.agconnect:agcp:1.6.4.300' 9 | classpath "org.jlleitschuh.gradle:ktlint-gradle:11.5.1" 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | classpath "org.jetbrains.kotlin:kotlin-allopen:$kotlin_version" 12 | classpath "dev.testify:plugin:2.0.0-alpha02" 13 | classpath 'com.google.firebase:perf-plugin:1.4.2' 14 | } 15 | } 16 | 17 | plugins { 18 | id 'com.android.application' version '7.2.1' apply false 19 | id 'com.android.library' version '7.2.1' apply false 20 | id 'org.jetbrains.kotlin.android' version '1.8.0' apply false 21 | id 'org.jlleitschuh.gradle.ktlint' version "10.3.0" 22 | } 23 | 24 | allprojects { 25 | //Support @JvmDefault 26 | tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { 27 | kotlinOptions { 28 | freeCompilerArgs = ['-Xjvm-default=enable'] //enable or compatibility 29 | jvmTarget = '11' 30 | } 31 | } 32 | } 33 | 34 | subprojects { 35 | apply plugin: "org.jlleitschuh.gradle.ktlint" 36 | } 37 | 38 | task clean(type: Delete) { 39 | delete rootProject.buildDir 40 | } 41 | 42 | ext { 43 | compileSdkVersion = 34 44 | minSdkVersion = 21 45 | targetSdkVersion = 31 46 | 47 | androidXCoreVersion = '1.9.0' 48 | androidXPreferenceVersion = '1.1.1' 49 | androidXSwiperefreshlayout = '1.1.0' 50 | materialComponentsVersion = '1.8.0' 51 | appCompatVersion = '1.6.1' 52 | kotlinReflect = '1.7.10' 53 | 54 | constraintlayoutVersion = '2.1.4' 55 | navigationVersion = '2.5.3' 56 | 57 | testExpressoVersion = '3.5.1' 58 | testJunitVersion = '4.13.2' 59 | testJunitAndroidVersion = '1.1.5' 60 | lifecycleViewmodelVersion = '2.4.0' 61 | calligraphy3Version = '3.1.1' 62 | viewpumpVersion = '2.0.3' 63 | 64 | viewmodelVersion = '2.5.1' 65 | koin_version = '3.2.0' 66 | okhttp_webserver_version = '4.11.0' 67 | 68 | pushVersion = '3.5.2' 69 | okhttpVersion = '4.9.3' 70 | retrofitVersion = '2.11.0' 71 | rxjava2Version = '2.2.21' 72 | rxAndroidVersion = '2.1.1' 73 | streamVersion = '1.2.1' 74 | gsonVersion = '2.11.0' 75 | picasso_version = '2.8' 76 | ffmpeg = '0.3.2' 77 | fcmVersion = '23.0.6' 78 | work_version = "2.7.1" 79 | sqlite_version = "2.0.1" 80 | crypto_tink = '1.7.0' 81 | markwonVersion = '4.6.2' 82 | robolectricVersion = '4.11' 83 | androidXJunitVersion = '1.1.3' 84 | androidXTestVersion = '1.5.0' 85 | espressoVersion = '3.4.0' 86 | mockitoVersion = '3.12.4' 87 | mockitoInlineVersion = '2.21.0' 88 | webserver_version = '2.35.0' 89 | test_ext = '1.1.5' 90 | kaspresso_version = '1.5.3' 91 | retrofitVersion = '2.9.0' 92 | parcelerVersion = '1.1.12' 93 | parcelerVersion = '1.1.12' 94 | firebaseMessagingVersion = '23.4.0' 95 | hmsMessagingVersion = '6.11.0.300' 96 | appCenterSdkVersion = '4.4.5' 97 | } 98 | -------------------------------------------------------------------------------- /docs/image1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/docs/image1.jpg -------------------------------------------------------------------------------- /docs/image2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/docs/image2.jpg -------------------------------------------------------------------------------- /docs/image3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/docs/image3.jpg -------------------------------------------------------------------------------- /docs/image4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/docs/image4.png -------------------------------------------------------------------------------- /docs/image5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/docs/image5.png -------------------------------------------------------------------------------- /docs/image6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/docs/image6.png -------------------------------------------------------------------------------- /docs/image7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/docs/image7.png -------------------------------------------------------------------------------- /docs/image8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/docs/image8.png -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | # When configured, Gradle will run in incubating parallel mode. 10 | # This option should only be used with decoupled projects. More details, visit 11 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 12 | # org.gradle.parallel=true 13 | # AndroidX package structure to make it clearer which packages are bundled with the 14 | # Android operating system, and which are packaged with your app"s APK 15 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 16 | # Kotlin code style for this project: "official" or "obsolete": 17 | # Enables namespacing of each library's R class so that its R class includes only the 18 | # resources declared in the library itself and none from the library's dependencies, 19 | # thereby reducing the size of the R class for that library 20 | version=4.42.0 21 | android.enableJetifier=true 22 | android.useAndroidX=true 23 | org.gradle.configureondemand=true 24 | org.gradle.daemon=true 25 | org.gradle.jvmargs=-Xmx4096m -XX:MaxMetaspaceSize=2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 26 | org.gradle.parallel=true 27 | android.nonTransitiveRClass=true 28 | kotlin.code.style=official 29 | org.gradle.unsafe.configuration-cache=false -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Mar 06 12:42:14 EET 2023 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /push-lib/Android_SDK_autodraw_3.3.0.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/push-lib/Android_SDK_autodraw_3.3.0.docx -------------------------------------------------------------------------------- /push-lib/MFMSPushLite.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/push-lib/MFMSPushLite.pdf -------------------------------------------------------------------------------- /push-lib/edna-PushService-MobileSDK-Android-Lite_3.3.0.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreadsMobileLib/edna-sdk-android/a95049023ec0b62ff91021b48069f385a8ab3011/push-lib/edna-PushService-MobileSDK-Android-Lite_3.3.0.docx -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | google() 5 | mavenCentral() 6 | maven { url "https://jitpack.io" } 7 | maven { url 'https://plugins.gradle.org/m2/' } 8 | maven { url 'https://maven-pub.edna.ru/repository/maven-public/' } 9 | maven { url 'https://developer.huawei.com/repo/' } 10 | } 11 | } 12 | dependencyResolutionManagement { 13 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 14 | repositories { 15 | google() 16 | mavenCentral() 17 | maven { url "https://jitpack.io" } 18 | maven { url 'https://plugins.gradle.org/m2/' } 19 | maven { url 'https://maven-pub.edna.ru/repository/maven-public/' } 20 | maven { url 'https://developer.huawei.com/repo/' } 21 | } 22 | } 23 | rootProject.name = "Edna ChatCenter" 24 | include ':app' --------------------------------------------------------------------------------