├── client ├── .gitignore ├── src │ ├── test │ │ ├── resources │ │ │ ├── mockito-extensions │ │ │ │ └── org.mockito.plugins.MockMaker │ │ │ ├── model.member.json │ │ │ └── message.json │ │ └── java │ │ │ └── io │ │ │ └── getstream │ │ │ └── chat │ │ │ └── android │ │ │ └── client │ │ │ ├── token │ │ │ ├── FakeTokenProvider.kt │ │ │ └── FakeTokenManager.kt │ │ │ ├── Mother.kt │ │ │ ├── parser │ │ │ ├── MapAdapterTest.kt │ │ │ ├── SkipExtraDataGson.kt │ │ │ ├── DateAdapterTest.kt │ │ │ ├── EventAdapterTest.kt │ │ │ └── ChatParserImplTest.kt │ │ │ ├── utils │ │ │ ├── VerifyUtils.kt │ │ │ ├── RetroSuccess.kt │ │ │ ├── observable │ │ │ │ ├── FakeChatSocket.kt │ │ │ │ └── FakeSocketService.kt │ │ │ └── RetroError.kt │ │ │ ├── testing │ │ │ └── utils.kt │ │ │ ├── api │ │ │ ├── FakeResponse.kt │ │ │ └── FakeChain.kt │ │ │ └── DatesTests.kt │ ├── main │ │ ├── res │ │ │ ├── values │ │ │ │ ├── colors.xml │ │ │ │ ├── styles.xml │ │ │ │ └── strings.xml │ │ │ └── drawable │ │ │ │ ├── stream_ic_file_csv.xml │ │ │ │ ├── stream_ic_file_doc.xml │ │ │ │ ├── stream_ic_file_mov.xml │ │ │ │ ├── stream_ic_file_mp3.xml │ │ │ │ ├── stream_ic_file_pdf.xml │ │ │ │ ├── stream_ic_file_ppt.xml │ │ │ │ ├── stream_ic_file_tar.xml │ │ │ │ ├── stream_ic_file_xls.xml │ │ │ │ ├── stream_ic_file_zip.xml │ │ │ │ └── stream_ic_notification.xml │ │ ├── java │ │ │ └── io │ │ │ │ └── getstream │ │ │ │ └── chat │ │ │ │ └── android │ │ │ │ └── client │ │ │ │ ├── models │ │ │ │ ├── GuestUser.kt │ │ │ │ ├── ModelFields.kt │ │ │ │ ├── Command.kt │ │ │ │ ├── UserEntity.kt │ │ │ │ ├── Device.kt │ │ │ │ ├── ChannelMute.kt │ │ │ │ ├── Mute.kt │ │ │ │ ├── ChannelUserRead.kt │ │ │ │ ├── CustomObject.kt │ │ │ │ ├── Member.kt │ │ │ │ ├── Flag.kt │ │ │ │ ├── Config.kt │ │ │ │ ├── Reaction.kt │ │ │ │ ├── User.kt │ │ │ │ ├── Attachment.kt │ │ │ │ ├── Extensions.kt │ │ │ │ └── Channel.kt │ │ │ │ ├── api │ │ │ │ ├── models │ │ │ │ │ ├── UploadFileResponse.kt │ │ │ │ │ ├── SendEventRequest.kt │ │ │ │ │ ├── UpdateCooldownRequest.kt │ │ │ │ │ ├── MuteChannelRequest.kt │ │ │ │ │ ├── CompletableResponse.kt │ │ │ │ │ ├── MarkReadRequest.kt │ │ │ │ │ ├── TranslateMessageRequest.kt │ │ │ │ │ ├── HideChannelRequest.kt │ │ │ │ │ ├── SearchMessagesResponse.kt │ │ │ │ │ ├── FlagResponse.kt │ │ │ │ │ ├── MessageRequest.kt │ │ │ │ │ ├── MessageResponse.kt │ │ │ │ │ ├── ReactionRequest.kt │ │ │ │ │ ├── ReactionResponse.kt │ │ │ │ │ ├── UpdateUsersResponse.kt │ │ │ │ │ ├── GetSyncHistory.kt │ │ │ │ │ ├── UpdateUsersRequest.kt │ │ │ │ │ ├── GetDevicesResponse.kt │ │ │ │ │ ├── GetRepliesResponse.kt │ │ │ │ │ ├── QueryMembersResponse.kt │ │ │ │ │ ├── QueryUserListResponse.kt │ │ │ │ │ ├── GetReactionsResponse.kt │ │ │ │ │ ├── GetSyncHistoryResponse.kt │ │ │ │ │ ├── UpdateChannelRequest.kt │ │ │ │ │ ├── EventResponse.kt │ │ │ │ │ ├── AddMembersRequest.kt │ │ │ │ │ ├── RemoveMembersRequest.kt │ │ │ │ │ ├── RejectInviteRequest.kt │ │ │ │ │ ├── AddDeviceRequest.kt │ │ │ │ │ ├── MuteUserRequest.kt │ │ │ │ │ ├── TokenResponse.kt │ │ │ │ │ ├── AcceptInviteRequest.kt │ │ │ │ │ ├── MuteUserResponse.kt │ │ │ │ │ ├── SendActionRequest.kt │ │ │ │ │ ├── BanUserRequest.kt │ │ │ │ │ ├── MuteResponse.kt │ │ │ │ │ ├── GuestUserRequest.kt │ │ │ │ │ ├── Pagination.kt │ │ │ │ │ ├── SearchMessagesRequest.kt │ │ │ │ │ ├── QueryUsersRequest.kt │ │ │ │ │ ├── QuerySort.kt │ │ │ │ │ ├── ChannelResponse.kt │ │ │ │ │ ├── QueryMembersRequest.kt │ │ │ │ │ ├── ChannelRequest.kt │ │ │ │ │ ├── RetroProgressCallback.kt │ │ │ │ │ ├── WatchChannelRequest.kt │ │ │ │ │ ├── QueryChannelsRequest.kt │ │ │ │ │ ├── ProgressRequestBody.kt │ │ │ │ │ └── QueryChannelRequest.kt │ │ │ │ ├── QueryChannelsResponse.kt │ │ │ │ ├── RetrofitCallMapper.kt │ │ │ │ ├── ErrorCall.kt │ │ │ │ ├── ChatClientConfig.kt │ │ │ │ ├── HeadersInterceptor.kt │ │ │ │ ├── RetrofitCdnApi.kt │ │ │ │ └── TokenAuthInterceptor.kt │ │ │ │ ├── utils │ │ │ │ ├── UuidGenerator.kt │ │ │ │ ├── UuidGeneratorImpl.kt │ │ │ │ ├── ImmediateTokenProvider.kt │ │ │ │ ├── ProgressCallback.kt │ │ │ │ ├── SystemTimeProvider.kt │ │ │ │ ├── SyncStatus.kt │ │ │ │ ├── ChatUtils.kt │ │ │ │ ├── Result.kt │ │ │ │ ├── observable │ │ │ │ │ ├── Subscriptions.kt │ │ │ │ │ ├── Subscription.kt │ │ │ │ │ └── ChatObservable.kt │ │ │ │ └── FilterObject.kt │ │ │ │ ├── socket │ │ │ │ ├── SocketErrorMessage.kt │ │ │ │ ├── ErrorResponse.kt │ │ │ │ ├── InitConnectionListener.kt │ │ │ │ ├── Socket.kt │ │ │ │ ├── SocketListener.kt │ │ │ │ ├── ChatSocket.kt │ │ │ │ ├── ChatSocketService.kt │ │ │ │ ├── ChatSocketImpl.kt │ │ │ │ └── SocketFactory.kt │ │ │ │ ├── errors │ │ │ │ ├── ChatError.kt │ │ │ │ ├── ChatParsingError.kt │ │ │ │ ├── ChatRequestError.kt │ │ │ │ ├── ChatNetworkError.kt │ │ │ │ └── ChatErrorCode.kt │ │ │ │ ├── parser │ │ │ │ ├── IgnoreSerialisation.kt │ │ │ │ ├── IgnoreDeserialisation.kt │ │ │ │ ├── UrlQueryPayload.kt │ │ │ │ ├── MapAdapter.kt │ │ │ │ ├── ChatParser.kt │ │ │ │ ├── adapters │ │ │ │ │ └── QuerySortAdapter.kt │ │ │ │ ├── FilterObjectAdapter.kt │ │ │ │ ├── UrlQueryPayloadFactory.kt │ │ │ │ ├── DateAdapter.kt │ │ │ │ └── TypeAdapterFactory.kt │ │ │ │ ├── token │ │ │ │ ├── TokenProvider.kt │ │ │ │ ├── TokenManager.kt │ │ │ │ └── TokenManagerImpl.kt │ │ │ │ ├── notifications │ │ │ │ ├── DeviceRegisteredListener.kt │ │ │ │ ├── FirebaseMessageParser.kt │ │ │ │ ├── NotificationLoadDataListener.kt │ │ │ │ ├── ChatFirebaseMessagingService.kt │ │ │ │ ├── handler │ │ │ │ │ └── NotificationConfig.kt │ │ │ │ └── FirebaseMessageParserImpl.kt │ │ │ │ ├── ClientState.kt │ │ │ │ ├── extensions │ │ │ │ ├── FileExtensions.kt │ │ │ │ └── CommonExtensions.kt │ │ │ │ ├── logger │ │ │ │ ├── ChatLoggerHandler.kt │ │ │ │ ├── TaggedLogger.kt │ │ │ │ ├── ChatLogLevel.kt │ │ │ │ ├── ChatSilentLogger.kt │ │ │ │ ├── TaggedLoggerImpl.kt │ │ │ │ └── ChatLogger.kt │ │ │ │ ├── call │ │ │ │ └── Call.kt │ │ │ │ ├── helpers │ │ │ │ └── AttachmentHelper.kt │ │ │ │ ├── ClientExtensions.kt │ │ │ │ └── controllers │ │ │ │ └── ChannelControllerExtensions.kt │ │ └── AndroidManifest.xml │ ├── release │ │ └── java │ │ │ └── io.getstream.chat.android.client.di │ │ │ └── ChatModule.kt │ ├── androidTest │ │ └── java │ │ │ └── io │ │ │ └── getstream │ │ │ └── chat │ │ │ └── android │ │ │ └── client │ │ │ └── utils │ │ │ ├── Utils.kt │ │ │ ├── TestChatUtils.kt │ │ │ ├── TestInitListener.kt │ │ │ └── EventsConsumer.kt │ └── debug │ │ └── java │ │ └── io │ │ └── getstream │ │ └── chat │ │ └── android │ │ └── client │ │ └── di │ │ └── ChatModule.kt ├── scripts │ └── docs.gradle ├── proguard-rules.pro └── consumer-proguard-rules.pro ├── sample ├── .gitignore ├── consumer-rules.pro ├── src │ ├── main │ │ ├── res │ │ │ ├── values │ │ │ │ └── strings.xml │ │ │ └── layout │ │ │ │ ├── activity_sync_channel.xml │ │ │ │ ├── channel_list_item.xml │ │ │ │ ├── activity_push.xml │ │ │ │ ├── activity_home.xml │ │ │ │ ├── fragment_channels.xml │ │ │ │ ├── activity_channels.xml │ │ │ │ └── activity_socket_tests.xml │ │ ├── java │ │ │ └── io │ │ │ │ └── getstream │ │ │ │ └── chat │ │ │ │ └── android │ │ │ │ └── client │ │ │ │ └── sample │ │ │ │ ├── common │ │ │ │ ├── CachedResult.kt │ │ │ │ ├── Page.kt │ │ │ │ ├── ChatChannel.kt │ │ │ │ ├── SyncHistoryActivity.kt │ │ │ │ ├── ApiMapper.kt │ │ │ │ ├── KeyValue.kt │ │ │ │ ├── Channel.kt │ │ │ │ ├── DiffCallback.kt │ │ │ │ ├── OneToOneActivity.kt │ │ │ │ ├── HomeActivity.kt │ │ │ │ ├── DbUtils.kt │ │ │ │ └── BaseChannelsListFragment.kt │ │ │ │ ├── ViewState.kt │ │ │ │ ├── utils │ │ │ │ ├── UserConfig.kt │ │ │ │ ├── LiveDataUtils.kt │ │ │ │ ├── UtilsMessages.kt │ │ │ │ └── PaginationListener.kt │ │ │ │ ├── examples │ │ │ │ ├── basic │ │ │ │ │ └── ChannelsListFragment.kt │ │ │ │ ├── livedata │ │ │ │ │ ├── ChannelsViewModel.kt │ │ │ │ │ └── ChannelsListFragment.kt │ │ │ │ ├── coroutines │ │ │ │ │ ├── ChannelsListFragment.kt │ │ │ │ │ └── ChannelsViewModel.kt │ │ │ │ └── rx │ │ │ │ │ └── ChannelsViewModelRx.kt │ │ │ │ ├── repositories │ │ │ │ ├── ChannelsRepositorySync.kt │ │ │ │ ├── ChannelsRepositoryLive.kt │ │ │ │ └── ChannelsRepositoryRx.kt │ │ │ │ ├── cache │ │ │ │ └── AppDatabase.kt │ │ │ │ └── ChannelsCache.kt │ │ └── AndroidManifest.xml │ ├── test │ │ └── java │ │ │ └── io │ │ │ └── getstream │ │ │ └── chat │ │ │ └── android │ │ │ └── client │ │ │ └── sample │ │ │ └── ExampleUnitTest.kt │ └── androidTest │ │ └── java │ │ └── io │ │ └── getstream │ │ └── chat │ │ └── android │ │ └── client │ │ └── sample │ │ └── ExampleInstrumentedTest.kt ├── proguard-rules.pro ├── app-config.json └── google-services.json ├── settings.gradle ├── docs ├── client-lifecycle.md ├── client-lifecycle.png ├── logging.md ├── token-provider.md ├── example-basic-async.md ├── example-pagination-channels.md ├── example-pagination-messages.md ├── issues.md ├── unread-messages.md ├── client-lifecycle.drawio ├── example-mvvm-livedata.md ├── example-mvvm-coroutines.md └── example-mvvm-rxjava.md ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitignore ├── .github ├── workflows │ ├── check-docs-snippets.yml │ └── android.yml ├── pull_request_template.md └── ISSUE_TEMPLATE │ └── bug_report.md └── gradle.properties /client/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /sample/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /sample/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':client', ':sample' 2 | -------------------------------------------------------------------------------- /docs/client-lifecycle.md: -------------------------------------------------------------------------------- 1 | # Client life cycle 2 | 3 | ![life-cycle](client-lifecycle.png) -------------------------------------------------------------------------------- /client/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker: -------------------------------------------------------------------------------- 1 | mock-maker-inline -------------------------------------------------------------------------------- /client/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /client/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/client-lifecycle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/stream-chat-android-client/HEAD/docs/client-lifecycle.png -------------------------------------------------------------------------------- /sample/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | sample 3 | 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/stream-chat-android-client/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /client/src/main/res/drawable/stream_ic_file_csv.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /client/src/main/res/drawable/stream_ic_file_doc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /client/src/main/res/drawable/stream_ic_file_mov.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /client/src/main/res/drawable/stream_ic_file_mp3.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /client/src/main/res/drawable/stream_ic_file_pdf.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /client/src/main/res/drawable/stream_ic_file_ppt.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /client/src/main/res/drawable/stream_ic_file_tar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /client/src/main/res/drawable/stream_ic_file_xls.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /client/src/main/res/drawable/stream_ic_file_zip.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/models/GuestUser.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.models 2 | 3 | public data class GuestUser(val user: User, val token: String) 4 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/models/UploadFileResponse.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api.models 2 | 3 | internal data class UploadFileResponse(val file: String) 4 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/utils/UuidGenerator.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.utils 2 | 3 | internal interface UuidGenerator { 4 | fun generate(): String 5 | } 6 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/models/SendEventRequest.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api.models 2 | 3 | internal data class SendEventRequest(val event: Map) 4 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/models/UpdateCooldownRequest.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api.models 2 | 3 | public data class UpdateCooldownRequest(val cooldown: Int) 4 | -------------------------------------------------------------------------------- /sample/src/main/java/io/getstream/chat/android/client/sample/common/CachedResult.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.sample.common 2 | 3 | class CachedResult(val data: T, val isUpdating: Boolean) 4 | -------------------------------------------------------------------------------- /sample/src/main/java/io/getstream/chat/android/client/sample/common/Page.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.sample.common 2 | 3 | class Page(val offset: Int, val limit: Int, val data: List) 4 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/models/MuteChannelRequest.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api.models 2 | 3 | internal data class MuteChannelRequest(val channel_cid: String) 4 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/socket/SocketErrorMessage.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.socket 2 | 3 | public data class SocketErrorMessage(val error: ErrorResponse? = null) 4 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/models/CompletableResponse.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api.models 2 | 3 | internal data class CompletableResponse(val duration: String = "") 4 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/models/MarkReadRequest.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api.models 2 | 3 | internal data class MarkReadRequest( 4 | val message_id: String 5 | ) 6 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/models/TranslateMessageRequest.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api.models 2 | 3 | internal data class TranslateMessageRequest(val language: String) 4 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/models/HideChannelRequest.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api.models 2 | 3 | internal data class HideChannelRequest(val clearHistory: Boolean = false) 4 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/models/SearchMessagesResponse.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api.models 2 | 3 | internal data class SearchMessagesResponse(val results: List = emptyList()) 4 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/models/ModelFields.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.models 2 | 3 | internal class ModelFields { 4 | companion object { 5 | const val MEMBERS = "members" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .composite 3 | .gradle 4 | /local.properties 5 | /.idea/ 6 | /projectFilesBackup/ 7 | /projectFilesBackup1/ 8 | .DS_Store 9 | /build 10 | /captures 11 | .externalNativeBuild 12 | .cxx 13 | /sample/app-config-custom.json 14 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/models/FlagResponse.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api.models 2 | 3 | import io.getstream.chat.android.client.models.Flag 4 | 5 | internal data class FlagResponse(val flag: Flag) 6 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/errors/ChatError.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.errors 2 | 3 | public open class ChatError( 4 | public val message: String? = null, 5 | public val cause: Throwable? = null 6 | ) 7 | -------------------------------------------------------------------------------- /sample/src/main/java/io/getstream/chat/android/client/sample/common/ChatChannel.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.sample.common 2 | 3 | data class ChatChannel( 4 | val id: String, 5 | val name: String, 6 | val updatedAt: Int 7 | ) 8 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /sample/src/main/java/io/getstream/chat/android/client/sample/common/SyncHistoryActivity.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.sample.common 2 | 3 | import androidx.appcompat.app.AppCompatActivity 4 | 5 | class SyncHistoryActivity : AppCompatActivity() 6 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/models/MessageRequest.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api.models 2 | 3 | import io.getstream.chat.android.client.models.Message 4 | 5 | internal data class MessageRequest(val message: Message) 6 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/models/MessageResponse.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api.models 2 | 3 | import io.getstream.chat.android.client.models.Message 4 | 5 | internal data class MessageResponse(val message: Message) 6 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/models/ReactionRequest.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api.models 2 | 3 | import io.getstream.chat.android.client.models.Reaction 4 | 5 | internal data class ReactionRequest(val reaction: Reaction) 6 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/parser/IgnoreSerialisation.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.parser 2 | 3 | @Retention(AnnotationRetention.RUNTIME) 4 | @Target(AnnotationTarget.FIELD) 5 | internal annotation class IgnoreSerialisation 6 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/models/ReactionResponse.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api.models 2 | 3 | import io.getstream.chat.android.client.models.Reaction 4 | 5 | internal data class ReactionResponse(val reaction: Reaction) 6 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/models/Command.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.models 2 | 3 | public data class Command( 4 | val name: String, 5 | val description: String, 6 | val args: String, 7 | val set: String 8 | ) 9 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/parser/IgnoreDeserialisation.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.parser 2 | 3 | @Retention(AnnotationRetention.RUNTIME) 4 | @Target(AnnotationTarget.FIELD) 5 | internal annotation class IgnoreDeserialisation 6 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/models/UpdateUsersResponse.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api.models 2 | 3 | import io.getstream.chat.android.client.models.User 4 | 5 | internal data class UpdateUsersResponse(val users: Map) 6 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/token/TokenProvider.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.token 2 | 3 | import androidx.annotation.WorkerThread 4 | 5 | public interface TokenProvider { 6 | @WorkerThread 7 | public fun loadToken(): String 8 | } 9 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/models/GetSyncHistory.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api.models 2 | 3 | import java.util.Date 4 | 5 | internal data class GetSyncHistory( 6 | val channel_cids: List, 7 | val last_sync_at: Date 8 | ) 9 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/models/UpdateUsersRequest.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api.models 2 | 3 | import io.getstream.chat.android.client.models.User 4 | 5 | public data class UpdateUsersRequest( 6 | val users: Map 7 | ) 8 | -------------------------------------------------------------------------------- /client/src/main/res/drawable/stream_ic_notification.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /docs/logging.md: -------------------------------------------------------------------------------- 1 | # Logging 2 | 3 | ```kotlin 4 | val client = StreamChatClient.Builder() 5 | .loggingLevel(BuildConfig.DEBUG ? ALL : NOTHING) 6 | .loggerHandler(object: LoggerHandler() { 7 | override fun logExeption(t:Throwable) { 8 | //log exception 9 | } 10 | }) 11 | ``` -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/models/GetDevicesResponse.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api.models 2 | 3 | import io.getstream.chat.android.client.models.Device 4 | 5 | internal data class GetDevicesResponse(val devices: List = emptyList()) 6 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/models/GetRepliesResponse.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api.models 2 | 3 | import io.getstream.chat.android.client.models.Message 4 | 5 | internal data class GetRepliesResponse(val messages: List = emptyList()) 6 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/models/QueryMembersResponse.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api.models 2 | 3 | import io.getstream.chat.android.client.models.Member 4 | 5 | internal data class QueryMembersResponse(val members: List = emptyList()) 6 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/models/QueryUserListResponse.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api.models 2 | 3 | import io.getstream.chat.android.client.models.User 4 | 5 | internal data class QueryUserListResponse(val users: List = emptyList()) 6 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/models/UserEntity.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.models 2 | 3 | public interface UserEntity { 4 | 5 | public var user: User 6 | 7 | public fun getUserId(): String { 8 | return user.id 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/models/GetReactionsResponse.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api.models 2 | 3 | import io.getstream.chat.android.client.models.Reaction 4 | 5 | internal data class GetReactionsResponse(val reactions: List = emptyList()) 6 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/models/GetSyncHistoryResponse.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api.models 2 | 3 | import io.getstream.chat.android.client.events.ChatEvent 4 | 5 | internal data class GetSyncHistoryResponse( 6 | val events: List 7 | ) 8 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/models/UpdateChannelRequest.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api.models 2 | 3 | import io.getstream.chat.android.client.models.Message 4 | 5 | internal data class UpdateChannelRequest(val data: Map, val message: Message?) 6 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/models/EventResponse.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api.models 2 | 3 | import io.getstream.chat.android.client.events.ChatEvent 4 | 5 | internal data class EventResponse( 6 | val event: ChatEvent, 7 | var duration: String = "" 8 | ) 9 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/QueryChannelsResponse.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api 2 | 3 | import io.getstream.chat.android.client.api.models.ChannelResponse 4 | 5 | internal data class QueryChannelsResponse( 6 | var channels: List = emptyList() 7 | ) 8 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/models/AddMembersRequest.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api.models 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | internal data class AddMembersRequest( 6 | @SerializedName("add_members") 7 | val members: List 8 | ) 9 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/errors/ChatParsingError.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.errors 2 | 3 | internal class ChatParsingError : Exception { 4 | constructor(message: String?) : super(message) 5 | constructor(message: String?, cause: Throwable?) : super(message, cause) 6 | } 7 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/models/RemoveMembersRequest.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api.models 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | internal data class RemoveMembersRequest( 6 | @SerializedName("remove_members") 7 | val members: List 8 | ) 9 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/utils/UuidGeneratorImpl.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.utils 2 | 3 | import java.util.UUID 4 | 5 | internal class UuidGeneratorImpl : UuidGenerator { 6 | override fun generate(): String { 7 | return UUID.randomUUID().toString() 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /sample/src/main/java/io/getstream/chat/android/client/sample/ViewState.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.sample 2 | 3 | sealed class ViewState { 4 | data class Success(val data: T) : ViewState() 5 | data class Error(val error: Throwable) : ViewState() 6 | class Loading : ViewState() 7 | } 8 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/models/RejectInviteRequest.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api.models 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | internal data class RejectInviteRequest( 6 | @SerializedName("reject_invite") 7 | val rejectInvite: Boolean = true 8 | ) 9 | -------------------------------------------------------------------------------- /client/src/release/java/io.getstream.chat.android.client.di/ChatModule.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.di 2 | 3 | import android.content.Context 4 | import io.getstream.chat.android.client.api.ChatClientConfig 5 | 6 | internal class ChatModule(appContext: Context, config: ChatClientConfig) : BaseChatModule(appContext, config) 7 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/utils/ImmediateTokenProvider.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.utils 2 | 3 | import io.getstream.chat.android.client.token.TokenProvider 4 | 5 | internal class ImmediateTokenProvider(private val token: String) : TokenProvider { 6 | override fun loadToken(): String = token 7 | } 8 | -------------------------------------------------------------------------------- /client/src/test/java/io/getstream/chat/android/client/token/FakeTokenProvider.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.token 2 | 3 | internal class FakeTokenProvider(vararg val tokens: String) : TokenProvider { 4 | 5 | var tokenId = 0 6 | 7 | override fun loadToken(): String { 8 | return tokens[tokenId++] 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/models/Device.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.models 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | public data class Device( 6 | @SerializedName("id") 7 | val id: String, 8 | @SerializedName("push_provider") 9 | var pushProvider: String = "" 10 | ) 11 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/models/AddDeviceRequest.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api.models 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | internal data class AddDeviceRequest( 6 | @SerializedName("id") 7 | val firebaseToken: String 8 | ) { 9 | val push_provider = "firebase" 10 | } 11 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/notifications/DeviceRegisteredListener.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.notifications 2 | 3 | import io.getstream.chat.android.client.errors.ChatError 4 | 5 | public interface DeviceRegisteredListener { 6 | public fun onDeviceRegisteredSuccess() 7 | public fun onDeviceRegisteredError(error: ChatError) 8 | } 9 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/models/MuteUserRequest.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api.models 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | internal data class MuteUserRequest( 6 | @SerializedName("target_id") 7 | val targetId: String, 8 | @SerializedName("user_id") 9 | val userId: String 10 | ) 11 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/models/ChannelMute.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.models 2 | 3 | import com.google.gson.annotations.SerializedName 4 | import java.util.Date 5 | 6 | public data class ChannelMute( 7 | val user: User, 8 | val channel: Channel, 9 | @SerializedName("created_at") 10 | val createdAt: Date 11 | ) 12 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/utils/ProgressCallback.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.utils 2 | 3 | import io.getstream.chat.android.client.errors.ChatError 4 | 5 | public interface ProgressCallback { 6 | public fun onSuccess(file: String) 7 | public fun onError(error: ChatError) 8 | public fun onProgress(progress: Long) 9 | } 10 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/models/TokenResponse.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api.models 2 | 3 | import com.google.gson.annotations.SerializedName 4 | import io.getstream.chat.android.client.models.User 5 | 6 | internal data class TokenResponse( 7 | val user: User, 8 | @SerializedName("access_token") 9 | val accessToken: String 10 | ) 11 | -------------------------------------------------------------------------------- /sample/src/main/java/io/getstream/chat/android/client/sample/utils/UserConfig.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.sample.utils 2 | 3 | import io.getstream.chat.android.client.models.User 4 | 5 | data class UserConfig( 6 | val userId: String, 7 | val token: String, 8 | val apiKey: String 9 | ) { 10 | fun getUser(): User { 11 | return User(userId) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/socket/ErrorResponse.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.socket 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | public data class ErrorResponse( 6 | val code: Int = -1, 7 | var message: String = "", 8 | @SerializedName("StatusCode") 9 | var statusCode: Int = -1 10 | ) { 11 | var duration: String = "" 12 | } 13 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/utils/SystemTimeProvider.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.utils 2 | 3 | public class SystemTimeProvider { 4 | public fun provideCurrentTimeInSeconds(): Long { 5 | return System.currentTimeMillis() / MILLIS_TO_SECONDS_FACTOR 6 | } 7 | 8 | private companion object { 9 | const val MILLIS_TO_SECONDS_FACTOR = 1000L 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/ClientState.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client 2 | 3 | import io.getstream.chat.android.client.models.User 4 | 5 | internal class ClientState { 6 | var user: User? = null 7 | var connectionId: String? = null 8 | var socketConnected: Boolean = false 9 | 10 | fun reset() { 11 | user = null 12 | connectionId = null 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/models/Mute.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.models 2 | 3 | import com.google.gson.annotations.SerializedName 4 | import java.util.Date 5 | 6 | public data class Mute( 7 | var user: User, 8 | var target: User, 9 | @SerializedName("created_at") 10 | var createdAt: Date, 11 | @SerializedName("updated_at") 12 | var updatedAt: Date 13 | ) 14 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/models/AcceptInviteRequest.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api.models 2 | 3 | import io.getstream.chat.android.client.models.User 4 | 5 | internal data class AcceptInviteRequest( 6 | val user: User, 7 | val message: AcceptInviteMessage, 8 | val accept_invite: Boolean = true 9 | ) { 10 | data class AcceptInviteMessage(val text: String? = null) 11 | } 12 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/models/MuteUserResponse.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api.models 2 | 3 | import com.google.gson.annotations.SerializedName 4 | import io.getstream.chat.android.client.models.Mute 5 | import io.getstream.chat.android.client.models.User 6 | 7 | internal data class MuteUserResponse( 8 | var mute: Mute, 9 | @SerializedName("own_user") 10 | var ownUser: User 11 | ) 12 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/models/ChannelUserRead.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.models 2 | 3 | import com.google.gson.annotations.SerializedName 4 | import java.util.Date 5 | 6 | public data class ChannelUserRead( 7 | override var user: User, 8 | @SerializedName("last_read") 9 | var lastRead: Date? = null, 10 | @SerializedName("unread_messages") 11 | var unreadMessages: Int = 0 12 | ) : UserEntity 13 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/parser/UrlQueryPayload.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.parser 2 | 3 | /** 4 | * Some requests passing payload as url query parameter. 5 | * This annotations tells gson to convert it to json, rather that call [toString] 6 | * See [UrlQueryPayloadFactory] 7 | */ 8 | @Retention(AnnotationRetention.RUNTIME) 9 | @Target(AnnotationTarget.VALUE_PARAMETER) 10 | internal annotation class UrlQueryPayload 11 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/models/SendActionRequest.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api.models 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | public data class SendActionRequest( 6 | @SerializedName("channel_id") 7 | val channelId: String, 8 | @SerializedName("message_id") 9 | val messageId: String, 10 | val type: String, 11 | @SerializedName("form_data") 12 | val formData: Map 13 | ) 14 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/notifications/FirebaseMessageParser.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.notifications 2 | 3 | import com.google.firebase.messaging.RemoteMessage 4 | 5 | public interface FirebaseMessageParser { 6 | public fun isValid(message: RemoteMessage): Boolean 7 | public fun parse(message: RemoteMessage): Data 8 | 9 | public data class Data(val messageId: String, val channelType: String, val channelId: String) 10 | } 11 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/models/BanUserRequest.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api.models 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | internal data class BanUserRequest( 6 | @SerializedName("target_user_id") 7 | var targetUserId: String, 8 | var timeout: Int, 9 | var reason: String, 10 | @SerializedName("type") 11 | var channelType: String, 12 | @SerializedName("id") 13 | var channelId: String 14 | ) 15 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/activity_sync_channel.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 9 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/RetrofitCallMapper.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api 2 | 3 | import io.getstream.chat.android.client.call.Call 4 | import io.getstream.chat.android.client.call.RetrofitCall 5 | import io.getstream.chat.android.client.parser.ChatParser 6 | 7 | internal class RetrofitCallMapper(private val chatParser: ChatParser) { 8 | fun map(call: retrofit2.Call): Call { 9 | return RetrofitCall(call, chatParser) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/extensions/FileExtensions.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.extensions 2 | 3 | import android.webkit.MimeTypeMap 4 | import okhttp3.MediaType 5 | import okhttp3.MediaType.Companion.toMediaType 6 | import java.io.File 7 | 8 | internal fun File.getMimeType(): String = 9 | MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension) ?: "application/octet-stream" 10 | 11 | internal fun File.getMediaType(): MediaType = getMimeType().toMediaType() 12 | -------------------------------------------------------------------------------- /sample/src/test/java/io/getstream/chat/android/client/sample/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.sample 2 | 3 | import org.junit.Assert.assertEquals 4 | import org.junit.Test 5 | 6 | /** 7 | * Example local unit test, which will execute on the development machine (host). 8 | * 9 | * See [testing documentation](http://d.android.com/tools/testing). 10 | */ 11 | class ExampleUnitTest { 12 | @Test 13 | fun addition_isCorrect() { 14 | assertEquals(4, 2 + 2) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /docs/token-provider.md: -------------------------------------------------------------------------------- 1 | # Token Provider 2 | 3 | 1. For tokens without expiration 4 | 5 | ```kotlin 6 | val token = "token" 7 | client.setUser(User("id"), token) 8 | ``` 9 | 10 | 2. For tokens with expiration 11 | 12 | ```kotlin 13 | val tokenProvider = object: TokenProvider() { 14 | // Executed in background thread 15 | override fun loadToken(): String { 16 | return api.getChatToken().execute() 17 | } 18 | } 19 | client.setUser(User("id"), tokenProvider) 20 | ``` 21 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/models/MuteResponse.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api.models 2 | 3 | import com.google.gson.annotations.SerializedName 4 | import io.getstream.chat.android.client.models.Mute 5 | import io.getstream.chat.android.client.models.User 6 | 7 | internal data class MuteResponse( 8 | @SerializedName("own_user") 9 | val user: User, 10 | @SerializedName("channel_mute") 11 | val mute: Mute, 12 | @SerializedName("channel_mutes") 13 | val mutes: List 14 | ) 15 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/notifications/NotificationLoadDataListener.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.notifications 2 | 3 | import io.getstream.chat.android.client.errors.ChatError 4 | import io.getstream.chat.android.client.models.Channel 5 | import io.getstream.chat.android.client.models.Message 6 | 7 | public interface NotificationLoadDataListener { 8 | 9 | public fun onLoadSuccess(channel: Channel, message: Message) 10 | 11 | public fun onLoadFail(messageId: String, error: ChatError) 12 | } 13 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/socket/InitConnectionListener.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.socket 2 | 3 | import io.getstream.chat.android.client.errors.ChatError 4 | import io.getstream.chat.android.client.models.User 5 | 6 | public abstract class InitConnectionListener { 7 | 8 | public open fun onSuccess(data: ConnectionData) { 9 | } 10 | 11 | public open fun onError(error: ChatError) { 12 | } 13 | 14 | public data class ConnectionData(val user: User, val connectionId: String) 15 | } 16 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/models/GuestUserRequest.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api.models 2 | 3 | import io.getstream.chat.android.client.parser.IgnoreSerialisation 4 | 5 | internal data class GuestUserRequest constructor( 6 | @IgnoreSerialisation 7 | val id: String, 8 | @IgnoreSerialisation 9 | val name: String 10 | ) { 11 | 12 | var user = GuestUserBody(id, name) 13 | 14 | data class GuestUserBody( 15 | val id: String, 16 | val name: String 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /docs/example-basic-async.md: -------------------------------------------------------------------------------- 1 | # Basic async example 2 | ```kotlin 3 | class ChannelsActivity: AppCompatActivity() { 4 | fun onCreate() { 5 | val client = StreamChatClient("api-key", "token") 6 | client.setUser(ChatUser("id"), { result -> 7 | if(result.isSuccess()) 8 | showChannels(result.data()) 9 | else 10 | showError(result.error()) 11 | } 12 | } 13 | 14 | fun showChannels(channels:List) { 15 | val adapter = ChannelsAdapter(channels) 16 | channelsView.setAdapter(adapter) 17 | } 18 | } 19 | ``` -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/socket/Socket.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.socket 2 | 3 | import io.getstream.chat.android.client.events.ChatEvent 4 | import io.getstream.chat.android.client.parser.ChatParser 5 | import okhttp3.WebSocket 6 | 7 | internal class Socket(val socket: WebSocket, val parser: ChatParser) { 8 | 9 | fun send(event: ChatEvent) { 10 | socket.send(parser.toJson(event)) 11 | } 12 | 13 | fun close(code: Int, reason: String) { 14 | socket.close(code, reason) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /docs/example-pagination-channels.md: -------------------------------------------------------------------------------- 1 | # Channels pagination 2 | 3 | Get first page 4 | 5 | ```kotlin 6 | val channel = ChatChannel("id") 7 | val limit = 25 8 | val result = client.queryChannels(channel, ChannelsQuery(limit)).execute() 9 | val channels = result.getData() 10 | ``` 11 | 12 | Get subsequent pages 13 | 14 | ```kotlin 15 | val channel = ChatChannel("id") 16 | val limit = 25 17 | val offset = 25 18 | val lastMessage = "message-id" 19 | val result = client.queryChannels(channel, ChannelsQuery(limit, offset)).execute() 20 | val channels = result.getData() 21 | ``` -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/extensions/CommonExtensions.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.extensions 2 | 3 | internal fun safeLet( 4 | p1: T1?, 5 | p2: T2?, 6 | p3: T3?, 7 | block: (T1, T2, T3) -> R? 8 | ): R? { 9 | return if (p1 != null && p2 != null && p3 != null) block(p1, p2, p3) else null 10 | } 11 | 12 | internal fun safeLet(p1: T1?, p2: T2?, block: (T1, T2) -> R?): R? { 13 | return if (p1 != null && p2 != null) block(p1, p2) else null 14 | } 15 | -------------------------------------------------------------------------------- /client/src/test/resources/model.member.json: -------------------------------------------------------------------------------- 1 | { 2 | "user_id": "stream-eugene", 3 | "user": { 4 | "id": "stream-eugene", 5 | "role": "user", 6 | "created_at": "2020-04-02T17:55:03.278292Z", 7 | "updated_at": "2020-05-22T12:49:26.739314Z", 8 | "last_active": "2020-05-22T12:31:04.710067Z", 9 | "banned": false, 10 | "online": true, 11 | "image": "https://bit.ly/321RmWb", 12 | "invisible": false, 13 | "name": "stream-eugene" 14 | }, 15 | "created_at": "2020-04-25T02:49:33.953141Z", 16 | "updated_at": "2020-04-25T02:49:33.953141Z" 17 | } -------------------------------------------------------------------------------- /sample/src/main/java/io/getstream/chat/android/client/sample/common/ApiMapper.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.sample.common 2 | 3 | object ApiMapper { 4 | 5 | fun mapChannel(channel: ChatChannel): Channel { 6 | return Channel().apply { 7 | remoteId = channel.id 8 | name = channel.name 9 | updatedAt = channel.updatedAt 10 | } 11 | } 12 | 13 | fun mapChannels(channels: List): List { 14 | return channels.map { 15 | mapChannel(it) 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.github/workflows/check-docs-snippets.yml: -------------------------------------------------------------------------------- 1 | name: Check documentation snippets 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | 8 | jobs: 9 | notifiy-snippets-repository: 10 | 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Call curl 14 | run: | 15 | curl -XPOST -H "Authorization: token ${{secrets.GITHUB_TOKEN}}" -H "Accept: application/vnd.github.everest-preview+json" -H "Content-Type: application/json" https://api.github.com/repos/GetStream/docs-snippets-android/dispatches --data '{"event_type": "check-on-new-release"}' 16 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | < Jira issue link, if applicable > 2 | 3 | ### Description 4 | 5 | Describe what this PR changes, why we're making the change, how it should be tested... 6 | 7 | ### Checklist 8 | 9 | - [ ] I have signed the [Stream CLA](https://docs.google.com/forms/d/e/1FAIpQLScFKsKkAJI7mhCr7K9rEIOpqIDThrWxuvxnwUq2XkHyG154vQ/viewform) (required) 10 | - [ ] PR targets the `develop` branch 11 | - [ ] Changelog updated with client-facing changes 12 | - [ ] New code is covered by unit tests 13 | - [ ] Comparison screenshots added for visual changes 14 | - [ ] Reviewers added 15 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/ErrorCall.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api 2 | 3 | import io.getstream.chat.android.client.call.ChatCallImpl 4 | import io.getstream.chat.android.client.errors.ChatError 5 | import io.getstream.chat.android.client.utils.Result 6 | 7 | internal class ErrorCall(val e: ChatError) : ChatCallImpl() { 8 | override fun execute(): Result { 9 | return Result(null, e) 10 | } 11 | 12 | override fun enqueue(callback: (Result) -> Unit) { 13 | callback(Result(null, e)) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/logger/ChatLoggerHandler.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.logger 2 | 3 | public interface ChatLoggerHandler { 4 | public fun logT(throwable: Throwable) 5 | 6 | public fun logT(tag: Any, throwable: Throwable) 7 | 8 | public fun logI(tag: Any, message: String) 9 | 10 | public fun logD(tag: Any, message: String) 11 | 12 | public fun logW(tag: Any, message: String) 13 | 14 | public fun logE(tag: Any, message: String) 15 | 16 | public fun logE(tag: Any, message: String, throwable: Throwable) 17 | } 18 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/models/CustomObject.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.models 2 | 3 | public interface CustomObject { 4 | public var extraData: MutableMap 5 | 6 | @Suppress("UNCHECKED_CAST") 7 | public fun getExtraValue(key: String, default: T): T { 8 | return if (extraData.containsKey(key)) { 9 | extraData[key] as T 10 | } else { 11 | default 12 | } 13 | } 14 | 15 | public fun putExtraValue(key: String, value: Any) { 16 | extraData[key] = value 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /docs/example-pagination-messages.md: -------------------------------------------------------------------------------- 1 | # Messages pagination 2 | 3 | Get first page 4 | 5 | ```kotlin 6 | val channel = ChatChannel("id") 7 | val limit = 25 8 | val result = client.queryChannel(channel, ChannelQuery().withMessages(limit)).execute() 9 | val messages = result.getData().getMessages() 10 | ``` 11 | 12 | Get subsequent pages 13 | 14 | ```kotlin 15 | val channel = ChatChannel("id") 16 | val limit = 25 17 | val lastMessage = "message-id" 18 | val result = client.queryChannel(channel, ChannelQuery().withMessages(lastMessage, limit)).execute() 19 | val messages = result.getData().getMessages() 20 | ``` -------------------------------------------------------------------------------- /client/src/androidTest/java/io/getstream/chat/android/client/utils/Utils.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.utils 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | 5 | class Utils { 6 | companion object { 7 | fun runOnUi(call: () -> Unit): ThenVerify { 8 | InstrumentationRegistry.getInstrumentation().runOnMainSync { 9 | call() 10 | } 11 | return ThenVerify() 12 | } 13 | } 14 | 15 | class ThenVerify { 16 | fun andThen(call: () -> Unit) { 17 | call() 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/errors/ChatRequestError.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.errors 2 | 3 | import java.io.IOException 4 | 5 | /** 6 | * Used to interrupt okhttp request. 7 | * Only descendant [IOException] of can propagate call execution 8 | */ 9 | internal class ChatRequestError( 10 | message: String, 11 | val streamCode: Int, 12 | val statusCode: Int, 13 | cause: Throwable? = null 14 | ) : IOException(message, cause) { 15 | override fun toString(): String { 16 | return "streamCode: $streamCode, statusCode: $statusCode, message: $message" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/parser/MapAdapter.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.parser 2 | 3 | import com.google.gson.TypeAdapter 4 | import com.google.gson.stream.JsonReader 5 | import com.google.gson.stream.JsonWriter 6 | 7 | internal class MapAdapter(private val delegateMapAdapter: TypeAdapter>) : TypeAdapter>() { 8 | override fun read(reader: JsonReader?): Map<*, *> = delegateMapAdapter.read(reader) 9 | override fun write(writer: JsonWriter?, value: Map<*, *>?) = 10 | delegateMapAdapter.write(writer, value?.filterValues { it != null }?.takeIf { it.isNotEmpty() }) 11 | } 12 | -------------------------------------------------------------------------------- /sample/src/main/java/io/getstream/chat/android/client/sample/utils/LiveDataUtils.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.sample.utils 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MediatorLiveData 5 | 6 | fun LiveData.combineWith( 7 | liveData: LiveData, 8 | block: (T?, K?) -> R 9 | ): LiveData { 10 | val result = MediatorLiveData() 11 | result.addSource(this) { 12 | result.value = block.invoke(this.value, liveData.value) 13 | } 14 | result.addSource(liveData) { 15 | result.value = block.invoke(this.value, liveData.value) 16 | } 17 | return result 18 | } 19 | -------------------------------------------------------------------------------- /client/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Chat message 5 | You\'ve received new message 6 | Reply 7 | Type message 8 | Mark as read 9 | stream_GetStreamClient 10 | New chat messages 11 | 12 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/parser/ChatParser.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.parser 2 | 3 | import io.getstream.chat.android.client.errors.ChatNetworkError 4 | import io.getstream.chat.android.client.utils.Result 5 | import okhttp3.Response 6 | import retrofit2.Retrofit 7 | 8 | internal interface ChatParser { 9 | 10 | fun toJson(any: Any): String 11 | fun fromJson(raw: String, clazz: Class): T 12 | fun fromJsonOrError(raw: String, clazz: Class): Result 13 | fun toError(okHttpResponse: Response): ChatNetworkError 14 | fun configRetrofit(builder: Retrofit.Builder): Retrofit.Builder 15 | } 16 | -------------------------------------------------------------------------------- /sample/src/main/java/io/getstream/chat/android/client/sample/common/KeyValue.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.sample.common 2 | 3 | import android.content.Context 4 | 5 | class KeyValue(context: Context) { 6 | 7 | private val prefs = context.getSharedPreferences("default-prefs", Context.MODE_PRIVATE) 8 | 9 | fun set(key: String, value: Boolean) { 10 | prefs.edit().putBoolean(key, value).apply() 11 | } 12 | 13 | fun getBoolean(key: String, default: Boolean): Boolean { 14 | return prefs.getBoolean(key, default) 15 | } 16 | 17 | companion object { 18 | const val CHANNELS_STORED = "channels-stored" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/models/Pagination.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api.models 2 | 3 | public enum class Pagination(private val value: String) { 4 | 5 | GREATER_THAN("id_gt"), 6 | GREATER_THAN_OR_EQUAL("id_gte"), 7 | LESS_THAN("id_lt"), 8 | LESS_THAN_OR_EQUAL("id_lte"); 9 | 10 | @Deprecated( 11 | message = "Use toString() instead", 12 | replaceWith = ReplaceWith("toString()"), 13 | level = DeprecationLevel.ERROR 14 | ) 15 | public fun get(): String { 16 | return value 17 | } 18 | 19 | override fun toString(): String { 20 | return value 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/models/SearchMessagesRequest.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api.models 2 | 3 | import io.getstream.chat.android.client.parser.IgnoreSerialisation 4 | import io.getstream.chat.android.client.utils.FilterObject 5 | 6 | public data class SearchMessagesRequest( 7 | val offset: Int, 8 | val limit: Int, 9 | @IgnoreSerialisation 10 | val channelFilter: FilterObject, 11 | @IgnoreSerialisation 12 | val messageFilter: FilterObject 13 | ) { 14 | val filter_conditions: HashMap = channelFilter.toMap() 15 | val message_filter_conditions: HashMap = messageFilter.toMap() 16 | } 17 | -------------------------------------------------------------------------------- /client/src/test/java/io/getstream/chat/android/client/Mother.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client 2 | 3 | import com.flextrade.jfixture.JFixture 4 | import com.flextrade.kfixture.KFixture 5 | import io.getstream.chat.android.client.models.Attachment 6 | 7 | internal object Mother { 8 | private val fixture = JFixture() 9 | 10 | fun randomAttachment(attachmentBuilder: Attachment.() -> Unit): Attachment { 11 | return KFixture(fixture) { 12 | sameInstance( 13 | Attachment.UploadState::class.java, 14 | Attachment.UploadState.Success 15 | ) 16 | } ().apply(attachmentBuilder) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/token/TokenManager.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.token 2 | 3 | import androidx.annotation.UiThread 4 | import androidx.annotation.WorkerThread 5 | import io.getstream.chat.android.client.utils.Result 6 | 7 | internal interface TokenManager { 8 | 9 | @UiThread 10 | fun loadAsync(listener: (Result) -> Unit = {}) 11 | 12 | @UiThread 13 | fun loadAsync() 14 | 15 | @WorkerThread 16 | fun loadSync() 17 | 18 | fun expireToken() 19 | fun setTokenProvider(provider: TokenProvider) 20 | fun getToken(): String 21 | fun hasToken(): Boolean 22 | fun hasTokenProvider(): Boolean 23 | } 24 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/utils/SyncStatus.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.utils 2 | 3 | public enum class SyncStatus(public val status: Int) { 4 | /** when the entity is new or changed */ 5 | SYNC_NEEDED(-1), 6 | /** when the entity has been succesfully synced */ 7 | COMPLETED(1), 8 | /** after the retry strategy we still failed to sync this */ 9 | FAILED_PERMANENTLY(2), 10 | /** when sync is in progress */ 11 | IN_PROGRESS(3); 12 | 13 | public companion object { 14 | private val map = values().associateBy(SyncStatus::status) 15 | public fun fromInt(type: Int): SyncStatus? = map[type] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/socket/SocketListener.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.socket 2 | 3 | import io.getstream.chat.android.client.errors.ChatError 4 | import io.getstream.chat.android.client.events.ChatEvent 5 | import io.getstream.chat.android.client.events.ConnectedEvent 6 | 7 | public open class SocketListener { 8 | 9 | public open fun onConnecting() { 10 | } 11 | 12 | public open fun onConnected(event: ConnectedEvent) { 13 | } 14 | 15 | public open fun onDisconnected() { 16 | } 17 | 18 | public open fun onError(error: ChatError) { 19 | } 20 | 21 | public open fun onEvent(event: ChatEvent) { 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/logger/TaggedLogger.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.logger 2 | 3 | import io.getstream.chat.android.client.errors.ChatError 4 | 5 | public interface TaggedLogger { 6 | public fun logI(message: String) 7 | 8 | public fun logD(message: String) 9 | 10 | public fun logW(message: String) 11 | 12 | public fun logE(message: String) 13 | 14 | public fun logE(throwable: Throwable) 15 | 16 | public fun logE(chatError: ChatError) 17 | 18 | public fun logE(message: String, throwable: Throwable) 19 | 20 | public fun logE(message: String, chatError: ChatError) 21 | 22 | public fun getLevel(): ChatLogLevel 23 | } 24 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/models/QueryUsersRequest.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api.models 2 | 3 | import io.getstream.chat.android.client.parser.IgnoreSerialisation 4 | import io.getstream.chat.android.client.utils.FilterObject 5 | 6 | public data class QueryUsersRequest @JvmOverloads constructor( 7 | @IgnoreSerialisation 8 | var filter: FilterObject, 9 | val offset: Int, 10 | val limit: Int, 11 | @IgnoreSerialisation 12 | var querySort: QuerySort = QuerySort(), 13 | var presence: Boolean = false 14 | ) { 15 | val sort: MutableList> = querySort.data 16 | val filter_conditions: Map = filter.toMap() 17 | } 18 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/logger/ChatLogLevel.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.logger 2 | 3 | public enum class ChatLogLevel(private val severity: Int) { 4 | /** 5 | * Show all Logs. 6 | */ 7 | ALL(0), 8 | /** 9 | * Show DEBUG, WARNING, ERROR logs 10 | */ 11 | DEBUG(1), 12 | /** 13 | * Show WARNING and ERROR logs 14 | */ 15 | WARN(2), 16 | /** 17 | * Show ERRORs only 18 | */ 19 | ERROR(3), 20 | /** 21 | * Don't show any Logs. 22 | */ 23 | NOTHING(4); 24 | 25 | internal fun isMoreOrEqualsThan(level: ChatLogLevel): Boolean { 26 | return level.severity >= severity 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /sample/src/main/java/io/getstream/chat/android/client/sample/examples/basic/ChannelsListFragment.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.sample.examples.basic 2 | 3 | import android.os.Bundle 4 | import io.getstream.chat.android.client.sample.common.BaseChannelsListFragment 5 | 6 | class ChannelsListFragment : BaseChannelsListFragment() { 7 | 8 | override fun reload() { 9 | } 10 | 11 | override fun onCreate(savedInstanceState: Bundle?) { 12 | super.onCreate(savedInstanceState) 13 | 14 | // App.client.queryChannels( 15 | // ChannelsQuery().apply { 16 | // this.limit = 10 17 | // this.offset = 10 18 | // } 19 | // ) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/models/Member.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.models 2 | 3 | import com.google.gson.annotations.SerializedName 4 | import java.util.Date 5 | 6 | public data class Member( 7 | override var user: User, 8 | var role: String? = null, 9 | @SerializedName("created_at") 10 | var createdAt: Date? = null, 11 | @SerializedName("updated_at") 12 | var updatedAt: Date? = null, 13 | @SerializedName("invited") 14 | var isInvited: Boolean? = null, 15 | @SerializedName("invite_accepted_at") 16 | var inviteAcceptedAt: Date? = null, 17 | @SerializedName("invite_rejected_at") 18 | var inviteRejectedAt: Date? = null 19 | ) : UserEntity 20 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/socket/ChatSocket.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.socket 2 | 3 | import io.getstream.chat.android.client.models.User 4 | import io.getstream.chat.android.client.utils.observable.ChatObservable 5 | 6 | internal interface ChatSocket { 7 | fun connect(user: User) 8 | fun connectAnonymously() 9 | @Deprecated( 10 | message = "Use addListener and removeListener directly instead (or the subscribe methods of ChatClient)", 11 | level = DeprecationLevel.WARNING 12 | ) 13 | fun events(): ChatObservable 14 | fun addListener(listener: SocketListener) 15 | fun removeListener(listener: SocketListener) 16 | fun disconnect() 17 | } 18 | -------------------------------------------------------------------------------- /client/src/test/java/io/getstream/chat/android/client/parser/MapAdapterTest.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.parser 2 | 3 | import com.google.gson.Gson 4 | import org.amshove.kluent.`should be equal to` 5 | import org.junit.jupiter.api.Test 6 | 7 | internal class MapAdapterTest { 8 | val mapAdapter = MapAdapter(Gson().getAdapter(Map::class.java)) 9 | 10 | @Test 11 | fun `Should render emptyMap as null`() { 12 | mapAdapter.toJson(emptyMap()) `should be equal to` "null" 13 | } 14 | 15 | @Test 16 | fun `Should omit key with null values`() { 17 | mapAdapter.toJson(mapOf("a" to "b", "c" to null, "d" to 1)) `should be equal to` "{\"a\":\"b\",\"d\":1}" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /client/scripts/docs.gradle: -------------------------------------------------------------------------------- 1 | /** 2 | * Compiles source code and documentation into release aar 3 | */ 4 | 5 | task sourcesJar(type: Jar) { 6 | from android.sourceSets.main.java.srcDirs 7 | getArchiveClassifier().set('sources') 8 | } 9 | 10 | task javadoc(type: Javadoc) { 11 | failOnError false 12 | source = android.sourceSets.main.java.sourceFiles 13 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) 14 | classpath += configurations.compile 15 | } 16 | 17 | task javadocJar(type: Jar, dependsOn: javadoc) { 18 | getArchiveClassifier().set('javadoc') 19 | from javadoc.destinationDir 20 | } 21 | 22 | artifacts { 23 | archives sourcesJar 24 | archives javadocJar 25 | } -------------------------------------------------------------------------------- /client/src/androidTest/java/io/getstream/chat/android/client/utils/TestChatUtils.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.utils 2 | 3 | import org.assertj.core.api.Assertions.assertThat 4 | import org.assertj.core.api.Assertions.assertThatThrownBy 5 | import org.junit.Test 6 | 7 | class TestChatUtils { 8 | @Test 9 | fun validDevToken() { 10 | val devToken = ChatUtils.devToken("bender") 11 | assertThat(devToken).isEqualTo("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjpiZW5kZXJ9.devtoken") 12 | } 13 | 14 | @Test 15 | fun emptyDevToken() { 16 | assertThatThrownBy { 17 | ChatUtils.devToken("") 18 | }.isInstanceOf(IllegalArgumentException::class.java) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **SDK version** 14 | - x.x.x 15 | 16 | **To Reproduce** 17 | Steps to reproduce the behavior: 18 | 1. Go to '...' 19 | 2. Click on '....' 20 | 3. Scroll down to '....' 21 | 4. See error 22 | 23 | **Expected behavior** 24 | A clear and concise description of what you expected to happen. 25 | 26 | **Device:** 27 | - Vendor and model: [e.g. Samsung S8] 28 | - Android version: [e.g. 9] 29 | 30 | **Screenshots** 31 | If applicable, add screenshots to help explain your problem. 32 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/socket/ChatSocketService.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.socket 2 | 3 | import io.getstream.chat.android.client.errors.ChatError 4 | import io.getstream.chat.android.client.events.ChatEvent 5 | import io.getstream.chat.android.client.events.ConnectedEvent 6 | import io.getstream.chat.android.client.models.User 7 | 8 | internal interface ChatSocketService { 9 | 10 | fun connect(endpoint: String, apiKey: String, user: User?) 11 | 12 | fun disconnect() 13 | 14 | fun addListener(listener: SocketListener) 15 | fun removeListener(listener: SocketListener) 16 | 17 | fun onSocketError(error: ChatError) 18 | fun onConnectionResolved(event: ConnectedEvent) 19 | fun onEvent(event: ChatEvent) 20 | } 21 | -------------------------------------------------------------------------------- /client/src/test/java/io/getstream/chat/android/client/utils/VerifyUtils.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.utils 2 | 3 | import io.getstream.chat.android.client.errors.ChatNetworkError 4 | import org.assertj.core.api.Assertions 5 | 6 | internal fun verifyError(result: Result, statusCode: Int) { 7 | Assertions.assertThat(result.isSuccess).isFalse() 8 | Assertions.assertThat(result.error()).isInstanceOf(ChatNetworkError::class.java) 9 | 10 | val error = result.error() as ChatNetworkError 11 | Assertions.assertThat(error.statusCode).isEqualTo(statusCode) 12 | } 13 | 14 | internal fun verifySuccess(result: Result, equalsTo: T) { 15 | Assertions.assertThat(result.isSuccess).isTrue() 16 | Assertions.assertThat(result.data()).isEqualTo(equalsTo) 17 | } 18 | -------------------------------------------------------------------------------- /client/src/androidTest/java/io/getstream/chat/android/client/utils/TestInitListener.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.utils 2 | 3 | import io.getstream.chat.android.client.errors.ChatError 4 | import io.getstream.chat.android.client.socket.InitConnectionListener 5 | 6 | class TestInitListener : InitConnectionListener() { 7 | 8 | private var data: ConnectionData? = null 9 | private var error: ChatError? = null 10 | 11 | fun onSuccessIsCalled(): Boolean { 12 | return data != null 13 | } 14 | 15 | fun onErrorIsCalled(): Boolean { 16 | return error != null 17 | } 18 | 19 | override fun onSuccess(data: ConnectionData) { 20 | this.data = data 21 | } 22 | 23 | override fun onError(error: ChatError) { 24 | this.error = error 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /sample/src/main/java/io/getstream/chat/android/client/sample/common/Channel.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.sample.common 2 | 3 | import androidx.room.ColumnInfo 4 | import androidx.room.Entity 5 | import androidx.room.PrimaryKey 6 | import java.util.UUID 7 | 8 | @Entity(tableName = "channels") 9 | class Channel { 10 | 11 | @PrimaryKey 12 | var id: String = UUID.randomUUID().toString() 13 | 14 | @ColumnInfo(name = "remote_id") 15 | var remoteId: String = "" 16 | 17 | @ColumnInfo(name = "name") 18 | var name: String = "" 19 | 20 | @ColumnInfo(name = "updated_at") 21 | var updatedAt: Int = -1 22 | 23 | @ColumnInfo(name = "synced") 24 | var synched: Boolean = false 25 | 26 | override fun toString(): String { 27 | return "{$remoteId}" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/models/QuerySort.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api.models 2 | 3 | public data class QuerySort( 4 | public val data: MutableList> = mutableListOf() 5 | ) { 6 | 7 | private fun add(fieldName: String, direction: Int): QuerySort { 8 | val map = mutableMapOf() 9 | map["field"] = fieldName 10 | map["direction"] = direction 11 | data.add(map) 12 | return this 13 | } 14 | 15 | public fun asc(field: String): QuerySort { 16 | return add(field, ASC) 17 | } 18 | 19 | public fun desc(field: String): QuerySort { 20 | return add(field, DESC) 21 | } 22 | 23 | private companion object { 24 | const val DESC = -1 25 | const val ASC = 1 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/models/ChannelResponse.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api.models 2 | 3 | import io.getstream.chat.android.client.models.Channel 4 | import io.getstream.chat.android.client.models.ChannelUserRead 5 | import io.getstream.chat.android.client.models.Member 6 | import io.getstream.chat.android.client.models.Message 7 | import io.getstream.chat.android.client.models.User 8 | import java.util.Date 9 | 10 | internal data class ChannelResponse( 11 | val channel: Channel, 12 | val messages: List? = null, 13 | var members: List? = null, 14 | var watchers: List? = null, 15 | var read: List? = null, 16 | val watcher_count: Int = 0, 17 | val hidden: Boolean? = null, 18 | val hide_messages_before: Date? = null 19 | ) 20 | -------------------------------------------------------------------------------- /client/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /sample/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /client/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /client/src/test/java/io/getstream/chat/android/client/testing/utils.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.testing 2 | 3 | import java.io.BufferedReader 4 | import java.io.InputStream 5 | import java.io.InputStreamReader 6 | import java.nio.charset.Charset 7 | import java.util.stream.Collectors 8 | 9 | private val resClass = ResClass() 10 | 11 | /** 12 | * Loads files under /resources directory 13 | * [path] "/model.member.json" 14 | */ 15 | internal fun loadResource(path: String): String { 16 | return convert(resClass.javaClass.getResourceAsStream(path)) 17 | } 18 | 19 | internal fun convert(inputStream: InputStream): String { 20 | BufferedReader(InputStreamReader(inputStream, Charset.defaultCharset())).use { br -> 21 | return br.lines().collect(Collectors.joining(System.lineSeparator())) 22 | } 23 | } 24 | 25 | private class ResClass 26 | -------------------------------------------------------------------------------- /docs/issues.md: -------------------------------------------------------------------------------- 1 | # Issues 2 | # Mentioned user in message 3 | Backend api endpoint for sending message accepts message object with `mentioned_users` field with array of user string ids, returns message object with the same field, but with different type: array of user objects. `Message` model handles it: 4 | ```kotlin 5 | @IgnoreDeserialisation 6 | @SerializedName("mentioned_users") 7 | var mentionedUsersIds: MutableList = mutableListOf(), 8 | 9 | @IgnoreSerialisation 10 | @SerializedName("mentioned_users") 11 | var mentionedUsers: MutableList = mutableListOf(), 12 | ``` 13 | Hence to send message with mentioned user `mentionedUsersIds` must be used: 14 | ```kotlin 15 | val message = Message(text = "hello") 16 | message.mentionedUsersIds.add("some-user-id") 17 | client.sendMessage("messagin", "channel-id", message).enqueue { messageResult -> 18 | 19 | } 20 | ``` -------------------------------------------------------------------------------- /docs/unread-messages.md: -------------------------------------------------------------------------------- 1 | # Unread messages 2 | `Channel` object has `read` field which contains unread data per user. Extension function `getUnreadMessagesCount` can be used to get total unread messages. 3 | ```kotlin 4 | val channelType = "messaging" 5 | val channelId = "channel-id" 6 | val userId = "user-id" 7 | val request = QueryChannelRequest().withState() 8 | 9 | client.queryChannel(channelType, channelId, request).enqueue { result -> 10 | 11 | if (result.isSuccess) { 12 | val channel = result.data() 13 | val totalUnreadMessages = channel.getUnreadMessagesCount() 14 | val unreadMessagesOfAUser = channel.read.first { 15 | it.user.id == userId 16 | }.unreadMessages 17 | } else { 18 | val error = result.error() 19 | error.printStackTrace() 20 | println(error.message) 21 | } 22 | 23 | } 24 | ``` -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/utils/ChatUtils.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.utils 2 | 3 | import android.util.Base64 4 | import java.nio.charset.StandardCharsets 5 | 6 | public object ChatUtils { 7 | @JvmStatic 8 | public fun devToken(userId: String): String { 9 | require(userId.isNotEmpty()) { "User id must not be empty" } 10 | val header = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" // {"alg": "HS256", "typ": "JWT"} 11 | val devSignature = "devtoken" 12 | val a = arrayOfNulls(3) 13 | val payload = "{\"user_id\":$userId}" 14 | val payloadBase64 = Base64.encodeToString(payload.toByteArray(StandardCharsets.UTF_8), Base64.NO_WRAP) 15 | a[0] = header 16 | a[1] = payloadBase64 17 | a[2] = devSignature 18 | return a.joinToString(".") 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /client/src/test/java/io/getstream/chat/android/client/token/FakeTokenManager.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.token 2 | 3 | import io.getstream.chat.android.client.utils.Result 4 | 5 | internal class FakeTokenManager(val tkn: String) : TokenManager { 6 | override fun loadAsync(listener: (Result) -> Unit) { 7 | listener(Result(tkn)) 8 | } 9 | 10 | override fun loadAsync() { 11 | } 12 | 13 | override fun loadSync() { 14 | } 15 | 16 | override fun expireToken() { 17 | } 18 | 19 | override fun setTokenProvider(provider: TokenProvider) { 20 | } 21 | 22 | override fun getToken(): String { 23 | return tkn 24 | } 25 | 26 | override fun hasToken(): Boolean { 27 | return true 28 | } 29 | 30 | override fun hasTokenProvider(): Boolean { 31 | return true 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /client/src/androidTest/java/io/getstream/chat/android/client/utils/EventsConsumer.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.utils 2 | 3 | import io.getstream.chat.android.client.events.ChatEvent 4 | 5 | class EventsConsumer(val expected: List>) { 6 | 7 | var received = mutableListOf() 8 | 9 | fun onEvent(event: ChatEvent) { 10 | received.add(event) 11 | } 12 | 13 | fun isReceived(): Boolean { 14 | 15 | expected.forEach { expectedType -> 16 | received.forEach { event -> 17 | if (expectedType.isInstance(event)) { 18 | return true 19 | } 20 | } 21 | } 22 | 23 | return false 24 | } 25 | 26 | fun isReceivedExactly(check: List>): Boolean { 27 | return check == expected 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/ChatClientConfig.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api 2 | 3 | import io.getstream.chat.android.client.logger.ChatLogger 4 | import io.getstream.chat.android.client.notifications.handler.ChatNotificationHandler 5 | import io.getstream.chat.android.client.token.TokenManager 6 | import io.getstream.chat.android.client.token.TokenManagerImpl 7 | 8 | internal class ChatClientConfig( 9 | val apiKey: String, 10 | var httpUrl: String, 11 | var cdnHttpUrl: String, 12 | var wssUrl: String, 13 | var baseTimeout: Long, 14 | var cdnTimeout: Long, 15 | val warmUp: Boolean, 16 | val loggerConfig: ChatLogger.Config, 17 | val notificationsHandler: ChatNotificationHandler, 18 | val tokenManager: TokenManager = TokenManagerImpl() 19 | ) { 20 | 21 | var isAnonymous: Boolean = false 22 | } 23 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/parser/adapters/QuerySortAdapter.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.parser.adapters 2 | 3 | import com.google.gson.Gson 4 | import com.google.gson.TypeAdapter 5 | import com.google.gson.stream.JsonReader 6 | import com.google.gson.stream.JsonWriter 7 | import io.getstream.chat.android.client.api.models.QuerySort 8 | import java.io.IOException 9 | import java.util.ArrayList 10 | 11 | internal class QuerySortAdapter( 12 | private val gson: Gson 13 | ) : TypeAdapter() { 14 | override fun write(out: JsonWriter, value: QuerySort?) { 15 | val adapter = gson.getAdapter(ArrayList::class.java) 16 | adapter.write(out, value?.data as? ArrayList<*>) 17 | } 18 | 19 | override fun read(`in`: JsonReader?): QuerySort? { 20 | throw IOException("QuerySort must not be deserialized") 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /sample/src/androidTest/java/io/getstream/chat/android/client/sample/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.sample 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4 4 | import androidx.test.platform.app.InstrumentationRegistry 5 | import org.junit.Assert.assertEquals 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | /** 10 | * Instrumented test, which will execute on an Android device. 11 | * 12 | * See [testing documentation](http://d.android.com/tools/testing). 13 | */ 14 | @RunWith(AndroidJUnit4::class) 15 | class ExampleInstrumentedTest { 16 | @Test 17 | fun useAppContext() { 18 | // Context of the app under test. 19 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 20 | assertEquals("io.getstream.chat.android.client.sample.test", appContext.packageName) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /sample/src/main/java/io/getstream/chat/android/client/sample/repositories/ChannelsRepositorySync.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.sample.repositories 2 | 3 | import io.getstream.chat.android.client.ChatClient 4 | import io.getstream.chat.android.client.sample.ChannelsCache 5 | import io.getstream.chat.android.client.sample.common.Channel 6 | 7 | class ChannelsRepositorySync( 8 | private val client: ChatClient, 9 | private val cache: ChannelsCache 10 | ) { 11 | fun getChannels(): List { 12 | 13 | return null!! 14 | 15 | // val result = client.queryChannels().execute() 16 | // return if (result.isSuccess()) { 17 | // val channels = ApiMapper.mapChannels(result.data()) 18 | // cache.storeSync(channels) 19 | // channels 20 | // } else { 21 | // cache.getAllSync() 22 | // } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /client/src/test/java/io/getstream/chat/android/client/api/FakeResponse.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api 2 | 3 | import okhttp3.MediaType 4 | import okhttp3.MediaType.Companion.toMediaType 5 | import okhttp3.ResponseBody 6 | import okio.Buffer 7 | import okio.BufferedSource 8 | import java.nio.charset.Charset 9 | 10 | internal data class FakeResponse(val statusCode: Int, val body: Body? = null) { 11 | class Body(data: String) : ResponseBody() { 12 | 13 | val buffer = Buffer().writeString(data, Charset.defaultCharset()) 14 | 15 | override fun contentLength(): Long { 16 | return buffer.size 17 | } 18 | 19 | override fun contentType(): MediaType? { 20 | return "application/json".toMediaType() 21 | } 22 | 23 | override fun source(): BufferedSource { 24 | return buffer 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/call/Call.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.call 2 | 3 | import androidx.annotation.UiThread 4 | import androidx.annotation.WorkerThread 5 | import io.getstream.chat.android.client.errors.ChatError 6 | import io.getstream.chat.android.client.utils.Result 7 | 8 | public interface Call { 9 | 10 | @WorkerThread 11 | public fun execute(): Result 12 | 13 | @UiThread 14 | public fun enqueue(callback: (Result) -> Unit = {}) 15 | 16 | public fun cancel() 17 | 18 | public fun map(mapper: (T) -> K): Call 19 | public fun onError(handler: (ChatError) -> Unit): Call 20 | public fun onSuccess(handler: (T) -> Unit): Call 21 | public fun zipWith(call: Call): Call> 22 | public fun zipWith(callK: Call, callP: Call): Call> 23 | } 24 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/models/Flag.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.models 2 | 3 | import com.google.gson.annotations.SerializedName 4 | import java.util.Date 5 | 6 | public data class Flag( 7 | val user: User, 8 | @SerializedName("target_user") 9 | val targetUser: User?, 10 | @SerializedName("target_message_id") 11 | val targetMessageId: String, 12 | @SerializedName("created_at") 13 | val reviewedBy: String, 14 | @SerializedName("created_by_automod") 15 | val createdByAutomod: Boolean, 16 | @SerializedName("approved_at") 17 | val createdAt: Date, 18 | @SerializedName("updated_at") 19 | val updatedAt: Date, 20 | @SerializedName("reviewed_at") 21 | val reviewedAt: Date, 22 | @SerializedName("reviewed_by") 23 | val approvedAt: Date, 24 | @SerializedName("rejected_at") 25 | val rejectedAt: Date 26 | ) 27 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/notifications/ChatFirebaseMessagingService.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.notifications 2 | 3 | import com.google.firebase.messaging.FirebaseMessagingService 4 | import com.google.firebase.messaging.RemoteMessage 5 | import io.getstream.chat.android.client.ChatClient 6 | import io.getstream.chat.android.client.logger.ChatLogger 7 | 8 | internal class ChatFirebaseMessagingService : FirebaseMessagingService() { 9 | private val logger = ChatLogger.get("ChatFirebaseMessagingService") 10 | 11 | override fun onMessageReceived(remoteMessage: RemoteMessage) { 12 | logger.logD("onMessageReceived(): $remoteMessage") 13 | ChatClient.instance().onMessageReceived(remoteMessage, this) 14 | } 15 | 16 | override fun onNewToken(token: String) { 17 | ChatClient.instance().onNewTokenReceived(token, this) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/models/QueryMembersRequest.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api.models 2 | 3 | import com.google.gson.annotations.SerializedName 4 | import io.getstream.chat.android.client.models.Member 5 | import io.getstream.chat.android.client.parser.IgnoreSerialisation 6 | import io.getstream.chat.android.client.utils.FilterObject 7 | 8 | internal data class QueryMembersRequest( 9 | @SerializedName("type") 10 | val channelType: String, 11 | @SerializedName("id") 12 | val channelId: String, 13 | @IgnoreSerialisation 14 | var filter: FilterObject, 15 | val offset: Int, 16 | val limit: Int, 17 | @IgnoreSerialisation 18 | var querySort: QuerySort = QuerySort(), 19 | val members: List = emptyList() 20 | ) { 21 | val sort = querySort.data 22 | val filter_conditions: Map = filter.toMap() 23 | } 24 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/utils/Result.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.utils 2 | 3 | import io.getstream.chat.android.client.errors.ChatError 4 | 5 | public data class Result( 6 | private val data: T?, 7 | private val error: ChatError? 8 | ) { 9 | 10 | public constructor(data: T) : this(data, null) 11 | public constructor(error: ChatError) : this(null, error) 12 | 13 | val isSuccess: Boolean 14 | get() = data != null 15 | 16 | val isError: Boolean 17 | get() = error != null 18 | 19 | public fun data(): T { 20 | return checkNotNull(data) { "Result is not successful. Check result.isSuccess before reading the data." } 21 | } 22 | 23 | public fun error(): ChatError { 24 | return checkNotNull(error) { "Result is successful, not an error. Check result.isSuccess before reading the error." } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/parser/FilterObjectAdapter.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.parser 2 | 3 | import com.google.gson.Gson 4 | import com.google.gson.TypeAdapter 5 | import com.google.gson.stream.JsonReader 6 | import com.google.gson.stream.JsonWriter 7 | import io.getstream.chat.android.client.utils.FilterObject 8 | 9 | internal class FilterObjectAdapter(val gson: Gson) : TypeAdapter() { 10 | 11 | override fun write(out: JsonWriter, value: FilterObject) { 12 | val adapter = gson.getAdapter(HashMap::class.java) 13 | adapter.write(out, value.toMap()) 14 | } 15 | 16 | @Suppress("UNCHECKED_CAST") 17 | override fun read(reader: JsonReader): FilterObject { 18 | val adapter = gson.getAdapter(HashMap::class.java) 19 | val data = adapter.read(reader) as HashMap 20 | return FilterObject(data) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/models/ChannelRequest.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api.models 2 | 3 | @Suppress("UNCHECKED_CAST") 4 | internal interface ChannelRequest> { 5 | 6 | var state: Boolean 7 | var watch: Boolean 8 | var presence: Boolean 9 | 10 | fun withWatch(): T { 11 | watch = true 12 | return this as T 13 | } 14 | 15 | fun withState(): T { 16 | state = true 17 | return this as T 18 | } 19 | 20 | fun noWatch(): T { 21 | watch = false 22 | return this as T 23 | } 24 | 25 | fun noState(): T { 26 | state = false 27 | return this as T 28 | } 29 | 30 | fun withPresence(): T { 31 | presence = true 32 | return this as T 33 | } 34 | 35 | fun noPresence(): T { 36 | presence = false 37 | return this as T 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/HeadersInterceptor.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api 2 | 3 | import io.getstream.chat.android.client.ChatClient 4 | import okhttp3.Interceptor 5 | import okhttp3.Response 6 | 7 | internal class HeadersInterceptor(val config: ChatClientConfig) : Interceptor { 8 | override fun intercept(chain: Interceptor.Chain): Response { 9 | val authType = if (config.isAnonymous) "anonymous" else "jwt" 10 | val request = chain.request() 11 | .newBuilder() 12 | .addHeader("Content-Type", "application/json") 13 | .addHeader("stream-auth-type", authType) 14 | .addHeader("Accept-Encoding", "application/gzip") 15 | .addHeader("X-STREAM-CLIENT", ChatClient.instance().getVersion()) 16 | .addHeader("Cache-Control", "no-cache") 17 | .build() 18 | return chain.proceed(request) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /sample/src/main/java/io/getstream/chat/android/client/sample/repositories/ChannelsRepositoryLive.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.sample.repositories 2 | 3 | import androidx.lifecycle.LiveData 4 | import io.getstream.chat.android.client.ChatClient 5 | import io.getstream.chat.android.client.sample.ChannelsCache 6 | import io.getstream.chat.android.client.sample.common.Channel 7 | 8 | class ChannelsRepositoryLive( 9 | private val client: ChatClient, 10 | private val cache: ChannelsCache 11 | ) { 12 | 13 | fun getChannels(): LiveData> { 14 | 15 | return null!! 16 | 17 | // val call = client.queryChannels() 18 | // val live = cache.getAllLive() 19 | // 20 | // call.enqueue { result -> 21 | // if (result.isSuccess()) { 22 | // cache.storeAsync(ApiMapper.mapChannels(result.data())) 23 | // } 24 | // } 25 | // 26 | // return live 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/parser/UrlQueryPayloadFactory.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.parser 2 | 3 | import com.google.gson.Gson 4 | import retrofit2.Converter 5 | import retrofit2.Retrofit 6 | import java.lang.reflect.Type 7 | 8 | internal class UrlQueryPayloadFactory(private val gson: Gson) : Converter.Factory() { 9 | 10 | override fun stringConverter( 11 | type: Type, 12 | annotations: Array, 13 | retrofit: Retrofit 14 | ): Converter<*, String>? { 15 | 16 | return if (annotations.filterIsInstance().isNotEmpty()) { 17 | UrlQueryPayloadConverted(gson) 18 | } else { 19 | super.stringConverter(type, annotations, retrofit) 20 | } 21 | } 22 | } 23 | 24 | private class UrlQueryPayloadConverted(val gson: Gson) : Converter { 25 | override fun convert(value: Any): String { 26 | return gson.toJson(value) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/channel_list_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 14 | 15 | 20 | 21 | 26 | 27 | -------------------------------------------------------------------------------- /sample/app-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_key": "qk4nn7rpcn75", 3 | "api_endpoint": "chat-us-east-staging.stream-io-api.com", 4 | "api_timeout": 6000, 5 | "cdn_timeout": 30000, 6 | "users": [ 7 | { 8 | "id": "bender", 9 | "name": "Bender", 10 | "image": "https://bit.ly/321RmWb", 11 | "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiYmVuZGVyIn0.3KYJIoYvSPgTURznP8nWvsA2Yj2-vLqrm-ubqAeOlcQ" 12 | }, 13 | { 14 | "id": "broken-waterfall-5", 15 | "name": "Jon Snow", 16 | "image": "https://bit.ly/2u9Vc0r", 17 | "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiYnJva2VuLXdhdGVyZmFsbC01In0.d1xKTlD_D0G-VsBoDBNbaLjO-2XWNA8rlTm4ru4sMHg" 18 | }, 19 | { 20 | "id": "steep-moon-9", 21 | "name": "Steep moon", 22 | "image": "https://i.imgur.com/EgEPqWZ.jpg", 23 | "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3RlZXAtbW9vbi05In0.K7uZEqKmiVb5_Y7XFCmlz64SzOV34hoMpeqRSz7g4YI" 24 | } 25 | ] 26 | } -------------------------------------------------------------------------------- /sample/src/main/java/io/getstream/chat/android/client/sample/common/DiffCallback.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.sample.common 2 | 3 | import androidx.recyclerview.widget.DiffUtil 4 | 5 | internal class DiffCallback( 6 | private val oldData: List, 7 | private val newData: List 8 | ) : 9 | DiffUtil.Callback() { 10 | override fun getOldListSize(): Int { 11 | return oldData.size 12 | } 13 | 14 | override fun getNewListSize(): Int { 15 | return newData.size 16 | } 17 | 18 | override fun areItemsTheSame( 19 | oldItemPosition: Int, 20 | newItemPosition: Int 21 | ): Boolean { 22 | return oldData[oldItemPosition].remoteId == newData[newItemPosition].remoteId 23 | } 24 | 25 | override fun areContentsTheSame( 26 | oldItemPosition: Int, 27 | newItemPosition: Int 28 | ): Boolean { 29 | return oldData[oldItemPosition].updatedAt == newData[newItemPosition].updatedAt 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/helpers/AttachmentHelper.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.helpers 2 | 3 | import io.getstream.chat.android.client.models.Attachment 4 | import io.getstream.chat.android.client.utils.SystemTimeProvider 5 | import okhttp3.HttpUrl.Companion.toHttpUrlOrNull 6 | 7 | public class AttachmentHelper(private val systemTimeProvider: SystemTimeProvider = SystemTimeProvider()) { 8 | 9 | public fun hasValidImageUrl(attachment: Attachment): Boolean { 10 | val url = attachment.imageUrl?.toHttpUrlOrNull() ?: return false 11 | if (url.queryParameterNames.contains(QUERY_KEY_NAME_EXPIRES).not()) { 12 | return true 13 | } 14 | val timestamp = url.queryParameter(QUERY_KEY_NAME_EXPIRES)?.toLongOrNull() ?: return false 15 | return timestamp > systemTimeProvider.provideCurrentTimeInSeconds() 16 | } 17 | 18 | private companion object { 19 | const val QUERY_KEY_NAME_EXPIRES = "Expires" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /client/src/debug/java/io/getstream/chat/android/client/di/ChatModule.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.di 2 | 3 | import android.content.Context 4 | import com.facebook.stetho.Stetho 5 | import com.facebook.stetho.okhttp3.StethoInterceptor 6 | import io.getstream.chat.android.client.api.ChatClientConfig 7 | import io.getstream.chat.android.client.parser.ChatParser 8 | import okhttp3.OkHttpClient 9 | 10 | internal class ChatModule(appContext: Context, config: ChatClientConfig) : BaseChatModule(appContext, config) { 11 | 12 | init { 13 | Stetho.initializeWithDefaults(appContext) 14 | } 15 | 16 | override fun clientBuilder( 17 | connectTimeout: Long, 18 | writeTimeout: Long, 19 | readTimeout: Long, 20 | config: ChatClientConfig, 21 | parser: ChatParser 22 | ): OkHttpClient.Builder { 23 | return super.clientBuilder(connectTimeout, writeTimeout, readTimeout, config, parser) 24 | .addNetworkInterceptor(StethoInterceptor()) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/notifications/handler/NotificationConfig.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.notifications.handler 2 | 3 | import io.getstream.chat.android.client.R 4 | 5 | public data class NotificationConfig( 6 | val notificationChannelId: Int = R.string.stream_chat_notification_channel_id, 7 | val notificationChannelName: Int = R.string.stream_chat_notification_channel_name, 8 | val smallIcon: Int = R.drawable.stream_ic_notification, 9 | val firebaseMessageIdKey: String = "message_id", 10 | val firebaseMessageTextKey: String = "message_text", 11 | val firebaseChannelIdKey: String = "channel_id", 12 | val firebaseChannelTypeKey: String = "channel_type", 13 | val firebaseChannelNameKey: String = "channel_name", 14 | val errorCaseNotificationTitle: Int = R.string.stream_chat_notification_title, 15 | val errorCaseNotificationContent: Int = R.string.stream_chat_notification_content, 16 | val useProvidedFirebaseInstance: Boolean = true 17 | ) 18 | -------------------------------------------------------------------------------- /client/src/test/java/io/getstream/chat/android/client/utils/RetroSuccess.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.utils 2 | 3 | import okhttp3.Request 4 | import okio.Timeout 5 | import retrofit2.Call 6 | import retrofit2.Callback 7 | import retrofit2.Response 8 | 9 | internal class RetroSuccess(val result: T) : Call { 10 | override fun enqueue(callback: Callback) { 11 | callback.onResponse(this, execute()) 12 | } 13 | 14 | override fun isExecuted(): Boolean { 15 | return true 16 | } 17 | 18 | override fun clone(): Call { 19 | return this 20 | } 21 | 22 | override fun isCanceled(): Boolean { 23 | return false 24 | } 25 | 26 | override fun cancel() { 27 | } 28 | 29 | override fun execute(): Response { 30 | return Response.success(result) 31 | } 32 | 33 | override fun request(): Request { 34 | return null!! 35 | } 36 | 37 | override fun timeout(): Timeout { 38 | return Timeout() 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/models/RetroProgressCallback.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api.models 2 | 3 | import io.getstream.chat.android.client.errors.ChatError 4 | import io.getstream.chat.android.client.utils.ProgressCallback 5 | import retrofit2.Call 6 | import retrofit2.Callback 7 | 8 | internal class RetroProgressCallback( 9 | private val callback: ProgressCallback 10 | ) : Callback { 11 | 12 | override fun onFailure(call: Call, t: Throwable) { 13 | callback.onError(ChatError(cause = t)) 14 | } 15 | 16 | override fun onResponse( 17 | call: Call, 18 | response: retrofit2.Response 19 | ) { 20 | val body = response.body() 21 | if (body == null) { 22 | onFailure(call, RuntimeException("file response is null")) 23 | } else { 24 | val file = body.file 25 | callback.onSuccess(file) 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /sample/src/main/java/io/getstream/chat/android/client/sample/utils/UtilsMessages.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.sample.utils 2 | 3 | import android.widget.Toast 4 | import io.getstream.chat.android.client.errors.ChatError 5 | import io.getstream.chat.android.client.sample.App 6 | import io.getstream.chat.android.client.utils.Result 7 | 8 | object UtilsMessages { 9 | 10 | fun show(error: ChatError) { 11 | error.cause?.printStackTrace() 12 | show(error.message.toString()) 13 | } 14 | 15 | fun show(msg: String) { 16 | App.instance.latestResumed?.runOnUiThread { 17 | Toast.makeText(App.instance, msg, Toast.LENGTH_SHORT).show() 18 | } 19 | } 20 | 21 | fun show(result: Result<*>) { 22 | show("success", "error", result) 23 | } 24 | 25 | fun show(success: String, error: String, result: Result<*>) { 26 | if (result.isSuccess) { 27 | show(success) 28 | } else { 29 | show(error + " " + result.error().message) 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/errors/ChatNetworkError.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.errors 2 | 3 | public class ChatNetworkError private constructor( 4 | public val description: String, 5 | cause: Throwable? = null, 6 | public val streamCode: Int, 7 | public val statusCode: Int 8 | ) : ChatError( 9 | "Status code: $statusCode, with stream code: $streamCode, description: $description", 10 | cause 11 | ) { 12 | public companion object { 13 | public fun create( 14 | code: ChatErrorCode, 15 | cause: Throwable? = null, 16 | statusCode: Int = -1 17 | ): ChatNetworkError { 18 | return ChatNetworkError(code.description, cause, code.code, statusCode) 19 | } 20 | 21 | public fun create( 22 | streamCode: Int, 23 | description: String, 24 | statusCode: Int, 25 | cause: Throwable? = null 26 | ): ChatNetworkError { 27 | return ChatNetworkError(description, cause, streamCode, statusCode) 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/ClientExtensions.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client 2 | 3 | import io.getstream.chat.android.client.events.ChatEvent 4 | import io.getstream.chat.android.client.utils.observable.Disposable 5 | import kotlin.reflect.KClass 6 | 7 | public inline fun ChatClient.subscribeFor( 8 | crossinline listener: (event: T) -> Unit 9 | ): Disposable { 10 | return this.subscribeFor( 11 | T::class.java, 12 | listener = { event -> 13 | listener(event as T) 14 | } 15 | ) 16 | } 17 | 18 | public fun ChatClient.subscribeFor( 19 | vararg eventTypes: KClass, 20 | listener: (event: ChatEvent) -> Unit 21 | ): Disposable { 22 | val javaClassTypes: Array> = eventTypes.map { it.java }.toTypedArray() 23 | return subscribeFor(*javaClassTypes, listener = listener) 24 | } 25 | 26 | public inline fun ChatClient.subscribeForSingle( 27 | noinline listener: (event: T) -> Unit 28 | ): Disposable { 29 | return this.subscribeForSingle(T::class.java, listener) 30 | } 31 | -------------------------------------------------------------------------------- /.github/workflows/android.yml: -------------------------------------------------------------------------------- 1 | name: Android CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | test: 7 | name: Run Unit Tests 8 | runs-on: ubuntu-18.04 9 | 10 | steps: 11 | - name: Check out code 12 | uses: actions/checkout@v2 13 | - name: Set up JDK 1.8 14 | uses: actions/setup-java@v1 15 | with: 16 | java-version: 1.8 17 | - name: ktlint 18 | run: ./gradlew ktlintCheck 19 | - name: Unit tests 20 | run: ./gradlew test --stacktrace 21 | - name: Coverage 22 | run: ./gradlew jacocoTestReport 23 | - name: Upload Coverage 24 | uses: codecov/codecov-action@v1 25 | - name: Upload testDebugUnitTest results 26 | uses: actions/upload-artifact@v2 27 | if: failure() 28 | with: 29 | name: testDebugUnitTest 30 | path: client/build/reports/tests/testDebugUnitTest 31 | - name: Upload testDebugUnitTest results 32 | uses: actions/upload-artifact@v2 33 | if: failure() 34 | with: 35 | name: testReleaseUnitTest 36 | path: client/build/reports/tests/testReleaseUnitTest 37 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/logger/ChatSilentLogger.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.logger 2 | 3 | import io.getstream.chat.android.client.errors.ChatError 4 | 5 | internal class ChatSilentLogger : ChatLogger { 6 | 7 | override fun getLevel(): ChatLogLevel { 8 | return ChatLogLevel.NOTHING 9 | } 10 | 11 | override fun logE(tag: Any, throwable: Throwable) { 12 | // silent 13 | } 14 | 15 | override fun logE(tag: Any, message: String, throwable: Throwable) { 16 | // silent 17 | } 18 | 19 | override fun logE(tag: Any, chatError: ChatError) { 20 | // silent 21 | } 22 | 23 | override fun logE(tag: Any, message: String, chatError: ChatError) { 24 | // silent 25 | } 26 | 27 | override fun logI(tag: Any, message: String) { 28 | // silent 29 | } 30 | 31 | override fun logD(tag: Any, message: String) { 32 | // silent 33 | } 34 | 35 | override fun logW(tag: Any, message: String) { 36 | // silent 37 | } 38 | 39 | override fun logE(tag: Any, message: String) { 40 | // silent 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /client/src/test/java/io/getstream/chat/android/client/utils/observable/FakeChatSocket.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.utils.observable 2 | 3 | import io.getstream.chat.android.client.events.ChatEvent 4 | import io.getstream.chat.android.client.models.User 5 | import io.getstream.chat.android.client.socket.ChatSocket 6 | import io.getstream.chat.android.client.socket.SocketListener 7 | 8 | internal class FakeChatSocket : ChatSocket { 9 | 10 | private val listeners = mutableSetOf() 11 | 12 | override fun connect(user: User) { 13 | } 14 | 15 | override fun connectAnonymously() { 16 | } 17 | 18 | override fun events(): ChatObservable { 19 | error("not implemented") 20 | } 21 | 22 | override fun addListener(listener: SocketListener) { 23 | listeners += listener 24 | } 25 | 26 | override fun removeListener(listener: SocketListener) { 27 | listeners -= listener 28 | } 29 | 30 | fun sendEvent(event: ChatEvent) { 31 | listeners.forEach { 32 | it.onEvent(event) 33 | } 34 | } 35 | 36 | override fun disconnect() { 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /sample/src/main/java/io/getstream/chat/android/client/sample/cache/AppDatabase.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.sample.cache 2 | 3 | import android.content.Context 4 | import androidx.room.Database 5 | import androidx.room.Room 6 | import androidx.room.RoomDatabase 7 | import io.getstream.chat.android.client.sample.common.Channel 8 | 9 | @Database(entities = [Channel::class], version = 2) 10 | abstract class AppDatabase : RoomDatabase() { 11 | 12 | abstract fun channels(): ChannelsDao 13 | 14 | companion object { 15 | 16 | @Volatile 17 | private var INSTANCE: AppDatabase? = null 18 | 19 | fun getInstance(context: Context): AppDatabase = 20 | INSTANCE ?: synchronized(this) { 21 | INSTANCE ?: buildDatabase(context).also { INSTANCE = it } 22 | } 23 | 24 | private fun buildDatabase(context: Context) = 25 | Room.databaseBuilder( 26 | context.applicationContext, 27 | AppDatabase::class.java, 28 | "main.db" 29 | ) 30 | .fallbackToDestructiveMigration() 31 | .build() 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/api/models/WatchChannelRequest.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.api.models 2 | 3 | public class WatchChannelRequest : QueryChannelRequest() { 4 | 5 | init { 6 | watch = true 7 | presence = false 8 | state = true 9 | } 10 | 11 | override fun withData(data: Map): WatchChannelRequest { 12 | return super.withData(data) as WatchChannelRequest 13 | } 14 | 15 | override fun withMembers(limit: Int, offset: Int): WatchChannelRequest { 16 | return super.withMembers(limit, offset) as WatchChannelRequest 17 | } 18 | 19 | override fun withWatchers(limit: Int, offset: Int): WatchChannelRequest { 20 | return super.withWatchers(limit, offset) as WatchChannelRequest 21 | } 22 | 23 | override fun withMessages(limit: Int): WatchChannelRequest { 24 | return super.withMessages(limit) as WatchChannelRequest 25 | } 26 | 27 | override fun withMessages(direction: Pagination, messageId: String, limit: Int): WatchChannelRequest { 28 | return super.withMessages(direction, messageId, limit) as WatchChannelRequest 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/controllers/ChannelControllerExtensions.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.controllers 2 | 3 | import io.getstream.chat.android.client.events.ChatEvent 4 | import io.getstream.chat.android.client.utils.observable.Disposable 5 | import kotlin.reflect.KClass 6 | 7 | public inline fun ChannelController.subscribeFor( 8 | crossinline listener: (event: T) -> Unit 9 | ): Disposable { 10 | return this.subscribeFor( 11 | T::class.java, 12 | listener = { event -> 13 | listener(event as T) 14 | } 15 | ) 16 | } 17 | 18 | public fun ChannelController.subscribeFor( 19 | vararg eventTypes: KClass, 20 | listener: (event: ChatEvent) -> Unit 21 | ): Disposable { 22 | val javaClassTypes: Array> = eventTypes.map { it.java }.toTypedArray() 23 | return subscribeFor(*javaClassTypes, listener = listener) 24 | } 25 | 26 | public inline fun ChannelController.subscribeForSingle( 27 | noinline listener: (event: T) -> Unit 28 | ): Disposable { 29 | return this.subscribeForSingle(T::class.java, listener) 30 | } 31 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/errors/ChatErrorCode.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.errors 2 | 3 | public enum class ChatErrorCode(public val code: Int, public val description: String) { 4 | 5 | /** 6 | * Local 7 | */ 8 | NETWORK_FAILED(1000, "Response is failed. See cause"), 9 | PARSER_ERROR(1001, "Unable to parse error"), 10 | SOCKET_CLOSED(1002, "Server closed connection"), 11 | SOCKET_FAILURE(1003, "See stack trace in logs. Intercept error in error handler of setUser"), 12 | CANT_PARSE_CONNECTION_EVENT(1004, "Unable to parse connection event"), 13 | CANT_PARSE_EVENT(1005, "Unable to parse event"), 14 | INVALID_TOKEN(1006, "Invalid token"), 15 | UNDEFINED_TOKEN(1007, "No defined token. Check if client.setUser was called and finished"), 16 | UNABLE_TO_PARSE_SOCKET_EVENT(1008, "Socket event payload either invalid or null"), 17 | NO_ERROR_BODY(1009, "No error body. See http status code"), 18 | 19 | /** 20 | * Backend 21 | */ 22 | TOKEN_EXPIRED(40, "Token expired, new one must be requested."), 23 | API_KEY_NOT_FOUND(2, "Api key is not found, verify it if it's correct or was created.") 24 | } 25 | -------------------------------------------------------------------------------- /client/src/test/java/io/getstream/chat/android/client/utils/RetroError.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.utils 2 | 3 | import okhttp3.MediaType.Companion.toMediaType 4 | import okhttp3.Request 5 | import okhttp3.ResponseBody.Companion.toResponseBody 6 | import okio.Timeout 7 | import retrofit2.Call 8 | import retrofit2.Callback 9 | import retrofit2.Response 10 | 11 | internal class RetroError(val statusCode: Int) : Call { 12 | override fun enqueue(callback: Callback) { 13 | callback.onResponse(this, execute()) 14 | } 15 | 16 | override fun isExecuted(): Boolean { 17 | return true 18 | } 19 | 20 | override fun clone(): Call { 21 | return this 22 | } 23 | 24 | override fun isCanceled(): Boolean { 25 | return false 26 | } 27 | 28 | override fun cancel() { 29 | } 30 | 31 | override fun execute(): Response { 32 | return Response.error( 33 | statusCode, 34 | "{Server error}".toResponseBody("text/plain".toMediaType()) 35 | ) 36 | } 37 | 38 | override fun request(): Request { 39 | return null!! 40 | } 41 | 42 | override fun timeout(): Timeout { 43 | return Timeout() 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /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 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app's APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true 20 | # Kotlin code style for this project: "official" or "obsolete": 21 | kotlin.code.style=official 22 | -------------------------------------------------------------------------------- /sample/src/main/java/io/getstream/chat/android/client/sample/examples/livedata/ChannelsViewModel.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.sample.examples.livedata 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MediatorLiveData 5 | import androidx.lifecycle.MutableLiveData 6 | import androidx.lifecycle.Transformations 7 | import io.getstream.chat.android.client.sample.ViewState 8 | import io.getstream.chat.android.client.sample.common.Channel 9 | import io.getstream.chat.android.client.sample.repositories.ChannelsRepositoryLive 10 | 11 | class ChannelsViewModel(private val repository: ChannelsRepositoryLive) { 12 | 13 | fun channels(): LiveData>> { 14 | 15 | val liveData = MediatorLiveData>>() 16 | 17 | liveData.addSource(MutableLiveData>>(ViewState.Loading())) { 18 | liveData.value = it 19 | } 20 | 21 | liveData.addSource( 22 | Transformations.map(repository.getChannels()) { channels -> 23 | ViewState.Success(channels) 24 | } 25 | ) { 26 | if (it.data.isNotEmpty()) liveData.value = it 27 | } 28 | 29 | return liveData 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /sample/src/main/java/io/getstream/chat/android/client/sample/examples/coroutines/ChannelsListFragment.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.sample.examples.coroutines 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import io.getstream.chat.android.client.sample.App 6 | import io.getstream.chat.android.client.sample.ViewState 7 | import io.getstream.chat.android.client.sample.common.BaseChannelsListFragment 8 | 9 | class ChannelsListFragment : BaseChannelsListFragment() { 10 | override fun reload() { 11 | load() 12 | } 13 | 14 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 15 | load() 16 | } 17 | 18 | private fun load() { 19 | val vm = ChannelsViewModel(App.channelsRepositorySync) 20 | 21 | vm.channels().observe(viewLifecycleOwner) { state -> 22 | when (state) { 23 | is ViewState.Loading -> { 24 | drawLoading() 25 | } 26 | is ViewState.Error -> { 27 | drawError(state.error) 28 | } 29 | is ViewState.Success -> { 30 | drawSuccess(state.data) 31 | } 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /sample/src/main/java/io/getstream/chat/android/client/sample/examples/livedata/ChannelsListFragment.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.sample.examples.livedata 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import io.getstream.chat.android.client.sample.App 6 | import io.getstream.chat.android.client.sample.ViewState 7 | import io.getstream.chat.android.client.sample.common.BaseChannelsListFragment 8 | 9 | class ChannelsListFragment : BaseChannelsListFragment() { 10 | 11 | override fun reload() { 12 | load() 13 | } 14 | 15 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 16 | load() 17 | } 18 | 19 | private fun load() { 20 | val vm = ChannelsViewModel(App.channelsRepositoryLive) 21 | 22 | vm.channels().observe(viewLifecycleOwner) { state -> 23 | when (state) { 24 | is ViewState.Loading -> { 25 | drawLoading() 26 | } 27 | is ViewState.Error -> { 28 | drawError(state.error) 29 | } 30 | is ViewState.Success -> { 31 | drawSuccess(state.data) 32 | } 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /sample/src/main/java/io/getstream/chat/android/client/sample/examples/rx/ChannelsViewModelRx.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.sample.examples.rx 2 | 3 | import io.getstream.chat.android.client.sample.ViewState 4 | import io.getstream.chat.android.client.sample.ViewState.Success 5 | import io.getstream.chat.android.client.sample.common.Channel 6 | import io.getstream.chat.android.client.sample.repositories.ChannelsRepositoryRx 7 | import io.reactivex.Observable 8 | import io.reactivex.android.schedulers.AndroidSchedulers 9 | import io.reactivex.schedulers.Schedulers 10 | 11 | class ChannelsViewModelRx(val repository: ChannelsRepositoryRx) { 12 | 13 | fun channels(offset: Int, limit: Int): Observable>> { 14 | return loadChannels(offset, limit) 15 | } 16 | 17 | private fun loadChannels( 18 | offset: Int, 19 | limit: Int 20 | ): Observable>> { 21 | return repository.getChannels(offset, limit) 22 | .map>> { Success(it) } 23 | .startWith(ViewState.Loading()) 24 | .onErrorReturn { ViewState.Error(it) } 25 | .subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/models/Config.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.models 2 | 3 | import com.google.gson.annotations.SerializedName 4 | import java.util.Date 5 | 6 | public data class Config( 7 | @SerializedName("created_at") 8 | var created_at: Date? = null, 9 | 10 | @SerializedName("updated_at") 11 | var updated_at: Date? = null, 12 | 13 | @SerializedName("typing_events") 14 | var isTypingEvents: Boolean = false, 15 | 16 | @SerializedName("read_events") 17 | var isReadEvents: Boolean = false, 18 | 19 | @SerializedName("connect_events") 20 | var isConnectEvents: Boolean = false, 21 | 22 | @SerializedName("search") 23 | var isSearch: Boolean = false, 24 | 25 | @SerializedName("reactions") 26 | var isReactionsEnabled: Boolean = false, 27 | 28 | @SerializedName("replies") 29 | var isRepliesEnabled: Boolean = false, 30 | 31 | @SerializedName("mutes") 32 | var isMutes: Boolean = false, 33 | 34 | @SerializedName("max_message_length") 35 | var maxMessageLength: Int = 0, 36 | 37 | var automod: String = "", 38 | var infinite: String = "", 39 | var name: String = "", 40 | 41 | var commands: List = mutableListOf() 42 | ) 43 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/utils/observable/Subscriptions.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.utils.observable 2 | 3 | import io.getstream.chat.android.client.events.ChatEvent 4 | 5 | public interface Disposable { 6 | public val isDisposed: Boolean 7 | public fun dispose() 8 | } 9 | 10 | internal interface EventSubscription : Disposable { 11 | fun onNext(event: ChatEvent) 12 | } 13 | 14 | internal open class SubscriptionImpl( 15 | private val filter: (ChatEvent) -> Boolean, 16 | listener: ((ChatEvent) -> Unit) 17 | ) : EventSubscription { 18 | 19 | private var listener: ((ChatEvent) -> Unit)? = listener 20 | 21 | override var isDisposed: Boolean = false 22 | 23 | var afterEventDelivered: () -> Unit = {} 24 | 25 | override fun dispose() { 26 | isDisposed = true 27 | listener = null 28 | } 29 | 30 | final override fun onNext(event: ChatEvent) { 31 | check(!isDisposed) { "Subscription already disposed, onNext should not be called on it" } 32 | 33 | if (filter(event)) { 34 | try { 35 | listener!!.invoke(event) 36 | } finally { 37 | afterEventDelivered() 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /sample/google-services.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_info": { 3 | "project_number": "964144059857", 4 | "firebase_url": "https://low-level-client-sample.firebaseio.com", 5 | "project_id": "low-level-client-sample", 6 | "storage_bucket": "low-level-client-sample.appspot.com" 7 | }, 8 | "client": [ 9 | { 10 | "client_info": { 11 | "mobilesdk_app_id": "1:964144059857:android:f30e2bf4686dd0b7c33cf0", 12 | "android_client_info": { 13 | "package_name": "io.getstream.chat.android.client.sample" 14 | } 15 | }, 16 | "oauth_client": [ 17 | { 18 | "client_id": "964144059857-ieq7416mql0lcmuc6rnbnili18hn3ts1.apps.googleusercontent.com", 19 | "client_type": 3 20 | } 21 | ], 22 | "api_key": [ 23 | { 24 | "current_key": "AIzaSyAO0kLwyETBr96zAM6KESNTOeLjjpSt9pI" 25 | } 26 | ], 27 | "services": { 28 | "appinvite_service": { 29 | "other_platform_oauth_client": [ 30 | { 31 | "client_id": "964144059857-ieq7416mql0lcmuc6rnbnili18hn3ts1.apps.googleusercontent.com", 32 | "client_type": 3 33 | } 34 | ] 35 | } 36 | } 37 | } 38 | ], 39 | "configuration_version": "1" 40 | } -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/logger/TaggedLoggerImpl.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.logger 2 | 3 | import io.getstream.chat.android.client.errors.ChatError 4 | 5 | internal class TaggedLoggerImpl( 6 | private val tag: Any, 7 | private val logger: ChatLogger 8 | ) : TaggedLogger { 9 | 10 | override fun logI(message: String) { 11 | logger.logI(tag, message) 12 | } 13 | 14 | override fun logD(message: String) { 15 | logger.logD(tag, message) 16 | } 17 | 18 | override fun logW(message: String) { 19 | logger.logW(tag, message) 20 | } 21 | 22 | override fun logE(message: String) { 23 | logger.logE(tag, message) 24 | } 25 | 26 | override fun logE(throwable: Throwable) { 27 | logger.logE(tag, throwable) 28 | } 29 | 30 | override fun logE(message: String, throwable: Throwable) { 31 | logger.logE(tag, message, throwable) 32 | } 33 | 34 | override fun getLevel(): ChatLogLevel { 35 | return logger.getLevel() 36 | } 37 | 38 | override fun logE(message: String, chatError: ChatError) { 39 | logger.logE(tag, message, chatError) 40 | } 41 | 42 | override fun logE(chatError: ChatError) { 43 | logger.logE(tag, chatError) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /client/consumer-proguard-rules.pro: -------------------------------------------------------------------------------- 1 | ## Stream Chat Android Client Proguard Rules 2 | 3 | -keep class io.getstream.chat.android.client.api.* { *; } 4 | -keep class io.getstream.chat.android.client.api.models.* { *; } 5 | -keep class io.getstream.chat.android.client.errors.* { *; } 6 | -keep class io.getstream.chat.android.client.events.* { *; } 7 | -keep class io.getstream.chat.android.client.models.* { *; } 8 | -keep class io.getstream.chat.android.client.parser.* { *; } 9 | -keep class io.getstream.chat.android.client.socket.* { *; } 10 | -keep class io.getstream.chat.android.client.socket.EventsParser.TypedEvent { *; } 11 | -keep class io.getstream.chat.android.client.utils.FilterObject { *; } 12 | -keep class io.getstream.chat.android.client.utils.Result { *; } 13 | -keep class io.getstream.chat.android.client.utils.SyncStatus { *; } 14 | 15 | -keepattributes Signature,*Annotation* 16 | 17 | -keepattributes EnclosingMethod 18 | 19 | -keep class kotlin.** { *; } 20 | -keep class kotlin.Metadata { *; } 21 | -dontwarn kotlin.** 22 | -keepclassmembers class **$WhenMappings { 23 | ; 24 | } 25 | -keepclassmembers class kotlin.Metadata { 26 | public ; 27 | } 28 | -assumenosideeffects class kotlin.jvm.internal.Intrinsics { 29 | static void checkParameterIsNotNull(java.lang.Object, java.lang.String); 30 | } -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/utils/FilterObject.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.utils 2 | 3 | public data class FilterObject(var data: MutableMap = mutableMapOf()) { 4 | 5 | public constructor(key: String, value: Any) : this() { 6 | data[key] = (normalizeValue(value)) 7 | } 8 | 9 | init { 10 | // cleanup references to prevent serialization issues 11 | data = toMap() 12 | } 13 | 14 | public fun put(key: String, value: Any): FilterObject { 15 | data[key] = normalizeValue(value) 16 | 17 | return this 18 | } 19 | 20 | // cleanup references to prevent serialization issues 21 | private fun normalizeValue(value: Any): Any { 22 | return if (value is FilterObject) { 23 | value.toMap() 24 | } else if (value is Array<*> && value.isArrayOf()) { 25 | value.map { (it as FilterObject).toMap() } 26 | } else { 27 | return value 28 | } 29 | } 30 | 31 | @Suppress("UNCHECKED_CAST") 32 | public fun toMap(): HashMap { 33 | val data: HashMap = HashMap() 34 | 35 | for ((key, value) in this.data.entries) { 36 | data[key] = normalizeValue(value) 37 | } 38 | return data 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/activity_push.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /sample/src/main/java/io/getstream/chat/android/client/sample/utils/PaginationListener.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.sample.utils 2 | 3 | import androidx.recyclerview.widget.LinearLayoutManager 4 | import androidx.recyclerview.widget.RecyclerView 5 | 6 | abstract class PaginationListener(private val pageSize: Int) : RecyclerView.OnScrollListener() { 7 | override fun onScrolled( 8 | recyclerView: RecyclerView, 9 | dx: Int, 10 | dy: Int 11 | ) { 12 | super.onScrolled(recyclerView, dx, dy) 13 | val layoutManager = 14 | recyclerView.layoutManager as LinearLayoutManager? 15 | val visibleItemCount = layoutManager!!.childCount 16 | val totalItemCount = layoutManager.itemCount 17 | val firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition() 18 | val loading = isLoading() 19 | val lastPage = isLastPage() 20 | if (!loading && !lastPage) { 21 | if (visibleItemCount + firstVisibleItemPosition >= totalItemCount && 22 | firstVisibleItemPosition >= 0 && totalItemCount >= pageSize 23 | ) { 24 | loadMoreItems() 25 | } 26 | } 27 | } 28 | 29 | protected abstract fun loadMoreItems() 30 | protected abstract fun isLastPage(): Boolean 31 | protected abstract fun isLoading(): Boolean 32 | } 33 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/notifications/FirebaseMessageParserImpl.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.notifications 2 | 3 | import com.google.firebase.messaging.RemoteMessage 4 | import io.getstream.chat.android.client.notifications.handler.ChatNotificationHandler 5 | 6 | internal class FirebaseMessageParserImpl(val handler: ChatNotificationHandler) : FirebaseMessageParser { 7 | 8 | private val messageIdKey = handler.getFirebaseMessageIdKey() 9 | private val channelTypeKey = handler.getFirebaseChannelTypeKey() 10 | private val channelIdKey = handler.getFirebaseChannelIdKey() 11 | 12 | override fun isValid(message: RemoteMessage): Boolean { 13 | return verifyPayload(message) 14 | } 15 | 16 | override fun parse(message: RemoteMessage): FirebaseMessageParser.Data { 17 | val messageId = message.data[messageIdKey]!! 18 | val channelId = message.data[channelIdKey]!! 19 | val channelType = message.data[channelTypeKey]!! 20 | 21 | return FirebaseMessageParser.Data(messageId, channelType, channelId) 22 | } 23 | 24 | private fun verifyPayload(message: RemoteMessage): Boolean { 25 | val keys = setOf(messageIdKey, channelIdKey, channelTypeKey) 26 | return message.data.keys.containsAll(keys) && 27 | keys.none { key -> message.data[key].isNullOrEmpty() } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /client/src/main/java/io/getstream/chat/android/client/models/Reaction.kt: -------------------------------------------------------------------------------- 1 | package io.getstream.chat.android.client.models 2 | 3 | import com.google.gson.annotations.SerializedName 4 | import io.getstream.chat.android.client.parser.IgnoreDeserialisation 5 | import io.getstream.chat.android.client.parser.IgnoreSerialisation 6 | import io.getstream.chat.android.client.utils.SyncStatus 7 | import java.util.Date 8 | 9 | public data class Reaction( 10 | @SerializedName("message_id") 11 | var messageId: String = "", 12 | var type: String = "", 13 | var score: Int = 0, 14 | var user: User? = null, 15 | @SerializedName("user_id") 16 | var userId: String = "", 17 | @SerializedName("created_at") 18 | var createdAt: Date? = null, 19 | 20 | @SerializedName("updated_at") 21 | var updatedAt: Date? = null, 22 | 23 | @IgnoreSerialisation 24 | var syncStatus: SyncStatus = SyncStatus.COMPLETED, 25 | 26 | @IgnoreSerialisation 27 | @IgnoreDeserialisation 28 | override var extraData: MutableMap = mutableMapOf() 29 | 30 | ) : CustomObject { 31 | // this is a workaround around a backend issue 32 | // for some reason we sometimes only get the user id and not the user object 33 | // this needs more investigation on the backend side of things 34 | public fun fetchUserId(): String { 35 | return user?.id ?: userId 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /client/src/test/resources/message.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "8584452-6d711169-0224-41c2-b9aa-1adbe624521b", 3 | "text": "150", 4 | "html": "\u003cp\u003e150\u003c/p\u003e\n", 5 | "type": "regular", 6 | "user": { 7 | "id": "8584452", 8 | "role": "user", 9 | "created_at": "2020-05-18T08:18:23.208978Z", 10 | "updated_at": "2020-06-23T10:41:44.289505Z", 11 | "last_active": "2020-06-23T10:15:41.442538Z", 12 | "banned": false, 13 | "online": true, 14 | "language": "en", 15 | "picture_path": "https://vestiairecollective.imgix.net/profil/8584452-c417564aa562b8a09ac4ea5abfeca7ec.jpg?auto=format\u0026fm=pjpg\u0026w=88\u0026h=88\u0026fit=crop", 16 | "profile": { 17 | "deeplink": "com.vestiairecollective.vestiaire://action?type=profil\u0026id=8584452", 18 | "url": "/members/profile-8584452.shtml" 19 | }, 20 | "username": "mota_shailesh" 21 | }, 22 | "attachments": [ 23 | ], 24 | "latest_reactions": [ 25 | ], 26 | "own_reactions": [ 27 | ], 28 | "reaction_counts": { 29 | }, 30 | "reaction_scores": { 31 | }, 32 | "reply_count": 0, 33 | "created_at": "2020-06-10T11:04:31.0Z", 34 | "updated_at": "2020-06-10T11:04:31.419588Z", 35 | "mentioned_users": [ 36 | ], 37 | "silent": false, 38 | "isYesterday": false, 39 | "cid": "", 40 | "date": "", 41 | "time": "", 42 | "isToday": false, 43 | "isStartDay": false, 44 | "commandInfo": { 45 | } 46 | } -------------------------------------------------------------------------------- /sample/src/main/res/layout/activity_home.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 |